gfio: inherit and show more options correctly in edit job
[fio.git] / goptions.c
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
14 struct gopt {
15         GtkWidget *box;
16         unsigned int opt_index;
17         unsigned int opt_type;
18 };
19
20 struct gopt_combo {
21         struct gopt gopt;
22         GtkWidget *combo;
23 };
24
25 struct gopt_int {
26         struct gopt gopt;
27         GtkWidget *spin;
28 };
29
30 struct gopt_bool {
31         struct gopt gopt;
32         GtkWidget *check;
33 };
34
35 struct gopt_str {
36         struct gopt gopt;
37         GtkWidget *entry;
38 };
39
40 struct gopt_range {
41         struct gopt gopt;
42         GtkWidget *spins[4];
43 };
44
45 static struct gopt *gopt_new_str_store(struct fio_option *o, const char *text)
46 {
47         struct gopt_str *s;
48         GtkWidget *label;
49
50         s = malloc(sizeof(*s));
51
52         s->gopt.box = gtk_hbox_new(FALSE, 3);
53         label = gtk_label_new(o->name);
54         gtk_box_pack_start(GTK_BOX(s->gopt.box), label, FALSE, FALSE, 0);
55
56         s->entry = gtk_entry_new();
57         if (text)
58                 gtk_entry_set_text(GTK_ENTRY(s->entry), text);
59         gtk_entry_set_editable(GTK_ENTRY(s->entry), 1);
60
61         if (o->def)
62                 gtk_entry_set_text(GTK_ENTRY(s->entry), o->def);
63
64         gtk_box_pack_start(GTK_BOX(s->gopt.box), s->entry, FALSE, FALSE, 0);
65         return &s->gopt;
66 }
67
68 static struct gopt_combo *__gopt_new_combo(struct fio_option *o)
69 {
70         struct gopt_combo *combo;
71         GtkWidget *label;
72
73         combo = malloc(sizeof(*combo));
74
75         combo->gopt.box = gtk_hbox_new(FALSE, 3);
76         label = gtk_label_new(o->name);
77         gtk_box_pack_start(GTK_BOX(combo->gopt.box), label, FALSE, FALSE, 0);
78
79         combo->combo = gtk_combo_box_new_text();
80         gtk_box_pack_start(GTK_BOX(combo->gopt.box), combo->combo, FALSE, FALSE, 0);
81
82         return combo;
83 }
84
85 static struct gopt *gopt_new_combo_str(struct fio_option *o, const char *text)
86 {
87         struct gopt_combo *combo;
88         struct value_pair *vp;
89         int i, active = 0;
90
91         combo = __gopt_new_combo(o);
92
93         i = 0;
94         vp = &o->posval[0];
95         while (vp->ival) {
96                 gtk_combo_box_append_text(GTK_COMBO_BOX(combo->combo), vp->ival);
97                 if (o->def && !strcmp(vp->ival, o->def))
98                         active = i;
99                 if (text && !strcmp(vp->ival, text))
100                         active = i;
101                 vp++;
102                 i++;
103         }
104
105         gtk_combo_box_set_active(GTK_COMBO_BOX(combo->combo), active);
106         return &combo->gopt;
107 }
108
109 static struct gopt *gopt_new_combo_int(struct fio_option *o, unsigned int *ip)
110 {
111         struct gopt_combo *combo;
112         struct value_pair *vp;
113         int i, active = 0;
114
115         combo = __gopt_new_combo(o);
116
117         i = 0;
118         vp = &o->posval[0];
119         while (vp->ival) {
120                 gtk_combo_box_append_text(GTK_COMBO_BOX(combo->combo), vp->ival);
121                 if (ip && vp->oval == *ip)
122                         active = i;
123                 vp++;
124                 i++;
125         }
126
127         gtk_combo_box_set_active(GTK_COMBO_BOX(combo->combo), active);
128         return &combo->gopt;
129 }
130
131 static struct gopt *__gopt_new_int(struct fio_option *o, unsigned long long *p)
132 {
133         unsigned long long defval;
134         struct gopt_int *i;
135         guint maxval;
136         GtkWidget *label;
137
138         i = malloc(sizeof(*i));
139         i->gopt.box = gtk_hbox_new(FALSE, 3);
140         label = gtk_label_new(o->name);
141         gtk_box_pack_start(GTK_BOX(i->gopt.box), label, FALSE, FALSE, 0);
142
143         maxval = o->maxval;
144         if (!maxval)
145                 maxval = UINT_MAX;
146
147         defval = 0;
148         if (p)
149                 defval = *p;
150         else if (o->def) {
151                 long long val;
152
153                 check_str_bytes(o->def, &val, NULL);
154                 defval = val;
155         }
156
157         i->spin = gtk_spin_button_new_with_range(o->minval, maxval, 1.0);
158         gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(i->spin), GTK_UPDATE_IF_VALID);
159         gtk_spin_button_set_value(GTK_SPIN_BUTTON(i->spin), defval);
160
161         gtk_box_pack_start(GTK_BOX(i->gopt.box), i->spin, FALSE, FALSE, 0);
162         return &i->gopt;
163 }
164
165 static struct gopt *gopt_new_int(struct fio_option *o, unsigned int *ip)
166 {
167         unsigned long long ullp;
168
169         if (ip) {
170                 ullp = *ip;
171                 return __gopt_new_int(o, &ullp);
172         }
173
174         return __gopt_new_int(o, NULL);
175 }
176
177 static struct gopt *gopt_new_ullong(struct fio_option *o, unsigned long long *p)
178 {
179         return __gopt_new_int(o, p);
180 }
181
182 static struct gopt *gopt_new_bool(struct fio_option *o, unsigned int *val)
183 {
184         struct gopt_bool *b;
185         GtkWidget *label;
186         int defstate = 0;
187
188         b = malloc(sizeof(*b));
189         b->gopt.box = gtk_hbox_new(FALSE, 3);
190         label = gtk_label_new(o->name);
191         gtk_box_pack_start(GTK_BOX(b->gopt.box), label, FALSE, FALSE, 0);
192
193         b->check = gtk_check_button_new();
194         if (val)
195                 defstate = *val;
196         else if (o->def && !strcmp(o->def, "1"))
197                 defstate = 1;
198
199         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b->check), defstate);
200
201         gtk_box_pack_start(GTK_BOX(b->gopt.box), b->check, FALSE, FALSE, 0);
202         return &b->gopt;
203 }
204
205 static struct gopt *gopt_new_int_range(struct fio_option *o, unsigned int **ip)
206 {
207         struct gopt_range *r;
208         gint maxval, defval;
209         GtkWidget *label;
210         int i;
211
212         r = malloc(sizeof(*r));
213         r->gopt.box = gtk_hbox_new(FALSE, 3);
214         label = gtk_label_new(o->name);
215         gtk_box_pack_start(GTK_BOX(r->gopt.box), label, FALSE, FALSE, 0);
216
217         maxval = o->maxval;
218         if (!maxval)
219                 maxval = INT_MAX;
220
221         defval = 0;
222         if (o->def) {
223                 long long val;
224
225                 check_str_bytes(o->def, &val, NULL);
226                 defval = val;
227         }
228
229         for (i = 0; i < 4; i++) {
230                 r->spins[i] = gtk_spin_button_new_with_range(o->minval, maxval, 512);
231                 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(r->spins[i]), GTK_UPDATE_IF_VALID);
232                 if (ip)
233                         gtk_spin_button_set_value(GTK_SPIN_BUTTON(r->spins[i]), *ip[i]);
234                 else
235                         gtk_spin_button_set_value(GTK_SPIN_BUTTON(r->spins[i]), defval);
236
237                 gtk_box_pack_start(GTK_BOX(r->gopt.box), r->spins[i], FALSE, FALSE, 0);
238         }
239
240         return &r->gopt;
241 }
242
243 static void gopt_add_option(GtkWidget *hbox, struct fio_option *o,
244                             unsigned int opt_index, struct thread_options *to)
245 {
246         struct gopt *go = NULL;
247
248         switch (o->type) {
249         case FIO_OPT_STR_VAL:
250         case FIO_OPT_STR_VAL_TIME: {
251                 unsigned long long *ullp = NULL;
252
253                 if (o->off1)
254                         ullp = td_var(to, o->off1);
255
256                 go = gopt_new_ullong(o, ullp);
257                 break;
258                 }
259         case FIO_OPT_INT: {
260                 unsigned int *ip = NULL;
261
262                 if (o->off1)
263                         ip = td_var(to, o->off1);
264
265                 go = gopt_new_int(o, ip);
266                 break;
267                 }
268         case FIO_OPT_STR_SET:
269         case FIO_OPT_BOOL: {
270                 unsigned int *ip = NULL;
271
272                 if (o->off1)
273                         ip = td_var(to, o->off1);
274
275                 go = gopt_new_bool(o, ip);
276                 break;
277                 }
278         case FIO_OPT_STR: {
279                 unsigned int *ip = NULL;
280
281                 if (o->off1)
282                         ip = td_var(to, o->off1);
283
284                 go = gopt_new_combo_int(o, ip);
285                 break;
286                 }
287         case FIO_OPT_STR_STORE: {
288                 char *text = NULL;
289
290                 if (o->off1 && !o->cb) {
291                         char **p = td_var(to, o->off1);
292                         text = *p;
293                 }
294
295                 if (!o->posval[0].ival) {
296                         go = gopt_new_str_store(o, text);
297                         break;
298                 }
299
300                 go = gopt_new_combo_str(o, text);
301                 break;
302                 }
303         case FIO_OPT_STR_MULTI:
304                 go = gopt_new_combo_str(o, NULL);
305                 break;
306         case FIO_OPT_RANGE: {
307                 unsigned int *ip[4] = { td_var(to, o->off1),
308                                         td_var(to, o->off2),
309                                         td_var(to, o->off3),
310                                         td_var(to, o->off4) };
311
312                 go = gopt_new_int_range(o, ip);
313                 break;
314                 }
315         /* still need to handle this one */
316         case FIO_OPT_FLOAT_LIST:
317                 break;
318         case FIO_OPT_DEPRECATED:
319                 break;
320         default:
321                 printf("ignore type %u\n", o->type);
322                 break;
323         }
324
325         if (go) {
326                 if (o->help)
327                         gtk_widget_set_tooltip_text(go->box, o->help);
328         
329                 gtk_box_pack_start(GTK_BOX(hbox), go->box, FALSE, FALSE, 5);
330                 go->opt_index = opt_index;
331                 go->opt_type = o->type;
332         }
333 }
334
335 static void gopt_add_options(GtkWidget **vboxes, struct thread_options *to)
336 {
337         GtkWidget *hbox = NULL;
338         int i;
339
340         for (i = 0; fio_options[i].name; i++) {
341                 struct fio_option *o = &fio_options[i];
342                 unsigned int mask = o->category;
343                 struct opt_group *og;
344
345                 while ((og = opt_group_from_mask(&mask)) != NULL) {
346                         GtkWidget *vbox = vboxes[ffz(~og->mask)];
347
348                         hbox = gtk_hbox_new(FALSE, 3);
349                         gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
350                         gopt_add_option(hbox, o, i, to);
351                 }
352         }
353 }
354
355 static GtkWidget *gopt_add_group_tab(GtkWidget *notebook, struct opt_group *og)
356 {
357         GtkWidget *box, *vbox, *scroll;
358
359         scroll = gtk_scrolled_window_new(NULL, NULL);
360         gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
361         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
362
363         vbox = gtk_vbox_new(FALSE, 3);
364         box = gtk_hbox_new(FALSE, 0);
365         gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
366         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
367         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scroll, gtk_label_new(og->name));
368
369         return vbox;
370 }
371
372 static void gopt_add_group_tabs(GtkWidget *notebook, GtkWidget **vbox)
373 {
374         struct opt_group *og;
375         unsigned int i = 0;
376
377         do {
378                 unsigned int mask = (1U << i);
379
380                 og = opt_group_from_mask(&mask);
381                 if (!og)
382                         break;
383                 vbox[i] = gopt_add_group_tab(notebook, og);
384                 i++;
385         } while (1);
386 }
387
388 void gopt_get_options_window(GtkWidget *window, struct thread_options *o)
389 {
390         GtkWidget *dialog, *notebook;
391         GtkWidget *vboxes[__FIO_OPT_G_NR];
392
393         dialog = gtk_dialog_new_with_buttons("Fio options",
394                         GTK_WINDOW(window), GTK_DIALOG_DESTROY_WITH_PARENT,
395                         GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
396                         GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
397
398         gtk_widget_set_size_request(GTK_WIDGET(dialog), 1024, 768);
399
400         notebook = gtk_notebook_new();
401         gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
402         gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
403         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), notebook, TRUE, TRUE, 5);
404
405         gopt_add_group_tabs(notebook, vboxes);
406
407         gopt_add_options(vboxes, o);
408
409         gtk_widget_show_all(dialog);
410
411         gtk_dialog_run(GTK_DIALOG(dialog));
412
413         gtk_widget_destroy(dialog);
414 }