diff options
author | David Robillard <d@drobilla.net> | 2008-08-12 00:20:16 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2008-08-12 00:20:16 +0000 |
commit | 102e899c331bd2ed9902467a077164e209c918f9 (patch) | |
tree | b7fe5ec873582cc8a0fc0862f9da045d12b2259a /vstgui/vstcontrols.cpp | |
parent | 2b679f152e1c3104ac178b6c78ac0b1edf954ff6 (diff) | |
download | mda.lv2-102e899c331bd2ed9902467a077164e209c918f9.tar.gz mda.lv2-102e899c331bd2ed9902467a077164e209c918f9.tar.bz2 mda.lv2-102e899c331bd2ed9902467a077164e209c918f9.zip |
VSTUI X11 port and embeddable GTK wrapper.
Build mdaSpecMeter and GUI.
git-svn-id: http://svn.drobilla.net/lad/mda-lv2@1340 a436a847-0d15-0410-975c-d299462d15a1
Diffstat (limited to 'vstgui/vstcontrols.cpp')
-rw-r--r-- | vstgui/vstcontrols.cpp | 3651 |
1 files changed, 3651 insertions, 0 deletions
diff --git a/vstgui/vstcontrols.cpp b/vstgui/vstcontrols.cpp new file mode 100644 index 0000000..0b0800c --- /dev/null +++ b/vstgui/vstcontrols.cpp @@ -0,0 +1,3651 @@ +/* ---------------------------------------------------------------------------- + * VSTGUI for X11/LV2/PNG + * Author: Dave Robillard + * Released under the revised BSD license, as below + * ---------------------------------------------------------------------------- + * + * Based on: + * ---------------------------------------------------------------------------- + * VSTGUIL: Graphical User Interface Framework for VST plugins on LINUX + * Version: 0.1, Date: 2007/01/21 + * Author: kRAkEn/gORe + * + * Which was based on: + * ---------------------------------------------------------------------------- + * VSTGUI: Graphical User Interface Framework for VST plugins + * Version 3.0 $Date: 2005/08/12 12:45:00 $ + * ---------------------------------------------------------------------------- + * 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" + +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) +{ + delta = 500; + + 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) +{ +// DBG ("COnOffButton::draw"); + + 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) +{ +// DBG ("CKnob::draw"); + + 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) +{ +// DBG ("CParamDisplay::draw"); + + 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)) + { + if (style & k3DIn) + pContext->setFrameColor (backColor); + else + pContext->setFrameColor (frameColor); + CPoint p; + pContext->moveTo (p (size.left, size.bottom)); + pContext->lineTo (p (size.left, size.top)); + pContext->lineTo (p (size.right + 1, size.top)); + + if (style & k3DIn) + pContext->setFrameColor (frameColor); + else + pContext->setFrameColor (backColor); + pContext->moveTo (p (size.right, size.top + 1)); + pContext->lineTo (p (size.right, size.bottom)); + pContext->lineTo (p (size.left, size.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 newFontID) +{ + // to force the redraw + if (fontID != newFontID) + setDirty (); + fontID = newFontID; +} + +//------------------------------------------------------------------------ +void CParamDisplay::setTxtFace (CTxtFace newTxtFace) +{ + // to force the redraw + if (txtFace != newTxtFace) + setDirty (); + txtFace = newTxtFace; +} + +//------------------------------------------------------------------------ +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; + 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) +{ +// DBG ("CTextLabel::draw"); + + 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) +{ + this->listener = listener; + this->tag = tag; + + if (txt) + strcpy (text, txt); + else + strcpy (text, ""); + 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) +{ +// DBG ("CTextEdit::draw"); + + if (platformControl) + { + 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); + } + } +} + +//------------------------------------------------------------------------ +// #include <Xm/Text.h> +extern XFontStruct *gFontStructs[]; + +//------------------------------------------------------------------------ +void CTextEdit::takeFocus (CDrawContext *pContext) +{ + bWasReturnPressed = false; + +/* + // 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 = gFontStructs [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); +*/ +} + +//------------------------------------------------------------------------ +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); + + char *pNewText = XmTextGetString ((Widget)platformControl); + strcpy (text, pNewText); + XtFree (pNewText); + + XtUnmanageChild ((Widget)platformControl); + XtDestroyWidget ((Widget)platformControl); + + 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 () +{ + backgroundColor = kGreyCColor; + selectionColor = kBlueCColor; + textColor = kBlackCColor; + hiliteTextColor = kWhiteCColor; + disableTextColor = kWhiteCColor; + + font = kNormalFontSmall; +} + +//------------------------------------------------------------------------ +COptionMenuScheme::~COptionMenuScheme () +{ +} + +//------------------------------------------------------------------------ +void COptionMenuScheme::getItemSize (const char* text, CDrawContext* pContext, CPoint& size) +{ + if (!strcmp (text, kMenuSeparator)) // separator + { + // 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 :( + } + else + { + pContext->setFont (font); + size.h = pContext->getStringWidth (text) + 18; + size.v = 18; + } +} + +//------------------------------------------------------------------------ +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); +} + + +//------------------------------------------------------------------------ +// 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 (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) +{ +// DBG ("COptionMenu::draw"); + + 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); + } +} + +//------------------------------------------------------------------------ +/* +#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); +} +*/ + +//------------------------------------------------------------------------ +COptionMenu *COptionMenu::getLastItemMenu (long &idxInMenu) const +{ + idxInMenu = lastMenu ? (long)lastMenu->getValue (): -1; + return lastMenu; +} + +//------------------------------------------------------------------------ +COptionMenu *COptionMenu::getItemMenu (long idx, long &idxInMenu, long &offsetIdx) +{ + 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 (); +} + +//------------------------------------------------------------------------ +void *COptionMenu::appendItems (long &offsetIdx) +{ + //bool multipleCheck = style & (kMultipleCheckStyle & ~kCheckStyle); + return NULL; +} + +//------------------------------------------------------------------------ +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; + + 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); + getFrame ()->setFocusView (0); + endEdit(); +*/ +} + +//------------------------------------------------------------------------ +void COptionMenu::looseFocus (CDrawContext *pContext) +{ + if (platformControl == 0) + return; + +/* + for (long i = 0; i < nbEntries; i++) + if (itemWidget[i]) + XtDestroyWidget ((Widget)itemWidget[i]); + + if (platformControl) + { + XtUnmanageChild ((Widget)platformControl); + XtDestroyWidget ((Widget)platformControl); + } +*/ + platformControl = 0; +} + +//------------------------------------------------------------------------ +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 ()->getBackBuffer ()); + + setValue (currentIndex); + + if (listener) + listener->valueChanged (pContext, this); + delete pContext; +} + + +//------------------------------------------------------------------------ +// 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) +{ +// DBG ("CAnimKnob::draw"); + + 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) +{ +// DBG ("CVerticalSwitch::draw"); + + 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), + iMaxPositions (iMaxPositions), + heightOfOneImage (heightOfOneImage) +{ + setDefaultValue (0.f); +} + +//------------------------------------------------------------------------ +CHorizontalSwitch::~CHorizontalSwitch () +{} + +//------------------------------------------------------------------------ +void CHorizontalSwitch::draw (CDrawContext *pContext) +{ +// DBG ("CHorizontalSwitch::draw"); + + 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) +{ +// DBG ("CRockerSwitch::draw"); + + 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) +{ + // DBG ("CMovieBitmap::draw"); + + 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) +{ + // DBG ("CMovieButton::draw"); + + 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) +{ + // DBG ("CAutoAnimation::draw"); + + 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; + + pOScreen = 0; // @TODO - faking offscreen +// pOScreen = new COffscreenContext (getFrame (), widthControl, heightControl, kBlackCColor); + + return CControl::attached (parent); +} + +//----------------------------------------------------------------------------- +bool CSlider::removed (CView *parent) +{ + if (pOScreen) + { + delete pOScreen; + pOScreen = 0; + } + return CControl::removed (parent); +} + +//------------------------------------------------------------------------ +void CSlider::draw (CDrawContext *pContext) +{ + // DBG ("CSlider::draw"); + + CDrawContext* drawContext = pOScreen ? pOScreen : pContext; + + if (pOScreen && bTransparencyEnabled) + pOScreen->copyTo (pContext, size); + + 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) +{ +// DBG ("CSpecialDigit::draw"); + + 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) +{ + // DBG ("CKickButton::draw"); + + 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 (); +} + + +//------------------------------------------------------------------------ +// 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) +{ + // DBG ("CSplashScreen::draw"); + + 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) + { + if (getFrame () && getFrame ()->setModalView (this)) + { + keepSize = size; + size = toDisplay; + mouseableArea = size; +// draw (pContext); + 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 () +{ + setDirty (); + value = 0.f; + + size = keepSize; + if (getFrame ()) + { + if (getFrame ()->getModalView () == this) + { + getFrame ()->setModalView (NULL); + getFrame ()->redraw (); + } + } +} + +//------------------------------------------------------------------------ +// 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) +{ + bUseOffscreen = false; + + setDecreaseStepValue (0.1f); + + 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; // @TODO - faking offscreen + bUseOffscreen = false; +} + +//----------------------------------------------------------------------------- +bool CVuMeter::removed (CView *parent) +{ + if (pOScreen) + { + delete pOScreen; + pOScreen = 0; + } + return CControl::removed (parent); +} + +//------------------------------------------------------------------------ +void CVuMeter::draw (CDrawContext *_pContext) +{ + // DBG ("CVuMeter::draw"); + + 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); +} + +} // namespace VSTGUI + +//------------------------------------------------------------------------ +// END. +//------------------------------------------------------------------------ |