1 /*
2 ** Surge Synthesizer is Free and Open Source Software
3 **
4 ** Surge is made available under the Gnu General Public License, v3.0
5 ** https://www.gnu.org/licenses/gpl-3.0.en.html
6 **
7 ** Copyright 2004-2020 by various individuals as described by the Git transaction log
8 **
9 ** All source at: https://github.com/surge-synthesizer/surge.git
10 **
11 ** Surge was a commercial product from 2004-2018, with Copyright and ownership
12 ** in that period held by Claes Johanson at Vember Audio. Claes made Surge
13 ** open source in September 2018.
14 */
15 
16 #include "SurgeGUIEditor.h"
17 #include "CSurgeSlider.h"
18 #include "guihelpers.h"
19 #include "resource.h"
20 #include "DspUtilities.h"
21 #include "MouseCursorControl.h"
22 #include "CScalableBitmap.h"
23 #include "SurgeBitmaps.h"
24 #include "SurgeStorage.h"
25 #include "SkinColors.h"
26 #include "StringOps.h"
27 #include <UserDefaults.h>
28 #include <iostream>
29 
30 using namespace VSTGUI;
31 using namespace std;
32 
33 extern CFontRef displayFont;
34 
35 enum
36 {
37     cs_none = 0,
38     cs_drag = 1,
39 };
40 
41 class SurgeStorage;
42 
43 CSurgeSlider::MoveRateState CSurgeSlider::sliderMoveRateState = kUnInitialized;
44 
CSurgeSlider(const CPoint & loc,long stylee,IControlListener * listener,long tag,bool is_mod,std::shared_ptr<SurgeBitmaps> bitmapStore,SurgeStorage * storage)45 CSurgeSlider::CSurgeSlider(const CPoint &loc, long stylee, IControlListener *listener, long tag,
46                            bool is_mod, std::shared_ptr<SurgeBitmaps> bitmapStore,
47                            SurgeStorage *storage)
48     : CControl(CRect(loc, CPoint(1, 1)), listener, tag, 0),
49       Surge::UI::CursorControlAdapterWithMouseDelta<CSurgeSlider>(storage)
50 {
51     this->style = stylee;
52     this->is_mod = is_mod;
53 
54     this->storage = storage;
55 
56     labfont = displayFont;
57 #if !TARGET_JUCE_UI
58     labfont->remember();
59 #endif
60 
61     modmode = 0;
62     disabled = false;
63 
64     label[0] = 0;
65     pModHandle = 0;
66 
67     typex = 0;
68     typey = 0;
69     typehx = 0;
70     typehy = 0;
71     qdvalue = value;
72 
73     edit_value = 0;
74     drawcount_debug = 0;
75 
76     controlstate = cs_none;
77 
78     has_modulation = false;
79     has_modulation_current = false;
80 
81     restvalue = 0.0f;
82     restmodval = 0.0f;
83 
84     CRect size;
85 
86     if (style & CSlider::kHorizontal)
87     {
88         if (style & kWhite)
89             typehy = 1;
90 
91         range = 112;
92         size = CRect(0, 0, 140, 26);
93         size.offset(loc.x, max(0, (int)loc.y));
94         handle_rect_orig = CRect(1, 0, 20, 14);
95         offsetHandle = CPoint(9, 5);
96     }
97     else
98     {
99         if (!(style & CSlider::kTop))
100             style |= CSlider::kBottom; // CSlider::kBottom by default
101 
102         if (style & kWhite)
103             typehy = 0;
104         else
105             typehy = 1;
106 
107         if (style & kMini)
108             range = 39;
109         else
110             range = 56;
111         size = CRect(0, 0, 22, 84);
112         size.offset(loc.x, max(0, (int)loc.y - 3));
113         handle_rect_orig = CRect(1, 0, 15, 19);
114         offsetHandle = CPoint(7, 9);
115     }
116 
117     setViewSize(size);
118     setMouseableArea(size);
119 }
120 
setModValue(float val)121 void CSurgeSlider::setModValue(float val)
122 {
123     this->modval = val;
124     invalid();
125 }
126 
~CSurgeSlider()127 CSurgeSlider::~CSurgeSlider() { labfont->forget(); }
128 
setLabel(const char * txt)129 void CSurgeSlider::setLabel(const char *txt)
130 {
131     int rp = 0;
132     for (int i = 0; i < 255; i++)
133     {
134         label[i] = txt[rp++];
135     }
136     label[255] = 0;
137     setDirty();
138 }
139 
draw(CDrawContext * dc)140 void CSurgeSlider::draw(CDrawContext *dc)
141 {
142 #if 0
143 	CColor c;
144 	c.red = rand()&255;
145 	c.green = rand()&255;
146 	c.blue = rand()&255;
147 	dc->setFillColor(c);
148 	dc->fillRect(size);
149 #endif
150 
151     bool drawLabel = true;
152 
153     if (skin && skinControl &&
154         skin->propertyValue(skinControl, Surge::Skin::Component::HIDE_SLIDER_LABEL, "") == "true")
155     {
156         drawLabel = false;
157     }
158 
159     labfont->setStyle(font_style);
160     labfont->setSize(font_size);
161 
162     CRect size = getViewSize();
163 
164     if (style & CSlider::kHorizontal)
165     {
166         if (style & kSemitone)
167             typey = 2;
168         else if (isBipolFn())
169             typey = 1;
170         else
171             typey = 0;
172         if (style & kWhite)
173             typey += 3;
174     }
175     else
176     {
177         if (style & kMini)
178             typey = 2;
179         else if (isBipolFn())
180             typey = 1;
181         else
182             typey = 0;
183     }
184 
185     typex = has_modulation ? 1 : 0;
186     if (has_modulation_current)
187         typex = 2;
188 
189     float slider_alpha = (disabled || isDeactivatedFn()) ? 0.35 : 1.0;
190     bool showHandle = true;
191 #if LINUX && !TARGET_JUCE_UI
192     // linux transparency is a bit broken (i have it patched to ignored) as described in
193     // #2053. For now just disable handles on linux transparent sliders, which is a bummer
194     // but better than a mispaint, then come back to this after 1.7.0
195     if (disabled || isDeactivatedFn())
196     {
197         showHandle = false;
198         slider_alpha = 1.0;
199     }
200 #endif
201 
202     if (pTray)
203     {
204         // CRect trect(0,0,pTray->getWidth(),pTray->getHeight());
205         CRect trect;
206         if (style & CSlider::kHorizontal)
207             trect = CRect(0, 0, 133, 14);
208         else
209             trect = CRect(0, 0, 16, 75);
210 
211         trect.offset(size.left, size.top);
212 
213         if (style & CSlider::kHorizontal)
214             trect.offset(2, 5);
215         else
216             trect.offset(2, 2);
217 
218         if (style & CSlider::kHorizontal)
219             pTray->draw(dc, trect, CPoint(133 * typex, 14 * typey), slider_alpha);
220         else
221             pTray->draw(dc, trect, CPoint(16 * typex, 75 * typey), slider_alpha);
222     }
223 
224     CRect headrect;
225     if (style & CSlider::kHorizontal)
226         headrect = CRect(0, 0, 28, 24);
227     else
228         headrect = CRect(0, 0, 24, 28);
229 
230     if ((hasLabFn || label[0]) && (style & CSlider::kHorizontal))
231     {
232         if (drawLabel)
233         {
234             CRect trect(0, 0, 111, 13);
235             trect.offset(size.left, size.top);
236             trect.offset(13, 12);
237             trect.inset(2, 0);
238 
239             // if (label_id >= 0) pLabels->draw(dc,trect,CPoint(0,8*label_id),0xff);
240             //		int a = 'a' + (rand()&31);
241             //		label[1] = a;
242             // sprintf(label,"%i",drawcount_debug++);
243 
244             if (style & kWhite)
245                 dc->setFontColor(skin->getColor(Colors::Slider::Label::Light));
246             else
247                 dc->setFontColor(skin->getColor(Colors::Slider::Label::Dark));
248             dc->setFont(labfont);
249             dc->setFrameColor(kRedCColor);
250 
251             // final x, y offset set from the skin
252             trect.offset(text_hoffset, text_voffset);
253             if (hasLabFn)
254             {
255                 auto tl = labfn();
256                 dc->drawString(tl.c_str(), trect, text_align, true);
257             }
258             else
259             {
260                 dc->drawString(label, trect, text_align, true);
261             }
262         }
263     }
264 
265     CRect hrect(headrect);
266     handle_rect = handle_rect_orig;
267     hrect.offset(size.left, size.top);
268     if (style & CSlider::kHorizontal)
269         hrect.offset(0, 3);
270 
271     float dispv = limit_range(qdvalue, 0.f, 1.f);
272     if (style & CSlider::kRight || style & CSlider::kBottom)
273         dispv = 1 - dispv;
274     dispv *= range;
275 
276     if (style & CSlider::kHorizontal)
277     {
278         hrect.offset(dispv + 1, 0);
279         handle_rect.offset(dispv + 1, 0);
280     }
281     else
282     {
283         hrect.offset(1, dispv);
284         handle_rect.offset(1, dispv);
285     }
286 
287     if (modmode)
288     {
289         CRect trect = hrect;
290         CRect trect2 = hrect;
291 
292         CColor ColBar = skin->getColor(Colors::Slider::Modulation::Positive);
293         CColor ColBarNeg = skin->getColor(Colors::Slider::Modulation::Negative);
294 
295         ColBar.alpha = (int)(slider_alpha * 255.f);
296         ColBarNeg.alpha = (int)(slider_alpha * 255.f);
297 
298         // We want modval + value to be bound by -1 and 1. So:
299         float modup = modval;
300         float moddn = modval;
301         bool overtop = false;
302         bool overbot = false;
303         bool overotherside = false;
304 
305         if (modup + value > 1.f)
306         {
307             overtop = true;
308             modup = 1.f - value;
309         }
310         if (modup + value < 0.f)
311         {
312             overbot = true;
313             overotherside = true;
314             modup = 0.f - value;
315         }
316         if (value - moddn < 0.f)
317         {
318             overbot = true;
319             moddn = value + 0.f;
320         }
321         if (value - moddn > 1.f)
322         {
323             overtop = true;
324             overotherside = true;
325             moddn = value - 1.f;
326         }
327 
328         // at some point in the future draw something special with overtop and overbot
329         modup *= range;
330         moddn *= range;
331 
332         std::vector<CRect> drawThese;
333         std::vector<CRect> drawTheseToo;
334 
335         if (style & CSlider::kHorizontal)
336         {
337             trect.top += 8;
338             trect.bottom = trect.top + 2;
339 
340             trect2.top += 8;
341             trect2.bottom = trect2.top + 2;
342 
343             if (!modulation_is_bipolar)
344             {
345                 trect.left += 11;
346                 trect.right = trect.left + modup;
347             }
348             else
349             {
350                 trect.left += 11;
351                 trect.right = trect.left + modup;
352 
353                 trect2.right -= 17;
354                 trect2.left = trect2.right - moddn;
355                 if (!overotherside && overbot)
356                     trect2.left += 1;
357             }
358 
359             if (trect.left > trect.right)
360                 std::swap(trect.left, trect.right);
361 
362             if (trect2.left > trect2.right)
363                 std::swap(trect2.left, trect2.right);
364 
365             drawThese.push_back(trect);
366             drawTheseToo.push_back(trect2);
367 
368             if (overtop)
369             {
370                 CRect topr;
371 
372                 if (overotherside)
373                 {
374                     topr.top = trect2.top;
375                     topr.bottom = trect2.bottom;
376                     topr.left = trect2.right + 1;
377                     topr.right = topr.left + 4;
378 
379                     drawTheseToo.push_back(topr);
380                 }
381                 else
382                 {
383                     topr.top = trect.top;
384                     topr.bottom = trect.bottom;
385                     topr.left = trect.right + 1;
386                     topr.right = topr.left + 4;
387 
388                     drawThese.push_back(topr);
389                 }
390             }
391 
392             if (overbot)
393             {
394                 CRect topr;
395 
396                 if (overotherside)
397                 {
398                     topr.top = trect.top;
399                     topr.bottom = trect.bottom;
400                     topr.left = trect.left - 3;
401                     topr.right = topr.left + 1;
402 
403                     drawThese.push_back(topr);
404                 }
405                 else
406                 {
407                     topr.top = trect2.top;
408                     topr.bottom = trect2.bottom;
409                     topr.left = trect2.left - 5;
410                     topr.right = topr.left + 4;
411 
412                     drawTheseToo.push_back(topr);
413                 }
414             }
415         }
416         else
417         {
418             trect.left += 8;
419             trect.right = trect.left + 2;
420 
421             trect2.left += 8;
422             trect2.right = trect2.left + 2;
423 
424             if (!modulation_is_bipolar)
425             {
426                 trect.top += 11;
427                 trect.bottom = trect.top - modup;
428             }
429             else
430             {
431                 trect.top += 11;
432                 trect.bottom = trect.top - modup;
433 
434                 trect2.bottom -= 17;
435                 trect2.top = trect2.bottom + moddn;
436                 if (overotherside && overtop)
437                     trect2.top += 1;
438             }
439 
440             if (trect.top < trect.bottom)
441                 std::swap(trect.top, trect.bottom);
442 
443             if (trect2.top < trect2.bottom)
444                 std::swap(trect2.top, trect2.bottom);
445 
446             if (overbot)
447             {
448                 CRect topr;
449 
450                 if (overotherside)
451                 {
452                     topr.left = trect.left;
453                     topr.right = trect.right;
454                     topr.bottom = trect.top + 1;
455                     topr.top = topr.bottom + 4;
456 
457                     drawThese.push_back(topr);
458                 }
459                 else
460                 {
461                     topr.left = trect2.left;
462                     topr.right = trect2.right;
463                     topr.bottom = trect2.top + 1;
464                     topr.top = topr.bottom + 4;
465 
466                     drawTheseToo.push_back(topr);
467                 }
468             }
469 
470             if (overtop)
471             {
472                 CRect topr;
473 
474                 if (overotherside)
475                 {
476                     topr.left = trect2.left;
477                     topr.right = trect2.right;
478                     topr.bottom = trect2.bottom - 1;
479                     topr.top = topr.bottom - 4;
480 
481                     drawTheseToo.push_back(topr);
482                 }
483                 else
484                 {
485                     topr.left = trect.left;
486                     topr.right = trect.right;
487                     topr.bottom = trect.bottom - 1;
488                     topr.top = topr.bottom - 4;
489 
490                     drawThese.push_back(topr);
491                 }
492             }
493 
494             drawThese.push_back(trect);
495             drawTheseToo.push_back(trect2);
496         }
497 
498         for (auto r : drawThese)
499         {
500             dc->setFillColor(ColBar);
501             dc->drawRect(r, VSTGUI::kDrawFilled);
502         }
503 
504         if (modulation_is_bipolar)
505         {
506             for (auto r : drawTheseToo)
507             {
508                 dc->setFillColor(ColBarNeg);
509                 dc->drawRect(r, VSTGUI::kDrawFilled);
510             }
511         }
512     }
513 
514     if (pHandle && showHandle && (modmode != 2) && (!isDeactivatedFn() || !disabled))
515     {
516         draghandlecenter = hrect.getCenter();
517 
518         if (style & CSlider::kHorizontal)
519         {
520             pHandle->draw(dc, hrect, CPoint(0, 24 * typehy), modmode ? slider_alpha : slider_alpha);
521             if (pHandleHover && in_hover && (!modmode))
522                 pHandleHover->draw(dc, hrect, CPoint(0, 24 * typehy),
523                                    modmode ? slider_alpha : slider_alpha);
524 
525             if (is_temposync)
526             {
527                 if (in_hover && pTempoSyncHoverHandle && (!modmode))
528                 {
529                     pTempoSyncHoverHandle->draw(dc, hrect, CPoint(0, 0), slider_alpha);
530                 }
531                 else if (pTempoSyncHandle)
532                 {
533                     pTempoSyncHandle->draw(dc, hrect, CPoint(0, 0), slider_alpha);
534                 }
535                 else
536                 {
537                     dc->setFont(labfont);
538                     dc->setFontColor(CColor(80, 80, 100));
539                     auto newRect = hrect;
540                     newRect.top += 1;
541                     newRect.left += 1;
542                     newRect.bottom = newRect.top + 15;
543                     newRect.right = newRect.left + 21;
544 
545                     auto tRect = newRect;
546                     tRect.right = tRect.left + 11;
547                     tRect.left += 2;
548 
549                     auto sRect = newRect;
550                     sRect.left += 11;
551                     sRect.right -= 2;
552                     dc->drawString("T", tRect, kCenterText, true);
553                     dc->drawString("S", sRect, kCenterText, true);
554                 }
555             }
556         }
557         else if ((!isDeactivatedFn() || !disabled))
558         {
559             pHandle->draw(
560                 dc, hrect, CPoint(0, 28 * typehy),
561                 modmode ? slider_alpha
562                         : slider_alpha); // used to be 0x80 which was solid - lets keep taht for now
563             if (pHandleHover && in_hover && (!modmode))
564                 pHandleHover->draw(
565                     dc, hrect, CPoint(0, 28 * typehy),
566                     modmode
567                         ? slider_alpha
568                         : slider_alpha); // used to be 0x80 which was solid - lets keep taht for now
569 
570             if (is_temposync)
571             {
572                 if (in_hover && pTempoSyncHoverHandle && (!modmode))
573                 {
574                     pTempoSyncHoverHandle->draw(dc, hrect, CPoint(0, 0), slider_alpha);
575                 }
576                 else if (pTempoSyncHandle)
577                 {
578                     pTempoSyncHandle->draw(dc, hrect, CPoint(0, 0), slider_alpha);
579                 }
580                 else
581                 {
582                     auto newRect = hrect;
583                     newRect.top += 1;
584                     newRect.left += 1;
585                     newRect.bottom = newRect.top + 20;
586                     newRect.right = newRect.left + 16;
587 
588                     dc->setFont(labfont);
589                     dc->setFontColor(CColor(80, 80, 100));
590 
591                     auto tRect = newRect;
592                     tRect.bottom = tRect.top + 11;
593                     tRect.top += 2;
594 
595                     auto sRect = newRect;
596                     sRect.top += 11;
597                     sRect.bottom -= 2;
598 
599                     dc->drawString("T", tRect, kCenterText, true);
600                     dc->drawString("S", sRect, kCenterText, true);
601                 }
602             }
603         }
604     }
605 
606     // draw mod-fader
607     if (pHandle && showHandle && modmode && (!isDeactivatedFn() || !disabled))
608     {
609         CRect hrect(headrect);
610         handle_rect = handle_rect_orig;
611         hrect.offset(size.left, size.top);
612         if (style & CSlider::kHorizontal)
613             hrect.offset(0, 3);
614 
615         float dispv;
616         if (modmode == 2)
617             dispv = limit_range(0.5f + 0.5f * modval, 0.f, 1.f);
618         else
619             dispv = limit_range(modval + value, 0.f, 1.f);
620 
621         if (style & CSlider::kRight || style & CSlider::kBottom)
622             dispv = 1 - dispv;
623         dispv *= range;
624 
625         if (style & CSlider::kHorizontal)
626         {
627             hrect.offset(dispv + 1, 0);
628             handle_rect.offset(dispv + 1, 0);
629         }
630         else
631         {
632             hrect.offset(1, dispv);
633             handle_rect.offset(1, dispv);
634         }
635 
636         if (style & CSlider::kHorizontal)
637         {
638             pHandle->draw(dc, hrect, CPoint(28, 24 * typehy), slider_alpha);
639             if (pHandleHover && in_hover)
640                 pHandleHover->draw(dc, hrect, CPoint(28, 24 * typehy), slider_alpha);
641         }
642         else
643         {
644             pHandle->draw(dc, hrect, CPoint(24, 28 * typehy), slider_alpha);
645             if (pHandleHover && in_hover)
646                 pHandleHover->draw(dc, hrect, CPoint(24, 28 * typehy), slider_alpha);
647         }
648         draghandlecenter = hrect.getCenter();
649     }
650 
651     setDirty(false);
652 }
653 
bounceValue(const bool keeprest)654 void CSurgeSlider::bounceValue(const bool keeprest)
655 {
656     if (keeprest)
657     {
658         if (restvalue != 0.0f)
659         {
660             value += restvalue;
661             restvalue = 0.0f;
662         }
663         if (restmodval != 0.0f)
664         {
665             modval += restmodval;
666             restmodval = 0.0f;
667         }
668     }
669 
670     if (value > vmax)
671     {
672         restvalue = value - vmax;
673         value = vmax;
674     }
675     else if (value < vmin)
676     {
677         restvalue = value - vmin;
678         value = vmin;
679     }
680 
681     if (modval > 1.f)
682     {
683         restmodval = modval - 1.f;
684         modval = 1.f;
685     }
686     if (modval < -1.f)
687     {
688         restmodval = modval - (-1.f);
689         modval = -1.f;
690     }
691 }
692 
isInMouseInteraction()693 bool CSurgeSlider::isInMouseInteraction() { return controlstate == cs_drag; }
694 
onMouseDown(CPoint & where,const CButtonState & buttons)695 CMouseEventResult CSurgeSlider::onMouseDown(CPoint &where, const CButtonState &buttons)
696 {
697     startPosition = where;
698     currentPosition = where;
699 
700     {
701         auto sge = dynamic_cast<SurgeGUIEditor *>(listener);
702         if (sge)
703         {
704             sge->clear_infoview_peridle = 0;
705         }
706     }
707     if (storage)
708         this->hideCursor = !Surge::UI::showCursor(storage);
709 
710     hasBeenDraggedDuringMouseGesture = false;
711     if (wheelInitiatedEdit)
712         while (editing)
713             endEdit();
714     wheelInitiatedEdit = false;
715 
716     if (listener && buttons & (kAlt | kRButton | kMButton | kButton4 | kButton5 | kShift |
717                                kControl | kApple | kDoubleClick))
718     {
719         unenqueueCursorHideIfMoved();
720 
721         if (listener->controlModifierClicked(this, buttons) != 0)
722         {
723             return kMouseDownEventHandledButDontNeedMovedOrUpEvents;
724         }
725     }
726 
727     onMouseDownCursorHelper(where);
728 
729     if (controlstate)
730     {
731         return kMouseEventHandled;
732     }
733 
734     if ((buttons & kLButton) && !controlstate)
735     {
736         beginEdit();
737         controlstate = cs_drag;
738         // getFrame()->setCursor( VSTGUI::kCursorHand );
739 
740         edit_value = modmode ? &modval : &value;
741         oldVal = *edit_value;
742 
743         restvalue = 0.f;
744         restmodval = 0.f;
745 
746         // Show the infowindow. Bit heavy handed
747         bounceValue();
748         if (listener)
749             listener->valueChanged(this);
750 
751         // detachCursor(where);
752         enqueueCursorHideIfMoved(where);
753         return kMouseEventHandled;
754     }
755     return kMouseEventHandled;
756 }
757 
onMouseUp(CPoint & where,const CButtonState & buttons)758 CMouseEventResult CSurgeSlider::onMouseUp(CPoint &where, const CButtonState &buttons)
759 {
760     unenqueueCursorHideIfMoved();
761     {
762         auto sge = dynamic_cast<SurgeGUIEditor *>(listener);
763         if (sge)
764         {
765             sge->clear_infoview_peridle = -1;
766         }
767     }
768 
769     bool resetPosition = hasBeenDraggedDuringMouseGesture;
770 
771     // "elastic edit" - resets to the value before the drag started if Alt is held
772     if (buttons & kAlt)
773     {
774         hasBeenDraggedDuringMouseGesture = false;
775         resetPosition = false;
776         *edit_value = oldVal;
777         setDirty();
778         if (isDirty() && listener)
779             listener->valueChanged(this);
780     }
781 
782     if (controlstate)
783     {
784 #if MAC
785         /*if(buttons & kRButton)
786         {
787                 statezoom = 1.0f;
788                 return kMouseEventHandled;
789         }*/
790 #endif
791         endEdit();
792         controlstate = cs_none;
793         // getFrame()->setCursor( VSTGUI::kCursorDefault );
794 
795         edit_value = nullptr;
796 
797         if (resetPosition && startPosition != currentPosition)
798         {
799             endCursorHide(draghandlecenter);
800         }
801         else
802         {
803             endCursorHide();
804         }
805 
806         // attachCursor();
807     }
808 
809     return kMouseEventHandled;
810 }
811 
onMouseEntered(VSTGUI::CPoint & where,const VSTGUI::CButtonState & buttons)812 VSTGUI::CMouseEventResult CSurgeSlider::onMouseEntered(VSTGUI::CPoint &where,
813                                                        const VSTGUI::CButtonState &buttons)
814 {
815     in_hover = true;
816 
817     {
818         auto sge = dynamic_cast<SurgeGUIEditor *>(listener);
819         if (sge)
820         {
821             sge->sliderHoverStart(getTag());
822         }
823     }
824 
825     invalid();
826     return kMouseEventHandled;
827 }
828 
onMouseExited(VSTGUI::CPoint & where,const VSTGUI::CButtonState & buttons)829 VSTGUI::CMouseEventResult CSurgeSlider::onMouseExited(VSTGUI::CPoint &where,
830                                                       const VSTGUI::CButtonState &buttons)
831 {
832     in_hover = false;
833 
834     {
835         auto sge = dynamic_cast<SurgeGUIEditor *>(listener);
836         if (sge)
837         {
838             sge->sliderHoverEnd(getTag());
839         }
840     }
841 
842     if (wheelInitiatedEdit)
843         while (editing)
844             endEdit();
845     wheelInitiatedEdit = false;
846     invalid();
847     return kMouseEventHandled;
848 }
849 
getMouseDeltaScaling(const CPoint & where,const CButtonState & buttons)850 double CSurgeSlider::getMouseDeltaScaling(const CPoint &where, const CButtonState &buttons)
851 {
852     double rate;
853 
854     switch (CSurgeSlider::sliderMoveRateState)
855     {
856     case kSlow:
857         rate = 0.3;
858         break;
859     case kMedium:
860         rate = 0.7;
861         break;
862     case kExact:
863         rate = 1.0;
864         break;
865     case kLegacy:
866     default:
867         rate = 0.3 * moverate;
868         break;
869     }
870 
871     if (buttons.isTouch())
872         rate = 1.0;
873 
874     if (buttons & kRButton)
875         rate *= 0.1;
876     if (buttons & kShift)
877         rate *= 0.1;
878 
879     return rate;
880 }
881 
onMouseMoveDelta(const CPoint & where,const CButtonState & buttons,double dx,double dy)882 void CSurgeSlider::onMouseMoveDelta(const CPoint &where, const CButtonState &buttons, double dx,
883                                     double dy)
884 {
885     currentPosition = where;
886     if (controlstate != cs_drag)
887     {
888         // FIXME - deal with modulation
889         auto handle_rect = handle_rect_orig;
890         auto size = getViewSize();
891         handle_rect.offset(size.left, size.top);
892 
893         float dispv = limit_range(qdvalue, 0.f, 1.f);
894         if (style & CSlider::kRight || style & CSlider::kBottom)
895             dispv = 1 - dispv;
896         dispv *= range;
897 
898         if (modmode)
899         {
900             if (modmode == 2)
901                 dispv = limit_range(0.5f + 0.5f * modval, 0.f, 1.f);
902             else
903                 dispv = limit_range(modval + value, 0.f, 1.f);
904 
905             if (style & CSlider::kRight || style & CSlider::kBottom)
906                 dispv = 1 - dispv;
907             dispv *= range;
908         }
909 
910         if (style & CSlider::kHorizontal)
911         {
912             handle_rect.offset(dispv + 1, 3);
913         }
914         else
915         {
916             handle_rect.offset(1, dispv);
917         }
918 
919         /*
920         if( handle_rect.pointInside(where) )
921            getFrame()->setCursor( VSTGUI::kCursorHand );
922         else
923            getFrame()->setCursor( VSTGUI::kCursorDefault );
924         */
925     }
926 
927     if ((controlstate == cs_drag) && (buttons & kLButton))
928     {
929         hasBeenDraggedDuringMouseGesture = true;
930         if (!edit_value)
931             return;
932         CPoint p;
933 
934         double diff;
935         if (style & CSlider::kHorizontal)
936             diff = dx;
937         else
938             diff = dy;
939 
940         if (style & CSlider::kRight || style & CSlider::kBottom)
941             diff = -diff;
942 
943         *edit_value += diff / (float)range;
944 
945         // this "retain" means if you turnaround an overshoot you have to use it all up first, vs
946         // starting the turnaround right away. That behavior only makes sense in exact mode.
947         bounceValue(sliderMoveRateState == MoveRateState::kExact);
948 
949         setDirty();
950 
951         if (isDirty() && listener)
952             listener->valueChanged(this);
953 
954         /*if (isDirty ())
955                 invalid ();*/
956     }
957 }
958 
SetQuantitizedDispValue(float f)959 void CSurgeSlider::SetQuantitizedDispValue(float f)
960 {
961     qdvalue = f;
962 
963     // if (isDirty ())
964     invalid();
965 }
966 
setValue(float val)967 void CSurgeSlider::setValue(float val)
968 {
969     if ((controlstate != cs_drag))
970     {
971         value = val;
972         qdvalue = val;
973     }
974 }
975 
onWheel(const VSTGUI::CPoint & where,const float & distance,const VSTGUI::CButtonState & buttons)976 bool CSurgeSlider::onWheel(const VSTGUI::CPoint &where, const float &distance,
977                            const VSTGUI::CButtonState &buttons)
978 {
979     // shift + scrollwheel for fine control
980     double rate = 0.1 * moverate;
981     if (buttons & kShift)
982         rate *= 0.05;
983 
984     edit_value = modmode ? &modval : &value;
985     oldVal = *edit_value;
986 
987     if (editing == 0)
988     {
989         wheelInitiatedEdit = true;
990         beginEdit();
991     }
992 
993     if (intRange && isStepped && !(buttons & kControl))
994     {
995         *edit_value += distance / (intRange);
996     }
997     else
998     {
999         *edit_value += rate * distance;
1000     }
1001 
1002     bounceValue();
1003     if (modmode)
1004     {
1005         setModValue(*edit_value);
1006     }
1007     else
1008     {
1009         setValue(value);
1010     }
1011     setDirty();
1012     if (isDirty() && listener)
1013         listener->valueChanged(this);
1014 
1015     /*
1016     ** If we call endEdit it will dismiss the infowindow so instead
1017     ** use the state management where I have a lignering begin
1018     ** which I clear when we click or leave.
1019     */
1020     edit_value = nullptr;
1021     return true;
1022 }
1023 
onSkinChanged()1024 void CSurgeSlider::onSkinChanged()
1025 {
1026     if (style & CSlider::kHorizontal)
1027     {
1028         pTray = associatedBitmapStore->getBitmap(IDB_SLIDER_HORIZ_BG);
1029         pHandle = associatedBitmapStore->getBitmap(IDB_SLIDER_HORIZ_HANDLE);
1030         pTempoSyncHandle =
1031             associatedBitmapStore->getBitmapByStringID("TEMPOSYNC_HORIZONTAL_OVERLAY");
1032         pTempoSyncHoverHandle =
1033             associatedBitmapStore->getBitmapByStringID("TEMPOSYNC_HORIZONTAL_HOVER_OVERLAY");
1034     }
1035     else
1036     {
1037         pTray = associatedBitmapStore->getBitmap(IDB_SLIDER_VERT_BG);
1038         pHandle = associatedBitmapStore->getBitmap(IDB_SLIDER_VERT_HANDLE);
1039         pTempoSyncHandle = associatedBitmapStore->getBitmapByStringID("TEMPOSYNC_VERTICAL_OVERLAY");
1040         pTempoSyncHoverHandle =
1041             associatedBitmapStore->getBitmapByStringID("TEMPOSYNC_VERTICAL_HOVER_OVERLAY");
1042     }
1043 
1044     if (skinControl.get())
1045     {
1046         auto htr = skin->propertyValue(skinControl, Surge::Skin::Component::SLIDER_TRAY);
1047         if (htr.isJust())
1048         {
1049             pTray = associatedBitmapStore->getBitmapByStringID(htr.fromJust());
1050         }
1051         auto hi = skin->propertyValue(skinControl, Surge::Skin::Component::HANDLE_IMAGE);
1052         if (hi.isJust())
1053         {
1054             pHandle = associatedBitmapStore->getBitmapByStringID(hi.fromJust());
1055         }
1056         auto ho = skin->propertyValue(skinControl, Surge::Skin::Component::HANDLE_HOVER_IMAGE);
1057         if (ho.isJust())
1058         {
1059             pHandleHover = associatedBitmapStore->getBitmapByStringID(ho.fromJust());
1060         }
1061         auto ht = skin->propertyValue(skinControl, Surge::Skin::Component::HANDLE_TEMPOSYNC_IMAGE);
1062         if (ht.isJust())
1063         {
1064             pTempoSyncHandle = associatedBitmapStore->getBitmapByStringID(ht.fromJust());
1065         }
1066 
1067         auto hth =
1068             skin->propertyValue(skinControl, Surge::Skin::Component::HANDLE_TEMPOSYNC_HOVER_IMAGE);
1069         if (hth.isJust())
1070         {
1071             pTempoSyncHoverHandle = associatedBitmapStore->getBitmapByStringID(hth.fromJust());
1072         }
1073     }
1074 
1075     if (!pHandleHover)
1076         pHandleHover = skin->hoverBitmapOverlayForBackgroundBitmap(
1077             skinControl, dynamic_cast<CScalableBitmap *>(pHandle), associatedBitmapStore,
1078             Surge::UI::Skin::HoverType::HOVER);
1079 }
1080