Next Previous Contents

7. Range Widgets

The category of range widgets includes the ubiquitous scrollbar widget and the less common scale widget. Though these two types of widgets are typically used for vastly different purposes, they are quite similar in function and implementation. Range widgets allow the user to visually manipulate a value within a specified range (hence the name).

All range widgets share a set of common graphic elements, each of which has its own X window and receives events. They all contain a "trough" and a "slider" (what is sometimes called a "thumbwheel" in other GUI environments). Dragging the slider with the pointer moves it back and forth within the trough, while clicking in the trough advances the slider towards the location of the click, either completely, or by a designated amount (called a "page"), depending on which button was used.

7.1 The Scale Widgets

Scale widgets are used to set an explicitly numeric parameter which has a visual correlate, and which the user might be expected to adjust primarily by sight. For example, the GtkColorSelection compound widget contains scale widgets which control the components of the colour being selected. Typically, the precise value of the number is less important here than its side-effects, and thus the user should be spared the effort of reaching for the keyboard.

Creating a Scale Widget

There are actually two types of scale widget: GtkHScale widgets, which are horizontal, and GtkVScale widgets, which

are vertical. (Most programmers seem to favour horizontal scale widgets). Since they work essentially the same way, there's no need to treat them separately here. The following functions, defined in <gtk/gtkvscale.h> and <gtk/gtkhscale.h>, create vertical and horizontal scale widgets, respectively:

GtkWidget* gtk_vscale_new( GtkAdjustment *adjustment );

GtkWidget* gtk_hscale_new( GtkAdjustment *adjustment );

adjustment can either be an adjustment which has already been created with gtk_adjustment_new(), or NULL, in which case, an anonymous GtkAdjustment is created with all of its values set to 0.0. If you're thoroughly confused by now, see The Adjustment Object below for an explanation of what exactly the adjustment argument does and how to create and manipulate it.

Functions, Signals, and Macros

Scale widgets can display their current value as a number beside the trough. The default behaviour is to show the value, but you can change this with this function:

void gtk_scale_set_draw_value( GtkScale *scale,
                               gint      draw_value );

As you might have guessed, draw_value is either TRUE or FALSE, with predictable consequences for either one.

The value displayed by a scale widget is rounded to one decimal point by default (as is the value field in its GtkAdjustment... but I digress). You can change this with:

void gtk_scale_set_digits( GtkScale *scale,
                           gint     digits);

where digits is the number of decimal places you want. You can set digits to anything you like, but no more than 13 decimal places will actually be drawn on screen. This probably isn't too horribly restrictive.

Finally, the value can be drawn in different positions relative to the trough:

void gtk_scale_set_value_pos( GtkScale        *scale,
                              GtkPositionType  pos );

If you've read the section on the notebook widget, then you know what the possible values of pos are. They are defined as type GtkPositionType and can take one of the following values:

If you position the value on the "side" of the trough (e.g. on the top or bottom of a horizontal scale widget), then it will follow the slider up and down the trough.

All the preceding functions are defined in <gtk/gtkscale.h>. The other signals and functions defined in the header files for the scale widgets are either not useful for anyone other than writers of scale widgets, or are the standard GTK+ type-casting macros and functions.

7.2 The Scrollbar Widgets

These are your standard, run-of-the-mill scrollbars. As with the scale widgets, there are separate types for horizontal and vertical scrollbars. There really isn't much to say about these. You create them with the following functions:

GtkWidget* gtk_hscrollbar_new( GtkAdjustment *adjustment );

GtkWidget* gtk_vscrollbar_new( GtkAdjustment *adjustment );

and that's about it (if you don't believe me, look in the header files!). Again, adjustment can either be a pointer to an existing GtkAdjustment, or NULL, in which case one will be created for you.

7.3 The Adjustment Object

As you might have noticed, there really isn't much to the various range widgets themselves from the programmer's point of view. Most of your program's interaction with these widgets will take place by way of the heretofore mysterious adjustment object.

Every range widget contains a pointer to a GtkAdjustment object. You'll usually create one of these in order to pass it to the gtk_*_new() function which creates a range widget, or some compound widget that uses range widgets, such as GtkScrolledWindow or GtkCList.

