//-----------------------------------------------------------------------------
// VST Plug-Ins SDK
// VSTGUI: Graphical User Interface Framework for VST plugins : 
// Standard Control Objects
//
// Version 3.0       $Date: 2006/02/09 10:18:20 $
//
// Added new objects        : Michael Schmidt          08.97
// Added new objects        : Yvan Grabit              01.98
// Added BeOS version       : Georges-Edouard Berenger 05.99
// Added MacOSX version     : Arne Scheffler           02.03
//
//-----------------------------------------------------------------------------
// VSTGUI LICENSE
// © 2004, Steinberg Media Technologies, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
// 
//   * Redistributions of source code must retain the above copyright notice, 
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation 
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this 
//     software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A  PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>

#ifndef __vstcontrols__
#include "vstcontrols.h"
#endif

#include "vstkeycode.h"

#if (__MACH__ && __MWERKS__)
 #ifndef cosf
 #define cosf (float)cos
 #endif

 #ifndef sinf
 #define sinf (float)sin
 #endif
#endif

BEGIN_NAMESPACE_VSTGUI

// some external variables (vstgui.cpp)
extern long gStandardFontSize [];
extern const char *gStandardFontName [];

//------------------------------------------------------------------------
// CControl
//------------------------------------------------------------------------
/*! @class CControl
This object manages the tag identification and the value of a control object.

Note:
Since version 2.1, when an object uses the transparency for its background and draws on it (tranparency area)
or the transparency area changes during different draws (CMovieBitmap ,...), the background will be false (not updated),
you have to rewrite the draw function in order to redraw the background and then call the draw of the object.
*/
CControl::CControl (const CRect &size, CControlListener *listener, long tag,
					CBitmap *pBackground)
:	CView (size), 
	listener (listener), tag (tag), oldValue (1), defaultValue (0.5f),
	value (0), vmin (0), vmax (1.f), wheelInc (0.1f), lastTicks (-1)
{
	#if WINDOWS
		delta = GetDoubleClickTime ();
	#elif MAC
		delta = GetDblTime ();
	#else
		delta = 500;
	#endif

	if (delta < 250)
		delta = 250;

	setTransparency (false);
	setMouseEnabled (true);
	backOffset (0 ,0);

	setBackground (pBackground);
}

//------------------------------------------------------------------------
CControl::~CControl ()
{
}

//------------------------------------------------------------------------
void CControl::beginEdit ()
{
	// begin of edit parameter
	getFrame ()->setFocusView(this);
	getFrame ()->beginEdit (tag);
}

//------------------------------------------------------------------------
void CControl::endEdit ()
{
	// end of edit parameter
	getFrame ()->endEdit (tag);
}

//------------------------------------------------------------------------
bool CControl::isDirty () const
{
	if (oldValue != value || CView::isDirty ())
		return true;
	return false;
}

//------------------------------------------------------------------------
void CControl::setDirty (const bool val)
{
	CView::setDirty (val);
	if (val)
	{
		if (value != -1.f)
			oldValue = -1.f;
		else
			oldValue = 0.f;
	}
	else
		oldValue = value;
}

//------------------------------------------------------------------------
void CControl::setBackOffset (CPoint &offset)
{
	backOffset = offset;
}

//-----------------------------------------------------------------------------
void CControl::copyBackOffset ()
{
	backOffset (size.left, size.top);
}

//------------------------------------------------------------------------
void CControl::bounceValue ()
{
	if (value > vmax)
		value = vmax;
	else if (value < vmin)
		value = vmin;
}

//-----------------------------------------------------------------------------
bool CControl::checkDefaultValue (CDrawContext *pContext, long button)
{
	if (button == (kControl|kLButton))
	{
		// begin of edit parameter
		beginEdit ();
	
		value = getDefaultValue ();
		if (isDirty () && listener)
			listener->valueChanged (pContext, this);

		// end of edit parameter
		endEdit ();
		return true;
	}
	return false;
}

//-----------------------------------------------------------------------------
bool CControl::isDoubleClick ()
{
	long ticks = getFrame ()->getTicks ();
	if (lastTicks <= 0)
	{
		lastTicks = ticks;
		return false;
	}

	if (lastTicks + delta > ticks)
		lastTicks = 0;
	else
	{
		lastTicks = ticks;
		return false;
	}
	return true;
}

//------------------------------------------------------------------------
// COnOffButton
//------------------------------------------------------------------------
/*! @class COnOffButton
Define a button with 2 positions.
The pixmap includes the 2 subpixmaps (i.e the rectangle used for the display of this button is half-height of the pixmap).
When its value changes, the listener is called.
*/
COnOffButton::COnOffButton (const CRect &size, CControlListener *listener, long tag,
                            CBitmap *background, long style)
: CControl (size, listener, tag, background)
, style (style)
{}

//------------------------------------------------------------------------
COnOffButton::~COnOffButton ()
{}

//------------------------------------------------------------------------
void COnOffButton::draw (CDrawContext *pContext)
{
	CCoord off;

	if (value && pBackground)
		off = pBackground->getHeight () / 2;
	else
		off = 0;

	if (pBackground)
	{
		if (bTransparencyEnabled)
			pBackground->drawTransparent (pContext, size, CPoint (0, off));
		else
			pBackground->draw (pContext, size, CPoint (0, off));
	}
	setDirty (false);
}

//------------------------------------------------------------------------
void COnOffButton::mouse (CDrawContext *pContext, CPoint &where, long button)
{
	if (!bMouseEnabled)
		return;

 	if (button == -1) button = pContext->getMouseButtons ();
	if (!(button & kLButton))
		return;

	if (listener && button & (kAlt | kShift | kControl | kApple))
	{
		if (listener->controlModifierClicked (pContext, this, button) != 0)
			return;
	}
	value = ((long)value) ? 0.f : 1.f;
	
	if (listener && style == kPostListenerUpdate)
	{
		// begin of edit parameter
		beginEdit ();
	
		listener->valueChanged (pContext, this);
	
		// end of edit parameter
		endEdit ();
	}
	
	doIdleStuff ();
	
	if (listener && style == kPreListenerUpdate)
	{
		// begin of edit parameter
		beginEdit ();
	
		listener->valueChanged (pContext, this);
	
		// end of edit parameter
		endEdit ();
	}
}


//------------------------------------------------------------------------
// CKnob
//------------------------------------------------------------------------
/*! @class CKnob
Define a knob with a given background and foreground handle.
The handle describes a circle over the background (between -45deg and +225deg).
By clicking Alt+Left Mouse the default value is used.
By clicking Alt+Left Mouse the value changes with a vertical move (version 2.1)
*/
CKnob::CKnob (const CRect &size, CControlListener *listener, long tag,
              CBitmap *background, CBitmap *handle, const CPoint &offset)
:	CControl (size, listener, tag, background), offset (offset), pHandle (handle)
{
	if (pHandle)
	{
		pHandle->remember ();
		inset = (long)((float)pHandle->getWidth () / 2.f + 2.5f);
	}
	else
		inset = 3;
	
	colorShadowHandle = kGreyCColor;
	colorHandle = kWhiteCColor;
	radius = (float)(size.right - size.left) / 2.f;

	rangeAngle = 10.f;
	setStartAngle ((float)(5.f * kPI / 4.f));
	setRangeAngle ((float)(-3.f * kPI / 2.f));
	zoomFactor = 1.5f;

	setWantsFocus (true);
}

//------------------------------------------------------------------------
CKnob::~CKnob ()
{
	if (pHandle)
		pHandle->forget ();
}

//------------------------------------------------------------------------
void CKnob::draw (CDrawContext *pContext)
{
	if (pBackground)
	{
		if (bTransparencyEnabled)
			pBackground->drawTransparent (pContext, size, offset);
		else
			pBackground->draw (pContext, size, offset);
	}
	drawHandle (pContext);
	setDirty (false);
}

//------------------------------------------------------------------------
void CKnob::drawHandle (CDrawContext *pContext)
{
	CPoint where;
	valueToPoint (where);

	if (pHandle)
	{
		long width  = (long)pHandle->getWidth ();
		long height = (long)pHandle->getHeight ();
		where.offset (size.left - width / 2, size.top - height / 2);

		CRect handleSize (0, 0, width, height);
		handleSize.offset (where.h, where.v);
		pHandle->drawTransparent (pContext, handleSize);
	}
	else
	{
		CPoint origin (size.width () / 2, size.height () / 2);
		
		where.offset (size.left - 1, size.top);
		origin.offset (size.left - 1, size.top);
		pContext->setFrameColor (colorShadowHandle);
		pContext->moveTo (where);
		pContext->lineTo (origin);
		
		where.offset (1, -1);
		origin.offset (1, -1);
		pContext->setFrameColor (colorHandle);
		pContext->moveTo (where);
		pContext->lineTo (origin);
	}
}

//------------------------------------------------------------------------
void CKnob::mouse (CDrawContext *pContext, CPoint &where, long button)
{
	if (!bMouseEnabled)
		return;

	if (button == -1) button = pContext->getMouseButtons ();
	if (!(button & kLButton))
		return;

	if (listener && button & (kAlt | kShift | kControl | kApple))
	{
		if (listener->controlModifierClicked (pContext, this, button) != 0)
			return;
	}

	// check if default value wanted
	if (checkDefaultValue (pContext, button))
		return;

	float old = oldValue;
	CPoint firstPoint;
	bool  modeLinear = false;
	float fEntryState = value;
	float middle = (vmax - vmin) * 0.5f;
	float range = 200.f;
	float coef = (vmax - vmin) / range;
	long  oldButton = button;

	long mode    = kCircularMode;
	long newMode = getFrame ()->getKnobMode ();
	if (kLinearMode == newMode)
	{
		if (!(button & kAlt))
			mode = newMode;
	}
	else if (button & kAlt) 
		mode = kLinearMode;

	if (mode == kLinearMode && (button & kLButton))
	{
		if (button & kShift)
			range *= zoomFactor;
		firstPoint = where;
		modeLinear = true;
		coef = (vmax - vmin) / range;
	}
	else
	{
		CPoint where2 (where);
		where2.offset (-size.left, -size.top);
		old = valueFromPoint (where2);
	}

	CPoint oldWhere (-1, -1);

	// begin of edit parameter
	beginEdit ();
	do
	{
		button = pContext->getMouseButtons ();
		if (where != oldWhere)
		{
			oldWhere = where;
			if (modeLinear)
			{
				CCoord diff = (firstPoint.v - where.v) + (where.h - firstPoint.h);
				if (button != oldButton)
				{
					range = 200.f;
					if (button & kShift)
						range *= zoomFactor;
	
					float coef2 = (vmax - vmin) / range;
					fEntryState += diff * (coef - coef2);
					coef = coef2;
					oldButton = button;
				}
				value = fEntryState + diff * coef;
				bounceValue ();
			}
			else
			{
				where.offset (-size.left, -size.top);
				value = valueFromPoint (where);
				if (old - value > middle)
					value = vmax;
				else if (value - old > middle)
					value = vmin;
				else
					old = value;
			}
			if (isDirty () && listener)
				listener->valueChanged (pContext, this);
		}
		getMouseLocation (pContext, where);
		doIdleStuff ();

	} while (button & kLButton);

	// end of edit parameter
	endEdit ();
}

//------------------------------------------------------------------------
bool CKnob::onWheel (CDrawContext *pContext, const CPoint &where, float distance)
{
	if (!bMouseEnabled)
		return false;

	long buttons = pContext->getMouseButtons ();
	if (buttons & kShift)
		value += 0.1f * distance * wheelInc;
	else
		value += distance * wheelInc;
	bounceValue ();

	if (isDirty () && listener)
	{
		// begin of edit parameter
		beginEdit ();
	
		listener->valueChanged (pContext, this);
	
		// end of edit parameter
		endEdit ();
	}
	return true;
}

//------------------------------------------------------------------------
long CKnob::onKeyDown (VstKeyCode& keyCode)
{
	switch (keyCode.virt)
	{
	case VKEY_UP :
	case VKEY_RIGHT :
	case VKEY_DOWN :
	case VKEY_LEFT :
		{
			float distance = 1.f;
			if (keyCode.virt == VKEY_DOWN || keyCode.virt == VKEY_LEFT)
				distance = -distance;

			if (keyCode.modifier & MODIFIER_SHIFT)
				value += 0.1f * distance * wheelInc;
			else
				value += distance * wheelInc;
			bounceValue ();

			if (isDirty () && listener)
			{
				// begin of edit parameter
				beginEdit ();
				
				listener->valueChanged (0, this);
			
				// end of edit parameter
				endEdit ();
			}
		} return 1;
	}
	return -1;
}

//------------------------------------------------------------------------
void CKnob::setStartAngle (float val)
{
	startAngle = val;
	compute ();
}

//------------------------------------------------------------------------
void CKnob::setRangeAngle (float val)
{
	rangeAngle = val;
	compute ();
}

//------------------------------------------------------------------------
void CKnob::compute ()
{
	aCoef = (vmax - vmin) / rangeAngle;
	bCoef = vmin - aCoef * startAngle;
	halfAngle = ((float)k2PI - fabsf (rangeAngle)) * 0.5f;
	setDirty ();
}

//------------------------------------------------------------------------
void CKnob::valueToPoint (CPoint &point) const
{
	float alpha = (value - bCoef) / aCoef;
	point.h = (long)(radius + cosf (alpha) * (radius - inset) + 0.5f);
	point.v = (long)(radius - sinf (alpha) * (radius - inset) + 0.5f);
}

//------------------------------------------------------------------------
float CKnob::valueFromPoint (CPoint &point) const
{
	float v;
	float alpha = (float)atan2 (radius - point.v, point.h - radius);
	if (alpha < 0.f)
		alpha += (float)k2PI;

	float alpha2 = alpha - startAngle;
	if (rangeAngle < 0)
	{
		alpha2 -= rangeAngle;
		float alpha3 = alpha2;
		if (alpha3 < 0.f)
			alpha3 += (float)k2PI;
		else if (alpha3 > k2PI)
			alpha3 -= (float)k2PI;
		if (alpha3 > halfAngle - rangeAngle)
			v = vmax;
		else if (alpha3 > -rangeAngle)
			v = vmin;
		else
		{
			if (alpha2 > halfAngle - rangeAngle)
				alpha2 -= (float)k2PI;
			else if (alpha2 < -halfAngle)
				alpha2 += (float)k2PI;
			v = aCoef * alpha2 + vmax;
		}
	}
	else
	{
		float alpha3 = alpha2;
		if (alpha3 < 0.f)
			alpha3 += (float)k2PI;
		else if (alpha3 > k2PI)
			alpha3 -= (float)k2PI;
		if (alpha3 > rangeAngle + halfAngle)
			v = vmin;
		else if (alpha3 > rangeAngle)
			v = vmax;
		else
		{
			if (alpha2 > rangeAngle + halfAngle)
				alpha2 -= (float)k2PI;
			else if (alpha2 < -halfAngle)
				alpha2 += (float)k2PI;
			v = aCoef * alpha2 + vmin;
		}
	}

	return v;
}

//------------------------------------------------------------------------
void CKnob::setColorShadowHandle (CColor color)
{
	colorShadowHandle = color;
	setDirty ();
}

//------------------------------------------------------------------------
void CKnob::setColorHandle (CColor color)
{
	colorHandle = color;
	setDirty ();
}

//------------------------------------------------------------------------
void CKnob::setHandleBitmap (CBitmap *bitmap)
{
	if (pHandle)
	{
		pHandle->forget ();
		pHandle = 0;
	}

	if (bitmap)
	{
		pHandle = bitmap;
		pHandle->remember ();
		inset = (long)((float)pHandle->getWidth () / 2.f + 2.5f);
	}
}


//------------------------------------------------------------------------
// CParamDisplay
//------------------------------------------------------------------------
/*! @class CParamDisplay
Define a rectangle view where a text-value can be displayed with a given font and color.
The user can specify its convert function (from float to char) by default the string format is "%2.2f".
The text-value is centered in the given rect.
*/
CParamDisplay::CParamDisplay (const CRect &size, CBitmap *background, const long style)
:	CControl (size, 0, 0, background), stringConvert (0), stringConvert2 (0), string2FloatConvert (0),
	horiTxtAlign (kCenterText), style (style), bTextTransparencyEnabled (true)
{
	backOffset (0, 0);

	fontID      = kNormalFont;
	txtFace     = kNormalFace;
	fontColor   = kWhiteCColor;
	backColor   = kBlackCColor;
	frameColor  = kBlackCColor;
	shadowColor = kRedCColor;
	userData    = 0;
	if (style & kNoDrawStyle)
		setDirty (false);
}

//------------------------------------------------------------------------
CParamDisplay::~CParamDisplay ()
{}

//------------------------------------------------------------------------
void CParamDisplay::setStyle (long val)
{
	if (style != val)
	{
		style = val;
		setDirty ();
	}
}

//------------------------------------------------------------------------
void CParamDisplay::draw (CDrawContext *pContext)
{
	char string[256];
	string[0] = 0;

	if (stringConvert2)
		stringConvert2 (value, string, userData);
	else if (stringConvert)
		stringConvert (value, string);
	else
		sprintf (string, "%2.2f", value);

	drawText (pContext, string);
}

//------------------------------------------------------------------------
void CParamDisplay::drawText (CDrawContext *pContext, char *string, CBitmap *newBack)
{
	setDirty (false);

	if (style & kNoDrawStyle)
		return;

	// draw the background
	if (newBack)
	{
		if (bTransparencyEnabled)
			newBack->drawTransparent (pContext, size, backOffset);
		else
			newBack->draw (pContext, size, backOffset);
	}
	else if (pBackground)
	{
		if (bTransparencyEnabled)
			pBackground->drawTransparent (pContext, size, backOffset);
		else
			pBackground->draw (pContext, size, backOffset);
	}
	else
	{
		if (!bTransparencyEnabled)
		{
			pContext->setFillColor (backColor);
			pContext->fillRect (size);
	
			if (!(style & (k3DIn|k3DOut|kNoFrame))) 
			{
				pContext->setFrameColor (frameColor);
				pContext->drawRect (size);
			}
		}
	}
	// draw the frame for the 3D effect
	if (style & (k3DIn|k3DOut)) 
	{
		CRect r (size);
		r.right--; r.top++;
		r.bottom--;
		pContext->setLineWidth (1);
		if (style & k3DIn)
			pContext->setFrameColor (backColor);
		else
			pContext->setFrameColor (frameColor);
		CPoint p;
		pContext->moveTo (p (r.left, r.bottom));
		pContext->lineTo (p (r.left, r.top));
		pContext->lineTo (p (r.right, r.top));

		if (style & k3DIn)
			pContext->setFrameColor (frameColor);
		else
			pContext->setFrameColor (backColor);
		pContext->moveTo (p (r.right, r.top));
		pContext->lineTo (p (r.right, r.bottom));
		pContext->lineTo (p (r.left, r.bottom));
	}

	if (!(style & kNoTextStyle) && string)
	{
		CRect oldClip;
		pContext->getClipRect (oldClip);
		CRect newClip (size);
		newClip.bound (oldClip);
		pContext->setClipRect (newClip);
		pContext->setFont (fontID, 0, txtFace);
	
		// draw darker text (as shadow)
		if (style & kShadowText) 
		{
			CRect newSize (size);
			newSize.offset (1, 1);
			pContext->setFontColor (shadowColor);
			pContext->drawString (string, newSize, !bTextTransparencyEnabled, horiTxtAlign);
		}
		pContext->setFontColor (fontColor);
		pContext->drawString (string, size, !bTextTransparencyEnabled, horiTxtAlign);
		pContext->setClipRect (oldClip);
	}
}

//------------------------------------------------------------------------
void CParamDisplay::setFont (CFont fontID)
{
	// to force the redraw
	if (this->fontID != fontID)
		setDirty ();
	this->fontID = fontID;
}

//------------------------------------------------------------------------
void CParamDisplay::setTxtFace (CTxtFace txtFace)
{
	// to force the redraw
	if (this->txtFace != txtFace)
		setDirty ();
	this->txtFace = txtFace;
}

