Next Previous Contents

8. Miscellaneous Widgets

8.1 Labels

Labels are used a lot in GTK--; they're the main method of placing text in windows. Labels emit no signals, because they do not have an associated X window, and therefore cannot receive X events. If you need to catch signals or do clipping for labels, you can use an Event Box; see Event Boxes.

The constructors for Gtk::Label are

Gtk::Label(const nstring &label = 0);

The sole argument is the string you wish the label to display.

nstring is a special GTK-- version of the C++ string, which can also be a NULL pointer. It's internal to GTK--; all you need to know about it is that you can pass 0 as an nstring argument value whenever you would want to pass a NULL ptr - something you can't do with a standard C++ string. This is analogous to passing a NULL pointer to a C function taking a char* argument:

void a_c_func(char *arg);
...
a_c_func(0);

void a_cxx_func(nstring &arg);
...
a_cxx_func(0);

To change the label's text after creation, use the method:

void Gtk::Label::set_text(const string &str);

where &str is the new string.

The space needed for the new string will be automatically adjusted if needed. You can produce multi-line labels by putting line breaks in the label string.

To retrieve the current string, use:

string Gtk::Label::get_text();

The label text can be justified using:

void Gtk::Label::set_justify(GtkJustification jtype);

Values for jtype are:

The label widget is also capable of word-wrapping the text automatically. This can be activated using:

void Gtk::Label::set_line_wrap (bool wrap);

If you want your label underlined, then you can set a pattern on the label:

void Gtk::Label::set_pattern (const nstring &pattern);

The pattern argument indicates how the underlining should look. It consists of a string of underscore and space characters. An underscore indicates that the corresponding character in the label should be underlined. For example, the string

"__     __"
would underline the first two characters, and the eigth and ninth characters.

Below is a short example to illustrate these functions. This example makes use of the Frame widget to better demonstrate the label styles. It also gives some indication of what GTK-- developers do in their off-hours. (The Frame widget is explained in the Frame section.)

Source location: examples/label/label.cc

#include <gtk--/box.h>
#include <gtk--/frame.h>
#include <gtk--/label.h>
#include <gtk--/window.h>
#include <gtk--/main.h>

class AppWindow : Gtk::Window
{
public: 
  AppWindow();
  ~AppWindow();

  /* It's a good idea to do this for all application windows. */
  gint delete_event_impl (GdkEventAny*)
  {
    Gtk::Main::quit();
    return 0;
  }

};

AppWindow::AppWindow()
  : Gtk::Window (GTK_WINDOW_TOPLEVEL)
{
  Gtk::Box *hbox;
  Gtk::Box *vbox;
  Gtk::Frame* frame;
  Gtk::Label* label;

  /* Set some window properties */
  set_title("Label Example");
  set_border_width (5);

  /* Here we connect the "destroy" event to a signal handler */ 
  destroy.connect (Gtk::Main::quit.slot());

  vbox = manage( new Gtk::VBox (false, 5) );
  hbox = manage( new Gtk::HBox (false, 5) );
  add (*hbox);
  hbox->pack_start (*vbox, false, false);

  frame = manage( new Gtk::Frame ("Normal Label") );
  label = manage( new Gtk::Label ("This is a Normal label") );
  frame->add (*label);
  vbox->pack_start (*frame, false, false);
  
  frame = manage( new Gtk::Frame ("Multi-line Label") );
  label = manage( new Gtk::Label ("This is a Multi-line label.\nSecond line\n" \
                         "Third line"));
  frame->add (*label);
  vbox->pack_start (*frame, false, false);

  frame = manage( new Gtk::Frame ("Left Justified Label") );
  label = manage( new Gtk::Label ("This is a Left-Justified\n" \
                         "Multi-line label.\nThird      line"));
  label->set_justify (GTK_JUSTIFY_LEFT);
  frame->add (*label);
  vbox->pack_start (*frame, false, false);
  
  frame = manage( new Gtk::Frame ("Right Justified Label"));
  label = manage( new Gtk::Label ("This is a Right-Justified\nMulti-line label.\n" \
                         "Fourth line, (j/k)"));
  label->set_justify (GTK_JUSTIFY_RIGHT);
  frame->add (*label);
  vbox->pack_start (*frame, false, false);

  vbox = manage( new Gtk::VBox (false, 5) );
  hbox->pack_start (*vbox, false, false);

  frame = manage( new Gtk::Frame ("Line wrapped label"));
  label = manage( new Gtk::Label ("This is an example of a line-wrapped label.  It " \
                         "should not be taking up the entire             " /* big space to test spacing */\
                         "width allocated to it, but automatically " \
                         "wraps the words to fit.  " \
                         "The time has come, for all good men, to come to " \
                         "the aid of their party.  " \
                         "The sixth sheik's six sheep's sick.\n" \
                         "     It supports multiple paragraphs correctly, " \
                         "and  correctly   adds "\
                         "many          extra  spaces. "));
  label->set_line_wrap (true);
  frame->add (*label);
  vbox->pack_start (*frame, false, false);
  
  frame = manage( new Gtk::Frame("Filled, wrapped label") );
  label = manage( new Gtk::Label("This is an example of a line-wrapped, filled label.  " \
                         "It should be taking "\
                         "up the entire              width allocated to it.  " \
                         "Here is a seneance to prove "\
                         "my point.  Here is another sentence. "\
                         "Here comes the sun, do de do de do.\n"\
                         "    This is a new paragraph.\n"\
                         "    This is another newer, longer, better " \
                         "paragraph.  It is coming to an end, "\
                         "unfortunately.") );
  label->set_justify (GTK_JUSTIFY_FILL);
  label->set_line_wrap (true);
  frame->add (*label);
  vbox->pack_start (*frame, false, false);
  
  frame = manage( new Gtk::Frame("Underlined label") );
  label = manage( new Gtk::Label("This label is underlined!\n"
                         "This one is underlined in quite a funky fashion") );
  label->set_justify (GTK_JUSTIFY_LEFT);
  label->set_pattern ("_________________________ _ _________ _ ______     __ _______ ___");
  frame->add (*label);
  vbox->pack_start (*frame, false, false);
  
  show_all ();
}

AppWindow::~AppWindow() {}

int main( int   argc,
          char *argv[] )
{
  /* Initialise GTK */
  Gtk::Main kit(&argc, &argv);
    
  /* Create a new window */
  AppWindow app;
    
  /* Enter the event loop */
  Gtk::Main::run ();
    
  return(0);
}

8.2 Arrows

The Arrow widget draws an arrowhead, facing in a number of possible directions and having a number of possible styles. It can be very useful when placed on a button in many applications.

The constructor for Gtk::Arrow is

Gtk::Arrow( GtkArrowType   arrow_type,
           GtkShadowType  shadow_type );

and you can manipulate it with

void Gtk::Arrow::set(GtkArrowType   arrow_type,
                     GtkShadowType  shadow_type );

The arrow_type argument may take one of the following values:

These values indicate the direction in which the arrow will point. The shadow_type argument may take one of these values:

Here's a brief example illustrating the use of arrows:

Source location: examples/arrow/arrow.cc

#include <gtk--/arrow.h>
#include <gtk--/box.h>
#include <gtk--/button.h>
#include <gtk--/window.h>
#include <gtk--/main.h>

class ArrowButton : public Gtk::Button
{
public:
   ArrowButton(GtkArrowType,GtkShadowType);
   ~ArrowButton();
};

/* Create an Arrow widget with the specified parameters
 * and pack it into a button */
ArrowButton::ArrowButton(GtkArrowType arrow_type,GtkShadowType shadow_type)
  : Gtk::Button()
{
  Gtk::Arrow* arrow = manage (new Gtk::Arrow (arrow_type, shadow_type));
  add (*arrow);
}
  
ArrowButton::~ArrowButton() {};

/* We will derive our new application window from window */
class AppWindow : public Gtk::Window
{
public:
  AppWindow();
  ~AppWindow();

  /* It's a good idea to do this for all application windows. */
  gint delete_event_impl (GdkEventAny*) 
  {
    Gtk::Main::quit(); 
    return 0;
  }
 
};

