1 /*
2     Copyright (C) 2011 Tom Szilagyi
3 
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18 
19 #include <gtk/gtk.h>
20 #include <math.h>
21 #include <stdlib.h>
22 
23 #include "ir_meter.h"
24 #include "ir_utils.h"
25 
26 G_DEFINE_TYPE(IRMeter, ir_meter, GTK_TYPE_DRAWING_AREA);
27 
28 typedef struct _IRMeterPrivate IRMeterPrivate;
29 
30 #define IR_METER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), IR_METER_TYPE, IRMeterPrivate))
31 
32 struct _IRMeterPrivate {
33 	GdkPixmap * pixmap;
34 	float level;
35 };
36 
37 
38 static gboolean ir_meter_expose(GtkWidget * widget, GdkEventExpose * event);
39 static gboolean ir_meter_configure(GtkWidget * widget, GdkEventConfigure * event);
40 static void ir_meter_destroy(GtkObject * object);
41 
ir_meter_class_init(IRMeterClass * cls)42 static void ir_meter_class_init(IRMeterClass * cls) {
43 	GtkObjectClass * obj_class = GTK_OBJECT_CLASS(cls); /* changed G_ to GTK_ */
44 	GtkWidgetClass * widget_class = GTK_WIDGET_CLASS(cls);
45 	widget_class->expose_event = ir_meter_expose;
46 	widget_class->configure_event = ir_meter_configure;
47 	obj_class->destroy = ir_meter_destroy;
48 	g_type_class_add_private(obj_class, sizeof(IRMeterPrivate));
49 }
50 
ir_meter_init(IRMeter * w)51 static void ir_meter_init(IRMeter * w) {
52 	IRMeterPrivate * p = IR_METER_GET_PRIVATE(w);
53 	p->pixmap = NULL;
54 	p->level = 0.0;
55 }
56 
ir_meter_destroy(GtkObject * object)57 static void ir_meter_destroy(GtkObject * object) {
58 	IRMeter * w = IR_METER(object);
59 	IRMeterPrivate * p = IR_METER_GET_PRIVATE(w);
60 	if (p->pixmap) {
61 		gdk_pixmap_unref(p->pixmap);
62 		p->pixmap = NULL;
63 	}
64 }
65 
draw_fullscale(GtkWidget * widget)66 static void draw_fullscale(GtkWidget * widget) {
67 	IRMeterPrivate * p = IR_METER_GET_PRIVATE(widget);
68 	int w = widget->allocation.width;
69 	int h = widget->allocation.height;
70 
71 	cairo_t * cr = gdk_cairo_create(p->pixmap);
72 
73 	float fzero = 1.0f + 90.0f/96.0f; /* result of convert_real_to_scale(ADJ_*_GAIN, 0) */
74 	fzero = exp10(fzero);
75 	fzero = (fzero - 10.0f) / 90.0f;
76 	int zero = h * (1.0 - fzero);
77 
78 	cairo_rectangle(cr, 0, 0, w, zero);
79 	cairo_set_source_rgb(cr, 1, 0, 0);
80 	cairo_fill_preserve(cr);
81 	cairo_set_source_rgb(cr, 1, 0, 0);
82 	cairo_stroke(cr);
83 
84 	int n = h - zero - 1;
85 	int i;
86 	for (i = 0; i < n/2; i++) {
87 		cairo_set_source_rgb(cr, 0, 1.0, 2.0*(float)i/n);
88 		cairo_move_to(cr, 0, zero + i + 1);
89 		cairo_line_to(cr, w, zero + i + 1);
90 		cairo_stroke(cr);
91 	}
92 	for (; i < n; i++) {
93 		cairo_set_source_rgb(cr, 0, 1.0 - 2.0*(float)(i-n/2)/n, 1.0);
94 		cairo_move_to(cr, 0, zero + i + 1);
95 		cairo_line_to(cr, w, zero + i + 1);
96 		cairo_stroke(cr);
97 	}
98 	cairo_destroy(cr);
99 }
100 
draw(GtkWidget * widget)101 static void draw(GtkWidget * widget) {
102 	IRMeterPrivate * p = IR_METER_GET_PRIVATE(widget);
103 	int w = widget->allocation.width;
104 	int h = widget->allocation.height;
105 	int pos = h * (1.0f - (p->level - 10.0f) / 90.0f);
106 
107 	cairo_t * cr = gdk_cairo_create(widget->window);
108 	gdk_cairo_set_source_pixmap(cr, p->pixmap, 0, 0);
109 
110 	cairo_rectangle(cr, 0, 0, w, pos);
111 	cairo_set_source_rgb(cr, 0, 0, 0);
112 	cairo_fill_preserve(cr);
113 	cairo_set_source_rgb(cr, 0, 0, 0);
114 	cairo_stroke(cr);
115 
116 	cairo_destroy(cr);
117 }
118 
ir_meter_configure(GtkWidget * widget,GdkEventConfigure * event)119 static gboolean ir_meter_configure(GtkWidget * widget, GdkEventConfigure * event) {
120 	IRMeterPrivate * p = IR_METER_GET_PRIVATE(widget);
121 	int w = widget->allocation.width;
122 	int h = widget->allocation.height;
123 	if (p->pixmap) {
124 		gdk_pixmap_unref(p->pixmap);
125 	}
126 	p->pixmap = gdk_pixmap_new(widget->window, w, h, -1);
127 	draw_fullscale(widget);
128 	draw(widget);
129 	return TRUE;
130 }
131 
ir_meter_expose(GtkWidget * widget,GdkEventExpose * event)132 static gboolean ir_meter_expose(GtkWidget * widget, GdkEventExpose * event) {
133 	IRMeterPrivate * p = IR_METER_GET_PRIVATE(widget);
134 	gdk_draw_drawable(widget->window,
135 			  widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
136 			  p->pixmap,
137 			  event->area.x, event->area.y,
138 			  event->area.x, event->area.y,
139 			  event->area.width, event->area.height);
140 	draw(widget);
141 	return FALSE;
142 }
143 
ir_meter_redraw(IRMeter * w)144 void ir_meter_redraw(IRMeter * w) {
145 	GtkWidget * widget;
146 	GdkRegion * region;
147 	widget = GTK_WIDGET(w);
148 	if (!widget->window) {
149 		return;
150 	}
151 	region = gdk_drawable_get_clip_region(widget->window);
152 	gdk_window_invalidate_region(widget->window, region, TRUE);
153 	gdk_window_process_updates(widget->window, TRUE);
154 	gdk_region_destroy(region);
155 }
156 
ir_meter_redraw_all(IRMeter * w)157 void ir_meter_redraw_all(IRMeter * w) {
158 	GtkWidget * widget = GTK_WIDGET(w);
159 	if (!widget->window) {
160 		return;
161 	}
162 	draw_fullscale(widget);
163 	ir_meter_redraw(w);
164 }
165 
ir_meter_set_level(IRMeter * w,float level)166 void ir_meter_set_level(IRMeter * w, float level) {
167 	if (!w || !GTK_IS_WIDGET(w)) {
168 		return;
169 	}
170 	IRMeterPrivate * p = IR_METER_GET_PRIVATE(w);
171 	p->level = level;
172 	ir_meter_redraw(w);
173 }
174 
ir_meter_new(void)175 GtkWidget * ir_meter_new(void) {
176 	return (GtkWidget *)g_object_new(IR_METER_TYPE, NULL);
177 }
178