Aside from specifying some characteristics related to the range widget's appearance and behaviour, the GtkAdjustment you pass to this function becomes "attached" to the newly-created range widget and from that point on will always contain the numerical value corresponding to the position of the slider (unless, at some point in the future, you set a new adjustment for the range widget).

One adjustment object can be shared between many range widgets. Reusing the same adjustment object across several range widgets will cause them all to change when one of them is changed.

Creating a GtkAdjustment

You create an adjustment using:

GtkObject* gtk_adjustment_new( gfloat value,
                               gfloat lower,
                               gfloat upper,
                               gfloat step_increment,
                               gfloat page_increment,
                               gfloat page_size );

It may or may not be obvious by now that the values given to gtk_adjustment_new() are simply arbitrary floating-point values. The mapping between these values and the on-screen size of the range widget and its constituent parts is handled by the range widget. Thus, you're free to use whatever numbers are most meaningful to your program.

The value argument is the initial value you want to give to the adjustment. The lower argument specifies the lowest value which the adjustment can hold, or, in other words, the lowest value which the user can select using the range widget which uses this adjustment. The step_increment argument specifies the "smaller" of the two increments by which the user can change the value, while the page_increment is the "larger" one. Key and Mouse Bindings below describes the default key and mouse bindings for range widgets, and how they relate to these increments. The page_size argument is only relevant for scrollbars. Its most obvious effect is that it determines the size of the slider; however, you should set it based on the "size" of the visible area of whatever you're scrolling.

As an example, say you're writing a text editor. You might want to have the value of the vertical scrollbar beside the editing area correspond to the line number of the first visible line in the editing area. In that case, you might call gtk_adjustment_new() like this:

GtkObject *adj;
              
adj = gtk_adjustment_new (0, first_line, last_line, 1,
                          window_height - 2, window_height);

where window_height is the number of visible lines in the window.

Finally, with regard to the upper argument to gtk_adjustment_new, you'll notice that, since the value of the adjustment corresponds to the first visible line in the window, the maximum value in the adjustment is not actually last_line, but rather last_line - window_height (or, in more general terms, upper - page_height). This is a little confusing at first, but it makes sense if you think about it in terms of what the user expects to see in a scrolled window when the scrollbar's slider is moved all the way to the end of the trough.

Since the size of the slider on scale widgets is invariable, to avoid excessive confusion, it's a good idea to set the page_size to 0.0 for adjustments that are only going to be used for scale widgets.

Inside the GtkAdjustment object

OK, you say, that's nice, but how do I get at all these values, and, more importantly, how do I know when the user has moved the slider around? To answer these questions and more, let's start by taking a look at struct _GtkAdjustment itself:

struct _GtkAdjustment
{
  GtkData data;
  
  gfloat lower;
  gfloat upper;
  gfloat value;
  gfloat step_increment;
  gfloat page_increment;
  gfloat page_size;
};     

struct _GtkAdjustmentClass
{
  GtkDataClass parent_class;
  
  void (* changed)       (GtkAdjustment *adjustment);
  void (* value_changed) (GtkAdjustment *adjustment);
};

The first thing you should know is that there aren't any handy-dandy macros or accessor functions for getting the value out of a GtkAdjustment, so you'll have to (horror of horrors) do it like a real C programmer. Don't worry - the GTK_ADJUSTMENT (Object) macro does run-time type checking (as do all the GTK+ type-casting macros, actually). On the other hand, unless you're writing a new type of range widget, you probably don't want to set these fields directly. To set value, you can use:

void gtk_adjustment_set_value( GtkAdjustment *adjustment,
                               gfloat         value );

If you need to change the other fields, and you don't intend to do this very frequently, it's best to create a new GtkAdjustment and set it with gtk_range_set_adjustment(), as detailed in Common Functions, Signals, and Macros below.

You might have noticed that, while adjustments are not widgets, they are still a "subclass" of GtkObject. Therefore, they can (and do) emit signals of their own.