AppWindow::AppWindow() 
  /* Create a new window */
  : Gtk::Window(GTK_WINDOW_TOPLEVEL)
{
  ArrowButton *button;
  Gtk::HBox    *box;

  set_title ("Arrow Buttons");

  /* Sets the border width of the window. */
  set_border_width (10);
  
  /* Create a box to hold the arrows/buttons */
  box=manage (new Gtk::HBox (false, 0));
  box->set_border_width (2);

  /* Pack and show all our widgets */
  button = manage (new ArrowButton (GTK_ARROW_LEFT, GTK_SHADOW_ETCHED_IN));
  box->pack_start (*button, false, false, 3);

  button = manage(new ArrowButton (GTK_ARROW_RIGHT, GTK_SHADOW_ETCHED_IN));
  box->pack_start(*button, false, false, 3);

  button = manage (new ArrowButton (GTK_ARROW_LEFT, GTK_SHADOW_ETCHED_OUT));
  box->pack_start (*button, false, false, 3);

  button = manage (new ArrowButton (GTK_ARROW_RIGHT, GTK_SHADOW_ETCHED_OUT));
  box->pack_start (*button, false, false, 3);
 
  add (*box);
  show_all ();
}

AppWindow::~AppWindow() {};

int main (int argc, char *argv[])
{
  Gtk::Main kit(argc, argv);
  AppWindow arrows;

  kit.run ();
  return 0;
}

8.3 The Tooltips Widget

These are the little text strings that pop up when you leave your pointer over a button or other widget for a few seconds. They are easy to use, so we will just explain them without giving a full example; if you want to see them in action, take a look at the eventbox.cc example.

Widgets that do not receieve events (widgets that do not have their own window) will not work with tooltips.

The constructor is extremely simple:

Gtk::Tooltips();

Once you have created a new tooltip, and the widget you wish to use it on, simply use this call to set it:

void Gtk::Tooltips::set_tip(const Gtk::Widget &widget,
                            const nstring &tip_text,
                            const nstring &tip_private );

The first argument is the widget you wish to have this tooltip pop up for, and the next is the text you want to display. The last argument is a text string that can be used as an identifier when using Gtk::TipsQuery to implement context sensitive help; it should, for the present, be set to 0.

Here's a short example:

Gtk::Tooltips tooltips;
Gtk::Widget button("button1");
.
.
.
tooltips.set_tip (button, "This is button 1", 0);

There are other calls that can be used with tooltips:

void Gtk::Tooltips::enable();

Enables a disabled set of tooltips.

void Gtk::Tooltips::disable();

Disables an enabled set of tooltips.

void Gtk::Tooltips::set_delay(gint delay);

Sets how many milliseconds you have to hold your pointer over the widget before the tooltip will pop up. The default is 500 milliseconds (half a second).

void gtk_tooltips::set_colors(const Gdk_Color &background,
                              const Gdk_Color &foreground );

Sets the foreground and background color of the tooltips.

8.4 Progress Bars

Progress bars are used to show the status of an operation. There are two ways to create a progress bar: with or without an associated Gtk::Adjustment. If you don't provide an adjustment, one will be created for you:

Gtk::ProgressBar();
Gtk::ProgressBar(GtkAdjustment &adjustment);

The second method has the advantage that we can use the adjustment object to specify our own range parameters for the progress bar.

The adjustment object of a progress bar can be changed using:

void Gtk::Progress::set_adjustment(GtkAdjustment &adjustment);

(Gtk::ProgressBar derives from Gtk::Progress).

After creating the progress bar, you can set its value using:

void Gtk::Progress::bar_update(gfloat percentage);

where percentage is a number, from 0 to 1, indicating what fraction of the bar should be filled up.

A progress bar may be set to one of four orientations using the method

void Gtk::ProgressBar::set_orientation( GtkProgressBarOrientation orientation );

where the orientation argument may take one of the following values to indicate the direction in which the progress bar should move:

When used as a measure of how far a process has progressed, the Gtk::ProgressBar can be set to display its value in either a continuous or discrete mode. In continuous mode, the progress bar is updated for each value. In discrete mode, the progress bar is updated in a number of discrete blocks. The number of blocks is also configurable.

The style of a progress bar can be set using the following method:

void Gtk::ProgressBar::set_bar_style( GtkProgressBarStyle  style );

The style parameter can take one of two values:

The number of discrete blocks can be set by calling

void Gtk::ProgressBar::set_discrete_blocks(guint blocks);

Besides indicating the amount of progress that has occured, the progress bar can also be used to indicate that there is some activity; this is done by placing the progress bar in activity mode. In this mode, the progress bar displays a small rectangle which moves back and forth. Activity mode is useful in situations where the progress of an operation cannot be calculated as a value range (e.g., receiving a file of unknown length).

Activity mode is selected by passing a non-zero value to the following method:

void Gtk::Progress::set_activity_mode(guint activity_mode);

The step size of the activity indicator, and the number of blocks, are set using the following methods:

void Gtk::ProgressBar::set_activity_step(guint step);
void Gtk::ProgressBar::set_activity_blocks(guint blocks);

When in continuous mode, the progress bar can also display a configurable text string within its trough, using the following method:

void Gtk::Progress::set_format_string(const string &);

The argument is similiar to one that would be used in a C printf statement. The following directives may be used within the string:

You can select whether or not to display this string using this method:

void Gtk::Progress::set_show_text(bool show_text);

The show_text argument is a boolean true/false value. The appearance of the text can be modified further using:

void Gtk::Progress::set_text_alignment(gfloat x_align,
                                       gfloat y_align);

The x_align and y_align arguments take a value between 0.0 and 1.0. Their value indicates the position of the text string within the trough. Values of 0.0 for both place the string in the top left hand corner; values of 0.5 (the default) centres the text, and values of 1.0 place the text in the lower right hand corner.

The current text setting of a progress object can be retrieved using the current or a specified adjustment value using the following two methods. They return the formatted string that would be displayed within the trough:

string Gtk::Progress::get_current_text();
string Gtk::Progress::get_text_from_value(gfloat value);

There is also another way to change the range and value of a progress object using the following method:

void Gtk::Progress::configure(gfloat value,
                              gfloat min,
                              gfloat max );

This function provides a convenient way to set the range and value of a progress object.

The remaining functions can be used to get and set the current value of a progess object in various types and formats:

void Gtk::Progress::set_percentage(gfloat percentage);
void Gtk::Progress::set_value(gfloat value);
gfloat Gtk::Progress::get_value();
gfloat Gtk::Progress::get_current_percentage();
gfloat Gtk::Progress::get_percentage_from_value(gfloat value);

The last of these takes a number falling between the minimum and maximum values of the progress object's adjustment, and returns the percentage to pass to set_percentage.

Progress bars are often used with idle timeouts, described in the section Timeouts, I/O and Idle Functions, to give the illusion of multitasking.

Here is an example using a progress bar, updated using timeouts. This code also shows you how to reset the progress bar.

Source location: examples/progressbar/progressbar.cc

#include <gtk--/progressbar.h>
#include <gtk--/table.h>
#include <gtk--/label.h>
#include <gtk--/button.h>
#include <gtk--/main.h>
#include <gtk--/window.h>

// Gtk-- version of the "progress bar" example from the gtk+ tutorial

class ProgressBar : public Gtk::Window
{
  bool m_pstat;
  
  Gtk::Button m_bReset, m_bCancel;
  Gtk::Label m_label;
  Gtk::ProgressBar m_pbar;
  Gtk::Table m_table;

  gint delete_event_impl(GdkEventAny*) { 
    Gtk::Main::quit(); return 0; 
  }
  
public:
  int progress();
  void progress_r() { m_pstat = false; }
  
  ProgressBar();
};

