Rack: Menu with custom icons
[calf.git] / src / custom_ctl.cpp
blob84743c2ed327fe2b661e1273ad1d1afe5bed74f7
1 /* Calf DSP Library
2 * Custom controls (line graph, knob).
3 * Copyright (C) 2007-2010 Krzysztof Foltman, Torben Hohn, Markus Schmidt
4 * and others
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
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 GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General
17 * Public License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301 USA
21 #include "config.h"
22 #include <calf/giface.h>
23 #include <calf/custom_ctl.h>
24 #include <gdk/gdkkeysyms.h>
25 #include <math.h>
26 #include <gdk/gdk.h>
27 #include <gtk/gtk.h>
28 #include <sys/time.h>
29 #include <iostream>
31 using namespace calf_plugins;
32 using namespace dsp;
34 ///////////////////////////////////////// utility functions ///////////////////////////////////////////////
36 void line_graph_background(cairo_t* c, int x, int y, int sx, int sy, int ox, int oy, float brightness, int shadow, float lights, float dull)
38 float br = brightness * 0.5 + 0.5;
40 // outer frame (black)
41 int pad = 0;
43 cairo_rectangle(
44 c, pad + x, pad + y, sx + ox * 2 - pad * 2, sy + oy * 2 - pad * 2);
45 cairo_set_source_rgb(c, 0, 0, 0);
46 cairo_fill(c);
48 // black light effect
49 pad = 1;
50 cairo_rectangle(
51 c, pad + x, pad + y, sx + ox * 2 - pad * 2, sy + oy * 2 - pad * 2);
52 cairo_pattern_t *pat2 = cairo_pattern_create_linear (
53 x, y, x, y + sy + oy * 2 - pad * 2);
54 cairo_pattern_add_color_stop_rgba (pat2, 0, 0.23, 0.23, 0.23, 1);
55 cairo_pattern_add_color_stop_rgba (pat2, 0.33, 0.13, 0.13, 0.13, 1);
56 cairo_pattern_add_color_stop_rgba (pat2, 0.33, 0.05, 0.05, 0.05, 1);
57 cairo_pattern_add_color_stop_rgba (pat2, 0.5, 0, 0, 0, 1);
58 cairo_set_source (c, pat2);
59 cairo_fill(c);
60 cairo_pattern_destroy(pat2);
62 cairo_rectangle(c, x + ox - 1, y + oy - 1, sx + 2, sy + 2);
63 cairo_set_source_rgb (c, 0, 0, 0);
64 cairo_fill(c);
66 // inner yellowish screen
67 cairo_pattern_t *pt = cairo_pattern_create_linear(x + ox, y + oy, x + ox, y + sy);
68 cairo_pattern_add_color_stop_rgb(pt, 0.0, br * 0.71, br * 0.82, br * 0.33);
69 cairo_pattern_add_color_stop_rgb(pt, 1.0, br * 0.89, br * 1.00, br * 0.54);
70 cairo_set_source (c, pt);
71 cairo_rectangle(c, x + ox, y + oy, sx, sy);
72 cairo_fill(c);
73 cairo_pattern_destroy(pt);
75 if (shadow) {
76 // top shadow
77 pt = cairo_pattern_create_linear(x + ox, y + oy, x + ox, y + oy + shadow);
78 cairo_pattern_add_color_stop_rgba(pt, 0.0, 0,0,0,0.6);
79 cairo_pattern_add_color_stop_rgba(pt, 1.0, 0,0,0,0);
80 cairo_set_source (c, pt);
81 cairo_rectangle(c, x + ox, y + oy, sx, shadow);
82 cairo_fill(c);
83 cairo_pattern_destroy(pt);
85 // left shadow
86 pt = cairo_pattern_create_linear(x + ox, y + oy, x + ox + (float)shadow * 0.7, y + oy);
87 cairo_pattern_add_color_stop_rgba(pt, 0.0, 0,0,0,0.3);
88 cairo_pattern_add_color_stop_rgba(pt, 1.0, 0,0,0,0);
89 cairo_set_source (c, pt);
90 cairo_rectangle(c, x + ox, y + oy, (float)shadow * 0.7, sy);
91 cairo_fill(c);
92 cairo_pattern_destroy(pt);
94 // right shadow
95 pt = cairo_pattern_create_linear(x + ox + sx - (float)shadow * 0.7, y + oy, x + ox + sx, y + oy);
96 cairo_pattern_add_color_stop_rgba(pt, 0.0, 0,0,0,0);
97 cairo_pattern_add_color_stop_rgba(pt, 1.0, 0,0,0,0.3);
98 cairo_set_source (c, pt);
99 cairo_rectangle(c, x + ox + sx - (float)shadow * 0.7, y + oy, 5, sy);
100 cairo_fill(c);
101 cairo_pattern_destroy(pt);
104 if(dull) {
105 // left dull
106 pt = cairo_pattern_create_linear(x + ox, y + oy, x + ox + sx / 2, y + oy);
107 cairo_pattern_add_color_stop_rgba(pt, 0.0, 0,0,0,dull);
108 cairo_pattern_add_color_stop_rgba(pt, 1.0, 0,0,0,0);
109 cairo_set_source (c, pt);
110 cairo_rectangle(c, x + ox, y + oy, sx / 2, sy);
111 cairo_fill(c);
112 cairo_pattern_destroy(pt);
114 // right dull
115 pt = cairo_pattern_create_linear(x + ox + sx / 2, y + oy, x + ox + sx, y + oy);
116 cairo_pattern_add_color_stop_rgba(pt, 0.0, 0,0,0,0);
117 cairo_pattern_add_color_stop_rgba(pt, 1.0, 0,0,0,dull);
118 cairo_set_source (c, pt);
119 cairo_rectangle(c, x + ox + sx / 2, y + oy, sx / 2, sy);
120 cairo_fill(c);
121 cairo_pattern_destroy(pt);
124 if(lights > 0) {
125 // light sources
126 int div = 1;
127 int light_w = sx;
128 while(light_w / div > 300)
129 div += 1;
130 int w = sx / div;
131 cairo_rectangle(c, x + ox, y + oy, sx, sy);
132 for(int i = 0; i < div; i ++) {
133 cairo_pattern_t *pt = cairo_pattern_create_radial(
134 x + ox + w * i + w / 2.f, y + oy, 1,
135 x + ox + w * i + w / 2.f, y + ox + sy * 0.25, w / 2.f);
136 cairo_pattern_add_color_stop_rgba (pt, 0, 1, 1, 0.8, lights);
137 cairo_pattern_add_color_stop_rgba (pt, 1, 0.89, 1.00, 0.45, 0);
138 cairo_set_source (c, pt);
139 cairo_fill_preserve(c);
140 pt = cairo_pattern_create_radial(
141 x + ox + w * i + w / 2.f, y + oy + sy, 1,
142 x + ox + w * i + w / 2.f, y + ox + sy * 0.75, w / 2.f);
143 cairo_pattern_add_color_stop_rgba (pt, 0, 1, 1, 0.8, lights);
144 cairo_pattern_add_color_stop_rgba (pt, 1, 0.89, 1.00, 0.45, 0);
145 cairo_set_source (c, pt);
146 cairo_fill_preserve(c);
151 ///////////////////////////////////////// phase graph ///////////////////////////////////////////////
153 static void
154 calf_phase_graph_draw_background( cairo_t *ctx, int sx, int sy, int ox, int oy )
156 int cx = ox + sx / 2;
157 int cy = oy + sy / 2;
159 line_graph_background(ctx, 0, 0, sx, sy, ox, oy);
160 cairo_set_source_rgb(ctx, 0.35, 0.4, 0.2);
161 cairo_select_font_face(ctx, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
162 cairo_set_font_size(ctx, 9);
163 cairo_text_extents_t te;
165 cairo_text_extents (ctx, "M", &te);
166 cairo_move_to (ctx, cx + 5, oy + 12);
167 cairo_show_text (ctx, "M");
169 cairo_text_extents (ctx, "S", &te);
170 cairo_move_to (ctx, ox + 5, cy - 5);
171 cairo_show_text (ctx, "S");
173 cairo_text_extents (ctx, "L", &te);
174 cairo_move_to (ctx, ox + 18, oy + 12);
175 cairo_show_text (ctx, "L");
177 cairo_text_extents (ctx, "R", &te);
178 cairo_move_to (ctx, ox + sx - 22, oy + 12);
179 cairo_show_text (ctx, "R");
181 cairo_set_line_width(ctx, 1);
183 cairo_move_to(ctx, ox, oy + sy * 0.5);
184 cairo_line_to(ctx, ox + sx, oy + sy * 0.5);
185 cairo_stroke(ctx);
187 cairo_move_to(ctx, ox + sx * 0.5, oy);
188 cairo_line_to(ctx, ox + sx * 0.5, oy + sy);
189 cairo_stroke(ctx);
191 cairo_set_source_rgba(ctx, 0, 0, 0, 0.2);
192 cairo_move_to(ctx, ox, oy);
193 cairo_line_to(ctx, ox + sx, oy + sy);
194 cairo_stroke(ctx);
196 cairo_move_to(ctx, ox, oy + sy);
197 cairo_line_to(ctx, ox + sx, oy);
198 cairo_stroke(ctx);
200 static void
201 calf_phase_graph_copy_surface(cairo_t *ctx, cairo_surface_t *source, float fade = 1.f)
203 // copy a surface to a cairo context
204 cairo_save(ctx);
205 cairo_set_source_surface(ctx, source, 0, 0);
206 if (fade < 1.0) {
207 float rnd = (float)rand() / (float)RAND_MAX / 100;
208 cairo_paint_with_alpha(ctx, fade * 0.35 + 0.05 + rnd);
209 } else {
210 cairo_paint(ctx);
212 cairo_restore(ctx);
214 static gboolean
215 calf_phase_graph_expose (GtkWidget *widget, GdkEventExpose *event)
217 g_assert(CALF_IS_PHASE_GRAPH(widget));
218 CalfPhaseGraph *pg = CALF_PHASE_GRAPH(widget);
219 if (!pg->source)
220 return FALSE;
222 // dimensions
223 int ox = 5, oy = 5;
224 int sx = widget->allocation.width - ox * 2, sy = widget->allocation.height - oy * 2;
225 sx += sx % 2 - 1;
226 sy += sy % 2 - 1;
227 int rad = sx / 2;
228 int cx = ox + sx / 2;
229 int cy = oy + sy / 2;
231 // some values as pointers for the audio plug-in call
232 std::string legend;
233 float *data = new float[2 * sx];
234 float * phase_buffer = 0;
235 int length = 0;
236 int mode = 2;
237 float fade = 0.05;
238 bool use_fade = true;
239 int accuracy = 1;
240 bool display = true;
242 // cairo initialization stuff
243 cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(widget->window));
244 cairo_t *ctx_back;
245 cairo_t *ctx_cache;
247 if( pg->background == NULL ) {
248 // looks like its either first call or the widget has been resized.
249 // create the background surface (stolen from line graph)...
250 cairo_surface_t *window_surface = cairo_get_target(c);
251 pg->background = cairo_surface_create_similar(window_surface,
252 CAIRO_CONTENT_COLOR,
253 widget->allocation.width,
254 widget->allocation.height );
255 pg->cache = cairo_surface_create_similar(window_surface,
256 CAIRO_CONTENT_COLOR,
257 widget->allocation.width,
258 widget->allocation.height );
260 // ...and draw some bling bling onto it...
261 ctx_back = cairo_create(pg->background);
262 calf_phase_graph_draw_background(ctx_back, sx, sy, ox, oy);
263 // ...and copy it to the cache
264 ctx_cache = cairo_create(pg->cache);
265 calf_phase_graph_copy_surface(ctx_cache, pg->background, 1);
266 } else {
267 ctx_back = cairo_create(pg->background);
268 ctx_cache = cairo_create(pg->cache);
271 pg->source->get_phase_graph(&phase_buffer, &length, &mode, &use_fade,
272 &fade, &accuracy, &display);
274 // process some values set by the plug-in
275 accuracy *= 2;
276 accuracy = 12 - accuracy;
278 calf_phase_graph_copy_surface(ctx_cache, pg->background, use_fade ? fade : 1);
280 if(display) {
281 cairo_rectangle(ctx_cache, ox, oy, sx, sy);
282 cairo_clip(ctx_cache);
283 cairo_set_source_rgba(ctx_cache, 0.35, 0.4, 0.2, 1);
284 double _a;
285 for(int i = 0; i < length; i+= accuracy) {
286 float l = phase_buffer[i];
287 float r = phase_buffer[i + 1];
288 if(l == 0.f and r == 0.f) continue;
289 else if(r == 0.f and l > 0.f) _a = M_PI / 2.f;
290 else if(r == 0.f and l < 0.f) _a = 3.f *M_PI / 2.f;
291 else _a = pg->_atan(l / r, l, r);
292 double _R = sqrt(pow(l, 2) + pow(r, 2));
293 _a += M_PI / 4.f;
294 float x = (-1.f)*_R * cos(_a);
295 float y = _R * sin(_a);
296 // mask the cached values
297 switch(mode) {
298 case 0:
299 // small dots
300 cairo_rectangle (ctx_cache, x * rad + cx, y * rad + cy, 1, 1);
301 break;
302 case 1:
303 // medium dots
304 cairo_rectangle (ctx_cache, x * rad + cx - 0.25, y * rad + cy - 0.25, 1.5, 1.5);
305 break;
306 case 2:
307 // big dots
308 cairo_rectangle (ctx_cache, x * rad + cx - 0.5, y * rad + cy - 0.5, 2, 2);
309 break;
310 case 3:
311 // fields
312 if(i == 0) cairo_move_to(ctx_cache,
313 x * rad + cx, y * rad + cy);
314 else cairo_line_to(ctx_cache,
315 x * rad + cx, y * rad + cy);
316 break;
317 case 4:
318 // lines
319 if(i == 0) cairo_move_to(ctx_cache,
320 x * rad + cx, y * rad + cy);
321 else cairo_line_to(ctx_cache,
322 x * rad + cx, y * rad + cy);
323 break;
326 // draw
327 switch(mode) {
328 case 0:
329 case 1:
330 case 2:
331 cairo_fill(ctx_cache);
332 break;
333 case 3:
334 cairo_fill(ctx_cache);
335 break;
336 case 4:
337 cairo_set_line_width(ctx_cache, 0.5);
338 cairo_stroke(ctx_cache);
339 break;
343 calf_phase_graph_copy_surface(c, pg->cache, 1);
345 cairo_destroy(c);
346 cairo_destroy(ctx_back);
347 cairo_destroy(ctx_cache);
348 delete []data;
349 // printf("exposed %p %dx%d %d+%d\n", widget->window, event->area.x, event->area.y, event->area.width, event->area.height);
350 return TRUE;
353 static void
354 calf_phase_graph_size_request (GtkWidget *widget,
355 GtkRequisition *requisition)
357 g_assert(CALF_IS_PHASE_GRAPH(widget));
358 // CalfLineGraph *lg = CALF_LINE_GRAPH(widget);
361 static void
362 calf_phase_graph_size_allocate (GtkWidget *widget,
363 GtkAllocation *allocation)
365 g_assert(CALF_IS_PHASE_GRAPH(widget));
366 CalfPhaseGraph *lg = CALF_PHASE_GRAPH(widget);
368 GtkWidgetClass *parent_class = (GtkWidgetClass *) g_type_class_peek_parent( CALF_PHASE_GRAPH_GET_CLASS( lg ) );
370 if(lg->background)
371 cairo_surface_destroy(lg->background);
372 lg->background = NULL;
374 widget->allocation = *allocation;
375 GtkAllocation &a = widget->allocation;
376 if (a.width > a.height) {
377 a.x += (a.width - a.height) / 2;
378 a.width = a.height;
380 if (a.width < a.height) {
381 a.y += (a.height - a.width) / 2;
382 a.height = a.width;
384 parent_class->size_allocate(widget, &a);
387 static void
388 calf_phase_graph_class_init (CalfPhaseGraphClass *klass)
390 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
391 widget_class->expose_event = calf_phase_graph_expose;
392 widget_class->size_request = calf_phase_graph_size_request;
393 widget_class->size_allocate = calf_phase_graph_size_allocate;
396 static void
397 calf_phase_graph_unrealize (GtkWidget *widget, CalfPhaseGraph *pg)
399 if( pg->background )
400 cairo_surface_destroy(pg->background);
401 pg->background = NULL;
404 static void
405 calf_phase_graph_init (CalfPhaseGraph *self)
407 GtkWidget *widget = GTK_WIDGET(self);
408 widget->requisition.width = 40;
409 widget->requisition.height = 40;
410 self->background = NULL;
411 g_signal_connect(GTK_OBJECT(widget), "unrealize", G_CALLBACK(calf_phase_graph_unrealize), (gpointer)self);
414 GtkWidget *
415 calf_phase_graph_new()
417 return GTK_WIDGET(g_object_new (CALF_TYPE_PHASE_GRAPH, NULL));
420 GType
421 calf_phase_graph_get_type (void)
423 static GType type = 0;
424 if (!type) {
425 static const GTypeInfo type_info = {
426 sizeof(CalfPhaseGraphClass),
427 NULL, /* base_init */
428 NULL, /* base_finalize */
429 (GClassInitFunc)calf_phase_graph_class_init,
430 NULL, /* class_finalize */
431 NULL, /* class_data */
432 sizeof(CalfPhaseGraph),
433 0, /* n_preallocs */
434 (GInstanceInitFunc)calf_phase_graph_init
437 GTypeInfo *type_info_copy = new GTypeInfo(type_info);
439 for (int i = 0; ; i++) {
440 char *name = g_strdup_printf("CalfPhaseGraph%u%d", ((unsigned int)(intptr_t)calf_phase_graph_class_init) >> 16, i);
441 if (g_type_from_name(name)) {
442 free(name);
443 continue;
445 type = g_type_register_static( GTK_TYPE_DRAWING_AREA,
446 name,
447 type_info_copy,
448 (GTypeFlags)0);
449 free(name);
450 break;
453 return type;
457 ///////////////////////////////////////// toggle ///////////////////////////////////////////////
459 static gboolean
460 calf_toggle_expose (GtkWidget *widget, GdkEventExpose *event)
462 g_assert(CALF_IS_TOGGLE(widget));
464 CalfToggle *self = CALF_TOGGLE(widget);
466 float sx = self->size ? self->size : 1.f / 3.f * 2.f;
467 float sy = self->size ? self->size : 1;
468 int x = widget->allocation.x + widget->allocation.width / 2 - sx * 15 - sx * 2;
469 int y = widget->allocation.y + widget->allocation.height / 2 - sy * 10 - sy * 3;
470 int width = sx * 34;
471 int height = sy * 26;
473 gdk_draw_pixbuf(GDK_DRAWABLE(widget->window),
474 widget->style->fg_gc[0],
475 self->toggle_image[self->size],
476 20 - sx * 2,
477 20 - sy * 3 + (sy * 20 + 40) * floor(.5 + gtk_range_get_value(GTK_RANGE(widget))),
480 width,
481 height,
482 GDK_RGB_DITHER_NORMAL, 0, 0);
483 return TRUE;
486 static void
487 calf_toggle_size_request (GtkWidget *widget,
488 GtkRequisition *requisition)
490 g_assert(CALF_IS_TOGGLE(widget));
492 CalfToggle *self = CALF_TOGGLE(widget);
494 float sx = self->size ? self->size : 1.f / 3.f * 2.f;
495 float sy = self->size ? self->size : 1;
497 requisition->width = 30 * sx;
498 requisition->height = 20 * sy;
501 static gboolean
502 calf_toggle_button_press (GtkWidget *widget, GdkEventButton *event)
504 g_assert(CALF_IS_TOGGLE(widget));
505 GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(widget));
506 if (gtk_range_get_value(GTK_RANGE(widget)) == adj->lower)
508 gtk_range_set_value(GTK_RANGE(widget), adj->upper);
509 } else {
510 gtk_range_set_value(GTK_RANGE(widget), adj->lower);
512 return TRUE;
515 static gboolean
516 calf_toggle_key_press (GtkWidget *widget, GdkEventKey *event)
518 switch(event->keyval)
520 case GDK_Return:
521 case GDK_KP_Enter:
522 case GDK_space:
523 return calf_toggle_button_press(widget, NULL);
525 return FALSE;
528 static void
529 calf_toggle_class_init (CalfToggleClass *klass)
531 // GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
532 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
533 widget_class->expose_event = calf_toggle_expose;
534 widget_class->size_request = calf_toggle_size_request;
535 widget_class->button_press_event = calf_toggle_button_press;
536 widget_class->key_press_event = calf_toggle_key_press;
539 static void
540 calf_toggle_init (CalfToggle *self)
542 GtkWidget *widget = GTK_WIDGET(self);
543 GTK_WIDGET_SET_FLAGS (GTK_WIDGET(self), GTK_CAN_FOCUS);
544 widget->requisition.width = 30;
545 widget->requisition.height = 20;
546 self->size = 1;
547 GError *error = NULL;
548 self->toggle_image[0] = gdk_pixbuf_new_from_file(PKGLIBDIR "/toggle0_silver.png", &error);
549 self->toggle_image[1] = gdk_pixbuf_new_from_file(PKGLIBDIR "/toggle1_silver.png", &error);
550 self->toggle_image[2] = gdk_pixbuf_new_from_file(PKGLIBDIR "/toggle2_silver.png", &error);
551 g_assert(self->toggle_image != NULL);
554 GtkWidget *
555 calf_toggle_new()
557 GtkAdjustment *adj = (GtkAdjustment *)gtk_adjustment_new(0, 0, 1, 1, 0, 0);
558 return calf_toggle_new_with_adjustment(adj);
561 static gboolean calf_toggle_value_changed(gpointer obj)
563 GtkWidget *widget = (GtkWidget *)obj;
564 CalfToggle *self = CALF_TOGGLE(widget);
565 float sx = self->size ? self->size : 1.f / 3.f * 2.f;
566 float sy = self->size ? self->size : 1;
567 gtk_widget_queue_draw_area(widget,
568 widget->allocation.x - sx * 2,
569 widget->allocation.y - sy * 3,
570 self->size * 34,
571 self->size * 26);
572 return FALSE;
575 GtkWidget *calf_toggle_new_with_adjustment(GtkAdjustment *_adjustment)
577 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_TOGGLE, NULL ));
578 if (widget) {
579 gtk_range_set_adjustment(GTK_RANGE(widget), _adjustment);
580 g_signal_connect(GTK_OBJECT(widget), "value-changed", G_CALLBACK(calf_toggle_value_changed), widget);
582 return widget;
585 GType
586 calf_toggle_get_type (void)
588 static GType type = 0;
589 if (!type) {
591 static const GTypeInfo type_info = {
592 sizeof(CalfToggleClass),
593 NULL, /* base_init */
594 NULL, /* base_finalize */
595 (GClassInitFunc)calf_toggle_class_init,
596 NULL, /* class_finalize */
597 NULL, /* class_data */
598 sizeof(CalfToggle),
599 0, /* n_preallocs */
600 (GInstanceInitFunc)calf_toggle_init
603 for (int i = 0; ; i++) {
604 char *name = g_strdup_printf("CalfToggle%u%d",
605 ((unsigned int)(intptr_t)calf_toggle_class_init) >> 16, i);
606 if (g_type_from_name(name)) {
607 free(name);
608 continue;
610 type = g_type_register_static( GTK_TYPE_RANGE,
611 name,
612 &type_info,
613 (GTypeFlags)0);
614 free(name);
615 break;
618 return type;
621 ///////////////////////////////////////// frame ///////////////////////////////////////////////
624 GtkWidget *
625 calf_frame_new(const char *label)
627 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_FRAME, NULL ));
628 CalfFrame *self = CALF_FRAME(widget);
629 gtk_frame_set_label(GTK_FRAME(self), label);
630 return widget;
632 static gboolean
633 calf_frame_expose (GtkWidget *widget, GdkEventExpose *event)
635 g_assert(CALF_IS_FRAME(widget));
636 if (gtk_widget_is_drawable (widget)) {
638 GdkWindow *window = widget->window;
639 cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(window));
640 cairo_text_extents_t extents;
642 int ox = widget->allocation.x;
643 int oy = widget->allocation.y;
644 int sx = widget->allocation.width;
645 int sy = widget->allocation.height;
647 double rad = 8;
648 double a = 1.5;
649 double pad = 4;
650 double txp = 4;
651 double m = 1;
652 double size = 10;
654 cairo_rectangle(c, ox, oy, sx, sy);
655 cairo_clip(c);
658 const gchar *lab = gtk_frame_get_label(GTK_FRAME(widget));
660 cairo_select_font_face(c, "Sans",
661 CAIRO_FONT_SLANT_NORMAL,
662 CAIRO_FONT_WEIGHT_NORMAL);
663 cairo_set_font_size(c, size);
665 cairo_text_extents(c, lab, &extents);
667 double lw = extents.width + txp * 2.;
669 cairo_set_line_width(c, 1.);
671 cairo_move_to(c, ox + rad + txp + m, oy + size - 2 + m);
672 cairo_set_source_rgb(c, 0.99,0.99,0.99);
673 cairo_show_text(c, lab);
675 cairo_set_source_rgb(c, 0.9,0.9,0.9);
677 rad = 8;
678 cairo_move_to(c, ox + a + m, oy + pad + rad + a + m);
679 cairo_arc (c, ox + rad + a + m, oy + rad + a + pad + m, rad, 1 * M_PI, 1.5 * M_PI);
680 cairo_move_to(c, ox + rad + a + lw + m, oy + a + pad + m);
681 cairo_line_to(c, ox + sx + a - rad - m - 1, oy + a + pad + m);
682 cairo_arc (c, ox + sx - rad + a - 2*m - 1, oy + rad + a + pad + m, rad, 1.5 * M_PI, 2 * M_PI);
683 cairo_line_to(c, ox + sx + a - 2*m - 1, oy + a + sy - rad - 2*m - 1);
684 rad = 9;
685 cairo_arc (c, ox + sx - rad + a - 2*m - 1, oy + sy - rad + a - 2*m - 1, rad, 0 * M_PI, 0.5 * M_PI);
686 rad = 8;
687 cairo_line_to(c, ox + a + rad + m, oy + sy + a - 2*m - 1);
688 cairo_arc (c, ox + rad + a + m, oy + sy - rad + a - 2*m - 1, rad, 0.5 * M_PI, 1 * M_PI);
689 cairo_line_to(c, ox + a + m, oy + a + rad + pad + m);
690 cairo_stroke(c);
692 a = 0.5;
694 cairo_set_source_rgb(c, 0.66,0.66,0.66);
696 rad = 9;
697 cairo_move_to(c, ox + a + m, oy + pad + rad + a + m);
698 cairo_arc (c, ox + rad + a + m, oy + rad + a + pad + m, rad, 1 * M_PI, 1.5 * M_PI);
699 cairo_move_to(c, ox + rad + a + lw + m, oy + a + pad + m);
700 rad = 8;
701 cairo_line_to(c, ox + sx + a - rad - m, oy + a + pad + m);
702 cairo_arc (c, ox + sx - rad + a - 2*m - 1, oy + rad + a + pad + m, rad, 1.5 * M_PI, 2 * M_PI);
703 cairo_line_to(c, ox + sx + a - 2*m - 1, oy + a + sy - rad - 2*m);
704 cairo_arc (c, ox + sx - rad + a - 2*m - 1, oy + sy - rad + a - 2*m - 1, rad, 0 * M_PI, 0.5 * M_PI);
705 cairo_line_to(c, ox + a + rad + m, oy + sy + a - 2*m - 1);
706 cairo_arc (c, ox + rad + a + m, oy + sy - rad + a - 2*m - 1, rad, 0.5 * M_PI, 1 * M_PI);
707 cairo_line_to(c, ox + a + m, oy + a + rad + pad + m);
708 cairo_stroke(c);
710 cairo_destroy(c);
712 if (gtk_bin_get_child(GTK_BIN(widget))) {
713 gtk_container_propagate_expose(GTK_CONTAINER(widget),
714 gtk_bin_get_child(GTK_BIN(widget)),
715 event);
717 return FALSE;
720 static void
721 calf_frame_class_init (CalfFrameClass *klass)
723 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
724 widget_class->expose_event = calf_frame_expose;
727 static void
728 calf_frame_init (CalfFrame *self)
730 GtkWidget *widget = GTK_WIDGET(self);
731 widget->requisition.width = 40;
732 widget->requisition.height = 40;
735 GType
736 calf_frame_get_type (void)
738 static GType type = 0;
739 if (!type) {
740 static const GTypeInfo type_info = {
741 sizeof(CalfFrameClass),
742 NULL, /* base_init */
743 NULL, /* base_finalize */
744 (GClassInitFunc)calf_frame_class_init,
745 NULL, /* class_finalize */
746 NULL, /* class_data */
747 sizeof(CalfFrame),
748 0, /* n_preallocs */
749 (GInstanceInitFunc)calf_frame_init
752 for (int i = 0; ; i++) {
753 char *name = g_strdup_printf("CalfFrame%u%d",
754 ((unsigned int)(intptr_t)calf_frame_class_init) >> 16, i);
755 if (g_type_from_name(name)) {
756 free(name);
757 continue;
759 type = g_type_register_static(GTK_TYPE_FRAME,
760 name,
761 &type_info,
762 (GTypeFlags)0);
763 free(name);
764 break;
767 return type;
770 ///////////////////////////////////////// combo box ///////////////////////////////////////////////
773 GtkWidget *
774 calf_combobox_new()
776 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_COMBOBOX, NULL ));
777 return widget;
779 static gboolean
780 calf_combobox_expose (GtkWidget *widget, GdkEventExpose *event)
782 g_assert(CALF_IS_COMBOBOX(widget));
784 if (gtk_widget_is_drawable (widget)) {
786 int pad = 4;
788 GtkTreeModel *model = gtk_combo_box_get_model(GTK_COMBO_BOX (widget));
789 GtkTreeIter iter;
790 gchar *lab;
791 if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter))
792 gtk_tree_model_get (model, &iter, 0, &lab, -1);
793 else
794 lab = g_strdup("");
796 GdkWindow *window = widget->window;
797 cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(window));
799 int x = widget->allocation.x;
800 int y = widget->allocation.y;
801 int sx = widget->allocation.width;
802 int sy = widget->allocation.height;
803 gint mx, my;
804 bool hover = false;
806 cairo_rectangle(c, x, y, sx, sy);
807 cairo_clip(c);
809 gtk_widget_get_pointer(GTK_WIDGET(widget), &mx, &my);
810 if (mx >= 0 and mx < sx and my >= 0 and my < sy)
811 hover = true;
813 line_graph_background(c, x, y, sx - pad * 2, sy - pad * 2, pad, pad, g_ascii_isspace(lab[0]) ? 0 : 1, 4, hover ? 0.5 : 0, hover ? 0.1 : 0.25);
815 cairo_select_font_face(c, "Sans",
816 CAIRO_FONT_SLANT_NORMAL,
817 CAIRO_FONT_WEIGHT_NORMAL);
818 cairo_set_font_size(c, 12);
820 cairo_move_to(c, x + pad + 3, y + sy / 2 + 5);
821 cairo_set_source_rgb(c, 0.,0.,0.);
822 cairo_show_text(c, lab);
823 g_free(lab);
825 cairo_surface_t *image;
826 image = cairo_image_surface_create_from_png(PKGLIBDIR "combo_arrow.png");
827 cairo_set_source_surface(c, image, x + sx - 20, y + sy / 2 - 5);
828 cairo_rectangle(c, x, y, sx, sy);
829 cairo_fill(c);
831 cairo_destroy(c);
833 return FALSE;
836 static void
837 calf_combobox_class_init (CalfComboboxClass *klass)
839 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
840 widget_class->expose_event = calf_combobox_expose;
843 static void
844 calf_combobox_init (CalfCombobox *self)
846 GtkWidget *widget = GTK_WIDGET(self);
847 widget->requisition.width = 40;
848 widget->requisition.height = 20;
851 GType
852 calf_combobox_get_type (void)
854 static GType type = 0;
855 if (!type) {
856 static const GTypeInfo type_info = {
857 sizeof(CalfComboboxClass),
858 NULL, /* base_init */
859 NULL, /* base_finalize */
860 (GClassInitFunc)calf_combobox_class_init,
861 NULL, /* class_finalize */
862 NULL, /* class_data */
863 sizeof(CalfCombobox),
864 0, /* n_preallocs */
865 (GInstanceInitFunc)calf_combobox_init
868 for (int i = 0; ; i++) {
869 char *name = g_strdup_printf("CalfCombobox%u%d",
870 ((unsigned int)(intptr_t)calf_combobox_class_init) >> 16, i);
871 if (g_type_from_name(name)) {
872 free(name);
873 continue;
875 type = g_type_register_static(GTK_TYPE_COMBO_BOX_TEXT,
876 name,
877 &type_info,
878 (GTypeFlags)0);
879 free(name);
880 break;
883 return type;
887 ///////////////////////////////////////// notebook ///////////////////////////////////////////////
889 #define GTK_NOTEBOOK_PAGE(_glist_) ((GtkNotebookPage *)((GList *)(_glist_))->data)
890 struct _GtkNotebookPage
892 GtkWidget *child;
893 GtkWidget *tab_label;
894 GtkWidget *menu_label;
895 GtkWidget *last_focus_child; /* Last descendant of the page that had focus */
897 guint default_menu : 1; /* If true, we create the menu label ourself */
898 guint default_tab : 1; /* If true, we create the tab label ourself */
899 guint expand : 1;
900 guint fill : 1;
901 guint pack : 1;
902 guint reorderable : 1;
903 guint detachable : 1;
905 /* if true, the tab label was visible on last allocation; we track this so
906 * that we know to redraw the tab area if a tab label was hidden then shown
907 * without changing position */
908 guint tab_allocated_visible : 1;
910 GtkRequisition requisition;
911 GtkAllocation allocation;
913 gulong mnemonic_activate_signal;
914 gulong notify_visible_handler;
917 GtkWidget *
918 calf_notebook_new()
920 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_NOTEBOOK, NULL ));
921 return widget;
923 static gboolean
924 calf_notebook_expose (GtkWidget *widget, GdkEventExpose *event)
926 g_assert(CALF_IS_NOTEBOOK(widget));
928 GtkNotebook *notebook;
929 notebook = GTK_NOTEBOOK (widget);
930 CalfNotebookClass *klass = CALF_NOTEBOOK_CLASS(GTK_OBJECT_GET_CLASS(widget));
932 if (gtk_widget_is_drawable (widget)) {
934 GdkWindow *window = widget->window;
935 cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(window));
936 cairo_pattern_t *pat = NULL;
938 int x = widget->allocation.x;
939 int y = widget->allocation.y;
940 int sx = widget->allocation.width;
941 int sy = widget->allocation.height;
942 int tx = widget->style->xthickness;
943 int ty = widget->style->ythickness;
944 int lh = 19;
945 int bh = lh + 2 * ty;
947 cairo_rectangle(c, x, y, sx, sy);
948 cairo_clip(c);
950 int add = 0;
952 if (notebook->show_tabs) {
953 GtkNotebookPage *page;
954 GList *pages;
956 gint sp;
957 gtk_widget_style_get(widget, "tab-overlap", &sp, NULL);
959 pages = notebook->children;
961 int cn = 0;
962 while (pages) {
963 page = GTK_NOTEBOOK_PAGE (pages);
964 pages = pages->next;
965 if (page->tab_label->window == event->window &&
966 gtk_widget_is_drawable (page->tab_label)) {
967 int lx = page->tab_label->allocation.x;
968 int lw = page->tab_label->allocation.width;
970 // fix the labels position
971 page->tab_label->allocation.y = y + ty;
972 page->tab_label->allocation.height = lh;
974 // draw tab background
975 cairo_rectangle(c, lx - tx, y, lw + 2 * tx, bh);
976 cairo_set_source_rgba(c, 0,0,0, page != notebook->cur_page ? 0.25 : 0.5);
977 cairo_fill(c);
979 if (page == notebook->cur_page) {
980 // draw tab light
981 cairo_rectangle(c, lx - tx + 2, y + 2, lw + 2 * tx - 4, 2);
982 pat = cairo_pattern_create_radial(lx + lw / 2, y + bh / 2, 1, lx + lw / 2, y + bh / 2, lw + tx * 2);
983 cairo_pattern_add_color_stop_rgb(pat, 0, 50. / 255, 1, 1);
984 cairo_pattern_add_color_stop_rgb(pat, 0.3, 2. / 255, 180. / 255, 1);
985 cairo_pattern_add_color_stop_rgb(pat, 0.5, 19. / 255, 220. / 255, 1);
986 cairo_pattern_add_color_stop_rgb(pat, 1, 2. / 255, 120. / 255, 1);
987 cairo_set_source(c, pat);
988 cairo_fill(c);
990 cairo_rectangle(c, lx - tx + 2, y + 1, lw + 2 * tx - 4, 1);
991 cairo_set_source_rgba(c, 0,0,0,0.5);
992 cairo_fill(c);
994 cairo_rectangle(c, lx - tx + 2, y + 4, lw + 2 * tx - 4, 1);
995 cairo_set_source_rgba(c, 1,1,1,0.3);
996 cairo_fill(c);
999 // draw labels
1000 gtk_container_propagate_expose (GTK_CONTAINER (notebook), page->tab_label, event);
1002 cn++;
1004 add = bh;
1007 // draw main body
1008 cairo_rectangle(c, x, y + add, sx, sy - add);
1009 cairo_set_source_rgba(c, 0,0,0,0.5);
1010 cairo_fill(c);
1012 // draw frame
1013 cairo_rectangle(c, x + 0.5, y + add + 0.5, sx - 1, sy - add - 1);
1014 pat = cairo_pattern_create_linear(x, y + add, x, y + sy - add);
1015 cairo_pattern_add_color_stop_rgba(pat, 0, 0, 0, 0, 0.3);
1016 cairo_pattern_add_color_stop_rgba(pat, 0.5, 0.5, 0.5, 0.5, 0);
1017 cairo_pattern_add_color_stop_rgba(pat, 1, 1, 1, 1, 0.2);
1018 cairo_set_source (c, pat);
1019 cairo_set_line_width(c, 1);
1020 cairo_stroke_preserve(c);
1022 int sw = gdk_pixbuf_get_width(klass->screw);
1023 int sh = gdk_pixbuf_get_height(klass->screw);
1025 // draw screws
1026 gdk_cairo_set_source_pixbuf(c, klass->screw, x, y + add);
1027 cairo_fill_preserve(c);
1028 gdk_cairo_set_source_pixbuf(c, klass->screw, x + sx - sw, y + add);
1029 cairo_fill_preserve(c);
1030 gdk_cairo_set_source_pixbuf(c, klass->screw, x, y + sy - sh);
1031 cairo_fill_preserve(c);
1032 gdk_cairo_set_source_pixbuf(c, klass->screw, x + sx - sh, y + sy - sh);
1033 cairo_fill_preserve(c);
1035 // propagate expose to all children
1036 if (notebook->cur_page)
1037 gtk_container_propagate_expose (GTK_CONTAINER (notebook),
1038 notebook->cur_page->child,
1039 event);
1041 cairo_pattern_destroy(pat);
1042 cairo_destroy(c);
1045 return FALSE;
1048 static void
1049 calf_notebook_class_init (CalfNotebookClass *klass)
1051 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
1052 widget_class->expose_event = calf_notebook_expose;
1054 klass->screw = gdk_pixbuf_new_from_file (PKGLIBDIR "screw_black.png", 0);
1057 static void
1058 calf_notebook_init (CalfNotebook *self)
1060 //GtkWidget *widget = GTK_WIDGET(self);
1063 GType
1064 calf_notebook_get_type (void)
1066 static GType type = 0;
1067 if (!type) {
1068 static const GTypeInfo type_info = {
1069 sizeof(CalfNotebookClass),
1070 NULL, /* base_init */
1071 NULL, /* base_finalize */
1072 (GClassInitFunc)calf_notebook_class_init,
1073 NULL, /* class_finalize */
1074 NULL, /* class_data */
1075 sizeof(CalfNotebook),
1076 0, /* n_preallocs */
1077 (GInstanceInitFunc)calf_notebook_init
1080 for (int i = 0; ; i++) {
1081 char *name = g_strdup_printf("CalfNotebook%u%d",
1082 ((unsigned int)(intptr_t)calf_notebook_class_init) >> 16, i);
1083 if (g_type_from_name(name)) {
1084 free(name);
1085 continue;
1087 type = g_type_register_static(GTK_TYPE_NOTEBOOK,
1088 name,
1089 &type_info,
1090 (GTypeFlags)0);
1091 free(name);
1092 break;
1095 return type;
1098 ///////////////////////////////////////// fader ///////////////////////////////////////////////
1100 static void calf_fader_set_layout(GtkWidget *widget)
1102 GtkRange *range = GTK_RANGE(widget);
1103 CalfFader *fader = CALF_FADER(widget);
1104 CalfFaderLayout layout = fader->layout;
1106 int hor = fader->horizontal;
1108 // widget layout
1109 layout.x = widget->allocation.x;
1110 layout.y = widget->allocation.y;
1111 layout.w = widget->allocation.width;
1112 layout.h = widget->allocation.height;
1114 // trough layout
1115 layout.tx = range->range_rect.x + layout.x;
1116 layout.ty = range->range_rect.y + layout.y;
1117 layout.tw = range->range_rect.width;
1118 layout.th = range->range_rect.height;
1119 layout.tc = hor ? layout.ty + layout.th / 2 : layout.tx + layout.tw / 2;
1121 // screw layout
1122 layout.scw = gdk_pixbuf_get_width(fader->screw);
1123 layout.sch = gdk_pixbuf_get_height(fader->screw);
1124 layout.scx1 = hor ? layout.tx : layout.tc - layout.scw / 2;
1125 layout.scy1 = hor ? layout.tc - layout.sch / 2 : layout.ty;
1126 layout.scx2 = hor ? layout.tx + layout.tw - layout.scw : layout.tc - layout.scw / 2;
1127 layout.scy2 = hor ? layout.tc - layout.sch / 2 : layout.ty + layout.th - layout.sch;
1129 // slit layout
1130 layout.sw = hor ? layout.tw - layout.scw * 2 - 2: 2;
1131 layout.sh = hor ? 2 : layout.th - layout.sch * 2 - 2;
1132 layout.sx = hor ? layout.tx + layout.scw + 1 : layout.tc - 1;
1133 layout.sy = hor ? layout.tc - 1 : layout.ty + layout.sch + 1;
1135 // slider layout
1136 layout.slw = gdk_pixbuf_get_width(fader->slider);
1137 layout.slh = gdk_pixbuf_get_height(fader->slider);
1138 layout.slx = fader->horizontal ? 0 : layout.tc - layout.slw / 2;
1139 layout.sly = fader->horizontal ? layout.tc - layout.slh / 2 : 0;
1141 //printf("widg %d %d %d %d | trgh %d %d %d %d %d | slid %d %d %d %d | slit %d %d %d %d\n",
1142 //layout.x, layout.y, layout.w, layout.h,
1143 //layout.tx, layout.ty, layout.tw, layout.th, layout.tc,
1144 //layout.slx, layout.sly, layout.slw, layout.slh,
1145 //layout.sx, layout.sy, layout.sw, layout.sh);
1147 fader->layout = layout;
1150 GtkWidget *
1151 calf_fader_new(const int horiz = 0, const int size = 2, const double min = 0, const double max = 1, const double step = 0.1)
1153 GtkObject *adj;
1154 gint digits;
1156 adj = gtk_adjustment_new (min, min, max, step, 10 * step, 0);
1158 if (fabs (step) >= 1.0 || step == 0.0)
1159 digits = 0;
1160 else
1161 digits = std::min(5, abs((gint) floor (log10 (fabs (step)))));
1163 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_FADER, NULL ));
1164 CalfFader *self = CALF_FADER(widget);
1166 GTK_RANGE(widget)->orientation = horiz ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1167 gtk_range_set_adjustment(GTK_RANGE(widget), GTK_ADJUSTMENT(adj));
1168 gtk_scale_set_digits(GTK_SCALE(widget), digits);
1170 self->size = size;
1171 self->horizontal = horiz;
1172 self->hover = 0;
1174 gchar * file = g_strdup_printf("%sslider%d-%s.png", PKGLIBDIR, size, horiz ? "horiz" : "vert");
1175 self->slider = gdk_pixbuf_new_from_file (file, 0);
1176 file = g_strdup_printf("%sslider%d-%s-prelight.png", PKGLIBDIR, size, horiz ? "horiz" : "vert");
1177 self->sliderpre = gdk_pixbuf_new_from_file(file, 0);
1179 self->screw = gdk_pixbuf_new_from_file (PKGLIBDIR "screw_silver.png", 0);
1181 return widget;
1184 static bool calf_fader_hover(GtkWidget *widget)
1186 CalfFader *fader = CALF_FADER(widget);
1187 CalfFaderLayout layout = fader->layout;
1188 gint mx, my;
1189 gtk_widget_get_pointer(GTK_WIDGET(widget), &mx, &my);
1191 if ((fader->horizontal and mx + layout.x >= layout.tx
1192 and mx + layout.x < layout.tx + layout.tw
1193 and my + layout.y >= layout.sly
1194 and my + layout.y < layout.sly + layout.slh)
1195 or (!fader->horizontal and mx + layout.x >= layout.slx
1196 and mx + layout.x < layout.slx + layout.slw
1197 and my + layout.y >= layout.ty
1198 and my + layout.y < layout.ty + layout.th))
1199 return true;
1200 return false;
1202 static void calf_fader_check_hover_change(GtkWidget *widget)
1204 CalfFader *fader = CALF_FADER(widget);
1205 bool hover = calf_fader_hover(widget);
1206 if (hover != fader->hover)
1207 gtk_widget_queue_draw(widget);
1208 fader->hover = hover;
1210 static gboolean
1211 calf_fader_motion (GtkWidget *widget, GdkEventMotion *event)
1213 calf_fader_check_hover_change(widget);
1214 return FALSE;
1217 static gboolean
1218 calf_fader_enter (GtkWidget *widget, GdkEventCrossing *event)
1220 calf_fader_check_hover_change(widget);
1221 return FALSE;
1224 static gboolean
1225 calf_fader_leave (GtkWidget *widget, GdkEventCrossing *event)
1227 CALF_FADER(widget)->hover = false;
1228 gtk_widget_queue_draw(widget);
1229 return FALSE;
1231 static void
1232 calf_fader_allocate (GtkWidget *widget, GtkAllocation *allocation)
1234 calf_fader_set_layout(widget);
1236 static gboolean
1237 calf_fader_expose (GtkWidget *widget, GdkEventExpose *event)
1239 g_assert(CALF_IS_FADER(widget));
1240 if (gtk_widget_is_drawable (widget)) {
1242 GdkWindow *window = widget->window;
1243 GtkScale *scale = GTK_SCALE(widget);
1244 GtkRange *range = GTK_RANGE(widget);
1245 CalfFader *fader = CALF_FADER(widget);
1246 CalfFaderLayout layout = fader->layout;
1247 cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(window));
1249 cairo_rectangle(c, layout.x, layout.y, layout.w, layout.h);
1250 cairo_clip(c);
1252 // draw slit
1253 double r = range->adjustment->upper - range->adjustment->lower;
1254 double v0 = range->adjustment->value - range->adjustment->lower;
1255 if ((fader->horizontal and gtk_range_get_inverted(range))
1256 or (!fader->horizontal and gtk_range_get_inverted(range)))
1257 v0 = -v0 + 1;
1258 int vp = v0 / r * (fader->horizontal ? layout.w - layout.slw : layout.h - layout.slh)
1259 + (fader->horizontal ? layout.slw : layout.slh) / 2
1260 + (fader->horizontal ? layout.x : layout.y);
1261 layout.slx = fader->horizontal ? vp - layout.slw / 2 : layout.tc - layout.slw / 2;
1262 layout.sly = fader->horizontal ? layout.tc - layout.slh / 2 : vp - layout.slh / 2;
1264 cairo_rectangle(c, layout.sx - 1, layout.sy - 1, layout.sw, layout.sh);
1265 cairo_set_source_rgba(c, 0,0,0, 0.25);
1266 cairo_fill(c);
1267 cairo_rectangle(c, layout.sx + 1, layout.sy + 1, layout.sw, layout.sh);
1268 cairo_set_source_rgba(c, 1,1,1, 0.125);
1269 cairo_fill(c);
1270 cairo_rectangle(c, layout.sx, layout.sy, layout.sw, layout.sh);
1271 cairo_set_source_rgb(c, 0,0,0);
1272 cairo_fill(c);
1274 // draw screws
1275 cairo_rectangle(c, layout.x, layout.y, layout.w, layout.h);
1276 gdk_cairo_set_source_pixbuf(c, fader->screw, layout.scx1, layout.scy1);
1277 cairo_fill_preserve(c);
1278 gdk_cairo_set_source_pixbuf(c, fader->screw, layout.scx2, layout.scy2);
1279 cairo_fill_preserve(c);
1281 // draw slider
1282 if (fader->hover or gtk_grab_get_current() == widget)
1283 gdk_cairo_set_source_pixbuf(c, fader->sliderpre, layout.slx, layout.sly);
1284 else
1285 gdk_cairo_set_source_pixbuf(c, fader->slider, layout.slx, layout.sly);
1286 cairo_fill(c);
1288 // draw value label
1289 if (scale->draw_value) {
1290 PangoLayout *layout;
1291 gint _x, _y;
1292 layout = gtk_scale_get_layout (scale);
1293 gtk_scale_get_layout_offsets (scale, &_x, &_y);
1294 gtk_paint_layout (widget->style, window, GTK_STATE_NORMAL, FALSE, NULL,
1295 widget, fader->horizontal ? "hscale" : "vscale", _x, _y, layout);
1298 cairo_destroy(c);
1300 return FALSE;
1303 static void
1304 calf_fader_class_init (CalfFaderClass *klass)
1306 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
1307 widget_class->expose_event = calf_fader_expose;
1310 static void
1311 calf_fader_init (CalfFader *self)
1313 GtkWidget *widget = GTK_WIDGET(self);
1314 widget->requisition.width = 40;
1315 widget->requisition.height = 40;
1317 gtk_signal_connect(GTK_OBJECT(widget), "motion-notify-event", GTK_SIGNAL_FUNC (calf_fader_motion), NULL);
1318 gtk_signal_connect(GTK_OBJECT(widget), "enter-notify-event", GTK_SIGNAL_FUNC (calf_fader_enter), NULL);
1319 gtk_signal_connect(GTK_OBJECT(widget), "leave-notify-event", GTK_SIGNAL_FUNC (calf_fader_leave), NULL);
1320 gtk_signal_connect(GTK_OBJECT(widget), "size-allocate", GTK_SIGNAL_FUNC (calf_fader_allocate), NULL);
1323 GType
1324 calf_fader_get_type (void)
1326 static GType type = 0;
1327 if (!type) {
1328 static const GTypeInfo type_info = {
1329 sizeof(CalfFaderClass),
1330 NULL, /* base_init */
1331 NULL, /* base_finalize */
1332 (GClassInitFunc)calf_fader_class_init,
1333 NULL, /* class_finalize */
1334 NULL, /* class_data */
1335 sizeof(CalfFader),
1336 0, /* n_preallocs */
1337 (GInstanceInitFunc)calf_fader_init
1340 for (int i = 0; ; i++) {
1341 char *name = g_strdup_printf("CalfFader%u%d",
1342 ((unsigned int)(intptr_t)calf_fader_class_init) >> 16, i);
1343 if (g_type_from_name(name)) {
1344 free(name);
1345 continue;
1347 type = g_type_register_static(GTK_TYPE_SCALE,
1348 name,
1349 &type_info,
1350 (GTypeFlags)0);
1351 free(name);
1352 break;
1355 return type;
1359 ///////////////////////////////////////// button ///////////////////////////////////////////////
1361 GtkWidget *
1362 calf_button_new(const gchar *label)
1364 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_BUTTON, NULL ));
1365 gtk_button_set_label(GTK_BUTTON(widget), label);
1366 return widget;
1368 static gboolean
1369 calf_button_expose (GtkWidget *widget, GdkEventExpose *event)
1371 g_assert(CALF_IS_BUTTON(widget) || CALF_IS_TOGGLE_BUTTON(widget) || CALF_IS_RADIO_BUTTON(widget));
1373 if (gtk_widget_is_drawable (widget)) {
1375 int pad = 2;
1377 GdkWindow *window = widget->window;
1378 GtkWidget *child = GTK_BIN (widget)->child;
1379 cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(window));
1380 cairo_pattern_t *pat = NULL;
1382 int x = widget->allocation.x;
1383 int y = widget->allocation.y;
1384 int sx = widget->allocation.width;
1385 int sy = widget->allocation.height;
1387 cairo_rectangle(c, x, y, sx, sy);
1388 cairo_clip(c);
1390 cairo_rectangle(c, x, y, sx, sy);
1391 pat = cairo_pattern_create_radial(x + sx / 2, y + sy / 2, 1, x + sx / 2, y + sy / 2, sx / 2);
1392 switch (gtk_widget_get_state(widget)) {
1393 case GTK_STATE_NORMAL:
1394 default:
1395 cairo_pattern_add_color_stop_rgb(pat, 0.3, 39. / 255, 52. / 255, 87. / 255);
1396 cairo_pattern_add_color_stop_rgb(pat, 1.0, 6. / 255, 5. / 255, 14. / 255);
1397 break;
1398 case GTK_STATE_PRELIGHT:
1399 cairo_pattern_add_color_stop_rgb(pat, 0.3, 19. / 255, 237. / 255, 254. / 255);
1400 cairo_pattern_add_color_stop_rgb(pat, 1.0, 0. / 255, 45. / 255, 206. / 255);
1401 break;
1402 case GTK_STATE_ACTIVE:
1403 case GTK_STATE_SELECTED:
1404 cairo_pattern_add_color_stop_rgb(pat, 0.0, 19. / 255, 237. / 255, 254. / 255);
1405 cairo_pattern_add_color_stop_rgb(pat, 0.3, 10. / 255, 200. / 255, 240. / 255);
1406 cairo_pattern_add_color_stop_rgb(pat, 0.7, 19. / 255, 237. / 255, 254. / 255);
1407 cairo_pattern_add_color_stop_rgb(pat, 1.0, 2. / 255, 168. / 255, 230. / 255);
1408 break;
1411 cairo_set_source(c, pat);
1412 cairo_fill(c);
1414 cairo_rectangle(c, x + pad, y + pad, sx - pad * 2, sy - pad * 2);
1415 if (CALF_IS_TOGGLE_BUTTON(widget) or CALF_IS_RADIO_BUTTON(widget)) {
1416 cairo_new_sub_path (c);
1417 cairo_rectangle(c, x + sx - pad * 2 - 23, y + sy / 2 - 1, 22, 2);
1418 cairo_set_fill_rule(c, CAIRO_FILL_RULE_EVEN_ODD);
1420 pat = cairo_pattern_create_linear(x + pad, y + pad, x + pad, y + sy - pad * 2);
1421 cairo_pattern_add_color_stop_rgb(pat, 0.0, 0.92, 0.92, 0.92);
1422 cairo_pattern_add_color_stop_rgb(pat, 1.0, 0.70, 0.70, 0.70);
1423 cairo_set_source(c, pat);
1424 cairo_fill(c);
1426 int _h = GTK_WIDGET(GTK_BIN(widget)->child)->allocation.height + 0;
1427 int _y = y + (sy - _h) / 2;
1428 cairo_rectangle(c, x + pad, _y, sx - pad * 2, _h);
1429 if (CALF_IS_TOGGLE_BUTTON(widget) or CALF_IS_RADIO_BUTTON(widget)) {
1430 cairo_new_sub_path (c);
1431 cairo_rectangle(c, x + sx - pad * 2 - 23, y + sy / 2 - 1, 22, 2);
1432 cairo_set_fill_rule(c, CAIRO_FILL_RULE_EVEN_ODD);
1434 pat = cairo_pattern_create_linear(x + pad, _y, x + pad, _y + _h);
1435 cairo_pattern_add_color_stop_rgb(pat, 1.0, 0.92, 0.92, 0.92);
1436 cairo_pattern_add_color_stop_rgb(pat, 0.0, 0.70, 0.70, 0.70);
1437 cairo_set_source(c, pat);
1438 cairo_fill(c);
1440 cairo_destroy(c);
1441 gtk_container_propagate_expose (GTK_CONTAINER (widget), child, event);
1443 return FALSE;
1446 static void
1447 calf_button_class_init (CalfButtonClass *klass)
1449 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
1450 widget_class->expose_event = calf_button_expose;
1453 static void
1454 calf_button_init (CalfButton *self)
1456 GtkWidget *widget = GTK_WIDGET(self);
1457 widget->requisition.width = 40;
1458 widget->requisition.height = 20;
1461 GType
1462 calf_button_get_type (void)
1464 static GType type = 0;
1465 if (!type) {
1466 static const GTypeInfo type_info = {
1467 sizeof(CalfButtonClass),
1468 NULL, /* base_init */
1469 NULL, /* base_finalize */
1470 (GClassInitFunc)calf_button_class_init,
1471 NULL, /* class_finalize */
1472 NULL, /* class_data */
1473 sizeof(CalfButton),
1474 0, /* n_preallocs */
1475 (GInstanceInitFunc)calf_button_init
1478 for (int i = 0; ; i++) {
1479 char *name = g_strdup_printf("CalfButton%u%d",
1480 ((unsigned int)(intptr_t)calf_button_class_init) >> 16, i);
1481 if (g_type_from_name(name)) {
1482 free(name);
1483 continue;
1485 type = g_type_register_static(GTK_TYPE_BUTTON,
1486 name,
1487 &type_info,
1488 (GTypeFlags)0);
1489 free(name);
1490 break;
1493 return type;
1497 ///////////////////////////////////////// toggle button ///////////////////////////////////////////////
1499 GtkWidget *
1500 calf_toggle_button_new(const gchar *label)
1502 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_TOGGLE_BUTTON, NULL ));
1503 gtk_button_set_label(GTK_BUTTON(widget), label);
1504 return widget;
1507 static void
1508 calf_toggle_button_class_init (CalfToggleButtonClass *klass)
1510 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
1511 widget_class->expose_event = calf_button_expose;
1514 static void
1515 calf_toggle_button_init (CalfToggleButton *self)
1517 GtkWidget *widget = GTK_WIDGET(self);
1518 widget->requisition.width = 40;
1519 widget->requisition.height = 20;
1522 GType
1523 calf_toggle_button_get_type (void)
1525 static GType type = 0;
1526 if (!type) {
1527 static const GTypeInfo type_info = {
1528 sizeof(CalfToggleButtonClass),
1529 NULL, /* base_init */
1530 NULL, /* base_finalize */
1531 (GClassInitFunc)calf_toggle_button_class_init,
1532 NULL, /* class_finalize */
1533 NULL, /* class_data */
1534 sizeof(CalfToggleButton),
1535 0, /* n_preallocs */
1536 (GInstanceInitFunc)calf_toggle_button_init
1539 for (int i = 0; ; i++) {
1540 char *name = g_strdup_printf("CalfToggleButton%u%d",
1541 ((unsigned int)(intptr_t)calf_toggle_button_class_init) >> 16, i);
1542 if (g_type_from_name(name)) {
1543 free(name);
1544 continue;
1546 type = g_type_register_static(GTK_TYPE_TOGGLE_BUTTON,
1547 name,
1548 &type_info,
1549 (GTypeFlags)0);
1550 free(name);
1551 break;
1554 return type;
1557 ///////////////////////////////////////// radio button ///////////////////////////////////////////////
1559 GtkWidget *
1560 calf_radio_button_new(const gchar *label)
1562 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_RADIO_BUTTON, NULL ));
1563 gtk_button_set_label(GTK_BUTTON(widget), label);
1564 return widget;
1567 static void
1568 calf_radio_button_class_init (CalfRadioButtonClass *klass)
1570 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
1571 widget_class->expose_event = calf_button_expose;
1574 static void
1575 calf_radio_button_init (CalfRadioButton *self)
1577 GtkWidget *widget = GTK_WIDGET(self);
1578 widget->requisition.width = 40;
1579 widget->requisition.height = 20;
1582 GType
1583 calf_radio_button_get_type (void)
1585 static GType type = 0;
1586 if (!type) {
1587 static const GTypeInfo type_info = {
1588 sizeof(CalfRadioButtonClass),
1589 NULL, /* base_init */
1590 NULL, /* base_finalize */
1591 (GClassInitFunc)calf_radio_button_class_init,
1592 NULL, /* class_finalize */
1593 NULL, /* class_data */
1594 sizeof(CalfRadioButton),
1595 0, /* n_preallocs */
1596 (GInstanceInitFunc)calf_radio_button_init
1599 for (int i = 0; ; i++) {
1600 char *name = g_strdup_printf("CalfRadioButton%u%d",
1601 ((unsigned int)(intptr_t)calf_radio_button_class_init) >> 16, i);
1602 if (g_type_from_name(name)) {
1603 free(name);
1604 continue;
1606 type = g_type_register_static(GTK_TYPE_RADIO_BUTTON,
1607 name,
1608 &type_info,
1609 (GTypeFlags)0);
1610 free(name);
1611 break;
1614 return type;
1617 ///////////////////////////////////////// tap button ///////////////////////////////////////////////
1619 GtkWidget *
1620 calf_tap_button_new()
1622 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_TAP_BUTTON, NULL ));
1623 return widget;
1626 static gboolean
1627 calf_tap_button_expose (GtkWidget *widget, GdkEventExpose *event)
1629 g_assert(CALF_IS_TAP_BUTTON(widget));
1630 CalfTapButton *self = CALF_TAP_BUTTON(widget);
1632 int x = widget->allocation.x + widget->allocation.width / 2 - 35;
1633 int y = widget->allocation.y + widget->allocation.height / 2 - 35;
1634 int width = 70;
1635 int height = 70;
1637 gdk_draw_pixbuf(GDK_DRAWABLE(widget->window),
1638 widget->style->fg_gc[0],
1639 self->image[self->state],
1644 width,
1645 height,
1646 GDK_RGB_DITHER_NORMAL, 0, 0);
1647 return TRUE;
1650 static void
1651 calf_tap_button_size_request (GtkWidget *widget,
1652 GtkRequisition *requisition)
1654 g_assert(CALF_IS_TAP_BUTTON(widget));
1655 requisition->width = 70;
1656 requisition->height = 70;
1658 static void
1659 calf_tap_button_class_init (CalfTapButtonClass *klass)
1661 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
1662 widget_class->expose_event = calf_tap_button_expose;
1663 widget_class->size_request = calf_tap_button_size_request;
1665 static void
1666 calf_tap_button_init (CalfTapButton *self)
1668 GtkWidget *widget = GTK_WIDGET(self);
1669 widget->requisition.width = 70;
1670 widget->requisition.height = 70;
1671 self->state = 0;
1672 GError *error = NULL;
1673 self->image[0] = gdk_pixbuf_new_from_file(PKGLIBDIR "/tap_inactive.png", &error);
1674 self->image[1] = gdk_pixbuf_new_from_file(PKGLIBDIR "/tap_prelight.png", &error);
1675 self->image[2] = gdk_pixbuf_new_from_file(PKGLIBDIR "/tap_active.png", &error);
1678 GType
1679 calf_tap_button_get_type (void)
1681 static GType type = 0;
1682 if (!type) {
1683 static const GTypeInfo type_info = {
1684 sizeof(CalfTapButtonClass),
1685 NULL, /* base_init */
1686 NULL, /* base_finalize */
1687 (GClassInitFunc)calf_tap_button_class_init,
1688 NULL, /* class_finalize */
1689 NULL, /* class_data */
1690 sizeof(CalfTapButton),
1691 0, /* n_preallocs */
1692 (GInstanceInitFunc)calf_tap_button_init
1695 for (int i = 0; ; i++) {
1696 char *name = g_strdup_printf("CalfTapButton%u%d",
1697 ((unsigned int)(intptr_t)calf_tap_button_class_init) >> 16, i);
1698 if (g_type_from_name(name)) {
1699 free(name);
1700 continue;
1702 type = g_type_register_static(GTK_TYPE_BUTTON,
1703 name,
1704 &type_info,
1705 (GTypeFlags)0);
1706 free(name);
1707 break;
1710 return type;