New design
[calf.git] / src / custom_ctl.cpp
blobf8837b7aae5bbd0659f82775aeb4daac996c40af
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 <algorithm>
30 #include <iostream>
32 using namespace calf_plugins;
33 using namespace dsp;
35 ///////////////////////////////////////// utility functions ///////////////////////////////////////////////
37 void create_rectangle (cairo_t * cr, gint x, gint y, gint width, gint height, gint rad) {
38 if (rad == 0) {
39 cairo_rectangle(cr, x, y, width, height);
40 return;
42 cairo_move_to(cr,x+rad,y); // Move to A
43 cairo_line_to(cr,x+width-rad,y); // Straight line to B
44 cairo_curve_to(cr,x+width,y,x+width,y,x+width,y+rad); // Curve to C, Control points are both at Q
45 cairo_line_to(cr,x+width,y+height-rad); // Move to D
46 cairo_curve_to(cr,x+width,y+height,x+width,y+height,x+width-rad,y+height); // Curve to E
47 cairo_line_to(cr,x+rad,y+height); // Line to F
48 cairo_curve_to(cr,x,y+height,x,y+height,x,y+height-rad); // Curve to G
49 cairo_line_to(cr,x,y+rad); // Line to H
50 cairo_curve_to(cr,x,y,x,y,x+rad,y); // Curve to A
54 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)
56 float br = brightness * 0.5 + 0.5;
58 // outer frame (black)
59 int pad = 0;
61 //cairo_rectangle(
62 //c, pad + x, pad + y, sx + ox * 2 - pad * 2, sy + oy * 2 - pad * 2);
63 //cairo_set_source_rgb(c, 0.0, 0.0, 0.0);
64 //cairo_fill(c);
66 // black light effect
67 pad = 0;
68 create_rectangle(
69 c, pad + x, pad + y, sx + ox * 2 - pad * 2, sy + oy * 2 - pad * 2, 0);
70 cairo_pattern_t *pat2 = cairo_pattern_create_linear (
71 x, y, x, y + sy + oy * 2 - pad * 2);
72 cairo_pattern_add_color_stop_rgba (pat2, 0, 0.96, 0.96, 0.96, 1);
73 cairo_pattern_add_color_stop_rgba (pat2, 0.99, 0.7, 0.7, 0.7, 1);
74 //cairo_pattern_add_color_stop_rgba (pat2, 0.33, 0.05, 0.05, 0.05, 1);
75 //cairo_pattern_add_color_stop_rgba (pat2, 0.5, 0, 0, 0, 1);
76 cairo_set_source (c, pat2);
77 cairo_fill(c);
78 cairo_pattern_destroy(pat2);
80 //cairo_rectangle(c, x + ox - 1, y + oy - 1, sx + 2, sy + 2);
81 //cairo_set_source_rgb (c, 0, 0, 0);
82 //cairo_fill(c);
84 // inner yellowish screen
85 cairo_pattern_t *pt = cairo_pattern_create_linear(x + ox, y + oy, x + ox, y + sy);
86 cairo_pattern_add_color_stop_rgb(pt, 0.0, br * 0.71, br * 0.82, br * 0.33);
87 cairo_pattern_add_color_stop_rgb(pt, 1.0, br * 0.89, br * 1.00, br * 0.54);
88 cairo_set_source (c, pt);
89 cairo_rectangle(c, x + ox, y + oy, sx, sy);
90 cairo_fill(c);
91 cairo_pattern_destroy(pt);
93 if (shadow) {
94 // top shadow
95 pt = cairo_pattern_create_linear(x + ox, y + oy, x + ox, y + oy + shadow);
96 cairo_pattern_add_color_stop_rgba(pt, 0.0, 0,0,0,0.6);
97 cairo_pattern_add_color_stop_rgba(pt, 1.0, 0,0,0,0);
98 cairo_set_source (c, pt);
99 cairo_rectangle(c, x + ox, y + oy, sx, shadow);
100 cairo_fill(c);
101 cairo_pattern_destroy(pt);
103 // left shadow
104 pt = cairo_pattern_create_linear(x + ox, y + oy, x + ox + (float)shadow * 0.7, y + oy);
105 cairo_pattern_add_color_stop_rgba(pt, 0.0, 0,0,0,0.3);
106 cairo_pattern_add_color_stop_rgba(pt, 1.0, 0,0,0,0);
107 cairo_set_source (c, pt);
108 cairo_rectangle(c, x + ox, y + oy, (float)shadow * 0.7, sy);
109 cairo_fill(c);
110 cairo_pattern_destroy(pt);
112 // right shadow
113 pt = cairo_pattern_create_linear(x + ox + sx - (float)shadow * 0.7, y + oy, x + ox + sx, y + oy);
114 cairo_pattern_add_color_stop_rgba(pt, 0.0, 0,0,0,0);
115 cairo_pattern_add_color_stop_rgba(pt, 1.0, 0,0,0,0.3);
116 cairo_set_source (c, pt);
117 cairo_rectangle(c, x + ox + sx - (float)shadow * 0.7, y + oy, (float)shadow * 0.7, sy);
118 cairo_fill(c);
119 cairo_pattern_destroy(pt);
122 if(dull) {
123 // left dull
124 pt = cairo_pattern_create_linear(x + ox, y + oy, x + ox + sx / 2, y + oy);
125 cairo_pattern_add_color_stop_rgba(pt, 0.0, 0,0,0,dull);
126 cairo_pattern_add_color_stop_rgba(pt, 1.0, 0,0,0,0);
127 cairo_set_source (c, pt);
128 cairo_rectangle(c, x + ox, y + oy, sx / 2, sy);
129 cairo_fill(c);
130 cairo_pattern_destroy(pt);
132 // right dull
133 pt = cairo_pattern_create_linear(x + ox + sx / 2, y + oy, x + ox + sx, y + oy);
134 cairo_pattern_add_color_stop_rgba(pt, 0.0, 0,0,0,0);
135 cairo_pattern_add_color_stop_rgba(pt, 1.0, 0,0,0,dull);
136 cairo_set_source (c, pt);
137 cairo_rectangle(c, x + ox + sx / 2, y + oy, sx / 2, sy);
138 cairo_fill(c);
139 cairo_pattern_destroy(pt);
142 if(lights > 0) {
143 // light sources
144 int div = 1;
145 while(sx / div > 300)
146 div += 1;
147 float w = float(sx) / float(div);
148 cairo_rectangle(c, x + ox, y + oy, sx, sy);
149 for(int i = 0; i < div; i ++) {
150 cairo_pattern_t *pt = cairo_pattern_create_radial(
151 x + ox + w * i + w / 2.f, y + oy, 1,
152 x + ox + w * i + w / 2.f, std::min(w / 2.0 + y + oy, y + oy + sy * 0.25) - 1, w / 2.f);
153 cairo_pattern_add_color_stop_rgba (pt, 0, 1, 1, 0.8, lights);
154 cairo_pattern_add_color_stop_rgba (pt, 1, 0.89, 1.00, 0.45, 0);
155 cairo_set_source (c, pt);
156 cairo_fill_preserve(c);
157 pt = cairo_pattern_create_radial(
158 x + ox + w * i + w / 2.f, y + oy + sy, 1,
159 x + ox + w * i + w / 2.f, std::max(sy - w / 2.0 + y + oy, y + oy + sy * 0.75) + 1, w / 2.f);
160 cairo_pattern_add_color_stop_rgba (pt, 0, 1, 1, 0.8, lights);
161 cairo_pattern_add_color_stop_rgba (pt, 1, 0.89, 1.00, 0.45, 0);
162 cairo_set_source (c, pt);
163 cairo_fill_preserve(c);
164 cairo_pattern_destroy(pt);
169 ///////////////////////////////////////// phase graph ///////////////////////////////////////////////
171 static void
172 calf_phase_graph_draw_background( cairo_t *ctx, int sx, int sy, int ox, int oy )
174 int cx = ox + sx / 2;
175 int cy = oy + sy / 2;
177 line_graph_background(ctx, 0, 0, sx, sy, ox, oy);
178 cairo_set_source_rgb(ctx, 0.35, 0.4, 0.2);
179 cairo_select_font_face(ctx, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
180 cairo_set_font_size(ctx, 9);
181 cairo_text_extents_t te;
183 cairo_text_extents (ctx, "M", &te);
184 cairo_move_to (ctx, cx + 5, oy + 12);
185 cairo_show_text (ctx, "M");
187 cairo_text_extents (ctx, "S", &te);
188 cairo_move_to (ctx, ox + 5, cy - 5);
189 cairo_show_text (ctx, "S");
191 cairo_text_extents (ctx, "L", &te);
192 cairo_move_to (ctx, ox + 18, oy + 12);
193 cairo_show_text (ctx, "L");
195 cairo_text_extents (ctx, "R", &te);
196 cairo_move_to (ctx, ox + sx - 22, oy + 12);
197 cairo_show_text (ctx, "R");
199 cairo_set_line_width(ctx, 1);
201 cairo_move_to(ctx, ox, oy + sy * 0.5);
202 cairo_line_to(ctx, ox + sx, oy + sy * 0.5);
203 cairo_stroke(ctx);
205 cairo_move_to(ctx, ox + sx * 0.5, oy);
206 cairo_line_to(ctx, ox + sx * 0.5, oy + sy);
207 cairo_stroke(ctx);
209 cairo_set_source_rgba(ctx, 0, 0, 0, 0.2);
210 cairo_move_to(ctx, ox, oy);
211 cairo_line_to(ctx, ox + sx, oy + sy);
212 cairo_stroke(ctx);
214 cairo_move_to(ctx, ox, oy + sy);
215 cairo_line_to(ctx, ox + sx, oy);
216 cairo_stroke(ctx);
218 static void
219 calf_phase_graph_copy_surface(cairo_t *ctx, cairo_surface_t *source, float fade = 1.f)
221 // copy a surface to a cairo context
222 cairo_save(ctx);
223 cairo_set_source_surface(ctx, source, 0, 0);
224 if (fade < 1.0) {
225 float rnd = (float)rand() / (float)RAND_MAX / 100;
226 cairo_paint_with_alpha(ctx, fade * 0.35 + 0.05 + rnd);
227 } else {
228 cairo_paint(ctx);
230 cairo_restore(ctx);
232 static gboolean
233 calf_phase_graph_expose (GtkWidget *widget, GdkEventExpose *event)
235 g_assert(CALF_IS_PHASE_GRAPH(widget));
236 CalfPhaseGraph *pg = CALF_PHASE_GRAPH(widget);
237 if (!pg->source)
238 return FALSE;
240 // dimensions
241 int ox = 0, oy = 0;
242 int sx = widget->allocation.width - ox * 2, sy = widget->allocation.height - oy * 2;
243 sx += sx % 2 - 1;
244 sy += sy % 2 - 1;
245 int rad = sx / 2;
246 int cx = ox + sx / 2;
247 int cy = oy + sy / 2;
249 // some values as pointers for the audio plug-in call
250 float * phase_buffer = 0;
251 int length = 0;
252 int mode = 2;
253 float fade = 0.05;
254 bool use_fade = true;
255 int accuracy = 1;
256 bool display = true;
258 // cairo initialization stuff
259 cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(widget->window));
260 cairo_t *ctx_back;
261 cairo_t *ctx_cache;
263 if( pg->background == NULL ) {
264 // looks like its either first call or the widget has been resized.
265 // create the background surface (stolen from line graph)...
266 cairo_surface_t *window_surface = cairo_get_target(c);
267 pg->background = cairo_surface_create_similar(window_surface,
268 CAIRO_CONTENT_COLOR,
269 widget->allocation.width,
270 widget->allocation.height );
271 pg->cache = cairo_surface_create_similar(window_surface,
272 CAIRO_CONTENT_COLOR,
273 widget->allocation.width,
274 widget->allocation.height );
276 // ...and draw some bling bling onto it...
277 ctx_back = cairo_create(pg->background);
278 calf_phase_graph_draw_background(ctx_back, sx, sy, ox, oy);
279 // ...and copy it to the cache
280 ctx_cache = cairo_create(pg->cache);
281 calf_phase_graph_copy_surface(ctx_cache, pg->background, 1);
282 } else {
283 ctx_back = cairo_create(pg->background);
284 ctx_cache = cairo_create(pg->cache);
287 pg->source->get_phase_graph(&phase_buffer, &length, &mode, &use_fade,
288 &fade, &accuracy, &display);
290 // process some values set by the plug-in
291 accuracy *= 2;
292 accuracy = 12 - accuracy;
294 calf_phase_graph_copy_surface(ctx_cache, pg->background, use_fade ? fade : 1);
296 if(display) {
297 cairo_rectangle(ctx_cache, ox, oy, sx, sy);
298 cairo_clip(ctx_cache);
299 cairo_set_source_rgba(ctx_cache, 0.35, 0.4, 0.2, 1);
300 double _a;
301 for(int i = 0; i < length; i+= accuracy) {
302 float l = phase_buffer[i];
303 float r = phase_buffer[i + 1];
304 if(l == 0.f and r == 0.f) continue;
305 else if(r == 0.f and l > 0.f) _a = M_PI / 2.f;
306 else if(r == 0.f and l < 0.f) _a = 3.f *M_PI / 2.f;
307 else _a = pg->_atan(l / r, l, r);
308 double _R = sqrt(pow(l, 2) + pow(r, 2));
309 _a += M_PI / 4.f;
310 float x = (-1.f)*_R * cos(_a);
311 float y = _R * sin(_a);
312 // mask the cached values
313 switch(mode) {
314 case 0:
315 // small dots
316 cairo_rectangle (ctx_cache, x * rad + cx, y * rad + cy, 1, 1);
317 break;
318 case 1:
319 // medium dots
320 cairo_rectangle (ctx_cache, x * rad + cx - 0.25, y * rad + cy - 0.25, 1.5, 1.5);
321 break;
322 case 2:
323 // big dots
324 cairo_rectangle (ctx_cache, x * rad + cx - 0.5, y * rad + cy - 0.5, 2, 2);
325 break;
326 case 3:
327 // fields
328 if(i == 0) cairo_move_to(ctx_cache,
329 x * rad + cx, y * rad + cy);
330 else cairo_line_to(ctx_cache,
331 x * rad + cx, y * rad + cy);
332 break;
333 case 4:
334 // lines
335 if(i == 0) cairo_move_to(ctx_cache,
336 x * rad + cx, y * rad + cy);
337 else cairo_line_to(ctx_cache,
338 x * rad + cx, y * rad + cy);
339 break;
342 // draw
343 switch(mode) {
344 case 0:
345 case 1:
346 case 2:
347 cairo_fill(ctx_cache);
348 break;
349 case 3:
350 cairo_fill(ctx_cache);
351 break;
352 case 4:
353 cairo_set_line_width(ctx_cache, 0.5);
354 cairo_stroke(ctx_cache);
355 break;
359 calf_phase_graph_copy_surface(c, pg->cache, 1);
361 cairo_destroy(c);
362 cairo_destroy(ctx_back);
363 cairo_destroy(ctx_cache);
364 // printf("exposed %p %dx%d %d+%d\n", widget->window, event->area.x, event->area.y, event->area.width, event->area.height);
365 return TRUE;
368 static void
369 calf_phase_graph_size_request (GtkWidget *widget,
370 GtkRequisition *requisition)
372 g_assert(CALF_IS_PHASE_GRAPH(widget));
373 // CalfLineGraph *lg = CALF_LINE_GRAPH(widget);
376 static void
377 calf_phase_graph_size_allocate (GtkWidget *widget,
378 GtkAllocation *allocation)
380 g_assert(CALF_IS_PHASE_GRAPH(widget));
381 CalfPhaseGraph *lg = CALF_PHASE_GRAPH(widget);
383 GtkWidgetClass *parent_class = (GtkWidgetClass *) g_type_class_peek_parent( CALF_PHASE_GRAPH_GET_CLASS( lg ) );
385 if(lg->background)
386 cairo_surface_destroy(lg->background);
387 lg->background = NULL;
389 widget->allocation = *allocation;
390 GtkAllocation &a = widget->allocation;
391 if (a.width > a.height) {
392 a.x += (a.width - a.height) / 2;
393 a.width = a.height;
395 if (a.width < a.height) {
396 a.y += (a.height - a.width) / 2;
397 a.height = a.width;
399 parent_class->size_allocate(widget, &a);
402 static void
403 calf_phase_graph_class_init (CalfPhaseGraphClass *klass)
405 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
406 widget_class->expose_event = calf_phase_graph_expose;
407 widget_class->size_request = calf_phase_graph_size_request;
408 widget_class->size_allocate = calf_phase_graph_size_allocate;
411 static void
412 calf_phase_graph_unrealize (GtkWidget *widget, CalfPhaseGraph *pg)
414 if( pg->background )
415 cairo_surface_destroy(pg->background);
416 pg->background = NULL;
419 static void
420 calf_phase_graph_init (CalfPhaseGraph *self)
422 GtkWidget *widget = GTK_WIDGET(self);
423 widget->requisition.width = 40;
424 widget->requisition.height = 40;
425 self->background = NULL;
426 g_signal_connect(GTK_OBJECT(widget), "unrealize", G_CALLBACK(calf_phase_graph_unrealize), (gpointer)self);
429 GtkWidget *
430 calf_phase_graph_new()
432 return GTK_WIDGET(g_object_new (CALF_TYPE_PHASE_GRAPH, NULL));
435 GType
436 calf_phase_graph_get_type (void)
438 static GType type = 0;
439 if (!type) {
440 static const GTypeInfo type_info = {
441 sizeof(CalfPhaseGraphClass),
442 NULL, /* base_init */
443 NULL, /* base_finalize */
444 (GClassInitFunc)calf_phase_graph_class_init,
445 NULL, /* class_finalize */
446 NULL, /* class_data */
447 sizeof(CalfPhaseGraph),
448 0, /* n_preallocs */
449 (GInstanceInitFunc)calf_phase_graph_init
452 GTypeInfo *type_info_copy = new GTypeInfo(type_info);
454 for (int i = 0; ; i++) {
455 char *name = g_strdup_printf("CalfPhaseGraph%u%d", ((unsigned int)(intptr_t)calf_phase_graph_class_init) >> 16, i);
456 if (g_type_from_name(name)) {
457 free(name);
458 continue;
460 type = g_type_register_static( GTK_TYPE_DRAWING_AREA,
461 name,
462 type_info_copy,
463 (GTypeFlags)0);
464 free(name);
465 break;
468 return type;
472 ///////////////////////////////////////// toggle ///////////////////////////////////////////////
474 static gboolean
475 calf_toggle_expose (GtkWidget *widget, GdkEventExpose *event)
477 g_assert(CALF_IS_TOGGLE(widget));
479 CalfToggle *self = CALF_TOGGLE(widget);
481 float sx = self->size ? self->size : 1.f / 3.f * 2.f;
482 float sy = self->size ? self->size : 1;
483 int x = widget->allocation.x + widget->allocation.width / 2 - sx * 15 - sx * 2;
484 int y = widget->allocation.y + widget->allocation.height / 2 - sy * 10 - sy * 3;
485 int width = sx * 34;
486 int height = sy * 26;
488 gdk_draw_pixbuf(GDK_DRAWABLE(widget->window),
489 widget->style->fg_gc[0],
490 self->toggle_image[self->size],
491 20 - sx * 2,
492 20 - sy * 3 + (sy * 20 + 40) * floor(.5 + gtk_range_get_value(GTK_RANGE(widget))),
495 width,
496 height,
497 GDK_RGB_DITHER_NORMAL, 0, 0);
498 return TRUE;
501 static void
502 calf_toggle_size_request (GtkWidget *widget,
503 GtkRequisition *requisition)
505 g_assert(CALF_IS_TOGGLE(widget));
507 CalfToggle *self = CALF_TOGGLE(widget);
509 float sx = self->size ? self->size : 1.f / 3.f * 2.f;
510 float sy = self->size ? self->size : 1;
512 requisition->width = 30 * sx;
513 requisition->height = 20 * sy;
516 static gboolean
517 calf_toggle_button_press (GtkWidget *widget, GdkEventButton *event)
519 g_assert(CALF_IS_TOGGLE(widget));
520 GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(widget));
521 if (gtk_range_get_value(GTK_RANGE(widget)) == adj->lower)
523 gtk_range_set_value(GTK_RANGE(widget), adj->upper);
524 } else {
525 gtk_range_set_value(GTK_RANGE(widget), adj->lower);
527 return TRUE;
530 static gboolean
531 calf_toggle_key_press (GtkWidget *widget, GdkEventKey *event)
533 switch(event->keyval)
535 case GDK_Return:
536 case GDK_KP_Enter:
537 case GDK_space:
538 return calf_toggle_button_press(widget, NULL);
540 return FALSE;
543 static void
544 calf_toggle_class_init (CalfToggleClass *klass)
546 // GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
547 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
548 widget_class->expose_event = calf_toggle_expose;
549 widget_class->size_request = calf_toggle_size_request;
550 widget_class->button_press_event = calf_toggle_button_press;
551 widget_class->key_press_event = calf_toggle_key_press;
554 static void
555 calf_toggle_init (CalfToggle *self)
557 GtkWidget *widget = GTK_WIDGET(self);
558 GTK_WIDGET_SET_FLAGS (GTK_WIDGET(self), GTK_CAN_FOCUS);
559 widget->requisition.width = 30;
560 widget->requisition.height = 20;
561 self->size = 1;
562 GError *error = NULL;
563 self->toggle_image[0] = gdk_pixbuf_new_from_file(PKGLIBDIR "/toggle0_silver.png", &error);
564 self->toggle_image[1] = gdk_pixbuf_new_from_file(PKGLIBDIR "/toggle1_silver.png", &error);
565 self->toggle_image[2] = gdk_pixbuf_new_from_file(PKGLIBDIR "/toggle2_silver.png", &error);
566 g_assert(self->toggle_image != NULL);
569 GtkWidget *
570 calf_toggle_new()
572 GtkAdjustment *adj = (GtkAdjustment *)gtk_adjustment_new(0, 0, 1, 1, 0, 0);
573 return calf_toggle_new_with_adjustment(adj);
576 static gboolean calf_toggle_value_changed(gpointer obj)
578 GtkWidget *widget = (GtkWidget *)obj;
579 CalfToggle *self = CALF_TOGGLE(widget);
580 float sx = self->size ? self->size : 1.f / 3.f * 2.f;
581 float sy = self->size ? self->size : 1;
582 gtk_widget_queue_draw_area(widget,
583 widget->allocation.x - sx * 2,
584 widget->allocation.y - sy * 3,
585 self->size * 34,
586 self->size * 26);
587 return FALSE;
590 GtkWidget *calf_toggle_new_with_adjustment(GtkAdjustment *_adjustment)
592 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_TOGGLE, NULL ));
593 if (widget) {
594 gtk_range_set_adjustment(GTK_RANGE(widget), _adjustment);
595 g_signal_connect(GTK_OBJECT(widget), "value-changed", G_CALLBACK(calf_toggle_value_changed), widget);
597 return widget;
600 GType
601 calf_toggle_get_type (void)
603 static GType type = 0;
604 if (!type) {
606 static const GTypeInfo type_info = {
607 sizeof(CalfToggleClass),
608 NULL, /* base_init */
609 NULL, /* base_finalize */
610 (GClassInitFunc)calf_toggle_class_init,
611 NULL, /* class_finalize */
612 NULL, /* class_data */
613 sizeof(CalfToggle),
614 0, /* n_preallocs */
615 (GInstanceInitFunc)calf_toggle_init
618 for (int i = 0; ; i++) {
619 char *name = g_strdup_printf("CalfToggle%u%d",
620 ((unsigned int)(intptr_t)calf_toggle_class_init) >> 16, i);
621 if (g_type_from_name(name)) {
622 free(name);
623 continue;
625 type = g_type_register_static( GTK_TYPE_RANGE,
626 name,
627 &type_info,
628 (GTypeFlags)0);
629 free(name);
630 break;
633 return type;
636 ///////////////////////////////////////// frame ///////////////////////////////////////////////
639 GtkWidget *
640 calf_frame_new(const char *label)
642 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_FRAME, NULL ));
643 CalfFrame *self = CALF_FRAME(widget);
644 gtk_frame_set_label(GTK_FRAME(self), label);
645 return widget;
647 static gboolean
648 calf_frame_expose (GtkWidget *widget, GdkEventExpose *event)
650 g_assert(CALF_IS_FRAME(widget));
651 if (gtk_widget_is_drawable (widget)) {
653 GdkWindow *window = widget->window;
654 cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(window));
655 cairo_text_extents_t extents;
657 int ox = widget->allocation.x;
658 int oy = widget->allocation.y;
659 int sx = widget->allocation.width;
660 int sy = widget->allocation.height;
662 double rad = 8;
663 double a = 1.5;
664 double pad = 4;
665 double txp = 4;
666 double m = 1;
667 double size = 10;
669 cairo_rectangle(c, ox, oy, sx, sy);
670 cairo_clip(c);
673 const gchar *lab = gtk_frame_get_label(GTK_FRAME(widget));
675 cairo_select_font_face(c, "Sans",
676 CAIRO_FONT_SLANT_NORMAL,
677 CAIRO_FONT_WEIGHT_NORMAL);
678 cairo_set_font_size(c, size);
680 cairo_text_extents(c, lab, &extents);
682 double lw = extents.width + txp * 2.;
684 cairo_set_line_width(c, 1.);
686 cairo_move_to(c, ox + rad + txp + m, oy + size - 2 + m);
687 cairo_set_source_rgb(c, 0.99,0.99,0.99);
688 cairo_show_text(c, lab);
690 cairo_set_source_rgb(c, 0.9,0.9,0.9);
692 rad = 6;
693 cairo_move_to(c, ox + a + m, oy + pad + rad + a + m);
694 cairo_arc (c, ox + rad + a + m, oy + rad + a + pad + m, rad, 1 * M_PI, 1.5 * M_PI);
695 cairo_move_to(c, ox + rad + a + lw + m, oy + a + pad + m);
696 cairo_line_to(c, ox + sx + a - rad - m - 1, oy + a + pad + m);
697 cairo_arc (c, ox + sx - rad + a - 2*m - 1, oy + rad + a + pad + m, rad, 1.5 * M_PI, 2 * M_PI);
698 cairo_line_to(c, ox + sx + a - 2*m - 1, oy + a + sy - rad - 2*m - 1);
699 rad = 6;
700 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);
701 rad = 6;
702 cairo_line_to(c, ox + a + rad + m, oy + sy + a - 2*m - 1);
703 cairo_arc (c, ox + rad + a + m, oy + sy - rad + a - 2*m - 1, rad, 0.5 * M_PI, 1 * M_PI);
704 cairo_line_to(c, ox + a + m, oy + a + rad + pad + m);
705 cairo_stroke(c);
707 //a = 0.5;
709 //cairo_set_source_rgb(c, 0.66,0.66,0.66);
711 //rad = 9;
712 //cairo_move_to(c, ox + a + m, oy + pad + rad + a + m);
713 //cairo_arc (c, ox + rad + a + m, oy + rad + a + pad + m, rad, 1 * M_PI, 1.5 * M_PI);
714 //cairo_move_to(c, ox + rad + a + lw + m, oy + a + pad + m);
715 //rad = 8;
716 //cairo_line_to(c, ox + sx + a - rad - m, oy + a + pad + m);
717 //cairo_arc (c, ox + sx - rad + a - 2*m - 1, oy + rad + a + pad + m, rad, 1.5 * M_PI, 2 * M_PI);
718 //cairo_line_to(c, ox + sx + a - 2*m - 1, oy + a + sy - rad - 2*m);
719 //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);
720 //cairo_line_to(c, ox + a + rad + m, oy + sy + a - 2*m - 1);
721 //cairo_arc (c, ox + rad + a + m, oy + sy - rad + a - 2*m - 1, rad, 0.5 * M_PI, 1 * M_PI);
722 //cairo_line_to(c, ox + a + m, oy + a + rad + pad + m);
723 //cairo_stroke(c);
725 cairo_destroy(c);
727 if (gtk_bin_get_child(GTK_BIN(widget))) {
728 gtk_container_propagate_expose(GTK_CONTAINER(widget),
729 gtk_bin_get_child(GTK_BIN(widget)),
730 event);
732 return FALSE;
735 static void
736 calf_frame_class_init (CalfFrameClass *klass)
738 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
739 widget_class->expose_event = calf_frame_expose;
742 static void
743 calf_frame_init (CalfFrame *self)
745 GtkWidget *widget = GTK_WIDGET(self);
746 widget->requisition.width = 40;
747 widget->requisition.height = 40;
750 GType
751 calf_frame_get_type (void)
753 static GType type = 0;
754 if (!type) {
755 static const GTypeInfo type_info = {
756 sizeof(CalfFrameClass),
757 NULL, /* base_init */
758 NULL, /* base_finalize */
759 (GClassInitFunc)calf_frame_class_init,
760 NULL, /* class_finalize */
761 NULL, /* class_data */
762 sizeof(CalfFrame),
763 0, /* n_preallocs */
764 (GInstanceInitFunc)calf_frame_init
767 for (int i = 0; ; i++) {
768 char *name = g_strdup_printf("CalfFrame%u%d",
769 ((unsigned int)(intptr_t)calf_frame_class_init) >> 16, i);
770 if (g_type_from_name(name)) {
771 free(name);
772 continue;
774 type = g_type_register_static(GTK_TYPE_FRAME,
775 name,
776 &type_info,
777 (GTypeFlags)0);
778 free(name);
779 break;
782 return type;
785 ///////////////////////////////////////// combo box ///////////////////////////////////////////////
788 GtkWidget *
789 calf_combobox_new()
791 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_COMBOBOX, NULL ));
792 GtkCellRenderer *column = gtk_cell_renderer_text_new();
793 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(widget), column, TRUE);
794 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(widget), column,
795 "text", 0,
796 NULL);
797 return widget;
799 static gboolean
800 calf_combobox_expose (GtkWidget *widget, GdkEventExpose *event)
802 g_assert(CALF_IS_COMBOBOX(widget));
804 if (gtk_widget_is_drawable (widget)) {
806 int padx = 4;
807 int pady = 3;
809 GtkTreeModel *model = gtk_combo_box_get_model(GTK_COMBO_BOX (widget));
810 GtkTreeIter iter;
811 gchar *lab;
812 if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter))
813 gtk_tree_model_get (model, &iter, 0, &lab, -1);
814 else
815 lab = g_strdup("");
817 GdkWindow *window = widget->window;
818 cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(window));
820 int x = widget->allocation.x;
821 int y = widget->allocation.y;
822 int sx = widget->allocation.width;
823 int sy = widget->allocation.height;
824 gint mx, my;
825 bool hover = false;
827 create_rectangle(c, x, y, sx, sy, 8);
828 cairo_clip(c);
830 gtk_widget_get_pointer(GTK_WIDGET(widget), &mx, &my);
831 if (mx >= 0 and mx < sx and my >= 0 and my < sy)
832 hover = true;
834 line_graph_background(c, x, y, sx - padx * 2, sy - pady * 2, padx, pady, g_ascii_isspace(lab[0]) ? 0 : 1, 4, hover ? 0.5 : 0, hover ? 0.1 : 0.25);
836 cairo_select_font_face(c, "Sans",
837 CAIRO_FONT_SLANT_NORMAL,
838 CAIRO_FONT_WEIGHT_NORMAL);
839 cairo_set_font_size(c, 12);
841 cairo_move_to(c, x + padx + 3, y + sy / 2 + 5);
842 cairo_set_source_rgb(c, 0.,0.,0.);
843 cairo_show_text(c, lab);
844 g_free(lab);
846 cairo_surface_t *image;
847 image = cairo_image_surface_create_from_png(PKGLIBDIR "combo_arrow.png");
848 cairo_set_source_surface(c, image, x + sx - 20, y + sy / 2 - 5);
849 cairo_rectangle(c, x, y, sx, sy);
850 cairo_fill(c);
852 cairo_destroy(c);
854 return FALSE;
857 static void
858 calf_combobox_class_init (CalfComboboxClass *klass)
860 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
861 widget_class->expose_event = calf_combobox_expose;
864 static void
865 calf_combobox_init (CalfCombobox *self)
867 GtkWidget *widget = GTK_WIDGET(self);
868 widget->requisition.width = 40;
869 widget->requisition.height = 20;
872 GType
873 calf_combobox_get_type (void)
875 static GType type = 0;
876 if (!type) {
877 static const GTypeInfo type_info = {
878 sizeof(CalfComboboxClass),
879 NULL, /* base_init */
880 NULL, /* base_finalize */
881 (GClassInitFunc)calf_combobox_class_init,
882 NULL, /* class_finalize */
883 NULL, /* class_data */
884 sizeof(CalfCombobox),
885 0, /* n_preallocs */
886 (GInstanceInitFunc)calf_combobox_init
889 for (int i = 0; ; i++) {
890 char *name = g_strdup_printf("CalfCombobox%u%d",
891 ((unsigned int)(intptr_t)calf_combobox_class_init) >> 16, i);
892 if (g_type_from_name(name)) {
893 free(name);
894 continue;
896 type = g_type_register_static(GTK_TYPE_COMBO_BOX,
897 name,
898 &type_info,
899 (GTypeFlags)0);
900 free(name);
901 break;
904 return type;
908 ///////////////////////////////////////// notebook ///////////////////////////////////////////////
910 #define GTK_NOTEBOOK_PAGE(_glist_) ((GtkNotebookPage *)((GList *)(_glist_))->data)
911 struct _GtkNotebookPage
913 GtkWidget *child;
914 GtkWidget *tab_label;
915 GtkWidget *menu_label;
916 GtkWidget *last_focus_child; /* Last descendant of the page that had focus */
918 guint default_menu : 1; /* If true, we create the menu label ourself */
919 guint default_tab : 1; /* If true, we create the tab label ourself */
920 guint expand : 1;
921 guint fill : 1;
922 guint pack : 1;
923 guint reorderable : 1;
924 guint detachable : 1;
926 /* if true, the tab label was visible on last allocation; we track this so
927 * that we know to redraw the tab area if a tab label was hidden then shown
928 * without changing position */
929 guint tab_allocated_visible : 1;
931 GtkRequisition requisition;
932 GtkAllocation allocation;
934 gulong mnemonic_activate_signal;
935 gulong notify_visible_handler;
938 GtkWidget *
939 calf_notebook_new()
941 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_NOTEBOOK, NULL ));
942 return widget;
944 static gboolean
945 calf_notebook_expose (GtkWidget *widget, GdkEventExpose *event)
947 g_assert(CALF_IS_NOTEBOOK(widget));
949 GtkNotebook *notebook;
950 notebook = GTK_NOTEBOOK (widget);
951 CalfNotebookClass *klass = CALF_NOTEBOOK_CLASS(GTK_OBJECT_GET_CLASS(widget));
953 if (gtk_widget_is_drawable (widget)) {
955 GdkWindow *window = widget->window;
956 cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(window));
957 cairo_pattern_t *pat = NULL;
959 int x = widget->allocation.x;
960 int y = widget->allocation.y;
961 int sx = widget->allocation.width;
962 int sy = widget->allocation.height;
963 int tx = widget->style->xthickness;
964 int ty = widget->style->ythickness;
965 int lh = 19;
966 int bh = lh + 2 * ty;
968 cairo_rectangle(c, x, y, sx, sy);
969 cairo_clip(c);
971 int add = 0;
973 if (notebook->show_tabs) {
974 GtkNotebookPage *page;
975 GList *pages;
977 gint sp;
978 gtk_widget_style_get(widget, "tab-overlap", &sp, NULL);
980 pages = notebook->children;
982 int cn = 0;
983 while (pages) {
984 page = GTK_NOTEBOOK_PAGE (pages);
985 pages = pages->next;
986 if (page->tab_label->window == event->window &&
987 gtk_widget_is_drawable (page->tab_label)) {
988 int lx = page->tab_label->allocation.x;
989 int lw = page->tab_label->allocation.width;
991 // fix the labels position
992 page->tab_label->allocation.y = y + ty;
993 page->tab_label->allocation.height = lh;
995 // draw tab background
996 cairo_rectangle(c, lx - tx, y, lw + 2 * tx, bh);
997 cairo_set_source_rgba(c, 0,0,0, page != notebook->cur_page ? 0.25 : 0.5);
998 cairo_fill(c);
1000 if (page == notebook->cur_page) {
1001 // draw tab light
1002 cairo_rectangle(c, lx - tx + 2, y + 2, lw + 2 * tx - 4, 2);
1003 pat = cairo_pattern_create_radial(lx + lw / 2, y + bh / 2, 1, lx + lw / 2, y + bh / 2, lw + tx * 2);
1004 cairo_pattern_add_color_stop_rgb(pat, 0, 50. / 255, 1, 1);
1005 cairo_pattern_add_color_stop_rgb(pat, 0.3, 2. / 255, 180. / 255, 1);
1006 cairo_pattern_add_color_stop_rgb(pat, 0.5, 19. / 255, 220. / 255, 1);
1007 cairo_pattern_add_color_stop_rgb(pat, 1, 2. / 255, 120. / 255, 1);
1008 cairo_set_source(c, pat);
1009 cairo_fill(c);
1011 cairo_rectangle(c, lx - tx + 2, y + 1, lw + 2 * tx - 4, 1);
1012 cairo_set_source_rgba(c, 0,0,0,0.5);
1013 cairo_fill(c);
1015 cairo_rectangle(c, lx - tx + 2, y + 4, lw + 2 * tx - 4, 1);
1016 cairo_set_source_rgba(c, 1,1,1,0.3);
1017 cairo_fill(c);
1020 // draw labels
1021 gtk_container_propagate_expose (GTK_CONTAINER (notebook), page->tab_label, event);
1023 cn++;
1025 add = bh;
1028 // draw main body
1029 cairo_rectangle(c, x, y + add, sx, sy - add);
1030 cairo_set_source_rgba(c, 0,0,0,0.5);
1031 cairo_fill(c);
1033 // draw frame
1034 cairo_rectangle(c, x + 0.5, y + add + 0.5, sx - 1, sy - add - 1);
1035 pat = cairo_pattern_create_linear(x, y + add, x, y + sy - add);
1036 cairo_pattern_add_color_stop_rgba(pat, 0, 0, 0, 0, 0.3);
1037 cairo_pattern_add_color_stop_rgba(pat, 0.5, 0.5, 0.5, 0.5, 0);
1038 cairo_pattern_add_color_stop_rgba(pat, 1, 1, 1, 1, 0.2);
1039 cairo_set_source (c, pat);
1040 cairo_set_line_width(c, 1);
1041 cairo_stroke_preserve(c);
1043 int sw = gdk_pixbuf_get_width(klass->screw);
1044 int sh = gdk_pixbuf_get_height(klass->screw);
1046 // draw screws
1047 gdk_cairo_set_source_pixbuf(c, klass->screw, x, y + add);
1048 cairo_fill_preserve(c);
1049 gdk_cairo_set_source_pixbuf(c, klass->screw, x + sx - sw, y + add);
1050 cairo_fill_preserve(c);
1051 gdk_cairo_set_source_pixbuf(c, klass->screw, x, y + sy - sh);
1052 cairo_fill_preserve(c);
1053 gdk_cairo_set_source_pixbuf(c, klass->screw, x + sx - sh, y + sy - sh);
1054 cairo_fill_preserve(c);
1056 // propagate expose to all children
1057 if (notebook->cur_page)
1058 gtk_container_propagate_expose (GTK_CONTAINER (notebook),
1059 notebook->cur_page->child,
1060 event);
1062 cairo_pattern_destroy(pat);
1063 cairo_destroy(c);
1066 return FALSE;
1069 static void
1070 calf_notebook_class_init (CalfNotebookClass *klass)
1072 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
1073 widget_class->expose_event = calf_notebook_expose;
1075 klass->screw = gdk_pixbuf_new_from_file (PKGLIBDIR "screw_black.png", 0);
1078 static void
1079 calf_notebook_init (CalfNotebook *self)
1081 //GtkWidget *widget = GTK_WIDGET(self);
1084 GType
1085 calf_notebook_get_type (void)
1087 static GType type = 0;
1088 if (!type) {
1089 static const GTypeInfo type_info = {
1090 sizeof(CalfNotebookClass),
1091 NULL, /* base_init */
1092 NULL, /* base_finalize */
1093 (GClassInitFunc)calf_notebook_class_init,
1094 NULL, /* class_finalize */
1095 NULL, /* class_data */
1096 sizeof(CalfNotebook),
1097 0, /* n_preallocs */
1098 (GInstanceInitFunc)calf_notebook_init
1101 for (int i = 0; ; i++) {
1102 char *name = g_strdup_printf("CalfNotebook%u%d",
1103 ((unsigned int)(intptr_t)calf_notebook_class_init) >> 16, i);
1104 if (g_type_from_name(name)) {
1105 free(name);
1106 continue;
1108 type = g_type_register_static(GTK_TYPE_NOTEBOOK,
1109 name,
1110 &type_info,
1111 (GTypeFlags)0);
1112 free(name);
1113 break;
1116 return type;
1119 ///////////////////////////////////////// fader ///////////////////////////////////////////////
1121 static void calf_fader_set_layout(GtkWidget *widget)
1123 GtkRange *range = GTK_RANGE(widget);
1124 CalfFader *fader = CALF_FADER(widget);
1125 CalfFaderLayout layout = fader->layout;
1127 int hor = fader->horizontal;
1129 // widget layout
1130 layout.x = widget->allocation.x;
1131 layout.y = widget->allocation.y;
1132 layout.w = widget->allocation.width;
1133 layout.h = widget->allocation.height;
1135 // trough layout
1136 layout.tx = range->range_rect.x + layout.x;
1137 layout.ty = range->range_rect.y + layout.y;
1138 layout.tw = range->range_rect.width;
1139 layout.th = range->range_rect.height;
1140 layout.tc = hor ? layout.ty + layout.th / 2 : layout.tx + layout.tw / 2;
1142 // screw layout
1143 layout.scw = gdk_pixbuf_get_width(fader->screw);
1144 layout.sch = gdk_pixbuf_get_height(fader->screw);
1145 layout.scx1 = hor ? layout.tx : layout.tc - layout.scw / 2;
1146 layout.scy1 = hor ? layout.tc - layout.sch / 2 : layout.ty;
1147 layout.scx2 = hor ? layout.tx + layout.tw - layout.scw : layout.tc - layout.scw / 2;
1148 layout.scy2 = hor ? layout.tc - layout.sch / 2 : layout.ty + layout.th - layout.sch;
1150 // slit layout
1151 layout.sw = hor ? layout.tw - layout.scw * 2 - 2: 2;
1152 layout.sh = hor ? 2 : layout.th - layout.sch * 2 - 2;
1153 layout.sx = hor ? layout.tx + layout.scw + 1 : layout.tc - 1;
1154 layout.sy = hor ? layout.tc - 1 : layout.ty + layout.sch + 1;
1156 // slider layout
1157 layout.slw = gdk_pixbuf_get_width(fader->slider);
1158 layout.slh = gdk_pixbuf_get_height(fader->slider);
1159 layout.slx = fader->horizontal ? 0 : layout.tc - layout.slw / 2;
1160 layout.sly = fader->horizontal ? layout.tc - layout.slh / 2 : 0;
1162 //printf("widg %d %d %d %d | trgh %d %d %d %d %d | slid %d %d %d %d | slit %d %d %d %d\n",
1163 //layout.x, layout.y, layout.w, layout.h,
1164 //layout.tx, layout.ty, layout.tw, layout.th, layout.tc,
1165 //layout.slx, layout.sly, layout.slw, layout.slh,
1166 //layout.sx, layout.sy, layout.sw, layout.sh);
1168 fader->layout = layout;
1171 GtkWidget *
1172 calf_fader_new(const int horiz = 0, const int size = 2, const double min = 0, const double max = 1, const double step = 0.1)
1174 GtkObject *adj;
1175 gint digits;
1177 adj = gtk_adjustment_new (min, min, max, step, 10 * step, 0);
1179 if (fabs (step) >= 1.0 || step == 0.0)
1180 digits = 0;
1181 else
1182 digits = std::min(5, abs((gint) floor (log10 (fabs (step)))));
1184 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_FADER, NULL ));
1185 CalfFader *self = CALF_FADER(widget);
1187 GTK_RANGE(widget)->orientation = horiz ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1188 gtk_range_set_adjustment(GTK_RANGE(widget), GTK_ADJUSTMENT(adj));
1189 gtk_scale_set_digits(GTK_SCALE(widget), digits);
1191 self->size = size;
1192 self->horizontal = horiz;
1193 self->hover = 0;
1195 gchar * file = g_strdup_printf("%sslider%d-%s.png", PKGLIBDIR, size, horiz ? "horiz" : "vert");
1196 self->slider = gdk_pixbuf_new_from_file (file, 0);
1197 file = g_strdup_printf("%sslider%d-%s-prelight.png", PKGLIBDIR, size, horiz ? "horiz" : "vert");
1198 self->sliderpre = gdk_pixbuf_new_from_file(file, 0);
1200 self->screw = gdk_pixbuf_new_from_file (PKGLIBDIR "screw_silver.png", 0);
1202 return widget;
1205 static bool calf_fader_hover(GtkWidget *widget)
1207 CalfFader *fader = CALF_FADER(widget);
1208 CalfFaderLayout layout = fader->layout;
1209 gint mx, my;
1210 gtk_widget_get_pointer(GTK_WIDGET(widget), &mx, &my);
1212 if ((fader->horizontal and mx + layout.x >= layout.tx
1213 and mx + layout.x < layout.tx + layout.tw
1214 and my + layout.y >= layout.sly
1215 and my + layout.y < layout.sly + layout.slh)
1216 or (!fader->horizontal and mx + layout.x >= layout.slx
1217 and mx + layout.x < layout.slx + layout.slw
1218 and my + layout.y >= layout.ty
1219 and my + layout.y < layout.ty + layout.th))
1220 return true;
1221 return false;
1223 static void calf_fader_check_hover_change(GtkWidget *widget)
1225 CalfFader *fader = CALF_FADER(widget);
1226 bool hover = calf_fader_hover(widget);
1227 if (hover != fader->hover)
1228 gtk_widget_queue_draw(widget);
1229 fader->hover = hover;
1231 static gboolean
1232 calf_fader_motion (GtkWidget *widget, GdkEventMotion *event)
1234 calf_fader_check_hover_change(widget);
1235 return FALSE;
1238 static gboolean
1239 calf_fader_enter (GtkWidget *widget, GdkEventCrossing *event)
1241 calf_fader_check_hover_change(widget);
1242 return FALSE;
1245 static gboolean
1246 calf_fader_leave (GtkWidget *widget, GdkEventCrossing *event)
1248 CALF_FADER(widget)->hover = false;
1249 gtk_widget_queue_draw(widget);
1250 return FALSE;
1252 static void
1253 calf_fader_allocate (GtkWidget *widget, GtkAllocation *allocation)
1255 calf_fader_set_layout(widget);
1257 static gboolean
1258 calf_fader_expose (GtkWidget *widget, GdkEventExpose *event)
1260 g_assert(CALF_IS_FADER(widget));
1261 if (gtk_widget_is_drawable (widget)) {
1263 GdkWindow *window = widget->window;
1264 GtkScale *scale = GTK_SCALE(widget);
1265 GtkRange *range = GTK_RANGE(widget);
1266 CalfFader *fader = CALF_FADER(widget);
1267 CalfFaderLayout layout = fader->layout;
1268 cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(window));
1270 cairo_rectangle(c, layout.x, layout.y, layout.w, layout.h);
1271 cairo_clip(c);
1273 // draw slit
1274 double r = range->adjustment->upper - range->adjustment->lower;
1275 double v0 = range->adjustment->value - range->adjustment->lower;
1276 if ((fader->horizontal and gtk_range_get_inverted(range))
1277 or (!fader->horizontal and gtk_range_get_inverted(range)))
1278 v0 = -v0 + 1;
1279 int vp = v0 / r * (fader->horizontal ? layout.w - layout.slw : layout.h - layout.slh)
1280 + (fader->horizontal ? layout.slw : layout.slh) / 2
1281 + (fader->horizontal ? layout.x : layout.y);
1282 layout.slx = fader->horizontal ? vp - layout.slw / 2 : layout.tc - layout.slw / 2;
1283 layout.sly = fader->horizontal ? layout.tc - layout.slh / 2 : vp - layout.slh / 2;
1285 cairo_rectangle(c, layout.sx - 1, layout.sy - 1, layout.sw, layout.sh);
1286 cairo_set_source_rgba(c, 0,0,0, 0.25);
1287 cairo_fill(c);
1288 cairo_rectangle(c, layout.sx + 1, layout.sy + 1, layout.sw, layout.sh);
1289 cairo_set_source_rgba(c, 1,1,1, 0.125);
1290 cairo_fill(c);
1291 cairo_rectangle(c, layout.sx, layout.sy, layout.sw, layout.sh);
1292 cairo_set_source_rgb(c, 0,0,0);
1293 cairo_fill(c);
1295 // draw screws
1296 cairo_rectangle(c, layout.x, layout.y, layout.w, layout.h);
1297 gdk_cairo_set_source_pixbuf(c, fader->screw, layout.scx1, layout.scy1);
1298 cairo_fill_preserve(c);
1299 gdk_cairo_set_source_pixbuf(c, fader->screw, layout.scx2, layout.scy2);
1300 cairo_fill_preserve(c);
1302 // draw slider
1303 if (fader->hover or gtk_grab_get_current() == widget)
1304 gdk_cairo_set_source_pixbuf(c, fader->sliderpre, layout.slx, layout.sly);
1305 else
1306 gdk_cairo_set_source_pixbuf(c, fader->slider, layout.slx, layout.sly);
1307 cairo_fill(c);
1309 // draw value label
1310 if (scale->draw_value) {
1311 PangoLayout *layout;
1312 gint _x, _y;
1313 layout = gtk_scale_get_layout (scale);
1314 gtk_scale_get_layout_offsets (scale, &_x, &_y);
1315 gtk_paint_layout (widget->style, window, GTK_STATE_NORMAL, FALSE, NULL,
1316 widget, fader->horizontal ? "hscale" : "vscale", _x, _y, layout);
1319 cairo_destroy(c);
1321 return FALSE;
1324 static void
1325 calf_fader_class_init (CalfFaderClass *klass)
1327 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
1328 widget_class->expose_event = calf_fader_expose;
1331 static void
1332 calf_fader_init (CalfFader *self)
1334 GtkWidget *widget = GTK_WIDGET(self);
1335 widget->requisition.width = 40;
1336 widget->requisition.height = 40;
1338 gtk_signal_connect(GTK_OBJECT(widget), "motion-notify-event", GTK_SIGNAL_FUNC (calf_fader_motion), NULL);
1339 gtk_signal_connect(GTK_OBJECT(widget), "enter-notify-event", GTK_SIGNAL_FUNC (calf_fader_enter), NULL);
1340 gtk_signal_connect(GTK_OBJECT(widget), "leave-notify-event", GTK_SIGNAL_FUNC (calf_fader_leave), NULL);
1341 gtk_signal_connect(GTK_OBJECT(widget), "size-allocate", GTK_SIGNAL_FUNC (calf_fader_allocate), NULL);
1344 GType
1345 calf_fader_get_type (void)
1347 static GType type = 0;
1348 if (!type) {
1349 static const GTypeInfo type_info = {
1350 sizeof(CalfFaderClass),
1351 NULL, /* base_init */
1352 NULL, /* base_finalize */
1353 (GClassInitFunc)calf_fader_class_init,
1354 NULL, /* class_finalize */
1355 NULL, /* class_data */
1356 sizeof(CalfFader),
1357 0, /* n_preallocs */
1358 (GInstanceInitFunc)calf_fader_init
1361 for (int i = 0; ; i++) {
1362 char *name = g_strdup_printf("CalfFader%u%d",
1363 ((unsigned int)(intptr_t)calf_fader_class_init) >> 16, i);
1364 if (g_type_from_name(name)) {
1365 free(name);
1366 continue;
1368 type = g_type_register_static(GTK_TYPE_SCALE,
1369 name,
1370 &type_info,
1371 (GTypeFlags)0);
1372 free(name);
1373 break;
1376 return type;
1380 ///////////////////////////////////////// button ///////////////////////////////////////////////
1382 GtkWidget *
1383 calf_button_new(const gchar *label)
1385 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_BUTTON, NULL ));
1386 gtk_button_set_label(GTK_BUTTON(widget), label);
1387 return widget;
1389 static gboolean
1390 calf_button_expose (GtkWidget *widget, GdkEventExpose *event)
1392 g_assert(CALF_IS_BUTTON(widget) || CALF_IS_TOGGLE_BUTTON(widget) || CALF_IS_RADIO_BUTTON(widget));
1394 if (gtk_widget_is_drawable (widget)) {
1396 int pad = 2;
1398 GdkWindow *window = widget->window;
1399 GtkWidget *child = GTK_BIN (widget)->child;
1400 cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(window));
1401 cairo_pattern_t *pat = NULL;
1403 int x = widget->allocation.x;
1404 int y = widget->allocation.y;
1405 int sx = widget->allocation.width;
1406 int sy = widget->allocation.height;
1408 cairo_rectangle(c, x, y, sx, sy);
1409 cairo_clip(c);
1411 cairo_rectangle(c, x, y, sx, sy);
1412 pat = cairo_pattern_create_radial(x + sx / 2, y + sy / 2, 1, x + sx / 2, y + sy / 2, sx / 2);
1413 switch (gtk_widget_get_state(widget)) {
1414 case GTK_STATE_NORMAL:
1415 default:
1416 cairo_pattern_add_color_stop_rgb(pat, 0.3, 39. / 255, 52. / 255, 87. / 255);
1417 cairo_pattern_add_color_stop_rgb(pat, 1.0, 6. / 255, 5. / 255, 14. / 255);
1418 break;
1419 case GTK_STATE_PRELIGHT:
1420 cairo_pattern_add_color_stop_rgb(pat, 0.3, 19. / 255, 237. / 255, 254. / 255);
1421 cairo_pattern_add_color_stop_rgb(pat, 1.0, 0. / 255, 45. / 255, 206. / 255);
1422 break;
1423 case GTK_STATE_ACTIVE:
1424 case GTK_STATE_SELECTED:
1425 cairo_pattern_add_color_stop_rgb(pat, 0.0, 19. / 255, 237. / 255, 254. / 255);
1426 cairo_pattern_add_color_stop_rgb(pat, 0.3, 10. / 255, 200. / 255, 240. / 255);
1427 cairo_pattern_add_color_stop_rgb(pat, 0.7, 19. / 255, 237. / 255, 254. / 255);
1428 cairo_pattern_add_color_stop_rgb(pat, 1.0, 2. / 255, 168. / 255, 230. / 255);
1429 break;
1432 cairo_set_source(c, pat);
1433 cairo_fill(c);
1435 cairo_rectangle(c, x + pad, y + pad, sx - pad * 2, sy - pad * 2);
1436 if (CALF_IS_TOGGLE_BUTTON(widget) or CALF_IS_RADIO_BUTTON(widget)) {
1437 cairo_new_sub_path (c);
1438 cairo_rectangle(c, x + sx - pad * 2 - 23, y + sy / 2 - 1, 22, 2);
1439 cairo_set_fill_rule(c, CAIRO_FILL_RULE_EVEN_ODD);
1441 pat = cairo_pattern_create_linear(x + pad, y + pad, x + pad, y + sy - pad * 2);
1442 cairo_pattern_add_color_stop_rgb(pat, 0.0, 0.92, 0.92, 0.92);
1443 cairo_pattern_add_color_stop_rgb(pat, 1.0, 0.70, 0.70, 0.70);
1444 cairo_set_source(c, pat);
1445 cairo_fill(c);
1447 int _h = GTK_WIDGET(GTK_BIN(widget)->child)->allocation.height + 0;
1448 int _y = y + (sy - _h) / 2;
1449 cairo_rectangle(c, x + pad, _y, sx - pad * 2, _h);
1450 if (CALF_IS_TOGGLE_BUTTON(widget) or CALF_IS_RADIO_BUTTON(widget)) {
1451 cairo_new_sub_path (c);
1452 cairo_rectangle(c, x + sx - pad * 2 - 23, y + sy / 2 - 1, 22, 2);
1453 cairo_set_fill_rule(c, CAIRO_FILL_RULE_EVEN_ODD);
1455 pat = cairo_pattern_create_linear(x + pad, _y, x + pad, _y + _h);
1456 cairo_pattern_add_color_stop_rgb(pat, 1.0, 0.92, 0.92, 0.92);
1457 cairo_pattern_add_color_stop_rgb(pat, 0.0, 0.70, 0.70, 0.70);
1458 cairo_set_source(c, pat);
1459 cairo_fill(c);
1461 cairo_destroy(c);
1462 gtk_container_propagate_expose (GTK_CONTAINER (widget), child, event);
1464 return FALSE;
1467 static void
1468 calf_button_class_init (CalfButtonClass *klass)
1470 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
1471 widget_class->expose_event = calf_button_expose;
1474 static void
1475 calf_button_init (CalfButton *self)
1477 GtkWidget *widget = GTK_WIDGET(self);
1478 widget->requisition.width = 40;
1479 widget->requisition.height = 20;
1482 GType
1483 calf_button_get_type (void)
1485 static GType type = 0;
1486 if (!type) {
1487 static const GTypeInfo type_info = {
1488 sizeof(CalfButtonClass),
1489 NULL, /* base_init */
1490 NULL, /* base_finalize */
1491 (GClassInitFunc)calf_button_class_init,
1492 NULL, /* class_finalize */
1493 NULL, /* class_data */
1494 sizeof(CalfButton),
1495 0, /* n_preallocs */
1496 (GInstanceInitFunc)calf_button_init
1499 for (int i = 0; ; i++) {
1500 char *name = g_strdup_printf("CalfButton%u%d",
1501 ((unsigned int)(intptr_t)calf_button_class_init) >> 16, i);
1502 if (g_type_from_name(name)) {
1503 free(name);
1504 continue;
1506 type = g_type_register_static(GTK_TYPE_BUTTON,
1507 name,
1508 &type_info,
1509 (GTypeFlags)0);
1510 free(name);
1511 break;
1514 return type;
1518 ///////////////////////////////////////// toggle button ///////////////////////////////////////////////
1520 GtkWidget *
1521 calf_toggle_button_new(const gchar *label)
1523 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_TOGGLE_BUTTON, NULL ));
1524 gtk_button_set_label(GTK_BUTTON(widget), label);
1525 return widget;
1528 static void
1529 calf_toggle_button_class_init (CalfToggleButtonClass *klass)
1531 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
1532 widget_class->expose_event = calf_button_expose;
1535 static void
1536 calf_toggle_button_init (CalfToggleButton *self)
1538 GtkWidget *widget = GTK_WIDGET(self);
1539 widget->requisition.width = 40;
1540 widget->requisition.height = 20;
1543 GType
1544 calf_toggle_button_get_type (void)
1546 static GType type = 0;
1547 if (!type) {
1548 static const GTypeInfo type_info = {
1549 sizeof(CalfToggleButtonClass),
1550 NULL, /* base_init */
1551 NULL, /* base_finalize */
1552 (GClassInitFunc)calf_toggle_button_class_init,
1553 NULL, /* class_finalize */
1554 NULL, /* class_data */
1555 sizeof(CalfToggleButton),
1556 0, /* n_preallocs */
1557 (GInstanceInitFunc)calf_toggle_button_init
1560 for (int i = 0; ; i++) {
1561 char *name = g_strdup_printf("CalfToggleButton%u%d",
1562 ((unsigned int)(intptr_t)calf_toggle_button_class_init) >> 16, i);
1563 if (g_type_from_name(name)) {
1564 free(name);
1565 continue;
1567 type = g_type_register_static(GTK_TYPE_TOGGLE_BUTTON,
1568 name,
1569 &type_info,
1570 (GTypeFlags)0);
1571 free(name);
1572 break;
1575 return type;
1578 ///////////////////////////////////////// radio button ///////////////////////////////////////////////
1580 GtkWidget *
1581 calf_radio_button_new(const gchar *label)
1583 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_RADIO_BUTTON, NULL ));
1584 gtk_button_set_label(GTK_BUTTON(widget), label);
1585 return widget;
1588 static void
1589 calf_radio_button_class_init (CalfRadioButtonClass *klass)
1591 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
1592 widget_class->expose_event = calf_button_expose;
1595 static void
1596 calf_radio_button_init (CalfRadioButton *self)
1598 GtkWidget *widget = GTK_WIDGET(self);
1599 widget->requisition.width = 40;
1600 widget->requisition.height = 20;
1603 GType
1604 calf_radio_button_get_type (void)
1606 static GType type = 0;
1607 if (!type) {
1608 static const GTypeInfo type_info = {
1609 sizeof(CalfRadioButtonClass),
1610 NULL, /* base_init */
1611 NULL, /* base_finalize */
1612 (GClassInitFunc)calf_radio_button_class_init,
1613 NULL, /* class_finalize */
1614 NULL, /* class_data */
1615 sizeof(CalfRadioButton),
1616 0, /* n_preallocs */
1617 (GInstanceInitFunc)calf_radio_button_init
1620 for (int i = 0; ; i++) {
1621 char *name = g_strdup_printf("CalfRadioButton%u%d",
1622 ((unsigned int)(intptr_t)calf_radio_button_class_init) >> 16, i);
1623 if (g_type_from_name(name)) {
1624 free(name);
1625 continue;
1627 type = g_type_register_static(GTK_TYPE_RADIO_BUTTON,
1628 name,
1629 &type_info,
1630 (GTypeFlags)0);
1631 free(name);
1632 break;
1635 return type;
1638 ///////////////////////////////////////// tap button ///////////////////////////////////////////////
1640 GtkWidget *
1641 calf_tap_button_new()
1643 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_TAP_BUTTON, NULL ));
1644 return widget;
1647 static gboolean
1648 calf_tap_button_expose (GtkWidget *widget, GdkEventExpose *event)
1650 g_assert(CALF_IS_TAP_BUTTON(widget));
1651 CalfTapButton *self = CALF_TAP_BUTTON(widget);
1653 int x = widget->allocation.x + widget->allocation.width / 2 - 35;
1654 int y = widget->allocation.y + widget->allocation.height / 2 - 35;
1655 int width = 70;
1656 int height = 70;
1658 gdk_draw_pixbuf(GDK_DRAWABLE(widget->window),
1659 widget->style->fg_gc[0],
1660 self->image[self->state],
1665 width,
1666 height,
1667 GDK_RGB_DITHER_NORMAL, 0, 0);
1668 return TRUE;
1671 static void
1672 calf_tap_button_size_request (GtkWidget *widget,
1673 GtkRequisition *requisition)
1675 g_assert(CALF_IS_TAP_BUTTON(widget));
1676 requisition->width = 70;
1677 requisition->height = 70;
1679 static void
1680 calf_tap_button_class_init (CalfTapButtonClass *klass)
1682 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
1683 widget_class->expose_event = calf_tap_button_expose;
1684 widget_class->size_request = calf_tap_button_size_request;
1686 static void
1687 calf_tap_button_init (CalfTapButton *self)
1689 GtkWidget *widget = GTK_WIDGET(self);
1690 widget->requisition.width = 70;
1691 widget->requisition.height = 70;
1692 self->state = 0;
1693 GError *error = NULL;
1694 self->image[0] = gdk_pixbuf_new_from_file(PKGLIBDIR "/tap_inactive.png", &error);
1695 self->image[1] = gdk_pixbuf_new_from_file(PKGLIBDIR "/tap_prelight.png", &error);
1696 self->image[2] = gdk_pixbuf_new_from_file(PKGLIBDIR "/tap_active.png", &error);
1699 GType
1700 calf_tap_button_get_type (void)
1702 static GType type = 0;
1703 if (!type) {
1704 static const GTypeInfo type_info = {
1705 sizeof(CalfTapButtonClass),
1706 NULL, /* base_init */
1707 NULL, /* base_finalize */
1708 (GClassInitFunc)calf_tap_button_class_init,
1709 NULL, /* class_finalize */
1710 NULL, /* class_data */
1711 sizeof(CalfTapButton),
1712 0, /* n_preallocs */
1713 (GInstanceInitFunc)calf_tap_button_init
1716 for (int i = 0; ; i++) {
1717 char *name = g_strdup_printf("CalfTapButton%u%d",
1718 ((unsigned int)(intptr_t)calf_tap_button_class_init) >> 16, i);
1719 if (g_type_from_name(name)) {
1720 free(name);
1721 continue;
1723 type = g_type_register_static(GTK_TYPE_BUTTON,
1724 name,
1725 &type_info,
1726 (GTypeFlags)0);
1727 free(name);
1728 break;
1731 return type;