The various widgets that use the GtkAdjustment object will emit the "value_changed" signal on an adjustment whenever they change its value (see Update Policies below for more detail). This happens both when user input causes the slider to move on a range widget, as well as when the program explicitly changes the value with gtk_adjustment_set_value(). So, for example, if you have a scale widget, and you want to change the rotation of a picture whenever its value changes, you would create a callback like this:

void cb_rotate_picture (GtkAdjustment *adj, GtkWidget *picture)
{
  set_picture_rotation (picture, adj->value);
...

and connect it to the scale widget's adjustment like this:

gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
                    GTK_SIGNAL_FUNC (cb_rotate_picture), picture);

The "changed" signal is somewhat more elusive. It is never emitted directly due to the user's actions. Rather, programs or other widgets should emit it on a GtkAdjustment when they modify any of its fields directly. This will force any range widgets that use this adjustment to recalculate and redraw if necessary. This is useful if you have a number of range widgets using the same GtkAdjustment, and don't want to call gtk_range_set_adjustment() for all of them. It's also handy if you are going to be continuously changing these values, such as in our hypothetical text editor, where the upper field will have to change every time a new line is added, and you don't want the extra overhead of creating a new GtkAdjustment object every time.

7.4 Common Functions, Signals, and Macros

The GtkRange widget class is fairly complicated internally, but, like all the "base class" widgets, most of its complexity is only interesting if you want to hack on it. Also, almost all of the functions and signals it defines are only really used in writing derived widgets. There are, however, a few useful functions and concepts that are defined in gtkrange.h and are common to all range widgets.

Update Policies

The "update policy" of a range widget defines at what points during user interaction it will change the value field of its GtkAdjustment and emit the "value_changed" signal on this GtkAdjustment. The update policies, defined in <gtk/gtkenums.h> as the enum GtkUpdateType, are:

The update policy of a range widget can be set by casting it using the GTK_RANGE (Widget) macro and passing it to this function:

void gtk_range_set_update_policy( GtkRange      *range,
                                  GtkUpdateType  policy );

Getting and setting adjustments

Getting and setting the adjustment for a range widget "on the fly" is done, predictably, with:

GtkAdjustment* gtk_range_get_adjustment( GtkRange *range );

void gtk_range_set_adjustment( GtkRange      *range,
                               GtkAdjustment *adjustment );

gtk_range_get_adjustment() returns a pointer to the adjustment to which range is connected.

gtk_range_set_adjustment() does absolutely nothing if you pass it the adjustment that range is already using, regardless of whether you changed any of its fields or not. If you pass it a new GtkAdjustment, it will unreference the old one if it exists (possibly destroying it), connect the appropriate signals to the new one, and call the private function gtk_range_adjustment_changed(), which will (or at least, is supposed to...) recalculate the size and/or position of the slider and redraw if necessary. As mentioned above, if you wish to reuse the same GtkAdjustment, when you modify its values directly, you should emit the "changed" signal on it, like this:

gtk_signal_emit_by_name (GTK_OBJECT (adjustment), "changed");

7.5 Key and Mouse bindings

All of the GTK+ range widgets react to mouse clicks in more or less the same way. Clicking button 1 in the trough will cause its adjustment's page_increment to be added or subtracted from its value, and the slider to be moved accordingly. Clicking button 2 in the trough will jump the slider to the point at which the button was clicked. Clicking any button on a scrollbar's arrows will cause its adjustment's value to change step_increment at a time.

The key bindings, by contrast, are slightly different between horizontal and vertical range widgets, for obvious reasons. They are also not quite the same for scale widgets as they are for scrollbars, for somewhat less obvious reasons (possibly to avoid confusion between the keys for horizontal and vertical scrollbars in scrolled windows, where both operate on the same area).

Vertical Range Widgets

All vertical range widgets can be operated with the up and down arrow keys, as well as with the Page Up and Page Down keys. The arrows move the slider up and down by step_increment, while Page Up and Page Down move it by page_increment.

The user can also move the slider all the way to one end or the other of the trough using the keyboard. With the GtkVScale widget, this is done with the Home and End keys, whereas with the GtkVScrollbar widget, this is done by typing Control-Page Up and Control-Page Down.

Horizontal Range Widgets

