Merge branch 'master' of https://scm.cri.ensmp.fr/git/Faustine
[Faustine.git] / interpretor / faust-0.9.47mr3 / architecture / gui / faustgtk.h
1 #ifndef FAUST_GTKUI_H
2 #define FAUST_GTKUI_H
3
4 #include "GUI.h"
5
6 /******************************************************************************
7 *******************************************************************************
8
9 GRAPHIC USER INTERFACE
10 gtk interface
11
12 *******************************************************************************
13 *******************************************************************************/
14 #include <string>
15 #include <set>
16
17 #include <math.h>
18 #include <stdlib.h>
19 #include <string.h>
20
21 #include <gtk/gtk.h>
22 #include <gdk/gdkkeysyms.h>
23 #include <assert.h>
24
25 using namespace std;
26
27 #define max(x,y) (((x)>(y)) ? (x) : (y))
28 #define min(x,y) (((x)<(y)) ? (x) : (y))
29
30 #define stackSize 256
31
32 // Insertion modes
33
34 #define kSingleMode 0
35 #define kBoxMode 1
36 #define kTabMode 2
37
38 //------------ calculate needed precision
39 static int precision(double n)
40 {
41 if (n < 0.009999) return 3;
42 else if (n < 0.099999) return 2;
43 else if (n < 0.999999) return 1;
44 else return 0;
45 }
46
47 namespace gtk_knob
48 {
49
50 class GtkKnob
51 {
52 private:
53 double start_x, start_y, max_value;
54 public:
55 GtkRange parent;
56 int last_quadrant;
57 GtkKnob();
58 ~GtkKnob();
59 GtkWidget *gtk_knob_new_with_adjustment(GtkAdjustment *_adjustment);
60
61 };
62
63 #define GTK_TYPE_KNOB (gtk_knob_get_type())
64 #define GTK_KNOB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_KNOB, GtkKnob))
65 #define GTK_IS_KNOB(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_KNOB))
66 #define GTK_KNOB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_KNOB, GtkKnobClass))
67 #define GTK_IS_KNOB_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_KNOB))
68
69 GtkKnob::GtkKnob()
70 // GtkKnob constructor
71 {
72
73 }
74
75 GtkKnob::~GtkKnob()
76 {
77 // Nothing specific to do...
78 }
79
80 struct GtkKnobClass {
81 GtkRangeClass parent_class;
82 int knob_x;
83 int knob_y;
84 int knob_step;
85 int button_is;
86
87 };
88
89 //------forward declaration
90 GType gtk_knob_get_type ();
91
92 /****************************************************************
93 ** calculate the knop pointer with dead zone
94 */
95
96 const double scale_zero = 20 * (M_PI/180); // defines "dead zone" for knobs
97
98 static void knob_expose(GtkWidget *widget, int knob_x, int knob_y, GdkEventExpose *event, int arc_offset)
99 {
100 /** check resize **/
101 int grow;
102 if(widget->allocation.width > widget->allocation.height) {
103 grow = widget->allocation.height;
104 } else {
105 grow = widget->allocation.width;
106 }
107 knob_x = grow-4;
108 knob_y = grow-4;
109 /** get values for the knob **/
110 GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(widget));
111 int knobx = (widget->allocation.x+2 + (widget->allocation.width-4 - knob_x) * 0.5);
112 int knoby = (widget->allocation.y+2 + (widget->allocation.height-4 - knob_y) * 0.5);
113 int knobx1 = (widget->allocation.x+2 + (widget->allocation.width-4)* 0.5);
114 int knoby1 = (widget->allocation.y+2 + (widget->allocation.height-4) * 0.5);
115 double knobstate = (adj->value - adj->lower) / (adj->upper - adj->lower);
116 double angle = scale_zero + knobstate * 2 * (M_PI - scale_zero);
117 double knobstate1 = (0. - adj->lower) / (adj->upper - adj->lower);
118 double pointer_off = knob_x/6;
119 double radius = min(knob_x-pointer_off, knob_y-pointer_off) / 2;
120 double lengh_x = (knobx+radius+pointer_off/2) - radius * sin(angle);
121 double lengh_y = (knoby+radius+pointer_off/2) + radius * cos(angle);
122 double radius1 = min(knob_x, knob_y) / 2 ;
123
124 /** get widget forground color convert to cairo **/
125 GtkStyle *style = gtk_widget_get_style (widget);
126 double r = min(0.6,style->fg[gtk_widget_get_state(widget)].red/65535.0),
127 g = min(0.6,style->fg[gtk_widget_get_state(widget)].green/65535.0),
128 b = min(0.6,style->fg[gtk_widget_get_state(widget)].blue/65535.0);
129
130 /** paint focus **/
131 if (GTK_WIDGET_HAS_FOCUS(widget)== TRUE) {
132 gtk_paint_focus(widget->style, widget->window, GTK_STATE_NORMAL, NULL, widget, NULL,
133 knobx-2, knoby-2, knob_x+4, knob_y+4);
134 }
135 /** create clowing knobs with cairo **/
136 cairo_t *cr = gdk_cairo_create(GDK_DRAWABLE(widget->window));
137 GdkRegion *region;
138 region = gdk_region_rectangle (&widget->allocation);
139 gdk_region_intersect (region, event->region);
140 gdk_cairo_region (cr, region);
141 cairo_clip (cr);
142
143 cairo_arc(cr,knobx1+arc_offset, knoby1+arc_offset, knob_x/2.1, 0, 2 * M_PI );
144 cairo_pattern_t*pat =
145 cairo_pattern_create_radial (knobx1+arc_offset-knob_x/6,knoby1+arc_offset-knob_x/6, 1,knobx1+arc_offset,knoby1+arc_offset,knob_x/2.1 );
146 if(adj->lower<0 && adj->value>0.) {
147 cairo_pattern_add_color_stop_rgb (pat, 0, r+0.4, g+0.4 + knobstate-knobstate1, b+0.4);
148 cairo_pattern_add_color_stop_rgb (pat, 0.7, r+0.15, g+0.15 + (knobstate-knobstate1)*0.5, b+0.15);
149 cairo_pattern_add_color_stop_rgb (pat, 1, r, g, b);
150 } else if(adj->lower<0 && adj->value<=0.) {
151 cairo_pattern_add_color_stop_rgb (pat, 0, r+0.4 +knobstate1- knobstate, g+0.4, b+0.4);
152 cairo_pattern_add_color_stop_rgb (pat, 0.7, r+0.15 +(knobstate1- knobstate)*0.5, g+0.15, b+0.15);
153 cairo_pattern_add_color_stop_rgb (pat, 1, r, g, b);
154 } else {
155 cairo_pattern_add_color_stop_rgb (pat, 0, r+0.4, g+0.4 +knobstate, b+0.4);
156 cairo_pattern_add_color_stop_rgb (pat, 0.7, r+0.15, g+0.15 + knobstate*0.5, b+0.15);
157 cairo_pattern_add_color_stop_rgb (pat, 1, r, g, b);
158 }
159 cairo_set_source (cr, pat);
160 cairo_fill_preserve (cr);
161 gdk_cairo_set_source_color(cr, gtk_widget_get_style (widget)->fg);
162 cairo_set_line_width(cr, 2.0);
163 cairo_stroke(cr);
164
165 /** create a rotating pointer on the kob**/
166 cairo_set_source_rgb(cr, 0.1, 0.1, 0.1);
167 cairo_set_line_width(cr,max(3, min(7, knob_x/15)));
168 cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
169 cairo_set_line_join(cr, CAIRO_LINE_JOIN_BEVEL);
170 cairo_move_to(cr, knobx+radius1, knoby+radius1);
171 cairo_line_to(cr,lengh_x,lengh_y);
172 cairo_stroke(cr);
173 cairo_set_source_rgb(cr, 0.9, 0.9, 0.9);
174 cairo_set_line_width(cr,min(5, max(1,knob_x/30)));
175 cairo_move_to(cr, knobx+radius1, knoby+radius1);
176 cairo_line_to(cr,lengh_x,lengh_y);
177 cairo_stroke(cr);
178 cairo_pattern_destroy (pat);
179 gdk_region_destroy (region);
180 cairo_destroy(cr);
181 }
182
183 /****************************************************************
184 ** general expose events for all "knob" controllers
185 */
186
187 //----------- draw the Knob when moved
188 static gboolean gtk_knob_expose (GtkWidget *widget, GdkEventExpose *event)
189 {
190 g_assert(GTK_IS_KNOB(widget));
191 GtkKnobClass *klass = GTK_KNOB_CLASS(GTK_OBJECT_GET_CLASS(widget));
192 knob_expose(widget, klass->knob_x, klass->knob_y, event, 0);
193 return TRUE;
194 }
195
196 /****************************************************************
197 ** set initial size for GdkDrawable per type
198 */
199
200 static void gtk_knob_size_request (GtkWidget *widget, GtkRequisition *requisition)
201 {
202 g_assert(GTK_IS_KNOB(widget));
203 GtkKnobClass *klass = GTK_KNOB_CLASS(GTK_OBJECT_GET_CLASS(widget));
204 requisition->width = klass->knob_x;
205 requisition->height = klass->knob_y;
206 }
207
208 /****************************************************************
209 ** set value from key bindings
210 */
211
212 static void gtk_knob_set_value (GtkWidget *widget, int dir_down)
213 {
214 g_assert(GTK_IS_KNOB(widget));
215
216 GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(widget));
217
218 int oldstep = (int)(0.5f + (adj->value - adj->lower) / adj->step_increment);
219 int step;
220 int nsteps = (int)(0.5f + (adj->upper - adj->lower) / adj->step_increment);
221 if (dir_down)
222 step = oldstep - 1;
223 else
224 step = oldstep + 1;
225 float value = adj->lower + step * double(adj->upper - adj->lower) / nsteps;
226 gtk_widget_grab_focus(widget);
227 gtk_range_set_value(GTK_RANGE(widget), value);
228 }
229
230 /****************************************************************
231 ** keyboard bindings
232 */
233
234 static gboolean gtk_knob_key_press (GtkWidget *widget, GdkEventKey *event)
235 {
236 g_assert(GTK_IS_KNOB(widget));
237
238 GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(widget));
239 switch (event->keyval) {
240 case GDK_Home:
241 gtk_range_set_value(GTK_RANGE(widget), adj->lower);
242 return TRUE;
243 case GDK_End:
244 gtk_range_set_value(GTK_RANGE(widget), adj->upper);
245 return TRUE;
246 case GDK_Up:
247 gtk_knob_set_value(widget, 0);
248 return TRUE;
249 case GDK_Right:
250 gtk_knob_set_value(widget, 0);
251 return TRUE;
252 case GDK_Down:
253 gtk_knob_set_value(widget, 1);
254 return TRUE;
255 case GDK_Left:
256 gtk_knob_set_value(widget, 1);
257 return TRUE;
258 }
259
260 return FALSE;
261 }
262
263 /****************************************************************
264 ** alternative (radial) knob motion mode (ctrl + mouse pressed)
265 */
266
267 static void knob_pointer_event(GtkWidget *widget, gdouble x, gdouble y, int knob_x, int knob_y,
268 bool drag, int state)
269 {
270 static double last_y = 2e20;
271 GtkKnob *knob = GTK_KNOB(widget);
272 GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(widget));
273 double radius = min(knob_x, knob_y) / 2;
274 int knobx = (widget->allocation.width - knob_x) / 2;
275 int knoby = (widget->allocation.height - knob_y) / 2;
276 double posx = (knobx + radius) - x; // x axis right -> left
277 double posy = (knoby + radius) - y; // y axis top -> bottom
278 double value;
279 if (!drag) {
280 if (state & GDK_CONTROL_MASK) {
281 last_y = 2e20;
282 return;
283 } else {
284 last_y = posy;
285 }
286 }
287 if (last_y < 1e20) { // in drag started with Control Key
288 const double scaling = 0.005;
289 double scal = (state & GDK_SHIFT_MASK ? scaling*0.1 : scaling);
290 value = (last_y - posy) * scal;
291 last_y = posy;
292 gtk_range_set_value(GTK_RANGE(widget), adj->value - value * (adj->upper - adj->lower));
293 return;
294 }
295
296 double angle = atan2(-posx, posy) + M_PI; // clockwise, zero at 6 o'clock, 0 .. 2*M_PI
297 if (drag) {
298 // block "forbidden zone" and direct moves between quadrant 1 and 4
299 int quadrant = 1 + int(angle/M_PI_2);
300 if (knob->last_quadrant == 1 && (quadrant == 3 || quadrant == 4)) {
301 angle = scale_zero;
302 } else if (knob->last_quadrant == 4 && (quadrant == 1 || quadrant == 2)) {
303 angle = 2*M_PI - scale_zero;
304 } else {
305 if (angle < scale_zero) {
306 angle = scale_zero;
307 } else if (angle > 2*M_PI - scale_zero) {
308 angle = 2*M_PI - scale_zero;
309 }
310 knob->last_quadrant = quadrant;
311 }
312 } else {
313 if (angle < scale_zero) {
314 angle = scale_zero;
315 } else if (angle > 2*M_PI - scale_zero) {
316 angle = 2*M_PI - scale_zero;
317 }
318 knob->last_quadrant = 0;
319 }
320 angle = (angle - scale_zero) / (2 * (M_PI-scale_zero)); // normalize to 0..1
321 gtk_range_set_value(GTK_RANGE(widget), adj->lower + angle * (adj->upper - adj->lower));
322 }
323
324 /****************************************************************
325 ** mouse button pressed set value
326 */
327
328 static gboolean gtk_knob_button_press (GtkWidget *widget, GdkEventButton *event)
329 {
330 g_assert(GTK_IS_KNOB(widget));
331
332 GtkKnobClass *klass = GTK_KNOB_CLASS(GTK_OBJECT_GET_CLASS(widget));
333
334
335 switch (event->button) {
336 case 1: // left button
337 gtk_widget_grab_focus(widget);
338 gtk_widget_grab_default (widget);
339 gtk_grab_add(widget);
340 klass->button_is = 1;
341 knob_pointer_event(widget, event->x, event->y, klass->knob_x, klass->knob_y,
342 false, event->state);
343 break;
344 case 2: //wheel
345 klass->button_is = 2;
346 break;
347 case 3: // right button
348 klass->button_is = 3;
349 break;
350 default: // do nothing
351 break;
352 }
353 return TRUE;
354 }
355
356 /****************************************************************
357 ** mouse button release
358 */
359
360 static gboolean gtk_knob_button_release (GtkWidget *widget, GdkEventButton *event)
361 {
362 g_assert(GTK_IS_KNOB(widget));
363 GTK_KNOB_CLASS(GTK_OBJECT_GET_CLASS(widget))->button_is = 0;
364 if (GTK_WIDGET_HAS_GRAB(widget))
365 gtk_grab_remove(widget);
366 return FALSE;
367 }
368
369 /****************************************************************
370 ** set the value from mouse movement
371 */
372
373 static gboolean gtk_knob_pointer_motion (GtkWidget *widget, GdkEventMotion *event)
374 {
375 g_assert(GTK_IS_KNOB(widget));
376 GtkKnobClass *klass = GTK_KNOB_CLASS(GTK_OBJECT_GET_CLASS(widget));
377
378 gdk_event_request_motions (event);
379
380 if (GTK_WIDGET_HAS_GRAB(widget)) {
381 knob_pointer_event(widget, event->x, event->y, klass->knob_x, klass->knob_y,
382 true, event->state);
383 }
384 return FALSE;
385 }
386
387 /****************************************************************
388 ** set value from mouseweel
389 */
390
391 static gboolean gtk_knob_scroll (GtkWidget *widget, GdkEventScroll *event)
392 {
393 usleep(5000);
394 gtk_knob_set_value(widget, event->direction);
395 return FALSE;
396 }
397
398 /****************************************************************
399 ** init the GtkKnobClass
400 */
401
402 static void gtk_knob_class_init (GtkKnobClass *klass)
403 {
404 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
405
406 /** set here the sizes and steps for the used knob **/
407 //--------- small knob size and steps
408
409 klass->knob_x = 30;
410 klass->knob_y = 30;
411 klass->knob_step = 86;
412
413 //--------- event button
414 klass->button_is = 0;
415
416 //--------- connect the events with funktions
417 widget_class->expose_event = gtk_knob_expose;
418 widget_class->size_request = gtk_knob_size_request;
419 widget_class->button_press_event = gtk_knob_button_press;
420 widget_class->button_release_event = gtk_knob_button_release;
421 widget_class->motion_notify_event = gtk_knob_pointer_motion;
422 widget_class->key_press_event = gtk_knob_key_press;
423 widget_class->scroll_event = gtk_knob_scroll;
424 }
425
426 /****************************************************************
427 ** init the Knob type/size
428 */
429
430 static void gtk_knob_init (GtkKnob *knob)
431 {
432 GtkWidget *widget = GTK_WIDGET(knob);
433 GtkKnobClass *klass = GTK_KNOB_CLASS(GTK_OBJECT_GET_CLASS(widget));
434
435 GTK_WIDGET_SET_FLAGS (GTK_WIDGET(knob), GTK_CAN_FOCUS);
436 GTK_WIDGET_SET_FLAGS (GTK_WIDGET(knob), GTK_CAN_DEFAULT);
437
438 widget->requisition.width = klass->knob_x;
439 widget->requisition.height = klass->knob_y;
440
441 }
442
443 /****************************************************************
444 ** redraw when value changed
445 */
446
447 static gboolean gtk_knob_value_changed(gpointer obj)
448 {
449 GtkWidget *widget = (GtkWidget *)obj;
450 gtk_widget_queue_draw(widget);
451 return FALSE;
452 }
453
454 /****************************************************************
455 ** create small knob
456 */
457
458 GtkWidget *GtkKnob::gtk_knob_new_with_adjustment(GtkAdjustment *_adjustment)
459 {
460 GtkWidget *widget = GTK_WIDGET( g_object_new (GTK_TYPE_KNOB, NULL ));
461 GtkKnob *knob = GTK_KNOB(widget);
462 knob->last_quadrant = 0;
463 if (widget) {
464 gtk_range_set_adjustment(GTK_RANGE(widget), _adjustment);
465 g_signal_connect(GTK_OBJECT(widget), "value-changed",
466 G_CALLBACK(gtk_knob_value_changed), widget);
467 }
468 return widget;
469 }
470
471 /****************************************************************
472 ** get the Knob type
473 */
474
475 GType gtk_knob_get_type (void)
476 {
477 static GType kn_type = 0;
478 if (!kn_type) {
479 static const GTypeInfo kn_info = {
480 sizeof(GtkKnobClass), NULL, NULL, (GClassInitFunc)gtk_knob_class_init, NULL, NULL, sizeof (GtkKnob), 0, (GInstanceInitFunc)gtk_knob_init
481 };
482 kn_type = g_type_register_static(GTK_TYPE_RANGE, "GtkKnob", &kn_info, (GTypeFlags)0);
483 }
484 return kn_type;
485 }
486 }/* end of gtk_knob namespace */
487
488 gtk_knob::GtkKnob myGtkKnob;
489
490 /**
491 * rmWhiteSpaces(): Remove the leading and trailing white spaces of a string
492 * (but not those in the middle of the string)
493 */
494 static string rmWhiteSpaces(const string& s)
495 {
496 size_t i = s.find_first_not_of(" \t");
497 size_t j = s.find_last_not_of(" \t");
498
499 if (i != string::npos & j != string::npos) {
500 return s.substr(i, 1+j-i);
501 } else {
502 return "";
503 }
504 }
505
506
507 /**
508 * Extracts metdata from a label : 'vol [unit: dB]' -> 'vol' + metadata
509 */
510 static void extractMetadata(const string& fulllabel, string& label, map<string, string>& metadata)
511 {
512 enum {kLabel, kEscape1, kEscape2, kEscape3, kKey, kValue};
513 int state = kLabel; int deep = 0;
514 string key, value;
515
516 for (unsigned int i=0; i < fulllabel.size(); i++) {
517 char c = fulllabel[i];
518 switch (state) {
519 case kLabel :
520 assert (deep == 0);
521 switch (c) {
522 case '\\' : state = kEscape1; break;
523 case '[' : state = kKey; deep++; break;
524 default : label += c;
525 }
526 break;
527
528 case kEscape1 :
529 label += c;
530 state = kLabel;
531 break;
532
533 case kEscape2 :
534 key += c;
535 state = kKey;
536 break;
537
538 case kEscape3 :
539 value += c;
540 state = kValue;
541 break;
542
543 case kKey :
544 assert (deep > 0);
545 switch (c) {
546 case '\\' : state = kEscape2;
547 break;
548
549 case '[' : deep++;
550 key += c;
551 break;
552
553 case ':' : if (deep == 1) {
554 state = kValue;
555 } else {
556 key += c;
557 }
558 break;
559 case ']' : deep--;
560 if (deep < 1) {
561 metadata[rmWhiteSpaces(key)] = "";
562 state = kLabel;
563 key="";
564 value="";
565 } else {
566 key += c;
567 }
568 break;
569 default : key += c;
570 }
571 break;
572
573 case kValue :
574 assert (deep > 0);
575 switch (c) {
576 case '\\' : state = kEscape3;
577 break;
578
579 case '[' : deep++;
580 value += c;
581 break;
582
583 case ']' : deep--;
584 if (deep < 1) {
585 metadata[rmWhiteSpaces(key)]=rmWhiteSpaces(value);
586 state = kLabel;
587 key="";
588 value="";
589 } else {
590 value += c;
591 }
592 break;
593 default : value += c;
594 }
595 break;
596
597 default :
598 cerr << "ERROR unrecognized state " << state << endl;
599 }
600 }
601 label = rmWhiteSpaces(label);
602 }
603
604
605 class GTKUI : public GUI
606 {
607 private :
608 static bool fInitialized;
609 static map<float*, float> fGuiSize; // map widget zone with widget size coef
610 static map<float*, string> fTooltip; // map widget zone with tooltip strings
611 static set<float*> fKnobSet; // set of widget zone to be knobs
612 string gGroupTooltip;
613
614 bool isKnob(float* zone){return fKnobSet.count(zone) > 0;}
615
616 protected :
617 GtkWidget* fWindow;
618 int fTop;
619 GtkWidget* fBox[stackSize];
620 int fMode[stackSize];
621 bool fStopped;
622
623 GtkWidget* addWidget(const char* label, GtkWidget* w);
624 virtual void pushBox(int mode, GtkWidget* w);
625
626
627 public :
628 static const gboolean expand = TRUE;
629 static const gboolean fill = TRUE;
630 static const gboolean homogene = FALSE;
631
632 GTKUI(char * name, int* pargc, char*** pargv);
633
634 // -- Labels and metadata
635
636 virtual void declare (float* zone, const char* key, const char* value);
637 virtual int checkLabelOptions (GtkWidget* widget, const string& fullLabel, string& simplifiedLabel);
638 virtual void checkForTooltip (float* zone, GtkWidget* widget);
639
640 // -- layout groups
641
642 virtual void openFrameBox(const char* label);
643 virtual void openTabBox(const char* label = "");
644 virtual void openHorizontalBox(const char* label = "");
645 virtual void openVerticalBox(const char* label = "");
646
647 // -- extra widget's layouts
648
649 virtual void openDialogBox(const char* label, float* zone);
650 virtual void openEventBox(const char* label = "");
651 virtual void openHandleBox(const char* label = "");
652 virtual void openExpanderBox(const char* label, float* zone);
653
654 virtual void closeBox();
655 virtual void adjustStack(int n);
656
657 // -- active widgets
658
659 virtual void addButton(const char* label, float* zone);
660 virtual void addToggleButton(const char* label, float* zone);
661 virtual void addCheckButton(const char* label, float* zone);
662 virtual void addVerticalSlider(const char* label, float* zone, float init, float min, float max, float step);
663 virtual void addHorizontalSlider(const char* label, float* zone, float init, float min, float max, float step);
664 virtual void addKnob(const char* label, float* zone, float init, float min, float max, float step);
665 virtual void addNumEntry(const char* label, float* zone, float init, float min, float max, float step);
666
667 // -- passive display widgets
668
669 virtual void addNumDisplay(const char* label, float* zone, int precision);
670 virtual void addTextDisplay(const char* label, float* zone, const char* names[], float min, float max);
671 virtual void addHorizontalBargraph(const char* label, float* zone, float min, float max);
672 virtual void addVerticalBargraph(const char* label, float* zone, float min, float max);
673
674 virtual void show();
675 virtual void run();
676
677 };
678
679
680
681 /******************************************************************************
682 *******************************************************************************
683
684 GRAPHIC USER INTERFACE (v2)
685 gtk implementation
686
687 *******************************************************************************
688 *******************************************************************************/
689
690 // global static fields
691
692 bool GTKUI::fInitialized = false;
693 map<float*, float> GTKUI::fGuiSize;
694 map<float*, string> GTKUI::fTooltip;
695 set<float*> GTKUI::fKnobSet; // set of widget zone to be knobs
696
697 /**
698 * Format tooltip string by replacing some white spaces by
699 * return characters so that line width doesn't exceed n.
700 * Limitation : long words exceeding n are not cut
701 */
702 static string formatTooltip(int n, const string& tt)
703 {
704 string ss = tt; // ss string we are going to format
705 int lws = 0; // last white space encountered
706 int lri = 0; // last return inserted
707 for (int i=0; i<tt.size(); i++) {
708 if (tt[i] == ' ') lws = i;
709 if (((i-lri) >= n) && (lws > lri)) {
710 // insert return here
711 ss[lws] = '\n';
712 lri = lws;
713 }
714 }
715 cout << ss;
716 return ss;
717 }
718
719
720
721 static gint delete_event( GtkWidget *widget, GdkEvent *event, gpointer data )
722 {
723 return FALSE;
724 }
725
726 static void destroy_event( GtkWidget *widget, gpointer data )
727 {
728 gtk_main_quit ();
729 }
730
731
732 GTKUI::GTKUI(char * name, int* pargc, char*** pargv)
733 {
734 if (!fInitialized) {
735 gtk_init(pargc, pargv);
736 fInitialized = true;
737 }
738
739 fWindow = gtk_window_new (GTK_WINDOW_TOPLEVEL);
740 //gtk_container_set_border_width (GTK_CONTAINER (fWindow), 10);
741 gtk_window_set_title (GTK_WINDOW (fWindow), name);
742 gtk_signal_connect (GTK_OBJECT (fWindow), "delete_event", GTK_SIGNAL_FUNC (delete_event), NULL);
743 gtk_signal_connect (GTK_OBJECT (fWindow), "destroy", GTK_SIGNAL_FUNC (destroy_event), NULL);
744
745 fTop = 0;
746 fBox[fTop] = gtk_vbox_new (homogene, 4);
747 fMode[fTop] = kBoxMode;
748 gtk_container_add (GTK_CONTAINER (fWindow), fBox[fTop]);
749 fStopped = false;
750 }
751
752 // empilement des boites
753
754 void GTKUI::pushBox(int mode, GtkWidget* w)
755 {
756 ++fTop;
757 assert(fTop < stackSize);
758 fMode[fTop] = mode;
759 fBox[fTop] = w;
760 }
761
762
763 /**
764 * Remove n levels from the stack S before the top level
765 * adjustStack(n): S -> S' with S' = S(0),S(n+1),S(n+2),...
766 */
767 void GTKUI::adjustStack(int n)
768 {
769 if (n > 0) {
770 assert(fTop >= n);
771
772 fTop -= n;
773 fMode[fTop] = fMode[fTop+n];
774 fBox[fTop] = fBox[fTop+n];
775 }
776 }
777
778 void GTKUI::closeBox()
779 {
780 --fTop;
781 assert(fTop >= 0);
782 }
783
784
785 /**
786 * Analyses the widget zone metadata declarations and takes
787 * appropriate actions
788 */
789 void GTKUI::declare(float* zone, const char* key, const char* value)
790 {
791 if (zone == 0) {
792 // special zone 0 means group metadata
793 if (strcmp(key,"tooltip")==0) {
794 // only group tooltip are currently implemented
795 gGroupTooltip = formatTooltip(30, value);
796 }
797 } else {
798 if (strcmp(key,"size")==0) {
799 fGuiSize[zone]=atof(value);
800 }
801 else if (strcmp(key,"tooltip")==0) {
802 fTooltip[zone] = formatTooltip(30,value) ;
803 }
804 else if (strcmp(key,"style")==0) {
805 if (strcmp(value,"knob") == 0) {
806 fKnobSet.insert(zone);
807 }
808 }
809 }
810 }
811
812
813
814 /**
815 * Analyses a full label and activates the relevant options. returns a simplified
816 * label (without options) and an amount of stack adjustement (in case additional
817 * containers were pushed on the stack).
818 */
819
820 int GTKUI::checkLabelOptions(GtkWidget* widget, const string& fullLabel, string& simplifiedLabel)
821 {
822 map<string, string> metadata;
823 extractMetadata(fullLabel, simplifiedLabel, metadata);
824
825 if (metadata.count("tooltip")) {
826 gtk_tooltips_set_tip (gtk_tooltips_new (), widget, metadata["tooltip"].c_str(), NULL);
827 }
828 if (metadata["option"] == "detachable") {
829 openHandleBox(simplifiedLabel.c_str());
830 return 1;
831 }
832
833 //---------------------
834 if (gGroupTooltip != string()) {
835 gtk_tooltips_set_tip (gtk_tooltips_new (), widget, gGroupTooltip.c_str(), NULL);
836 gGroupTooltip = string();
837 }
838
839 //----------------------
840
841 // no adjustement of the stack needed
842 return 0;
843 }
844
845 /**
846 * Check if a tooltip is associated to a zone and add it to the corresponding widget
847 */
848 void GTKUI::checkForTooltip(float* zone, GtkWidget* widget)
849 {
850 if (fTooltip.count(zone)) {
851 gtk_tooltips_set_tip (gtk_tooltips_new (), widget, fTooltip[zone].c_str(), NULL);
852 }
853 }
854
855
856 // les differentes boites
857
858 void GTKUI::openFrameBox(const char* label)
859 {
860 GtkWidget * box = gtk_frame_new (label);
861 //gtk_container_set_border_width (GTK_CONTAINER (box), 10);
862
863 pushBox(kSingleMode, addWidget(label, box));
864 }
865
866
867 void GTKUI::openTabBox(const char* fullLabel)
868 {
869 string label;
870 GtkWidget* widget = gtk_notebook_new();
871
872 int adjust = checkLabelOptions(widget, fullLabel, label);
873
874 pushBox(kTabMode, addWidget(label.c_str(), widget));
875
876 // adjust stack because otherwise Handlebox will remain open
877 adjustStack(adjust);
878 }
879
880
881 void GTKUI::openHorizontalBox(const char* fullLabel)
882 {
883 string label;
884 GtkWidget* box = gtk_hbox_new (homogene, 4);
885 int adjust = checkLabelOptions(box, fullLabel, label);
886
887 gtk_container_set_border_width (GTK_CONTAINER (box), 10);
888
889 if (fMode[fTop] != kTabMode && label[0] != 0) {
890 GtkWidget * frame = addWidget(label.c_str(), gtk_frame_new (label.c_str()));
891 gtk_container_add (GTK_CONTAINER(frame), box);
892 gtk_widget_show(box);
893 pushBox(kBoxMode, box);
894 } else {
895 pushBox(kBoxMode, addWidget(label.c_str(), box));
896 }
897
898 // adjust stack because otherwise Handlebox will remain open
899 adjustStack(adjust);
900 }
901
902
903 void GTKUI::openVerticalBox(const char* fullLabel)
904 {
905 string label;
906 GtkWidget * box = gtk_vbox_new (homogene, 4);
907 int adjust = checkLabelOptions(box, fullLabel, label);
908
909 gtk_container_set_border_width (GTK_CONTAINER (box), 10);
910
911 if (fMode[fTop] != kTabMode && label[0] != 0) {
912 GtkWidget * frame = addWidget(label.c_str(), gtk_frame_new (label.c_str()));
913 gtk_container_add (GTK_CONTAINER(frame), box);
914 gtk_widget_show(box);
915 pushBox(kBoxMode, box);
916 } else {
917 pushBox(kBoxMode, addWidget(label.c_str(), box));
918 }
919
920 // adjust stack because otherwise Handlebox will remain open
921 adjustStack(adjust);
922 }
923
924
925 void GTKUI::openHandleBox(const char* label)
926 {
927 GtkWidget * box = gtk_hbox_new (homogene, 4);
928 gtk_container_set_border_width (GTK_CONTAINER (box), 2);
929 if (fMode[fTop] != kTabMode && label[0] != 0)
930 {
931 GtkWidget * frame = addWidget(label, gtk_handle_box_new ());
932 gtk_container_add (GTK_CONTAINER(frame), box);
933 gtk_widget_show(box);
934 pushBox(kBoxMode, box);
935 }
936 else
937 {
938 pushBox(kBoxMode, addWidget(label, box));
939 }
940 }
941
942
943 void GTKUI::openEventBox(const char* label)
944 {
945 GtkWidget * box = gtk_hbox_new (homogene, 4);
946 gtk_container_set_border_width (GTK_CONTAINER (box), 2);
947 if (fMode[fTop] != kTabMode && label[0] != 0)
948 {
949 GtkWidget * frame = addWidget(label, gtk_event_box_new ());
950 gtk_container_add (GTK_CONTAINER(frame), box);
951 gtk_widget_show(box);
952 pushBox(kBoxMode, box);
953 }
954 else
955 {
956 pushBox(kBoxMode, addWidget(label, box));
957 }
958 }
959
960
961 struct uiExpanderBox : public uiItem
962 {
963 GtkExpander* fButton;
964 uiExpanderBox(GUI* ui, float* zone, GtkExpander* b) : uiItem(ui, zone), fButton(b) {}
965 static void expanded (GtkWidget *widget, gpointer data)
966 {
967 float v = gtk_expander_get_expanded (GTK_EXPANDER(widget));
968 if (v == 1.000000)
969 {
970 v = 0;
971 }
972 else v = 1;
973 ((uiItem*)data)->modifyZone(v);
974 }
975
976 virtual void reflectZone()
977 {
978 float v = *fZone;
979 fCache = v;
980 gtk_expander_set_expanded(GTK_EXPANDER(fButton), v);
981 }
982 };
983
984 void GTKUI::openExpanderBox(const char* label, float* zone)
985 {
986 *zone = 0.0;
987 GtkWidget * box = gtk_hbox_new (homogene, 4);
988 gtk_container_set_border_width (GTK_CONTAINER (box), 2);
989 if (fMode[fTop] != kTabMode && label[0] != 0)
990 {
991 GtkWidget * frame = addWidget(label, gtk_expander_new (label));
992 gtk_container_add (GTK_CONTAINER(frame), box);
993 uiExpanderBox* c = new uiExpanderBox(this, zone, GTK_EXPANDER(frame));
994 gtk_signal_connect (GTK_OBJECT (frame), "activate", GTK_SIGNAL_FUNC (uiExpanderBox::expanded), (gpointer)c);
995 gtk_widget_show(box);
996 pushBox(kBoxMode, box);
997 }
998 else
999 {
1000 pushBox(kBoxMode, addWidget(label, box));
1001 }
1002 }
1003
1004
1005
1006 GtkWidget* GTKUI::addWidget(const char* label, GtkWidget* w)
1007 {
1008 switch (fMode[fTop]) {
1009 case kSingleMode : gtk_container_add (GTK_CONTAINER(fBox[fTop]), w); break;
1010 case kBoxMode : gtk_box_pack_start (GTK_BOX(fBox[fTop]), w, expand, fill, 0); break;
1011 case kTabMode : gtk_notebook_append_page (GTK_NOTEBOOK(fBox[fTop]), w, gtk_label_new(label)); break;
1012 }
1013 gtk_widget_show (w);
1014 return w;
1015 }
1016
1017 // --------------------------- Press button ---------------------------
1018
1019 struct uiButton : public uiItem
1020 {
1021 GtkButton* fButton;
1022
1023 uiButton (GUI* ui, float* zone, GtkButton* b) : uiItem(ui, zone), fButton(b) {}
1024
1025 static void pressed( GtkWidget *widget, gpointer data )
1026 {
1027 uiItem* c = (uiItem*) data;
1028 c->modifyZone(1.0);
1029 }
1030
1031 static void released( GtkWidget *widget, gpointer data )
1032 {
1033 uiItem* c = (uiItem*) data;
1034 c->modifyZone(0.0);
1035 }
1036
1037 virtual void reflectZone()
1038 {
1039 float v = *fZone;
1040 fCache = v;
1041 if (v > 0.0) gtk_button_pressed(fButton); else gtk_button_released(fButton);
1042 }
1043 };
1044
1045 void GTKUI::addButton(const char* label, float* zone)
1046 {
1047 *zone = 0.0;
1048 GtkWidget* button = gtk_button_new_with_label (label);
1049 addWidget(label, button);
1050
1051 uiButton* c = new uiButton(this, zone, GTK_BUTTON(button));
1052
1053 gtk_signal_connect (GTK_OBJECT (button), "pressed", GTK_SIGNAL_FUNC (uiButton::pressed), (gpointer) c);
1054 gtk_signal_connect (GTK_OBJECT (button), "released", GTK_SIGNAL_FUNC (uiButton::released), (gpointer) c);
1055
1056 checkForTooltip(zone, button);
1057 }
1058
1059 // --------------------------- Toggle Buttons ---------------------------
1060
1061 struct uiToggleButton : public uiItem
1062 {
1063 GtkToggleButton* fButton;
1064
1065 uiToggleButton(GUI* ui, float* zone, GtkToggleButton* b) : uiItem(ui, zone), fButton(b) {}
1066
1067 static void toggled (GtkWidget *widget, gpointer data)
1068 {
1069 float v = (GTK_TOGGLE_BUTTON (widget)->active) ? 1.0 : 0.0;
1070 ((uiItem*)data)->modifyZone(v);
1071 }
1072
1073 virtual void reflectZone()
1074 {
1075 float v = *fZone;
1076 fCache = v;
1077 gtk_toggle_button_set_active(fButton, v > 0.0);
1078 }
1079 };
1080
1081 void GTKUI::addToggleButton(const char* label, float* zone)
1082 {
1083 *zone = 0.0;
1084 GtkWidget* button = gtk_toggle_button_new_with_label (label);
1085 addWidget(label, button);
1086
1087 uiToggleButton* c = new uiToggleButton(this, zone, GTK_TOGGLE_BUTTON(button));
1088 gtk_signal_connect (GTK_OBJECT (button), "toggled", GTK_SIGNAL_FUNC (uiToggleButton::toggled), (gpointer) c);
1089
1090 checkForTooltip(zone, button);
1091 }
1092
1093
1094
1095 void show_dialog(GtkWidget *widget, gpointer data)
1096 {
1097 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget)) == TRUE)
1098 {
1099 gtk_widget_show(GTK_WIDGET(data));
1100 gint root_x, root_y;
1101 gtk_window_get_position (GTK_WINDOW(data), &root_x, &root_y);
1102 root_y -= 120;
1103 gtk_window_move(GTK_WINDOW(data), root_x, root_y);
1104 }
1105 else gtk_widget_hide(GTK_WIDGET(data));
1106 }
1107
1108 static gboolean deleteevent( GtkWidget *widget, gpointer data )
1109 {
1110 return TRUE;
1111 }
1112
1113 void GTKUI::openDialogBox(const char* label, float* zone)
1114 {
1115 // create toplevel window and set properties
1116 GtkWidget * dialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1117 gtk_window_set_decorated(GTK_WINDOW(dialog), TRUE);
1118 gtk_window_set_deletable(GTK_WINDOW(dialog), FALSE);
1119 gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
1120 gtk_window_set_gravity(GTK_WINDOW(dialog), GDK_GRAVITY_SOUTH);
1121 gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(fWindow));
1122 gtk_window_set_position (GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
1123 gtk_window_set_keep_below (GTK_WINDOW(dialog), FALSE);
1124 gtk_window_set_title (GTK_WINDOW (dialog), label);
1125 g_signal_connect (G_OBJECT (dialog), "delete_event", G_CALLBACK (deleteevent), NULL);
1126 gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE);
1127
1128 GtkWidget * box = gtk_hbox_new (homogene, 4);
1129
1130 *zone = 0.0;
1131 GtkWidget* button = gtk_toggle_button_new ();
1132 gtk_signal_connect (GTK_OBJECT (button), "toggled", GTK_SIGNAL_FUNC (show_dialog), (gpointer) dialog);
1133
1134 gtk_container_add (GTK_CONTAINER(fBox[fTop]), button);
1135 gtk_container_add (GTK_CONTAINER(dialog), box);
1136 gtk_widget_show (button);
1137 gtk_widget_show(box);
1138 pushBox(kBoxMode, box);
1139 }
1140
1141
1142
1143
1144 // --------------------------- Check Button ---------------------------
1145
1146 struct uiCheckButton : public uiItem
1147 {
1148 GtkToggleButton* fButton;
1149
1150 uiCheckButton(GUI* ui, float* zone, GtkToggleButton* b) : uiItem(ui, zone), fButton(b) {}
1151
1152 static void toggled (GtkWidget *widget, gpointer data)
1153 {
1154 float v = (GTK_TOGGLE_BUTTON (widget)->active) ? 1.0 : 0.0;
1155 ((uiItem*)data)->modifyZone(v);
1156 }
1157
1158 virtual void reflectZone()
1159 {
1160 float v = *fZone;
1161 fCache = v;
1162 gtk_toggle_button_set_active(fButton, v > 0.0);
1163 }
1164 };
1165
1166 void GTKUI::addCheckButton(const char* label, float* zone)
1167 {
1168 *zone = 0.0;
1169 GtkWidget* button = gtk_check_button_new_with_label (label);
1170 addWidget(label, button);
1171
1172 uiCheckButton* c = new uiCheckButton(this, zone, GTK_TOGGLE_BUTTON(button));
1173 gtk_signal_connect (GTK_OBJECT (button), "toggled", GTK_SIGNAL_FUNC(uiCheckButton::toggled), (gpointer) c);
1174
1175 checkForTooltip(zone, button);
1176 }
1177
1178
1179 // --------------------------- Adjustmenty based widgets ---------------------------
1180
1181 struct uiAdjustment : public uiItem
1182 {
1183 GtkAdjustment* fAdj;
1184
1185 uiAdjustment(GUI* ui, float* zone, GtkAdjustment* adj) : uiItem(ui, zone), fAdj(adj) {}
1186
1187 static void changed (GtkWidget *widget, gpointer data)
1188 {
1189 float v = GTK_ADJUSTMENT (widget)->value;
1190 ((uiItem*)data)->modifyZone(v);
1191 }
1192
1193 virtual void reflectZone()
1194 {
1195 float v = *fZone;
1196 fCache = v;
1197 gtk_adjustment_set_value(fAdj, v);
1198 }
1199 };
1200
1201 // --------------------------- format knob value display ---------------------------
1202
1203 struct uiValueDisplay : public uiItem
1204 {
1205 GtkLabel* fLabel;
1206 int fPrecision ;
1207
1208 uiValueDisplay(GUI* ui, float* zone, GtkLabel* label, int precision)
1209 : uiItem(ui, zone), fLabel(label), fPrecision(precision) {}
1210
1211 virtual void reflectZone()
1212 {
1213 float v = *fZone;
1214 fCache = v;
1215 char s[64];
1216 if (fPrecision <= 0)
1217 snprintf(s, 63, "%d", int(v));
1218
1219 else if (fPrecision > 3)
1220 snprintf(s, 63, "%f", v);
1221
1222 else if (fPrecision == 1)
1223 {
1224 const char* format[] = {"%.1f", "%.2f", "%.3f"};
1225 snprintf(s, 63, format[1-1], v);
1226 }
1227 else if (fPrecision == 2)
1228 {
1229 const char* format[] = {"%.1f", "%.2f", "%.3f"};
1230 snprintf(s, 63, format[2-1], v);
1231 }
1232 else
1233 {
1234 const char* format[] = {"%.1f", "%.2f", "%.3f"};
1235 snprintf(s, 63, format[3-1], v);
1236 }
1237 gtk_label_set_text(fLabel, s);
1238 }
1239 };
1240
1241 // ------------------------------- Knob -----------------------------------------
1242
1243 void GTKUI::addKnob(const char* label, float* zone, float init, float min, float max, float step)
1244 {
1245 *zone = init;
1246 GtkObject* adj = gtk_adjustment_new(init, min, max, step, 10*step, 0);
1247
1248 uiAdjustment* c = new uiAdjustment(this, zone, GTK_ADJUSTMENT(adj));
1249
1250 gtk_signal_connect (GTK_OBJECT (adj), "value-changed", GTK_SIGNAL_FUNC (uiAdjustment::changed), (gpointer) c);
1251
1252 GtkWidget* slider = gtk_vbox_new (FALSE, 0);
1253 GtkWidget* fil = gtk_vbox_new (FALSE, 0);
1254 GtkWidget* rei = gtk_vbox_new (FALSE, 0);
1255 GtkWidget* re =myGtkKnob.gtk_knob_new_with_adjustment(GTK_ADJUSTMENT(adj));
1256 GtkWidget* lw = gtk_label_new("");
1257 new uiValueDisplay(this, zone, GTK_LABEL(lw),precision(step));
1258 gtk_container_add (GTK_CONTAINER(rei), re);
1259 if(fGuiSize[zone]) {
1260 float size = 30 * fGuiSize[zone];
1261 gtk_widget_set_size_request(rei, size, size );
1262 gtk_box_pack_start (GTK_BOX(slider), fil, TRUE, TRUE, 0);
1263 gtk_box_pack_start (GTK_BOX(slider), rei, FALSE, FALSE, 0);
1264 } else {
1265 gtk_container_add (GTK_CONTAINER(slider), fil);
1266 gtk_container_add (GTK_CONTAINER(slider), rei);
1267 }
1268 gtk_container_add (GTK_CONTAINER(slider), lw);
1269 gtk_widget_show_all(slider);
1270
1271 if (label && label[0]!=0) {
1272 openFrameBox(label);
1273 addWidget(label, slider);
1274 closeBox();
1275 } else {
1276 addWidget(label, slider);
1277 }
1278
1279 checkForTooltip(zone, slider);
1280 }
1281
1282 // -------------------------- Vertical Slider -----------------------------------
1283
1284 void GTKUI::addVerticalSlider(const char* label, float* zone, float init, float min, float max, float step)
1285 {
1286 if (isKnob(zone)) {
1287 addKnob(label, zone, init, min, max, step);
1288 return;
1289 }
1290 *zone = init;
1291 GtkObject* adj = gtk_adjustment_new(init, min, max, step, 10*step, 0);
1292
1293 uiAdjustment* c = new uiAdjustment(this, zone, GTK_ADJUSTMENT(adj));
1294
1295 gtk_signal_connect (GTK_OBJECT (adj), "value-changed", GTK_SIGNAL_FUNC (uiAdjustment::changed), (gpointer) c);
1296
1297 GtkWidget* slider = gtk_vscale_new (GTK_ADJUSTMENT(adj));
1298 gtk_scale_set_digits(GTK_SCALE(slider), precision(step));
1299 float size = 160;
1300 if(fGuiSize[zone]) {
1301 size = 160 * fGuiSize[zone];
1302 }
1303 gtk_widget_set_size_request(slider, -1, size);
1304
1305 gtk_range_set_inverted (GTK_RANGE(slider), TRUE);
1306
1307 if (label && label[0]!=0) {
1308 openFrameBox(label);
1309 addWidget(label, slider);
1310 closeBox();
1311 } else {
1312 addWidget(label, slider);
1313 }
1314
1315 checkForTooltip(zone, slider);
1316 }
1317
1318 // -------------------------- Horizontal Slider -----------------------------------
1319
1320 void GTKUI::addHorizontalSlider(const char* label, float* zone, float init, float min, float max, float step)
1321 {
1322 if (isKnob(zone)) {
1323 addKnob(label, zone, init, min, max, step);
1324 return;
1325 }
1326 *zone = init;
1327 GtkObject* adj = gtk_adjustment_new(init, min, max, step, 10*step, 0);
1328
1329 uiAdjustment* c = new uiAdjustment(this, zone, GTK_ADJUSTMENT(adj));
1330
1331 gtk_signal_connect (GTK_OBJECT (adj), "value-changed", GTK_SIGNAL_FUNC (uiAdjustment::changed), (gpointer) c);
1332
1333 GtkWidget* slider = gtk_hscale_new (GTK_ADJUSTMENT(adj));
1334 gtk_scale_set_digits(GTK_SCALE(slider), precision(step));
1335 float size = 160;
1336 if(fGuiSize[zone]) {
1337 size = 160 * fGuiSize[zone];
1338 }
1339 gtk_widget_set_size_request(slider, size, -1);
1340
1341 if (label && label[0]!=0) {
1342 openFrameBox(label);
1343 addWidget(label, slider);
1344 closeBox();
1345 } else {
1346 addWidget(label, slider);
1347 }
1348
1349 checkForTooltip(zone, slider);
1350 }
1351
1352
1353 // ------------------------------ Num Entry -----------------------------------
1354
1355 void GTKUI::addNumEntry(const char* label, float* zone, float init, float min, float max, float step)
1356 {
1357 if (isKnob(zone)) {
1358 addKnob(label, zone, init, min, max, step);
1359 return;
1360 }
1361 *zone = init;
1362 GtkObject* adj = gtk_adjustment_new(init, min, max, step, 10*step, step);
1363
1364 uiAdjustment* c = new uiAdjustment(this, zone, GTK_ADJUSTMENT(adj));
1365
1366 gtk_signal_connect (GTK_OBJECT (adj), "value-changed", GTK_SIGNAL_FUNC (uiAdjustment::changed), (gpointer) c);
1367
1368 GtkWidget* spinner = gtk_spin_button_new (GTK_ADJUSTMENT(adj), 0.005, precision(step));
1369
1370 openFrameBox(label);
1371 addWidget(label, spinner);
1372 closeBox();
1373
1374 checkForTooltip(zone, spinner);
1375 }
1376
1377
1378 // ========================== passive widgets ===============================
1379
1380
1381 // ------------------------------ Progress Bar -----------------------------------
1382
1383 struct uiBargraph : public uiItem
1384 {
1385 GtkProgressBar* fProgressBar;
1386 float fMin;
1387 float fMax;
1388
1389 uiBargraph(GUI* ui, float* zone, GtkProgressBar* pbar, float lo, float hi)
1390 : uiItem(ui, zone), fProgressBar(pbar), fMin(lo), fMax(hi) {}
1391
1392 float scale(float v) { return (v-fMin)/(fMax-fMin); }
1393
1394 virtual void reflectZone()
1395 {
1396 float v = *fZone;
1397 fCache = v;
1398 gtk_progress_bar_set_fraction(fProgressBar, scale(v));
1399 }
1400 };
1401
1402
1403
1404 void GTKUI::addVerticalBargraph(const char* label, float* zone, float lo, float hi)
1405 {
1406 GtkWidget* pb = gtk_progress_bar_new();
1407 gtk_progress_bar_set_orientation(GTK_PROGRESS_BAR(pb), GTK_PROGRESS_BOTTOM_TO_TOP);
1408 gtk_widget_set_size_request(pb, 8, -1);
1409 new uiBargraph(this, zone, GTK_PROGRESS_BAR(pb), lo, hi);
1410 openFrameBox(label);
1411 addWidget(label, pb);
1412 closeBox();
1413
1414 checkForTooltip(zone, pb);
1415 }
1416
1417
1418 void GTKUI::addHorizontalBargraph(const char* label, float* zone, float lo, float hi)
1419 {
1420 GtkWidget* pb = gtk_progress_bar_new();
1421 gtk_progress_bar_set_orientation(GTK_PROGRESS_BAR(pb), GTK_PROGRESS_LEFT_TO_RIGHT);
1422 gtk_widget_set_size_request(pb, -1, 8);
1423 new uiBargraph(this, zone, GTK_PROGRESS_BAR(pb), lo, hi);
1424 openFrameBox(label);
1425 addWidget(label, pb);
1426 closeBox();
1427
1428 checkForTooltip(zone, pb);
1429 }
1430
1431
1432 // ------------------------------ Num Display -----------------------------------
1433
1434 struct uiNumDisplay : public uiItem
1435 {
1436 GtkLabel* fLabel;
1437 int fPrecision;
1438
1439 uiNumDisplay(GUI* ui, float* zone, GtkLabel* label, int precision)
1440 : uiItem(ui, zone), fLabel(label), fPrecision(precision) {}
1441
1442 virtual void reflectZone()
1443 {
1444 float v = *fZone;
1445 fCache = v;
1446 char s[64];
1447 if (fPrecision <= 0) {
1448 snprintf(s, 63, "%d", int(v));
1449 } else if (fPrecision>3) {
1450 snprintf(s, 63, "%f", v);
1451 } else {
1452 const char* format[] = {"%.1f", "%.2f", "%.3f"};
1453 snprintf(s, 63, format[fPrecision-1], v);
1454 }
1455 gtk_label_set_text(fLabel, s);
1456 }
1457 };
1458
1459
1460 void GTKUI::addNumDisplay(const char* label, float* zone, int precision )
1461 {
1462 GtkWidget* lw = gtk_label_new("");
1463 new uiNumDisplay(this, zone, GTK_LABEL(lw), precision);
1464 openFrameBox(label);
1465 addWidget(label, lw);
1466 closeBox();
1467
1468 checkForTooltip(zone, lw);
1469 }
1470
1471
1472 // ------------------------------ Text Display -----------------------------------
1473
1474 struct uiTextDisplay : public uiItem
1475 {
1476 GtkLabel* fLabel;
1477 const char** fNames;
1478 float fMin;
1479 float fMax;
1480 int fNum;
1481
1482
1483 uiTextDisplay (GUI* ui, float* zone, GtkLabel* label, const char* names[], float lo, float hi)
1484 : uiItem(ui, zone), fLabel(label), fNames(names), fMin(lo), fMax(hi)
1485 {
1486 fNum = 0;
1487 while (fNames[fNum] != 0) fNum++;
1488 }
1489
1490 virtual void reflectZone()
1491 {
1492 float v = *fZone;
1493 fCache = v;
1494
1495 int idx = int(fNum*(v-fMin)/(fMax-fMin));
1496
1497 if (idx < 0) idx = 0;
1498 else if (idx >= fNum) idx = fNum-1;
1499
1500 gtk_label_set_text(fLabel, fNames[idx]);
1501 }
1502 };
1503
1504
1505 void GTKUI::addTextDisplay(const char* label, float* zone, const char* names[], float lo, float hi )
1506 {
1507 GtkWidget* lw = gtk_label_new("");
1508 new uiTextDisplay (this, zone, GTK_LABEL(lw), names, lo, hi);
1509 openFrameBox(label);
1510 addWidget(label, lw);
1511 closeBox();
1512
1513 checkForTooltip(zone, lw);
1514 }
1515
1516
1517
1518 void GTKUI::show()
1519 {
1520 assert(fTop == 0);
1521 gtk_widget_show (fBox[0]);
1522 gtk_widget_show (fWindow);
1523 }
1524
1525
1526 /**
1527 * Update all user items reflecting zone z
1528 */
1529
1530 static gboolean callUpdateAllGuis(gpointer)
1531 {
1532 GUI::updateAllGuis();
1533 return TRUE;
1534 }
1535
1536
1537 void GTKUI::run()
1538 {
1539 assert(fTop == 0);
1540 gtk_widget_show (fBox[0]);
1541 gtk_widget_show (fWindow);
1542 gtk_timeout_add(40, callUpdateAllGuis, 0);
1543 gtk_main ();
1544 stop();
1545 }
1546
1547
1548
1549 #endif
1550