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 #pragma once
17 
18 #include "globals.h"
19 
20 #if TARGET_AUDIOUNIT
21 //#include "vstkeycode.h"
22 #include "vstgui/plugin-bindings/plugguieditor.h"
23 typedef VSTGUI::PluginGUIEditor EditorType;
24 #elif TARGET_VST3
25 #include "public.sdk/source/vst/vstguieditor.h"
26 #include "pluginterfaces/gui/iplugviewcontentscalesupport.h"
27 typedef Steinberg::Vst::VSTGUIEditor EditorType;
28 #define PARENT_PLUGIN_TYPE SurgeVst3Processor
29 #elif TARGET_VST2
30 #if LINUX
31 #include "../linux/linux-aeffguieditor.h"
32 typedef VSTGUI::LinuxAEffGUIEditor EditorType;
33 #else
34 #include "vstgui/plugin-bindings/aeffguieditor.h"
35 typedef VSTGUI::AEffGUIEditor EditorType;
36 #endif
37 #elif TARGET_JUCE_UI
38 #include <JuceHeader.h>
39 #include "efvg/escape_from_vstgui.h"
40 #include "SurgeSynthEditor.h"
41 typedef EscapeFromVSTGUI::JuceVSTGUIEditorAdapter EditorType;
42 #define PARENT_PLUGIN_TYPE SurgeSynthEditor
43 #else
44 #include "vstgui/plugin-bindings/plugguieditor.h"
45 typedef VSTGUI::PluginGUIEditor EditorType;
46 #endif
47 
48 #ifndef PARENT_PLUGIN_TYPE
49 #define PARENT_PLUGIN_TYPE void
50 #endif
51 
52 #include "SurgeStorage.h"
53 #include "SurgeBitmaps.h"
54 
55 #include "vstcontrols.h"
56 #include "SurgeSynthesizer.h"
57 
58 #include "SkinSupport.h"
59 #include "SkinColors.h"
60 
61 #include <vector>
62 #include "MSEGEditor.h"
63 
64 class CSurgeSlider;
65 class CModulationSourceButton;
66 class CAboutBox;
67 class CMidiLearnOverlay;
68 
69 #if TARGET_VST3
70 namespace Steinberg
71 {
72 namespace Vst
73 {
74 class IContextMenu;
75 }
76 } // namespace Steinberg
77 #endif
78 
79 struct SGEDropAdapter;
80 
81 class SurgeGUIEditor : public EditorType,
82                        public VSTGUI::IControlListener,
83                        public VSTGUI::IKeyboardHook
84 #if TARGET_VST3
85     ,
86                        public Steinberg::IPlugViewContentScaleSupport
87 #endif
88 {
89   private:
90     using super = EditorType;
91 
92   public:
93     SurgeGUIEditor(PARENT_PLUGIN_TYPE *effect, SurgeSynthesizer *synth, void *userdata = nullptr);
94     virtual ~SurgeGUIEditor();
95 
96     static int start_paramtag_value;
97 
98 #if TARGET_AUDIOUNIT | TARGET_VST2 | TARGET_LV2
99     void idle() override;
100 #else
101     void idle();
102 #endif
103     bool queue_refresh;
104     virtual void toggle_mod_editing();
105 
106     virtual void beginEdit(long index);
107     virtual void endEdit(long index);
108 
109     static long applyParameterOffset(long index);
110     static long unapplyParameterOffset(long index);
111 
112 #if !TARGET_VST3
113     bool open(void *parent) override;
114     void close() override;
115 #else
116     virtual bool PLUGIN_API
117     open(void *parent, const VSTGUI::PlatformType &platformType = VSTGUI::kDefaultNative) override;
118     virtual void PLUGIN_API close() override;
119 
onWheel(float distance)120     virtual Steinberg::tresult PLUGIN_API onWheel(float distance) override
121     {
122         /*
123         ** in VST3 the VstGuiEditorBase we have - even if the OS has handled it
124         ** a call to the VSTGUIEditor::onWheel (trust me; I put in stack traces
125         ** and prints). That's probably wrong. But when you use VSTGUI Zoom
126         ** and frame->getCurrentPosition gets screwed up because VSTGUI has transform
127         ** bugs it is definitely wrong. So the mouse wheel event gets mistakenly
128         ** delivered twice (OK) but to the wrong spot (not OK!).
129         **
130         ** So stop the superclass from doing that by just making this do nothing.
131         */
132         return Steinberg::kResultFalse;
133     }
134 
canResize()135     virtual Steinberg::tresult PLUGIN_API canResize() override { return Steinberg::kResultTrue; }
136 
137     void resizeFromIdleSentinel();
138 
139     bool initialZoom();
140     virtual Steinberg::tresult PLUGIN_API onSize(Steinberg::ViewRect *newSize) override;
141     virtual Steinberg::tresult PLUGIN_API
142     checkSizeConstraint(Steinberg::ViewRect *newSize) override;
setContentScaleFactor(ScaleFactor factor)143     virtual Steinberg::tresult PLUGIN_API setContentScaleFactor(ScaleFactor factor) override
144     {
145         // Unused for now. Consider removing this callback since not all hosts use it
146         return Steinberg::kResultTrue;
147     }
148 
149 #endif
150 
151     bool pause_idle_updates = false;
152     int enqueuePatchId = -1;
flushEnqueuedPatchId()153     void flushEnqueuedPatchId()
154     {
155         auto t = enqueuePatchId;
156         enqueuePatchId = -1;
157         if (t >= 0)
158         {
159             synth->patchid_queue = t;
160             // Looks scary but remember this only runs if audio thread is off
161             synth->processThreadunsafeOperations();
162             patchCountdown = 200;
163         }
164     }
165 
166   protected:
167     int32_t onKeyDown(const VstKeyCode &code,
168                       VSTGUI::CFrame *frame) override; ///< should return 1 if no further key down
169                                                        ///< processing should apply, otherwise -1
170     int32_t onKeyUp(const VstKeyCode &code,
171                     VSTGUI::CFrame *frame) override; ///< should return 1 if no further key up
172                                                      ///< processing should apply, otherwise -1
173 
174     virtual void setParameter(long index, float value);
175 
176     // listener class
177     void valueChanged(VSTGUI::CControl *control) override;
178     int32_t controlModifierClicked(VSTGUI::CControl *pControl,
179                                    VSTGUI::CButtonState button) override;
180     void controlBeginEdit(VSTGUI::CControl *pControl) override;
181     void controlEndEdit(VSTGUI::CControl *pControl) override;
182 
183   public:
184     void refresh_mod();
185     void forceautomationchangefor(Parameter *p);
186 
187     void effectSettingsBackgroundClick(int whichScene);
188 
189 #if TARGET_VST3
190   public:
191     /**
192      * getIPlugFrame
193      *
194      * Amazingly, IPlugView has a setFrame( IPlugFrame ) method but
195      * no getter. It does, however, store the value as a protected
196      * variable. To collaborate for zoom, we need this reference
197      * in the VST Processor, so expose this function
198      */
getIPlugFrame()199     Steinberg::IPlugFrame *getIPlugFrame() { return plugFrame; }
200 #endif
201     void setDisabledForParameter(Parameter *p, CSurgeSlider *s);
202     void showSettingsMenu(VSTGUI::CRect &menuRect);
203 
204     static bool fromSynthGUITag(SurgeSynthesizer *synth, int tag, SurgeSynthesizer::ID &q);
205     // If n_scenes > 2, then this initialization and the modsource_editor one below will need to
206     // adjust
207     int current_scene = 0, current_osc[n_scenes] = {0, 0}, current_fx = 0;
208 
209   private:
210     void openOrRecreateEditor();
211     void makeStorePatchDialog();
212     void close_editor();
213     bool isControlVisible(ControlGroup controlGroup, int controlGroupEntry);
214     void repushAutomationFor(Parameter *p);
215     SurgeSynthesizer *synth = nullptr;
216     bool editor_open = false;
217     bool mod_editor = false;
218     modsources modsource = ms_lfo1, modsource_editor[n_scenes] = {ms_lfo1, ms_lfo1};
219     int fxbypass_tag = 0, f1subtypetag = 0, f2subtypetag = 0, filterblock_tag = 0, fmconfig_tag = 0;
220     double lastTempo = 0;
221     int lastTSNum = 0, lastTSDen = 0;
222     void draw_infowindow(int ptag, VSTGUI::CControl *control, bool modulate, bool forceMB = false);
223     void adjustSize(float &width, float &height) const;
224 
225     struct patchdata
226     {
227         std::string name;
228         std::string category;
229         std::string comments;
230         std::string author;
231     };
232 
233     void setBitmapZoomFactor(float zf);
234     void showTooLargeZoomError(double width, double height, float zf) const;
235     void showMinimumZoomError() const;
236 
237     /*
238     ** Zoom Implementation
239     **
240     ** Zoom works by the system maintaining a zoom factor (created by user actions)
241     **
242     ** All zoom factors are set in units of percentages as ints. So no zoom is "100",
243     ** and double size is "200"
244     */
245 
246     float zoomFactor = 100;
247     float initialZoomFactor = 100;
248 
249     int patchCountdown = -1;
250 
251   public:
populateDawExtraState(SurgeSynthesizer * synth)252     void populateDawExtraState(SurgeSynthesizer *synth)
253     {
254         auto des = &(synth->storage.getPatch().dawExtraState);
255 
256         des->isPopulated = true;
257         des->editor.instanceZoomFactor = zoomFactor;
258         des->editor.current_scene = current_scene;
259         des->editor.current_fx = current_fx;
260         des->editor.modsource = modsource;
261         for (int i = 0; i < n_scenes; ++i)
262         {
263             des->editor.current_osc[i] = current_osc[i];
264             des->editor.modsource_editor[i] = modsource_editor[i];
265 
266             des->editor.msegStateIsPopulated = true;
267             for (int lf = 0; lf < n_lfos; ++lf)
268             {
269                 des->editor.msegEditState[i][lf].timeEditMode = msegEditState[i][lf].timeEditMode;
270             }
271         }
272         des->editor.isMSEGOpen = (editorOverlayTagAtClose == "msegEditor");
273     }
274 
loadFromDAWExtraState(SurgeSynthesizer * synth)275     void loadFromDAWExtraState(SurgeSynthesizer *synth)
276     {
277         auto des = &(synth->storage.getPatch().dawExtraState);
278         if (des->isPopulated)
279         {
280             auto sz = des->editor.instanceZoomFactor;
281             if (sz > 0)
282                 setZoomFactor(sz);
283             current_scene = des->editor.current_scene;
284             current_fx = des->editor.current_fx;
285             modsource = des->editor.modsource;
286 
287             for (int i = 0; i < n_scenes; ++i)
288             {
289                 current_osc[i] = des->editor.current_osc[i];
290                 modsource_editor[i] = des->editor.modsource_editor[i];
291                 if (des->editor.msegStateIsPopulated)
292                 {
293                     for (int lf = 0; lf < n_lfos; ++lf)
294                     {
295                         msegEditState[i][lf].timeEditMode =
296                             des->editor.msegEditState[i][lf].timeEditMode;
297                     }
298                 }
299             }
300             if (des->editor.isMSEGOpen)
301             {
302                 showMSEGEditorOnNextIdleOrOpen = true;
303             }
304         }
305     }
306 
setZoomCallback(std::function<void (SurgeGUIEditor *,bool resizeWindow)> f)307     void setZoomCallback(std::function<void(SurgeGUIEditor *, bool resizeWindow)> f)
308     {
309         zoom_callback = f;
310         setZoomFactor(getZoomFactor()); // notify the new callback
311     }
getZoomFactor()312     float getZoomFactor() const { return zoomFactor; }
313     void setZoomFactor(float zf);
314     void setZoomFactor(float zf, bool resizeWindow);
315     void resizeWindow(float zf);
316     bool doesZoomFitToScreen(float zf,
317                              float &correctedZf); // returns true if it fits; false if not; sets
318                                                   // correctedZF to right size in either case
319 
320     void swapFX(int source, int target, SurgeSynthesizer::FXReorderMode m);
321 
322     /*
323     ** Callbacks from the Status Panel. If this gets to be too many perhaps make these an interface?
324     */
325     void showMPEMenu(VSTGUI::CPoint &where);
326     void showTuningMenu(VSTGUI::CPoint &where);
327     void showZoomMenu(VSTGUI::CPoint &where);
328     void showLfoMenu(VSTGUI::CPoint &menuRect);
329 
330     void toggleMPE();
331     void toggleTuning();
332     void scaleFileDropped(std::string fn);
333     void mappingFileDropped(std::string fn);
334     std::string tuningToHtml();
335 
336     void swapControllers(int t1, int t2);
337     void openModTypeinOnDrop(int ms, VSTGUI::CControl *sl, int tgt);
338 
queueRebuildUI()339     void queueRebuildUI()
340     {
341         queue_refresh = true;
342         synth->refresh_editor = true;
343     }
344 
345     std::string midiMappingToHtml();
346 
347     // These are unused right now
348     enum SkinInspectorFlags
349     {
350         COLORS = 1 << 0,
351         COMPONENTS = 1 << 1,
352 
353         ALL = COLORS | COMPONENTS
354     };
355     std::string skinInspectorHtml(SkinInspectorFlags f = ALL);
356 
357     /*
358     ** Modulation Hover Support
359     */
360     void sliderHoverStart(int tag);
361     void sliderHoverEnd(int tag);
362 
getWindowSizeX()363     int getWindowSizeX() const { return wsx; }
getWindowSizeY()364     int getWindowSizeY() const { return wsy; }
365 
366     /*
367      * We have an enumerated set of overlay tags which we can push
368      * to the UI. You *have* to give a new overlay type a tag in
369      * order for it to work.
370      */
371     enum OverlayTags
372     {
373         NO_EDITOR,
374         MSEG_EDITOR,
375         STORE_PATCH
376     };
377 
378     void addEditorOverlay(
379         VSTGUI::CView *c,
380         std::string editorTitle, // A window display title - whatever you want
381         OverlayTags editorTag,   // A tag by editor class. Please unique, no spaces.
382         const VSTGUI::CPoint &topleft = VSTGUI::CPoint(0, 0), bool modalOverlay = true,
383         bool hasCloseButton = true, std::function<void()> onClose = []() {});
384     void dismissEditorOfType(OverlayTags ofType);
topmostEditorTag()385     OverlayTags topmostEditorTag()
386     {
387         if (!editorOverlay.size())
388             return NO_EDITOR;
389         return editorOverlay.back().first;
390     }
isAnyOverlayPresent(OverlayTags tag)391     bool isAnyOverlayPresent(OverlayTags tag)
392     {
393         for (auto el : editorOverlay)
394         {
395             if (el.first == tag)
396                 return true;
397         }
398         return false;
399     }
400 
401     std::string getDisplayForTag(long tag);
402 
queuePatchFileLoad(std::string file)403     void queuePatchFileLoad(std::string file)
404     {
405         strncpy(synth->patchid_file, file.c_str(), FILENAME_MAX);
406         synth->has_patchid_file = true;
407     }
408 
409     void closeStorePatchDialog();
410     void showStorePatchDialog();
411 
412     void lfoShapeChanged(int prior, int curr);
413     void showMSEGEditor();
414     void closeMSEGEditor();
415     void toggleMSEGEditor();
416     void broadcastMSEGState();
417     int msegIsOpenFor = -1, msegIsOpenInScene = -1;
418     bool showMSEGEditorOnNextIdleOrOpen = false;
419 
420     MSEGEditor::State msegEditState[n_scenes][n_lfos];
421     MSEGEditor::State mostRecentCopiedMSEGState;
422 
423     int oscilatorMenuIndex[n_scenes][n_oscs] = {0};
424 
425     bool hasIdleRun = false;
426     VSTGUI::CPoint resizeToOnIdle = VSTGUI::CPoint(-1, -1);
427     /*
428      * A countdown which will clearr the bitmap store offscreen caches
429      * after N idles if set to a positive N. This is needed as some
430      * DAWS (cough cough LIVE) handle draw and resize events in a
431      * different order so the offscren cache when going classic->royal
432      * at 100% (for instance) is the wrong size.
433      */
434     int clearOffscreenCachesAtZero = -1;
435 
436   private:
437     SGEDropAdapter *dropAdapter = nullptr;
438     friend class SGEDropAdapter;
439 
440   public:
441     bool canDropTarget(const std::string &fname); // these come as const char* from vstgui
442     bool onDrop(const std::string &fname);
443 
444   private:
445     VSTGUI::CRect positionForModulationGrid(modsources entry);
446 
447     int wsx = BASE_WINDOW_SIZE_X;
448     int wsy = BASE_WINDOW_SIZE_Y;
449 
450     /**
451      * findLargestFittingZoomBetween
452      *
453      * Finds the largest zoom which will fit your current screen between a lower and upper bound.
454      * Will never return something smaller than lower or larger than upper. Default is as large as
455      * possible, quantized in units of zoomQuanta, with the maximum screen usage percentages
456      * protecting for screen real estate. The function also needs to know the 100% size of the UI
457      * the baseW and baseH)
458      *
459      * for instance findLargestFittingZoomBetween( 100, 200, 5, 90, bw, bh )
460      *
461      * would find the largest possible zoom which uses at most 90% of your screen real estate but
462      * would also guarantee that the result % 5 == 0.
463      */
464     int findLargestFittingZoomBetween(int zoomLow, int zoomHigh, int zoomQuanta,
465                                       int percentageOfScreenAvailable, float baseW, float baseH);
466 
467   public:
468     void showAboutBox(int devModeGrid = -1);
469     void hideAboutBox();
470 
471     void showMidiLearnOverlay(const VSTGUI::CRect &r);
472     void hideMidiLearnOverlay();
473 
474   private:
475 #if TARGET_VST3
476     Steinberg::Vst::IContextMenu *
477     addVst3MenuForParams(VSTGUI::COptionMenu *c, const SurgeSynthesizer::ID &,
478                          int &eid); // just a noop if you aren't a vst3 of course
479 #endif
480 
481     std::function<void(SurgeGUIEditor *, bool resizeWindow)> zoom_callback;
482     bool zoomInvalid = false;
483     int minimumZoom = 100;
484 
485     int selectedFX[8];
486     std::string fxPresetName[8];
487 
488   public:
489     std::shared_ptr<SurgeBitmaps> bitmapStore = nullptr;
490 
491   private:
492     bool modsource_is_alternate[n_modsources];
493 
494   public:
495     void toggleAlternateFor(VSTGUI::CControl *c);
496 
497   private:
498     VSTGUI::CControl *vu[16];
499     VSTGUI::CControl *infowindow, *patchname, *ccfxconf = nullptr;
500     VSTGUI::CControl *statusMPE = nullptr, *statusTune = nullptr, *statusZoom = nullptr;
501     CAboutBox *aboutbox = nullptr;
502     CMidiLearnOverlay *midiLearnOverlay = nullptr;
503     VSTGUI::CTextEdit *patchName = nullptr;
504     VSTGUI::CTextEdit *patchCategory = nullptr;
505     VSTGUI::CTextEdit *patchCreator = nullptr;
506     VSTGUI::CTextEdit *patchComment = nullptr;
507     VSTGUI::CCheckBox *patchTuning = nullptr;
508     VSTGUI::CTextLabel *patchTuningLabel = nullptr;
509 #if BUILD_IS_DEBUG
510     VSTGUI::CTextLabel *debugLabel = nullptr;
511 #endif
512 
513     VSTGUI::CViewContainer *typeinDialog = nullptr;
514     VSTGUI::CTextEdit *typeinValue = nullptr;
515     VSTGUI::CTextLabel *typeinLabel = nullptr;
516     VSTGUI::CTextLabel *typeinPriorValueLabel = nullptr;
517     VSTGUI::CControl *typeinEditControl = nullptr;
518     VSTGUI::CControl *msegEditSwitch = nullptr;
519     enum TypeInMode
520     {
521         Inactive,
522         Param,
523         Control
524     } typeinMode = Inactive;
525     std::vector<VSTGUI::CView *> removeFromFrame;
526     int typeinResetCounter = -1;
527     std::string typeinResetLabel = "";
528 
529     // Data structures for a list of overlays and associated data witht hem
530     std::vector<std::pair<OverlayTags, VSTGUI::CViewContainer *>> editorOverlay;
531     std::unordered_map<VSTGUI::CViewContainer *, VSTGUI::CView *>
532         editorOverlayContentsWeakReference;
533     std::unordered_map<VSTGUI::CViewContainer *, std::function<void()>> editorOverlayOnClose;
534 
535     VSTGUI::CViewContainer *minieditOverlay = nullptr;
536     VSTGUI::CTextEdit *minieditTypein = nullptr;
537     std::function<void(const char *)> minieditOverlayDone = [](const char *) {};
538 
539   public:
540     std::string editorOverlayTagAtClose; // FIXME what is this?
541     void promptForMiniEdit(const std::string &value, const std::string &prompt,
542                            const std::string &title, const VSTGUI::CPoint &where,
543                            std::function<void(const std::string &)> onOK);
544 
545   private:
546     VSTGUI::CTextLabel *lfoNameLabel = nullptr;
547     VSTGUI::CTextLabel *fxPresetLabel = nullptr;
548 
549     std::string modulatorName(int ms, bool forButton);
550 
551     Parameter *typeinEditTarget = nullptr;
552     int typeinModSource = -1;
553 
554     VSTGUI::CControl *polydisp = nullptr;
555     VSTGUI::CControl *oscdisplay = nullptr;
556     VSTGUI::CControl *splitpointControl = nullptr;
557 
558     static const int n_paramslots = 1024;
559     VSTGUI::CControl *param[n_paramslots] = {};
560     VSTGUI::CControl *nonmod_param[n_paramslots] = {};
561     CModulationSourceButton *gui_modsrc[n_modsources] = {};
562     VSTGUI::CControl *metaparam[n_customcontrollers] = {};
563     VSTGUI::CControl *lfodisplay = nullptr;
564     VSTGUI::CControl *filtersubtype[2] = {};
565     VSTGUI::CControl *fxmenu = nullptr;
566     int clear_infoview_countdown = 0;
567 
568   public:
569     int clear_infoview_peridle = -1;
570     bool useDevMenu = false;
571 
572   private:
573     float blinktimer = 0;
574     bool blinkstate = false;
575     PARENT_PLUGIN_TYPE *_effect = nullptr;
576     void *_userdata = nullptr;
577 #if TARGET_JUCE_UI
578     std::shared_ptr<int> _idleTimer; // FIXME
579 #else
580     VSTGUI::SharedPointer<VSTGUI::CVSTGUITimer> _idleTimer;
581 #endif
582     int firstIdleCountdown = 0;
583 
584     /*
585     ** Utility Function
586     */
587     VSTGUI::CCommandMenuItem *addCallbackMenu(VSTGUI::COptionMenu *toThis, std::string label,
588                                               std::function<void()> op);
589 
590     VSTGUI::COptionMenu *
591     makeSmoothMenu(VSTGUI::CRect &menuRect, const std::string &key, int defaultValue,
592                    std::function<void(ControllerModulationSource::SmoothingMode)> setSmooth);
593 
594     VSTGUI::COptionMenu *makeMpeMenu(VSTGUI::CRect &rect, bool showhelp);
595     VSTGUI::COptionMenu *makeTuningMenu(VSTGUI::CRect &rect, bool showhelp);
596     VSTGUI::COptionMenu *makeZoomMenu(VSTGUI::CRect &rect, bool showhelp);
597     VSTGUI::COptionMenu *makeSkinMenu(VSTGUI::CRect &rect);
598     VSTGUI::COptionMenu *makeUserSettingsMenu(VSTGUI::CRect &rect);
599     VSTGUI::COptionMenu *makeDataMenu(VSTGUI::CRect &rect);
600     VSTGUI::COptionMenu *makeMidiMenu(VSTGUI::CRect &rect);
601     VSTGUI::COptionMenu *makeDevMenu(VSTGUI::CRect &rect);
602     VSTGUI::COptionMenu *makeLfoMenu(VSTGUI::CRect &rect);
603     VSTGUI::COptionMenu *makeMonoModeOptionsMenu(VSTGUI::CRect &rect, bool updateDefaults);
604     bool scannedForMidiPresets = false;
605 
606     void resetSmoothing(ControllerModulationSource::SmoothingMode t);
607     void resetPitchSmoothing(ControllerModulationSource::SmoothingMode t);
608 
609   public:
610     std::string helpURLFor(Parameter *p); // this requires internal state so doesn't have statics
611     std::string helpURLForSpecial(std::string special); // these can be either this way or static
612 
613     static std::string helpURLForSpecial(SurgeStorage *, std::string special);
614     static std::string fullyResolvedHelpURL(std::string helpurl);
615 
616   private:
617 #if TARGET_VST3
618     OBJ_METHODS(SurgeGUIEditor, EditorType)
619     DEFINE_INTERFACES
620     DEF_INTERFACE(Steinberg::IPlugViewContentScaleSupport)
621     END_DEFINE_INTERFACES(EditorType)
622     REFCOUNT_METHODS(EditorType)
623 #endif
624 
625     void promptForUserValueEntry(Parameter *p, VSTGUI::CControl *c, int modulationSource = -1);
626 
627     /*
628     ** Skin support
629     */
630     Surge::UI::Skin::ptr_t currentSkin;
631     void setupSkinFromEntry(const Surge::UI::SkinDB::Entry &entry);
632     void reloadFromSkin();
633     VSTGUI::CControl *layoutComponentForSkin(std::shared_ptr<Surge::UI::Skin::Control> skinCtrl,
634                                              long tag, int paramIndex = -1, Parameter *p = nullptr,
635                                              int style = 0);
636 
637     /*
638     ** General MIDI CC names
639     */
640     const char midicc_names[128][42] = {"Bank Select MSB",
641                                         "Modulation Wheel MSB",
642                                         "Breath Controller MSB",
643                                         "Control 3 MSB",
644                                         "Foot Controller MSB",
645                                         "Portamento Time MSB",
646                                         "Data Entry MSB",
647                                         "Volume MSB",
648                                         "Balance MSB",
649                                         "Control 9 MSB",
650                                         "Pan MSB",
651                                         "Expression MSB",
652                                         "Effect #1 MSB",
653                                         "Effect #2 MSB",
654                                         "Control 14 MSB",
655                                         "Control 15 MSB",
656                                         "General Purpose Controller #1 MSB",
657                                         "General Purpose Controller #2 MSB",
658                                         "General Purpose Controller #3 MSB",
659                                         "General Purpose Controller #4 MSB",
660                                         "Control 20 MSB",
661                                         "Control 21 MSB",
662                                         "Control 22 MSB",
663                                         "Control 23 MSB",
664                                         "Control 24 MSB",
665                                         "Control 25 MSB",
666                                         "Control 26 MSB",
667                                         "Control 27 MSB",
668                                         "Control 28 MSB",
669                                         "Control 29 MSB",
670                                         "Control 30 MSB",
671                                         "Control 31 MSB",
672                                         "Bank Select LSB",
673                                         "Modulation Wheel LSB",
674                                         "Breath Controller LSB",
675                                         "Control 3 LSB",
676                                         "Foot Controller LSB",
677                                         "Portamento Time LSB",
678                                         "Data Entry LSB",
679                                         "Volume LSB",
680                                         "Balance LSB",
681                                         "Control 9 LSB",
682                                         "Pan LSB",
683                                         "Expression LSB",
684                                         "Effect #1 LSB",
685                                         "Effect #2 LSB",
686                                         "Control 14 LSB",
687                                         "Control 15 LSB",
688                                         "General Purpose Controller #1 LSB",
689                                         "General Purpose Controller #2 LSB",
690                                         "General Purpose Controller #3 LSB",
691                                         "General Purpose Controller #4 LSB",
692                                         "Control 20 LSB",
693                                         "Control 21 LSB",
694                                         "Control 22 LSB",
695                                         "Control 23 LSB",
696                                         "Control 24 LSB",
697                                         "Control 25 LSB",
698                                         "Control 26 LSB",
699                                         "Control 27 LSB",
700                                         "Control 28 LSB",
701                                         "Control 29 LSB",
702                                         "Control 30 LSB",
703                                         "Control 31 LSB",
704                                         "Sustain Pedal",
705                                         "Portamento Pedal",
706                                         "Sostenuto Pedal",
707                                         "Soft Pedal",
708                                         "Legato Pedal",
709                                         "Hold Pedal",
710                                         "Sound Control #1 Sound Variation",
711                                         "Sound Control #2 Timbre",
712                                         "Sound Control #3 Release Time",
713                                         "Sound Control #4 Attack Time",
714                                         "Sound Control #5 Brightness / MPE Timbre",
715                                         "Sound Control #6 Decay Time",
716                                         "Sound Control #7 Vibrato Rate",
717                                         "Sound Control #8 Vibrato Depth",
718                                         "Sound Control #9 Vibrato Delay",
719                                         "Sound Control #10 Control 79",
720                                         "General Purpose Controller #5",
721                                         "General Purpose Controller #6",
722                                         "General Purpose Controller #7",
723                                         "General Purpose Controller #8",
724                                         "Portamento Control",
725                                         "Control 85",
726                                         "Control 86",
727                                         "Control 87",
728                                         "High Resolution Velocity Prefix",
729                                         "Control 89",
730                                         "Control 90",
731                                         "Reverb Send Level",
732                                         "Tremolo Depth",
733                                         "Chorus Send Level",
734                                         "Celeste Depth",
735                                         "Phaser Depth",
736                                         "Data Increment",
737                                         "Data Decrement",
738                                         "NRPN LSB",
739                                         "NRPN MSB",
740                                         "RPN LSB",
741                                         "RPN MLSB",
742                                         "Control 102",
743                                         "Control 103",
744                                         "Control 104",
745                                         "Control 105",
746                                         "Control 106",
747                                         "Control 107",
748                                         "Control 108",
749                                         "Control 109",
750                                         "Control 110",
751                                         "Control 111",
752                                         "Control 112",
753                                         "Control 113",
754                                         "Control 114",
755                                         "Control 115",
756                                         "Control 116",
757                                         "Control 117",
758                                         "Control 118",
759                                         "Control 119",
760                                         "Control 120 All Sound Off",
761                                         "Control 121 Reset All Controllers",
762                                         "Control 122 Local Control On/Of",
763                                         "Control 123 All Notes Off",
764                                         "Control 124 Omni Mode Off",
765                                         "Control 125 Omni Mode On",
766                                         "Control 126 Mono Mode Off",
767                                         "Control 127 Mono Mode On"};
768 };
769