1 /* B.Schaffl
2  * MIDI Pattern Delay Plugin
3  *
4  * Copyright (C) 2018 - 2020 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 "BSchafflGUI.hpp"
22 #include "Ports.hpp"
23 #include "screen.h"
24 #include "BUtilities/vsystem.hpp"
25 
26 
BSchafflGUI(const char * bundle_path,const LV2_Feature * const * features,PuglNativeView parentWindow)27 BSchafflGUI::BSchafflGUI (const char *bundle_path, const LV2_Feature *const *features, PuglNativeView parentWindow) :
28 	Window (1020, 480, "B.Schaffl", parentWindow, true, PUGL_MODULE, 0),
29 	controller (NULL), write_function (NULL),
30 	pluginPath (bundle_path ? std::string (bundle_path) : std::string ("")),
31 
32 	mContainer (0, 0, 1020, 480, "main"),
33 
34 	helpButton (950, 60, 24, 24, "halobutton", BSCHAFFL_LABEL_HELP),
35 	ytButton (980, 60, 24, 24, "halobutton", BSCHAFFL_LABEL_TUTORIAL),
36 
37 	toolbox (380, 330, 620, 30, "scontainer"),
38 	toolIcon (4, 2 , 40, 26, "widget", pluginPath + "inc/tools.png"),
39 	convertToShapeIcon (50, 5, 50, 20, "widget", pluginPath + "inc/convert_to_linear_shape.png"),
40 	convertToStepsIcon (50, 5, 50, 20, "widget", pluginPath + "inc/convert_to_steps.png"),
41 	convertToShapeButton (48, 3, 54, 24, "halobutton", BSCHAFFL_LABEL_CONVERT_TO_SHAPE),
42 	convertToStepsButton (48, 3, 54, 24, "halobutton", BSCHAFFL_LABEL_CONVERT_TO_STEPS),
43 	markersToolbox (120, 5, 50, 20, "widget", pluginPath + "inc/markers_tb.png"),
44 	shapeToolbox (190, 5, 140, 20, "widget", pluginPath + "inc/shape_tools_tb.png"),
45 	editToolbox (360, 5, 80, 20, "widget", pluginPath + "inc/edit_tb.png"),
46 	historyToolbox (460, 5, 80, 20, "widget", pluginPath + "inc/history_tb.png"),
47 	gridToolbox (560, 5, 50, 20, "widget", pluginPath + "inc/grid_tb.png"),
48 	markersAutoButton (118, 3, 24, 24, "halobutton", BSCHAFFL_LABEL_ALL_MARKERS_AUTO),
49 	markersManualButton (148, 3, 24, 24, "halobutton", BSCHAFFL_LABEL_ALL_MARKERS_MANUAL),
50 	shapeToolButtons
51 	{
52 		HaloToggleButton (188, 3, 24, 24, "halobutton", BSCHAFFL_LABEL_SELECT),
53 		HaloToggleButton (218, 3, 24, 24, "halobutton", BSCHAFFL_LABEL_POINT_NODE),
54 		HaloToggleButton (248, 3, 24, 24, "halobutton", BSCHAFFL_LABEL_AUTO_NODE),
55 		HaloToggleButton (278, 3, 24, 24, "halobutton", BSCHAFFL_LABEL_SYMMETRIC_NODE),
56 		HaloToggleButton (308, 3, 24, 24, "halobutton", BSCHAFFL_LABEL_ASYMMETRIC_NODE)
57 	},
58 	editToolButtons
59 	{
60 		HaloButton (358, 3, 24, 24, "halobutton", BSCHAFFL_LABEL_CUT),
61 		HaloButton (388, 3, 24, 24, "halobutton", BSCHAFFL_LABEL_COPY),
62 		HaloButton (418, 3, 24, 24, "halobutton", BSCHAFFL_LABEL_PASTE)
63 	},
64 	historyToolButtons
65 	{
66 		HaloButton (458, 3, 24, 24, "halobutton", BSCHAFFL_LABEL_RESET),
67 		HaloButton (488, 3, 24, 24, "halobutton", BSCHAFFL_LABEL_UNDO),
68 		HaloButton (518, 3, 24, 24, "halobutton", BSCHAFFL_LABEL_REDO)
69 	},
70 	gridShowButton (558, 3, 24, 24, "halobutton", BSCHAFFL_LABEL_SHOW_GRID),
71 	gridSnapButton (588, 3, 24, 24, "halobutton", BSCHAFFL_LABEL_SNAP_GRID),
72 
73 	convertToShapeMessage
74 	(
75 		560, 100, 300, 150, "menu",
76 		BSCHAFFL_LABEL_CONVERT_TO_SHAPE_TITLE,
77 		BSCHAFFL_LABEL_CONVERT_TO_SHAPE_TEXT,
78 		std::vector<std::string>{BSCHAFFL_LABEL_OK, BSCHAFFL_LABEL_CANCEL}
79 	),
80         convertToShapeToLinearButton (20, 70, 12, 12, "slider", 0.0),
81         convertToShapeToLinearText (60, 70, 200, 20, "menu/text", BSCHAFFL_LABEL_CONVERT_TO_SHAPE_LINEAR_TEXT),
82         convertToShapeToConstButton (20, 90, 12, 12, "slider", 0.0),
83         convertToShapeToConstText (60, 90, 200, 20, "menu/text", BSCHAFFL_LABEL_CONVERT_TO_SHAPE_CONST_TEXT),
84 
85 	convertToStepsMessage
86 	(
87 		560, 100, 300, 110, "menu",
88 		BSCHAFFL_LABEL_CONVERT_TO_STEPS_TITLE,
89 		BSCHAFFL_LABEL_CONVERT_TO_STEPS_TEXT,
90 		std::vector<std::string>{BSCHAFFL_LABEL_OK, BSCHAFFL_LABEL_CANCEL}
91 	),
92 
93 	midiChFilterIcon (0, 0, 300, 20, "widget", pluginPath + BSCHAFFL_FILENAME_MIDI_CH_FILTER),
94 	midiChFilterContainer (0, 0, 340, 140, "screen"),
95 	midiChFilterText (10, 10, 320, 50, "text", BSCHAFFL_LABEL_MIDI_CH),
96 	midiChFilterAllSwitch (10, 36, 28, 14, "slider", 1),
97 	midiChFilterAllLabel (44, 33, 120, 20, "lflabel", BSCHAFFL_LABEL_ALL),
98 
99 	midiMsgFilterIcon (0, 0, 300, 20, "widget", pluginPath + BSCHAFFL_FILENAME_MIDI_MSG_FILTER),
100 	midiMsgFilterContainer (0, 0, 340, 200, "screen"),
101 	midiMsgFilterText (10, 10, 320, 20, "text", BSCHAFFL_LABEL_MIDI_MSG),
102 	midiMsgFilterAllSwitch (10, 36, 28, 14, "slider", 1),
103 	midiMsgFilterAllLabel (50, 33, 120, 20, "lflabel", BSCHAFFL_LABEL_ALL),
104 
105 	midiNoteOptionsIcon (0, 0, 300, 20, "widget", pluginPath + BSCHAFFL_FILENAME_MIDI_NOTE_OPTIONS),
106         midiNoteOptionsContainer (0, 0, 340, 210, "screen"),
107 	midiNoteText (10, 10, 320, 50, "text", BSCHAFFL_LABEL_NOTE_TEXT),
108         midiNotePositionSwitch (10, 68, 28, 14, "slider", 0.0),
109         midiNotePositionLabel (50, 65, 240, 20, "lflabel", BSCHAFFL_LABEL_NOTE_KEEP_POSITION),
110         midiNoteValueSwitch (10, 88, 28, 14, "slider", 0.0),
111         midiNoteValueLabel (50, 85, 180, 20, "lflabel", BSCHAFFL_LABEL_NOTE_KEEP_DURATION),
112         midiNoteOverlapListbox
113 	(
114 		160, 110, 100, 20, 0, 20, 100, 80, "menu",
115 		BItems::ItemList ({{0, BSCHAFFL_LABEL_DO_NOTHING}, {1, BSCHAFFL_LABEL_SPLIT}, {2, BSCHAFFL_LABEL_MERGE}}),
116 		0
117 	),
118         midiNoteOverlapLabel (10, 110, 150, 20, "lflabel", BSCHAFFL_LABEL_IF_NOTES_OVERLAP_TITLE),
119 	midiNoteText2 (10, 140, 320, 30, "text", BSCHAFFL_LABEL_IF_NOTES_OVERLAP_TEXT),
120         midiNoteOffAmpLabel (10, 180, 110, 20, "lflabel", BSCHAFFL_LABEL_NOTE_OFF_AMP),
121         midiNoteOffAmpListbox
122 	(
123 		120, 180, 220, 20, 0, 20, 220, 60, "menu",
124 		BItems::ItemList ({{0, BSCHAFFL_LABEL_USE_NOTE_ON_AMP}, {1, BSCHAFFL_LABEL_CALCULATE_FROM_END}}),
125 		0
126 	),
127 
128 	smartQuantizationIcon (0, 0, 300, 20, "widget", pluginPath + BSCHAFFL_FILENAME_SMART_QUANTIZATION),
129 	smartQuantizationContainer (0, 0, 340, 200, "screen"),
130         smartQuantizationRangeSlider (10, 80, 110, 28, "slider", 0.25, 0.0, 0.5, 0.0, "%1.2f"),
131         smartQuantizationMappingSwitch (10, 156, 28, 14, "slider", 1.0),
132         smartQuantizationPositioningSwitch (10, 176, 28, 14, "slider", 0.0),
133         smartQuantizationText1 (10, 10, 320, 70, "text", BSCHAFFL_LABEL_SMART_QUANTIZATION_TEXT_1),
134         smartQuantizationRangeLabel (130, 90, 150, 20, "lflabel", BSCHAFFL_LABEL_RANGE_STEPS),
135 	smartQuantizationText2 (10, 120, 320, 30, "text", BSCHAFFL_LABEL_SMART_QUANTIZATION_TEXT_2),
136         smartQuantizationMappingLabel (50, 153, 180, 20, "lflabel", BSCHAFFL_LABEL_ASSIGN_TO_STEP),
137         smartQuantizationPositionLabel (50, 173, 180, 20, "lflabel", BSCHAFFL_LABEL_FIT_TO_STEP),
138 
139 	userLatencyIcon (0, 0, 300, 20, "widget", pluginPath + BSCHAFFL_FILENAME_LATENCY),
140 	userLatencyContainer (0, 0, 340, 180, "screen"),
141 	timeCompensText (10, 10, 320, 30, "text", BSCHAFFL_LABEL_LATENCY_COMPENSATION_TEXT),
142 	timeCompensLabel (50, 45, 230, 20, "lflabel", BSCHAFFL_LABEL_LATENCY_COMPENSATION_TITLE),
143 	timeCompensSwitch (10, 48, 28, 14, "slider", 0.0),
144 	userLatencyText (10, 80, 320, 30, "text", BSCHAFFL_LABEL_LATENCY_USER_TEXT),
145 	userLatencySwitch (10, 118, 28, 14, "slider", 0.0),
146 	userLatencyLabel (50, 115, 180, 20, "lflabel", BSCHAFFL_LABEL_LATENCY_USER_TITLE),
147 	userLatencyValue (0, 0, 0, 0, "widget", 0.0, 0.0, 192000, 1.0),
148 	userLatencySlider (10, 140, 160, 28, "slider", 0, 0, 192000, 1, "%6.0f"),
149 	userLatencyUnitListbox
150 	(
151 		180, 150, 90, 20, 0, 20, 90, 60, "menu",
152 		BItems::ItemList ({{1, BSCHAFFL_LABEL_FRAMES}, {2, BSCHAFFL_LABEL_MS}}),
153 		1
154 	),
155 
156 	selectMenu
157 	(
158 		20, 90, 340, 350, "selectmenu",
159 		std::list<std::pair<Widget*, Widget*>>
160 		({
161 			{&midiChFilterIcon, &midiChFilterContainer},
162 			{&midiMsgFilterIcon, &midiMsgFilterContainer},
163 			{&midiNoteOptionsIcon, &midiNoteOptionsContainer},
164 			{&smartQuantizationIcon, &smartQuantizationContainer},
165 			{&userLatencyIcon, &userLatencyContainer}
166 		})
167 	),
168 
169 	sharedDataSelection (58, 448, 304, 24, "widget", 0, 0, 4, 1),
170 
171 	sContainer (380, 90, 620, 240, "scontainer"),
172 
173 	modeSwitch (426, 71, 28, 14, "slider", 0.0),
174 	seqLenValueListbox
175 	(
176 		380, 380, 50, 20, 0, -220, 50, 220, "menu",
177 		BItems::ItemList
178 		({
179 			{0.125, "1/8"}, {0.25, "1/4"}, {0.333333, "1/3"}, {0.5, "1/2"},
180 			{0.666667, "2/3"}, {0.75, "3/4"}, {1, "1"}, {2, "2"}, {3, "3"}, {4, "4"},
181 			{6, "6"}, {8, "8"}, {12, "12"}, {16, "16"}
182 		}), 1.0
183 	),
184 	seqLenBaseListbox
185 	(
186 		440, 380, 90, 20, 0, -80, 90, 80, "menu",
187 		BItems::ItemList
188 		({{0, BSCHAFFL_LABEL_SECONDS}, {1, BSCHAFFL_LABEL_BEATS}, {2, BSCHAFFL_LABEL_BARS}}), 2.0
189 	),
190 	ampSwingControl
191 	(
192 		565, 372, 120, 28, "slider", 1.0, 0.0078125, 128.0, 0.0, "%3.1f",
193 		[] (const double val, const double min, const double max)
__anon2ccd2c280102(const double val, const double min, const double max) 194 		{return (val >= 1.0 ? 1.0 - 0.5 / LIMIT (val, min, max) : 0.5 * LIMIT (val, min, max));},
195 		[] (const double frac, const double min, const double max)
__anon2ccd2c280202(const double frac, const double min, const double max) 196 		{return (frac >= 0.5 ? 0.5 / (1.0 - LIMIT (frac, 0, 1)) : 2 * LIMIT (frac, 0, 1));}
197 	),
198 	ampRandomControl (720, 372, 120, 28, "slider", 0.0, 0.0, 1.0, 0.0, "%1.2f"),
199 	ampProcessControl (875, 372, 120, 28, "procslider", 1.0, -1.0, 2.0, 0.0, "%1.2f"),
200 	swingControl (565, 422, 120, 28, "slider", 1.0, 1.0 / 3.0, 3.0, 0.0),
201 	swingRandomControl (720, 422, 120, 28, "slider", 0.0, 0.0, 1.0, 0.0, "%1.2f"),
202 	swingProcessControl (875, 422, 120, 28, "procslider", 0.0, 0.0, 1.0, 0.0, "%1.2f"),
203 	nrStepsControl (380, 422, 155, 28, "slider", 1.0, 1.0, MAXSTEPS, 1.0, "%2.0f"),
204 	seqLenIcon (385, 405, 140, 12, "widget", pluginPath + BSCHAFFL_FILENAME_SEQUENCE_SIZE),
205         ampSwingIcon (555, 405, 140, 12, "widget", pluginPath + BSCHAFFL_FILENAME_AMP_SWING),
206         ampRandomIcon (710, 405, 140, 12, "widget", pluginPath + BSCHAFFL_FILENAME_AMP_RANDOM),
207         ampProcessIcon (865, 405, 140, 12, "widget", pluginPath + BSCHAFFL_FILENAME_AMP_PROCESS),
208         nrStepsIcon (385, 455, 140, 12, "widget", pluginPath + BSCHAFFL_FILENAME_NR_OF_STEPS),
209         swingIcon (555, 455, 140, 12, "widget", pluginPath + BSCHAFFL_FILENAME_STEPS_SWING),
210         swingRandomIcon (710, 455, 140, 12, "widget", pluginPath + BSCHAFFL_FILENAME_STEPS_RANDOM),
211         swingProcessIcon (865, 455, 140, 12, "widget", pluginPath + BSCHAFFL_FILENAME_STEPS_PROCESS),
212 
213 	stepControlContainer (40, 40, 580, 130, "widget", 0.0),
214 	shapeWidget (40, 40, 580, 130, "shape"),
215 	markerListBox (12, -68, 86, 66, "listbox", BItems::ItemList ({BSCHAFFL_LABEL_AUTO, BSCHAFFL_LABEL_MANUAL})),
216 
217 	latencyValue (0, 0, 0, 0, "widget", 0),
218 	latencyDisplay (900, 10, 120, 10, "smlabel", ""),
219 	controllers{nullptr},
220 	messageLabel (480, 63, 465, 20, "hilabel", ""),
221 	inIcon (4, 14, 32, 12, "widget", pluginPath + BSCHAFFL_FILENAME_IN),
222 	ampIcon (4, 100, 32, 20, "widget", pluginPath + BSCHAFFL_FILENAME_AMP),
223 	delIcon (2, 185, 36, 12, "widget", pluginPath + BSCHAFFL_FILENAME_STR),
224 	outIcon (4, 214, 32, 12, "widget", pluginPath + BSCHAFFL_FILENAME_OUT),
225 
226 	clipboard (),
227 
228 	sz (1.0), bgImageSurface (nullptr),
229 	map (NULL), rate (48000.0)
230 
231 {
232 	// Init
233 	for (unsigned int i = 0; i < midiMsgFilterSwitches.size(); ++i)
234 	{
235 		midiMsgFilterSwitches[i] = BWidgets::HSwitch (10, 56 + i * 20, 28, 14, "slider", 1);
236 		midiMsgFilterLabels[i] = BWidgets::Label (50, 53 + i * 20, 180, 20, "lflabel", midiMsgGroupTexts[i]);
237 	}
238 
239 	for (unsigned int i = 0; i < midiChFilterSwitches.size(); ++i)
240 	{
241 		midiChFilterSwitches[i] = BWidgets::HSwitch (10 + 80.0 * int (i / 4), 56 + int (i % 4) * 20, 28, 14, "slider", 1);
242 		midiChFilterLabels[i] = BWidgets::Label (44 + 80.0 * int (i / 4), 53  + int (i % 4) * 20, 36, 20, "lflabel", "#" + std::to_string (i + 1));
243 	}
244 
245 	for (int i = 0; i < 4; ++i) sharedDataButtons[i] = HaloToggleButton (80 * i, 0, 64, 24, "halobutton", BSCHAFFL_LABEL_SHARED_DATA " " + std::to_string (i + 1));
246 
247 	// Link widgets to controllers
248 	controllers[SEQ_LEN_VALUE] = &seqLenValueListbox;
249 	controllers[SEQ_LEN_BASE] = &seqLenBaseListbox;
250 	controllers[AMP_SWING] = &ampSwingControl;
251 	controllers[AMP_RANDOM] = &ampRandomControl;
252 	controllers[AMP_PROCESS] = &ampProcessControl;
253 	controllers[SWING] = &swingControl;
254 	controllers[SWING_RANDOM] = &swingRandomControl;
255 	controllers[SWING_PROCESS] = &swingProcessControl;
256 	controllers[NR_OF_STEPS] = &nrStepsControl;
257 	controllers[AMP_MODE] = &modeSwitch;
258 	controllers[NOTE_POSITION_STR] = &midiNotePositionSwitch;
259 	controllers[NOTE_VALUE_STR] = &midiNoteValueSwitch;
260 	controllers[NOTE_OVERLAP] = &midiNoteOverlapListbox;
261 	controllers[NOTE_OFF_AMP] = &midiNoteOffAmpListbox;
262 	controllers[QUANT_RANGE] = &smartQuantizationRangeSlider;
263         controllers[QUANT_MAP] = &smartQuantizationMappingSwitch;
264         controllers[QUANT_POS] = &smartQuantizationPositioningSwitch;
265 	controllers[TIME_COMPENS] = &timeCompensSwitch;
266 	controllers[USR_LATENCY] = &userLatencySwitch;
267 	controllers[USR_LATENCY_FR] = &userLatencyValue;
268 
269 	for (unsigned int i = 0; i < midiChFilterSwitches.size(); ++i) controllers[MIDI_CH_FILTER + i] = &midiChFilterSwitches[i];
270 	for (unsigned int i = 0; i < midiMsgFilterSwitches.size(); ++i) controllers[MSG_FILTER_NOTE + i] = &midiMsgFilterSwitches[i];
271 	for (int i = 0; i < MAXSTEPS - 1; ++i) controllers[STEP_POS + i] = &markerWidgets[i];
272 	for (int i = 0; i < MAXSTEPS; ++i) controllers[STEP_LEV + i] = &stepControl[i];
273 	controllers[LATENCY] = &latencyValue;
274 
275 	// Set callbacks
276 	for (BWidgets::ValueWidget* v : controllers) v->setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, BSchafflGUI::valueChangedCallback);
277 	midiChFilterAllSwitch.setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, BSchafflGUI::valueChangedCallback);
278 	midiMsgFilterAllSwitch.setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, BSchafflGUI::valueChangedCallback);
279 	userLatencySlider.setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, BSchafflGUI::valueChangedCallback);
280 	userLatencyUnitListbox.setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, BSchafflGUI::valueChangedCallback);
281 	for (HaloToggleButton& s: sharedDataButtons) s.setCallbackFunction (BEvents::EventType::BUTTON_PRESS_EVENT, BSchafflGUI::sharedDataClickedCallback);
282 	sharedDataSelection.setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, BSchafflGUI::valueChangedCallback);
283 	markerListBox.setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, BSchafflGUI::listBoxChangedCallback);
284 	convertToShapeButton.setCallbackFunction (BEvents::EventType::BUTTON_PRESS_EVENT, BSchafflGUI::convertButtonClickedCallback);
285 	convertToStepsButton.setCallbackFunction (BEvents::EventType::BUTTON_PRESS_EVENT, BSchafflGUI::convertButtonClickedCallback);
286 	convertToShapeToLinearButton.setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, BSchafflGUI::lightButtonClickedCallback);
287 	convertToShapeToConstButton.setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, BSchafflGUI::lightButtonClickedCallback);
288 	markersAutoButton.setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, BSchafflGUI::markersAutoClickedCallback);
289 	markersManualButton.setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, BSchafflGUI::markersAutoClickedCallback);
290 	for (HaloToggleButton& s: shapeToolButtons) s.setCallbackFunction (BEvents::EventType::BUTTON_PRESS_EVENT, BSchafflGUI::shapeToolClickedCallback);
291 	for (HaloButton& e: editToolButtons) e.setCallbackFunction (BEvents::EventType::BUTTON_PRESS_EVENT, BSchafflGUI::editToolClickedCallback);
292 	for (HaloButton& h: historyToolButtons) h.setCallbackFunction (BEvents::EventType::BUTTON_PRESS_EVENT, BSchafflGUI::historyToolClickedCallback);
293 	gridShowButton.setCallbackFunction (BEvents::EventType::BUTTON_PRESS_EVENT, BSchafflGUI::gridToolClickedCallback);
294 	gridSnapButton.setCallbackFunction (BEvents::EventType::BUTTON_PRESS_EVENT, BSchafflGUI::gridToolClickedCallback);
295 	helpButton.setCallbackFunction(BEvents::BUTTON_PRESS_EVENT, helpButtonClickedCallback);
296 	ytButton.setCallbackFunction(BEvents::BUTTON_PRESS_EVENT, ytButtonClickedCallback);
297 	shapeWidget.setCallbackFunction (BEvents::VALUE_CHANGED_EVENT, shapeChangedCallback);
298 	stepControlContainer.setCallbackFunction (BEvents::VALUE_CHANGED_EVENT, valueChangedCallback);
299 
300 	// Configure widgets
301 	bgImageSurface = cairo_image_surface_create_from_png ((pluginPath + BSCHAFFL_FILENAME_BG).c_str());
302 	widgetBg.loadFillFromCairoSurface (bgImageSurface);
303 	userLatencySlider.hide();
304 	userLatencyUnitListbox.hide();
305 	nrStepsControl.setScrollable (true);
306 	nrStepsControl.getDisplayLabel ()->setState (BColors::ACTIVE);
307 	swingControl.getDisplayLabel ()->setState (BColors::ACTIVE);
308 	markerListBox.setStacking (BWidgets::STACKING_OVERSIZE);
309 
310 	shapeWidget.hide();
311 	shapeWidget.setMergeable (BEvents::POINTER_DRAG_EVENT, false);
312 	shapeWidget.setTool (ToolType::POINT_NODE_TOOL);
313 	shapeWidget.setDefaultShape ();
314 	shapeWidget.setScaleParameters (0.05, 0, 1.1);
315 	shapeWidget.setMajorXSteps (1.0);
316 	shapeWidget.setMinorXSteps (1.0/16.0);
317 	shapeWidget.setLowerLimit (0.0);
318 	shapeWidget.setHigherLimit (1.0);
319 
320 	shapeToolButtons[1].setValue (1.0);
321 	gridSnapButton.setValue (1.0);
322 	shapeToolbox.hide();
323 	editToolbox.hide();
324 	//historyToolbox.hide();
325 	gridToolbox.hide();
326 	for (HaloToggleButton& s : shapeToolButtons) s.hide();
327 	for (HaloButton& e : editToolButtons) e.hide();
328 	//for (HaloButton& h : historyToolButtons) h.hide();
329 	gridShowButton.hide();
330 	gridSnapButton.hide();
331 	convertToShapeIcon.hide();
332 
333 	convertToShapeMessage.setStacking (BWidgets::STACKING_OVERSIZE);
334 
335 	//Initialialize and configure stepControllers
336 	double sw = sContainer.getEffectiveWidth() - 40;
337 	double sx = sContainer.getXOffset() + 40;
338 	for (int i = 0; i < MAXSTEPS; ++i)
339 	{
340 		stepControl[i] = BWidgets::VSlider ((i + 0.5) * sw / MAXSTEPS + sx - 7, 20, 14, 80, "slider", 1.0, 0.0, 1.0, 0.01);
341 		stepControl[i].setHardChangeable (false);
342 		stepControl[i].setScrollable (true);
343 		stepControl[i].setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, BSchafflGUI::valueChangedCallback);
344 		stepControl[i].applyTheme (theme, "slider");
345 		stepControlContainer.add (stepControl[i]);
346 
347 		stepControlLabel[i] = BWidgets::Label ((i + 0.5) * sw / MAXSTEPS + sx - 14, 0, 28, 20, "mlabel", "1.00");
348 		stepControlLabel[i].applyTheme (theme, "mlabel");
349 		stepControlLabel[i].setState (BColors::ACTIVE);
350 		stepControlLabel[i].setEditable (true);
351 		stepControlLabel[i].setCallbackFunction(BEvents::EventType::MESSAGE_EVENT, stepControlLabelMessageCallback);
352 		stepControlContainer.add (stepControlLabel[i]);
353 
354 		inputStepLabel[i] = BWidgets::Label (sx + (i + 0.1) * sw / MAXSTEPS, 10, 0.8 * sw / MAXSTEPS, 20, "steplabel", "#" + std::to_string (i + 1));
355 		sContainer.add (inputStepLabel[i]);
356 
357 		outputStepLabel[i] = BWidgets::Label (sx + (i + 0.1) * sw / MAXSTEPS, 180, 0.8 * sw / MAXSTEPS, 20, "steplabel", "#" + std::to_string (i + 1));
358 		sContainer.add (outputStepLabel[i]);
359 	}
360 
361 	//Initialialize and configure markers
362 	for (int i = 0; i < MAXSTEPS - 1; ++i)
363 	{
364 		markerWidgets[i] = Marker ((i + 1) * sw / MAXSTEPS + sx - 5, 154, 10, 16, "marker", (double(i) + 1.0) / MAXSTEPS, 0.0, 1.0, 0.0);
365 		markerWidgets[i].setHasValue (false);
366 		markerWidgets[i].setDraggable (true);
367 		markerWidgets[i].setCallbackFunction (BEvents::EventType::BUTTON_PRESS_EVENT, BSchafflGUI::markerClickedCallback);
368 		markerWidgets[i].setCallbackFunction (BEvents::EventType::POINTER_DRAG_EVENT, BSchafflGUI::markerDraggedCallback);
369 		markerWidgets[i].setCallbackFunction (BEvents::EventType::VALUE_CHANGED_EVENT, BSchafflGUI::valueChangedCallback);
370 		markerWidgets[i].applyTheme (theme, "slider");
371 		sContainer.add (markerWidgets[i]);
372 	}
373 
374 	setAutoMarkers ();
375 	rearrange_controllers ();
376 	redrawSContainer ();
377 	applyTheme (theme);
378 
379 	// Pack widgets
380 	midiChFilterContainer.add (midiChFilterText);
381 	midiChFilterContainer.add (midiChFilterAllSwitch);
382 	midiChFilterContainer.add (midiChFilterAllLabel);
383 	for (unsigned int i = 0; i < midiChFilterSwitches.size(); ++i)
384 	{
385 		midiChFilterContainer.add (midiChFilterSwitches[i]);
386 		midiChFilterContainer.add (midiChFilterLabels[i]);
387 	}
388 
389 	midiMsgFilterContainer.add (midiMsgFilterText);
390 	midiMsgFilterContainer.add (midiMsgFilterAllSwitch);
391 	midiMsgFilterContainer.add (midiMsgFilterAllLabel);
392 	for (unsigned int i = 0; i < midiMsgFilterSwitches.size(); ++i)
393 	{
394 		midiMsgFilterContainer.add (midiMsgFilterSwitches[i]);
395 		midiMsgFilterContainer.add (midiMsgFilterLabels[i]);
396 	}
397 
398 	midiNoteOptionsContainer.add (midiNoteText);
399         midiNoteOptionsContainer.add (midiNotePositionSwitch);
400         midiNoteOptionsContainer.add (midiNotePositionLabel);
401         midiNoteOptionsContainer.add (midiNoteValueSwitch);
402         midiNoteOptionsContainer.add (midiNoteValueLabel);
403         midiNoteOptionsContainer.add (midiNoteOverlapListbox);
404         midiNoteOptionsContainer.add (midiNoteOverlapLabel);
405 	midiNoteOptionsContainer.add (midiNoteText2);
406 	midiNoteOptionsContainer.add (midiNoteOffAmpLabel);
407 	midiNoteOptionsContainer.add (midiNoteOffAmpListbox);
408 
409 	smartQuantizationContainer.add (smartQuantizationRangeSlider);
410         smartQuantizationContainer.add (smartQuantizationMappingSwitch);
411         smartQuantizationContainer.add (smartQuantizationPositioningSwitch);
412         smartQuantizationContainer.add (smartQuantizationText1);
413         smartQuantizationContainer.add (smartQuantizationRangeLabel);
414 	smartQuantizationContainer.add (smartQuantizationText2);
415         smartQuantizationContainer.add (smartQuantizationMappingLabel);
416         smartQuantizationContainer.add (smartQuantizationPositionLabel);
417 
418 	userLatencyContainer.add (timeCompensText);
419 	userLatencyContainer.add (timeCompensLabel);
420 	userLatencyContainer.add (timeCompensSwitch);
421 	userLatencyContainer.add (userLatencyText);
422 	userLatencyContainer.add (userLatencySwitch);
423 	userLatencyContainer.add (userLatencyLabel);
424 	userLatencyContainer.add (userLatencyValue);
425 	userLatencyContainer.add (userLatencySlider);
426 	userLatencyContainer.add (userLatencyUnitListbox);
427 
428 	for (HaloToggleButton& s : sharedDataButtons) sharedDataSelection.add (s);
429 	mContainer.add (sharedDataSelection);
430 
431 	sContainer.add (inIcon);
432 	sContainer.add (ampIcon);
433 	sContainer.add (delIcon);
434 	sContainer.add (outIcon);
435 	sContainer.add (stepControlContainer);
436 	sContainer.add (shapeWidget);
437 
438 	mContainer.add (selectMenu);
439 	mContainer.add (sContainer);
440 	mContainer.add (helpButton);
441 	mContainer.add (ytButton);
442 
443 	toolbox.add (toolIcon);
444 	toolbox.add (convertToShapeIcon);
445 	toolbox.add (convertToStepsIcon);
446 	toolbox.add (convertToShapeButton);
447 	toolbox.add (convertToStepsButton);
448 	toolbox.add (markersToolbox);
449 	toolbox.add (shapeToolbox);
450 	toolbox.add (editToolbox);
451 	toolbox.add (historyToolbox);
452 	toolbox.add (gridToolbox);
453 	toolbox.add (markersAutoButton);
454 	toolbox.add (markersManualButton);
455 	for (HaloToggleButton& s : shapeToolButtons) toolbox.add (s);
456 	for (HaloButton& e : editToolButtons) toolbox.add (e);
457 	for (HaloButton& h : historyToolButtons) toolbox.add (h);
458 	toolbox.add (gridShowButton);
459 	toolbox.add (gridSnapButton);
460 	mContainer.add (toolbox);
461 
462 	mContainer.add (modeSwitch);
463 	mContainer.add (seqLenValueListbox);
464 	mContainer.add (seqLenBaseListbox);
465 	mContainer.add (ampSwingControl);
466 	mContainer.add (ampRandomControl);
467 	mContainer.add (ampProcessControl);
468 	mContainer.add (swingControl);
469 	mContainer.add (swingRandomControl);
470 	mContainer.add (swingProcessControl);
471 	mContainer.add (nrStepsControl);
472 	mContainer.add (latencyValue);
473 	mContainer.add (latencyDisplay);
474 	mContainer.add (messageLabel);
475 
476 	mContainer.add (seqLenIcon);
477         mContainer.add (ampSwingIcon);
478         mContainer.add (ampRandomIcon);
479         mContainer.add (ampProcessIcon);
480         mContainer.add (nrStepsIcon);
481         mContainer.add (swingIcon);
482         mContainer.add (swingRandomIcon);
483         mContainer.add (swingProcessIcon);
484 
485 	convertToShapeMessage.add (convertToShapeToLinearButton);
486         convertToShapeMessage.add (convertToShapeToLinearText);
487         convertToShapeMessage.add (convertToShapeToConstButton);
488         convertToShapeMessage.add (convertToShapeToConstText);
489 
490 	add (mContainer);
491 
492 	std::array<double, MAXSTEPS> s;
493 	s.fill (1.0);
494 	sliderHistory.setDefault (s);
495 	sliderHistory.clear();
496 
497 	//Scan host features for URID map
498 	LV2_URID_Map* m = NULL;
499 	for (int i = 0; features[i]; ++i)
500 	{
501 		if (strcmp(features[i]->URI, LV2_URID__map) == 0) m = (LV2_URID_Map*) features[i]->data;
502 	}
503 	if (!m) throw std::invalid_argument ("Host does not support urid:map");
504 
505 	//Map URIS
506 	map = m;
507 	getURIs (map, &uris);
508 
509 	// Initialize forge
510 	lv2_atom_forge_init (&forge,map);
511 }
512 
~BSchafflGUI()513 BSchafflGUI::~BSchafflGUI() {}
514 
portEvent(uint32_t port_index,uint32_t buffer_size,uint32_t format,const void * buffer)515 void BSchafflGUI::portEvent(uint32_t port_index, uint32_t buffer_size, uint32_t format, const void* buffer)
516 {
517 	// Notify port
518 	if ((format == uris.atom_eventTransfer) && (port_index == OUTPUT))
519 	{
520 		const LV2_Atom* atom = (const LV2_Atom*) buffer;
521 		if ((atom->type == uris.atom_Blank) || (atom->type == uris.atom_Object))
522 		{
523 			const LV2_Atom_Object* obj = (const LV2_Atom_Object*) atom;
524 
525 			// Linked / unlinked to shared data
526 			if (obj->body.otype == uris.bschaffl_sharedDataLinkEvent)
527 			{
528 				LV2_Atom *oNr = NULL;
529 
530 				lv2_atom_object_get
531 				(
532 					obj,
533 					uris.bschaffl_sharedDataNr, &oNr,
534 					NULL
535 				);
536 
537 				if (oNr && (oNr->type == uris.atom_Int))
538 				{
539 					const int nr = ((LV2_Atom_Int*)oNr)->body;
540 					if ((nr >= 0) && (nr <= 4) && (nr != sharedDataSelection.getValue()))
541 					{
542 						sharedDataSelection.setValueable (false);
543 						sharedDataSelection.setValue (nr);
544 						sharedDataSelection.setValueable (true);
545 
546 						for (int i = 0; i < 4; ++i)
547 						{
548 							sharedDataButtons[i].setValueable (false);
549 							sharedDataButtons[i].setValue (i == nr - 1 ? 1 : 0);
550 							sharedDataButtons[i].setValueable (true);
551 						}
552 
553 					}
554 				}
555 			}
556 
557 			// Controller changed
558 			else if (obj->body.otype == uris.bschaffl_controllerEvent)
559 			{
560 				LV2_Atom *oNr = NULL, *oVal = NULL;
561 
562 				lv2_atom_object_get
563 				(
564 					obj,
565 					uris.bschaffl_controllerNr, &oNr,
566 					uris.bschaffl_controllerValue, &oVal,
567 					NULL
568 				);
569 
570 				if (oNr && (oNr->type == uris.atom_Int) && oVal && (oVal->type == uris.atom_Float))
571 				{
572 					const int nr =  ((LV2_Atom_Int*)oNr)->body;
573 					const float val = ((LV2_Atom_Float*)oVal)->body;
574 
575 					if ((nr >= STEP_POS) && (nr < STEP_POS + MAXSTEPS - 1))
576 					{
577 						setMarker (nr - STEP_POS, val);
578 						setAutoMarkers ();
579 						rearrange_controllers ();
580 						redrawSContainer ();
581 					}
582 
583 					else setController (nr, val);
584 				}
585 			}
586 
587 			// Status notification
588 			if (obj->body.otype == uris.bschaffl_statusEvent)
589 			{
590 				const LV2_Atom *oPos = NULL, *oLat = NULL, *oRate = NULL;
591 				lv2_atom_object_get
592 				(
593 					obj,
594 					uris.bschaffl_step, &oPos,
595 					uris.bschaffl_latency, &oLat,
596 					uris.bschaffl_rate, &oRate,
597 					0
598 				);
599 				if (oPos && (oPos->type == uris.atom_Int))
600 				{
601 					const int step = LIMIT (((LV2_Atom_Int*)oPos)->body, 0, MAXSTEPS - 1);
602 					if (inputStepLabel[step].getName() != "actsteplabel")
603 					{
604 						for (int i = 0; i < MAXSTEPS; ++i)
605 						{
606 							if (i == step)
607 							{
608 								inputStepLabel[i].rename ("actsteplabel");
609 								outputStepLabel[i].rename ("actsteplabel");
610 							}
611 
612 							else
613 							{
614 								inputStepLabel[i].rename ("steplabel");
615 								outputStepLabel[i].rename ("steplabel");
616 							}
617 
618 							inputStepLabel[i].applyTheme (theme);
619 							outputStepLabel[i].applyTheme (theme);
620 						}
621 					}
622 				}
623 
624 				if (oLat && (oLat->type == uris.atom_Float))
625 				{
626 					const float latencyMs = ((LV2_Atom_Float*)oLat)->body;
627 					latencyDisplay.setText ((latencyMs > 0) ? BSCHAFFL_LABEL_LATENCY ": " + BUtilities::to_string (latencyMs, "%5.1f") + " " BSCHAFFL_LABEL_MS : "");
628 				}
629 
630 				if (oRate && (oRate->type == uris.atom_Double))
631 				{
632 					rate = ((LV2_Atom_Double*)oRate)->body;
633 
634 					const float latencyFr =
635 					(
636 						userLatencyUnitListbox.getValue() == 2.0 ?
637 						userLatencySlider.getValue() * rate / 1000.0 :
638 						userLatencySlider.getValue()
639 
640 					);
641 					userLatencyValue.setValue (latencyFr);
642 				}
643 			}
644 
645 			// Shape notification
646 			else if (obj->body.otype == uris.bschaffl_shapeEvent)
647 			{
648 				LV2_Atom *oData = NULL;
649 				lv2_atom_object_get
650 				(
651 					obj,
652 					uris.bschaffl_shapeData, &oData,
653 					NULL
654 				);
655 
656 				if (oData && (oData->type == uris.atom_Vector))
657 				{
658 
659 					const LV2_Atom_Vector* vec = (const LV2_Atom_Vector*) oData;
660 					if (vec->body.child_type == uris.atom_Float)
661 					{
662 						int32_t size = (int32_t) ((oData->size - sizeof(LV2_Atom_Vector_Body)) / (7 * sizeof (float)));
663 						float* data = (float*) (&vec->body + 1);
664 
665 						shapeWidget.setValueEnabled (false);
666 						shapeWidget.clearShape ();
667 						for (int i = 0; (i < size) && (i < MAXNODES); ++i)
668 						{
669 							Node node;
670 							node.nodeType = NodeType (int (data[i * 7]));
671 							node.point.x = data[i * 7 + 1];
672 							node.point.y = data[i * 7 + 2];
673 							node.handle1.x = data[i * 7 + 3];
674 							node.handle1.y = data[i * 7 + 4];
675 							node.handle2.x = data[i * 7 + 5];
676 							node.handle2.y = data[i * 7 + 6];
677 							shapeWidget.appendRawNode (node);
678 						}
679 						shapeWidget.validateShape();
680 						shapeWidget.pushToSnapshots ();
681 						shapeWidget.update ();
682 						shapeWidget.setValueEnabled (true);
683 					}
684 				}
685 			}
686 
687 			// Message notification
688 			if (obj->body.otype == uris.notify_messageEvent)
689 			{
690 				const LV2_Atom* data = NULL;
691 				lv2_atom_object_get(obj, uris.notify_message, &data, 0);
692 				if (data && (data->type == uris.atom_Int))
693 				{
694 					const int messageNr = ((LV2_Atom_Int*)data)->body;
695 					std::string msg = ((messageNr >= NO_MSG) && (messageNr <= MAX_MSG) ? messageStrings[messageNr] : "");
696 					messageLabel.setText (msg);
697 				}
698 			}
699 		}
700 	}
701 
702 	// Scan controller ports
703 	else if ((format == 0) && (port_index >= CONTROLLERS) && (port_index < CONTROLLERS + NR_CONTROLLERS) && (sharedDataSelection.getValue() == 0))
704 	{
705 		float* pval = (float*) buffer;
706 		const int controllerNr = port_index - CONTROLLERS;
707 
708 		if ((controllerNr >= STEP_POS) && (controllerNr < STEP_POS + MAXSTEPS - 1))
709 		{
710 			setMarker (controllerNr - STEP_POS, *pval);
711 			setAutoMarkers ();
712 			rearrange_controllers ();
713 			redrawSContainer ();
714 		}
715 
716 		else setController (controllerNr, *pval);
717 	}
718 }
719 
resizeGUI()720 void BSchafflGUI::resizeGUI()
721 {
722 	hide ();
723 
724 	// Resize Fonts
725 	defaultFont.setFontSize (12 * sz);
726 	lfFont.setFontSize (12 * sz);
727 	txFont.setFontSize (12 * sz);
728 	mdFont.setFontSize (10 * sz);
729 	smFont.setFontSize (8 * sz);
730 
731 	// Resize Background
732 	cairo_surface_t* surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1020 * sz, 480 * sz);
733 	cairo_t* cr = cairo_create (surface);
734 	cairo_scale (cr, sz, sz);
735 	cairo_set_source_surface(cr, bgImageSurface, 0, 0);
736 	cairo_paint(cr);
737 	widgetBg.loadFillFromCairoSurface(surface);
738 	cairo_destroy (cr);
739 	cairo_surface_destroy (surface);
740 
741 	// Resize widgets
742 	RESIZE (mContainer, 0, 0, 1020, 480, sz);
743 
744 	RESIZE (helpButton, 950, 60, 24, 24, sz);
745 	RESIZE (ytButton, 980, 60, 24, 24, sz);
746 
747 	RESIZE (toolbox, 380, 330, 620, 40, sz);
748 	RESIZE (toolIcon, 4, 2, 40, 26, sz);
749 	RESIZE (convertToShapeIcon, 50, 5, 50, 20, sz);
750 	RESIZE (convertToStepsIcon, 50, 5, 50, 20, sz);
751 	RESIZE (convertToShapeButton, 48, 3, 54, 24, sz);
752 	RESIZE (convertToStepsButton, 48, 3, 54, 24, sz);
753 	RESIZE (markersToolbox, 120, 5, 50, 20, sz);
754 	RESIZE (shapeToolbox, 190, 5, 140, 20, sz);
755 	RESIZE (editToolbox, 360, 5, 80, 20, sz);
756 	RESIZE (historyToolbox, 460, 5, 80, 20, sz);
757 	RESIZE (gridToolbox, 560, 5, 50, 20, sz);
758 	RESIZE (markersAutoButton, 118, 3, 24, 24, sz);
759 	RESIZE (markersManualButton, 148, 3, 24, 24, sz);
760 	for (size_t i = 0; i < shapeToolButtons.size(); ++i) {RESIZE (shapeToolButtons[i], 188 + i * 30, 3, 24, 24, sz);}
761 	for (size_t i = 0; i < editToolButtons.size(); ++i) {RESIZE (editToolButtons[i], 358 + i * 30, 3, 24, 24, sz);}
762 	for (size_t i = 0; i < historyToolButtons.size(); ++i) {RESIZE (historyToolButtons[i], 458 + i * 30, 3, 24, 24, sz);}
763 	RESIZE (gridShowButton, 588, 3, 24, 24, sz);
764 	RESIZE (gridSnapButton, 588, 3, 24, 24, sz);
765 
766 	RESIZE (midiChFilterIcon, 0, 0, 300, 20, sz);
767 	RESIZE (midiChFilterContainer, 0, 0, 340, 140, sz);
768 	RESIZE (midiChFilterText, 10, 10, 320, 50, sz);
769 	RESIZE (midiChFilterAllSwitch, 10, 36, 28, 14, sz);
770 	RESIZE (midiChFilterAllLabel, 44, 33, 120, 20, sz);
771 	for (unsigned int i = 0; i < midiChFilterSwitches.size(); ++i)
772 	{
773 		RESIZE (midiChFilterSwitches[i], 10 + 80.0 * int (i / 4), 56 + int (i % 4) * 20, 28, 14, sz);
774 		RESIZE (midiChFilterLabels[i], 44 + 80.0 * int (i / 4), 53  + int (i % 4) * 20, 36, 20, sz);
775 	}
776 
777 	RESIZE (midiMsgFilterIcon, 0, 0, 300, 20, sz);
778 	RESIZE (midiMsgFilterContainer, 0, 0, 340, 200, sz);
779 	RESIZE (midiMsgFilterText, 10, 10, 320, 20, sz);
780 	RESIZE (midiMsgFilterAllSwitch, 10, 36, 28, 14, sz);
781 	RESIZE (midiMsgFilterAllLabel, 50, 33, 120, 20, sz);
782 	for (unsigned int i = 0; i < midiMsgFilterSwitches.size(); ++i)
783 	{
784 		RESIZE (midiMsgFilterSwitches[i], 10, 56 + i * 20, 28, 14, sz);
785 		RESIZE (midiMsgFilterLabels[i], 50, 53 + i * 20, 180, 20, sz);
786 	}
787 
788 	RESIZE (midiNoteOptionsIcon, 0, 0, 300, 20, sz);
789         RESIZE (midiNoteOptionsContainer, 0, 0, 340, 210, sz);
790 	RESIZE (midiNoteText, 10, 10, 320, 50, sz);
791 	RESIZE (midiNotePositionSwitch, 10, 68, 28, 14, sz);
792 	RESIZE (midiNotePositionLabel, 50, 65, 240, 20, sz);
793 	RESIZE (midiNoteValueSwitch, 10, 88, 28, 14, sz);
794 	RESIZE (midiNoteValueLabel, 50, 85, 180, 20, sz);
795 	RESIZE (midiNoteOverlapListbox, 160, 110, 100, 20, sz);
796 	midiNoteOverlapListbox.resizeListBox (BUtilities::Point (100 * sz, 80 * sz));
797 	midiNoteOverlapListbox.moveListBox (BUtilities::Point (0, 20 * sz));
798 	midiNoteOverlapListbox.resizeListBoxItems (BUtilities::Point (100 * sz, 20 * sz));
799 	RESIZE (midiNoteOverlapLabel, 10, 110, 150, 20, sz);
800 	RESIZE (midiNoteText2, 10, 140, 320, 30, sz);
801         RESIZE (midiNoteOffAmpLabel, 10, 180, 110, 20, sz);
802         RESIZE (midiNoteOffAmpListbox, 120, 180, 220, 20, sz);
803 	midiNoteOffAmpListbox.resizeListBox (BUtilities::Point (220 * sz, 60 * sz));
804 	midiNoteOffAmpListbox.moveListBox (BUtilities::Point (0, 20 * sz));
805 	midiNoteOffAmpListbox.resizeListBoxItems (BUtilities::Point (220 * sz, 20 * sz));
806 
807 	RESIZE (smartQuantizationIcon, 0, 0, 300, 20, sz);
808 	RESIZE (smartQuantizationContainer, 0, 0, 340, 200, sz);
809 	RESIZE (smartQuantizationRangeSlider, 10, 80, 110, 28, sz);
810 	RESIZE (smartQuantizationMappingSwitch, 10, 156, 28, 14, sz);
811 	RESIZE (smartQuantizationPositioningSwitch, 10, 176, 28, 14, sz);
812 	RESIZE (smartQuantizationText1, 10, 10, 320, 70, sz);
813 	RESIZE (smartQuantizationRangeLabel, 130, 90, 150, 20, sz);
814 	RESIZE (smartQuantizationText2, 10, 120, 320, 30, sz);
815 	RESIZE (smartQuantizationMappingLabel, 50, 153, 180, 20, sz);
816 	RESIZE (smartQuantizationPositionLabel, 50, 173, 180, 20, sz);
817 
818 	RESIZE (userLatencyIcon, 0, 0, 300, 20, sz);
819 	RESIZE (userLatencyContainer, 0, 0, 340, 180, sz);
820 	RESIZE (timeCompensText, 10, 10, 320, 30, sz);
821 	RESIZE (timeCompensLabel, 50, 45, 230, 20, sz);
822 	RESIZE (timeCompensSwitch, 10, 48, 28, 14, sz);
823 	RESIZE (userLatencyText, 10, 80, 320, 30, sz);
824 	RESIZE (userLatencySwitch, 10, 118, 28, 14, sz);
825 	RESIZE (userLatencyLabel, 50, 115, 180, 20, sz);
826 	RESIZE (userLatencySlider, 10, 140, 160, 28, sz);
827 	RESIZE (userLatencyUnitListbox, 180, 150, 90, 20, sz);
828 	userLatencyUnitListbox.resizeListBox (BUtilities::Point (90 * sz, 60 * sz));
829 	userLatencyUnitListbox.moveListBox (BUtilities::Point (0, 20 * sz));
830 	userLatencyUnitListbox.resizeListBoxItems (BUtilities::Point (40 * sz, 20 * sz));
831 
832 	RESIZE (selectMenu, 20, 90, 340, 350, sz);
833 
834 	RESIZE (sharedDataSelection, 58, 448, 304, 24, sz);
835 	for (int i = 0; i < 4; ++i) {RESIZE (sharedDataButtons[i], 80 * i, 0, 64, 24, sz);}
836 
837 	RESIZE (sContainer, 380, 90, 620, 240, sz);
838 
839 	RESIZE (modeSwitch, 426, 71, 28, 14, sz);
840 	RESIZE (seqLenValueListbox, 380, 380, 50, 20, sz);
841 	seqLenValueListbox.resizeListBox (BUtilities::Point (50 * sz, 220 * sz));
842 	seqLenValueListbox.moveListBox (BUtilities::Point (0, -220 * sz));
843 	seqLenValueListbox.resizeListBoxItems (BUtilities::Point (50 * sz, 20 * sz));
844 	RESIZE (seqLenBaseListbox, 440, 380, 90, 20, sz);
845 	seqLenBaseListbox.resizeListBox (BUtilities::Point (90 * sz, 80 * sz));
846 	seqLenBaseListbox.moveListBox (BUtilities::Point (0, -80 * sz));
847 	seqLenBaseListbox.resizeListBoxItems (BUtilities::Point (90 * sz, 20 * sz));
848 	RESIZE (ampSwingControl, 565, 372, 120, 28, sz);
849 	RESIZE (ampRandomControl, 720, 372, 120, 28, sz);
850 	RESIZE (ampProcessControl, 875, 372, 120, 28, sz);
851 	RESIZE (swingControl, 565, 422, 120, 28, sz);
852 	RESIZE (swingRandomControl, 720, 422, 120, 28, sz);
853 	RESIZE (swingProcessControl, 875, 422, 120, 28, sz);
854 	RESIZE (nrStepsControl, 380, 422, 155, 28, sz);
855 	RESIZE (stepControlContainer, 40, 40, 580, 130, sz);
856 	RESIZE (shapeWidget, 40, 40, 580, 130, sz);
857 	RESIZE (markerListBox, 12, -68, 86, 66, sz);
858 	markerListBox.resizeItems (BUtilities::Point (80 * sz, 20 * sz));
859 
860 	RESIZE (seqLenIcon, 385, 405, 140, 12, sz);
861         RESIZE (ampSwingIcon, 555, 405, 140, 12, sz);
862         RESIZE (ampRandomIcon, 710, 405, 140, 12, sz);
863         RESIZE (ampProcessIcon, 865, 405, 140, 12, sz);
864         RESIZE (nrStepsIcon, 385, 455, 140, 12, sz);
865         RESIZE (swingIcon, 555, 455, 140, 12, sz);
866         RESIZE (swingRandomIcon, 710, 455, 140, 12, sz);
867         RESIZE (swingProcessIcon, 865, 455, 140, 12, sz);
868 
869 	RESIZE (messageLabel, 480, 63, 465, 20, sz);
870 	RESIZE (latencyDisplay, 900, 10, 120, 10, sz);
871 
872 	RESIZE (inIcon, 4, 14, 32, 12, sz);
873 	RESIZE (ampIcon, 4, 100, 32, 20, sz);
874 	RESIZE (delIcon, 2, 185, 36, 12, sz);
875 	RESIZE (outIcon, 4, 214, 32, 12, sz);
876 
877 	// Update monitors
878 	rearrange_controllers ();
879 	redrawSContainer ();
880 
881 	// Apply changes
882 	applyTheme (theme);
883 	show ();
884 }
885 
applyTheme(BStyles::Theme & theme)886 void BSchafflGUI::applyTheme (BStyles::Theme& theme)
887 {
888 	mContainer.applyTheme (theme);
889 
890 	midiChFilterIcon.applyTheme (theme);
891         midiChFilterContainer.applyTheme (theme);
892 	midiChFilterText.applyTheme (theme);
893 	midiChFilterAllSwitch.applyTheme (theme);
894 	midiChFilterAllLabel.applyTheme (theme);
895 	for (unsigned int i = 0; i < midiChFilterSwitches.size(); ++i)
896 	{
897 		midiChFilterSwitches[i].applyTheme (theme);
898 		midiChFilterLabels[i].applyTheme (theme);
899 	}
900 
901 	midiMsgFilterIcon.applyTheme (theme);
902         midiMsgFilterContainer.applyTheme (theme);
903 	midiMsgFilterText.applyTheme (theme);
904 	midiMsgFilterAllSwitch.applyTheme (theme);
905 	midiMsgFilterAllLabel.applyTheme (theme);
906 	for (unsigned int i = 0; i < midiMsgFilterSwitches.size(); ++i)
907 	{
908 		midiMsgFilterSwitches[i].applyTheme (theme);
909 		midiMsgFilterLabels[i].applyTheme (theme);
910 	}
911 
912 	midiNoteOptionsIcon.applyTheme (theme);
913         midiNoteOptionsContainer.applyTheme (theme);
914 	midiNoteText.applyTheme (theme);
915         midiNotePositionSwitch.applyTheme (theme);
916         midiNotePositionLabel.applyTheme (theme);
917         midiNoteValueSwitch.applyTheme (theme);
918         midiNoteValueLabel.applyTheme (theme);
919         midiNoteOverlapListbox.applyTheme (theme);
920         midiNoteOverlapLabel.applyTheme (theme);
921 	midiNoteText2.applyTheme (theme);
922         midiNoteOffAmpLabel.applyTheme (theme);
923         midiNoteOffAmpListbox.applyTheme (theme);
924 
925 	smartQuantizationIcon.applyTheme (theme);
926 	smartQuantizationContainer.applyTheme (theme);
927         smartQuantizationRangeSlider.applyTheme (theme);
928         smartQuantizationMappingSwitch.applyTheme (theme);
929         smartQuantizationPositioningSwitch.applyTheme (theme);
930         smartQuantizationText1.applyTheme (theme);
931         smartQuantizationRangeLabel.applyTheme (theme);
932 	smartQuantizationText2.applyTheme (theme);
933         smartQuantizationMappingLabel.applyTheme (theme);
934         smartQuantizationPositionLabel.applyTheme (theme);
935 
936 	userLatencyIcon.applyTheme (theme);
937         userLatencyContainer.applyTheme (theme);
938 	timeCompensText.applyTheme (theme);
939 	timeCompensLabel.applyTheme (theme);
940 	timeCompensSwitch.applyTheme (theme);
941 	userLatencyText.applyTheme (theme);
942 	userLatencySwitch.applyTheme (theme);
943 	userLatencyLabel.applyTheme (theme);
944 	userLatencyValue.applyTheme (theme);
945 	userLatencySlider.applyTheme (theme);
946 	userLatencyUnitListbox.applyTheme (theme);
947 
948 	selectMenu.applyTheme (theme);
949 
950 	sharedDataSelection.applyTheme (theme);
951 	for (HaloToggleButton& s : sharedDataButtons) s.applyTheme (theme);
952 	sContainer.applyTheme (theme);
953 	helpButton.applyTheme (theme);
954 	ytButton.applyTheme (theme);
955 
956 	toolbox.applyTheme (theme);
957 	toolIcon.applyTheme (theme);
958 	convertToShapeIcon.applyTheme (theme);
959 	convertToStepsIcon.applyTheme (theme);
960 	convertToShapeButton.applyTheme (theme);
961 	convertToStepsButton.applyTheme (theme);
962 	markersToolbox.applyTheme (theme);
963 	shapeToolbox.applyTheme (theme);
964 	editToolbox.applyTheme (theme);
965 	historyToolbox.applyTheme (theme);
966 	gridToolbox.applyTheme (theme);
967 	markersAutoButton.applyTheme (theme);
968 	markersManualButton.applyTheme (theme);
969 	for (HaloToggleButton& s : shapeToolButtons) s.applyTheme (theme);
970 	for (HaloButton& e : editToolButtons) e.applyTheme (theme);
971 	for (HaloButton& h : historyToolButtons) h.applyTheme (theme);
972 	gridShowButton.applyTheme (theme);
973 	gridSnapButton.applyTheme (theme);
974 
975 	convertToShapeMessage.applyTheme (theme);
976         convertToShapeToLinearButton.applyTheme (theme);
977         convertToShapeToLinearText.applyTheme (theme);
978         convertToShapeToConstButton.applyTheme (theme);
979         convertToShapeToConstText.applyTheme (theme);
980 
981 	convertToStepsMessage.applyTheme (theme);
982 
983 	seqLenIcon.applyTheme (theme);
984         ampSwingIcon.applyTheme (theme);
985         ampRandomIcon.applyTheme (theme);
986         ampProcessIcon.applyTheme (theme);
987         nrStepsIcon.applyTheme (theme);
988         swingIcon.applyTheme (theme);
989         swingRandomIcon.applyTheme (theme);
990         swingProcessIcon.applyTheme (theme);
991 
992 	modeSwitch.applyTheme (theme);
993 	seqLenValueListbox.applyTheme (theme);
994 	seqLenBaseListbox.applyTheme (theme);
995 	ampSwingControl.applyTheme (theme);
996 	ampRandomControl.applyTheme (theme);
997 	ampProcessControl.applyTheme (theme);
998 	swingControl.applyTheme (theme);
999 	swingRandomControl.applyTheme (theme);
1000 	swingProcessControl.applyTheme (theme);
1001 	stepControlContainer.applyTheme (theme);
1002 	shapeWidget.applyTheme (theme);
1003 	nrStepsControl.applyTheme (theme);
1004 	latencyValue.applyTheme (theme);
1005 	latencyDisplay.applyTheme (theme);
1006 	messageLabel.applyTheme (theme);
1007 	sContainer.applyTheme (theme);
1008 	markerListBox.applyTheme (theme);
1009 	inIcon.applyTheme (theme);
1010 	ampIcon.applyTheme (theme);
1011 	delIcon.applyTheme (theme);
1012 	outIcon.applyTheme (theme);
1013 	for (int i = 0; i < MAXSTEPS; ++i)
1014 	{
1015 		stepControl[i].applyTheme (theme);
1016 		stepControlLabel[i].applyTheme (theme);
1017 		inputStepLabel[i].applyTheme (theme);
1018 		outputStepLabel[i].applyTheme (theme);
1019 	}
1020 }
1021 
onConfigureRequest(BEvents::ExposeEvent * event)1022 void BSchafflGUI::onConfigureRequest (BEvents::ExposeEvent* event)
1023 {
1024 	Window::onConfigureRequest (event);
1025 
1026 	sz = (getWidth() / 1020 > getHeight() / 480 ? getHeight() / 480 : getWidth() / 1020);
1027 	resizeGUI ();
1028 }
1029 
onCloseRequest(BEvents::WidgetEvent * event)1030 void BSchafflGUI::onCloseRequest (BEvents::WidgetEvent* event)
1031 {
1032 	if (event)
1033 	{
1034 		Widget* requestWidget = event->getRequestWidget ();
1035 		if (requestWidget)
1036 		{
1037 			if (requestWidget == (Widget*) &convertToShapeMessage)
1038 			{
1039 				if (convertToShapeMessage.getValue() == 1.0)
1040 				{
1041 					if (convertToShapeToLinearButton.getValue() != 0.0)
1042 					{
1043 						int nrSteps = nrStepsControl.getValue();
1044 						shapeWidget.setValueEnabled (false);
1045 						shapeWidget.clearShape ();
1046 
1047 						shapeWidget.appendRawNode
1048 						(
1049 							Node
1050 							(
1051 								END_NODE,
1052 								{0.0, (getStepValue (0) + getStepValue (nrSteps - 1)) /2},
1053 								{0.0, 0.0},
1054 								{0.0, 0.0}
1055 							)
1056 						);
1057 
1058 						for (int i = 0; i < nrSteps; ++i)
1059 						{
1060 							shapeWidget.appendRawNode
1061 							(
1062 								Node
1063 								(
1064 									POINT_NODE,
1065 									{(double (i) + 0.5) / double (nrSteps), getStepValue (i)},
1066 									{0.0, 0.0},
1067 									{0.0, 0.0}
1068 								)
1069 							);
1070 						}
1071 
1072 						shapeWidget.appendRawNode
1073 						(
1074 							Node
1075 							(
1076 								END_NODE,
1077 								{1.0, (getStepValue (0) + getStepValue (nrSteps - 1)) /2},
1078 								{0.0, 0.0},
1079 								{0.0, 0.0}
1080 							)
1081 						);
1082 
1083 						shapeWidget.validateShape();
1084 						shapeWidget.pushToSnapshots ();
1085 						shapeWidget.update ();
1086 						shapeWidget.setValueEnabled (true);
1087 						shapeWidget.setValue (1.0);
1088 					}
1089 
1090 					else if (convertToShapeToConstButton.getValue() != 0.0)
1091 					{
1092 						int nrSteps = nrStepsControl.getValue();
1093 						shapeWidget.setValueEnabled (false);
1094 						shapeWidget.clearShape ();
1095 
1096 						shapeWidget.appendRawNode (Node (END_NODE, {0.0, (getStepValue (0))}, {0.0, 0.0}, {0.0, 0.0}));
1097 
1098 						for (int i = 0; i < nrSteps; ++i)
1099 						{
1100 							if (i != 0)
1101 							{
1102 								shapeWidget.appendRawNode
1103 								(
1104 									Node
1105 									(
1106 										POINT_NODE,
1107 										{double (i) / double (nrSteps), getStepValue (i)},
1108 										{0.0, 0.0},
1109 										{0.0, 0.0}
1110 									)
1111 								);
1112 							}
1113 
1114 							shapeWidget.appendRawNode
1115 							(
1116 								Node
1117 								(
1118 									POINT_NODE,
1119 									{double (i + 1) / double (nrSteps) - 1.0 / double (MAPRES), getStepValue (i)},
1120 									{0.0, 0.0},
1121 									{0.0, 0.0}
1122 								)
1123 							);
1124 						}
1125 
1126 						shapeWidget.appendRawNode (Node (END_NODE, {1.0, (getStepValue (0))}, {0.0, 0.0}, {0.0, 0.0}));
1127 
1128 						shapeWidget.validateShape();
1129 						shapeWidget.pushToSnapshots ();
1130 						shapeWidget.update ();
1131 						shapeWidget.setValueEnabled (true);
1132 						shapeWidget.setValue (1.0);
1133 					}
1134 				}
1135 
1136 
1137 				if (requestWidget->getMainWindow()) release (requestWidget);
1138 			}
1139 
1140 			else if (requestWidget == (Widget*) &convertToStepsMessage)
1141 			{
1142 				if (convertToStepsMessage.getValue() == 1.0)
1143 				{
1144 					// Convert to sliders
1145 					int nrSteps = nrStepsControl.getValue();
1146 					for (int i = 0; i < nrSteps; ++i)
1147 					{
1148 						stepControl[i].setValue (shapeWidget.getMapValue ((double (i) + 0.5) / double (nrSteps)));
1149 					}
1150 				}
1151 
1152 				if (requestWidget->getMainWindow()) release (requestWidget);
1153 			}
1154 
1155 			else Window::onCloseRequest (event);
1156 		}
1157 	}
1158 }
1159 
getStepValue(const int stepNr) const1160 double BSchafflGUI::getStepValue (const int stepNr) const
1161 {
1162 	const float aswing = ((stepNr % 2) == 0 ? ampSwingControl.getValue() : 1.0 / ampSwingControl.getValue());
1163 	return stepControl[stepNr].getValue() * LIM (aswing, 0, 1);
1164 }
1165 
sendUiStatus(const bool on)1166 void BSchafflGUI::sendUiStatus (const bool on)
1167 {
1168 	uint8_t obj_buf[64];
1169 	lv2_atom_forge_set_buffer(&forge, obj_buf, sizeof(obj_buf));
1170 
1171 	LV2_Atom_Forge_Frame frame;
1172 	LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object(&forge, &frame, 0, (on ? uris.bschaffl_uiOn : uris.bschaffl_uiOff));
1173 	lv2_atom_forge_pop(&forge, &frame);
1174 	write_function(controller, INPUT, lv2_atom_total_size(msg), uris.atom_eventTransfer, msg);
1175 }
1176 
sendShape()1177 void BSchafflGUI::sendShape ()
1178 {
1179 	size_t size = shapeWidget.size ();
1180 
1181 	uint8_t obj_buf[4096];
1182 	lv2_atom_forge_set_buffer(&forge, obj_buf, sizeof(obj_buf));
1183 
1184 	// Load shapeBuffer
1185 	float shapeBuffer[MAXNODES * 7];
1186 	for (unsigned int i = 0; i < size; ++i)
1187 	{
1188 		Node node = shapeWidget.getRawNode (i);
1189 		shapeBuffer[i * 7 + 0] = (float)node.nodeType;
1190 		shapeBuffer[i * 7 + 1] = (float)node.point.x;
1191 		shapeBuffer[i * 7 + 2] = (float)node.point.y;
1192 		shapeBuffer[i * 7 + 3] = (float)node.handle1.x;
1193 		shapeBuffer[i * 7 + 4] = (float)node.handle1.y;
1194 		shapeBuffer[i * 7 + 5] = (float)node.handle2.x;
1195 		shapeBuffer[i * 7 + 6] = (float)node.handle2.y;
1196 	}
1197 
1198 	// Notify shapeBuffer
1199 	LV2_Atom_Forge_Frame frame;
1200 	LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object (&forge, &frame, 0, uris.bschaffl_shapeEvent);
1201 	lv2_atom_forge_key(&forge, uris.bschaffl_shapeData);
1202 	lv2_atom_forge_vector(&forge, sizeof(float), uris.atom_Float, (uint32_t) (7 * size), &shapeBuffer);
1203 	lv2_atom_forge_pop(&forge, &frame);
1204 	write_function (controller, INPUT, lv2_atom_total_size(msg), uris.atom_eventTransfer, msg);
1205 }
1206 
sendSharedDataNr()1207 void BSchafflGUI::sendSharedDataNr ()
1208 {
1209 	uint8_t obj_buf[64];
1210 	lv2_atom_forge_set_buffer(&forge, obj_buf, sizeof(obj_buf));
1211 
1212 	LV2_Atom_Forge_Frame frame;
1213 	LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object(&forge, &frame, 0, uris.bschaffl_sharedDataLinkEvent);
1214 	lv2_atom_forge_key (&forge, uris.bschaffl_sharedDataNr);
1215 	lv2_atom_forge_int (&forge, sharedDataSelection.getValue());
1216 	lv2_atom_forge_pop(&forge, &frame);
1217 	write_function(controller, INPUT, lv2_atom_total_size(msg), uris.atom_eventTransfer, msg);
1218 }
1219 
sendController(const int nr,const float value)1220 void BSchafflGUI::sendController (const int nr, const float value)
1221 {
1222 	uint8_t obj_buf[64];
1223 	lv2_atom_forge_set_buffer(&forge, obj_buf, sizeof(obj_buf));
1224 
1225 	LV2_Atom_Forge_Frame frame;
1226 	LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object(&forge, &frame, 0, uris.bschaffl_controllerEvent);
1227 	lv2_atom_forge_key (&forge, uris.bschaffl_controllerNr);
1228 	lv2_atom_forge_int (&forge, nr);
1229 	lv2_atom_forge_key (&forge, uris.bschaffl_controllerValue);
1230 	lv2_atom_forge_float (&forge, value);
1231 	lv2_atom_forge_pop(&forge, &frame);
1232 	write_function(controller, INPUT, lv2_atom_total_size(msg), uris.atom_eventTransfer, msg);
1233 }
1234 
setController(const int nr,const double value)1235 float BSchafflGUI::setController (const int nr, const double value)
1236 {
1237 	controllers[nr]->setValueable (false);
1238 	controllers[nr]->setValue (value);
1239 	controllers[nr]->setValueable (true);
1240 
1241 	if (nr == AMP_MODE)
1242 	{
1243 		if (value == 0.0f)
1244 		{
1245 			stepControlContainer.show();
1246 			shapeWidget.hide();
1247 			shapeToolbox.hide();
1248 			editToolbox.hide();
1249 			gridToolbox.hide();
1250 			for (HaloToggleButton& s : shapeToolButtons) s.hide();
1251 			for (HaloButton& e : editToolButtons) e.hide();
1252 			gridShowButton.hide();
1253 			gridSnapButton.hide();
1254 			ampSwingControl.setState (BColors::NORMAL);
1255 			convertToShapeIcon.hide();
1256 			convertToStepsIcon.show();
1257 			convertToShapeButton.hide();
1258 			convertToStepsButton.show();
1259 			if (convertToShapeMessage.getParent()) release (&convertToShapeMessage);
1260 		}
1261 		else
1262 		{
1263 			stepControlContainer.hide();
1264 			shapeWidget.show();
1265 			shapeToolbox.show();
1266 			editToolbox.show();
1267 			gridToolbox.show();
1268 			for (HaloToggleButton& s : shapeToolButtons) s.show();
1269 			for (HaloButton& e : editToolButtons) e.show();
1270 			gridShowButton.show();
1271 			gridSnapButton.show();
1272 			ampSwingControl.setState (BColors::INACTIVE);
1273 			convertToShapeIcon.show();
1274 			convertToStepsIcon.hide();
1275 			convertToShapeButton.show();
1276 			convertToStepsButton.hide();
1277 			if (convertToStepsMessage.getParent()) release (&convertToStepsMessage);
1278 		}
1279 	}
1280 
1281 	else if ((nr >= MIDI_CH_FILTER) && (nr < MIDI_CH_FILTER + NR_MIDI_CHS))
1282 	{
1283 		int count = 0;
1284 		for (BWidgets::HSwitch& s : midiChFilterSwitches)
1285 		{
1286 			if (s.getValue() != 0.0) ++count;
1287 		}
1288 
1289 		if (count == 0)
1290 		{
1291 			midiChFilterAllSwitch.setState (BColors::NORMAL);
1292 			midiChFilterAllLabel.setState (BColors::NORMAL);
1293 			midiChFilterAllSwitch.setValue (0.0);
1294 		}
1295 
1296 		else if (count == NR_MIDI_CHS)
1297 		{
1298 			midiChFilterAllSwitch.setState (BColors::NORMAL);
1299 			midiChFilterAllLabel.setState (BColors::NORMAL);
1300 			midiChFilterAllSwitch.setValue (1.0);
1301 		}
1302 
1303 		else
1304 		{
1305 			midiChFilterAllSwitch.setState (BColors::INACTIVE);
1306 			midiChFilterAllLabel.setState (BColors::INACTIVE);
1307 		}
1308 	}
1309 
1310 	else if ((nr >= MSG_FILTER_NOTE) && (nr < MSG_FILTER_NOTE + NR_MIDI_MSG_FILTERS))
1311 	{
1312 		int count = 0;
1313 		for (BWidgets::HSwitch& s : midiMsgFilterSwitches)
1314 		{
1315 			if (s.getValue() != 0.0) ++count;
1316 		}
1317 
1318 		if (count == 0)
1319 		{
1320 			midiMsgFilterAllSwitch.setState (BColors::NORMAL);
1321 			midiMsgFilterAllLabel.setState (BColors::NORMAL);
1322 			midiMsgFilterAllSwitch.setValue (0.0);
1323 		}
1324 
1325 		else if (count == NR_MIDI_MSG_FILTERS)
1326 		{
1327 			midiMsgFilterAllSwitch.setState (BColors::NORMAL);
1328 			midiMsgFilterAllLabel.setState (BColors::NORMAL);
1329 			midiMsgFilterAllSwitch.setValue (1.0);
1330 		}
1331 
1332 		else
1333 		{
1334 			midiMsgFilterAllSwitch.setState (BColors::INACTIVE);
1335 			midiMsgFilterAllLabel.setState (BColors::INACTIVE);
1336 		}
1337 	}
1338 
1339 	else if (nr == USR_LATENCY)
1340 	{
1341 		if (value == 0.0)
1342 		{
1343 			userLatencySlider.hide();
1344 			userLatencyUnitListbox.hide();
1345 		}
1346 		else
1347 		{
1348 			userLatencySlider.show();
1349 			userLatencyUnitListbox.show();
1350 		}
1351 	}
1352 
1353 	else if ((nr >= STEP_POS) && (nr < STEP_POS + MAXSTEPS - 1)) return (((Marker*)controllers[nr])->hasValue() ? value : 0.0f);
1354 
1355 	else if ((nr >= STEP_LEV) && (nr < STEP_LEV + MAXSTEPS))
1356 	{
1357 		int stepNr = nr - STEP_LEV;
1358 		stepControlLabel[stepNr].setText (BUtilities::to_string (value, "%1.2f"));
1359 
1360 		std::array<double, MAXSTEPS> u = sliderHistory.undo();
1361 		for (int i = 0; i < MAXSTEPS; ++i)
1362 		{
1363 			if ((i != stepNr) && (float (u[i]) != float (stepControl[i].getValue())))
1364 			{
1365 				sliderHistory.redo();
1366 				break;
1367 			}
1368 		}
1369 		stepControlContainer.setValue (1.0);
1370 	}
1371 
1372 	else if (nr == AMP_SWING) rearrange_controllers();
1373 
1374 	else if (nr == AMP_PROCESS)
1375 	{
1376 		if ((value >= 0.0) && (value <= 1.0)) ampProcessControl.setState (BColors::NORMAL);
1377 		else ampProcessControl.setState (BColors::ACTIVE);
1378 	}
1379 
1380 	else if (nr == SWING)
1381 	{
1382 		setAutoMarkers();
1383 		rearrange_controllers();
1384 		redrawSContainer();
1385 	}
1386 
1387 	else if (nr == NR_OF_STEPS)
1388 	{
1389 		shapeWidget.setMinorXSteps (1.0 / value);
1390 		setAutoMarkers();
1391 		rearrange_controllers();
1392 		redrawSContainer();
1393 	}
1394 
1395 	return value;
1396 }
1397 
setMarker(const int markerNr,double value)1398 void BSchafflGUI::setMarker (const int markerNr, double value)
1399 {
1400 	if ((markerNr < 0) || (markerNr >= MAXSTEPS - 1)) return;
1401 
1402 	// Value 0.0: Automatic
1403 	if (value == 0.0)
1404 	{
1405 		markerWidgets[markerNr].setHasValue (false);
1406 	}
1407 
1408 	else
1409 	{
1410 		// Set value and switch off automatic
1411 		value = LIMIT (value, MINMARKERVALUE, 1.0);
1412 		markerWidgets[markerNr].setHasValue (true);
1413 		markerWidgets[markerNr].setValue (value);
1414 
1415 		// Validate ancessors
1416 		for (int i = markerNr - 1; i >= 0; --i)
1417 		{
1418 			if (markerWidgets[i].hasValue())
1419 			{
1420 				if (markerWidgets[i].getValue() > value) markerWidgets[i].setValue (value);
1421 				else break;
1422 			}
1423 		}
1424 
1425 		// Validate successors
1426 		for (int i = markerNr + 1; i < MAXSTEPS - 1; ++i)
1427 		{
1428 			if (markerWidgets[i].hasValue())
1429 			{
1430 				if (markerWidgets[i].getValue() < value) markerWidgets[i].setValue (value);
1431 				else break;
1432 			}
1433 		}
1434 	}
1435 }
1436 
setAutoMarkers()1437 void BSchafflGUI::setAutoMarkers ()
1438 {
1439 	int nrMarkers = controllers[NR_OF_STEPS]->getValue() - 1;
1440 	int start = 0;
1441 	for (int i = 0; i < nrMarkers; ++i)
1442 	{
1443 		if (!markerWidgets[i].hasValue())
1444 		{
1445 			if ((i == nrMarkers - 1) || (markerWidgets[i + 1].hasValue()))
1446 			{
1447 				double swing = 2.0 * swingControl.getValue() / (swingControl.getValue() + 1.0);
1448 				double anc = (start == 0 ? 0 : markerWidgets[start - 1].getValue());
1449 				double suc = (i == nrMarkers - 1 ? 1 : markerWidgets[i + 1].getValue());
1450 				double diff = suc - anc;
1451 				double dist = i - start + 1.0 + (int (i - start) & 1 ? ((start & 1) ? 2.0 - swing : swing) : 1.0);
1452 				double step = (diff < 0 ? 0 : diff / dist);
1453 				for (int j = start; j <= i; ++j)
1454 				{
1455 					double f = ((j & 1) ? 2.0 - swing : swing);
1456 					anc += f * step;
1457 					markerWidgets[j].setValue (anc);
1458 				}
1459 			}
1460 		}
1461 		else start = i + 1;
1462 	}
1463 }
1464 
rearrange_controllers()1465 void BSchafflGUI::rearrange_controllers ()
1466 {
1467 	const int nrStepsi = controllers[NR_OF_STEPS]->getValue();
1468 
1469 	if ((nrStepsi < 1) || (nrStepsi > MAXSTEPS)) return;
1470 
1471 	const double sw = sContainer.getWidth() - 40 * sz;
1472 	const double sx = 40 * sz;
1473 	const double sccw = stepControlContainer.getWidth();
1474 	const double oddf = (ampSwingControl.getValue() >= 1.0 ? 1.0 : ampSwingControl.getValue());
1475 	const double evenf = (ampSwingControl.getValue() >= 1.0 ? 1.0 / ampSwingControl.getValue() : 1.0);
1476 
1477 	for (int i = 0; i < MAXSTEPS; ++i)
1478 	{
1479 		if (i < nrStepsi)
1480 		{
1481 			stepControl[i].resize (14 * sz, (14 + LIMIT (96 * ((i % 2) == 0 ? oddf : evenf), 0, 96 )) * sz);
1482 			stepControl[i].moveTo ((i + 0.5) * sccw / nrStepsi - 7 * sz, 130 * sz - stepControl[i].getHeight());
1483 			stepControl[i].show();
1484 
1485 			if (i < nrStepsi - 1) markerWidgets[i].resize (10 * sz, 16 * sz);
1486 
1487 			stepControlLabel[i].moveTo ((i + 0.5) * sccw / nrStepsi - 14 * sz, 0);
1488 			stepControlLabel[i].resize (28 * sz, 20 * sz);
1489 			stepControlLabel[i].show();
1490 
1491 			inputStepLabel[i].moveTo (sx + (i + 0.1) * sw / nrStepsi, 10 * sz);
1492 			inputStepLabel[i].resize (0.8 * (sw / nrStepsi), 20 * sz);
1493 			inputStepLabel[i].show();
1494 
1495 			const double outx = (i == 0 ? 0.0 : markerWidgets[i - 1].getValue() * sw);
1496 			const double outw =
1497 			(
1498 				i == 0 ?
1499 				markerWidgets[i].getValue() * sw :
1500 				(
1501 					i < nrStepsi - 1 ?
1502 					(markerWidgets[i].getValue() - markerWidgets[i - 1].getValue()) * sw :
1503 					(1 - markerWidgets[i - 1].getValue()) * sw
1504 				)
1505 			);
1506 			outputStepLabel[i].moveTo (sx + outx + 0.1 * outw, 210 * sz);
1507 			outputStepLabel[i].resize (0.8 * outw, 20 * sz);
1508 			outputStepLabel[i].show();
1509 
1510 		}
1511 		else
1512 		{
1513 			stepControl[i].hide ();
1514 			stepControlLabel[i].hide();
1515 			inputStepLabel[i].hide();
1516 			outputStepLabel[i].hide();
1517 		}
1518 	}
1519 
1520 	for (int i = 0; i < MAXSTEPS - 1; ++i)
1521 	{
1522 		if (i < nrStepsi - 1)
1523 		{
1524 			markerWidgets[i].moveTo (markerWidgets[i].getValue() * sw + sx - 5 * sz, 184 * sz);
1525 			markerWidgets[i].show ();
1526 		}
1527 		else markerWidgets[i].hide ();
1528 	}
1529 }
1530 
valueChangedCallback(BEvents::Event * event)1531 void BSchafflGUI::valueChangedCallback (BEvents::Event* event)
1532 {
1533 	if ((event) && (event->getWidget ()))
1534 	{
1535 		BWidgets::ValueWidget* widget = (BWidgets::ValueWidget*) event->getWidget ();
1536 		const double value = widget->getValue();
1537 
1538 		if (widget->getMainWindow ())
1539 		{
1540 			BSchafflGUI* ui = (BSchafflGUI*) widget->getMainWindow ();
1541 
1542 			// Get controller nr
1543 			int controllerNr = -1;
1544 			for (int i = 0; i < NR_CONTROLLERS; ++i)
1545 			{
1546 				if (widget == ui->controllers[i])
1547 				{
1548 					controllerNr = i;
1549 					break;
1550 				}
1551 			}
1552 
1553 			if (controllerNr >= 0)
1554 			{
1555 				if (controllerNr != LATENCY)
1556 				{
1557 					const float v = ui->setController (controllerNr, value);
1558 					if (ui->sharedDataSelection.getValue()) ui->sendController (controllerNr, v);
1559 					else ui->write_function (ui->controller, CONTROLLERS + controllerNr, sizeof (float), 0, &v);
1560 				}
1561 			}
1562 
1563 			else if (widget == &ui->stepControlContainer)
1564 			{
1565 				if (value != 0.0)
1566 				{
1567 					std::array<double, MAXSTEPS> steps;
1568 					for (int i = 0; i < MAXSTEPS; ++i) steps[i] = ui->stepControl[i].getValue();
1569 					ui->sliderHistory.push (steps);
1570 					ui->stepControlContainer.setValue (0.0);
1571 				}
1572 			}
1573 
1574 			else if (widget == &ui->midiChFilterAllSwitch)
1575 			{
1576 				for (BWidgets::HSwitch& s : ui->midiChFilterSwitches) s.setValue (value);
1577 			}
1578 
1579 			else if (widget == &ui->midiMsgFilterAllSwitch)
1580 			{
1581 				for (BWidgets::HSwitch& s : ui->midiMsgFilterSwitches) s.setValue (value);
1582 			}
1583 
1584 			else if (widget == &ui->userLatencySlider)
1585 			{
1586 				const double latencyFr =
1587 				(
1588 					ui->userLatencyUnitListbox.getValue() == 2.0 ?
1589 					ui->userLatencySlider.getValue() * ui->rate / 1000.0 :
1590 					ui->userLatencySlider.getValue()
1591 
1592 				);
1593 				ui->userLatencyValue.setValue (latencyFr);
1594 			}
1595 
1596 			else if (widget == &ui->userLatencyUnitListbox)
1597 			{
1598 				// Translate slider value
1599 				const double val =
1600 				(
1601 					ui->userLatencyUnitListbox.getValue() == 2.0 ?
1602 					ui->userLatencySlider.getValue() * 1000.0 / ui->rate :
1603 					ui->userLatencySlider.getValue() * ui->rate / 1000.0
1604 
1605 				);
1606 
1607 				// Set new slider limits
1608 				const double max = (ui->userLatencyUnitListbox.getValue() == 2.0 ? 192000000.0 / ui->rate : 192000.0);
1609 				ui->userLatencySlider.setValueable (false);
1610  				ui->userLatencySlider.setMax (max);
1611 				ui->userLatencySlider.setValueable (true);
1612 				ui->userLatencySlider.setValue (val);
1613 			}
1614 
1615 			else if (widget == &ui->sharedDataSelection)
1616 			{
1617 				const int val = ui->sharedDataSelection.getValue() - 1;
1618 				for (int i = 0; i < 4; ++i)
1619 				{
1620 					ui->sharedDataButtons[i].setValueable (false);
1621 					ui->sharedDataButtons[i].setValue (i == val ? 1 : 0);
1622 					ui->sharedDataButtons[i].setValueable (true);
1623 				}
1624 
1625 				ui->sendSharedDataNr();
1626 			}
1627 		}
1628 	}
1629 }
1630 
shapeChangedCallback(BEvents::Event * event)1631 void BSchafflGUI::shapeChangedCallback (BEvents::Event* event)
1632 {
1633 	if (!event) return;
1634 	ShapeWidget* widget = (ShapeWidget*) event->getWidget ();
1635 	if (!widget) return;
1636 	float value = widget->getValue();
1637 	if (value != 1.0) return;
1638 	BSchafflGUI* ui = (BSchafflGUI*) widget->getMainWindow();
1639 	if (!ui) return;
1640 
1641 	ui->sendShape ();
1642 }
1643 
markerClickedCallback(BEvents::Event * event)1644 void BSchafflGUI::markerClickedCallback (BEvents::Event* event)
1645 {
1646 	if (!event) return;
1647 	BEvents::PointerEvent* pev = (BEvents::PointerEvent*) event;
1648 	if (pev->getButton() != BDevices::RIGHT_BUTTON) return;
1649 	Marker* marker = (Marker*)event->getWidget();
1650 	if (!marker) return;
1651 	marker->raiseToTop();
1652 	BSchafflGUI* ui = (BSchafflGUI*)marker->getMainWindow();
1653 	if (!ui) return;
1654 
1655 	int nrSteps = ui->controllers[NR_OF_STEPS]->getValue();
1656 
1657 	for (int i = 0; i < nrSteps - 1; ++i)
1658 	{
1659 		if (marker == &ui->markerWidgets[i])
1660 		{
1661 			Marker* oldMarker = (Marker*) ui->markerListBox.getParent();
1662 			ui->markerListBox.setValue (UNSELECTED);
1663 
1664 			if (oldMarker && (oldMarker == marker))
1665 			{
1666 				if (ui->markerListBox.isVisible()) ui->markerListBox.hide();
1667 				else ui->markerListBox.show ();
1668 			}
1669 
1670 			else if (oldMarker && (oldMarker != marker))
1671 			{
1672 				oldMarker->release (&ui->markerListBox);
1673 				marker->add (ui->markerListBox);
1674 				ui->markerListBox.show();
1675 			}
1676 
1677 			else
1678 			{
1679 				marker->add (ui->markerListBox);
1680 				ui->markerListBox.show();
1681 			}
1682 
1683 		}
1684 	}
1685 }
1686 
markerDraggedCallback(BEvents::Event * event)1687 void BSchafflGUI::markerDraggedCallback (BEvents::Event* event)
1688 {
1689 	if (!event) return;
1690 	BEvents::PointerEvent* pev = (BEvents::PointerEvent*) event;
1691 	if (pev->getButton() != BDevices::LEFT_BUTTON) return;
1692 	Marker* marker = (Marker*)event->getWidget();
1693 	if (!marker) return;
1694 	marker->raiseToTop();
1695 	BSchafflGUI* ui = (BSchafflGUI*)marker->getMainWindow();
1696 	if (!ui) return;
1697 
1698 	int nrSteps = ui->controllers[NR_OF_STEPS]->getValue();
1699 
1700 	for (int i = 0; i < nrSteps - 1; ++i)
1701 	{
1702 		if (marker == &ui->markerWidgets[i])
1703 		{
1704 			double x0 = ui->sContainer.getXOffset() + 40 * ui->sz;
1705 			double w = ui->sContainer. getEffectiveWidth() - 40 * ui->sz;
1706 			double frac = (w > 0 ? (pev->getPosition().x + marker->getPosition().x - x0) / w : MINMARKERVALUE);
1707 			frac = LIMIT (frac, MINMARKERVALUE, 1.0);
1708 
1709 			// Limit to antecessors value
1710 			for (int j = i - 1; j >= 0; --j)
1711 			{
1712 				if (ui->markerWidgets[j].hasValue())
1713 				{
1714 					if (frac < ui->markerWidgets[j].getValue()) frac = ui->markerWidgets[j].getValue();
1715 					break;
1716 				}
1717 			}
1718 
1719 			// Limit to successors value
1720 			for (int j = i + 1; j < nrSteps - 1; ++j)
1721 			{
1722 				if (ui->markerWidgets[j].hasValue())
1723 				{
1724 					if (frac > ui->markerWidgets[j].getValue()) frac = ui->markerWidgets[j].getValue();
1725 					break;
1726 				}
1727 			}
1728 
1729 			ui->setMarker (i, frac);
1730 			ui->setAutoMarkers();
1731 			ui->rearrange_controllers();
1732 			ui->redrawSContainer();
1733 			break;
1734 		}
1735 	}
1736 }
1737 
listBoxChangedCallback(BEvents::Event * event)1738 void BSchafflGUI::listBoxChangedCallback (BEvents::Event* event)
1739 {
1740 	if (!event) return;
1741 	BEvents::ValueChangedEvent* vev = (BEvents::ValueChangedEvent*) event;
1742 	BWidgets::ListBox* lb = (BWidgets::ListBox*) vev->getWidget();
1743 	if (!lb) return;
1744 	Marker* m = (Marker*) lb->getParent();
1745 	if (!m) return;
1746 	BSchafflGUI* ui = (BSchafflGUI*)m->getMainWindow();
1747 	if (!ui) return;
1748 
1749 	double value = vev->getValue();
1750 	if (value == 1.0) m->setHasValue (false);
1751 	else if (value == 2.0) m->setHasValue (true);
1752 	else return;
1753 
1754 	lb->hide();
1755 	ui->setAutoMarkers();
1756 	ui->rearrange_controllers();
1757 	ui->redrawSContainer();
1758 }
1759 
markersAutoClickedCallback(BEvents::Event * event)1760 void BSchafflGUI::markersAutoClickedCallback (BEvents::Event* event)
1761 {
1762 	if (!event) return;
1763 	BEvents::ValueChangedEvent* vev = (BEvents::ValueChangedEvent*) event;
1764 	if (vev->getValue() == 0.0) return;
1765 	HaloButton* hb = (HaloButton*) vev->getWidget();
1766 	if (!hb) return;
1767 	BSchafflGUI* ui = (BSchafflGUI*)hb->getMainWindow();
1768 	if (!ui) return;
1769 
1770 	if (hb == &ui->markersAutoButton)
1771 	{
1772 		for (int i = 0; i < MAXSTEPS - 1; ++i)
1773 		{
1774 			const float v = 0;
1775 			ui->markerWidgets[i].setHasValue (false);
1776 			if (ui->sharedDataSelection.getValue()) ui->sendController (STEP_POS + i, v);
1777 			else ui->write_function (ui->controller, CONTROLLERS + STEP_POS + i, sizeof (float), 0, &v);
1778 		}
1779 	}
1780 
1781 	else if (hb == &ui->markersManualButton)
1782 	{
1783 		for (int i = 0; i < MAXSTEPS - 1; ++i)
1784 		{
1785 			const float v = ui->markerWidgets[i].getValue();
1786 			ui->markerWidgets[i].setHasValue (true);
1787 			if (ui->sharedDataSelection.getValue()) ui->sendController (STEP_POS + i, v);
1788 			else ui->write_function (ui->controller, CONTROLLERS + STEP_POS + i, sizeof (float), 0, &v);
1789 		}
1790 	}
1791 
1792 	ui->setAutoMarkers();
1793 	ui->rearrange_controllers();
1794 	ui->redrawSContainer();
1795 }
1796 
shapeToolClickedCallback(BEvents::Event * event)1797 void BSchafflGUI::shapeToolClickedCallback(BEvents::Event* event)
1798 {
1799 	if (!event) return;
1800 	HaloToggleButton* widget = (HaloToggleButton*) event->getWidget ();
1801 	if (!widget) return;
1802 	float value = widget->getValue();
1803 	BSchafflGUI* ui = (BSchafflGUI*) widget->getMainWindow();
1804 	if (!ui) return;
1805 
1806 	// Identify tool
1807 	int widgetNr = 0;
1808 	if (value)
1809 	{
1810 		for (int i = 1; i < NR_TOOLS; ++i)
1811 		{
1812 			if (widget == &ui->shapeToolButtons[i - 1])
1813 			{
1814 				widgetNr = i;
1815 				break;
1816 			}
1817 		}
1818 	}
1819 
1820 	ui->shapeWidget.setTool (ToolType (widgetNr));
1821 
1822 	// Allow only one button pressed
1823 	for (HaloToggleButton& s : ui->shapeToolButtons)
1824 	{
1825 		if (&s != widget) s.setValue (0.0);
1826 	}
1827 }
1828 
editToolClickedCallback(BEvents::Event * event)1829 void BSchafflGUI::editToolClickedCallback(BEvents::Event* event)
1830 {
1831 	if (!event) return;
1832 	HaloButton* widget = (HaloButton*) event->getWidget ();
1833 	if (!widget) return;
1834 	float value = widget->getValue();
1835 	if (value != 1.0) return;
1836 	BSchafflGUI* ui = (BSchafflGUI*) widget->getMainWindow();
1837 	if (!ui) return;
1838 
1839 	// Identify editButtons
1840 	int widgetNr = -1;
1841 	for (size_t i = 0; i < ui->editToolButtons.size(); ++i)
1842 	{
1843 		if (widget == &ui->editToolButtons[i])
1844 		{
1845 			widgetNr = i;
1846 			break;
1847 		}
1848 	}
1849 
1850 	// Action
1851 	switch (widgetNr)
1852 	{
1853 		case 0:		ui->clipboard = ui->shapeWidget.cutSelection();
1854 				break;
1855 
1856 		case 1:		ui->clipboard = ui->shapeWidget.copySelection();
1857 				break;
1858 
1859 		case 2:		ui->shapeWidget.pasteSelection (ui->clipboard);
1860 				break;
1861 
1862 		default:	break;
1863 	}
1864 }
1865 
historyToolClickedCallback(BEvents::Event * event)1866 void BSchafflGUI::historyToolClickedCallback(BEvents::Event* event)
1867 {
1868 	if (!event) return;
1869 	HaloButton* widget = (HaloButton*) event->getWidget ();
1870 	if (!widget) return;
1871 	float value = widget->getValue();
1872 	if (value != 1.0) return;
1873 	BSchafflGUI* ui = (BSchafflGUI*) widget->getMainWindow();
1874 	if (!ui) return;
1875 
1876 	// Identify editButtons
1877 	int widgetNr = -1;
1878 	for (size_t i = 0; i < ui->historyToolButtons.size(); ++i)
1879 	{
1880 		if (widget == &ui->historyToolButtons[i])
1881 		{
1882 			widgetNr = i;
1883 			break;
1884 		}
1885 	}
1886 
1887 	// Action
1888 	if (ui->modeSwitch.getValue() == 0.0)
1889 	{
1890 		std::array<double, MAXSTEPS> values;
1891 
1892 		switch (widgetNr)
1893 		{
1894 			case 0:		values.fill (1.0);
1895 					ui->sliderHistory.push (values);
1896 					break;
1897 
1898 			case 1:		values = ui->sliderHistory.undo();
1899 					break;
1900 
1901 			case 2:		values = ui->sliderHistory.redo();
1902 					break;
1903 
1904 			default:	break;
1905 		}
1906 
1907 		for (int i = 0; i < MAXSTEPS; ++i)
1908 		{
1909 			float value = values[i];
1910 			ui->stepControl[i].setValueable (false);
1911 			ui->stepControl[i].setValue (value);
1912 			ui->stepControl[i].setValueable (true);
1913 			ui->stepControlLabel[i].setText (BUtilities::to_string (value, "%1.2f"));
1914 			if (ui->sharedDataSelection.getValue()) ui->sendController (STEP_LEV + i, value);
1915 			else ui->write_function (ui->controller, CONTROLLERS + STEP_LEV + i, sizeof (float), 0, &value);
1916 		}
1917 	}
1918 
1919 	else
1920 	{
1921 		switch (widgetNr)
1922 		{
1923 			case 0:		ui->shapeWidget.reset();
1924 					break;
1925 
1926 			case 1:		ui->shapeWidget.undo();
1927 					break;
1928 
1929 			case 2:		ui->shapeWidget.redo();
1930 					break;
1931 
1932 			default:	break;
1933 		}
1934 	}
1935 }
1936 
gridToolClickedCallback(BEvents::Event * event)1937 void BSchafflGUI::gridToolClickedCallback (BEvents::Event* event)
1938 {
1939 	if (!event) return;
1940 	HaloToggleButton* widget = (HaloToggleButton*) event->getWidget ();
1941 	if (!widget) return;
1942 	float value = widget->getValue();
1943 	BSchafflGUI* ui = (BSchafflGUI*) widget->getMainWindow();
1944 	if (!ui) return;
1945 
1946 	if (widget == &ui->gridShowButton)
1947 	{
1948 		if (value) ui->shapeWidget.showGrid();
1949 		else ui->shapeWidget.hideGrid();
1950 		ui->shapeWidget.setSnap (false);
1951 		ui->gridSnapButton.setValue (0.0);
1952 	}
1953 
1954 	else if (widget == &ui->gridSnapButton)
1955 	{
1956 		if (value)
1957 		{
1958 			ui->shapeWidget.showGrid();
1959 			ui->shapeWidget.setSnap (true);
1960 		}
1961 		else
1962 		{
1963 			ui->shapeWidget.hideGrid();
1964 			ui->shapeWidget.setSnap (false);
1965 		}
1966 		ui->gridShowButton.setValue (0.0);
1967 	}
1968 }
1969 
convertButtonClickedCallback(BEvents::Event * event)1970 void BSchafflGUI::convertButtonClickedCallback (BEvents::Event* event)
1971 {
1972 	if (!event) return;
1973 	HaloButton* widget = (HaloButton*) event->getWidget ();
1974 	if (!widget) return;
1975 	BSchafflGUI* ui = (BSchafflGUI*) widget->getMainWindow();
1976 	if (!ui) return;
1977 
1978 	if (widget == &ui->convertToShapeButton)
1979 	{
1980 		if (!ui->convertToShapeMessage.getParent())
1981 		{
1982 			ui->add (ui->convertToShapeMessage);
1983 			ui->convertToShapeToLinearButton.setValue (1.0);
1984 		}
1985 	}
1986 
1987 	else if (widget == &ui->convertToStepsButton)
1988 	{
1989 		if (!ui->convertToStepsMessage.getParent()) ui->add (ui->convertToStepsMessage);
1990 	}
1991 }
1992 
lightButtonClickedCallback(BEvents::Event * event)1993 void BSchafflGUI::lightButtonClickedCallback (BEvents::Event* event)
1994 {
1995 	if (!event) return;
1996 	LightButton* widget = (LightButton*) event->getWidget ();
1997 	if (!widget) return;
1998 	double value = widget->getValue();
1999 	if (!value) return;
2000 	BWidgets::Widget* parent = widget->getParent();
2001 	if (!parent) return;
2002 	std::vector<BWidgets::Widget*> children = parent->getChildren();
2003 	BSchafflGUI* ui = (BSchafflGUI*) widget->getMainWindow();
2004 	if (!ui) return;
2005 
2006 	// Un-toggle all other LightButtons
2007 	for (BWidgets::Widget* w : children)
2008 	{
2009 		LightButton* l = dynamic_cast<LightButton*> (w);
2010 		if ((l) && (l != widget)) l->setValue (0.0);
2011 	}
2012 }
2013 
sharedDataClickedCallback(BEvents::Event * event)2014 void BSchafflGUI::sharedDataClickedCallback (BEvents::Event* event)
2015 {
2016 	if (!event) return;
2017 	HaloToggleButton* widget = (HaloToggleButton*) event->getWidget ();
2018 	if (!widget) return;
2019 	double value = widget->getValue();
2020 	BSchafflGUI* ui = (BSchafflGUI*) widget->getMainWindow();
2021 	if (!ui) return;
2022 
2023 	if (value)
2024 	{
2025 		for (int i = 0; i < 4; ++i)
2026 		{
2027 			if (widget == &ui->sharedDataButtons[i])
2028 			{
2029 				ui->sharedDataSelection.setValue (i + 1);
2030 				return;
2031 			}
2032 		}
2033 	}
2034 	ui->sharedDataSelection.setValue (0);
2035 }
2036 
helpButtonClickedCallback(BEvents::Event * event)2037 void BSchafflGUI::helpButtonClickedCallback (BEvents::Event* event)
2038 {
2039 	char cmd[] = WWW_BROWSER_CMD;
2040 	char param[] = HELP_URL;
2041 	char* argv[] = {cmd, param, NULL};
2042 	std::cerr << "BSchaffl.lv2#GUI: Call " << HELP_URL << " for help.\n";
2043 	if (BUtilities::vsystem (argv) == -1) std::cerr << "BSchaffl.lv2#GUI: Couldn't fork.\n";
2044 }
2045 
ytButtonClickedCallback(BEvents::Event * event)2046 void BSchafflGUI::ytButtonClickedCallback (BEvents::Event* event)
2047 {
2048 	char cmd[] = WWW_BROWSER_CMD;
2049 	char param[] = YT_URL;
2050 	char* argv[] = {cmd, param, NULL};
2051 	std::cerr << "BSchaffl.lv2#GUI: Call " << YT_URL << " for tutorial video.\n";
2052 	if (BUtilities::vsystem (argv) == -1) std::cerr << "BSchaffl.lv2#GUI: Couldn't fork.\n";
2053 }
2054 
stepControlLabelMessageCallback(BEvents::Event * event)2055 void BSchafflGUI::stepControlLabelMessageCallback (BEvents::Event* event)
2056 {
2057 	if (event && event->getWidget())
2058 	{
2059 		BWidgets::Label* l = (BWidgets::Label*)event->getWidget();
2060 		BSchafflGUI* ui = (BSchafflGUI*)l->getMainWindow();
2061 		if (ui)
2062 		{
2063 			for (int i = 0; i < MAXSTEPS; ++i)
2064 			{
2065 				if (l == &ui->stepControlLabel[i])
2066 				{
2067 					double val = ui->stepControl[i].getValue();
2068 					try {val = BUtilities::stof (l->getText());}
2069 					catch (std::invalid_argument &ia)
2070 					{
2071 						fprintf (stderr, "%s\n", ia.what());
2072 						l->setText (BUtilities::to_string (val, "%1.2f"));
2073 						return;
2074 					}
2075 
2076 					ui->stepControl[i].setValue (val);
2077 					l->setText (BUtilities::to_string (ui->stepControl[i].getValue(), "%1.2f"));
2078 					break;
2079 				}
2080 			}
2081 		}
2082 	}
2083 }
2084 
redrawSContainer()2085 void BSchafflGUI::redrawSContainer ()
2086 {
2087 	double width = sContainer.getWidth ();
2088 	double x0 = sContainer.getXOffset();
2089 	double y0 = sContainer.getYOffset();
2090 	double height = sContainer.getHeight ();
2091 
2092 	cairo_surface_clear (sContainer.getDrawingSurface ());
2093 	cairo_t* cr = cairo_create (sContainer.getDrawingSurface ());
2094 	if (cairo_status (cr) != CAIRO_STATUS_SUCCESS) return;
2095 
2096 	cairo_pattern_t* pat = cairo_pattern_create_linear (0, 0, 0, height);
2097 	cairo_pattern_add_color_stop_rgba (pat, 0.0, CAIRO_RGBA (BColors::black));
2098 	cairo_pattern_add_color_stop_rgba (pat, 1.0, 0.0, 0.0, 0.0, 0.5);
2099 	cairo_rectangle (cr, 0, 0, width, height);
2100 	cairo_set_source (cr, pat);
2101 	cairo_fill (cr);
2102 	cairo_pattern_destroy (pat);
2103 
2104 	int nrSteps = controllers[NR_OF_STEPS]->getValue();
2105 
2106 	// Step lines
2107 	cairo_set_line_width (cr, 1.0);
2108 	cairo_set_source_rgba (cr, CAIRO_RGBA (BColors::grey));
2109 	cairo_move_to (cr, 40 * sz - x0, -y0);
2110 	cairo_rel_line_to (cr, 0, 240 * sz);
2111 
2112 	for (int i = 0; i < nrSteps - 1; ++i)
2113 	{
2114 		cairo_move_to (cr, double (i + 1) / double (nrSteps) * (width - 40 * sz) + 40 * sz - x0, -y0);
2115 		cairo_rel_line_to (cr, 0, 170 * sz);
2116 		cairo_line_to (cr, markerWidgets[i].getValue() * (width - 40 * sz) + 40 * sz - x0, 180 * sz - y0);
2117 		cairo_rel_line_to (cr, 0, 60 * sz);
2118 		cairo_stroke (cr);
2119 	}
2120 
2121 	cairo_destroy (cr);
2122 	sContainer.update();
2123 }
2124 
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)2125 static LV2UI_Handle instantiate (const LV2UI_Descriptor *descriptor, const char *plugin_uri, const char *bundle_path,
2126 						  LV2UI_Write_Function write_function, LV2UI_Controller controller, LV2UI_Widget *widget,
2127 						  const LV2_Feature *const *features)
2128 {
2129 	PuglNativeView parentWindow = 0;
2130 	LV2UI_Resize* resize = NULL;
2131 
2132 	if (strcmp(plugin_uri, BSCHAFFL_URI) != 0)
2133 	{
2134 		std::cerr << "BSchaffl.lv2#GUI: GUI does not support plugin with URI " << plugin_uri << std::endl;
2135 		return NULL;
2136 	}
2137 
2138 	for (int i = 0; features[i]; ++i)
2139 	{
2140 		if (!strcmp(features[i]->URI, LV2_UI__parent)) parentWindow = (PuglNativeView) features[i]->data;
2141 		else if (!strcmp(features[i]->URI, LV2_UI__resize)) resize = (LV2UI_Resize*)features[i]->data;
2142 	}
2143 	if (parentWindow == 0) std::cerr << "BSchaffl.lv2#GUI: No parent window.\n";
2144 
2145 	// New instance
2146 	BSchafflGUI* ui;
2147 	try {ui = new BSchafflGUI (bundle_path, features, parentWindow);}
2148 	catch (std::exception& exc)
2149 	{
2150 		std::cerr << "BSchaffl.lv2#GUI: Instantiation failed. " << exc.what () << std::endl;
2151 		return NULL;
2152 	}
2153 
2154 	ui->controller = controller;
2155 	ui->write_function = write_function;
2156 
2157 	// Reduce min GUI size for small displays
2158 	double sz = 1.0;
2159 	int screenWidth  = getScreenWidth ();
2160 	int screenHeight = getScreenHeight ();
2161 	if ((screenWidth < 1060) || (screenHeight < 520)) sz = 0.66;
2162 
2163 	if (resize) resize->ui_resize(resize->handle, 1020 * sz, 480 * sz);
2164 
2165 	*widget = (LV2UI_Widget) puglGetNativeWindow (ui->getPuglView ());
2166 
2167 	ui->sendUiStatus (true);
2168 	return (LV2UI_Handle) ui;
2169 }
2170 
cleanup(LV2UI_Handle ui)2171 static void cleanup(LV2UI_Handle ui)
2172 {
2173 	BSchafflGUI* pluginGui = (BSchafflGUI*) ui;
2174 	if (pluginGui) delete pluginGui;
2175 }
2176 
portEvent(LV2UI_Handle ui,uint32_t port_index,uint32_t buffer_size,uint32_t format,const void * buffer)2177 static void portEvent(LV2UI_Handle ui, uint32_t port_index, uint32_t buffer_size,
2178 	uint32_t format, const void* buffer)
2179 {
2180 	BSchafflGUI* pluginGui = (BSchafflGUI*) ui;
2181 	if (pluginGui) pluginGui->portEvent(port_index, buffer_size, format, buffer);
2182 }
2183 
callIdle(LV2UI_Handle ui)2184 static int callIdle (LV2UI_Handle ui)
2185 {
2186 	BSchafflGUI* pluginGui = (BSchafflGUI*) ui;
2187 	if (pluginGui) pluginGui->handleEvents ();
2188 	return 0;
2189 }
2190 
callResize(LV2UI_Handle ui,int width,int height)2191 static int callResize (LV2UI_Handle ui, int width, int height)
2192 {
2193 	BSchafflGUI* self = (BSchafflGUI*) ui;
2194 	if (!self) return 0;
2195 
2196 	BEvents::ExposeEvent* ev = new BEvents::ExposeEvent (self, self, BEvents::CONFIGURE_REQUEST_EVENT, self->getPosition().x, self->getPosition().y, width, height);
2197 	self->addEventToQueue (ev);
2198 	return 0;
2199 }
2200 
2201 static const LV2UI_Idle_Interface idle = {callIdle};
2202 static const LV2UI_Resize resize = {nullptr, callResize};
2203 
extensionData(const char * uri)2204 static const void* extensionData(const char* uri)
2205 {
2206 	if (!strcmp(uri, LV2_UI__idleInterface)) return &idle;
2207 	else if(!strcmp(uri, LV2_UI__resize)) return &resize;
2208 	else return NULL;
2209 }
2210 
2211 static const LV2UI_Descriptor guiDescriptor = {
2212 		BSCHAFFLGUI_URI,
2213 		instantiate,
2214 		cleanup,
2215 		portEvent,
2216 		extensionData
2217 };
2218 
2219 // LV2 Symbol Export
lv2ui_descriptor(uint32_t index)2220 LV2_SYMBOL_EXPORT const LV2UI_Descriptor *lv2ui_descriptor(uint32_t index)
2221 {
2222 	switch (index) {
2223 	case 0: return &guiDescriptor;
2224 	default:return NULL;
2225     }
2226 }
2227