]> sigrok.org Git - sigrok-gtk.git/blob - sigview.c
gtk: Consistent 'sigrok' spelling.
[sigrok-gtk.git] / sigview.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 <sigrok.h>
21 #include <gtk/gtk.h>
22
23 #include <string.h>
24 #include <math.h>
25
26 #include "gtkcellrenderersignal.h"
27 #include "sigrok-gtk.h"
28
29 /* FIXME: No globals */
30 GtkListStore *siglist;
31
32 static void format_func(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
33                 GtkTreeModel *siglist, GtkTreeIter *iter, gpointer cb_data)
34 {
35         int probe;
36         char *colour;
37         GArray *data;
38
39         (void)tree_column;
40         (void)cb_data;
41
42         /* TODO: In future we will get data here from tree model.
43          * PD data streams will be kept in the list.  The logic stream
44          * will be used if data is NULL.
45          */
46         gtk_tree_model_get(siglist, iter, 1, &colour, 2, &probe, -1);
47
48         /* Try get summary data from the list */
49         data = g_object_get_data(G_OBJECT(siglist), "summarydata");
50         if (!data)
51                 data = g_object_get_data(G_OBJECT(siglist), "sampledata");
52
53         g_object_set(G_OBJECT(cell), "data", data, "probe", probe,
54                                 "foreground", colour, NULL);
55 }
56
57 static gboolean do_scroll_event(GtkTreeView *tv, GdkEventScroll *e)
58 {
59         gint x, y, cx;
60         GtkTreeViewColumn *col;
61
62         gtk_tree_view_widget_to_tree_coords(tv, e->x, e->y, &x, &y);
63         if (!gtk_tree_view_get_path_at_pos(tv, x, y, NULL, &col, &cx, NULL))
64                 return FALSE;
65         if (col != g_object_get_data(G_OBJECT(tv), "signalcol"))
66                 return FALSE;
67
68         switch (e->direction) {
69         case GDK_SCROLL_UP:
70                 sigview_zoom(GTK_WIDGET(tv), 1.2, cx);
71                 break;
72         case GDK_SCROLL_DOWN:
73                 sigview_zoom(GTK_WIDGET(tv), 1/1.2, cx);
74                 break;
75         default:
76                 /* Surpress warning about unswitch enum values */
77                 break;
78         }
79         
80         return TRUE;
81 }
82
83 static gboolean do_motion_event(GtkWidget *tv, GdkEventMotion *e,
84                                 GObject *cel)
85 {
86         GObject *siglist;
87         gint x;
88         gint offset;
89         GArray *data;
90         guint nsamples;
91         GtkTreeViewColumn *col;
92         gint width;
93         gdouble scale, *rscale;
94         GtkAdjustment *adj;
95
96         x = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(tv), "motion-x"));
97         g_object_set_data(G_OBJECT(tv), "motion-x", GINT_TO_POINTER((gint)e->x));
98         adj = g_object_get_data(G_OBJECT(tv), "hadj");
99
100         siglist = G_OBJECT(gtk_tree_view_get_model(GTK_TREE_VIEW(tv)));
101         data = g_object_get_data(siglist, "sampledata");
102         rscale = g_object_get_data(siglist, "rscale");
103         nsamples = (data->len / g_array_get_element_size(data)) - 1;
104         col = g_object_get_data(G_OBJECT(tv), "signalcol");
105         width = gtk_tree_view_column_get_width(col);
106
107         g_object_get(cel, "offset", &offset, "scale", &scale, NULL);
108         offset += x - e->x;
109         if (offset < 0)
110                 offset = 0;
111         if (offset > nsamples * *rscale - width)
112                 offset = nsamples * *rscale - width;
113
114         gtk_adjustment_set_value(adj, offset);
115
116         return TRUE;
117 }
118
119 static gboolean do_button_event(GtkTreeView *tv, GdkEventButton *e,
120                                 GObject *cel)
121 {
122         int h;
123         gint x, y;
124         GtkTreeViewColumn *col;
125
126         if (e->button != 3)
127                 return FALSE;
128
129         gtk_tree_view_widget_to_tree_coords(tv, e->x, e->y, &x, &y);
130
131         switch (e->type) {
132         case GDK_BUTTON_PRESS:
133                 if (!gtk_tree_view_get_path_at_pos(tv, x, y, NULL, &col, NULL, NULL))
134                         return FALSE;
135                 if (col != g_object_get_data(G_OBJECT(tv), "signalcol"))
136                         return FALSE;
137                 h = g_signal_connect(tv, "motion-notify-event", G_CALLBACK(do_motion_event), cel);
138                 g_object_set_data(G_OBJECT(tv), "motion-handler", GINT_TO_POINTER(h));
139                 g_object_set_data(G_OBJECT(tv), "motion-x", GINT_TO_POINTER((gint)e->x));
140                 break;
141         case GDK_BUTTON_RELEASE:
142                 h = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(tv), "motion-handler"));
143                 if (!h)
144                         return TRUE;
145                 g_signal_handler_disconnect(GTK_WIDGET(tv), h);
146                 g_object_set_data(G_OBJECT(tv), "motion-handler", NULL);
147                 break;
148         default:
149                 /* Surpress warning about unswitch enum values */
150                 break;
151         }
152         return TRUE;
153 }
154
155 static void col_resized(GtkWidget *col)
156 {
157         sigview_zoom(col, 1, 0);
158 }
159
160 static void pan_changed(GtkAdjustment *adj, GtkWidget *sigview)
161 {
162         GObject *cel = g_object_get_data(G_OBJECT(sigview), "signalcel");
163         g_object_set(cel, "offset", (gint)gtk_adjustment_get_value(adj), NULL);
164         gtk_widget_queue_draw(sigview);
165 }
166
167 GtkWidget *sigview_init(void)
168 {
169         GtkWidget *sw, *tv;
170         GtkTreeViewColumn *col;
171         GtkCellRenderer *cel;
172
173         sw = gtk_scrolled_window_new(NULL, NULL);
174         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
175                                 GTK_POLICY_ALWAYS, GTK_POLICY_AUTOMATIC);
176         tv = gtk_tree_view_new();
177         gtk_container_add(GTK_CONTAINER(sw), tv);
178         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tv), FALSE);
179         g_object_set(G_OBJECT(tv), "reorderable", TRUE, NULL);
180
181         col = gtk_tree_view_column_new_with_attributes(NULL,
182                                         gtk_cell_renderer_text_new(),
183                                         "text", 0, "foreground", 1, NULL);
184         gtk_tree_view_append_column(GTK_TREE_VIEW(tv), col);
185
186         cel = gtk_cell_renderer_signal_new();
187         g_object_set(G_OBJECT(cel), "ypad", 1, NULL);
188         g_signal_connect(tv, "scroll-event", G_CALLBACK(do_scroll_event), cel);
189         g_signal_connect(tv, "button-press-event", G_CALLBACK(do_button_event), cel);
190         g_signal_connect(tv, "button-release-event", G_CALLBACK(do_button_event), cel);
191         col = gtk_tree_view_column_new();
192         g_object_set_data(G_OBJECT(tv), "signalcol", col);
193         g_object_set_data(G_OBJECT(tv), "signalcel", cel);
194         gtk_tree_view_column_pack_start(col, cel, TRUE);
195         gtk_tree_view_column_set_cell_data_func(col, cel, format_func,
196                                         NULL, NULL);
197         gtk_tree_view_append_column(GTK_TREE_VIEW(tv), col);
198         g_signal_connect_swapped(col, "notify::width",
199                                         G_CALLBACK(col_resized), tv);
200
201         siglist = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_STRING,
202                                         G_TYPE_INT);
203         gtk_tree_view_set_model(GTK_TREE_VIEW(tv), GTK_TREE_MODEL(siglist));
204
205         GtkObject *pan = gtk_adjustment_new(0, 0, 0, 1, 1, 1);
206         g_object_set_data(G_OBJECT(tv), "hadj", pan);
207         gtk_range_set_adjustment(GTK_RANGE(GTK_SCROLLED_WINDOW(sw)->hscrollbar),
208                                 GTK_ADJUSTMENT(pan));
209         g_signal_connect(pan, "value-changed", G_CALLBACK(pan_changed), tv);
210         
211         return sw;
212 }
213
214 static GArray *summarize(GArray *in, gdouble *scale)
215 {
216         GArray *ret;
217         int skip = 1 / (*scale * 4);
218         guint64 i, j, k, l;
219         unsigned unitsize = g_array_get_element_size(in);
220         uint8_t s[unitsize];
221         uint8_t smask[unitsize];
222
223         g_return_val_if_fail(skip > 1, NULL);
224
225         ret = g_array_sized_new(FALSE, FALSE, unitsize, in->len / skip);
226         ret->len = in->len / skip;
227         *scale *= skip;
228
229         memset(s, 0, unitsize);
230         for (i = 0, k = 0; i < (in->len/unitsize); i += skip, k++) {
231                 memset(smask, 0xFF, unitsize);
232                 for (j = i; j < i+skip; j++) {
233                         for (l = 0; l < unitsize; l++) {
234                                 uint8_t ns = (in->data[(j*unitsize)+l] ^ s[l]) & smask[l];
235                                 /* ns is now bits we need to toggle */
236                                 s[l] ^= ns;
237                                 smask[l] &= ~ns;
238                         }
239                 }
240                 memcpy(&ret->data[k*unitsize], s, unitsize);
241         }
242         return ret;
243 }
244
245 void sigview_zoom(GtkWidget *sigview, gdouble zoom, gint offset)
246 {
247         GObject *siglist;
248         GtkTreeViewColumn *col;
249         GtkCellRendererSignal *cel;
250         GtkAdjustment *adj;
251         /* data and scale refer to summary */
252         GArray *data;
253         gdouble scale;
254         /* rdata and rscale refer to complete data */
255         GArray *rdata;
256         gdouble *rscale;
257         gint ofs;
258         gint width;
259         guint nsamples;
260
261         /* This is so that sigview_zoom() may be called with pointer
262          * to the GtkTreeView or containing GtkScrolledWindow, as is the
263          * case when called from outside this module.
264          */
265         if (GTK_IS_SCROLLED_WINDOW(sigview))
266                 sigview = gtk_bin_get_child(GTK_BIN(sigview));
267
268         g_return_if_fail(GTK_IS_TREE_VIEW(sigview));
269
270         col = g_object_get_data(G_OBJECT(sigview), "signalcol");
271         adj = g_object_get_data(G_OBJECT(sigview), "hadj");
272         width = gtk_tree_view_column_get_width(col);
273
274         siglist = G_OBJECT(gtk_tree_view_get_model(GTK_TREE_VIEW(sigview)));
275         rdata = g_object_get_data(siglist, "sampledata");
276         rscale = g_object_get_data(siglist, "rscale");
277         if (!rscale) {
278                 rscale = g_malloc(sizeof(rscale));
279                 *rscale = 1;
280                 g_object_set_data(siglist, "rscale", rscale);
281         }
282         data = g_object_get_data(siglist, "summarydata");
283         if (!rdata)
284                 return;
285         if (!data)
286                 data = rdata;
287         nsamples = (rdata->len / g_array_get_element_size(rdata)) - 1;
288         if ((fabs(*rscale - (double)width/nsamples) < 1e-12) && (zoom < 1))
289                 return;
290
291         cel = g_object_get_data(G_OBJECT(sigview), "signalcel");
292         g_object_get(cel, "scale", &scale, "offset", &ofs, NULL);
293
294         ofs += offset;
295                 
296         scale *= zoom;
297         *rscale *= zoom;
298         ofs *= zoom;
299
300         ofs -= offset;
301
302         if (ofs < 0)
303                 ofs = 0;
304
305         if (*rscale < (double)width/nsamples) {
306                 *rscale = (double)width/nsamples;
307                 scale = *rscale;
308                 if (data && (data != rdata))
309                         g_array_free(data, TRUE);
310                 data = rdata;
311         }
312
313         if (ofs > nsamples * *rscale - width)
314                 ofs = nsamples * *rscale - width;
315
316         gtk_adjustment_configure(adj, ofs, 0, nsamples * *rscale, 
317                         width/16, width/2, width);
318
319         if (scale < 0.125) {
320                 g_object_set_data(siglist,
321                         "summarydata", summarize(data, &scale));
322                 if (data && (data != rdata))
323                         g_array_free(data, TRUE);
324         } else if ((scale > 1) && (*rscale < 1)) {
325                 scale = *rscale;
326                 g_object_set_data(siglist,
327                         "summarydata", summarize(rdata, &scale));
328                 if (data && (data != rdata))
329                         g_array_free(data, TRUE);
330         }
331
332         g_object_set(cel, "scale", scale, "offset", ofs, NULL);
333         gtk_widget_queue_draw(GTK_WIDGET(sigview));
334 }
335