X-Git-Url: https://scm.cri.ensmp.fr/git/Faustine.git/blobdiff_plain/c7f552fd8888da2f0d8cfb228fe0f28d3df3a12c..b4b6f2ea75b9f0f3ca918f5b84016610bf7a4d4f:/interpretor/faust-0.9.47mr3/architecture/gui/faustgtk.h diff --git a/interpretor/faust-0.9.47mr3/architecture/gui/faustgtk.h b/interpretor/faust-0.9.47mr3/architecture/gui/faustgtk.h deleted file mode 100644 index 68e6055..0000000 --- a/interpretor/faust-0.9.47mr3/architecture/gui/faustgtk.h +++ /dev/null @@ -1,1550 +0,0 @@ -#ifndef FAUST_GTKUI_H -#define FAUST_GTKUI_H - -#include "GUI.h" - -/****************************************************************************** -******************************************************************************* - - GRAPHIC USER INTERFACE - gtk interface - -******************************************************************************* -*******************************************************************************/ -#include -#include - -#include -#include -#include - -#include -#include -#include - -using namespace std; - -#define max(x,y) (((x)>(y)) ? (x) : (y)) -#define min(x,y) (((x)<(y)) ? (x) : (y)) - -#define stackSize 256 - -// Insertion modes - -#define kSingleMode 0 -#define kBoxMode 1 -#define kTabMode 2 - -//------------ calculate needed precision -static int precision(double n) -{ - if (n < 0.009999) return 3; - else if (n < 0.099999) return 2; - else if (n < 0.999999) return 1; - else return 0; -} - -namespace gtk_knob -{ - -class GtkKnob -{ -private: - double start_x, start_y, max_value; -public: - GtkRange parent; - int last_quadrant; - GtkKnob(); - ~GtkKnob(); - GtkWidget *gtk_knob_new_with_adjustment(GtkAdjustment *_adjustment); - -}; - -#define GTK_TYPE_KNOB (gtk_knob_get_type()) -#define GTK_KNOB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_KNOB, GtkKnob)) -#define GTK_IS_KNOB(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_KNOB)) -#define GTK_KNOB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_KNOB, GtkKnobClass)) -#define GTK_IS_KNOB_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_KNOB)) - -GtkKnob::GtkKnob() -// GtkKnob constructor -{ - -} - -GtkKnob::~GtkKnob() -{ - // Nothing specific to do... -} - -struct GtkKnobClass { - GtkRangeClass parent_class; - int knob_x; - int knob_y; - int knob_step; - int button_is; - -}; - -//------forward declaration -GType gtk_knob_get_type (); - -/**************************************************************** - ** calculate the knop pointer with dead zone - */ - -const double scale_zero = 20 * (M_PI/180); // defines "dead zone" for knobs - -static void knob_expose(GtkWidget *widget, int knob_x, int knob_y, GdkEventExpose *event, int arc_offset) -{ - /** check resize **/ - int grow; - if(widget->allocation.width > widget->allocation.height) { - grow = widget->allocation.height; - } else { - grow = widget->allocation.width; - } - knob_x = grow-4; - knob_y = grow-4; - /** get values for the knob **/ - GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(widget)); - int knobx = (widget->allocation.x+2 + (widget->allocation.width-4 - knob_x) * 0.5); - int knoby = (widget->allocation.y+2 + (widget->allocation.height-4 - knob_y) * 0.5); - int knobx1 = (widget->allocation.x+2 + (widget->allocation.width-4)* 0.5); - int knoby1 = (widget->allocation.y+2 + (widget->allocation.height-4) * 0.5); - double knobstate = (adj->value - adj->lower) / (adj->upper - adj->lower); - double angle = scale_zero + knobstate * 2 * (M_PI - scale_zero); - double knobstate1 = (0. - adj->lower) / (adj->upper - adj->lower); - double pointer_off = knob_x/6; - double radius = min(knob_x-pointer_off, knob_y-pointer_off) / 2; - double lengh_x = (knobx+radius+pointer_off/2) - radius * sin(angle); - double lengh_y = (knoby+radius+pointer_off/2) + radius * cos(angle); - double radius1 = min(knob_x, knob_y) / 2 ; - - /** get widget forground color convert to cairo **/ - GtkStyle *style = gtk_widget_get_style (widget); - double r = min(0.6,style->fg[gtk_widget_get_state(widget)].red/65535.0), - g = min(0.6,style->fg[gtk_widget_get_state(widget)].green/65535.0), - b = min(0.6,style->fg[gtk_widget_get_state(widget)].blue/65535.0); - - /** paint focus **/ - if (GTK_WIDGET_HAS_FOCUS(widget)== TRUE) { - gtk_paint_focus(widget->style, widget->window, GTK_STATE_NORMAL, NULL, widget, NULL, - knobx-2, knoby-2, knob_x+4, knob_y+4); - } - /** create clowing knobs with cairo **/ - cairo_t *cr = gdk_cairo_create(GDK_DRAWABLE(widget->window)); - GdkRegion *region; - region = gdk_region_rectangle (&widget->allocation); - gdk_region_intersect (region, event->region); - gdk_cairo_region (cr, region); - cairo_clip (cr); - - cairo_arc(cr,knobx1+arc_offset, knoby1+arc_offset, knob_x/2.1, 0, 2 * M_PI ); - cairo_pattern_t*pat = - 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 ); - if(adj->lower<0 && adj->value>0.) { - cairo_pattern_add_color_stop_rgb (pat, 0, r+0.4, g+0.4 + knobstate-knobstate1, b+0.4); - cairo_pattern_add_color_stop_rgb (pat, 0.7, r+0.15, g+0.15 + (knobstate-knobstate1)*0.5, b+0.15); - cairo_pattern_add_color_stop_rgb (pat, 1, r, g, b); - } else if(adj->lower<0 && adj->value<=0.) { - cairo_pattern_add_color_stop_rgb (pat, 0, r+0.4 +knobstate1- knobstate, g+0.4, b+0.4); - cairo_pattern_add_color_stop_rgb (pat, 0.7, r+0.15 +(knobstate1- knobstate)*0.5, g+0.15, b+0.15); - cairo_pattern_add_color_stop_rgb (pat, 1, r, g, b); - } else { - cairo_pattern_add_color_stop_rgb (pat, 0, r+0.4, g+0.4 +knobstate, b+0.4); - cairo_pattern_add_color_stop_rgb (pat, 0.7, r+0.15, g+0.15 + knobstate*0.5, b+0.15); - cairo_pattern_add_color_stop_rgb (pat, 1, r, g, b); - } - cairo_set_source (cr, pat); - cairo_fill_preserve (cr); - gdk_cairo_set_source_color(cr, gtk_widget_get_style (widget)->fg); - cairo_set_line_width(cr, 2.0); - cairo_stroke(cr); - - /** create a rotating pointer on the kob**/ - cairo_set_source_rgb(cr, 0.1, 0.1, 0.1); - cairo_set_line_width(cr,max(3, min(7, knob_x/15))); - cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); - cairo_set_line_join(cr, CAIRO_LINE_JOIN_BEVEL); - cairo_move_to(cr, knobx+radius1, knoby+radius1); - cairo_line_to(cr,lengh_x,lengh_y); - cairo_stroke(cr); - cairo_set_source_rgb(cr, 0.9, 0.9, 0.9); - cairo_set_line_width(cr,min(5, max(1,knob_x/30))); - cairo_move_to(cr, knobx+radius1, knoby+radius1); - cairo_line_to(cr,lengh_x,lengh_y); - cairo_stroke(cr); - cairo_pattern_destroy (pat); - gdk_region_destroy (region); - cairo_destroy(cr); -} - -/**************************************************************** - ** general expose events for all "knob" controllers - */ - -//----------- draw the Knob when moved -static gboolean gtk_knob_expose (GtkWidget *widget, GdkEventExpose *event) -{ - g_assert(GTK_IS_KNOB(widget)); - GtkKnobClass *klass = GTK_KNOB_CLASS(GTK_OBJECT_GET_CLASS(widget)); - knob_expose(widget, klass->knob_x, klass->knob_y, event, 0); - return TRUE; -} - -/**************************************************************** - ** set initial size for GdkDrawable per type - */ - -static void gtk_knob_size_request (GtkWidget *widget, GtkRequisition *requisition) -{ - g_assert(GTK_IS_KNOB(widget)); - GtkKnobClass *klass = GTK_KNOB_CLASS(GTK_OBJECT_GET_CLASS(widget)); - requisition->width = klass->knob_x; - requisition->height = klass->knob_y; -} - -/**************************************************************** - ** set value from key bindings - */ - -static void gtk_knob_set_value (GtkWidget *widget, int dir_down) -{ - g_assert(GTK_IS_KNOB(widget)); - - GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(widget)); - - int oldstep = (int)(0.5f + (adj->value - adj->lower) / adj->step_increment); - int step; - int nsteps = (int)(0.5f + (adj->upper - adj->lower) / adj->step_increment); - if (dir_down) - step = oldstep - 1; - else - step = oldstep + 1; - float value = adj->lower + step * double(adj->upper - adj->lower) / nsteps; - gtk_widget_grab_focus(widget); - gtk_range_set_value(GTK_RANGE(widget), value); -} - -/**************************************************************** - ** keyboard bindings - */ - -static gboolean gtk_knob_key_press (GtkWidget *widget, GdkEventKey *event) -{ - g_assert(GTK_IS_KNOB(widget)); - - GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(widget)); - switch (event->keyval) { - case GDK_Home: - gtk_range_set_value(GTK_RANGE(widget), adj->lower); - return TRUE; - case GDK_End: - gtk_range_set_value(GTK_RANGE(widget), adj->upper); - return TRUE; - case GDK_Up: - gtk_knob_set_value(widget, 0); - return TRUE; - case GDK_Right: - gtk_knob_set_value(widget, 0); - return TRUE; - case GDK_Down: - gtk_knob_set_value(widget, 1); - return TRUE; - case GDK_Left: - gtk_knob_set_value(widget, 1); - return TRUE; - } - - return FALSE; -} - -/**************************************************************** - ** alternative (radial) knob motion mode (ctrl + mouse pressed) - */ - -static void knob_pointer_event(GtkWidget *widget, gdouble x, gdouble y, int knob_x, int knob_y, - bool drag, int state) -{ - static double last_y = 2e20; - GtkKnob *knob = GTK_KNOB(widget); - GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(widget)); - double radius = min(knob_x, knob_y) / 2; - int knobx = (widget->allocation.width - knob_x) / 2; - int knoby = (widget->allocation.height - knob_y) / 2; - double posx = (knobx + radius) - x; // x axis right -> left - double posy = (knoby + radius) - y; // y axis top -> bottom - double value; - if (!drag) { - if (state & GDK_CONTROL_MASK) { - last_y = 2e20; - return; - } else { - last_y = posy; - } - } - if (last_y < 1e20) { // in drag started with Control Key - const double scaling = 0.005; - double scal = (state & GDK_SHIFT_MASK ? scaling*0.1 : scaling); - value = (last_y - posy) * scal; - last_y = posy; - gtk_range_set_value(GTK_RANGE(widget), adj->value - value * (adj->upper - adj->lower)); - return; - } - - double angle = atan2(-posx, posy) + M_PI; // clockwise, zero at 6 o'clock, 0 .. 2*M_PI - if (drag) { - // block "forbidden zone" and direct moves between quadrant 1 and 4 - int quadrant = 1 + int(angle/M_PI_2); - if (knob->last_quadrant == 1 && (quadrant == 3 || quadrant == 4)) { - angle = scale_zero; - } else if (knob->last_quadrant == 4 && (quadrant == 1 || quadrant == 2)) { - angle = 2*M_PI - scale_zero; - } else { - if (angle < scale_zero) { - angle = scale_zero; - } else if (angle > 2*M_PI - scale_zero) { - angle = 2*M_PI - scale_zero; - } - knob->last_quadrant = quadrant; - } - } else { - if (angle < scale_zero) { - angle = scale_zero; - } else if (angle > 2*M_PI - scale_zero) { - angle = 2*M_PI - scale_zero; - } - knob->last_quadrant = 0; - } - angle = (angle - scale_zero) / (2 * (M_PI-scale_zero)); // normalize to 0..1 - gtk_range_set_value(GTK_RANGE(widget), adj->lower + angle * (adj->upper - adj->lower)); -} - -/**************************************************************** - ** mouse button pressed set value - */ - -static gboolean gtk_knob_button_press (GtkWidget *widget, GdkEventButton *event) -{ - g_assert(GTK_IS_KNOB(widget)); - - GtkKnobClass *klass = GTK_KNOB_CLASS(GTK_OBJECT_GET_CLASS(widget)); - - - switch (event->button) { - case 1: // left button - gtk_widget_grab_focus(widget); - gtk_widget_grab_default (widget); - gtk_grab_add(widget); - klass->button_is = 1; - knob_pointer_event(widget, event->x, event->y, klass->knob_x, klass->knob_y, - false, event->state); - break; - case 2: //wheel - klass->button_is = 2; - break; - case 3: // right button - klass->button_is = 3; - break; - default: // do nothing - break; - } - return TRUE; -} - -/**************************************************************** - ** mouse button release - */ - -static gboolean gtk_knob_button_release (GtkWidget *widget, GdkEventButton *event) -{ - g_assert(GTK_IS_KNOB(widget)); - GTK_KNOB_CLASS(GTK_OBJECT_GET_CLASS(widget))->button_is = 0; - if (GTK_WIDGET_HAS_GRAB(widget)) - gtk_grab_remove(widget); - return FALSE; -} - -/**************************************************************** - ** set the value from mouse movement - */ - -static gboolean gtk_knob_pointer_motion (GtkWidget *widget, GdkEventMotion *event) -{ - g_assert(GTK_IS_KNOB(widget)); - GtkKnobClass *klass = GTK_KNOB_CLASS(GTK_OBJECT_GET_CLASS(widget)); - - gdk_event_request_motions (event); - - if (GTK_WIDGET_HAS_GRAB(widget)) { - knob_pointer_event(widget, event->x, event->y, klass->knob_x, klass->knob_y, - true, event->state); - } - return FALSE; -} - -/**************************************************************** - ** set value from mouseweel - */ - -static gboolean gtk_knob_scroll (GtkWidget *widget, GdkEventScroll *event) -{ - usleep(5000); - gtk_knob_set_value(widget, event->direction); - return FALSE; -} - -/**************************************************************** - ** init the GtkKnobClass - */ - -static void gtk_knob_class_init (GtkKnobClass *klass) -{ - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); - - /** set here the sizes and steps for the used knob **/ -//--------- small knob size and steps - - klass->knob_x = 30; - klass->knob_y = 30; - klass->knob_step = 86; - -//--------- event button - klass->button_is = 0; - -//--------- connect the events with funktions - widget_class->expose_event = gtk_knob_expose; - widget_class->size_request = gtk_knob_size_request; - widget_class->button_press_event = gtk_knob_button_press; - widget_class->button_release_event = gtk_knob_button_release; - widget_class->motion_notify_event = gtk_knob_pointer_motion; - widget_class->key_press_event = gtk_knob_key_press; - widget_class->scroll_event = gtk_knob_scroll; -} - -/**************************************************************** - ** init the Knob type/size - */ - -static void gtk_knob_init (GtkKnob *knob) -{ - GtkWidget *widget = GTK_WIDGET(knob); - GtkKnobClass *klass = GTK_KNOB_CLASS(GTK_OBJECT_GET_CLASS(widget)); - - GTK_WIDGET_SET_FLAGS (GTK_WIDGET(knob), GTK_CAN_FOCUS); - GTK_WIDGET_SET_FLAGS (GTK_WIDGET(knob), GTK_CAN_DEFAULT); - - widget->requisition.width = klass->knob_x; - widget->requisition.height = klass->knob_y; - -} - -/**************************************************************** - ** redraw when value changed - */ - -static gboolean gtk_knob_value_changed(gpointer obj) -{ - GtkWidget *widget = (GtkWidget *)obj; - gtk_widget_queue_draw(widget); - return FALSE; -} - -/**************************************************************** - ** create small knob - */ - -GtkWidget *GtkKnob::gtk_knob_new_with_adjustment(GtkAdjustment *_adjustment) -{ - GtkWidget *widget = GTK_WIDGET( g_object_new (GTK_TYPE_KNOB, NULL )); - GtkKnob *knob = GTK_KNOB(widget); - knob->last_quadrant = 0; - if (widget) { - gtk_range_set_adjustment(GTK_RANGE(widget), _adjustment); - g_signal_connect(GTK_OBJECT(widget), "value-changed", - G_CALLBACK(gtk_knob_value_changed), widget); - } - return widget; -} - -/**************************************************************** - ** get the Knob type - */ - -GType gtk_knob_get_type (void) -{ - static GType kn_type = 0; - if (!kn_type) { - static const GTypeInfo kn_info = { - sizeof(GtkKnobClass), NULL, NULL, (GClassInitFunc)gtk_knob_class_init, NULL, NULL, sizeof (GtkKnob), 0, (GInstanceInitFunc)gtk_knob_init - }; - kn_type = g_type_register_static(GTK_TYPE_RANGE, "GtkKnob", &kn_info, (GTypeFlags)0); - } - return kn_type; -} -}/* end of gtk_knob namespace */ - -gtk_knob::GtkKnob myGtkKnob; - -/** - * rmWhiteSpaces(): Remove the leading and trailing white spaces of a string - * (but not those in the middle of the string) - */ -static string rmWhiteSpaces(const string& s) -{ - size_t i = s.find_first_not_of(" \t"); - size_t j = s.find_last_not_of(" \t"); - - if (i != string::npos & j != string::npos) { - return s.substr(i, 1+j-i); - } else { - return ""; - } -} - - -/** - * Extracts metdata from a label : 'vol [unit: dB]' -> 'vol' + metadata - */ -static void extractMetadata(const string& fulllabel, string& label, map& metadata) -{ - enum {kLabel, kEscape1, kEscape2, kEscape3, kKey, kValue}; - int state = kLabel; int deep = 0; - string key, value; - - for (unsigned int i=0; i < fulllabel.size(); i++) { - char c = fulllabel[i]; - switch (state) { - case kLabel : - assert (deep == 0); - switch (c) { - case '\\' : state = kEscape1; break; - case '[' : state = kKey; deep++; break; - default : label += c; - } - break; - - case kEscape1 : - label += c; - state = kLabel; - break; - - case kEscape2 : - key += c; - state = kKey; - break; - - case kEscape3 : - value += c; - state = kValue; - break; - - case kKey : - assert (deep > 0); - switch (c) { - case '\\' : state = kEscape2; - break; - - case '[' : deep++; - key += c; - break; - - case ':' : if (deep == 1) { - state = kValue; - } else { - key += c; - } - break; - case ']' : deep--; - if (deep < 1) { - metadata[rmWhiteSpaces(key)] = ""; - state = kLabel; - key=""; - value=""; - } else { - key += c; - } - break; - default : key += c; - } - break; - - case kValue : - assert (deep > 0); - switch (c) { - case '\\' : state = kEscape3; - break; - - case '[' : deep++; - value += c; - break; - - case ']' : deep--; - if (deep < 1) { - metadata[rmWhiteSpaces(key)]=rmWhiteSpaces(value); - state = kLabel; - key=""; - value=""; - } else { - value += c; - } - break; - default : value += c; - } - break; - - default : - cerr << "ERROR unrecognized state " << state << endl; - } - } - label = rmWhiteSpaces(label); -} - - -class GTKUI : public GUI -{ - private : - static bool fInitialized; - static map fGuiSize; // map widget zone with widget size coef - static map fTooltip; // map widget zone with tooltip strings - static set fKnobSet; // set of widget zone to be knobs - string gGroupTooltip; - - bool isKnob(float* zone){return fKnobSet.count(zone) > 0;} - - protected : - GtkWidget* fWindow; - int fTop; - GtkWidget* fBox[stackSize]; - int fMode[stackSize]; - bool fStopped; - - GtkWidget* addWidget(const char* label, GtkWidget* w); - virtual void pushBox(int mode, GtkWidget* w); - - - public : - static const gboolean expand = TRUE; - static const gboolean fill = TRUE; - static const gboolean homogene = FALSE; - - GTKUI(char * name, int* pargc, char*** pargv); - - // -- Labels and metadata - - virtual void declare (float* zone, const char* key, const char* value); - virtual int checkLabelOptions (GtkWidget* widget, const string& fullLabel, string& simplifiedLabel); - virtual void checkForTooltip (float* zone, GtkWidget* widget); - - // -- layout groups - - virtual void openFrameBox(const char* label); - virtual void openTabBox(const char* label = ""); - virtual void openHorizontalBox(const char* label = ""); - virtual void openVerticalBox(const char* label = ""); - - // -- extra widget's layouts - - virtual void openDialogBox(const char* label, float* zone); - virtual void openEventBox(const char* label = ""); - virtual void openHandleBox(const char* label = ""); - virtual void openExpanderBox(const char* label, float* zone); - - virtual void closeBox(); - virtual void adjustStack(int n); - - // -- active widgets - - virtual void addButton(const char* label, float* zone); - virtual void addToggleButton(const char* label, float* zone); - virtual void addCheckButton(const char* label, float* zone); - virtual void addVerticalSlider(const char* label, float* zone, float init, float min, float max, float step); - virtual void addHorizontalSlider(const char* label, float* zone, float init, float min, float max, float step); - virtual void addKnob(const char* label, float* zone, float init, float min, float max, float step); - virtual void addNumEntry(const char* label, float* zone, float init, float min, float max, float step); - - // -- passive display widgets - - virtual void addNumDisplay(const char* label, float* zone, int precision); - virtual void addTextDisplay(const char* label, float* zone, const char* names[], float min, float max); - virtual void addHorizontalBargraph(const char* label, float* zone, float min, float max); - virtual void addVerticalBargraph(const char* label, float* zone, float min, float max); - - virtual void show(); - virtual void run(); - -}; - - - -/****************************************************************************** -******************************************************************************* - - GRAPHIC USER INTERFACE (v2) - gtk implementation - -******************************************************************************* -*******************************************************************************/ - -// global static fields - -bool GTKUI::fInitialized = false; -map GTKUI::fGuiSize; -map GTKUI::fTooltip; -set GTKUI::fKnobSet; // set of widget zone to be knobs - -/** -* Format tooltip string by replacing some white spaces by -* return characters so that line width doesn't exceed n. -* Limitation : long words exceeding n are not cut -*/ -static string formatTooltip(int n, const string& tt) -{ - string ss = tt; // ss string we are going to format - int lws = 0; // last white space encountered - int lri = 0; // last return inserted - for (int i=0; i= n) && (lws > lri)) { - // insert return here - ss[lws] = '\n'; - lri = lws; - } - } - cout << ss; - return ss; -} - - - -static gint delete_event( GtkWidget *widget, GdkEvent *event, gpointer data ) -{ - return FALSE; -} - -static void destroy_event( GtkWidget *widget, gpointer data ) -{ - gtk_main_quit (); -} - - -GTKUI::GTKUI(char * name, int* pargc, char*** pargv) -{ - if (!fInitialized) { - gtk_init(pargc, pargv); - fInitialized = true; - } - - fWindow = gtk_window_new (GTK_WINDOW_TOPLEVEL); - //gtk_container_set_border_width (GTK_CONTAINER (fWindow), 10); - gtk_window_set_title (GTK_WINDOW (fWindow), name); - gtk_signal_connect (GTK_OBJECT (fWindow), "delete_event", GTK_SIGNAL_FUNC (delete_event), NULL); - gtk_signal_connect (GTK_OBJECT (fWindow), "destroy", GTK_SIGNAL_FUNC (destroy_event), NULL); - - fTop = 0; - fBox[fTop] = gtk_vbox_new (homogene, 4); - fMode[fTop] = kBoxMode; - gtk_container_add (GTK_CONTAINER (fWindow), fBox[fTop]); - fStopped = false; -} - -// empilement des boites - -void GTKUI::pushBox(int mode, GtkWidget* w) -{ - ++fTop; - assert(fTop < stackSize); - fMode[fTop] = mode; - fBox[fTop] = w; -} - - -/** - * Remove n levels from the stack S before the top level - * adjustStack(n): S -> S' with S' = S(0),S(n+1),S(n+2),... - */ -void GTKUI::adjustStack(int n) -{ - if (n > 0) { - assert(fTop >= n); - - fTop -= n; - fMode[fTop] = fMode[fTop+n]; - fBox[fTop] = fBox[fTop+n]; - } -} - -void GTKUI::closeBox() -{ - --fTop; - assert(fTop >= 0); -} - - -/** - * Analyses the widget zone metadata declarations and takes - * appropriate actions - */ -void GTKUI::declare(float* zone, const char* key, const char* value) -{ - if (zone == 0) { - // special zone 0 means group metadata - if (strcmp(key,"tooltip")==0) { - // only group tooltip are currently implemented - gGroupTooltip = formatTooltip(30, value); - } - } else { - if (strcmp(key,"size")==0) { - fGuiSize[zone]=atof(value); - } - else if (strcmp(key,"tooltip")==0) { - fTooltip[zone] = formatTooltip(30,value) ; - } - else if (strcmp(key,"style")==0) { - if (strcmp(value,"knob") == 0) { - fKnobSet.insert(zone); - } - } - } -} - - - -/** - * Analyses a full label and activates the relevant options. returns a simplified - * label (without options) and an amount of stack adjustement (in case additional - * containers were pushed on the stack). - */ - -int GTKUI::checkLabelOptions(GtkWidget* widget, const string& fullLabel, string& simplifiedLabel) -{ - map metadata; - extractMetadata(fullLabel, simplifiedLabel, metadata); - - if (metadata.count("tooltip")) { - gtk_tooltips_set_tip (gtk_tooltips_new (), widget, metadata["tooltip"].c_str(), NULL); - } - if (metadata["option"] == "detachable") { - openHandleBox(simplifiedLabel.c_str()); - return 1; - } - - //--------------------- - if (gGroupTooltip != string()) { - gtk_tooltips_set_tip (gtk_tooltips_new (), widget, gGroupTooltip.c_str(), NULL); - gGroupTooltip = string(); - } - - //---------------------- - - // no adjustement of the stack needed - return 0; -} - -/** - * Check if a tooltip is associated to a zone and add it to the corresponding widget - */ -void GTKUI::checkForTooltip(float* zone, GtkWidget* widget) -{ - if (fTooltip.count(zone)) { - gtk_tooltips_set_tip (gtk_tooltips_new (), widget, fTooltip[zone].c_str(), NULL); - } -} - - -// les differentes boites - -void GTKUI::openFrameBox(const char* label) -{ - GtkWidget * box = gtk_frame_new (label); - //gtk_container_set_border_width (GTK_CONTAINER (box), 10); - - pushBox(kSingleMode, addWidget(label, box)); -} - - -void GTKUI::openTabBox(const char* fullLabel) -{ - string label; - GtkWidget* widget = gtk_notebook_new(); - - int adjust = checkLabelOptions(widget, fullLabel, label); - - pushBox(kTabMode, addWidget(label.c_str(), widget)); - - // adjust stack because otherwise Handlebox will remain open - adjustStack(adjust); -} - - -void GTKUI::openHorizontalBox(const char* fullLabel) -{ - string label; - GtkWidget* box = gtk_hbox_new (homogene, 4); - int adjust = checkLabelOptions(box, fullLabel, label); - - gtk_container_set_border_width (GTK_CONTAINER (box), 10); - - if (fMode[fTop] != kTabMode && label[0] != 0) { - GtkWidget * frame = addWidget(label.c_str(), gtk_frame_new (label.c_str())); - gtk_container_add (GTK_CONTAINER(frame), box); - gtk_widget_show(box); - pushBox(kBoxMode, box); - } else { - pushBox(kBoxMode, addWidget(label.c_str(), box)); - } - - // adjust stack because otherwise Handlebox will remain open - adjustStack(adjust); -} - - -void GTKUI::openVerticalBox(const char* fullLabel) -{ - string label; - GtkWidget * box = gtk_vbox_new (homogene, 4); - int adjust = checkLabelOptions(box, fullLabel, label); - - gtk_container_set_border_width (GTK_CONTAINER (box), 10); - - if (fMode[fTop] != kTabMode && label[0] != 0) { - GtkWidget * frame = addWidget(label.c_str(), gtk_frame_new (label.c_str())); - gtk_container_add (GTK_CONTAINER(frame), box); - gtk_widget_show(box); - pushBox(kBoxMode, box); - } else { - pushBox(kBoxMode, addWidget(label.c_str(), box)); - } - - // adjust stack because otherwise Handlebox will remain open - adjustStack(adjust); -} - - -void GTKUI::openHandleBox(const char* label) -{ - GtkWidget * box = gtk_hbox_new (homogene, 4); - gtk_container_set_border_width (GTK_CONTAINER (box), 2); - if (fMode[fTop] != kTabMode && label[0] != 0) - { - GtkWidget * frame = addWidget(label, gtk_handle_box_new ()); - gtk_container_add (GTK_CONTAINER(frame), box); - gtk_widget_show(box); - pushBox(kBoxMode, box); - } - else - { - pushBox(kBoxMode, addWidget(label, box)); - } -} - - -void GTKUI::openEventBox(const char* label) -{ - GtkWidget * box = gtk_hbox_new (homogene, 4); - gtk_container_set_border_width (GTK_CONTAINER (box), 2); - if (fMode[fTop] != kTabMode && label[0] != 0) - { - GtkWidget * frame = addWidget(label, gtk_event_box_new ()); - gtk_container_add (GTK_CONTAINER(frame), box); - gtk_widget_show(box); - pushBox(kBoxMode, box); - } - else - { - pushBox(kBoxMode, addWidget(label, box)); - } -} - - -struct uiExpanderBox : public uiItem -{ - GtkExpander* fButton; - uiExpanderBox(GUI* ui, float* zone, GtkExpander* b) : uiItem(ui, zone), fButton(b) {} - static void expanded (GtkWidget *widget, gpointer data) - { - float v = gtk_expander_get_expanded (GTK_EXPANDER(widget)); - if (v == 1.000000) - { - v = 0; - } - else v = 1; - ((uiItem*)data)->modifyZone(v); - } - - virtual void reflectZone() - { - float v = *fZone; - fCache = v; - gtk_expander_set_expanded(GTK_EXPANDER(fButton), v); - } -}; - -void GTKUI::openExpanderBox(const char* label, float* zone) -{ - *zone = 0.0; - GtkWidget * box = gtk_hbox_new (homogene, 4); - gtk_container_set_border_width (GTK_CONTAINER (box), 2); - if (fMode[fTop] != kTabMode && label[0] != 0) - { - GtkWidget * frame = addWidget(label, gtk_expander_new (label)); - gtk_container_add (GTK_CONTAINER(frame), box); - uiExpanderBox* c = new uiExpanderBox(this, zone, GTK_EXPANDER(frame)); - gtk_signal_connect (GTK_OBJECT (frame), "activate", GTK_SIGNAL_FUNC (uiExpanderBox::expanded), (gpointer)c); - gtk_widget_show(box); - pushBox(kBoxMode, box); - } - else - { - pushBox(kBoxMode, addWidget(label, box)); - } -} - - - -GtkWidget* GTKUI::addWidget(const char* label, GtkWidget* w) -{ - switch (fMode[fTop]) { - case kSingleMode : gtk_container_add (GTK_CONTAINER(fBox[fTop]), w); break; - case kBoxMode : gtk_box_pack_start (GTK_BOX(fBox[fTop]), w, expand, fill, 0); break; - case kTabMode : gtk_notebook_append_page (GTK_NOTEBOOK(fBox[fTop]), w, gtk_label_new(label)); break; - } - gtk_widget_show (w); - return w; -} - -// --------------------------- Press button --------------------------- - -struct uiButton : public uiItem -{ - GtkButton* fButton; - - uiButton (GUI* ui, float* zone, GtkButton* b) : uiItem(ui, zone), fButton(b) {} - - static void pressed( GtkWidget *widget, gpointer data ) - { - uiItem* c = (uiItem*) data; - c->modifyZone(1.0); - } - - static void released( GtkWidget *widget, gpointer data ) - { - uiItem* c = (uiItem*) data; - c->modifyZone(0.0); - } - - virtual void reflectZone() - { - float v = *fZone; - fCache = v; - if (v > 0.0) gtk_button_pressed(fButton); else gtk_button_released(fButton); - } -}; - -void GTKUI::addButton(const char* label, float* zone) -{ - *zone = 0.0; - GtkWidget* button = gtk_button_new_with_label (label); - addWidget(label, button); - - uiButton* c = new uiButton(this, zone, GTK_BUTTON(button)); - - gtk_signal_connect (GTK_OBJECT (button), "pressed", GTK_SIGNAL_FUNC (uiButton::pressed), (gpointer) c); - gtk_signal_connect (GTK_OBJECT (button), "released", GTK_SIGNAL_FUNC (uiButton::released), (gpointer) c); - - checkForTooltip(zone, button); -} - -// --------------------------- Toggle Buttons --------------------------- - -struct uiToggleButton : public uiItem -{ - GtkToggleButton* fButton; - - uiToggleButton(GUI* ui, float* zone, GtkToggleButton* b) : uiItem(ui, zone), fButton(b) {} - - static void toggled (GtkWidget *widget, gpointer data) - { - float v = (GTK_TOGGLE_BUTTON (widget)->active) ? 1.0 : 0.0; - ((uiItem*)data)->modifyZone(v); - } - - virtual void reflectZone() - { - float v = *fZone; - fCache = v; - gtk_toggle_button_set_active(fButton, v > 0.0); - } -}; - -void GTKUI::addToggleButton(const char* label, float* zone) -{ - *zone = 0.0; - GtkWidget* button = gtk_toggle_button_new_with_label (label); - addWidget(label, button); - - uiToggleButton* c = new uiToggleButton(this, zone, GTK_TOGGLE_BUTTON(button)); - gtk_signal_connect (GTK_OBJECT (button), "toggled", GTK_SIGNAL_FUNC (uiToggleButton::toggled), (gpointer) c); - - checkForTooltip(zone, button); -} - - - -void show_dialog(GtkWidget *widget, gpointer data) -{ - if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget)) == TRUE) - { - gtk_widget_show(GTK_WIDGET(data)); - gint root_x, root_y; - gtk_window_get_position (GTK_WINDOW(data), &root_x, &root_y); - root_y -= 120; - gtk_window_move(GTK_WINDOW(data), root_x, root_y); - } - else gtk_widget_hide(GTK_WIDGET(data)); -} - -static gboolean deleteevent( GtkWidget *widget, gpointer data ) -{ -return TRUE; -} - -void GTKUI::openDialogBox(const char* label, float* zone) -{ - // create toplevel window and set properties - GtkWidget * dialog = gtk_window_new (GTK_WINDOW_TOPLEVEL); - gtk_window_set_decorated(GTK_WINDOW(dialog), TRUE); - gtk_window_set_deletable(GTK_WINDOW(dialog), FALSE); - gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE); - gtk_window_set_gravity(GTK_WINDOW(dialog), GDK_GRAVITY_SOUTH); - gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(fWindow)); - gtk_window_set_position (GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE); - gtk_window_set_keep_below (GTK_WINDOW(dialog), FALSE); - gtk_window_set_title (GTK_WINDOW (dialog), label); - g_signal_connect (G_OBJECT (dialog), "delete_event", G_CALLBACK (deleteevent), NULL); - gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE); - - GtkWidget * box = gtk_hbox_new (homogene, 4); - - *zone = 0.0; - GtkWidget* button = gtk_toggle_button_new (); - gtk_signal_connect (GTK_OBJECT (button), "toggled", GTK_SIGNAL_FUNC (show_dialog), (gpointer) dialog); - - gtk_container_add (GTK_CONTAINER(fBox[fTop]), button); - gtk_container_add (GTK_CONTAINER(dialog), box); - gtk_widget_show (button); - gtk_widget_show(box); - pushBox(kBoxMode, box); -} - - - - -// --------------------------- Check Button --------------------------- - -struct uiCheckButton : public uiItem -{ - GtkToggleButton* fButton; - - uiCheckButton(GUI* ui, float* zone, GtkToggleButton* b) : uiItem(ui, zone), fButton(b) {} - - static void toggled (GtkWidget *widget, gpointer data) - { - float v = (GTK_TOGGLE_BUTTON (widget)->active) ? 1.0 : 0.0; - ((uiItem*)data)->modifyZone(v); - } - - virtual void reflectZone() - { - float v = *fZone; - fCache = v; - gtk_toggle_button_set_active(fButton, v > 0.0); - } -}; - -void GTKUI::addCheckButton(const char* label, float* zone) -{ - *zone = 0.0; - GtkWidget* button = gtk_check_button_new_with_label (label); - addWidget(label, button); - - uiCheckButton* c = new uiCheckButton(this, zone, GTK_TOGGLE_BUTTON(button)); - gtk_signal_connect (GTK_OBJECT (button), "toggled", GTK_SIGNAL_FUNC(uiCheckButton::toggled), (gpointer) c); - - checkForTooltip(zone, button); -} - - -// --------------------------- Adjustmenty based widgets --------------------------- - -struct uiAdjustment : public uiItem -{ - GtkAdjustment* fAdj; - - uiAdjustment(GUI* ui, float* zone, GtkAdjustment* adj) : uiItem(ui, zone), fAdj(adj) {} - - static void changed (GtkWidget *widget, gpointer data) - { - float v = GTK_ADJUSTMENT (widget)->value; - ((uiItem*)data)->modifyZone(v); - } - - virtual void reflectZone() - { - float v = *fZone; - fCache = v; - gtk_adjustment_set_value(fAdj, v); - } -}; - -// --------------------------- format knob value display --------------------------- - -struct uiValueDisplay : public uiItem -{ - GtkLabel* fLabel; - int fPrecision ; - - uiValueDisplay(GUI* ui, float* zone, GtkLabel* label, int precision) - : uiItem(ui, zone), fLabel(label), fPrecision(precision) {} - - virtual void reflectZone() - { - float v = *fZone; - fCache = v; - char s[64]; - if (fPrecision <= 0) - snprintf(s, 63, "%d", int(v)); - - else if (fPrecision > 3) - snprintf(s, 63, "%f", v); - - else if (fPrecision == 1) - { - const char* format[] = {"%.1f", "%.2f", "%.3f"}; - snprintf(s, 63, format[1-1], v); - } - else if (fPrecision == 2) - { - const char* format[] = {"%.1f", "%.2f", "%.3f"}; - snprintf(s, 63, format[2-1], v); - } - else - { - const char* format[] = {"%.1f", "%.2f", "%.3f"}; - snprintf(s, 63, format[3-1], v); - } - gtk_label_set_text(fLabel, s); - } -}; - -// ------------------------------- Knob ----------------------------------------- - -void GTKUI::addKnob(const char* label, float* zone, float init, float min, float max, float step) -{ - *zone = init; - GtkObject* adj = gtk_adjustment_new(init, min, max, step, 10*step, 0); - - uiAdjustment* c = new uiAdjustment(this, zone, GTK_ADJUSTMENT(adj)); - - gtk_signal_connect (GTK_OBJECT (adj), "value-changed", GTK_SIGNAL_FUNC (uiAdjustment::changed), (gpointer) c); - - GtkWidget* slider = gtk_vbox_new (FALSE, 0); - GtkWidget* fil = gtk_vbox_new (FALSE, 0); - GtkWidget* rei = gtk_vbox_new (FALSE, 0); - GtkWidget* re =myGtkKnob.gtk_knob_new_with_adjustment(GTK_ADJUSTMENT(adj)); - GtkWidget* lw = gtk_label_new(""); - new uiValueDisplay(this, zone, GTK_LABEL(lw),precision(step)); - gtk_container_add (GTK_CONTAINER(rei), re); - if(fGuiSize[zone]) { - float size = 30 * fGuiSize[zone]; - gtk_widget_set_size_request(rei, size, size ); - gtk_box_pack_start (GTK_BOX(slider), fil, TRUE, TRUE, 0); - gtk_box_pack_start (GTK_BOX(slider), rei, FALSE, FALSE, 0); - } else { - gtk_container_add (GTK_CONTAINER(slider), fil); - gtk_container_add (GTK_CONTAINER(slider), rei); - } - gtk_container_add (GTK_CONTAINER(slider), lw); - gtk_widget_show_all(slider); - - if (label && label[0]!=0) { - openFrameBox(label); - addWidget(label, slider); - closeBox(); - } else { - addWidget(label, slider); - } - - checkForTooltip(zone, slider); -} - -// -------------------------- Vertical Slider ----------------------------------- - -void GTKUI::addVerticalSlider(const char* label, float* zone, float init, float min, float max, float step) -{ - if (isKnob(zone)) { - addKnob(label, zone, init, min, max, step); - return; - } - *zone = init; - GtkObject* adj = gtk_adjustment_new(init, min, max, step, 10*step, 0); - - uiAdjustment* c = new uiAdjustment(this, zone, GTK_ADJUSTMENT(adj)); - - gtk_signal_connect (GTK_OBJECT (adj), "value-changed", GTK_SIGNAL_FUNC (uiAdjustment::changed), (gpointer) c); - - GtkWidget* slider = gtk_vscale_new (GTK_ADJUSTMENT(adj)); - gtk_scale_set_digits(GTK_SCALE(slider), precision(step)); - float size = 160; - if(fGuiSize[zone]) { - size = 160 * fGuiSize[zone]; - } - gtk_widget_set_size_request(slider, -1, size); - - gtk_range_set_inverted (GTK_RANGE(slider), TRUE); - - if (label && label[0]!=0) { - openFrameBox(label); - addWidget(label, slider); - closeBox(); - } else { - addWidget(label, slider); - } - - checkForTooltip(zone, slider); -} - -// -------------------------- Horizontal Slider ----------------------------------- - -void GTKUI::addHorizontalSlider(const char* label, float* zone, float init, float min, float max, float step) -{ - if (isKnob(zone)) { - addKnob(label, zone, init, min, max, step); - return; - } - *zone = init; - GtkObject* adj = gtk_adjustment_new(init, min, max, step, 10*step, 0); - - uiAdjustment* c = new uiAdjustment(this, zone, GTK_ADJUSTMENT(adj)); - - gtk_signal_connect (GTK_OBJECT (adj), "value-changed", GTK_SIGNAL_FUNC (uiAdjustment::changed), (gpointer) c); - - GtkWidget* slider = gtk_hscale_new (GTK_ADJUSTMENT(adj)); - gtk_scale_set_digits(GTK_SCALE(slider), precision(step)); - float size = 160; - if(fGuiSize[zone]) { - size = 160 * fGuiSize[zone]; - } - gtk_widget_set_size_request(slider, size, -1); - - if (label && label[0]!=0) { - openFrameBox(label); - addWidget(label, slider); - closeBox(); - } else { - addWidget(label, slider); - } - - checkForTooltip(zone, slider); -} - - -// ------------------------------ Num Entry ----------------------------------- - -void GTKUI::addNumEntry(const char* label, float* zone, float init, float min, float max, float step) -{ - if (isKnob(zone)) { - addKnob(label, zone, init, min, max, step); - return; - } - *zone = init; - GtkObject* adj = gtk_adjustment_new(init, min, max, step, 10*step, step); - - uiAdjustment* c = new uiAdjustment(this, zone, GTK_ADJUSTMENT(adj)); - - gtk_signal_connect (GTK_OBJECT (adj), "value-changed", GTK_SIGNAL_FUNC (uiAdjustment::changed), (gpointer) c); - - GtkWidget* spinner = gtk_spin_button_new (GTK_ADJUSTMENT(adj), 0.005, precision(step)); - - openFrameBox(label); - addWidget(label, spinner); - closeBox(); - - checkForTooltip(zone, spinner); -} - - -// ========================== passive widgets =============================== - - -// ------------------------------ Progress Bar ----------------------------------- - -struct uiBargraph : public uiItem -{ - GtkProgressBar* fProgressBar; - float fMin; - float fMax; - - uiBargraph(GUI* ui, float* zone, GtkProgressBar* pbar, float lo, float hi) - : uiItem(ui, zone), fProgressBar(pbar), fMin(lo), fMax(hi) {} - - float scale(float v) { return (v-fMin)/(fMax-fMin); } - - virtual void reflectZone() - { - float v = *fZone; - fCache = v; - gtk_progress_bar_set_fraction(fProgressBar, scale(v)); - } -}; - - - -void GTKUI::addVerticalBargraph(const char* label, float* zone, float lo, float hi) -{ - GtkWidget* pb = gtk_progress_bar_new(); - gtk_progress_bar_set_orientation(GTK_PROGRESS_BAR(pb), GTK_PROGRESS_BOTTOM_TO_TOP); - gtk_widget_set_size_request(pb, 8, -1); - new uiBargraph(this, zone, GTK_PROGRESS_BAR(pb), lo, hi); - openFrameBox(label); - addWidget(label, pb); - closeBox(); - - checkForTooltip(zone, pb); -} - - -void GTKUI::addHorizontalBargraph(const char* label, float* zone, float lo, float hi) -{ - GtkWidget* pb = gtk_progress_bar_new(); - gtk_progress_bar_set_orientation(GTK_PROGRESS_BAR(pb), GTK_PROGRESS_LEFT_TO_RIGHT); - gtk_widget_set_size_request(pb, -1, 8); - new uiBargraph(this, zone, GTK_PROGRESS_BAR(pb), lo, hi); - openFrameBox(label); - addWidget(label, pb); - closeBox(); - - checkForTooltip(zone, pb); -} - - -// ------------------------------ Num Display ----------------------------------- - -struct uiNumDisplay : public uiItem -{ - GtkLabel* fLabel; - int fPrecision; - - uiNumDisplay(GUI* ui, float* zone, GtkLabel* label, int precision) - : uiItem(ui, zone), fLabel(label), fPrecision(precision) {} - - virtual void reflectZone() - { - float v = *fZone; - fCache = v; - char s[64]; - if (fPrecision <= 0) { - snprintf(s, 63, "%d", int(v)); - } else if (fPrecision>3) { - snprintf(s, 63, "%f", v); - } else { - const char* format[] = {"%.1f", "%.2f", "%.3f"}; - snprintf(s, 63, format[fPrecision-1], v); - } - gtk_label_set_text(fLabel, s); - } -}; - - -void GTKUI::addNumDisplay(const char* label, float* zone, int precision ) -{ - GtkWidget* lw = gtk_label_new(""); - new uiNumDisplay(this, zone, GTK_LABEL(lw), precision); - openFrameBox(label); - addWidget(label, lw); - closeBox(); - - checkForTooltip(zone, lw); -} - - -// ------------------------------ Text Display ----------------------------------- - -struct uiTextDisplay : public uiItem -{ - GtkLabel* fLabel; - const char** fNames; - float fMin; - float fMax; - int fNum; - - - uiTextDisplay (GUI* ui, float* zone, GtkLabel* label, const char* names[], float lo, float hi) - : uiItem(ui, zone), fLabel(label), fNames(names), fMin(lo), fMax(hi) - { - fNum = 0; - while (fNames[fNum] != 0) fNum++; - } - - virtual void reflectZone() - { - float v = *fZone; - fCache = v; - - int idx = int(fNum*(v-fMin)/(fMax-fMin)); - - if (idx < 0) idx = 0; - else if (idx >= fNum) idx = fNum-1; - - gtk_label_set_text(fLabel, fNames[idx]); - } -}; - - -void GTKUI::addTextDisplay(const char* label, float* zone, const char* names[], float lo, float hi ) -{ - GtkWidget* lw = gtk_label_new(""); - new uiTextDisplay (this, zone, GTK_LABEL(lw), names, lo, hi); - openFrameBox(label); - addWidget(label, lw); - closeBox(); - - checkForTooltip(zone, lw); -} - - - -void GTKUI::show() -{ - assert(fTop == 0); - gtk_widget_show (fBox[0]); - gtk_widget_show (fWindow); -} - - -/** - * Update all user items reflecting zone z - */ - -static gboolean callUpdateAllGuis(gpointer) -{ - GUI::updateAllGuis(); - return TRUE; -} - - -void GTKUI::run() -{ - assert(fTop == 0); - gtk_widget_show (fBox[0]); - gtk_widget_show (fWindow); - gtk_timeout_add(40, callUpdateAllGuis, 0); - gtk_main (); - stop(); -} - - - -#endif -