1 /* B.Shapr
2  * Beat / envelope shaper LV2 plugin
3  *
4  * Copyright (C) 2019 by Sven Jähnichen
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3, or (at your option)
9  * any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20 
21 #include "BShaprGUI.hpp"
22 
BShaprGUI(const char * bundlePath,const LV2_Feature * const * features,PuglNativeView parentWindow)23 BShaprGUI::BShaprGUI (const char *bundlePath, const LV2_Feature *const *features, PuglNativeView parentWindow) :
24 	Window (1200, 710, "B.Shapr", parentWindow, true, PUGL_MODULE, 0),
25 	controller (NULL), write_function (NULL),
26 	bpm (120), beatsPerBar (4.0), beatUnit (4),
27 
28 	mContainer (0, 0, 1200, 710, "widget"),
29 	messageLabel (500, 45, 500, 20, "label", ""),
30 	bypassButton (1090, 15, 16, 16, "redbutton"),
31 	drywetDial (1144, 5, 33, 40, "dial", 1.0, 0.0, 1.0, 0.0, "%1.2f"),
32 	midiTriggerSwitch (760, 568, 30, 12, "dial", 0),
33 	midiPiano (760, 585, 150, 30, "widget", 0, 11),
34 	midiThruSwitch (1044, 575, 12, 30, "dial", 0),
35 	baseValueSelect (480, 660, 100, 20, "select", 1.0, 1.0, 16.0, 0.01),
36 	baseListBox (620, 660, 100, 20, 0, -80, 100, 80, "menu", BItems::ItemList ({{0, "Seconds"}, {1, "Beats"}, {2, "Bars"}})),
37 	monitorContainer (24, 134, 1152, 352, "monitor"),
38 	monitorHorizon1 (0, 0, 0, 64, 352, "horizon"),
39 	monitorHorizon2 (-1152, 0, 0, 64, 352, "horizon"),
40 	input1Monitor (0, 0, 1152, 176, "monitor.in"),
41 	output1Monitor (0, 0, 1152, 176, "monitor.out"),
42 	input2Monitor (0, 176, 1152, 176, "monitor.in"),
43 	output2Monitor (0, 176, 1152, 176, "monitor.out"),
44 
45 	shapeBuffer {0.0},
46 	horizonPos (0), monitorScale (0.25), minorXSteps (1.0), majorXSteps (1.0),
47 	clipboard (),
48 	pluginPath (bundlePath ? std::string (bundlePath) : std::string ("")),
49 	sz (1.0),
50 	bgImageSurface (nullptr), forge (), urids (), map (NULL)
51 
52 {
53 	// Init shapes
54 	for (int i = 0; i < MAXSHAPES; ++i)
55 	{
56 		shapeGui[i].tabContainer = BWidgets::Widget (20 + i * 148, 90, 147, 40, "tab");
57 		shapeGui[i].tabIcon = BWidgets::ImageIcon (0, 12.5, 120, 15, "widget", pluginPath + "inc/Shape" + std::to_string (i + 1) + ".png");
58 		for (int j = 0; j< NRSYMBOLS; ++j) shapeGui[i].tabSymbol[j] = SymbolWidget (120 + (j % 2) * 14 , 8 + int (j / 2) * 14, 10, 10, "symbol", SWSymbol(j));
59 		shapeGui[i].shapeContainer = BWidgets::Widget (20, 130, 1160, 510, "widget");
60 
61 		// Method menu
62 		BItems::ItemList il;
63 		shapeGui[i].methodIcons = {};
64 		for (int j = 0; j < MAXEFFECTS; ++j)
65 		{
66 			std::string iconPath = "";
67 			int index = 0;
68 			for (int k = 0; k < MAXEFFECTS; ++k)
69 			{
70 				if (j == methods[k].listIndex)
71 				{
72 					iconPath = pluginPath + methods[k].iconFileName;
73 					index = k;
74 					break;
75 				}
76 			}
77 
78 			shapeGui[i].methodIcons.push_back (BWidgets::ImageIcon (0, 0, 154, 54, "icon", std::vector<std::string>{iconPath, iconPath}));
79 
80 			BWidgets::ImageIcon* icon = &*std::prev (shapeGui[i].methodIcons.end ());
81 			cairo_t* cr = cairo_create (icon->getIconSurface (BColors::NORMAL));
82 			cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.5);
83 			cairo_paint (cr);
84 			cairo_destroy (cr);
85 
86 			il.push_back (BItems::Item (index, icon));
87 		}
88 
89 		shapeGui[i].targetListBox = BWidgets::PopupListBox (20, 443, 174, 54, 0, -380, 154, 380, "menu2", il, 0);
90 
91 		shapeGui[i].shapeWidget = ShapeWidget (4, 4, 1152, 352, "shape");
92 		shapeGui[i].tabMsgBox = nullptr;
93 		shapeGui[i].tabMsgBoxBg = nullptr;
94 		shapeGui[i].smoothingLabel = BWidgets::Label (960, 410, 40, 10, "ssmlabel", "Smooth");
95 		shapeGui[i].smoothingDial = BWidgets::DialValue (960, 366, 40, 48, "dial", 20.0, 0.0, 100.0, 0, "%3.1f ms");
96 		shapeGui[i].toolSelect = SelectWidget (133, 368, 284, 44, "tool", 44, 44, 5, 2,
97 			{"Select", "Point node", "Auto Bezier node", "Symmetric Bezier node", "Asymmetric Bezier node"});
98 		for (int j = 0; j < 4; ++j) shapeGui[i].editWidgets[j] = EditWidget (463 + j * 60, 368, 44, 44, "widget", editWidgetLabels[j]);
99 		for (int j = 4; j < 7; ++j) shapeGui[i].editWidgets[j] = EditWidget (503 + j * 60, 368, 44, 44, "widget", editWidgetLabels[j]);
100 		shapeGui[i].gridSelect = SelectWidget (1043, 368, 104, 44, "tool", 44, 44, 2, 2, {"Show grid", "Snap to grid"});
101 		shapeGui[i].drywetLabel = BWidgets::Label (500, 494, 50, 16, "smlabel", "dry/wet");
102 		shapeGui[i].drywetDial = BWidgets::DialValue (500, 434, 50, 60, "dial", 1.0, 0.0, 1.0, 0, "%1.2f");
103 
104 		for (int j = 0; j < MAXOPTIONS; ++j)
105 		{
106 			switch (options[j].widgetType)
107 			{
108 				case NO_WIDGET:
109 					shapeGui[i].optionWidgets[j] = nullptr;
110 					break;
111 
112 				case DIAL_WIDGET:
113 					shapeGui[i].optionWidgets[j] = new BWidgets::DialValue
114 					(
115 						0, 0, 50, 60, "dial",
116 						options[j].value, options[j].limit.min, options[j].limit.max, options[j].limit.step,
117 						options[j].param.get<std::string>()
118 					);
119 					if (!shapeGui[i].optionWidgets[j]) throw std::bad_alloc();
120 					shapeGui[i].optionWidgets[j]->setHardChangeable (false);
121 					shapeGui[i].optionLabels[j] = BWidgets::Label (220 + j * 70, 494, 70, 16, "smlabel", options[j].name);
122 					break;
123 
124 				case POPUP_WIDGET:
125 					{
126 						BItems::ItemList il = options[j].param.get<BItems::ItemList>();
127 						size_t max = 0;
128 						for (BItems::Item const& it : il)
129 						{
130 							if (it.getWidget())
131 							{
132 								BWidgets::Label* l = (BWidgets::Label*) it.getWidget();
133 								if (l->getText().size() > max) max = l->getText().size();
134 							}
135 						}
136 						int w = max * 9 + 20;
137 						w = LIMIT (w, 50, 270 - j * 70);
138 						int h = (il.size() + 1) * 20;
139 						h = LIMIT (h, 20, 400);
140 						shapeGui[i].optionWidgets[j] = new BWidgets::PopupListBox
141 						(
142 							0, 0, w, 20, 0, -h, w, h, "menu", il,
143 							options[j].value
144 						);
145 						if (!shapeGui[i].optionWidgets[j]) throw std::bad_alloc();
146 						shapeGui[i].optionLabels[j] = BWidgets::Label (220 + j * 70, 494, w, 16, "smlabel", options[j].name);
147 					}
148 					break;
149 
150 				default:
151 					shapeGui[i].optionWidgets[j] = nullptr;
152 					break;
153 			}
154 		}
155 	}
156 
157 	// Init main monitor
158 	initMonitors ();
159 
160 	// Link controller widgets
161 	controllerWidgets.fill (nullptr);
162 	controllerWidgets[BYPASS] = (BWidgets::ValueWidget*) &bypassButton;
163 	controllerWidgets[DRY_WET] = (BWidgets::ValueWidget*) &drywetDial;
164 	controllerWidgets[MIDI_CONTROL] = (BWidgets::ValueWidget*) &midiTriggerSwitch;
165 	controllerWidgets[MIDI_THRU] = (BWidgets::ValueWidget*) &midiThruSwitch;
166 	controllerWidgets[BASE] = (BWidgets::ValueWidget*) &baseListBox;
167 	controllerWidgets[BASE_VALUE] = (BWidgets::ValueWidget*) &baseValueSelect;
168 	for (int i = 0; i < MAXSHAPES; ++i)
169 	{
170 		controllerWidgets[SHAPERS + i * SH_SIZE + SH_TARGET] = (BWidgets::ValueWidget*) &shapeGui[i].targetListBox;
171 		controllerWidgets[SHAPERS + i * SH_SIZE + SH_SMOOTHING] = (BWidgets::ValueWidget*) &shapeGui[i].smoothingDial;
172 		controllerWidgets[SHAPERS + i * SH_SIZE + SH_DRY_WET] = (BWidgets::ValueWidget*) &shapeGui[i].drywetDial;
173 
174 		for (int j = 0 ; j < MAXOPTIONS; ++j)
175 		{
176 			controllerWidgets[SHAPERS + i * SH_SIZE + SH_OPTION + j] = (shapeGui[i].optionWidgets[j] ? shapeGui[i].optionWidgets[j] : nullptr);
177 		}
178 	}
179 
180 	// Init controllers
181 	for (int i = 0; i < NR_CONTROLLERS; ++i) controllers[i] = (controllerWidgets[i] ? controllerWidgets[i]->getValue () : 0);
182 
183 	// Set callbacks
184 	bypassButton.setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, BShaprGUI::valueChangedCallback);
185 	drywetDial.setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, BShaprGUI::valueChangedCallback);
186 	midiTriggerSwitch.setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, BShaprGUI::valueChangedCallback);
187 	midiPiano.setCallbackFunction (BEvents::EventType::BUTTON_PRESS_EVENT, BShaprGUI::pianoCallback);
188 	midiPiano.setCallbackFunction (BEvents::EventType::BUTTON_RELEASE_EVENT, BShaprGUI::pianoCallback);
189 	midiPiano.setCallbackFunction (BEvents::EventType::POINTER_DRAG_EVENT, BShaprGUI::pianoCallback);
190 	midiThruSwitch.setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, BShaprGUI::valueChangedCallback);
191 	baseValueSelect.setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, BShaprGUI::valueChangedCallback);
192 	baseListBox.setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, BShaprGUI::valueChangedCallback);
193 	monitorContainer.setCallbackFunction (BEvents::EventType::WHEEL_SCROLL_EVENT, BShaprGUI::wheelScrolledCallback);
194 	for (int i = 0; i < MAXSHAPES; ++i)
195 	{
196 		shapeGui[i].tabContainer.setCallbackFunction (BEvents::EventType::BUTTON_PRESS_EVENT, BShaprGUI::tabClickedCallback);
197 		shapeGui[i].tabIcon.setCallbackFunction (BEvents::EventType::BUTTON_PRESS_EVENT, BShaprGUI::tabClickedCallback);
198 		for (int j = 0; j< NRSYMBOLS; ++j) shapeGui[i].tabSymbol[j].setCallbackFunction (BEvents::EventType::BUTTON_PRESS_EVENT, BShaprGUI::tabClickedCallback);
199 		shapeGui[i].smoothingDial.setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, BShaprGUI::valueChangedCallback);
200 		shapeGui[i].targetListBox.setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, BShaprGUI::valueChangedCallback);
201 		shapeGui[i].drywetDial.setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, BShaprGUI::valueChangedCallback);
202 		shapeGui[i].shapeWidget.setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, BShaprGUI::shapeChangedCallback);
203 		shapeGui[i].toolSelect.setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, BShaprGUI::toolChangedCallback);
204 		for (int j = 0; j < 7; ++j) shapeGui[i].editWidgets[j].setCallbackFunction (BEvents::EventType::BUTTON_PRESS_EVENT, BShaprGUI::editClickedCallback);
205 		for (int j = 0; j < 7; ++j) shapeGui[i].editWidgets[j].setCallbackFunction (BEvents::EventType::BUTTON_RELEASE_EVENT, BShaprGUI::editReleasedCallback);
206 		shapeGui[i].gridSelect.setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, BShaprGUI::gridChangedCallback);
207 
208 		for (int j = 0 ; j < MAXOPTIONS; ++j)
209 		{
210 			if (shapeGui[i].optionWidgets[j]) shapeGui[i].optionWidgets[j]->setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, BShaprGUI::valueChangedCallback);
211 		}
212 
213 		shapeGui[i].shapeWidget.setMergeable (BEvents::POINTER_DRAG_EVENT, false);
214 	}
215 
216 	// Configure widgets
217 	calculateXSteps ();
218 	mContainer.loadImage (BColors::NORMAL, pluginPath + BG_FILE);
219 	std::vector<bool> keys (12, true);
220 	drywetDial.setHardChangeable (false);
221 	midiPiano.setKeysToggleable (true);
222 	midiPiano.pressKeys (keys);
223 	midiPiano.hide();
224 	monitorContainer.setScrollable (true);
225 	input1Monitor.setScrollable (false);
226 	output1Monitor.setScrollable (false);
227 	input2Monitor.setScrollable (false);
228 	output2Monitor.setScrollable (false);
229 	shapeGui[0].tabContainer.rename ("activetab");
230 	for (unsigned int i = 0; i < MAXSHAPES; ++i)
231 	{
232 		for (int j = 0; j< NRSYMBOLS; ++j)
233 		{
234 			if (j != CLOSESYMBOL) shapeGui[i].tabSymbol[j].hide ();
235 		}
236 		shapeGui[i].shapeWidget.setTool (ToolType::POINT_NODE_TOOL);
237 		shapeGui[i].shapeWidget.setScaleParameters (methods[0].anchorYPos, methods[0].anchorValue, methods[0].ratio);
238 		shapeGui[i].shapeWidget.setLowerLimit (methods[0].limit.min);
239 		shapeGui[i].shapeWidget.setHigherLimit (methods[0].limit.max);
240 		shapeGui[i].shapeWidget.setTransformation (methods[0].transformFactor, methods[0].transformOffset);
241 		shapeGui[i].smoothingDial.setHardChangeable (false);
242 		shapeGui[i].drywetDial.setHardChangeable (false);
243 
244 		for (int j = 0; j < MAXOPTIONS; ++j)
245 		{
246 			if (shapeGui[i].optionWidgets[j]) shapeGui[i].optionWidgets[j]->hide ();
247 			shapeGui[i].optionLabels[j].hide ();
248 		}
249 
250 		shapeGui[i].shapeContainer.setScrollable (false);
251 		if (i >= 1) shapeGui[i].shapeContainer.hide ();
252 	}
253 	applyChildThemes ();
254 	getKeyGrabStack()->add (this);
255 
256 	// Pack widgets
257 	monitorContainer.add (input1Monitor);
258 	monitorContainer.add (output1Monitor);
259 	monitorContainer.add (input2Monitor);
260 	monitorContainer.add (output2Monitor);
261 	monitorContainer.add (monitorHorizon1);
262 	monitorContainer.add (monitorHorizon2);
263 	mContainer.add (monitorContainer);
264 	for (unsigned int i = 0; i < MAXSHAPES; ++i)
265 	{
266 		shapeGui[i].tabContainer.add (shapeGui[i].tabIcon);
267 		for (int j = 0; j< NRSYMBOLS; ++j) shapeGui[i].tabContainer.add (shapeGui[i].tabSymbol[j]);
268 		mContainer.add (shapeGui[i].tabContainer);
269 		shapeGui[i].shapeContainer.add (shapeGui[i].smoothingLabel);
270 		shapeGui[i].shapeContainer.add (shapeGui[i].smoothingDial);
271 		shapeGui[i].shapeContainer.add (shapeGui[i].drywetLabel);
272 		shapeGui[i].shapeContainer.add (shapeGui[i].drywetDial);
273 		shapeGui[i].shapeContainer.add (shapeGui[i].shapeWidget);
274 		shapeGui[i].shapeContainer.add (shapeGui[i].toolSelect);
275 		for (int j = 0; j < 7; ++j) shapeGui[i].shapeContainer.add (shapeGui[i].editWidgets[j]);
276 		shapeGui[i].shapeContainer.add (shapeGui[i].gridSelect);
277 		shapeGui[i].shapeContainer.add (shapeGui[i].targetListBox);
278 
279 		for (int j = 0; j < MAXOPTIONS; ++j)
280 		{
281 			if (shapeGui[i].optionWidgets[j]) shapeGui[i].shapeContainer.add (*shapeGui[i].optionWidgets[j]);
282 			shapeGui[i].shapeContainer.add (shapeGui[i].optionLabels[j]);
283 		}
284 
285 		mContainer.add (shapeGui[i].shapeContainer);
286 	}
287 	mContainer.add (bypassButton);
288 	mContainer.add (drywetDial);
289 	mContainer.add (midiTriggerSwitch);
290 	mContainer.add (midiPiano);
291 	mContainer.add (midiThruSwitch);
292 	mContainer.add (messageLabel);
293 	mContainer.add (baseValueSelect);
294 	mContainer.add (baseListBox);
295 	add (mContainer);
296 
297 	// Post addition configurations
298 	for (int i = 0; i < MAXSHAPES; ++i)
299 	{
300 		shapeGui[i].shapeWidget.setDefaultShape();
301 		shapeGui[i].shapeWidget.setValueEnabled (true);
302 	}
303 
304 	//Scan host features for URID map
305 	LV2_URID_Map* m = NULL;
306 	for (int i = 0; features[i]; ++i)
307 	{
308 		if (strcmp(features[i]->URI, LV2_URID__map) == 0) m = (LV2_URID_Map*) features[i]->data;
309 	}
310 	if (!m) throw std::invalid_argument ("Host does not support urid:map");
311 
312 	//Map URIS
313 	map = m;
314 	mapURIDs (map, &urids);
315 
316 	// Initialize forge
317 	lv2_atom_forge_init (&forge, map);
318 }
319 
~BShaprGUI()320 BShaprGUI::~BShaprGUI()
321 {
322 	for (int i = 0; i < MAXSHAPES; ++i)
323 	{
324 		for (int j = 0; j < MAXOPTIONS; ++j)
325 		{
326 			if (shapeGui[i].optionWidgets[j]) delete shapeGui[i].optionWidgets[j];
327 		}
328 
329 		if (shapeGui[i].tabMsgBox) delete shapeGui[i].tabMsgBox;
330 		if (shapeGui[i].tabMsgBoxBg) delete shapeGui[i].tabMsgBoxBg;
331 	}
332 	sendGuiOff ();
333 }
334 
portEvent(uint32_t port,uint32_t bufferSize,uint32_t format,const void * buffer)335 void BShaprGUI::portEvent(uint32_t port, uint32_t bufferSize, uint32_t format, const void* buffer)
336 {
337 	// Notify port
338 	if ((format == urids.atom_eventTransfer) && (port == NOTIFY))
339 	{
340 		const LV2_Atom* atom = (const LV2_Atom*) buffer;
341 		if ((atom->type == urids.atom_Blank) || (atom->type == urids.atom_Object))
342 		{
343 			const LV2_Atom_Object* obj = (const LV2_Atom_Object*) atom;
344 
345 			// Monitor notification
346 			if (obj->body.otype == urids.notify_monitorEvent)
347 			{
348 				const LV2_Atom* data = NULL;
349 				lv2_atom_object_get(obj, urids.notify_monitor, &data, 0);
350 				if (data && (data->type == urids.atom_Vector))
351 				{
352 					const LV2_Atom_Vector* vec = (const LV2_Atom_Vector*) data;
353 					if (vec->body.child_type == urids.atom_Float)
354 					{
355 						uint32_t notificationsCount = (uint32_t) ((data->size - sizeof(LV2_Atom_Vector_Body)) / sizeof (BShaprNotifications));
356 						BShaprNotifications* notifications = (BShaprNotifications*) (&vec->body + 1);
357 						if (notificationsCount > 0)
358 						{
359 							std::pair<int, int> pos = translateNotification (notifications, notificationsCount);
360 							int p1 = LIMIT (pos.first, 0, MONITORBUFFERSIZE - 1);
361 							int p2 = LIMIT (pos.second, 0, MONITORBUFFERSIZE - 1);
362 
363 							if (p1 <= p2)
364 							{
365 								updateMonitors (p1, p2);
366 								updateHorizon ();
367 							}
368 							else
369 							{
370 								updateMonitors (p1, MONITORBUFFERSIZE - 1);
371 								updateMonitors (0, p2);
372 								updateHorizon ();
373 							}
374 						}
375 					}
376 				}
377 				else std::cerr << "BShapr.lv2#GUI: Corrupt audio message." << std::endl;
378 			}
379 
380 			// Message notification
381 			else if (obj->body.otype == urids.notify_messageEvent)
382 			{
383 				const LV2_Atom* data = NULL;
384 				lv2_atom_object_get(obj, urids.notify_message, &data, 0);
385 				if (data && (data->type == urids.atom_Int))
386 				{
387 					const int messageNr = ((LV2_Atom_Int*)data)->body;
388 					std::string msg = ((messageNr >= NO_MSG) && (messageNr <= MAX_MSG) ? messageStrings[messageNr] : "");
389 					messageLabel.setText (msg);
390 				}
391 			}
392 
393 			// Status notification
394 			else if (obj->body.otype == urids.notify_statusEvent)
395 			{
396 				const LV2_Atom *oBpb = NULL, *oBu = NULL, *oBpm = NULL;
397 				lv2_atom_object_get
398 				(
399 					obj,
400 					urids.time_beatsPerBar, &oBpb,
401 					urids.time_beatUnit, &oBu,
402 					urids.time_beatsPerMinute, &oBpm,
403 					0);
404 				if (oBpb && (oBpb->type == urids.atom_Float))
405 				{
406 					float value = ((LV2_Atom_Float*)oBpb)->body;
407 					if (value != 0.0f)
408 					{
409 						beatsPerBar = value;
410 						calculateXSteps ();
411 						updateHorizon ();
412 					}
413 				}
414 
415 				if (oBu && (oBu->type == urids.atom_Int))
416 				{
417 					float value = ((LV2_Atom_Float*)oBu)->body;
418 					if (int (value) != 0)
419 					{
420 						beatUnit = value;
421 						calculateXSteps ();
422 					}
423 				}
424 
425 				if (oBpm && (oBpm->type == urids.atom_Float))
426 				{
427 					float value = ((LV2_Atom_Float*)oBpm)->body;
428 					if (value != 0.0f)
429 					{
430 						bpm = value;
431 						updateHorizon ();
432 					}
433 				}
434 			}
435 
436 			// Shape notification
437 			else if (obj->body.otype == urids.notify_shapeEvent)
438 			{
439 				LV2_Atom *sNr = NULL, *sData = NULL;
440 				lv2_atom_object_get (obj, urids.notify_shapeNr, &sNr,
441 										  urids.notify_shapeData, &sData, 0);
442 
443 				if (sNr && (sNr->type == urids.atom_Int) &&
444 					sData && (sData->type == urids.atom_Vector))
445 				{
446 					int shapeNr = ((LV2_Atom_Int*)sNr)->body;
447 
448 					if ((shapeNr >= 0) && (shapeNr < MAXSHAPES))
449 					{
450 						const LV2_Atom_Vector* vec = (const LV2_Atom_Vector*) sData;
451 						size_t vecSize = (sData->size - sizeof(LV2_Atom_Vector_Body)) / (7 * sizeof (float));
452 						if (vec->body.child_type == urids.atom_Float)
453 						{
454 							shapeGui[shapeNr].shapeWidget.setValueEnabled (false);
455 							shapeGui[shapeNr].shapeWidget.clearShape ();
456 							float* data = (float*)(&vec->body + 1);
457 							for (unsigned int nodeNr = 0; (nodeNr < vecSize) && (nodeNr < MAXNODES); ++nodeNr)
458 							{
459 								Node node (&data[nodeNr * 7]);
460 								shapeGui[shapeNr].shapeWidget.appendRawNode (node);
461 							}
462 							shapeGui[shapeNr].shapeWidget.validateShape();
463 							shapeGui[shapeNr].shapeWidget.pushToSnapshots ();
464 							shapeGui[shapeNr].shapeWidget.update ();
465 							shapeGui[shapeNr].shapeWidget.setValueEnabled (true);
466 						}
467 					}
468 				}
469 			}
470 		}
471 	}
472 
473 
474 	// Scan controller ports
475 	else if ((format == 0) && (port >= CONTROLLERS) && (port < CONTROLLERS + NR_CONTROLLERS))
476 	{
477 		// Calulate controller nr and validate controller value
478 		int controllerNr = port - CONTROLLERS;
479 		float* pval = (float*) buffer;
480 
481 		if (controllerNr < SHAPERS)
482 		{
483 			float oldValue = controllers[controllerNr];
484 			float value = globalControllerLimits[controllerNr].validate (*pval);
485 			controllers[controllerNr] = value;
486 
487 			// Active shape
488 			if (controllerNr == ACTIVE_SHAPE)
489 			{
490 				int sh = LIMIT (oldValue, 1, MAXSHAPES) - 1;
491 				shapeGui[sh].tabContainer.rename ("tab");
492 				shapeGui[sh].tabContainer.applyTheme (theme);
493 				shapeGui[sh].shapeContainer.hide();
494 				int nsh = value - 1;
495 				shapeGui[nsh].tabContainer.rename ("activetab");
496 				shapeGui[nsh].tabContainer.applyTheme (theme);
497 				shapeGui[nsh].shapeContainer.show();
498 				updateHorizon ();
499 			}
500 
501 			// Keys
502 			else if (controllerNr == MIDI_KEYS)
503 			{
504 				uint32_t bits = value;
505 				std::vector<bool> keys (12, false);
506 				for (int i = 0; i < 12; ++i)
507 				{
508 					if (bits & (1 << i)) keys[i] = true;
509 				}
510 				midiPiano.pressKeys (keys);
511 			}
512 
513 			// MIDI connected ?
514 			// Show or hide piano
515 			else if (controllerNr == MIDI_CONTROL)
516 			{
517 				if (controllerWidgets[MIDI_CONTROL]) controllerWidgets[MIDI_CONTROL]->setValue (value);
518 
519 				if (value == 1.0f) midiPiano.show ();
520 				else midiPiano.hide ();
521 			}
522 
523 			// Base, base value
524 			// Update recalculate shaper grid steps
525 			else if ((controllerNr == BASE) || (controllerNr == BASE_VALUE))
526 			{
527 				if (controllerWidgets[controllerNr]) controllerWidgets[controllerNr]->setValue (value);
528 			}
529 		}
530 
531 		// Shapers
532 		else if ((controllerNr >= SHAPERS) && (controllerNr < SHAPERS + SH_SIZE * MAXSHAPES))
533 		{
534 			int shapeNr = (port - CONTROLLERS - SHAPERS) / SH_SIZE;
535 			int shapeWidgetNr = port - CONTROLLERS - SHAPERS - shapeNr * SH_SIZE;
536 			float value = shapeControllerLimits[shapeWidgetNr].validate (*pval);
537 			controllers[controllerNr] = value;
538 			if (controllerWidgets[controllerNr]) controllerWidgets[controllerNr]->setValue (value);
539 
540 			// SH_OUTPUT
541 			if (shapeWidgetNr == SH_OUTPUT) updateTabs ();
542 		}
543 	}
544 }
545 
updateTabs()546 void BShaprGUI::updateTabs ()
547 {
548 	int lastShape = 0;
549 	for (int i = 0; i < MAXSHAPES; ++i)
550 	{
551 		if (controllers[SHAPERS + i * SH_SIZE + SH_OUTPUT] == AUDIO_OUT) lastShape = i;
552 	}
553 
554 	// Show or hide tabs
555 	for (int i = 0; i < MAXSHAPES; ++i)
556 	{
557 		if (i <= lastShape) shapeGui[i].tabContainer.show ();
558 		else shapeGui[i].tabContainer.hide ();
559 	}
560 
561 	// Show or hide tabSymbols
562 	for (int i = 0; i < MAXSHAPES; ++i)
563 	{
564 		if (lastShape + 1 < MAXSHAPES) shapeGui[i].tabSymbol[ADDSYMBOL].show ();
565 		else shapeGui[i].tabSymbol[ADDSYMBOL].hide ();
566 
567 		if ((i > 0) && (i <= lastShape)) shapeGui[i].tabSymbol[LEFTSYMBOL].show ();
568 		else shapeGui[i].tabSymbol[LEFTSYMBOL].hide ();
569 
570 		if (i < lastShape) shapeGui[i].tabSymbol[RIGHTSYMBOL].show ();
571 		else shapeGui[i].tabSymbol[RIGHTSYMBOL].hide ();
572 	}
573 }
574 
resizeGUI(const double sz)575 void BShaprGUI::resizeGUI(const double sz)
576 {
577 	hide ();
578 
579 	// Resize Fonts
580 	defaultFont.setFontSize (12.0 * sz);
581 	smFont.setFontSize (10.0 * sz);
582 	ssmFont.setFontSize (8.0 * sz);
583 	lfLabelFont.setFontSize (12.0 * sz);
584 
585 	// Resize widgets
586 	RESIZE (mContainer, 0, 0, 1200, 710, sz);
587 	RESIZE (messageLabel, 500, 45, 500, 20, sz);
588 	RESIZE (bypassButton, 1090, 15, 16, 16, sz);
589 	RESIZE (drywetDial, 1144, 5, 33, 40, sz);
590 	RESIZE (midiTriggerSwitch, 760, 568, 30, 12, sz);
591 	RESIZE (midiPiano, 760, 585, 150, 30, sz);
592 	RESIZE (midiThruSwitch, 1044, 575, 12, 30, sz);
593 	RESIZE (baseValueSelect, 480, 660, 100, 20, sz);
594 	RESIZE (baseListBox, 620, 660, 100, 20, sz);
595 	baseListBox.resizeListBox (BUtilities::Point (100 * sz, 80 * sz));
596 	baseListBox.moveListBox (BUtilities::Point (0, -80 * sz));
597 	RESIZE (monitorContainer, 24, 134, 1152, 352, sz);
598 
599 	// monitorHorizon1/2
600 	monitorHorizon1.setFadeoutWidth (64 * sz);
601 	monitorHorizon2.setFadeoutWidth (64 * sz);
602 	monitorHorizon1.setHeight (352 * sz);
603 	monitorHorizon2.setHeight (352 * sz);
604 
605 	RESIZE (input1Monitor, 0, 0, 1152, 176, sz);
606 	RESIZE (output1Monitor, 0, 0, 1152, 176, sz);
607 	RESIZE (input2Monitor, 0, 176, 1152, 176, sz);
608 	RESIZE (output2Monitor, 0, 176, 1152, 176, sz);
609 	for (int i = 0; i < MAXSHAPES; ++i)
610 	{
611 		RESIZE (shapeGui[i].tabContainer, 20 + i * 148, 90, 147, 40, sz);
612 		RESIZE (shapeGui[i].tabIcon, 0, 12.5, 120, 15, sz);
613 		for (int j = 0; j< NRSYMBOLS; ++j) RESIZE (shapeGui[i].tabSymbol[j], 120 + (j % 2) * 14 , 8 + int (j / 2) * 14, 10, 10, sz);
614 		if (shapeGui[i].tabMsgBox)
615 		{
616 			RESIZE
617 			(
618 				*shapeGui[i].tabMsgBox,
619 				shapeGui[i].tabMsgBox->getPosition().x / this->sz,
620 				shapeGui[i].tabMsgBox->getPosition().y / this->sz,
621 				shapeGui[i].tabMsgBox->getWidth () / this->sz,
622 				shapeGui[i].tabMsgBox->getHeight () / this->sz,
623 				sz
624 			);
625 		}
626 		if (shapeGui[i].tabMsgBoxBg) RESIZE (*shapeGui[i].tabMsgBoxBg, 0, 0, 1200, 710, sz);
627 		RESIZE (shapeGui[i].shapeContainer, 20, 130, 1160, 590, sz);
628 		RESIZE (shapeGui[i].smoothingLabel, 960, 410, 40, 10, sz);
629 		RESIZE (shapeGui[i].smoothingDial, 960, 366, 40, 48, sz);
630 		RESIZE (shapeGui[i].targetListBox, 20, 443, 174, 54, sz);
631 		shapeGui[i].targetListBox.resizeListBox (BUtilities::Point (174 * sz - 20, 380 * sz));
632 		shapeGui[i].targetListBox.moveListBox (BUtilities::Point (0, -380 * sz));
633 		shapeGui[i].targetListBox.resizeListBoxItems (BUtilities::Point (174 * sz - 20, 54 * sz));
634 		RESIZE (shapeGui[i].drywetLabel, 500, 494, 50, 16, sz);
635 		RESIZE (shapeGui[i].drywetDial, 500, 434, 50, 60, sz);
636 		RESIZE (shapeGui[i].shapeWidget, 4, 4, 1152, 352, sz);
637 		RESIZE (shapeGui[i].toolSelect, 133, 368, 284, 44, sz);
638 		shapeGui[i].toolSelect.resizeSelection (44 * sz, 44 * sz);
639 		for (int j = 0; j < 4; ++j) RESIZE (shapeGui[i].editWidgets[j], 463 + j * 60, 368, 44, 44, sz);
640 		for (int j = 4; j < 7; ++j) RESIZE (shapeGui[i].editWidgets[j], 503 + j * 60, 368, 44, 44, sz);
641 		RESIZE (shapeGui[i].gridSelect, 1043, 368, 104, 44, sz);
642 		shapeGui[i].gridSelect.resizeSelection (44 * sz, 44 * sz);
643 
644 		int methodNr = shapeGui[i].targetListBox.getValue ();
645 
646 		for (int j = 0; j < MAXOPTIONWIDGETS; ++j)
647 		{
648 			int optionNr = methods[methodNr].optionIndexes[j];
649 			if (optionNr != NO_OPT)
650 			{
651 				if (shapeGui[i].optionWidgets[optionNr])
652 				{
653 					switch (options[optionNr].widgetType)
654 					{
655 						case DIAL_WIDGET:
656 							RESIZE ((*shapeGui[i].optionWidgets[optionNr]), 230 + j * 70, 434, 50, 60, sz);
657 							RESIZE ((shapeGui[i].optionLabels[optionNr]), 220 + j * 70, 494, 70, 16, sz);
658 							break;
659 
660 						case POPUP_WIDGET:
661 							{
662 								BItems::ItemList il = options[optionNr].param.get<BItems::ItemList>();
663 								size_t max = 0;
664 								for (BItems::Item const& it : il)
665 								{
666 									if (it.getWidget())
667 									{
668 										BWidgets::Label* l = (BWidgets::Label*) it.getWidget();
669 										if (l->getText().size() > max) max = l->getText().size();
670 									}
671 								}
672 								int w = max * 9 + 20;
673 								w = LIMIT (w, 50, 270 - j * 70);
674 								int h = (il.size() + 1) * 20;
675 								h = LIMIT (h, 20, 400);
676 
677 								RESIZE ((*shapeGui[i].optionWidgets[optionNr]), 220 + j * 70, 455, w, 20, sz);
678 								((BWidgets::PopupListBox*) shapeGui[i].optionWidgets[optionNr])->resizeListBox (BUtilities::Point (w * sz, h * sz));
679 								((BWidgets::PopupListBox*) shapeGui[i].optionWidgets[optionNr])->moveListBox (BUtilities::Point (0, -h * sz));
680 
681 								RESIZE ((shapeGui[i].optionLabels[optionNr]), 220 + j * 70, 494, w, 16, sz);
682 							}
683 							break;
684 
685 						default:
686 							break;
687 					}
688 				}
689 			}
690 		}
691 	}
692 
693 	this-> sz = sz;
694 
695 	// Update monitor, const std::string& name
696 	initMonitors ();
697 	updateMonitors (0, MONITORBUFFERSIZE - 1);
698 	updateHorizon ();
699 
700 	// Apply changes
701 	applyChildThemes ();
702 	show ();
703 }
704 
applyChildThemes()705 void BShaprGUI::applyChildThemes ()
706 {
707 	mContainer.applyTheme (theme);
708 	messageLabel.applyTheme (theme);
709 	bypassButton.applyTheme (theme);
710 	drywetDial.applyTheme (theme);
711 	midiTriggerSwitch.applyTheme (theme);
712 	midiPiano.applyTheme (theme);
713 	midiThruSwitch.applyTheme (theme);
714 	baseValueSelect.applyTheme (theme);
715 	baseListBox.applyTheme (theme);
716 	monitorContainer.applyTheme (theme);
717 	input1Monitor.applyTheme (theme);
718 	output1Monitor.applyTheme (theme);
719 	input2Monitor.applyTheme (theme);
720 	output2Monitor.applyTheme (theme);
721 	for (unsigned int i = 0; i < MAXSHAPES; ++i)
722 	{
723 		shapeGui[i].shapeContainer.applyTheme (theme);
724 		shapeGui[i].tabContainer.applyTheme (theme);
725 		shapeGui[i].tabIcon.applyTheme (theme);
726 		for (int j = 0; j< NRSYMBOLS; ++j) shapeGui[i].tabSymbol[j].applyTheme (theme);
727 		if (shapeGui[i].tabMsgBox) shapeGui[i].tabMsgBox->applyTheme (theme);
728 		if (shapeGui[i].tabMsgBoxBg) shapeGui[i].tabMsgBoxBg->applyTheme (theme);
729 		shapeGui[i].smoothingLabel.applyTheme (theme);
730 		shapeGui[i].smoothingDial.applyTheme (theme);
731 		shapeGui[i].targetListBox.applyTheme (theme);
732 		shapeGui[i].drywetLabel.applyTheme (theme);
733 		shapeGui[i].drywetDial.applyTheme (theme);
734 		shapeGui[i].shapeWidget.applyTheme (theme);
735 		shapeGui[i].toolSelect.applyTheme (theme);
736 		for (int j = 0; j < 7; ++j) shapeGui[i].editWidgets[j].applyTheme (theme);
737 		shapeGui[i].gridSelect.applyTheme (theme);
738 
739 		int methodNr = shapeGui[i].targetListBox.getValue ();
740 		for (int j = 0; j < MAXOPTIONWIDGETS; ++j)
741 		{
742 			int optionNr = methods[methodNr].optionIndexes[j];
743 			if (optionNr != NO_OPT)
744 			{
745 				if (shapeGui[i].optionWidgets[optionNr]) shapeGui[i].optionWidgets[optionNr]->applyTheme (theme);
746 				shapeGui[i].optionLabels[optionNr].applyTheme (theme);
747 			}
748 		}
749 	}
750 }
751 
onConfigureRequest(BEvents::ExposeEvent * event)752 void BShaprGUI::onConfigureRequest (BEvents::ExposeEvent* event)
753 {
754 	Window::onConfigureRequest (event);
755 
756 	resizeGUI (getWidth() / 1200 > getHeight() / 710 ? getHeight() / 710 : getWidth() / 1200);
757 }
758 
onCloseRequest(BEvents::WidgetEvent * event)759 void BShaprGUI::onCloseRequest (BEvents::WidgetEvent* event)
760 {
761 	if (event)
762 	{
763 		Widget* requestWidget = event->getRequestWidget ();
764 		if (requestWidget)
765 		{
766 			for (int i = 0; i < MAXSHAPES; ++i)
767 			{
768 				if (requestWidget == ((BWidgets::Widget*)shapeGui[i].tabMsgBox))
769 				{
770 					double value = shapeGui[i].tabMsgBox->getValue ();
771 
772 					// Yes
773 					if (value == 2) deleteShape (i);
774 
775 					delete shapeGui[i].tabMsgBox;
776 					shapeGui[i].tabMsgBox = nullptr;
777 					delete shapeGui[i].tabMsgBoxBg;
778 					shapeGui[i].tabMsgBoxBg = nullptr;
779 
780 					return;
781 				}
782 			}
783 
784 			Window::onCloseRequest (event);
785 		}
786 	}
787 }
788 
onKeyPressed(BEvents::KeyEvent * event)789 void BShaprGUI::onKeyPressed (BEvents::KeyEvent* event)
790 {
791 	if ((event) && (event->getKey() == BDevices::KEY_SHIFT))
792 	{
793 		for (int i = 0; i < MAXSHAPES; ++i)
794 		{
795 			shapeGui[i].shapeWidget.setScrollable (false);
796 		}
797 	}
798 }
799 
onKeyReleased(BEvents::KeyEvent * event)800 void BShaprGUI::onKeyReleased (BEvents::KeyEvent* event)
801 {
802 	if ((event) && (event->getKey() == BDevices::KEY_SHIFT))
803 	{
804 		for (int i = 0; i < MAXSHAPES; ++i)
805 		{
806 			shapeGui[i].shapeWidget.setScrollable (true);
807 		}
808 	}
809 }
810 
sendGuiOn()811 void BShaprGUI::sendGuiOn ()
812 {
813 	uint8_t obj_buf[64];
814 	lv2_atom_forge_set_buffer(&forge, obj_buf, sizeof(obj_buf));
815 
816 	LV2_Atom_Forge_Frame frame;
817 	LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object(&forge, &frame, 0, urids.ui_on);
818 	lv2_atom_forge_pop(&forge, &frame);
819 	write_function(controller, CONTROL, lv2_atom_total_size(msg), urids.atom_eventTransfer, msg);
820 }
821 
sendGuiOff()822 void BShaprGUI::sendGuiOff ()
823 {
824 	uint8_t obj_buf[64];
825 	lv2_atom_forge_set_buffer(&forge, obj_buf, sizeof(obj_buf));
826 
827 	LV2_Atom_Forge_Frame frame;
828 	LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object(&forge, &frame, 0, urids.ui_off);
829 	lv2_atom_forge_pop(&forge, &frame);
830 	write_function(controller, CONTROL, lv2_atom_total_size(msg), urids.atom_eventTransfer, msg);
831 }
832 
sendShape(size_t shapeNr)833 void BShaprGUI::sendShape (size_t shapeNr)
834 {
835 	size_t size = shapeGui[shapeNr].shapeWidget.size ();
836 
837 	uint8_t obj_buf[4096];
838 	lv2_atom_forge_set_buffer(&forge, obj_buf, sizeof(obj_buf));
839 
840 	// Load shapeBuffer
841 	for (unsigned int i = 0; i < size; ++i)
842 	{
843 		Node node = shapeGui[shapeNr].shapeWidget.getRawNode (i);
844 		shapeBuffer[i * 7 + 0] = (float)node.nodeType;
845 		shapeBuffer[i * 7 + 1] = (float)node.point.x;
846 		shapeBuffer[i * 7 + 2] = (float)node.point.y;
847 		shapeBuffer[i * 7 + 3] = (float)node.handle1.x;
848 		shapeBuffer[i * 7 + 4] = (float)node.handle1.y;
849 		shapeBuffer[i * 7 + 5] = (float)node.handle2.x;
850 		shapeBuffer[i * 7 + 6] = (float)node.handle2.y;
851 	}
852 
853 	// Notify shapeBuffer
854 	LV2_Atom_Forge_Frame frame;
855 	LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object (&forge, &frame, 0, urids.notify_shapeEvent);
856 	lv2_atom_forge_key(&forge, urids.notify_shapeNr);
857 	lv2_atom_forge_int(&forge, shapeNr);
858 	lv2_atom_forge_key(&forge, urids.notify_shapeData);
859 	lv2_atom_forge_vector(&forge, sizeof(float), urids.atom_Float, (uint32_t) (7 * size), &shapeBuffer);
860 	lv2_atom_forge_pop(&forge, &frame);
861 	write_function (controller, CONTROL, lv2_atom_total_size(msg), urids.atom_eventTransfer, msg);
862 }
863 
864 
865 
setController(const int controllerNr,const float value)866 void BShaprGUI::setController (const int controllerNr, const float value)
867 {
868 	if ((controllerNr < 0) || (controllerNr >= NR_CONTROLLERS)) return;
869 
870 	if (controllerWidgets[controllerNr]) controllerWidgets[controllerNr]->setValue (value);
871 	else
872 	{
873 		controllers[controllerNr] = value;
874 		write_function(controller, CONTROLLERS + controllerNr, sizeof(float), 0, &controllers[controllerNr]);
875 	}
876 }
877 
deleteShape(const int shapeNr)878 void BShaprGUI::deleteShape (const int shapeNr)
879 {
880 	if ((shapeNr < 0) || (shapeNr >= MAXSHAPES)) return;
881 
882 	// Scan SH_OUTPUT for the last shaper sending data to audio out
883 	// Limited backward compatibilty to v0.3.2 or older
884 	int lastShape = 0;
885 	for (int i = 0; i < MAXSHAPES; ++i)
886 	{
887 		if (controllers[SHAPERS + i * SH_SIZE + SH_OUTPUT] == AUDIO_OUT) lastShape = i;
888 	}
889 
890 	// Deletion behind last shape (shouldn't happen)
891 	if (shapeNr > lastShape)
892 	{
893 		// Hide all tabContainers > lastShape
894 		for (int i = 0; i < MAXSHAPES; ++i)
895 		{
896 			if (i <= lastShape) shapeGui[i].tabContainer.show ();
897 			else shapeGui[i].tabContainer.hide ();
898 		}
899 
900 		// Jump to last valid shape
901 		if (controllers[ACTIVE_SHAPE] - 1 > lastShape) switchShape (lastShape);
902 
903 		// Get sure that SH_INPUT of shape 0 == AUDIO_IN
904 		if (controllers[SHAPERS + SH_INPUT] != AUDIO_IN) setController (SHAPERS + SH_INPUT, AUDIO_IN);
905 	}
906 
907 	// Deletion of last shape
908 	else if (shapeNr == lastShape)
909 	{
910 		// shapeNr == 0: Reset shape
911 		if (shapeNr == 0)
912 		{
913 			// Reset shape 0 controllers
914 			setController (SHAPERS + SH_INPUT, AUDIO_IN);
915 			setController (SHAPERS + SH_INPUT_AMP, 1.0f);
916 			shapeGui[0].targetListBox.setValue (0.0);
917 			shapeGui[0].drywetDial.setValue (1.0);
918 			setController (SHAPERS + SH_OUTPUT, AUDIO_OUT);
919 			setController (SHAPERS + SH_OUTPUT_AMP, 1.0f);
920 
921 			for (int j = 0; j < MAXOPTIONS; ++j)
922 			{
923 				if (shapeGui[0].optionWidgets[j]) shapeGui[0].optionWidgets[j]->setValue (options[j].value);
924 			}
925 
926 			// Reset shape 0
927 			shapeGui[0].shapeWidget.setDefaultShape ();
928 
929 			// Hide all tabContainers >= 1
930 			for (int i = 0; i < MAXSHAPES; ++i)
931 			{
932 				if (i == 0) shapeGui[i].tabContainer.show ();
933 				else shapeGui[i].tabContainer.hide ();
934 			}
935 
936 			// Jump to shape 0
937 			switchShape (0);
938 		}
939 
940 		// Otherwise: delete shapeNr
941 		else
942 		{
943 			// Link shapeNr - 1 to audio out; unlink shapeNr
944 			setController (SHAPERS + (shapeNr - 1) * SH_SIZE + SH_OUTPUT, AUDIO_OUT);
945 			setController (SHAPERS + shapeNr * SH_SIZE + SH_OUTPUT, INTERNAL);
946 
947 			// Hide all tabContainers >= shapeNr
948 			for (int i = 0; i < MAXSHAPES; ++i)
949 			{
950 				if (i < shapeNr) shapeGui[i].tabContainer.show ();
951 				else shapeGui[i].tabContainer.hide ();
952 			}
953 
954 			// Jump to last valid shape
955 			if (controllers[ACTIVE_SHAPE] - 1 > shapeNr - 1) switchShape (shapeNr - 1);
956 
957 			// Get sure that SH_INPUT of shape 0 == AUDIO_IN
958 			if (controllers[SHAPERS + SH_INPUT] != AUDIO_IN) setController (SHAPERS + SH_INPUT, AUDIO_IN);
959 		}
960 	}
961 
962 	// Deletion of start or middle shape
963 	else
964 	{
965 		// Shift shapers
966 		for (int i = shapeNr; i < lastShape; ++i)
967 		{
968 			// Copy controllers
969 			int destNr = SHAPERS + i * SH_SIZE;
970 			int srcNr = SHAPERS + (i + 1) * SH_SIZE;
971 
972 			if (i == 0) setController (destNr + SH_INPUT, AUDIO_IN);
973 			else setController (destNr + SH_INPUT, OUTPUT + i - 1);
974 
975 			setController (destNr + SH_INPUT_AMP, controllers[srcNr + SH_INPUT_AMP]);
976 			shapeGui[i].targetListBox.setValue (shapeGui[i + 1].targetListBox.getValue ());
977 			shapeGui[i].drywetDial.setValue (shapeGui[i + 1].drywetDial.getValue ());
978 
979 			if (i == lastShape - 1) setController (destNr + SH_OUTPUT, AUDIO_OUT);
980 			else setController (destNr + SH_OUTPUT, controllers[srcNr + SH_OUTPUT]);
981 
982 			setController (destNr + SH_OUTPUT_AMP, controllers[srcNr + SH_OUTPUT_AMP]);
983 
984 			for (int j = 0; j < MAXOPTIONS; ++j)
985 			{
986 				if (shapeGui[i].optionWidgets[j] && shapeGui[i + 1].optionWidgets[j])
987 				{
988 					shapeGui[i].optionWidgets[j]->setValue (shapeGui[i + 1].optionWidgets[j]->getValue ());
989 				}
990 			}
991 
992 			// Copy shapes
993 			shapeGui[i].shapeWidget = shapeGui [i + 1].shapeWidget;
994 		}
995 
996 		// Unlink lastShape
997 		setController (SHAPERS + lastShape * SH_SIZE + SH_OUTPUT, INTERNAL);
998 
999 		// Hide all tabContainers >= lastShape
1000 		for (int i = 0; i < MAXSHAPES; ++i)
1001 		{
1002 			if (i < lastShape) shapeGui[i].tabContainer.show ();
1003 			else shapeGui[i].tabContainer.hide ();
1004 		}
1005 
1006 		// Jump to last valid shape
1007 		if (controllers[ACTIVE_SHAPE] - 1 > lastShape - 1) switchShape (lastShape - 1);
1008 
1009 		// Get sure that SH_INPUT of shape 0 == AUDIO_IN
1010 		if (controllers[SHAPERS + SH_INPUT] != AUDIO_IN) setController (SHAPERS + SH_INPUT, AUDIO_IN);
1011 	}
1012 
1013 	updateTabs ();
1014 }
1015 
insertShape(const int shapeNr)1016 void BShaprGUI::insertShape (const int shapeNr)
1017 {
1018 	if ((shapeNr < 0) || (shapeNr >= MAXSHAPES - 1)) return;
1019 
1020 	// Scan SH_OUTPUT for the last shaper sending data to audio out
1021 	// Limited backward compatibilty to v0.3.2 or older
1022 	int lastShape = 0;
1023 	for (int i = 0; i < MAXSHAPES; ++i)
1024 	{
1025 		if (controllers[SHAPERS + i * SH_SIZE + SH_OUTPUT] == AUDIO_OUT) lastShape = i;
1026 	}
1027 
1028 	if (lastShape >= MAXSHAPES - 1) return;
1029 
1030 	// Insertion just behind last shape
1031 	if (shapeNr >= lastShape)
1032 	{
1033 		// Init shape lastShape + 1
1034 		int destNr = SHAPERS + (lastShape + 1) * SH_SIZE;
1035 		int srcNr = SHAPERS + lastShape * SH_SIZE;
1036 
1037 		setController (destNr + SH_INPUT, OUTPUT + lastShape);
1038 		setController (destNr + SH_INPUT_AMP, 1.0f);
1039 		shapeGui[lastShape + 1].targetListBox.setValue (0.0);
1040 		shapeGui[lastShape + 1].drywetDial.setValue (1.0);
1041 		setController (destNr + SH_OUTPUT, AUDIO_OUT);
1042 		setController (destNr + SH_OUTPUT_AMP, 1.0f);
1043 
1044 		for (int j = 0; j < MAXOPTIONS; ++j)
1045 		{
1046 			if (shapeGui[lastShape + 1].optionWidgets[j]) shapeGui[lastShape + 1].optionWidgets[j]->setValue (options[j].value);
1047 		}
1048 
1049 		// Reset shape
1050 		shapeGui[lastShape + 1].shapeWidget.setDefaultShape ();
1051 
1052 		// Link lastShape
1053 		setController (srcNr + SH_OUTPUT, INTERNAL);
1054 
1055 		// Show lastShape + 1
1056 		for (int i = 0; i < MAXSHAPES; ++i)
1057 		{
1058 			if (i <= lastShape + 1) shapeGui[i].tabContainer.show ();
1059 			else shapeGui[i].tabContainer.hide ();
1060 		}
1061 
1062 		switchShape (lastShape + 1);
1063 	}
1064 
1065 	// Insertion in the middlde
1066 	else
1067 	{
1068 		// Shift shapers
1069 		for (int i = lastShape; i > shapeNr; --i)
1070 		{
1071 			int destNr = SHAPERS + (i + 1) * SH_SIZE;
1072 			int srcNr = SHAPERS + i * SH_SIZE;
1073 
1074 			setController (destNr + SH_INPUT, OUTPUT + i);
1075 			setController (destNr + SH_INPUT_AMP, controllers[srcNr + SH_INPUT_AMP]);
1076 			shapeGui[i + 1].targetListBox.setValue (shapeGui[i].targetListBox.getValue ());
1077 			shapeGui[i + 1].drywetDial.setValue (shapeGui[i].drywetDial.getValue ());
1078 			setController (destNr + SH_OUTPUT, controllers[srcNr + SH_OUTPUT]);
1079 			setController (destNr + SH_OUTPUT_AMP, controllers[srcNr + SH_OUTPUT_AMP]);
1080 
1081 			for (int j = 0; j < MAXOPTIONS; ++j)
1082 			{
1083 				if (shapeGui[i].optionWidgets[j] && shapeGui[i + 1].optionWidgets[j])
1084 				{
1085 					shapeGui[i + 1].optionWidgets[j]->setValue (shapeGui[i].optionWidgets[j]->getValue ());
1086 				}
1087 			}
1088 
1089 			// Copy shapes
1090 			shapeGui[i + 1].shapeWidget = shapeGui[i].shapeWidget;
1091 		}
1092 
1093 		// Init shape widgets shapeNr + 1
1094 		int destNr = SHAPERS + (shapeNr + 1) * SH_SIZE;
1095 		setController (destNr + SH_INPUT, OUTPUT + shapeNr);
1096 		setController (destNr + SH_INPUT_AMP, 1.0f);
1097 		shapeGui[shapeNr + 1].targetListBox.setValue (0.0f);
1098 		shapeGui[shapeNr + 1].drywetDial.setValue (1.0f);
1099 		setController (destNr + SH_OUTPUT, INTERNAL);
1100 		setController (destNr + SH_OUTPUT_AMP, 1.0f);
1101 
1102 		// Reset shape shapeNr + 1
1103 		shapeGui[shapeNr + 1].shapeWidget.setDefaultShape ();
1104 
1105 		// Show lastShape + 1
1106 		for (int i = 0; i < MAXSHAPES; ++i)
1107 		{
1108 			if (i <= lastShape + 1) shapeGui[i].tabContainer.show ();
1109 			else shapeGui[i].tabContainer.hide ();
1110 		}
1111 
1112 		switchShape (shapeNr + 1);
1113 	}
1114 
1115 	updateTabs ();
1116 }
1117 
swapShapes(const int source,const int dest)1118 void BShaprGUI::swapShapes (const int source, const int dest)
1119 {
1120 	if ((source < 0) || (dest < 0)) return;
1121 
1122 	// Scan SH_OUTPUT for the last shaper sending data to audio out
1123 	// Limited backward compatibilty to v0.3.2 or older
1124 	int lastShape = 0;
1125 	for (int i = 0; i < MAXSHAPES; ++i)
1126 	{
1127 		if (controllers[SHAPERS + i * SH_SIZE + SH_OUTPUT] == AUDIO_OUT) lastShape = i;
1128 	}
1129 
1130 	if ((source > lastShape) || (dest > lastShape)) return;
1131 
1132 	float value;
1133 	int destNr = SHAPERS + dest * SH_SIZE;
1134 	int srcNr = SHAPERS + source * SH_SIZE;
1135 
1136 	value = controllers[destNr + SH_TARGET];
1137 	shapeGui[dest].targetListBox.setValue (controllers[srcNr + SH_TARGET]);
1138 	shapeGui[source].targetListBox.setValue (value);
1139 
1140 	value = controllers[dest + SH_DRY_WET];
1141 	shapeGui[dest].drywetDial.setValue (controllers[srcNr + SH_DRY_WET]);
1142 	shapeGui[source].drywetDial.setValue (value);
1143 
1144 	for (int j = 0; j < MAXOPTIONS; ++j)
1145 	{
1146 		if (shapeGui[source].optionWidgets[j] && shapeGui[dest].optionWidgets[j])
1147 		{
1148 			value = shapeGui[dest].optionWidgets[j]->getValue ();
1149 			shapeGui[dest].optionWidgets[j]->setValue (shapeGui[source].optionWidgets[j]->getValue ());
1150 			shapeGui[source].optionWidgets[j]->setValue (value);
1151 		}
1152 	}
1153 
1154 	// Swap shapes
1155 	ShapeWidget shBuffer;
1156 	shBuffer = shapeGui[dest].shapeWidget;
1157 	shapeGui[dest].shapeWidget = shapeGui[source].shapeWidget;
1158 	shapeGui[source].shapeWidget = shBuffer;
1159 
1160 	if (controllers[ACTIVE_SHAPE] - 1 == source) switchShape (dest);
1161 	else if (controllers[ACTIVE_SHAPE] - 1 == dest) switchShape (source);
1162 	updateTabs ();
1163 }
1164 
switchShape(const int shapeNr)1165 void BShaprGUI::switchShape (const int shapeNr)
1166 {
1167 	if ((shapeNr < 0) || (shapeNr >= MAXSHAPES) || shapeNr == controllers[ACTIVE_SHAPE] - 1) return;
1168 
1169 	int oldShapeNr = LIMIT (controllers[ACTIVE_SHAPE], 1, MAXSHAPES) - 1;
1170 	shapeGui[oldShapeNr].tabContainer.rename ("tab");
1171 	shapeGui[oldShapeNr].tabContainer.applyTheme (theme);
1172 	shapeGui[oldShapeNr].shapeContainer.hide();
1173 	setController (ACTIVE_SHAPE, shapeNr + 1);
1174 	shapeGui[shapeNr].tabContainer.rename ("activetab");
1175 	shapeGui[shapeNr].tabContainer.applyTheme (theme);
1176 	shapeGui[shapeNr].shapeContainer.show();
1177 	updateHorizon ();
1178 }
1179 
valueChangedCallback(BEvents::Event * event)1180 void BShaprGUI::valueChangedCallback (BEvents::Event* event)
1181 {
1182 	if ((event) && (event->getWidget ()))
1183 	{
1184 		BWidgets::ValueWidget* widget = (BWidgets::ValueWidget*) event->getWidget ();
1185 		float value = widget->getValue ();
1186 
1187 		if (widget->getMainWindow ())
1188 		{
1189 			BShaprGUI* ui = (BShaprGUI*) widget->getMainWindow ();
1190 			int widgetNr = -1;
1191 
1192 			for (int i = 0; i < NR_CONTROLLERS; ++i)
1193 			{
1194 				if (widget == ui->controllerWidgets[i])
1195 				{
1196 					widgetNr = i;
1197 					break;
1198 				}
1199 			}
1200 
1201 			// Controllers (menus, dials, selectors, ...)
1202 			if (widgetNr >= 0)
1203 			{
1204 				if (widgetNr == MIDI_CONTROL)
1205 				{
1206 					if (value == 1.0f) ui->midiPiano.show ();
1207 					else ui->midiPiano.hide ();
1208 				}
1209 
1210 				else if ((widgetNr == BASE) || (widgetNr == BASE_VALUE))
1211 				{
1212 					ui->controllers[widgetNr] = value;
1213 					ui->calculateXSteps ();
1214 					ui->updateHorizon ();
1215 				}
1216 
1217 				else if (widgetNr >= SHAPERS)
1218 				{
1219 					int shapeNr = (widgetNr - SHAPERS) / SH_SIZE;
1220 					int shapeWidgetNr = (widgetNr - SHAPERS) - shapeNr * SH_SIZE;
1221 
1222 					// Target
1223 					if (shapeWidgetNr == SH_TARGET)
1224 					{
1225 						int nr = LIMIT (value, 0, MAXEFFECTS - 1);
1226 
1227 						// Change transformation
1228 						ui->shapeGui[shapeNr].shapeWidget.setTransformation (methods[nr].transformFactor, methods[nr].transformOffset);
1229 
1230 						// Set shapeWidget display parameters (limits, unit, prefix, ...)
1231 						ui->shapeGui[shapeNr].shapeWidget.setScaleParameters
1232 						(
1233 							methods[nr].anchorYPos,
1234 							methods[nr].anchorValue,
1235 							methods[nr].ratio
1236 						);
1237 						ui->shapeGui[shapeNr].shapeWidget.setUnit (methods[nr].unit);
1238 						ui->shapeGui[shapeNr].shapeWidget.setPrefix (methods[nr].prefix);
1239 						ui->shapeGui[shapeNr].shapeWidget.setLowerLimit (methods[nr].limit.min);
1240 						ui->shapeGui[shapeNr].shapeWidget.setHigherLimit (methods[nr].limit.max);
1241 						ui->shapeGui[shapeNr].shapeWidget.update();
1242 
1243 						// Hide old controllers
1244 						int oldMethodNr = ui->controllers[widgetNr];
1245 						for (int i = 0; i < MAXOPTIONWIDGETS; ++i)
1246 						{
1247 							int optionNr = methods[oldMethodNr].optionIndexes[i];
1248 							if (optionNr != NO_OPT)
1249 							{
1250 								if (ui->shapeGui[shapeNr].optionWidgets[optionNr]) ui->shapeGui[shapeNr].optionWidgets[optionNr]->hide ();
1251 								ui->shapeGui[shapeNr].optionLabels[optionNr].hide ();
1252 							}
1253 						}
1254 
1255 						// Configure and show new controllers
1256 						int methodNr = value;
1257 						for (int i = 0; i < MAXOPTIONWIDGETS; ++i)
1258 						{
1259 							int optionNr = methods[methodNr].optionIndexes[i];
1260 
1261 							if (optionNr != NO_OPT)
1262 							{
1263 								if (options[optionNr].widgetType != NO_WIDGET)
1264 								{
1265 									if (options[optionNr].widgetType == DIAL_WIDGET)
1266 									{
1267 										if (ui->shapeGui[shapeNr].optionWidgets[optionNr])
1268 										{
1269 											RESIZE ((*ui->shapeGui[shapeNr].optionWidgets[optionNr]), 230 + i * 70, 434, 50, 60, ui->sz);
1270 											RESIZE ((ui->shapeGui[shapeNr].optionLabels[optionNr]), 220 + i * 70, 494, 70, 16, ui->sz);
1271 										}
1272 									}
1273 
1274 									else if (options[optionNr].widgetType == POPUP_WIDGET)
1275 									{
1276 										if (ui->shapeGui[shapeNr].optionWidgets[optionNr])
1277 										{
1278 											BItems::ItemList il = options[optionNr].param.get<BItems::ItemList>();
1279 											size_t max = 0;
1280 											for (BItems::Item const& it : il)
1281 											{
1282 												if (it.getWidget())
1283 												{
1284 													BWidgets::Label* l = (BWidgets::Label*) it.getWidget();
1285 													if (l->getText().size() > max) max = l->getText().size();
1286 												}
1287 											}
1288 											int w = max * 9 + 20;
1289 											w = LIMIT (w, 50, 270 - i * 70);
1290 											int h = (il.size() + 1) * 20;
1291 											h = LIMIT (h, 20, 400);
1292 
1293 											RESIZE ((*ui->shapeGui[shapeNr].optionWidgets[optionNr]), 220 + i * 70, 455, w, 20, ui->sz);
1294 											((BWidgets::PopupListBox*) ui->shapeGui[shapeNr].optionWidgets[optionNr])->resizeListBox (BUtilities::Point (w * ui->sz, h * ui->sz));
1295 											((BWidgets::PopupListBox*) ui->shapeGui[shapeNr].optionWidgets[optionNr])->moveListBox (BUtilities::Point (0, -h * ui->sz));
1296 
1297 											RESIZE ((ui->shapeGui[shapeNr].optionLabels[optionNr]), 220 + i * 70, 494, w, 16, ui->sz);
1298 										}
1299 									}
1300 
1301 									// Show option controller
1302 									if (ui->shapeGui[shapeNr].optionWidgets[optionNr])
1303 									{
1304 										ui->shapeGui[shapeNr].optionWidgets[optionNr]->applyTheme (ui->theme);
1305 										ui->shapeGui[shapeNr].optionWidgets[optionNr]->show ();
1306 									}
1307 
1308 									// Show option label
1309 									ui->shapeGui[shapeNr].optionLabels[optionNr].applyTheme (ui->theme);
1310 									//RESIZE ((ui->shapeGui[shapeNr].optionLabels[optionNr]), 220 + i * 70, 494, 60, 16, ui->sz);
1311 									ui->shapeGui[shapeNr].optionLabels[optionNr].show ();
1312 								}
1313 							}
1314 
1315 						}
1316 					}
1317 
1318 					else if (shapeWidgetNr == SH_SMOOTHING)
1319 					{
1320 						if (shapeNr == ui->controllers[ACTIVE_SHAPE] - 1) ui->updateHorizon ();
1321 					}
1322 				}
1323 				ui->controllers[widgetNr] = value;
1324 				ui->write_function(ui->controller, CONTROLLERS + widgetNr, sizeof(float), 0, &ui->controllers[widgetNr]);
1325 			}
1326 		}
1327 	}
1328 }
1329 
tabClickedCallback(BEvents::Event * event)1330 void BShaprGUI::tabClickedCallback (BEvents::Event* event)
1331 {
1332 	if ((event) && (event->getWidget ()))
1333 	{
1334 		BWidgets::Widget* widget = (BWidgets::Widget*) event->getWidget ();
1335 		if (widget->getMainWindow ())
1336 		{
1337 			BShaprGUI* ui = (BShaprGUI*) widget->getMainWindow ();
1338 			for (int i = 0; i < MAXSHAPES; ++i)
1339 			{
1340 				if ((widget == &ui->shapeGui[i].tabIcon) || (widget == &ui->shapeGui[i].tabContainer))
1341 				{
1342 					ui->switchShape (i);
1343 					break;
1344 				}
1345 
1346 				else if (widget == &ui->shapeGui[i].tabSymbol[CLOSESYMBOL])
1347 				{
1348 					if (ui->shapeGui[i].tabMsgBox) delete ui->shapeGui[i].tabMsgBox;
1349 					ui->shapeGui[i].tabMsgBox = nullptr;
1350 					if (ui->shapeGui[i].tabMsgBoxBg) delete ui->shapeGui[i].tabMsgBoxBg;
1351 					ui->shapeGui[i].tabMsgBoxBg = nullptr;
1352 
1353 					ui->shapeGui[i].tabMsgBox = new BWidgets::MessageBox (500 * ui->sz, 240 * ui->sz, 200 * ui->sz, 120 * ui->sz, "msgbox", "Delete shape " + std::to_string (i + 1), "Do you really want to delete this shape and all its content and settings ?", {"No", "Yes"});
1354 					if (ui->shapeGui[i].tabMsgBox)
1355 					{
1356 						ui->shapeGui[i].tabMsgBox->applyTheme (ui->theme);
1357 
1358 						ui->shapeGui[i].tabMsgBoxBg = new BWidgets::Widget (0, 0, 1200 * ui->sz, 710 * ui->sz, "widget");
1359 						if (ui->shapeGui[i].tabMsgBoxBg)
1360 						{
1361 							ui->shapeGui[i].tabMsgBoxBg->applyTheme (ui->theme);
1362 							ui->shapeGui[i].tabMsgBoxBg->add (*ui->shapeGui[i].tabMsgBox);
1363 							ui->mContainer.add (*ui->shapeGui[i].tabMsgBoxBg);
1364 						}
1365 					}
1366 					break;
1367 				}
1368 
1369 				else if (widget == &ui->shapeGui[i].tabSymbol[ADDSYMBOL])
1370 				{
1371 					ui->insertShape (i);
1372 					break;
1373 				}
1374 
1375 				else if (widget == &ui->shapeGui[i].tabSymbol[LEFTSYMBOL])
1376 				{
1377 					ui->swapShapes (i, i - 1);
1378 					break;
1379 				}
1380 
1381 				else if (widget == &ui->shapeGui[i].tabSymbol[RIGHTSYMBOL])
1382 				{
1383 					ui->swapShapes (i, i + 1);
1384 					break;
1385 				}
1386 			}
1387 		}
1388 	}
1389 }
1390 
shapeChangedCallback(BEvents::Event * event)1391 void BShaprGUI::shapeChangedCallback (BEvents::Event* event)
1392 {
1393 	if ((event) && (event->getWidget ()))
1394 	{
1395 		BWidgets::ValueWidget* widget = (BWidgets::ValueWidget*) event->getWidget ();
1396 
1397 		if (widget->getMainWindow () && (widget->getValue () == 1))
1398 		{
1399 			BShaprGUI* ui = (BShaprGUI*) widget->getMainWindow ();
1400 
1401 			for (int i = 0; i < MAXSHAPES; ++i)
1402 			{
1403 				if (widget == (BWidgets::ValueWidget*) &ui->shapeGui[i].shapeWidget)
1404 				{
1405 					ui->sendShape(i);
1406 					break;
1407 				}
1408 			}
1409 		}
1410 	}
1411 }
1412 
1413 
toolChangedCallback(BEvents::Event * event)1414 void BShaprGUI::toolChangedCallback (BEvents::Event* event)
1415 {
1416 	if ((event) && (event->getWidget ()))
1417 	{
1418 		BWidgets::ValueWidget* widget = (BWidgets::ValueWidget*) event->getWidget ();
1419 
1420 		if (widget->getMainWindow ())
1421 		{
1422 			BShaprGUI* ui = (BShaprGUI*) widget->getMainWindow ();
1423 
1424 			for (int i = 0; i < MAXSHAPES; ++i)
1425 			{
1426 				if (widget == (BWidgets::ValueWidget*) &ui->shapeGui[i].toolSelect)
1427 				{
1428 					ui->shapeGui[i].shapeWidget.setTool((ToolType) ui->shapeGui[i].toolSelect.getValue ());
1429 					break;
1430 				}
1431 			}
1432 		}
1433 	}
1434 }
1435 
editClickedCallback(BEvents::Event * event)1436 void BShaprGUI::editClickedCallback (BEvents::Event* event)
1437 {
1438 	if ((event) && (event->getWidget ()))
1439 	{
1440 		BWidgets::Widget* widget = (BWidgets::Widget*) event->getWidget ();
1441 
1442 		if (widget->getMainWindow ())
1443 		{
1444 			BShaprGUI* ui = (BShaprGUI*) widget->getMainWindow ();
1445 
1446 			for (int i = 0; i < MAXSHAPES; ++i)
1447 			{
1448 				for (int j = 0; j < 7; ++j)
1449 				{
1450 					if (widget == &ui->shapeGui[i].editWidgets[j])
1451 					{
1452 						ui->shapeGui[i].editWidgets[j].rename ("frame");
1453 						ui->shapeGui[i].editWidgets[j].applyTheme (ui->theme);
1454 
1455 						switch (j)
1456 						{
1457 							case 0:		ui->clipboard = ui->shapeGui[i].shapeWidget.cutSelection ();
1458 									return;
1459 
1460 							case 1:		ui->clipboard = ui->shapeGui[i].shapeWidget.copySelection ();
1461 									return;
1462 
1463 							case 2:		ui->shapeGui[i].shapeWidget.pasteSelection (ui->clipboard);
1464 									return;
1465 
1466 							case 3:		ui->shapeGui[i].shapeWidget.deleteSelection ();
1467 									return;
1468 
1469 							case 4:		ui->shapeGui[i].shapeWidget.reset ();
1470 									return;
1471 
1472 							case 5:		ui->shapeGui[i].shapeWidget.undo ();
1473 									return;
1474 
1475 							case 6:		ui->shapeGui[i].shapeWidget.redo ();
1476 									return;
1477 
1478 							default:	return;
1479 						}
1480 					}
1481 				}
1482 			}
1483 		}
1484 	}
1485 }
1486 
editReleasedCallback(BEvents::Event * event)1487 void BShaprGUI::editReleasedCallback (BEvents::Event* event)
1488 {
1489 	if ((event) && (event->getWidget ()))
1490 	{
1491 		BWidgets::Widget* widget = (BWidgets::Widget*) event->getWidget ();
1492 
1493 		if (widget->getMainWindow ())
1494 		{
1495 			BShaprGUI* ui = (BShaprGUI*) widget->getMainWindow ();
1496 
1497 			for (int i = 0; i < MAXSHAPES; ++i)
1498 			{
1499 				for (int j = 0; j < 7; ++j)
1500 				{
1501 					if (widget == &ui->shapeGui[i].editWidgets[j])
1502 					{
1503 						ui->shapeGui[i].editWidgets[j].rename ("widget");
1504 						ui->shapeGui[i].editWidgets[j].applyTheme (ui->theme);
1505 					}
1506 				}
1507 			}
1508 		}
1509 	}
1510 }
1511 
gridChangedCallback(BEvents::Event * event)1512 void BShaprGUI::gridChangedCallback (BEvents::Event* event)
1513 {
1514 	if ((event) && (event->getWidget ()))
1515 	{
1516 		BWidgets::ValueWidget* widget = (BWidgets::ValueWidget*) event->getWidget ();
1517 
1518 		if (widget->getMainWindow ())
1519 		{
1520 			BShaprGUI* ui = (BShaprGUI*) widget->getMainWindow ();
1521 
1522 			for (int i = 0; i < MAXSHAPES; ++i)
1523 			{
1524 				if (widget == (BWidgets::ValueWidget*) &ui->shapeGui[i].gridSelect)
1525 				{
1526 					int value =  ui->shapeGui[i].gridSelect.getValue ();
1527 					switch (value)
1528 					{
1529 						case 0: ui->shapeGui[i].shapeWidget.hideGrid ();
1530 							ui->shapeGui[i].shapeWidget.setSnap (false);
1531 							break;
1532 
1533 						case 1: ui->shapeGui[i].shapeWidget.showGrid ();
1534 							ui->shapeGui[i].shapeWidget.setSnap (false);
1535 							break;
1536 
1537 						case 2: ui->shapeGui[i].shapeWidget.showGrid ();
1538 							ui->shapeGui[i].shapeWidget.setSnap (true);
1539 							break;
1540 
1541 						default:break;
1542 					}
1543 					break;
1544 				}
1545 			}
1546 		}
1547 	}
1548 }
1549 
wheelScrolledCallback(BEvents::Event * event)1550 void BShaprGUI::wheelScrolledCallback (BEvents::Event* event)
1551 {
1552 	if ((event) && (event->getWidget ()))
1553 	{
1554 		BEvents::WheelEvent* we = (BEvents::WheelEvent*) event;
1555 		BWidgets::Widget* widget = event->getWidget ();
1556 
1557 		if (widget->getMainWindow ())
1558 		{
1559 			BShaprGUI* ui = (BShaprGUI*) widget->getMainWindow ();
1560 
1561 			ui->monitorScale = ui->monitorScale * (1 + 0.01 * we->getDelta().y);
1562 			if (ui->monitorScale < 0.01) ui->monitorScale = 0.01;
1563 			ui->input1Monitor.setZoom (ui->monitorScale);
1564 			ui->output1Monitor.setZoom (ui->monitorScale);
1565 			ui->input2Monitor.setZoom (ui->monitorScale);
1566 			ui->output2Monitor.setZoom (ui->monitorScale);
1567 		}
1568 	}
1569 }
1570 
pianoCallback(BEvents::Event * event)1571 void BShaprGUI::pianoCallback (BEvents::Event* event)
1572 {
1573 	if ((event) && (event->getWidget ()))
1574 	{
1575 		BWidgets::Widget* widget = event->getWidget ();
1576 
1577 		if (widget->getMainWindow ())
1578 		{
1579 			BShaprGUI* ui = (BShaprGUI*) widget->getMainWindow ();
1580 
1581 			std::vector<bool> keys = ui->midiPiano.getPressedKeys ();
1582 			size_t sz = (keys.size () < 12 ? keys.size () : 12);
1583 			uint32_t bits = 0;
1584 
1585 			for (unsigned int i = 0; i < sz; ++i)
1586 			{
1587 				if (keys[i]) bits += (1 << i);
1588 			}
1589 
1590 			if (bits != uint32_t (ui->controllers[MIDI_KEYS]))
1591 			{
1592 				ui->controllers[MIDI_KEYS] = bits;
1593 				ui->write_function(ui->controller, CONTROLLERS + MIDI_KEYS, sizeof(float), 0, &ui->controllers[MIDI_KEYS]);
1594 			}
1595 		}
1596 	}
1597 }
1598 
calculateXSteps()1599 void BShaprGUI::calculateXSteps ()
1600 {
1601 	majorXSteps = (controllers[BASE_VALUE] != 0.0 ? 1.0 / controllers[BASE_VALUE] : 1.0);
1602 
1603 	switch ((BShaprBaseIndex)controllers[BASE])
1604 	{
1605 		case SECONDS:	minorXSteps = majorXSteps / 10.0;
1606 				break;
1607 
1608 		case BEATS:	if (beatUnit != 0) minorXSteps = majorXSteps / (16.0 / ((double)beatUnit));
1609 				else minorXSteps = majorXSteps / 4.0;
1610 				break;
1611 
1612 		case BARS:	if (beatsPerBar != 0.0f) minorXSteps = majorXSteps / beatsPerBar;
1613 				else minorXSteps = majorXSteps / 4.0;
1614 				break;
1615 
1616 		default:	minorXSteps = 1.0;
1617 				break;
1618 	}
1619 
1620 	if (controllers[BASE_VALUE] >= 10.0) minorXSteps = majorXSteps;
1621 
1622 	for (int sh = 0; sh < MAXSHAPES; ++sh)
1623 	{
1624 		shapeGui[sh].shapeWidget.setMinorXSteps (minorXSteps);
1625 		shapeGui[sh].shapeWidget.setMajorXSteps (majorXSteps);
1626 		shapeGui[sh].shapeWidget.update ();
1627 	}
1628 }
1629 
initMonitors()1630 void BShaprGUI::initMonitors ()
1631 {
1632 	input1Monitor.clear ();
1633 	output1Monitor.clear ();
1634 	input2Monitor.clear ();
1635 	output2Monitor.clear ();
1636 	horizonPos = 0;
1637 }
1638 
translateNotification(BShaprNotifications * notifications,uint32_t notificationsCount)1639 std::pair<int, int> BShaprGUI::translateNotification (BShaprNotifications* notifications, uint32_t notificationsCount)
1640 {
1641 	if (notificationsCount == 0) return std::make_pair (0, 0);
1642 
1643 	int startpos = notifications[0].position;
1644 	int monitorpos = startpos;
1645 	for (unsigned int i = 0; i < notificationsCount; ++i)
1646 	{
1647 		monitorpos = LIMIT (notifications[i].position, 0, MONITORBUFFERSIZE - 1);
1648 
1649 		input1Monitor.addData (monitorpos, notifications[i].input1);
1650 		output1Monitor.addData (monitorpos, notifications[i].output1);
1651 		input2Monitor.addData (monitorpos, notifications[i].input2);
1652 		output2Monitor.addData (monitorpos, notifications[i].output2);
1653 
1654 		horizonPos = double (monitorpos) / MONITORBUFFERSIZE;
1655 	}
1656 	return std::make_pair (startpos, monitorpos);
1657 }
1658 
updateMonitors(int start,int end)1659 void BShaprGUI::updateMonitors (int start, int end)
1660 {
1661 	input1Monitor.redrawRange (start, end);
1662 	output1Monitor.redrawRange (start, end);
1663 	input2Monitor.redrawRange (start, end);
1664 	output2Monitor.redrawRange (start, end);
1665 }
1666 
updateHorizon()1667 void BShaprGUI::updateHorizon ()
1668 {
1669 	double width = monitorContainer.getEffectiveWidth ();
1670 	int shapeNr = LIMIT (controllers[ACTIVE_SHAPE], 1, MAXSHAPES) - 1;
1671 	double smTime = shapeGui[shapeNr].smoothingDial.getValue () / 1000;
1672 
1673 	double smWidth;
1674 	switch (int (controllers[BASE]))
1675 	{
1676 		case SECONDS :	smWidth = width * smTime / controllers[BASE_VALUE];
1677 				break;
1678 		case BEATS:	smWidth = width * smTime * (bpm / 60.0) / controllers[BASE_VALUE];
1679 				break;
1680 		case BARS:	smWidth = width * smTime * (bpm / 60.0 / beatsPerBar) / controllers[BASE_VALUE];
1681 				break;
1682 		default:	break;
1683 	}
1684 
1685 	monitorHorizon1.setSmoothingWidth (smWidth);
1686 	monitorHorizon1.moveLineTo (horizonPos * width, 0);
1687 	monitorHorizon2.setSmoothingWidth (smWidth);
1688 	monitorHorizon2.moveLineTo ((horizonPos - 1) * width, 0);
1689 }
1690 
instantiate(const LV2UI_Descriptor * descriptor,const char * plugin_uri,const char * bundle_path,LV2UI_Write_Function write_function,LV2UI_Controller controller,LV2UI_Widget * widget,const LV2_Feature * const * features)1691 static LV2UI_Handle instantiate (const LV2UI_Descriptor *descriptor, const char *plugin_uri, const char *bundle_path,
1692 						  LV2UI_Write_Function write_function, LV2UI_Controller controller, LV2UI_Widget *widget,
1693 						  const LV2_Feature *const *features)
1694 {
1695 	PuglNativeView parentWindow = 0;
1696 	LV2UI_Resize* resize = NULL;
1697 
1698 	if (strcmp(plugin_uri, BSHAPR_URI) != 0)
1699 	{
1700 		std::cerr << "BShapr.lv2#GUI: GUI does not support plugin with URI " << plugin_uri << std::endl;
1701 		return NULL;
1702 	}
1703 
1704 	for (int i = 0; features[i]; ++i)
1705 	{
1706 		if (!strcmp(features[i]->URI, LV2_UI__parent)) parentWindow = (PuglNativeView) features[i]->data;
1707 		else if (!strcmp(features[i]->URI, LV2_UI__resize)) resize = (LV2UI_Resize*)features[i]->data;
1708 	}
1709 	if (parentWindow == 0) std::cerr << "BShapr.lv2#GUI: No parent window.\n";
1710 
1711 	// New instance
1712 	BShaprGUI* ui;
1713 	try {ui = new BShaprGUI (bundle_path, features, parentWindow);}
1714 	catch (std::exception& exc)
1715 	{
1716 		std::cerr << "BShapr.lv2#GUI: Instantiation failed. " << exc.what () << std::endl;
1717 		return NULL;
1718 	}
1719 
1720 	ui->controller = controller;
1721 	ui->write_function = write_function;
1722 
1723 	// Reduce min GUI size for small displays
1724 	double sz = 1.0;
1725 	int screenWidth  = getScreenWidth ();
1726 	int screenHeight = getScreenHeight ();
1727 	if ((screenWidth < 1240) || (screenHeight < 720)) sz = 0.66;
1728 	if ((screenWidth < 840) || (screenHeight < 530)) sz = 0.50;
1729 
1730 	if (resize) resize->ui_resize (resize->handle, 1200 * sz, 710 * sz);
1731 	*widget = (LV2UI_Widget) puglGetNativeWindow (ui->getPuglView ());
1732 	ui->sendGuiOn();
1733 
1734 	return (LV2UI_Handle) ui;
1735 }
1736 
cleanup(LV2UI_Handle ui)1737 static void cleanup(LV2UI_Handle ui)
1738 {
1739 	BShaprGUI* pluginGui = (BShaprGUI*) ui;
1740 	if (pluginGui) delete pluginGui;
1741 }
1742 
portEvent(LV2UI_Handle ui,uint32_t port_index,uint32_t buffer_size,uint32_t format,const void * buffer)1743 static void portEvent(LV2UI_Handle ui, uint32_t port_index, uint32_t buffer_size,
1744 	uint32_t format, const void* buffer)
1745 {
1746 	BShaprGUI* pluginGui = (BShaprGUI*) ui;
1747 	if (pluginGui) pluginGui->portEvent(port_index, buffer_size, format, buffer);
1748 }
1749 
callIdle(LV2UI_Handle ui)1750 static int callIdle (LV2UI_Handle ui)
1751 {
1752 	BShaprGUI* pluginGui = (BShaprGUI*) ui;
1753 	if (pluginGui) pluginGui->handleEvents ();
1754 	return 0;
1755 }
1756 
callResize(LV2UI_Handle ui,int width,int height)1757 static int callResize (LV2UI_Handle ui, int width, int height)
1758 {
1759 	BShaprGUI* self = (BShaprGUI*) ui;
1760 	if (!self) return 0;
1761 
1762 	BEvents::ExposeEvent* ev = new BEvents::ExposeEvent (self, self, BEvents::CONFIGURE_REQUEST_EVENT, self->getPosition().x, self->getPosition().y, width, height);
1763 	self->addEventToQueue (ev);
1764 	return 0;
1765 }
1766 
1767 static const LV2UI_Idle_Interface idle = {callIdle};
1768 static const LV2UI_Resize resize = {nullptr, callResize};
1769 
extensionData(const char * uri)1770 static const void* extensionData(const char* uri)
1771 {
1772 	if (!strcmp(uri, LV2_UI__idleInterface)) return &idle;
1773 	else if(!strcmp(uri, LV2_UI__resize)) return &resize;
1774 	else return NULL;
1775 }
1776 
1777 static const LV2UI_Descriptor guiDescriptor = {
1778 		BSHAPR_GUI_URI,
1779 		instantiate,
1780 		cleanup,
1781 		portEvent,
1782 		extensionData
1783 };
1784 
1785 // LV2 Symbol Export
lv2ui_descriptor(uint32_t index)1786 LV2_SYMBOL_EXPORT const LV2UI_Descriptor *lv2ui_descriptor(uint32_t index)
1787 {
1788 	switch (index) {
1789 	case 0: return &guiDescriptor;
1790 	default:return NULL;
1791     }
1792 }
1793