ProgressBar::ProgressBar() :
  m_pstat(true),
  m_bReset("Reset"), m_bCancel("Cancel"),
  m_label("Progress Bar Example"),
  m_table(3,2, true)
{
  set_border_width(10);
  add(m_table);

  // You don't need attach_default(), Gtk::Table::attach() already has
  // default args
  m_table.attach(m_label, 0,2,0,1);
  m_table.attach(m_pbar,  0,2,0,2);

  // Set the timeout to handle automatic updating of the progress bar
  Gtk::Main::timeout.connect(slot(this, &ProgressBar::progress),50);

  m_bReset.clicked.connect(slot(this, &ProgressBar::progress_r));
  m_table.attach(m_bReset, 0,1,2,3);
  
  m_bCancel.clicked.connect(Gtk::Main::quit.slot());

  m_table.attach(m_bCancel, 1,2,2,3);

  show_all();
}

int
ProgressBar::progress()
{
  gfloat pvalue = m_pbar.get_current_percentage();

  pvalue += 0.01;

  if ((pvalue >= 1.0) || (m_pstat == false)) {
    pvalue = 0.0;
    m_pstat = true;
  }

  m_pbar.set_percentage(pvalue);

  return true;
}


int main (int argc, char *argv[])
{
  Gtk::Main myapp(argc, argv);

  ProgressBar progressbar;

  myapp.run();
  return 0;
}

8.5 Dialogs

The Dialog widget is a window with a few widgets pre-packed into it for you. It consists of a VBox, a horizontal separator, and an HBox, stacked on top of each other. The HBox is at the bottom and comprises the action area, which generally contains buttons such as "OK", "Cancel", etc. The Dialog widget is useful for displaying pop-up messages and other similar tasks. There is only one constructor for it:

Gtk::Dialog();

You can pack a button in the action area by doing something like this:

Gtk::Dialog dialog;
Gtk::Button button("click me");
Gtk::HBox *dialogHBox = dialog.get_action_area();
dialogHBox->pack_start (button, true, true, 0);
button.show();

You can add to the VBox area by packing, for instance, a label into it:

Gtk::Label label("Dialogs are groovy");
Gtk::VBox *vbox = dialog.get_vbox();
vbox->pack_start (label, true, true, 0);
label.show();

If the simple functionality provided by the default vertical and horizontal boxes in the two areas does't give you enough control for your application, then you can simply pack another layout widget into the boxes provided. For example, you could pack a table into the vertical box.

8.6 Pixmaps

Pixmaps are data structures that contain pictures. These pictures can be used in various places; they're often used as icons on the X-Windows desktop, or as cursors. A pixmap which only has 2 colours is called a bitmap; there are a few additional routines for handling this common special case.

To understand pixmaps, it might help to understand a bit about how X-windows works. Under X-windows, applications do not need to be running on the same computer that is interacting with the user. Instead, the various applications, called clients, all communicate with a program which displays the graphics and handles the keyboard and mouse. This program which interacts directly with the user is called a display server or X server. Since the communication might take place over a network, it's important to keep some information within the X server. Pixmaps, for example, are stored in the memory of the X server. This means that once pixmap values are set, they don't need to be re-transmitted over the network; instead, a command is sent to the X server which says something like "display pixmap number XYZ here."

Note that even if you're using GTK-- on a non-X-windows platform, you still need to understand pixmaps; using them will keep your code portable, and they're used in many places in GTK--.

There are several ways to create pixmaps in GTK--. Most simply, you can use one of the following constructors:

Gtk::Pixmap(Gtk::Widget &w, const nstring &xpmfilename);
Gtk::Pixmap(Gtk::Widget &w, const gchar *const *data);

In both cases, the Gtk::Widget passed as the first argument should be the one in which the pixmap will eventually be displayed (e.g. a container, like a Gtk::Window or Gtk::Button); it's called the reference widget.

The first constructor builds a pixmap from an XPM file. XPM is a human-readable representation of a pixmap; it is widely used for storing pixmap data on X-windows systems. The xpmfilename parameter of the first constructor is a path to an XPM file.

The second constructor builds the pixmap from raw XPM- data; the data parameter of the second constructor is a pointer to the data. This data must not be freed until the reference widget is realised (i.e., displayed).

Another, slightly more complicated, way to construct a Gtk::Pixmap is to first build a Gdk_Pixmap structure using routines from GDK--. As with the first two constructors we showed you, you can do this either with in-memory data, or from data read from a file. Here is how to create a pixmap from data in memory:

Gdk_Bitmap( Gdk_Window &window,
            gchar     *data,
            gint       width,
            gint       height );

This routine is used to create a single-plane pixmap (2 colours). Each bit of the data represents whether that pixel is off or on. Width and height are in pixels. Gdk_Window is the current window; pixmap resources are meaningful only in the context of the screen where they are to be displayed.

Gdk_Pixmap ( GdkWindow *window,
             gchar     *data,
             gint       width,
             gint       height,
             gint       depth,
             const Gdk_Color &fg,
             const Gdk_Color &bg );

This is used to create a pixmap of the given depth (number of colours) from the bitmap data specified. fg and bg are the foreground and background colours to use.

Gdk_Pixmap( Gdk_Window   &window,
            Gdk_Bitmap   &mask,
            const Gdk_Color &transparent_color,
            const string &filename );

As before, filename is a path to an XPM file, which will be loaded into the new pixmap structure. The mask specifies which bits of the pixmap are to be opaque; all other bits are coloured using the colour specified by transparent_color. We'll give an example of this technique later on.

Gdk_Pixmap::create_from_xpm_d( const Gdk_Drawable &drawable,
                               Gdk_Bitmap &mask,
                               const Gdk_Color &transparent_color,
                               gchar *const *data );

This constructor is used to create a pixmap from XPM data incorporated into the program's code. An example:

/* XPM */
static const char *const xpm_data[] = {
"16 16 3 1",
"       c None",
".      c #000000000000",
"X      c #FFFFFFFFFFFF",
"                ",
"   ......       ",
"   .XXX.X.      ",
"   .XXX.XX.     ",
"   .XXX.XXX.    ",
"   .XXX.....    ",
"   .XXXXXXX.    ",
"   .XXXXXXX.    ",
"   .XXXXXXX.    ",
"   .XXXXXXX.    ",
"   .XXXXXXX.    ",
"   .XXXXXXX.    ",
"   .XXXXXXX.    ",
"   .........    ",
"                ",
"                "};

If you've got a keen eye, you'll recognise the famous 'document' icon (a dog-eared sheet of paper) in the above code.

When you're done using a pixmap and not likely to reuse it again soon, it is a good idea to delete it. Pixmaps should be considered a precious resource.

Once you've created a pixmap, you can display it as a GTK-- widget. The appropriate constructor is

Gtk::Pixmap(const Gdk_Pixmap &pixmap,
            const Gdk_Bitmap &mask);

The other pixmap widget calls are:

void  Gtk::Pixmap::set(const Gdk_Pixmap &newpixmap,
                      const Gdk_Bitmap &mask );

void  Gtk::Pixmap::get(Gdk_Pixmap &pixmap,
                      Gdk_Bitmap &mask);

Gtk::Pixmap::set is used to change the pixmap that the widget is currently managing; newpixmap is the pixmap created using GDK.

In the following example, we make a button which is marked with a pixmap:

Source location: examples/pixmap/pixmap.cc

#include <iostream>
#include <gtk--/window.h>
#include <gtk--/main.h>
#include <gtk--/pixmap.h>
#include <gtk--/button.h>

// Gtk-- version of the "pixmap" example from the gtk+ tutorial

/* XPM data of Open-File icon */
static const char * xpm_data[] = {
  "16 16 3 1",
  "       c None",
  ".      c #000000000000",
  "X      c #FFFFFFFFFFFF",
  "                ",
  "   ......       ",
  "   .XXX.X.      ",
  "   .XXX.XX.     ",
  "   .XXX.XXX.    ",
  "   .XXX.....    ",
  "   .XXXXXXX.    ",
  "   .XXXXXXX.    ",
  "   .XXXXXXX.    ",
  "   .XXXXXXX.    ",
  "   .XXXXXXX.    ",
  "   .XXXXXXX.    ",
  "   .XXXXXXX.    ",
  "   .........    ",
  "                ",
  "                "
};

class Pixmap : public Gtk::Window
{
  Gtk::Button m_button;
  Gtk::Pixmap m_pixmap;

