diff options
| author | pepper <peppersclothescult@gmail.com> | 2015-01-10 21:37:24 -0800 |
|---|---|---|
| committer | pepper <peppersclothescult@gmail.com> | 2015-01-10 21:37:24 -0800 |
| commit | 58f8437f4b8b741ddc8e7bcde21bf983cc618430 (patch) | |
| tree | bfd0a9d601274fe56de15a4eaeb0998f9481419d /vendor/vstsdk2.4/vstgui.sf/vstgui/vstcontrols.cpp | |
| parent | 36773a28ece1641a2d827a29869cdd4c38e87925 (diff) | |
Diffstat (limited to 'vendor/vstsdk2.4/vstgui.sf/vstgui/vstcontrols.cpp')
| -rw-r--r-- | vendor/vstsdk2.4/vstgui.sf/vstgui/vstcontrols.cpp | 5516 |
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. +//------------------------------------------------------------------------ |
