options: add ->hide_on_set
[fio.git] / goptions.c
CommitLineData
9af4a244
JA
1#include <locale.h>
2#include <malloc.h>
3#include <string.h>
4
5#include <glib.h>
6#include <cairo.h>
7#include <gtk/gtk.h>
8
9#include "fio.h"
10#include "gfio.h"
11#include "ghelpers.h"
12#include "parse.h"
13
14struct gopt {
15 GtkWidget *box;
16 unsigned int opt_index;
17 unsigned int opt_type;
8f9e46ab 18 gulong sig_handler;
9af4a244
JA
19};
20
21struct gopt_combo {
22 struct gopt gopt;
23 GtkWidget *combo;
24};
25
26struct gopt_int {
27 struct gopt gopt;
90265353 28 unsigned int lastval;
9af4a244
JA
29 GtkWidget *spin;
30};
31
32struct gopt_bool {
33 struct gopt gopt;
34 GtkWidget *check;
35};
36
37struct gopt_str {
38 struct gopt gopt;
39 GtkWidget *entry;
40};
41
7fc35ff7
JA
42struct gopt_str_val {
43 struct gopt gopt;
44 GtkWidget *spin;
45 GtkWidget *combo;
481c242b 46 unsigned int maxindex;
7fc35ff7
JA
47};
48
1da10c19
JA
49#define GOPT_RANGE_SPIN 4
50
9af4a244
JA
51struct gopt_range {
52 struct gopt gopt;
1da10c19 53 GtkWidget *spins[GOPT_RANGE_SPIN];
9af4a244
JA
54};
55
7386d4ae
JA
56struct gopt_str_multi {
57 struct gopt gopt;
58 GtkWidget *checks[PARSE_MAX_VP];
59};
60
e8b0e958 61static GtkWidget *gopt_widgets[FIO_MAX_OPTS];
ec0218ff 62
e8b0e958
JA
63struct gopt_frame_widget {
64 GtkWidget *vbox[2];
65 unsigned int nr;
66};
67static struct gopt_frame_widget gopt_g_widgets[__FIO_OPT_G_NR];
ec0218ff 68
e8b0e958 69static GtkWidget *gopt_get_group_frame(GtkWidget *box, unsigned int groupmask)
ec0218ff 70{
e8b0e958
JA
71 unsigned int mask, group;
72 struct opt_group *og;
73 GtkWidget *frame, *hbox;
74 struct gopt_frame_widget *gfw;
75
76 if (!groupmask)
77 return 0;
78
79 mask = groupmask;
80 og = opt_group_cat_from_mask(&mask);
81 if (!og)
82 return NULL;
83
84 group = ffz(~groupmask);
85 gfw = &gopt_g_widgets[group];
86 if (!gfw->vbox[0]) {
87 frame = gtk_frame_new(og->name);
88 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 3);
89 hbox = gtk_hbox_new(FALSE, 0);
90 gtk_container_add(GTK_CONTAINER(frame), hbox);
91 gfw->vbox[0] = gtk_vbox_new(TRUE, 5);
92 gfw->vbox[1] = gtk_vbox_new(TRUE, 5);
93 gtk_box_pack_start(GTK_BOX(hbox), gfw->vbox[0], TRUE, TRUE, 5);
94 gtk_box_pack_start(GTK_BOX(hbox), gfw->vbox[1], TRUE, TRUE, 5);
ec0218ff 95 }
e8b0e958
JA
96
97 hbox = gtk_hbox_new(FALSE, 3);
98 gtk_box_pack_start(GTK_BOX(gfw->vbox[gfw->nr++ & 1]), hbox, FALSE, FALSE, 5);
99 return hbox;
ec0218ff 100}
b6caa836
JA
101
102/*
103 * Mark children as invisible, if needed.
104 */
231edf61
JA
105static void gopt_set_children_visible(struct fio_option *parent,
106 gboolean visible)
b6caa836
JA
107{
108 struct fio_option *o;
109 int i;
110
a4ed77fe
JA
111 if (parent->hide_on_set)
112 visible = !visible;
113
b6caa836
JA
114 /*
115 * This isn't super fast, but it should not be an issue. If it is, we
116 * can speed it up by caching the lookup at least. Or we can do it
117 * once, at init time.
118 */
119 for (i = 0; fio_options[i].name; i++) {
120 o = &fio_options[i];
ec0218ff 121 if (!o->parent || !o->hide)
b6caa836
JA
122 continue;
123
124 if (strcmp(parent->name, o->parent))
125 continue;
126
e8b0e958
JA
127 if (gopt_widgets[i])
128 gtk_widget_set_sensitive(gopt_widgets[i], visible);
b6caa836
JA
129 }
130}
131
132static void gopt_str_changed(GtkEntry *entry, gpointer data)
133{
134 struct gopt_str *s = (struct gopt_str *) data;
135 struct fio_option *o = &fio_options[s->gopt.opt_index];
136 const gchar *text;
137 int set;
138
139 text = gtk_entry_get_text(GTK_ENTRY(s->entry));
140 set = strcmp(text, "") != 0;
141 gopt_set_children_visible(o, set);
142}
143
ec0218ff 144static void gopt_mark_index(struct gopt *gopt, unsigned int idx)
b6caa836 145{
e8b0e958 146 assert(!gopt_widgets[idx]);
b6caa836 147 gopt->opt_index = idx;
e8b0e958 148 gopt_widgets[idx] = gopt->box;
b6caa836
JA
149}
150
231edf61
JA
151static void gopt_str_destroy(GtkWidget *w, gpointer data)
152{
153 struct gopt_str *s = (struct gopt_str *) data;
154
155 free(s);
156 gtk_widget_destroy(w);
157}
158
159static struct gopt *gopt_new_str_store(struct fio_option *o, const char *text,
160 unsigned int idx)
9af4a244
JA
161{
162 struct gopt_str *s;
163 GtkWidget *label;
164
165 s = malloc(sizeof(*s));
a01a1bc5 166 memset(s, 0, sizeof(*s));
9af4a244
JA
167
168 s->gopt.box = gtk_hbox_new(FALSE, 3);
e8b0e958
JA
169 if (!o->lname)
170 label = gtk_label_new(o->name);
171 else
172 label = gtk_label_new(o->lname);
9af4a244
JA
173
174 s->entry = gtk_entry_new();
ec0218ff 175 gopt_mark_index(&s->gopt, idx);
789f4ccd
JA
176 if (text)
177 gtk_entry_set_text(GTK_ENTRY(s->entry), text);
9af4a244
JA
178 gtk_entry_set_editable(GTK_ENTRY(s->entry), 1);
179
180 if (o->def)
181 gtk_entry_set_text(GTK_ENTRY(s->entry), o->def);
182
4fbcd659
JA
183 s->gopt.sig_handler = g_signal_connect(GTK_OBJECT(s->entry), "changed", G_CALLBACK(gopt_str_changed), s);
184 g_signal_connect(GTK_OBJECT(s->entry), "destroy", G_CALLBACK(gopt_str_destroy), s);
185
9af4a244 186 gtk_box_pack_start(GTK_BOX(s->gopt.box), s->entry, FALSE, FALSE, 0);
e8b0e958 187 gtk_box_pack_start(GTK_BOX(s->gopt.box), label, FALSE, FALSE, 0);
90265353 188 o->gui_data = s;
9af4a244
JA
189 return &s->gopt;
190}
191
b6caa836 192static void gopt_combo_changed(GtkComboBox *box, gpointer data)
9af4a244 193{
b6caa836
JA
194 struct gopt_combo *c = (struct gopt_combo *) data;
195 struct fio_option *o = &fio_options[c->gopt.opt_index];
196
197 printf("combo %s changed\n", o->name);
198}
199
231edf61
JA
200static void gopt_combo_destroy(GtkWidget *w, gpointer data)
201{
202 struct gopt_combo *c = (struct gopt_combo *) data;
203
204 free(c);
205 gtk_widget_destroy(w);
206}
207
208static struct gopt_combo *__gopt_new_combo(struct fio_option *o,
209 unsigned int idx)
b6caa836
JA
210{
211 struct gopt_combo *c;
9af4a244 212 GtkWidget *label;
9af4a244 213
b6caa836 214 c = malloc(sizeof(*c));
a01a1bc5 215 memset(c, 0, sizeof(*c));
9af4a244 216
b6caa836 217 c->gopt.box = gtk_hbox_new(FALSE, 3);
e8b0e958
JA
218 if (!o->lname)
219 label = gtk_label_new(o->name);
220 else
221 label = gtk_label_new(o->lname);
9af4a244 222
b6caa836 223 c->combo = gtk_combo_box_new_text();
ec0218ff 224 gopt_mark_index(&c->gopt, idx);
231edf61 225 g_signal_connect(GTK_OBJECT(c->combo), "destroy", G_CALLBACK(gopt_combo_destroy), c);
b6caa836 226
e8b0e958
JA
227 gtk_box_pack_start(GTK_BOX(c->gopt.box), c->combo, FALSE, FALSE, 0);
228 gtk_box_pack_start(GTK_BOX(c->gopt.box), label, FALSE, FALSE, 0);
229
90265353 230 o->gui_data = c;
b6caa836 231 return c;
39f04336
JA
232}
233
231edf61
JA
234static struct gopt *gopt_new_combo_str(struct fio_option *o, const char *text,
235 unsigned int idx)
39f04336 236{
4fbcd659 237 struct gopt_combo *c;
39f04336
JA
238 struct value_pair *vp;
239 int i, active = 0;
240
4fbcd659 241 c = __gopt_new_combo(o, idx);
39f04336 242
9af4a244
JA
243 i = 0;
244 vp = &o->posval[0];
245 while (vp->ival) {
4fbcd659 246 gtk_combo_box_append_text(GTK_COMBO_BOX(c->combo), vp->ival);
9af4a244
JA
247 if (o->def && !strcmp(vp->ival, o->def))
248 active = i;
39f04336
JA
249 if (text && !strcmp(vp->ival, text))
250 active = i;
9af4a244
JA
251 vp++;
252 i++;
253 }
254
4fbcd659
JA
255 gtk_combo_box_set_active(GTK_COMBO_BOX(c->combo), active);
256 c->gopt.sig_handler = g_signal_connect(GTK_OBJECT(c->combo), "changed", G_CALLBACK(gopt_combo_changed), c);
257 return &c->gopt;
9af4a244
JA
258}
259
231edf61
JA
260static struct gopt *gopt_new_combo_int(struct fio_option *o, unsigned int *ip,
261 unsigned int idx)
9af4a244 262{
4fbcd659 263 struct gopt_combo *c;
39f04336
JA
264 struct value_pair *vp;
265 int i, active = 0;
266
4fbcd659 267 c = __gopt_new_combo(o, idx);
39f04336
JA
268
269 i = 0;
270 vp = &o->posval[0];
271 while (vp->ival) {
4fbcd659 272 gtk_combo_box_append_text(GTK_COMBO_BOX(c->combo), vp->ival);
39f04336
JA
273 if (ip && vp->oval == *ip)
274 active = i;
275 vp++;
276 i++;
277 }
278
4fbcd659
JA
279 gtk_combo_box_set_active(GTK_COMBO_BOX(c->combo), active);
280 c->gopt.sig_handler = g_signal_connect(GTK_OBJECT(c->combo), "changed", G_CALLBACK(gopt_combo_changed), c);
281 return &c->gopt;
39f04336
JA
282}
283
7386d4ae
JA
284static struct gopt *gopt_new_str_multi(struct fio_option *o, unsigned int idx)
285{
286 struct gopt_str_multi *m;
287 struct value_pair *vp;
288 GtkWidget *frame, *hbox;
289 int i;
290
291 m = malloc(sizeof(*m));
a01a1bc5 292 memset(m, 0, sizeof(*m));
7386d4ae
JA
293 m->gopt.box = gtk_hbox_new(FALSE, 3);
294 gopt_mark_index(&m->gopt, idx);
295
296 if (!o->lname)
297 frame = gtk_frame_new(o->name);
298 else
299 frame = gtk_frame_new(o->lname);
300 gtk_box_pack_start(GTK_BOX(m->gopt.box), frame, FALSE, FALSE, 3);
301
302 hbox = gtk_hbox_new(FALSE, 3);
303 gtk_container_add(GTK_CONTAINER(frame), hbox);
304
305 i = 0;
306 vp = &o->posval[0];
307 while (vp->ival) {
308 m->checks[i] = gtk_check_button_new_with_label(vp->ival);
309 gtk_widget_set_tooltip_text(m->checks[i], vp->help);
310 gtk_box_pack_start(GTK_BOX(hbox), m->checks[i], FALSE, FALSE, 3);
311 vp++;
312 }
313
314 return &m->gopt;
315}
316
b6caa836
JA
317static void gopt_int_changed(GtkSpinButton *spin, gpointer data)
318{
319 struct gopt_int *i = (struct gopt_int *) data;
320 struct fio_option *o = &fio_options[i->gopt.opt_index];
90265353
JA
321 GtkAdjustment *adj;
322 int value, delta;
323
324 adj = gtk_spin_button_get_adjustment(spin);
325 value = gtk_adjustment_get_value(adj);
326 delta = value - i->lastval;
327 i->lastval = value;
328
329 if (o->inv_opt) {
330 struct gopt_int *i_inv = o->inv_opt->gui_data;
331 int cur_val;
332
a01a1bc5
JA
333 assert(o->type == o->inv_opt->type);
334
90265353
JA
335 cur_val = gtk_spin_button_get_value(GTK_SPIN_BUTTON(i_inv->spin));
336 cur_val -= delta;
8f9e46ab 337 g_signal_handler_block(G_OBJECT(i_inv->spin), i_inv->gopt.sig_handler);
90265353 338 gtk_spin_button_set_value(GTK_SPIN_BUTTON(i_inv->spin), cur_val);
8f9e46ab 339 g_signal_handler_unblock(G_OBJECT(i_inv->spin), i_inv->gopt.sig_handler);
90265353 340 }
b6caa836
JA
341}
342
231edf61
JA
343static void gopt_int_destroy(GtkWidget *w, gpointer data)
344{
345 struct gopt_int *i = (struct gopt_int *) data;
346
347 free(i);
348 gtk_widget_destroy(w);
349}
350
351static struct gopt_int *__gopt_new_int(struct fio_option *o,
352 unsigned long long *p, unsigned int idx)
39f04336
JA
353{
354 unsigned long long defval;
9af4a244 355 struct gopt_int *i;
20eb06bd 356 guint maxval, interval;
9af4a244
JA
357 GtkWidget *label;
358
359 i = malloc(sizeof(*i));
a01a1bc5 360 memset(i, 0, sizeof(*i));
9af4a244 361 i->gopt.box = gtk_hbox_new(FALSE, 3);
e8b0e958
JA
362 if (!o->lname)
363 label = gtk_label_new(o->name);
364 else
365 label = gtk_label_new(o->lname);
9af4a244
JA
366
367 maxval = o->maxval;
368 if (!maxval)
39f04336 369 maxval = UINT_MAX;
9af4a244
JA
370
371 defval = 0;
39f04336
JA
372 if (p)
373 defval = *p;
374 else if (o->def) {
9af4a244
JA
375 long long val;
376
377 check_str_bytes(o->def, &val, NULL);
378 defval = val;
379 }
380
20eb06bd
JA
381 interval = 1.0;
382 if (o->interval)
383 interval = o->interval;
384
385 i->spin = gtk_spin_button_new_with_range(o->minval, maxval, interval);
ec0218ff 386 gopt_mark_index(&i->gopt, idx);
9af4a244
JA
387 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(i->spin), GTK_UPDATE_IF_VALID);
388 gtk_spin_button_set_value(GTK_SPIN_BUTTON(i->spin), defval);
90265353 389 i->lastval = defval;
8f9e46ab 390 i->gopt.sig_handler = g_signal_connect(G_OBJECT(i->spin), "value-changed", G_CALLBACK(gopt_int_changed), i);
231edf61 391 g_signal_connect(G_OBJECT(i->spin), "destroy", G_CALLBACK(gopt_int_destroy), i);
9af4a244
JA
392
393 gtk_box_pack_start(GTK_BOX(i->gopt.box), i->spin, FALSE, FALSE, 0);
e8b0e958 394 gtk_box_pack_start(GTK_BOX(i->gopt.box), label, FALSE, FALSE, 0);
b6caa836 395
90265353 396 o->gui_data = i;
b6caa836 397 return i;
9af4a244
JA
398}
399
231edf61
JA
400static struct gopt *gopt_new_int(struct fio_option *o, unsigned int *ip,
401 unsigned int idx)
39f04336
JA
402{
403 unsigned long long ullp;
b6caa836 404 struct gopt_int *i;
39f04336
JA
405
406 if (ip) {
407 ullp = *ip;
b6caa836
JA
408 i = __gopt_new_int(o, &ullp, idx);
409 } else
410 i = __gopt_new_int(o, NULL, idx);
411
412 return &i->gopt;
413}
39f04336 414
b6caa836
JA
415static struct gopt *gopt_new_ullong(struct fio_option *o, unsigned long long *p,
416 unsigned int idx)
417{
418 struct gopt_int *i;
419
420 i = __gopt_new_int(o, p, idx);
421 return &i->gopt;
39f04336
JA
422}
423
b6caa836 424static void gopt_bool_toggled(GtkToggleButton *button, gpointer data)
39f04336 425{
b6caa836
JA
426 struct gopt_bool *b = (struct gopt_bool *) data;
427 struct fio_option *o = &fio_options[b->gopt.opt_index];
428 gboolean set;
429
430 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b->check));
a01a1bc5
JA
431
432 if (o->inv_opt) {
433 struct gopt_bool *b_inv = o->inv_opt->gui_data;
434
435 assert(o->type == o->inv_opt->type);
436
8f9e46ab 437 g_signal_handler_block(G_OBJECT(b_inv->check), b_inv->gopt.sig_handler);
a01a1bc5 438 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b_inv->check), !set);
8f9e46ab 439 g_signal_handler_unblock(G_OBJECT(b_inv->check), b_inv->gopt.sig_handler);
a01a1bc5
JA
440 }
441
b6caa836 442 gopt_set_children_visible(o, set);
39f04336
JA
443}
444
231edf61
JA
445static void gopt_bool_destroy(GtkWidget *w, gpointer data)
446{
447 struct gopt_bool *b = (struct gopt_bool *) data;
448
449 free(b);
450 gtk_widget_destroy(w);
451}
452
453static struct gopt *gopt_new_bool(struct fio_option *o, unsigned int *val,
454 unsigned int idx)
9af4a244
JA
455{
456 struct gopt_bool *b;
457 GtkWidget *label;
458 int defstate = 0;
459
460 b = malloc(sizeof(*b));
a01a1bc5 461 memset(b, 0, sizeof(*b));
9af4a244 462 b->gopt.box = gtk_hbox_new(FALSE, 3);
e8b0e958
JA
463 if (!o->lname)
464 label = gtk_label_new(o->name);
465 else
466 label = gtk_label_new(o->lname);
9af4a244
JA
467
468 b->check = gtk_check_button_new();
ec0218ff 469 gopt_mark_index(&b->gopt, idx);
39f04336
JA
470 if (val)
471 defstate = *val;
472 else if (o->def && !strcmp(o->def, "1"))
9af4a244
JA
473 defstate = 1;
474
d872fb4c
JA
475 if (o->neg)
476 defstate = !defstate;
477
9af4a244 478 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b->check), defstate);
8f9e46ab 479 b->gopt.sig_handler = g_signal_connect(G_OBJECT(b->check), "toggled", G_CALLBACK(gopt_bool_toggled), b);
231edf61 480 g_signal_connect(G_OBJECT(b->check), "destroy", G_CALLBACK(gopt_bool_destroy), b);
9af4a244
JA
481
482 gtk_box_pack_start(GTK_BOX(b->gopt.box), b->check, FALSE, FALSE, 0);
e8b0e958 483 gtk_box_pack_start(GTK_BOX(b->gopt.box), label, FALSE, FALSE, 0);
90265353 484 o->gui_data = b;
9af4a244
JA
485 return &b->gopt;
486}
487
1da10c19
JA
488/*
489 * These are paired 0/1 and 2/3. 0/2 are min values, 1/3 are max values.
490 * If the max is made smaller than min, adjust min down.
491 * If the min is made larger than max, adjust the max.
492 */
493static void range_value_changed(GtkSpinButton *spin, gpointer data)
494{
495 struct gopt_range *r = (struct gopt_range *) data;
496 int changed = -1, i;
497 gint val, mval;
498
499 for (i = 0; i < GOPT_RANGE_SPIN; i++) {
500 if (GTK_SPIN_BUTTON(r->spins[i]) == spin) {
501 changed = i;
502 break;
503 }
504 }
505
506 assert(changed != -1);
507
508 /*
509 * Min changed
510 */
511 if (changed == 0 || changed == 2) {
512 GtkWidget *mspin = r->spins[changed + 1];
513
514 val = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(r->spins[changed]));
515 mval = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(mspin));
516 if (val > mval)
517 gtk_spin_button_set_value(GTK_SPIN_BUTTON(mspin), val);
518 } else {
519 GtkWidget *mspin = r->spins[changed - 1];
520
521 val = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(r->spins[changed]));
522 mval = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(mspin));
523 if (val < mval)
524 gtk_spin_button_set_value(GTK_SPIN_BUTTON(mspin), val);
525 }
526}
527
231edf61
JA
528static void gopt_range_destroy(GtkWidget *w, gpointer data)
529{
530 struct gopt_range *r = (struct gopt_range *) data;
531
532 free(r);
533 gtk_widget_destroy(w);
534}
535
b6caa836
JA
536static struct gopt *gopt_new_int_range(struct fio_option *o, unsigned int **ip,
537 unsigned int idx)
9af4a244
JA
538{
539 struct gopt_range *r;
540 gint maxval, defval;
541 GtkWidget *label;
20eb06bd 542 guint interval;
9af4a244
JA
543 int i;
544
545 r = malloc(sizeof(*r));
a01a1bc5 546 memset(r, 0, sizeof(*r));
9af4a244 547 r->gopt.box = gtk_hbox_new(FALSE, 3);
ec0218ff 548 gopt_mark_index(&r->gopt, idx);
e8b0e958
JA
549 if (!o->lname)
550 label = gtk_label_new(o->name);
551 else
552 label = gtk_label_new(o->lname);
9af4a244
JA
553
554 maxval = o->maxval;
555 if (!maxval)
556 maxval = INT_MAX;
557
558 defval = 0;
559 if (o->def) {
560 long long val;
561
562 check_str_bytes(o->def, &val, NULL);
563 defval = val;
564 }
565
20eb06bd
JA
566 interval = 1.0;
567 if (o->interval)
568 interval = o->interval;
569
1da10c19 570 for (i = 0; i < GOPT_RANGE_SPIN; i++) {
20eb06bd 571 r->spins[i] = gtk_spin_button_new_with_range(o->minval, maxval, interval);
9af4a244 572 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(r->spins[i]), GTK_UPDATE_IF_VALID);
39f04336
JA
573 if (ip)
574 gtk_spin_button_set_value(GTK_SPIN_BUTTON(r->spins[i]), *ip[i]);
575 else
576 gtk_spin_button_set_value(GTK_SPIN_BUTTON(r->spins[i]), defval);
9af4a244
JA
577
578 gtk_box_pack_start(GTK_BOX(r->gopt.box), r->spins[i], FALSE, FALSE, 0);
1da10c19 579 g_signal_connect(G_OBJECT(r->spins[i]), "value-changed", G_CALLBACK(range_value_changed), r);
9af4a244
JA
580 }
581
e8b0e958 582 gtk_box_pack_start(GTK_BOX(r->gopt.box), label, FALSE, FALSE, 0);
231edf61 583 g_signal_connect(G_OBJECT(r->gopt.box), "destroy", G_CALLBACK(gopt_range_destroy), r);
90265353 584 o->gui_data = r;
9af4a244
JA
585 return &r->gopt;
586}
587
7fc35ff7
JA
588static void gopt_str_val_destroy(GtkWidget *w, gpointer data)
589{
590 struct gopt_str_val *g = (struct gopt_str_val *) data;
591
592 free(g);
593 gtk_widget_destroy(w);
594}
595
596static void gopt_str_val_spin_wrapped(GtkSpinButton *spin, gpointer data)
597{
598 struct gopt_str_val *g = (struct gopt_str_val *) data;
599 unsigned int val;
600 GtkAdjustment *adj;
601 gint index;
602
603 adj = gtk_spin_button_get_adjustment(spin);
604 val = gtk_adjustment_get_value(adj);
605
606 /*
607 * Can't rely on exact value, as fast changes increment >= 1
608 */
609 if (!val) {
7fc35ff7 610 index = gtk_combo_box_get_active(GTK_COMBO_BOX(g->combo));
481c242b
JA
611 if (index + 1 <= g->maxindex) {
612 val = 1;
613 gtk_combo_box_set_active(GTK_COMBO_BOX(g->combo), ++index);
614 } else
615 val = 1023;
7fc35ff7
JA
616 gtk_spin_button_set_value(spin, val);
617 } else {
618 index = gtk_combo_box_get_active(GTK_COMBO_BOX(g->combo));
619 if (index) {
620 gtk_combo_box_set_active(GTK_COMBO_BOX(g->combo), --index);
621 gtk_spin_button_set_value(spin, 1023);
622 } else
623 gtk_spin_button_set_value(spin, 0);
624 }
625}
626
627static struct gopt *gopt_new_str_val(struct fio_option *o,
628 unsigned long long *p, unsigned int idx)
629{
630 struct gopt_str_val *g;
631 const gchar *postfix[] = { "B", "KB", "MB", "GB", "PB", "TB", "" };
632 GtkWidget *label;
633 int i;
634
635 g = malloc(sizeof(*g));
636 memset(g, 0, sizeof(*g));
637 g->gopt.box = gtk_hbox_new(FALSE, 3);
638 if (!o->lname)
639 label = gtk_label_new(o->name);
640 else
641 label = gtk_label_new(o->lname);
642
643 g->spin = gtk_spin_button_new_with_range(0.0, 1023.0, 1.0);
644 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(g->spin), GTK_UPDATE_IF_VALID);
645 gtk_spin_button_set_value(GTK_SPIN_BUTTON(g->spin), 0);
646 gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(g->spin), 1);
647 gtk_box_pack_start(GTK_BOX(g->gopt.box), g->spin, FALSE, FALSE, 0);
648 g_signal_connect(G_OBJECT(g->spin), "wrapped", G_CALLBACK(gopt_str_val_spin_wrapped), g);
649
650 g->combo = gtk_combo_box_new_text();
651 i = 0;
652 while (strlen(postfix[i])) {
653 gtk_combo_box_append_text(GTK_COMBO_BOX(g->combo), postfix[i]);
654 i++;
655 }
481c242b 656 g->maxindex = i - 1;
7fc35ff7
JA
657 gtk_combo_box_set_active(GTK_COMBO_BOX(g->combo), 0);
658 gtk_box_pack_start(GTK_BOX(g->gopt.box), g->combo, FALSE, FALSE, 0);
659 gtk_box_pack_start(GTK_BOX(g->gopt.box), label, FALSE, FALSE, 3);
660
661 g_signal_connect(G_OBJECT(g->gopt.box), "destroy", G_CALLBACK(gopt_str_val_destroy), g);
662 o->gui_data = g;
663 return &g->gopt;
664}
665
9af4a244 666static void gopt_add_option(GtkWidget *hbox, struct fio_option *o,
789f4ccd 667 unsigned int opt_index, struct thread_options *to)
9af4a244
JA
668{
669 struct gopt *go = NULL;
670
671 switch (o->type) {
7fc35ff7
JA
672 case FIO_OPT_STR_VAL: {
673 unsigned long long *ullp = NULL;
674
675 if (o->off1)
676 ullp = td_var(to, o->off1);
677
678 go = gopt_new_str_val(o, ullp, opt_index);
679 break;
680 }
39f04336
JA
681 case FIO_OPT_STR_VAL_TIME: {
682 unsigned long long *ullp = NULL;
683
684 if (o->off1)
685 ullp = td_var(to, o->off1);
789f4ccd 686
b6caa836 687 go = gopt_new_ullong(o, ullp, opt_index);
9af4a244 688 break;
789f4ccd 689 }
39f04336
JA
690 case FIO_OPT_INT: {
691 unsigned int *ip = NULL;
692
693 if (o->off1)
694 ip = td_var(to, o->off1);
695
b6caa836 696 go = gopt_new_int(o, ip, opt_index);
9af4a244 697 break;
39f04336 698 }
9af4a244 699 case FIO_OPT_STR_SET:
39f04336
JA
700 case FIO_OPT_BOOL: {
701 unsigned int *ip = NULL;
702
703 if (o->off1)
704 ip = td_var(to, o->off1);
705
b6caa836 706 go = gopt_new_bool(o, ip, opt_index);
39f04336
JA
707 break;
708 }
709 case FIO_OPT_STR: {
d872fb4c
JA
710 if (o->posval[0].ival) {
711 unsigned int *ip = NULL;
39f04336 712
d872fb4c
JA
713 if (o->off1)
714 ip = td_var(to, o->off1);
715
716 go = gopt_new_combo_int(o, ip, opt_index);
717 } else {
718 /* TODO: usually ->cb, or unsigned int pointer */
719 go = gopt_new_str_store(o, NULL, opt_index);
720 }
39f04336 721
9af4a244 722 break;
39f04336
JA
723 }
724 case FIO_OPT_STR_STORE: {
725 char *text = NULL;
726
d3944493 727 if (o->off1) {
39f04336
JA
728 char **p = td_var(to, o->off1);
729 text = *p;
730 }
731
9af4a244 732 if (!o->posval[0].ival) {
b6caa836 733 go = gopt_new_str_store(o, text, opt_index);
9af4a244
JA
734 break;
735 }
39f04336 736
b6caa836 737 go = gopt_new_combo_str(o, text, opt_index);
39f04336
JA
738 break;
739 }
9af4a244 740 case FIO_OPT_STR_MULTI:
7386d4ae 741 go = gopt_new_str_multi(o, opt_index);
9af4a244 742 break;
39f04336
JA
743 case FIO_OPT_RANGE: {
744 unsigned int *ip[4] = { td_var(to, o->off1),
745 td_var(to, o->off2),
746 td_var(to, o->off3),
747 td_var(to, o->off4) };
748
b6caa836 749 go = gopt_new_int_range(o, ip, opt_index);
9af4a244 750 break;
39f04336 751 }
9af4a244
JA
752 /* still need to handle this one */
753 case FIO_OPT_FLOAT_LIST:
754 break;
755 case FIO_OPT_DEPRECATED:
756 break;
757 default:
758 printf("ignore type %u\n", o->type);
759 break;
760 }
761
762 if (go) {
e8b0e958
JA
763 GtkWidget *dest;
764
9af4a244
JA
765 if (o->help)
766 gtk_widget_set_tooltip_text(go->box, o->help);
e8b0e958 767
9af4a244 768 go->opt_type = o->type;
e8b0e958
JA
769
770 dest = gopt_get_group_frame(hbox, o->group);
771 if (!dest)
772 gtk_box_pack_start(GTK_BOX(hbox), go->box, FALSE, FALSE, 5);
773 else
774 gtk_box_pack_start(GTK_BOX(dest), go->box, FALSE, FALSE, 5);
9af4a244
JA
775 }
776}
777
789f4ccd 778static void gopt_add_options(GtkWidget **vboxes, struct thread_options *to)
9af4a244
JA
779{
780 GtkWidget *hbox = NULL;
781 int i;
782
90265353
JA
783 /*
784 * First add all options
785 */
9af4a244
JA
786 for (i = 0; fio_options[i].name; i++) {
787 struct fio_option *o = &fio_options[i];
788 unsigned int mask = o->category;
789 struct opt_group *og;
790
791 while ((og = opt_group_from_mask(&mask)) != NULL) {
792 GtkWidget *vbox = vboxes[ffz(~og->mask)];
793
794 hbox = gtk_hbox_new(FALSE, 3);
795 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
789f4ccd 796 gopt_add_option(hbox, o, i, to);
9af4a244
JA
797 }
798 }
799}
800
801static GtkWidget *gopt_add_group_tab(GtkWidget *notebook, struct opt_group *og)
802{
803 GtkWidget *box, *vbox, *scroll;
804
805 scroll = gtk_scrolled_window_new(NULL, NULL);
806 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
807 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
808
809 vbox = gtk_vbox_new(FALSE, 3);
810 box = gtk_hbox_new(FALSE, 0);
811 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
812 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
813 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scroll, gtk_label_new(og->name));
814
815 return vbox;
816}
817
818static void gopt_add_group_tabs(GtkWidget *notebook, GtkWidget **vbox)
819{
820 struct opt_group *og;
ec0218ff 821 unsigned int i;
9af4a244 822
ec0218ff 823 i = 0;
9af4a244
JA
824 do {
825 unsigned int mask = (1U << i);
826
827 og = opt_group_from_mask(&mask);
828 if (!og)
829 break;
830 vbox[i] = gopt_add_group_tab(notebook, og);
831 i++;
832 } while (1);
833}
834
789f4ccd 835void gopt_get_options_window(GtkWidget *window, struct thread_options *o)
9af4a244
JA
836{
837 GtkWidget *dialog, *notebook;
e8b0e958 838 GtkWidget *vboxes[__FIO_OPT_C_NR];
9af4a244
JA
839
840 dialog = gtk_dialog_new_with_buttons("Fio options",
841 GTK_WINDOW(window), GTK_DIALOG_DESTROY_WITH_PARENT,
842 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
843 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
844
845 gtk_widget_set_size_request(GTK_WIDGET(dialog), 1024, 768);
846
847 notebook = gtk_notebook_new();
848 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
849 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
850 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), notebook, TRUE, TRUE, 5);
851
852 gopt_add_group_tabs(notebook, vboxes);
853
789f4ccd 854 gopt_add_options(vboxes, o);
9af4a244
JA
855
856 gtk_widget_show_all(dialog);
857
858 gtk_dialog_run(GTK_DIALOG(dialog));
859
860 gtk_widget_destroy(dialog);
a8baa347
SC
861 memset(gopt_widgets, 0, sizeof(gopt_widgets));
862 memset(gopt_g_widgets, 0, sizeof(gopt_g_widgets));
9af4a244 863}