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 #ifndef SCALEEDITOR_HPP_
22 #define SCALEEDITOR_HPP_
23 
24 #include <utility>
25 #include "definitions.h"
26 #include "BScale.hpp"
27 #include "ScaleMap.hpp"
28 #include "BWidgets/Widget.hpp"
29 #include "BWidgets/ValueWidget.hpp"
30 #include "BWidgets/HPianoRoll.hpp"
31 #include "BWidgets/Label.hpp"
32 #include "BWidgets/PopupListBox.hpp"
33 #include "BWidgets/TextButton.hpp"
34 #include "BWidgets/ImageIcon.hpp"
35 
36 #ifdef LOCALEFILE
37 #include LOCALEFILE
38 #else
39 #include "Locale_EN.hpp"
40 #endif
41 
42 class ScaleEditor : public BWidgets::ValueWidget
43 {
44 public:
45 	ScaleEditor ();
46 	ScaleEditor (const double x, const double y, const double width, const double height,
47 		     const std::string& name, const std::string& pluginPath,
48 		     const int mapNr, const ScaleMap& scaleMap, const BScale& scale);
49 
50 	virtual void resize (double width, double height) override;
51 	void setScaleMap (const ScaleMap& scaleMap);
52 	ScaleMap getScaleMap () const;
53 	void setScale (const BScale& scale);
54 	BScale getScale () const;
55 	void setMapNr (const int nr);
56 	int getMapNr () const;
57 
58 protected:
59 	void szScaleEditor ();
60 	void updateAltSymbol (int nr);
61 	static void symbolDragCallback (BEvents::Event* event);
62 	static void symbolListboxValueChangedCallback (BEvents::Event* event);
63 	static void noteListboxValueChangedCallback (BEvents::Event* event);
64 	static void buttonClickCallback (BEvents::Event* event);
65 	static void labelClickCallback (BEvents::Event* event);
66 	static void labelMessageCallback (BEvents::Event* event);
67 	static void pianoClickCallback (BEvents::Event* event);
68 
69 	BWidgets::Label nameLabel;
70 	BWidgets::Label scaleNameLabel;
71 	BWidgets::Label rowLabel;
72 	BWidgets::Label symbolLabel;
73 	BWidgets::Label noteLabel;
74 	BWidgets::Label altSymbolLabel;
75 	std::array<BWidgets::ImageIcon, ROWS> drumSymbol;
76 	std::array<BWidgets::ImageIcon, ROWS>noteSymbol;
77 	std::array<BWidgets::Label, ROWS> nrLabel;
78 	std::array<BWidgets::PopupListBox, ROWS> nrSymbolListbox;
79 	std::array<BWidgets::PopupListBox, ROWS> nrNoteListbox;
80 	std::array<BWidgets::Label, ROWS> nrNoteLabel;
81 	std::array<BWidgets::Label, ROWS> nrAltSymbolLabel;
82 	BWidgets::HPianoRoll piano;
83 	BWidgets::TextButton cancelButton;
84 	BWidgets::TextButton applyButton;
85 
86 	int pianoRoot;
87 	BScale scale;
88 	ScaleMap scaleMap;
89 	int mapNr;
90 
91 	double sz;
92 
93 	cairo_surface_t* bgSurface;
94 
95 	BColors::ColorSet txColors = {{{0.167, 0.37, 0.80, 1.0}, {0.33, 0.5, 0.85, 1.0}, {0.0, 0.0, 0.25, 1.0}, {0.0, 0.0, 0.0, 0.0}}};
96 	BColors::ColorSet bgColors = {{{0.15, 0.15, 0.15, 1.0}, {0.3, 0.3, 0.3, 1.0}, {0.05, 0.05, 0.05, 1.0}, {0.0, 0.0, 0.0, 1.0}}};
97 	BStyles::Border border = {{BColors::white, 1.0}, 0.0, 2.0, 0.0};
98 	BStyles::Border menuBorder = {{BColors::darkgrey, 1.0}, 0.0, 0.0, 0.0};
99 	BStyles::Border labelborder = {BStyles::noLine, 4.0, 0.0, 0.0};
100 	BStyles::Fill scaleEditorBg = BStyles::blackFill;
101 	BStyles::Fill menuBg = BStyles::Fill (BColors::Color (0.0, 0.0, 0.05, 1.0));
102 	BStyles::Font ctLabelFont = BStyles::Font ("Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL, 12.0,
103 						   BStyles::TEXT_ALIGN_CENTER, BStyles::TEXT_VALIGN_MIDDLE);
104 	BStyles::Font lfLabelFont = BStyles::Font ("Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL, 12.0,
105 						   BStyles::TEXT_ALIGN_LEFT, BStyles::TEXT_VALIGN_MIDDLE);
106 	BStyles::StyleSet defaultStyles = {"default", {{"background", STYLEPTR (&BStyles::noFill)},
107 					  {"border", STYLEPTR (&BStyles::noBorder)}}};
108 	BStyles::StyleSet labelStyles = {"labels", {{"background", STYLEPTR (&BStyles::noFill)},
109 						   {"border", STYLEPTR (&BStyles::noBorder)},
110 						   {"textcolors", STYLEPTR (&txColors)},
111 						   {"font", STYLEPTR (&ctLabelFont)}}};
112 
113 	BStyles::Theme theme = BStyles::Theme ({
114 		defaultStyles,
115 		{"scaleeditor", 	{{"background", STYLEPTR (&scaleEditorBg)},
116 					{"border", STYLEPTR (&border)}}},
117 		{"widget", 		{{"uses", STYLEPTR (&defaultStyles)}}},
118 		{"ctlabel",	 	{{"uses", STYLEPTR (&labelStyles)},
119 					 {"font", STYLEPTR (&ctLabelFont)}}},
120 		{"lflabel",	 	{{"uses", STYLEPTR (&labelStyles)},
121 					 {"font", STYLEPTR (&lfLabelFont)}}},
122 		{"button", 		{{"font", STYLEPTR (&ctLabelFont)}}},
123 		{"xsymbol",	 	{{"uses", STYLEPTR (&defaultStyles)},
124 					 {"fgcolors", STYLEPTR (&BColors::whites)},
125 					 {"font", STYLEPTR (&ctLabelFont)}}},
126 		{"menu",	 	{{"border", STYLEPTR (&menuBorder)},
127 					 {"background", STYLEPTR (&menuBg)}}},
128 		{"menu/item",	 	{{"uses", STYLEPTR (&defaultStyles)},
129 					 {"border", STYLEPTR (&labelborder)},
130 					 {"textcolors", STYLEPTR (&BColors::whites)},
131 					 {"font", STYLEPTR (&lfLabelFont)}}},
132 		{"menu/button",	 	{{"border", STYLEPTR (&menuBorder)},
133 					 {"background", STYLEPTR (&menuBg)},
134 					 {"bgcolors", STYLEPTR (&bgColors)}}},
135 		{"menu/listbox",	{{"border", STYLEPTR (&menuBorder)},
136 					 {"background", STYLEPTR (&menuBg)}}},
137 		{"menu/listbox/item",	{{"uses", STYLEPTR (&defaultStyles)},
138 					 {"border", STYLEPTR (&labelborder)},
139 					 {"textcolors", STYLEPTR (&BColors::whites)},
140 					 {"font", STYLEPTR (&lfLabelFont)}}},
141 		{"menu/listbox//button",{{"border", STYLEPTR (&menuBorder)},
142 					 {"background", STYLEPTR (&menuBg)},
143 					 {"bgcolors", STYLEPTR (&bgColors)}}}
144 	});
145 
146 };
147 
ScaleEditor()148 ScaleEditor::ScaleEditor() : ScaleEditor (0, 0, 0, 0, "editor", "", 0, ScaleMap (), BScale (0, defaultScale)) {}
149 
ScaleEditor(const double x,const double y,const double width,const double height,const std::string & name,const std::string & pluginPath,const int mapNr,const ScaleMap & scaleMap,const BScale & scale)150 ScaleEditor::ScaleEditor(const double x, const double y, const double width, const double height, const std::string& name,
151 						 const std::string& pluginPath, const int mapNr, const ScaleMap& scaleMap, const BScale& scale) :
152 		BWidgets::ValueWidget (x, y, width, height, name, 0.0),
153 		nameLabel (20, 60, 80, 20, "lflabel", BSEQUENCER_LABEL_SCALE_NAME ":"),
154 		scaleNameLabel (120, 60, 320, 20, "lflabel", scaleMap.name),
155 		rowLabel (20, 100, 80, 20, "lflabel", BSEQUENCER_LABEL_ROW),
156 		symbolLabel (70, 100, 48, 20, "ctlabel", BSEQUENCER_LABEL_MODE),
157 		noteLabel (148, 100, 80, 20, "ctlabel", BSEQUENCER_LABEL_NOTE),
158 		altSymbolLabel (248, 100, 80, 20, "ctlabel", BSEQUENCER_LABEL_SYMBOL),
159 		piano (50, 620, 260, 60, "widget", 0, 35),
160 		cancelButton (60, 720, 60, 20, "button", BSEQUENCER_LABEL_CANCEL),
161 		applyButton (240, 720, 60, 20, "button", BSEQUENCER_LABEL_APPLY),
162 		pianoRoot (0), scale (scale), scaleMap (scaleMap), mapNr (mapNr),
163 		sz (width / 360 < height / 760 ? width / 360 : height / 760)
164 
165 
166 {
167 	setDraggable (true);
168 	setFocusable (true);	// Only to block underlying pads callback
169 	setStacking (BWidgets::STACKING_CATCH);
170 
171 	bgSurface = cairo_image_surface_create_from_png ((pluginPath + "ScaleEditor.png").c_str());
172 	scaleEditorBg.loadFillFromCairoSurface(bgSurface);
173 
174 	scaleNameLabel.setEditable (true);
175 	scaleNameLabel.setCallbackFunction(BEvents::BUTTON_PRESS_EVENT, labelClickCallback);
176 	scaleNameLabel.setCallbackFunction(BEvents::MESSAGE_EVENT, labelMessageCallback);
177 
178 	// Create note symbols (for nrListbox)
179 	BScale sc = BScale(0, {CROMATICSCALE});
180 	std::list<BItems::Item> noteNameItems;
181 	for (int i = 0; i < 120; ++i)
182 	{
183 		int octave = (i / 12) - 1;
184 		std::string strNote = std::to_string(i) + " (" + sc.getSymbol (i) + (octave != 0 ? std::to_string(octave) : "") + ")";
185 		noteNameItems.push_back (BItems::Item (i, strNote));
186 	}
187 
188 	// Init nr widgets
189 	for (int i = 0; i < ROWS; ++i)
190 	{
191 
192 		drumSymbol[i] = BWidgets::ImageIcon (0, 0, 48, 24, "widget", pluginPath + "DrumSymbol.png");
193 		drumSymbol[i].rename ("widget");
194 		drumSymbol[i].applyTheme (theme);
195 		noteSymbol[i] = BWidgets::ImageIcon (0, 0, 48, 24, "widget", pluginPath + "NoteSymbol.png");
196 		noteSymbol[i].rename ("widget");
197 		noteSymbol[i].applyTheme (theme);
198 
199 		BItems::ItemList il;
200 		il.push_back (BItems::Item (0, &noteSymbol[i]));
201 		il.push_back (BItems::Item (1, &drumSymbol[i]));
202 
203 		nrSymbolListbox[i] =  BWidgets::PopupListBox (60, 580 - i * 30, 68, 24, 68, 68, "menu", il, 0);
204 		nrSymbolListbox[i].setCallbackFunction(BEvents::VALUE_CHANGED_EVENT, symbolListboxValueChangedCallback);
205 		nrSymbolListbox[i].rename ("menu");
206 		nrSymbolListbox[i].applyTheme (theme);
207 		add (nrSymbolListbox[i]);
208 
209 		nrLabel[i] = BWidgets::Label (20, 580 - i * 30, 30, 24, "lflabel", std::to_string (i + 1));
210 		nrLabel[i].rename ("lflabel");
211 		nrLabel[i].applyTheme (theme);
212 		add (nrLabel[i]);
213 
214 		if (i >= 6) nrNoteListbox[i] = BWidgets::PopupListBox (148, 580 - i * 30, 80, 24, 80, 240, "menu", noteNameItems, 0);
215 		else nrNoteListbox[i] = BWidgets::PopupListBox (148, 580 - i * 30, 80, 24, 0, -240, 80, 240, "menu", noteNameItems, 0);
216 		nrNoteListbox[i].setCallbackFunction(BEvents::VALUE_CHANGED_EVENT, noteListboxValueChangedCallback);
217 		nrNoteListbox[i].rename ("menu");
218 		nrNoteListbox[i].applyTheme (theme);
219 		add (nrNoteListbox[i]);
220 
221 		nrNoteLabel[i] = BWidgets::Label (148, 580 - i * 30, 80, 24, "ctlabel", "(" BSEQUENCER_LABEL_USES_SCALE ")");
222 		nrNoteLabel[i].rename ("ctlabel");
223 		nrNoteLabel[i].applyTheme (theme);
224 		add (nrNoteLabel[i]);
225 		nrNoteLabel[i].hide ();
226 
227 		nrAltSymbolLabel[i] = BWidgets::Label (248, 580 - i * 30, 80, 24, "ctlabel", "");
228 		nrAltSymbolLabel[i].rename ("ctlabel");
229 		nrAltSymbolLabel[i].setEditable (true);
230 		nrAltSymbolLabel[i].setCallbackFunction(BEvents::BUTTON_PRESS_EVENT, labelClickCallback);
231 		nrAltSymbolLabel[i].setCallbackFunction(BEvents::MESSAGE_EVENT, labelMessageCallback);
232 		updateAltSymbol (i);
233 		nrAltSymbolLabel[i].applyTheme (theme);
234 		add (nrAltSymbolLabel[i]);
235 	}
236 
237 	cancelButton.setCallbackFunction(BEvents::BUTTON_CLICK_EVENT, buttonClickCallback);
238 	applyButton.setCallbackFunction(BEvents::BUTTON_CLICK_EVENT, buttonClickCallback);
239 
240 	piano.setKeysToggleable (true);
241 	piano.setCallbackFunction(BEvents::BUTTON_PRESS_EVENT, pianoClickCallback);
242 	piano.setCallbackFunction(BEvents::BUTTON_RELEASE_EVENT, pianoClickCallback);
243 	piano.setCallbackFunction(BEvents::POINTER_DRAG_EVENT, pianoClickCallback);
244 
245 	setScale (scale);
246 	setScaleMap (scaleMap);
247 
248 	nameLabel.applyTheme (theme);
249 	scaleNameLabel.applyTheme (theme);
250 	rowLabel.applyTheme (theme);
251 	symbolLabel.applyTheme (theme);
252 	noteLabel.applyTheme (theme);
253 	altSymbolLabel.applyTheme (theme);
254 	cancelButton.applyTheme (theme);
255 	applyButton.applyTheme (theme);
256 	piano.applyTheme (theme);
257 	applyTheme (theme);
258 
259 	add (nameLabel);
260 	add (scaleNameLabel);
261 	add (rowLabel);
262 	add (symbolLabel);
263 	add (noteLabel);
264 	add (altSymbolLabel);
265 	add (cancelButton);
266 	add (applyButton);
267 	add (piano);
268 
269 	if (sz != 1.0) szScaleEditor();
270 }
271 
resize(double width,double height)272 void ScaleEditor::resize (double width, double height)
273 {
274 	double wf = width / 360;
275 	double hf = height / 760;
276 	double newsz = (wf < hf ? wf : hf);
277 	if (sz != newsz)
278 	{
279 		sz = newsz;
280 		szScaleEditor ();
281 	}
282 }
283 
szScaleEditor()284 void ScaleEditor::szScaleEditor ()
285 {
286 	// Scale Fonts
287 	ctLabelFont.setFontSize (12 * sz);
288 	lfLabelFont.setFontSize (12 * sz);
289 
290 	// Scale background
291 	cairo_surface_t* surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 360 * sz, 760 * sz);
292 	cairo_t* cr = cairo_create (surface);
293 	cairo_scale (cr, sz, sz);
294 	cairo_set_source_surface(cr, bgSurface, 0, 0);
295 	cairo_paint(cr);
296 	scaleEditorBg.loadFillFromCairoSurface(surface);
297 	cairo_destroy (cr);
298 	cairo_surface_destroy (surface);
299 
300 	nameLabel.moveTo (20 * sz, 60 * sz); nameLabel.resize (80 * sz, 20 * sz);
301 	scaleNameLabel.moveTo (120 * sz, 60 * sz); scaleNameLabel.resize(320 * sz, 20 * sz);
302 	rowLabel.moveTo (20 * sz, 100 * sz); rowLabel.resize (80 * sz, 20 * sz);
303 	symbolLabel.moveTo (70 * sz, 100 * sz); symbolLabel.resize (48 * sz, 20 * sz);
304 	noteLabel.moveTo (148 * sz, 100 * sz); noteLabel.resize (80 * sz, 20 * sz);
305 	altSymbolLabel.moveTo (248 * sz, 100 * sz); altSymbolLabel.resize (80 * sz, 20 * sz);
306 	cancelButton.moveTo (60 * sz, 720 * sz); cancelButton.resize (60 * sz, 20 * sz);
307 	applyButton.moveTo (240 * sz, 720 * sz); applyButton.resize (60 * sz, 20 * sz);
308 	piano.moveTo (50 * sz, 620 * sz); piano.resize (260 * sz, 60 * sz);
309 
310 	for (int i = 0; i < ROWS; ++i)
311 	{
312 		nrLabel[i].moveTo (20 * sz, (580 - i * 30) * sz); nrLabel[i].resize (30 * sz, 24 * sz);
313 		nrSymbolListbox[i].moveTo (60 * sz, (580 - i * 30) * sz); nrSymbolListbox[i].resize (68 * sz, 24 * sz);
314 		nrSymbolListbox[i].resizeListBox (BUtilities::Point (68 * sz, 68 * sz));
315 
316 		nrNoteListbox[i].moveTo (148 * sz, (580 - i * 30) * sz ); nrNoteListbox[i].resize (80 * sz, 24 * sz);
317 		nrNoteListbox[i].resizeListBox (BUtilities::Point (80 * sz, 240 * sz));
318 		if (i < 6) nrNoteListbox[i].moveListBox(BUtilities::Point (0, -240 * sz));
319 		nrNoteListbox[i].resizeListBoxItems (BUtilities::Point (80 * sz, 24 * sz));
320 
321 		nrNoteLabel[i].moveTo (148 * sz, (580 - i * 30) * sz); nrNoteLabel[i].resize (80 * sz, 24 * sz);
322 		nrAltSymbolLabel[i].moveTo (248 * sz, (580 - i * 30) * sz); nrAltSymbolLabel[i].resize (80 * sz, 24 * sz);
323 	}
324 
325 	nameLabel.applyTheme (theme);
326 	scaleNameLabel.applyTheme (theme);
327 	rowLabel.applyTheme (theme);
328 	symbolLabel.applyTheme (theme);
329 	noteLabel.applyTheme (theme);
330 	altSymbolLabel.applyTheme (theme);
331 	cancelButton.applyTheme (theme);
332 	applyButton.applyTheme (theme);
333 	piano.applyTheme (theme);
334 
335 	for (int i = 0; i < ROWS; ++i)
336 	{
337 		nrLabel[i].applyTheme (theme);
338 		nrSymbolListbox[i].applyTheme (theme);
339 		nrNoteListbox[i].applyTheme (theme);
340 		nrNoteLabel[i].applyTheme (theme);
341 		nrAltSymbolLabel[i].applyTheme (theme);
342 	}
343 
344 	applyTheme (theme);
345 	Widget::resize (360 * sz, 760 * sz);
346 }
347 
setScaleMap(const ScaleMap & scaleMap)348 void ScaleEditor::setScaleMap (const ScaleMap& scaleMap)
349 {
350 	this->scaleMap = scaleMap;
351 
352 	scaleNameLabel.setText(scaleMap.name);
353 
354 	// Set nr widgets
355 	for (int i = 0; i < ROWS; ++i)
356 	{
357 		if (scaleMap.elements[i] & 0x0100)
358 		{
359 			nrSymbolListbox[i].setValue (1);
360 			nrNoteLabel[i].hide ();
361 			nrNoteListbox[i].setValue (scaleMap.elements[i] & 0x0FF);
362 			nrNoteListbox[i].show ();
363 		}
364 
365 		else
366 		{
367 			nrSymbolListbox[i].setValue (0);
368 			nrNoteListbox[i].hide ();
369 			nrNoteLabel[i].show ();
370 		}
371 
372 		updateAltSymbol (i);
373 	}
374 }
375 
getScaleMap() const376 ScaleMap ScaleEditor::getScaleMap () const {return scaleMap;}
377 
setScale(const BScale & scale)378 void ScaleEditor::setScale (const BScale& scale)
379 {
380 	this->scale = scale;
381 	for (int i = 0; i < ROWS; ++i) updateAltSymbol (i);
382 
383 	// Set piano
384 	int pianoRoot = this->scale.getRoot() % 12;
385 	if (pianoRoot < 6) pianoRoot +=12;
386 	std::vector<bool> acKeys;
387 	std::vector<bool> prKeys;
388 
389 	for (int i = 0; i < 36; ++i)
390 	{
391 		if ((i >= pianoRoot) && (i < pianoRoot + 12)) acKeys.push_back (true);
392 		else acKeys.push_back (false);
393 
394 		if (this->scale.getElement (i/* + this->scale.getRoot()*/) != ENOTE) prKeys.push_back (true);
395 		else prKeys.push_back (false);
396 	}
397 
398 	piano.activateKeys (acKeys);
399 	piano.pressKeys (prKeys);
400 }
401 
getScale() const402 BScale ScaleEditor::getScale () const {return scale;}
403 
setMapNr(const int nr)404 void ScaleEditor::setMapNr (const int nr) {mapNr = nr;}
405 
getMapNr() const406 int ScaleEditor::getMapNr () const {return mapNr;}
407 
updateAltSymbol(int nr)408 void ScaleEditor::updateAltSymbol (int nr)
409 {
410 	std::string symbol;
411 	if (scaleMap.altSymbols[nr] != "") symbol = scaleMap.altSymbols[nr];
412 	else if (!(scaleMap.elements[nr] & 0x0100)) symbol = scale.getSymbol (scaleMap.elements[nr]);
413 	nrAltSymbolLabel[nr].setText (symbol);
414 }
415 
symbolListboxValueChangedCallback(BEvents::Event * event)416 void ScaleEditor::symbolListboxValueChangedCallback (BEvents::Event* event)
417 {
418 	if ((!event) || (!event->getWidget()) || (!event->getWidget()->getParent())) return;
419 
420 	BWidgets::PopupListBox* listbox = (BWidgets::PopupListBox*)(event->getWidget());
421 	ScaleEditor* scaleEditor = (ScaleEditor*)(listbox->getParent());
422 	int nr = -1;
423 	int value = ((BEvents::ValueChangedEvent*)event)->getValue ();
424 
425 	for (int i = 0; i < ROWS; ++i)
426 	{
427 		if (listbox == &(scaleEditor->nrSymbolListbox[i]))
428 		{
429 			nr = i;
430 			break;
431 		}
432 	}
433 
434 	if (nr >= 0)
435 	{
436 		if (value)
437 		{
438 			scaleEditor->scaleMap.elements[nr] = ((int(scaleEditor->nrNoteListbox[nr].getValue())) | 0x0100);
439 			scaleEditor->nrNoteListbox[nr].show ();
440 			scaleEditor->nrNoteLabel[nr].hide ();
441 			BWidgets::Label* l = (BWidgets::Label*) scaleEditor->nrNoteListbox[nr].getItem()->getWidget ();
442 			if (l) scaleEditor->scaleMap.altSymbols[nr] = l->getText ();
443 			scaleEditor->updateAltSymbol (nr);
444 		}
445 
446 		else
447 		{
448 			scaleEditor->scaleMap.elements[nr] = 0;		// Will be substituted by auto numbering later
449 			scaleEditor->nrNoteListbox[nr].hide ();
450 			scaleEditor->nrNoteLabel[nr].show ();
451 		}
452 
453 		// Update auto numbering of scale map elements
454 		for (int i = 0, count = 0; i < ROWS; ++i)
455 		{
456 			if (!(scaleEditor->scaleMap.elements[i] & 0x0100))
457 			{
458 				scaleEditor->scaleMap.elements[i] = count;
459 				scaleEditor->scaleMap.altSymbols[i] = "";
460 				++count;
461 			}
462 			scaleEditor->updateAltSymbol (i);
463 		}
464 	}
465 }
466 
noteListboxValueChangedCallback(BEvents::Event * event)467 void ScaleEditor::noteListboxValueChangedCallback (BEvents::Event* event)
468 {
469 	if ((!event) || (!event->getWidget()) || (!event->getWidget()->getParent())) return;
470 
471 	BWidgets::PopupListBox* listbox = (BWidgets::PopupListBox*)(event->getWidget());
472 	ScaleEditor* scaleEditor = (ScaleEditor*)(listbox->getParent());
473 	int nr = -1;
474 
475 	for (int i = 0; i < ROWS; ++i)
476 	{
477 		if (listbox == &(scaleEditor->nrNoteListbox[i]))
478 		{
479 			nr = i;
480 			break;
481 		}
482 	}
483 
484 	if ((nr >=0 ) && (scaleEditor->scaleMap.elements[nr] & 0x0100))
485 	{
486 		scaleEditor->scaleMap.elements[nr] = (((int)(scaleEditor->nrNoteListbox[nr].getValue())) | 0x0100);
487 		BWidgets::Label* l = (BWidgets::Label*) scaleEditor->nrNoteListbox[nr].getItem()->getWidget ();
488 		if (l) scaleEditor->scaleMap.altSymbols[nr] = l->getText ();
489 		scaleEditor->updateAltSymbol (nr);
490 	}
491 }
492 
buttonClickCallback(BEvents::Event * event)493 void ScaleEditor::buttonClickCallback (BEvents::Event* event)
494 {
495 	if ((event) && (event->getEventType() == BEvents::BUTTON_CLICK_EVENT) && (event->getWidget()))
496 	{
497 		BWidgets::TextButton* button = (BWidgets::TextButton*) event->getWidget();
498 		if (button->getParent())
499 		{
500 			ScaleEditor* scaleEditor = (ScaleEditor*)(button->getParent());
501 
502 			if (button == &(scaleEditor->cancelButton)) scaleEditor->setValue (-1.0);
503 			else if (button == &(scaleEditor->applyButton)) scaleEditor->setValue (1.0);
504 			scaleEditor->postCloseRequest ();
505 		}
506 	}
507 }
508 
labelClickCallback(BEvents::Event * event)509 void ScaleEditor::labelClickCallback (BEvents::Event* event)
510 {
511 	if ((event) && (event->getEventType() == BEvents::BUTTON_PRESS_EVENT) && (event->getWidget()))
512 	{
513 		BWidgets::Label* label = (BWidgets::Label*) event->getWidget();
514 		if (label->getParent ())
515 		{
516 			ScaleEditor* scaleEditor = (ScaleEditor*)(label->getParent());
517 
518 			// Switch off edit mode for all other editable labels
519 			if (label == &scaleEditor->scaleNameLabel)
520 			{
521 				for (BWidgets::Label& l : scaleEditor->nrAltSymbolLabel)
522 				{
523 					if (l.getEditMode ()) l.applyEdit ();
524 				}
525 			}
526 
527 			else
528 			{
529 				if (scaleEditor->scaleNameLabel.getMainWindow ())
530 				{
531 					scaleEditor->scaleNameLabel.getMainWindow ()->getKeyGrabStack()->remove (&scaleEditor->scaleNameLabel);
532 				}
533 				scaleEditor->scaleNameLabel.setEditMode (false);
534 				for (BWidgets::Label& l : scaleEditor->nrAltSymbolLabel)
535 				{
536 					if ((label != &l) && l.getEditMode ()) l.applyEdit ();
537 				}
538 			}
539 		}
540 	}
541 }
542 
labelMessageCallback(BEvents::Event * event)543 void ScaleEditor::labelMessageCallback (BEvents::Event* event)
544 {
545 	if ((event) && (event->getEventType() == BEvents::MESSAGE_EVENT) && (event->getWidget()))
546 	{
547 		BEvents::MessageEvent* me = (BEvents::MessageEvent*) event;
548 
549 		if (me->getName () == BWIDGETS_LABEL_TEXT_CHANGED_MESSAGE)
550 		{
551 			BWidgets::Label* label = (BWidgets::Label*) event->getWidget();
552 			if (label->getParent ())
553 			{
554 				ScaleEditor* scaleEditor = (ScaleEditor*)(label->getParent());
555 
556 				if (label == &scaleEditor->scaleNameLabel) scaleEditor->scaleMap.name = scaleEditor->scaleNameLabel.getText ();
557 
558 				else for (size_t i = 0; i < ROWS; ++i)
559 				{
560 					BWidgets::Label* l = &scaleEditor->nrAltSymbolLabel[i];
561 					if (label == l)
562 					{
563 						scaleEditor->scaleMap.altSymbols[i] = l->getText ();
564 						break;
565 					}
566 				}
567 			}
568 		}
569 	}
570 }
571 
pianoClickCallback(BEvents::Event * event)572 void ScaleEditor::pianoClickCallback (BEvents::Event* event)
573 {
574 	if ((event) && (event->getWidget()))
575 	{
576 		BWidgets::HPianoRoll* piano = (BWidgets::HPianoRoll*)(event->getWidget());
577 		if (piano->getParent())
578 		{
579 			ScaleEditor* scaleEditor = (ScaleEditor*)(piano->getParent());
580 			std::vector<bool> pressedKeys = piano->getPressedKeys();
581 			BScale* scale = &(scaleEditor->scale);
582 			int pianoRoot = scale->getRoot() % 12;
583 			if (pianoRoot < 6) pianoRoot +=12;
584 
585 			// Build new BScale elementarray from piano
586 			BScaleNotes newScaleNotes;
587 			newScaleNotes.fill (ENOTE);
588 			for (int i = 0, count = 0; i < 12; ++i)
589 			{
590 				if (pressedKeys[i + pianoRoot])
591 				{
592 					newScaleNotes[count] = i;
593 					++count;
594 				}
595 			}
596 
597 			// Something changed?
598 			if (newScaleNotes != scale->getScale())
599 			{
600 				scale->setScale (newScaleNotes);
601 
602 				// Update auto numbering of scale map elements
603 				for (int i = 0, count = 0; i < ROWS; ++i)
604 				{
605 					if (!(scaleEditor->scaleMap.elements[i] & 0x0100))
606 					{
607 						scaleEditor->scaleMap.elements[i] = count;
608 						scaleEditor->scaleMap.altSymbols[i] = "";
609 						++count;
610 					}
611 					scaleEditor->updateAltSymbol (i);
612 				}
613 			}
614 		}
615 	}
616 }
617 
618 #endif /* SCALEEDITOR_HPP_ */
619