gtk: More sample width fixes.
[sigrok:sigrok.git] / gtk / gtkcellrenderersignal.c
1 /*
2  * This file is part of the sigrok project.
3  *
4  * Copyright (C) 2011 Gareth McMullin <gareth@blacksphere.co.nz>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
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
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <gtk/gtk.h>
21
22 #include "gtkcellrenderersignal.h"
23
24 enum
25 {
26         PROP_0,
27         PROP_DATA,
28         PROP_PROBE,
29         PROP_FOREGROUND,
30         PROP_SCALE,
31         PROP_OFFSET,
32 };
33
34 struct _GtkCellRendererSignalPrivate
35 {
36         GArray *data;
37         guint32 probe;
38         GdkColor foreground;
39         gdouble scale;
40         gint offset;
41 };
42
43 static void gtk_cell_renderer_signal_finalize(GObject *object);
44 static void gtk_cell_renderer_signal_get_property(GObject *object,
45                                 guint param_id, GValue *value,
46                                 GParamSpec *pspec);
47 static void gtk_cell_renderer_signal_set_property(GObject *object,
48                                 guint param_id, const GValue *value,
49                                 GParamSpec *pspec);
50 static void gtk_cell_renderer_signal_get_size(GtkCellRenderer *cell,
51                                 GtkWidget *widget,
52                                 GdkRectangle *cell_area,
53                                 gint *x_offset,
54                                 gint *y_offset,
55                                 gint *width,
56                                 gint *height);
57 static void gtk_cell_renderer_signal_render(GtkCellRenderer *cell,
58                                 GdkWindow *window,
59                                 GtkWidget *widget,
60                                 GdkRectangle *background_area,
61                                 GdkRectangle *cell_area,
62                                 GdkRectangle *expose_area,
63                                 GtkCellRendererState flags);
64
65
66 G_DEFINE_TYPE(GtkCellRendererSignal, gtk_cell_renderer_signal, GTK_TYPE_CELL_RENDERER);
67
68 static void
69 gtk_cell_renderer_signal_class_init (GtkCellRendererSignalClass *klass)
70 {
71         GObjectClass *object_class = G_OBJECT_CLASS (klass);
72         GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
73
74         object_class->finalize = gtk_cell_renderer_signal_finalize;
75         object_class->get_property = gtk_cell_renderer_signal_get_property;
76         object_class->set_property = gtk_cell_renderer_signal_set_property;
77
78         cell_class->get_size = gtk_cell_renderer_signal_get_size;
79         cell_class->render = gtk_cell_renderer_signal_render;
80
81         g_object_class_install_property(object_class,
82                                 PROP_DATA,
83                                 g_param_spec_pointer("data",
84                                                 "Data",
85                                                 "Binary samples data",
86                                                 G_PARAM_READWRITE));
87
88         g_object_class_install_property (object_class,
89                                 PROP_PROBE,
90                                 g_param_spec_int("probe",
91                                                 "Probe",
92                                                 "Bit in Data to display",
93                                                 -1, G_MAXINT, -1,
94                                                 G_PARAM_READWRITE));
95
96         g_object_class_install_property (object_class,
97                                 PROP_FOREGROUND,
98                                 g_param_spec_string("foreground",
99                                                 "Foreground",
100                                                 "Foreground Colour",
101                                                 NULL,
102                                                 G_PARAM_WRITABLE));
103
104         g_object_class_install_property (object_class,
105                                 PROP_SCALE,
106                                 g_param_spec_double("scale",
107                                                 "Scale",
108                                                 "Pixels per sample",
109                                                 0, 100, 1,
110                                                 G_PARAM_READWRITE));
111
112         g_object_class_install_property (object_class,
113                                 PROP_OFFSET,
114                                 g_param_spec_int("offset",
115                                                 "Offset",
116                                                 "Offset...",
117                                                 0, G_MAXINT, 0,
118                                                 G_PARAM_READWRITE));
119
120         g_type_class_add_private (object_class,
121                         sizeof (GtkCellRendererSignalPrivate));
122 }
123
124 static void gtk_cell_renderer_signal_init(GtkCellRendererSignal *cel)
125 {
126         GtkCellRendererSignalPrivate *priv;
127
128         cel->priv = G_TYPE_INSTANCE_GET_PRIVATE(cel,
129                                 GTK_TYPE_CELL_RENDERER_SIGNAL,
130                                 GtkCellRendererSignalPrivate);
131         priv = cel->priv;
132
133         priv->data = NULL;
134         priv->probe = -1;
135         priv->scale = 1;
136         priv->offset = 0;
137 }
138
139 GtkCellRenderer *gtk_cell_renderer_signal_new(void)
140 {
141         return g_object_new(GTK_TYPE_CELL_RENDERER_SIGNAL, NULL);
142 }
143
144 static void gtk_cell_renderer_signal_finalize(GObject *object)
145 {
146         GtkCellRendererSignal *cel = GTK_CELL_RENDERER_SIGNAL(object);
147         GtkCellRendererSignalPrivate *priv = cel->priv;
148         /* Keep this around, because it'll be useful in future */
149         (void)priv;
150         G_OBJECT_CLASS(gtk_cell_renderer_signal_parent_class)->finalize(object);
151 }
152
153 static void
154 gtk_cell_renderer_signal_get_property(GObject *object,
155                                 guint param_id,
156                                 GValue *value,
157                                 GParamSpec *pspec)
158 {
159         GtkCellRendererSignal *cel = GTK_CELL_RENDERER_SIGNAL(object);
160         GtkCellRendererSignalPrivate *priv = cel->priv;
161
162         switch (param_id) {
163         case PROP_DATA:
164                 g_value_set_pointer(value, priv->data);
165                 break;
166         case PROP_PROBE:
167                 g_value_set_int(value, priv->probe);
168                 break;
169         case PROP_SCALE:
170                 g_value_set_double(value, priv->scale);
171                 break;
172         case PROP_OFFSET:
173                 g_value_set_int(value, priv->offset);
174                 break;
175         default:
176                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
177         }
178 }
179
180 static void
181 gtk_cell_renderer_signal_set_property(GObject *object,
182                                 guint param_id,
183                                 const GValue *value,
184                                 GParamSpec *pspec)
185 {
186         GtkCellRendererSignal *cel = GTK_CELL_RENDERER_SIGNAL(object);
187         GtkCellRendererSignalPrivate *priv = cel->priv;
188
189         switch (param_id) {
190         case PROP_DATA:
191                 priv->data = g_value_get_pointer(value);
192                 break;
193         case PROP_PROBE:
194                 priv->probe = g_value_get_int(value);
195                 break;
196         case PROP_FOREGROUND:
197                 gdk_color_parse(g_value_get_string(value), &priv->foreground);
198                 break;
199         case PROP_SCALE:
200                 priv->scale = g_value_get_double(value);
201                 break;
202         case PROP_OFFSET:
203                 priv->offset = g_value_get_int(value);
204                 break;
205         default:
206                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
207         }
208 }
209
210 static void
211 gtk_cell_renderer_signal_get_size(GtkCellRenderer *cell,
212                                 GtkWidget *widget,
213                                 GdkRectangle *cell_area,
214                                 gint *x_offset,
215                                 gint *y_offset,
216                                 gint *width,
217                                 gint *height)
218 {
219         (void)cell;
220         (void)widget;
221         (void)cell_area;
222
223         /* FIXME: What about cell_area? */
224         if (width) *width = 0;
225         if (height) *height = 30;
226
227         if (x_offset) *x_offset = 0;
228         if (y_offset) *y_offset = 0;
229 }
230
231
232 static gboolean sample(GArray *data, gint probe, guint i)
233 {
234         int unitsize = g_array_get_element_size(data);
235         g_return_val_if_fail(i < data->len, FALSE);
236         g_return_val_if_fail(probe < unitsize * 8, FALSE);
237
238         return data->data[(i*unitsize) + probe/8] & (1 << (probe & 7));
239 }
240
241 static void
242 gtk_cell_renderer_signal_render(GtkCellRenderer *cell,
243                                 GdkWindow *window,
244                                 GtkWidget *widget,
245                                 GdkRectangle *background_area,
246                                 GdkRectangle *cell_area,
247                                 GdkRectangle *expose_area,
248                                 GtkCellRendererState flags)
249 {
250         GtkCellRendererSignal *cel = GTK_CELL_RENDERER_SIGNAL(cell);
251         GtkCellRendererSignalPrivate *priv= cel->priv;
252         guint nsamples = priv->data->len;
253         gint xpad, ypad;
254         int x, y, w, h;
255         guint si;
256         gdouble o;
257
258         (void)widget;
259         (void)expose_area;
260         (void)flags;
261
262         gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
263         x = cell_area->x + xpad;
264         y = cell_area->y + ypad;
265         w = cell_area->width - xpad * 2;
266         h = cell_area->height - ypad * 2;
267
268         cairo_t *cr = gdk_cairo_create(GDK_DRAWABLE(window));
269
270         /* Set clipping region to background rectangle.
271          * This prevents us drawing over other cells.
272          */
273         cairo_new_path(cr);
274         gdk_cairo_rectangle(cr, background_area);
275         cairo_clip(cr);
276
277         cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND);
278         gdk_cairo_set_source_color(cr, &priv->foreground);
279         /*cairo_set_line_width(cr, 1);*/
280         cairo_new_path(cr);
281
282         si = priv->offset / priv->scale;
283         if (si >= nsamples)
284                 return;
285         o = x - (priv->offset - si * priv->scale);
286
287         guint32 oldsample = sample(priv->data, priv->probe, si++);
288         cairo_move_to(cr, o, y +
289                 (oldsample ? 0 : h));
290         o += priv->scale;
291         
292         while ((si < nsamples) && (o - priv->scale < x+w)) {
293                 guint32 cursample = sample(priv->data, priv->probe, si);
294                 if (cursample != oldsample) {
295                         cairo_line_to(cr, o - priv->scale/8, y +
296                                 (oldsample ? 0 : h));
297                         cairo_line_to(cr, o + priv->scale/8, y +
298                                 (cursample ? 0 : h));
299                         oldsample = cursample;
300                 }
301                 o += priv->scale;
302                 si++;
303         }
304         cairo_line_to(cr, o - priv->scale/8, y +
305                 (oldsample ? 0 : h));
306
307         cairo_stroke(cr);
308         cairo_destroy(cr);
309 }