The left and right arrow keys work as you might expect in these widgets, moving the slider back and forth by step_increment. The Home and End keys move the slider to the ends of the trough. For the GtkHScale widget, moving the slider by page_increment is accomplished with Control-Left and Control-Right, while for GtkHScrollbar, it's done with Control-Home and Control-End.

7.6 Example

This example is a somewhat modified version of the "range widgets" test from testgtk.c. It basically puts up a window with three range widgets all connected to the same adjustment, and a couple of controls for adjusting some of the parameters for scale widgets mentioned above, so you can see how they affect the way these widgets work for the user.

/* example-start rangewidgets rangewidgets.c */

#include <gtk/gtk.h>

GtkWidget *hscale, *vscale;

void cb_pos_menu_select (GtkWidget *item, GtkPositionType pos)
{
  /* set the value position on both scale widgets */
  gtk_scale_set_value_pos (GTK_SCALE (hscale), pos);
  gtk_scale_set_value_pos (GTK_SCALE (vscale), pos);
}

void cb_update_menu_select (GtkWidget *item, GtkUpdateType policy)
{
  /* set the update policy for both scale widgets */
  gtk_range_set_update_policy (GTK_RANGE (hscale), policy);
  gtk_range_set_update_policy (GTK_RANGE (vscale), policy);
}

void cb_digits_scale (GtkAdjustment *adj)
{
  /* set the number of decimal places to which adj->vaule is rounded
   */
  gtk_scale_set_digits (GTK_SCALE (hscale), (gint) adj->value);
  gtk_scale_set_digits (GTK_SCALE (vscale), (gint) adj->value);
}

void cb_page_size (GtkAdjustment *get, GtkAdjustment *set)
{
  /* set the page size and page increment size of the sample
     adjustment to the value specified by the "Page Size" scale */
  set->page_size = get->value;
  set->page_increment = get->value;
  /* now emit the "changed" signal to reconfigure all the widgets that
     are attached to this adjustment */
  gtk_signal_emit_by_name (GTK_OBJECT (set), "changed");
}

void cb_draw_value (GtkToggleButton *button)
{
  /* turn the value display on the scale widgets off or on depending
     on the state of the checkbutton */
  gtk_scale_set_draw_value (GTK_SCALE (hscale), button->active);
  gtk_scale_set_draw_value (GTK_SCALE (vscale), button->active);  
}

/* convenience functions */

GtkWidget *make_menu_item (gchar *name, GtkSignalFunc callback,
                           gpointer data)
{
  GtkWidget *item;
  
  item = gtk_menu_item_new_with_label (name);
  gtk_signal_connect (GTK_OBJECT (item), "activate",
                      callback, data);
  gtk_widget_show (item);

  return item;
}

void scale_set_default_values (GtkScale *scale)
{
  gtk_range_set_update_policy (GTK_RANGE (scale),
                               GTK_UPDATE_CONTINUOUS);
  gtk_scale_set_digits (scale, 1);
  gtk_scale_set_value_pos (scale, GTK_POS_TOP);
  gtk_scale_set_draw_value (scale, TRUE);
}

/* makes the sample window */

