]> sigrok.org Git - sigrok-gtk.git/blame - sigview.c
sr/srd: Use SR_LOG_*/SRD_LOG_* macros.
[sigrok-gtk.git] / sigview.c
CommitLineData
3f63165c
UH
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 */
30GtkListStore *siglist;
31
32static void format_func(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
33 GtkTreeModel *siglist, GtkTreeIter *iter, gpointer user_data)
34{
35 int probe;
36 char *colour;
37 GArray *data;
38
39 (void)tree_column;
40 (void)user_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
57static 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
83static 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
119static 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
155static void col_resized(GtkWidget *col)
156{
157 sigview_zoom(col, 1, 0);
158}
159
160static 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
167GtkWidget *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
214static 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
245void 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