//------------------------------------------------------------------------
void CParamDisplay::setFontColor (CColor color)
{
	// to force the redraw
	if (fontColor != color)
		setDirty ();
	fontColor = color;
}

//------------------------------------------------------------------------
void CParamDisplay::setBackColor (CColor color)
{
	// to force the redraw
	if (backColor != color)
		setDirty ();
	backColor = color;
}

//------------------------------------------------------------------------
void CParamDisplay::setFrameColor (CColor color)
{
	// to force the redraw
	if (frameColor != color)
		setDirty ();
	frameColor = color;
}

//------------------------------------------------------------------------
void CParamDisplay::setShadowColor (CColor color)
{
	// to force the redraw
	if (shadowColor != color)
		setDirty ();
	shadowColor = color;
}

//------------------------------------------------------------------------
void CParamDisplay::setHoriAlign (CHoriTxtAlign hAlign)
{
	// to force the redraw
	if (horiTxtAlign != hAlign)
		setDirty ();
	horiTxtAlign = hAlign;
}

//------------------------------------------------------------------------
void CParamDisplay::setStringConvert (void (*convert) (float value, char *string))
{
	stringConvert = convert;
}

//------------------------------------------------------------------------
void CParamDisplay::setStringConvert (void (*convert) (float value, char *string,
									  void *userDta), void *userData)
{
	stringConvert2 = convert;
	this->userData = userData;
}

//------------------------------------------------------------------------
void CParamDisplay::setString2FloatConvert (void (*convert) (char *string, float &output))
{
	string2FloatConvert = convert;
}

//------------------------------------------------------------------------
// CTextLabel
//------------------------------------------------------------------------
/*! @class CTextLabel
*/
CTextLabel::CTextLabel (const CRect& size, const char* txt, CBitmap* background, const long style)
: CParamDisplay (size, background, style)
, text (0)
{
	setText (txt);
}

//------------------------------------------------------------------------
CTextLabel::~CTextLabel ()
{
	freeText ();
}

//------------------------------------------------------------------------
void CTextLabel::freeText ()
{
	if (text)
		free (text);
	text = 0;
}

//------------------------------------------------------------------------
void CTextLabel::setText (const char* txt)
{
	freeText ();
	if (txt)
	{
		text = (char*)malloc (strlen (txt)+1);
		strcpy (text, txt);
	}
}

//------------------------------------------------------------------------
const char* CTextLabel::getText () const
{
	return text;
}

//------------------------------------------------------------------------
void CTextLabel::draw (CDrawContext *pContext)
{
	drawText (pContext, text);
	setDirty (false);
}


//------------------------------------------------------------------------
// CTextEdit
//------------------------------------------------------------------------
/*! @class CTextEdit
Define a rectangle view where a text-value can be displayed and edited with a given font and color.
The user can specify its convert function (from char to char). The text-value is centered in the given rect.
A pixmap can be used as background.
*/
CTextEdit::CTextEdit (const CRect &size, CControlListener *listener, long tag,
	const char *txt, CBitmap *background, const long style)
:	CParamDisplay (size, background, style), platformFontColor (0), platformControl (0),
	platformFont (0), editConvert (0), editConvert2 (0)
{
#if (MAC && !MACX)
	text_edit = 0;
#endif
	this->listener = listener;
	this->tag = tag;

	if (txt)
		strcpy (text, txt);
	else
		strcpy (text, "");
#if MAC
	// remember our VST plugin's resource map ID (it should be the current one at this moment)
	pluginResID = CurResFile();
#endif
#if QUARTZ
	textControl = 0;
#endif
	setWantsFocus (true);
}

//------------------------------------------------------------------------
CTextEdit::~CTextEdit ()
{}

//------------------------------------------------------------------------
void CTextEdit::setText (char *txt)
{
	if (txt)
	{
		if (strcmp (text, txt))
		{
			strcpy (text, txt);

			// to force the redraw
			setDirty ();
		}
	}
	else
	{
		if (strcmp (text, ""))
		{
			strcpy (text, "");

			// to force the redraw
			setDirty ();
		}
	}
}

//------------------------------------------------------------------------
void CTextEdit::getText (char *txt) const
{
	if (txt)
		strcpy (txt, text);
}

//------------------------------------------------------------------------
void CTextEdit::draw (CDrawContext *pContext)
{
	if (platformControl)
	{
		#if MACX
		#if QUARTZ
		if (textControl)
		{
			HIViewSetNeedsDisplay (textControl, true);
		}
		else
		#endif
		TXNDraw ((TXNObject)platformControl, NULL);
		#endif
		setDirty (false);
		return;
	}

	char string[256];
	string[0] = 0;

	if (editConvert2)
		editConvert2 (text, string, userData);
	else if (editConvert)
		editConvert (text, string);
	// Allow to display strings through the stringConvert
	// callbacks inherited from CParamDisplay
	else if (stringConvert2)
	{
		string[0] = 0;
		stringConvert2 (value, string, userData);
		strcpy(text, string);
	}
	else if (stringConvert)
	{
		string[0] = 0;
		stringConvert (value, string);
		strcpy(text, string);
	}
	else
		sprintf (string, "%s", text);

	drawText (pContext, string);
	setDirty (false);
}

//------------------------------------------------------------------------
void CTextEdit::mouse (CDrawContext *pContext, CPoint &where, long button)
{
	if (!bMouseEnabled)
		return;

	if (button == -1) button = pContext->getMouseButtons ();

	if (listener && button & (kAlt | kShift | kControl | kApple))
	{
		if (listener->controlModifierClicked (pContext, this, button) != 0)
			return;
	}

	if (button & kLButton)
	{
		if (getFrame ()->getFocusView () != this)
		{
			if (style & kDoubleClickStyle)
				if (!isDoubleClick ())
					return;
		
			beginEdit();
			takeFocus (pContext);
		}
	}
}

//------------------------------------------------------------------------
#if WINDOWS
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>

END_NAMESPACE_VSTGUI

#if PLUGGUI
	extern HINSTANCE ghInst;
	inline HINSTANCE GetInstance () { return ghInst; }
#else
	extern void* hInstance;
	inline HINSTANCE GetInstance () { return (HINSTANCE)hInstance; }
#endif

BEGIN_NAMESPACE_VSTGUI

extern long standardFontSize [];
extern const char *standardFontName [];

#ifdef STRICT
#define WINDOWSPROC WNDPROC
#else
#define WINDOWSPROC FARPROC
#endif

