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 "SkinModel.h"
17 #include <unordered_map>
18 #include <map>
19 #include <vector>
20 #include <algorithm>
21 #include <cstring>
22 #include "resource.h"
23 #include "SkinColors.h"
24 #include "strnatcmp.h"
25 
26 /*
27  * This file implements the innards of the Connector class and the SkinColor class
28  *
29  * If you want to add a new connection, add it to SkinModel.h/SkinModel.cpp
30  * not here.
31  */
32 
33 namespace Surge
34 {
35 namespace Skin
36 {
37 
38 std::unordered_map<std::string, std::shared_ptr<Connector::Payload>> *idmap;
39 std::unordered_map<Connector::NonParameterConnection, std::shared_ptr<Connector::Payload>> *npcMap;
40 std::unordered_map<std::string, Surge::Skin::Color> *colMap;
41 std::unordered_map<int, std::shared_ptr<Component::Payload>> *registeredComponents;
42 
guaranteeMap()43 void guaranteeMap()
44 {
45     static bool firstTime = true;
46     if (firstTime)
47     {
48         idmap = new std::unordered_map<std::string, std::shared_ptr<Connector::Payload>>();
49         npcMap = new std::unordered_map<Connector::NonParameterConnection,
50                                         std::shared_ptr<Connector::Payload>>();
51         colMap = new std::unordered_map<std::string, Surge::Skin::Color>();
52         registeredComponents = new std::unordered_map<int, std::shared_ptr<Component::Payload>>();
53         firstTime = false;
54     }
55 }
56 
57 struct HarvestMaps
58 {
HarvestMapsSurge::Skin::HarvestMaps59     HarvestMaps() { guaranteeMap(); }
~HarvestMapsSurge::Skin::HarvestMaps60     ~HarvestMaps()
61     {
62         delete idmap;
63         delete npcMap;
64         delete colMap;
65         delete registeredComponents;
66     }
67 };
68 
69 HarvestMaps hmDeleter;
70 
71 int componentidbase = 100000;
Component()72 Component::Component() noexcept { payload = std::make_shared<Payload>(); }
73 
Component(const std::string & internalClassname)74 Component::Component(const std::string &internalClassname) noexcept
75 {
76     payload = std::make_shared<Payload>();
77     payload->id = componentidbase++;
78     payload->internalClassname = internalClassname;
79     guaranteeMap();
80     registeredComponents->insert(std::make_pair(payload->id, payload));
81     withProperty(Properties::X, {"x"}, "X position of the widget");
82     withProperty(Properties::Y, {"y"}, "Y position of the widget");
83     withProperty(Properties::W, {"w"}, "Width of the widget");
84     withProperty(Properties::H, {"h"}, "Height of the widget");
85 }
86 
~Component()87 Component::~Component() {}
88 
allComponentIds()89 std::vector<int> Component::allComponentIds()
90 {
91     guaranteeMap();
92     std::vector<int> res;
93     for (auto const &p : *registeredComponents)
94         res.push_back(p.first);
95     std::sort(res.begin(), res.end());
96     return res;
97 }
98 
componentById(int i)99 Component Component::componentById(int i)
100 {
101     guaranteeMap();
102     if (registeredComponents->find(i) != registeredComponents->end())
103     {
104         auto res = Component();
105         res.payload = registeredComponents->at(i);
106         return res;
107     }
108     return Surge::Skin::Components::None;
109 }
110 
propertyEnumToString(Properties p)111 std::string Component::propertyEnumToString(Properties p)
112 {
113 #define PN(x)                                                                                      \
114     case x:                                                                                        \
115         return #x;
116     switch (p)
117     {
118         PN(X)
119         PN(Y)
120         PN(W)
121         PN(H)
122         PN(BACKGROUND)
123         PN(HOVER_IMAGE)
124         PN(HOVER_ON_IMAGE)
125         PN(IMAGE)
126         PN(ROWS)
127         PN(COLUMNS)
128         PN(FRAMES)
129         PN(FRAME_OFFSET)
130         PN(NUMBERFIELD_CONTROLMODE)
131         PN(DRAGGABLE_HSWITCH)
132         PN(BACKGROUND_COLOR)
133         PN(FRAME_COLOR)
134 
135         PN(SLIDER_TRAY)
136         PN(HANDLE_IMAGE)
137         PN(HANDLE_HOVER_IMAGE)
138         PN(HANDLE_TEMPOSYNC_IMAGE)
139         PN(HANDLE_TEMPOSYNC_HOVER_IMAGE)
140         PN(HIDE_SLIDER_LABEL)
141 
142         PN(CONTROL_TEXT)
143         PN(FONT_SIZE)
144         PN(FONT_STYLE)
145         PN(TEXT)
146         PN(TEXT_ALIGN)
147         PN(TEXT_ALL_CAPS)
148         PN(TEXT_COLOR)
149         PN(TEXT_HOVER_COLOR)
150         PN(TEXT_HOFFSET)
151         PN(TEXT_VOFFSET)
152 
153         PN(GLYPH_PLACEMENT)
154         PN(GLYPH_W)
155         PN(GLYPH_H)
156         PN(GLPYH_ACTIVE)
157         PN(GLYPH_IMAGE)
158         PN(GLYPH_HOVER_IMAGE)
159     }
160 #undef PN
161     // This will never happen since the switch is exhaustive or the code wouldn't compile,
162     // and is just here to silence a gcc warning which is incorrect
163     return std::string("error") + std::to_string((int)p);
164 }
165 
166 std::shared_ptr<Connector::Payload>
makePayload(const std::string & id,float x,float y,float w,float h,const Component & c,Connector::NonParameterConnection n=Connector::PARAMETER_CONNECTED)167 makePayload(const std::string &id, float x, float y, float w, float h, const Component &c,
168             Connector::NonParameterConnection n = Connector::PARAMETER_CONNECTED)
169 {
170     guaranteeMap();
171     auto res = std::make_shared<Connector::Payload>();
172     res->id = id;
173     res->posx = x;
174     res->posy = y;
175     res->w = w;
176     res->h = h;
177     res->defaultComponent = c;
178 
179     idmap->insert(std::make_pair(id, res));
180     if (n != Connector::PARAMETER_CONNECTED)
181         npcMap->insert(std::make_pair(n, res));
182     return res;
183 }
184 
Connector()185 Connector::Connector() noexcept
186 {
187     guaranteeMap();
188     payload = std::shared_ptr<Connector::Payload>();
189 }
190 
Connector(const std::string & id,float x,float y)191 Connector::Connector(const std::string &id, float x, float y) noexcept
192 {
193     payload = makePayload(id, x, y, -1, -1, Components::Slider);
194 }
195 
Connector(const std::string & id,float x,float y,const Component & c)196 Connector::Connector(const std::string &id, float x, float y, const Component &c) noexcept
197 {
198     payload = makePayload(id, x, y, -1, -1, c);
199 }
200 
Connector(const std::string & id,float x,float y,float w,float h,const Component & c)201 Connector::Connector(const std::string &id, float x, float y, float w, float h,
202                      const Component &c) noexcept
203 {
204     payload = makePayload(id, x, y, w, h, c);
205 }
206 
Connector(const std::string & id,float x,float y,float w,float h,const Component & c,NonParameterConnection n)207 Connector::Connector(const std::string &id, float x, float y, float w, float h, const Component &c,
208                      NonParameterConnection n) noexcept
209 {
210     payload = makePayload(id, x, y, w, h, c, n);
211 }
212 
Connector(const std::string & id,float x,float y,NonParameterConnection n)213 Connector::Connector(const std::string &id, float x, float y, NonParameterConnection n) noexcept
214 {
215     payload = makePayload(id, x, y, -1, -1, Components::None, n);
216 }
217 
asMixerSolo()218 Connector &Connector::asMixerSolo() noexcept
219 {
220     payload->defaultComponent = Components::Switch;
221     payload->w = 22;
222     payload->h = 15;
223     return withBackground(IDB_MIXER_SOLO);
224 }
asMixerMute()225 Connector &Connector::asMixerMute() noexcept
226 {
227     payload->defaultComponent = Components::Switch;
228     payload->w = 22;
229     payload->h = 15;
230     return withBackground(IDB_MIXER_MUTE);
231 }
asMixerRoute()232 Connector &Connector::asMixerRoute() noexcept
233 {
234     payload->defaultComponent = Components::HSwitch2;
235     payload->w = 22;
236     payload->h = 15;
237     return withHSwitch2Properties(IDB_MIXER_OSC_ROUTING, 3, 1, 3);
238 }
239 
asJogPlusMinus()240 Connector &Connector::asJogPlusMinus() noexcept
241 {
242     payload->defaultComponent = Components::HSwitch2;
243     payload->w = 32;
244     payload->h = 12;
245     return withHSwitch2Properties(IDB_PREVNEXT_JOG, 2, 1, 2)
246         .withProperty(Component::DRAGGABLE_HSWITCH, false);
247 }
248 
connectorByID(const std::string & id)249 Connector Connector::connectorByID(const std::string &id)
250 {
251     guaranteeMap();
252     Connector c;
253     if (idmap->find(id) != idmap->end())
254         c.payload = idmap->at(id);
255     return c;
256 }
connectorByNonParameterConnection(NonParameterConnection n)257 Connector Connector::connectorByNonParameterConnection(NonParameterConnection n)
258 {
259     guaranteeMap();
260     Connector c;
261     if (npcMap->find(n) != npcMap->end())
262         c.payload = npcMap->at(n);
263     return c;
264 }
265 
connectorsByComponentType(const Component & c)266 std::vector<Connector> Connector::connectorsByComponentType(const Component &c)
267 {
268     auto res = std::vector<Connector>();
269     guaranteeMap();
270     for (auto it : *idmap)
271     {
272         if (it.second->defaultComponent == c)
273             res.push_back(Connector(it.second));
274     }
275     return res;
276 }
277 
Color(std::string name,int r,int g,int b)278 Color::Color(std::string name, int r, int g, int b) : name(name), r(r), g(g), b(b), a(255)
279 {
280     guaranteeMap();
281     colMap->insert(std::make_pair(name, *this));
282 }
Color(std::string name,int r,int g,int b,int a)283 Color::Color(std::string name, int r, int g, int b, int a) : name(name), r(r), g(g), b(b), a(a)
284 {
285     guaranteeMap();
286     colMap->insert(std::make_pair(name, *this));
287 }
288 
colorByName(const std::string & n)289 Color Color::colorByName(const std::string &n)
290 {
291     guaranteeMap();
292     if (colMap->find(n) != colMap->end())
293         return colMap->at(n);
294     return Color(n, 255, 0, 0);
295 }
296 
getAllColors()297 std::vector<Color> Color::getAllColors()
298 {
299     guaranteeMap();
300     auto res = std::vector<Color>();
301     for (auto c : *colMap)
302         res.push_back(c.second);
303     std::sort(res.begin(), res.end(), [](const Color &a, const Color &b) {
304         return strcmp(a.name.c_str(), b.name.c_str()) < 0;
305     });
306     return res;
307 }
308 
allConnectorIDs()309 std::vector<std::string> Connector::allConnectorIDs()
310 {
311     guaranteeMap();
312     auto res = std::vector<std::string>();
313     for (auto c : *idmap)
314         res.push_back(c.first);
315     std::sort(res.begin(), res.end(), [](auto a, auto b) {
316         auto q = strnatcasecmp(a.c_str(), b.c_str());
317         return q < 0;
318     });
319     return res;
320 }
321 } // namespace Skin
322 } // namespace Surge
323