  gint delete_event_impl(GdkEventAny*) { 
    Gtk::Main::quit(); return 0; 
  }

public:
  Pixmap();
  void button_clicked() { cout << "button clicked" << endl; }

};

Pixmap::Pixmap() :
  m_pixmap(xpm_data)
{
  add(m_button);
  m_button.add(m_pixmap);

  m_button.clicked.connect(slot(this, &Pixmap::button_clicked));
  
  show_all();
}


int main (int argc, char *argv[])
{
          
  Gtk::Main myapp(argc, argv);

  Pixmap pixmap;

  myapp.run();
  return 0;
}

We could, in the example above, have loaded the pixmap data from a file instead. For example, to load the pixmap from an XPM data file called icon0.xpm in the current directory, we would have created the pixmap using:

Gtk::Pixmap pixmap(window, "./icon0.xpm" );
window.add(pixmap);

One disadvantage of using pixmaps is that the displayed object is always rectangular, regardless of the image; sometimes you might want to use icons that have other shapes. This can be done using shaped windows. A shaped window is a pixmap where the background pixels are transparent. This way, when the background image is multi-coloured, we don't overwrite it with a rectangular, non-matching border around our icon. The following example displays a picture of a wheelbarrow on the screen:

Source location: examples/wheelbarrow/wheelbarrow.cc

#include <iostream>
#include <gtk--/window.h>
#include <gtk--/pixmap.h>
#include <gtk--/fixed.h>
#include <gtk--/main.h>

//From <iostream> 
using std::cout;
using std::endl;

// Gtk-- version of the "wheelbarrow" example from the gtk+ tutorial

// XPM
static char * WheelbarrowFull_xpm[] = {
  "48 48 64 1",
  "       c None",
  ".      c #DF7DCF3CC71B",
  "X      c #965875D669A6",
  "o      c #71C671C671C6",
  "O      c #A699A289A699",
  "+      c #965892489658",
  "@      c #8E38410330C2",
  "#      c #D75C7DF769A6",
  "$      c #F7DECF3CC71B",
  "%      c #96588A288E38",
  "&      c #A69992489E79",
  "*      c #8E3886178E38",
  "=      c #104008200820",
  "-      c #596510401040",
  ";      c #C71B30C230C2",
  ":      c #C71B9A699658",
  ">      c #618561856185",
  ",      c #20811C712081",
  "<      c #104000000000",
  "1      c #861720812081",
  "2      c #DF7D4D344103",
  "3      c #79E769A671C6",
  "4      c #861782078617",
  "5      c #41033CF34103",
  "6      c #000000000000",
  "7      c #49241C711040",
  "8      c #492445144924",
  "9      c #082008200820",
  "0      c #69A618611861",
  "q      c #B6DA71C65144",
  "w      c #410330C238E3",
  "e      c #CF3CBAEAB6DA",
  "r      c #71C6451430C2",
  "t      c #EFBEDB6CD75C",
  "y      c #28A208200820",
  "u      c #186110401040",
  "i      c #596528A21861",
  "p      c #71C661855965",
  "a      c #A69996589658",
  "s      c #30C228A230C2",
  "d      c #BEFBA289AEBA",
  "f      c #596545145144",
  "g      c #30C230C230C2",
  "h      c #8E3882078617",
  "j      c #208118612081",
  "k      c #38E30C300820",
  "l      c #30C2208128A2",
  "z      c #38E328A238E3",
  "x      c #514438E34924",
  "c      c #618555555965",
  "v      c #30C2208130C2",
  "b      c #38E328A230C2",
  "n      c #28A228A228A2",
  "m      c #41032CB228A2",
  "M      c #104010401040",
  "N      c #492438E34103",
  "B      c #28A2208128A2",
  "V      c #A699596538E3",
  "C      c #30C21C711040",
  "Z      c #30C218611040",
  "A      c #965865955965",
  "S      c #618534D32081",
  "D      c #38E31C711040",
  "F      c #082000000820",
  "                                                ",
  "          .XoO                                  ",
  "         +@#$%o&                                ",
  "         *=-;#::o+                              ",
  "           >,<12#:34                            ",
  "             45671#:X3                          ",
  "               +89<02qwo                        ",
  "e*                >,67;ro                       ",
  "ty>                 459@>+&&                    ",
  "$2u+                  ><ipas8*                  ",
  "%$;=*                *3:.Xa.dfg>                ",
  "Oh$;ya             *3d.a8j,Xe.d3g8+             ",
  " Oh$;ka          *3d$a8lz,,xxc:.e3g54           ",
  "  Oh$;kO       *pd$%svbzz,sxxxxfX..&wn>         ",
  "   Oh$@mO    *3dthwlsslszjzxxxxxxx3:td8M4       ",
  "    Oh$@g& *3d$XNlvvvlllm,mNwxxxxxxxfa.:,B*     ",
  "     Oh$@,Od.czlllllzlmmqV@V#V@fxxxxxxxf:%j5&   ",
  "      Oh$1hd5lllslllCCZrV#r#:#2AxxxxxxxxxcdwM*  ",
  "       OXq6c.%8vvvllZZiqqApA:mq:Xxcpcxxxxxfdc9* ",
  "        2r<6gde3bllZZrVi7S@SV77A::qApxxxxxxfdcM ",
  "        :,q-6MN.dfmZZrrSS:#riirDSAX@Af5xxxxxfevo",
  "         +A26jguXtAZZZC7iDiCCrVVii7Cmmmxxxxxx%3g",
  "          *#16jszN..3DZZZZrCVSA2rZrV7Dmmwxxxx&en",
  "           p2yFvzssXe:fCZZCiiD7iiZDiDSSZwwxx8e*>",
  "           OA1<jzxwwc:$d%NDZZZZCCCZCCZZCmxxfd.B ",
  "            3206Bwxxszx%et.eaAp77m77mmmf3&eeeg* ",
  "             @26MvzxNzvlbwfpdettttttttttt.c,n&  ",
  "             *;16=lsNwwNwgsvslbwwvccc3pcfu<o    ",
  "              p;<69BvwwsszslllbBlllllllu<5+     ",
  "              OS0y6FBlvvvzvzss,u=Blllj=54       ",
  "               c1-699Blvlllllu7k96MMMg4         ",
  "               *10y8n6FjvllllB<166668           ",
  "                S-kg+>666<M<996-y6n<8*          ",
  "                p71=4 m69996kD8Z-66698&&        ",
  "                &i0ycm6n4 ogk17,0<6666g         ",
  "                 N-k-<>     >=01-kuu666>        ",
  "                 ,6ky&      &46-10ul,66,        ",
  "                 Ou0<>       o66y<ulw<66&       ",
  "                  *kk5       >66By7=xu664       ",
  "                   <<M4      466lj<Mxu66o       ",
  "                   *>>       +66uv,zN666*       ",
  "                              566,xxj669        ",
  "                              4666FF666>        ",
  "                               >966666M         ",
  "                                oM6668+         ",
  "                                  *4            ",
  "                                                ",
  "                                                "
};

class Wheelbarrow : public Gtk::Window
{
  Gtk::Fixed m_fixed;
  Gtk::Pixmap m_pixmap;
  
  gint delete_event_impl(GdkEventAny*) { 
    Gtk::Main::quit(); return 0; 
  }

public:
  Wheelbarrow();
  void button_clicked() { cout << "button clicked" << endl; }

};

Wheelbarrow::Wheelbarrow() :
  Gtk::Window(GTK_WINDOW_POPUP),
  m_pixmap(WheelbarrowFull_xpm)
{
  Gdk_Pixmap pixmap;
  Gdk_Bitmap mask;

  m_fixed.set_usize(200, 200);
  m_fixed.put(m_pixmap, 0, 0);
  add(m_fixed);

  set_uposition(20, 400);
  
  show_all(); // first show everything : this is needed for the Pixmap
  // to be actually instantiated

  m_pixmap.get(pixmap, mask);

  shape_combine_mask(mask, 0, 0);
  show();
}


int main (int argc, char *argv[])
{
          
  Gtk::Main myapp(&argc, &argv);

  Wheelbarrow wheelbarrow;

  myapp.run();
  return 0;
}