static WINDOWSPROC oldWndProcEdit;
LONG_PTR WINAPI WindowProcEdit (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
LONG_PTR WINAPI WindowProcEdit (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{	
	switch (message)
	{
		case WM_GETDLGCODE :
		{
			long flags = DLGC_WANTALLKEYS;
			return flags;
		}

		case WM_KEYDOWN:
		{
			if (wParam == VK_RETURN)
			{
				CTextEdit *textEdit = (CTextEdit*) GetWindowLongPtr (hwnd, GWLP_USERDATA);
				if (textEdit)
				{
					textEdit->bWasReturnPressed = true;
					textEdit->looseFocus ();
				}
			}
		} break;

		case WM_KILLFOCUS:
		{
			CTextEdit *textEdit = (CTextEdit*) GetWindowLongPtr (hwnd, GWLP_USERDATA);
			if (textEdit)
				textEdit->looseFocus ();
		} break;
	}

	return CallWindowProc (oldWndProcEdit, hwnd, message, wParam, lParam);
}

//------------------------------------------------------------------------
#elif MOTIF
#include <Xm/Text.h>
extern XFontStruct *gFontStructs[];
#endif

//------------------------------------------------------------------------
#if BEOS
#include <TextView.h>
#include <Window.h>

class BeTextView : public BTextView
{
public:
	BeTextView (CTextEdit* cTextEdit, BRect frame,
				const char *name, BRect textRect);
	void MakeFocus (bool focusState = true);
	void KeyDown (const char *bytes, int32 numBytes);
private:
	CTextEdit *cTextEdit;
};

//------------------------------------------------------------------------
BeTextView::BeTextView (CTextEdit* cTextEdit, BRect frame, const char *name, BRect textRect)
: BTextView (frame, name, textRect, B_FOLLOW_NONE), cTextEdit (cTextEdit)
{}

//------------------------------------------------------------------------
void BeTextView::MakeFocus (bool focusState)
{
	BTextView::MakeFocus (focusState);
	if (!focusState && cTextEdit)
		cTextEdit->looseFocus ();
}

//------------------------------------------------------------------------
void BeTextView::KeyDown (const char *bytes, int32 numBytes)
{
	if (cTextEdit && (bytes[0] == B_RETURN || bytes[0] == B_TAB))
		cTextEdit->looseFocus ();
	else
		BTextView::KeyDown (bytes, numBytes);
}
#endif

#if MACX
static EventHandlerRef gTextEditEventHandler = 0;
static bool gTextEditCanceled = false;
pascal OSStatus CarbonEventsTextControlProc (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData);
pascal OSStatus CarbonEventsTextControlProc (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData)
{
	OSStatus result = eventNotHandledErr;
	UInt32 eventClass = GetEventClass (inEvent);
	UInt32 eventKind = GetEventKind (inEvent);
	CTextEdit* textEdit = (CTextEdit*)inUserData;

	switch (eventClass)
	{
		case kEventClassKeyboard:
		{
			switch (eventKind)
			{
				case kEventRawKeyDown:
				case kEventRawKeyRepeat:
				{
					char macCharCode;
					UInt32 keyCode;
					UInt32 modifiers;
					GetEventParameter (inEvent, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof (char), NULL, &macCharCode);
					GetEventParameter (inEvent, kEventParamKeyCode, typeUInt32, NULL, sizeof (UInt32), NULL, &keyCode);
					GetEventParameter (inEvent, kEventParamKeyModifiers, typeUInt32, NULL, sizeof (UInt32), NULL, &modifiers);
					if (macCharCode == 13 || macCharCode == 3 || macCharCode == 27)
					{
						if (macCharCode == 27)
							gTextEditCanceled = true;
						else
							textEdit->bWasReturnPressed = true;

						WindowRef window = (WindowRef) (textEdit->getFrame ()->getSystemWindow());
						GrafPtr	savedPort;
						bool portChanged = window ? QDSwapPort (GetWindowPort (window), &savedPort) : false;

						// remember the current resource map ID
						short currentResID = CurResFile();
						short vstResFileID = textEdit->pluginResID;
						// if it's different (and if it's valid), set the current resource map ID to our plugin's resource map
						if ( (vstResFileID != currentResID) && (vstResFileID > 0) )
							UseResFile(vstResFileID);

						textEdit->looseFocus ();

						// revert the window port, if we changed it
						if (portChanged)
							QDSwapPort (savedPort, NULL);
						// revert the current resource map, if we changed it
						if ( (currentResID > 0) && (vstResFileID != currentResID) && (vstResFileID > 0) )
							UseResFile(currentResID);

						result = noErr;
					}
					#if QUARTZ
					else if (textEdit->textControl)
						break;
					#endif
					else if (modifiers & cmdKey)
					{
						result = noErr;
						TXNObject text_edit = (TXNObject) (textEdit->platformControl);
						switch (toupper(macCharCode))
						{
							// copy
							case 'C':
								if (!TXNIsSelectionEmpty(text_edit))
								{
									OSStatus scrapErr = ClearCurrentScrap();
									scrapErr = TXNCopy(text_edit);
									result = noErr;
								}
								break;
							// cut
							case 'X':
								if (!TXNIsSelectionEmpty(text_edit))
								{
									OSStatus scrapErr = ClearCurrentScrap();
									scrapErr = TXNCut(text_edit);
									result = noErr;
								}
								break;
							// paste
							case 'V':
								TXNPaste(text_edit);
								result = noErr;
								break;

							// select all
							case 'A':
								TXNSelectAll(text_edit);
								break;

							default:
								break;
						}
					}
					else
					{
						EventRecord eventRecord;
						if (ConvertEventRefToEventRecord (inEvent, &eventRecord))
							TXNKeyDown ((TXNObject)textEdit->platformControl, &eventRecord);
						result = noErr;
					}
					break;
				}
			}
			break;
		}
		case kEventClassMouse:
		{
			switch (eventKind)
			{
				case kEventMouseDown:
				case kEventMouseUp:
				{
					WindowRef window;
					GetEventParameter (inEvent, kEventParamWindowRef, typeWindowRef, NULL, sizeof (WindowRef), NULL, &window);
					HIPoint p;
					GetEventParameter (inEvent, kEventParamMouseLocation, typeHIPoint, NULL, sizeof (HIPoint), NULL, &p);
					Point point = {(short)p.y, (short)p.x};
					QDGlobalToLocalPoint (GetWindowPort (window), &point);
					Rect rect;
					TXNGetViewRect ((TXNObject)textEdit->platformControl, &rect);
					if (PtInRect (point, &rect))
					{
						EventRecord eventRecord;
						if (eventKind == kEventMouseDown && ConvertEventRefToEventRecord (inEvent, &eventRecord))
							TXNClick ((TXNObject)textEdit->platformControl, &eventRecord);
						result = noErr;
					}
					break;
				}
				case kEventMouseMoved:
				{
					TXNAdjustCursor ((TXNObject)textEdit->platformControl, NULL);
					break;
				}
			}
			break;
		}
		case kEventClassWindow:
		{
			WindowRef window;
			if (GetEventParameter (inEvent, kEventParamDirectObject, typeWindowRef, NULL, sizeof (WindowRef), NULL, &window) != noErr)
				break;
			switch (eventKind)
			{
				case kEventWindowDeactivated:
				{
					result = CallNextEventHandler (inHandlerCallRef, inEvent);
					ClearKeyboardFocus (window);

					// set up the correct drawing port for the window
					GrafPtr	savedPort;
					bool portChanged = QDSwapPort (GetWindowPort (window), &savedPort);

					// remember the current resource map ID
					short currentResID = CurResFile();
					short vstResFileID = textEdit->pluginResID;
					// if it's different (and if it's valid), set the current resource map ID to our plugin's resource map
					if ( (vstResFileID != currentResID) && (vstResFileID > 0) )
						UseResFile(vstResFileID);

					textEdit->looseFocus ();

					// revert the window port, if we changed it
					if (portChanged)
						QDSwapPort (savedPort, NULL);
					// revert the current resource map, if we changed it
					if ( (currentResID > 0) && (vstResFileID != currentResID) && (vstResFileID > 0) )
						UseResFile(currentResID);

					break;
				}
			}
			break;
		}
	}
	return result;
}
#endif

#if MAC && CALL_NOT_IN_CARBON
#include <Scrap.h>
#include <ctype.h>
#define ClearCurrentScrap	ZeroScrap
#endif

//------------------------------------------------------------------------
void CTextEdit::takeFocus (CDrawContext *pContext)
{
	bWasReturnPressed = false;

#if WINDOWS || MACX
	// calculate offset for CViewContainers
	CRect rect (size);
	CView* parent = getParentView ();
	CRect vSize;
	while (parent)
	{
		if (parent->notify (this, kMsgCheckIfViewContainer) == kMessageNotified)
		{
			parent->getViewSize (vSize);
			rect.offset (vSize.left, vSize.top);
		}
		parent = parent->getParentView ();
	}
	if (pContext)
		rect.offset (pContext->offset.h, pContext->offset.v);
#endif

#if WINDOWS
	int wstyle = 0;
	if (horiTxtAlign == kLeftText)
		wstyle |= ES_LEFT;
	else if (horiTxtAlign == kRightText)
		wstyle |= ES_RIGHT;
	else
		wstyle |= ES_CENTER;

	wstyle |= WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL;
	platformControl = (void*)CreateWindow (
		"EDIT", text, wstyle,
		rect.left, rect.top, rect.width ()/* + 1*/, rect.height ()/* + 1*/,
		(HWND)getFrame ()->getSystemWindow (), NULL, GetInstance (), 0);

	// get/set the current font
	LOGFONT logfont = {0};

	long fontH = gStandardFontSize [fontID];
	if (fontH > rect.height () - 2)
		fontH = rect.height () - 2;

	logfont.lfWeight = FW_NORMAL;
	logfont.lfHeight = -fontH;
	logfont.lfPitchAndFamily = VARIABLE_PITCH | FF_SWISS;
	strcpy (logfont.lfFaceName, gStandardFontName[fontID]);

	logfont.lfClipPrecision	 = CLIP_STROKE_PRECIS;
	logfont.lfOutPrecision	 = OUT_STRING_PRECIS;
	logfont.lfQuality 	     = DEFAULT_QUALITY;
	logfont.lfCharSet        = ANSI_CHARSET;
  
	platformFont = (HANDLE)CreateFontIndirect (&logfont);
	platformFontColor = 0;

	SetWindowLongPtr ((HWND)platformControl, GWLP_USERDATA, (LONG_PTR)this);
	SendMessage ((HWND)platformControl, WM_SETFONT, (WPARAM)platformFont, true);
	SendMessage ((HWND)platformControl, EM_SETMARGINS, EC_LEFTMARGIN|EC_RIGHTMARGIN, MAKELONG (0, 0));
	SendMessage ((HWND)platformControl, EM_SETSEL, 0, -1);
	SendMessage ((HWND)platformControl, EM_LIMITTEXT, 255, 0);
	SetFocus ((HWND)platformControl);

	oldWndProcEdit = (WINDOWSPROC)SetWindowLongPtr ((HWND)platformControl, GWLP_WNDPROC, (LONG_PTR)WindowProcEdit);

#elif MAC
#if MACX
	WindowRef window = (WindowRef)getFrame ()->getSystemWindow ();
	#if QUARTZ
	WindowAttributes winAttributes;
	GetWindowAttributes (window, &winAttributes);
	if (winAttributes & kWindowCompositingAttribute)
	{
		Rect r;
		r.left   = (short)rect.left;// + 2;
		r.right  = (short)rect.right;// - 4;
		r.top    = (short)rect.top;// + 2;
		r.bottom = (short)rect.bottom;// - 4;
		if (rect.getHeight () > gStandardFontSize [fontID])
		{
			r.top = (short)(rect.top + rect.getHeight () / 2 - gStandardFontSize [fontID] / 2 + 1);
			r.bottom = (short)(r.top + gStandardFontSize [fontID]);
		}
		if (CreateEditUnicodeTextControl (NULL, &r, NULL, false, NULL, &textControl) == noErr)
		{
			HIViewAddSubview ((HIViewRef)getFrame ()->getPlatformControl (), textControl);
			HIViewSetFirstSubViewFocus ((HIViewRef)getFrame ()->getPlatformControl (), textControl);
			HIViewAdvanceFocus ((HIViewRef)getFrame ()->getPlatformControl (), 0);
			EventTypeSpec eventTypes[] = { { kEventClassWindow, kEventWindowDeactivated }, { kEventClassKeyboard, kEventRawKeyDown }, { kEventClassKeyboard, kEventRawKeyRepeat } };
			InstallControlEventHandler (textControl, CarbonEventsTextControlProc, GetEventTypeCount (eventTypes), eventTypes, this, &gTextEditEventHandler);
			platformControl = textControl;
			if (strlen (text) > 0)
			{
				CFStringRef textString = CFStringCreateWithCString (NULL, text, kCFStringEncodingUTF8);
				if (textString)
				{
					SetControlData (textControl, kControlEditTextPart, kControlEditTextCFStringTag, sizeof (CFStringRef), &textString);
					CFRelease (textString);
				}
				ControlEditTextSelectionRec selection;
				selection.selStart = 0;
				selection.selEnd = strlen (text);
				SetControlData (textControl, kControlEditTextPart, kControlEditTextSelectionTag, sizeof (ControlEditTextSelectionRec), &selection);
			}
			Boolean singleLineStyle = true;
			SetControlData (textControl, kControlEditTextPart, kControlEditTextSingleLineTag, sizeof (Boolean), &singleLineStyle);
			ControlFontStyleRec fontStyle;
			memset (&fontStyle, 0, sizeof (fontStyle));
			fontStyle.flags = kControlUseJustMask | kControlUseSizeMask | kControlUseFontMask;
			switch (horiTxtAlign)
			{
				case kLeftText: fontStyle.just = teFlushLeft; break;
				case kRightText: fontStyle.just = teFlushRight; break;
				default: fontStyle.just = teCenter; break;
			}
			fontStyle.size = gStandardFontSize [fontID];
			extern const char* gMacXfontNames[];
			Str255 fontName;
			CopyCStringToPascal ((const char*)gMacXfontNames[fontID], fontName); 
			GetFNum (fontName, &fontStyle.font);
			SetControlData (textControl, kControlEditTextPart, kControlFontStyleTag, sizeof (fontStyle), &fontStyle);
			HIViewSetVisible (textControl, true);
		}
		return;
	}
	if (pContext)
		rect.offset (pContext->offsetScreen.h, pContext->offsetScreen.v);
	#endif
	static bool gTXNInititalized = false;
	if (!gTXNInititalized)
	{
		TXNMacOSPreferredFontDescription defaults;  // fontID, pointSize, encoding, and fontStyle
		defaults.fontID = 0;
		defaults.pointSize = kTXNDefaultFontSize;
		defaults.encoding = CreateTextEncoding(kTextEncodingMacRoman, kTextEncodingDefaultVariant, kTextEncodingDefaultFormat);
		defaults.fontStyle = kTXNDefaultFontStyle;
		TXNInitOptions options = 0;
		TXNInitTextension(&defaults, 1, options);
		gTXNInititalized = true;
	}
	gTextEditCanceled = false;
	TXNFrameOptions iFrameOptions = kTXNMonostyledTextMask | kTXNDisableDragAndDropMask | kTXNSingleLineOnlyMask; //kTXNNoKeyboardSyncMask | kTXNDisableDragAndDropMask | kTXNSingleLineOnlyMask | kTXNMonostyledTextMask;
	TXNFrameID frameID = 0;
	TXNObjectRefcon iRefCon = 0;
	TXNObject object;
	Rect r;
	r.left   = (short)rect.left;
	r.right  = (short)rect.right;
	r.top    = (short)rect.top;
	r.bottom = (short)rect.bottom;
	OSStatus err;
	err = TXNNewObject (NULL, window, &r, iFrameOptions, kTXNTextEditStyleFrameType, kTXNSingleStylePerTextDocumentResType, kTXNMacOSEncoding, &object, &frameID, iRefCon);
	if (err == noErr)
	{
		platformControl = object;
		TXNSetFrameBounds ((TXNObject)platformControl, r.top, r.left, r.bottom, r.right, frameID);

		if (strlen (text) > 0)
			TXNSetData ((TXNObject)platformControl, kTXNTextData, (void*)text, strlen (text), kTXNStartOffset, kTXNEndOffset);
		// set background
		extern void CColor2RGBColor (const CColor &cc, RGBColor &rgb);
		RGBColor rgbBackColor = { 0, 0, 0};
		CColor2RGBColor (kWhiteCColor /*backColor*/, rgbBackColor);
		RGBColor rgbTextColor = { 32767, 32767, 32767 };
		CColor2RGBColor (kBlackCColor /*fontColor*/, rgbTextColor);
		RGBBackColor (&rgbBackColor);
		RGBForeColor (&rgbTextColor);
		TXNBackground txnBackground;
		txnBackground.bgType = kTXNBackgroundTypeRGB;
		txnBackground.bg.color = rgbBackColor;
		TXNSetBackground ((TXNObject)platformControl, &txnBackground);
		// set justification
		TXNControlTag	controlTag[1];
		TXNControlData	controlData[1];
		SInt32			just;
		switch (horiTxtAlign)
		{
			case kLeftText :	just = kTXNFlushLeft; break;
			case kRightText :	just = kTXNFlushRight; break;
			default :			just = kTXNCenter; break;
		}
		controlTag[0] = kTXNJustificationTag;
		controlData[0].sValue = just;
		TXNSetTXNObjectControls ((TXNObject)platformControl, false, 1, controlTag, controlData);
		// set font
		TXNTypeAttributes attributes[3];
		// font name
		extern const unsigned char* gMacXfontNames[];
		
		short familyID;
		#if QUARTZ
		Str255 fontName;
		CopyCStringToPascal ((const char*)gMacXfontNames[fontID], fontName); 
		GetFNum (fontName, &familyID);
		#else
		GetFNum (gMacXfontNames[fontID], &familyID);
		#endif

		ATSUFontID fontNameID;

		ATSUFONDtoFontID (familyID, 0, &fontNameID);
		
		attributes[0].tag = kATSUFontTag;
		attributes[0].size = sizeof(ATSUFontID);
		attributes[0].data.dataPtr = &fontNameID;
		// size
		long fontSize = gStandardFontSize [fontID] << 16; // needs to be in Fixed format
		attributes[1].tag = kTXNQDFontSizeAttribute;
		attributes[1].size = kTXNFontSizeAttributeSize;
		attributes[1].data.dataValue = fontSize;
		// color
		attributes[2].tag = kTXNQDFontColorAttribute;
		attributes[2].size = kTXNQDFontColorAttributeSize;
		attributes[2].data.dataPtr = &rgbTextColor;

		TXNSetTypeAttributes ((TXNObject)platformControl, 3, attributes, kTXNStartOffset, kTXNEndOffset);

		SetUserFocusWindow (window);
		AdvanceKeyboardFocus (window);
		TXNActivate ((TXNObject)platformControl, frameID, false);
		TXNFocus ((TXNObject)platformControl, true);
		EventTypeSpec eventTypes[] = { { kEventClassMouse, kEventMouseMoved }, { kEventClassMouse, kEventMouseDown }, { kEventClassMouse, kEventMouseUp }, { kEventClassWindow, kEventWindowDeactivated }, { kEventClassKeyboard, kEventRawKeyDown }, { kEventClassKeyboard, kEventRawKeyRepeat } };
		InstallWindowEventHandler (window, CarbonEventsTextControlProc, GetEventTypeCount (eventTypes), eventTypes, this, &gTextEditEventHandler);
		TXNSelectAll ((TXNObject)platformControl);

	}

#else
	bool ende = false;
	char c;
	EventRecord theEvent;
	Rect rect, vrect;

	platformControl = new char[256];
	
	rect.left   = size.left;
	rect.right  = size.right;
	rect.top    = size.top;
	rect.bottom = size.bottom;
	#if !TARGET_API_MAC_CARBON
	rect.bottom++;
	rect.right++;
	#endif
		
	if (pContext)
	{
		rect.left   += pContext->offset.h;
		rect.right  += pContext->offset.h;
		rect.top    += pContext->offset.v;
		rect.bottom += pContext->offset.v;
	}
	vrect = rect;
	
	vrect.top++;
	vrect.left++;
	vrect.right--;
	#if TARGET_API_MAC_CARBON
	vrect.bottom--;
	#endif
			
	RGBColor blackrgb = {0, 0, 0};
	RGBColor whitergb = {0xffff, 0xffff, 0xffff};
	RGBForeColor (&blackrgb);
	RGBBackColor (&whitergb);

	EraseRect (&rect);
	//FrameRect (&rect); // Dave
	#if !TARGET_API_MAC_CARBON
	InsetRect (&vrect, 0, -2);
	#endif
	
	TextFont (kFontIDHelvetica);
	TextSize (gStandardFontSize [fontID]);

	text_edit = TENew (&rect, &vrect);
	
	if (horiTxtAlign == kLeftText)
		TESetAlignment (teJustLeft, (TEHandle)text_edit);
	else if (horiTxtAlign == kRightText)
		TESetAlignment (teJustRight, (TEHandle)text_edit);
	else
		TESetAlignment (teJustCenter, (TEHandle)text_edit);

	char string[256];
	strcpy (string, text);
	TESetText (string, strlen (string), (TEHandle)text_edit);
	TESetSelect (0, strlen (string), (TEHandle)text_edit);
	TEUpdate (&(**(TEHandle)text_edit).viewRect, (TEHandle)text_edit);
	TEActivate ((TEHandle)text_edit);
	HLock ((Handle)text_edit);

	(**(TEHandle)text_edit).crOnly = -1;

	bLoosefocusWanted = false;

	while (!ende && !bLoosefocusWanted)
	{
		GetNextEvent (everyEvent, &theEvent);
		switch (theEvent.what)
		{
			case nullEvent:
				doIdleStuff ();
				break;
			case autoKey :
			case keyDown :
				RGBForeColor (&blackrgb);
				RGBBackColor (&whitergb);
				
				c = theEvent.message & charCodeMask;
				if (c == 13 || c == 3 || c == 27)
				{
					if (c == 13)
						bWasReturnPressed = true;
					ende = true;
				}
				else if (((theEvent.message >> 8) & 0xFF) == 0x75)
				{
			 		if ((**(TEHandle)text_edit).selEnd < (**(TEHandle)text_edit).teLength) 
					{
				 		if (((**(TEHandle)text_edit).selEnd - (**(TEHandle)text_edit).selStart) > 1)
				 			TEDelete ((TEHandle)text_edit);
				 		else
				 			TEKey (0x1D, (TEHandle)text_edit);
						c = 8;
					}
					else
						c = -1;
				}
				if (theEvent.modifiers & cmdKey)
				{
					switch (toupper(c))
					{
						// copy
						case 'C':
							if ((**(TEHandle)text_edit).selEnd > (**(TEHandle)text_edit).selStart)
							{
								OSStatus scrapErr = ClearCurrentScrap();
								TECopy((TEHandle)text_edit);
								if (scrapErr == noErr)
									scrapErr = TEToScrap();
							}
							break;
						// cut
						case 'X':
							if ((**(TEHandle)text_edit).selEnd > (**(TEHandle)text_edit).selStart)
							{
								OSStatus scrapErr = ClearCurrentScrap();
								TECut((TEHandle)text_edit);
								if (scrapErr == noErr)
									scrapErr = TEToScrap();
							}
							break;
						// paste
						case 'V':
							{
								OSErr scrapErr = TEFromScrap();
								TEPaste((TEHandle)text_edit);
							}
							break;
						// select all
						case 'A':
							TESetSelect(0, (**(TEHandle)text_edit).teLength, (TEHandle)text_edit);
							break;
						default:
							break;
					}
				}
				else if (!ende)
					TEKey (c, (TEHandle)text_edit);
				break;
			case mouseDown :
				GlobalToLocal (&theEvent.where);
				if (PtInRect (theEvent.where, &rect))
				{
					bool shiftdown = (theEvent.modifiers & shiftKey) != 0;
					TEClick (theEvent.where, shiftdown, (TEHandle)text_edit);
				}
				else
					ende = true;
				break;
		}
	}
	CharsHandle h = TEGetText ((TEHandle)text_edit);
	short length = (**(TEHandle)text_edit).teLength;
	if (length > 255)
		length = 255;
	strncpy ((char*)platformControl, (char*)*h, length);
	((char*)platformControl)[length] = 0;

	HUnlock ((Handle)text_edit);
	TEDeactivate ((TEHandle)text_edit);
	TEDispose ((TEHandle)text_edit);
	text_edit = 0;
	looseFocus ();
	
#endif
#elif MOTIF
	// we have to add the Text to the parent !!
	Dimension posX, posY;
	Widget widget = (Widget)(getFrame ()->getSystemWindow ());
	XtVaGetValues (widget, XmNx, &posX, XmNy, &posY, 0);
  
	Arg args[20];
	int n = 0;
	XtSetArg (args[n], XmNx, size.left + posX); n++;
	XtSetArg (args[n], XmNy, size.top + posY); n++;
	XtSetArg (args[n], XmNwidth, size.width () + 1); n++;
	XtSetArg (args[n], XmNheight, size.height () + 2); n++;
	
	XtSetArg (args[n], XmNvalue, text); n++;
	
	XtSetArg (args[n], XmNshadowType, XmSHADOW_IN); n++;
	XtSetArg (args[n], XmNshadowThickness, 0); n++;
	XtSetArg (args[n], XmNcursorPositionVisible, true); n++;

	XtSetArg (args[n], XmNmarginWidth, 0); n++;
	XtSetArg (args[n], XmNmarginHeight, 0); n++;
	XtSetArg (args[n], XmNresizeHeight, True); n++;
	XtSetArg (args[n], XmNborderWidth, 0); n++;
	XtSetArg (args[n], XmNeditMode, XmSINGLE_LINE_EDIT); n++;
	
	// get/set the current font
	XmFontList fl = 0;
	XFontStruct* fs = fontStructs[fontID];
	if (fs)
	{
		XmFontListEntry entry = XmFontListEntryCreate (XmFONTLIST_DEFAULT_TAG, XmFONT_IS_FONT, fs);
		XmFontList fl = XmFontListAppendEntry (0, entry);
		XtSetArg (args[n], XmNfontList, fl); n++;
	}

	platformControl = XmCreateText (XtParent (widget), "Text", args, n);
	XtManageChild ((Widget)platformControl);
	if (fl)
		XmFontListFree (fl);
	XmTextSetSelection ((Widget)platformControl, 0, strlen (text), 0);
	XmTextSetHighlight ((Widget)platformControl, 0, strlen (text), XmHIGHLIGHT_SELECTED);

#elif BEOS
	BView* plugView = (BView*) getFrame ()->getSystemWindow ();
	CRect rect;
	getFrame ()->getSize (&rect);
	BRect r (rect.left + size.left, rect.top + size.top, rect.left + size.right, rect.top + size.bottom);
	BRect tr = r;
	tr.OffsetTo (B_ORIGIN);
	tr.InsetBy (1, 1);
	BeTextView* textView = new BeTextView (this, r, NULL, tr);
	plugView->Window ()->Activate (true);
	plugView->AddChild (textView);
	BFont	font;
	if (fontID < 0 || fontID >= kNumStandardFonts)
		fontID = kSystemFont;
	extern const char* gStandardFontStyle[];
	font.SetFamilyAndStyle (gStandardFontName[fontID], gStandardFontStyle[fontID]);
	font.SetSize (gStandardFontSize[fontID]);
	rgb_color c = { fontColor.red, fontColor.green, fontColor.blue, 255 };
	textView->SetFontAndColor (&font, B_FONT_FAMILY_AND_STYLE | B_FONT_SIZE, &c);
	rgb_color cv = { backColor.red, backColor.green, backColor.blue, 255 };
	textView->SetMaxBytes (255);
	textView->SetViewColor (cv);
	alignment a;
	if (horiTxtAlign == kRightText)
		a = B_ALIGN_RIGHT;
	else if (horiTxtAlign == kLeftText)
		a = B_ALIGN_LEFT;
	else
		a = B_ALIGN_CENTER;
	textView->SetAlignment (a);
	textView->SetText (text);
	textView->DisallowChar ('\n');
	textView->DisallowChar ('\t');
	textView->MakeResizable (true);
	textView->MakeFocus (true);
	textView->SelectAll ();
	platformControl = textView;
#endif
}

//------------------------------------------------------------------------
void CTextEdit::looseFocus (CDrawContext *pContext)
{
	// Call this yet to avoid recursive call
	endEdit();
	if (getFrame ()->getFocusView () == this)
		getFrame ()->setFocusView (0);

	if (platformControl == 0)
		return;

	char oldText[256];
	strcpy (oldText, text);
	
#if WINDOWS
	char newText[255];
	GetWindowText ((HWND)platformControl, newText, 255);
	strcpy (text, newText);

	DestroyWindow ((HWND)platformControl);
	if (platformFont)
	{
		DeleteObject ((HGDIOBJ)platformFont);
		platformFont = 0;
	}
	if (platformFontColor)
	{
		DeleteObject (platformFontColor);
		platformFontColor = 0;
	}

#elif MAC

	#if MACX
	if (platformControl == 0)
		return;

	if (gTextEditEventHandler)
		RemoveEventHandler (gTextEditEventHandler);
	gTextEditEventHandler = 0;
	#if QUARTZ
	if (textControl)
	{
		CFStringRef cfstr;
		if (!gTextEditCanceled && GetControlData (textControl, kControlEditTextPart, kControlEditTextCFStringTag, sizeof cfstr, (void*)&cfstr, NULL) == noErr)
		{
			CFStringGetCString (cfstr, text, 255, kCFStringEncodingUTF8);
			CFRelease (cfstr);
		}
		HIViewSetVisible (textControl, false);
		HIViewRemoveFromSuperview (textControl);
		textControl = 0;
		pParentFrame->setCursor (kCursorDefault);
	}
	else
	#endif
	{
		if (!gTextEditCanceled)
		{
			CharsHandle dataHandle;
			TXNGetDataEncoded ((TXNObject)platformControl, kTXNStartOffset, kTXNEndOffset, &dataHandle, kTXNTextData);
			if (dataHandle != NULL && GetHandleSize (dataHandle) > 0)
			{
				long s = GetHandleSize (dataHandle);
				strncpy (text, *dataHandle, (s > 255) ? 255 : s);
				text [(s > 255) ? 255 : s] = 0;
				DisposeHandle (dataHandle);
			}
			else
				text[0] = 0;
		}
		TXNFocus ((TXNObject)platformControl, false);
		TXNDeleteObject ((TXNObject)platformControl);
	}
	
	platformControl = 0;

	setDirty (true);

	#if 0//QUARTZ
	CRect fr (size);
	CPoint offset;
	localToFrame (offset);
	fr.offset (offset.x, offset.y);
	
	RgnHandle rgn = NewRgn ();
	MacSetRectRgn (rgn, fr.left, fr.top, fr.right, fr.bottom);
	HIViewSetNeedsDisplayInRegion ((HIViewRef)getFrame ()->getPlatformControl () , rgn, true);
	DisposeRgn (rgn);
	#endif
//	doIdleStuff ();
	
	#else

	strcpy (text, (char*)platformControl);
	delete[] platformControl;
	#endif
#elif MOTIF
	char *pNewText = XmTextGetString ((Widget)platformControl);
	strcpy (text, pNewText);
	XtFree (pNewText);
	
	XtUnmanageChild ((Widget)platformControl);
	XtDestroyWidget ((Widget)platformControl);

#elif BEOS
	BTextView* textView = (BTextView*) platformControl;
	strncpy (text, textView->Text (), 255);
	if (textView->LockLooper ())
	{
		BWindow* w = textView->Window ();
		textView->RemoveSelf ();
		w->Unlock ();
	}
	delete textView;

#endif
	CPoint origOffset;
	bool resetContextOffset = false;
	if (!pContext)
	{
		// create a local context
		pContext = getFrame ()->createDrawContext ();
		if (getParentView ())
		{
			resetContextOffset = true;
			origOffset.x = pContext->offset.x;
			origOffset.y = pContext->offset.y;
			CView *view= getParentView ();
			CRect rect2;
			view->getViewSize (rect2);
			CPoint offset;
			view->localToFrame (offset);
			rect2.offset (offset.x, offset.y);
			pContext->offset.h = rect2.left;
			pContext->offset.v = rect2.top;
		}
	}
	else
		pContext->remember ();

	// update dependency
	bool change = false;
	if (strcmp (oldText, text))
	{
		change = true;
		if (listener)
			listener->valueChanged (pContext, this);
	}

	platformControl = 0;
	if (resetContextOffset)
	{
		pContext->offset.x = origOffset.x;
		pContext->offset.y = origOffset.y;
	}
	pContext->forget ();

	if (change)
		doIdleStuff ();

	CView* receiver = pParentView ? pParentView : pParentFrame;
	if (receiver)
		receiver->notify (this, "LooseFocus");
}

//------------------------------------------------------------------------
void CTextEdit::setTextEditConvert (void (*convert) (char *input, char *string))
{
	editConvert = convert;
}

//------------------------------------------------------------------------
void CTextEdit::setTextEditConvert (void (*convert) (char *input, char *string,
									  void *userDta), void *userData)
{
	editConvert2 = convert;
	this->userData = userData;
}

//------------------------------------------------------------------------
// COptionMenuScheme
//------------------------------------------------------------------------
/*! @class COptionMenuScheme
Used to define the appearance (font color, background color...) of a popup-menu.
To define the scheme of a menu, use the appropriate setScheme method (see COptionMenu).
@section coptionmenuscheme_new_in_3_0 New since 3.0
You can also use the global variable gOptionMenuScheme to use one scheme on all menus.
@section coptionmenuscheme_note Note
If you want to use it on Mac OS X, you must set the macro MAC_ENABLE_MENU_SCHEME (needs Mac OS X 10.3 or higher)
*/
COptionMenuScheme* gOptionMenuScheme = 0;

//------------------------------------------------------------------------
COptionMenuScheme::COptionMenuScheme ()
{
#if WINDOWS
	COLORREF c = GetSysColor (COLOR_MENU);
	backgroundColor (GetRValue (c), GetGValue (c), GetBValue (c), 0);
	c = GetSysColor (COLOR_HIGHLIGHT);
	selectionColor (GetRValue (c), GetGValue (c), GetBValue (c), 0);
	c = GetSysColor (COLOR_MENUTEXT);
	textColor (GetRValue (c), GetGValue (c), GetBValue (c), 0);
	c = GetSysColor (COLOR_HIGHLIGHTTEXT);
	hiliteTextColor (GetRValue (c), GetGValue (c), GetBValue (c), 0);
	c = GetSysColor (COLOR_GRAYTEXT);
	disableTextColor (GetRValue (c), GetGValue (c), GetBValue (c), 0);
#else
	backgroundColor = kGreyCColor;
	selectionColor = kBlueCColor;
	textColor = kBlackCColor;
	hiliteTextColor = kWhiteCColor;
	disableTextColor = kWhiteCColor;
#endif
	font = kNormalFontSmall;
	#if MAC_ENABLE_MENU_SCHEME
	registerWithToolbox ();
	#endif
}

//------------------------------------------------------------------------
COptionMenuScheme::~COptionMenuScheme ()
{
	#if MAC_ENABLE_MENU_SCHEME
	unregisterWithToolbox ();
	#endif
}

//------------------------------------------------------------------------
void COptionMenuScheme::getItemSize (const char* text, CDrawContext* pContext, CPoint& size)
{
	if (!strcmp (text, kMenuSeparator)) // separator
	{
		#if MAC
		size.h = 6;
		size.v = 9;
		#else
		// was: size.h = size.v = 6;
		size.h = 6;
		size.v = 18;
		// separators must have same height, otherwise we have problems
		// in multi-column menus :(
		#endif
	}
	else
	{
		pContext->setFont (font);
		size.h = pContext->getStringWidth (text) + 18;
		size.v = 18;
		#if MAC
		size.h += 18;
		#endif
	}
}

//------------------------------------------------------------------------
void COptionMenuScheme::drawItemBack (CDrawContext* pContext, const CRect& rect, bool hilite)
{
	if (hilite)
		pContext->setFillColor (selectionColor);
	else
		pContext->setFillColor (backgroundColor);
	pContext->fillRect (rect);
}

//------------------------------------------------------------------------
void COptionMenuScheme::drawItem (const char* text, long itemId, long state, CDrawContext* pContext, const CRect& rect)
{	
	bool hilite = (state & kSelected) != 0;
	
	drawItemBack (pContext, rect, hilite);

	if (!strcmp (text, kMenuSeparator))
	{
		CCoord y = rect.top + rect.height () / 2;

		const CColor bc = { 0, 0, 0, 150};
		const CColor wc = { 255, 255, 255, 150};
		
		pContext->setFrameColor (bc);
		pContext->moveTo (CPoint (rect.left + 2, y - 1));
		pContext->lineTo (CPoint (rect.right - 2, y - 1));
		pContext->setFrameColor (wc);
		pContext->moveTo (CPoint (rect.left + 2, y));
		pContext->lineTo (CPoint (rect.right - 2, y));
		return;
	}

	CRect r;
	if (state & kChecked)
	{		
		r (6, 4, 14, 12);
		r.offset (rect.left, rect.top);
		if (hilite)
			pContext->setFillColor (hiliteTextColor);
		else
			pContext->setFillColor (textColor);
		pContext->fillEllipse (r);
	}

	r = rect;
	r.left += 18;
	pContext->setFont (font);
	if (state & kDisabled)
		pContext->setFontColor (disableTextColor);
	else
	{
		if (hilite)
			pContext->setFontColor (hiliteTextColor);
		else
			pContext->setFontColor (textColor);
	}

	// this needs to be done right, without changing the text pointer in anyway ;-)
	char *ptr = (char*)strstr (text, "\t");
	if (ptr)
	{
		char modifier[32];
		strcpy (modifier, ptr + 1);
		*ptr = 0;
		pContext->drawString (text, r, false, kLeftText);

		*ptr = '\t';
		r.left = r.right - 50;
		pContext->drawString (modifier, r, false, kLeftText);
	}
	else
		pContext->drawString (text, r, false, kLeftText);
}

#if MAC_ENABLE_MENU_SCHEME
#define kHIMenuViewClassID              CFSTR("com.apple.HIMenuView")

struct HIMenuScheme
{
	HIViewRef hiView;
	COptionMenuScheme* scheme;
	COptionMenu* menu;
	COffscreenContext* offscreenContext;
	float maxWidth;
};

const EventParamName kEventParamCOptionMenuScheme = 'COMS';
const EventParamName kEventParamCOptionMenu = 'COM ';

#define kItemHeight		18

static CFStringRef gOptionMenuSchemeClassID = 0;
static HIObjectClassRef gMenuClassRef = 0;
static CFIndex gOptionMenuSchemeClassIDRefCount = 0;

//------------------------------------------------------------------------
void COptionMenuScheme::registerWithToolbox ()
{
	if (gOptionMenuSchemeClassID == 0)
	{
		gOptionMenuSchemeClassID = CFStringCreateWithFormat (NULL, NULL, CFSTR("net.sourceforge.vstgui.COptionMenuSchemeClassID.%d"), this);

		static const EventTypeSpec events[] =
		{
			{ kEventClassHIObject, kEventHIObjectConstruct } ,
			{ kEventClassHIObject, kEventHIObjectInitialize },
			{ kEventClassHIObject, kEventHIObjectDestruct },

			{ kEventClassControl, kEventControlHitTest },
			{ kEventClassControl, kEventControlGetPartRegion },
			{ kEventClassControl, kEventControlDraw },
			{ kEventClassControl, kEventControlGetOptimalBounds },

			{ kEventClassMenu, kEventMenuCreateFrameView },

			{ kEventClassScrollable, kEventScrollableGetInfo },
		};

		HIObjectRegisterSubclass (	gOptionMenuSchemeClassID,
									kHIMenuViewClassID,
									kNilOptions,
									COptionMenuScheme::eventHandler,
									GetEventTypeCount (events),
									events,
									NULL,
									&gMenuClassRef);
		gOptionMenuSchemeClassIDRefCount = CFGetRetainCount (gOptionMenuSchemeClassID);
	}
	else
		CFRetain (gOptionMenuSchemeClassID);
}

//------------------------------------------------------------------------
void COptionMenuScheme::unregisterWithToolbox ()
{
	if (gOptionMenuSchemeClassID)
	{
		if (CFGetRetainCount (gOptionMenuSchemeClassID) == gOptionMenuSchemeClassIDRefCount)
		{
			HIObjectUnregisterClass (gMenuClassRef);
			gMenuClassRef = 0;
		}
		CFRelease (gOptionMenuSchemeClassID);
	}
}

//------------------------------------------------------------------------
pascal OSStatus COptionMenuScheme::eventHandler (EventHandlerCallRef inCallRef, EventRef inEvent, void *inUserData)
{
	OSStatus err = eventNotHandledErr;
	UInt32 eventClass = GetEventClass (inEvent);
	UInt32 eventKind = GetEventKind (inEvent);
	HIMenuScheme* scheme = (HIMenuScheme*)inUserData;
	
	switch (eventClass)
	{
		case kEventClassHIObject:
		{
			switch (eventKind)
			{
				case kEventHIObjectConstruct:
				{
					scheme = (HIMenuScheme*)calloc (1, sizeof (HIMenuScheme));
					GetEventParameter (inEvent, kEventParamHIObjectInstance, typeHIObjectRef, NULL, sizeof (HIObjectRef), NULL, &scheme->hiView);
					SetEventParameter (inEvent, kEventParamHIObjectInstance, typeVoidPtr, sizeof (HIMenuScheme*), &scheme);
					scheme->maxWidth = 100;
					err = noErr;
					break;
				}
				case kEventHIObjectInitialize:
				{
					err = CallNextEventHandler (inCallRef, inEvent);
					if (err == noErr)
					{
						GetEventParameter (inEvent, kEventParamCOptionMenuScheme, typeVoidPtr, NULL, sizeof (COptionMenuScheme*), NULL, &scheme->scheme);
						GetEventParameter (inEvent, kEventParamCOptionMenu, typeVoidPtr, NULL, sizeof (COptionMenu*), NULL, &scheme->menu);
						scheme->scheme->remember ();
						scheme->menu->remember ();
						scheme->offscreenContext = new COffscreenContext (scheme->menu->getFrame (), 600, 100);
					}
					break;
				}
				case kEventHIObjectDestruct:
				{
					delete scheme->offscreenContext;
					scheme->scheme->forget ();
					scheme->menu->forget ();
					free (scheme);
					err = noErr;
					break;
				}
			}
			break;
		}
		case kEventClassControl:
		{
			HIViewRef control;
			GetEventParameter (inEvent, kEventParamDirectObject, typeControlRef, NULL, sizeof (HIViewRef), NULL, &control);
			switch (eventKind)
			{
				case kEventControlInitialize:
				{
					err = CallNextEventHandler (inCallRef, inEvent);
					break;
				}
				case kEventControlHitTest:
				{
					HIPoint mouseLoc;
					GetEventParameter (inEvent, kEventParamMouseLocation, typeHIPoint, NULL, sizeof (mouseLoc), NULL, &mouseLoc);
					ControlPartCode partHit = mouseLoc.y / kItemHeight + 1;
					char temp[1024];
					CPoint size;
					long yPos = 0;
					for (long i = 0; i < scheme->menu->getNbEntries (); i++)
					{
						scheme->menu->getEntry (i, temp);
						scheme->scheme->getItemSize (temp, scheme->offscreenContext, size);
						yPos += size.y;
						if (yPos >= mouseLoc.y)
						{
							partHit = i + 1;
							break;
						}
					}
					SetEventParameter (inEvent, kEventParamControlPart, typeControlPartCode, sizeof (partHit), &partHit);
					err = noErr;
					break;
				}
				case kEventControlGetOptimalBounds:
				{
					HIRect r = { {0, 0}, { 0, 0 }};
					r.size.width = scheme->maxWidth;
					char temp[1024];
					CPoint size;
					for (long i = 0; i < scheme->menu->getNbEntries (); i++)
					{
						scheme->menu->getEntry (i, temp);
						scheme->scheme->getItemSize (temp, scheme->offscreenContext, size);
						if (!strncmp (temp, kMenuSubMenu, 2))
							size.x += 16;
						r.size.height += size.y;
						if (r.size.width < size.x)
							r.size.width = size.x;
					}
					//r.size.height += scheme->menu->getNbEntries ();
					scheme->maxWidth = r.size.width;
					SetEventParameter (inEvent, kEventParamControlOptimalBounds, typeHIRect, sizeof (HIRect), &r);
					err = noErr;
					break;
				}
				case kEventControlGetPartRegion:
				{
					HIRect r = { {0, 0}, { 0, 0 }};
					ControlPartCode whichItem;
					RgnHandle outRegion = NULL;
					GetEventParameter (inEvent, kEventParamControlPart, typeControlPartCode, NULL, sizeof (whichItem), NULL, &whichItem);
					GetEventParameter (inEvent, kEventParamControlRegion, typeQDRgnHandle, NULL, sizeof(outRegion), NULL, &outRegion);
					if (whichItem <= 0)
					{
						r.size.width = scheme->maxWidth;
						char temp[1024];
						CPoint size;
						for (long i = 0; i < scheme->menu->getNbEntries (); i++)
						{
							scheme->menu->getEntry (i, temp);
							scheme->scheme->getItemSize (temp, scheme->offscreenContext, size);
							if (!strncmp (temp, kMenuSubMenu, 2))
								size.x += 16;
							r.size.height += size.y;
							if (r.size.width < size.x)
								r.size.width = size.x;
						}
						scheme->maxWidth = r.size.width;
						r.size.height += scheme->menu->getNbEntries ();
					}
					else
					{
						char temp[1024];
						CPoint size;
						for (long i = 0; i < whichItem; i++)
						{
							r.origin.y += size.y;
							scheme->menu->getEntry (i, temp);
							scheme->scheme->getItemSize (temp, scheme->offscreenContext, size);
							r.size.height += size.y;
						}
						r.size.width = scheme->maxWidth;
					}
					SetRectRgn (outRegion, (short) r.origin.x, (short) r.origin.y,(short) r.origin.x + r.size.width, (short) r.origin.y+ r.size.height + 1);
					err = noErr;
					break;
				}
				case kEventControlDraw:
				{
					CGContextRef cgContext;
					GetEventParameter (inEvent, kEventParamCGContextRef, typeCGContextRef, NULL, sizeof (cgContext), NULL, &cgContext);
					HIRect r;
					HIViewGetBounds (control, &r);
					CGContextClearRect (cgContext, r);
					if (control != scheme->hiView)
					{
						err = noErr;
						break;
					}
					ControlPartCode focusPart;
					HIViewGetFocusPart (scheme->hiView, &focusPart);
					focusPart--;
					WindowRef window = HIViewGetWindow (scheme->hiView);
					CDrawContext context (NULL, cgContext, window);
					char entryText[1024];
					CPoint size;
					CRect rect (0, 0);
					rect.setHeight (kItemHeight);
					rect.setWidth (scheme->maxWidth);
					for (int i = 0; i < scheme->menu->getNbEntries (); i++)
					{
						if (scheme->menu->getEntry (i, entryText))
						{
							scheme->scheme->getItemSize (entryText, &context, size);
							long state = scheme->menu->isCheckEntry (i) ? kChecked : 0;
							if (focusPart >= 0 && focusPart == i)
								state |= kSelected;
							long offset = 0;
							if (!strncmp (entryText, kMenuSubMenu, 2))
							{
								state |= kSubMenu;
								offset = 2;
							}
							else if (!strncmp (entryText, kMenuTitle, 2))
							{
								state |= kTitle;
								offset = 2;
							}
							else if (!strncmp (entryText, kMenuDisable, 2))
							{
								state |= kDisabled;
								offset = 2;
							}
							rect.setHeight (size.y+1);
							context.setClipRect (rect);
							scheme->scheme->drawItem (entryText+offset, i, state, &context, rect);
							rect.offset (0, size.y);
						}
					}
					break;
				}
				case kEventControlGetFrameMetrics:
				{
					err = CallNextEventHandler (inCallRef, inEvent);
					HIViewFrameMetrics	metrics;
					GetEventParameter (inEvent, kEventParamControlFrameMetrics, typeControlFrameMetrics, NULL, sizeof (metrics), NULL, &metrics);
					metrics.top = metrics.bottom = 0;
					SetEventParameter (inEvent, kEventParamControlFrameMetrics, typeControlFrameMetrics, sizeof (metrics), &metrics);
					break;
				}
				case kEventControlOwningWindowChanged:
				{
					WindowRef newWindow = GetControlOwner (control);
					HIWindowChangeFeatures (newWindow, 0, kWindowIsOpaque);
					err = noErr;
					HIViewRef root = HIViewGetRoot (newWindow);
					if (root)
					{
						HIRect bounds, frame;
						HIViewGetBounds (root, &bounds);
						HIViewGetFrame (root, &frame);
					}
					break;
				}
			}
			break;
		}
		case kEventClassMenu:
		{
			switch (eventKind)
			{
				case kEventMenuCreateFrameView:
				{
					err = CallNextEventHandler (inCallRef, inEvent);
					HIViewRef frameView;
					GetEventParameter (inEvent, kEventParamMenuFrameView, typeControlRef, NULL, sizeof (ControlRef), NULL, &frameView);
					HIViewFindByID (frameView, kHIViewWindowContentID, &frameView);
					if (frameView)
					{
						EventTypeSpec events [] = { { kEventClassControl, kEventControlDraw }, { kEventClassControl, kEventControlOwningWindowChanged }, { kEventClassControl, kEventControlGetFrameMetrics } };
						InstallControlEventHandler (frameView, COptionMenuScheme::eventHandler, GetEventTypeCount (events), events, scheme, NULL);
					}
					break;
				}
			}
			break;
		}
		case kEventClassScrollable:
		{
			switch (eventKind)
			{
				case kEventScrollableGetInfo:
				{
					HISize size;
					HIPoint origin = { 0, 0 };

					size.width = 200;
					size.height = kItemHeight * (scheme->menu->getNbEntries () + 1);;

					SetEventParameter(inEvent, kEventParamImageSize, typeHISize, sizeof( size ), &size );
					SetEventParameter(inEvent, kEventParamViewSize, typeHISize, sizeof( size ), &size );
					SetEventParameter(inEvent, kEventParamOrigin, typeHIPoint, sizeof( origin ), &origin );

					// line size is 1/10th total size
					size.width /= 10;
					size.height /= 10;

					SetEventParameter(inEvent, kEventParamLineSize, typeHISize, sizeof( size ), &size );

					err = noErr;
					break;
				}
			}
			break;
		}
	}
	
	return err;
}
#endif

//------------------------------------------------------------------------
// COptionMenu
//------------------------------------------------------------------------
/*! @class COptionMenu
Define a rectangle view where a text-value can be displayed with a given font and color.
The text-value is centered in the given rect.
A pixmap can be used as background, a second pixmap can be used when the option menu is popuped.
There are 2 styles with or without a shadowed text. When a mouse click occurs, a popup menu is displayed.
*/
COptionMenu::COptionMenu (const CRect &size, CControlListener *listener, long tag,
                          CBitmap *background, CBitmap *bgWhenClick, const long style)
:	CParamDisplay (size, background, style), bgWhenClick (bgWhenClick), nbItemsPerColumn (-1),
	prefixNumbers (0), scheme (0)
{
	this->listener = listener;
	this->tag = tag;

	nbEntries = 0;
	nbSubMenus = 0;
	currentIndex = -1;
	lastButton = kRButton;
	platformControl = 0;
	lastResult = -1;
	lastMenu = 0;

	#if MAC
	menuID = 0;
	#endif
	
	if (bgWhenClick)
		bgWhenClick->remember ();

	nbSubMenuAllocated = nbAllocated = 0;
	
	check = 0;
	entry = 0;
	submenuEntry = 0;
}

//------------------------------------------------------------------------
COptionMenu::~COptionMenu ()
{
	removeAllEntry ();

	if (bgWhenClick)
		bgWhenClick->forget ();
}

//------------------------------------------------------------------------
void COptionMenu::setPrefixNumbers (long preCount)
{
	prefixNumbers = preCount;
}

//-----------------------------------------------------------------------------
bool COptionMenu::allocateSubMenu (long nb)
{
	long newAllocated = nbSubMenuAllocated + nb;

	if (submenuEntry)
		submenuEntry = (COptionMenu**)realloc (submenuEntry, newAllocated * sizeof (COptionMenu*));
	else
		submenuEntry = (COptionMenu**)malloc (newAllocated * sizeof (COptionMenu*));

	long i;
	for (i = nbSubMenuAllocated; i < newAllocated; i++)
		submenuEntry[i] = 0;

	nbSubMenuAllocated = newAllocated;

	return true;
}

//------------------------------------------------------------------------
bool COptionMenu::allocateMenu (long nb)
{
	long newAllocated = nbAllocated + nb;

	if (check)
		check = (bool*)realloc (check, newAllocated * sizeof (bool));
	else
		check = (bool*)malloc (newAllocated * sizeof (bool));
	if (!check)
		return false;

	if (entry)
		entry = (char**)realloc (entry, newAllocated * sizeof (char*));
	else
		entry = (char**)malloc (newAllocated * sizeof (char*));
	if (!entry)
	{
		free (check);
		return false;
	}

	long i;
	for (i = nbAllocated; i < newAllocated; i++)
	{
		check[i] = false;
		entry[i] = 0;
	}

	nbAllocated = newAllocated;

	return true;
}

//------------------------------------------------------------------------
COptionMenu* COptionMenu::getSubMenu (long idx) const
{
	if (submenuEntry && idx < nbSubMenus)
		return submenuEntry[idx];
	return 0;
}

//------------------------------------------------------------------------
bool COptionMenu::addEntry (COptionMenu *subMenu, char *txt)
{
	if (nbEntries >= MAX_ENTRY || !subMenu || !txt)
		return false;

	if (nbEntries >= nbAllocated)
		if (!allocateMenu (32))
			return false;

	entry[nbEntries] = (char*)malloc (256);
	switch (prefixNumbers)
	{
		case 2:
			sprintf (entry[nbEntries], "-M%1d %s", (int)(nbEntries + 1), txt);
			break;

		case 3:
			sprintf (entry[nbEntries], "-M%02d %s", (int)(nbEntries + 1), txt);
			break;

		case 4:
			sprintf (entry[nbEntries], "-M%03d %s", (int)(nbEntries + 1), txt);
			break;

		default:
			sprintf (entry[nbEntries], "-M%s", txt);
	}


	if (nbSubMenus >= nbSubMenuAllocated)
		if (!allocateSubMenu (10))
			return false;

	submenuEntry[nbSubMenus++] = subMenu;
	subMenu->remember ();

	nbEntries++;

	if (currentIndex < 0)
		currentIndex = 0;
	
	return true;
}

//------------------------------------------------------------------------
bool COptionMenu::addEntry (char *txt, long index)
{
	if (nbEntries >= MAX_ENTRY)
		return false;
	
	if (nbEntries >= nbAllocated)
		if (!allocateMenu (32))
			return false;
		
	entry[nbEntries] = (char*)malloc (256);

	long pos = nbEntries;

	// switch the entries for the insert
	if (index >= 0)
	{
		for (long i = nbEntries; i > index; i--)
			strcpy (entry[i], entry[i - 1]);
		if (index >= nbEntries)
			pos = nbEntries;
		else
			pos = index;	
		if (currentIndex >= index)
			currentIndex++;	
	}

	*entry[pos] = 0;
	if (txt)
	{
		switch (prefixNumbers)
		{
			case 2:
				sprintf (entry[pos], "%1d %s", (int)(index + 1), txt);
				break;

			case 3:
				sprintf (entry[pos], "%02d %s", (int)(index + 1), txt);
				break;

			case 4:
				sprintf (entry[pos], "%03d %s", (int)(index + 1), txt);
				break;

			default:
				strncpy (entry[pos], txt, 256);
		}
	}
	
	nbEntries++;
	
	if (currentIndex < 0)
		currentIndex = 0;
	
	return true;
}

//------------------------------------------------------------------------
long COptionMenu::getCurrent (char *txt, bool countSeparator) const
{
	if (currentIndex < 0)
		return -1;

	long result = 0;
	
	if (countSeparator)
	{
		if (txt)
			strcpy (txt, entry[currentIndex]);
		result = currentIndex;
	}
	else
	{
		for (long i = 0; i < currentIndex; i++)
		{
			if (strcmp (entry[i], kMenuSeparator) && strncmp (entry[i], kMenuTitle, 2))
				result++;
		}
		if (txt)
			strcpy (txt, entry[currentIndex]);
	}
	return result;
}

//------------------------------------------------------------------------
bool COptionMenu::setCurrent (long index, bool countSeparator)
{
	if (index < 0 || index >= nbEntries)
		return false;

	if (countSeparator)
	{
		if (!strcmp (entry[index], kMenuSeparator) && strncmp (entry[index], kMenuTitle, 2))
			return false;

		currentIndex = index;
	}
	else
	{
		long newCurrent = 0;
		long i = 0;
		while (i <= index && newCurrent < nbEntries)
		{
			if (strcmp (entry[newCurrent], kMenuSeparator) && strncmp (entry[newCurrent], kMenuTitle, 2))
				i++;
			newCurrent++;
		}
		currentIndex = newCurrent - 1;
	}
	if (style & (kMultipleCheckStyle & ~kCheckStyle))
		check[currentIndex] = !check[currentIndex];

	// to force the redraw
	setDirty ();

	return true;
}

//------------------------------------------------------------------------
bool COptionMenu::getEntry (long index, char *txt) const
{
	if (index < 0 || index >= nbEntries)
		return false;

	if (txt)
		strcpy (txt, entry[index]);
	return true;
}

//------------------------------------------------------------------------
bool COptionMenu::setEntry (long index, char *txt)
{
	if (index < 0 || index >= nbEntries)
		return false;

	if (txt)
		strcpy (entry[index], txt);
	return true;
}

//------------------------------------------------------------------------
bool COptionMenu::removeEntry (long index)
{
	if (index < 0 || index >= nbEntries)
		return false;
	
	nbEntries--;

	// switch the entries
	for (long i = index; i < nbEntries; i++)
	{
		strcpy (entry[i], entry[i + 1]);
		check[i] = check [i + 1];
	}

	if (currentIndex >= index)
		currentIndex--;

	// delete the last one
	free (entry[nbEntries]);
	entry[nbEntries] = 0;
	check[nbEntries] = false;

	if (nbEntries == 0)
		currentIndex = -1;
	return true;
}

//------------------------------------------------------------------------
bool COptionMenu::removeAllEntry ()
{
	long i;
	for (i = 0; i < nbEntries; i++)
	{
		free (entry[i]);
		entry[i] = 0;
		check[i] = false;
	}

	nbEntries = 0;
	currentIndex = -1;

	for (i = 0; i < nbSubMenus; i++)
	{
		submenuEntry[i]->forget ();
		submenuEntry[i] = 0;
	}
	nbSubMenus = 0;

	if (check)
		free (check);
	check = 0;
	if (entry)
		free (entry);
	entry = 0;
	if (submenuEntry)
		free (submenuEntry);
	submenuEntry = 0;
	nbAllocated = 0;
	nbSubMenuAllocated = 0;

	return true;
}

//------------------------------------------------------------------------
long COptionMenu::getIndex (char *txt) const
{
	if (!txt)
		return -1;

	// search entries
	for (long i = 0; i < nbEntries; i++)
		if (!strcmp (entry[i], txt))
			return i;

	// not found
	return -1;
}

//------------------------------------------------------------------------
bool COptionMenu::checkEntry (long index, bool state)
{
	if (index < 0 || index >= nbEntries)
		return false;

	check[index] = state;

	return true;
}

//------------------------------------------------------------------------
bool COptionMenu::checkEntryAlone (long index)
{
	if (index < 0 || index >= nbEntries)
		return false;
	for (long i = 0; i < nbEntries; i++)
		check[i] = false;
	check[index] = true;

	return true;
}

//------------------------------------------------------------------------
bool COptionMenu::isCheckEntry (long index) const
{
	if (index < 0 || index >= nbEntries)
		return false;

	return check[index];
}

//------------------------------------------------------------------------
void COptionMenu::draw (CDrawContext *pContext)
{
	if (currentIndex >= 0 && nbEntries) 
		drawText (pContext, entry[currentIndex] + prefixNumbers);
	else
		drawText (pContext, NULL);
}

//------------------------------------------------------------------------
void COptionMenu::mouse (CDrawContext *pContext, CPoint &where, long button)
{
	if (!bMouseEnabled || !getFrame () || !pContext)
		return;

	lastButton = (button != -1) ? button : pContext->getMouseButtons ();

	if (listener && button & (kAlt | kShift | kControl | kApple))
	{
		if (listener->controlModifierClicked (pContext, this, button) != 0)
			return;
	}

	if (lastButton & (kLButton|kRButton|kApple))
	{
		if (bgWhenClick)
		{
			char string[256];
			if (currentIndex >= 0)
				sprintf (string, "%s", entry[currentIndex]);
			else
				string[0] = 0;
		
			drawText (pContext, string, bgWhenClick);
		}

		beginEdit();
		takeFocus (pContext);
	}
}

//------------------------------------------------------------------------
#if MOTIF
#include <Xm/RowColumn.h>
#include <Xm/ToggleB.h>
#include <Xm/PushB.h>
#include <Xm/SeparatoG.h>

static void _unmapCallback (Widget item, XtPointer clientData, XtPointer callData);
static void _activateCallback (Widget item, XtPointer clientData, XtPointer callData);

//------------------------------------------------------------------------
static void _unmapCallback (Widget item, XtPointer clientData, XtPointer callData)
{
	COptionMenu *optionMenu= (COptionMenu*)clientData;
	optionMenu->looseFocus ();
}

//------------------------------------------------------------------------
static void _activateCallback (Widget item, XtPointer clientData, XtPointer callData)
{
	COptionMenu *optionMenu= (COptionMenu*)clientData;
	optionMenu->setCurrentSelected ((void*)item);
}
#endif

//------------------------------------------------------------------------
#if BEOS
#include <PopUpMenu.h>
#include <MenuItem.h>
#endif

//------------------------------------------------------------------------
COptionMenu *COptionMenu::getLastItemMenu (long &idxInMenu) const
{
	idxInMenu = lastMenu ? (long)lastMenu->getValue (): -1;
	return lastMenu;
}

//------------------------------------------------------------------------
COptionMenu *COptionMenu::getItemMenu (long idx, long &idxInMenu, long &offsetIdx)
{
#if WINDOWS
	long oldIDx = offsetIdx;
	offsetIdx += nbEntries;

	if (idx < offsetIdx)
	{
		idxInMenu = idx - oldIDx;
		return this;
	}
	
#elif MAC
	if (menuID == offsetIdx)
	{
		idxInMenu = idx;
		return this;
	}
#endif
	COptionMenu *menu = 0;
	for (long i = 0; i < nbSubMenus; i++)
	{
		menu = submenuEntry[i]->getItemMenu (idx, idxInMenu, offsetIdx);
		if (menu)
			break;
	}
	return menu;
}

//------------------------------------------------------------------------
void COptionMenu::removeItems ()
{
	for (long i = 0; i < nbSubMenus; i++)
		submenuEntry[i]->removeItems ();
	
#if WINDOWS
	// destroy the menu
	if (platformControl)
		DestroyMenu ((HMENU)platformControl);
	platformControl = 0;

#elif MAC
	// destroy the menu
	if (menuID)
		DeleteMenu (menuID);
	if (platformControl)
		DisposeMenu ((MenuHandle)platformControl);
	platformControl = 0;

#endif
}

//------------------------------------------------------------------------
void *COptionMenu::appendItems (long &offsetIdx)
{
	bool multipleCheck = style & (kMultipleCheckStyle & ~kCheckStyle);

#if WINDOWS
	void *menu = (void*)CreatePopupMenu ();
	
	bool ownerDraw = (scheme != 0) || (gOptionMenuScheme != 0);

	int flags = 0;
	long idxSubmenu = 0;
	long offset = offsetIdx;
	offsetIdx += nbEntries;
	long inc = 0;
	for (long i = 0; i < nbEntries; i++)
	{
		//---Separator-----
		if (!strcmp (entry[i], kMenuSeparator))
		{
			if (ownerDraw)
				AppendMenu ((HMENU)menu, MF_OWNERDRAW|MF_SEPARATOR, 0, entry[i]);
			else
				AppendMenu ((HMENU)menu, MF_SEPARATOR, 0, entry[i]);
		}
		else
		{
			flags = ownerDraw ? MF_OWNERDRAW : MF_STRING;
			if (nbEntries < 160 && nbItemsPerColumn > 0 && inc && !(inc % nbItemsPerColumn))
				flags |= MF_MENUBARBREAK;

			if (!strncmp (entry[i], kMenuSubMenu, 2))
			{
				if (idxSubmenu < nbSubMenus)
				{
					void *submenu = submenuEntry[idxSubmenu]->appendItems (offsetIdx);
					if (submenu)
					{
						idxSubmenu++;
						AppendMenu ((HMENU)menu, flags|MF_POPUP|MF_ENABLED, (UINT_PTR)submenu, entry[i] + 2);
					}
					else
						continue;
				}
				else
					continue;
			}
			//---Disable/Gray entry-----------
			else if (!strncmp (entry[i], kMenuDisable, 2))
			{
				AppendMenu ((HMENU)menu, flags|MF_GRAYED, offset + inc, entry[i] + 2);
			}
			//---Disable entry--------
			else if (!strncmp (entry[i], kMenuTitle, 2))
			{
				AppendMenu ((HMENU)menu, flags|MF_DISABLED, offset + inc, entry[i] + 2);
			}
			//---Multiple Checked entry---
			else if (multipleCheck)
			{
				AppendMenu ((HMENU)menu, flags|MF_ENABLED | 
					(check[i] ? MF_CHECKED : MF_UNCHECKED), offset + inc, entry[i]);
			}
			//---Checked Entry---------
			else if (style & kCheckStyle)
			{
				AppendMenu ((HMENU)menu, flags|MF_ENABLED | 
					((i == currentIndex) ? MF_CHECKED : MF_UNCHECKED), offset + inc, entry[i]);
			}
			else
				AppendMenu ((HMENU)menu, flags|MF_ENABLED, offset + inc, entry[i]);
		}
		inc++;
	}
	platformControl = menu;
	return menu;

#elif MAC
	//---Get an non-existing ID for the menu:
	menuID = UniqueID ('MENU');
		
	MenuHandle theMenu = 0;
	//---Create the menu
	#if MAC_ENABLE_MENU_SCHEME
	extern long pSystemVersion;
	if ((scheme || gOptionMenuScheme) && pSystemVersion >= 0x1030)
	{
		COptionMenuScheme* s = gOptionMenuScheme ? gOptionMenuScheme : scheme;
		EventRef initEvent = NULL;
		if (CreateEvent (NULL, kEventClassHIObject, kEventHIObjectInitialize, 0, 0, &initEvent) == noErr)
		{
			MenuDefSpec customMenuDef;
			COptionMenu* optMenu = this;
			SetEventParameter (initEvent, kEventParamCOptionMenu, typeVoidPtr, sizeof(COptionMenu*), &optMenu);
			customMenuDef.defType = kMenuDefClassID;
			customMenuDef.u.view.classID = gOptionMenuSchemeClassID;
			customMenuDef.u.view.initEvent = initEvent;
			SetEventParameter (initEvent, kEventParamCOptionMenuScheme, typeVoidPtr, sizeof(COptionMenuScheme*), &s);
			CreateCustomMenu (&customMenuDef, menuID, 0, &theMenu);
			ReleaseEvent (initEvent);
			if (theMenu == NULL)
				return NULL;
		}
	}
	else
	#endif
		theMenu = NewMenu (menuID, "\pPopUp");

	char text2[256];
	long keyChar;
	long idxSubmenu = 0;
	offsetIdx += nbEntries;
	short keyModifiers;
	bool useGlyph;
	for (long i = 0; i < nbEntries; i++)
	{
		keyChar = 0;
		keyModifiers = kMenuNoModifiers;
		useGlyph = false;
		
		strcpy (text2, entry[i]);
		char *ptr = strstr (text2, "\t");
		if (ptr)
		{
			*ptr = '\0';
			ptr++;
			if (strlen (ptr) > 0)
			{
				if (!strstr (ptr, "Ctrl"))
				   	keyModifiers |= kMenuNoCommandModifier;
				if (strstr (ptr, "Alt"))
					keyModifiers |= kMenuOptionModifier;
				if (strstr (ptr, "Shift"))
					keyModifiers |= kMenuShiftModifier;
				if (strstr (ptr, "Apple"))
					keyModifiers |= kMenuControlModifier;
				
				if (!strncmp (ptr, "Del", 3))
					keyChar = 0x17;
				else if (!strncmp (ptr, "Left", 4))
				{
					keyChar = 0x64;
					useGlyph = true;
				}
				else if (!strncmp (ptr, "Right", 5))
				{
					keyChar = 0x65;
					useGlyph = true;
				}
				else if (!strncmp (ptr, "Up", 2))
				{
					keyChar = 0x68;
					useGlyph = true;
				}
				else if (!strncmp (ptr, "Down", 4))
				{
					keyChar = 0x6a;
					useGlyph = true;
				}
				else
					keyChar = (long)ptr[strlen (ptr) - 1];
			}
			else 
			{
				keyModifiers = kMenuNoCommandModifier;
				keyChar = (long)*ptr;	
			}
		}
		#if TARGET_API_MAC_CARBON

		if (!strcmp (entry[i], kMenuSeparator))
		{
			AppendMenuItemTextWithCFString (theMenu, CFSTR(""), kMenuItemAttrSeparator, 0, NULL);
		}
		else
		{
			CFStringRef itemString = 0;
			MenuItemAttributes itemAttribs = kMenuItemAttrIgnoreMeta;
			// Submenu
			if (!strncmp (entry[i], kMenuSubMenu, 2))
			{
				if (idxSubmenu < nbSubMenus)
				{
					itemString = CFStringCreateWithCString (NULL, entry[i] + 2, kCFStringEncodingUTF8);
					InsertMenuItemTextWithCFString (theMenu, itemString, i+1, itemAttribs, 0);
					CFRelease (itemString);
					void *submenu = submenuEntry[idxSubmenu]->appendItems (offsetIdx);
					if (submenu)
					{
						SetMenuItemHierarchicalID (theMenu, i + 1, submenuEntry[idxSubmenu]->getMenuID ());
						idxSubmenu++;
						continue;
					}
					else
						continue;
				}
				else
					continue;
			}
			//---Disable/Gray entry-----------
			else if (!strncmp (entry[i], kMenuDisable, 2))
			{
				itemString = CFStringCreateWithCString (NULL, entry[i] + 2, kCFStringEncodingUTF8);
				itemAttribs |= kMenuItemAttrDisabled;
			}
			//---Disable entry--------
			else if (!strncmp (entry[i], kMenuTitle, 2))
			{
				itemString = CFStringCreateWithCString (NULL, entry[i] + 2, kCFStringEncodingUTF8);
				itemAttribs |= kMenuItemAttrSectionHeader;
			}
			else
				itemString = CFStringCreateWithCString (NULL, entry[i], kCFStringEncodingUTF8);

			InsertMenuItemTextWithCFString (theMenu, itemString, i+1, itemAttribs, 0);
			CFRelease (itemString);
		}

		#else
		Str255 menuItem;
		strcpy ((char*)menuItem, (const char*)"\p\0");
		menuItem[0] = strlen ((const char*)text2);
		AppendMenu (theMenu, "\pjunk");
		
		if (!strncmp (text2, kMenuTitle, 2) || !strncmp (text2, kMenuDisable, 2) || !strncmp (text2, kMenuSubMenu, 2))
		{
			menuItem[0] -= 2;
			strcat ((char*)menuItem, text2 + 2);
	
			//---Disable item--------
			if (!strncmp (text2, kMenuDisable, 2))
			{
				#if TARGET_API_MAC_CARBON
				DisableMenuItem (theMenu, i + 1);
				#else
				DisableItem (theMenu, i + 1);
				#endif
			}
			//---Submenu-------------
			else if (!strncmp (text2, kMenuSubMenu, 2))
			{
				if (idxSubmenu < nbSubMenus)
				{
					void *submenu = submenuEntry[idxSubmenu]->appendItems (offsetIdx);
					if (submenu)
					{
						SetMenuItemHierarchicalID (theMenu, i + 1, submenuEntry[idxSubmenu]->getMenuID ());
						idxSubmenu++;
					}
					else
						continue;
				}
				else
					continue;
			}
		}
		else
		{
			strcat ((char*)menuItem, text2);
		}
		SetMenuItemText (theMenu, i + 1, menuItem);

		#endif // !TARGET_API_MAC_CARBON

		//---Set the shortcut
		if (keyChar != 0)
		{
			SetItemCmd (theMenu, i + 1, keyChar);
			if (useGlyph)
				SetMenuItemKeyGlyph (theMenu, i + 1, keyChar);
			SetMenuItemModifiers (theMenu, i + 1, keyModifiers);
		}
		
		if (multipleCheck && check[i])
			CheckMenuItem (theMenu, i + 1, true);

	}
	
	// set the check
	if (style & kCheckStyle && !multipleCheck)
		CheckMenuItem (theMenu, currentIndex + 1, true);

	InsertMenu ((MenuHandle)theMenu, -1);
	
	platformControl = (void*)theMenu;
	return platformControl;
#endif
	return platformControl;
}

//------------------------------------------------------------------------
void COptionMenu::setValue (float val)
{
	if ((long)val < 0 || (long)val >= nbEntries)
		return;
	
	currentIndex = (long)val;
	if (style & (kMultipleCheckStyle & ~kCheckStyle))
		check[currentIndex] = !check[currentIndex];
	CParamDisplay::setValue (val);
	
	// to force the redraw
	setDirty ();
}

//------------------------------------------------------------------------
void COptionMenu::takeFocus (CDrawContext *pContext)
{
	if (!getFrame ())
		return;

	bool multipleCheck = style & (kMultipleCheckStyle & ~kCheckStyle);
	lastResult = -1;
	lastMenu = 0;

#if MAC || WINDOWS
	// calculate Screen Position
	#if WINDOWS
	HWND hwnd = (HWND)getFrame ()->getSystemWindow ();

	#endif

	CRect rect;
	if (pContext)
	{
		rect.left = pContext->offsetScreen.h;
		rect.top  = pContext->offsetScreen.v;
	}
	else
	{
		#if WINDOWS
		RECT rctWinParent;
		GetWindowRect (hwnd, &rctWinParent);
		rect.left = rctWinParent.left;
		rect.top  = rctWinParent.top;
		#elif QUARTZ
		HIRect bounds;
		HIViewRef control = (HIViewRef)getFrame ()->getPlatformControl ();
		HIViewGetFrame (control, &bounds);
		WindowRef window = (WindowRef)getFrame ()->getSystemWindow ();
		WindowAttributes attr;
		GetWindowAttributes (window, &attr);
		if (attr & kWindowCompositingAttribute)
		{
			HIViewRef contentView;
			HIViewFindByID (HIViewGetRoot (window), kHIViewWindowContentID, &contentView);
			if (HIViewGetSuperview (control) != contentView)
				HIViewConvertRect (&bounds, control, contentView);
			bounds.origin.x += getFrame ()->hiScrollOffset.x;
			bounds.origin.y += getFrame ()->hiScrollOffset.y;
		}
		rect.left = (CCoord)bounds.origin.x;
		rect.top = (CCoord)bounds.origin.y;
		#endif
	}
	CView* parent = getParentView ();
	while (parent)
	{
		if (parent->notify (this, kMsgCheckIfViewContainer) == kMessageNotified)
		{
			CRect vSize;
			parent->getViewSize (vSize);
			rect.offset (vSize.left, vSize.top);
		}
		parent = parent->getParentView ();
	}
#endif
	
#if WINDOWS
	MSG msg;
	long result = -1;

	//---Create the popup menu---
	long offIdx = 0;
	appendItems (offIdx);
	
	//---Popup the menu---
	long offset;
	if (style & kPopupStyle)
		offset = (long)(rect.top + size.top);
	else
		offset = (long)(rect.top + size.bottom);

	int flags = TPM_LEFTALIGN;
	if (lastButton & kRButton)
		flags |= TPM_RIGHTBUTTON;

	if (TrackPopupMenu ((HMENU)platformControl, flags, 
			 (int)(rect.left + size.left), offset, 0, hwnd, 0))
	{
		if (PeekMessage (&msg, hwnd, WM_COMMAND, WM_COMMAND, PM_REMOVE))
		{
			if (HIWORD (msg.wParam) == 0)
			{
				result = LOWORD (msg.wParam);
				lastResult = result;
			}
		}
	}

	//---Destroy the menu----
	removeItems ();
	
	//---Update the dependencies
	if (result != -1 || bgWhenClick)
	{
		CDrawContext *pContextTemp = 0;
		HDC hdc;
		if (!pContext && result != -1)
		{
	 		// create a local context
			hdc = GetDC (hwnd);
			pContextTemp = new CDrawContext (getFrame (), hdc, hwnd);
		}
		else
			pContextTemp = pContext;
	
		// to force the redraw
		if (bgWhenClick)
			setDirty ();

		if (result != -1)
		{
			long idx = 0;
			offIdx = 0;
			COptionMenu *menu = getItemMenu (result, idx, offIdx);
			if (menu)
			{
				lastMenu = menu;
				menu->setValue ((float)idx);
			
				// update dependency
				if (listener)
					listener->valueChanged (pContextTemp, menu);
			}
		}

		// redraw the display
		// AAAAARRRRGHHHHHHHHHHHHH!!
		//doIdleStuff ();
		//setDirty (false);

		if (!pContext && pContextTemp)
		{
			delete pContextTemp;
			ReleaseDC (hwnd, hdc);
		}
	}

#elif MAC
	// no entries, no menu
	if (nbEntries == 0)
	{
		endEdit();
		getFrame ()->setFocusView (0);
		return;
	}
		
	//---Transform local coordinates to global coordinates
	long offset;

	if (style & kPopupStyle)
		offset = (long)size.top;
	else
		offset = (long)size.bottom;

	CCoord gx = 0, gy = 0;
	Point LToG;
	getFrame()->getPosition(gx, gy);
	LToG.v = (short)(gy + rect.top + offset);
	LToG.h = (short)(gx + rect.left + size.left);
		
	//---Create the popup menu---
	long offIdx = 0;
	MenuHandle theMenu = (MenuHandle)appendItems (offIdx);

	// Calculate the menu size (height and width)
	CalcMenuSize (theMenu);
	
	// Get a handle to the screen
	RgnHandle rgn = GetGrayRgn ();
	#if TARGET_API_MAC_CARBON
	Rect bounds;
	GetRegionBounds (rgn, &bounds);
	int bottom      = bounds.bottom;
	long menuHeight = GetMenuHeight (theMenu);
	#else
	int bottom      = (*rgn)->rgnBBox.bottom;
	long menuHeight = (*theMenu)->menuHeight;
	#endif

	// Calculate the size of one menu item (round to the next int)
	int menuItemSize = (menuHeight + nbEntries - 1) / nbEntries;

	setDirty (false);	

	//---Popup the Menu
	long popUpItem = 1;
	long PopUpMenuItem = 0;

	if (LToG.v + menuHeight >= bottom - menuItemSize / 2)
	{
		if (nbEntries * menuItemSize >= bottom)
		{
			popUpItem = currentIndex + 2;
			if (popUpItem > nbEntries)
				popUpItem = nbEntries;
		}
		if (nbEntries * menuItemSize >= bottom)
			PopUpMenuItem = PopUpMenuSelect (theMenu, LToG.v, LToG.h, popUpItem);
		else
			PopUpMenuItem = PopUpMenuSelect (theMenu, bottom - menuHeight - menuItemSize, LToG.h, popUpItem);
	}
	else
		PopUpMenuItem = PopUpMenuSelect (theMenu, LToG.v, LToG.h, popUpItem);

	//---Destroy the menu----
	removeItems ();
	
	// HiWord indicates MenuID, LoWord indicates the item index
	short result = LoWord (PopUpMenuItem) - 1;	
	lastResult = result;
	short menuIDResult = HiWord (PopUpMenuItem);
	if (menuIDResult != 0 || bgWhenClick) 
	{
		// to force the redraw
		if (bgWhenClick)
			setDirty ();

		if (!pContext && menuIDResult != 0)
		{
			pContext = getFrame ()->createDrawContext ();
		}
		else if (pContext)
			pContext->remember ();

		if (menuIDResult != 0)
		{
			long idx = 0;
			offIdx = menuIDResult;
			COptionMenu *menu = getItemMenu (result, idx, offIdx);
			if (menu)
			{
				lastMenu = menu;
				menu->setValue (result);
				if (listener)
					listener->valueChanged (pContext, menu);
			}
		}

		if (pContext)
			pContext->forget ();
	}

#elif MOTIF
	Arg args[10];
	int n = 0;
	
	// get the position of the pParent
	CRect rect;
	getFrame ()->getSize (&rect);

	if (pContext)
	{
		rect.left += pContext->offset.h;
		rect.top  += pContext->offset.v;
	}

	// create a popup menu
	int offset;
	if (style & kPopupStyle)
		offset = (int)(rect.top + size.top);
	else
		offset = (int)(rect.top + size.bottom);

	XtSetArg (args[n], XmNx, rect.left + size.left); n++;
	XtSetArg (args[n], XmNy, offset); n++;
	XtSetArg (args[n], XmNmenuHistory, currentIndex); n++;
	XtSetArg (args[n], XmNtraversalOn, true); n++;

	platformControl = (void*)XmCreatePopupMenu ((Widget)(getFrame ()->getSystemWindow ()), 
			"popup", args, n);

	XtAddCallback ((Widget)platformControl, XmNunmapCallback, _unmapCallback, this);

	// insert the menu items
	for (long i = 0; i < nbEntries; i++)
	{
		if (!strcmp (entry[i], kMenuSeparator))
		{
			itemWidget[i] = (void*)XtCreateManagedWidget ("separator", 
							 xmSeparatorGadgetClass, (Widget)platformControl, 0, 0);
		}
		else
		{
			if (multipleCheck)
			{
				itemWidget[i] = (void*)XtVaCreateManagedWidget (entry[i], 
					xmToggleButtonWidgetClass, (Widget)platformControl,
					XmNset, check[i], XmNvisibleWhenOff, false, 0);
				XtAddCallback ((Widget)itemWidget[i], XmNvalueChangedCallback, _activateCallback, this);
			}
			else if (style & kCheckStyle)
			{
				itemWidget[i] = (void*)XtVaCreateManagedWidget (entry[i], 
					xmToggleButtonWidgetClass, (Widget)platformControl,
					XmNset, (i == currentIndex) ? true : false, XmNvisibleWhenOff, false, 0);
				XtAddCallback ((Widget)itemWidget[i], XmNvalueChangedCallback, _activateCallback, this);
			}
			else 
			{
				itemWidget[i] = (void*)XtVaCreateManagedWidget (entry[i], 
					xmPushButtonWidgetClass, (Widget)platformControl, 0);
				XtAddCallback ((Widget)itemWidget[i], XmNactivateCallback, _activateCallback, this);
			}
		}
	}

	XtManageChild ((Widget)platformControl);

#elif BEOS
	BPopUpMenu* popup = new BPopUpMenu ("popup", false, false);
	BMessage*	message;
	BMenuItem*	item;
	for (long i = 0; i < nbEntries; i++)
	{	
		if (strcmp (entry[i], kMenuSeparator) == 0)
			popup->AddSeparatorItem ();
		else
		{
			message = new BMessage (i);
			item = new BMenuItem (entry[i], message);
			popup->AddItem (item);
			if (multipleCheck)
			{
				if (check[i])
					item->SetMarked (true);
			}
			else if ((style & kCheckStyle) && currentIndex == i)
				item->SetMarked (true);
		}
	}
	CRect rect;
	getFrame ()->getSize (&rect);
	if (pContext)
	{
		rect.left += pContext->offset.h;
		rect.top  += pContext->offset.v;
	}
	long offset;
	if (style & kPopupStyle)
		offset = (int)(rect.top + size.top);
	else
		offset = (int)(rect.top + size.bottom);
	BPoint where (rect.left + size.left, offset);
	BView* plugView = (BView*) getFrame ()->getSystemWindow ();
	plugView->ConvertToScreen (&where);
	item = popup->Go (where);
	if (item)
	{
		message = item->Message ();
		if (message)
		{
			CDrawContext *pContextTemp = pContext;
			// create a local context
			if (!pContextTemp)
				pContextTemp = new CDrawContext (getFrame (), plugView, NULL);

			setValue (message->what);
			
			if (listener)
				listener->valueChanged (pContextTemp, this);
	
			// redraw the display
			draw (pContextTemp);

			if (!pContext && pContextTemp)
				delete pContextTemp;
		}
	}
	delete popup;
#endif

	getFrame ()->setFocusView (0);
	endEdit();
}

//------------------------------------------------------------------------
void COptionMenu::looseFocus (CDrawContext *pContext)
{	
	if (platformControl == 0) 
		return;

#if WINDOWS
#elif MAC
#elif MOTIF
	for (long i = 0; i < nbEntries; i++)
		if (itemWidget[i])
			XtDestroyWidget ((Widget)itemWidget[i]);

	if (platformControl)
	{
		XtUnmanageChild ((Widget)platformControl);
		XtDestroyWidget ((Widget)platformControl);
	}
#endif

	platformControl = 0;
}

#if MOTIF
//------------------------------------------------------------------------
void COptionMenu::setCurrentSelected (void *itemSelected)
{
	// retrieve the current index
	if (itemSelected != 0)
	{
		for (long i = 0; i < nbEntries; i++)
			if (itemWidget[i] == itemSelected)
			{
				currentIndex = i;
				break;
			}
	}

	// update dependency
	CDrawContext *pContext = new CDrawContext (getFrame (), (void*)getFrame ()->getGC (), (void*)getFrame ()->getWindow ());

	setValue (currentIndex);

	if (listener)
		listener->valueChanged (pContext, this);
	delete pContext;
}
#endif


//------------------------------------------------------------------------
// CAnimKnob
//------------------------------------------------------------------------
/*! @class CAnimKnob
Such as a CKnob control object, but there is a unique pixmap which contains different views (subpixmaps) of this knob.
According to the value, a specific subpixmap is displayed. The different subpixmaps are stacked in the pixmap object.
*/
CAnimKnob::CAnimKnob (const CRect &size, CControlListener *listener, long tag,
                      CBitmap *background, CPoint &offset)
: CKnob (size, listener, tag, background, 0, offset), bInverseBitmap (false)
{
	heightOfOneImage = size.height ();
	subPixmaps = (short)(background->getHeight () / heightOfOneImage);
	inset = 0;
}

//------------------------------------------------------------------------
CAnimKnob::CAnimKnob (const CRect &size, CControlListener *listener, long tag,
                      long subPixmaps,         // number of subPixmaps
                      CCoord heightOfOneImage,   // height of one image in pixel
                      CBitmap *background, CPoint &offset)
: CKnob (size, listener, tag, background, 0, offset), 
   subPixmaps (subPixmaps), heightOfOneImage (heightOfOneImage), bInverseBitmap (false)
{
	inset = 0;
}

//------------------------------------------------------------------------
CAnimKnob::~CAnimKnob ()
{}

//-----------------------------------------------------------------------------------------------
bool CAnimKnob::isDirty () const
{
	if (!bDirty)
	{
		CPoint p;
		valueToPoint (p);
		if (p == lastDrawnPoint)
			return false;
	}
	return CKnob::isDirty ();
}

//------------------------------------------------------------------------
void CAnimKnob::draw (CDrawContext *pContext)
{
	CPoint where (0, 0);
	if (value >= 0.f) 
	{
		CCoord tmp = heightOfOneImage * (subPixmaps - 1);
		if (bInverseBitmap)
			where.v = (long)((1 - value) * (float)tmp);
		else
			where.v = (long)(value * (float)tmp);
		for (CCoord realY = 0; realY <= tmp; realY += heightOfOneImage) 
		{
			if (where.v < realY) 
			{
				where.v = realY - heightOfOneImage;
				if (where.v < 0)
					where.v = 0;
				break;
			}
		}
	}

	if (pBackground)
	{
		if (bTransparencyEnabled)
			pBackground->drawTransparent (pContext, size, where);
		else
			pBackground->draw (pContext, size, where);
	}
	valueToPoint (lastDrawnPoint);
	setDirty (false);
}

//------------------------------------------------------------------------
// CVerticalSwitch
//------------------------------------------------------------------------
/*! @class CVerticalSwitch
Define a switch with a given number of positions, the current position is defined by the position
of the last click on this object (the object is divided in its height by the number of position).
Each position has its subpixmap, each subpixmap is stacked in the given handle pixmap.
By clicking Alt+Left Mouse the default value is used.
*/
CVerticalSwitch::CVerticalSwitch (const CRect &size, CControlListener *listener, long tag,
                                  CBitmap *background, CPoint &offset)
: CControl (size, listener, tag, background), offset (offset)
{
	heightOfOneImage = size.height ();
	subPixmaps = (long)(background->getHeight () / heightOfOneImage);
	iMaxPositions = subPixmaps;

	setDefaultValue (0.f);
}

//------------------------------------------------------------------------
CVerticalSwitch::CVerticalSwitch (const CRect &size, CControlListener *listener, long tag,
                                  long subPixmaps,       // number of subPixmaps
                                  CCoord heightOfOneImage, // height of one image in pixel
                                  long iMaxPositions,
                                  CBitmap *background, CPoint &offset)
: CControl (size, listener, tag, background), offset (offset),
	subPixmaps (subPixmaps), heightOfOneImage (heightOfOneImage),
	iMaxPositions (iMaxPositions)
{
	setDefaultValue (0.f);
}

//------------------------------------------------------------------------
CVerticalSwitch::~CVerticalSwitch ()
{}

//------------------------------------------------------------------------
void CVerticalSwitch::draw (CDrawContext *pContext)
{
	if (pBackground)
	{
		// source position in bitmap
		CPoint where (0, heightOfOneImage * ((long)(value * (iMaxPositions - 1) + 0.5f)));

		if (bTransparencyEnabled)
			pBackground->drawTransparent (pContext, size, where);
		else
			pBackground->draw (pContext, size, where);
	}
	setDirty (false);
}

//------------------------------------------------------------------------
void CVerticalSwitch::mouse (CDrawContext *pContext, CPoint &where, long button)
{
	if (!bMouseEnabled)
		return;
	
	if (button == -1) button = pContext->getMouseButtons ();
	if (!(button & kLButton))
		return;

	if (listener && button & (kAlt | kShift | kControl | kApple))
	{
		if (listener->controlModifierClicked (pContext, this, button) != 0)
			return;
	}

	// check if default value wanted
	if (checkDefaultValue (pContext, button))
		return;

	double coef = (double)heightOfOneImage / (double)iMaxPositions;

	// begin of edit parameter
	beginEdit ();
	do
	{
		value = (long)((where.v - size.top) / coef) / (float)(iMaxPositions - 1);
		if (value > 1.f)
			value = 1.f;
		else if (value < 0.f)
			value = 0.f;

		if (isDirty () && listener)
			listener->valueChanged (pContext, this);
		
		getMouseLocation (pContext, where);
		
		doIdleStuff ();
	}
	while (pContext->getMouseButtons () == button);

	// end of edit parameter
	endEdit ();
}


//------------------------------------------------------------------------
// CHorizontalSwitch
//------------------------------------------------------------------------
/*! @class CHorizontalSwitch
Same as the CVerticalSwitch but horizontal.
*/
CHorizontalSwitch::CHorizontalSwitch (const CRect &size, CControlListener *listener, long tag,
								  CBitmap *background, CPoint &offset)
: CControl (size, listener, tag, background), offset (offset)
{
	heightOfOneImage = size.width ();
	subPixmaps = (long)(background->getWidth () / heightOfOneImage);
	iMaxPositions = subPixmaps;

	setDefaultValue (0.f);
}

//------------------------------------------------------------------------
CHorizontalSwitch::CHorizontalSwitch (const CRect &size, CControlListener *listener, long tag,
                                  long subPixmaps,   // number of subPixmaps
                                  CCoord heightOfOneImage, // height of one image in pixel
                                  long iMaxPositions,
                                  CBitmap *background, CPoint &offset)
: CControl (size, listener, tag, background), offset (offset),
	subPixmaps (subPixmaps), heightOfOneImage (heightOfOneImage),
	iMaxPositions (iMaxPositions)
{
	setDefaultValue (0.f);
}

//------------------------------------------------------------------------
CHorizontalSwitch::~CHorizontalSwitch ()
{}

//------------------------------------------------------------------------
void CHorizontalSwitch::draw (CDrawContext *pContext)
{
	if (pBackground)
	{
		// source position in bitmap
		CPoint where (0, heightOfOneImage * ((long)(value * (iMaxPositions - 1) + 0.5f)));

		if (bTransparencyEnabled)
			pBackground->drawTransparent (pContext, size, where);
		else
			pBackground->draw (pContext, size, where);
	}
	setDirty (false);
}

//------------------------------------------------------------------------
void CHorizontalSwitch::mouse (CDrawContext *pContext, CPoint &where, long button)
{
	if (!bMouseEnabled)
		return;

	if (button == -1) button = pContext->getMouseButtons ();

	if (listener && button & (kAlt | kShift | kControl | kApple))
	{
		if (listener->controlModifierClicked (pContext, this, button) != 0)
			return;
	}

	if (!(button & kLButton))
		return;
	
	// check if default value wanted
	if (checkDefaultValue (pContext, button))
		return;

	double coef = (double)pBackground->getWidth () / (double)iMaxPositions;

	// begin of edit parameter
	beginEdit ();
	do
	{
		value = (long)((where.h - size.left) / coef) / (float)(iMaxPositions - 1);
		if (value > 1.f)
			value = 1.f;
		else if (value < 0.f)
			value = 0.f;

		if (isDirty () && listener)
			listener->valueChanged (pContext, this);
		
		getMouseLocation (pContext, where);
		
		doIdleStuff ();
	}
	while (pContext->getMouseButtons () == button);

	// end of edit parameter
	endEdit ();
}


//------------------------------------------------------------------------
// CRockerSwitch
//------------------------------------------------------------------------
/*! @class CRockerSwitch
Define a rocker switch with 3 states using 3 subpixmaps.
One click on its leftside, then the first subpixmap is displayed.
One click on its rightside, then the third subpixmap is displayed.
When the mouse button is relaxed, the second subpixmap is framed.
*/
CRockerSwitch::CRockerSwitch (const CRect &size, CControlListener *listener, long tag,              // identifier tag (ID)
                              CBitmap *background, CPoint &offset, const long style)
:	CControl (size, listener, tag, background), offset (offset), style (style)
{
	heightOfOneImage = size.width ();
}

//------------------------------------------------------------------------
CRockerSwitch::CRockerSwitch (const CRect &size, CControlListener *listener, long tag,              // identifier tag (ID)
                              CCoord heightOfOneImage, // height of one image in pixel
                              CBitmap *background, CPoint &offset, const long style)
:	CControl (size, listener, tag, background), offset (offset), 
	heightOfOneImage (heightOfOneImage), style (style)
{}

//------------------------------------------------------------------------
CRockerSwitch::~CRockerSwitch ()
{}

//------------------------------------------------------------------------
void CRockerSwitch::draw (CDrawContext *pContext)
{
	CPoint where (offset.h, offset.v);

	if (value == 1.f)
		where.v += 2 * heightOfOneImage;
	else if (value == 0.f)
		where.v += heightOfOneImage;

	if (pBackground)
	{
		if (bTransparencyEnabled)
			pBackground->drawTransparent (pContext, size, where);
		else
			pBackground->draw (pContext, size, where);
	}
	setDirty (false);
}

//------------------------------------------------------------------------
void CRockerSwitch::mouse (CDrawContext *pContext, CPoint &where, long button)
{
	if (!bMouseEnabled)
		return;

	if (button == -1) button = pContext->getMouseButtons ();

	if (listener && button & (kAlt | kShift | kControl | kApple))
	{
		if (listener->controlModifierClicked (pContext, this, button) != 0)
			return;
	}

	if (!(button & kLButton))
		return;

	float fEntryState = value;

	CCoord  width_2  = size.width () / 2;
	CCoord  height_2 = size.height () / 2;
	
	// begin of edit parameter
	beginEdit ();
	
	if (button)
	{
		do
		{
			if (style & kHorizontal) 
			{
				if (where.h >= size.left && where.v >= size.top  &&
					where.h <= (size.left + width_2) && where.v <= size.bottom)
					value = -1.0f;
				else if (where.h >= (size.left + width_2) && where.v >= size.top  &&
					where.h <= size.right && where.v <= size.bottom)
					value = 1.0f;
				else
					value = fEntryState;
			}
			else
			{
				if (where.h >= size.left && where.v >= size.top  &&
					where.h <= size.right && where.v <= (size.top + height_2))
					value = -1.0f;
				else if (where.h >= size.left && where.v >= (size.top + height_2) &&
					where.h <= size.right && where.v <= size.bottom)
					value = 1.0f;
				else
					value = fEntryState;
			}

			if (isDirty () && listener)
				listener->valueChanged (pContext, this);

			getMouseLocation (pContext, where);

			doIdleStuff ();
		}
		while (pContext->getMouseButtons ());
	}
	else
	{
		if (where.h >= size.left && where.v >= size.top  &&
				where.h <= (size.left + width_2) && where.v <= size.bottom)
			value = -1.0f;
		else if (where.h >= (size.left + width_2) && where.v >= size.top  &&
				where.h <= size.right && where.v <= size.bottom)
			value = 1.0f;

		if (listener)
			listener->valueChanged (pContext, this);
	}

	value = 0.f;  // set button to UNSELECTED state
	if (listener)
		listener->valueChanged (pContext, this);

	// end of edit parameter
	endEdit ();
}

//------------------------------------------------------------------------
bool CRockerSwitch::onWheel (CDrawContext *pContext, const CPoint &where, float distance)
{
	if (!bMouseEnabled)
		return false;

	if (distance > 0)
		value = -1.0f;
	else
		value = 1.0f;

	// begin of edit parameter
	beginEdit ();

	if (isDirty () && listener)
		listener->valueChanged (pContext, this);

	value = 0.0f;  // set button to UNSELECTED state
	if (listener)
		listener->valueChanged (pContext, this);
	
	// end of edit parameter
	endEdit ();

	return true;
}


//------------------------------------------------------------------------
// CMovieBitmap
//------------------------------------------------------------------------
/*! @class CMovieBitmap
A movie pixmap allows to display different subpixmaps according to its current value.
*/
CMovieBitmap::CMovieBitmap (const CRect &size, CControlListener *listener, long tag,
                            CBitmap *background, CPoint &offset)
  :	CControl (size, listener, tag, background), offset (offset),
		subPixmaps (subPixmaps), heightOfOneImage (heightOfOneImage)
{
	heightOfOneImage = size.height ();
	subPixmaps = (long)(background->getHeight () / heightOfOneImage);
}

//------------------------------------------------------------------------
CMovieBitmap::CMovieBitmap (const CRect &size, CControlListener *listener, long tag,
                            long subPixmaps,        // number of subPixmaps
                            CCoord heightOfOneImage,  // height of one image in pixel
                            CBitmap *background, CPoint &offset)
  :	CControl (size, listener, tag, background), offset (offset),
		subPixmaps (subPixmaps), heightOfOneImage (heightOfOneImage)
{}

//------------------------------------------------------------------------
CMovieBitmap::~CMovieBitmap ()
{}

//------------------------------------------------------------------------
void CMovieBitmap::draw (CDrawContext *pContext)
{
	CPoint where (offset.h, offset.v);

	if (value > 1.0f)
		value = 1.0f;

 	if (value > 0.0f)
		where.v += heightOfOneImage * (int)(value * (subPixmaps - 1) + 0.5);

	if (pBackground)
	{
		if (bTransparencyEnabled)
			pBackground->drawTransparent (pContext, size, where);
		else
			pBackground->draw (pContext, size, where);
	}
	setDirty (false);
}


//------------------------------------------------------------------------
// CMovieButton
//------------------------------------------------------------------------
/*! @class CMovieButton
A movie button is a bi-states button with 2 subpixmaps. These subpixmaps are stacked in the pixmap.
*/
CMovieButton::CMovieButton (const CRect &size, CControlListener *listener, long tag,              // identifier tag (ID)
                            CBitmap *background, CPoint &offset)
: CControl (size, listener, tag, background), offset (offset), buttonState (value)
{
	heightOfOneImage = size.height ();
}

//------------------------------------------------------------------------
CMovieButton::CMovieButton (const CRect &size, CControlListener *listener, long tag,
                            CCoord heightOfOneImage, // height of one image in pixel
                            CBitmap *background, CPoint &offset)
	:	CControl (size, listener, tag, background), offset (offset),
		heightOfOneImage (heightOfOneImage), buttonState (value)
{}

//------------------------------------------------------------------------
CMovieButton::~CMovieButton ()
{}

//------------------------------------------------------------------------
void CMovieButton::draw (CDrawContext *pContext)
{
	CPoint where;

	where.h = 0;

	bounceValue ();

	if (value)
		where.v = heightOfOneImage;
	else
		where.v = 0;

	if (pBackground)
	{
		if (bTransparencyEnabled)
			pBackground->drawTransparent (pContext, size, where);
		else
			pBackground->draw (pContext, size, where);
	}
	buttonState = value;

	setDirty (false);
}

//------------------------------------------------------------------------
void CMovieButton::mouse (CDrawContext *pContext, CPoint &where, long button)
{
	if (!bMouseEnabled)
		return;

	if (button == -1) button = pContext->getMouseButtons ();

	if (listener && button & (kAlt | kShift | kControl | kApple))
	{
		if (listener->controlModifierClicked (pContext, this, button) != 0)
			return;
	}

	if (!(button & kLButton))
		return;

	// this simulates a real windows button
	float fEntryState = value;

	// begin of edit parameter
	beginEdit ();
		
	if (pContext->getMouseButtons ())
	{
		do
		{
			if (where.h >= size.left &&
					where.v >= size.top  &&
					where.h <= size.right &&
					where.v <= size.bottom)
				value = !fEntryState;
			else
				value = fEntryState;
	    
			if (isDirty () && listener)
				listener->valueChanged (pContext, this);
	    
			getMouseLocation (pContext, where);

			doIdleStuff ();
		}
		while (pContext->getMouseButtons () == button);
	}
	else
	{
		value = !value;
		if (listener)
			listener->valueChanged (pContext, this);
	}

	// end of edit parameter
	endEdit ();

	buttonState = value;
}


//------------------------------------------------------------------------
// CAutoAnimation
//------------------------------------------------------------------------
/*! @class CAutoAnimation
An auto-animation control contains a given number of subpixmap which can be displayed in loop.
Two functions allows to get the previous or the next subpixmap (these functions increase or decrease the current value of this control).
*/
// displays bitmaps within a (child-) window
CAutoAnimation::CAutoAnimation (const CRect &size, CControlListener *listener, long tag,
                                CBitmap *background, CPoint &offset)
: CControl (size, listener, tag, background), offset (offset), bWindowOpened (false)
{
	heightOfOneImage = size.height ();
	subPixmaps = (long)(background->getHeight () / heightOfOneImage);

	totalHeightOfBitmap = heightOfOneImage * subPixmaps;
}

//------------------------------------------------------------------------
CAutoAnimation::CAutoAnimation (const CRect &size, CControlListener *listener, long tag,
                                long subPixmaps,	 // number of subPixmaps...
                                CCoord heightOfOneImage, // height of one image in pixel
                                CBitmap *background, CPoint &offset)
	:	CControl (size, listener, tag, background), offset (offset),
		subPixmaps (subPixmaps), heightOfOneImage (heightOfOneImage),
		bWindowOpened (false)
{
	totalHeightOfBitmap = heightOfOneImage * subPixmaps;
}

//------------------------------------------------------------------------
CAutoAnimation::~CAutoAnimation ()
{}

//------------------------------------------------------------------------
void CAutoAnimation::draw (CDrawContext *pContext)
{
	if (isWindowOpened ())
	{	
		CPoint where;
		where.v = (long)value + offset.v;
		where.h = offset.h;
		
		if (pBackground)
		{
			if (bTransparencyEnabled)
				pBackground->drawTransparent (pContext, size, where);
			else
				pBackground->draw (pContext, size, where);
		}
	}
	setDirty (false);
}

//------------------------------------------------------------------------
void CAutoAnimation::mouse (CDrawContext *pContext, CPoint &where, long button)
{
	if (!bMouseEnabled)
		return;
	
	if (button == -1) button = pContext->getMouseButtons ();

	if (listener && button & (kAlt | kShift | kControl | kApple))
	{
		if (listener->controlModifierClicked (pContext, this, button) != 0)
			return;
	}

	if (!(button & kLButton))
		return;

	if (!isWindowOpened ())
	{	
		value = 0;
		openWindow ();
		setDirty (); // force to redraw
		if (listener)
			listener->valueChanged (pContext, this);
	}
	else
	{                                                                       
		// stop info animation
		value = 0; // draw first pic of bitmap
		setDirty ();
		closeWindow ();
	}
}

//------------------------------------------------------------------------
void CAutoAnimation::openWindow ()
{
	bWindowOpened = true;
}

//------------------------------------------------------------------------
void CAutoAnimation::closeWindow ()
{
	bWindowOpened = false;
}

//------------------------------------------------------------------------
void CAutoAnimation::nextPixmap ()
{
	value += heightOfOneImage;
	if (value >= (totalHeightOfBitmap - heightOfOneImage))
		value = 0;
}

//------------------------------------------------------------------------
void CAutoAnimation::previousPixmap ()
{
	value -= heightOfOneImage;
	if (value < 0.f)
		value = (float)(totalHeightOfBitmap - heightOfOneImage - 1);
}


//------------------------------------------------------------------------
// CSlider
//------------------------------------------------------------------------
/*! @class CSlider
Define a slider with a given background and handle.
The range of variation of the handle should be defined.
By default the handler is drawn with transparency (white color).
By clicking Alt+Left Mouse the default value is used.
*/
CSlider::CSlider (const CRect &rect, CControlListener *listener, long tag,
                  long      iMinPos, // min position in pixel
                  long      iMaxPos, // max position in pixel
                  CBitmap  *handle,  // bitmap of slider
                  CBitmap  *background, // bitmap of background
                  CPoint   &offset,  // offset in the background
                  const long style)  // style (kBottom,kRight,kTop,kLeft,kHorizontal,kVertical)
  :	CControl (rect, listener, tag, background),	offset (offset), pHandle (handle),
	pOScreen (0), style (style), bFreeClick (true)
{
	setDrawTransparentHandle (true);

	if (pHandle)
	{
		pHandle->remember ();
		widthOfSlider  = pHandle->getWidth ();
		heightOfSlider = pHandle->getHeight ();
	}
	else
	{
		widthOfSlider  = 1; 
		heightOfSlider = 1;
	}

	widthControl  = size.width ();
	heightControl = size.height ();

	if (style & kHorizontal)
	{
		minPos = iMinPos - size.left;
		rangeHandle = iMaxPos - iMinPos;
		CPoint p (0, 0);
		setOffsetHandle (p);
	}
	else
	{
		minPos = iMinPos - size.top;
		rangeHandle = iMaxPos - iMinPos;
		CPoint p (0, 0);
		setOffsetHandle (p);
	}

	zoomFactor = 10.f;

	setWantsFocus (true);
}

//------------------------------------------------------------------------
CSlider::CSlider (const CRect &rect, CControlListener *listener, long tag,
                  CPoint   &offsetHandle,    // handle offset
                  long     _rangeHandle, // size of handle range
                  CBitmap  *handle,     // bitmap of slider
                  CBitmap  *background, // bitmap of background
                  CPoint   &offset,     // offset in the background
                  const long style)     // style (kBottom,kRight,kTop,kLeft,kHorizontal,kVertical)
:	CControl (rect, listener, tag, background), offset (offset), pHandle (handle),
	pOScreen (0), style (style), minPos (0), bFreeClick (true)
{
	setDrawTransparentHandle (true);

	if (pHandle)
	{
		pHandle->remember ();
		widthOfSlider  = pHandle->getWidth ();
		heightOfSlider = pHandle->getHeight ();
	}
	else
	{
		widthOfSlider  = 1; 
		heightOfSlider = 1;
	}

	widthControl  = size.width ();
	heightControl = size.height ();
	if (style & kHorizontal)
		rangeHandle = _rangeHandle - widthOfSlider;
	else
		rangeHandle = _rangeHandle - heightOfSlider;

	setOffsetHandle (offsetHandle);
	
	zoomFactor = 10.f;

	setWantsFocus (true);
}

//------------------------------------------------------------------------
CSlider::~CSlider ()
{
	if (pHandle)
		pHandle->forget ();
}

//------------------------------------------------------------------------
void CSlider::setOffsetHandle (CPoint &val)
{
	offsetHandle = val;

	if (style & kHorizontal)
	{
		minTmp = offsetHandle.h + minPos;
		maxTmp = minTmp + rangeHandle + widthOfSlider;
	}
	else
	{
		minTmp = offsetHandle.v + minPos;
		maxTmp = minTmp + rangeHandle + heightOfSlider;
	}
}

//-----------------------------------------------------------------------------
bool CSlider::attached (CView *parent)
{
	if (pOScreen)
		delete pOScreen;
	#if !MACX
	pOScreen = new COffscreenContext (getFrame (), widthControl, heightControl, kBlackCColor);
	#endif		
	return CControl::attached (parent);
}

//-----------------------------------------------------------------------------
bool CSlider::removed (CView *parent)
{
	if (pOScreen)
	{
		delete pOScreen;
		pOScreen = 0;
	}
	return CControl::removed (parent);
}

//------------------------------------------------------------------------
void CSlider::draw (CDrawContext *pContext)
{
	CDrawContext* drawContext = pOScreen ? pOScreen : pContext;

	#if 1
	if (pOScreen && bTransparencyEnabled)
		pOScreen->copyTo (pContext, size);
	#endif
	float fValue;
	if (style & kLeft || style & kTop)
		fValue = value;
	else 
		fValue = 1.f - value;
	
	// (re)draw background
	CRect rect (0, 0, widthControl, heightControl);
	if (!pOScreen)
		rect.offset (size.left, size.top);
	if (pBackground)
	{
		if (bTransparencyEnabled)
			pBackground->drawTransparent (drawContext, rect, offset);
		else
			pBackground->draw (drawContext, rect, offset);
	}
	
	// calc new coords of slider
	CRect   rectNew;
	if (style & kHorizontal)
	{
		rectNew.top    = offsetHandle.v;
		rectNew.bottom = rectNew.top + heightOfSlider;	

		rectNew.left   = offsetHandle.h + (int)(fValue * rangeHandle);
		rectNew.left   = (rectNew.left < minTmp) ? minTmp : rectNew.left;

		rectNew.right  = rectNew.left + widthOfSlider;
		rectNew.right  = (rectNew.right > maxTmp) ? maxTmp : rectNew.right;
	}
	else
	{
		rectNew.left   = offsetHandle.h;
		rectNew.right  = rectNew.left + widthOfSlider;	

		rectNew.top    = offsetHandle.v + (int)(fValue * rangeHandle);
		rectNew.top    = (rectNew.top < minTmp) ? minTmp : rectNew.top;

		rectNew.bottom = rectNew.top + heightOfSlider;
		rectNew.bottom = (rectNew.bottom > maxTmp) ? maxTmp : rectNew.bottom;
	}
	if (!pOScreen)
		rectNew.offset (size.left, size.top);

	// draw slider at new position
	if (pHandle)
	{
		if (bDrawTransparentEnabled)
			pHandle->drawTransparent (drawContext, rectNew);
		else 
			pHandle->draw (drawContext, rectNew);
	}

	if (pOScreen)
		pOScreen->copyFrom (pContext, size);
	
	setDirty (false);
}

//------------------------------------------------------------------------
void CSlider::mouse (CDrawContext *pContext, CPoint &where, long button)
{
	if (!bMouseEnabled)
		return;

	if (button == -1) button = pContext->getMouseButtons ();

	if (listener && button & (kAlt | kShift | kControl | kApple))
	{
		if (listener->controlModifierClicked (pContext, this, button) != 0)
			return;
	}

	// check if default value wanted
	if (checkDefaultValue (pContext, button))
		return;
	
	// allow left mousebutton only
	if (!(button & kLButton))
		return;

	CCoord delta;
	if (style & kHorizontal)
		delta = size.left + offsetHandle.h;
	else
		delta = size.top + offsetHandle.v;
	if (!bFreeClick)
	{
		float fValue;
		if (style & kLeft || style & kTop)
			fValue = value;
		else 
			fValue = 1.f - value;
		CCoord actualPos;
		CRect rect;

		if (style & kHorizontal)
		{
			actualPos = offsetHandle.h + (int)(fValue * rangeHandle) + size.left;

			rect.left   = actualPos;
			rect.top    = size.top  + offsetHandle.v;
			rect.right  = rect.left + widthOfSlider;
			rect.bottom = rect.top  + heightOfSlider;

			if (!where.isInside (rect))
				return;
			else
				delta += where.h - actualPos;
		}
		else
		{
			actualPos = offsetHandle.v + (int)(fValue * rangeHandle) + size.top;
		
			rect.left   = size.left  + offsetHandle.h;
			rect.top    = actualPos;
			rect.right  = rect.left + widthOfSlider;
			rect.bottom = rect.top  + heightOfSlider;

			if (!where.isInside (rect))
				return;
			else
				delta += where.v - actualPos;
		}
	} 
	else
	{
		if (style & kHorizontal)
			delta += widthOfSlider / 2 - 1;
		else
			delta += heightOfSlider / 2 - 1;
	}
	
	float oldVal    = value;
	long  oldButton = button;

	// begin of edit parameter
	beginEdit ();

	while (1)
	{
		button = pContext->getMouseButtons ();
		if (!(button & kLButton))
			break;

		if ((oldButton != button) && (button & kShift))
		{
			oldVal    = value;
			oldButton = button;
		}
		else if (!(button & kShift))
			oldVal = value;

		if (style & kHorizontal)
			value = (float)(where.h - delta) / (float)rangeHandle;
		else
			value = (float)(where.v - delta) / (float)rangeHandle;
			
		if (style & kRight || style & kBottom)
			value = 1.f - value;

		if (button & kShift)
			value = oldVal + ((value - oldVal) / zoomFactor);
		bounceValue ();
    	    
		if (isDirty () && listener)
			listener->valueChanged (pContext, this);

		getMouseLocation (pContext, where);

		doIdleStuff ();
	}

	// end of edit parameter
	endEdit ();
}

//------------------------------------------------------------------------
bool CSlider::onWheel (CDrawContext *pContext, const CPoint &where, float distance)
{
	if (!bMouseEnabled)
		return false;

	long buttons = pContext->getMouseButtons ();
	if (buttons & kShift)
		value += 0.1f * distance * wheelInc;
	else
		value += distance * wheelInc;
	bounceValue ();

	if (isDirty () && listener)
	{
		// begin of edit parameter
		beginEdit ();
	
		listener->valueChanged (pContext, this);
	
		// end of edit parameter
		endEdit ();
	}

	return true;
}

//------------------------------------------------------------------------
long CSlider::onKeyDown (VstKeyCode& keyCode)
{
	switch (keyCode.virt)
	{
	case VKEY_UP :
	case VKEY_RIGHT :
	case VKEY_DOWN :
	case VKEY_LEFT :
		{
			float distance = 1.f;
			if (keyCode.virt == VKEY_DOWN || keyCode.virt == VKEY_LEFT)
				distance = -distance;

			if (keyCode.modifier & MODIFIER_SHIFT)
				value += 0.1f * distance * wheelInc;
			else
				value += distance * wheelInc;
			bounceValue ();

			if (isDirty () && listener)
			{
				// begin of edit parameter
				beginEdit ();
			
				listener->valueChanged (0, this);
			
				// end of edit parameter
				endEdit ();
			}
		} return 1;
	}
	return -1;
}

//------------------------------------------------------------------------
void CSlider::setHandle (CBitmap *_pHandle)
{
	if (pHandle)
		pHandle->forget ();
	pHandle = _pHandle;
	if (pHandle)
	{
		pHandle->remember ();
		widthOfSlider  = pHandle->getWidth ();
		heightOfSlider = pHandle->getHeight ();
	}
}


//------------------------------------------------------------------------
// CVerticalSlider
//------------------------------------------------------------------------
/*! @class CVerticalSlider
This is the vertical slider. See CSlider.
*/
CVerticalSlider::CVerticalSlider (const CRect &rect, CControlListener *listener, long tag,
                                  long      iMinPos, // min position in pixel
                                  long      iMaxPos, // max position in pixel
                                  CBitmap  *handle,  // bitmap of slider
                                  CBitmap  *background, // bitmap of background
                                  CPoint   &offset,  // offset in the background
                                  const long style)  // style (kLeft, kRight)
  :	CSlider (rect, listener, tag, iMinPos, iMaxPos, handle, background, offset, style|kVertical)
{}

//------------------------------------------------------------------------
CVerticalSlider::CVerticalSlider (const CRect &rect, CControlListener *listener, long tag,
                          CPoint   &offsetHandle,    // handle offset
                          long     rangeHandle, // size of handle range
                          CBitmap  *handle,     // bitmap of slider
                          CBitmap  *background, // bitmap of background
                          CPoint   &offset,     // offset in the background
                          const long style)     // style (kLeft, kRight)
:	CSlider (rect, listener, tag, offsetHandle, rangeHandle, handle, background, offset, style|kVertical)
{}


//------------------------------------------------------------------------
// CHorizontalSlider
//------------------------------------------------------------------------
/*! @class CHorizontalSlider
This is the horizontal slider. See CSlider.
*/
CHorizontalSlider::CHorizontalSlider (const CRect &rect, CControlListener *listener, long tag,
                                  long      iMinPos, // min Y position in pixel
                                  long      iMaxPos, // max Y position in pixel
                                  CBitmap  *handle,  // bitmap of slider
                                  CBitmap  *background, // bitmap of background
                                  CPoint   &offset,  // offset in the background
                                  const long style)  // style (kLeft, kRight)
  :	CSlider (rect, listener, tag, iMinPos, iMaxPos, handle, background, offset, style|kHorizontal)
{}

//------------------------------------------------------------------------
CHorizontalSlider::CHorizontalSlider (const CRect &rect, CControlListener *listener, long tag,
                          CPoint   &offsetHandle,    // handle offset
                          long     rangeHandle, // size of handle range
                          CBitmap  *handle,     // bitmap of slider
                          CBitmap  *background, // bitmap of background
                          CPoint   &offset,     // offset in the background
                          const long style)     // style (kLeft, kRight)
:	CSlider (rect, listener, tag, offsetHandle, rangeHandle, handle, background, offset, style|kHorizontal)
{}


//------------------------------------------------------------------------
// CSpecialDigit
//------------------------------------------------------------------------
/*! @class CSpecialDigit
Can be used to display a counter with maximum 7 digits.
All digit have the same size and are stacked in height in the pixmap.
*/
CSpecialDigit::CSpecialDigit (const CRect &size,
                              CControlListener *listener,
                              long      tag,        // tag identifier
                              long     dwPos,      // actual value
                              long      iNumbers,   // amount of numbers (max 7)
                              long      *xpos,      // array of all XPOS
                              long      *ypos,      // array of all YPOS
                              long      width,      // width of ONE number
                              long      height,     // height of ONE number
                              CBitmap  *background)    // bitmap numbers
  :	CControl (size, listener, tag, background),
		iNumbers (iNumbers), width (width), height (height)
{
	setValue ((float)dwPos);          // actual value

	if (iNumbers > 7)
		iNumbers = 7;

	if (xpos == NULL)
	{
		// automatically init xpos/ypos if not provided by caller
		const int numw = (const int)background->getWidth();
		int x = (int)size.left;
		for (long i = 0; i < iNumbers; i++)
		{
			this->xpos[i] = x; 
			this->ypos[i] = (long)size.top;
			x += numw;
		}
	} 
	else 
	{
		// store coordinates of x/y pos of each digit
		for (long i = 0; i < iNumbers; i++)
		{
			this->xpos[i] = xpos[i];
			this->ypos[i] = ypos[i];
		}
	}

	setMax ((float)pow (10., (double)iNumbers) - 1.0f);
	setMin (0.0f);
}

//------------------------------------------------------------------------
CSpecialDigit::~CSpecialDigit ()
{}

//------------------------------------------------------------------------
void CSpecialDigit::draw (CDrawContext *pContext)
{
	CPoint  where;
	CRect   rectDest;
	long    i, j;
	long    dwValue;
	long     one_digit[16];
  
	if ((long)value >= getMax ()) 
		dwValue = (long)getMax ();
	else if ((long)value < getMin ()) 
		dwValue = (long)getMin ();
	else
		dwValue = (long)value;
	
	for (i = 0, j = ((long)getMax () + 1) / 10; i < iNumbers; i++, j /= 10)
	{
		one_digit[i] = dwValue / j;
		dwValue -= (one_digit[i] * j);
	}
	
	where.h = 0;
	for (i = 0; i < iNumbers; i++)
	{	
		j = one_digit[i];
		if (j > 9)
			j = 9;
		
		rectDest.left   = xpos[i];
		rectDest.top    = ypos[i];
		
		rectDest.right  = rectDest.left + width;
		rectDest.bottom = rectDest.top  + height;		
		
		// where = src from bitmap
		where.v = j * height;
		if (pBackground)
		{
			if (bTransparencyEnabled)
				pBackground->drawTransparent (pContext, rectDest, where);
			else
				pBackground->draw (pContext, rectDest, where);
		}
	}
		
	setDirty (false);
}

//------------------------------------------------------------------------
float CSpecialDigit::getNormValue () const
{ 
	float fTemp;
	fTemp = value / getMax ();
	if (fTemp > 1.0f)
		fTemp = 1.0f;
	else if (fTemp < 0.0f) 
		fTemp = 0.0f;
	
	return fTemp;
}


//------------------------------------------------------------------------
// CKickButton
//------------------------------------------------------------------------
/*! @class CKickButton
Define a button with 2 states using 2 subpixmaps.
One click on it, then the second subpixmap is displayed.
When the mouse button is relaxed, the first subpixmap is framed.
*/
CKickButton::CKickButton (const CRect &size, CControlListener *listener, long tag,
                          CBitmap *background, CPoint &offset)
:	CControl (size, listener, tag, background), offset (offset)
{
	heightOfOneImage = size.height ();
}

//------------------------------------------------------------------------
CKickButton::CKickButton (const CRect &size, CControlListener *listener, long tag,
                          CCoord heightOfOneImage, // height of one image in pixel
                          CBitmap *background, CPoint &offset)
:	CControl (size, listener, tag, background), offset (offset), 
	heightOfOneImage (heightOfOneImage)
{}

//------------------------------------------------------------------------
CKickButton::~CKickButton ()
{}

//------------------------------------------------------------------------
void CKickButton::draw (CDrawContext *pContext)
{
	CPoint where (offset.h, offset.v);

	bounceValue ();

	if (value)
		where.v += heightOfOneImage;

	if (pBackground)
	{
		if (bTransparencyEnabled)
			pBackground->drawTransparent (pContext, size, where);
		else
			pBackground->draw (pContext, size, where);
	}
	setDirty (false);
}

//------------------------------------------------------------------------
void CKickButton::mouse (CDrawContext *pContext, CPoint &where, long button)
{
	if (!bMouseEnabled)
		return;
	
	if (button == -1) button = pContext->getMouseButtons ();

	if (listener && button & (kAlt | kShift | kControl | kApple))
	{
		if (listener->controlModifierClicked (pContext, this, button) != 0)
			return;
	}

	if (!(button & kLButton))
		return;

	// this simulates a real windows button
	float fEntryState = value;

	// begin of edit parameter
	beginEdit ();

	if (pContext->getMouseButtons () == kLButton)
	{
		do
		{
			if (where.h >= size.left && where.v >= size.top  &&
			    where.h <= size.right && where.v <= size.bottom)
				value = !fEntryState;
			else
				value = fEntryState;
			
			if (isDirty () && listener)
				listener->valueChanged (pContext, this);
			
			getMouseLocation (pContext, where);
			
			doIdleStuff ();
		}
		while (pContext->getMouseButtons () == kLButton);
	}
	else
	{
		value = !value;
		if (listener)
			listener->valueChanged (pContext, this);
	}

	value = 0.0f;  // set button to UNSELECTED state
	if (listener)
		listener->valueChanged (pContext, this);

	// end of edit parameter
	endEdit ();
}


//------------------------------------------------------------------------
class CSplashScreenView : public CView
{
public:
	CSplashScreenView (const CRect& size, CSplashScreen* splashScreen) 
	: CView (size)
	, splashScreen (splashScreen) 
	{
		setTransparency (splashScreen->getTransparency ());
		setBackground (splashScreen->getBackground ());
	}
	 
	void draw (CDrawContext *pContext)
	{
		if (bTransparencyEnabled)
		{
			if (splashScreen->getBitmapTransparency ())
				pBackground->drawAlphaBlend (pContext, size, splashScreen->getOffset (), splashScreen->getBitmapTransparency ());
			else
			pBackground->drawTransparent (pContext, size, splashScreen->getOffset ());
		}
		else
			pBackground->draw (pContext, size, splashScreen->getOffset ());
		setDirty (false);
	}

	void mouse (CDrawContext *pContext, CPoint &where, long button)
	{
		if (button & kLButton)
		{
			splashScreen->unSplash (pContext);
			getFrame ()->setDirty (true);
			getFrame ()->setModalView (0);
			forget ();
		}
	}

protected:
	CSplashScreen* splashScreen;
};

//------------------------------------------------------------------------
// CSplashScreen
//------------------------------------------------------------------------
/*! @class CSplashScreen
One click on its activated region and its pixmap is displayed, in this state the other control can not be used,
an another click on the displayed area reinstalls the normal frame.
This can be used to display a help view over the other views.
*/
// one click draw its pixmap, an another click redraw its parent
CSplashScreen::CSplashScreen (const CRect &size, CControlListener *listener, long tag,
                              CBitmap *background,
                              CRect   &toDisplay,
                              CPoint  &offset)
:	CControl (size, listener, tag, background), 
	toDisplay (toDisplay), offset (offset), bitmapTransparency (255)
{}

//------------------------------------------------------------------------
CSplashScreen::~CSplashScreen ()
{}

//------------------------------------------------------------------------
void CSplashScreen::setBitmapTransparency (unsigned char transparency)
{
	bitmapTransparency = transparency;
	setTransparency (bitmapTransparency != 255);
}

//------------------------------------------------------------------------
void CSplashScreen::draw (CDrawContext *pContext)
{
	if (value && pBackground)
	{
		if (bTransparencyEnabled)
		{
			if (bitmapTransparency)
				pBackground->drawAlphaBlend (pContext, toDisplay, offset, bitmapTransparency);
			else
			pBackground->drawTransparent (pContext, toDisplay, offset);
		}
		else
			pBackground->draw (pContext, toDisplay, offset);
	}
	setDirty (false);
}

//------------------------------------------------------------------------
bool CSplashScreen::hitTest (const CPoint& where, const long buttons)
{
	bool result = CView::hitTest (where, buttons);
	if (result && !(buttons & kLButton))
		return false;
	return result;
}

//------------------------------------------------------------------------
void CSplashScreen::mouse (CDrawContext *pContext, CPoint &where, long button)
{
	if (!bMouseEnabled)
		return;

	if (button == -1) button = pContext->getMouseButtons ();

	if (listener && button & (kAlt | kShift | kControl | kApple))
	{
		if (listener->controlModifierClicked (pContext, this, button) != 0)
			return;
	}

	if (!(button & kLButton))
		return;

	value = !value;
	if (value)
	{
		CSplashScreenView* ssv = new CSplashScreenView (toDisplay, this);
		if (getFrame () && getFrame ()->setModalView (ssv))
		{
//			keepSize = size;
//			size = toDisplay;
//			mouseableArea = size;
			if (listener)
				listener->valueChanged (pContext, this);
		}
		setDirty ();
	}
//	else
//	{
//		size = keepSize;
//		mouseableArea = size;
//		if (listener)
//			listener->valueChanged (pContext, this);
//		if (getFrame ())
//		{
//			getFrame ()->setDirty (true);
//			getFrame ()->setModalView (NULL);
//		}
//	}
}

//------------------------------------------------------------------------
void CSplashScreen::unSplash (CDrawContext* pContext)
{
//	setDirty ();
//	value = 0.f;
//
//	size = keepSize;
//	if (getFrame ())
//	{
//		if (getFrame ()->getModalView () == this)
//		{
//			getFrame ()->setModalView (NULL);
//			getFrame ()->redraw ();
//		}
//	}
	if (value)
	{
		value = 0;
		if (listener)
			listener->valueChanged (pContext, this);
	}
}

//------------------------------------------------------------------------
// CVuMeter
//------------------------------------------------------------------------
CVuMeter::CVuMeter (const CRect &size, CBitmap *onBitmap, CBitmap *offBitmap,
                    long nbLed, const long style)
	: CControl (size, 0, 0),
	  onBitmap (onBitmap), offBitmap (offBitmap), pOScreen (0),
	  nbLed (nbLed), style (style)
{
	setDecreaseStepValue (0.1f);

#if WINDOWS && !USE_LIBPNG
	setUseOffscreen (true);
#endif
	
	if (onBitmap)
		onBitmap->remember ();
	if (offBitmap)
		offBitmap->remember ();

	rectOn  (size.left, size.top, size.right, size.bottom);
	rectOff (size.left, size.top, size.right, size.bottom);
}

//------------------------------------------------------------------------
CVuMeter::~CVuMeter ()
{
	if (onBitmap)
		onBitmap->forget ();
	if (offBitmap)
		offBitmap->forget ();
}

//------------------------------------------------------------------------
void CVuMeter::setDirty (const bool val)
{
	CView::setDirty (val);
}

//-----------------------------------------------------------------------------
bool CVuMeter::attached (CView *parent)
{
	if (pOScreen)
		delete pOScreen;

	if (bUseOffscreen)
	{
		pOScreen = new COffscreenContext (getFrame (), (long)size.width (), (long)size.height (), kBlackCColor);
		rectOn  (0, 0, size.width (), size.height ());
		rectOff (0, 0, size.width (), size.height ());
	}
	else
	{
		rectOn  (size.left, size.top, size.right, size.bottom);
		rectOff (size.left, size.top, size.right, size.bottom);
	}

	return CControl::attached (parent);
}

//------------------------------------------------------------------------
void CVuMeter::setUseOffscreen (bool val)
{
	bUseOffscreen = val;
}

//-----------------------------------------------------------------------------
bool CVuMeter::removed (CView *parent)
{
	if (pOScreen)
	{
		delete pOScreen;
		pOScreen = 0;
	}
	return CControl::removed (parent);
}

//------------------------------------------------------------------------
void CVuMeter::draw (CDrawContext *_pContext)
{
	if (!onBitmap) 
		return;

	CPoint pointOn;
	CPoint pointOff;
	CDrawContext *pContext = _pContext;

	bounceValue ();
	
	float newValue = oldValue - decreaseValue;
	if (newValue < value)
		newValue = value;
	oldValue = newValue;

	if (bUseOffscreen)
	{
		if (!pOScreen)
		{
			pOScreen = new COffscreenContext (getFrame (), (long)size.width (), (long)size.height (), kBlackCColor);
			rectOn  (0, 0, size.width (), size.height ());
			rectOff (0, 0, size.width (), size.height ());
		}
		pContext = pOScreen;
	}

	if (style & kHorizontal) 
	{
		CCoord tmp = (long)(((long)(nbLed * newValue + 0.5f) / (float)nbLed) * onBitmap->getWidth ());
		pointOff (tmp, 0);
		if (!bUseOffscreen)
		tmp += size.left;

		rectOff.left = tmp;
		rectOn.right = tmp;
	}
	else 
	{
		CCoord tmp = (long)(((long)(nbLed * (getMax () - newValue) + 0.5f) / (float)nbLed) * onBitmap->getHeight ());
		pointOn (0, tmp);
		if (!bUseOffscreen)
		tmp += size.top;

		rectOff.bottom = tmp;
		rectOn.top     = tmp;
	}

	if (offBitmap)
	{
		if (bTransparencyEnabled)
			offBitmap->drawTransparent (pContext, rectOff, pointOff);
		else
			offBitmap->draw (pContext, rectOff, pointOff);
	}

	if (bTransparencyEnabled)
		onBitmap->drawTransparent (pContext, rectOn, pointOn);
	else
		onBitmap->draw (pContext, rectOn, pointOn);

	if (pOScreen)
		pOScreen->copyFrom (_pContext, size);
	setDirty (false);
}

END_NAMESPACE_VSTGUI
//------------------------------------------------------------------------
// END.
//------------------------------------------------------------------------
