New rack rack-ears
[calf.git] / src / custom_ctl.cpp
blob7a417c9b429a3b50fc83c2f86e0f3fa77dab8a80
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>
31 #include <calf/drawingutils.h>
33 using namespace calf_plugins;
34 using namespace dsp;
36 ///////////////////////////////////////// phase graph ///////////////////////////////////////////////
38 static void
39 calf_phase_graph_draw_background(GtkWidget *widget, cairo_t *ctx, int sx, int sy, int ox, int oy )
41 int cx = ox + sx / 2;
42 int cy = oy + sy / 2;
44 display_background(widget, ctx, 0, 0, sx, sy, ox, oy);
45 cairo_set_source_rgb(ctx, 0.35, 0.4, 0.2);
46 cairo_select_font_face(ctx, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
47 cairo_set_font_size(ctx, 9);
48 cairo_text_extents_t te;
50 cairo_text_extents (ctx, "M", &te);
51 cairo_move_to (ctx, cx + 5, oy + 12);
52 cairo_show_text (ctx, "M");
54 cairo_text_extents (ctx, "S", &te);
55 cairo_move_to (ctx, ox + 5, cy - 5);
56 cairo_show_text (ctx, "S");
58 cairo_text_extents (ctx, "L", &te);
59 cairo_move_to (ctx, ox + 18, oy + 12);
60 cairo_show_text (ctx, "L");
62 cairo_text_extents (ctx, "R", &te);
63 cairo_move_to (ctx, ox + sx - 22, oy + 12);
64 cairo_show_text (ctx, "R");
66 cairo_set_line_width(ctx, 1);
68 cairo_move_to(ctx, ox, oy + sy * 0.5);
69 cairo_line_to(ctx, ox + sx, oy + sy * 0.5);
70 cairo_stroke(ctx);
72 cairo_move_to(ctx, ox + sx * 0.5, oy);
73 cairo_line_to(ctx, ox + sx * 0.5, oy + sy);
74 cairo_stroke(ctx);
76 cairo_set_source_rgba(ctx, 0, 0, 0, 0.2);
77 cairo_move_to(ctx, ox, oy);
78 cairo_line_to(ctx, ox + sx, oy + sy);
79 cairo_stroke(ctx);
81 cairo_move_to(ctx, ox, oy + sy);
82 cairo_line_to(ctx, ox + sx, oy);
83 cairo_stroke(ctx);
85 static void
86 calf_phase_graph_copy_surface(cairo_t *ctx, cairo_surface_t *source, float fade = 1.f)
88 // copy a surface to a cairo context
89 cairo_save(ctx);
90 cairo_set_source_surface(ctx, source, 0, 0);
91 if (fade < 1.0) {
92 float rnd = (float)rand() / (float)RAND_MAX / 100;
93 cairo_paint_with_alpha(ctx, fade * 0.35 + 0.05 + rnd);
94 } else {
95 cairo_paint(ctx);
97 cairo_restore(ctx);
99 static gboolean
100 calf_phase_graph_expose (GtkWidget *widget, GdkEventExpose *event)
102 g_assert(CALF_IS_PHASE_GRAPH(widget));
103 CalfPhaseGraph *pg = CALF_PHASE_GRAPH(widget);
104 if (!pg->source)
105 return FALSE;
107 // dimensions
108 int ox = 0, oy = 0;
109 int sx = widget->allocation.width - ox * 2, sy = widget->allocation.height - oy * 2;
110 sx += sx % 2 - 1;
111 sy += sy % 2 - 1;
112 int rad = sx / 2;
113 int cx = ox + sx / 2;
114 int cy = oy + sy / 2;
116 // some values as pointers for the audio plug-in call
117 float * phase_buffer = 0;
118 int length = 0;
119 int mode = 2;
120 float fade = 0.05;
121 bool use_fade = true;
122 int accuracy = 1;
123 bool display = true;
125 // cairo initialization stuff
126 cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(widget->window));
127 cairo_t *ctx_back;
128 cairo_t *ctx_cache;
130 if( pg->background == NULL ) {
131 // looks like its either first call or the widget has been resized.
132 // create the background surface (stolen from line graph)...
133 cairo_surface_t *window_surface = cairo_get_target(c);
134 pg->background = cairo_surface_create_similar(window_surface,
135 CAIRO_CONTENT_COLOR,
136 widget->allocation.width,
137 widget->allocation.height );
138 pg->cache = cairo_surface_create_similar(window_surface,
139 CAIRO_CONTENT_COLOR,
140 widget->allocation.width,
141 widget->allocation.height );
143 // ...and draw some bling bling onto it...
144 ctx_back = cairo_create(pg->background);
145 calf_phase_graph_draw_background(widget, ctx_back, sx, sy, ox, oy);
146 // ...and copy it to the cache
147 ctx_cache = cairo_create(pg->cache);
148 calf_phase_graph_copy_surface(ctx_cache, pg->background, 1);
149 } else {
150 ctx_back = cairo_create(pg->background);
151 ctx_cache = cairo_create(pg->cache);
154 pg->source->get_phase_graph(&phase_buffer, &length, &mode, &use_fade,
155 &fade, &accuracy, &display);
157 // process some values set by the plug-in
158 accuracy *= 2;
159 accuracy = 12 - accuracy;
161 calf_phase_graph_copy_surface(ctx_cache, pg->background, use_fade ? fade : 1);
163 if(display) {
164 cairo_rectangle(ctx_cache, ox, oy, sx, sy);
165 cairo_clip(ctx_cache);
166 cairo_set_source_rgba(ctx_cache, 0.35, 0.4, 0.2, 1);
167 double _a;
168 for(int i = 0; i < length; i+= accuracy) {
169 float l = phase_buffer[i];
170 float r = phase_buffer[i + 1];
171 if(l == 0.f and r == 0.f) continue;
172 else if(r == 0.f and l > 0.f) _a = M_PI / 2.f;
173 else if(r == 0.f and l < 0.f) _a = 3.f *M_PI / 2.f;
174 else _a = pg->_atan(l / r, l, r);
175 double _R = sqrt(pow(l, 2) + pow(r, 2));
176 _a += M_PI / 4.f;
177 float x = (-1.f)*_R * cos(_a);
178 float y = _R * sin(_a);
179 // mask the cached values
180 switch(mode) {
181 case 0:
182 // small dots
183 cairo_rectangle (ctx_cache, x * rad + cx, y * rad + cy, 1, 1);
184 break;
185 case 1:
186 // medium dots
187 cairo_rectangle (ctx_cache, x * rad + cx - 0.25, y * rad + cy - 0.25, 1.5, 1.5);
188 break;
189 case 2:
190 // big dots
191 cairo_rectangle (ctx_cache, x * rad + cx - 0.5, y * rad + cy - 0.5, 2, 2);
192 break;
193 case 3:
194 // fields
195 if(i == 0) cairo_move_to(ctx_cache,
196 x * rad + cx, y * rad + cy);
197 else cairo_line_to(ctx_cache,
198 x * rad + cx, y * rad + cy);
199 break;
200 case 4:
201 // lines
202 if(i == 0) cairo_move_to(ctx_cache,
203 x * rad + cx, y * rad + cy);
204 else cairo_line_to(ctx_cache,
205 x * rad + cx, y * rad + cy);
206 break;
209 // draw
210 switch(mode) {
211 case 0:
212 case 1:
213 case 2:
214 cairo_fill(ctx_cache);
215 break;
216 case 3:
217 cairo_fill(ctx_cache);
218 break;
219 case 4:
220 cairo_set_line_width(ctx_cache, 0.5);
221 cairo_stroke(ctx_cache);
222 break;
226 calf_phase_graph_copy_surface(c, pg->cache, 1);
228 cairo_destroy(c);
229 cairo_destroy(ctx_back);
230 cairo_destroy(ctx_cache);
231 // printf("exposed %p %dx%d %d+%d\n", widget->window, event->area.x, event->area.y, event->area.width, event->area.height);
232 return TRUE;
235 static void
236 calf_phase_graph_size_request (GtkWidget *widget,
237 GtkRequisition *requisition)
239 g_assert(CALF_IS_PHASE_GRAPH(widget));
240 // CalfLineGraph *lg = CALF_LINE_GRAPH(widget);
243 static void
244 calf_phase_graph_size_allocate (GtkWidget *widget,
245 GtkAllocation *allocation)
247 g_assert(CALF_IS_PHASE_GRAPH(widget));
248 CalfPhaseGraph *lg = CALF_PHASE_GRAPH(widget);
250 GtkWidgetClass *parent_class = (GtkWidgetClass *) g_type_class_peek_parent( CALF_PHASE_GRAPH_GET_CLASS( lg ) );
252 if(lg->background)
253 cairo_surface_destroy(lg->background);
254 lg->background = NULL;
256 widget->allocation = *allocation;
257 GtkAllocation &a = widget->allocation;
258 if (a.width > a.height) {
259 a.x += (a.width - a.height) / 2;
260 a.width = a.height;
262 if (a.width < a.height) {
263 a.y += (a.height - a.width) / 2;
264 a.height = a.width;
266 parent_class->size_allocate(widget, &a);
269 static void
270 calf_phase_graph_class_init (CalfPhaseGraphClass *klass)
272 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
273 widget_class->expose_event = calf_phase_graph_expose;
274 widget_class->size_request = calf_phase_graph_size_request;
275 widget_class->size_allocate = calf_phase_graph_size_allocate;
278 static void
279 calf_phase_graph_unrealize (GtkWidget *widget, CalfPhaseGraph *pg)
281 if( pg->background )
282 cairo_surface_destroy(pg->background);
283 pg->background = NULL;
286 static void
287 calf_phase_graph_init (CalfPhaseGraph *self)
289 GtkWidget *widget = GTK_WIDGET(self);
290 widget->requisition.width = 40;
291 widget->requisition.height = 40;
292 self->background = NULL;
293 g_signal_connect(GTK_OBJECT(widget), "unrealize", G_CALLBACK(calf_phase_graph_unrealize), (gpointer)self);
296 GtkWidget *
297 calf_phase_graph_new()
299 return GTK_WIDGET(g_object_new (CALF_TYPE_PHASE_GRAPH, NULL));
302 GType
303 calf_phase_graph_get_type (void)
305 static GType type = 0;
306 if (!type) {
307 static const GTypeInfo type_info = {
308 sizeof(CalfPhaseGraphClass),
309 NULL, /* base_init */
310 NULL, /* base_finalize */
311 (GClassInitFunc)calf_phase_graph_class_init,
312 NULL, /* class_finalize */
313 NULL, /* class_data */
314 sizeof(CalfPhaseGraph),
315 0, /* n_preallocs */
316 (GInstanceInitFunc)calf_phase_graph_init
319 GTypeInfo *type_info_copy = new GTypeInfo(type_info);
321 for (int i = 0; ; i++) {
322 char *name = g_strdup_printf("CalfPhaseGraph%u%d", ((unsigned int)(intptr_t)calf_phase_graph_class_init) >> 16, i);
323 if (g_type_from_name(name)) {
324 free(name);
325 continue;
327 type = g_type_register_static( GTK_TYPE_DRAWING_AREA,
328 name,
329 type_info_copy,
330 (GTypeFlags)0);
331 free(name);
332 break;
335 return type;
339 ///////////////////////////////////////// toggle ///////////////////////////////////////////////
341 static gboolean
342 calf_toggle_expose (GtkWidget *widget, GdkEventExpose *event)
344 g_assert(CALF_IS_TOGGLE(widget));
346 CalfToggle *self = CALF_TOGGLE(widget);
348 float sx = self->size ? self->size : 1.f / 3.f * 2.f;
349 float sy = self->size ? self->size : 1;
350 int x = widget->allocation.x + widget->allocation.width / 2 - sx * 15 - sx * 2;
351 int y = widget->allocation.y + widget->allocation.height / 2 - sy * 10 - sy * 3;
352 int width = sx * 34;
353 int height = sy * 26;
355 gdk_draw_pixbuf(GDK_DRAWABLE(widget->window),
356 widget->style->fg_gc[0],
357 self->toggle_image[self->size],
358 20 - sx * 2,
359 20 - sy * 3 + (sy * 20 + 40) * floor(.5 + gtk_range_get_value(GTK_RANGE(widget))),
362 width,
363 height,
364 GDK_RGB_DITHER_NORMAL, 0, 0);
365 return TRUE;
368 static void
369 calf_toggle_size_request (GtkWidget *widget,
370 GtkRequisition *requisition)
372 g_assert(CALF_IS_TOGGLE(widget));
374 CalfToggle *self = CALF_TOGGLE(widget);
376 float sx = self->size ? self->size : 1.f / 3.f * 2.f;
377 float sy = self->size ? self->size : 1;
379 requisition->width = 30 * sx;
380 requisition->height = 20 * sy;
383 static gboolean
384 calf_toggle_button_press (GtkWidget *widget, GdkEventButton *event)
386 g_assert(CALF_IS_TOGGLE(widget));
387 GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(widget));
388 if (gtk_range_get_value(GTK_RANGE(widget)) == adj->lower)
390 gtk_range_set_value(GTK_RANGE(widget), adj->upper);
391 } else {
392 gtk_range_set_value(GTK_RANGE(widget), adj->lower);
394 return TRUE;
397 static gboolean
398 calf_toggle_key_press (GtkWidget *widget, GdkEventKey *event)
400 switch(event->keyval)
402 case GDK_Return:
403 case GDK_KP_Enter:
404 case GDK_space:
405 return calf_toggle_button_press(widget, NULL);
407 return FALSE;
410 static void
411 calf_toggle_class_init (CalfToggleClass *klass)
413 // GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
414 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
415 widget_class->expose_event = calf_toggle_expose;
416 widget_class->size_request = calf_toggle_size_request;
417 widget_class->button_press_event = calf_toggle_button_press;
418 widget_class->key_press_event = calf_toggle_key_press;
421 static void
422 calf_toggle_init (CalfToggle *self)
424 GtkWidget *widget = GTK_WIDGET(self);
425 GTK_WIDGET_SET_FLAGS (GTK_WIDGET(self), GTK_CAN_FOCUS);
426 widget->requisition.width = 30;
427 widget->requisition.height = 20;
428 self->size = 1;
429 GError *error = NULL;
430 self->toggle_image[0] = gdk_pixbuf_new_from_file(PKGLIBDIR "/toggle0_silver.png", &error);
431 self->toggle_image[1] = gdk_pixbuf_new_from_file(PKGLIBDIR "/toggle1_silver.png", &error);
432 self->toggle_image[2] = gdk_pixbuf_new_from_file(PKGLIBDIR "/toggle2_silver.png", &error);
433 g_assert(self->toggle_image != NULL);
436 GtkWidget *
437 calf_toggle_new()
439 GtkAdjustment *adj = (GtkAdjustment *)gtk_adjustment_new(0, 0, 1, 1, 0, 0);
440 return calf_toggle_new_with_adjustment(adj);
443 static gboolean calf_toggle_value_changed(gpointer obj)
445 GtkWidget *widget = (GtkWidget *)obj;
446 CalfToggle *self = CALF_TOGGLE(widget);
447 float sx = self->size ? self->size : 1.f / 3.f * 2.f;
448 float sy = self->size ? self->size : 1;
449 gtk_widget_queue_draw_area(widget,
450 widget->allocation.x - sx * 2,
451 widget->allocation.y - sy * 3,
452 self->size * 34,
453 self->size * 26);
454 return FALSE;
457 GtkWidget *calf_toggle_new_with_adjustment(GtkAdjustment *_adjustment)
459 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_TOGGLE, NULL ));
460 if (widget) {
461 gtk_range_set_adjustment(GTK_RANGE(widget), _adjustment);
462 g_signal_connect(GTK_OBJECT(widget), "value-changed", G_CALLBACK(calf_toggle_value_changed), widget);
464 return widget;
467 GType
468 calf_toggle_get_type (void)
470 static GType type = 0;
471 if (!type) {
473 static const GTypeInfo type_info = {
474 sizeof(CalfToggleClass),
475 NULL, /* base_init */
476 NULL, /* base_finalize */
477 (GClassInitFunc)calf_toggle_class_init,
478 NULL, /* class_finalize */
479 NULL, /* class_data */
480 sizeof(CalfToggle),
481 0, /* n_preallocs */
482 (GInstanceInitFunc)calf_toggle_init
485 for (int i = 0; ; i++) {
486 char *name = g_strdup_printf("CalfToggle%u%d",
487 ((unsigned int)(intptr_t)calf_toggle_class_init) >> 16, i);
488 if (g_type_from_name(name)) {
489 free(name);
490 continue;
492 type = g_type_register_static( GTK_TYPE_RANGE,
493 name,
494 &type_info,
495 (GTypeFlags)0);
496 free(name);
497 break;
500 return type;
503 ///////////////////////////////////////// frame ///////////////////////////////////////////////
506 GtkWidget *
507 calf_frame_new(const char *label)
509 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_FRAME, NULL ));
510 CalfFrame *self = CALF_FRAME(widget);
511 gtk_frame_set_label(GTK_FRAME(self), label);
512 return widget;
514 static gboolean
515 calf_frame_expose (GtkWidget *widget, GdkEventExpose *event)
517 g_assert(CALF_IS_FRAME(widget));
518 if (gtk_widget_is_drawable (widget)) {
520 GdkWindow *window = widget->window;
521 cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(window));
522 cairo_text_extents_t extents;
524 int ox = widget->allocation.x;
525 int oy = widget->allocation.y;
526 int sx = widget->allocation.width;
527 int sy = widget->allocation.height;
529 double rad = 8;
530 double a = 1.5;
531 double pad = 4;
532 double txp = 4;
533 double m = 1;
534 double size = 10;
536 float r, g, b;
538 cairo_rectangle(c, ox, oy, sx, sy);
539 cairo_clip(c);
542 const gchar *lab = gtk_frame_get_label(GTK_FRAME(widget));
544 cairo_select_font_face(c, "Sans",
545 CAIRO_FONT_SLANT_NORMAL,
546 CAIRO_FONT_WEIGHT_NORMAL);
547 cairo_set_font_size(c, size);
549 cairo_text_extents(c, lab, &extents);
551 double lw = extents.width + txp * 2.;
553 cairo_set_line_width(c, 1.);
555 cairo_move_to(c, ox + rad + txp + m, oy + size - 2 + m);
556 get_text_color(widget, NULL, &r, &g, &b);
557 cairo_set_source_rgb(c, r, g, b);
558 cairo_show_text(c, lab);
559 get_fg_color(widget, NULL, &r, &g, &b);
560 cairo_set_source_rgb(c, r, g, b);
562 rad = 6;
563 cairo_move_to(c, ox + a + m, oy + pad + rad + a + m);
564 cairo_arc (c, ox + rad + a + m, oy + rad + a + pad + m, rad, 1 * M_PI, 1.5 * M_PI);
565 cairo_move_to(c, ox + rad + a + lw + m, oy + a + pad + m);
566 cairo_line_to(c, ox + sx + a - rad - m - 1, oy + a + pad + m);
567 cairo_arc (c, ox + sx - rad + a - 2*m - 1, oy + rad + a + pad + m, rad, 1.5 * M_PI, 2 * M_PI);
568 cairo_line_to(c, ox + sx + a - 2*m - 1, oy + a + sy - rad - 2*m - 1);
569 rad = 6;
570 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);
571 rad = 6;
572 cairo_line_to(c, ox + a + rad + m, oy + sy + a - 2*m - 1);
573 cairo_arc (c, ox + rad + a + m, oy + sy - rad + a - 2*m - 1, rad, 0.5 * M_PI, 1 * M_PI);
574 cairo_line_to(c, ox + a + m, oy + a + rad + pad + m);
575 cairo_stroke(c);
577 //a = 0.5;
579 //cairo_set_source_rgb(c, 0.66,0.66,0.66);
581 //rad = 9;
582 //cairo_move_to(c, ox + a + m, oy + pad + rad + a + m);
583 //cairo_arc (c, ox + rad + a + m, oy + rad + a + pad + m, rad, 1 * M_PI, 1.5 * M_PI);
584 //cairo_move_to(c, ox + rad + a + lw + m, oy + a + pad + m);
585 //rad = 8;
586 //cairo_line_to(c, ox + sx + a - rad - m, oy + a + pad + m);
587 //cairo_arc (c, ox + sx - rad + a - 2*m - 1, oy + rad + a + pad + m, rad, 1.5 * M_PI, 2 * M_PI);
588 //cairo_line_to(c, ox + sx + a - 2*m - 1, oy + a + sy - rad - 2*m);
589 //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);
590 //cairo_line_to(c, ox + a + rad + m, oy + sy + a - 2*m - 1);
591 //cairo_arc (c, ox + rad + a + m, oy + sy - rad + a - 2*m - 1, rad, 0.5 * M_PI, 1 * M_PI);
592 //cairo_line_to(c, ox + a + m, oy + a + rad + pad + m);
593 //cairo_stroke(c);
595 cairo_destroy(c);
597 if (gtk_bin_get_child(GTK_BIN(widget))) {
598 gtk_container_propagate_expose(GTK_CONTAINER(widget),
599 gtk_bin_get_child(GTK_BIN(widget)),
600 event);
602 return FALSE;
605 static void
606 calf_frame_class_init (CalfFrameClass *klass)
608 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
609 widget_class->expose_event = calf_frame_expose;
612 static void
613 calf_frame_init (CalfFrame *self)
615 GtkWidget *widget = GTK_WIDGET(self);
616 widget->requisition.width = 40;
617 widget->requisition.height = 40;
620 GType
621 calf_frame_get_type (void)
623 static GType type = 0;
624 if (!type) {
625 static const GTypeInfo type_info = {
626 sizeof(CalfFrameClass),
627 NULL, /* base_init */
628 NULL, /* base_finalize */
629 (GClassInitFunc)calf_frame_class_init,
630 NULL, /* class_finalize */
631 NULL, /* class_data */
632 sizeof(CalfFrame),
633 0, /* n_preallocs */
634 (GInstanceInitFunc)calf_frame_init
637 for (int i = 0; ; i++) {
638 char *name = g_strdup_printf("CalfFrame%u%d",
639 ((unsigned int)(intptr_t)calf_frame_class_init) >> 16, i);
640 if (g_type_from_name(name)) {
641 free(name);
642 continue;
644 type = g_type_register_static(GTK_TYPE_FRAME,
645 name,
646 &type_info,
647 (GTypeFlags)0);
648 free(name);
649 break;
652 return type;
655 ///////////////////////////////////////// combo box ///////////////////////////////////////////////
658 GtkWidget *
659 calf_combobox_new()
661 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_COMBOBOX, NULL ));
662 GtkCellRenderer *column = gtk_cell_renderer_text_new();
663 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(widget), column, TRUE);
664 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(widget), column,
665 "text", 0,
666 NULL);
667 return widget;
669 static gboolean
670 calf_combobox_expose (GtkWidget *widget, GdkEventExpose *event)
672 g_assert(CALF_IS_COMBOBOX(widget));
674 if (gtk_widget_is_drawable (widget)) {
676 int padx = 4;
677 int pady = 3;
678 float r, g, b;
680 GtkTreeModel *model = gtk_combo_box_get_model(GTK_COMBO_BOX (widget));
681 GtkTreeIter iter;
682 gchar *lab;
683 if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter))
684 gtk_tree_model_get (model, &iter, 0, &lab, -1);
685 else
686 lab = g_strdup("");
688 GdkWindow *window = widget->window;
689 cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(window));
691 int x = widget->allocation.x;
692 int y = widget->allocation.y;
693 int sx = widget->allocation.width;
694 int sy = widget->allocation.height;
695 gint mx, my;
696 bool hover = false;
698 create_rectangle(c, x, y, sx, sy, 8);
699 cairo_clip(c);
701 gtk_widget_get_pointer(GTK_WIDGET(widget), &mx, &my);
702 if (mx >= 0 and mx < sx and my >= 0 and my < sy)
703 hover = true;
705 display_background(widget, 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);
707 cairo_select_font_face(c, "Sans",
708 CAIRO_FONT_SLANT_NORMAL,
709 CAIRO_FONT_WEIGHT_NORMAL);
710 cairo_set_font_size(c, 12);
712 cairo_move_to(c, x + padx + 3, y + sy / 2 + 5);
714 get_fg_color(widget, NULL, &r, &g, &b);
715 cairo_set_source_rgb(c, r, g, b);
716 cairo_show_text(c, lab);
717 g_free(lab);
719 cairo_surface_t *image;
720 image = cairo_image_surface_create_from_png(PKGLIBDIR "combo_arrow.png");
721 cairo_set_source_surface(c, image, x + sx - 20, y + sy / 2 - 5);
722 cairo_rectangle(c, x, y, sx, sy);
723 cairo_fill(c);
725 cairo_destroy(c);
727 return FALSE;
730 static void
731 calf_combobox_class_init (CalfComboboxClass *klass)
733 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
734 widget_class->expose_event = calf_combobox_expose;
737 static void
738 calf_combobox_init (CalfCombobox *self)
740 GtkWidget *widget = GTK_WIDGET(self);
741 widget->requisition.width = 40;
742 widget->requisition.height = 20;
745 GType
746 calf_combobox_get_type (void)
748 static GType type = 0;
749 if (!type) {
750 static const GTypeInfo type_info = {
751 sizeof(CalfComboboxClass),
752 NULL, /* base_init */
753 NULL, /* base_finalize */
754 (GClassInitFunc)calf_combobox_class_init,
755 NULL, /* class_finalize */
756 NULL, /* class_data */
757 sizeof(CalfCombobox),
758 0, /* n_preallocs */
759 (GInstanceInitFunc)calf_combobox_init
762 for (int i = 0; ; i++) {
763 char *name = g_strdup_printf("CalfCombobox%u%d",
764 ((unsigned int)(intptr_t)calf_combobox_class_init) >> 16, i);
765 if (g_type_from_name(name)) {
766 free(name);
767 continue;
769 type = g_type_register_static(GTK_TYPE_COMBO_BOX,
770 name,
771 &type_info,
772 (GTypeFlags)0);
773 free(name);
774 break;
777 return type;
781 ///////////////////////////////////////// notebook ///////////////////////////////////////////////
783 #define GTK_NOTEBOOK_PAGE(_glist_) ((GtkNotebookPage *)((GList *)(_glist_))->data)
784 struct _GtkNotebookPage
786 GtkWidget *child;
787 GtkWidget *tab_label;
788 GtkWidget *menu_label;
789 GtkWidget *last_focus_child; /* Last descendant of the page that had focus */
791 guint default_menu : 1; /* If true, we create the menu label ourself */
792 guint default_tab : 1; /* If true, we create the tab label ourself */
793 guint expand : 1;
794 guint fill : 1;
795 guint pack : 1;
796 guint reorderable : 1;
797 guint detachable : 1;
799 /* if true, the tab label was visible on last allocation; we track this so
800 * that we know to redraw the tab area if a tab label was hidden then shown
801 * without changing position */
802 guint tab_allocated_visible : 1;
804 GtkRequisition requisition;
805 GtkAllocation allocation;
807 gulong mnemonic_activate_signal;
808 gulong notify_visible_handler;
811 GtkWidget *
812 calf_notebook_new()
814 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_NOTEBOOK, NULL ));
815 return widget;
817 static gboolean
818 calf_notebook_expose (GtkWidget *widget, GdkEventExpose *event)
820 g_assert(CALF_IS_NOTEBOOK(widget));
822 GtkNotebook *notebook;
823 notebook = GTK_NOTEBOOK (widget);
824 CalfNotebookClass *klass = CALF_NOTEBOOK_CLASS(GTK_OBJECT_GET_CLASS(widget));
826 if (gtk_widget_is_drawable (widget)) {
828 GdkWindow *window = widget->window;
829 cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(window));
830 cairo_pattern_t *pat = NULL;
832 int x = widget->allocation.x;
833 int y = widget->allocation.y;
834 int sx = widget->allocation.width;
835 int sy = widget->allocation.height;
836 int tx = widget->style->xthickness;
837 int ty = widget->style->ythickness;
838 int lh = 19;
839 int bh = lh + 2 * ty;
841 cairo_rectangle(c, x, y, sx, sy);
842 cairo_clip(c);
844 int add = 0;
846 if (notebook->show_tabs) {
847 GtkNotebookPage *page;
848 GList *pages;
850 gint sp;
851 gtk_widget_style_get(widget, "tab-overlap", &sp, NULL);
853 pages = notebook->children;
855 int cn = 0;
856 while (pages) {
857 page = GTK_NOTEBOOK_PAGE (pages);
858 pages = pages->next;
859 if (page->tab_label->window == event->window &&
860 gtk_widget_is_drawable (page->tab_label)) {
861 int lx = page->tab_label->allocation.x;
862 int lw = page->tab_label->allocation.width;
864 // fix the labels position
865 page->tab_label->allocation.y = y + ty;
866 page->tab_label->allocation.height = lh;
868 // draw tab background
869 cairo_rectangle(c, lx - tx, y, lw + 2 * tx, bh);
870 cairo_set_source_rgba(c, 0,0,0, page != notebook->cur_page ? 0.25 : 0.5);
871 cairo_fill(c);
873 if (page == notebook->cur_page) {
874 // draw tab light
875 cairo_rectangle(c, lx - tx + 2, y + 2, lw + 2 * tx - 4, 2);
876 pat = cairo_pattern_create_radial(lx + lw / 2, y + bh / 2, 1, lx + lw / 2, y + bh / 2, lw + tx * 2);
877 cairo_pattern_add_color_stop_rgb(pat, 0, 50. / 255, 1, 1);
878 cairo_pattern_add_color_stop_rgb(pat, 0.3, 2. / 255, 180. / 255, 1);
879 cairo_pattern_add_color_stop_rgb(pat, 0.5, 19. / 255, 220. / 255, 1);
880 cairo_pattern_add_color_stop_rgb(pat, 1, 2. / 255, 120. / 255, 1);
881 cairo_set_source(c, pat);
882 cairo_fill(c);
884 cairo_rectangle(c, lx - tx + 2, y + 1, lw + 2 * tx - 4, 1);
885 cairo_set_source_rgba(c, 0,0,0,0.5);
886 cairo_fill(c);
888 cairo_rectangle(c, lx - tx + 2, y + 4, lw + 2 * tx - 4, 1);
889 cairo_set_source_rgba(c, 1,1,1,0.3);
890 cairo_fill(c);
893 // draw labels
894 gtk_container_propagate_expose (GTK_CONTAINER (notebook), page->tab_label, event);
896 cn++;
898 add = bh;
901 // draw main body
902 cairo_rectangle(c, x, y + add, sx, sy - add);
903 cairo_set_source_rgba(c, 0,0,0,0.5);
904 cairo_fill(c);
906 // draw frame
907 cairo_rectangle(c, x + 0.5, y + add + 0.5, sx - 1, sy - add - 1);
908 pat = cairo_pattern_create_linear(x, y + add, x, y + sy - add);
909 cairo_pattern_add_color_stop_rgba(pat, 0, 0, 0, 0, 0.3);
910 cairo_pattern_add_color_stop_rgba(pat, 0.5, 0.5, 0.5, 0.5, 0);
911 cairo_pattern_add_color_stop_rgba(pat, 1, 1, 1, 1, 0.2);
912 cairo_set_source (c, pat);
913 cairo_set_line_width(c, 1);
914 cairo_stroke_preserve(c);
916 int sw = gdk_pixbuf_get_width(klass->screw);
917 int sh = gdk_pixbuf_get_height(klass->screw);
919 // draw screws
920 gdk_cairo_set_source_pixbuf(c, klass->screw, x, y + add);
921 cairo_fill_preserve(c);
922 gdk_cairo_set_source_pixbuf(c, klass->screw, x + sx - sw, y + add);
923 cairo_fill_preserve(c);
924 gdk_cairo_set_source_pixbuf(c, klass->screw, x, y + sy - sh);
925 cairo_fill_preserve(c);
926 gdk_cairo_set_source_pixbuf(c, klass->screw, x + sx - sh, y + sy - sh);
927 cairo_fill_preserve(c);
929 // propagate expose to all children
930 if (notebook->cur_page)
931 gtk_container_propagate_expose (GTK_CONTAINER (notebook),
932 notebook->cur_page->child,
933 event);
935 cairo_pattern_destroy(pat);
936 cairo_destroy(c);
939 return FALSE;
942 static void
943 calf_notebook_class_init (CalfNotebookClass *klass)
945 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
946 widget_class->expose_event = calf_notebook_expose;
948 klass->screw = gdk_pixbuf_new_from_file (PKGLIBDIR "screw_black.png", 0);
951 static void
952 calf_notebook_init (CalfNotebook *self)
954 //GtkWidget *widget = GTK_WIDGET(self);
957 GType
958 calf_notebook_get_type (void)
960 static GType type = 0;
961 if (!type) {
962 static const GTypeInfo type_info = {
963 sizeof(CalfNotebookClass),
964 NULL, /* base_init */
965 NULL, /* base_finalize */
966 (GClassInitFunc)calf_notebook_class_init,
967 NULL, /* class_finalize */
968 NULL, /* class_data */
969 sizeof(CalfNotebook),
970 0, /* n_preallocs */
971 (GInstanceInitFunc)calf_notebook_init
974 for (int i = 0; ; i++) {
975 char *name = g_strdup_printf("CalfNotebook%u%d",
976 ((unsigned int)(intptr_t)calf_notebook_class_init) >> 16, i);
977 if (g_type_from_name(name)) {
978 free(name);
979 continue;
981 type = g_type_register_static(GTK_TYPE_NOTEBOOK,
982 name,
983 &type_info,
984 (GTypeFlags)0);
985 free(name);
986 break;
989 return type;
992 ///////////////////////////////////////// fader ///////////////////////////////////////////////
994 static void calf_fader_set_layout(GtkWidget *widget)
996 GtkRange *range = GTK_RANGE(widget);
997 CalfFader *fader = CALF_FADER(widget);
998 CalfFaderLayout layout = fader->layout;
1000 int hor = fader->horizontal;
1002 // widget layout
1003 layout.x = widget->allocation.x;
1004 layout.y = widget->allocation.y;
1005 layout.w = widget->allocation.width;
1006 layout.h = widget->allocation.height;
1008 // trough layout
1009 layout.tx = range->range_rect.x + layout.x;
1010 layout.ty = range->range_rect.y + layout.y;
1011 layout.tw = range->range_rect.width;
1012 layout.th = range->range_rect.height;
1013 layout.tc = hor ? layout.ty + layout.th / 2 : layout.tx + layout.tw / 2;
1015 // screw layout
1016 layout.scw = gdk_pixbuf_get_width(fader->screw);
1017 layout.sch = gdk_pixbuf_get_height(fader->screw);
1018 layout.scx1 = hor ? layout.tx : layout.tc - layout.scw / 2;
1019 layout.scy1 = hor ? layout.tc - layout.sch / 2 : layout.ty;
1020 layout.scx2 = hor ? layout.tx + layout.tw - layout.scw : layout.tc - layout.scw / 2;
1021 layout.scy2 = hor ? layout.tc - layout.sch / 2 : layout.ty + layout.th - layout.sch;
1023 // slit layout
1024 layout.sw = hor ? layout.tw - layout.scw * 2 - 2: 2;
1025 layout.sh = hor ? 2 : layout.th - layout.sch * 2 - 2;
1026 layout.sx = hor ? layout.tx + layout.scw + 1 : layout.tc - 1;
1027 layout.sy = hor ? layout.tc - 1 : layout.ty + layout.sch + 1;
1029 // slider layout
1030 layout.slw = gdk_pixbuf_get_width(fader->slider);
1031 layout.slh = gdk_pixbuf_get_height(fader->slider);
1032 layout.slx = fader->horizontal ? 0 : layout.tc - layout.slw / 2;
1033 layout.sly = fader->horizontal ? layout.tc - layout.slh / 2 : 0;
1035 //printf("widg %d %d %d %d | trgh %d %d %d %d %d | slid %d %d %d %d | slit %d %d %d %d\n",
1036 //layout.x, layout.y, layout.w, layout.h,
1037 //layout.tx, layout.ty, layout.tw, layout.th, layout.tc,
1038 //layout.slx, layout.sly, layout.slw, layout.slh,
1039 //layout.sx, layout.sy, layout.sw, layout.sh);
1041 fader->layout = layout;
1044 GtkWidget *
1045 calf_fader_new(const int horiz = 0, const int size = 2, const double min = 0, const double max = 1, const double step = 0.1)
1047 GtkObject *adj;
1048 gint digits;
1050 adj = gtk_adjustment_new (min, min, max, step, 10 * step, 0);
1052 if (fabs (step) >= 1.0 || step == 0.0)
1053 digits = 0;
1054 else
1055 digits = std::min(5, abs((gint) floor (log10 (fabs (step)))));
1057 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_FADER, NULL ));
1058 CalfFader *self = CALF_FADER(widget);
1060 GTK_RANGE(widget)->orientation = horiz ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1061 gtk_range_set_adjustment(GTK_RANGE(widget), GTK_ADJUSTMENT(adj));
1062 gtk_scale_set_digits(GTK_SCALE(widget), digits);
1064 self->size = size;
1065 self->horizontal = horiz;
1066 self->hover = 0;
1068 gchar * file = g_strdup_printf("%sslider%d-%s.png", PKGLIBDIR, size, horiz ? "horiz" : "vert");
1069 self->slider = gdk_pixbuf_new_from_file (file, 0);
1070 file = g_strdup_printf("%sslider%d-%s-prelight.png", PKGLIBDIR, size, horiz ? "horiz" : "vert");
1071 self->sliderpre = gdk_pixbuf_new_from_file(file, 0);
1073 self->screw = gdk_pixbuf_new_from_file (PKGLIBDIR "screw_silver.png", 0);
1075 return widget;
1078 static bool calf_fader_hover(GtkWidget *widget)
1080 CalfFader *fader = CALF_FADER(widget);
1081 CalfFaderLayout layout = fader->layout;
1082 gint mx, my;
1083 gtk_widget_get_pointer(GTK_WIDGET(widget), &mx, &my);
1085 if ((fader->horizontal and mx + layout.x >= layout.tx
1086 and mx + layout.x < layout.tx + layout.tw
1087 and my + layout.y >= layout.sly
1088 and my + layout.y < layout.sly + layout.slh)
1089 or (!fader->horizontal and mx + layout.x >= layout.slx
1090 and mx + layout.x < layout.slx + layout.slw
1091 and my + layout.y >= layout.ty
1092 and my + layout.y < layout.ty + layout.th))
1093 return true;
1094 return false;
1096 static void calf_fader_check_hover_change(GtkWidget *widget)
1098 CalfFader *fader = CALF_FADER(widget);
1099 bool hover = calf_fader_hover(widget);
1100 if (hover != fader->hover)
1101 gtk_widget_queue_draw(widget);
1102 fader->hover = hover;
1104 static gboolean
1105 calf_fader_motion (GtkWidget *widget, GdkEventMotion *event)
1107 calf_fader_check_hover_change(widget);
1108 return FALSE;
1111 static gboolean
1112 calf_fader_enter (GtkWidget *widget, GdkEventCrossing *event)
1114 calf_fader_check_hover_change(widget);
1115 return FALSE;
1118 static gboolean
1119 calf_fader_leave (GtkWidget *widget, GdkEventCrossing *event)
1121 CALF_FADER(widget)->hover = false;
1122 gtk_widget_queue_draw(widget);
1123 return FALSE;
1125 static void
1126 calf_fader_allocate (GtkWidget *widget, GtkAllocation *allocation)
1128 calf_fader_set_layout(widget);
1130 static gboolean
1131 calf_fader_expose (GtkWidget *widget, GdkEventExpose *event)
1133 g_assert(CALF_IS_FADER(widget));
1134 if (gtk_widget_is_drawable (widget)) {
1136 GdkWindow *window = widget->window;
1137 GtkScale *scale = GTK_SCALE(widget);
1138 GtkRange *range = GTK_RANGE(widget);
1139 CalfFader *fader = CALF_FADER(widget);
1140 CalfFaderLayout layout = fader->layout;
1141 cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(window));
1143 cairo_rectangle(c, layout.x, layout.y, layout.w, layout.h);
1144 cairo_clip(c);
1146 // draw slit
1147 double r = range->adjustment->upper - range->adjustment->lower;
1148 double v0 = range->adjustment->value - range->adjustment->lower;
1149 if ((fader->horizontal and gtk_range_get_inverted(range))
1150 or (!fader->horizontal and gtk_range_get_inverted(range)))
1151 v0 = -v0 + 1;
1152 int vp = v0 / r * (fader->horizontal ? layout.w - layout.slw : layout.h - layout.slh)
1153 + (fader->horizontal ? layout.slw : layout.slh) / 2
1154 + (fader->horizontal ? layout.x : layout.y);
1155 layout.slx = fader->horizontal ? vp - layout.slw / 2 : layout.tc - layout.slw / 2;
1156 layout.sly = fader->horizontal ? layout.tc - layout.slh / 2 : vp - layout.slh / 2;
1158 cairo_rectangle(c, layout.sx - 1, layout.sy - 1, layout.sw, layout.sh);
1159 cairo_set_source_rgba(c, 0,0,0, 0.25);
1160 cairo_fill(c);
1161 cairo_rectangle(c, layout.sx + 1, layout.sy + 1, layout.sw, layout.sh);
1162 cairo_set_source_rgba(c, 1,1,1, 0.125);
1163 cairo_fill(c);
1164 cairo_rectangle(c, layout.sx, layout.sy, layout.sw, layout.sh);
1165 cairo_set_source_rgb(c, 0,0,0);
1166 cairo_fill(c);
1168 // draw screws
1169 cairo_rectangle(c, layout.x, layout.y, layout.w, layout.h);
1170 gdk_cairo_set_source_pixbuf(c, fader->screw, layout.scx1, layout.scy1);
1171 cairo_fill_preserve(c);
1172 gdk_cairo_set_source_pixbuf(c, fader->screw, layout.scx2, layout.scy2);
1173 cairo_fill_preserve(c);
1175 // draw slider
1176 if (fader->hover or gtk_grab_get_current() == widget)
1177 gdk_cairo_set_source_pixbuf(c, fader->sliderpre, layout.slx, layout.sly);
1178 else
1179 gdk_cairo_set_source_pixbuf(c, fader->slider, layout.slx, layout.sly);
1180 cairo_fill(c);
1182 // draw value label
1183 if (scale->draw_value) {
1184 PangoLayout *layout;
1185 gint _x, _y;
1186 layout = gtk_scale_get_layout (scale);
1187 gtk_scale_get_layout_offsets (scale, &_x, &_y);
1188 gtk_paint_layout (widget->style, window, GTK_STATE_NORMAL, FALSE, NULL,
1189 widget, fader->horizontal ? "hscale" : "vscale", _x, _y, layout);
1192 cairo_destroy(c);
1194 return FALSE;
1197 static void
1198 calf_fader_class_init (CalfFaderClass *klass)
1200 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
1201 widget_class->expose_event = calf_fader_expose;
1204 static void
1205 calf_fader_init (CalfFader *self)
1207 GtkWidget *widget = GTK_WIDGET(self);
1208 widget->requisition.width = 40;
1209 widget->requisition.height = 40;
1211 gtk_signal_connect(GTK_OBJECT(widget), "motion-notify-event", GTK_SIGNAL_FUNC (calf_fader_motion), NULL);
1212 gtk_signal_connect(GTK_OBJECT(widget), "enter-notify-event", GTK_SIGNAL_FUNC (calf_fader_enter), NULL);
1213 gtk_signal_connect(GTK_OBJECT(widget), "leave-notify-event", GTK_SIGNAL_FUNC (calf_fader_leave), NULL);
1214 gtk_signal_connect(GTK_OBJECT(widget), "size-allocate", GTK_SIGNAL_FUNC (calf_fader_allocate), NULL);
1217 GType
1218 calf_fader_get_type (void)
1220 static GType type = 0;
1221 if (!type) {
1222 static const GTypeInfo type_info = {
1223 sizeof(CalfFaderClass),
1224 NULL, /* base_init */
1225 NULL, /* base_finalize */
1226 (GClassInitFunc)calf_fader_class_init,
1227 NULL, /* class_finalize */
1228 NULL, /* class_data */
1229 sizeof(CalfFader),
1230 0, /* n_preallocs */
1231 (GInstanceInitFunc)calf_fader_init
1234 for (int i = 0; ; i++) {
1235 char *name = g_strdup_printf("CalfFader%u%d",
1236 ((unsigned int)(intptr_t)calf_fader_class_init) >> 16, i);
1237 if (g_type_from_name(name)) {
1238 free(name);
1239 continue;
1241 type = g_type_register_static(GTK_TYPE_SCALE,
1242 name,
1243 &type_info,
1244 (GTypeFlags)0);
1245 free(name);
1246 break;
1249 return type;
1253 ///////////////////////////////////////// button ///////////////////////////////////////////////
1255 GtkWidget *
1256 calf_button_new(const gchar *label)
1258 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_BUTTON, NULL ));
1259 gtk_button_set_label(GTK_BUTTON(widget), label);
1260 return widget;
1262 static gboolean
1263 calf_button_expose (GtkWidget *widget, GdkEventExpose *event)
1265 g_assert(CALF_IS_BUTTON(widget) || CALF_IS_TOGGLE_BUTTON(widget) || CALF_IS_RADIO_BUTTON(widget));
1267 if (gtk_widget_is_drawable (widget)) {
1269 int pad = 2;
1271 GdkWindow *window = widget->window;
1272 GtkWidget *child = GTK_BIN (widget)->child;
1273 cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(window));
1274 cairo_pattern_t *pat = NULL;
1276 int x = widget->allocation.x;
1277 int y = widget->allocation.y;
1278 int sx = widget->allocation.width;
1279 int sy = widget->allocation.height;
1281 cairo_rectangle(c, x, y, sx, sy);
1282 cairo_clip(c);
1284 cairo_rectangle(c, x, y, sx, sy);
1285 pat = cairo_pattern_create_radial(x + sx / 2, y + sy / 2, 1, x + sx / 2, y + sy / 2, sx / 2);
1286 switch (gtk_widget_get_state(widget)) {
1287 case GTK_STATE_NORMAL:
1288 default:
1289 cairo_pattern_add_color_stop_rgb(pat, 0.3, 39. / 255, 52. / 255, 87. / 255);
1290 cairo_pattern_add_color_stop_rgb(pat, 1.0, 6. / 255, 5. / 255, 14. / 255);
1291 break;
1292 case GTK_STATE_PRELIGHT:
1293 cairo_pattern_add_color_stop_rgb(pat, 0.3, 19. / 255, 237. / 255, 254. / 255);
1294 cairo_pattern_add_color_stop_rgb(pat, 1.0, 0. / 255, 45. / 255, 206. / 255);
1295 break;
1296 case GTK_STATE_ACTIVE:
1297 case GTK_STATE_SELECTED:
1298 cairo_pattern_add_color_stop_rgb(pat, 0.0, 19. / 255, 237. / 255, 254. / 255);
1299 cairo_pattern_add_color_stop_rgb(pat, 0.3, 10. / 255, 200. / 255, 240. / 255);
1300 cairo_pattern_add_color_stop_rgb(pat, 0.7, 19. / 255, 237. / 255, 254. / 255);
1301 cairo_pattern_add_color_stop_rgb(pat, 1.0, 2. / 255, 168. / 255, 230. / 255);
1302 break;
1305 cairo_set_source(c, pat);
1306 cairo_fill(c);
1308 cairo_rectangle(c, x + pad, y + pad, sx - pad * 2, sy - pad * 2);
1309 if (CALF_IS_TOGGLE_BUTTON(widget) or CALF_IS_RADIO_BUTTON(widget)) {
1310 cairo_new_sub_path (c);
1311 cairo_rectangle(c, x + sx - pad * 2 - 23, y + sy / 2 - 1, 22, 2);
1312 cairo_set_fill_rule(c, CAIRO_FILL_RULE_EVEN_ODD);
1314 pat = cairo_pattern_create_linear(x + pad, y + pad, x + pad, y + sy - pad * 2);
1315 cairo_pattern_add_color_stop_rgb(pat, 0.0, 0.92, 0.92, 0.92);
1316 cairo_pattern_add_color_stop_rgb(pat, 1.0, 0.70, 0.70, 0.70);
1317 cairo_set_source(c, pat);
1318 cairo_fill(c);
1320 int _h = GTK_WIDGET(GTK_BIN(widget)->child)->allocation.height + 0;
1321 int _y = y + (sy - _h) / 2;
1322 cairo_rectangle(c, x + pad, _y, sx - pad * 2, _h);
1323 if (CALF_IS_TOGGLE_BUTTON(widget) or CALF_IS_RADIO_BUTTON(widget)) {
1324 cairo_new_sub_path (c);
1325 cairo_rectangle(c, x + sx - pad * 2 - 23, y + sy / 2 - 1, 22, 2);
1326 cairo_set_fill_rule(c, CAIRO_FILL_RULE_EVEN_ODD);
1328 pat = cairo_pattern_create_linear(x + pad, _y, x + pad, _y + _h);
1329 cairo_pattern_add_color_stop_rgb(pat, 1.0, 0.92, 0.92, 0.92);
1330 cairo_pattern_add_color_stop_rgb(pat, 0.0, 0.70, 0.70, 0.70);
1331 cairo_set_source(c, pat);
1332 cairo_fill(c);
1334 cairo_destroy(c);
1335 gtk_container_propagate_expose (GTK_CONTAINER (widget), child, event);
1337 return FALSE;
1340 static void
1341 calf_button_class_init (CalfButtonClass *klass)
1343 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
1344 widget_class->expose_event = calf_button_expose;
1347 static void
1348 calf_button_init (CalfButton *self)
1350 GtkWidget *widget = GTK_WIDGET(self);
1351 widget->requisition.width = 40;
1352 widget->requisition.height = 20;
1355 GType
1356 calf_button_get_type (void)
1358 static GType type = 0;
1359 if (!type) {
1360 static const GTypeInfo type_info = {
1361 sizeof(CalfButtonClass),
1362 NULL, /* base_init */
1363 NULL, /* base_finalize */
1364 (GClassInitFunc)calf_button_class_init,
1365 NULL, /* class_finalize */
1366 NULL, /* class_data */
1367 sizeof(CalfButton),
1368 0, /* n_preallocs */
1369 (GInstanceInitFunc)calf_button_init
1372 for (int i = 0; ; i++) {
1373 char *name = g_strdup_printf("CalfButton%u%d",
1374 ((unsigned int)(intptr_t)calf_button_class_init) >> 16, i);
1375 if (g_type_from_name(name)) {
1376 free(name);
1377 continue;
1379 type = g_type_register_static(GTK_TYPE_BUTTON,
1380 name,
1381 &type_info,
1382 (GTypeFlags)0);
1383 free(name);
1384 break;
1387 return type;
1391 ///////////////////////////////////////// toggle button ///////////////////////////////////////////////
1393 GtkWidget *
1394 calf_toggle_button_new(const gchar *label)
1396 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_TOGGLE_BUTTON, NULL ));
1397 gtk_button_set_label(GTK_BUTTON(widget), label);
1398 return widget;
1401 static void
1402 calf_toggle_button_class_init (CalfToggleButtonClass *klass)
1404 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
1405 widget_class->expose_event = calf_button_expose;
1408 static void
1409 calf_toggle_button_init (CalfToggleButton *self)
1411 GtkWidget *widget = GTK_WIDGET(self);
1412 widget->requisition.width = 40;
1413 widget->requisition.height = 20;
1416 GType
1417 calf_toggle_button_get_type (void)
1419 static GType type = 0;
1420 if (!type) {
1421 static const GTypeInfo type_info = {
1422 sizeof(CalfToggleButtonClass),
1423 NULL, /* base_init */
1424 NULL, /* base_finalize */
1425 (GClassInitFunc)calf_toggle_button_class_init,
1426 NULL, /* class_finalize */
1427 NULL, /* class_data */
1428 sizeof(CalfToggleButton),
1429 0, /* n_preallocs */
1430 (GInstanceInitFunc)calf_toggle_button_init
1433 for (int i = 0; ; i++) {
1434 char *name = g_strdup_printf("CalfToggleButton%u%d",
1435 ((unsigned int)(intptr_t)calf_toggle_button_class_init) >> 16, i);
1436 if (g_type_from_name(name)) {
1437 free(name);
1438 continue;
1440 type = g_type_register_static(GTK_TYPE_TOGGLE_BUTTON,
1441 name,
1442 &type_info,
1443 (GTypeFlags)0);
1444 free(name);
1445 break;
1448 return type;
1451 ///////////////////////////////////////// radio button ///////////////////////////////////////////////
1453 GtkWidget *
1454 calf_radio_button_new(const gchar *label)
1456 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_RADIO_BUTTON, NULL ));
1457 gtk_button_set_label(GTK_BUTTON(widget), label);
1458 return widget;
1461 static void
1462 calf_radio_button_class_init (CalfRadioButtonClass *klass)
1464 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
1465 widget_class->expose_event = calf_button_expose;
1468 static void
1469 calf_radio_button_init (CalfRadioButton *self)
1471 GtkWidget *widget = GTK_WIDGET(self);
1472 widget->requisition.width = 40;
1473 widget->requisition.height = 20;
1476 GType
1477 calf_radio_button_get_type (void)
1479 static GType type = 0;
1480 if (!type) {
1481 static const GTypeInfo type_info = {
1482 sizeof(CalfRadioButtonClass),
1483 NULL, /* base_init */
1484 NULL, /* base_finalize */
1485 (GClassInitFunc)calf_radio_button_class_init,
1486 NULL, /* class_finalize */
1487 NULL, /* class_data */
1488 sizeof(CalfRadioButton),
1489 0, /* n_preallocs */
1490 (GInstanceInitFunc)calf_radio_button_init
1493 for (int i = 0; ; i++) {
1494 char *name = g_strdup_printf("CalfRadioButton%u%d",
1495 ((unsigned int)(intptr_t)calf_radio_button_class_init) >> 16, i);
1496 if (g_type_from_name(name)) {
1497 free(name);
1498 continue;
1500 type = g_type_register_static(GTK_TYPE_RADIO_BUTTON,
1501 name,
1502 &type_info,
1503 (GTypeFlags)0);
1504 free(name);
1505 break;
1508 return type;
1511 ///////////////////////////////////////// tap button ///////////////////////////////////////////////
1513 GtkWidget *
1514 calf_tap_button_new()
1516 GtkWidget *widget = GTK_WIDGET( g_object_new (CALF_TYPE_TAP_BUTTON, NULL ));
1517 return widget;
1520 static gboolean
1521 calf_tap_button_expose (GtkWidget *widget, GdkEventExpose *event)
1523 g_assert(CALF_IS_TAP_BUTTON(widget));
1524 CalfTapButton *self = CALF_TAP_BUTTON(widget);
1526 int x = widget->allocation.x + widget->allocation.width / 2 - 35;
1527 int y = widget->allocation.y + widget->allocation.height / 2 - 35;
1528 int width = 70;
1529 int height = 70;
1531 gdk_draw_pixbuf(GDK_DRAWABLE(widget->window),
1532 widget->style->fg_gc[0],
1533 self->image[self->state],
1538 width,
1539 height,
1540 GDK_RGB_DITHER_NORMAL, 0, 0);
1541 return TRUE;
1544 static void
1545 calf_tap_button_size_request (GtkWidget *widget,
1546 GtkRequisition *requisition)
1548 g_assert(CALF_IS_TAP_BUTTON(widget));
1549 requisition->width = 70;
1550 requisition->height = 70;
1552 static void
1553 calf_tap_button_class_init (CalfTapButtonClass *klass)
1555 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
1556 widget_class->expose_event = calf_tap_button_expose;
1557 widget_class->size_request = calf_tap_button_size_request;
1559 static void
1560 calf_tap_button_init (CalfTapButton *self)
1562 GtkWidget *widget = GTK_WIDGET(self);
1563 widget->requisition.width = 70;
1564 widget->requisition.height = 70;
1565 self->state = 0;
1566 GError *error = NULL;
1567 self->image[0] = gdk_pixbuf_new_from_file(PKGLIBDIR "/tap_inactive.png", &error);
1568 self->image[1] = gdk_pixbuf_new_from_file(PKGLIBDIR "/tap_prelight.png", &error);
1569 self->image[2] = gdk_pixbuf_new_from_file(PKGLIBDIR "/tap_active.png", &error);
1572 GType
1573 calf_tap_button_get_type (void)
1575 static GType type = 0;
1576 if (!type) {
1577 static const GTypeInfo type_info = {
1578 sizeof(CalfTapButtonClass),
1579 NULL, /* base_init */
1580 NULL, /* base_finalize */
1581 (GClassInitFunc)calf_tap_button_class_init,
1582 NULL, /* class_finalize */
1583 NULL, /* class_data */
1584 sizeof(CalfTapButton),
1585 0, /* n_preallocs */
1586 (GInstanceInitFunc)calf_tap_button_init
1589 for (int i = 0; ; i++) {
1590 char *name = g_strdup_printf("CalfTapButton%u%d",
1591 ((unsigned int)(intptr_t)calf_tap_button_class_init) >> 16, i);
1592 if (g_type_from_name(name)) {
1593 free(name);
1594 continue;
1596 type = g_type_register_static(GTK_TYPE_BUTTON,
1597 name,
1598 &type_info,
1599 (GTypeFlags)0);
1600 free(name);
1601 break;
1604 return type;