To make the wheelbarrow image sensitive to mouse-clicks, we can attach a callback to the button_press_event signal to make it do something. The following code will make the picture sensitive to a mouse button being pressed, which makes the application terminate:

window.set_events(window.get_events() | GDK_BUTTON_PRESS_MASK );
window.button_press_event.connect( slot(&close_application) );

8.7 Rulers

Ruler widgets are used to indicate the location of the mouse pointer in a given window; they're often found in drawing programs. A window can have a vertical ruler spanning across the width and a horizontal ruler spanning down the height. A small triangular indicator on the ruler shows the exact location of the pointer relative to the ruler.

Horizontal and vertical rulers have one constructor each:

Gtk::HRuler();    // horizontal ruler
Gtk::VRuler();    // vertical ruler

Once a ruler is created, you can define the unit of measurement. Units of measure for rulers can be GTK_PIXELS, GTK_INCHES or GTK_CENTIMETERS. This is set using

void Gtk::Ruler::set_metric(GtkMetricType metric);

The default measurement is GTK_PIXELS (note that this avoids showing favouritism for other systems of measurement!).

ruler.set_metric(GTK_PIXELS);

Other important characteristics of a ruler are how to mark the units of scale, and where the position indicator is initially placed. These are set for a ruler using

void Gtk::Ruler::set_range(gfloat lower,
                           gfloat upper,
                           gfloat position,
                           gfloat max_size );

The arguments lower and upper define the extent of the ruler, and max_size is the largest possible number that will be displayed. position defines the initial position of the pointer indicator within the ruler.

A vertical ruler can be made to span an 800 pixel wide window using:

vruler.set_range(0, 800, 0, 800);

The markings displayed on the ruler will be from 0 to 800, with a number for every 100 pixels. If instead we wanted the ruler to range from 7 to 16, we could write

vruler.set_range(7, 16, 0, 20);

The indicator on the ruler is a small triangular mark that indicates the position of the pointer relative to the ruler. If you want the indicator to follow the mouse pointer, you need to connect the ruler's motion_notify_event() callback to the motion_notify_event signal. For example:

m_area.motion_notify_event.connect(m_hrule.motion_notify_event.slot());

This statement appears in the example below.

The following example creates a drawing area with a horizontal ruler above it and a vertical ruler to the left of it. The size of the drawing area is 600 pixels wide by 400 pixels high. The horizontal ruler spans from 7 to 13 with a mark every 100 pixels, while the vertical ruler spans from 0 to 400 with a mark every 100 pixels. Placement of the drawing area and the rulers is done using a table.

Source location: examples/rulers/rulers.cc

#include <gtk--/main.h>
#include <gtk--/table.h>
#include <gtk--/window.h>
#include <gtk--/ruler.h>
#include <gtk--/drawingarea.h>

// Gtk-- version of the "rulers" example from the gtk+ tutorial

class Rulers : public Gtk::Window
{
  Gtk::Table m_table;
  Gtk::DrawingArea m_area;
  Gtk::HRuler m_hrule;
  Gtk::VRuler m_vrule;
  
  gint delete_event_impl(GdkEventAny*) { 
    Gtk::Main::quit(); return 0; 
  }
  static const int XSIZE = 600,
    YSIZE = 400;
  
public:
  Rulers();
  
};

Rulers::Rulers() :
  m_table(3, 2, false)
{
  set_border_width(10);
  add(m_table);
  
  m_area.size(XSIZE, YSIZE);
  
  m_table.attach(m_area, 1,2,1,2,
                 GtkAttachOptions(GTK_EXPAND|GTK_FILL), GTK_FILL, 0, 0);
  
  m_area.set_events(GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK );
 
  // The horizontal ruler goes on top.  As the mouse moves across the
  // drawing area, a motion_notify_event is passed to the appropriate
  // event handler for the ruler.

  m_hrule.set_metric(GTK_PIXELS);
  m_hrule.set_range(7, 13, 0, 20 );
  m_area.motion_notify_event.connect(m_hrule.motion_notify_event.slot());
  m_table.attach(m_hrule, 1,2,0,1,
                 GTK_EXPAND|GTK_SHRINK|GTK_FILL, GTK_FILL,
                 0, 0);

  m_vrule.set_metric(GTK_PIXELS);
  m_vrule.set_range(0, YSIZE, 10, YSIZE );
  m_area.motion_notify_event.connect(m_vrule.motion_notify_event.slot());
  m_table.attach(m_vrule, 0, 1, 1, 2,
                 GTK_FILL, GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0 );

  show_all();
  
}

int main (int argc, char *argv[])
{
  Gtk::Main myapp(argc, argv);

  Rulers rulers;

  myapp.run();
  return 0;
}

8.8 Statusbars

Statusbars are used to display status messages. They keep a stack of the messages pushed onto them, so that popping the current message will redisplay the previous text message.

To make it easier for different parts of an application to use the same statusbar to display messages, the statusbar widget issues context identifiers which are used to identify different 'users'. These context identifiers are then be used to manipulate individual messages on the stack. The use of context identifiers is not optional; you have to obtain one to display messages on a statusbar.

Gtk::Statusbar has a very simple constructor:

Gtk::StatusBar();

A new context identifier is requested using a call to the following method. context_description is a short textual description of the context:

guint Gtk::StatusBar::get_context_id(const nstring &context_description);

There are three methods that can operate on statusbars:

guint Gtk::StatusBar::push(guint context_id,
                           const nstring &text );

void Gtk::StatusBar::pop(guint context_id );

void Gtk::StatusBar::remove(guint context_id,
                            guint message_id ); 

The first, Gtk::StatusBar::push(), is used to add a new message to the statusbar. It returns a message identifier, which can be passed later to the function Gtk::StatusBar::remove to remove the message with the given message and context identifiers from the statusbar's stack. Gtk::StatusBar::pop() removes the message highest in the stack having the given context identifier.

