summaryrefslogtreecommitdiff
path: root/vendor/vstsdk2.4/vstgui.sf/vstgui/vstcontrols.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/vstsdk2.4/vstgui.sf/vstgui/vstcontrols.cpp')
-rw-r--r--vendor/vstsdk2.4/vstgui.sf/vstgui/vstcontrols.cpp5516
1 files changed, 5516 insertions, 0 deletions
diff --git a/vendor/vstsdk2.4/vstgui.sf/vstgui/vstcontrols.cpp b/vendor/vstsdk2.4/vstgui.sf/vstgui/vstcontrols.cpp
new file mode 100644
index 0000000..ba164d9
--- /dev/null
+++ b/vendor/vstsdk2.4/vstgui.sf/vstgui/vstcontrols.cpp
@@ -0,0 +1,5516 @@
+//-----------------------------------------------------------------------------
+// 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 = 1.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
+}
+
+//------------------------------------------------------------------------
+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.
+//------------------------------------------------------------------------