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