2 * This file is part of the sigrok project.
4 * Copyright (C) 2011 Gareth McMullin <gareth@blacksphere.co.nz>
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.
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.
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/>.
26 #include "gtkcellrenderersignal.h"
27 #include "sigrok-gtk.h"
29 /* FIXME: No globals */
30 GtkListStore *siglist;
32 static void format_func(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
33 GtkTreeModel *siglist, GtkTreeIter *iter, gpointer cb_data)
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.
46 gtk_tree_model_get(siglist, iter, 1, &colour, 2, &probe, -1);
48 /* Try get summary data from the list */
49 data = g_object_get_data(G_OBJECT(siglist), "summarydata");
51 data = g_object_get_data(G_OBJECT(siglist), "sampledata");
53 g_object_set(G_OBJECT(cell), "data", data, "probe", probe,
54 "foreground", colour, NULL);
57 static gboolean do_scroll_event(GtkTreeView *tv, GdkEventScroll *e)
60 GtkTreeViewColumn *col;
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))
65 if (col != g_object_get_data(G_OBJECT(tv), "signalcol"))
68 switch (e->direction) {
70 sigview_zoom(GTK_WIDGET(tv), 1.2, cx);
73 sigview_zoom(GTK_WIDGET(tv), 1/1.2, cx);
76 /* Surpress warning about unswitch enum values */
83 static gboolean do_motion_event(GtkWidget *tv, GdkEventMotion *e,
91 GtkTreeViewColumn *col;
93 gdouble scale, *rscale;
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");
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);
107 g_object_get(cel, "offset", &offset, "scale", &scale, NULL);
111 if (offset > nsamples * *rscale - width)
112 offset = nsamples * *rscale - width;
114 gtk_adjustment_set_value(adj, offset);
119 static gboolean do_button_event(GtkTreeView *tv, GdkEventButton *e,
124 GtkTreeViewColumn *col;
129 gtk_tree_view_widget_to_tree_coords(tv, e->x, e->y, &x, &y);
132 case GDK_BUTTON_PRESS:
133 if (!gtk_tree_view_get_path_at_pos(tv, x, y, NULL, &col, NULL, NULL))
135 if (col != g_object_get_data(G_OBJECT(tv), "signalcol"))
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));
141 case GDK_BUTTON_RELEASE:
142 h = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(tv), "motion-handler"));
145 g_signal_handler_disconnect(GTK_WIDGET(tv), h);
146 g_object_set_data(G_OBJECT(tv), "motion-handler", NULL);
149 /* Surpress warning about unswitch enum values */
155 static void col_resized(GtkWidget *col)
157 sigview_zoom(col, 1, 0);
160 static void pan_changed(GtkAdjustment *adj, GtkWidget *sigview)
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);
167 GtkWidget *sigview_init(void)
170 GtkTreeViewColumn *col;
171 GtkCellRenderer *cel;
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);
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);
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,
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);
201 siglist = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_STRING,
203 gtk_tree_view_set_model(GTK_TREE_VIEW(tv), GTK_TREE_MODEL(siglist));
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);
214 static GArray *summarize(GArray *in, gdouble *scale)
217 int skip = 1 / (*scale * 4);
219 unsigned unitsize = g_array_get_element_size(in);
221 uint8_t smask[unitsize];
223 g_return_val_if_fail(skip > 1, NULL);
225 ret = g_array_sized_new(FALSE, FALSE, unitsize, in->len / skip);
226 ret->len = in->len / skip;
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 */
240 memcpy(&ret->data[k*unitsize], s, unitsize);
245 void sigview_zoom(GtkWidget *sigview, gdouble zoom, gint offset)
248 GtkTreeViewColumn *col;
249 GtkCellRendererSignal *cel;
251 /* data and scale refer to summary */
254 /* rdata and rscale refer to complete data */
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.
265 if (GTK_IS_SCROLLED_WINDOW(sigview))
266 sigview = gtk_bin_get_child(GTK_BIN(sigview));
268 g_return_if_fail(GTK_IS_TREE_VIEW(sigview));
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);
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");
278 rscale = g_malloc(sizeof(rscale));
280 g_object_set_data(siglist, "rscale", rscale);
282 data = g_object_get_data(siglist, "summarydata");
287 nsamples = (rdata->len / g_array_get_element_size(rdata)) - 1;
288 if ((fabs(*rscale - (double)width/nsamples) < 1e-12) && (zoom < 1))
291 cel = g_object_get_data(G_OBJECT(sigview), "signalcel");
292 g_object_get(cel, "scale", &scale, "offset", &ofs, NULL);
305 if (*rscale < (double)width/nsamples) {
306 *rscale = (double)width/nsamples;
308 if (data && (data != rdata))
309 g_array_free(data, TRUE);
313 if (ofs > nsamples * *rscale - width)
314 ofs = nsamples * *rscale - width;
316 gtk_adjustment_configure(adj, ofs, 0, nsamples * *rscale,
317 width/16, width/2, width);
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)) {
326 g_object_set_data(siglist,
327 "summarydata", summarize(rdata, &scale));
328 if (data && (data != rdata))
329 g_array_free(data, TRUE);
332 g_object_set(cel, "scale", scale, "offset", ofs, NULL);
333 gtk_widget_queue_draw(GTK_WIDGET(sigview));