1 //------------------------------------------------------------------------
2 // This file is part of VSTGUI. It is subject to the license terms
3 // in the LICENSE file found in the top-level directory of this
4 // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
5 // Flags : clang-format SMTGSequencer
6 
7 #include "keyboardview.h"
8 #include "vstgui/lib/cbitmap.h"
9 #include "vstgui/lib/coffscreencontext.h"
10 #include "vstgui/uidescription/detail/uiviewcreatorattributes.h"
11 #include "vstgui/uidescription/iviewcreator.h"
12 #include "vstgui/uidescription/uiattributes.h"
13 #include "vstgui/uidescription/uiviewcreator.h"
14 #include "vstgui/uidescription/uiviewfactory.h"
15 
16 //------------------------------------------------------------------------
17 namespace VSTGUI {
18 
19 //------------------------------------------------------------------------
KeyboardView()20 KeyboardView::KeyboardView ()
21 {
22 	setDrawNoteText (true);
23 }
24 
25 //------------------------------------------------------------------------
calcYParameter(NoteIndex note,CCoord y) const26 double KeyboardView::calcYParameter (NoteIndex note, CCoord y) const
27 {
28 	if (note == -1)
29 		return 0.f;
30 	y -= getViewSize ().top;
31 	auto r = getNoteRect (note);
32 	return y / r.getHeight ();
33 }
34 
35 //------------------------------------------------------------------------
calcXParameter(NoteIndex note,CCoord x) const36 double KeyboardView::calcXParameter (NoteIndex note, CCoord x) const
37 {
38 	if (note == -1)
39 		return 0.f;
40 	auto r = getNoteRect (note);
41 	x -= r.left;
42 	return x / r.getWidth ();
43 }
44 
45 #if VSTGUI_TOUCH_EVENT_HANDLING
46 //------------------------------------------------------------------------
onTouchEvent(ITouchEvent & event)47 void KeyboardView::onTouchEvent (ITouchEvent& event)
48 {
49 	for (auto touch : event)
50 	{
51 		if (touch.second.target != 0 && touch.second.target != this)
52 			continue;
53 		switch (touch.second.state)
54 		{
55 			case ITouchEvent::kBegan:
56 			{
57 				onTouchBegin (touch, event);
58 				break;
59 			}
60 			case ITouchEvent::kMoved:
61 			{
62 				if (touch.second.target == this)
63 				{
64 					onTouchMove (touch, event);
65 				}
66 				break;
67 			}
68 			case ITouchEvent::kCanceled:
69 			case ITouchEvent::kEnded:
70 			{
71 				if (touch.second.target == this)
72 				{
73 					onTouchEnd (touch, event);
74 				}
75 				break;
76 			}
77 			case ITouchEvent::kNoChange:
78 			case ITouchEvent::kUndefined: break;
79 		}
80 	}
81 }
82 
83 //------------------------------------------------------------------------
onTouchBegin(const ITouchEvent::TouchPair & touch,ITouchEvent & event)84 void KeyboardView::onTouchBegin (const ITouchEvent::TouchPair& touch, ITouchEvent& event)
85 {
86 	CPoint where (touch.second.location);
87 	frameToLocal (where);
88 	auto note = pointToNote (where, false);
89 	if (note >= 0)
90 	{
91 		for (auto nt : noteTouches)
92 		{
93 			if (nt.second.note == note)
94 			{
95 				// currently no multiple touches for the same note
96 				return;
97 			}
98 		}
99 		// TODO: velocity
100 		NoteTouch noteTouch (note);
101 		event.setTouchTarget (touch.first, this, false);
102 		setKeyPressed (note, true);
103 		if (delegate)
104 			noteTouch.noteID = delegate->onNoteOn (note, calcXParameter (note, where.x),
105 			                                       calcYParameter (note, where.y));
106 		noteTouches.insert (std::pair<int32_t, NoteTouch> (touch.first, noteTouch));
107 	}
108 }
109 
110 //------------------------------------------------------------------------
onTouchMove(const ITouchEvent::TouchPair & touch,ITouchEvent & event)111 void KeyboardView::onTouchMove (const ITouchEvent::TouchPair& touch, ITouchEvent& event)
112 {
113 	CPoint where (touch.second.location);
114 	frameToLocal (where);
115 	auto note = pointToNote (where, false);
116 	auto noteTouch = noteTouches.find (touch.first);
117 	if (noteTouch != noteTouches.end ())
118 	{
119 		if (note != noteTouch->second.note)
120 		{
121 			auto noteOff = noteTouch->second.note;
122 			auto noteOffID = noteTouch->second.noteID;
123 			setKeyPressed (noteTouch->second.note, false);
124 			if (note >= 0)
125 			{
126 				noteTouch->second.note = note;
127 				setKeyPressed (noteTouch->second.note, true);
128 				if (delegate)
129 					noteTouch->second.noteID = delegate->onNoteOn (
130 					    note, calcXParameter (note, where.x), calcYParameter (note, where.y));
131 			}
132 			else
133 			{
134 				noteTouches.erase (noteTouch);
135 			}
136 			if (delegate)
137 				delegate->onNoteOff (noteOff, noteOffID);
138 		}
139 		else if (note == noteTouch->second.note)
140 		{
141 			if (delegate)
142 			{
143 				delegate->onNoteModulation (noteTouch->second.noteID,
144 				                            calcXParameter (note, where.x),
145 				                            calcYParameter (note, where.y));
146 			}
147 		}
148 	}
149 }
150 
151 //------------------------------------------------------------------------
onTouchEnd(const ITouchEvent::TouchPair & touch,ITouchEvent & event)152 void KeyboardView::onTouchEnd (const ITouchEvent::TouchPair& touch, ITouchEvent& event)
153 {
154 	event.unsetTouchTarget (touch.first, this);
155 	auto noteTouch = noteTouches.find (touch.first);
156 	if (noteTouch != noteTouches.end ())
157 	{
158 		setKeyPressed (noteTouch->second.note, false);
159 		if (delegate)
160 			delegate->onNoteOff (noteTouch->second.note, noteTouch->second.noteID);
161 		noteTouches.erase (noteTouch);
162 	}
163 }
164 #else
165 //------------------------------------------------------------------------
doNoteOff()166 void KeyboardView::doNoteOff ()
167 {
168 	if (pressedNote != -1)
169 	{
170 		if (delegate)
171 			delegate->onNoteOff (pressedNote, noteID);
172 		else
173 			setKeyPressed (pressedNote, false);
174 		noteID = -1;
175 		pressedNote = -1;
176 	}
177 }
178 
179 //------------------------------------------------------------------------
doNoteOn(int16_t note,double yPos,double xPos)180 void KeyboardView::doNoteOn (int16_t note, double yPos, double xPos)
181 {
182 	pressedNote = note;
183 	if (pressedNote != -1)
184 	{
185 		if (delegate)
186 			noteID = delegate->onNoteOn (pressedNote, yPos, xPos);
187 		else
188 			setKeyPressed (pressedNote, true);
189 	}
190 }
191 
192 //------------------------------------------------------------------------
onMouseDown(CPoint & where,const CButtonState & buttons)193 CMouseEventResult KeyboardView::onMouseDown (CPoint& where, const CButtonState& buttons)
194 {
195 	if (buttons.isLeftButton ())
196 	{
197 		auto note = pointToNote (where, false);
198 		if (note != -1)
199 		{
200 			doNoteOn (note, calcXParameter (note, where.x), calcYParameter (note, where.y));
201 		}
202 		return kMouseEventHandled;
203 	}
204 	return kMouseEventNotHandled;
205 }
206 
207 //------------------------------------------------------------------------
onMouseMoved(CPoint & where,const CButtonState & buttons)208 CMouseEventResult KeyboardView::onMouseMoved (CPoint& where, const CButtonState& buttons)
209 {
210 	if (buttons.isLeftButton ())
211 	{
212 		auto note = pointToNote (where, false);
213 		if (note == pressedNote)
214 		{
215 			if (delegate)
216 			{
217 				delegate->onNoteModulation (noteID, calcXParameter (note, where.x),
218 				                            calcYParameter (note, where.y));
219 			}
220 		}
221 		else
222 		{
223 			doNoteOff ();
224 			doNoteOn (note, calcXParameter (note, where.x), calcYParameter (note, where.y));
225 		}
226 	}
227 	return kMouseEventHandled;
228 }
229 
230 //------------------------------------------------------------------------
onMouseUp(CPoint & where,const CButtonState & buttons)231 CMouseEventResult KeyboardView::onMouseUp (CPoint& where, const CButtonState& buttons)
232 {
233 	if (buttons.isLeftButton ())
234 	{
235 		doNoteOff ();
236 		return kMouseEventHandled;
237 	}
238 	return kMouseEventNotHandled;
239 }
240 
241 //------------------------------------------------------------------------
onMouseCancel()242 CMouseEventResult KeyboardView::onMouseCancel ()
243 {
244 	doNoteOff ();
245 	return kMouseEventHandled;
246 }
247 
248 #endif
249 
250 //------------------------------------------------------------------------
registerKeyRangeChangedListener(IKeyboardViewKeyRangeChangedListener * listener)251 void KeyboardViewRangeSelector::registerKeyRangeChangedListener (
252     IKeyboardViewKeyRangeChangedListener* listener)
253 {
254 	listeners.add (listener);
255 }
256 
257 //------------------------------------------------------------------------
unregisterKeyRangeChangedListener(IKeyboardViewKeyRangeChangedListener * listener)258 void KeyboardViewRangeSelector::unregisterKeyRangeChangedListener (
259     IKeyboardViewKeyRangeChangedListener* listener)
260 {
261 	listeners.remove (listener);
262 }
263 
264 //------------------------------------------------------------------------
drawRect(CDrawContext * context,const CRect & dirtyRect)265 void KeyboardViewRangeSelector::drawRect (CDrawContext* context, const CRect& dirtyRect)
266 {
267 	KeyboardViewBase::drawRect (context, dirtyRect);
268 
269 	auto r1 = getNoteRect (selectionRange.position);
270 	auto r2 = getNoteRect (selectionRange.position + selectionRange.length);
271 
272 	r1.offset (-r1.getWidth (), 0);
273 	r2.offset (r2.getWidth (), 0);
274 	r1.left = getViewSize ().left;
275 	r2.right = getViewSize ().right;
276 	r1.bottom = getViewSize ().bottom;
277 	r2.bottom = getViewSize ().bottom;
278 
279 	context->setFillColor (CColor (0, 0, 0, 110));
280 	if (!r1.isEmpty ())
281 		context->drawRect (r1, kDrawFilled);
282 	if (!r2.isEmpty ())
283 		context->drawRect (r2, kDrawFilled);
284 }
285 
286 //------------------------------------------------------------------------
setKeyRange(NoteIndex startNote,NumNotes numKeys)287 void KeyboardViewRangeSelector::setKeyRange (NoteIndex startNote, NumNotes numKeys)
288 {
289 	KeyboardViewBase::setKeyRange (startNote, numKeys);
290 	if (selectionRange.position < getKeyRangeStart ())
291 		selectionRange.position = getKeyRangeStart ();
292 }
293 
294 //------------------------------------------------------------------------
setSelectionRange(const Range & _range)295 void KeyboardViewRangeSelector::setSelectionRange (const Range& _range)
296 {
297 	if (selectionRange != _range)
298 	{
299 		selectionRange = _range;
300 		invalid ();
301 		listeners.forEach ([this] (auto& listener) { listener->onKeyRangeChanged (this); });
302 	}
303 }
304 
305 //------------------------------------------------------------------------
setSelectionMinMax(NumNotes minRange,NumNotes maxRange)306 void KeyboardViewRangeSelector::setSelectionMinMax (NumNotes minRange, NumNotes maxRange)
307 {
308 	rangeMin = minRange;
309 	rangeMax = maxRange;
310 }
311 
312 //------------------------------------------------------------------------
getNumWhiteKeysSelected() const313 auto KeyboardViewRangeSelector::getNumWhiteKeysSelected () const -> NumNotes
314 {
315 	NumNotes whiteKeys = 0;
316 	for (auto i = selectionRange.position; i <= selectionRange.position + selectionRange.length;
317 	     ++i)
318 	{
319 		if (isWhiteKey (i))
320 			whiteKeys++;
321 	}
322 	return whiteKeys;
323 }
324 
325 #if VSTGUI_TOUCH_EVENT_HANDLING
326 //------------------------------------------------------------------------
onTouchBegin(const ITouchEvent::TouchPair & touch,ITouchEvent & event)327 void KeyboardViewRangeSelector::onTouchBegin (const ITouchEvent::TouchPair& touch,
328                                               ITouchEvent& event)
329 {
330 	CPoint where (touch.second.location);
331 	frameToLocal (where);
332 	auto note = pointToNote (where, false);
333 	if (note >= 0)
334 	{
335 		if (touchIds[0] == -1)
336 		{
337 			if (note <= selectionRange.position ||
338 			    note > selectionRange.position + selectionRange.length)
339 			{
340 				touchIds[0] = touch.first;
341 				touchStartNote[0] = note;
342 				touchMode = note <= selectionRange.position ? kChangeRangeFront : kChangeRangeBack;
343 				event.setTouchTarget (touch.first, this, false);
344 				selectionRangeOnTouchStart = selectionRange;
345 			}
346 			else if (note > selectionRange.position &&
347 			         note < selectionRange.position + selectionRange.length)
348 			{
349 				touchIds[0] = touch.first;
350 				touchStartNote[0] = note;
351 				touchMode = kMoveRange;
352 				event.setTouchTarget (touch.first, this, false);
353 				selectionRangeOnTouchStart = selectionRange;
354 			}
355 		}
356 		else
357 		{
358 			if (touchMode != kMoveRange)
359 			{
360 			}
361 			else if (note > touchStartNote[0])
362 			{
363 				touchIds[1] = touch.first;
364 				touchStartNote[1] = note;
365 				event.setTouchTarget (touch.first, this, false);
366 				touchMode = kChangeRangeFront;
367 			}
368 		}
369 	}
370 }
371 
372 //------------------------------------------------------------------------
bound(int32_t value,int32_t min,int32_t max)373 static int32_t bound (int32_t value, int32_t min, int32_t max)
374 {
375 	if (value < min)
376 		value = min;
377 	else if (value > max)
378 		value = max;
379 	return value;
380 }
381 
382 //------------------------------------------------------------------------
onTouchMove(const ITouchEvent::TouchPair & touch,ITouchEvent & event)383 void KeyboardViewRangeSelector::onTouchMove (const ITouchEvent::TouchPair& touch,
384                                              ITouchEvent& event)
385 {
386 	CPoint where (touch.second.location);
387 	frameToLocal (where);
388 	auto note = pointToNote (where, true);
389 	if (touchIds[0] == touch.first)
390 	{
391 		if (touchMode == kMoveRange || touchMode == kChangeRangeFront)
392 		{
393 			if (note >= 0)
394 			{
395 				auto move = note - touchStartNote[0];
396 				auto newRangeStart =
397 				    bound (selectionRangeOnTouchStart.position + move, getKeyRangeStart (),
398 				           (getKeyRangeStart () + getNumKeys ()) - selectionRange.length);
399 				if (!isWhiteKey (newRangeStart))
400 					newRangeStart++;
401 				auto length = selectionRange.length;
402 				if (touchMode == kChangeRangeFront)
403 				{
404 					length = selectionRange.length + (selectionRange.position - newRangeStart);
405 					if (length < rangeMin || length > rangeMax)
406 						return;
407 				}
408 				setSelectionRange (Range (newRangeStart, length));
409 			}
410 		}
411 		else if (touchMode == kChangeRangeBack)
412 		{
413 			if (note >= 0)
414 			{
415 				auto move = note - touchStartNote[0];
416 				auto length = selectionRangeOnTouchStart.length + move;
417 				if (!isWhiteKey (selectionRange.position + length))
418 					length++;
419 				if (length < rangeMin || length > rangeMax)
420 					return;
421 				setSelectionRange (Range (selectionRange.position, length));
422 			}
423 		}
424 	}
425 	else
426 	{
427 	}
428 }
429 
430 //------------------------------------------------------------------------
onTouchEvent(ITouchEvent & event)431 void KeyboardViewRangeSelector::onTouchEvent (ITouchEvent& event)
432 {
433 	for (auto touch : event)
434 	{
435 		if (touch.second.target != 0 && touch.second.target != this)
436 			continue;
437 		switch (touch.second.state)
438 		{
439 			case ITouchEvent::kBegan:
440 			{
441 				if (touchIds[0] != -1 && touchIds[1] != -1)
442 					continue;
443 				onTouchBegin (touch, event);
444 				break;
445 			}
446 			case ITouchEvent::kCanceled:
447 			case ITouchEvent::kEnded:
448 			{
449 				if (touch.second.target == this)
450 				{
451 					if (touchIds[0] == touch.first)
452 					{
453 						touchIds[0] = -1;
454 					}
455 					else
456 					{
457 						touchIds[1] = -1;
458 					}
459 					event.unsetTouchTarget (touch.first, this);
460 				}
461 				break;
462 			}
463 			case ITouchEvent::kMoved:
464 			{
465 				if (touch.second.target == this)
466 				{
467 					onTouchMove (touch, event);
468 				}
469 				break;
470 			}
471 			case ITouchEvent::kNoChange:
472 			case ITouchEvent::kUndefined: break;
473 		}
474 	}
475 }
476 
477 //------------------------------------------------------------------------
wantsMultiTouchEvents() const478 bool KeyboardViewRangeSelector::wantsMultiTouchEvents () const
479 {
480 	return true;
481 }
482 #else
483 
484 //------------------------------------------------------------------------
onMouseDown(CPoint & where,const CButtonState & buttons)485 CMouseEventResult KeyboardViewRangeSelector::onMouseDown (CPoint& where,
486                                                           const CButtonState& buttons)
487 {
488 	if (buttons.isLeftButton ())
489 	{
490 		moveStartRange = selectionRange;
491 		moveStartNote = pointToNote (where, true);
492 		if (moveStartNote < selectionRange.position ||
493 		    moveStartNote >= selectionRange.position + selectionRange.length)
494 		{
495 			auto middle = selectionRange.length / 2;
496 			if (moveStartNote < middle)
497 				moveStartRange.position = 0;
498 			else
499 				moveStartRange.position = moveStartNote - middle;
500 			return onMouseMoved (where, buttons);
501 		}
502 		return kMouseEventHandled;
503 	}
504 	return kMouseEventNotHandled;
505 }
506 
507 //------------------------------------------------------------------------
onMouseMoved(CPoint & where,const CButtonState & buttons)508 CMouseEventResult KeyboardViewRangeSelector::onMouseMoved (CPoint& where,
509                                                            const CButtonState& buttons)
510 {
511 	if (buttons.isLeftButton () && moveStartNote != -1)
512 	{
513 		auto note = pointToNote (where, true);
514 		if (note != -1)
515 		{
516 			auto offset = note - moveStartNote;
517 			Range r = moveStartRange;
518 			if (static_cast<NoteIndex> (r.position) + offset < 0)
519 				r.position = 0;
520 			else if (static_cast<NoteIndex> (r.position + r.length) + offset >= MaxNotes)
521 				r.position = (MaxNotes - 1) - r.length;
522 			else
523 				r.position += offset;
524 			setSelectionRange (r);
525 		}
526 	}
527 	return kMouseEventHandled;
528 }
529 
530 //------------------------------------------------------------------------
onMouseUp(CPoint & where,const CButtonState & buttons)531 CMouseEventResult KeyboardViewRangeSelector::onMouseUp (CPoint& where, const CButtonState& buttons)
532 {
533 	if (buttons.isLeftButton ())
534 		moveStartNote = -1;
535 	return kMouseEventHandled;
536 }
537 
538 //------------------------------------------------------------------------
onMouseCancel()539 CMouseEventResult KeyboardViewRangeSelector::onMouseCancel ()
540 {
541 	moveStartNote = -1;
542 	return kMouseEventHandled;
543 }
544 
545 #endif
546 
547 //------------------------------------------------------------------------
548 //------------------------------------------------------------------------
549 //------------------------------------------------------------------------
KeyboardViewBase()550 KeyboardViewBase::KeyboardViewBase () : CView (CRect (0, 0, 0, 0)), noteNameFont (kSystemFont)
551 {
552 }
553 
554 //------------------------------------------------------------------------
setViewSize(const CRect & rect,bool invalid)555 void KeyboardViewBase::setViewSize (const CRect& rect, bool invalid)
556 {
557 	CView::setViewSize (rect, invalid);
558 	noteRectCacheInvalid = true;
559 }
560 
561 //------------------------------------------------------------------------
sizeToFit()562 bool KeyboardViewBase::sizeToFit ()
563 {
564 	if (noteRectCacheInvalid)
565 		updateNoteRectCache ();
566 
567 	auto r = getNoteRect (startNote + numKeys - 1);
568 	r.setWidth (r.right);
569 	r.setHeight (getViewSize ().getHeight ());
570 	r.originize ();
571 
572 	setViewSize (r);
573 	setMouseableArea (r);
574 	return true;
575 }
576 
577 //------------------------------------------------------------------------
getNumWhiteKeys() const578 auto KeyboardViewBase::getNumWhiteKeys () const -> NumNotes
579 {
580 	NumNotes whiteKeys = 0;
581 	for (NoteIndex i = startNote; i <= startNote + numKeys; ++i)
582 	{
583 		if (isWhiteKey (i))
584 			whiteKeys++;
585 	}
586 	return whiteKeys;
587 }
588 
589 //------------------------------------------------------------------------
setKeyRange(NoteIndex _startNote,NumNotes _numKeys)590 void KeyboardViewBase::setKeyRange (NoteIndex _startNote, NumNotes _numKeys)
591 {
592 	vstgui_assert (_startNote >= 0 && _numKeys >= 0);
593 	if (_startNote < 0 || _numKeys < 0)
594 		return;
595 
596 	if (static_cast<int32_t> (_numKeys) + static_cast<int32_t> (_startNote) >=
597 	    static_cast<int32_t> (MaxNotes))
598 	{
599 		_numKeys = (MaxNotes - 1) - _startNote;
600 	}
601 	startNote = _startNote;
602 	numKeys = _numKeys;
603 	noteRectCacheInvalid = true;
604 	invalid ();
605 }
606 
607 //------------------------------------------------------------------------
setNoteNameFont(CFontDesc * font)608 void KeyboardViewBase::setNoteNameFont (CFontDesc* font)
609 {
610 	if (font != noteNameFont)
611 	{
612 		noteNameFont = font;
613 		if (drawNoteText)
614 			invalid ();
615 	}
616 }
617 
618 //------------------------------------------------------------------------
setDrawNoteText(bool state)619 void KeyboardViewBase::setDrawNoteText (bool state)
620 {
621 	if (state != drawNoteText)
622 	{
623 		drawNoteText = state;
624 		invalid ();
625 	}
626 }
627 
628 //------------------------------------------------------------------------
setWhiteKeyWidth(CCoord width)629 void KeyboardViewBase::setWhiteKeyWidth (CCoord width)
630 {
631 	if (whiteKeyWidth != width)
632 	{
633 		whiteKeyWidth = width;
634 		whiteKeyBitmapCache = nullptr;
635 		noteRectCacheInvalid = true;
636 		invalid ();
637 	}
638 }
639 
640 //------------------------------------------------------------------------
setBlackKeyWidth(CCoord width)641 void KeyboardViewBase::setBlackKeyWidth (CCoord width)
642 {
643 	if (blackKeyWidth != width)
644 	{
645 		blackKeyWidth = width;
646 		blackKeyBitmapCache = nullptr;
647 		noteRectCacheInvalid = true;
648 		invalid ();
649 	}
650 }
651 
652 //------------------------------------------------------------------------
setBlackKeyHeight(CCoord height)653 void KeyboardViewBase::setBlackKeyHeight (CCoord height)
654 {
655 	if (blackKeyHeight != height)
656 	{
657 		blackKeyHeight = height;
658 		noteRectCacheInvalid = true;
659 		invalid ();
660 	}
661 }
662 
663 //------------------------------------------------------------------------
setLineWidth(CCoord width)664 void KeyboardViewBase::setLineWidth (CCoord width)
665 {
666 	if (lineWidth != width)
667 	{
668 		lineWidth = width;
669 		invalid ();
670 	}
671 }
672 
673 //------------------------------------------------------------------------
setFrameColor(CColor color)674 void KeyboardViewBase::setFrameColor (CColor color)
675 {
676 	if (frameColor != color)
677 	{
678 		frameColor = color;
679 		invalid ();
680 	}
681 }
682 
683 //------------------------------------------------------------------------
setFontColor(CColor color)684 void KeyboardViewBase::setFontColor (CColor color)
685 {
686 	if (fontColor != color)
687 	{
688 		fontColor = color;
689 		invalid ();
690 	}
691 }
692 
693 //------------------------------------------------------------------------
setWhiteKeyColor(CColor color)694 void KeyboardViewBase::setWhiteKeyColor (CColor color)
695 {
696 	if (whiteKeyColor != color)
697 	{
698 		whiteKeyColor = color;
699 		invalid ();
700 	}
701 }
702 
703 //------------------------------------------------------------------------
setWhiteKeyPressedColor(CColor color)704 void KeyboardViewBase::setWhiteKeyPressedColor (CColor color)
705 {
706 	if (whiteKeyPressedColor != color)
707 	{
708 		whiteKeyPressedColor = color;
709 		invalid ();
710 	}
711 }
712 
713 //------------------------------------------------------------------------
setBlackKeyColor(CColor color)714 void KeyboardViewBase::setBlackKeyColor (CColor color)
715 {
716 	if (blackKeyColor != color)
717 	{
718 		blackKeyColor = color;
719 		invalid ();
720 	}
721 }
722 
723 //------------------------------------------------------------------------
setBlackKeyPressedColor(CColor color)724 void KeyboardViewBase::setBlackKeyPressedColor (CColor color)
725 {
726 	if (blackKeyPressedColor != color)
727 	{
728 		blackKeyPressedColor = color;
729 		invalid ();
730 	}
731 }
732 
733 //------------------------------------------------------------------------
createBitmapCache()734 void KeyboardViewBase::createBitmapCache ()
735 {
736 	auto whiteKeyBitmap = getBitmap (BitmapID::WhiteKeyUnpressed);
737 	auto blackKeyBitmap = getBitmap (BitmapID::BlackKeyUnpressed);
738 	if (!whiteKeyBitmap || !blackKeyBitmap)
739 		return;
740 
741 	if (auto offscreen = COffscreenContext::create (getFrame (), whiteKeyWidth, getHeight ()))
742 	{
743 		offscreen->beginDraw ();
744 		CRect r (0, 0, whiteKeyWidth, getHeight ());
745 		r.left -= whiteKeyBitmapInset.left;
746 		r.right += whiteKeyBitmapInset.right;
747 		r.top -= whiteKeyBitmapInset.top;
748 		r.bottom += whiteKeyBitmapInset.bottom;
749 		whiteKeyBitmap->draw (offscreen, r);
750 		offscreen->endDraw ();
751 		whiteKeyBitmapCache = offscreen->getBitmap ();
752 	}
753 
754 	if (auto offscreen = COffscreenContext::create (getFrame (), blackKeyWidth, blackKeyHeight))
755 	{
756 		offscreen->beginDraw ();
757 		CRect r (0, 0, blackKeyWidth, blackKeyHeight);
758 		r.left -= blackKeyBitmapInset.left;
759 		r.right += blackKeyBitmapInset.right;
760 		r.top -= blackKeyBitmapInset.top;
761 		r.bottom += blackKeyBitmapInset.bottom;
762 		blackKeyBitmap->draw (offscreen, r);
763 		offscreen->endDraw ();
764 		blackKeyBitmapCache = offscreen->getBitmap ();
765 	}
766 }
767 
768 //------------------------------------------------------------------------
drawRect(CDrawContext * context,const CRect & dirtyRect)769 void KeyboardViewBase::drawRect (CDrawContext* context, const CRect& dirtyRect)
770 {
771 	if (noteRectCacheInvalid)
772 		updateNoteRectCache ();
773 
774 	if (whiteKeyBitmapCache == nullptr || blackKeyBitmapCache == nullptr)
775 		createBitmapCache ();
776 
777 	context->setLineWidth (lineWidth == -1 ? context->getHairlineSize () : lineWidth);
778 	context->setFrameColor (frameColor);
779 	context->setFontColor (fontColor);
780 	context->setFont (noteNameFont);
781 	context->setDrawMode (kAntiAliasing | kNonIntegralMode);
782 
783 	for (NoteIndex i = startNote; i <= startNote + numKeys; i++)
784 	{
785 		if (isWhiteKey (i) == false)
786 			continue;
787 		CRect r = getNoteRect (i);
788 		if (dirtyRect.rectOverlap (r) == false)
789 			continue;
790 		drawNote (context, r, i, true);
791 		if (drawNoteText && i % 12 == 0)
792 		{
793 			char text[5];
794 			snprintf (text, 4, "C%d", (i / 12) - 2);
795 			r.top = r.bottom - context->getFont ()->getSize () - 10;
796 			context->drawString (text, r);
797 		}
798 	}
799 	for (NoteIndex i = startNote; i <= startNote + numKeys; i++)
800 	{
801 		if (isWhiteKey (i) == true)
802 			continue;
803 		CRect r = getNoteRect (i);
804 		if (dirtyRect.rectOverlap (r) == false)
805 			continue;
806 		drawNote (context, r, i, false);
807 	}
808 }
809 
810 //------------------------------------------------------------------------
drawNote(CDrawContext * context,CRect & rect,NoteIndex note,bool isWhite) const811 void KeyboardViewBase::drawNote (CDrawContext* context, CRect& rect, NoteIndex note,
812                                  bool isWhite) const
813 {
814 	CBitmap* keyBitmap = nullptr;
815 	CRect bitmapRect (rect);
816 	if (isWhite)
817 	{
818 		bitmapRect.left -= whiteKeyBitmapInset.left;
819 		bitmapRect.right += whiteKeyBitmapInset.right;
820 		bitmapRect.top -= whiteKeyBitmapInset.top;
821 		bitmapRect.bottom += whiteKeyBitmapInset.bottom;
822 	}
823 	else
824 	{
825 		bitmapRect.left -= blackKeyBitmapInset.left;
826 		bitmapRect.right += blackKeyBitmapInset.right;
827 		bitmapRect.top -= blackKeyBitmapInset.top;
828 		bitmapRect.bottom += blackKeyBitmapInset.bottom;
829 	}
830 
831 	if (keyPressed[note])
832 		keyBitmap = getBitmap (isWhite ? BitmapID::WhiteKeyPressed : BitmapID::BlackKeyPressed);
833 	else
834 	{
835 		if (isWhite)
836 		{
837 			if (whiteKeyBitmapCache && whiteKeyBitmapCache->getWidth () == bitmapRect.getWidth () &&
838 			    whiteKeyBitmapCache->getHeight () == bitmapRect.getHeight ())
839 				keyBitmap = whiteKeyBitmapCache;
840 			else
841 				keyBitmap = getBitmap (BitmapID::WhiteKeyUnpressed);
842 		}
843 		else
844 		{
845 			if (blackKeyBitmapCache && blackKeyBitmapCache->getWidth () == bitmapRect.getWidth () &&
846 			    blackKeyBitmapCache->getHeight () == bitmapRect.getHeight ())
847 				keyBitmap = blackKeyBitmapCache;
848 			else
849 				keyBitmap = getBitmap (BitmapID::BlackKeyUnpressed);
850 		}
851 	}
852 
853 	if (keyBitmap)
854 	{
855 		keyBitmap->draw (context, bitmapRect);
856 	}
857 	else
858 	{
859 		if (keyPressed[note])
860 			context->setFillColor (isWhite ? whiteKeyPressedColor : blackKeyPressedColor);
861 		else
862 			context->setFillColor (isWhite ? whiteKeyColor : blackKeyColor);
863 		context->drawRect (rect, isWhite ? kDrawFilledAndStroked : kDrawFilled);
864 	}
865 	if (keyPressed[note] && isWhite)
866 	{
867 		NoteIndex otherNote;
868 		if (note > startNote)
869 		{
870 			otherNote = note - 1;
871 			if (!isWhiteKey (otherNote))
872 				otherNote--;
873 			if (keyPressed[otherNote] == false)
874 			{
875 				if (auto b = getBitmap (BitmapID::WhiteKeyShadowLeft))
876 				{
877 					b->draw (context, bitmapRect);
878 				}
879 			}
880 		}
881 
882 		if (note < startNote + numKeys)
883 		{
884 			otherNote = note + 1;
885 			if (!isWhiteKey (otherNote))
886 				otherNote++;
887 			if (keyPressed[otherNote] == false)
888 			{
889 				if (auto b = getBitmap (BitmapID::WhiteKeyShadowRight))
890 				{
891 					b->draw (context, bitmapRect);
892 				}
893 			}
894 		}
895 	}
896 }
897 
898 //------------------------------------------------------------------------
calcNoteRect(NoteIndex note) const899 CRect KeyboardViewBase::calcNoteRect (NoteIndex note) const
900 {
901 	CRect result;
902 	if (note >= startNote && note <= startNote + numKeys)
903 	{
904 		for (NoteIndex i = startNote + 1; i <= note; i++)
905 		{
906 			bool isWhite = isWhiteKey (i);
907 			if (isWhite)
908 			{
909 				result.left += whiteKeyWidth;
910 			}
911 		}
912 		if (isWhiteKey (note))
913 		{
914 			result.setWidth (whiteKeyWidth);
915 			result.setHeight (getViewSize ().getHeight ());
916 		}
917 		else
918 		{
919 			result.left += whiteKeyWidth - blackKeyWidth / 2;
920 			result.setWidth (blackKeyWidth);
921 			result.setHeight (blackKeyHeight);
922 		}
923 	}
924 	result.offset (getViewSize ().left, getViewSize ().top);
925 	return result;
926 }
927 
928 //------------------------------------------------------------------------
updateNoteRectCache() const929 void KeyboardViewBase::updateNoteRectCache () const
930 {
931 	for (NoteIndex i = 0; i < MaxNotes; ++i)
932 		noteRectCache[i] = calcNoteRect (i);
933 
934 	CRect r = getNoteRect (startNote + numKeys);
935 	CCoord space = getViewSize ().right - r.right;
936 	if (space > 0)
937 	{
938 		space = fabs (space / 2.);
939 		for (NoteIndex i = startNote + 1; i <= startNote + numKeys; ++i)
940 			noteRectCache[i].offset (space, 0);
941 		noteRectCache[startNote].right += space;
942 		noteRectCache[startNote + numKeys].right = getViewSize ().right;
943 	}
944 	noteRectCacheInvalid = false;
945 }
946 
947 //------------------------------------------------------------------------
pointToNote(const CPoint & p,bool ignoreY) const948 auto KeyboardViewBase::pointToNote (const CPoint& p, bool ignoreY) const -> NoteIndex
949 {
950 	if (noteRectCacheInvalid)
951 		updateNoteRectCache ();
952 	NoteIndex result = 0;
953 	for (auto r : getNoteRectCache ())
954 	{
955 		if (!ignoreY)
956 		{
957 			if (r.pointInside (p))
958 			{
959 				if (isWhiteKey (result))
960 				{
961 					if (getNoteRect (result + 1).pointInside (p))
962 						return result + 1;
963 				}
964 				return result;
965 			}
966 		}
967 		else if (p.x >= r.left && p.x < r.right)
968 		{
969 			if (isWhiteKey (result))
970 			{
971 				auto r2 = getNoteRect (result + 1);
972 				if (p.x >= r2.left && p.x < r2.right)
973 					return result + 1;
974 			}
975 			return result;
976 		}
977 		result++;
978 	}
979 	return -1;
980 }
981 
982 //------------------------------------------------------------------------
invalidNote(NoteIndex note)983 void KeyboardViewBase::invalidNote (NoteIndex note)
984 {
985 	if (noteRectCacheInvalid)
986 		updateNoteRectCache ();
987 	invalidRect (getNoteRect (note));
988 }
989 
990 //------------------------------------------------------------------------
setKeyPressed(NoteIndex note,bool state)991 void KeyboardViewBase::setKeyPressed (NoteIndex note, bool state)
992 {
993 	vstgui_assert (note >= 0);
994 	if (note < 0)
995 		return;
996 
997 	if (keyPressed[note] != state)
998 	{
999 		keyPressed[note] = state;
1000 		invalidNote (note);
1001 		if (isWhiteKey (note))
1002 		{
1003 			if (note > startNote)
1004 			{
1005 				NoteIndex prevKey = note - 1;
1006 				if (!isWhiteKey (prevKey))
1007 					prevKey--;
1008 				invalidNote (prevKey);
1009 			}
1010 			if (note < startNote + numKeys)
1011 			{
1012 				NoteIndex nextKey = note + 1;
1013 				if (!isWhiteKey (nextKey))
1014 					nextKey++;
1015 				invalidNote (nextKey);
1016 			}
1017 		}
1018 	}
1019 }
1020 
1021 //------------------------------------------------------------------------
isWhiteKey(NoteIndex note) const1022 bool KeyboardViewBase::isWhiteKey (NoteIndex note) const
1023 {
1024 	note = note % 12;
1025 	return note == 0 || note == 2 || note == 4 || note == 5 || note == 7 || note == 9 || note == 11;
1026 }
1027 
1028 //------------------------------------------------------------------------
setWhiteKeyBitmapInset(const CRect & inset)1029 void KeyboardViewBase::setWhiteKeyBitmapInset (const CRect& inset)
1030 {
1031 	whiteKeyBitmapInset = inset;
1032 }
1033 
1034 //------------------------------------------------------------------------
setBlackKeyBitmapInset(const CRect & inset)1035 void KeyboardViewBase::setBlackKeyBitmapInset (const CRect& inset)
1036 {
1037 	blackKeyBitmapInset = inset;
1038 }
1039 
1040 //------------------------------------------------------------------------
setBitmap(BitmapID bID,CBitmap * bitmap)1041 void KeyboardViewBase::setBitmap (BitmapID bID, CBitmap* bitmap)
1042 {
1043 	bitmaps[static_cast<size_t> (bID)] = bitmap;
1044 	invalid ();
1045 }
1046 
1047 //------------------------------------------------------------------------
getBitmap(BitmapID bID) const1048 CBitmap* KeyboardViewBase::getBitmap (BitmapID bID) const
1049 {
1050 	return bitmaps[static_cast<size_t> (bID)];
1051 }
1052 
1053 //------------------------------------------------------------------------
1054 //------------------------------------------------------------------------
1055 //------------------------------------------------------------------------
1056 static const std::string kAttrWhiteKeyPressed = "white-key-pressed";
1057 static const std::string kAttrWhiteKeyUnpressed = "white-key-unpressed";
1058 static const std::string kAttrBlackKeyPressed = "black-key-pressed";
1059 static const std::string kAttrBlackKeyUnpressed = "black-key-unpressed";
1060 static const std::string kAttrWhiteKeyShadowLeft = "white-key-shadow-left";
1061 static const std::string kAttrWhiteKeyShadowRight = "white-key-shadow-right";
1062 static const std::string kAttrWhiteKeyWidth = "white-key-width";
1063 static const std::string kAttrBlackKeyWidth = "black-key-width";
1064 static const std::string kAttrBlackKeyHeight = "black-key-height";
1065 static const std::string kAttrWhiteKeyColor = "white-key-color";
1066 static const std::string kAttrWhiteKeyPressedColor = "white-key-pressed-color";
1067 static const std::string kAttrBlackKeyColor = "black-key-color";
1068 static const std::string kAttrBlackKeyPressedColor = "black-key-pressed-color";
1069 static const std::string kAttrStartNote = "start-note";
1070 static const std::string kAttrNumKeys = "num-keys";
1071 static const std::string kAttrNoteNameFont = "note-name-font";
1072 static const std::string kAttrDrawNoteText = "draw-note-text";
1073 
1074 using UIViewCreator::stringToColor;
1075 using UIViewCreator::stringToBitmap;
1076 using UIViewCreator::bitmapToString;
1077 using UIViewCreator::colorToString;
1078 
1079 //-----------------------------------------------------------------------------
1080 class KeyboardViewBaseCreator : public ViewCreatorAdapter
1081 {
1082 public:
1083 	using ViewType = KeyboardViewBase;
1084 
getBaseViewName() const1085 	IdStringPtr getBaseViewName () const override { return UIViewCreator::kCView; }
getAttributeNames(std::list<std::string> & attributeNames) const1086 	bool getAttributeNames (std::list<std::string>& attributeNames) const override
1087 	{
1088 		attributeNames.push_back (kAttrWhiteKeyPressed);
1089 		attributeNames.push_back (kAttrWhiteKeyUnpressed);
1090 		attributeNames.push_back (kAttrBlackKeyPressed);
1091 		attributeNames.push_back (kAttrBlackKeyUnpressed);
1092 		attributeNames.push_back (kAttrWhiteKeyShadowLeft);
1093 		attributeNames.push_back (kAttrWhiteKeyShadowRight);
1094 		attributeNames.push_back (kAttrWhiteKeyWidth);
1095 		attributeNames.push_back (kAttrBlackKeyWidth);
1096 		attributeNames.push_back (kAttrBlackKeyHeight);
1097 		attributeNames.push_back (UIViewCreator::kAttrFrameColor);
1098 		attributeNames.push_back (UIViewCreator::kAttrFontColor);
1099 		attributeNames.push_back (kAttrWhiteKeyColor);
1100 		attributeNames.push_back (kAttrWhiteKeyPressedColor);
1101 		attributeNames.push_back (kAttrBlackKeyColor);
1102 		attributeNames.push_back (kAttrBlackKeyPressedColor);
1103 		attributeNames.push_back (UIViewCreator::kAttrFrameWidth);
1104 		attributeNames.push_back (kAttrStartNote);
1105 		attributeNames.push_back (kAttrNumKeys);
1106 		attributeNames.push_back (kAttrDrawNoteText);
1107 		attributeNames.push_back (kAttrNoteNameFont);
1108 		return true;
1109 	}
getAttributeType(const std::string & attributeName) const1110 	AttrType getAttributeType (const std::string& attributeName) const override
1111 	{
1112 		auto bitmapID = attrNameToBitmapID (attributeName);
1113 		if (bitmapID != ViewType::BitmapID::NumBitmaps)
1114 			return kBitmapType;
1115 		if (attributeName == kAttrWhiteKeyWidth)
1116 			return kFloatType;
1117 		if (attributeName == kAttrBlackKeyWidth)
1118 			return kFloatType;
1119 		if (attributeName == kAttrBlackKeyHeight)
1120 			return kFloatType;
1121 		if (attributeName == kAttrStartNote)
1122 			return kIntegerType;
1123 		if (attributeName == kAttrNumKeys)
1124 			return kIntegerType;
1125 		if (attributeName == kAttrDrawNoteText)
1126 			return kBooleanType;
1127 		if (attributeName == kAttrNoteNameFont)
1128 			return kFontType;
1129 		if (attributeName == UIViewCreator::kAttrFrameWidth)
1130 			return kFloatType;
1131 		if (attributeName == UIViewCreator::kAttrFrameColor)
1132 			return kColorType;
1133 		if (attributeName == UIViewCreator::kAttrFontColor)
1134 			return kColorType;
1135 		if (attributeName == kAttrWhiteKeyColor)
1136 			return kColorType;
1137 		if (attributeName == kAttrWhiteKeyPressedColor)
1138 			return kColorType;
1139 		if (attributeName == kAttrBlackKeyColor)
1140 			return kColorType;
1141 		if (attributeName == kAttrBlackKeyPressedColor)
1142 			return kColorType;
1143 		return kUnknownType;
1144 	}
getAttributeValue(CView * view,const std::string & attributeName,std::string & stringValue,const IUIDescription * desc) const1145 	bool getAttributeValue (CView* view, const std::string& attributeName, std::string& stringValue,
1146 	                        const IUIDescription* desc) const override
1147 	{
1148 		auto kv = dynamic_cast<ViewType*> (view);
1149 		if (!kv)
1150 			return false;
1151 		auto bitmapID = attrNameToBitmapID (attributeName);
1152 		if (bitmapID != ViewType::BitmapID::NumBitmaps)
1153 		{
1154 			stringValue = "";
1155 			if (auto bitmap = kv->getBitmap (bitmapID))
1156 				bitmapToString (bitmap, stringValue, desc);
1157 			return true;
1158 		}
1159 		if (attributeName == UIViewCreator::kAttrFrameColor)
1160 		{
1161 			colorToString (kv->getFrameColor (), stringValue, desc);
1162 			return true;
1163 		}
1164 		if (attributeName == UIViewCreator::kAttrFontColor)
1165 		{
1166 			colorToString (kv->getFontColor (), stringValue, desc);
1167 			return true;
1168 		}
1169 		if (attributeName == kAttrWhiteKeyColor)
1170 		{
1171 			colorToString (kv->getWhiteKeyColor (), stringValue, desc);
1172 			return true;
1173 		}
1174 		if (attributeName == kAttrWhiteKeyPressedColor)
1175 		{
1176 			colorToString (kv->getWhiteKeyPressedColor (), stringValue, desc);
1177 			return true;
1178 		}
1179 		if (attributeName == kAttrBlackKeyColor)
1180 		{
1181 			colorToString (kv->getBlackKeyColor (), stringValue, desc);
1182 			return true;
1183 		}
1184 		if (attributeName == kAttrBlackKeyPressedColor)
1185 		{
1186 			colorToString (kv->getBlackKeyPressedColor (), stringValue, desc);
1187 			return true;
1188 		}
1189 		if (attributeName == kAttrWhiteKeyWidth)
1190 		{
1191 			stringValue = numberToString (kv->getWhiteKeyWidth ());
1192 			return true;
1193 		}
1194 		if (attributeName == kAttrBlackKeyWidth)
1195 		{
1196 			stringValue = numberToString (kv->getBlackKeyWidth ());
1197 			return true;
1198 		}
1199 		if (attributeName == kAttrBlackKeyHeight)
1200 		{
1201 			stringValue = numberToString (kv->getBlackKeyHeight ());
1202 			return true;
1203 		}
1204 		if (attributeName == kAttrStartNote)
1205 		{
1206 			stringValue = numberToString<int32_t> (kv->getKeyRangeStart ());
1207 			return true;
1208 		}
1209 		if (attributeName == kAttrNumKeys)
1210 		{
1211 			stringValue = numberToString<int32_t> (kv->getNumKeys ());
1212 			return true;
1213 		}
1214 		if (attributeName == UIViewCreator::kAttrFrameWidth)
1215 		{
1216 			stringValue = numberToString (kv->getLineWidth ());
1217 			return true;
1218 		}
1219 		if (attributeName == kAttrDrawNoteText)
1220 		{
1221 			stringValue = kv->getDrawNoteText () ? "true" : "false";
1222 			return true;
1223 		}
1224 		if (attributeName == kAttrNoteNameFont)
1225 		{
1226 			UTF8StringPtr fontName = desc->lookupFontName (kv->getNoteNameFont ());
1227 			if (fontName)
1228 			{
1229 				stringValue = fontName;
1230 				return true;
1231 			}
1232 			return false;
1233 		}
1234 		return false;
1235 	}
apply(CView * view,const UIAttributes & attributes,const IUIDescription * desc) const1236 	bool apply (CView* view, const UIAttributes& attributes,
1237 	            const IUIDescription* desc) const override
1238 	{
1239 		auto kv = dynamic_cast<ViewType*> (view);
1240 		if (!kv)
1241 			return false;
1242 		CBitmap* bitmap;
1243 		if (stringToBitmap (attributes.getAttributeValue (kAttrWhiteKeyPressed), bitmap, desc))
1244 			kv->setBitmap (ViewType::BitmapID::WhiteKeyPressed, bitmap);
1245 		if (stringToBitmap (attributes.getAttributeValue (kAttrWhiteKeyUnpressed), bitmap, desc))
1246 			kv->setBitmap (ViewType::BitmapID::WhiteKeyUnpressed, bitmap);
1247 		if (stringToBitmap (attributes.getAttributeValue (kAttrBlackKeyPressed), bitmap, desc))
1248 			kv->setBitmap (ViewType::BitmapID::BlackKeyPressed, bitmap);
1249 		if (stringToBitmap (attributes.getAttributeValue (kAttrBlackKeyUnpressed), bitmap, desc))
1250 			kv->setBitmap (ViewType::BitmapID::BlackKeyUnpressed, bitmap);
1251 		if (stringToBitmap (attributes.getAttributeValue (kAttrWhiteKeyShadowLeft), bitmap, desc))
1252 			kv->setBitmap (ViewType::BitmapID::WhiteKeyShadowLeft, bitmap);
1253 		if (stringToBitmap (attributes.getAttributeValue (kAttrWhiteKeyShadowRight), bitmap, desc))
1254 			kv->setBitmap (ViewType::BitmapID::WhiteKeyShadowRight, bitmap);
1255 
1256 		CColor color;
1257 		if (stringToColor (attributes.getAttributeValue (UIViewCreator::kAttrFrameColor), color,
1258 		                   desc))
1259 			kv->setFrameColor (color);
1260 		if (stringToColor (attributes.getAttributeValue (UIViewCreator::kAttrFontColor), color,
1261 		                   desc))
1262 			kv->setFontColor (color);
1263 		if (stringToColor (attributes.getAttributeValue (kAttrWhiteKeyColor), color, desc))
1264 			kv->setWhiteKeyColor (color);
1265 		if (stringToColor (attributes.getAttributeValue (kAttrWhiteKeyPressedColor), color, desc))
1266 			kv->setWhiteKeyPressedColor (color);
1267 		if (stringToColor (attributes.getAttributeValue (kAttrBlackKeyColor), color, desc))
1268 			kv->setBlackKeyColor (color);
1269 		if (stringToColor (attributes.getAttributeValue (kAttrBlackKeyPressedColor), color, desc))
1270 			kv->setBlackKeyPressedColor (color);
1271 
1272 		CCoord c;
1273 		if (attributes.getDoubleAttribute (kAttrWhiteKeyWidth, c))
1274 			kv->setWhiteKeyWidth (c);
1275 		if (attributes.getDoubleAttribute (kAttrBlackKeyWidth, c))
1276 			kv->setBlackKeyWidth (c);
1277 		if (attributes.getDoubleAttribute (kAttrBlackKeyHeight, c))
1278 			kv->setBlackKeyHeight (c);
1279 		if (attributes.getDoubleAttribute (UIViewCreator::kAttrFrameWidth, c))
1280 			kv->setLineWidth (c);
1281 		auto startNote = static_cast<int32_t> (kv->getKeyRangeStart ());
1282 		auto numKeys = static_cast<int32_t> (kv->getNumKeys ());
1283 		attributes.getIntegerAttribute (kAttrStartNote, startNote);
1284 		attributes.getIntegerAttribute (kAttrNumKeys, numKeys);
1285 		kv->setKeyRange (startNote, numKeys);
1286 		bool b;
1287 		if (attributes.getBooleanAttribute (kAttrDrawNoteText, b))
1288 			kv->setDrawNoteText (b);
1289 		if (auto fontName = attributes.getAttributeValue (kAttrNoteNameFont))
1290 			kv->setNoteNameFont (desc->getFont (fontName->data ()));
1291 
1292 		return true;
1293 	}
1294 
getAttributeValueRange(const std::string & attributeName,double & minValue,double & maxValue) const1295 	bool getAttributeValueRange (const std::string& attributeName, double& minValue,
1296 	                             double& maxValue) const override
1297 	{
1298 		if (attributeName == kAttrNumKeys)
1299 		{
1300 			minValue = 12;
1301 			maxValue = KeyboardViewBase::MaxNotes - 1;
1302 			return true;
1303 		}
1304 		return false;
1305 	}
1306 
1307 //-----------------------------------------------------------------------------
1308 	// TODO: this is a clone see uiviewcreator.cpp
1309 	template <typename T>
numberToString(T value)1310 	static std::string numberToString (T value)
1311 	{
1312 		std::stringstream str;
1313 		str << value;
1314 		return str.str ();
1315 	}
1316 
attrNameToBitmapID(const std::string & attributeName)1317 	static ViewType::BitmapID attrNameToBitmapID (const std::string& attributeName)
1318 	{
1319 		if (attributeName == kAttrWhiteKeyPressed)
1320 			return ViewType::BitmapID::WhiteKeyPressed;
1321 		if (attributeName == kAttrWhiteKeyUnpressed)
1322 			return ViewType::BitmapID::WhiteKeyUnpressed;
1323 		if (attributeName == kAttrBlackKeyPressed)
1324 			return ViewType::BitmapID::BlackKeyPressed;
1325 		if (attributeName == kAttrBlackKeyUnpressed)
1326 			return ViewType::BitmapID::BlackKeyUnpressed;
1327 		if (attributeName == kAttrWhiteKeyShadowLeft)
1328 			return ViewType::BitmapID::WhiteKeyShadowLeft;
1329 		if (attributeName == kAttrWhiteKeyShadowRight)
1330 			return ViewType::BitmapID::WhiteKeyShadowRight;
1331 		return ViewType::BitmapID::NumBitmaps;
1332 	}
1333 };
1334 
1335 //------------------------------------------------------------------------
1336 class KeyboardViewCreator : public KeyboardViewBaseCreator
1337 {
1338 public:
KeyboardViewCreator()1339 	KeyboardViewCreator () { UIViewFactory::registerViewCreator (*this); }
getViewName() const1340 	IdStringPtr getViewName () const override { return "KeyboardView"; }
getDisplayName() const1341 	UTF8StringPtr getDisplayName () const override { return "Keyboard"; }
create(const UIAttributes & attributes,const IUIDescription * description) const1342 	CView* create (const UIAttributes& attributes, const IUIDescription* description) const override
1343 	{
1344 		return new KeyboardView ();
1345 	}
1346 };
1347 KeyboardViewCreator __gKeyboardViewCreator;
1348 
1349 static const std::string kAttrSelectionRangeMin = "sel-range-min";
1350 static const std::string kAttrSelectionRangeMax = "sel-range-max";
1351 //------------------------------------------------------------------------
1352 class KeyboardViewRangeSelectorCreator : public KeyboardViewBaseCreator
1353 {
1354 public:
KeyboardViewRangeSelectorCreator()1355 	KeyboardViewRangeSelectorCreator () { UIViewFactory::registerViewCreator (*this); }
getViewName() const1356 	IdStringPtr getViewName () const override { return "KeyboardViewRangeSelector"; }
getDisplayName() const1357 	UTF8StringPtr getDisplayName () const override { return "Keyboard Range Selector"; }
create(const UIAttributes & attributes,const IUIDescription * description) const1358 	CView* create (const UIAttributes& attributes, const IUIDescription* description) const override
1359 	{
1360 		return new KeyboardViewRangeSelector ();
1361 	}
getAttributeNames(std::list<std::string> & attributeNames) const1362 	bool getAttributeNames (std::list<std::string>& attributeNames) const override
1363 	{
1364 		attributeNames.push_back (kAttrSelectionRangeMin);
1365 		attributeNames.push_back (kAttrSelectionRangeMax);
1366 		return KeyboardViewBaseCreator::getAttributeNames (attributeNames);
1367 	}
getAttributeType(const std::string & attributeName) const1368 	AttrType getAttributeType (const std::string& attributeName) const override
1369 	{
1370 		if (attributeName == kAttrSelectionRangeMin)
1371 			return kIntegerType;
1372 		if (attributeName == kAttrSelectionRangeMax)
1373 			return kIntegerType;
1374 		return KeyboardViewBaseCreator::getAttributeType (attributeName);
1375 	}
getAttributeValue(CView * view,const std::string & attributeName,std::string & stringValue,const IUIDescription * desc) const1376 	bool getAttributeValue (CView* view, const std::string& attributeName, std::string& stringValue,
1377 	                        const IUIDescription* desc) const override
1378 	{
1379 		auto kb = dynamic_cast<KeyboardViewRangeSelector*> (view);
1380 		if (!kb)
1381 			return false;
1382 		if (attributeName == kAttrSelectionRangeMin)
1383 		{
1384 			stringValue = numberToString<int32_t> (kb->getSelectionMin ());
1385 			return true;
1386 		}
1387 		if (attributeName == kAttrSelectionRangeMax)
1388 		{
1389 			stringValue = numberToString<int32_t> (kb->getSelectionMax ());
1390 			return true;
1391 		}
1392 		return KeyboardViewBaseCreator::getAttributeValue (view, attributeName, stringValue, desc);
1393 	}
apply(CView * view,const UIAttributes & attributes,const IUIDescription * desc) const1394 	bool apply (CView* view, const UIAttributes& attributes,
1395 	            const IUIDescription* desc) const override
1396 	{
1397 		auto kb = dynamic_cast<KeyboardViewRangeSelector*> (view);
1398 		if (!kb)
1399 			return false;
1400 		auto selMin = static_cast<int32_t> (kb->getSelectionMin ());
1401 		auto selMax = static_cast<int32_t> (kb->getSelectionMax ());
1402 		attributes.getIntegerAttribute (kAttrSelectionRangeMin, selMin);
1403 		attributes.getIntegerAttribute (kAttrSelectionRangeMax, selMax);
1404 		kb->setSelectionMinMax (selMin, selMax);
1405 		return KeyboardViewBaseCreator::apply (view, attributes, desc);
1406 	}
1407 };
1408 KeyboardViewRangeSelectorCreator __gKeyboardViewRangeSelectorCreator;
1409 
1410 //------------------------------------------------------------------------
1411 } // VSTGUI
1412