This may seem like an awfully complicated mechanism for such a seemingly simple task, but if you've done much GUI programming, you'll quickly appreciate what this does for you. GUI interfaces often have multiple things going on at once (whether they're multi-threaded or not), and it can sometimes be more trouble than it's worth to give each task a way to display messages to the user. Gtk::StatusBar makes it easy (well, easier, at any rate).

The following example creates a statusbar and two buttons: one for pushing items onto the statusbar, and one for popping the last item back off.

Source location: examples/statusbar/statusbar.cc

#include <gtk--/box.h>
#include <gtk--/button.h>
#include <gtk--/main.h>
#include <gtk--/window.h>
#include <gtk--/statusbar.h>

// Gtk-- version of the "statusbar" example from the gtk+ tutorial

class StatusBar : public Gtk::Window
{
  Gtk::VBox m_vbox;
  Gtk::Button m_bPush, m_bPop;
  Gtk::Statusbar m_status_bar;

  // Note : m_context_id has to be declared *after* m_status_bar to be
  // initialized in the constructor's init list
  unsigned int m_context_id,
    m_count;  
 
  void push_item(unsigned int context_id);
  void pop_item(unsigned int context_id) { m_status_bar.pop(context_id); }
  
 public:
  StatusBar();

  /* It's a good idea to do this for all application windows. */
  gint delete_event_impl (GdkEventAny*)
  {
    Gtk::Main::quit();
    return 0;
  }

};


StatusBar::StatusBar() :
  m_vbox(false, 1),
  m_bPush("push item"),
  m_bPop("pop last item"),
  m_context_id(m_status_bar.get_context_id("Statusbar example")),
  m_count(1)
{
  set_usize(200, 100);
  set_title("Gtk-- Statusbar Example");
  
  add(m_vbox);
  
  m_vbox.pack_start(m_status_bar);

  m_bPush.clicked.connect(bind(slot(this, &StatusBar::push_item), m_context_id));
  m_vbox.pack_start(m_bPush);
  
  m_bPop.clicked.connect(bind(slot(this, &StatusBar::pop_item), m_context_id));
  m_vbox.pack_start(m_bPop);
  
  show_all();
}

void
StatusBar::push_item(unsigned int context_id)
{
  char buff[20];

  g_snprintf(buff, 20, "Item %d", m_count++);
  m_status_bar.push(context_id, buff);
}

int main (int argc, char *argv[])
{
  Gtk::Main myapp(argc, argv);

  StatusBar statusbar;

  myapp.run();
  return 0;
}

8.9 Text Entries

Text-entry widgets allow the user to enter text (surprisingly enough); they're called Gtk::Entry in GTK--. They have two constructors:

Gtk::Entry();
Gtk::Entry(guint16 max);

The first creates an empty text-entry widget; the second creates an empty text-entry widget with a fixed limit on the length of its text.

There are several functions for altering the text in a Gtk::Entry widget:

void Gtk::Entry::set_text(const string &text);
void Gtk::Entry::append_text(const string &text);
void Gtk::Entry::prepend_text(const string &text);

Gtk::Entry::set_text() replaces the current text in the entry widget with the string in text; Gtk::Entry::append_text() and Gtk::Entry::prepend_text() add the contents of text to the end and beginning of the current text, respectively.

You can change the position of the insertion point using

void Gtk::Entry::set_position(gint position);

The way the position argument is interpreted is as follows. If the current contents of the widget are

hello world

and you call set_position() with an argument of 5, and then type the letter X, the contents will be

helloX world

You can retrieve the current contents of a text-entry widget using

string Gtk::Entry::get_text();

Occasionally you might want to make a text-entry widget read-only. This can be done by passing false to the following method:

void Gtk::Entry::set_editable(bool  editable);

A text-entry widget whose editable flag is turned off won't respond to keypresses; however, the text in it can still be selected.

Another fairly common use for text-entry widgets is for the input of passwords, passphrases and other information you don't want echoed on the screen. Gtk::Entry provides the visibility flag to handle these cases:

void Gtk::Entry::set_visibility(bool  visible);

When the visibility flag is set to false, the letters typed into the text-entry widget show up as asterisks. (This can be changed on the fly, as you'll see in the example below.)

Gtk::Entry supports a selection region for copying text, and other purposes. You can change the selection region using

void Gtk::Entry::select_region(gint start,
                               gint end );

start and end are interpreted just as position is for set_position() (see above).

You might want to be notified whenever the user types in a text entry widget. Gtk::Entry provides two signals, activate and changed, for just this purpose. activate is emitted when the user presses the enter key in a text-entry widget; changed is emitted when the text in the widget changes. You can use these to validate or filter the text the user types, among other things.

Here is an example using Gtk::Entry. It provides, besides a Gtk::Entry widget, two checkboxes, with which you can toggle the editable and visible flags.

Source location: examples/entry/entry.cc

#include <iostream>
#include <gtk--/box.h>
#include <gtk--/button.h>
#include <gtk--/checkbutton.h>
#include <gtk--/entry.h>
#include <gtk--/main.h>
#include <gtk--/window.h>

// Gtk-- version of the "entry" example from the gtk+ tutorial

class Entry : public Gtk::Window
{
  Gtk::HBox m_hbox;
  Gtk::VBox m_vbox;
  Gtk::Entry m_entry;
  Gtk::Button m_bClose;
  Gtk::CheckButton m_cbEditable, m_cbVisible;

  void toggleEditable() { m_entry.set_editable(m_cbEditable.get_active()); }
  
  void toggleVisibility() { m_entry.set_visibility(m_cbVisible.get_active()); }
  void enterCallback();

  gint delete_event_impl(GdkEventAny*) { 
    Gtk::Main::quit(); return 0; 
  }
  
public:
  Entry();
};

Entry::Entry() :
  m_hbox(false, 0),
  m_vbox(false, 0),
  m_entry(50),
  m_bClose("Close"),
  m_cbEditable("Editable"),
  m_cbVisible("Visible")
{

  set_usize(200, 100);
  set_title("Gtk-- Entry");
  
  add(m_vbox);
  
  m_entry.activate.connect(slot(this, &Entry::enterCallback));
  m_entry.set_text("hello");
  m_entry.append_text(" world");
  m_entry.select_region(0, m_entry.get_text_length());
  m_vbox.pack_start(m_entry);
  
  // Note that add() can also be used instead of pack_xxx()
  m_vbox.add(m_hbox);

  m_hbox.pack_start(m_cbEditable);
  m_cbEditable.toggled.connect(slot(this, &Entry::toggleEditable));
  m_cbEditable.set_active(true);

  m_hbox.pack_start(m_cbVisible);
  m_cbVisible.toggled.connect(slot(this,&Entry::toggleVisibility));
  m_cbVisible.set_active(true);

  m_bClose.clicked.connect(Gtk::Main::quit.slot());
  m_vbox.pack_start(m_bClose);
  m_bClose.set_flags(GTK_CAN_DEFAULT);
  m_bClose.grab_default();
  
  show_all();
}

void
Entry::enterCallback()
{
  cout << "Entry contents : " << m_entry.get_text() << endl;
}

int main (int argc, char *argv[])
{
  Gtk::Main myapp(&argc, &argv);

  Entry entry;

  myapp.run();
  return 0;
}

8.10 Spinbuttons

Spinbuttons allow the user to select a value from a range of numeric values. It consists of a text entry box with up and down arrow buttons attached to the side. Selecting one of the buttons causes the value to 'spin' up and down across the range of possible values. The entry box may also be used to enter a value directly.

A spinbutton's value can have an adjustable number of decimal places, and the step size is configurable. Spinbuttons have an 'auto-repeat' feature as well: holding down one of the arrows can optionally cause the value to change more quickly the longer the arrow is held down.

Spinbuttons use an Adjustment object to hold information about the range of values they can have. Recall that an adjustment widget is created with the following constructor:

Gtk::Adjustment( gfloat value,
                 gfloat lower,
                 gfloat upper,
                 gfloat step_increment,
                 gfloat page_increment,
                 gfloat page_size );

These attributes of an Adjustment are used by the Spin Button in the following way:

Additionally, mouse button 3 can be used to jump directly to the upper or lower values.

Here is the Gtk::SpinButton constructor:

Gtk::SpinButton( GtkAdjustment  &adjustment,
                 gfloat         climb_rate,
                 guint          digits );

adjustment is the adjustment object that this spinbutton will use. The climb_rate argument is an acceleration factor for when the arrows are held down; a value of 0.0 turns acceleration off, and 1.0 is the maximum. The digits argument specifies the number of decimal places in the spinbutton's value.

The adjustment object can be set and retrieved using the following functions:

void Gtk::SpinButton::set_adjustment(Gtk::Adjustment& adjustment);
Gtk::Adjustment* Gtk::SpinButton::get_adjustment() const;

The number of decimal places can be altered using:

void Gtk::SpinButton::set_digits(guint digits) ;

You can set the spinbutton's value using:

void Gtk::SpinButton::set_value(gfloat value);

The value can be retrieved as either a floating point or integer value with the following functions:

gfloat Gtk::SpinButton::get_value_as_float();
gint Gtk::SpinButton::get_value_as_int();

The spin() method allows you to 'spin' the spinbutton, just as though its arrows had been clicked. Its prototype is

void Gtk::SpinButton::spin( GtkSpinType direction,
                            gfloat      increment );

where the direction parameter can take one of the following values:

GTK_SPIN_STEP_FORWARD, GTK_SPIN_STEP_BACKWARD

Move the value up or down, respectively, by increment. If increment is 0, change the value by the value of step_increment in the adjustment object.

GTK_SPIN_PAGE_FORWARD, GTK_SPIN_PAGE_BACKWARD

Change the value by increment.

GTK_SPIN_HOME

Set the value to the minimum value specified in the adjustment.

GTK_SPIN_END

Set the value to the maximum value specified in the adjustment.

GTK_SPIN_USER_DEFINED

Change the value by increment.

To prevent the user from typing non-numeric characters into the entry box, pass true to

void Gtk::SpinButton::set_numeric(bool numeric);

You can cause the spinbutton to 'wrap' between its upper and lower bounds using

void Gtk::SpinButton::set_wrap(bool wrap);

To force the spinbutton's value to be 'snapped' to the nearest step_increment in the adjustment, use

void Gtk::SpinButton::set_snap_to_ticks(bool snap_to_ticks);

You can modify the update policy of a spinbutton using

void Gtk::SpinButton::set_update_policy(GtkSpinButtonUpdatePolicy policy);

where policy is one of GTK_UPDATE_ALWAYS or GTK_UPDATE_IF_VALID. GTK_UPDATE_ALWAYS causes the spinbutton to ignore errors encountered while converting the text in the entry box to a numeric value. You can use this setting to allow the spinbutton to accept non-numeric values. GTK_UPDATE_IF_VALID causes the spinbutton to reject values that do not parse correctly, or that are outside the valid numeric range specified in the adjustment object.

You can change the spinbutton's shadow style using

void Gtk::SpinButton::set_shadow_type(GtkShadowType shadow_type);

where shadow_type is one of

You can force an immediate update using

void Gtk::SpinButton::update();

Here's an example of a spinbutton in action:

Source location: examples/spinbutton/spinbutton.cc

#include <stdio.h>
#include <gtk--/checkbutton.h>
#include <gtk--/window.h>
#include <gtk--/frame.h>
#include <gtk--/box.h>
#include <gtk--/main.h>
#include <gtk--/spinbutton.h>
#include <gtk--/label.h>
#include <gtk--/adjustment.h>

class ToggleSnap: public Gtk::CheckButton
{
  Gtk::SpinButton* spin_;
public:
  ToggleSnap(Gtk::SpinButton *spin,const Gtk::string& s) 
      : Gtk::CheckButton(s), spin_(spin)
    {}
  void clicked_impl()
    { 
      Gtk::CheckButton::clicked_impl();
      spin_->set_snap_to_ticks(get_active()); 
    }
};

class ToggleNumeric: public Gtk::CheckButton
{
  Gtk::SpinButton* spin_;
public:
  ToggleNumeric(Gtk::SpinButton *spin,const Gtk::string& s) 
      : Gtk::CheckButton(s), spin_(spin)
    {}
  void clicked_impl()
    { 
      Gtk::CheckButton::clicked_impl();
      spin_->set_numeric(get_active()); 
    }
};

class AppWindow : public Gtk::Window
{
  Gtk::SpinButton *spinner1;
  Gtk::Label  *val_label;
public:
  AppWindow();
  ~AppWindow();

  /* It's a good idea to do this for all application windows. */
  gint delete_event_impl (GdkEventAny*)
  {
    Gtk::Main::quit();
    return 0;
  }

  void change_digits( Gtk::SpinButton *spin );
  void get_value(int display );

};


void AppWindow::change_digits( Gtk::SpinButton *spin )
{
  spinner1->set_digits ( spin->get_value_as_int ());
}

void AppWindow::get_value(int display )
{
  gchar buf[32];

  if (display == 1)
    sprintf (buf, "%d", spinner1->get_value_as_int ());
  else
    sprintf (buf, "%0.*f", spinner1->gtkobj()->digits,
             spinner1->get_value_as_float ());
  val_label->set_text (buf);
}


AppWindow::AppWindow()
  : Gtk::Window (GTK_WINDOW_TOPLEVEL)
{
  Gtk::Frame      *frame;
  Gtk::Box        *hbox;
  Gtk::Box        *main_vbox;
  Gtk::Box        *vbox;
  Gtk::Box        *vbox2;
  Gtk::SpinButton *spinner2;
  Gtk::SpinButton *spinner;
  Gtk::Button     *button;
  Gtk::CheckButton *checkbutton;
  Gtk::Label      *label;
  Gtk::Adjustment *adj;

  set_title ("Spin Button");

  main_vbox = manage( new Gtk::VBox (false, 5) );
  main_vbox->set_border_width (10);
  add (*main_vbox);
  
  frame = manage( new Gtk::Frame ("Not accelerated") );
  main_vbox->pack_start (*frame, true, true, 0);
  
  vbox = manage( new Gtk::VBox (false, 0) );
  vbox->set_border_width (5);
  frame->add (*vbox);
  
  /* Day, month, year spinners */
  
  hbox = manage( new Gtk::HBox (false, 0) );
  vbox->pack_start (*hbox, true, true, 5);
  
  vbox2 = manage( new Gtk::VBox (false, 0) );
  hbox->pack_start (*vbox2, true, true, 5);
  
  label = manage( new Gtk::Label ("Day :", 0, 0.5) );
  vbox2->pack_start (*label, false, true, 0);
  
  adj = manage( new Gtk::Adjustment (1.0, 1.0, 31.0, 1.0, 5.0, 0.0) );
  spinner = manage( new Gtk::SpinButton (*adj, 0, 0) );
  spinner->set_wrap (true);
  spinner->set_shadow_type (GTK_SHADOW_OUT);
  vbox2->pack_start (*spinner, false, true, 0);
  
  vbox2 = manage( new Gtk::VBox (false, 0) );
  hbox->pack_start (*vbox2, true, true, 5);
  
  label = manage( new Gtk::Label ("Month :", 0, 0.5) );
  vbox2->pack_start (*label, false, true, 0);
  
  adj = manage( new Gtk::Adjustment (1.0, 1.0, 12.0, 1.0, 5.0, 0.0) );
  spinner = manage( new Gtk::SpinButton (*adj, 0, 0) );
  spinner->set_wrap (true);
  spinner->set_shadow_type (GTK_SHADOW_ETCHED_IN);
  vbox2->pack_start (*spinner, false, true, 0);
  
  vbox2 = manage( new Gtk::VBox (false, 0) );
  hbox->pack_start (*vbox2, true, true, 5);
  
  label = manage( new Gtk::Label ("Year :", 0, 0.5) );
  vbox2->pack_start (*label, false, true, 0);
  
  adj = manage( new Gtk::Adjustment (1998.0, 0.0, 2100.0, 1.0, 100.0, 0.0) );
  spinner = manage( new Gtk::SpinButton (*adj, 0, 0) );
  spinner->set_wrap (false);
  spinner->set_shadow_type (GTK_SHADOW_IN);
  spinner->set_usize (55, 0);
  vbox2->pack_start (*spinner, false, true, 0);
  
  frame = manage( new Gtk::Frame ("Accelerated") );
  main_vbox->pack_start (*frame, true, true, 0);
  
  vbox = manage( new Gtk::VBox(false, 0) );
  vbox->set_border_width (5);
  frame->add (*vbox);
  
  hbox = manage( new Gtk::HBox (false, 0) );
  vbox->pack_start (*hbox, false, true, 5);
  
  vbox2 = manage( new Gtk::VBox (false, 0) );
  hbox->pack_start (*vbox2, true, true, 5);
  
  label = manage( new Gtk::Label ("Value :", 0, 0.5) );
  vbox2->pack_start (*label, false, true, 0);
  
  adj = manage( new Gtk::Adjustment (0.0, -10000.0, 10000.0,
                                              0.5, 100.0, 0.0) );
  spinner1 = manage( new Gtk::SpinButton (*adj, 1.0, 2) );
  spinner1->set_wrap (true);
  spinner1->set_usize (100, 0);
  vbox2->pack_start (*spinner1, false, true, 0);
  
  vbox2 = manage( new Gtk::VBox (false, 0) );
  hbox->pack_start (*vbox2, true, true, 5);
  
  label = manage( new Gtk::Label ("Digits :", 0, 0.5) );
  vbox2->pack_start (*label, false, true, 0);
  
  adj = manage( new Gtk::Adjustment (2, 1, 5, 1, 1, 0) );

  spinner2 = manage( new Gtk::SpinButton (*adj, 0.0, 0) );
  spinner2->set_wrap (true);
  adj->value_changed.connect(bind(slot(this, &AppWindow::change_digits),spinner2));
  vbox2->pack_start (*spinner2, false, true, 0);
  
  hbox = manage( new Gtk::HBox (false, 0) );
  vbox->pack_start (*hbox, false, true, 5);
  
  checkbutton = manage( new ToggleSnap (spinner, "Snap to 0.5-ticks"));
  vbox->pack_start (*checkbutton, true, true, 0);
  checkbutton->set_active (true);
  
  checkbutton = manage( new ToggleNumeric (spinner, "Numeric only input mode") );
  vbox->pack_start ( *checkbutton, true, true, 0);
  checkbutton->set_active (true);
  
  val_label = manage( new Gtk::Label ("") );
  
  hbox = manage( new Gtk::HBox (false, 0) );
  vbox->pack_start (*hbox, false, true, 5);

  button = manage( new Gtk::Button ("Value as Int") );
  button->clicked.connect(bind(slot(this,&AppWindow::get_value),1));
  hbox->pack_start (*button, true, true, 5);
  
  button = manage( new Gtk::Button ("Value as Float") );
  button->clicked.connect(bind(slot(this,&AppWindow::get_value),2));
  hbox->pack_start (*button, true, true, 5);
  
  vbox->pack_start (*val_label, true, true, 0);
  val_label->set_text ("0");
  
  hbox = manage( new Gtk::HBox (false, 0));
  main_vbox->pack_start (*hbox, false, true, 0);
  
  button = manage( new Gtk::Button ("Close") );
  button->clicked.connect(Gtk::Main::quit.slot());
  hbox->pack_start (*button, true, true, 5);

  show_all ();
}

AppWindow::~AppWindow() {}

int main( int   argc,
          char *argv[] )
{
  /* Initialise GTK */
  Gtk::Main kit(&argc, &argv);
    
  /* Create a new window */
  AppWindow app;
    
  /* Enter the event loop */
  Gtk::Main::run ();
    
  return(0);
} 

8.11 Combo boxes

A combo box is like a cross between a flip-menu and a text-entry widget. It consists of a text-entry box with an arrow on the side; clicking the arrow reveals a menu of entries, and clicking on one of the entries enters it into the entry box. The entry box otherwise works just like a regular text-entry widget.

The constructor for the combo box is

Gtk::Combo()

A Gtk::Combo contains a Gtk::Entry widget, which is used to implement the entry box. You can obtain the Gtk::Entry using the member function

Gtk::Entry* Gtk::Combo::get_entry() const;

To set the values in the flip-menu, use

void Gtk::Combo::set_popdown_strings(const Gtk::SArray& strings);

where strings is a list of the strings you want to appear in the list. A Gtk::SArray is a converter object which can take any kind of STL vector container; this means that you can pass vectors or lists to this function, and things will work as you expect. For example, the following is legal:

list<string> gl;

gl.push_back("String 1");
gl.push_back("String 2");
gl.push_back("String 3"); 
gl.push_back("String 4");

combo.set_popdown_strings(gl);

Gtk::Combo has a number of key-bindings which make the widget a bit easier to use. You can allow the values in the list to be selected using the up-arrow and down-arrow keys by passing true to

void Gtk::Combo::set_use_arrows(bool val);

Normally, when you press (for example) the down-arrow in the entry-box, but you're on the last item in the flip-menu, the focus will change. You can make the value wrap around instead by passing true to

void Gtk::Combo::set_use_arrows_always(bool val);

Gtk::Combo also features completion; hitting ALT-TAB in the entry box will cause the widget to attempt to complete the text in it using the values in the flip-menu. You can set whether the completion search is case-sensitive or not by calling

void Gtk::Combo::set_case_sensitive(gint val);

8.12 Colour Selection Widget

The colour selection widget is used for interactively selecting colours. This composite widget lets the user select a colour by manipulating RGB (Red, Green, Blue) and HSV (Hue, Saturation, Value) triples. This is done either by adjusting single values with sliders or entries, or by picking the desired colour from a hue-saturation wheel/value bar. Optionally, the opacity of the colour can also be set.

The colour selection widget currently emits only one signal, color_changed, which is emitted whenever the current colour in the widget changes, either when the user changes it, or if it's set explicitly through Gtk::ColorSelection::set_color().

The colour selection widget can be used on its own, as part of a dialog, for example, or you can use the Gtk::ColorSelectionDialog, which is a complete colour-selection dialog using Gtk::ColorSelection.

The constructor for Gtk::ColorSelection looks like this:

Gtk::ColorSelection();

You'll probably not be using this constructor directly. It creates an orphan Gtk::ColorSelection widget which you'll have to parent yourself.

Gtk::ColorSelectionDialog has the following constructor:

 
Gtk::ColorSelectionDialog(const nstring &title);

Gtk::ColorSelectionDialog inherits from Gtk::Dialog. It consists of a Gtk::Frame containing a Gtk::ColorSelection widget, a Gtk::HSeparator, and a Gtk::HBox with three buttons in it, marked "Ok", "Cancel" and "Help". (You can obtain the button widgets using the get_ok_button(), get_cancel_button() and get_help_button() methods.)

Gtk::ColorSelection has an adjustable update policy, which can be set using

void Gtk::ColorSelection::set_update_policy(GtkUpdateType policy);

where policy is one of GTK_UPDATE_CONTINUOUS, GTK_UPDATE_DISCONTINUOUS, or GTK_UPDATE_DELAYED. The default policy is GTK_UPDATE_CONTINUOUS, which means that the current colour is updated continuously when the user drags the sliders or presses the mouse and drags in the hue-saturation wheel or value bar. If you experience performance problems, you may want to set the policy to GTK_UPDATE_DISCONTINUOUS or GTK_UPDATE_DELAYED.

The colour selection widget supports the adjustment of the opacity of a colour (also known as the colour's alpha channel). This is disabled by default; to activate this feature, pass true to

void Gtk::ColorSelection::set_opacity(gint use_opacity);

You can set the colour using

void Gtk::ColorSelection::set_color(gdouble *color);

where color is an array of gdoubles. The array should contain four doubles, representing the red, green, blue, and alpha channels, in that order. (If opacity is not enabled, the alpha component is ignored.) The values of each channel must be between 0 and 1.

You can get the currently selected colour using

void Gtk::ColorSelection::get_color(gdouble *color);

color should be a pointer to an array of gdouble; the function will fill in this array with the currently selected colour.

8.13 File Selection Widget

The file selection widget is a complete file-selection dialog. Its constructor is:

Gtk::FileSelection(const nstring &title);

The title will be used in the window's title bar.

To set the selected file, use

void Gtk::FileSelection::set_filename(const string &filename);

This is useful for bringing the user to a specific directory, or to set a default filename.

To get the currently selected filename, call

string Gtk::FileSelection::get_filename();

There are also accessor functions for the widgets contained within the file selection widget. These are:

You'll primarily use these for connecting signals; usually you'll connect something to at least the OK and Cancel buttons.

Here is an example of using the file selection widget:

Source location: examples/filesel/filesel.cc

/* example-start filesel filesel.cc */
#include <iostream>
#include <gtk--/fileselection.h>
#include <gtk--/main.h>

//From <iostream> 
using std::cout;
using std::endl;

using SigC::slot;

class filesel: public Gtk::FileSelection
{
public:
    filesel();
private:
    /* Get the selected filename and print it to the console */
    void file_ok_sel() {
        cout << "file_ok_sel: " << get_filename() << endl;
    }
    gint delete_event_impl(GdkEventAny*) { 
        Gtk::Main::quit(); return 0; 
    }
};

filesel::filesel():
    Gtk::FileSelection("File selection")
{
    /* Connect the ok_button_ to file_ok_sel function */
    get_ok_button()->clicked.connect(slot(this, &filesel::file_ok_sel));
    /* Connect the cancel_button_ to hiding the window */
    get_cancel_button()->clicked.connect(hide.slot());
    /* Connect hiding the window to exit the program */
    hide.connect(Gtk::Main::quit.slot());
}


int main (int argc, char *argv[])
{
    /* Initialize GTK-- */
    Gtk::Main m(&argc, &argv);
    
    /* Create a new file selection widget */
    filesel filew;
    
    /* Lets set the filename, as if this were a save dialog, and we are giving
       a default filename */
    filew.set_filename("penguin.png");

    filew.show();
    m.run();

    return 0;
}
/* example-end */


Next Previous Contents