//----------------------------------------------------------------------------- // VST Plug-Ins SDK // VSTGUI: Graphical User Interface Framework for VST plugins : // Standard Control Objects // // Version 3.0 $Date: 2006/02/09 10:18:20 $ // // Added new objects : Michael Schmidt 08.97 // Added new objects : Yvan Grabit 01.98 // Added BeOS version : Georges-Edouard Berenger 05.99 // Added MacOSX version : Arne Scheffler 02.03 // //----------------------------------------------------------------------------- // VSTGUI LICENSE // © 2004, Steinberg Media Technologies, All Rights Reserved //----------------------------------------------------------------------------- // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // * Neither the name of the Steinberg Media Technologies nor the names of its // contributors may be used to endorse or promote products derived from this // software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. // IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE // OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED // OF THE POSSIBILITY OF SUCH DAMAGE. //----------------------------------------------------------------------------- #include #include #include #include #ifndef __vstcontrols__ #include "vstcontrols.h" #endif #include "vstkeycode.h" #if (__MACH__ && __MWERKS__) #ifndef cosf #define cosf (float)cos #endif #ifndef sinf #define sinf (float)sin #endif #endif BEGIN_NAMESPACE_VSTGUI // some external variables (vstgui.cpp) extern long gStandardFontSize []; extern const char *gStandardFontName []; //------------------------------------------------------------------------ // CControl //------------------------------------------------------------------------ /*! @class CControl This object manages the tag identification and the value of a control object. Note: Since version 2.1, when an object uses the transparency for its background and draws on it (tranparency area) or the transparency area changes during different draws (CMovieBitmap ,...), the background will be false (not updated), you have to rewrite the draw function in order to redraw the background and then call the draw of the object. */ CControl::CControl (const CRect &size, CControlListener *listener, long tag, CBitmap *pBackground) : CView (size), listener (listener), tag (tag), oldValue (1), defaultValue (0.5f), value (0), vmin (0), vmax (1.f), wheelInc (0.1f), lastTicks (-1) { #if WINDOWS delta = GetDoubleClickTime (); #elif MAC delta = GetDblTime (); #else delta = 500; #endif if (delta < 250) delta = 250; setTransparency (false); setMouseEnabled (true); backOffset (0 ,0); setBackground (pBackground); } //------------------------------------------------------------------------ CControl::~CControl () { } //------------------------------------------------------------------------ void CControl::beginEdit () { // begin of edit parameter getFrame ()->setFocusView(this); getFrame ()->beginEdit (tag); } //------------------------------------------------------------------------ void CControl::endEdit () { // end of edit parameter getFrame ()->endEdit (tag); } //------------------------------------------------------------------------ bool CControl::isDirty () const { if (oldValue != value || CView::isDirty ()) return true; return false; } //------------------------------------------------------------------------ void CControl::setDirty (const bool val) { CView::setDirty (val); if (val) { if (value != -1.f) oldValue = -1.f; else oldValue = 0.f; } else oldValue = value; } //------------------------------------------------------------------------ void CControl::setBackOffset (CPoint &offset) { backOffset = offset; } //----------------------------------------------------------------------------- void CControl::copyBackOffset () { backOffset (size.left, size.top); } //------------------------------------------------------------------------ void CControl::bounceValue () { if (value > vmax) value = vmax; else if (value < vmin) value = vmin; } //----------------------------------------------------------------------------- bool CControl::checkDefaultValue (CDrawContext *pContext, long button) { if (button == (kControl|kLButton)) { // begin of edit parameter beginEdit (); value = getDefaultValue (); if (isDirty () && listener) listener->valueChanged (pContext, this); // end of edit parameter endEdit (); return true; } return false; } //----------------------------------------------------------------------------- bool CControl::isDoubleClick () { long ticks = getFrame ()->getTicks (); if (lastTicks <= 0) { lastTicks = ticks; return false; } if (lastTicks + delta > ticks) lastTicks = 0; else { lastTicks = ticks; return false; } return true; } //------------------------------------------------------------------------ // COnOffButton //------------------------------------------------------------------------ /*! @class COnOffButton Define a button with 2 positions. The pixmap includes the 2 subpixmaps (i.e the rectangle used for the display of this button is half-height of the pixmap). When its value changes, the listener is called. */ COnOffButton::COnOffButton (const CRect &size, CControlListener *listener, long tag, CBitmap *background, long style) : CControl (size, listener, tag, background) , style (style) {} //------------------------------------------------------------------------ COnOffButton::~COnOffButton () {} //------------------------------------------------------------------------ void COnOffButton::draw (CDrawContext *pContext) { CCoord off; if (value && pBackground) off = pBackground->getHeight () / 2; else off = 0; if (pBackground) { if (bTransparencyEnabled) pBackground->drawTransparent (pContext, size, CPoint (0, off)); else pBackground->draw (pContext, size, CPoint (0, off)); } setDirty (false); } //------------------------------------------------------------------------ void COnOffButton::mouse (CDrawContext *pContext, CPoint &where, long button) { if (!bMouseEnabled) return; if (button == -1) button = pContext->getMouseButtons (); if (!(button & kLButton)) return; if (listener && button & (kAlt | kShift | kControl | kApple)) { if (listener->controlModifierClicked (pContext, this, button) != 0) return; } value = ((long)value) ? 0.f : 1.f; if (listener && style == kPostListenerUpdate) { // begin of edit parameter beginEdit (); listener->valueChanged (pContext, this); // end of edit parameter endEdit (); } doIdleStuff (); if (listener && style == kPreListenerUpdate) { // begin of edit parameter beginEdit (); listener->valueChanged (pContext, this); // end of edit parameter endEdit (); } } //------------------------------------------------------------------------ // CKnob //------------------------------------------------------------------------ /*! @class CKnob Define a knob with a given background and foreground handle. The handle describes a circle over the background (between -45deg and +225deg). By clicking Alt+Left Mouse the default value is used. By clicking Alt+Left Mouse the value changes with a vertical move (version 2.1) */ CKnob::CKnob (const CRect &size, CControlListener *listener, long tag, CBitmap *background, CBitmap *handle, const CPoint &offset) : CControl (size, listener, tag, background), offset (offset), pHandle (handle) { if (pHandle) { pHandle->remember (); inset = (long)((float)pHandle->getWidth () / 2.f + 2.5f); } else inset = 3; colorShadowHandle = kGreyCColor; colorHandle = kWhiteCColor; radius = (float)(size.right - size.left) / 2.f; rangeAngle = 1.f; setStartAngle ((float)(5.f * kPI / 4.f)); setRangeAngle ((float)(-3.f * kPI / 2.f)); zoomFactor = 1.5f; setWantsFocus (true); } //------------------------------------------------------------------------ CKnob::~CKnob () { if (pHandle) pHandle->forget (); } //------------------------------------------------------------------------ void CKnob::draw (CDrawContext *pContext) { if (pBackground) { if (bTransparencyEnabled) pBackground->drawTransparent (pContext, size, offset); else pBackground->draw (pContext, size, offset); } drawHandle (pContext); setDirty (false); } //------------------------------------------------------------------------ void CKnob::drawHandle (CDrawContext *pContext) { CPoint where; valueToPoint (where); if (pHandle) { long width = (long)pHandle->getWidth (); long height = (long)pHandle->getHeight (); where.offset (size.left - width / 2, size.top - height / 2); CRect handleSize (0, 0, width, height); handleSize.offset (where.h, where.v); pHandle->drawTransparent (pContext, handleSize); } else { CPoint origin (size.width () / 2, size.height () / 2); where.offset (size.left - 1, size.top); origin.offset (size.left - 1, size.top); pContext->setFrameColor (colorShadowHandle); pContext->moveTo (where); pContext->lineTo (origin); where.offset (1, -1); origin.offset (1, -1); pContext->setFrameColor (colorHandle); pContext->moveTo (where); pContext->lineTo (origin); } } //------------------------------------------------------------------------ void CKnob::mouse (CDrawContext *pContext, CPoint &where, long button) { if (!bMouseEnabled) return; if (button == -1) button = pContext->getMouseButtons (); if (!(button & kLButton)) return; if (listener && button & (kAlt | kShift | kControl | kApple)) { if (listener->controlModifierClicked (pContext, this, button) != 0) return; } // check if default value wanted if (checkDefaultValue (pContext, button)) return; float old = oldValue; CPoint firstPoint; bool modeLinear = false; float fEntryState = value; float middle = (vmax - vmin) * 0.5f; float range = 200.f; float coef = (vmax - vmin) / range; long oldButton = button; long mode = kCircularMode; long newMode = getFrame ()->getKnobMode (); if (kLinearMode == newMode) { if (!(button & kAlt)) mode = newMode; } else if (button & kAlt) mode = kLinearMode; if (mode == kLinearMode && (button & kLButton)) { if (button & kShift) range *= zoomFactor; firstPoint = where; modeLinear = true; coef = (vmax - vmin) / range; } else { CPoint where2 (where); where2.offset (-size.left, -size.top); old = valueFromPoint (where2); } CPoint oldWhere (-1, -1); // begin of edit parameter beginEdit (); do { button = pContext->getMouseButtons (); if (where != oldWhere) { oldWhere = where; if (modeLinear) { CCoord diff = (firstPoint.v - where.v) + (where.h - firstPoint.h); if (button != oldButton) { range = 200.f; if (button & kShift) range *= zoomFactor; float coef2 = (vmax - vmin) / range; fEntryState += diff * (coef - coef2); coef = coef2; oldButton = button; } value = fEntryState + diff * coef; bounceValue (); } else { where.offset (-size.left, -size.top); value = valueFromPoint (where); if (old - value > middle) value = vmax; else if (value - old > middle) value = vmin; else old = value; } if (isDirty () && listener) listener->valueChanged (pContext, this); } getMouseLocation (pContext, where); doIdleStuff (); } while (button & kLButton); // end of edit parameter endEdit (); } //------------------------------------------------------------------------ bool CKnob::onWheel (CDrawContext *pContext, const CPoint &where, float distance) { if (!bMouseEnabled) return false; long buttons = pContext->getMouseButtons (); if (buttons & kShift) value += 0.1f * distance * wheelInc; else value += distance * wheelInc; bounceValue (); if (isDirty () && listener) { // begin of edit parameter beginEdit (); listener->valueChanged (pContext, this); // end of edit parameter endEdit (); } return true; } //------------------------------------------------------------------------ long CKnob::onKeyDown (VstKeyCode& keyCode) { switch (keyCode.virt) { case VKEY_UP : case VKEY_RIGHT : case VKEY_DOWN : case VKEY_LEFT : { float distance = 1.f; if (keyCode.virt == VKEY_DOWN || keyCode.virt == VKEY_LEFT) distance = -distance; if (keyCode.modifier & MODIFIER_SHIFT) value += 0.1f * distance * wheelInc; else value += distance * wheelInc; bounceValue (); if (isDirty () && listener) { // begin of edit parameter beginEdit (); listener->valueChanged (0, this); // end of edit parameter endEdit (); } } return 1; } return -1; } //------------------------------------------------------------------------ void CKnob::setStartAngle (float val) { startAngle = val; compute (); } //------------------------------------------------------------------------ void CKnob::setRangeAngle (float val) { rangeAngle = val; compute (); } //------------------------------------------------------------------------ void CKnob::compute () { aCoef = (vmax - vmin) / rangeAngle; bCoef = vmin - aCoef * startAngle; halfAngle = ((float)k2PI - fabsf (rangeAngle)) * 0.5f; setDirty (); } //------------------------------------------------------------------------ void CKnob::valueToPoint (CPoint &point) const { float alpha = (value - bCoef) / aCoef; point.h = (long)(radius + cosf (alpha) * (radius - inset) + 0.5f); point.v = (long)(radius - sinf (alpha) * (radius - inset) + 0.5f); } //------------------------------------------------------------------------ float CKnob::valueFromPoint (CPoint &point) const { float v; float alpha = (float)atan2 (radius - point.v, point.h - radius); if (alpha < 0.f) alpha += (float)k2PI; float alpha2 = alpha - startAngle; if (rangeAngle < 0) { alpha2 -= rangeAngle; float alpha3 = alpha2; if (alpha3 < 0.f) alpha3 += (float)k2PI; else if (alpha3 > k2PI) alpha3 -= (float)k2PI; if (alpha3 > halfAngle - rangeAngle) v = vmax; else if (alpha3 > -rangeAngle) v = vmin; else { if (alpha2 > halfAngle - rangeAngle) alpha2 -= (float)k2PI; else if (alpha2 < -halfAngle) alpha2 += (float)k2PI; v = aCoef * alpha2 + vmax; } } else { float alpha3 = alpha2; if (alpha3 < 0.f) alpha3 += (float)k2PI; else if (alpha3 > k2PI) alpha3 -= (float)k2PI; if (alpha3 > rangeAngle + halfAngle) v = vmin; else if (alpha3 > rangeAngle) v = vmax; else { if (alpha2 > rangeAngle + halfAngle) alpha2 -= (float)k2PI; else if (alpha2 < -halfAngle) alpha2 += (float)k2PI; v = aCoef * alpha2 + vmin; } } return v; } //------------------------------------------------------------------------ void CKnob::setColorShadowHandle (CColor color) { colorShadowHandle = color; setDirty (); } //------------------------------------------------------------------------ void CKnob::setColorHandle (CColor color) { colorHandle = color; setDirty (); } //------------------------------------------------------------------------ void CKnob::setHandleBitmap (CBitmap *bitmap) { if (pHandle) { pHandle->forget (); pHandle = 0; } if (bitmap) { pHandle = bitmap; pHandle->remember (); inset = (long)((float)pHandle->getWidth () / 2.f + 2.5f); } } //------------------------------------------------------------------------ // CParamDisplay //------------------------------------------------------------------------ /*! @class CParamDisplay Define a rectangle view where a text-value can be displayed with a given font and color. The user can specify its convert function (from float to char) by default the string format is "%2.2f". The text-value is centered in the given rect. */ CParamDisplay::CParamDisplay (const CRect &size, CBitmap *background, const long style) : CControl (size, 0, 0, background), stringConvert (0), stringConvert2 (0), string2FloatConvert (0), horiTxtAlign (kCenterText), style (style), bTextTransparencyEnabled (true) { backOffset (0, 0); fontID = kNormalFont; txtFace = kNormalFace; fontColor = kWhiteCColor; backColor = kBlackCColor; frameColor = kBlackCColor; shadowColor = kRedCColor; userData = 0; if (style & kNoDrawStyle) setDirty (false); } //------------------------------------------------------------------------ CParamDisplay::~CParamDisplay () {} //------------------------------------------------------------------------ void CParamDisplay::setStyle (long val) { if (style != val) { style = val; setDirty (); } } //------------------------------------------------------------------------ void CParamDisplay::draw (CDrawContext *pContext) { char string[256]; string[0] = 0; if (stringConvert2) stringConvert2 (value, string, userData); else if (stringConvert) stringConvert (value, string); else sprintf (string, "%2.2f", value); drawText (pContext, string); } //------------------------------------------------------------------------ void CParamDisplay::drawText (CDrawContext *pContext, char *string, CBitmap *newBack) { setDirty (false); if (style & kNoDrawStyle) return; // draw the background if (newBack) { if (bTransparencyEnabled) newBack->drawTransparent (pContext, size, backOffset); else newBack->draw (pContext, size, backOffset); } else if (pBackground) { if (bTransparencyEnabled) pBackground->drawTransparent (pContext, size, backOffset); else pBackground->draw (pContext, size, backOffset); } else { if (!bTransparencyEnabled) { pContext->setFillColor (backColor); pContext->fillRect (size); if (!(style & (k3DIn|k3DOut|kNoFrame))) { pContext->setFrameColor (frameColor); pContext->drawRect (size); } } } // draw the frame for the 3D effect if (style & (k3DIn|k3DOut)) { CRect r (size); r.right--; r.top++; r.bottom--; pContext->setLineWidth (1); if (style & k3DIn) pContext->setFrameColor (backColor); else pContext->setFrameColor (frameColor); CPoint p; pContext->moveTo (p (r.left, r.bottom)); pContext->lineTo (p (r.left, r.top)); pContext->lineTo (p (r.right, r.top)); if (style & k3DIn) pContext->setFrameColor (frameColor); else pContext->setFrameColor (backColor); pContext->moveTo (p (r.right, r.top)); pContext->lineTo (p (r.right, r.bottom)); pContext->lineTo (p (r.left, r.bottom)); } if (!(style & kNoTextStyle) && string) { CRect oldClip; pContext->getClipRect (oldClip); CRect newClip (size); newClip.bound (oldClip); pContext->setClipRect (newClip); pContext->setFont (fontID, 0, txtFace); // draw darker text (as shadow) if (style & kShadowText) { CRect newSize (size); newSize.offset (1, 1); pContext->setFontColor (shadowColor); pContext->drawString (string, newSize, !bTextTransparencyEnabled, horiTxtAlign); } pContext->setFontColor (fontColor); pContext->drawString (string, size, !bTextTransparencyEnabled, horiTxtAlign); pContext->setClipRect (oldClip); } } //------------------------------------------------------------------------ void CParamDisplay::setFont (CFont fontID) { // to force the redraw if (this->fontID != fontID) setDirty (); this->fontID = fontID; } //------------------------------------------------------------------------ void CParamDisplay::setTxtFace (CTxtFace txtFace) { // to force the redraw if (this->txtFace != txtFace) setDirty (); this->txtFace = txtFace; } //------------------------------------------------------------------------ void CParamDisplay::setFontColor (CColor color) { // to force the redraw if (fontColor != color) setDirty (); fontColor = color; } //------------------------------------------------------------------------ void CParamDisplay::setBackColor (CColor color) { // to force the redraw if (backColor != color) setDirty (); backColor = color; } //------------------------------------------------------------------------ void CParamDisplay::setFrameColor (CColor color) { // to force the redraw if (frameColor != color) setDirty (); frameColor = color; } //------------------------------------------------------------------------ void CParamDisplay::setShadowColor (CColor color) { // to force the redraw if (shadowColor != color) setDirty (); shadowColor = color; } //------------------------------------------------------------------------ void CParamDisplay::setHoriAlign (CHoriTxtAlign hAlign) { // to force the redraw if (horiTxtAlign != hAlign) setDirty (); horiTxtAlign = hAlign; } //------------------------------------------------------------------------ void CParamDisplay::setStringConvert (void (*convert) (float value, char *string)) { stringConvert = convert; } //------------------------------------------------------------------------ void CParamDisplay::setStringConvert (void (*convert) (float value, char *string, void *userDta), void *userData) { stringConvert2 = convert; this->userData = userData; } //------------------------------------------------------------------------ void CParamDisplay::setString2FloatConvert (void (*convert) (char *string, float &output)) { string2FloatConvert = convert; } //------------------------------------------------------------------------ // CTextLabel //------------------------------------------------------------------------ /*! @class CTextLabel */ CTextLabel::CTextLabel (const CRect& size, const char* txt, CBitmap* background, const long style) : CParamDisplay (size, background, style) , text (0) { setText (txt); } //------------------------------------------------------------------------ CTextLabel::~CTextLabel () { freeText (); } //------------------------------------------------------------------------ void CTextLabel::freeText () { if (text) free (text); text = 0; } //------------------------------------------------------------------------ void CTextLabel::setText (const char* txt) { freeText (); if (txt) { text = (char*)malloc (strlen (txt)+1); strcpy (text, txt); } } //------------------------------------------------------------------------ const char* CTextLabel::getText () const { return text; } //------------------------------------------------------------------------ void CTextLabel::draw (CDrawContext *pContext) { drawText (pContext, text); setDirty (false); } //------------------------------------------------------------------------ // CTextEdit //------------------------------------------------------------------------ /*! @class CTextEdit Define a rectangle view where a text-value can be displayed and edited with a given font and color. The user can specify its convert function (from char to char). The text-value is centered in the given rect. A pixmap can be used as background. */ CTextEdit::CTextEdit (const CRect &size, CControlListener *listener, long tag, const char *txt, CBitmap *background, const long style) : CParamDisplay (size, background, style), platformFontColor (0), platformControl (0), platformFont (0), editConvert (0), editConvert2 (0) { #if (MAC && !MACX) text_edit = 0; #endif this->listener = listener; this->tag = tag; if (txt) strcpy (text, txt); else strcpy (text, ""); #if MAC // remember our VST plugin's resource map ID (it should be the current one at this moment) pluginResID = CurResFile(); #endif #if QUARTZ textControl = 0; #endif setWantsFocus (true); } //------------------------------------------------------------------------ CTextEdit::~CTextEdit () {} //------------------------------------------------------------------------ void CTextEdit::setText (char *txt) { if (txt) { if (strcmp (text, txt)) { strcpy (text, txt); // to force the redraw setDirty (); } } else { if (strcmp (text, "")) { strcpy (text, ""); // to force the redraw setDirty (); } } } //------------------------------------------------------------------------ void CTextEdit::getText (char *txt) const { if (txt) strcpy (txt, text); } //------------------------------------------------------------------------ void CTextEdit::draw (CDrawContext *pContext) { if (platformControl) { #if MACX #if QUARTZ if (textControl) { HIViewSetNeedsDisplay (textControl, true); } else #endif TXNDraw ((TXNObject)platformControl, NULL); #endif setDirty (false); return; } char string[256]; string[0] = 0; if (editConvert2) editConvert2 (text, string, userData); else if (editConvert) editConvert (text, string); // Allow to display strings through the stringConvert // callbacks inherited from CParamDisplay else if (stringConvert2) { string[0] = 0; stringConvert2 (value, string, userData); strcpy(text, string); } else if (stringConvert) { string[0] = 0; stringConvert (value, string); strcpy(text, string); } else sprintf (string, "%s", text); drawText (pContext, string); setDirty (false); } //------------------------------------------------------------------------ void CTextEdit::mouse (CDrawContext *pContext, CPoint &where, long button) { if (!bMouseEnabled) return; if (button == -1) button = pContext->getMouseButtons (); if (listener && button & (kAlt | kShift | kControl | kApple)) { if (listener->controlModifierClicked (pContext, this, button) != 0) return; } if (button & kLButton) { if (getFrame ()->getFocusView () != this) { if (style & kDoubleClickStyle) if (!isDoubleClick ()) return; beginEdit(); takeFocus (pContext); } } } //------------------------------------------------------------------------ #if WINDOWS #define WIN32_LEAN_AND_MEAN 1 #include END_NAMESPACE_VSTGUI #if PLUGGUI extern HINSTANCE ghInst; inline HINSTANCE GetInstance () { return ghInst; } #else extern void* hInstance; inline HINSTANCE GetInstance () { return (HINSTANCE)hInstance; } #endif BEGIN_NAMESPACE_VSTGUI extern long standardFontSize []; extern const char *standardFontName []; #ifdef STRICT #define WINDOWSPROC WNDPROC #else #define WINDOWSPROC FARPROC #endif static WINDOWSPROC oldWndProcEdit; LONG_PTR WINAPI WindowProcEdit (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); LONG_PTR WINAPI WindowProcEdit (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_GETDLGCODE : { long flags = DLGC_WANTALLKEYS; return flags; } case WM_KEYDOWN: { if (wParam == VK_RETURN) { CTextEdit *textEdit = (CTextEdit*) GetWindowLongPtr (hwnd, GWLP_USERDATA); if (textEdit) { textEdit->bWasReturnPressed = true; textEdit->looseFocus (); } } } break; case WM_KILLFOCUS: { CTextEdit *textEdit = (CTextEdit*) GetWindowLongPtr (hwnd, GWLP_USERDATA); if (textEdit) textEdit->looseFocus (); } break; } return CallWindowProc (oldWndProcEdit, hwnd, message, wParam, lParam); } //------------------------------------------------------------------------ #elif MOTIF #include extern XFontStruct *gFontStructs[]; #endif //------------------------------------------------------------------------ #if BEOS #include #include class BeTextView : public BTextView { public: BeTextView (CTextEdit* cTextEdit, BRect frame, const char *name, BRect textRect); void MakeFocus (bool focusState = true); void KeyDown (const char *bytes, int32 numBytes); private: CTextEdit *cTextEdit; }; //------------------------------------------------------------------------ BeTextView::BeTextView (CTextEdit* cTextEdit, BRect frame, const char *name, BRect textRect) : BTextView (frame, name, textRect, B_FOLLOW_NONE), cTextEdit (cTextEdit) {} //------------------------------------------------------------------------ void BeTextView::MakeFocus (bool focusState) { BTextView::MakeFocus (focusState); if (!focusState && cTextEdit) cTextEdit->looseFocus (); } //------------------------------------------------------------------------ void BeTextView::KeyDown (const char *bytes, int32 numBytes) { if (cTextEdit && (bytes[0] == B_RETURN || bytes[0] == B_TAB)) cTextEdit->looseFocus (); else BTextView::KeyDown (bytes, numBytes); } #endif #if MACX static EventHandlerRef gTextEditEventHandler = 0; static bool gTextEditCanceled = false; pascal OSStatus CarbonEventsTextControlProc (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData); pascal OSStatus CarbonEventsTextControlProc (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData) { OSStatus result = eventNotHandledErr; UInt32 eventClass = GetEventClass (inEvent); UInt32 eventKind = GetEventKind (inEvent); CTextEdit* textEdit = (CTextEdit*)inUserData; switch (eventClass) { case kEventClassKeyboard: { switch (eventKind) { case kEventRawKeyDown: case kEventRawKeyRepeat: { char macCharCode; UInt32 keyCode; UInt32 modifiers; GetEventParameter (inEvent, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof (char), NULL, &macCharCode); GetEventParameter (inEvent, kEventParamKeyCode, typeUInt32, NULL, sizeof (UInt32), NULL, &keyCode); GetEventParameter (inEvent, kEventParamKeyModifiers, typeUInt32, NULL, sizeof (UInt32), NULL, &modifiers); if (macCharCode == 13 || macCharCode == 3 || macCharCode == 27) { if (macCharCode == 27) gTextEditCanceled = true; else textEdit->bWasReturnPressed = true; WindowRef window = (WindowRef) (textEdit->getFrame ()->getSystemWindow()); GrafPtr savedPort; bool portChanged = window ? QDSwapPort (GetWindowPort (window), &savedPort) : false; // remember the current resource map ID short currentResID = CurResFile(); short vstResFileID = textEdit->pluginResID; // if it's different (and if it's valid), set the current resource map ID to our plugin's resource map if ( (vstResFileID != currentResID) && (vstResFileID > 0) ) UseResFile(vstResFileID); textEdit->looseFocus (); // revert the window port, if we changed it if (portChanged) QDSwapPort (savedPort, NULL); // revert the current resource map, if we changed it if ( (currentResID > 0) && (vstResFileID != currentResID) && (vstResFileID > 0) ) UseResFile(currentResID); result = noErr; } #if QUARTZ else if (textEdit->textControl) break; #endif else if (modifiers & cmdKey) { result = noErr; TXNObject text_edit = (TXNObject) (textEdit->platformControl); switch (toupper(macCharCode)) { // copy case 'C': if (!TXNIsSelectionEmpty(text_edit)) { OSStatus scrapErr = ClearCurrentScrap(); scrapErr = TXNCopy(text_edit); result = noErr; } break; // cut case 'X': if (!TXNIsSelectionEmpty(text_edit)) { OSStatus scrapErr = ClearCurrentScrap(); scrapErr = TXNCut(text_edit); result = noErr; } break; // paste case 'V': TXNPaste(text_edit); result = noErr; break; // select all case 'A': TXNSelectAll(text_edit); break; default: break; } } else { EventRecord eventRecord; if (ConvertEventRefToEventRecord (inEvent, &eventRecord)) TXNKeyDown ((TXNObject)textEdit->platformControl, &eventRecord); result = noErr; } break; } } break; } case kEventClassMouse: { switch (eventKind) { case kEventMouseDown: case kEventMouseUp: { WindowRef window; GetEventParameter (inEvent, kEventParamWindowRef, typeWindowRef, NULL, sizeof (WindowRef), NULL, &window); HIPoint p; GetEventParameter (inEvent, kEventParamMouseLocation, typeHIPoint, NULL, sizeof (HIPoint), NULL, &p); Point point = {(short)p.y, (short)p.x}; QDGlobalToLocalPoint (GetWindowPort (window), &point); Rect rect; TXNGetViewRect ((TXNObject)textEdit->platformControl, &rect); if (PtInRect (point, &rect)) { EventRecord eventRecord; if (eventKind == kEventMouseDown && ConvertEventRefToEventRecord (inEvent, &eventRecord)) TXNClick ((TXNObject)textEdit->platformControl, &eventRecord); result = noErr; } break; } case kEventMouseMoved: { TXNAdjustCursor ((TXNObject)textEdit->platformControl, NULL); break; } } break; } case kEventClassWindow: { WindowRef window; if (GetEventParameter (inEvent, kEventParamDirectObject, typeWindowRef, NULL, sizeof (WindowRef), NULL, &window) != noErr) break; switch (eventKind) { case kEventWindowDeactivated: { result = CallNextEventHandler (inHandlerCallRef, inEvent); ClearKeyboardFocus (window); // set up the correct drawing port for the window GrafPtr savedPort; bool portChanged = QDSwapPort (GetWindowPort (window), &savedPort); // remember the current resource map ID short currentResID = CurResFile(); short vstResFileID = textEdit->pluginResID; // if it's different (and if it's valid), set the current resource map ID to our plugin's resource map if ( (vstResFileID != currentResID) && (vstResFileID > 0) ) UseResFile(vstResFileID); textEdit->looseFocus (); // revert the window port, if we changed it if (portChanged) QDSwapPort (savedPort, NULL); // revert the current resource map, if we changed it if ( (currentResID > 0) && (vstResFileID != currentResID) && (vstResFileID > 0) ) UseResFile(currentResID); break; } } break; } } return result; } #endif #if MAC && CALL_NOT_IN_CARBON #include #include #define ClearCurrentScrap ZeroScrap #endif //------------------------------------------------------------------------ void CTextEdit::takeFocus (CDrawContext *pContext) { bWasReturnPressed = false; #if WINDOWS || MACX // calculate offset for CViewContainers CRect rect (size); CView* parent = getParentView (); CRect vSize; while (parent) { if (parent->notify (this, kMsgCheckIfViewContainer) == kMessageNotified) { parent->getViewSize (vSize); rect.offset (vSize.left, vSize.top); } parent = parent->getParentView (); } if (pContext) rect.offset (pContext->offset.h, pContext->offset.v); #endif #if WINDOWS int wstyle = 0; if (horiTxtAlign == kLeftText) wstyle |= ES_LEFT; else if (horiTxtAlign == kRightText) wstyle |= ES_RIGHT; else wstyle |= ES_CENTER; wstyle |= WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL; platformControl = (void*)CreateWindow ( "EDIT", text, wstyle, rect.left, rect.top, rect.width ()/* + 1*/, rect.height ()/* + 1*/, (HWND)getFrame ()->getSystemWindow (), NULL, GetInstance (), 0); // get/set the current font LOGFONT logfont = {0}; long fontH = gStandardFontSize [fontID]; if (fontH > rect.height () - 2) fontH = rect.height () - 2; logfont.lfWeight = FW_NORMAL; logfont.lfHeight = -fontH; logfont.lfPitchAndFamily = VARIABLE_PITCH | FF_SWISS; strcpy (logfont.lfFaceName, gStandardFontName[fontID]); logfont.lfClipPrecision = CLIP_STROKE_PRECIS; logfont.lfOutPrecision = OUT_STRING_PRECIS; logfont.lfQuality = DEFAULT_QUALITY; logfont.lfCharSet = ANSI_CHARSET; platformFont = (HANDLE)CreateFontIndirect (&logfont); platformFontColor = 0; SetWindowLongPtr ((HWND)platformControl, GWLP_USERDATA, (LONG_PTR)this); SendMessage ((HWND)platformControl, WM_SETFONT, (WPARAM)platformFont, true); SendMessage ((HWND)platformControl, EM_SETMARGINS, EC_LEFTMARGIN|EC_RIGHTMARGIN, MAKELONG (0, 0)); SendMessage ((HWND)platformControl, EM_SETSEL, 0, -1); SendMessage ((HWND)platformControl, EM_LIMITTEXT, 255, 0); SetFocus ((HWND)platformControl); oldWndProcEdit = (WINDOWSPROC)SetWindowLongPtr ((HWND)platformControl, GWLP_WNDPROC, (LONG_PTR)WindowProcEdit); #elif MAC #if MACX WindowRef window = (WindowRef)getFrame ()->getSystemWindow (); #if QUARTZ WindowAttributes winAttributes; GetWindowAttributes (window, &winAttributes); if (winAttributes & kWindowCompositingAttribute) { Rect r; r.left = (short)rect.left;// + 2; r.right = (short)rect.right;// - 4; r.top = (short)rect.top;// + 2; r.bottom = (short)rect.bottom;// - 4; if (rect.getHeight () > gStandardFontSize [fontID]) { r.top = (short)(rect.top + rect.getHeight () / 2 - gStandardFontSize [fontID] / 2 + 1); r.bottom = (short)(r.top + gStandardFontSize [fontID]); } if (CreateEditUnicodeTextControl (NULL, &r, NULL, false, NULL, &textControl) == noErr) { HIViewAddSubview ((HIViewRef)getFrame ()->getPlatformControl (), textControl); HIViewSetFirstSubViewFocus ((HIViewRef)getFrame ()->getPlatformControl (), textControl); HIViewAdvanceFocus ((HIViewRef)getFrame ()->getPlatformControl (), 0); EventTypeSpec eventTypes[] = { { kEventClassWindow, kEventWindowDeactivated }, { kEventClassKeyboard, kEventRawKeyDown }, { kEventClassKeyboard, kEventRawKeyRepeat } }; InstallControlEventHandler (textControl, CarbonEventsTextControlProc, GetEventTypeCount (eventTypes), eventTypes, this, &gTextEditEventHandler); platformControl = textControl; if (strlen (text) > 0) { CFStringRef textString = CFStringCreateWithCString (NULL, text, kCFStringEncodingUTF8); if (textString) { SetControlData (textControl, kControlEditTextPart, kControlEditTextCFStringTag, sizeof (CFStringRef), &textString); CFRelease (textString); } ControlEditTextSelectionRec selection; selection.selStart = 0; selection.selEnd = strlen (text); SetControlData (textControl, kControlEditTextPart, kControlEditTextSelectionTag, sizeof (ControlEditTextSelectionRec), &selection); } Boolean singleLineStyle = true; SetControlData (textControl, kControlEditTextPart, kControlEditTextSingleLineTag, sizeof (Boolean), &singleLineStyle); ControlFontStyleRec fontStyle; memset (&fontStyle, 0, sizeof (fontStyle)); fontStyle.flags = kControlUseJustMask | kControlUseSizeMask | kControlUseFontMask; switch (horiTxtAlign) { case kLeftText: fontStyle.just = teFlushLeft; break; case kRightText: fontStyle.just = teFlushRight; break; default: fontStyle.just = teCenter; break; } fontStyle.size = gStandardFontSize [fontID]; extern const char* gMacXfontNames[]; Str255 fontName; CopyCStringToPascal ((const char*)gMacXfontNames[fontID], fontName); GetFNum (fontName, &fontStyle.font); SetControlData (textControl, kControlEditTextPart, kControlFontStyleTag, sizeof (fontStyle), &fontStyle); HIViewSetVisible (textControl, true); } return; } if (pContext) rect.offset (pContext->offsetScreen.h, pContext->offsetScreen.v); #endif static bool gTXNInititalized = false; if (!gTXNInititalized) { TXNMacOSPreferredFontDescription defaults; // fontID, pointSize, encoding, and fontStyle defaults.fontID = 0; defaults.pointSize = kTXNDefaultFontSize; defaults.encoding = CreateTextEncoding(kTextEncodingMacRoman, kTextEncodingDefaultVariant, kTextEncodingDefaultFormat); defaults.fontStyle = kTXNDefaultFontStyle; TXNInitOptions options = 0; TXNInitTextension(&defaults, 1, options); gTXNInititalized = true; } gTextEditCanceled = false; TXNFrameOptions iFrameOptions = kTXNMonostyledTextMask | kTXNDisableDragAndDropMask | kTXNSingleLineOnlyMask; //kTXNNoKeyboardSyncMask | kTXNDisableDragAndDropMask | kTXNSingleLineOnlyMask | kTXNMonostyledTextMask; TXNFrameID frameID = 0; TXNObjectRefcon iRefCon = 0; TXNObject object; Rect r; r.left = (short)rect.left; r.right = (short)rect.right; r.top = (short)rect.top; r.bottom = (short)rect.bottom; OSStatus err; err = TXNNewObject (NULL, window, &r, iFrameOptions, kTXNTextEditStyleFrameType, kTXNSingleStylePerTextDocumentResType, kTXNMacOSEncoding, &object, &frameID, iRefCon); if (err == noErr) { platformControl = object; TXNSetFrameBounds ((TXNObject)platformControl, r.top, r.left, r.bottom, r.right, frameID); if (strlen (text) > 0) TXNSetData ((TXNObject)platformControl, kTXNTextData, (void*)text, strlen (text), kTXNStartOffset, kTXNEndOffset); // set background extern void CColor2RGBColor (const CColor &cc, RGBColor &rgb); RGBColor rgbBackColor = { 0, 0, 0}; CColor2RGBColor (kWhiteCColor /*backColor*/, rgbBackColor); RGBColor rgbTextColor = { 32767, 32767, 32767 }; CColor2RGBColor (kBlackCColor /*fontColor*/, rgbTextColor); RGBBackColor (&rgbBackColor); RGBForeColor (&rgbTextColor); TXNBackground txnBackground; txnBackground.bgType = kTXNBackgroundTypeRGB; txnBackground.bg.color = rgbBackColor; TXNSetBackground ((TXNObject)platformControl, &txnBackground); // set justification TXNControlTag controlTag[1]; TXNControlData controlData[1]; SInt32 just; switch (horiTxtAlign) { case kLeftText : just = kTXNFlushLeft; break; case kRightText : just = kTXNFlushRight; break; default : just = kTXNCenter; break; } controlTag[0] = kTXNJustificationTag; controlData[0].sValue = just; TXNSetTXNObjectControls ((TXNObject)platformControl, false, 1, controlTag, controlData); // set font TXNTypeAttributes attributes[3]; // font name extern const unsigned char* gMacXfontNames[]; short familyID; #if QUARTZ Str255 fontName; CopyCStringToPascal ((const char*)gMacXfontNames[fontID], fontName); GetFNum (fontName, &familyID); #else GetFNum (gMacXfontNames[fontID], &familyID); #endif ATSUFontID fontNameID; ATSUFONDtoFontID (familyID, 0, &fontNameID); attributes[0].tag = kATSUFontTag; attributes[0].size = sizeof(ATSUFontID); attributes[0].data.dataPtr = &fontNameID; // size long fontSize = gStandardFontSize [fontID] << 16; // needs to be in Fixed format attributes[1].tag = kTXNQDFontSizeAttribute; attributes[1].size = kTXNFontSizeAttributeSize; attributes[1].data.dataValue = fontSize; // color attributes[2].tag = kTXNQDFontColorAttribute; attributes[2].size = kTXNQDFontColorAttributeSize; attributes[2].data.dataPtr = &rgbTextColor; TXNSetTypeAttributes ((TXNObject)platformControl, 3, attributes, kTXNStartOffset, kTXNEndOffset); SetUserFocusWindow (window); AdvanceKeyboardFocus (window); TXNActivate ((TXNObject)platformControl, frameID, false); TXNFocus ((TXNObject)platformControl, true); EventTypeSpec eventTypes[] = { { kEventClassMouse, kEventMouseMoved }, { kEventClassMouse, kEventMouseDown }, { kEventClassMouse, kEventMouseUp }, { kEventClassWindow, kEventWindowDeactivated }, { kEventClassKeyboard, kEventRawKeyDown }, { kEventClassKeyboard, kEventRawKeyRepeat } }; InstallWindowEventHandler (window, CarbonEventsTextControlProc, GetEventTypeCount (eventTypes), eventTypes, this, &gTextEditEventHandler); TXNSelectAll ((TXNObject)platformControl); } #else bool ende = false; char c; EventRecord theEvent; Rect rect, vrect; platformControl = new char[256]; rect.left = size.left; rect.right = size.right; rect.top = size.top; rect.bottom = size.bottom; #if !TARGET_API_MAC_CARBON rect.bottom++; rect.right++; #endif if (pContext) { rect.left += pContext->offset.h; rect.right += pContext->offset.h; rect.top += pContext->offset.v; rect.bottom += pContext->offset.v; } vrect = rect; vrect.top++; vrect.left++; vrect.right--; #if TARGET_API_MAC_CARBON vrect.bottom--; #endif RGBColor blackrgb = {0, 0, 0}; RGBColor whitergb = {0xffff, 0xffff, 0xffff}; RGBForeColor (&blackrgb); RGBBackColor (&whitergb); EraseRect (&rect); //FrameRect (&rect); // Dave #if !TARGET_API_MAC_CARBON InsetRect (&vrect, 0, -2); #endif TextFont (kFontIDHelvetica); TextSize (gStandardFontSize [fontID]); text_edit = TENew (&rect, &vrect); if (horiTxtAlign == kLeftText) TESetAlignment (teJustLeft, (TEHandle)text_edit); else if (horiTxtAlign == kRightText) TESetAlignment (teJustRight, (TEHandle)text_edit); else TESetAlignment (teJustCenter, (TEHandle)text_edit); char string[256]; strcpy (string, text); TESetText (string, strlen (string), (TEHandle)text_edit); TESetSelect (0, strlen (string), (TEHandle)text_edit); TEUpdate (&(**(TEHandle)text_edit).viewRect, (TEHandle)text_edit); TEActivate ((TEHandle)text_edit); HLock ((Handle)text_edit); (**(TEHandle)text_edit).crOnly = -1; bLoosefocusWanted = false; while (!ende && !bLoosefocusWanted) { GetNextEvent (everyEvent, &theEvent); switch (theEvent.what) { case nullEvent: doIdleStuff (); break; case autoKey : case keyDown : RGBForeColor (&blackrgb); RGBBackColor (&whitergb); c = theEvent.message & charCodeMask; if (c == 13 || c == 3 || c == 27) { if (c == 13) bWasReturnPressed = true; ende = true; } else if (((theEvent.message >> 8) & 0xFF) == 0x75) { if ((**(TEHandle)text_edit).selEnd < (**(TEHandle)text_edit).teLength) { if (((**(TEHandle)text_edit).selEnd - (**(TEHandle)text_edit).selStart) > 1) TEDelete ((TEHandle)text_edit); else TEKey (0x1D, (TEHandle)text_edit); c = 8; } else c = -1; } if (theEvent.modifiers & cmdKey) { switch (toupper(c)) { // copy case 'C': if ((**(TEHandle)text_edit).selEnd > (**(TEHandle)text_edit).selStart) { OSStatus scrapErr = ClearCurrentScrap(); TECopy((TEHandle)text_edit); if (scrapErr == noErr) scrapErr = TEToScrap(); } break; // cut case 'X': if ((**(TEHandle)text_edit).selEnd > (**(TEHandle)text_edit).selStart) { OSStatus scrapErr = ClearCurrentScrap(); TECut((TEHandle)text_edit); if (scrapErr == noErr) scrapErr = TEToScrap(); } break; // paste case 'V': { OSErr scrapErr = TEFromScrap(); TEPaste((TEHandle)text_edit); } break; // select all case 'A': TESetSelect(0, (**(TEHandle)text_edit).teLength, (TEHandle)text_edit); break; default: break; } } else if (!ende) TEKey (c, (TEHandle)text_edit); break; case mouseDown : GlobalToLocal (&theEvent.where); if (PtInRect (theEvent.where, &rect)) { bool shiftdown = (theEvent.modifiers & shiftKey) != 0; TEClick (theEvent.where, shiftdown, (TEHandle)text_edit); } else ende = true; break; } } CharsHandle h = TEGetText ((TEHandle)text_edit); short length = (**(TEHandle)text_edit).teLength; if (length > 255) length = 255; strncpy ((char*)platformControl, (char*)*h, length); ((char*)platformControl)[length] = 0; HUnlock ((Handle)text_edit); TEDeactivate ((TEHandle)text_edit); TEDispose ((TEHandle)text_edit); text_edit = 0; looseFocus (); #endif #elif MOTIF // we have to add the Text to the parent !! Dimension posX, posY; Widget widget = (Widget)(getFrame ()->getSystemWindow ()); XtVaGetValues (widget, XmNx, &posX, XmNy, &posY, 0); Arg args[20]; int n = 0; XtSetArg (args[n], XmNx, size.left + posX); n++; XtSetArg (args[n], XmNy, size.top + posY); n++; XtSetArg (args[n], XmNwidth, size.width () + 1); n++; XtSetArg (args[n], XmNheight, size.height () + 2); n++; XtSetArg (args[n], XmNvalue, text); n++; XtSetArg (args[n], XmNshadowType, XmSHADOW_IN); n++; XtSetArg (args[n], XmNshadowThickness, 0); n++; XtSetArg (args[n], XmNcursorPositionVisible, true); n++; XtSetArg (args[n], XmNmarginWidth, 0); n++; XtSetArg (args[n], XmNmarginHeight, 0); n++; XtSetArg (args[n], XmNresizeHeight, True); n++; XtSetArg (args[n], XmNborderWidth, 0); n++; XtSetArg (args[n], XmNeditMode, XmSINGLE_LINE_EDIT); n++; // get/set the current font XmFontList fl = 0; XFontStruct* fs = fontStructs[fontID]; if (fs) { XmFontListEntry entry = XmFontListEntryCreate (XmFONTLIST_DEFAULT_TAG, XmFONT_IS_FONT, fs); XmFontList fl = XmFontListAppendEntry (0, entry); XtSetArg (args[n], XmNfontList, fl); n++; } platformControl = XmCreateText (XtParent (widget), "Text", args, n); XtManageChild ((Widget)platformControl); if (fl) XmFontListFree (fl); XmTextSetSelection ((Widget)platformControl, 0, strlen (text), 0); XmTextSetHighlight ((Widget)platformControl, 0, strlen (text), XmHIGHLIGHT_SELECTED); #elif BEOS BView* plugView = (BView*) getFrame ()->getSystemWindow (); CRect rect; getFrame ()->getSize (&rect); BRect r (rect.left + size.left, rect.top + size.top, rect.left + size.right, rect.top + size.bottom); BRect tr = r; tr.OffsetTo (B_ORIGIN); tr.InsetBy (1, 1); BeTextView* textView = new BeTextView (this, r, NULL, tr); plugView->Window ()->Activate (true); plugView->AddChild (textView); BFont font; if (fontID < 0 || fontID >= kNumStandardFonts) fontID = kSystemFont; extern const char* gStandardFontStyle[]; font.SetFamilyAndStyle (gStandardFontName[fontID], gStandardFontStyle[fontID]); font.SetSize (gStandardFontSize[fontID]); rgb_color c = { fontColor.red, fontColor.green, fontColor.blue, 255 }; textView->SetFontAndColor (&font, B_FONT_FAMILY_AND_STYLE | B_FONT_SIZE, &c); rgb_color cv = { backColor.red, backColor.green, backColor.blue, 255 }; textView->SetMaxBytes (255); textView->SetViewColor (cv); alignment a; if (horiTxtAlign == kRightText) a = B_ALIGN_RIGHT; else if (horiTxtAlign == kLeftText) a = B_ALIGN_LEFT; else a = B_ALIGN_CENTER; textView->SetAlignment (a); textView->SetText (text); textView->DisallowChar ('\n'); textView->DisallowChar ('\t'); textView->MakeResizable (true); textView->MakeFocus (true); textView->SelectAll (); platformControl = textView; #endif } //------------------------------------------------------------------------ void CTextEdit::looseFocus (CDrawContext *pContext) { // Call this yet to avoid recursive call endEdit(); if (getFrame ()->getFocusView () == this) getFrame ()->setFocusView (0); if (platformControl == 0) return; char oldText[256]; strcpy (oldText, text); #if WINDOWS char newText[255]; GetWindowText ((HWND)platformControl, newText, 255); strcpy (text, newText); DestroyWindow ((HWND)platformControl); if (platformFont) { DeleteObject ((HGDIOBJ)platformFont); platformFont = 0; } if (platformFontColor) { DeleteObject (platformFontColor); platformFontColor = 0; } #elif MAC #if MACX if (platformControl == 0) return; if (gTextEditEventHandler) RemoveEventHandler (gTextEditEventHandler); gTextEditEventHandler = 0; #if QUARTZ if (textControl) { CFStringRef cfstr; if (!gTextEditCanceled && GetControlData (textControl, kControlEditTextPart, kControlEditTextCFStringTag, sizeof cfstr, (void*)&cfstr, NULL) == noErr) { CFStringGetCString (cfstr, text, 255, kCFStringEncodingUTF8); CFRelease (cfstr); } HIViewSetVisible (textControl, false); HIViewRemoveFromSuperview (textControl); textControl = 0; pParentFrame->setCursor (kCursorDefault); } else #endif { if (!gTextEditCanceled) { CharsHandle dataHandle; TXNGetDataEncoded ((TXNObject)platformControl, kTXNStartOffset, kTXNEndOffset, &dataHandle, kTXNTextData); if (dataHandle != NULL && GetHandleSize (dataHandle) > 0) { long s = GetHandleSize (dataHandle); strncpy (text, *dataHandle, (s > 255) ? 255 : s); text [(s > 255) ? 255 : s] = 0; DisposeHandle (dataHandle); } else text[0] = 0; } TXNFocus ((TXNObject)platformControl, false); TXNDeleteObject ((TXNObject)platformControl); } platformControl = 0; setDirty (true); #if 0//QUARTZ CRect fr (size); CPoint offset; localToFrame (offset); fr.offset (offset.x, offset.y); RgnHandle rgn = NewRgn (); MacSetRectRgn (rgn, fr.left, fr.top, fr.right, fr.bottom); HIViewSetNeedsDisplayInRegion ((HIViewRef)getFrame ()->getPlatformControl () , rgn, true); DisposeRgn (rgn); #endif // doIdleStuff (); #else strcpy (text, (char*)platformControl); delete[] platformControl; #endif #elif MOTIF char *pNewText = XmTextGetString ((Widget)platformControl); strcpy (text, pNewText); XtFree (pNewText); XtUnmanageChild ((Widget)platformControl); XtDestroyWidget ((Widget)platformControl); #elif BEOS BTextView* textView = (BTextView*) platformControl; strncpy (text, textView->Text (), 255); if (textView->LockLooper ()) { BWindow* w = textView->Window (); textView->RemoveSelf (); w->Unlock (); } delete textView; #endif CPoint origOffset; bool resetContextOffset = false; if (!pContext) { // create a local context pContext = getFrame ()->createDrawContext (); if (getParentView ()) { resetContextOffset = true; origOffset.x = pContext->offset.x; origOffset.y = pContext->offset.y; CView *view= getParentView (); CRect rect2; view->getViewSize (rect2); CPoint offset; view->localToFrame (offset); rect2.offset (offset.x, offset.y); pContext->offset.h = rect2.left; pContext->offset.v = rect2.top; } } else pContext->remember (); // update dependency bool change = false; if (strcmp (oldText, text)) { change = true; if (listener) listener->valueChanged (pContext, this); } platformControl = 0; if (resetContextOffset) { pContext->offset.x = origOffset.x; pContext->offset.y = origOffset.y; } pContext->forget (); if (change) doIdleStuff (); CView* receiver = pParentView ? pParentView : pParentFrame; if (receiver) receiver->notify (this, "LooseFocus"); } //------------------------------------------------------------------------ void CTextEdit::setTextEditConvert (void (*convert) (char *input, char *string)) { editConvert = convert; } //------------------------------------------------------------------------ void CTextEdit::setTextEditConvert (void (*convert) (char *input, char *string, void *userDta), void *userData) { editConvert2 = convert; this->userData = userData; } //------------------------------------------------------------------------ // COptionMenuScheme //------------------------------------------------------------------------ /*! @class COptionMenuScheme Used to define the appearance (font color, background color...) of a popup-menu. To define the scheme of a menu, use the appropriate setScheme method (see COptionMenu). @section coptionmenuscheme_new_in_3_0 New since 3.0 You can also use the global variable gOptionMenuScheme to use one scheme on all menus. @section coptionmenuscheme_note Note If you want to use it on Mac OS X, you must set the macro MAC_ENABLE_MENU_SCHEME (needs Mac OS X 10.3 or higher) */ COptionMenuScheme* gOptionMenuScheme = 0; //------------------------------------------------------------------------ COptionMenuScheme::COptionMenuScheme () { #if WINDOWS COLORREF c = GetSysColor (COLOR_MENU); backgroundColor (GetRValue (c), GetGValue (c), GetBValue (c), 0); c = GetSysColor (COLOR_HIGHLIGHT); selectionColor (GetRValue (c), GetGValue (c), GetBValue (c), 0); c = GetSysColor (COLOR_MENUTEXT); textColor (GetRValue (c), GetGValue (c), GetBValue (c), 0); c = GetSysColor (COLOR_HIGHLIGHTTEXT); hiliteTextColor (GetRValue (c), GetGValue (c), GetBValue (c), 0); c = GetSysColor (COLOR_GRAYTEXT); disableTextColor (GetRValue (c), GetGValue (c), GetBValue (c), 0); #else backgroundColor = kGreyCColor; selectionColor = kBlueCColor; textColor = kBlackCColor; hiliteTextColor = kWhiteCColor; disableTextColor = kWhiteCColor; #endif font = kNormalFontSmall; #if MAC_ENABLE_MENU_SCHEME registerWithToolbox (); #endif } //------------------------------------------------------------------------ COptionMenuScheme::~COptionMenuScheme () { #if MAC_ENABLE_MENU_SCHEME unregisterWithToolbox (); #endif } //------------------------------------------------------------------------ void COptionMenuScheme::getItemSize (const char* text, CDrawContext* pContext, CPoint& size) { if (!strcmp (text, kMenuSeparator)) // separator { #if MAC size.h = 6; size.v = 9; #else // was: size.h = size.v = 6; size.h = 6; size.v = 18; // separators must have same height, otherwise we have problems // in multi-column menus :( #endif } else { pContext->setFont (font); size.h = pContext->getStringWidth (text) + 18; size.v = 18; #if MAC size.h += 18; #endif } } //------------------------------------------------------------------------ void COptionMenuScheme::drawItemBack (CDrawContext* pContext, const CRect& rect, bool hilite) { if (hilite) pContext->setFillColor (selectionColor); else pContext->setFillColor (backgroundColor); pContext->fillRect (rect); } //------------------------------------------------------------------------ void COptionMenuScheme::drawItem (const char* text, long itemId, long state, CDrawContext* pContext, const CRect& rect) { bool hilite = (state & kSelected) != 0; drawItemBack (pContext, rect, hilite); if (!strcmp (text, kMenuSeparator)) { CCoord y = rect.top + rect.height () / 2; const CColor bc = { 0, 0, 0, 150}; const CColor wc = { 255, 255, 255, 150}; pContext->setFrameColor (bc); pContext->moveTo (CPoint (rect.left + 2, y - 1)); pContext->lineTo (CPoint (rect.right - 2, y - 1)); pContext->setFrameColor (wc); pContext->moveTo (CPoint (rect.left + 2, y)); pContext->lineTo (CPoint (rect.right - 2, y)); return; } CRect r; if (state & kChecked) { r (6, 4, 14, 12); r.offset (rect.left, rect.top); if (hilite) pContext->setFillColor (hiliteTextColor); else pContext->setFillColor (textColor); pContext->fillEllipse (r); } r = rect; r.left += 18; pContext->setFont (font); if (state & kDisabled) pContext->setFontColor (disableTextColor); else { if (hilite) pContext->setFontColor (hiliteTextColor); else pContext->setFontColor (textColor); } // this needs to be done right, without changing the text pointer in anyway ;-) char *ptr = (char*)strstr (text, "\t"); if (ptr) { char modifier[32]; strcpy (modifier, ptr + 1); *ptr = 0; pContext->drawString (text, r, false, kLeftText); *ptr = '\t'; r.left = r.right - 50; pContext->drawString (modifier, r, false, kLeftText); } else pContext->drawString (text, r, false, kLeftText); } #if MAC_ENABLE_MENU_SCHEME #define kHIMenuViewClassID CFSTR("com.apple.HIMenuView") struct HIMenuScheme { HIViewRef hiView; COptionMenuScheme* scheme; COptionMenu* menu; COffscreenContext* offscreenContext; float maxWidth; }; const EventParamName kEventParamCOptionMenuScheme = 'COMS'; const EventParamName kEventParamCOptionMenu = 'COM '; #define kItemHeight 18 static CFStringRef gOptionMenuSchemeClassID = 0; static HIObjectClassRef gMenuClassRef = 0; static CFIndex gOptionMenuSchemeClassIDRefCount = 0; //------------------------------------------------------------------------ void COptionMenuScheme::registerWithToolbox () { if (gOptionMenuSchemeClassID == 0) { gOptionMenuSchemeClassID = CFStringCreateWithFormat (NULL, NULL, CFSTR("net.sourceforge.vstgui.COptionMenuSchemeClassID.%d"), this); static const EventTypeSpec events[] = { { kEventClassHIObject, kEventHIObjectConstruct } , { kEventClassHIObject, kEventHIObjectInitialize }, { kEventClassHIObject, kEventHIObjectDestruct }, { kEventClassControl, kEventControlHitTest }, { kEventClassControl, kEventControlGetPartRegion }, { kEventClassControl, kEventControlDraw }, { kEventClassControl, kEventControlGetOptimalBounds }, { kEventClassMenu, kEventMenuCreateFrameView }, { kEventClassScrollable, kEventScrollableGetInfo }, }; HIObjectRegisterSubclass ( gOptionMenuSchemeClassID, kHIMenuViewClassID, kNilOptions, COptionMenuScheme::eventHandler, GetEventTypeCount (events), events, NULL, &gMenuClassRef); gOptionMenuSchemeClassIDRefCount = CFGetRetainCount (gOptionMenuSchemeClassID); } else CFRetain (gOptionMenuSchemeClassID); } //------------------------------------------------------------------------ void COptionMenuScheme::unregisterWithToolbox () { if (gOptionMenuSchemeClassID) { if (CFGetRetainCount (gOptionMenuSchemeClassID) == gOptionMenuSchemeClassIDRefCount) { HIObjectUnregisterClass (gMenuClassRef); gMenuClassRef = 0; } CFRelease (gOptionMenuSchemeClassID); } } //------------------------------------------------------------------------ pascal OSStatus COptionMenuScheme::eventHandler (EventHandlerCallRef inCallRef, EventRef inEvent, void *inUserData) { OSStatus err = eventNotHandledErr; UInt32 eventClass = GetEventClass (inEvent); UInt32 eventKind = GetEventKind (inEvent); HIMenuScheme* scheme = (HIMenuScheme*)inUserData; switch (eventClass) { case kEventClassHIObject: { switch (eventKind) { case kEventHIObjectConstruct: { scheme = (HIMenuScheme*)calloc (1, sizeof (HIMenuScheme)); GetEventParameter (inEvent, kEventParamHIObjectInstance, typeHIObjectRef, NULL, sizeof (HIObjectRef), NULL, &scheme->hiView); SetEventParameter (inEvent, kEventParamHIObjectInstance, typeVoidPtr, sizeof (HIMenuScheme*), &scheme); scheme->maxWidth = 100; err = noErr; break; } case kEventHIObjectInitialize: { err = CallNextEventHandler (inCallRef, inEvent); if (err == noErr) { GetEventParameter (inEvent, kEventParamCOptionMenuScheme, typeVoidPtr, NULL, sizeof (COptionMenuScheme*), NULL, &scheme->scheme); GetEventParameter (inEvent, kEventParamCOptionMenu, typeVoidPtr, NULL, sizeof (COptionMenu*), NULL, &scheme->menu); scheme->scheme->remember (); scheme->menu->remember (); scheme->offscreenContext = new COffscreenContext (scheme->menu->getFrame (), 600, 100); } break; } case kEventHIObjectDestruct: { delete scheme->offscreenContext; scheme->scheme->forget (); scheme->menu->forget (); free (scheme); err = noErr; break; } } break; } case kEventClassControl: { HIViewRef control; GetEventParameter (inEvent, kEventParamDirectObject, typeControlRef, NULL, sizeof (HIViewRef), NULL, &control); switch (eventKind) { case kEventControlInitialize: { err = CallNextEventHandler (inCallRef, inEvent); break; } case kEventControlHitTest: { HIPoint mouseLoc; GetEventParameter (inEvent, kEventParamMouseLocation, typeHIPoint, NULL, sizeof (mouseLoc), NULL, &mouseLoc); ControlPartCode partHit = mouseLoc.y / kItemHeight + 1; char temp[1024]; CPoint size; long yPos = 0; for (long i = 0; i < scheme->menu->getNbEntries (); i++) { scheme->menu->getEntry (i, temp); scheme->scheme->getItemSize (temp, scheme->offscreenContext, size); yPos += size.y; if (yPos >= mouseLoc.y) { partHit = i + 1; break; } } SetEventParameter (inEvent, kEventParamControlPart, typeControlPartCode, sizeof (partHit), &partHit); err = noErr; break; } case kEventControlGetOptimalBounds: { HIRect r = { {0, 0}, { 0, 0 }}; r.size.width = scheme->maxWidth; char temp[1024]; CPoint size; for (long i = 0; i < scheme->menu->getNbEntries (); i++) { scheme->menu->getEntry (i, temp); scheme->scheme->getItemSize (temp, scheme->offscreenContext, size); if (!strncmp (temp, kMenuSubMenu, 2)) size.x += 16; r.size.height += size.y; if (r.size.width < size.x) r.size.width = size.x; } //r.size.height += scheme->menu->getNbEntries (); scheme->maxWidth = r.size.width; SetEventParameter (inEvent, kEventParamControlOptimalBounds, typeHIRect, sizeof (HIRect), &r); err = noErr; break; } case kEventControlGetPartRegion: { HIRect r = { {0, 0}, { 0, 0 }}; ControlPartCode whichItem; RgnHandle outRegion = NULL; GetEventParameter (inEvent, kEventParamControlPart, typeControlPartCode, NULL, sizeof (whichItem), NULL, &whichItem); GetEventParameter (inEvent, kEventParamControlRegion, typeQDRgnHandle, NULL, sizeof(outRegion), NULL, &outRegion); if (whichItem <= 0) { r.size.width = scheme->maxWidth; char temp[1024]; CPoint size; for (long i = 0; i < scheme->menu->getNbEntries (); i++) { scheme->menu->getEntry (i, temp); scheme->scheme->getItemSize (temp, scheme->offscreenContext, size); if (!strncmp (temp, kMenuSubMenu, 2)) size.x += 16; r.size.height += size.y; if (r.size.width < size.x) r.size.width = size.x; } scheme->maxWidth = r.size.width; r.size.height += scheme->menu->getNbEntries (); } else { char temp[1024]; CPoint size; for (long i = 0; i < whichItem; i++) { r.origin.y += size.y; scheme->menu->getEntry (i, temp); scheme->scheme->getItemSize (temp, scheme->offscreenContext, size); r.size.height += size.y; } r.size.width = scheme->maxWidth; } SetRectRgn (outRegion, (short) r.origin.x, (short) r.origin.y,(short) r.origin.x + r.size.width, (short) r.origin.y+ r.size.height + 1); err = noErr; break; } case kEventControlDraw: { CGContextRef cgContext; GetEventParameter (inEvent, kEventParamCGContextRef, typeCGContextRef, NULL, sizeof (cgContext), NULL, &cgContext); HIRect r; HIViewGetBounds (control, &r); CGContextClearRect (cgContext, r); if (control != scheme->hiView) { err = noErr; break; } ControlPartCode focusPart; HIViewGetFocusPart (scheme->hiView, &focusPart); focusPart--; WindowRef window = HIViewGetWindow (scheme->hiView); CDrawContext context (NULL, cgContext, window); char entryText[1024]; CPoint size; CRect rect (0, 0); rect.setHeight (kItemHeight); rect.setWidth (scheme->maxWidth); for (int i = 0; i < scheme->menu->getNbEntries (); i++) { if (scheme->menu->getEntry (i, entryText)) { scheme->scheme->getItemSize (entryText, &context, size); long state = scheme->menu->isCheckEntry (i) ? kChecked : 0; if (focusPart >= 0 && focusPart == i) state |= kSelected; long offset = 0; if (!strncmp (entryText, kMenuSubMenu, 2)) { state |= kSubMenu; offset = 2; } else if (!strncmp (entryText, kMenuTitle, 2)) { state |= kTitle; offset = 2; } else if (!strncmp (entryText, kMenuDisable, 2)) { state |= kDisabled; offset = 2; } rect.setHeight (size.y+1); context.setClipRect (rect); scheme->scheme->drawItem (entryText+offset, i, state, &context, rect); rect.offset (0, size.y); } } break; } case kEventControlGetFrameMetrics: { err = CallNextEventHandler (inCallRef, inEvent); HIViewFrameMetrics metrics; GetEventParameter (inEvent, kEventParamControlFrameMetrics, typeControlFrameMetrics, NULL, sizeof (metrics), NULL, &metrics); metrics.top = metrics.bottom = 0; SetEventParameter (inEvent, kEventParamControlFrameMetrics, typeControlFrameMetrics, sizeof (metrics), &metrics); break; } case kEventControlOwningWindowChanged: { WindowRef newWindow = GetControlOwner (control); HIWindowChangeFeatures (newWindow, 0, kWindowIsOpaque); err = noErr; HIViewRef root = HIViewGetRoot (newWindow); if (root) { HIRect bounds, frame; HIViewGetBounds (root, &bounds); HIViewGetFrame (root, &frame); } break; } } break; } case kEventClassMenu: { switch (eventKind) { case kEventMenuCreateFrameView: { err = CallNextEventHandler (inCallRef, inEvent); HIViewRef frameView; GetEventParameter (inEvent, kEventParamMenuFrameView, typeControlRef, NULL, sizeof (ControlRef), NULL, &frameView); HIViewFindByID (frameView, kHIViewWindowContentID, &frameView); if (frameView) { EventTypeSpec events [] = { { kEventClassControl, kEventControlDraw }, { kEventClassControl, kEventControlOwningWindowChanged }, { kEventClassControl, kEventControlGetFrameMetrics } }; InstallControlEventHandler (frameView, COptionMenuScheme::eventHandler, GetEventTypeCount (events), events, scheme, NULL); } break; } } break; } case kEventClassScrollable: { switch (eventKind) { case kEventScrollableGetInfo: { HISize size; HIPoint origin = { 0, 0 }; size.width = 200; size.height = kItemHeight * (scheme->menu->getNbEntries () + 1);; SetEventParameter(inEvent, kEventParamImageSize, typeHISize, sizeof( size ), &size ); SetEventParameter(inEvent, kEventParamViewSize, typeHISize, sizeof( size ), &size ); SetEventParameter(inEvent, kEventParamOrigin, typeHIPoint, sizeof( origin ), &origin ); // line size is 1/10th total size size.width /= 10; size.height /= 10; SetEventParameter(inEvent, kEventParamLineSize, typeHISize, sizeof( size ), &size ); err = noErr; break; } } break; } } return err; } #endif //------------------------------------------------------------------------ // COptionMenu //------------------------------------------------------------------------ /*! @class COptionMenu Define a rectangle view where a text-value can be displayed with a given font and color. The text-value is centered in the given rect. A pixmap can be used as background, a second pixmap can be used when the option menu is popuped. There are 2 styles with or without a shadowed text. When a mouse click occurs, a popup menu is displayed. */ COptionMenu::COptionMenu (const CRect &size, CControlListener *listener, long tag, CBitmap *background, CBitmap *bgWhenClick, const long style) : CParamDisplay (size, background, style), bgWhenClick (bgWhenClick), nbItemsPerColumn (-1), prefixNumbers (0), scheme (0) { this->listener = listener; this->tag = tag; nbEntries = 0; nbSubMenus = 0; currentIndex = -1; lastButton = kRButton; platformControl = 0; lastResult = -1; lastMenu = 0; #if MAC menuID = 0; #endif if (bgWhenClick) bgWhenClick->remember (); nbSubMenuAllocated = nbAllocated = 0; check = 0; entry = 0; submenuEntry = 0; } //------------------------------------------------------------------------ COptionMenu::~COptionMenu () { removeAllEntry (); if (bgWhenClick) bgWhenClick->forget (); } //------------------------------------------------------------------------ void COptionMenu::setPrefixNumbers (long preCount) { prefixNumbers = preCount; } //----------------------------------------------------------------------------- bool COptionMenu::allocateSubMenu (long nb) { long newAllocated = nbSubMenuAllocated + nb; if (submenuEntry) submenuEntry = (COptionMenu**)realloc (submenuEntry, newAllocated * sizeof (COptionMenu*)); else submenuEntry = (COptionMenu**)malloc (newAllocated * sizeof (COptionMenu*)); long i; for (i = nbSubMenuAllocated; i < newAllocated; i++) submenuEntry[i] = 0; nbSubMenuAllocated = newAllocated; return true; } //------------------------------------------------------------------------ bool COptionMenu::allocateMenu (long nb) { long newAllocated = nbAllocated + nb; if (check) check = (bool*)realloc (check, newAllocated * sizeof (bool)); else check = (bool*)malloc (newAllocated * sizeof (bool)); if (!check) return false; if (entry) entry = (char**)realloc (entry, newAllocated * sizeof (char*)); else entry = (char**)malloc (newAllocated * sizeof (char*)); if (!entry) { free (check); return false; } long i; for (i = nbAllocated; i < newAllocated; i++) { check[i] = false; entry[i] = 0; } nbAllocated = newAllocated; return true; } //------------------------------------------------------------------------ COptionMenu* COptionMenu::getSubMenu (long idx) const { if (submenuEntry && idx < nbSubMenus) return submenuEntry[idx]; return 0; } //------------------------------------------------------------------------ bool COptionMenu::addEntry (COptionMenu *subMenu, char *txt) { if (nbEntries >= MAX_ENTRY || !subMenu || !txt) return false; if (nbEntries >= nbAllocated) if (!allocateMenu (32)) return false; entry[nbEntries] = (char*)malloc (256); switch (prefixNumbers) { case 2: sprintf (entry[nbEntries], "-M%1d %s", (int)(nbEntries + 1), txt); break; case 3: sprintf (entry[nbEntries], "-M%02d %s", (int)(nbEntries + 1), txt); break; case 4: sprintf (entry[nbEntries], "-M%03d %s", (int)(nbEntries + 1), txt); break; default: sprintf (entry[nbEntries], "-M%s", txt); } if (nbSubMenus >= nbSubMenuAllocated) if (!allocateSubMenu (10)) return false; submenuEntry[nbSubMenus++] = subMenu; subMenu->remember (); nbEntries++; if (currentIndex < 0) currentIndex = 0; return true; } //------------------------------------------------------------------------ bool COptionMenu::addEntry (char *txt, long index) { if (nbEntries >= MAX_ENTRY) return false; if (nbEntries >= nbAllocated) if (!allocateMenu (32)) return false; entry[nbEntries] = (char*)malloc (256); long pos = nbEntries; // switch the entries for the insert if (index >= 0) { for (long i = nbEntries; i > index; i--) strcpy (entry[i], entry[i - 1]); if (index >= nbEntries) pos = nbEntries; else pos = index; if (currentIndex >= index) currentIndex++; } *entry[pos] = 0; if (txt) { switch (prefixNumbers) { case 2: sprintf (entry[pos], "%1d %s", (int)(index + 1), txt); break; case 3: sprintf (entry[pos], "%02d %s", (int)(index + 1), txt); break; case 4: sprintf (entry[pos], "%03d %s", (int)(index + 1), txt); break; default: strncpy (entry[pos], txt, 256); } } nbEntries++; if (currentIndex < 0) currentIndex = 0; return true; } //------------------------------------------------------------------------ long COptionMenu::getCurrent (char *txt, bool countSeparator) const { if (currentIndex < 0) return -1; long result = 0; if (countSeparator) { if (txt) strcpy (txt, entry[currentIndex]); result = currentIndex; } else { for (long i = 0; i < currentIndex; i++) { if (strcmp (entry[i], kMenuSeparator) && strncmp (entry[i], kMenuTitle, 2)) result++; } if (txt) strcpy (txt, entry[currentIndex]); } return result; } //------------------------------------------------------------------------ bool COptionMenu::setCurrent (long index, bool countSeparator) { if (index < 0 || index >= nbEntries) return false; if (countSeparator) { if (!strcmp (entry[index], kMenuSeparator) && strncmp (entry[index], kMenuTitle, 2)) return false; currentIndex = index; } else { long newCurrent = 0; long i = 0; while (i <= index && newCurrent < nbEntries) { if (strcmp (entry[newCurrent], kMenuSeparator) && strncmp (entry[newCurrent], kMenuTitle, 2)) i++; newCurrent++; } currentIndex = newCurrent - 1; } if (style & (kMultipleCheckStyle & ~kCheckStyle)) check[currentIndex] = !check[currentIndex]; // to force the redraw setDirty (); return true; } //------------------------------------------------------------------------ bool COptionMenu::getEntry (long index, char *txt) const { if (index < 0 || index >= nbEntries) return false; if (txt) strcpy (txt, entry[index]); return true; } //------------------------------------------------------------------------ bool COptionMenu::setEntry (long index, char *txt) { if (index < 0 || index >= nbEntries) return false; if (txt) strcpy (entry[index], txt); return true; } //------------------------------------------------------------------------ bool COptionMenu::removeEntry (long index) { if (index < 0 || index >= nbEntries) return false; nbEntries--; // switch the entries for (long i = index; i < nbEntries; i++) { strcpy (entry[i], entry[i + 1]); check[i] = check [i + 1]; } if (currentIndex >= index) currentIndex--; // delete the last one free (entry[nbEntries]); entry[nbEntries] = 0; check[nbEntries] = false; if (nbEntries == 0) currentIndex = -1; return true; } //------------------------------------------------------------------------ bool COptionMenu::removeAllEntry () { long i; for (i = 0; i < nbEntries; i++) { free (entry[i]); entry[i] = 0; check[i] = false; } nbEntries = 0; currentIndex = -1; for (i = 0; i < nbSubMenus; i++) { submenuEntry[i]->forget (); submenuEntry[i] = 0; } nbSubMenus = 0; if (check) free (check); check = 0; if (entry) free (entry); entry = 0; if (submenuEntry) free (submenuEntry); submenuEntry = 0; nbAllocated = 0; nbSubMenuAllocated = 0; return true; } //------------------------------------------------------------------------ long COptionMenu::getIndex (char *txt) const { if (!txt) return -1; // search entries for (long i = 0; i < nbEntries; i++) if (!strcmp (entry[i], txt)) return i; // not found return -1; } //------------------------------------------------------------------------ bool COptionMenu::checkEntry (long index, bool state) { if (index < 0 || index >= nbEntries) return false; check[index] = state; return true; } //------------------------------------------------------------------------ bool COptionMenu::checkEntryAlone (long index) { if (index < 0 || index >= nbEntries) return false; for (long i = 0; i < nbEntries; i++) check[i] = false; check[index] = true; return true; } //------------------------------------------------------------------------ bool COptionMenu::isCheckEntry (long index) const { if (index < 0 || index >= nbEntries) return false; return check[index]; } //------------------------------------------------------------------------ void COptionMenu::draw (CDrawContext *pContext) { if (currentIndex >= 0 && nbEntries) drawText (pContext, entry[currentIndex] + prefixNumbers); else drawText (pContext, NULL); } //------------------------------------------------------------------------ void COptionMenu::mouse (CDrawContext *pContext, CPoint &where, long button) { if (!bMouseEnabled || !getFrame () || !pContext) return; lastButton = (button != -1) ? button : pContext->getMouseButtons (); if (listener && button & (kAlt | kShift | kControl | kApple)) { if (listener->controlModifierClicked (pContext, this, button) != 0) return; } if (lastButton & (kLButton|kRButton|kApple)) { if (bgWhenClick) { char string[256]; if (currentIndex >= 0) sprintf (string, "%s", entry[currentIndex]); else string[0] = 0; drawText (pContext, string, bgWhenClick); } beginEdit(); takeFocus (pContext); } } //------------------------------------------------------------------------ #if MOTIF #include #include #include #include static void _unmapCallback (Widget item, XtPointer clientData, XtPointer callData); static void _activateCallback (Widget item, XtPointer clientData, XtPointer callData); //------------------------------------------------------------------------ static void _unmapCallback (Widget item, XtPointer clientData, XtPointer callData) { COptionMenu *optionMenu= (COptionMenu*)clientData; optionMenu->looseFocus (); } //------------------------------------------------------------------------ static void _activateCallback (Widget item, XtPointer clientData, XtPointer callData) { COptionMenu *optionMenu= (COptionMenu*)clientData; optionMenu->setCurrentSelected ((void*)item); } #endif //------------------------------------------------------------------------ #if BEOS #include #include #endif //------------------------------------------------------------------------ COptionMenu *COptionMenu::getLastItemMenu (long &idxInMenu) const { idxInMenu = lastMenu ? (long)lastMenu->getValue (): -1; return lastMenu; } //------------------------------------------------------------------------ COptionMenu *COptionMenu::getItemMenu (long idx, long &idxInMenu, long &offsetIdx) { #if WINDOWS long oldIDx = offsetIdx; offsetIdx += nbEntries; if (idx < offsetIdx) { idxInMenu = idx - oldIDx; return this; } #elif MAC if (menuID == offsetIdx) { idxInMenu = idx; return this; } #endif COptionMenu *menu = 0; for (long i = 0; i < nbSubMenus; i++) { menu = submenuEntry[i]->getItemMenu (idx, idxInMenu, offsetIdx); if (menu) break; } return menu; } //------------------------------------------------------------------------ void COptionMenu::removeItems () { for (long i = 0; i < nbSubMenus; i++) submenuEntry[i]->removeItems (); #if WINDOWS // destroy the menu if (platformControl) DestroyMenu ((HMENU)platformControl); platformControl = 0; #elif MAC // destroy the menu if (menuID) DeleteMenu (menuID); if (platformControl) DisposeMenu ((MenuHandle)platformControl); platformControl = 0; #endif } //------------------------------------------------------------------------ void *COptionMenu::appendItems (long &offsetIdx) { bool multipleCheck = style & (kMultipleCheckStyle & ~kCheckStyle); #if WINDOWS void *menu = (void*)CreatePopupMenu (); bool ownerDraw = (scheme != 0) || (gOptionMenuScheme != 0); int flags = 0; long idxSubmenu = 0; long offset = offsetIdx; offsetIdx += nbEntries; long inc = 0; for (long i = 0; i < nbEntries; i++) { //---Separator----- if (!strcmp (entry[i], kMenuSeparator)) { if (ownerDraw) AppendMenu ((HMENU)menu, MF_OWNERDRAW|MF_SEPARATOR, 0, entry[i]); else AppendMenu ((HMENU)menu, MF_SEPARATOR, 0, entry[i]); } else { flags = ownerDraw ? MF_OWNERDRAW : MF_STRING; if (nbEntries < 160 && nbItemsPerColumn > 0 && inc && !(inc % nbItemsPerColumn)) flags |= MF_MENUBARBREAK; if (!strncmp (entry[i], kMenuSubMenu, 2)) { if (idxSubmenu < nbSubMenus) { void *submenu = submenuEntry[idxSubmenu]->appendItems (offsetIdx); if (submenu) { idxSubmenu++; AppendMenu ((HMENU)menu, flags|MF_POPUP|MF_ENABLED, (UINT_PTR)submenu, entry[i] + 2); } else continue; } else continue; } //---Disable/Gray entry----------- else if (!strncmp (entry[i], kMenuDisable, 2)) { AppendMenu ((HMENU)menu, flags|MF_GRAYED, offset + inc, entry[i] + 2); } //---Disable entry-------- else if (!strncmp (entry[i], kMenuTitle, 2)) { AppendMenu ((HMENU)menu, flags|MF_DISABLED, offset + inc, entry[i] + 2); } //---Multiple Checked entry--- else if (multipleCheck) { AppendMenu ((HMENU)menu, flags|MF_ENABLED | (check[i] ? MF_CHECKED : MF_UNCHECKED), offset + inc, entry[i]); } //---Checked Entry--------- else if (style & kCheckStyle) { AppendMenu ((HMENU)menu, flags|MF_ENABLED | ((i == currentIndex) ? MF_CHECKED : MF_UNCHECKED), offset + inc, entry[i]); } else AppendMenu ((HMENU)menu, flags|MF_ENABLED, offset + inc, entry[i]); } inc++; } platformControl = menu; return menu; #elif MAC //---Get an non-existing ID for the menu: menuID = UniqueID ('MENU'); MenuHandle theMenu = 0; //---Create the menu #if MAC_ENABLE_MENU_SCHEME extern long pSystemVersion; if ((scheme || gOptionMenuScheme) && pSystemVersion >= 0x1030) { COptionMenuScheme* s = gOptionMenuScheme ? gOptionMenuScheme : scheme; EventRef initEvent = NULL; if (CreateEvent (NULL, kEventClassHIObject, kEventHIObjectInitialize, 0, 0, &initEvent) == noErr) { MenuDefSpec customMenuDef; COptionMenu* optMenu = this; SetEventParameter (initEvent, kEventParamCOptionMenu, typeVoidPtr, sizeof(COptionMenu*), &optMenu); customMenuDef.defType = kMenuDefClassID; customMenuDef.u.view.classID = gOptionMenuSchemeClassID; customMenuDef.u.view.initEvent = initEvent; SetEventParameter (initEvent, kEventParamCOptionMenuScheme, typeVoidPtr, sizeof(COptionMenuScheme*), &s); CreateCustomMenu (&customMenuDef, menuID, 0, &theMenu); ReleaseEvent (initEvent); if (theMenu == NULL) return NULL; } } else #endif theMenu = NewMenu (menuID, "\pPopUp"); char text2[256]; long keyChar; long idxSubmenu = 0; offsetIdx += nbEntries; short keyModifiers; bool useGlyph; for (long i = 0; i < nbEntries; i++) { keyChar = 0; keyModifiers = kMenuNoModifiers; useGlyph = false; strcpy (text2, entry[i]); char *ptr = strstr (text2, "\t"); if (ptr) { *ptr = '\0'; ptr++; if (strlen (ptr) > 0) { if (!strstr (ptr, "Ctrl")) keyModifiers |= kMenuNoCommandModifier; if (strstr (ptr, "Alt")) keyModifiers |= kMenuOptionModifier; if (strstr (ptr, "Shift")) keyModifiers |= kMenuShiftModifier; if (strstr (ptr, "Apple")) keyModifiers |= kMenuControlModifier; if (!strncmp (ptr, "Del", 3)) keyChar = 0x17; else if (!strncmp (ptr, "Left", 4)) { keyChar = 0x64; useGlyph = true; } else if (!strncmp (ptr, "Right", 5)) { keyChar = 0x65; useGlyph = true; } else if (!strncmp (ptr, "Up", 2)) { keyChar = 0x68; useGlyph = true; } else if (!strncmp (ptr, "Down", 4)) { keyChar = 0x6a; useGlyph = true; } else keyChar = (long)ptr[strlen (ptr) - 1]; } else { keyModifiers = kMenuNoCommandModifier; keyChar = (long)*ptr; } } #if TARGET_API_MAC_CARBON if (!strcmp (entry[i], kMenuSeparator)) { AppendMenuItemTextWithCFString (theMenu, CFSTR(""), kMenuItemAttrSeparator, 0, NULL); } else { CFStringRef itemString = 0; MenuItemAttributes itemAttribs = kMenuItemAttrIgnoreMeta; // Submenu if (!strncmp (entry[i], kMenuSubMenu, 2)) { if (idxSubmenu < nbSubMenus) { itemString = CFStringCreateWithCString (NULL, entry[i] + 2, kCFStringEncodingUTF8); InsertMenuItemTextWithCFString (theMenu, itemString, i+1, itemAttribs, 0); CFRelease (itemString); void *submenu = submenuEntry[idxSubmenu]->appendItems (offsetIdx); if (submenu) { SetMenuItemHierarchicalID (theMenu, i + 1, submenuEntry[idxSubmenu]->getMenuID ()); idxSubmenu++; continue; } else continue; } else continue; } //---Disable/Gray entry----------- else if (!strncmp (entry[i], kMenuDisable, 2)) { itemString = CFStringCreateWithCString (NULL, entry[i] + 2, kCFStringEncodingUTF8); itemAttribs |= kMenuItemAttrDisabled; } //---Disable entry-------- else if (!strncmp (entry[i], kMenuTitle, 2)) { itemString = CFStringCreateWithCString (NULL, entry[i] + 2, kCFStringEncodingUTF8); itemAttribs |= kMenuItemAttrSectionHeader; } else itemString = CFStringCreateWithCString (NULL, entry[i], kCFStringEncodingUTF8); InsertMenuItemTextWithCFString (theMenu, itemString, i+1, itemAttribs, 0); CFRelease (itemString); } #else Str255 menuItem; strcpy ((char*)menuItem, (const char*)"\p\0"); menuItem[0] = strlen ((const char*)text2); AppendMenu (theMenu, "\pjunk"); if (!strncmp (text2, kMenuTitle, 2) || !strncmp (text2, kMenuDisable, 2) || !strncmp (text2, kMenuSubMenu, 2)) { menuItem[0] -= 2; strcat ((char*)menuItem, text2 + 2); //---Disable item-------- if (!strncmp (text2, kMenuDisable, 2)) { #if TARGET_API_MAC_CARBON DisableMenuItem (theMenu, i + 1); #else DisableItem (theMenu, i + 1); #endif } //---Submenu------------- else if (!strncmp (text2, kMenuSubMenu, 2)) { if (idxSubmenu < nbSubMenus) { void *submenu = submenuEntry[idxSubmenu]->appendItems (offsetIdx); if (submenu) { SetMenuItemHierarchicalID (theMenu, i + 1, submenuEntry[idxSubmenu]->getMenuID ()); idxSubmenu++; } else continue; } else continue; } } else { strcat ((char*)menuItem, text2); } SetMenuItemText (theMenu, i + 1, menuItem); #endif // !TARGET_API_MAC_CARBON //---Set the shortcut if (keyChar != 0) { SetItemCmd (theMenu, i + 1, keyChar); if (useGlyph) SetMenuItemKeyGlyph (theMenu, i + 1, keyChar); SetMenuItemModifiers (theMenu, i + 1, keyModifiers); } if (multipleCheck && check[i]) CheckMenuItem (theMenu, i + 1, true); } // set the check if (style & kCheckStyle && !multipleCheck) CheckMenuItem (theMenu, currentIndex + 1, true); InsertMenu ((MenuHandle)theMenu, -1); platformControl = (void*)theMenu; return platformControl; #endif } //------------------------------------------------------------------------ void COptionMenu::setValue (float val) { if ((long)val < 0 || (long)val >= nbEntries) return; currentIndex = (long)val; if (style & (kMultipleCheckStyle & ~kCheckStyle)) check[currentIndex] = !check[currentIndex]; CParamDisplay::setValue (val); // to force the redraw setDirty (); } //------------------------------------------------------------------------ void COptionMenu::takeFocus (CDrawContext *pContext) { if (!getFrame ()) return; bool multipleCheck = style & (kMultipleCheckStyle & ~kCheckStyle); lastResult = -1; lastMenu = 0; #if MAC || WINDOWS // calculate Screen Position #if WINDOWS HWND hwnd = (HWND)getFrame ()->getSystemWindow (); #endif CRect rect; if (pContext) { rect.left = pContext->offsetScreen.h; rect.top = pContext->offsetScreen.v; } else { #if WINDOWS RECT rctWinParent; GetWindowRect (hwnd, &rctWinParent); rect.left = rctWinParent.left; rect.top = rctWinParent.top; #elif QUARTZ HIRect bounds; HIViewRef control = (HIViewRef)getFrame ()->getPlatformControl (); HIViewGetFrame (control, &bounds); WindowRef window = (WindowRef)getFrame ()->getSystemWindow (); WindowAttributes attr; GetWindowAttributes (window, &attr); if (attr & kWindowCompositingAttribute) { HIViewRef contentView; HIViewFindByID (HIViewGetRoot (window), kHIViewWindowContentID, &contentView); if (HIViewGetSuperview (control) != contentView) HIViewConvertRect (&bounds, control, contentView); bounds.origin.x += getFrame ()->hiScrollOffset.x; bounds.origin.y += getFrame ()->hiScrollOffset.y; } rect.left = (CCoord)bounds.origin.x; rect.top = (CCoord)bounds.origin.y; #endif } CView* parent = getParentView (); while (parent) { if (parent->notify (this, kMsgCheckIfViewContainer) == kMessageNotified) { CRect vSize; parent->getViewSize (vSize); rect.offset (vSize.left, vSize.top); } parent = parent->getParentView (); } #endif #if WINDOWS MSG msg; long result = -1; //---Create the popup menu--- long offIdx = 0; appendItems (offIdx); //---Popup the menu--- long offset; if (style & kPopupStyle) offset = (long)(rect.top + size.top); else offset = (long)(rect.top + size.bottom); int flags = TPM_LEFTALIGN; if (lastButton & kRButton) flags |= TPM_RIGHTBUTTON; if (TrackPopupMenu ((HMENU)platformControl, flags, (int)(rect.left + size.left), offset, 0, hwnd, 0)) { if (PeekMessage (&msg, hwnd, WM_COMMAND, WM_COMMAND, PM_REMOVE)) { if (HIWORD (msg.wParam) == 0) { result = LOWORD (msg.wParam); lastResult = result; } } } //---Destroy the menu---- removeItems (); //---Update the dependencies if (result != -1 || bgWhenClick) { CDrawContext *pContextTemp = 0; HDC hdc; if (!pContext && result != -1) { // create a local context hdc = GetDC (hwnd); pContextTemp = new CDrawContext (getFrame (), hdc, hwnd); } else pContextTemp = pContext; // to force the redraw if (bgWhenClick) setDirty (); if (result != -1) { long idx = 0; offIdx = 0; COptionMenu *menu = getItemMenu (result, idx, offIdx); if (menu) { lastMenu = menu; menu->setValue ((float)idx); // update dependency if (listener) listener->valueChanged (pContextTemp, menu); } } // redraw the display // AAAAARRRRGHHHHHHHHHHHHH!! //doIdleStuff (); //setDirty (false); if (!pContext && pContextTemp) { delete pContextTemp; ReleaseDC (hwnd, hdc); } } #elif MAC // no entries, no menu if (nbEntries == 0) { endEdit(); getFrame ()->setFocusView (0); return; } //---Transform local coordinates to global coordinates long offset; if (style & kPopupStyle) offset = (long)size.top; else offset = (long)size.bottom; CCoord gx = 0, gy = 0; Point LToG; getFrame()->getPosition(gx, gy); LToG.v = (short)(gy + rect.top + offset); LToG.h = (short)(gx + rect.left + size.left); //---Create the popup menu--- long offIdx = 0; MenuHandle theMenu = (MenuHandle)appendItems (offIdx); // Calculate the menu size (height and width) CalcMenuSize (theMenu); // Get a handle to the screen RgnHandle rgn = GetGrayRgn (); #if TARGET_API_MAC_CARBON Rect bounds; GetRegionBounds (rgn, &bounds); int bottom = bounds.bottom; long menuHeight = GetMenuHeight (theMenu); #else int bottom = (*rgn)->rgnBBox.bottom; long menuHeight = (*theMenu)->menuHeight; #endif // Calculate the size of one menu item (round to the next int) int menuItemSize = (menuHeight + nbEntries - 1) / nbEntries; setDirty (false); //---Popup the Menu long popUpItem = 1; long PopUpMenuItem = 0; if (LToG.v + menuHeight >= bottom - menuItemSize / 2) { if (nbEntries * menuItemSize >= bottom) { popUpItem = currentIndex + 2; if (popUpItem > nbEntries) popUpItem = nbEntries; } if (nbEntries * menuItemSize >= bottom) PopUpMenuItem = PopUpMenuSelect (theMenu, LToG.v, LToG.h, popUpItem); else PopUpMenuItem = PopUpMenuSelect (theMenu, bottom - menuHeight - menuItemSize, LToG.h, popUpItem); } else PopUpMenuItem = PopUpMenuSelect (theMenu, LToG.v, LToG.h, popUpItem); //---Destroy the menu---- removeItems (); // HiWord indicates MenuID, LoWord indicates the item index short result = LoWord (PopUpMenuItem) - 1; lastResult = result; short menuIDResult = HiWord (PopUpMenuItem); if (menuIDResult != 0 || bgWhenClick) { // to force the redraw if (bgWhenClick) setDirty (); if (!pContext && menuIDResult != 0) { pContext = getFrame ()->createDrawContext (); } else if (pContext) pContext->remember (); if (menuIDResult != 0) { long idx = 0; offIdx = menuIDResult; COptionMenu *menu = getItemMenu (result, idx, offIdx); if (menu) { lastMenu = menu; menu->setValue (result); if (listener) listener->valueChanged (pContext, menu); } } if (pContext) pContext->forget (); } #elif MOTIF Arg args[10]; int n = 0; // get the position of the pParent CRect rect; getFrame ()->getSize (&rect); if (pContext) { rect.left += pContext->offset.h; rect.top += pContext->offset.v; } // create a popup menu int offset; if (style & kPopupStyle) offset = (int)(rect.top + size.top); else offset = (int)(rect.top + size.bottom); XtSetArg (args[n], XmNx, rect.left + size.left); n++; XtSetArg (args[n], XmNy, offset); n++; XtSetArg (args[n], XmNmenuHistory, currentIndex); n++; XtSetArg (args[n], XmNtraversalOn, true); n++; platformControl = (void*)XmCreatePopupMenu ((Widget)(getFrame ()->getSystemWindow ()), "popup", args, n); XtAddCallback ((Widget)platformControl, XmNunmapCallback, _unmapCallback, this); // insert the menu items for (long i = 0; i < nbEntries; i++) { if (!strcmp (entry[i], kMenuSeparator)) { itemWidget[i] = (void*)XtCreateManagedWidget ("separator", xmSeparatorGadgetClass, (Widget)platformControl, 0, 0); } else { if (multipleCheck) { itemWidget[i] = (void*)XtVaCreateManagedWidget (entry[i], xmToggleButtonWidgetClass, (Widget)platformControl, XmNset, check[i], XmNvisibleWhenOff, false, 0); XtAddCallback ((Widget)itemWidget[i], XmNvalueChangedCallback, _activateCallback, this); } else if (style & kCheckStyle) { itemWidget[i] = (void*)XtVaCreateManagedWidget (entry[i], xmToggleButtonWidgetClass, (Widget)platformControl, XmNset, (i == currentIndex) ? true : false, XmNvisibleWhenOff, false, 0); XtAddCallback ((Widget)itemWidget[i], XmNvalueChangedCallback, _activateCallback, this); } else { itemWidget[i] = (void*)XtVaCreateManagedWidget (entry[i], xmPushButtonWidgetClass, (Widget)platformControl, 0); XtAddCallback ((Widget)itemWidget[i], XmNactivateCallback, _activateCallback, this); } } } XtManageChild ((Widget)platformControl); #elif BEOS BPopUpMenu* popup = new BPopUpMenu ("popup", false, false); BMessage* message; BMenuItem* item; for (long i = 0; i < nbEntries; i++) { if (strcmp (entry[i], kMenuSeparator) == 0) popup->AddSeparatorItem (); else { message = new BMessage (i); item = new BMenuItem (entry[i], message); popup->AddItem (item); if (multipleCheck) { if (check[i]) item->SetMarked (true); } else if ((style & kCheckStyle) && currentIndex == i) item->SetMarked (true); } } CRect rect; getFrame ()->getSize (&rect); if (pContext) { rect.left += pContext->offset.h; rect.top += pContext->offset.v; } long offset; if (style & kPopupStyle) offset = (int)(rect.top + size.top); else offset = (int)(rect.top + size.bottom); BPoint where (rect.left + size.left, offset); BView* plugView = (BView*) getFrame ()->getSystemWindow (); plugView->ConvertToScreen (&where); item = popup->Go (where); if (item) { message = item->Message (); if (message) { CDrawContext *pContextTemp = pContext; // create a local context if (!pContextTemp) pContextTemp = new CDrawContext (getFrame (), plugView, NULL); setValue (message->what); if (listener) listener->valueChanged (pContextTemp, this); // redraw the display draw (pContextTemp); if (!pContext && pContextTemp) delete pContextTemp; } } delete popup; #endif getFrame ()->setFocusView (0); endEdit(); } //------------------------------------------------------------------------ void COptionMenu::looseFocus (CDrawContext *pContext) { if (platformControl == 0) return; #if WINDOWS #elif MAC #elif MOTIF for (long i = 0; i < nbEntries; i++) if (itemWidget[i]) XtDestroyWidget ((Widget)itemWidget[i]); if (platformControl) { XtUnmanageChild ((Widget)platformControl); XtDestroyWidget ((Widget)platformControl); } #endif platformControl = 0; } #if MOTIF //------------------------------------------------------------------------ void COptionMenu::setCurrentSelected (void *itemSelected) { // retrieve the current index if (itemSelected != 0) { for (long i = 0; i < nbEntries; i++) if (itemWidget[i] == itemSelected) { currentIndex = i; break; } } // update dependency CDrawContext *pContext = new CDrawContext (getFrame (), (void*)getFrame ()->getGC (), (void*)getFrame ()->getWindow ()); setValue (currentIndex); if (listener) listener->valueChanged (pContext, this); delete pContext; } #endif //------------------------------------------------------------------------ // CAnimKnob //------------------------------------------------------------------------ /*! @class CAnimKnob Such as a CKnob control object, but there is a unique pixmap which contains different views (subpixmaps) of this knob. According to the value, a specific subpixmap is displayed. The different subpixmaps are stacked in the pixmap object. */ CAnimKnob::CAnimKnob (const CRect &size, CControlListener *listener, long tag, CBitmap *background, CPoint &offset) : CKnob (size, listener, tag, background, 0, offset), bInverseBitmap (false) { heightOfOneImage = size.height (); subPixmaps = (short)(background->getHeight () / heightOfOneImage); inset = 0; } //------------------------------------------------------------------------ CAnimKnob::CAnimKnob (const CRect &size, CControlListener *listener, long tag, long subPixmaps, // number of subPixmaps CCoord heightOfOneImage, // height of one image in pixel CBitmap *background, CPoint &offset) : CKnob (size, listener, tag, background, 0, offset), subPixmaps (subPixmaps), heightOfOneImage (heightOfOneImage), bInverseBitmap (false) { inset = 0; } //------------------------------------------------------------------------ CAnimKnob::~CAnimKnob () {} //----------------------------------------------------------------------------------------------- bool CAnimKnob::isDirty () const { if (!bDirty) { CPoint p; valueToPoint (p); if (p == lastDrawnPoint) return false; } return CKnob::isDirty (); } //------------------------------------------------------------------------ void CAnimKnob::draw (CDrawContext *pContext) { CPoint where (0, 0); if (value >= 0.f) { CCoord tmp = heightOfOneImage * (subPixmaps - 1); if (bInverseBitmap) where.v = (long)((1 - value) * (float)tmp); else where.v = (long)(value * (float)tmp); for (CCoord realY = 0; realY <= tmp; realY += heightOfOneImage) { if (where.v < realY) { where.v = realY - heightOfOneImage; if (where.v < 0) where.v = 0; break; } } } if (pBackground) { if (bTransparencyEnabled) pBackground->drawTransparent (pContext, size, where); else pBackground->draw (pContext, size, where); } valueToPoint (lastDrawnPoint); setDirty (false); } //------------------------------------------------------------------------ // CVerticalSwitch //------------------------------------------------------------------------ /*! @class CVerticalSwitch Define a switch with a given number of positions, the current position is defined by the position of the last click on this object (the object is divided in its height by the number of position). Each position has its subpixmap, each subpixmap is stacked in the given handle pixmap. By clicking Alt+Left Mouse the default value is used. */ CVerticalSwitch::CVerticalSwitch (const CRect &size, CControlListener *listener, long tag, CBitmap *background, CPoint &offset) : CControl (size, listener, tag, background), offset (offset) { heightOfOneImage = size.height (); subPixmaps = (long)(background->getHeight () / heightOfOneImage); iMaxPositions = subPixmaps; setDefaultValue (0.f); } //------------------------------------------------------------------------ CVerticalSwitch::CVerticalSwitch (const CRect &size, CControlListener *listener, long tag, long subPixmaps, // number of subPixmaps CCoord heightOfOneImage, // height of one image in pixel long iMaxPositions, CBitmap *background, CPoint &offset) : CControl (size, listener, tag, background), offset (offset), subPixmaps (subPixmaps), heightOfOneImage (heightOfOneImage), iMaxPositions (iMaxPositions) { setDefaultValue (0.f); } //------------------------------------------------------------------------ CVerticalSwitch::~CVerticalSwitch () {} //------------------------------------------------------------------------ void CVerticalSwitch::draw (CDrawContext *pContext) { if (pBackground) { // source position in bitmap CPoint where (0, heightOfOneImage * ((long)(value * (iMaxPositions - 1) + 0.5f))); if (bTransparencyEnabled) pBackground->drawTransparent (pContext, size, where); else pBackground->draw (pContext, size, where); } setDirty (false); } //------------------------------------------------------------------------ void CVerticalSwitch::mouse (CDrawContext *pContext, CPoint &where, long button) { if (!bMouseEnabled) return; if (button == -1) button = pContext->getMouseButtons (); if (!(button & kLButton)) return; if (listener && button & (kAlt | kShift | kControl | kApple)) { if (listener->controlModifierClicked (pContext, this, button) != 0) return; } // check if default value wanted if (checkDefaultValue (pContext, button)) return; double coef = (double)heightOfOneImage / (double)iMaxPositions; // begin of edit parameter beginEdit (); do { value = (long)((where.v - size.top) / coef) / (float)(iMaxPositions - 1); if (value > 1.f) value = 1.f; else if (value < 0.f) value = 0.f; if (isDirty () && listener) listener->valueChanged (pContext, this); getMouseLocation (pContext, where); doIdleStuff (); } while (pContext->getMouseButtons () == button); // end of edit parameter endEdit (); } //------------------------------------------------------------------------ // CHorizontalSwitch //------------------------------------------------------------------------ /*! @class CHorizontalSwitch Same as the CVerticalSwitch but horizontal. */ CHorizontalSwitch::CHorizontalSwitch (const CRect &size, CControlListener *listener, long tag, CBitmap *background, CPoint &offset) : CControl (size, listener, tag, background), offset (offset) { heightOfOneImage = size.width (); subPixmaps = (long)(background->getWidth () / heightOfOneImage); iMaxPositions = subPixmaps; setDefaultValue (0.f); } //------------------------------------------------------------------------ CHorizontalSwitch::CHorizontalSwitch (const CRect &size, CControlListener *listener, long tag, long subPixmaps, // number of subPixmaps CCoord heightOfOneImage, // height of one image in pixel long iMaxPositions, CBitmap *background, CPoint &offset) : CControl (size, listener, tag, background), offset (offset), subPixmaps (subPixmaps), heightOfOneImage (heightOfOneImage), iMaxPositions (iMaxPositions) { setDefaultValue (0.f); } //------------------------------------------------------------------------ CHorizontalSwitch::~CHorizontalSwitch () {} //------------------------------------------------------------------------ void CHorizontalSwitch::draw (CDrawContext *pContext) { if (pBackground) { // source position in bitmap CPoint where (0, heightOfOneImage * ((long)(value * (iMaxPositions - 1) + 0.5f))); if (bTransparencyEnabled) pBackground->drawTransparent (pContext, size, where); else pBackground->draw (pContext, size, where); } setDirty (false); } //------------------------------------------------------------------------ void CHorizontalSwitch::mouse (CDrawContext *pContext, CPoint &where, long button) { if (!bMouseEnabled) return; if (button == -1) button = pContext->getMouseButtons (); if (listener && button & (kAlt | kShift | kControl | kApple)) { if (listener->controlModifierClicked (pContext, this, button) != 0) return; } if (!(button & kLButton)) return; // check if default value wanted if (checkDefaultValue (pContext, button)) return; double coef = (double)pBackground->getWidth () / (double)iMaxPositions; // begin of edit parameter beginEdit (); do { value = (long)((where.h - size.left) / coef) / (float)(iMaxPositions - 1); if (value > 1.f) value = 1.f; else if (value < 0.f) value = 0.f; if (isDirty () && listener) listener->valueChanged (pContext, this); getMouseLocation (pContext, where); doIdleStuff (); } while (pContext->getMouseButtons () == button); // end of edit parameter endEdit (); } //------------------------------------------------------------------------ // CRockerSwitch //------------------------------------------------------------------------ /*! @class CRockerSwitch Define a rocker switch with 3 states using 3 subpixmaps. One click on its leftside, then the first subpixmap is displayed. One click on its rightside, then the third subpixmap is displayed. When the mouse button is relaxed, the second subpixmap is framed. */ CRockerSwitch::CRockerSwitch (const CRect &size, CControlListener *listener, long tag, // identifier tag (ID) CBitmap *background, CPoint &offset, const long style) : CControl (size, listener, tag, background), offset (offset), style (style) { heightOfOneImage = size.width (); } //------------------------------------------------------------------------ CRockerSwitch::CRockerSwitch (const CRect &size, CControlListener *listener, long tag, // identifier tag (ID) CCoord heightOfOneImage, // height of one image in pixel CBitmap *background, CPoint &offset, const long style) : CControl (size, listener, tag, background), offset (offset), heightOfOneImage (heightOfOneImage), style (style) {} //------------------------------------------------------------------------ CRockerSwitch::~CRockerSwitch () {} //------------------------------------------------------------------------ void CRockerSwitch::draw (CDrawContext *pContext) { CPoint where (offset.h, offset.v); if (value == 1.f) where.v += 2 * heightOfOneImage; else if (value == 0.f) where.v += heightOfOneImage; if (pBackground) { if (bTransparencyEnabled) pBackground->drawTransparent (pContext, size, where); else pBackground->draw (pContext, size, where); } setDirty (false); } //------------------------------------------------------------------------ void CRockerSwitch::mouse (CDrawContext *pContext, CPoint &where, long button) { if (!bMouseEnabled) return; if (button == -1) button = pContext->getMouseButtons (); if (listener && button & (kAlt | kShift | kControl | kApple)) { if (listener->controlModifierClicked (pContext, this, button) != 0) return; } if (!(button & kLButton)) return; float fEntryState = value; CCoord width_2 = size.width () / 2; CCoord height_2 = size.height () / 2; // begin of edit parameter beginEdit (); if (button) { do { if (style & kHorizontal) { if (where.h >= size.left && where.v >= size.top && where.h <= (size.left + width_2) && where.v <= size.bottom) value = -1.0f; else if (where.h >= (size.left + width_2) && where.v >= size.top && where.h <= size.right && where.v <= size.bottom) value = 1.0f; else value = fEntryState; } else { if (where.h >= size.left && where.v >= size.top && where.h <= size.right && where.v <= (size.top + height_2)) value = -1.0f; else if (where.h >= size.left && where.v >= (size.top + height_2) && where.h <= size.right && where.v <= size.bottom) value = 1.0f; else value = fEntryState; } if (isDirty () && listener) listener->valueChanged (pContext, this); getMouseLocation (pContext, where); doIdleStuff (); } while (pContext->getMouseButtons ()); } else { if (where.h >= size.left && where.v >= size.top && where.h <= (size.left + width_2) && where.v <= size.bottom) value = -1.0f; else if (where.h >= (size.left + width_2) && where.v >= size.top && where.h <= size.right && where.v <= size.bottom) value = 1.0f; if (listener) listener->valueChanged (pContext, this); } value = 0.f; // set button to UNSELECTED state if (listener) listener->valueChanged (pContext, this); // end of edit parameter endEdit (); } //------------------------------------------------------------------------ bool CRockerSwitch::onWheel (CDrawContext *pContext, const CPoint &where, float distance) { if (!bMouseEnabled) return false; if (distance > 0) value = -1.0f; else value = 1.0f; // begin of edit parameter beginEdit (); if (isDirty () && listener) listener->valueChanged (pContext, this); value = 0.0f; // set button to UNSELECTED state if (listener) listener->valueChanged (pContext, this); // end of edit parameter endEdit (); return true; } //------------------------------------------------------------------------ // CMovieBitmap //------------------------------------------------------------------------ /*! @class CMovieBitmap A movie pixmap allows to display different subpixmaps according to its current value. */ CMovieBitmap::CMovieBitmap (const CRect &size, CControlListener *listener, long tag, CBitmap *background, CPoint &offset) : CControl (size, listener, tag, background), offset (offset), subPixmaps (subPixmaps), heightOfOneImage (heightOfOneImage) { heightOfOneImage = size.height (); subPixmaps = (long)(background->getHeight () / heightOfOneImage); } //------------------------------------------------------------------------ CMovieBitmap::CMovieBitmap (const CRect &size, CControlListener *listener, long tag, long subPixmaps, // number of subPixmaps CCoord heightOfOneImage, // height of one image in pixel CBitmap *background, CPoint &offset) : CControl (size, listener, tag, background), offset (offset), subPixmaps (subPixmaps), heightOfOneImage (heightOfOneImage) {} //------------------------------------------------------------------------ CMovieBitmap::~CMovieBitmap () {} //------------------------------------------------------------------------ void CMovieBitmap::draw (CDrawContext *pContext) { CPoint where (offset.h, offset.v); if (value > 1.0f) value = 1.0f; if (value > 0.0f) where.v += heightOfOneImage * (int)(value * (subPixmaps - 1) + 0.5); if (pBackground) { if (bTransparencyEnabled) pBackground->drawTransparent (pContext, size, where); else pBackground->draw (pContext, size, where); } setDirty (false); } //------------------------------------------------------------------------ // CMovieButton //------------------------------------------------------------------------ /*! @class CMovieButton A movie button is a bi-states button with 2 subpixmaps. These subpixmaps are stacked in the pixmap. */ CMovieButton::CMovieButton (const CRect &size, CControlListener *listener, long tag, // identifier tag (ID) CBitmap *background, CPoint &offset) : CControl (size, listener, tag, background), offset (offset), buttonState (value) { heightOfOneImage = size.height (); } //------------------------------------------------------------------------ CMovieButton::CMovieButton (const CRect &size, CControlListener *listener, long tag, CCoord heightOfOneImage, // height of one image in pixel CBitmap *background, CPoint &offset) : CControl (size, listener, tag, background), offset (offset), heightOfOneImage (heightOfOneImage), buttonState (value) {} //------------------------------------------------------------------------ CMovieButton::~CMovieButton () {} //------------------------------------------------------------------------ void CMovieButton::draw (CDrawContext *pContext) { CPoint where; where.h = 0; bounceValue (); if (value) where.v = heightOfOneImage; else where.v = 0; if (pBackground) { if (bTransparencyEnabled) pBackground->drawTransparent (pContext, size, where); else pBackground->draw (pContext, size, where); } buttonState = value; setDirty (false); } //------------------------------------------------------------------------ void CMovieButton::mouse (CDrawContext *pContext, CPoint &where, long button) { if (!bMouseEnabled) return; if (button == -1) button = pContext->getMouseButtons (); if (listener && button & (kAlt | kShift | kControl | kApple)) { if (listener->controlModifierClicked (pContext, this, button) != 0) return; } if (!(button & kLButton)) return; // this simulates a real windows button float fEntryState = value; // begin of edit parameter beginEdit (); if (pContext->getMouseButtons ()) { do { if (where.h >= size.left && where.v >= size.top && where.h <= size.right && where.v <= size.bottom) value = !fEntryState; else value = fEntryState; if (isDirty () && listener) listener->valueChanged (pContext, this); getMouseLocation (pContext, where); doIdleStuff (); } while (pContext->getMouseButtons () == button); } else { value = !value; if (listener) listener->valueChanged (pContext, this); } // end of edit parameter endEdit (); buttonState = value; } //------------------------------------------------------------------------ // CAutoAnimation //------------------------------------------------------------------------ /*! @class CAutoAnimation An auto-animation control contains a given number of subpixmap which can be displayed in loop. Two functions allows to get the previous or the next subpixmap (these functions increase or decrease the current value of this control). */ // displays bitmaps within a (child-) window CAutoAnimation::CAutoAnimation (const CRect &size, CControlListener *listener, long tag, CBitmap *background, CPoint &offset) : CControl (size, listener, tag, background), offset (offset), bWindowOpened (false) { heightOfOneImage = size.height (); subPixmaps = (long)(background->getHeight () / heightOfOneImage); totalHeightOfBitmap = heightOfOneImage * subPixmaps; } //------------------------------------------------------------------------ CAutoAnimation::CAutoAnimation (const CRect &size, CControlListener *listener, long tag, long subPixmaps, // number of subPixmaps... CCoord heightOfOneImage, // height of one image in pixel CBitmap *background, CPoint &offset) : CControl (size, listener, tag, background), offset (offset), subPixmaps (subPixmaps), heightOfOneImage (heightOfOneImage), bWindowOpened (false) { totalHeightOfBitmap = heightOfOneImage * subPixmaps; } //------------------------------------------------------------------------ CAutoAnimation::~CAutoAnimation () {} //------------------------------------------------------------------------ void CAutoAnimation::draw (CDrawContext *pContext) { if (isWindowOpened ()) { CPoint where; where.v = (long)value + offset.v; where.h = offset.h; if (pBackground) { if (bTransparencyEnabled) pBackground->drawTransparent (pContext, size, where); else pBackground->draw (pContext, size, where); } } setDirty (false); } //------------------------------------------------------------------------ void CAutoAnimation::mouse (CDrawContext *pContext, CPoint &where, long button) { if (!bMouseEnabled) return; if (button == -1) button = pContext->getMouseButtons (); if (listener && button & (kAlt | kShift | kControl | kApple)) { if (listener->controlModifierClicked (pContext, this, button) != 0) return; } if (!(button & kLButton)) return; if (!isWindowOpened ()) { value = 0; openWindow (); setDirty (); // force to redraw if (listener) listener->valueChanged (pContext, this); } else { // stop info animation value = 0; // draw first pic of bitmap setDirty (); closeWindow (); } } //------------------------------------------------------------------------ void CAutoAnimation::openWindow () { bWindowOpened = true; } //------------------------------------------------------------------------ void CAutoAnimation::closeWindow () { bWindowOpened = false; } //------------------------------------------------------------------------ void CAutoAnimation::nextPixmap () { value += heightOfOneImage; if (value >= (totalHeightOfBitmap - heightOfOneImage)) value = 0; } //------------------------------------------------------------------------ void CAutoAnimation::previousPixmap () { value -= heightOfOneImage; if (value < 0.f) value = (float)(totalHeightOfBitmap - heightOfOneImage - 1); } //------------------------------------------------------------------------ // CSlider //------------------------------------------------------------------------ /*! @class CSlider Define a slider with a given background and handle. The range of variation of the handle should be defined. By default the handler is drawn with transparency (white color). By clicking Alt+Left Mouse the default value is used. */ CSlider::CSlider (const CRect &rect, CControlListener *listener, long tag, long iMinPos, // min position in pixel long iMaxPos, // max position in pixel CBitmap *handle, // bitmap of slider CBitmap *background, // bitmap of background CPoint &offset, // offset in the background const long style) // style (kBottom,kRight,kTop,kLeft,kHorizontal,kVertical) : CControl (rect, listener, tag, background), offset (offset), pHandle (handle), pOScreen (0), style (style), bFreeClick (true) { setDrawTransparentHandle (true); if (pHandle) { pHandle->remember (); widthOfSlider = pHandle->getWidth (); heightOfSlider = pHandle->getHeight (); } else { widthOfSlider = 1; heightOfSlider = 1; } widthControl = size.width (); heightControl = size.height (); if (style & kHorizontal) { minPos = iMinPos - size.left; rangeHandle = iMaxPos - iMinPos; CPoint p (0, 0); setOffsetHandle (p); } else { minPos = iMinPos - size.top; rangeHandle = iMaxPos - iMinPos; CPoint p (0, 0); setOffsetHandle (p); } zoomFactor = 10.f; setWantsFocus (true); } //------------------------------------------------------------------------ CSlider::CSlider (const CRect &rect, CControlListener *listener, long tag, CPoint &offsetHandle, // handle offset long _rangeHandle, // size of handle range CBitmap *handle, // bitmap of slider CBitmap *background, // bitmap of background CPoint &offset, // offset in the background const long style) // style (kBottom,kRight,kTop,kLeft,kHorizontal,kVertical) : CControl (rect, listener, tag, background), offset (offset), pHandle (handle), pOScreen (0), style (style), minPos (0), bFreeClick (true) { setDrawTransparentHandle (true); if (pHandle) { pHandle->remember (); widthOfSlider = pHandle->getWidth (); heightOfSlider = pHandle->getHeight (); } else { widthOfSlider = 1; heightOfSlider = 1; } widthControl = size.width (); heightControl = size.height (); if (style & kHorizontal) rangeHandle = _rangeHandle - widthOfSlider; else rangeHandle = _rangeHandle - heightOfSlider; setOffsetHandle (offsetHandle); zoomFactor = 10.f; setWantsFocus (true); } //------------------------------------------------------------------------ CSlider::~CSlider () { if (pHandle) pHandle->forget (); } //------------------------------------------------------------------------ void CSlider::setOffsetHandle (CPoint &val) { offsetHandle = val; if (style & kHorizontal) { minTmp = offsetHandle.h + minPos; maxTmp = minTmp + rangeHandle + widthOfSlider; } else { minTmp = offsetHandle.v + minPos; maxTmp = minTmp + rangeHandle + heightOfSlider; } } //----------------------------------------------------------------------------- bool CSlider::attached (CView *parent) { if (pOScreen) delete pOScreen; #if !MACX pOScreen = new COffscreenContext (getFrame (), widthControl, heightControl, kBlackCColor); #endif return CControl::attached (parent); } //----------------------------------------------------------------------------- bool CSlider::removed (CView *parent) { if (pOScreen) { delete pOScreen; pOScreen = 0; } return CControl::removed (parent); } //------------------------------------------------------------------------ void CSlider::draw (CDrawContext *pContext) { CDrawContext* drawContext = pOScreen ? pOScreen : pContext; #if 1 if (pOScreen && bTransparencyEnabled) pOScreen->copyTo (pContext, size); #endif float fValue; if (style & kLeft || style & kTop) fValue = value; else fValue = 1.f - value; // (re)draw background CRect rect (0, 0, widthControl, heightControl); if (!pOScreen) rect.offset (size.left, size.top); if (pBackground) { if (bTransparencyEnabled) pBackground->drawTransparent (drawContext, rect, offset); else pBackground->draw (drawContext, rect, offset); } // calc new coords of slider CRect rectNew; if (style & kHorizontal) { rectNew.top = offsetHandle.v; rectNew.bottom = rectNew.top + heightOfSlider; rectNew.left = offsetHandle.h + (int)(fValue * rangeHandle); rectNew.left = (rectNew.left < minTmp) ? minTmp : rectNew.left; rectNew.right = rectNew.left + widthOfSlider; rectNew.right = (rectNew.right > maxTmp) ? maxTmp : rectNew.right; } else { rectNew.left = offsetHandle.h; rectNew.right = rectNew.left + widthOfSlider; rectNew.top = offsetHandle.v + (int)(fValue * rangeHandle); rectNew.top = (rectNew.top < minTmp) ? minTmp : rectNew.top; rectNew.bottom = rectNew.top + heightOfSlider; rectNew.bottom = (rectNew.bottom > maxTmp) ? maxTmp : rectNew.bottom; } if (!pOScreen) rectNew.offset (size.left, size.top); // draw slider at new position if (pHandle) { if (bDrawTransparentEnabled) pHandle->drawTransparent (drawContext, rectNew); else pHandle->draw (drawContext, rectNew); } if (pOScreen) pOScreen->copyFrom (pContext, size); setDirty (false); } //------------------------------------------------------------------------ void CSlider::mouse (CDrawContext *pContext, CPoint &where, long button) { if (!bMouseEnabled) return; if (button == -1) button = pContext->getMouseButtons (); if (listener && button & (kAlt | kShift | kControl | kApple)) { if (listener->controlModifierClicked (pContext, this, button) != 0) return; } // check if default value wanted if (checkDefaultValue (pContext, button)) return; // allow left mousebutton only if (!(button & kLButton)) return; CCoord delta; if (style & kHorizontal) delta = size.left + offsetHandle.h; else delta = size.top + offsetHandle.v; if (!bFreeClick) { float fValue; if (style & kLeft || style & kTop) fValue = value; else fValue = 1.f - value; CCoord actualPos; CRect rect; if (style & kHorizontal) { actualPos = offsetHandle.h + (int)(fValue * rangeHandle) + size.left; rect.left = actualPos; rect.top = size.top + offsetHandle.v; rect.right = rect.left + widthOfSlider; rect.bottom = rect.top + heightOfSlider; if (!where.isInside (rect)) return; else delta += where.h - actualPos; } else { actualPos = offsetHandle.v + (int)(fValue * rangeHandle) + size.top; rect.left = size.left + offsetHandle.h; rect.top = actualPos; rect.right = rect.left + widthOfSlider; rect.bottom = rect.top + heightOfSlider; if (!where.isInside (rect)) return; else delta += where.v - actualPos; } } else { if (style & kHorizontal) delta += widthOfSlider / 2 - 1; else delta += heightOfSlider / 2 - 1; } float oldVal = value; long oldButton = button; // begin of edit parameter beginEdit (); while (1) { button = pContext->getMouseButtons (); if (!(button & kLButton)) break; if ((oldButton != button) && (button & kShift)) { oldVal = value; oldButton = button; } else if (!(button & kShift)) oldVal = value; if (style & kHorizontal) value = (float)(where.h - delta) / (float)rangeHandle; else value = (float)(where.v - delta) / (float)rangeHandle; if (style & kRight || style & kBottom) value = 1.f - value; if (button & kShift) value = oldVal + ((value - oldVal) / zoomFactor); bounceValue (); if (isDirty () && listener) listener->valueChanged (pContext, this); getMouseLocation (pContext, where); doIdleStuff (); } // end of edit parameter endEdit (); } //------------------------------------------------------------------------ bool CSlider::onWheel (CDrawContext *pContext, const CPoint &where, float distance) { if (!bMouseEnabled) return false; long buttons = pContext->getMouseButtons (); if (buttons & kShift) value += 0.1f * distance * wheelInc; else value += distance * wheelInc; bounceValue (); if (isDirty () && listener) { // begin of edit parameter beginEdit (); listener->valueChanged (pContext, this); // end of edit parameter endEdit (); } return true; } //------------------------------------------------------------------------ long CSlider::onKeyDown (VstKeyCode& keyCode) { switch (keyCode.virt) { case VKEY_UP : case VKEY_RIGHT : case VKEY_DOWN : case VKEY_LEFT : { float distance = 1.f; if (keyCode.virt == VKEY_DOWN || keyCode.virt == VKEY_LEFT) distance = -distance; if (keyCode.modifier & MODIFIER_SHIFT) value += 0.1f * distance * wheelInc; else value += distance * wheelInc; bounceValue (); if (isDirty () && listener) { // begin of edit parameter beginEdit (); listener->valueChanged (0, this); // end of edit parameter endEdit (); } } return 1; } return -1; } //------------------------------------------------------------------------ void CSlider::setHandle (CBitmap *_pHandle) { if (pHandle) pHandle->forget (); pHandle = _pHandle; if (pHandle) { pHandle->remember (); widthOfSlider = pHandle->getWidth (); heightOfSlider = pHandle->getHeight (); } } //------------------------------------------------------------------------ // CVerticalSlider //------------------------------------------------------------------------ /*! @class CVerticalSlider This is the vertical slider. See CSlider. */ CVerticalSlider::CVerticalSlider (const CRect &rect, CControlListener *listener, long tag, long iMinPos, // min position in pixel long iMaxPos, // max position in pixel CBitmap *handle, // bitmap of slider CBitmap *background, // bitmap of background CPoint &offset, // offset in the background const long style) // style (kLeft, kRight) : CSlider (rect, listener, tag, iMinPos, iMaxPos, handle, background, offset, style|kVertical) {} //------------------------------------------------------------------------ CVerticalSlider::CVerticalSlider (const CRect &rect, CControlListener *listener, long tag, CPoint &offsetHandle, // handle offset long rangeHandle, // size of handle range CBitmap *handle, // bitmap of slider CBitmap *background, // bitmap of background CPoint &offset, // offset in the background const long style) // style (kLeft, kRight) : CSlider (rect, listener, tag, offsetHandle, rangeHandle, handle, background, offset, style|kVertical) {} //------------------------------------------------------------------------ // CHorizontalSlider //------------------------------------------------------------------------ /*! @class CHorizontalSlider This is the horizontal slider. See CSlider. */ CHorizontalSlider::CHorizontalSlider (const CRect &rect, CControlListener *listener, long tag, long iMinPos, // min Y position in pixel long iMaxPos, // max Y position in pixel CBitmap *handle, // bitmap of slider CBitmap *background, // bitmap of background CPoint &offset, // offset in the background const long style) // style (kLeft, kRight) : CSlider (rect, listener, tag, iMinPos, iMaxPos, handle, background, offset, style|kHorizontal) {} //------------------------------------------------------------------------ CHorizontalSlider::CHorizontalSlider (const CRect &rect, CControlListener *listener, long tag, CPoint &offsetHandle, // handle offset long rangeHandle, // size of handle range CBitmap *handle, // bitmap of slider CBitmap *background, // bitmap of background CPoint &offset, // offset in the background const long style) // style (kLeft, kRight) : CSlider (rect, listener, tag, offsetHandle, rangeHandle, handle, background, offset, style|kHorizontal) {} //------------------------------------------------------------------------ // CSpecialDigit //------------------------------------------------------------------------ /*! @class CSpecialDigit Can be used to display a counter with maximum 7 digits. All digit have the same size and are stacked in height in the pixmap. */ CSpecialDigit::CSpecialDigit (const CRect &size, CControlListener *listener, long tag, // tag identifier long dwPos, // actual value long iNumbers, // amount of numbers (max 7) long *xpos, // array of all XPOS long *ypos, // array of all YPOS long width, // width of ONE number long height, // height of ONE number CBitmap *background) // bitmap numbers : CControl (size, listener, tag, background), iNumbers (iNumbers), width (width), height (height) { setValue ((float)dwPos); // actual value if (iNumbers > 7) iNumbers = 7; if (xpos == NULL) { // automatically init xpos/ypos if not provided by caller const int numw = (const int)background->getWidth(); int x = (int)size.left; for (long i = 0; i < iNumbers; i++) { this->xpos[i] = x; this->ypos[i] = (long)size.top; x += numw; } } else { // store coordinates of x/y pos of each digit for (long i = 0; i < iNumbers; i++) { this->xpos[i] = xpos[i]; this->ypos[i] = ypos[i]; } } setMax ((float)pow (10., (double)iNumbers) - 1.0f); setMin (0.0f); } //------------------------------------------------------------------------ CSpecialDigit::~CSpecialDigit () {} //------------------------------------------------------------------------ void CSpecialDigit::draw (CDrawContext *pContext) { CPoint where; CRect rectDest; long i, j; long dwValue; long one_digit[16]; if ((long)value >= getMax ()) dwValue = (long)getMax (); else if ((long)value < getMin ()) dwValue = (long)getMin (); else dwValue = (long)value; for (i = 0, j = ((long)getMax () + 1) / 10; i < iNumbers; i++, j /= 10) { one_digit[i] = dwValue / j; dwValue -= (one_digit[i] * j); } where.h = 0; for (i = 0; i < iNumbers; i++) { j = one_digit[i]; if (j > 9) j = 9; rectDest.left = xpos[i]; rectDest.top = ypos[i]; rectDest.right = rectDest.left + width; rectDest.bottom = rectDest.top + height; // where = src from bitmap where.v = j * height; if (pBackground) { if (bTransparencyEnabled) pBackground->drawTransparent (pContext, rectDest, where); else pBackground->draw (pContext, rectDest, where); } } setDirty (false); } //------------------------------------------------------------------------ float CSpecialDigit::getNormValue () const { float fTemp; fTemp = value / getMax (); if (fTemp > 1.0f) fTemp = 1.0f; else if (fTemp < 0.0f) fTemp = 0.0f; return fTemp; } //------------------------------------------------------------------------ // CKickButton //------------------------------------------------------------------------ /*! @class CKickButton Define a button with 2 states using 2 subpixmaps. One click on it, then the second subpixmap is displayed. When the mouse button is relaxed, the first subpixmap is framed. */ CKickButton::CKickButton (const CRect &size, CControlListener *listener, long tag, CBitmap *background, CPoint &offset) : CControl (size, listener, tag, background), offset (offset) { heightOfOneImage = size.height (); } //------------------------------------------------------------------------ CKickButton::CKickButton (const CRect &size, CControlListener *listener, long tag, CCoord heightOfOneImage, // height of one image in pixel CBitmap *background, CPoint &offset) : CControl (size, listener, tag, background), offset (offset), heightOfOneImage (heightOfOneImage) {} //------------------------------------------------------------------------ CKickButton::~CKickButton () {} //------------------------------------------------------------------------ void CKickButton::draw (CDrawContext *pContext) { CPoint where (offset.h, offset.v); bounceValue (); if (value) where.v += heightOfOneImage; if (pBackground) { if (bTransparencyEnabled) pBackground->drawTransparent (pContext, size, where); else pBackground->draw (pContext, size, where); } setDirty (false); } //------------------------------------------------------------------------ void CKickButton::mouse (CDrawContext *pContext, CPoint &where, long button) { if (!bMouseEnabled) return; if (button == -1) button = pContext->getMouseButtons (); if (listener && button & (kAlt | kShift | kControl | kApple)) { if (listener->controlModifierClicked (pContext, this, button) != 0) return; } if (!(button & kLButton)) return; // this simulates a real windows button float fEntryState = value; // begin of edit parameter beginEdit (); if (pContext->getMouseButtons () == kLButton) { do { if (where.h >= size.left && where.v >= size.top && where.h <= size.right && where.v <= size.bottom) value = !fEntryState; else value = fEntryState; if (isDirty () && listener) listener->valueChanged (pContext, this); getMouseLocation (pContext, where); doIdleStuff (); } while (pContext->getMouseButtons () == kLButton); } else { value = !value; if (listener) listener->valueChanged (pContext, this); } value = 0.0f; // set button to UNSELECTED state if (listener) listener->valueChanged (pContext, this); // end of edit parameter endEdit (); } //------------------------------------------------------------------------ class CSplashScreenView : public CView { public: CSplashScreenView (const CRect& size, CSplashScreen* splashScreen) : CView (size) , splashScreen (splashScreen) { setTransparency (splashScreen->getTransparency ()); setBackground (splashScreen->getBackground ()); } void draw (CDrawContext *pContext) { if (bTransparencyEnabled) { if (splashScreen->getBitmapTransparency ()) pBackground->drawAlphaBlend (pContext, size, splashScreen->getOffset (), splashScreen->getBitmapTransparency ()); else pBackground->drawTransparent (pContext, size, splashScreen->getOffset ()); } else pBackground->draw (pContext, size, splashScreen->getOffset ()); setDirty (false); } void mouse (CDrawContext *pContext, CPoint &where, long button) { if (button & kLButton) { splashScreen->unSplash (pContext); getFrame ()->setDirty (true); getFrame ()->setModalView (0); forget (); } } protected: CSplashScreen* splashScreen; }; //------------------------------------------------------------------------ // CSplashScreen //------------------------------------------------------------------------ /*! @class CSplashScreen One click on its activated region and its pixmap is displayed, in this state the other control can not be used, an another click on the displayed area reinstalls the normal frame. This can be used to display a help view over the other views. */ // one click draw its pixmap, an another click redraw its parent CSplashScreen::CSplashScreen (const CRect &size, CControlListener *listener, long tag, CBitmap *background, CRect &toDisplay, CPoint &offset) : CControl (size, listener, tag, background), toDisplay (toDisplay), offset (offset), bitmapTransparency (255) {} //------------------------------------------------------------------------ CSplashScreen::~CSplashScreen () {} //------------------------------------------------------------------------ void CSplashScreen::setBitmapTransparency (unsigned char transparency) { bitmapTransparency = transparency; setTransparency (bitmapTransparency != 255); } //------------------------------------------------------------------------ void CSplashScreen::draw (CDrawContext *pContext) { if (value && pBackground) { if (bTransparencyEnabled) { if (bitmapTransparency) pBackground->drawAlphaBlend (pContext, toDisplay, offset, bitmapTransparency); else pBackground->drawTransparent (pContext, toDisplay, offset); } else pBackground->draw (pContext, toDisplay, offset); } setDirty (false); } //------------------------------------------------------------------------ bool CSplashScreen::hitTest (const CPoint& where, const long buttons) { bool result = CView::hitTest (where, buttons); if (result && !(buttons & kLButton)) return false; return result; } //------------------------------------------------------------------------ void CSplashScreen::mouse (CDrawContext *pContext, CPoint &where, long button) { if (!bMouseEnabled) return; if (button == -1) button = pContext->getMouseButtons (); if (listener && button & (kAlt | kShift | kControl | kApple)) { if (listener->controlModifierClicked (pContext, this, button) != 0) return; } if (!(button & kLButton)) return; value = !value; if (value) { CSplashScreenView* ssv = new CSplashScreenView (toDisplay, this); if (getFrame () && getFrame ()->setModalView (ssv)) { // keepSize = size; // size = toDisplay; // mouseableArea = size; if (listener) listener->valueChanged (pContext, this); } setDirty (); } // else // { // size = keepSize; // mouseableArea = size; // if (listener) // listener->valueChanged (pContext, this); // if (getFrame ()) // { // getFrame ()->setDirty (true); // getFrame ()->setModalView (NULL); // } // } } //------------------------------------------------------------------------ void CSplashScreen::unSplash (CDrawContext* pContext) { // setDirty (); // value = 0.f; // // size = keepSize; // if (getFrame ()) // { // if (getFrame ()->getModalView () == this) // { // getFrame ()->setModalView (NULL); // getFrame ()->redraw (); // } // } if (value) { value = 0; if (listener) listener->valueChanged (pContext, this); } } //------------------------------------------------------------------------ // CVuMeter //------------------------------------------------------------------------ CVuMeter::CVuMeter (const CRect &size, CBitmap *onBitmap, CBitmap *offBitmap, long nbLed, const long style) : CControl (size, 0, 0), onBitmap (onBitmap), offBitmap (offBitmap), pOScreen (0), nbLed (nbLed), style (style) { setDecreaseStepValue (0.1f); #if WINDOWS && !USE_LIBPNG setUseOffscreen (true); #endif if (onBitmap) onBitmap->remember (); if (offBitmap) offBitmap->remember (); rectOn (size.left, size.top, size.right, size.bottom); rectOff (size.left, size.top, size.right, size.bottom); } //------------------------------------------------------------------------ CVuMeter::~CVuMeter () { if (onBitmap) onBitmap->forget (); if (offBitmap) offBitmap->forget (); } //------------------------------------------------------------------------ void CVuMeter::setDirty (const bool val) { CView::setDirty (val); } //----------------------------------------------------------------------------- bool CVuMeter::attached (CView *parent) { if (pOScreen) delete pOScreen; if (bUseOffscreen) { pOScreen = new COffscreenContext (getFrame (), (long)size.width (), (long)size.height (), kBlackCColor); rectOn (0, 0, size.width (), size.height ()); rectOff (0, 0, size.width (), size.height ()); } else { rectOn (size.left, size.top, size.right, size.bottom); rectOff (size.left, size.top, size.right, size.bottom); } return CControl::attached (parent); } //------------------------------------------------------------------------ void CVuMeter::setUseOffscreen (bool val) { bUseOffscreen = val; } //----------------------------------------------------------------------------- bool CVuMeter::removed (CView *parent) { if (pOScreen) { delete pOScreen; pOScreen = 0; } return CControl::removed (parent); } //------------------------------------------------------------------------ void CVuMeter::draw (CDrawContext *_pContext) { if (!onBitmap) return; CPoint pointOn; CPoint pointOff; CDrawContext *pContext = _pContext; bounceValue (); float newValue = oldValue - decreaseValue; if (newValue < value) newValue = value; oldValue = newValue; if (bUseOffscreen) { if (!pOScreen) { pOScreen = new COffscreenContext (getFrame (), (long)size.width (), (long)size.height (), kBlackCColor); rectOn (0, 0, size.width (), size.height ()); rectOff (0, 0, size.width (), size.height ()); } pContext = pOScreen; } if (style & kHorizontal) { CCoord tmp = (long)(((long)(nbLed * newValue + 0.5f) / (float)nbLed) * onBitmap->getWidth ()); pointOff (tmp, 0); if (!bUseOffscreen) tmp += size.left; rectOff.left = tmp; rectOn.right = tmp; } else { CCoord tmp = (long)(((long)(nbLed * (getMax () - newValue) + 0.5f) / (float)nbLed) * onBitmap->getHeight ()); pointOn (0, tmp); if (!bUseOffscreen) tmp += size.top; rectOff.bottom = tmp; rectOn.top = tmp; } if (offBitmap) { if (bTransparencyEnabled) offBitmap->drawTransparent (pContext, rectOff, pointOff); else offBitmap->draw (pContext, rectOff, pointOff); } if (bTransparencyEnabled) onBitmap->drawTransparent (pContext, rectOn, pointOn); else onBitmap->draw (pContext, rectOn, pointOn); if (pOScreen) pOScreen->copyFrom (_pContext, size); setDirty (false); } END_NAMESPACE_VSTGUI //------------------------------------------------------------------------ // END. //------------------------------------------------------------------------