Next Previous Contents

3. Moving On

3.1 Data Types

There are a few types you've probably noticed in the previous examples that might need explaining. The gint, gchar and other "g" types that you saw are typedefs to int and char, respectively. This is done to get around that nasty dependency on the sizes of simple data types when doing calculations.

A good example is gint32, which will be typedefed to a 32 bit integer for any given platform, whether it be the 64 bit alpha, or the 32 bit i386. The typedefs are very straightforward and intuitive. They are all defined in glib/glib.h, which gets included from gtk.h.

3.2 Using X events

In addition to the signal mechanism described above, there are a set of events that reflect the X event mechanism. Callbacks may also be attached to these events, just like regular signals (in fact, from the application programmer's point of view, there is no difference). These events are:

Connecting callbacks to these events is done exactly as it is for any other signal. For example:

gint button_press_callback(GdkEventButton *event);
Gtk::Button button("label");
button.button_press_event.connect( slot(&button_press_callback) );

When the mouse is over the button and a mouse button is pressed, button_press_callback() will be called. The value returned from button_press_callback() indicates whether the callback "handled" the event. If the value is zero (false), then the callback did not handle the event, and GTK-- will pass the event on to the widget's parent. If the value is non-zero (i.e., true), the callback handled the event, and the event will not be seen by any other widget. (See Chapter Advanced Event and Signal Handling for more information on this mechanism.)

GdkEventButton is a structure containing the event's parameters, such as the coordinates of the mouse pointer at the time the button was pressed. There are several different types of GdkEvent structures; which one is used in the callback depends on the event type. For details on the GdkEvent data types, see Appendix GDK Event Types.

You'll find it useful to handle X events when there's something you can't accomplish with a widget's signals alone. Gtk::Button, for example, does not send mouse-pointer coordinates with the clicked signal; you could handle button_pressed_event for Gtk::Button if you needed this information. X events are also often used to handle key-presses.

Handling an X event doesn't affect the operation of a widget's other signals. If you handle button_pressed_event for Gtk::Button, you'll still be able to get the clicked signal, if you want them both; they are emitted at (nearly) the same time.

3.3 More on Signal Handlers

Let's take another look at the declaration for SigC::Signal1:

SigC::Connection Signal1<void,int>::connect( Slot1<void,int>& );

Notice that the return value is of type SigC::Connection. This is an object which you can use to control a connection to a callback. By keeping a copy of this object, you can disconnect its associated callback using the method SigC::Connection::disconnect().

3.4 An Upgraded Hello World

Let's take a look at a slightly improved helloworld with better examples of callbacks. Here we also introduce packing widgets, which is our next topic.

Source location: examples/helloworld2/helloworld2.cc

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

using std::cout;

using SigC::bind;
using SigC::slot;

class HelloWorld : public Gtk::Window
{
  Gtk::HBox m_box1;
  Gtk::Button m_button1, m_button2;

public:
  HelloWorld();
  
  // Our new improved callback. (see below)
  void callback(char* data);
  
  gint delete_event_impl(GdkEventAny*) { 
    Gtk::Main::quit(); return 0; 
  }
  
};

HelloWorld::HelloWorld() :
  // Gtk::Window(GTK_WINDOW_TOPLEVEL) : not needed.
  // GTK_WINDOW_TOPLEVEL is the constructor arg's default value
  m_box1(false, 0), // creates a box to pack widgets into
  m_button1("Button 1"),
  m_button2("Button 2")
{

  // this is a new call, this just sets the title of our new window to
  // "Hello Buttons!"
  set_title("Hello Buttons!");

  // sets the border width of the window.
  set_border_width(10);

  // put the box into the main window.
  add(m_box1);

  // Now when the button is clicked, we call the "callback" function
  // with a pointer to "button 1" as it's argument
  m_button1.clicked.connect(bind<char*>(slot(this, &HelloWorld::callback), "button 1"));

  // instead of gtk_container_add, we pack this button into the invisible
  // box, which has been packed into the window.
  // note that the pack_start default arguments are true, true, 0
  m_box1.pack_start(m_button1);
  
  // always remember this step, this tells GTK that our preparation
  // for this button is complete, and it can be displayed now.
  m_button1.show();

  // call the same callback function with a different argument,
  // passing a pointer to "button 2" instead.
  m_button2.clicked.connect(bind<char*>(slot(this, &HelloWorld::callback), "button 2"));

  m_box1.pack_start(m_button2);

  // The order in which we show the buttons is not really important,
  // but I recommend showing the window last, so it all pops up at
  // once.
  m_button2.show();
  
  m_box1.show();
  
  show();

  // NOTE : These lines can be replaced by
  // show_all();

}

// Our new improved callback.  The data passed to this method is
// printed to stdout.

// Note an important difference with the gtk+ version : you have to
// specify the correct type of the argument you intend to pass, or
// connect_to_method() won't compile because the compiler won't be
// able to instantiate the template correctly (unless you cast, of
// course)
void HelloWorld::callback(char* data)
{
  cout << "Hello World - " << (char*)data
       << " was pressed" << endl;
}

int main (int argc, char *argv[])
{
          
  // all GTK applications must have a gtk_main(). Control ends here
  // and waits for an event to occur (like a key press or mouse event).
  Gtk::Main kit(argc, argv);

  HelloWorld helloworld;

  kit.run();
  return 0;
}

Compile this program using the same linking arguments as our first example. This time, there is no easy way to exit the program; you have to use your window manager or command line to kill it. (Exercise: add a "Quit" button that will exit the program.) Try playing with the options to pack_start() while reading the next section. Also resize the window, and observe the behaviour.

Note that there is another useful constant for the Gtk::Window constructor: GTK_WINDOW_DIALOG. This causes the window manager to treat the window as a transient dialog window.


Next Previous Contents