1 /* B.SEQuencer
2  * MIDI Step Sequencer LV2 Plugin
3  *
4  * Copyright (C) 2018, 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 "BSEQuencer_GUI.hpp"
22 #include "BUtilities/to_string.hpp"
23 #include "BUtilities/vsystem.hpp"
24 #include <exception>
25 
BSEQuencer_GUI(const char * bundle_path,const LV2_Feature * const * features,PuglNativeView parentWindow)26 BSEQuencer_GUI::BSEQuencer_GUI (const char *bundle_path, const LV2_Feature *const *features, PuglNativeView parentWindow) :
27 	Window (1250, 820, "B.SEQuencer", parentWindow, true, PUGL_MODULE, 0),
28 	controller (NULL), write_function (NULL),
29 	pluginPath (bundle_path ? std::string (bundle_path) : std::string ("")),
30 	sz (1.0), bgImageSurface (nullptr),
31 	uris (), forge (), clipBoard (),
32 	cursorBits {0}, noteBits (0), chBits (0),
33 	tempTool (false), tempToolCh (0), wheelScrolled (false), modifier (MODIFIER_VELOCITY),
34 	mContainer (0, 0, 1250, 820, "main"),
35 	padSurface (98, 88, 804, 484, "box"),
36 	captionSurface (18, 88, 64, 484, "box"),
37 
38 	modeBox (920, 88, 310, 170, "box"),
39 	modeBoxLabel (10, 10, 290, 20, "ctlabel", BSEQUENCER_LABEL_PLAY_MODE),
40 	modeLabel (10, 80, 60, 20, "lflabel", BSEQUENCER_LABEL_MODE),
41 	modeListBox (80, 80, 220, 20, 220, 80, "menu", BItems::ItemList ({{1, BSEQUENCER_LABEL_AUTOPLAY}, {3, BSEQUENCER_LABEL_HOST_CONTROLLED} , {2, BSEQUENCER_LABEL_HOST_AND_MIDI}}), 2.0),
42 	modeAutoplayBpmLabel (10, 115, 120, 20, "lflabel", BSEQUENCER_LABEL_BPM),
43 	modeAutoplayBpmSlider (120, 105, 180, 25, "slider", 120.0, 1.0, 300.0, 0.0, "%3.1f"),
44 	modeAutoplayBpbLabel (10, 145, 120, 20, "lflabel", BSEQUENCER_LABEL_BPB),
45 	modeAutoplayBpbSlider (120, 135, 180, 25, "slider", 4.0, 1.0, 16.0, 1.0, "%2.0f"),
46 	modeMidiInChannelLabel (10, 110, 130, 20 , "lflabel", BSEQUENCER_LABEL_MIDI_INPUT_CH),
47 	modeMidiInChannelListBox (180, 110, 120, 20, 120, 200, "menu",
48 				  BItems::ItemList ({{0, BSEQUENCER_LABEL_ALL}, {1, "1"}, {2, "2"}, {3, "3"}, {4, "4"}, {5, "5"}, {6, "6"}, {7, "7"}, {8, "8"}, {9, "9"},
49 						     {10, "10"}, {11, "11"}, {12, "12"}, {13, "13"}, {14, "14"}, {15, "15"}, {16, "16"}})),
50 	modeOnKeyLabel (10, 140, 100, 20, "lflabel", BSEQUENCER_LABEL_ON_NOTE_ON),
51 	modeOnKeyListBox (180, 140, 120, 20, 120, 80, "menu", BItems::ItemList ({{0, BSEQUENCER_LABEL_RESTART}, {2, BSEQUENCER_LABEL_RESTART_SYNC}, {1, BSEQUENCER_LABEL_CONTINUE}})),
52 	modePlayLabel (10, 50, 205, 20, "lflabel", BSEQUENCER_LABEL_STATUS_PLAYING),
53 	modePlayButton (270, 40, 30, 30, "box", 1.0),
54 
55 	toolBox (920, 280, 310, 292, "box"),
56 	toolBoxLabel (10, 10, 290, 20, "ctlabel", BSEQUENCER_LABEL_TOOLBOX),
57 	toolButtonBox (0, 30, 310, 160, "widget"),
58 	toolWholeStepButton (210, 40, 80, 20, "tgbutton", BSEQUENCER_LABEL_WHOLE_STEP, 0.0),
59 	toolResetButton (90, 130, 20, 20, "tgbutton"),
60 	toolUndoButton (120, 130, 20, 20, "tgbutton"),
61 	toolRedoButton (150, 130, 20, 20, "tgbutton"),
62 	toolButtonBoxCtrlLabel (10, 10, 80, 20, "lflabel", BSEQUENCER_LABEL_CONTROLS),
63 	toolButtonBoxChLabel (10, 70, 80, 20, "lflabel", BSEQUENCER_LABEL_CHANNELS),
64 	toolButtonBoxEditLabel (10, 100, 80, 20, "lflabel", BSEQUENCER_LABEL_EDIT),
65 	toolGateLabel (10, 260, 30, 20, "ctlabel", BSEQUENCER_LABEL_GATE),
66 	toolGateSlider (12.5, 200, 25, 60, "dial", 1.0, 0.0, 1.0, 0.0, "%1.2f", BWidgets::LABEL_BOTTOM),
67 	toolNoteLabel (45, 260, 60, 20, "ctlabel", BSEQUENCER_LABEL_NOTE),
68 	toolNoteDial (50, 200, 50, 60, "dial", 0.0, -16.0, 16.0, 1.0, "%1.0f"),
69 	toolOctaveLabel (110, 260, 60, 20, "ctlabel", BSEQUENCER_LABEL_OCTAVE),
70 	toolOctaveDial (115, 200, 50, 60, "dial", 0.0, -8.0, 8.0, 1.0, "%1.0f"),
71 	toolVelocityLabel (175, 260, 60, 20, "ctlabel", BSEQUENCER_LABEL_VELOCITY),
72 	toolVelocityDial  (180, 200, 50, 60, "dial", 1.0, 0.0, 2.0, 0.0, "%1.2f"),
73 	toolDurationLabel (240, 260, 60, 20, "ctlabel", BSEQUENCER_LABEL_DURATION),
74 	toolDurationDial (245, 200, 50, 60, "dial", 1.0, 0.0, 1.0, 0.0, "%1.2f", UNIDIRECTIONAL),
75 
76 	propertiesBox (920, 590, 310, 210, "box"),
77 	propertiesBoxLabel (10, 10, 290, 20, "ctlabel", BSEQUENCER_LABEL_PROPERTIES),
78 	propertiesNrStepsLabel (10, 50, 170, 20, "lflabel", BSEQUENCER_LABEL_TOTAL_NUMBER_OF_STEPS),
79 	propertiesNrStepsListBox (210, 50, 90, 20, 90, 100, "menu",
80 				  BItems::ItemList ({{8, "8"}, {16, "16"}, {24, "24"}, {32, "32"}}), 16.0),
81 	propertiesStepsPerLabel (110, 85, 80, 20, "lflabel", BSEQUENCER_LABEL_STEPS_PER),
82 	propertiesStepsPerSlider (10, 75, 90, 25, "slider", 4.0, 1.0, 8.0, 1.0, "%2.0f"),
83 	propertiesBaseListBox (210, 85, 90, 20, 90, 60, "menu",
84 			       BItems::ItemList ({BSEQUENCER_LABEL_BEAT, BSEQUENCER_LABEL_BAR}), 1.0),
85 	propertiesRootLabel (10, 115, 60, 20, "lflabel", BSEQUENCER_LABEL_ROOT),
86 	propertiesRootListBox (110, 115, 90, 20, 0, -160, 90, 160, "menu",
87 			       BItems::ItemList ({{0, "C"}, {2, "D"}, {4, "E"}, {5, "F"}, {7, "G"}, {9, "A"}, {11, "B"}}), 0.0),
88 	propertiesSignatureListBox (210, 115, 90, 20, 90, 80, "menu",
89 				    BItems::ItemList ({{-1, "b"}, {0, ""}, {1, "#"}}), 0.0),
90 	propertiesOctaveLabel (10, 145, 60, 20, "lflabel", BSEQUENCER_LABEL_OCTAVE),
91 	propertiesOctaveListBox (210, 145, 90, 20, 0, -220, 90, 220, "menu",
92 				 BItems::ItemList ({{-1, "-1"}, {0, "0"}, {1, "1"}, {2, "2"}, {3, "3"}, {4, "4"}, {5, "5"}, {6, "6"}, {7, "7"}, {8, "8"}}), 4.0),
93 	propertiesScaleLabel (10, 175, 60, 20, "lflabel", BSEQUENCER_LABEL_SCALE),
94 	propertiesScaleEditIcon (70, 175, 20, 20, "widget", (bundle_path ? std::string (bundle_path) + EDIT_SYMBOL : std::string (""))),
95 	propertiesScaleListBox (),
96 
97 	helpButton (1166, 50, 24, 24, "halobutton", BSEQUENCER_LABEL_HELP),
98 	ytButton (1196, 50, 24, 24, "halobutton", BSEQUENCER_LABEL_FEATURE_TOUR),
99 	scaleEditor (nullptr)
100 {
101 	// Init scale maps
102 	for (int scaleNr = 0; scaleNr < NR_SYSTEM_SCALES + NR_USER_SCALES; ++scaleNr)
103 	{
104 		scaleMaps[scaleNr] = defaultScaleMaps[scaleNr];
105 	}
106 
107 	// Init propertiesScaleListBox
108 	BItems::ItemList scaleItems;
109 	for (int scaleNr = 0; scaleNr < NR_SYSTEM_SCALES + NR_USER_SCALES; ++scaleNr)
110 	{
111 		scaleItems.push_back (BItems::Item (scaleNr, scaleMaps[scaleNr].name));
112 	}
113 	propertiesScaleListBox = BWidgets::PopupListBox (100, 175, 200, 20, 0, -420, 200, 420, "menu", scaleItems, 0.0);
114 	propertiesScaleListBox.rename ("menu");
115 
116 	// Init toolbox buttons
117 	toolButtonBox.addButton (90, 70, 20, 20, {{0.0, 0.03, 0.06, 1.0}, NO_CTRL, BSEQUENCER_LABEL_NO_CHANNEL});
118 	for (int i = 1; i < NR_SEQUENCER_CHS + 1; ++i) toolButtonBox.addButton (90 + i * 30, 70, 20, 20, chButtonStyles[i]);
119 	toolButtonBox.addButton (90 , 10, 20, 20, {{0.0, 0.03, 0.06, 1.0}, NO_CTRL, BSEQUENCER_LABEL_NO_CONTROL});
120 	for (int i = 1; i < NR_CTRL_BUTTONS; ++i) toolButtonBox.addButton (90 + (i % 7) * 30, 10 + ((int) (i / 7)) * 30, 20, 20, ctrlButtonStyles[i]);
121 	for (int i = 0; i < NR_EDIT_BUTTONS; ++i) toolButtonBox.addButton (90 + i * 30, 100, 20, 20, editButtonStyles[i]);
122 
123 	// Init ChBoxes
124 	for (int i = 0; i < NR_SEQUENCER_CHS; ++i)
125 	{
126 		chBoxes[i].box = BWidgets::Widget (98 + i * 203.5, 590, 193.5, 210, "box");
127 		chBoxes[i].chSymbol = BWidgets::DrawingSurface (7, 7, 26, 26, "button");
128 		chBoxes[i].chLabel = BWidgets::Label (40, 10, 133.5, 20, "ctlabel", BSEQUENCER_LABEL_CHANNEL " " + std::to_string (i + 1));
129 		chBoxes[i].channelLabel = BWidgets::Label (10, 50, 100, 20, "lflabel", BSEQUENCER_LABEL_MIDI_CH);
130 		chBoxes[i].channelListBox = BWidgets::PopupListBox (123.5, 50, 60, 20, 60, 120, "menu",
131 								    BItems::ItemList({"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16"}),
132 								    i + 1);
133 		chBoxes[i].pitchLabel = BWidgets::Label (10, 80, 100, 20, "lflabel", BSEQUENCER_LABEL_INPUT_PITCH);
134 		chBoxes[i].pitchSwitch = BWidgets::HSwitch (132.5, 82, 42, 16, "ch" + std::to_string (i + 1), 0.0);
135 		chBoxes[i].pitchScreen = BWidgets::Widget (10, 80, 173.5, 20, "screen");
136 		chBoxes[i].pitchScreen.hide ();
137 		chBoxes[i].velocityDial = BWidgets::DialValue (25, 120, 50, 60, "ch" + std::to_string (i + 1), 1.0, 0.0, 2.0, 0.0, "%1.2f");
138 		chBoxes[i].velocityLabel = BWidgets::Label (20, 180, 60, 20, "ctlabel", BSEQUENCER_LABEL_VELOCITY);
139 		chBoxes[i].noteOffsetDial = BWidgets::DialValue (118.5, 120, 50, 60, "ch" + std::to_string (i + 1), 0.0, -127.0, 127.0, 1.0, "%1.0f");
140 		chBoxes[i].noteOffsetLabel = BWidgets::Label (113.5, 180, 60, 20, "ctlabel", BSEQUENCER_LABEL_OFFSET);
141 	}
142 
143 	// Link controllerWidgets
144 	controllerWidgets[MIDI_IN_CHANNEL] = (BWidgets::ValueWidget*) &modeMidiInChannelListBox;
145 	controllerWidgets[PLAY] = (BWidgets::ValueWidget*) &modePlayButton;
146 	controllerWidgets[MODE] = (BWidgets::ValueWidget*) &modeListBox;
147 	controllerWidgets[ON_KEY_PRESSED] = (BWidgets::ValueWidget*) &modeOnKeyListBox;
148 	controllerWidgets[NR_OF_STEPS] = (BWidgets::ValueWidget*) &propertiesNrStepsListBox;
149 	controllerWidgets[STEPS_PER] = (BWidgets::ValueWidget*) &propertiesStepsPerSlider;
150 	controllerWidgets[BASE] = (BWidgets::ValueWidget*) &propertiesBaseListBox;
151 	controllerWidgets[ROOT] = (BWidgets::ValueWidget*) &propertiesRootListBox;
152 	controllerWidgets[SIGNATURE] = (BWidgets::ValueWidget*) &propertiesSignatureListBox;
153 	controllerWidgets[OCTAVE] = (BWidgets::ValueWidget*) &propertiesOctaveListBox;
154 	controllerWidgets[SCALE] = (BWidgets::ValueWidget*) &propertiesScaleListBox;
155 	controllerWidgets[AUTOPLAY_BPM] = (BWidgets::ValueWidget*) &modeAutoplayBpmSlider;
156 	controllerWidgets[AUTOPLAY_BPB] = (BWidgets::ValueWidget*) &modeAutoplayBpbSlider;
157 	controllerWidgets[SELECTION_CH] = (BWidgets::ValueWidget*) &toolButtonBox;
158 	controllerWidgets[SELECTION_NOTE] = (BWidgets::ValueWidget*) &toolNoteDial;
159 	controllerWidgets[SELECTION_OCTAVE] = (BWidgets::ValueWidget*) &toolOctaveDial;
160 	controllerWidgets[SELECTION_VELOCITY] = (BWidgets::ValueWidget*) &toolVelocityDial;
161 	controllerWidgets[SELECTION_DURATION] = (BWidgets::ValueWidget*) &toolDurationDial;
162 	controllerWidgets[SELECTION_GATE_RAND] = (BWidgets::ValueWidget*) &toolGateSlider;
163 	controllerWidgets[SELECTION_NOTE_RAND] = (BWidgets::ValueWidget*) &toolNoteDial.range;
164 	controllerWidgets[SELECTION_OCTAVE_RAND] = (BWidgets::ValueWidget*) &toolOctaveDial.range;
165 	controllerWidgets[SELECTION_VELOCITY_RAND] = (BWidgets::ValueWidget*) &toolVelocityDial.range;
166 	controllerWidgets[SELECTION_DURATION_RAND] = (BWidgets::ValueWidget*) &toolDurationDial.range;
167 
168 	for (int i = 0; i < NR_SEQUENCER_CHS; ++i)
169 	{
170 		controllerWidgets[CH + i * CH_SIZE + PITCH] = (BWidgets::ValueWidget*) &chBoxes[i].pitchSwitch;
171 		controllerWidgets[CH + i * CH_SIZE + VELOCITY] = (BWidgets::ValueWidget*) &chBoxes[i].velocityDial;
172 		controllerWidgets[CH + i * CH_SIZE + MIDI_CHANNEL] = (BWidgets::ValueWidget*) &chBoxes[i].channelListBox;
173 		controllerWidgets[CH + i * CH_SIZE + NOTE_OFFSET] = (BWidgets::ValueWidget*) &chBoxes[i].noteOffsetDial;
174 	}
175 
176 	// Init controller values
177 	for (int i = 0; i < KNOBS_SIZE; ++i) controllers[i] = controllerWidgets[i]->getValue ();
178 
179 	// Set callback functions
180 	for (int i = 0; i < KNOBS_SIZE; ++i) controllerWidgets[i]->setCallbackFunction (BEvents::VALUE_CHANGED_EVENT, valueChangedCallback);
181 
182 	padSurface.setDraggable (true);
183 	padSurface.setCallbackFunction (BEvents::BUTTON_PRESS_EVENT, padsPressedCallback);
184 	padSurface.setCallbackFunction (BEvents::BUTTON_RELEASE_EVENT, padsPressedCallback);
185 	padSurface.setCallbackFunction (BEvents::POINTER_DRAG_EVENT, padsPressedCallback);
186 
187 	padSurface.setScrollable (true);
188 	padSurface.setCallbackFunction (BEvents::WHEEL_SCROLL_EVENT, padsScrolledCallback);
189 
190 	padSurface.setFocusable (true);
191 	padSurface.setCallbackFunction (BEvents::FOCUS_IN_EVENT, padsFocusedCallback);
192 	padSurface.setCallbackFunction (BEvents::FOCUS_OUT_EVENT, padsFocusedCallback);
193 	padSurface.setMergeable (BEvents::POINTER_DRAG_EVENT, false);
194 
195 	toolResetButton.setCallbackFunction(BEvents::VALUE_CHANGED_EVENT, resetClickedCallback);
196 	toolUndoButton.setCallbackFunction(BEvents::VALUE_CHANGED_EVENT, undoClickedCallback);
197 	toolRedoButton.setCallbackFunction(BEvents::VALUE_CHANGED_EVENT, undoClickedCallback);
198 
199 	helpButton.setCallbackFunction(BEvents::BUTTON_PRESS_EVENT, helpPressedCallback);
200 	ytButton.setCallbackFunction(BEvents::BUTTON_PRESS_EVENT, ytPressedCallback);
201 	propertiesScaleEditIcon.setCallbackFunction(BEvents::BUTTON_PRESS_EVENT, editPressedCallback);
202 
203 	// Apply theme
204 	bgImageSurface = cairo_image_surface_create_from_png ((pluginPath + BG_FILE).c_str());
205 	widgetBg.loadFillFromCairoSurface (bgImageSurface);
206 	applyTheme (theme);
207 
208 	toolDurationDial.range.setMax (0.0);
209 	toolGateSlider.setHardChangeable (false);
210 	toolNoteDial.setHardChangeable (false);
211 	toolOctaveDial.setHardChangeable (false);
212 	toolVelocityDial.setHardChangeable (false);
213 	toolDurationDial.setHardChangeable (false);
214 
215 	modeAutoplayBpmLabel.hide ();
216 	modeAutoplayBpmSlider.hide ();
217 	modeAutoplayBpbLabel.hide ();
218 	modeAutoplayBpbSlider.hide ();
219 
220 	modeBoxLabel.setState (BColors::ACTIVE);
221 	toolBoxLabel.setState (BColors::ACTIVE);
222 	propertiesBoxLabel.setState (BColors::ACTIVE);
223 	propertiesScaleEditIcon.hide ();
224 	for (int i = 0; i < NR_SEQUENCER_CHS; ++i)
225 	{
226 		chBoxes[i].chLabel.setState (BColors::ACTIVE);
227 		drawButton (chBoxes[i].chSymbol.getDrawingSurface(), 0, 0, 20, 20, chButtonStyles[i + 1].color, chButtonStyles[i + 1].symbol);
228 		chBoxes[i].noteOffsetDial.setHardChangeable (false);
229 		chBoxes[i].velocityDial.setHardChangeable (false);
230 	}
231 
232 
233 	// Pack widgets
234 
235 	modeBox.add (modeBoxLabel);
236 	modeBox.add (modeLabel);
237 	modeBox.add (modeListBox);
238 	modeBox.add (modeAutoplayBpmLabel);
239 	modeBox.add (modeAutoplayBpmSlider);
240 	modeBox.add (modeAutoplayBpbLabel);
241 	modeBox.add (modeAutoplayBpbSlider);
242 	modeBox.add (modeMidiInChannelLabel);
243 	modeBox.add (modeMidiInChannelListBox);
244 	modeBox.add (modeOnKeyLabel);
245 	modeBox.add (modeOnKeyListBox);
246 	modeBox.add (modePlayLabel);
247 	modeBox.add (modePlayButton);
248 
249 	toolBox.add (toolBoxLabel);
250 	toolBox.add (toolButtonBox);
251 
252 	toolButtonBox.add (toolButtonBoxCtrlLabel);
253 	toolButtonBox.add (toolButtonBoxChLabel);
254 	toolButtonBox.add (toolButtonBoxEditLabel);
255 	toolButtonBox.add (toolWholeStepButton);
256 	toolButtonBox.add (toolResetButton);
257 	toolButtonBox.add (toolUndoButton);
258 	toolButtonBox.add (toolRedoButton);
259 
260 	toolBox.add (toolGateLabel);
261 	toolBox.add (toolGateSlider);
262 	toolBox.add (toolNoteLabel);
263 	toolBox.add (toolNoteDial);
264 	toolBox.add (toolOctaveLabel);
265 	toolBox.add (toolOctaveDial);
266 	toolBox.add (toolVelocityLabel);
267 	toolBox.add (toolVelocityDial);
268 	toolBox.add (toolDurationLabel);
269 	toolBox.add (toolDurationDial);
270 
271 	propertiesBox.add (propertiesBoxLabel);
272 	propertiesBox.add (propertiesNrStepsLabel);
273 	propertiesBox.add (propertiesNrStepsListBox);
274 	propertiesBox.add (propertiesStepsPerLabel);
275 	propertiesBox.add (propertiesStepsPerSlider);
276 	propertiesBox.add (propertiesBaseListBox);
277 	propertiesBox.add (propertiesRootLabel);
278 	propertiesBox.add (propertiesRootListBox);
279 	propertiesBox.add (propertiesSignatureListBox);
280 	propertiesBox.add (propertiesOctaveLabel);
281 	propertiesBox.add (propertiesOctaveListBox);
282 	propertiesBox.add (propertiesScaleLabel);
283 	propertiesBox.add (propertiesScaleEditIcon);
284 	propertiesBox.add (propertiesScaleListBox);
285 
286 	for (int i = 0; i < NR_SEQUENCER_CHS; ++i)
287 	{
288 		chBoxes[i].box.add (chBoxes[i].chSymbol);
289 		chBoxes[i].box.add (chBoxes[i].chLabel);
290 		chBoxes[i].box.add (chBoxes[i].channelLabel);
291 		chBoxes[i].box.add (chBoxes[i].channelListBox);
292 		chBoxes[i].box.add (chBoxes[i].pitchLabel);
293 		chBoxes[i].box.add (chBoxes[i].pitchSwitch);
294 		chBoxes[i].box.add (chBoxes[i].pitchScreen);
295 		chBoxes[i].box.add (chBoxes[i].velocityDial);
296 		chBoxes[i].box.add (chBoxes[i].velocityLabel);
297 		chBoxes[i].box.add (chBoxes[i].noteOffsetDial);
298 		chBoxes[i].box.add (chBoxes[i].noteOffsetLabel);
299 	}
300 
301 	mContainer.add (captionSurface);
302 	mContainer.add (toolBox);
303 	mContainer.add (modeBox);
304 	mContainer.add (propertiesBox);
305 	mContainer.add (helpButton);
306 	mContainer.add (ytButton);
307 	mContainer.add (padSurface);
308 	for (int i = 0; i < NR_SEQUENCER_CHS; ++i) mContainer.add (chBoxes[i].box);
309 
310 	drawCaption ();
311 	drawPad();
312 	add (mContainer);
313 
314 	getKeyGrabStack()->add (this);
315 
316 	pattern.clear ();
317 
318 	//Scan host features for URID map
319 	LV2_URID_Map* map = NULL;
320 	for (int i = 0; features[i]; ++i)
321 	{
322 		if (strcmp(features[i]->URI, LV2_URID__map) == 0)
323 		{
324 			map = (LV2_URID_Map*) features[i]->data;
325 		}
326 	}
327 	if (!map) throw std::invalid_argument ("Host does not support urid:map");
328 
329 	//Map URIS
330 	getURIs (map, &uris);
331 
332 	// Initialize forge
333 	lv2_atom_forge_init (&forge, map);
334 }
335 
~BSEQuencer_GUI()336 BSEQuencer_GUI::~BSEQuencer_GUI ()
337 {
338 	if (scaleEditor) delete scaleEditor;
339 
340 	send_ui_off ();
341 }
342 
port_event(uint32_t port,uint32_t buffer_size,uint32_t format,const void * buffer)343 void BSEQuencer_GUI::port_event(uint32_t port, uint32_t buffer_size,
344 	uint32_t format, const void* buffer)
345 {
346 	// Notify port
347 	if ((format == uris.atom_eventTransfer) && (port == OUTPUT))
348 	{
349 		const LV2_Atom* atom = (const LV2_Atom*) buffer;
350 		if ((atom->type == uris.atom_Blank) || (atom->type == uris.atom_Object))
351 		{
352 			const LV2_Atom_Object* obj = (const LV2_Atom_Object*) atom;
353 
354 			// Pad notification
355 			if (obj->body.otype == uris.notify_padEvent)
356 			{
357 				LV2_Atom *oPad = NULL;
358 				lv2_atom_object_get(obj, uris.notify_pad, &oPad, NULL);
359 
360 				if (oPad && (oPad->type == uris.atom_Vector))
361 				{
362 					const LV2_Atom_Vector* vec = (const LV2_Atom_Vector*) oPad;
363 					if (vec->body.child_type == uris.atom_Float)
364 					{
365 						if (wheelScrolled)
366 						{
367 							pattern.store ();
368 							wheelScrolled = false;
369 						}
370 
371 						uint32_t size = (uint32_t) ((oPad->size - sizeof(LV2_Atom_Vector_Body)) / sizeof (PadMessage));
372 						PadMessage* pMes = (PadMessage*)(&vec->body + 1);
373 						for (unsigned int i = 0; i < size; ++i)
374 
375 						{
376 							int step = (int) pMes[i].step;
377 							int row = (int) pMes[i].row;
378 							if ((step >= 0) && (step < MAXSTEPS) && (row >= 0) && (row < ROWS))
379 							{
380 								pattern.setPad (row, step, Pad (pMes[i]));
381 							}
382 						}
383 						pattern.store ();
384 						drawPad ();
385 					}
386 				}
387 			}
388 
389 			// Status notifications
390 			else if (obj->body.otype == uris.notify_statusEvent)
391 			{
392 				LV2_Atom *oCursors = NULL, *oNotes = NULL, *oChs = NULL;
393 				lv2_atom_object_get
394 				(
395 					obj, uris.notify_cursors, &oCursors,
396 					uris.notify_notes, &oNotes,
397 					uris.notify_channels, &oChs,
398 					NULL
399 				);
400 
401 				// Cursor notifications
402 				if (oCursors && (oCursors->type == uris.atom_Vector))
403 				{
404 					const LV2_Atom_Vector* vec = (const LV2_Atom_Vector*) oCursors;
405 					if (vec->body.child_type == uris.atom_Int)
406 					{
407 						int* cb = (int*)(&vec->body + 1);
408 						if (memcmp (cursorBits, cb, MAXSTEPS * sizeof(int)))
409 						{
410 							memcpy (&cursorBits, cb, MAXSTEPS * sizeof(int));
411 							drawPad ();
412 						}
413 					}
414 				}
415 
416 				// Note notifications
417 				if (oNotes && (oNotes->type == uris.atom_Int) && (noteBits != ((uint32_t) ((LV2_Atom_Int*)oNotes)->body)))
418 				{
419 					noteBits = ((LV2_Atom_Int*)oNotes)->body;
420 					drawCaption ();
421 				}
422 
423 				// BSEQuencer channel (CH) notifications
424 				if (oChs && (oChs->type == uris.atom_Int))
425 				{
426 					chBits = ((LV2_Atom_Int*)oChs)->body;
427 					for (int i = 0; i < NR_SEQUENCER_CHS; ++i)
428 					{
429 						if ((1 << i) & chBits) chBoxes[i].chLabel.setTextColors (ltColors);
430 						else chBoxes[i].chLabel.setTextColors (txColors);
431 					}
432 				}
433 			}
434 
435 			// GUI user scales changed notifications
436 			else if (obj->body.otype == uris.notify_scaleMapsEvent)
437 			{
438 				int iD = 0;
439 				int scaleNr = 0;
440 
441 				LV2_Atom *oId = NULL, *oName = NULL, *oElements = NULL, *oAltSymbols = NULL, *oScale = NULL;
442 				lv2_atom_object_get
443 				(
444 					obj, uris.notify_scaleID,  &oId,
445 					uris.notify_scaleName, &oName,
446 					uris.notify_scaleElements, &oElements,
447 					uris.notify_scaleAltSymbols, &oAltSymbols,
448 					uris.notify_scale, &oScale,
449 					NULL
450 				);
451 
452 				if (oId && (oId->type == uris.atom_Int))
453 				{
454 					iD = ((LV2_Atom_Int*)oId)->body;
455 					for (int i = 0; i < NR_SYSTEM_SCALES + NR_USER_SCALES; ++i)
456 					{
457 						if (iD == scaleMaps[i].iD)
458 						{
459 							scaleNr = i;
460 							break;
461 						}
462 					}
463 				}
464 
465 				if ((scaleNr >= NR_SYSTEM_SCALES) && (scaleNr < NR_SYSTEM_SCALES + NR_USER_SCALES))
466 				{
467 					// Name
468 					if (oName && (oName->type == uris.atom_String))
469 					{
470 						scaleMaps[scaleNr].name = std::string ((char*) LV2_ATOM_BODY(oName));
471 						std::string s = (char*) LV2_ATOM_BODY(oName);
472 						if (propertiesScaleListBox.getItemList())
473 						{
474 							BItems::ItemList::iterator it = std::next (propertiesScaleListBox.getItemList()->begin (), scaleNr);
475 							BWidgets::Label* l = (BWidgets::Label*) (*it).getWidget ();
476 							if (l) l->setText (s);
477 							propertiesScaleListBox.update();
478 						}
479 					}
480 
481 					// Elements TODO exceptions
482 					if (oElements && (oElements->type == uris.atom_Vector))
483 					{
484 						const LV2_Atom_Vector* vec = (const LV2_Atom_Vector*) oElements;
485 						if (vec->body.child_type == uris.atom_Int)
486 						{
487 							memcpy (scaleMaps[scaleNr].elements.data(), &vec->body + 1, 16 * sizeof (int));
488 						}
489 					}
490 
491 					// Alt Symbols TODO exceptions
492 					if (oAltSymbols && (oAltSymbols->type == uris.atom_Vector))
493 					{
494 						const LV2_Atom_Vector* vec = (const LV2_Atom_Vector*) oAltSymbols;
495 						if (vec->body.child_type == uris.atom_String)
496 						{
497 							char rtAltSymbols[16][16];
498 							memcpy (rtAltSymbols, (&vec->body + 1), 16 * 16);
499 
500 							for (size_t r = 0; r < ROWS; ++r)
501 							{
502 								scaleMaps[scaleNr].altSymbols[r] = std::string (rtAltSymbols[r]);
503 							}
504 						}
505 					}
506 
507 					// Scale TODO exceptions
508 					if (oScale && (oScale->type == uris.atom_Vector))
509 					{
510 						const LV2_Atom_Vector* vec = (const LV2_Atom_Vector*) oScale;
511 						if (vec->body.child_type == uris.atom_Int)
512 						{
513 							BScaleNotes* notes = (BScaleNotes*) (&vec->body + 1);
514 							scaleMaps[scaleNr].scaleNotes = *notes;
515 						}
516 					}
517 
518 					if (scaleNr == controllers[SCALE]) drawCaption ();
519 
520 				}
521 			}
522 		}
523 	}
524 
525 	// Scan remaining ports
526 	else if ((format == 0) && (port >= KNOBS))
527 	{
528 		float* pval = (float*) buffer;
529 		controllerWidgets[port-KNOBS]->setValue (*pval);
530 	}
531 
532 }
533 
scale()534 void BSEQuencer_GUI::scale ()
535 {
536 	hide ();
537 	//Scale fonts
538 	ctLabelFont.setFontSize (12 * sz);
539 	tgLabelFont.setFontSize (12 * sz);
540 	iLabelFont.setFontSize (18 * sz);
541 	lfLabelFont.setFontSize (12 * sz);
542 
543 	//Background
544 	cairo_surface_t* surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1250 * sz, 820 * sz);
545 	cairo_t* cr = cairo_create (surface);
546 	cairo_scale (cr, sz, sz);
547 	cairo_set_source_surface(cr, bgImageSurface, 0, 0);
548 	cairo_paint(cr);
549 	widgetBg.loadFillFromCairoSurface(surface);
550 	cairo_destroy (cr);
551 	cairo_surface_destroy (surface);
552 
553 	//Scale widgets
554 	RESIZE (mContainer, 0, 0, 1250, 820, sz);
555 	RESIZE (padSurface, 98, 88, 804, 484, sz);
556 
557 	//RESIZE (padSurfaceFocusText, 0, 0, 100, 60, sz);
558 	scaleFocus ();
559 	RESIZE (captionSurface, 18, 88, 64, 484, sz);
560 
561 	RESIZE (modeBox, 920, 88, 310, 180, sz);
562 	RESIZE (modeBoxLabel, 10, 10, 290, 20, sz);
563 	RESIZE (modeLabel, 10, 80, 60, 20, sz);
564 	RESIZE (modeListBox, 80, 80, 220, 20, sz);
565 	modeListBox.resizeListBox(BUtilities::Point (220 * sz, 80 * sz));
566 	modeListBox.resizeListBoxItems(BUtilities::Point (220 * sz, 20 * sz));
567 	RESIZE (modeAutoplayBpmLabel, 10, 115, 120, 20, sz);
568 	RESIZE (modeAutoplayBpmSlider, 120, 105, 180, 25, sz);
569 	RESIZE (modeAutoplayBpbLabel, 10, 145, 120, 20, sz);
570 	RESIZE (modeAutoplayBpbSlider, 120, 135, 180, 25, sz);
571 	RESIZE (modeMidiInChannelLabel, 10, 110, 130, 20, sz);
572 	RESIZE (modeMidiInChannelListBox, 180, 110, 120, 20, sz);
573 	modeMidiInChannelListBox.resizeListBox (BUtilities::Point (120 * sz, 200 * sz));
574 	modeMidiInChannelListBox.resizeListBoxItems (BUtilities::Point (120 * sz, 20 * sz));
575 	RESIZE (modeOnKeyLabel, 10, 140, 100, 20, sz);
576 	RESIZE (modeOnKeyListBox, 180, 140, 120, 20, sz);
577 	modeOnKeyListBox.resizeListBox (BUtilities::Point (120 * sz, 80 * sz));
578 	modeOnKeyListBox.resizeListBoxItems (BUtilities::Point (120 * sz, 20 * sz));
579 	RESIZE (modePlayLabel, 10, 50, 205, 20, sz);
580 	RESIZE (modePlayButton, 270, 40, 30, 30, sz);
581 
582 	RESIZE (toolBox, 920, 280, 310, 292, sz);
583 	RESIZE (toolBoxLabel, 10, 10, 290, 20, sz);
584 	RESIZE (toolButtonBox, 0, 30, 310, 160, sz);
585 	RESIZE (toolWholeStepButton, 210, 40, 80, 20, sz);
586 	RESIZE (toolResetButton, 90, 130, 20, 20, sz);
587 	RESIZE (toolUndoButton, 120, 130, 20, 20, sz);
588 	RESIZE (toolRedoButton, 150, 130, 20, 20, sz);
589 	RESIZE (toolButtonBoxCtrlLabel, 10, 10, 80, 20, sz);
590 	RESIZE (toolButtonBoxChLabel, 10, 70, 80, 20, sz);
591 	RESIZE (toolButtonBoxEditLabel, 10, 100, 80, 20, sz);
592 	RESIZE (toolGateLabel, 10, 260, 30, 20, sz);
593 	RESIZE (toolGateSlider, 12.5, 200, 25, 60, sz);
594 	RESIZE (toolNoteLabel, 45, 260, 60, 20, sz);
595 	RESIZE (toolNoteDial, 50, 200, 50, 60, sz);
596 	RESIZE (toolOctaveLabel, 110, 260, 60, 20, sz);
597 	RESIZE (toolOctaveDial, 115, 200, 50, 60, sz);
598 	RESIZE (toolVelocityLabel, 175, 260, 60, 20, sz);
599 	RESIZE (toolVelocityDial, 180, 200, 50, 60, sz);
600 	RESIZE (toolDurationLabel, 240, 260, 60, 20, sz);
601 	RESIZE (toolDurationDial, 245, 200, 50, 60, sz);
602 
603 	RESIZE (propertiesBox, 920, 590, 310, 210, sz);
604 	RESIZE (propertiesBoxLabel, 10, 10, 290, 20, sz);
605 	RESIZE (propertiesNrStepsLabel, 10, 50, 170, 20, sz);
606 	RESIZE (propertiesNrStepsListBox, 210, 50, 90, 20, sz);
607 	propertiesNrStepsListBox.resizeListBox (BUtilities::Point (90 * sz, 100 * sz));
608 	propertiesNrStepsListBox.resizeListBoxItems (BUtilities::Point (90 * sz, 20 * sz));
609 	RESIZE (propertiesStepsPerSlider, 10, 75, 90, 25, sz);
610 	RESIZE (propertiesStepsPerLabel, 110, 85, 80, 20, sz);
611 	RESIZE (propertiesBaseListBox, 210, 85, 90, 20, sz);
612 	propertiesBaseListBox.resizeListBox (BUtilities::Point (90 * sz, 60 * sz));
613 	propertiesBaseListBox.resizeListBoxItems (BUtilities::Point (90 * sz, 20 * sz));
614 	RESIZE (propertiesRootLabel, 10, 115, 60, 20, sz);
615 	RESIZE (propertiesRootListBox, 110, 115, 90, 20, sz);
616 	propertiesRootListBox.resizeListBox (BUtilities::Point (90 * sz, 160 * sz));
617 	propertiesRootListBox.moveListBox (BUtilities::Point (0, -160 * sz));
618 	propertiesRootListBox.resizeListBoxItems (BUtilities::Point (90 * sz, 20 * sz));
619 	RESIZE (propertiesSignatureListBox, 210, 115, 90, 20, sz);
620 	propertiesSignatureListBox.resizeListBox (BUtilities::Point (90 * sz, 80 * sz));
621 	propertiesSignatureListBox.resizeListBoxItems (BUtilities::Point (90 * sz, 20 * sz));
622 	RESIZE (propertiesOctaveLabel, 10, 145, 65, 20, sz);
623 	RESIZE (propertiesOctaveListBox, 210, 145, 90, 20, sz);
624 	propertiesOctaveListBox.resizeListBox (BUtilities::Point (90 * sz, 220 * sz));
625 	propertiesOctaveListBox.moveListBox (BUtilities::Point (0, -220 * sz));
626 	propertiesOctaveListBox.resizeListBoxItems (BUtilities::Point (90 * sz, 20 * sz));
627 	RESIZE (propertiesScaleLabel, 10, 175, 60, 20, sz);
628 	RESIZE (propertiesScaleEditIcon, 70, 175, 20, 20, sz);
629 	RESIZE (propertiesScaleListBox, 100, 175, 200, 20, sz);
630 	propertiesScaleListBox.resizeListBox (BUtilities::Point (200 * sz, 420 * sz));
631 	propertiesScaleListBox.moveListBox (BUtilities::Point (0, -420 * sz));
632 	propertiesScaleListBox.resizeListBoxItems (BUtilities::Point (200 * sz, 20 * sz));
633 
634 	RESIZE (helpButton, 1166, 50, 24, 24, sz);
635 	RESIZE (ytButton, 1196, 50, 24, 24, sz);
636 	if (scaleEditor) {RESIZE ((*scaleEditor), 420, 20, 360, 760, sz);}
637 
638 	for (int i = 0; i < NR_SEQUENCER_CHS; ++i)
639 	{
640 		RESIZE (chBoxes[i].box, 98 + i * 203.5, 590, 193.5, 210, sz);
641 		RESIZE (chBoxes[i].chSymbol, 7, 7, 26, 26, sz);
642 		RESIZE (chBoxes[i].chLabel, 40, 10, 133.5, 20, sz);
643 		RESIZE (chBoxes[i].channelLabel, 10, 50, 100, 20, sz);
644 		RESIZE (chBoxes[i].channelListBox, 123.5, 50, 60, 20, sz);
645 		chBoxes[i].channelListBox.resizeListBox (BUtilities::Point (60 * sz, 120 * sz));
646 		chBoxes[i].channelListBox.resizeListBoxItems (BUtilities::Point (60 * sz, 20 * sz));
647 		RESIZE (chBoxes[i].pitchLabel, 10, 80, 100, 20, sz);
648 		RESIZE (chBoxes[i].pitchSwitch, 132.5, 82, 42, 16, sz);
649 		RESIZE (chBoxes[i].pitchScreen, 10, 80, 173.5, 20, sz);
650 		RESIZE (chBoxes[i].velocityDial, 25, 120, 50, 60, sz);
651 		RESIZE (chBoxes[i].velocityLabel, 20, 180, 60, 20, sz);
652 		RESIZE (chBoxes[i].noteOffsetDial, 118.5, 120, 50, 60, sz);
653 		RESIZE (chBoxes[i].noteOffsetLabel, 113.5, 180, 60, 20, sz);
654 
655 		drawButton
656 		(
657 			chBoxes[i].chSymbol.getDrawingSurface(),
658 			0, 0,
659 			chBoxes[i].chSymbol.getEffectiveWidth(),
660 			chBoxes[i].chSymbol.getEffectiveHeight(),
661 			chButtonStyles[i + 1].color,
662 			chButtonStyles[i + 1].symbol
663 		);
664 	}
665 	applyTheme (theme);
666 	drawCaption ();
667 	drawPad ();
668 	show ();
669 }
670 
scaleFocus()671 void BSEQuencer_GUI::scaleFocus ()
672 {
673 	cairo_surface_t* surface = padSurface.getDrawingSurface();
674 	cairo_t* cr = cairo_create (surface);
675 
676 	padSurface.focusText.resize (400,100);	// Maximize size first to omit breaks
677 	std::vector<std::string> textblock = padSurface.focusText.getTextBlock ();
678 	double blockheight = padSurface.focusText.getTextBlockHeight (textblock);
679 	double blockwidth = 0.0;
680 	for (std::string textline : textblock)
681 	{
682 		cairo_text_extents_t ext = padSurface.focusText.getFont ()->getTextExtents (cr, textline);
683 		if (ext.width > blockwidth) blockwidth = ext.width;
684 	}
685 	padSurface.focusText.resize (blockwidth + 2 * padSurface.focusText.getXOffset (), blockheight + 2 * padSurface.focusText.getYOffset ());
686 
687 	padSurface.focusText.resize();
688 
689 	cairo_destroy (cr);
690 }
691 
applyTheme(BStyles::Theme & theme)692 void BSEQuencer_GUI::applyTheme (BStyles::Theme& theme)
693 {
694 	mContainer.applyTheme (theme);
695 
696 	padSurface.applyTheme (theme);
697 	captionSurface.applyTheme (theme);
698 
699 	modeBox.applyTheme (theme);
700 	modeBoxLabel.applyTheme (theme);
701 	modeLabel.applyTheme (theme);
702 	modeListBox.applyTheme (theme);
703 	modeAutoplayBpmLabel.applyTheme (theme);
704 	modeAutoplayBpmSlider.applyTheme (theme);
705 	modeAutoplayBpbLabel.applyTheme (theme);
706 	modeAutoplayBpbSlider.applyTheme (theme);
707 	modeMidiInChannelLabel.applyTheme (theme);
708 	modeMidiInChannelListBox.applyTheme (theme);
709 	modeOnKeyLabel.applyTheme (theme);
710 	modeOnKeyListBox.applyTheme (theme);
711 	modePlayLabel.applyTheme (theme);
712 	modePlayButton.applyTheme (theme);
713 
714 	toolBox.applyTheme (theme);
715 	toolBoxLabel.applyTheme (theme);
716 	toolButtonBox.applyTheme (theme);
717 	toolWholeStepButton.applyTheme (theme);
718 	toolResetButton.applyTheme (theme);
719 	toolUndoButton.applyTheme (theme);
720 	toolRedoButton.applyTheme (theme);
721 	toolButtonBoxCtrlLabel.applyTheme (theme);
722 	toolButtonBoxChLabel.applyTheme (theme);
723 	toolButtonBoxEditLabel.applyTheme (theme);
724 	toolGateLabel.applyTheme (theme);
725 	toolGateSlider.applyTheme (theme);
726 	toolNoteLabel.applyTheme (theme);
727 	toolNoteDial.applyTheme (theme);
728 	toolOctaveLabel.applyTheme (theme);
729 	toolOctaveDial.applyTheme (theme);
730 	toolVelocityLabel.applyTheme (theme);
731 	toolVelocityDial.applyTheme (theme);
732 	toolDurationLabel.applyTheme (theme);
733 	toolDurationDial.applyTheme (theme);
734 
735 	propertiesBox.applyTheme (theme);
736 	propertiesNrStepsLabel.applyTheme (theme);
737 	propertiesNrStepsListBox.applyTheme (theme);
738 	propertiesBoxLabel.applyTheme (theme);
739 	propertiesStepsPerLabel.applyTheme (theme);
740 	propertiesStepsPerSlider.applyTheme (theme);
741 	propertiesBaseListBox.applyTheme (theme);
742 	propertiesRootLabel.applyTheme (theme);
743 	propertiesRootListBox.applyTheme (theme);
744 	propertiesSignatureListBox.applyTheme (theme);
745 	propertiesOctaveLabel.applyTheme (theme);
746 	propertiesOctaveListBox.applyTheme (theme);
747 	propertiesScaleLabel.applyTheme (theme);
748 	propertiesScaleEditIcon.applyTheme (theme);
749 	propertiesScaleListBox.applyTheme (theme);
750 
751 	for (int i = 0; i < NR_SEQUENCER_CHS; ++i)
752 	{
753 		chBoxes[i].box.applyTheme (theme);
754 		chBoxes[i].chSymbol.applyTheme (theme);
755 		chBoxes[i].chLabel.applyTheme (theme);
756 		chBoxes[i].channelLabel.applyTheme (theme);
757 		chBoxes[i].channelListBox.applyTheme (theme);
758 		chBoxes[i].pitchLabel.applyTheme (theme);
759 		chBoxes[i].pitchSwitch.applyTheme (theme);
760 		chBoxes[i].pitchScreen.applyTheme (theme);
761 		chBoxes[i].velocityDial.applyTheme (theme);
762 		chBoxes[i].velocityLabel.applyTheme (theme);
763 		chBoxes[i].noteOffsetDial.applyTheme (theme);
764 		chBoxes[i].noteOffsetLabel.applyTheme (theme);
765 	}
766 
767 	helpButton.applyTheme (theme);
768 	ytButton.applyTheme (theme);
769 	//scaleEditor.applyTheme (theme);
770 }
771 
onConfigureRequest(BEvents::ExposeEvent * event)772 void BSEQuencer_GUI::onConfigureRequest (BEvents::ExposeEvent* event)
773 {
774 	Window::onConfigureRequest (event);
775 
776 	sz = (getWidth() / 1250 > getHeight() / 820 ? getHeight() / 820 : getWidth() / 1250);
777 	scale ();
778 }
779 
onCloseRequest(BEvents::WidgetEvent * event)780 void BSEQuencer_GUI::onCloseRequest (BEvents::WidgetEvent* event)
781 {
782 	if (event && (scaleEditor) && (event->getRequestWidget () == scaleEditor))
783 	{
784 		int mapNr = scaleEditor->getMapNr();
785 		scaleMaps[mapNr] = scaleEditor->getScaleMap();
786 
787 		// Update captions
788 		if (controllers[SCALE] == mapNr) drawCaption();
789 
790 		// Update scale listbox
791 		BItems::ItemList::iterator it = std::next (propertiesScaleListBox.getItemList()->begin (), mapNr);
792 		BWidgets::Label* l = (BWidgets::Label*) (*it).getWidget ();
793 		if (l) l->setText (scaleMaps[mapNr].name);
794 		propertiesScaleListBox.update();
795 
796 		// Notify plugin
797 		send_scaleMaps (mapNr);
798 
799 		// And close / delete
800 		delete scaleEditor;
801 		scaleEditor = nullptr;
802 	}
803 	else Window::onCloseRequest (event);
804 }
805 
onKeyPressed(BEvents::KeyEvent * event)806 void BSEQuencer_GUI::onKeyPressed (BEvents::KeyEvent* event)
807 {
808 	if (!event) return;
809 	if (event->getKey() == BDevices::KEY_SHIFT) modifier = MODIFIER_DURATION;
810 	else if (event->getKey() == BDevices::KEY_CTRL) modifier = MODIFIER_OCTAVE;
811 }
812 
onKeyReleased(BEvents::KeyEvent * event)813 void BSEQuencer_GUI::onKeyReleased (BEvents::KeyEvent* event)
814 {
815 	if ((event) && ((event->getKey() == BDevices::KEY_SHIFT) || (event->getKey() == BDevices::KEY_CTRL))) modifier = MODIFIER_VELOCITY;
816 }
817 
send_ui_on()818 void BSEQuencer_GUI::send_ui_on ()
819 {
820 	uint8_t obj_buf[64];
821 	lv2_atom_forge_set_buffer(&forge, obj_buf, sizeof(obj_buf));
822 
823 	LV2_Atom_Forge_Frame frame;
824 	LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object(&forge, &frame, 0, uris.ui_on);
825 	lv2_atom_forge_pop(&forge, &frame);
826 	write_function(controller, INPUT, lv2_atom_total_size(msg), uris.atom_eventTransfer, msg);
827 }
828 
send_ui_off()829 void BSEQuencer_GUI::send_ui_off ()
830 {
831 	uint8_t obj_buf[64];
832 	lv2_atom_forge_set_buffer(&forge, obj_buf, sizeof(obj_buf));
833 
834 	LV2_Atom_Forge_Frame frame;
835 	LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object(&forge, &frame, 0, uris.ui_off);
836 	lv2_atom_forge_pop(&forge, &frame);
837 	write_function(controller, INPUT, lv2_atom_total_size(msg), uris.atom_eventTransfer, msg);
838 }
839 
send_pad(int row,int step)840 void BSEQuencer_GUI::send_pad (int row, int step)
841 {
842 	PadMessage padmsg (step, row, pattern.getPad (row, step));
843 
844 	uint8_t obj_buf[128];
845 	lv2_atom_forge_set_buffer(&forge, obj_buf, sizeof(obj_buf));
846 
847 	LV2_Atom_Forge_Frame frame;
848 	LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object(&forge, &frame, 0, uris.notify_padEvent);
849 	lv2_atom_forge_key(&forge, uris.notify_pad);
850 	lv2_atom_forge_vector(&forge, sizeof(float), uris.atom_Float, sizeof(PadMessage) / sizeof(float), (void*) &padmsg);
851 	lv2_atom_forge_pop(&forge, &frame);
852 	write_function(controller, INPUT, lv2_atom_total_size(msg), uris.atom_eventTransfer, msg);
853 }
854 
send_scaleMaps(int scaleNr)855 void BSEQuencer_GUI::send_scaleMaps (int scaleNr)
856 {
857 	RTScaleMap rtScaleMap; rtScaleMap = scaleMaps[scaleNr];
858 	uint8_t obj_buf[2048];
859 	lv2_atom_forge_set_buffer(&forge, obj_buf, sizeof(obj_buf));
860 
861 	LV2_Atom_Forge_Frame frame;
862 	LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_object(&forge, &frame, 0, uris.notify_scaleMapsEvent);
863 	lv2_atom_forge_key(&forge, uris.notify_scaleID);
864 	lv2_atom_forge_int(&forge, rtScaleMap.iD);
865 	lv2_atom_forge_key(&forge, uris.notify_scaleName);
866 	lv2_atom_forge_string (&forge, rtScaleMap.name, 64);
867 	lv2_atom_forge_key(&forge, uris.notify_scaleElements);
868 	lv2_atom_forge_vector(&forge, sizeof (int), uris.atom_Int, 16, (void*) rtScaleMap.elements);
869 	lv2_atom_forge_key(&forge, uris.notify_scaleAltSymbols);
870 	lv2_atom_forge_vector(&forge, 16, uris.atom_String, 16, (void*) rtScaleMap.altSymbols);
871 	lv2_atom_forge_key(&forge, uris.notify_scale);
872 	BScaleNotes* notes = &rtScaleMap.scaleNotes;
873 	lv2_atom_forge_vector(&forge, sizeof (int), uris.atom_Int, 12, (void*) notes);
874 	lv2_atom_forge_pop(&forge, &frame);
875 	write_function(controller, INPUT, lv2_atom_total_size(msg), uris.atom_eventTransfer, msg);
876 }
877 
valueChangedCallback(BEvents::Event * event)878 void BSEQuencer_GUI::valueChangedCallback(BEvents::Event* event)
879 {
880 	if ((event) && (event->getWidget ()))
881 	{
882 		BWidgets::ValueWidget* widget = (BWidgets::ValueWidget*) event->getWidget ();
883 		float value = widget->getValue();
884 
885 		if (widget->getMainWindow())
886 		{
887 			BSEQuencer_GUI* ui = (BSEQuencer_GUI*) widget->getMainWindow();
888 			int widgetNr = -1;
889 
890 			for (int i = 0; i < KNOBS_SIZE; ++i)
891 			{
892 				if (widget == ui->controllerWidgets[i])
893 				{
894 					widgetNr = i;
895 					break;
896 				}
897 			}
898 
899 			if (widgetNr >= 0)
900 			{
901 				//std::cerr << "BSEQuencer.lv2#GUI valueChangedCallback() for widget #" << widgetNr << ": " << value << "\n";
902 				ui->controllers[widgetNr] = value;
903 				ui->write_function(ui->controller, KNOBS + widgetNr, sizeof(float), 0, &ui->controllers[widgetNr]);
904 
905 				// Playing status changed
906 				if (widgetNr == PLAY)
907 				{
908 					if (value) ui->modePlayLabel.setText (BSEQUENCER_LABEL_STATUS_PLAYING);
909 					else ui->modePlayLabel.setText (BSEQUENCER_LABEL_STATUS_STOPPED);
910 				}
911 
912 				// Mode changed
913 				if (widgetNr == MODE)
914 				{
915 					if (value == AUTOPLAY)
916 					{
917 						ui->modeAutoplayBpmLabel.show ();
918 						ui->modeAutoplayBpmSlider.show ();
919 						ui->modeAutoplayBpbLabel.show ();
920 						ui->modeAutoplayBpbSlider.show ();
921 						ui->modeMidiInChannelLabel.hide ();
922 						ui->modeMidiInChannelListBox.hide ();
923 						ui->modeOnKeyLabel.hide ();
924 						ui->modeOnKeyListBox.hide ();
925 						for (int i = 0; i < NR_SEQUENCER_CHS; ++i) ui->chBoxes[i].pitchScreen.show ();
926 					}
927 
928 					else if (value == HOST_CONTROLLED)
929 					{
930 						ui->modeAutoplayBpmLabel.hide ();
931 						ui->modeAutoplayBpmSlider.hide ();
932 						ui->modeAutoplayBpbLabel.hide ();
933 						ui->modeAutoplayBpbSlider.hide ();
934 						ui->modeMidiInChannelLabel.show ();
935 						ui->modeMidiInChannelListBox.show ();
936 						ui->modeOnKeyLabel.show ();
937 						ui->modeOnKeyListBox.show ();
938 						for (int i = 0; i < NR_SEQUENCER_CHS; ++i) ui->chBoxes[i].pitchScreen.hide ();
939 					}
940 
941 					else
942 					{
943 						ui->modeAutoplayBpmLabel.hide ();
944 						ui->modeAutoplayBpmSlider.hide ();
945 						ui->modeAutoplayBpbLabel.hide ();
946 						ui->modeAutoplayBpbSlider.hide ();
947 						ui->modeMidiInChannelLabel.hide ();
948 						ui->modeMidiInChannelListBox.hide ();
949 						ui->modeOnKeyLabel.hide ();
950 						ui->modeOnKeyListBox.hide ();
951 						for (int i = 0; i < NR_SEQUENCER_CHS; ++i) ui->chBoxes[i].pitchScreen.show ();
952 					}
953 				}
954 
955 				// Scale changed
956 				if (widgetNr == SCALE)
957 				{
958 					if (value < NR_SYSTEM_SCALES) ui->propertiesScaleEditIcon.hide();
959 					else ui->propertiesScaleEditIcon.show();
960 				}
961 
962 				// Pad relevant changes
963 				if ((widgetNr == NR_OF_STEPS) || (widgetNr == STEPS_PER) ||(widgetNr == ROOT) || (widgetNr == SIGNATURE) ||
964 					(widgetNr == SCALE)) ui->drawPad ();
965 
966 				// Caption relevant changes
967 				if ((widgetNr == ROOT) || (widgetNr == SIGNATURE) || (widgetNr == SCALE)) ui->drawCaption ();
968 
969 				// Range dials: also update parents
970 				if
971 				(
972 					(widgetNr == SELECTION_NOTE_RAND) ||
973 					(widgetNr == SELECTION_OCTAVE_RAND) ||
974 					(widgetNr == SELECTION_VELOCITY_RAND) ||
975 					(widgetNr == SELECTION_DURATION_RAND)
976 				)
977 				{
978 					if (widget->getParent()) widget->getParent()->update();
979 				}
980 			}
981 		}
982 	}
983 }
984 
helpPressedCallback(BEvents::Event * event)985 void BSEQuencer_GUI::helpPressedCallback (BEvents::Event* event)
986 {
987 	char cmd[] = WWW_BROWSER_CMD;
988 	char param[] = HELP_URL;
989 	char* argv[] = {cmd, param, NULL};
990 	std::cerr << "BSEQuencer.lv2#GUI: Call " << HELP_URL << " for help.\n";
991 	if (BUtilities::vsystem (argv) == -1) std::cerr << "BSEQuencer.lv2#GUI: Couldn't fork.\n";
992 }
993 
ytPressedCallback(BEvents::Event * event)994 void BSEQuencer_GUI::ytPressedCallback (BEvents::Event* event)
995 {
996 	char cmd[] = WWW_BROWSER_CMD;
997 	char param[] = YT_URL;
998 	char* argv[] = {cmd, param, NULL};
999 	std::cerr << "BSEQuencer.lv2#GUI: Call " << YT_URL << " for tutorial video.\n";
1000 	if (BUtilities::vsystem (argv) == -1) std::cerr << "BSEQuencer.lv2#GUI: Couldn't fork.\n";
1001 }
1002 
editPressedCallback(BEvents::Event * event)1003 void BSEQuencer_GUI::editPressedCallback (BEvents::Event* event)
1004 {
1005 	if ((event) && (event->getWidget ()) && (event->getWidget()->getMainWindow()))
1006 	{
1007 		BSEQuencer_GUI* ui = (BSEQuencer_GUI*)(((BWidgets::Widget*)(event->getWidget()))->getMainWindow());
1008 		int mapNr = ui->propertiesScaleListBox.getValue();
1009 
1010 		if (!ui->scaleEditor)
1011 		{
1012 			ui->scaleEditor = new ScaleEditor
1013 			(
1014 				420 * ui->sz, 20 * ui->sz, 360 * ui->sz, 760 * ui->sz, "scaleeditor",
1015 				ui->pluginPath,
1016 				mapNr,
1017 				ui->scaleMaps[mapNr],
1018 				BScale
1019 				(
1020 					(int (ui->controllers[ROOT] + ui->controllers[SIGNATURE] + 12)) % 12,
1021 					(SignatureIndex) ui->controllers[SIGNATURE],
1022 					ui->scaleMaps[mapNr].scaleNotes
1023 				)
1024 			);
1025 			ui->add (*ui->scaleEditor);
1026 		}
1027 	}
1028 }
1029 
resetClickedCallback(BEvents::Event * event)1030 void BSEQuencer_GUI::resetClickedCallback (BEvents::Event* event)
1031 {
1032 	if ((event) && (event->getWidget ()) && (event->getWidget()->getMainWindow()))
1033 	{
1034 		BWidgets::Button* widget = (BWidgets::Button*)event->getWidget();
1035 		BSEQuencer_GUI* ui = (BSEQuencer_GUI*)(widget->getMainWindow());
1036 		double value = ((BEvents::ValueChangedEvent*) event)->getValue ();
1037 
1038 		if ((value == 1) && (widget == (BWidgets::Button*)&ui->toolResetButton))
1039 		{
1040 			if (ui->wheelScrolled)
1041 			{
1042 				ui->pattern.store ();
1043 				ui->wheelScrolled = false;
1044 			}
1045 
1046 			Pad p0 = Pad ();
1047 			for (int r = 0; r < ROWS; ++r)
1048 			{
1049 				for (int s = 0; s < MAXSTEPS; ++s)
1050 				{
1051 					ui->pattern.setPad (r, s, p0);
1052 					ui->send_pad (r, s);
1053 				}
1054 			}
1055 
1056 			ui->drawPad ();
1057 			ui->pattern.store ();
1058 		}
1059 	}
1060 }
1061 
undoClickedCallback(BEvents::Event * event)1062 void BSEQuencer_GUI::undoClickedCallback (BEvents::Event* event)
1063 {
1064 	if ((event) && (event->getWidget ()) && (event->getWidget()->getMainWindow()))
1065 	{
1066 		BWidgets::Button* widget = (BWidgets::Button*)event->getWidget();
1067 		BSEQuencer_GUI* ui = (BSEQuencer_GUI*)(widget->getMainWindow());
1068 		double value = ((BEvents::ValueChangedEvent*) event)->getValue ();
1069 
1070 		if (value == 1)
1071 		{
1072 			if ((widget == (BWidgets::Button*)&ui->toolUndoButton) || (widget == (BWidgets::Button*)&ui->toolRedoButton))
1073 			{
1074 				std::vector<PadMessage> padMessages = (widget == (BWidgets::Button*)&ui->toolUndoButton ? ui->pattern.undo () : ui->pattern.redo ());
1075 				for (PadMessage const& p : padMessages)
1076 				{
1077 					size_t r = LIMIT (p.row, 0, ROWS);
1078 					size_t s = LIMIT (p.step, 0, MAXSTEPS);
1079 					ui->send_pad (r, s);
1080 				}
1081 				ui->drawPad ();
1082 			}
1083 		}
1084 	}
1085 }
1086 
padsPressedCallback(BEvents::Event * event)1087 void BSEQuencer_GUI::padsPressedCallback (BEvents::Event* event)
1088 {
1089 	if ((event) && (event->getWidget ()) && (event->getWidget()->getMainWindow()) &&
1090 		((event->getEventType () == BEvents::BUTTON_PRESS_EVENT) ||
1091 		 (event->getEventType () == BEvents::BUTTON_RELEASE_EVENT) ||
1092 		 (event->getEventType () == BEvents::POINTER_DRAG_EVENT)))
1093 	{
1094 		BWidgets::DrawingSurface* widget = (BWidgets::DrawingSurface*) event->getWidget ();
1095 		BSEQuencer_GUI* ui = (BSEQuencer_GUI*) widget->getMainWindow();
1096 		BEvents::PointerEvent* pointerEvent = (BEvents::PointerEvent*) event;
1097 
1098 		if (ui->wheelScrolled)
1099 		{
1100 			ui->pattern.store ();
1101 			ui->wheelScrolled = false;
1102 		}
1103 
1104 		// Get size of drawing area
1105 		const double width = ui->padSurface.getEffectiveWidth ();
1106 		const double height = ui->padSurface.getEffectiveHeight ();
1107 
1108 		int row = (ROWS - 1) - ((int) ((pointerEvent->getPosition ().y - widget->getYOffset()) / (height / ROWS)));
1109 		int step = (pointerEvent->getPosition ().x - widget->getXOffset()) / (width / ui->controllerWidgets[NR_OF_STEPS]->getValue ());
1110 
1111 		if ((event->getEventType () == BEvents::BUTTON_PRESS_EVENT) || (event->getEventType () == BEvents::POINTER_DRAG_EVENT))
1112 		{
1113 
1114 			if ((row >= 0) && (row < ROWS) && (step >= 0) && (step < (int (ui->controllerWidgets[NR_OF_STEPS]->getValue ()))))
1115 			{
1116 				int start = step;
1117 				while (ui->pattern.padHasAntecessor (row, start)) --start;
1118 				Pad pd = ui->pattern.getPad (row, start);
1119 				int pdctrl = (int (pd.ch) & 0xF0) / 0x10;
1120 
1121 				// Left button: apply properties to pad
1122 				if (pointerEvent->getButton() == BDevices::LEFT_BUTTON)
1123 				{
1124 					if (ui->controllerWidgets[SELECTION_CH]->getValue() <= NR_SEQUENCER_CHS)
1125 					{
1126 						Pad props
1127 						(
1128 							ui->controllerWidgets[SELECTION_CH]->getValue() + pdctrl * 0x10,
1129 							ui->controllerWidgets[SELECTION_NOTE]->getValue(),
1130 							ui->controllerWidgets[SELECTION_OCTAVE]->getValue(),
1131 							ui->controllerWidgets[SELECTION_VELOCITY]->getValue(),
1132 							ui->controllerWidgets[SELECTION_DURATION]->getValue() + int (pd.duration - 0.00000001),
1133 							ui->controllerWidgets[SELECTION_GATE_RAND]->getValue(),
1134 							ui->controllerWidgets[SELECTION_NOTE_RAND]->getValue(),
1135 							ui->controllerWidgets[SELECTION_OCTAVE_RAND]->getValue(),
1136 							ui->controllerWidgets[SELECTION_VELOCITY_RAND]->getValue(),
1137 							ui->controllerWidgets[SELECTION_DURATION_RAND]->getValue()
1138 						);
1139 
1140 						// Delete ch if duration == 0
1141 						if (props.duration == 0.0f) props.ch = pdctrl * 0x10;
1142 
1143 						// Click on a pad with same settings as in toolbox => temporarily switch to delete
1144 						if ((props == pd) && (!ui->tempTool) && (event->getEventType () == BEvents::BUTTON_PRESS_EVENT))
1145 						{
1146 							ui->tempTool = true;
1147 							ui->tempToolCh = ui->controllerWidgets[SELECTION_CH]->getValue();
1148 							props.ch = pdctrl * 0x10;
1149 							ui->controllerWidgets[SELECTION_CH]->setValue (0);
1150 						}
1151 
1152 						// Overwrite if new data
1153 						if (!(props == pd))
1154 						{
1155 							int s = 0;
1156 							float d = props.duration;
1157 							do
1158 							{
1159 								props.duration = ((int (props.ch) & 0x0F) == 0 ? ui->controllerWidgets[SELECTION_DURATION]->getValue() : d);
1160 								ui->pattern.setPad (row, start + s, props);
1161 								ui->drawPad (row, start + s);
1162 								ui->send_pad (row, start + s);
1163 								--d;
1164 								++s;
1165 							} while (d > 0.0);
1166 						}
1167 					}
1168 
1169 					// CTRL function set
1170 					else if (ui->controllerWidgets[SELECTION_CH]->getValue() <= NR_SEQUENCER_CHS + NR_CTRL_BUTTONS)
1171 					{
1172 						int ctrl = ((int)ui->controllerWidgets[SELECTION_CH]->getValue() - NR_SEQUENCER_CHS - 1) * 0x10;
1173 
1174 						// Click on a pad with same settings as in toolbox => temporarily switch to delete
1175 						if (((int (pd.ch) & 0xF0) == ctrl) && (!ui->tempTool) && (event->getEventType () == BEvents::BUTTON_PRESS_EVENT))
1176 						{
1177 							ui->tempTool = true;
1178 							ui->tempToolCh = ui->controllerWidgets[SELECTION_CH]->getValue();
1179 							ui->controllerWidgets[SELECTION_CH]->setValue(NR_SEQUENCER_CHS + 1);
1180 							ctrl = ((int)ui->controllerWidgets[SELECTION_CH]->getValue() - NR_SEQUENCER_CHS - 1) * 0x10;
1181 						}
1182 
1183 						// Apply controller data
1184 						// Whole step button pressed ?
1185 						int startrow = row;
1186 						int endrow = row;
1187 						if (ui->toolWholeStepButton.getValue() == 1.0)
1188 						{
1189 							startrow = 0;
1190 							endrow = ROWS - 1;
1191 						}
1192 
1193 						for (int irow = startrow; irow <= endrow; ++irow)
1194 						{
1195 							int istart = step;
1196 							while (ui->pattern.padHasAntecessor (irow, istart)) --istart;
1197 
1198 							// Overwrite if new data
1199 							if ((int (ui->pattern.getPad (irow, istart).ch) & 0xF0) != ctrl)
1200 							{
1201 								int istep = istart;
1202 								do
1203 								{
1204 									Pad iPad = ui->pattern.getPad (irow, istep);
1205 									iPad.ch = (int (iPad.ch) & 0x0F) + (istep == istart ? ctrl : 0);
1206 									ui->pattern.setPad (irow, istep, iPad);
1207 									ui->drawPad (irow, istep);
1208 									ui->send_pad (irow, istep);
1209 									++istep;
1210 								} while (ui->pattern.padHasSuccessor (irow, istep - 1));
1211 							}
1212 						}
1213 					}
1214 
1215 					// Edit mode
1216 					else
1217 					{
1218 						int edit = ((int)ui->controllerWidgets[SELECTION_CH]->getValue() - NR_SEQUENCER_CHS - NR_CTRL_BUTTONS) * 0x100;
1219 
1220 						if (edit == EDIT_PICK)
1221 						{
1222 							ui->controllerWidgets[SELECTION_CH]->setValue (int (pd.ch) & 0x0F);
1223 							ui->controllerWidgets[SELECTION_OCTAVE]->setValue(pd.pitchOctave);
1224 							ui->controllerWidgets[SELECTION_VELOCITY]->setValue(pd.velocity);
1225 							ui->controllerWidgets[SELECTION_DURATION]->setValue(pd.duration);
1226 						}
1227 
1228 						else if (edit == EDIT_MERGE)
1229 						{
1230 							if (ui->clipBoard.ready)
1231 							{
1232 								ui->clipBoard.origin = std::make_pair (row, start);
1233 								ui->clipBoard.extends = std::make_pair (0, ui->pattern.padGetSize (row, start) - 1);
1234 								ui->clipBoard.ready = false;
1235 								ui->drawPad (row, step);
1236 							}
1237 
1238 							else
1239 							{
1240 								std::pair<int, int> newExtends = std::make_pair (0, step - ui->clipBoard.origin.second);
1241 								if (newExtends != ui->clipBoard.extends)
1242 								{
1243 									ui->clipBoard.extends = newExtends;
1244 									ui->drawPad ();
1245 								}
1246 							}
1247 						}
1248 
1249 						else if ((edit == EDIT_CUT) || (edit == EDIT_COPY) || (edit == EDIT_FLIPX) || (edit == EDIT_FLIPY))
1250 						{
1251 							if (ui->clipBoard.ready)
1252 							{
1253 								ui->clipBoard.origin = std::make_pair (row, start);
1254 								ui->clipBoard.extends = std::make_pair (0, ui->pattern.padGetSize (row, start) - 1);
1255 								ui->clipBoard.ready = false;
1256 								ui->drawPad (row, step);
1257 							}
1258 
1259 							else
1260 							{
1261 								int end = step;
1262 								while (ui->pattern.padHasSuccessor (row, end)) ++end;
1263 								std::pair<int, int> newExtends = std::make_pair (row - ui->clipBoard.origin.first, end - ui->clipBoard.origin.second);
1264 								if (newExtends != ui->clipBoard.extends)
1265 								{
1266 									ui->clipBoard.extends = newExtends;
1267 									ui->drawPad ();
1268 								}
1269 							}
1270 						}
1271 
1272 						else if (edit == EDIT_PASTE)
1273 						{
1274 							if (!ui->clipBoard.data.empty ())
1275 							{
1276 
1277 								for (int r = 0; r < int (ui->clipBoard.data.size ()); ++r)
1278 								{
1279 									// Clip merged pads
1280 									ui->padClip (row - r, step);
1281 									if (ui->clipBoard.data[r].size () > 1) ui->padClip (row - r, step + ui->clipBoard.data[r].size ());
1282 
1283 									for (int s = 0; s < int (ui->clipBoard.data[r].size ()); ++s)
1284 									{
1285 										if
1286 										(
1287 											(row - r >= 0) &&
1288 											(row - r < ROWS) &&
1289 											(step + s >= 0) &&
1290 											(step + s < int (ui->controllerWidgets[NR_OF_STEPS]->getValue ()))
1291 										)
1292 										{
1293 											ui->pattern.setPad (row - r, step + s, ui->clipBoard.data.at(r).at(s));
1294 											ui->drawPad (row - r, step + s);
1295 											ui->send_pad (row - r, step + s);
1296 										}
1297 									}
1298 								}
1299 							}
1300 						}
1301 					}
1302 				}
1303 
1304 				// Right button: copy pad to properties
1305 				else if ((pointerEvent->getButton() == BDevices::RIGHT_BUTTON) &&
1306 						 ((event->getEventType () == BEvents::BUTTON_PRESS_EVENT) ||
1307 						  (event->getEventType () == BEvents::POINTER_DRAG_EVENT)))
1308 				{
1309 					ui->controllerWidgets[SELECTION_CH]->setValue (int (pd.ch) & 0x0F);
1310 					ui->controllerWidgets[SELECTION_GATE_RAND]->setValue (pd.randGate);
1311 					ui->controllerWidgets[SELECTION_NOTE]->setValue (pd.pitchNote);
1312 					ui->controllerWidgets[SELECTION_OCTAVE]->setValue (pd.pitchOctave);
1313 					ui->controllerWidgets[SELECTION_VELOCITY]->setValue (pd.velocity);
1314 					ui->controllerWidgets[SELECTION_DURATION]->setValue (LIMIT (pd.duration, 0.0, 1.0));
1315 					ui->controllerWidgets[SELECTION_NOTE_RAND]->setValue (pd.randNote);
1316 					ui->controllerWidgets[SELECTION_OCTAVE_RAND]->setValue (pd.randOctave);
1317 					ui->controllerWidgets[SELECTION_VELOCITY_RAND]->setValue (pd.randVelocity);
1318 					ui->controllerWidgets[SELECTION_DURATION_RAND]->setValue (pd.randDuration);
1319 				}
1320 			}
1321 		}
1322 
1323 		else if ((event->getEventType () == BEvents::BUTTON_RELEASE_EVENT) && (pointerEvent->getButton() == BDevices::LEFT_BUTTON))
1324 		{
1325 			// Edit mode
1326 			if (ui->controllerWidgets[SELECTION_CH]->getValue() > NR_SEQUENCER_CHS + NR_CTRL_BUTTONS)
1327 			{
1328 				int edit = ((int)ui->controllerWidgets[SELECTION_CH]->getValue() - NR_SEQUENCER_CHS - NR_CTRL_BUTTONS) * 0x100;
1329 
1330 				if (edit == EDIT_MERGE)
1331 				{
1332 					int clipRMin = ui->clipBoard.origin.first;
1333 					int clipRMax = ui->clipBoard.origin.first + ui->clipBoard.extends.first;
1334 					if (clipRMin > clipRMax) std::swap (clipRMin, clipRMax);
1335 					int clipSMin = ui->clipBoard.origin.second;
1336 					int clipSMax = ui->clipBoard.origin.second + ui->clipBoard.extends.second;
1337 					if (clipSMin > clipSMax) std::swap (clipSMin, clipSMax);
1338 
1339 
1340 					// Split
1341 					if (ui->pattern.padGetSize (clipRMin, clipSMin) > 1)
1342 					{
1343 						// Find start
1344 						int start = clipSMin;
1345 						while (ui->pattern.padHasAntecessor (clipRMin, start)) --start;
1346 
1347 						// Find end
1348 						int end = clipSMax;
1349 						while (ui->pattern.padHasSuccessor (clipRMin, end)) ++end;
1350 
1351 						// Limit pad duration to 1.0 (= split)
1352 						for (int i = start; i <= end; ++i)
1353 						{
1354 							Pad pd = ui->pattern.getPad (clipRMin, i);
1355 							pd.duration = LIMIT (pd.duration, 0.0, 1.0);
1356 							ui->pattern.setPad (clipRMin, i, pd);
1357 							ui->send_pad (clipRMin, i);
1358 						}
1359 					}
1360 
1361 					// Merge
1362 					else
1363 					{
1364 						// Find start
1365 						int start = clipSMin;
1366 						while (ui->pattern.padHasAntecessor (clipRMin, start)) --start;
1367 
1368 						// Find end
1369 						int end = clipSMax;
1370 						while (ui->pattern.padHasSuccessor (clipRMin, end)) ++end;
1371 
1372 						Pad pd0 = ui->pattern.getPad (clipRMin, start);
1373 
1374 						// Count number of mergeable pads
1375 						int size = 0;
1376 						do ++size;
1377 						while
1378 						(
1379 							(start + size - 1 < end) &&
1380 							((int (pd0.ch) & 0x0F) == (int (ui->pattern.getPad (clipRMin, start + size).ch) & 0x0F)) &&
1381 							(ui->pattern.getPad (clipRMin, start + size).duration > 0.0)
1382 						);
1383 
1384 						// TODO Set control to first element
1385 						int ctrl = 0;
1386 						for (int i = 0; i < size; ++i)
1387 						{
1388 							Pad pdi = ui->pattern.getPad (clipRMin, start + i);
1389 							if (int (pdi.ch) & 0xF0)
1390 							{
1391 								ctrl = int (pdi.ch) & 0xF0;
1392 								break;
1393 							}
1394 						}
1395 						pd0.ch = int (pd0.ch) | ctrl;
1396 
1397 						pd0.duration = ui->pattern.getPad (clipRMin, start + size - 1).duration + size - 1;
1398 
1399 						for (int i = 0; i < size; ++i)
1400 						{
1401 							ui->pattern.setPad (clipRMin, start + i, pd0);
1402 							ui->send_pad (clipRMin, start + i);
1403 							--pd0.duration;
1404 							if (i == 0) pd0.ch = int (pd0.ch) & 0x0F;
1405 						}
1406 					}
1407 
1408 					ui->clipBoard.ready = true;
1409 					ui->drawPad();
1410 					ui->pattern.store ();
1411 				}
1412 
1413 				else if ((edit == EDIT_CUT) || (edit == EDIT_COPY) || (edit == EDIT_FLIPX) || (edit == EDIT_FLIPY))
1414 				{
1415 					int clipRMin = ui->clipBoard.origin.first;
1416 					int clipRMax = ui->clipBoard.origin.first + ui->clipBoard.extends.first;
1417 					if (clipRMin > clipRMax) std::swap (clipRMin, clipRMax);
1418 					int clipSMin = ui->clipBoard.origin.second;
1419 					int clipSMax = ui->clipBoard.origin.second + ui->clipBoard.extends.second;
1420 					if (clipSMin > clipSMax) std::swap (clipSMin, clipSMax);
1421 
1422 					// Clip merged Pads
1423 					if ((edit == EDIT_CUT) || (edit == EDIT_FLIPX) || (edit == EDIT_FLIPY))
1424 					{
1425 						for (int r = clipRMin; r < clipRMax; ++r)
1426 						{
1427 							ui->padClip (r, clipSMin);
1428 							if (clipSMin != clipSMax) ui->padClip (r, clipSMax + 1);
1429 						}
1430 					}
1431 
1432 					// Copy to clipboard
1433 					ui->clipBoard.data.clear ();
1434 					for (int r = clipRMax; r >= clipRMin; --r)
1435 					{
1436 						std::vector<Pad> padRow;
1437 						padRow.clear ();
1438 						for (int s = clipSMin; s <= clipSMax; ++s)
1439 						{
1440 							Pad pd = ui->pattern.getPad (r, s);
1441 
1442 							// Clip oversized merged pads
1443 							if (float(s) + pd.duration > float(clipSMax + 1.0f)) pd.duration = clipSMax + 1 - s;
1444 
1445 							padRow.push_back (pd);
1446 						}
1447 						ui->clipBoard.data.push_back (padRow);
1448 					}
1449 
1450 					if (edit == EDIT_FLIPX)
1451 					{
1452 						for (int dr = 0; dr < int (ui->clipBoard.data.size()); ++dr)
1453 						{
1454 							for (int ds = 0; ds < int (ui->clipBoard.data[dr].size());)
1455 							{
1456 								Pad pd = ui->clipBoard.data.at (dr).at (ds);
1457 								int pdsize = (pd.duration <= 0.0f ? 1.0 : int (ceil (pd.duration)));
1458 
1459 								for (int dds = 0; dds < pdsize; ++dds)
1460 								{
1461 									Pad pds = ui->clipBoard.data.at (dr).at (ds + dds);
1462 									ui->pattern.setPad (clipRMax - dr, clipSMax + 1 + dds - ds - pdsize, pds);
1463 									ui->send_pad (clipRMax - dr, clipSMax + 1 + dds - ds - pdsize);
1464 								}
1465 
1466 								ds += pdsize;
1467 							}
1468 						}
1469 						ui->pattern.store ();
1470 					}
1471 
1472 					else if (edit == EDIT_FLIPY)
1473 					{
1474 						for (int dr = 0; dr < int (ui->clipBoard.data.size()); ++dr)
1475 						{
1476 							for (int ds = 0; ds < int (ui->clipBoard.data[dr].size()); ++ds)
1477 							{
1478 								Pad pd = ui->clipBoard.data.at (dr).at (ds);
1479 								ui->pattern.setPad (clipRMin + dr, clipSMin + ds, pd);
1480 								ui->send_pad (clipRMin + dr, clipSMin + ds);
1481 							}
1482 						}
1483 						ui->pattern.store ();
1484 					}
1485 
1486 					// Cut: Clear pads
1487 					else if (edit == EDIT_CUT)
1488 					{
1489 						for (int r = clipRMax; r >= clipRMin; --r)
1490 						{
1491 							for (int s = clipSMin; s <= clipSMax; ++s)
1492 							{
1493 								ui->pattern.setPad (r, s, Pad ());
1494 								ui->send_pad (r, s);
1495 							}
1496 						}
1497 						ui->pattern.store ();
1498 					}
1499 
1500 					ui->clipBoard.ready = true;
1501 					ui->drawPad ();
1502 				}
1503 			}
1504 
1505 			// On BUTTON_RELEASE_EVENT and temporary delete mode: switch back
1506 			else if (ui->tempTool)
1507 			{
1508 				ui->tempTool = false;
1509 				ui->controllerWidgets[SELECTION_CH]->setValue (ui->tempToolCh);
1510 			}
1511 
1512 			else ui->pattern.store ();
1513 		}
1514 	}
1515 }
1516 
padsScrolledCallback(BEvents::Event * event)1517 void BSEQuencer_GUI::padsScrolledCallback (BEvents::Event* event)
1518 {
1519 	if ((event) && (event->getWidget ()) && (event->getWidget()->getMainWindow()) &&
1520 		((event->getEventType () == BEvents::WHEEL_SCROLL_EVENT)))
1521 	{
1522 		BWidgets::DrawingSurface* widget = (BWidgets::DrawingSurface*) event->getWidget ();
1523 		BSEQuencer_GUI* ui = (BSEQuencer_GUI*) widget->getMainWindow();
1524 		BEvents::WheelEvent* wheelEvent = (BEvents::WheelEvent*) event;
1525 
1526 		// Get size of drawing area
1527 		const double width = ui->padSurface.getEffectiveWidth ();
1528 		const double height = ui->padSurface.getEffectiveHeight ();
1529 
1530 		int row = (ROWS - 1) - ((int) ((wheelEvent->getPosition ().y - widget->getYOffset()) / (height / ROWS)));
1531 		int step = (wheelEvent->getPosition ().x - widget->getXOffset()) / (width / ui->controllerWidgets[NR_OF_STEPS]->getValue ());
1532 
1533 		if ((row >= 0) && (row < ROWS) && (step >= 0) && (step < ((int)ui->controllerWidgets[NR_OF_STEPS]->getValue ())))
1534 		{
1535 			while (ui->pattern.padHasAntecessor (row, step)) --step;
1536 			Pad pd = ui->pattern.getPad (row, step);
1537 			if (int (pd.ch) & 0x0F)
1538 			{
1539 				// SHIFT: Change duration
1540 				if (ui->modifier == MODIFIER_DURATION)
1541 				{
1542 					float d = pd.duration * (1.0f + 0.01f * wheelEvent->getDelta().y);
1543 					if ((d >= 0.0f) && (d <= int (ui->controllerWidgets[NR_OF_STEPS]->getValue() - step)))
1544 					{
1545 
1546 						// Delete last step of pad shinked
1547 						if (int (ceil(d)) < int (ceil (pd.duration)))
1548 						{
1549 							Pad pds = ui->pattern.getPad (row, step + int (pd.duration));
1550 							pds.ch = int (pds.ch) & 0xF0;
1551 							pds.duration = 0.0f;
1552 							ui->pattern.setPad (row, step + int (pd.duration), pds);
1553 							ui->drawPad (row, step + int (pd.duration));
1554 							ui->send_pad (row, step + int (pd.duration));
1555 						}
1556 
1557 						// Change duration
1558 						do
1559 						{
1560 							pd.duration = d;
1561 							ui->pattern.setPad (row, step, pd);
1562 							ui->drawPad (row, step);
1563 							ui->send_pad (row, step);
1564 							pd.ch = int (pd.ch) & 0x0F;
1565 							++step;
1566 							--d;
1567 						} while (d > 0.0f);
1568 					}
1569 				}
1570 
1571 				// CTRL: Change pitch octave
1572 				else if (ui->modifier == MODIFIER_OCTAVE)
1573 				{
1574 					int o = pd.pitchOctave + wheelEvent->getDelta().y;
1575 					o = LIMIT (o, -8, 8);
1576 					do
1577 					{
1578 						Pad pds = ui->pattern.getPad (row, step);
1579 						pds.pitchOctave = o;
1580 						ui->pattern.setPad (row, step, pds);
1581 						ui->drawPad (row, step);
1582 						ui->send_pad (row, step);
1583 						++step;
1584 					} while (ui->pattern.padHasSuccessor (row, step - 1));
1585 				}
1586 
1587 				// Otherwise: Change velocity
1588 				else
1589 				{
1590 					float v = pd.velocity * (1.0f + 0.01f * wheelEvent->getDelta().y);
1591 					v = LIMIT (v, 0.0f, 2.0f);
1592 					do
1593 					{
1594 						Pad pds = ui->pattern.getPad (row, step);
1595 						pds.velocity = v;
1596 						ui->pattern.setPad (row, step, pds);
1597 						ui->drawPad (row, step);
1598 						ui->send_pad (row, step);
1599 						++step;
1600 					} while (ui->pattern.padHasSuccessor (row, step - 1));
1601 				}
1602 
1603 				ui->wheelScrolled = true;
1604 			}
1605 		}
1606 	}
1607 }
1608 
padsFocusedCallback(BEvents::Event * event)1609 void BSEQuencer_GUI::padsFocusedCallback (BEvents::Event* event)
1610 {
1611 	if ((event) && (event->getWidget ()) && (event->getWidget()->getMainWindow()))
1612 	{
1613 		BWidgets::DrawingSurface* widget = (BWidgets::DrawingSurface*) event->getWidget ();
1614 		BSEQuencer_GUI* ui = (BSEQuencer_GUI*) widget->getMainWindow();
1615 		BEvents::FocusEvent* focusEvent = (BEvents::FocusEvent*) event;
1616 
1617 		// Get size of drawing area
1618 		const double width = ui->padSurface.getEffectiveWidth ();
1619 		const double height = ui->padSurface.getEffectiveHeight ();
1620 
1621 		int row = (ROWS - 1) - ((int) ((focusEvent->getPosition ().y - widget->getYOffset()) / (height / ROWS)));
1622 		int step = (focusEvent->getPosition ().x - widget->getXOffset()) / (width / ui->controllerWidgets[NR_OF_STEPS]->getValue ());
1623 		while (ui->pattern.padHasAntecessor (row, step)) --step;
1624 
1625 		if ((row >= 0) && (row < ROWS) && (step >= 0) && (step < ((int)ui->controllerWidgets[NR_OF_STEPS]->getValue ())))
1626 		{
1627 			Pad pd = ui->pattern.getPad (row, step);
1628 			double dm = fmod (pd.duration, 1.0);
1629 			if (dm == 0.0) dm = 1.0;
1630 			double rd = LIMIT (pd.randDuration, - dm, 0.0);
1631 			double pdr = pd.duration * rd / dm;
1632 			ui->padSurface.focusText.setText
1633 			(
1634 				BSEQUENCER_LABEL_CHANNEL ": " + std::to_string (int (pd.ch) & 0x0f) + "\n" +
1635 				BSEQUENCER_LABEL_GATE ": " + BUtilities::to_string (pd.randGate, "%1.2f") + "\n" +
1636 				BSEQUENCER_LABEL_NOTE ": " + std::to_string (int (pd.pitchNote)) + " ± " + std::to_string (int (abs (pd.randNote))) + "\n" +
1637 				BSEQUENCER_LABEL_OCTAVE ": " + std::to_string (int (pd.pitchOctave)) + " ± " + std::to_string (int (abs (pd.randOctave))) + "\n" +
1638 				BSEQUENCER_LABEL_VELOCITY ": " + BUtilities::to_string (pd.velocity, "%1.2f") + " ± " + BUtilities::to_string (abs (pd.randVelocity), "%1.2f") + "\n" +
1639 				BSEQUENCER_LABEL_DURATION ": " + BUtilities::to_string (pd.duration, "%1.2f") + " (" + BUtilities::to_string (pdr, "%1.2f") + ")");
1640 			ui->scaleFocus ();
1641 		}
1642 	}
1643 
1644 	if (event->getEventType () == BEvents::FOCUS_IN_EVENT) focusInCallback (event);
1645 	else if (event->getEventType () == BEvents::FOCUS_OUT_EVENT) focusOutCallback (event);
1646 }
1647 
drawCaption()1648 void BSEQuencer_GUI::drawCaption ()
1649 {
1650 	cairo_surface_t* surface = captionSurface.getDrawingSurface();
1651 	cairo_surface_clear (surface);
1652 	const double width = captionSurface.getEffectiveWidth ();
1653 	const double height = captionSurface.getEffectiveHeight ();
1654 	cairo_t* cr = cairo_create (surface);
1655 	BColors::Color textcolor = *txColors. getColor(BColors::ACTIVE);
1656 	cairo_set_source_rgba (cr, CAIRO_RGBA (textcolor));
1657 	cairo_select_font_face (cr, ctLabelFont.getFontFamily ().c_str (), ctLabelFont.getFontSlant (), ctLabelFont.getFontWeight ());
1658 
1659 	int scaleNr = controllers[SCALE];
1660 	BScale scale
1661 	(
1662 		((int)(controllers[ROOT] + controllers[SIGNATURE] + 12)) % 12,
1663 		(SignatureIndex) controllers[SIGNATURE],
1664 		scaleMaps[scaleNr].scaleNotes
1665 	);
1666 	std::string label = "";
1667 
1668 	for (int i = 0; i < ROWS; ++i)
1669 	{
1670 		BColors::Color color = BColors::invisible;
1671 		if (noteBits & (1 << i)) {color = ink; color.applyBrightness (0.75);}
1672 		drawButton (surface, 0, (ROWS - i - 1) * height / ROWS + 1, width, height / ROWS - 2, color, NO_CTRL);
1673 
1674 		ScaleMap* map = &(scaleMaps[scaleNr]);
1675 
1676 		if (map->altSymbols[i] != "") label = map->altSymbols[i];
1677 		else
1678 		{
1679 			int element = map->elements[i];
1680 			label = scale.getSymbol (element);
1681 			int note = scale.getMIDInote (element);
1682 			if (note != ENOTE)
1683 			{
1684 				if (note >= 12) label += " +" + std::to_string ((int) (note / 12));
1685 			}
1686 			else label = "ERR";
1687 		}
1688 
1689 		cairo_text_extents_t ext;
1690 
1691 		double fontsize = ctLabelFont.getFontSize () * sqrt (2);
1692 		do
1693 		{
1694 			fontsize = fontsize / sqrt (2);
1695 			cairo_set_font_size (cr, fontsize);
1696 			cairo_text_extents (cr, label.c_str(), &ext);
1697 		} while ((ext.width > width) && (fontsize >= ctLabelFont.getFontSize () * 0.5));
1698 
1699 		cairo_move_to (cr, width / 2 - ext.width / 2, (15.5 - i) * height / 16 + ext.height / 2);
1700 		cairo_show_text (cr, label.c_str());
1701 	}
1702 
1703 	cairo_destroy (cr);
1704 	captionSurface.update ();
1705 }
1706 
drawPad()1707 void BSEQuencer_GUI::drawPad ()
1708 {
1709 	cairo_surface_t* surface = padSurface.getDrawingSurface();
1710 	cairo_t* cr = cairo_create (surface);
1711 	for (int row = 0; row < ROWS; ++row)
1712 	{
1713 		for (int step = 0; step < ((int)controllerWidgets[NR_OF_STEPS]->getValue ()); ++step)
1714 		{
1715 			drawPad (cr, row, step);
1716 			while (pattern.padHasSuccessor (row, step)) ++step;
1717 		}
1718 	}
1719 	cairo_destroy (cr);
1720 	padSurface.update();
1721 }
1722 
drawPad(const int row,const int step)1723 void BSEQuencer_GUI::drawPad (const int row, const int step)
1724 {
1725 	cairo_surface_t* surface = padSurface.getDrawingSurface();
1726 	cairo_t* cr = cairo_create (surface);
1727 	drawPad (cr, row, step);
1728 	cairo_destroy (cr);
1729 	padSurface.update();
1730 }
1731 
drawPad(cairo_t * cr,const int row,const int step)1732 void BSEQuencer_GUI::drawPad (cairo_t* cr, const int row, const int step)
1733 {
1734 	int start = step;
1735 	while (pattern.padHasAntecessor (row, start)) --start;
1736 
1737 	Pad pd = pattern.getPad (row, start);
1738 
1739 	if ((!cr) || (cairo_status (cr) != CAIRO_STATUS_SUCCESS) || (row < 0) || (row >= ROWS) || (start < 0) ||
1740 		(start >= (int (controllerWidgets[NR_OF_STEPS]->getValue ())))) return;
1741 
1742 	// Get size of drawing area
1743 	const double width = padSurface.getEffectiveWidth ();
1744 	const double height = padSurface.getEffectiveHeight ();
1745 	const double w = width / controllerWidgets[NR_OF_STEPS]->getValue ();
1746 	const double h = height / ROWS;
1747 	const double x = start * w;
1748 	const double y = (ROWS - row - 1) * h;
1749 	const double xr = round (x);
1750 	const double yr = round (y);
1751 	int ps = pattern.padGetSize (row, start);
1752 	if (start + ps > int (controllerWidgets[NR_OF_STEPS]->getValue ())) ps = int (controllerWidgets[NR_OF_STEPS]->getValue ()) - start;
1753 	const double wr = round (x + w * ps) - xr;
1754 	const double hr = round (y + h) - yr;
1755 
1756 
1757 	// Odd or even?
1758 	BColors::Color bg = ((int (start / controllerWidgets[STEPS_PER]->getValue ())) % 2) ? oddPadBgColor : evenPadBgColor;
1759 
1760 	// Highlight selection
1761 	int clipRMin = clipBoard.origin.first;
1762 	int clipRMax = clipBoard.origin.first + clipBoard.extends.first;
1763 	if (clipRMin > clipRMax) std::swap (clipRMin, clipRMax);
1764 	int clipSMin = clipBoard.origin.second;
1765 	int clipSMax = clipBoard.origin.second + clipBoard.extends.second;
1766 	if (clipSMin > clipSMax) std::swap (clipSMin, clipSMax);
1767 
1768 	// Draw backgroung
1769 	int i = 0;
1770 	do
1771 	{
1772 		if ((start + i >= 0) && (start + i < int (controllerWidgets[NR_OF_STEPS]->getValue ())))
1773 		{
1774 			BColors::Color bgi = bg;
1775 			double xi = round (x + i * w);
1776 			double wi = round (x + (i + 1) * w) - xi;
1777 			if ((!clipBoard.ready) && (row >= clipRMin) && (row <= clipRMax) && (start + i >= clipSMin) && (start + i <= clipSMax)) bgi.applyBrightness (1.5);
1778 			cairo_set_source_rgba (cr, CAIRO_RGBA (bgi));
1779 			cairo_set_line_width (cr, 0.0);
1780 			cairo_rectangle (cr, xi, yr, wi, hr);
1781 			cairo_fill (cr);
1782 		}
1783 		++i;
1784 	} while (pattern.padHasSuccessor (row, start + i - 1));
1785 
1786 	// Draw pad
1787 	int ch = padGetChannel (row, start);
1788 	int ctrl = padGetControl (row, start);
1789 	double vel = (pd.velocity <= 1 ?  pd.velocity - 1 : (pd.velocity - 1) * 0.5);
1790 	int oct = pd.pitchOctave;
1791 
1792 	if ((ch >= 0) && (ch <= NR_SEQUENCER_CHS) && (ctrl >= 0) && (ctrl < NR_CTRL_BUTTONS))
1793 	{
1794 		BColors::Color color = chButtonStyles[ch].color;
1795 		if (ch > 0) color.applyBrightness(vel);
1796 		int i = 0;
1797 		do
1798 		{
1799 			if (cursorBits[start + i] & (1 << row))
1800 			{
1801 				color.setAlpha (1.0);
1802 				color.applyBrightness (0.75);
1803 				break;
1804 			}
1805 			++i;
1806 		}
1807 		while (pattern.padHasSuccessor (row, start + i - 1));
1808 
1809 		int symbol = ctrlButtonStyles[ctrl].symbol;
1810 
1811 		drawButton (cr, xr + 1, yr + 1, wr - 2, hr - 2, color, symbol, (pd.duration == 0 ? 1 : pd.duration / ceil (pd.duration)));
1812 
1813 		// Displays pitch octave
1814 		if ((pd.duration > 0.0) && (pd.velocity > 0.0))
1815 		{
1816 			cairo_surface_t* surface = padSurface.getDrawingSurface();
1817 			cairo_t* cr = cairo_create (surface);
1818 			double h = 0.75 * lfLabelFont.getFontSize ();
1819 
1820 			if (pd.pitchNote > 0)
1821 			{
1822 				cairo_move_to (cr, xr + 1.0 + 3.0 * sz, yr + 1.0 + 2.0 * sz + h);
1823 				cairo_line_to (cr, xr + 1.0 + 3.0 * sz, yr + 1.0 + 2.0 * sz + (1.0 - pd.pitchNote / 16.0) * h);
1824 				cairo_line_to (cr, xr + 1.0 + 2.0 * sz, yr + 1.0 + 3.0 * sz + (1.0 - pd.pitchNote / 16.0) * h);
1825 				cairo_move_to (cr, xr + 1.0 + 3.0 * sz, yr + 1.0 + 2.0 * sz + (1.0 - pd.pitchNote / 16.0) * h);
1826 				cairo_line_to (cr, xr + 1.0 + 4.0 * sz, yr + 1.0 + 3.0 * sz + (1.0 - pd.pitchNote / 16.0) * h);
1827 				cairo_set_line_width (cr, 1.0);
1828 				if ((ch == 4) && (pd.velocity > 0.5)) cairo_set_source_rgba (cr, CAIRO_RGBA (BColors::black));
1829 				else cairo_set_source_rgba (cr, CAIRO_RGBA (BColors::white));
1830 				cairo_stroke (cr);
1831 			}
1832 
1833 			if (pd.pitchNote < 0)
1834 			{
1835 				cairo_move_to (cr, xr + 1.0 + 3.0 * sz, yr + 1.0 + 2.0 * sz);
1836 				cairo_line_to (cr, xr + 1.0 + 3.0 * sz, yr + 1.0 + 2.0 * sz + (-pd.pitchNote / 16.0) * h);
1837 				cairo_line_to (cr, xr + 1.0 + 2.0 * sz, yr + 1.0 + 1.0 * sz + (-pd.pitchNote / 16.0) * h);
1838 				cairo_move_to (cr, xr + 1.0 + 3.0 * sz, yr + 1.0 + 2.0 * sz + (-pd.pitchNote / 16.0) * h);
1839 				cairo_line_to (cr, xr + 1.0 + 4.0 * sz, yr + 1.0 + 1.0 * sz + (-pd.pitchNote / 16.0) * h);
1840 				cairo_set_line_width (cr, 1.0);
1841 				if ((ch == 4) && (pd.velocity > 0.5)) cairo_set_source_rgba (cr, CAIRO_RGBA (BColors::black));
1842 				else cairo_set_source_rgba (cr, CAIRO_RGBA (BColors::white));
1843 				cairo_stroke (cr);
1844 			}
1845 
1846 			if (oct != 0)
1847 			{
1848 				std::string valstr = (oct <= 0 ? "" : "+") + std::to_string (oct);
1849 				if ((ch == 4) && (pd.velocity > 0.5)) cairo_set_source_rgba (cr, CAIRO_RGBA (BColors::black));
1850 				else cairo_set_source_rgba (cr, CAIRO_RGBA (BColors::white));
1851 				cairo_select_font_face (cr, lfLabelFont.getFontFamily ().c_str (), lfLabelFont.getFontSlant (), lfLabelFont.getFontWeight ());
1852 				cairo_set_font_size (cr, h);
1853 				cairo_move_to (cr, xr + 1.0 + 6.0 * sz, yr + 1.0 + 2.0 * sz + h);
1854 				cairo_show_text (cr, valstr.c_str ());
1855 			}
1856 
1857 			cairo_destroy (cr);
1858 		}
1859 	}
1860 }
1861 
padIsSelected(const int row,const int step)1862 bool BSEQuencer_GUI::padIsSelected (const int row, const int step)
1863 {
1864 	int s = step;
1865 	while (pattern.padHasAntecessor (row, step)) --s;
1866 
1867 	int clipRMin = clipBoard.origin.first;
1868 	int clipRMax = clipBoard.origin.first + clipBoard.extends.first;
1869 	if (clipRMin > clipRMax) std::swap (clipRMin, clipRMax);
1870 	int clipSMin = clipBoard.origin.second;
1871 	int clipSMax = clipBoard.origin.second + clipBoard.extends.second;
1872 	if (clipSMin > clipSMax) std::swap (clipSMin, clipSMax);
1873 	if (clipBoard.ready || (row < clipRMin) || (row > clipRMax)) return false;
1874 
1875 	do
1876 	{
1877 		if ((s >= clipSMin) && (s <= clipSMax)) return true;
1878 		++s;
1879 	} while (pattern.padHasSuccessor (row, s - 1));
1880 
1881 	return false;
1882 }
1883 
padGetChannel(const int row,const int step)1884 int BSEQuencer_GUI::padGetChannel (const int row, const int step)
1885 {
1886 	int start = step;
1887 	while (pattern.padHasAntecessor (row, start)) --start;
1888 	return int (pattern.getPad (row, start).ch) & 0x0F;
1889 }
1890 
padGetControl(const int row,const int step)1891 int BSEQuencer_GUI::padGetControl (const int row, const int step)
1892 {
1893 	int start = step;
1894 	while (pattern.padHasAntecessor (row, start)) --start;
1895 	return (int (pattern.getPad (row, start).ch) & 0xF0) / 0x10;
1896 }
1897 
padClip(const int row,const int step)1898 void BSEQuencer_GUI::padClip (const int row, const int step)
1899 {
1900 	if (pattern.padHasAntecessor (row, step))
1901 	{
1902 		int start = step - 1;
1903 		while (pattern.padHasAntecessor (row, start)) --start;
1904 		for (int s = start; s < step; ++s)
1905 		{
1906 			Pad pd = pattern.getPad (row, s);
1907 			pd.duration = step - s;
1908 			pattern.setPad (row, s, pd);
1909 			send_pad (row, s);
1910 		}
1911 		drawPad (row, start);
1912 		drawPad (row, step);
1913 	}
1914 }
1915 
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)1916 static LV2UI_Handle instantiate (const LV2UI_Descriptor *descriptor,
1917 						  const char *plugin_uri,
1918 						  const char *bundle_path,
1919 						  LV2UI_Write_Function write_function,
1920 						  LV2UI_Controller controller,
1921 						  LV2UI_Widget *widget,
1922 						  const LV2_Feature *const *features)
1923 {
1924 	PuglNativeView parentWindow = 0;
1925 	LV2UI_Resize* resize = NULL;
1926 
1927 	if (strcmp(plugin_uri, BSEQUENCER_URI) != 0)
1928 	{
1929 		std::cerr << "BSEQuencer.lv2#GUI: GUI does not support plugin with URI " << plugin_uri << std::endl;
1930 		return NULL;
1931 	}
1932 
1933 	for (int i = 0; features[i]; ++i)
1934 	{
1935 		if (!strcmp(features[i]->URI, LV2_UI__parent)) parentWindow = (PuglNativeView) features[i]->data;
1936 		else if (!strcmp(features[i]->URI, LV2_UI__resize)) resize = (LV2UI_Resize*)features[i]->data;
1937 	}
1938 	if (parentWindow == 0) std::cerr << "BSEQuencer.lv2#GUI: No parent window.\n";
1939 
1940 	// New instance
1941 	BSEQuencer_GUI* ui;
1942 	try {ui = new BSEQuencer_GUI (bundle_path, features, parentWindow);}
1943 	catch (std::exception& exc)
1944 	{
1945 		std::cerr << "BSEQuencer.lv2#GUI: Instantiation failed. " << exc.what () << std::endl;
1946 		return NULL;
1947 	}
1948 
1949 	ui->controller = controller;
1950 	ui->write_function = write_function;
1951 
1952 	// Reduce min GUI size for small displays
1953 	double sz = 1.0;
1954 	int screenWidth  = getScreenWidth ();
1955 	int screenHeight = getScreenHeight ();
1956 	if ((screenWidth < 870) || (screenHeight < 580)) sz = 0.5;
1957 	else if ((screenWidth < 1290) || (screenHeight < 860)) sz = 0.66;
1958 
1959 	/*
1960 	std::cerr << "B.SEQuencer_GUI.lv2 screen size " << screenWidth << " x " << screenHeight <<
1961 			". Set GUI size to " << 1250 * sz << " x " << 820 * sz << ".\n";
1962 	*/
1963 
1964 	if (resize) resize->ui_resize(resize->handle, 1250 * sz, 820 * sz);
1965 
1966 	*widget = (LV2UI_Widget) puglGetNativeWindow (ui->getPuglView ());
1967 
1968 	ui->send_ui_on();
1969 
1970 	return (LV2UI_Handle) ui;
1971 }
1972 
cleanup(LV2UI_Handle ui)1973 static void cleanup(LV2UI_Handle ui)
1974 {
1975 	BSEQuencer_GUI* self = (BSEQuencer_GUI*) ui;
1976 	if (self) delete self;
1977 }
1978 
port_event(LV2UI_Handle ui,uint32_t port_index,uint32_t buffer_size,uint32_t format,const void * buffer)1979 static void port_event(LV2UI_Handle ui, uint32_t port_index, uint32_t buffer_size,
1980 	uint32_t format, const void* buffer)
1981 {
1982 	BSEQuencer_GUI* self = (BSEQuencer_GUI*) ui;
1983 	if (self) self->port_event(port_index, buffer_size, format, buffer);
1984 }
1985 
call_idle(LV2UI_Handle ui)1986 static int call_idle (LV2UI_Handle ui)
1987 {
1988 	BSEQuencer_GUI* self = (BSEQuencer_GUI*) ui;
1989 	if (self) self->handleEvents ();
1990 	return 0;
1991 }
1992 
call_resize(LV2UI_Handle ui,int width,int height)1993 static int call_resize (LV2UI_Handle ui, int width, int height)
1994 {
1995 	BSEQuencer_GUI* self = (BSEQuencer_GUI*) ui;
1996 	if (!self) return 0;
1997 
1998 	BEvents::ExposeEvent* ev = new BEvents::ExposeEvent (self, self, BEvents::CONFIGURE_REQUEST_EVENT, self->getPosition().x, self->getPosition().y, width, height);
1999 	self->addEventToQueue (ev);
2000 	return 0;
2001 }
2002 
2003 static const LV2UI_Idle_Interface idle = {call_idle};
2004 static const LV2UI_Resize resize = {nullptr, call_resize};
2005 
extension_data(const char * uri)2006 static const void* extension_data(const char* uri)
2007 {
2008 	if (!strcmp(uri, LV2_UI__idleInterface)) return &idle;
2009 	else if(!strcmp(uri, LV2_UI__resize)) return &resize;
2010 	else return NULL;
2011 }
2012 
2013 static const LV2UI_Descriptor guiDescriptor = {
2014 		BSEQUENCER_GUI_URI,
2015 		instantiate,
2016 		cleanup,
2017 		port_event,
2018 		extension_data
2019 };
2020 
2021 // LV2 Symbol Export
lv2ui_descriptor(uint32_t index)2022 LV2_SYMBOL_EXPORT const LV2UI_Descriptor *lv2ui_descriptor(uint32_t index)
2023 {
2024 	switch (index) {
2025 	case 0: return &guiDescriptor;
2026 	default:return NULL;
2027     }
2028 }
2029 
2030 /* End of LV2 specific declarations
2031  *
2032  * *****************************************************************************
2033  *
2034  *
2035  */
2036