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 typedef
s 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 typedef
ed 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
.
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.
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()
.
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.