]> sigrok.org Git - sigrok-gtk.git/blob - toolbar.c
f4f8a5def4b8b906855f30ce0d240588fe341771
[sigrok-gtk.git] / toolbar.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 <stdlib.h>
21
22 #include <sigrok.h>
23
24 #include <gtk/gtk.h>
25
26 #include <stdlib.h>
27
28 #include "sigrok-gtk.h"
29
30 enum {
31         DEV_PROP_HWCAP,
32         DEV_PROP_TYPE,
33         DEV_PROP_SHORTNAME,
34         DEV_PROP_DESCRIPTION,
35         DEV_PROP_IS_TEXT,
36         DEV_PROP_TEXTVALUE,
37         DEV_PROP_BOOLVALUE,
38         MAX_DEV_PROP
39 };
40
41 static void prop_edited(GtkCellRendererText *cel, gchar *path, gchar *text,
42                         GtkListStore *props)
43 {
44         (void)cel;
45
46         struct sr_dev *dev = g_object_get_data(G_OBJECT(props), "dev");
47         GtkTreeIter iter;
48         int type, cap;
49         guint64 tmp_u64;
50         int ret = SR_ERR;
51
52         gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(props), &iter, path);
53         gtk_tree_model_get(GTK_TREE_MODEL(props), &iter,
54                                         DEV_PROP_HWCAP, &cap, 
55                                         DEV_PROP_TYPE, &type, -1);
56
57         switch (type) {
58         case SR_T_UINT64:
59                 if (sr_parse_sizestring(text, &tmp_u64) != SR_OK)
60                         return;
61
62                 ret = dev->driver->dev_config_set(dev->driver_index,
63                                                   cap, &tmp_u64);
64                 break;
65         case SR_T_CHAR:
66                 ret = dev->driver->dev_config_set(dev->driver_index, cap, text);
67                 break;
68         /* SR_T_BOOL will be handled by prop_toggled */
69         }
70
71         if (!ret)
72                 gtk_list_store_set(props, &iter, DEV_PROP_TEXTVALUE, text, -1);
73 }
74
75 static void prop_toggled(GtkCellRendererToggle *cel, gchar *path,
76                         GtkListStore *props)
77 {
78         struct sr_dev *dev = g_object_get_data(G_OBJECT(props), "dev");
79         GtkTreeIter iter;
80         int type, cap;
81         int ret;
82         gboolean val;
83         gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(props), &iter, path);
84         gtk_tree_model_get(GTK_TREE_MODEL(props), &iter,
85                                         DEV_PROP_HWCAP, &cap, 
86                                         DEV_PROP_TYPE, &type, -1);
87
88         val = !gtk_cell_renderer_toggle_get_active(cel);
89         ret = dev->driver->dev_config_set(dev->driver_index, cap, 
90                                           GINT_TO_POINTER(val));
91
92         if (!ret)
93                 gtk_list_store_set(props, &iter, DEV_PROP_BOOLVALUE, val, -1);
94 }
95
96 void dev_prop_bool_data_func(GtkCellLayout *cell_layout,
97                                 GtkCellRenderer *cell,
98                                 GtkTreeModel *tree_model,
99                                 GtkTreeIter *iter,
100                                 gpointer data)
101 {
102         (void)cell_layout;
103         (void)data;
104
105         gboolean istext, val;
106         gtk_tree_model_get(tree_model, iter, 
107                                 DEV_PROP_IS_TEXT, &istext, 
108                                 DEV_PROP_BOOLVALUE, &val, -1);
109         g_object_set(G_OBJECT(cell), "visible", !istext, "active", val, NULL);
110 }
111
112 static void dev_set_options(GtkAction *action, GtkWindow *parent)
113 {
114         (void)action;
115
116         struct sr_dev *dev = g_object_get_data(G_OBJECT(parent), "dev");
117         if (!dev)
118                 return;
119
120         GtkWidget *dialog = gtk_dialog_new_with_buttons("Device Properties",
121                                         parent, GTK_DIALOG_MODAL,
122                                         GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
123                                         NULL);
124         GtkWidget *sw = gtk_scrolled_window_new(NULL, NULL);
125         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
126                                 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
127         gtk_widget_set_size_request(sw, 300, 200);
128         GtkWidget *tv = gtk_tree_view_new();
129         gtk_container_add(GTK_CONTAINER(sw), tv);
130         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), sw,
131                                 TRUE, TRUE, 0);
132
133         /* Populate list store with config options */
134         GtkListStore *props = gtk_list_store_new(MAX_DEV_PROP, 
135                                         G_TYPE_INT, G_TYPE_INT,
136                                         G_TYPE_STRING, G_TYPE_STRING,
137                                         G_TYPE_BOOLEAN, G_TYPE_STRING,
138                                         G_TYPE_BOOLEAN);
139         gtk_tree_view_set_model(GTK_TREE_VIEW(tv), GTK_TREE_MODEL(props));
140         int *hwcaps = dev->driver->hwcap_get_all();
141         int cap;
142         GtkTreeIter iter;
143         for (cap = 0; hwcaps[cap]; cap++) {
144                 struct sr_hwcap_option *hwo;
145                 if (!(hwo = sr_hw_hwcap_get(hwcaps[cap])))
146                         continue;
147                 gtk_list_store_append(props, &iter);
148                 gtk_list_store_set(props, &iter, 
149                                         DEV_PROP_HWCAP, hwcaps[cap],
150                                         DEV_PROP_TYPE, hwo->type,
151                                         DEV_PROP_SHORTNAME, hwo->shortname,
152                                         DEV_PROP_DESCRIPTION, hwo->description,
153                                         DEV_PROP_IS_TEXT, hwo->type != SR_T_BOOL,
154                                         -1);
155         }
156
157         /* Popup tooltop containing description if mouse hovers */
158         gtk_tree_view_set_tooltip_column(GTK_TREE_VIEW(tv), 
159                                         DEV_PROP_DESCRIPTION);
160
161         /* Save device with list so that property can be set by edited
162          * handler. */
163         g_object_set_data(G_OBJECT(props), "dev", dev);
164
165         /* Add columns to the tree view */
166         GtkTreeViewColumn *col;
167         col = gtk_tree_view_column_new_with_attributes("Property",
168                                 gtk_cell_renderer_text_new(),
169                                 "text", DEV_PROP_SHORTNAME, NULL);
170         gtk_tree_view_append_column(GTK_TREE_VIEW(tv), col);
171         /* We pack both a text and toggle renderer.  Only one will be visible.
172          * depending on type.
173          */
174         GtkCellRenderer *cel = gtk_cell_renderer_text_new();
175         g_object_set(cel, "editable", TRUE, NULL);
176         g_signal_connect(cel, "edited", G_CALLBACK(prop_edited), props);
177         col = gtk_tree_view_column_new_with_attributes("Value",
178                                 cel, "text", DEV_PROP_TEXTVALUE, 
179                                 "visible", DEV_PROP_IS_TEXT, NULL);
180         cel = gtk_cell_renderer_toggle_new();
181         g_signal_connect(cel, "toggled", G_CALLBACK(prop_toggled), props);
182         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(col), cel, TRUE);
183         gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(col), cel, 
184                                 dev_prop_bool_data_func, NULL, NULL);
185         gtk_tree_view_append_column(GTK_TREE_VIEW(tv), col);
186
187
188         gtk_widget_show_all(dialog);
189         gtk_dialog_run(GTK_DIALOG(dialog));
190
191         gtk_widget_destroy(dialog);
192 }
193
194 enum {
195         PROBE_NUMBER,
196         PROBE_ENABLED,
197         PROBE_NAME,
198         PROBE_TRIGGER,
199         MAX_PROBE
200 };
201
202 static void probe_toggled(GtkCellRenderer *cel, gchar *path,
203                         GtkTreeModel *probes)
204 {
205         struct sr_dev *dev = g_object_get_data(G_OBJECT(probes), "dev");
206         GtkTreeIter iter;
207         struct sr_probe *probe;
208         gint i;
209         gboolean en;
210
211         (void)cel;
212
213         gtk_tree_model_get_iter_from_string(probes, &iter, path);
214         gtk_tree_model_get(probes, &iter, PROBE_NUMBER, &i, 
215                                         PROBE_ENABLED, &en, -1);
216         probe = sr_dev_probe_find(dev, i);
217         probe->enabled = !en;
218         gtk_list_store_set(GTK_LIST_STORE(probes), &iter, 
219                                         PROBE_ENABLED, probe->enabled, -1);
220 }
221
222 static void probe_named(GtkCellRendererText *cel, gchar *path, gchar *text,
223                         GtkTreeModel *probes)
224 {
225         struct sr_dev *dev = g_object_get_data(G_OBJECT(probes), "dev");
226         GtkTreeIter iter;
227         gint i;
228
229         (void)cel;
230
231         gtk_tree_model_get_iter_from_string(probes, &iter, path);
232         gtk_tree_model_get(probes, &iter, PROBE_NUMBER, &i, -1);
233         sr_dev_probe_name_set(dev, i, text);
234         gtk_list_store_set(GTK_LIST_STORE(probes), &iter, PROBE_NAME, text, -1);
235 }
236
237 static void probe_trigger_set(GtkCellRendererText *cel, gchar *path, 
238                         gchar *text, GtkTreeModel *probes)
239 {
240         struct sr_dev *dev = g_object_get_data(G_OBJECT(probes), "dev");
241         GtkTreeIter iter;
242         gint i;
243
244         (void)cel;
245
246         gtk_tree_model_get_iter_from_string(probes, &iter, path);
247         gtk_tree_model_get(probes, &iter, PROBE_NUMBER, &i, -1);
248         sr_dev_trigger_set(dev, i, text);
249         gtk_list_store_set(GTK_LIST_STORE(probes), &iter, 
250                                         PROBE_TRIGGER, text, -1);
251 }
252
253 static void dev_set_probes(GtkAction *action, GtkWindow *parent)
254 {
255         (void)action;
256
257         struct sr_dev *dev = g_object_get_data(G_OBJECT(parent), "dev");
258         if (!dev)
259                 return;
260
261         GtkWidget *dialog = gtk_dialog_new_with_buttons("Configure Probes",
262                                         parent, GTK_DIALOG_MODAL,
263                                         GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
264                                         NULL);
265         GtkWidget *sw = gtk_scrolled_window_new(NULL, NULL);
266         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
267                                 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
268         gtk_widget_set_size_request(sw, 300, 200);
269         GtkWidget *tv = gtk_tree_view_new();
270         gtk_container_add(GTK_CONTAINER(sw), tv);
271         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), sw,
272                                 TRUE, TRUE, 0);
273
274         /* Populate list store with probe options */
275         GtkListStore *probes = gtk_list_store_new(MAX_PROBE, 
276                                         G_TYPE_INT, G_TYPE_BOOLEAN,
277                                         G_TYPE_STRING, GTK_TYPE_STRING);
278         gtk_tree_view_set_model(GTK_TREE_VIEW(tv), GTK_TREE_MODEL(probes));
279         GtkTreeIter iter;
280         GSList *p;
281         int i;
282         for (p = dev->probes, i = 1; p; p = g_slist_next(p), i++) {
283                 struct sr_probe *probe = p->data;
284                 gtk_list_store_append(probes, &iter);
285                 gtk_list_store_set(probes, &iter, PROBE_NUMBER, i,
286                                         PROBE_ENABLED, probe->enabled, 
287                                         PROBE_NAME, probe->name, 
288                                         PROBE_TRIGGER, probe->trigger, 
289                                         -1);
290         }
291
292         /* Save device with list so that property can be set by edited
293          * handler. */
294         g_object_set_data(G_OBJECT(probes), "dev", dev);
295
296         /* Add columns to the tree view */
297         GtkTreeViewColumn *col;
298         col = gtk_tree_view_column_new_with_attributes("Probe",
299                                 gtk_cell_renderer_text_new(),
300                                 "text", PROBE_NUMBER, NULL);
301         gtk_tree_view_append_column(GTK_TREE_VIEW(tv), col);
302         GtkCellRenderer *cel = gtk_cell_renderer_toggle_new();
303         g_object_set(cel, "activatable", TRUE, NULL);
304         g_signal_connect(cel, "toggled", G_CALLBACK(probe_toggled), probes);
305         col = gtk_tree_view_column_new_with_attributes("En",
306                                 cel, "active", PROBE_ENABLED, NULL);
307         gtk_tree_view_append_column(GTK_TREE_VIEW(tv), col);
308         cel = gtk_cell_renderer_text_new();
309         g_object_set(cel, "editable", TRUE, NULL);
310         g_signal_connect(cel, "edited", G_CALLBACK(probe_named), probes);
311         col = gtk_tree_view_column_new_with_attributes("Signal Name", cel, 
312                                         "text", PROBE_NAME, 
313                                         "sensitive", PROBE_ENABLED, NULL);
314         gtk_tree_view_column_set_resizable(col, TRUE);
315         gtk_tree_view_append_column(GTK_TREE_VIEW(tv), col);
316         cel = gtk_cell_renderer_text_new();
317         g_object_set(cel, "editable", TRUE, NULL);
318         g_signal_connect(cel, "edited", G_CALLBACK(probe_trigger_set), probes);
319         col = gtk_tree_view_column_new_with_attributes("Trigger", cel, 
320                                         "text", PROBE_TRIGGER, 
321                                         "sensitive", PROBE_ENABLED, NULL);
322         gtk_tree_view_append_column(GTK_TREE_VIEW(tv), col);
323
324         gtk_widget_show_all(dialog);
325         gtk_dialog_run(GTK_DIALOG(dialog));
326
327         gtk_widget_destroy(dialog);
328 }
329
330 static void capture_run(GtkAction *action, GObject *parent)
331 {
332         (void)action;
333
334         struct sr_dev *dev = g_object_get_data(G_OBJECT(parent), "dev");
335         GtkEntry *timesamples = g_object_get_data(parent, "timesamples");
336         GtkComboBox *timeunit = g_object_get_data(parent, "timeunit");
337         gint i = gtk_combo_box_get_active(timeunit);
338         guint64 time_msec = 0;
339         guint64 limit_samples = 0;
340         
341         switch (i) {
342         case 0: /* Samples */
343                 sr_parse_sizestring(gtk_entry_get_text(timesamples), 
344                                 &limit_samples);
345                 break;
346         case 1: /* Milliseconds */
347                 time_msec = strtoull(gtk_entry_get_text(timesamples), NULL, 10);
348                 break;
349         case 2: /* Seconds */
350                 time_msec = strtoull(gtk_entry_get_text(timesamples), NULL, 10)
351                                 * 1000;
352                 break;
353         }
354
355         if (time_msec) {
356                 if (sr_driver_hwcap_exists(dev->driver, SR_HWCAP_LIMIT_MSEC)) {
357                         if (dev->driver->dev_config_set(dev->driver_index,
358                                                         SR_HWCAP_LIMIT_MSEC,
359                                                         &time_msec) != SR_OK) {
360                                 g_critical("Failed to configure time limit.");
361                                 sr_session_destroy();
362                                 return;
363                         }
364                 } else {
365                         /* time limit set, but device doesn't support this...
366                          * convert to samples based on the samplerate.
367                          */
368                         limit_samples = 0;
369                         if (sr_dev_has_hwcap(dev, SR_HWCAP_SAMPLERATE)) {
370                                 guint64 tmp_u64;
371                                 tmp_u64 = *((uint64_t *)dev->driver->dev_info_get(
372                                                         dev->driver_index,
373                                                         SR_DI_CUR_SAMPLERATE));
374                                 limit_samples = tmp_u64 * time_msec / (uint64_t) 1000;
375                         }
376                         if (limit_samples == 0) {
377                                 g_critical("Not enough time at this samplerate.");
378                                 return;
379                         }
380
381                         if (dev->driver->dev_config_set(dev->driver_index,
382                                                 SR_HWCAP_LIMIT_SAMPLES,
383                                                 &limit_samples) != SR_OK) {
384                                 g_critical("Failed to configure time-based sample limit.");
385                                 return;
386                         }
387                 }
388         }
389         if (limit_samples) {
390                 if (dev->driver->dev_config_set(dev->driver_index,
391                                                 SR_HWCAP_LIMIT_SAMPLES,
392                                                 &limit_samples) != SR_OK) {
393                         g_critical("Failed to configure sample limit.");
394                         return;
395                 }
396         }
397
398         if (dev->driver->dev_config_set(dev->driver_index,
399             SR_HWCAP_PROBECONFIG, (char *)dev->probes) != SR_OK) {
400                 printf("Failed to configure probes.\n");
401                 sr_session_destroy();
402                 return;
403         }
404
405         if (sr_session_start() != SR_OK) {
406                 g_critical("Failed to start session.");
407                 return;
408         }
409
410         sr_session_run();
411 }
412
413 static void dev_file_open(GtkAction *action, GtkWindow *parent)
414 {
415         (void)action;
416         static GtkWidget *dialog;
417         const gchar *filename;
418
419         if(!dialog)
420                 dialog = gtk_file_chooser_dialog_new("Open", parent,
421                                 GTK_FILE_CHOOSER_ACTION_OPEN,
422                                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
423                                 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
424         g_signal_connect(dialog, "delete-event",
425                                         G_CALLBACK(gtk_widget_hide_on_delete),
426                                         NULL);
427
428         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
429                 /* Dialog was cancelled or closed */
430                 gtk_widget_hide(dialog);
431                 return;
432         }
433
434         gtk_widget_hide(dialog);
435
436         filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
437         load_input_file(parent, filename);
438 }
439
440 void toggle_log(GtkToggleAction *action, GObject *parent)
441 {
442         GtkWidget *log = g_object_get_data(parent, "logview");
443         gtk_widget_set_visible(log, gtk_toggle_action_get_active(action));
444 }
445
446 void zoom_in(GtkAction *action, GObject *parent)
447 {
448         (void)action;
449
450         GtkWidget *sigview = g_object_get_data(parent, "sigview");
451         sigview_zoom(sigview, 1.5, 0);
452 }
453
454 void zoom_out(GtkAction *action, GObject *parent)
455 {
456         (void)action;
457
458         GtkWidget *sigview = g_object_get_data(parent, "sigview");
459         sigview_zoom(sigview, 1/1.5, 0);
460 }
461
462 void zoom_fit(GtkAction *action, GObject *parent)
463 {
464         (void)action;
465
466         GtkWidget *sigview = g_object_get_data(parent, "sigview");
467         sigview_zoom(sigview, 0, 0);
468 }
469
470 static const GtkActionEntry action_items[] = {
471         /* name, stock-id, label, accel, tooltip, callback */
472         {"DevMenu", NULL, "_Device", NULL, NULL, NULL},
473         {"DevOpen", GTK_STOCK_OPEN, "_Open", "<control>O",
474                 "Open Session File", G_CALLBACK(dev_file_open)},
475         {"DevSelectMenu", NULL, "Select Device", NULL, NULL, NULL},
476         {"DevRescan", GTK_STOCK_REFRESH, "_Rescan", "<control>R",
477                 "Rescan for LA devices", G_CALLBACK(dev_select_rescan)},
478         {"DevProperties", GTK_STOCK_PROPERTIES, "_Properties", "<control>P",
479                 "Configure LA", G_CALLBACK(dev_set_options)},
480         {"DevProbes", GTK_STOCK_COLOR_PICKER, "_Probes", "<control>O",
481                 "Configure Probes", G_CALLBACK(dev_set_probes)},
482         {"DevAcquire", GTK_STOCK_EXECUTE, "_Acquire", "<control>A",
483                 "Acquire Samples", G_CALLBACK(capture_run)},
484         {"Exit", GTK_STOCK_QUIT, "E_xit", "<control>Q",
485                 "Exit the program", G_CALLBACK(gtk_main_quit) },
486
487         {"ViewMenu", NULL, "_View", NULL, NULL, NULL},
488         {"ViewZoomIn", GTK_STOCK_ZOOM_IN, "Zoom _In", "plus", NULL,
489                 G_CALLBACK(zoom_in)},
490         {"ViewZoomOut", GTK_STOCK_ZOOM_OUT, "Zoom _Out", "minus",
491                 NULL, G_CALLBACK(zoom_out)},
492         {"ViewZoomFit", GTK_STOCK_ZOOM_FIT, NULL, NULL,
493                 NULL, G_CALLBACK(zoom_fit)},
494
495         {"HelpMenu", NULL, "_Help", NULL, NULL, NULL},
496         {"HelpWiki", GTK_STOCK_ABOUT, "Sigrok _Wiki", NULL, NULL,
497                 G_CALLBACK(help_wiki)},
498         {"HelpAbout", GTK_STOCK_ABOUT, "_About", NULL, NULL,
499                 G_CALLBACK(help_about)},
500 };
501
502 static const GtkToggleActionEntry toggle_items[] = {
503         /* name, stock-id, label, accel, tooltip, callback, isactive */
504         {"ViewLog", GTK_STOCK_JUSTIFY_LEFT, "_Log", NULL, NULL,
505                 G_CALLBACK(toggle_log), FALSE},
506 };
507
508 static const char ui_xml[] =
509 "<ui>"
510 "  <menubar>"
511 "    <menu action='DevMenu'>"
512 "      <menuitem action='DevOpen'/>"
513 "      <separator/>"
514 "      <menu action='DevSelectMenu'>"
515 "        <separator/>"
516 "        <menuitem action='DevRescan'/>"
517 "      </menu>"
518 "      <menuitem action='DevProperties'/>"
519 "      <menuitem action='DevProbes'/>"
520 "      <separator/>"
521 "      <menuitem action='DevAcquire'/>"
522 "      <separator/>"
523 "      <menuitem action='Exit'/>"
524 "    </menu>"
525 "    <menu action='ViewMenu'>"
526 "      <menuitem action='ViewZoomIn'/>"
527 "      <menuitem action='ViewZoomOut'/>"
528 "      <menuitem action='ViewZoomFit'/>"
529 "      <separator/>"
530 "      <menuitem action='ViewLog'/>"
531 "    </menu>"
532 "    <menu action='HelpMenu'>"
533 "      <menuitem action='HelpWiki'/>"
534 "      <menuitem action='HelpAbout'/>"
535 "    </menu>"
536 "  </menubar>"
537 "  <toolbar>"
538 "    <placeholder name='DevSelect'/>"
539 "    <toolitem action='DevRescan'/>"
540 "    <toolitem action='DevProperties'/>"
541 "    <toolitem action='DevProbes'/>"
542 "    <separator/>"
543 "    <placeholder name='DevSampleCount' />"
544 "    <toolitem action='DevAcquire'/>"
545 "    <separator/>"
546 "    <toolitem action='ViewZoomIn'/>"
547 "    <toolitem action='ViewZoomOut'/>"
548 "    <toolitem action='ViewZoomFit'/>"
549 "    <separator/>"
550 "  </toolbar>"
551 "</ui>";
552
553 GtkWidget *toolbar_init(GtkWindow *parent)
554 {
555         GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
556         GtkToolbar *toolbar;
557         GtkActionGroup *ag = gtk_action_group_new("Actions");
558         gtk_action_group_add_actions(ag, action_items,
559                                         G_N_ELEMENTS(action_items), parent);
560         gtk_action_group_add_toggle_actions(ag, toggle_items,
561                                         G_N_ELEMENTS(toggle_items), parent);
562
563         GtkUIManager *ui = gtk_ui_manager_new();
564         g_object_set_data(G_OBJECT(parent), "ui_manager", ui);
565         gtk_ui_manager_insert_action_group(ui, ag, 0);
566         GtkAccelGroup *accel = gtk_ui_manager_get_accel_group(ui);
567         gtk_window_add_accel_group(parent, accel);
568
569         GError *error = NULL;
570         if (!gtk_ui_manager_add_ui_from_string (ui, ui_xml, -1, &error)) {
571                 g_message ("building menus failed: %s", error->message);
572                 g_error_free (error);
573                 exit (-1);
574         }
575
576         GtkWidget *menubar = gtk_ui_manager_get_widget(ui, "/menubar");
577         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(menubar), FALSE, TRUE, 0);
578         toolbar = GTK_TOOLBAR(gtk_ui_manager_get_widget(ui, "/toolbar"));
579         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(toolbar), FALSE, TRUE, 0);
580
581         /* Device selection GtkComboBox */
582         GtkToolItem *toolitem = gtk_tool_item_new();
583         GtkWidget *align = gtk_alignment_new(0.5, 0.5, 2, 0);
584         GtkWidget *dev = dev_select_combo_box_new(parent);
585
586         gtk_container_add(GTK_CONTAINER(align), dev);
587         gtk_container_add(GTK_CONTAINER(toolitem), align);
588         gtk_toolbar_insert(toolbar, toolitem, 0);
589
590         /* Time/Samples entry */
591         toolitem = gtk_tool_item_new();
592         GtkWidget *timesamples = gtk_entry_new();
593         gtk_entry_set_text(GTK_ENTRY(timesamples), "100");
594         gtk_entry_set_alignment(GTK_ENTRY(timesamples), 1.0);
595         gtk_widget_set_size_request(timesamples, 100, -1);
596         gtk_container_add(GTK_CONTAINER(toolitem), timesamples);
597         gtk_toolbar_insert(toolbar, toolitem, 7);
598
599         /* Time unit combo box */
600         toolitem = gtk_tool_item_new();
601         align = gtk_alignment_new(0.5, 0.5, 2, 0);
602         GtkWidget *timeunit = gtk_combo_box_new_text();
603         gtk_combo_box_append_text(GTK_COMBO_BOX(timeunit), "samples");
604         gtk_combo_box_append_text(GTK_COMBO_BOX(timeunit), "ms");
605         gtk_combo_box_append_text(GTK_COMBO_BOX(timeunit), "s");
606         gtk_combo_box_set_active(GTK_COMBO_BOX(timeunit), 0);
607         gtk_container_add(GTK_CONTAINER(align), timeunit);
608         gtk_container_add(GTK_CONTAINER(toolitem), align);
609         gtk_toolbar_insert(toolbar, toolitem, 8);
610
611         g_object_set_data(G_OBJECT(parent), "timesamples", timesamples);
612         g_object_set_data(G_OBJECT(parent), "timeunit", timeunit);
613
614         return GTK_WIDGET(vbox);
615 }
616