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