1 /*
2     Foo-YC20 Base UI
3     Copyright (C) 2010  Sampo Savolainen <v2@iki.fi>
4 
5     This program is free software: you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation, either version 3 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #include <yc20-base-ui.h>
20 #include <foo-yc20-os.h>
21 
22 #include <string.h>
23 
24 #ifdef __WIN32__
25 
26 #include <windows.h>
27 
28 struct pointer_t {
29 	unsigned char *ptr;
30 	unsigned int length;
31 	unsigned int at;
32 };
33 
read_from_pointer(void * closure,unsigned char * data,unsigned int length)34 cairo_status_t read_from_pointer(void *closure, unsigned char *data, unsigned int length)
35 {
36 	struct pointer_t *ptr = (struct pointer_t *)closure;
37 
38 	if (ptr->at >= ptr->length) {
39 		return CAIRO_STATUS_SUCCESS;
40 	}
41 
42 	unsigned int len = length;
43 	if (len > (ptr->length - ptr->at)) {
44 		len = ptr->length - ptr->at;
45 	}
46 
47 	memcpy(data, ptr->ptr + ptr->at, len);
48 
49 	ptr->at += len;
50 
51 	return CAIRO_STATUS_SUCCESS;
52 }
53 
54 // For foo-yc20.exe, use 0)
55 // For the VSTi, createEffectInstance() will set this to the value from the dll initialization
56 HINSTANCE cairoResourceInstance = 0;
57 
58 namespace Wdgt
59 {
check_cairo_png(cairo_surface_t * s)60 	bool check_cairo_png(cairo_surface_t *s)
61 	{
62 		cairo_status_t _stat = cairo_surface_status(s);
63 		return !(_stat == CAIRO_STATUS_NO_MEMORY ||
64 				_stat == CAIRO_STATUS_FILE_NOT_FOUND ||
65 				_stat == CAIRO_STATUS_READ_ERROR);
66 
67 	}
68 
load_png(std::string file)69 	inline cairo_surface_t * load_png(std::string file)
70 	{
71 		HRSRC hRes = FindResource(cairoResourceInstance, file.c_str(), RT_RCDATA);
72 		HGLOBAL hMem = LoadResource(cairoResourceInstance, hRes);
73 
74 		struct pointer_t png_resource;
75 		png_resource.ptr    = (unsigned char *)LockResource(hMem);
76 		png_resource.length = SizeofResource(cairoResourceInstance, hRes);
77 		png_resource.at     = 0;
78 
79 		cairo_surface_t *ret = cairo_image_surface_create_from_png_stream (read_from_pointer, &png_resource);
80 		if (!check_cairo_png(ret)) {
81 			std::cerr << "Foo-YC20: could not open resource '" << file << "'" << std::endl;
82 		}
83 		return ret;
84 	}
85 
86 }
87 
88 #else
89 namespace Wdgt
90 {
91 
check_cairo_png(cairo_surface_t * s)92 	bool check_cairo_png(cairo_surface_t *s)
93 	{
94 		cairo_status_t _stat = cairo_surface_status(s);
95 		return !(_stat == CAIRO_STATUS_NO_MEMORY ||
96 				_stat == CAIRO_STATUS_FILE_NOT_FOUND ||
97 				_stat == CAIRO_STATUS_READ_ERROR);
98 
99 	}
100 
load_png(std::string file)101 	inline cairo_surface_t * load_png(std::string file)
102 	{
103 		std::string installed_file = INSTALL_LOCATION YC20_PNG_DIR + file;
104 		std::string local_file = YC20_PNG_DIR + file;
105 
106 		cairo_surface_t *ret = cairo_image_surface_create_from_png (installed_file.c_str());
107 		if (!check_cairo_png(ret)) {
108 			ret = cairo_image_surface_create_from_png (local_file.c_str());
109 		}
110 
111 		if (!check_cairo_png(ret)) {
112 			std::cerr << "Foo-YC20: could not open " << installed_file << " or a local copy in " << YC20_PNG_DIR << std::endl;
113 		}
114 		return ret;
115 	}
116 }
117 #endif
118 
119 
YC20BaseUI()120 YC20BaseUI::YC20BaseUI()
121 	: ui_scale(1.0)
122 	, hoverWdgt(0)
123 	, dragWdgt(0)
124 	, buttonPressWdgt(0)
125 {
126 	image_background = Wdgt::load_png("background.png");
127 
128 	drawbarWhiteImages[0] = Wdgt::load_png("white_0.png");
129 	drawbarWhiteImages[1] = Wdgt::load_png("white_1.png");
130 	drawbarWhiteImages[2] = Wdgt::load_png("white_2.png");
131 	drawbarWhiteImages[3] = Wdgt::load_png("white_3.png");
132 
133 	drawbarBlackImages[0] = Wdgt::load_png("black_0.png");
134 	drawbarBlackImages[1] = Wdgt::load_png("black_1.png");
135 	drawbarBlackImages[2] = Wdgt::load_png("black_2.png");
136 	drawbarBlackImages[3] = Wdgt::load_png("black_3.png");
137 
138 	drawbarGreenImages[0] = Wdgt::load_png("green_0.png");
139 	drawbarGreenImages[1] = Wdgt::load_png("green_1.png");
140 	drawbarGreenImages[2] = Wdgt::load_png("green_2.png");
141 	drawbarGreenImages[3] = Wdgt::load_png("green_3.png");
142 
143 	potentiometerImage = Wdgt::load_png("potentiometer.png");
144 
145 	// Widgets
146 	float pitch_x = 6.0;
147 	float pitch_x_long = 10.0;
148 	float pitch_x_longest = 20.0;
149 
150 	float x = 15.0;
151 	float y = 15.0;
152 
153 	// Pitch, volume & bass volume
154 	Wdgt::Potentiometer *pitch  = new Wdgt::Potentiometer(x, y, -1.0, 1.0, potentiometerImage);
155 	pitch->setName("pitch");
156 	x += 72.0 + pitch_x_longest;
157 
158 	Wdgt::Potentiometer *volume = new Wdgt::Potentiometer(x, y, 0.0, 1.0, potentiometerImage);
159 	volume->setName("volume");
160 	x += 72.0 + pitch_x_longest;
161 
162 	Wdgt::Potentiometer *bass_v = new Wdgt::Potentiometer(x, y, 0.0, 1.0, potentiometerImage);
163 	bass_v->setName("bass volume");
164 	x += 72.0 + pitch_x_longest + pitch_x_long;
165 
166 	wdgts.push_back(pitch);
167 	wdgts.push_back(volume);
168 	wdgts.push_back(bass_v);
169 
170 	// Vibrato section
171 	// Instead of the touch vibrato, we have a realism switch
172 	Wdgt::Drawbar *realism = new Wdgt::Drawbar(x, y, true, DRAWBAR_BLACK_EMPHASIS_ALPHA, drawbarBlackImages);
173 	realism->setName("realism");
174 	x += 40.0 + pitch_x;
175 
176 	Wdgt::Drawbar *vibrato = new Wdgt::Drawbar(x, y, true, DRAWBAR_BLACK_EMPHASIS_ALPHA, drawbarBlackImages);
177 	vibrato->setName("depth");
178 	x += 40.0 + pitch_x;
179 
180 	Wdgt::Drawbar *v_speed = new Wdgt::Drawbar(x, y, true, DRAWBAR_BLACK_EMPHASIS_ALPHA, drawbarBlackImages);
181 	v_speed->setName("speed");
182 	x += 40.0 + pitch_x_longest;
183 
184 	wdgts.push_back(realism);
185 	wdgts.push_back(vibrato);
186 	wdgts.push_back(v_speed);
187 
188 	// Bass
189 	Wdgt::Drawbar *bass_16  = new Wdgt::Drawbar(x, y, true, drawbarWhiteImages);
190 	bass_16->setName("16' b");
191 	x += 40.0 + pitch_x;
192 
193 	Wdgt::Drawbar *bass_8   = new Wdgt::Drawbar(x, y, true, drawbarWhiteImages);
194 	bass_8->setName("8' b");
195 	x += 40.0 + pitch_x;
196 
197 	Wdgt::Switch *bass_man = new Wdgt::Switch(x, y, DRAWBAR_BLACK_EMPHASIS_ALPHA, drawbarBlackImages);
198 	bass_man->setName("bass manual");
199 	x += 40.0 + pitch_x_longest;
200 
201 	wdgts.push_back(bass_16);
202 	wdgts.push_back(bass_8);
203 	wdgts.push_back(bass_man);
204 
205 	// Section I
206 	Wdgt::Drawbar *sect1_16    = new Wdgt::Drawbar(x, y, true, drawbarWhiteImages);
207 	sect1_16->setName("16' i");
208 	x += 40.0 + pitch_x;
209 
210 	Wdgt::Drawbar *sect1_8     = new Wdgt::Drawbar(x, y, true, drawbarWhiteImages);
211 	sect1_8->setName("8' i");
212 	x += 40.0 + pitch_x;
213 
214 	Wdgt::Drawbar *sect1_4     = new Wdgt::Drawbar(x, y, true, drawbarWhiteImages);
215 	sect1_4->setName("4' i");
216 	x += 40.0 + pitch_x;
217 
218 	Wdgt::Drawbar *sect1_2_2p3 = new Wdgt::Drawbar(x, y, true, drawbarWhiteImages);
219 	sect1_2_2p3->setName("2 2/3' i");
220 	x += 40.0 + pitch_x;
221 
222 	Wdgt::Drawbar *sect1_2     = new Wdgt::Drawbar(x, y, true, drawbarWhiteImages);
223 	sect1_2->setName("2' i");
224 	x += 40.0 + pitch_x;
225 
226 	Wdgt::Drawbar *sect1_1_3p5 = new Wdgt::Drawbar(x, y, true, drawbarWhiteImages);
227 	sect1_1_3p5->setName("1 3/5' i");
228 	x += 40.0 + pitch_x;
229 
230 	Wdgt::Drawbar *sect1_1     = new Wdgt::Drawbar(x, y, true, drawbarWhiteImages);
231 	sect1_1->setName("1' i");
232 	x += 40.0 + pitch_x_long;
233 
234 	wdgts.push_back(sect1_16);
235 	wdgts.push_back(sect1_8);
236 	wdgts.push_back(sect1_4);
237 	wdgts.push_back(sect1_2_2p3);
238 	wdgts.push_back(sect1_2);
239 	wdgts.push_back(sect1_1_3p5);
240 	wdgts.push_back(sect1_1);
241 
242 	// Balance & Brightness
243 	Wdgt::Drawbar *balance    = new Wdgt::Drawbar(x, y, false, DRAWBAR_BLACK_EMPHASIS_ALPHA, drawbarBlackImages);
244 	balance->setName("balance");
245 	x += 40.0 + pitch_x_long;
246 
247 	Wdgt::Drawbar *brightness = new Wdgt::Drawbar(x, y, false, DRAWBAR_BLACK_EMPHASIS_ALPHA, drawbarBlackImages);
248 	brightness->setName("bright");
249 	x += 40.0 + pitch_x_long;
250 
251 	wdgts.push_back(balance);
252 	wdgts.push_back(brightness);
253 
254 	// Section II
255 	Wdgt::Drawbar *sect2_16 = new Wdgt::Drawbar(x, y, true, drawbarWhiteImages);
256 	sect2_16->setName("16' ii");
257 	x += 40.0 + pitch_x;
258 
259 	Wdgt::Drawbar *sect2_8  = new Wdgt::Drawbar(x, y, true, drawbarWhiteImages);
260 	sect2_8->setName("8' ii");
261 	x += 40.0 + pitch_x;
262 
263 	Wdgt::Drawbar *sect2_4  = new Wdgt::Drawbar(x, y, true, drawbarWhiteImages);
264 	sect2_4->setName("4' ii");
265 	x += 40.0 + pitch_x;
266 
267 	Wdgt::Drawbar *sect2_2  = new Wdgt::Drawbar(x, y, true, drawbarWhiteImages);
268 	sect2_2->setName("2' ii");
269 	x += 40.0 + pitch_x_long;
270 
271 	sect2_16->setValue(1.0);
272 	sect2_8 ->setValue(0.66);
273 	sect2_4 ->setValue(0.33);
274 	sect2_2 ->setValue(0.0);
275 
276 	wdgts.push_back(sect2_16);
277 	wdgts.push_back(sect2_8);
278 	wdgts.push_back(sect2_4);
279 	wdgts.push_back(sect2_2);
280 
281 	// Percussion
282 	Wdgt::Drawbar *percussive = new Wdgt::Drawbar(x, y, true, drawbarGreenImages);
283 	percussive->setName("percussive");
284 
285 	wdgts.push_back(percussive);
286 
287 
288 	// Make the map
289 	for (std::list<Wdgt::Draggable *>::iterator i = wdgts.begin(); i !=  wdgts.end(); ++i) {
290 		Wdgt::Draggable	*draggable = (*i);
291 		wdgtPerLabel[draggable->getName()] = draggable;
292 	}
293 }
294 
295 void
set_scale(double new_scale)296 YC20BaseUI::set_scale(double new_scale)
297 {
298 	ui_scale = new_scale;
299 }
300 
301 Wdgt::Object *
identify_wdgt(double x,double y)302 YC20BaseUI::identify_wdgt(double x, double y)
303 {
304 	for (std::list<Wdgt::Draggable *>::iterator i = wdgts.begin(); i != wdgts.end(); ) {
305 		Wdgt::Draggable *obj = *i;
306 
307 		if (obj->intersectsPoint(x, y))
308 			return obj;
309 
310 		++i;
311 	}
312 
313 	return 0;
314 }
315 
316 void
mouse_movement(double x,double y)317 YC20BaseUI::mouse_movement(double x, double y)
318 {
319 	x /= ui_scale;
320 	y /= ui_scale;
321 
322 	if (dragWdgt != 0) {
323 
324 		if (dragWdgt->setValueFromDrag(predragValue, dragStartY, y)) {
325 			value_changed(dragWdgt);
326 			draw_wdgt(dragWdgt);
327 		}
328 		return;
329 	}
330 
331 	Wdgt::Object *newHover = identify_wdgt(x, y);
332 	if (newHover == hoverWdgt) {
333 		return;
334 	}
335 
336 	Wdgt::Object *oldHover = hoverWdgt;
337 
338 	hoverWdgt = newHover;
339 
340 	// Redraw ex-hover-widget
341 	if (oldHover != 0) {
342 		draw_wdgt(oldHover);
343 	}
344 
345 	// Redraw new hover-widget
346 	if (hoverWdgt != 0) {
347 		draw_wdgt(hoverWdgt);
348 	}
349 
350 	return;
351 }
352 
353 void
button_pressed(double x,double y)354 YC20BaseUI::button_pressed(double x, double y)
355 {
356 	x /= ui_scale;
357 	y /= ui_scale;
358 
359 	buttonPressWdgt = hoverWdgt;
360 	Wdgt::Draggable *obj = dynamic_cast<Wdgt::Draggable *>(buttonPressWdgt);
361 
362 	if (obj == 0) {
363 		return;
364 	}
365 
366 
367 	predragValue = obj->getValue();
368 
369 	dragWdgt = obj;
370 	dragStartX = x;
371 	dragStartY = y;
372 
373 	return;
374 }
375 
376 void
button_released(double x,double y)377 YC20BaseUI::button_released(double x, double y)
378 {
379 	x /= ui_scale;
380 	y /= ui_scale;
381 
382 	Wdgt::Object *exposeObj = 0;
383 
384 	if (dragWdgt != 0) {
385 		exposeObj = dragWdgt;
386 	}
387 
388 	buttonPressWdgt = 0;
389 	dragWdgt = 0;
390 	hoverWdgt = 0;
391 
392 	if (exposeObj != 0) {
393 		draw_wdgt(exposeObj);
394 	}
395 
396 	return;
397 
398 }
399 
400 void
draw_wdgt(Wdgt::Object * obj)401 YC20BaseUI::draw_wdgt(Wdgt::Object *obj)
402 {
403 	draw(obj->x1, obj->y1, obj->x2 - obj->x1, obj->y2 - obj->y1, false);
404 
405 	for (std::list<Wdgt::Object *>::iterator i = obj->dependents.begin(); i != obj->dependents.end(); ) {
406 		Wdgt::Object *dep = *i;
407 
408 		draw_wdgt(dep);
409 
410 		++i;
411 	}
412 }
413 
414 // x < 0 => don't clip
415 void
draw(double x,double y,double width,double height,bool scale)416 YC20BaseUI::draw(double x, double y, double width, double height, bool scale)
417 {
418 	bool clip = (x >= 0.0);
419 
420 	if (scale) {
421 		x	/= ui_scale;
422 		y	/= ui_scale;
423 		width	/= ui_scale;
424 		height	/= ui_scale;
425 	}
426 
427 	width++;
428 	height++;
429 
430 	cairo_t *cr = get_cairo_surface();
431 	if (cr == 0) {
432 		// Not realized yet
433 		return;
434 	}
435 
436 	cairo_scale(cr, ui_scale, ui_scale);
437 
438 	// double-buffer
439 	cairo_push_group_with_content(cr, CAIRO_CONTENT_COLOR);
440 
441 	// background
442 	cairo_set_source_surface(cr, image_background, 0.0, 0.0);
443 	// e4080a
444 	//cairo_set_source_rgb(cr, 228.0/255.0, 8.0/255.0, 10.0/255.0);
445 	cairo_paint(cr);
446 
447 	// wdgts
448 	for (std::list<Wdgt::Draggable *>::iterator i = wdgts.end(); i != wdgts.begin(); ) {
449 		--i;
450 
451 		Wdgt::Draggable *obj = *i;
452 
453 		if (!clip || obj->intersectsRectangle(x, y, width, height)) {
454 			obj->drawWidget( (hoverWdgt == obj), cr);
455 		}
456 	}
457 
458 	// finish drawing (retrieve double-buffer & draw it)
459 	cairo_pattern_t *bg = cairo_pop_group(cr);
460 
461 	cairo_copy_page(cr);
462 
463 	if (clip) {
464 		cairo_rectangle(cr, x, y, width+1, height+1);
465 		cairo_clip(cr);
466 	}
467 
468 
469 	cairo_set_source(cr,bg);
470 	cairo_paint(cr);
471 
472 	if (clip) {
473 		cairo_reset_clip(cr);
474 	}
475 
476 	cairo_pattern_destroy(bg);
477 
478 	return_cairo_surface(cr);
479 }
480 
~YC20BaseUI()481 YC20BaseUI::~YC20BaseUI()
482 {
483         for (std::list<Wdgt::Draggable *>::iterator i = wdgts.begin(); i != wdgts.end(); ) {
484                 Wdgt::Draggable *obj = *i;
485                 ++i;
486                 delete obj;
487         }
488 	wdgts.clear();
489 
490 	cairo_surface_destroy(image_background);
491 
492 	for (int i = 0; i < 4; i++) {
493 		cairo_surface_destroy(drawbarBlackImages[i]);
494 		cairo_surface_destroy(drawbarWhiteImages[i]);
495 		cairo_surface_destroy(drawbarGreenImages[i]);
496 	}
497 
498 	cairo_surface_destroy(potentiometerImage);
499 }
500 
501