void create_range_controls (void)
{
  GtkWidget *window;
  GtkWidget *box1, *box2, *box3;
  GtkWidget *button;
  GtkWidget *scrollbar;
  GtkWidget *separator;
  GtkWidget *opt, *menu, *item;
  GtkWidget *label;
  GtkWidget *scale;
  GtkObject *adj1, *adj2;

  /* standard window-creating stuff */
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_signal_connect (GTK_OBJECT (window), "destroy",
                      GTK_SIGNAL_FUNC(gtk_main_quit),
                      NULL);
  gtk_window_set_title (GTK_WINDOW (window), "range controls");

  box1 = gtk_vbox_new (FALSE, 0);
  gtk_container_add (GTK_CONTAINER (window), box1);
  gtk_widget_show (box1);

  box2 = gtk_hbox_new (FALSE, 10);
  gtk_container_border_width (GTK_CONTAINER (box2), 10);
  gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0);
  gtk_widget_show (box2);

  /* value, lower, upper, step_increment, page_increment, page_size */
  /* note that the page_size value only makes a difference for
     scrollbar widgets, and the highest value you'll get is actually
     (upper - page_size). */
  adj1 = gtk_adjustment_new (0.0, 0.0, 101.0, 0.1, 1.0, 1.0);
  
  vscale = gtk_vscale_new (GTK_ADJUSTMENT (adj1));
  scale_set_default_values (GTK_SCALE (vscale));
  gtk_box_pack_start (GTK_BOX (box2), vscale, TRUE, TRUE, 0);
  gtk_widget_show (vscale);

  box3 = gtk_vbox_new (FALSE, 10);
  gtk_box_pack_start (GTK_BOX (box2), box3, TRUE, TRUE, 0);
  gtk_widget_show (box3);

  /* reuse the same adjustment */
  hscale = gtk_hscale_new (GTK_ADJUSTMENT (adj1));
  gtk_widget_set_usize (GTK_WIDGET (hscale), 200, 30);
  scale_set_default_values (GTK_SCALE (hscale));
  gtk_box_pack_start (GTK_BOX (box3), hscale, TRUE, TRUE, 0);
  gtk_widget_show (hscale);

  /* reuse the same adjustment again */
  scrollbar = gtk_hscrollbar_new (GTK_ADJUSTMENT (adj1));
  /* notice how this causes the scales to always be updated
     continuously when the scrollbar is moved */
  gtk_range_set_update_policy (GTK_RANGE (scrollbar), 
                               GTK_UPDATE_CONTINUOUS);
  gtk_box_pack_start (GTK_BOX (box3), scrollbar, TRUE, TRUE, 0);
  gtk_widget_show (scrollbar);

  box2 = gtk_hbox_new (FALSE, 10);
  gtk_container_border_width (GTK_CONTAINER (box2), 10);
  gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0);
  gtk_widget_show (box2);

  /* a checkbutton to control whether the value is displayed or not */
  button = gtk_check_button_new_with_label
    ("Display value on scale widgets");
  gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (button), TRUE);
  gtk_signal_connect (GTK_OBJECT (button), "toggled", GTK_SIGNAL_FUNC
                      (cb_draw_value), NULL);
  gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
  gtk_widget_show (button);
  
  box2 = gtk_hbox_new (FALSE, 10);
  gtk_container_border_width (GTK_CONTAINER (box2), 10);

  /* an option menu to change the position of the value */
  label = gtk_label_new ("Scale Value Position:");
  gtk_box_pack_start (GTK_BOX (box2), label, FALSE, FALSE, 0);
  gtk_widget_show (label);
  
  opt = gtk_option_menu_new();
  menu = gtk_menu_new();

  item = make_menu_item ("Top", GTK_SIGNAL_FUNC (cb_pos_menu_select),
                         GINT_TO_POINTER (GTK_POS_TOP));
  gtk_menu_append (GTK_MENU (menu), item);
  
  item = make_menu_item ("Bottom", GTK_SIGNAL_FUNC (cb_pos_menu_select), 
                         GINT_TO_POINTER (GTK_POS_BOTTOM));
  gtk_menu_append (GTK_MENU (menu), item);
  
  item = make_menu_item ("Left", GTK_SIGNAL_FUNC (cb_pos_menu_select),
                         GINT_TO_POINTER (GTK_POS_LEFT));
  gtk_menu_append (GTK_MENU (menu), item);
  
  item = make_menu_item ("Right", GTK_SIGNAL_FUNC (cb_pos_menu_select),
                         GINT_TO_POINTER (GTK_POS_RIGHT));
  gtk_menu_append (GTK_MENU (menu), item);
  
  gtk_option_menu_set_menu (GTK_OPTION_MENU (opt), menu);
  gtk_box_pack_start (GTK_BOX (box2), opt, TRUE, TRUE, 0);
  gtk_widget_show (opt);

  gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0);
  gtk_widget_show (box2);

  box2 = gtk_hbox_new (FALSE, 10);
  gtk_container_border_width (GTK_CONTAINER (box2), 10);

  /* yet another option menu, this time for the update policy of the
     scale widgets */
  label = gtk_label_new ("Scale Update Policy:");
  gtk_box_pack_start (GTK_BOX (box2), label, FALSE, FALSE, 0);
  gtk_widget_show (label);
  
  opt = gtk_option_menu_new();
  menu = gtk_menu_new();
  
  item = make_menu_item ("Continuous",
                         GTK_SIGNAL_FUNC (cb_update_menu_select),
                         GINT_TO_POINTER (GTK_UPDATE_CONTINUOUS));
  gtk_menu_append (GTK_MENU (menu), item);
  
  item = make_menu_item ("Discontinuous",
                         GTK_SIGNAL_FUNC (cb_update_menu_select),
                         GINT_TO_POINTER (GTK_UPDATE_DISCONTINUOUS));
  gtk_menu_append (GTK_MENU (menu), item);
  
  item = make_menu_item ("Delayed",
                         GTK_SIGNAL_FUNC (cb_update_menu_select),
                         GINT_TO_POINTER (GTK_UPDATE_DELAYED));
  gtk_menu_append (GTK_MENU (menu), item);
  
  gtk_option_menu_set_menu (GTK_OPTION_MENU (opt), menu);
  gtk_box_pack_start (GTK_BOX (box2), opt, TRUE, TRUE, 0);
  gtk_widget_show (opt);
  
  gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0);
  gtk_widget_show (box2);

  box2 = gtk_hbox_new (FALSE, 10);
  gtk_container_border_width (GTK_CONTAINER (box2), 10);
  
  /* a GtkHScale widget for adjusting the number of digits on the
     sample scales. */
  label = gtk_label_new ("Scale Digits:");
  gtk_box_pack_start (GTK_BOX (box2), label, FALSE, FALSE, 0);
  gtk_widget_show (label);

  adj2 = gtk_adjustment_new (1.0, 0.0, 5.0, 1.0, 1.0, 0.0);
  gtk_signal_connect (GTK_OBJECT (adj2), "value_changed",
                      GTK_SIGNAL_FUNC (cb_digits_scale), NULL);
  scale = gtk_hscale_new (GTK_ADJUSTMENT (adj2));
  gtk_scale_set_digits (GTK_SCALE (scale), 0);
  gtk_box_pack_start (GTK_BOX (box2), scale, TRUE, TRUE, 0);
  gtk_widget_show (scale);

  gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0);
  gtk_widget_show (box2);
  
  box2 = gtk_hbox_new (FALSE, 10);
  gtk_container_border_width (GTK_CONTAINER (box2), 10);
  
  /* And, one last GtkHScale widget for adjusting the page size of the
     scrollbar. */
  label = gtk_label_new ("Scrollbar Page Size:");
  gtk_box_pack_start (GTK_BOX (box2), label, FALSE, FALSE, 0);
  gtk_widget_show (label);

  adj2 = gtk_adjustment_new (1.0, 1.0, 101.0, 1.0, 1.0, 0.0);
  gtk_signal_connect (GTK_OBJECT (adj2), "value_changed",
                      GTK_SIGNAL_FUNC (cb_page_size), adj1);
  scale = gtk_hscale_new (GTK_ADJUSTMENT (adj2));
  gtk_scale_set_digits (GTK_SCALE (scale), 0);
  gtk_box_pack_start (GTK_BOX (box2), scale, TRUE, TRUE, 0);
  gtk_widget_show (scale);

  gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0);
  gtk_widget_show (box2);

  separator = gtk_hseparator_new ();
  gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0);
  gtk_widget_show (separator);

  box2 = gtk_vbox_new (FALSE, 10);
  gtk_container_border_width (GTK_CONTAINER (box2), 10);
  gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0);
  gtk_widget_show (box2);

  button = gtk_button_new_with_label ("Quit");
  gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                             GTK_SIGNAL_FUNC(gtk_main_quit),
                             NULL);
  gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
  gtk_widget_grab_default (button);
  gtk_widget_show (button);

  gtk_widget_show (window);
}

int main (int argc, char *argv[])
{
  gtk_init(&argc, &argv);

  create_range_controls();

  gtk_main();

  return 0;
}

/* example-end */


Next Previous Contents