GTK– v1.2 Tutorial

> < >

by Guillaume Laurent

Michael Ashton

> < >

This is a tutorial on how to use GTK– (the C++ bindings to GTK). This tutorial is a work in progress. Not all of the sections have been completed; some have not been started. Some chapters have been written, but not edited or proofread. Since they do contain helpful information, they have been included, and are marked "draft". We are working hard to this tutorial helpful and accurate. We would very much appreciate any reports of inaccuracies or other errors in this document. Contributions are also most welcome. Post your suggestions, critiques or addenda to the GTK– mailing list. – The GTK– Development Team

Contents

1 Introduction 5

2 Getting Started 6 2.1TheoryofSignalsandCallbacks...... 8 2.2OverridingMemberFunctions...... 11 2.3HelloWorldinGTK–...... 13

3MovingOn 17 3.1DataTypes...... 17 3.2UsingXevents...... 18 3.3MoreonSignalHandlers...... 19 3.4AnUpgradedHelloWorld...... 19

4 Widget Overview 22 4.1Packing(light)...... 22 4.2UsingaGTK–widget...... 23 4.3WidgetHierarchy...... 23 4.4 Widgets Without Windows ...... 25

5 The Button Widget 25 5.1Pushbuttons...... 26 5.2 Toggle Buttons ...... 28 5.3 Checkboxes ...... 28 5.4RadioButtons ...... 28 6 Adjustments 31 6.1CreatinganAdjustment...... 32 6.2UsingAdjustmentstheEasyWay...... 32 6.3AdjustmentInternals...... 32

7 Range Widgets 34 7.1ScrollbarWidgets...... 34 7.2ScaleWidgets...... 34 7.2.1 CreatingaScaleWidget...... 34 7.2.2 Methods and Signals (well, methods, at least) ...... 35 7.3CommonFunctions...... 35 7.3.1 Setting the Update Policy ...... 36 7.4KeyandMousebindings...... 36 7.4.1 VerticalRangeWidgets...... 36 7.4.2 HorizontalRangeWidgets...... 37 7.5Example:Rangewidgets...... 37

8 Miscellaneous Widgets 41 8.1Labels...... 41 8.2Arrows...... 45 8.3 The Tooltips Widget ...... 47 8.4ProgressBars...... 48 8.5Dialogs...... 52 8.6Pixmaps...... 53 8.7Rulers...... 60 8.8Statusbars...... 62 8.9TextEntries...... 64 8.10Spinbuttons...... 67 8.11Comboboxes...... 74 8.12ColourSelectionWidget...... 75 8.13FileSelectionWidget...... 76

9 Container widgets in GTK– 78 9.1TheGTK–containerwidgetsystem...... 78 9.2 Using Gtk::Bin widgets...... 79 9.3 Understanding the GTK– multiple-item widgets ...... 80 9.4 Using the GTK– multiple-item widgets ...... 81 9.5 Container widgets: a rogues’ gallery ...... 83 10 Single-item widgets 84 10.1Frames...... 84 10.2AspectFrames...... 86 10.3TheEventBox ...... 88 10.4TheAlignmentwidget...... 89 10.5Viewports...... 90 10.6ScrolledWindows ...... 91 10.7PanedWindowWidgets...... 93

11 Packing widgets 97 11.1TheoryofPackingBoxes...... 97 11.2DetailsofBoxes...... 97 11.3PackingUsingTables...... 102

12 Multiple-item widgets 105 12.1FixedContainer ...... 105 12.2 Layout Container ...... 107 12.3Buttonboxes...... 108 12.4Toolbars ...... 113 12.5 Notebooks ...... 117

13 Tree Widget (draft) 122 13.1CreatingaTree...... 123 13.2AddingaSubtree...... 123 13.3HandlingtheSelectionList...... 123 13.4TreeWidgetInternals...... 124 13.4.1Signals...... 125 13.4.2 Methods ...... 125 13.5TreeItemWidget...... 127 13.5.1Signals...... 127 13.5.2 Functions and Macros ...... 128 13.6TreeExample...... 129

14 Text Widget (draft) 133 14.1CreatingandConfiguringaTextbox...... 133 14.2TextManipulation...... 134 14.3 Keyboard Shortcuts ...... 135 14.3.1MotionShortcuts...... 136 14.3.2 Editing Shortcuts ...... 136 14.3.3SelectionShortcuts...... 136 14.4Anexample...... 136

15 Timeouts, I/O and Idle Functions 140 15.1Timeouts...... 140 15.2MonitoringI/O...... 143 15.3IdleFunctions...... 145

16 Advanced Event and Signal Handling (draft) 148 16.1SignalFunctions...... 148 16.1.1ConnectingandDisconnectingSignalHandlers...... 148 16.1.2BlockingandUnblockingSignalHandlers...... 149 16.1.3 Emitting and Stopping Signals ...... 149 16.2SignalEmissionandPropagation...... 150

17 Tips For Writing Gtk– Applications (draft) 150

18 Contributing 150

19 Tutorial Copyright and Permissions Notice 151

A Gtk– Signals 151 A.1GtkObject...... 151 A.2GtkWidget...... 151 A.3GtkData...... 154 A.4GtkContainer...... 155 A.5GtkCalendar...... 155 A.6GtkEditable...... 155 A.7GtkTipsQuery...... 156 A.8GtkCList...... 156 A.9 GtkNotebook ...... 157 A.10Gtk::List...... 158 A.11GtkMenuShell...... 158 A.12GtkToolbar...... 158 A.13Gtk::Tree...... 158 A.14GtkButton...... 158 A.15GtkItem...... 159 A.16GtkWindow...... 159 A.17GtkHandleBox...... 159 A.18 GtkToggleButton ...... 159 A.19GtkMenuItem...... 159 A.20Gtk::ListItem...... 159 A.21Gtk::TreeItem...... 160 A.22GtkCheckMenuItem...... 160 A.23 GtkInputDialog ...... 160 A.24GtkColorSelection...... 160 A.25GtkStatusBar...... 160 A.26GtkCTree...... 161 A.27GtkCurve...... 161 A.28GtkAdjustment...... 161

B GDK Event Types 161

C Code Examples 169 C.1Tictactoe...... 169 C.1.1tictactoe.h...... 169 C.1.2tictactoe.c...... 169 C.1.3 ttt_test.c ...... 171 C.2GtkDial...... 171 C.2.1gtkdial.h...... 171 C.2.2gtkdial.c...... 173 C.3Scribble...... 184

1 Introduction

GTK– is a C++ wrapper for GTK, a library for creating graphical user interfaces. It is licensed using the LGPL license, so you can develop open software, , or even commercial non-free software using GTK– without having to spend anything for licenses or royalties. The primary authors of GTK– are:

¯ Tero Pulkkinen [email protected].fi

¯ Karl Nelson [email protected]

¯ Guillaume Laurent [email protected]

This tutorial is an attempt to document as much as possible of GTK–, but it is by no means complete. This tutorial as- sumes a good understanding of C++, and how to create C++ programs. Some basic C++ vocabulary and abbreviations are used, like "ctor" and "dtor" for "constructor" and "destructor", and "method" for "member functions". It would be a great benefit for the reader to have previous X programming experience, but it shouldn’t be necessary. If you are learning GTK– as your first widget set, please comment on how you found this tutorial, and what you had trouble with. g

This document is a work in progress. You can find updates on the GTK– home page. We would very much like to hear of any problems you have learning GTK– from this document, and would appreciate input as to how it may be improved. Please see the section on 18 (Contributing) for further information.

2 Getting Started

The first thing to do, of course, is to obtain GTK–. You can get the latest version from . If you don’t already have it, you will also need to install the companion library libsigc++, available from . The GTK– source distribution also contains the complete source to the examples used in this tutorial, along with Makefiles to aid compilation. The binary package distributions include it as well; the example sources are included with the gtkmm-devel RPM, which installs them to /usr/doc. You’ll need to copy the examples subdirectory to your home directory, or some other place where you have write access, to compile the examples, if you’re using the RPM distribution. To begin our introduction to GTK–, we’ll start with the simplest program possible. This program will create an empty 200x200 pixel window. It has no way of exiting except to be killed using the shell. Source location: examples/base/base.cc

#include #include

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

Gtk::Window window (GTK_WINDOW_TOPLEVEL); window.show();

kit.run(); // you will need to ^C to exit.

return(0); }

You can compile the above program with gcc using:

g++ base.cc -o base ‘gtkmm-config --cflags --libs‘

The meaning of the unusual compilation options is explained below. All GTK– programs must include certain GTK– headers; gtk–.h includes the entire GTK– kit, which is usually not a good idea, since it includes a meg or so of headers, but for simple programs, it suffices. The next line:

Gtk::Main kit(argc, argv);

creates a Gtk::Main object. This is needed in all GTK– applications. The constructor for this object initializes the GTK– library for use, sets up default signal handlers, and checks the arguments passed to your application on the command line, looking for the following options:

¯ -gtk-module g

¯ -g-fatal-warnings

¯ -gtk-debug

¯ -gtk-no-debug

¯ --debug

¯ -gdk-no-debug

¯ -display

¯ -sync

¯ -no-xshm

¯ -name

¯ -class

It removes these from the argument list, leaving anything it does not recognize for your application to parse or ignore. This ensures that all GTK– applications accept the same set of standard arguments. The next two lines of code create and display a window:

Gtk::Window window (GTK_WINDOW_TOPLEVEL); window.show();

The GTK_WINDOW_TOPLEVEL argument specifies that we want the window to undergo window manager decoration and placement. Rather than create a window of 0x0 size, a window without children is set to 200x200 by default so you can still manipulate it. The show() method lets GTK– know that we are done setting the attributes of this widget, and that it can display it. The last line enters the GTK– main processing loop.

kit.run();

This is another call you will see in every (working) GTK– application (the run() method of the Gtk::Main class). When control reaches this point, GTK– will sleep waiting for X events (such as button or key presses), timeouts, or file I/O notifications to occur. In this simple example, however, events are ignored. To compile this, use this command (assuming you’ve put the source code in simple.cc):

g++ -Wall -g simple.cc -o simple ‘gtkmm-config --cflags --libs‘

To simplify compilation, we use the program gtkmm-config, which is present in all (properly installed) Gtk– installations. This program ’knows’ what compiler switches are needed to compile programs that use GTK–. The -cflags option causes gtkmm-config to output a list of include directories for the compiler to look in; the - libs option causes it to output the list of libraries for the compiler to link with and the directories to find them in (try running it from your shell-prompt to see what it’s printing on your system). For the above compilation command to work, note that you must surround the gtkmm-config invocation with backquotes. Backquotes cause the shell to execute the command inside them, and to use the command’s output as part of the command line. Some of the libraries that are usually linked in are: g

¯ The GTK– library (-lgtkmm)

¯ The Gdk– library (-lgdkmm)

¯ The GTK library (-lgtk), the widget library, based on top of GDK.

¯ The GDK library (-lgdk), the wrapper.

¯ The gmodule library (-lgmodule), which is used to load run time extensions.

¯ The Xlib library (-lX11) which is used by GDK.

¯ The Xext library (-lXext). This contains code for shared memory pixmaps and other X extensions.

¯ The math library (-lm). This is used by GTK for various purposes.

This list will vary from installation to installation; run gtkmm-config -libs to see it for your system.

2.1 Theory of Signals and Callbacks

GTK is an event driven toolkit, which means it will sleep until an event occurs and control is passed to the appropriate function. This is done using a callback mechanism called a signal. When an event occurs, such as the press of a mouse button, the appropriate signal will be emitted by the widget that was pressed. This is how GTK does most of its useful work. There are a set of signals that all widgets inherit, such as "destroy", and there are also signals that are widget specific, such as "toggled" for toggle buttons. You can also create your own signals. To make a widget - a button for example - perform an action, we set up a signal handler to catch the signal(s) the button emits; the signal handler will perform whatever actions we want to be triggered by the button-press. GTK– objects emit signals using special signal-emitting objects which we will call signallers (they’re also known as "signal objects"). A good way to understand this is to think of a traffic light. In most countries, a traffic light is a box with three lights on it, one red, one green, and one yellow. Most people know what these lights mean: red means "stop", yellow means "slow down", and green means "go". If a traffic light were a GTK– object, it would have three "lamps" (signallers), which we might call Red, Green, and Yellow. In the same way, a (simple) GTK– button has only one "lamp" (signaller) on it: it’s called clicked, and it "blinks" (emits a signal) whenever somebody presses it. Naturally, signals aren’t much good unless somebody’s watching them; a traffic light could blink red all day, but it would just be showing pretty colours if nobody stopped for it. Fortunately, most people know about traffic lights, but computers are a bit duller; we have to tell them explicitly what signals to watch out for, and what to do when they see them. We do this by connecting a signaller to a callback function (or simply callback); the callback function is what does something in response to the signal. Thanks to the flexibility of libsigc++, the callback library used by GTK–, the callback can be almost any kind of function: it can be a method of some object, or a standalone static function - it can even be a signaller. There is a point of potential confusion here which you should watch out for. We’ve given the name "signaller" to the libsigc++ objects which emit signals, but that’s not what these objects are called by the library; libsigc++ signal objects are all of type SigC::Signal, as we’ll see later. Remember that these objects, even though they’re called "signals", are simply the "light bulbs" that do the signalling. In the traffic light example, the traffic light’s light bulbs would be called SigC::Signals in libsigc++ parlance; keep that in mind, and you should be alright. Here’s an example of a callback being connected to a signal:

#include #include

void hello() { g

cout << "Hello World" << endl; }

main() { Gtk::Button button("Hello World"); button.clicked.connect(slot(&hello)); }

There’s rather a lot to think about in this (non-functional) code. First let’s identify the parties involved:

¯ The callback function is hello().

¯ We’re hooking it up to the Gtk::Button object called button.

¯ When the Button emits its clicked signal, hello() will be called.

Now that we’ve got that straight, how exactly does hello() get called? The answer is that we connected hello() to one of the Gtk::Button’s signallers, in this case the one called clicked. A signaller is an object of one of the template classes SigC::Signal#, where # is a number from 0 to 7 (as we’ll show later, this number determines how many arguments the callback should take). A signaller’s sole purpose in life is to call callback functions. (An aside: GTK calls this scheme "signalling"; the sharp-eyed reader with GUI toolkit experience will note that this same design is oft seen under the name of "broadcaster-listener" (e.g., in Metrowerks’ PowerPlant framework for the Macintosh). It works in much the same way: one sets up broadcasters, and then connects listeners to them; the broadcaster keeps a list of the objects listening to it, and when someone gives the broadcaster a message, it calls all of its objects in its list with the message. In GTK–, signal objects play the role of broadcasters, and slots play the role of listeners - sort of. More on this later.) On first hearing of the concept of connecting callback functions to signal objects, one might well suppose that the signal object’s connect method takes as argument a function pointer. But C++ is a strongly typed language, so this won’t work, unless we’re certain that we will only ever need one type of callback function. But we won’t; we need lots of different types of callback functions, with varying numbers of arguments; and those arguments need to be of any type we want, and oh yes, we also need to be able to have any return value we want, and also we want complete compile-time type-checking, and for our beer to be free. Believe it or not, C++ can handle all of these requirements but the last one. (Sorry.) Not only that, we get added flexibility. The particular technique by which we achieve all these things does work, but like so many things in life, it involves a tradeoff; it can be a bit difficult to get your head ’round at first, because it involves what looks at first like an odd bit of syntactical gymnastics, and it makes very heavy use of templates (the magic behind most of the weirdness and wonderfulness of C++). But it works, and it works beautifully; so well, in fact, that although you may have come to GTK– for other reasons, you might well stick around because of the ingenious signalling system. Now let’s get back to the code. Here again is the line where we hooked up the callback:

... button.clicked.connect(slot(&hello)); ...

Keep in mind that we’re trying to set things up so that hello() gets called when we click the button. Now, note again that we don’t pass a pointer to hello() directly to the button’s signal’s connect() function (remember, we can’t). Instead, we pass it to something called slot(), and pass the result of that to connect(). What’s that slot() function for? slot() is a factory function which generates, unsurprisingly, slots. A slot is an object which looks and feels like a function, but is actually an object. Such beasts are known as function objects,orfunctors, and they’re the key to the g

GTK– operation. They’re used instead of pointers because a pointer can’t really know what kind of thing it’s pointing at. Compilers can give you the illusion of this, using typed pointers, but the illusion breaks down in this situation. Slots don’t have that problem. Unlike function pointers, slots "know" what kind of function they’re pointing at. You can make slots that point to member functions, static functions, normal functions - indeed, any kind of function at all - by invoking the proper slot-building factory function. Slots not only know what flavour of function they are pointing to, but they know where it is; if a slot is pointing to a method of some dynamically allocated object - which it can - the user of the slot doesn’t have to know what the type of that object is. This is what makes slots so much better than pointers-to-methods: if we used those, signal objects would have to know what the type of those objects was, and since signal objects often come as part of a library (e.g., GTK–), they simply can’t (unless you insist that the GTK– authors #include your headers in the GTK– source, which they won’t). Why is a slot called a "slot"? We don’t know exactly; but we’ve got a good guess. You’ve probably seen a postal box with a narrow rectangular opening in it just big enough for letters to be put through. English speakers tend to refer to narrow rectangular openings as "slots", and slots are often used for passing messages (bits of mail) about, just as they are in GTK–. If the Button in the previous example were telling you about its having been pressed by sending you a letter ("Dear developer: At eight o’clock, I was clicked .."), it would very likely post that letter by pushing it through a slot. In the world of GTK–, the button does indeed tell you of its clickedness by passing a "message" through a slot (object). Incidentally, the slot() functions are all declared as part of the namespace SigC. The fully qualified name of slot() is SigC::slot(); we’ve omitted it in these examples for brevity, but you need to know about this, in case you find your compiler telling you that it doesn’t know where slot() is. All libsigc++ types and static functions are declared in the SigC namespace. Here’s a slightly larger example of slots in action:

void callback();

class callback_class : public SigC::Object { void method(); };

callback_class callback_object;

main() { Gtk::Button button; button.clicked.connect( slot(&callback) ); button.clicked.connect( slot(callback_object, &callback_class::method) ); }

The first call to connect() is just like the one we saw last time; nothing new here. The next is more interesting. slot() is now called with two arguments (it’s overloaded). The first argument is "f", which is the object that our new slot will be pointing at; the second argument is a pointer to one of its methods. This particular version of slot() creates a slot which will, when "called", call the pointed-to method of the specified object, in this case f.mymethod(). Another thing to note about this example is that we placed the call to connect() twice for the same signal object. This is perfectly fine; the result will be that when the button is clicked, both slots will be called. You can make buttons do double, triple, or n-duty this way, without modifying much code. We just told you that the button’s clicked signaller is expecting to call a function with no arguments. All signallers have requirements like this; you can’t hook a function with two arguments to a signaller expecting to not have to provide any (unless you use an adapter, of course). Therefore, it’s important to know what type of slot you’ll be expected to provide to a given signaller. g

To find out what type of slot you can connect to a signaller, you can look it up in the documentation, or you can look at the signaller’s declaration - which you might have to do, since there might not be any documentation. Here’s an example of a signaller declaration you might see in the GTK– headers:

Gtk::EmitProxySignal1 focus;

This looks rather a mess; but fortunately, you can ignore most all of it. Other than the signaller’s name (focus), two things are important to note here: the number following the word Signal at the beginning (1, in this case), and the first two types in the list (gint and GtkDirectionType). The number indicates how many arguments the signaller will be giving out; the first type, gint, is the type that the callback ought to return; and the next type, GtkDirectionType, is the type of this signaller’s single argument. Don’t let the remaining types (CppObject- Type, BaseObjectType, etc.) worry you; they’re used internally by GTK–.

The same principles apply for signallers which send out more arguments. Here’s one that sends three (taken from >

Gtk::EmitProxySignal3 insert_text;

Granted, it looks even scarier, but it follows the same form. The number 3 at the end of the type’s name indicates that our callback will need three arguments. The first type in the type list is void, so that should be our callback’s return type. The following three types should be the argument types, in order, of our callback. Our callback function’s prototype could look like this:

void insert_text_cb(const gchar* foo, gint bar, gint* foobar);

The names of the formal parameters aren’t important at all, because the signaller will never see them. Of course, it is important to know what insert_text will be sending you in those argument, but for that you’ll have to consult the documentation.

2.2 Overriding Member Functions

(Note: In the following section, we often use the terms "function", "member function" and "method" interchangeably. If we refer to a "function" which belongs to a class, we really mean "member function" or "method". We apologise for any confusion.) So far, we’ve been telling you that the way to perform actions in response to button-presses and the like is to watch for signals. That’s certainly a good way to do things, and the only practical way to do it in straight GTK+. In GTK–, though, it’s not the only way. With GTK–, instead of laboriously connecting callbacks to signals, you can simply make a new class which inherits from a widget - say, a button - and then override one of its member functions, such as the one that gets called when somebody presses the button. This can be a lot simpler than hooking up signals for each thing you want to do. You can do this in the straight-C world of GTK+ too; that’s what GTK’s object system is for. But in GTK+, you have to go through some complicated procedures to get object-oriented features like inheritance and overloading. In C++, it’s simple, since those features are supported in the language itself; you can let the compiler do the dirty work. This is one of the places where the beauty of C++ really comes out. One wouldn’t think of subclassing a GTK+ widget simply to override its action method; it’s just too much trouble. In GTK+, you almost always use signals to get things done, unless you’re writing a new widget. But because overriding methods is so easy in C++, it’s entirely practical - and sensible - to subclass a button for that purpose. g

Overriding functions this way has a number of advantages. The first is that you can get a lot done with only a few lines of code; in some situations, you might find that you never need to use signals at all. It also means that you’ll rarely have to write an entirely new widget - you can almost always do what you want by subclassing. And because overriding is so simple, you’re a lot less likely to make the sort of silly mistakes you’ll probably make if you’re writing a new GTK+ widget in C. Also, overriding will usually result in reduced execution time; emitting and distributing signals takes longer than simply calling a virtual function. So why did we spend all that time explaining signals? There are two main reasons why you need to be familiar with them. Firstly, the GTK– library relies on them. Even if you don’t use them much yourself, you need to understand what’s going on when you see signals used. Secondly, subclassing isn’t always the best way to accomplish things. Making a new class adds a symbol to your program, and that requires additional resources; and when you subclass a widget, its actions are fixed at compile time - signals allow you to change the behaviour of objects as the program is running. You can also make a signal easily affect multiple unrelated objects; overriding is not meant for that purpose. The power of signals shouldn’t be ignored; you need them in your toolbox to be truly effective in GTK–. To summarise: both techniques are valid tools for different situations. Which one you tend to use will help define your personal GTK– programming style, but you need to know about them both. GTK– classes are designed with overriding in mind; they contain virtual member functions specifically intended to be overridden. These functions have _impl at the end of their names. All of the signals in GTK– classes have corresponding overridable methods. _impl methods are declared protected, so they can only be used by derived classes. Let’s look at an example of overriding. Here is the first example from the section on signals, rewritten to use overriding:

#include

class OverriddenButton : public Gtk::Button { protected: virtual void clicked_impl(); }

void OverriddenButton::clicked_impl() { cout << "Hello World" << endl; // call the parent’s version of the function Gtk::Button::clicked_impl(); }

main() { OverriddenButton button("Hello World"); }

Here, instead of making a Gtk::Button object, which we then connect a signal to, we define a new class called OverriddenButton,whichinherits from Gtk::Button. The only thing we change is the clicked_impl function, which is called whenever Gtk::Button emits the clicked signal. We define this function to print "Hello World" to stdout, and then we call the original, overridden function, to let Gtk::Button do what it would have done had we not overridden the clicked function. Note that we declared our personal clicked_impl function to be protected; you should always do this when you override a protected function. You don’t always have to call the parent’s function; there are times when you might not want to. Note that we called the parent function after writing "Hello World", but we could have called it before. In this simple example, it hardly matters much, but there are times when it will. With signals, it’s not quite so easy to change details like this, and you g

can do something here which you can’t do at all with signals: you can call the parent function in the middle of your custom code. On the other hand, to do this we had to make a new class, and this particular example came out a little longer, line- by-line, than the original. There are situations, though, where this extra work of making a new class can be extremely helpful. You might, for example, make a button which you want, say, 16 of, and you might want all of them to do roughly the same thing when pressed. Using signals, you’d have to manually create each button and then connect each one to a callback of some sort. If you make a new class, though, you don’t have to connect anything - you can make each button behave correctly as soon as it’s created. Not only that, you can make a new constructor for your class which takes extra parameters; you could then use these values to affect the behaviour of each object. The possibilities are virtually limitless. We noted that the clicked_impl function is provided by the Gtk::Button widget for the clicked signal; this same principle holds for all signals that a widget supports. Gtk::Button gives you _impl functions called pressed_impl, released_impl, clicked_impl, enter_impl,andleave_impl (these all correspond to Gtk::Button signals; see the chapter on buttons for details). It also inherits some _impl functions from Gtk::Widget, which are, of course, available to subclasses of Gtk::Button.

2.3 Hello World in GTK–

We’ve now learned enough to look at a real example, instead of the silly stuff we’ve been analysing. In accordance with an ancient tradition of computer science, we now introduce Hello World, a la GTK–: Source location: examples/helloworld/helloworld.cc

#include #include #include #include

using std::cout;

using SigC::slot;

class HelloWorld : public Gtk::Window { Gtk::Button m_button;

public: HelloWorld();

// this is a callback function. the data arguments are ignored in this example.. // More on callbacks below. void hello();

// When the window is given the "delete_event" signal (this is given // by the window manager, usually by the ’close’ option, or on the // titlebar), this in turn calls the delete_event signal and the // delete_event_impl virtual function. We will override the // virtual function. virtual int delete_event_impl(GdkEventAny *event);

}; g

// This is a callback that will hand a widget being destroyed. void destroy_handler() { Gtk::Main::quit(); }

HelloWorld::HelloWorld() : Gtk::Window(GTK_WINDOW_TOPLEVEL), // create a new window m_button("Hello World") // creates a new button with the la- bel "Hello World". { // Here we connect the "destroy" event to a signal handler. // This event occurs when we call gtk_widget_destroy() on the window, // or if we return ’FALSE’ in the "delete_event" callback. destroy.connect(slot(&destroy_handler));

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

// When the button receives the "clicked" signal, it will call the // hello() method. The hello() method is defined below. m_button.clicked.connect(slot(this, &HelloWorld::hello));

// This will cause the window to be destroyed by calling // gtk_widget_destroy(window) when "clicked". Again, the destroy // signal could come from here, or the window manager. m_button.clicked.connect(destroy.slot());

// This packs the button into the window (a gtk container). add(m_button);

// The final step is to display this newly created widget... m_button.show();

// and the window show();

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

void HelloWorld::hello() { cout << "Hello World" << endl; }

int HelloWorld::delete_event_impl(GdkEventAny *event) { cout << "delete event occured" << endl;

// if you return FALSE in the "delete_event" signal handler, // GTK will emit the "destroy" signal. Returning TRUE means g

// you don’t want the window to be destroyed. // This is useful for popping up ’are you sure you want to quit ?’ // type dialogs.

// Change TRUE to FALSE and the main window will be destroyed with // a "delete_event". return true; }

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; }

Try to compile and run it before going on. Pretty thrilling, eh? Let’s examine the code. First, the HelloWorld class:

class HelloWorld : public Gtk::Window { Gtk::Button m_button;

public: HelloWorld();

void hello(); virtual int delete_event_impl(GdkEventAny *event);

};

This class implements the "Hello World" window. It’s derived from Gtk::Window, and has a single Gtk::Button as a member. We’ll be using two signals, and we also override the _impl method for the widget’s delete_event signal. We’ve chosen to use the constructor to do all of the initialisation work for the window, including setting up the signals. Here it is, with the comments omitted:

HelloWorld::HelloWorld() : Gtk::Window(GTK_WINDOW_TOPLEVEL), m_button("Hello World") { set_border_width(10);

destroy.connect(slot(&destroy_handler)); m_button.clicked.connect(slot(this, &HelloWorld::hello)); m_button.clicked.connect(destroy.slot());

add(m_button); m_button.show(); g

show(); }

We’ve placed two initialiser statements in the declaration. The first one provides an argument to our parent’s construc- tor; the argument we’ve provided here simply tells GTK– to make us a full-fledged application window, instead of a transient dialog window or something of that sort. The next initialiser initialises our m_button object; we give it the label "Hello World". Next we run the window’s set_border_width() method. This sets the amount of space between the sides of the window and the widget it contains (windows can only contain a single widget, but this isn’t really a limitation, as you’ll see later on). Then we hook up some signals. We need to handle three signals: delete_event and destroy for the window, and clicked for the button. delete_event and destroy are defined in Gtk::Widget, and are therefore common to all GTK– widgets. (delete_event is one of a special class of signals which correspond to X events. We talk about those in Chapter 2.) A window receives a delete_event when someone clicks its close box; when it receives a destroy, it disappears (the way this mechanism works is explained below). First we hook up the destroy signal to a callback, destroy_handler(), which looks like this:

void destroy_handler() { Gtk::Main::quit(); }

When destroy_handler() is called, it invokes Gtk::Main::quit(), which, surprisingly enough, quits the program. This is what we want; when our only window disappears, we shouldn’t keep running. We next hook up two callbacks to m_button’s clicked signal. The first of these runs one of our member functions, hello(), which prints our friendly greeting to stdout. The other one may be a bit odd-looking at first, because we didn’t use slot() here; instead, we used a member function of destroy called slot(). This function, which is part of every libsigc++ Signal object, returns a slot which, when called, will emit the signal it came from. So, when the clicked signal occurs, and it calls this slot, that slot will emit the destroy signal, which will call the destroy_handler() function. (You can chain signals up this way as much as you please.) Therefore, clicking the button causes the program to quit. Next, we use the window’s add() method to put m_button in the window. (add() comes from Gtk::Container, which is described in the chapter on container widgets.) The add() method places the widget you give it in the window, but it doesn’t display the widget. GTK– widgets are always invisible when you create them; to get them to display, you must call their show() method, which is what we do in the next line. The same principle applies to the window itself (remember, it’s a widget too), so we show() it next. We could also have used the window’s show_all() method, instead of the two calls to show(). show_all() shows not only the widget, but all of the widgets it contains. That’s it for the constructor. Now what about the _impl function we overrode? We did this to handle the window’s delete_event signal, as we mentioned earlier. X doesn’t destroy windows when it sends them a delete event; in fact, it doesn’t do anything to them except send them the event. This is useful when you have, for example, a window that contains a document; catching the delete event allows you to ask the user whether he or she wants to save the document, if it hasn’t been. GTK– handles this event specially. A signal handler for delete_event is expected to return false if the window should really be destroyed, and true if it should stick around. To demonstrate this, we return true from our delete_event_impl():

int HelloWorld::delete_event_impl(GdkEventAny *event) g

{ cout << "delete event occured" << endl; return true; }

Therefore, when you click the window’s close box, it doesn’t go away; it merely prints "delete event occured" on stdout. Had we returned false instead, GTK– would cause our window to emit the destroy signal, and the program would quit. Incidentally, this is one situation where it’s perhaps a little bit silly to use signals. This is a case of a widget catching one of its own signals; instead of bothering to hook up a signal we’d be sending to ourselves, we override the _impl method. This makes sense, because we’re subclassing Gtk::Window anyway. Now let’s look at our program’s main() function. Here it is, sans comments:

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

HelloWorld helloworld;

kit.run(); return 0; }

First we instantiate an object called kit; this is of type Gtk::Main. Every GTK– program must have one of these. We pass our command-line arguments to its constructor; it takes the arguments it wants, and leaves you the rest, as we described earlier. Next we make an object of our HelloWorld class, whose constructor takes no arguments. At this point our window is on the screen; it only remains to invoke the run() method of our Gtk::Main object. This starts up the event loop; every GTK– program has one of these too. During the event loop, GTK– idles, waiting for events to come in from the X server. When it gets them, it does with them whatever is appropriate. Note that if you interrupt the event loop, or don’t run it, your program will effectively be "hung", because it won’t be able to respond to events. The same can happen if, when you receive a signal, you execute an operation that takes a really long time. You’ve probably seen X programs hang like this. GTK– does provide a way for you to give time to the event loop while you’re busy doing something; you can also use threads (but that’s way beyond the scope of this chapter). The kit.run() call will return when we invoke the Gtk::Main::quit() function. At that point we can consider ourselves finished, so we return from main() with a successful result code, having successfully spread a little cheer to the world. If you understood all that, then congratulations: you are now advanced to Journeyman Class, and may proceed to the next chapter. Add four to your maximum hit-points. Go forth and code!

3MovingOn

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 typedefstoint and char, respectively. This is done to get around that nasty dependency on the sizes of simple data types when doing calculations. g

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.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:

¯ event

¯ button_press_event

¯ button_release_event

¯ motion_notify_event

¯ delete_event

¯ destroy_event

¯ expose_event

¯ key_press_event

¯ key_release_event

¯ enter_notify_event

¯ leave_notify_event

¯ configure_event

¯ focus_in_event

¯ focus_out_event

¯ map_event

¯ unmap_event

¯ property_notify_event

¯ selection_clear_event

¯ selection_request_event

¯ selection_notify_event

¯ proximity_in_event

¯ proximity_out_event

¯ drag_begin_event

¯ drag_request_event

¯ drag_end_event

¯ drop_enter_event g

¯ drop_leave_event

¯ drop_data_available_event

¯ other_event

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 16 (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 B (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 but- ton_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::connect( Slot1& );

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 #include #include #include #include g

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(slot(this, &HelloWorld::callback), "but- ton 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(); g

// call the same callback function with a different argument, // passing a pointer to "button 2" instead. m_button2.clicked.connect(bind(slot(this, &HelloWorld::callback), "but- ton 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. g

4 Widget Overview

In this chapter, we briefly introduce the concept of packing, which is the primary mechanism for window layout in GTK–. We also present a brief catalog of the most commonly used GTK– widgets.

4.1 Packing (light)

If you’ve played with GTK applications, you’ve probably noticed that GTK windows seem "elastic"; they can usually be stretched in many different ways. This ability is due to the widget packing system, a concept borrowed (with several other concepts) from . Most GUI toolkits allow you to place widgets on a window in arbitrary positions. In Delphi or Visual C++, for example, you place a button on a form by specifying its coordinates relative to the upper left-hand corner of the window. While this is easy to understand, and gives you quite a bit of flexibility, it also has some disadvantages:

¯ It’s possible for widgets to overlap. This is very seldom what you want.

¯ It’s difficult to make a window resizable.

¯ It’s easy to make a window with unaligned widgets; this makes your program look disorganised. To avoid this problem, one must spend time laying out and aligning each widget. Changing the layout of a window can be a tedious process.

¯ Changing the layout of a window "on the fly" can be a non-trivial operation.

¯ Using a form painter to lay out your windows is all but a requirement.

GTK uses the packing system to solve these problems. Rather than making you specify the position and size of each widget in the window, you can instead tell GTK to arrange your widgets in rows, columns, and/or tables. GTK can size your window automatically, based on the sizes of the widgets it contains. You can further tune your layout by specifying padding distance, centering values, and minimum and maximum sizes for each of your widgets, among other things. GTK then uses all this information to do the right thing when the user resizes a window. GTK arranges windows hierarchically, using containers. A container is a widget which contains other widgets. Most GTK widgets are containers; windows, borders, notebook tabs, and buttons are all container widgets. There are two flavours of containers in GTK: single-child containers, which are all descendants of Gtk::Bin, and multiple-child containers, which are descendants of Gtk::Container. Most widgets in GTK are descendants of Gtk::Bin, including Gtk::Window. Yes, that’s correct: a GTK window can contain at most one widget. How, then, can we use a window for anything useful? By placing a multiple-child container in the window. GTK– offers several basic multi-child containers:

¯ Gtk::VBox places its widgets one on top of another.

¯ Gtk::HBox arranges its widgets side-by-side.

¯ Gtk::Packer packs its widgets in TK-style.

¯ Gtk::Fixed allows you to place widgets inside it the old-fashioned way, using coordinates.

¯ Gtk::Table arranges its widgets in a grid.

There are quite a few other containers in GTK–; see 9.1 (Container Widgets) for a complete discussion of them. If you’ve never used a packing toolkit before, it can take a bit of getting used to. You’ll probably find, however, that you don’t need to rely on form-painters with GTK quite as much as you did with other toolkits. For a complete g

discussion of packing and packing theory, see the section 11 (Packing Widgets). For now, we’ll just mention a few of the most common packing-related calls, because we’ll be using them in our upcoming examples:

¯ Gtk::Box::pack_start() places a widget near the top for VBoxes, and near the left for HBoxes.

¯ Gtk::Box::pack_end() places a widget near the bottom for VBoxes, and near the right for HBoxes.

Essentially, successive calls to pack_start() will result in your widgets being "packed in" in a row or column.

4.2 Using a GTK– widget

The steps for using a GTK– widget are:

1. Declare a variable of the type of the widget you wish to use.

2. Connect the signals and events you wish to use to the appropriate handlers, and/or subclass the widget and override the appropriate _impl methods.

3. Set the attributes of the widget.

4. Pack the widget into a container using the appropriate call, e.g. Gtk::Container::add() or Gtk::Box::pack_start().

5. Call Gtk::Widget::show() to display the widget.

Gtk::Widget::show() lets GTK– know that we are done setting the attributes of the widget, and that it is ready to be displayed. You can use Gtk::Widget::hide() to make it disappear again. The order in which you show the widgets is not important, but we do suggest that you show the window last; this way, the whole window will appear with its contents already drawn. Otherwise, the user will first see a blank window, and then the widgets will begin to appear in it, which doesn’t look quite as nice.

4.3 Widget Hierarchy

Here is the class hierarchy for GTK– widgets:

Gtk::Object +Gtk::Widget | +Gtk::Misc | | +Gtk::Label | | | +Gtk::AccelLabel | | | ‘Gtk::TipsQuery | | +Gtk::Arrow | | +Gtk::Image | | ‘Gtk::Pixmap | +Gtk::Container | | +Gtk::Bin | | | +Gtk::Alignment | | | +Gtk::Frame | | | | ‘Gtk::AspectFrame | | | +Gtk::Button | | | | +Gtk::ToggleButton | | | | | ‘Gtk::CheckButton | | | | | ‘Gtk::RadioButton g

| | | | ‘Gtk::OptionMenu | | | +Gtk::Item | | | | +Gtk::MenuItem | | | | | +Gtk::CheckMenuItem | | | | | | ‘Gtk::RadioMenuItem | | | | | ‘Gtk::TearoffMenuItem | | | | +Gtk::ListItem | | | | ‘Gtk::TreeItem | | | +Gtk::Window | | | | +Gtk::ColorSelectionDialog | | | | +Gtk::Dialog | | | | | ‘Gtk::InputDialog | | | | +Gtk::DrawWindow | | | | +Gtk::FileSelection | | | | +Gtk::FontSelectionDialog | | | | ‘Gtk::Plug | | | +Gtk::EventBox | | | +Gtk::HandleBox | | | +Gtk::ScrolledWindow | | | ‘Gtk::Viewport | | +Gtk::Box | | | +Gtk::ButtonBox | | | | +Gtk::HButtonBox | | | | ‘Gtk::VButtonBox | | | +Gtk::VBox | | | | +Gtk::ColorSelection | | | | ‘Gtk::GammaCurve | | | ‘Gtk::HBox | | | +Gtk::Combo | | | ‘Gtk::Statusbar | | +Gtk::CList | | | ‘Gtk::CTree | | +Gtk::Fixed | | +Gtk::Notebook | | | ‘Gtk::FontSelection | | +Gtk::Paned | | | +Gtk::HPaned | | | ‘Gtk::VPaned | | +Gtk::Layout | | +Gtk::List | | +Gtk::MenuShell | | | +Gtk::MenuBar | | | ‘Gtk::Menu | | +Gtk::Packer | | +Gtk::Socket | | +Gtk::Table | | +Gtk::Toolbar | | ‘Gtk::Tree | +Gtk::Calendar | +Gtk::DrawingArea | | ‘Gtk::Curve | +Gtk::Editable | | +Gtk::Entry | | | ‘Gtk::SpinButton | | ‘Gtk::Text g

| +Gtk::Ruler | | +Gtk::HRuler | | ‘Gtk::VRuler | +Gtk::Range | | +Gtk::Scale | | | +Gtk::HScale | | | ‘Gtk::VScale | | ‘Gtk::Scrollbar | | +Gtk::HScrollbar | | ‘Gtk::VScrollbar | +Gtk::Separator | | +Gtk::HSeparator | | ‘Gtk::VSeparator | +Gtk::Preview | ‘Gtk::Progress | ‘Gtk::ProgressBar +Gtk::Data | +Gtk::Adjustment | ‘Gtk::Tooltips ‘Gtk::ItemFactory

4.4 Widgets Without Windows

The following widgets do not have an associated window; they therefore do not receive events. If you want to capture events for these widgets, you can use a special container called Gtk::EventBox, which is described in the section 10.3 (EventBox).

Gtk::Alignment Gtk::Arrow Gtk::Bin Gtk::Box Gtk::Image Gtk::Item Gtk::Label Gtk::Pixmap Gtk::ScrolledWindow Gtk::Separator Gtk::Table Gtk::AspectFrame Gtk::Frame Gtk::VBox Gtk::HBox Gtk::VSeparator Gtk::HSeparator

These widgets are mainly used for decoration, so you won’t often need to capture events on them.

5 The Button Widget

We’ve already seen quite a bit of the button widget. We’ve been using it rather heavily in our examples so far, but the chances are that you’ll use it rather heavily throughout your GTK– career, so it’s a useful thing to introduce early on. This chapter provides a more thorough discussion of this important widget. g

GTK– provides four basic types of buttons:

Pushbuttons (class: Gtk::Button) Standard buttons, usually marked with a label or picture; usually they cause something to happen when you press them. See 5.1 (Pushbuttons).

Toggle buttons (class: Gtk::ToggleButton) These look like pushbuttons, but act differently: when you press a Pushbutton, it "springs" back up; a toggle button stays down until you press it again. Useful when you need an on/off switch. See 5.2 (Toggle Buttons).

Checkboxes (class: Gtk::CheckButton) These are buttons which act like toggle buttons, but are much smaller. They are usually marked with a label off to the side. They’re useful in all sorts of situations where you need an on/off setting. Whether you use this or a toggle button is a matter of the situation, the amount of space you have, personal preference, etc. See 5.3 (Checkboxes).

Radio buttons (class: Gtk::RadioButton) Named after the station-selectors on old car radios, these buttons travel in groups; pressing one causes all the others in its group to turn off. They are similar in form to Checkboxes (i.e., small control with a label to the side), but usually look different. Radio buttons are a good way to control options which are mutually exclusive. See 5.4 (Radio Buttons).

Note that, because of GTK’s theming system, the appearance of these widgets will vary. In the case of checkboxes and radio buttons, they may vary considerably.

5.1 Pushbuttons

There are two ways to create a pushbutton: you can pass a string to the Gtk::Button constructor to create a button with a label, or pass no argument to it to create a blank button. If you make a blank button, you can pack a label or pixmap (icon or picture) into it. You can also put another container widget into it, and place more widgets inside that. (This makes it possible to construct rather unusual layouts - imagine packing a button inside another button!) Here’s an example of creating a button with a picture and a label in it. We’ve broken up the code to make it easy for you to use in your own programs. Source location: examples/buttons/buttons.cc

#include #include #include #include

using std::cout;

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

class Buttons : public Gtk::Window { public: Gtk::Button *m_button; g

Buttons(); ~Buttons();

void callback(char *data) { cout << "Hello again - " << data << " was pressed" << endl; }

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

};

Buttons::Buttons() { m_button = manage(new Gtk::Button()); m_button -> add_pixlabel("info.xpm", "cool button");

set_title("Pixmap’d buttons!"); set_border_width(10);

m_button->clicked.connect(bind(slot(this, &Buttons::callback), "cool button"));

add(*m_button); show_all(); }

Buttons::~Buttons() {};

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

Buttons buttons;

kit.run(); return 0; }

Note that the XPMLabelBox class can be used to place XPMs and labels into any widget that can be a container. The Gtk::Button widget has the following signals: pressed Emitted when the button is pressed. released Emitted when the button is released. clicked Emitted when the button is pressed and released. enter g

Emitted when the mouse pointer moves over the button’s window. leave Emitted when the mouse pointer leaves the button’s window.

5.2 Toggle Buttons

The Gtk::ToggleButton constructors are:

Gtk::ToggleButton(); Gtk::ToggleButton(const string &label);

These work exactly the same as the Gtk::Button constructors. To retrieve the state of the toggle button, you can use the method Gtk::ToggleButton::get_active();this returns true if the button is "down". You can also set the toggle button’s state; the member function to use for this is:

void Gtk::ToggleButton::set_active(bool state);

Pass true to this method to force the button "down". Note that if you do this, and the state is actually changed, it causes the "clicked" signal to be emitted. This is usually what you want. You can use the following function to toggle the button, rather than forcing it to be up or down:

void Gtk::ToggleButton::toggled ();

This switches the button’s state, and causes the toggled signal to be emitted.

5.3 Checkboxes

Gtk::CheckButton inherits from Gtk::ToggleButton. The only real difference between the two is Gtk::CheckButton’s appearance; you can check, set, and toggle a checkbox using the same member functions as you would for Gtk::ToggleButton. The two Gtk::CheckButton constructors are

Gtk::CheckButton(); Gtk::CheckButton(const string &label);

and they work exactly as the ones for Gtk::Button.

5.4 Radio Buttons

Like checkboxes, radio buttons also inherit from Gtk::ToggleButton, but there are more substantial differences than mere appearance, due to their travelling in groups (you could have a radio button by itself, but they’re very social beings, and get lonely rather easily. Also, it doesn’t make much sense). The constructors for Gtk::RadioButton are:

Gtk::RadioButton(); Gtk::RadioButton(const string &label, gfloat xalign=0.5, gfloat yalign=0.5); Gtk::RadioButton(Group &groupx); Gtk::RadioButton(Group &groupx, const string &label, gfloat xalign=0.5, gfloat yalign=0.5); g

The first constructor makes a new radio button without a label; the second lets you specify a label, and also allows you to tweak the positioning of the label text, if you want. The third and fourth constructors are like the first two, except you also specify a group to put the new buttons in. (Of course, the group must exist first; read on to see how this works.) There are two ways to set up a group of radio buttons. The first way is to create the buttons, and set up their groups afterwards. Only the first two constructors are used. In the following example, we make a new window class called RadioButtons, and then put three radio buttons in it:

class RadioButtons : public Gtk::Window { Gtk::RadioButton m_rb1, m_rb2, m_rb3; RadioButtons(); };

RadioButtons::RadioButtons() : Window(), m_rb1("button1"), m_rb2("button2"), m_rb3("button3") { m_rb2.set_group(m_rb1.group()); m_rb3.set_group(m_rb2.group()); }

When a radio button is created without a group, a new group is made for it; this group can be retrieved through the radio button’s group() method. A radio button’s group can be changed using the set_group() method. In the example, we created three radio buttons without specifying groups for them. We then used set_group() on radio button m_rb2 to set its group to the same as m_rb1, and did likewise with m_rb3. The result is that all three buttons belong to the same group. The second way to set up radio buttons is to make a group first, and then add radio buttons to it. Here’s an example:

class RadioButtons : public Gtk::Window { RadioButtons(); };

RadioButtons::RadioButtons() : Window() { Gtk::RadioButton::Group gr; Gtk::RadioButton *m_rb1=manage( new Gtk::RadioButton(gr,"button1")); Gtk::RadioButton *m_rb2=manage( new Gtk::RadioButton(gr,"button2")); Gtk::RadioButton *m_rb3=manage( new Gtk::RadioButton(gr,"button3")); }

We made a new group by simply declaring a variable, gr, of type Gtk::RadioButton::Group.Thenwemade three radio buttons, using the third constructor to make each of them part of gr. Radio buttons are "off" when created; this means that when you first make a group of them, they’ll all be off. Often this is not what you want (or what the user expects), so don’t forget to turn one of them on using set_active():

void Gtk::ToggleButton::set_active(bool state); g

This function is inherited from Gtk::ToggleButton, and works the same. The following is an example of using radio buttons: Source location: examples/radiobuttons/radiobuttons.cc

#include #include #include #include #include #include // Gtk-- version of the radiobuttons example from the gtk+ tutorial

class RadioButtons : public Gtk::Window { public:

Gtk::VBox m_box1, m_box2, m_box3; Gtk::RadioButton m_rb1, m_rb2, m_rb3; Gtk::HSeparator m_seperator; Gtk::Button m_closeButton;

RadioButtons();

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

void close_application() { delete_event_impl(0); }

};

RadioButtons::RadioButtons() : m_box1(false, 0), m_box2(false, 10), m_box3(false, 10), m_rb1("button1"), m_rb2("button2"), m_rb3("button3"), m_closeButton("close") { set_title("radio buttons"); set_border_width(0);

m_rb2.set_group(m_rb1.group()); m_rb3.set_group(m_rb1.group());

add(m_box1);

m_box2.set_border_width(10); m_box1.pack_start(m_box2); j

m_box2.pack_start(m_rb1); m_box2.pack_start(m_rb2); m_rb2.set_active(true);

m_box2.pack_start(m_rb3);

m_box1.pack_start(m_seperator, false, true, 0);

m_box3.set_border_width(10); m_box1.pack_start(m_box3, false, true, 0);

m_closeButton.clicked.connect(slot(this,&RadioButtons::close_application));

m_box3.pack_start(m_closeButton); m_closeButton.set_flags(GTK_CAN_DEFAULT); m_closeButton.grab_default();

show_all(); }

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 myapp(&argc, &argv);

RadioButtons radiobuttons;

myapp.run(); return 0; }

6 Adjustments

GTK– has various widgets that can be visually adjusted by the user using the mouse or the keyboard, such as the range widgets (described in the section 7 (Range Widgets)). There are also a few widgets that display some adjustable portion of a larger area of data, such as the text widget and the viewport widget. Obviously, an application needs to be able to react to changes the user makes in range widgets. One way to do this would be to have each widget emit its own type of signal when its adjustment changes, and either pass the new value to the signal handler, or require it to look inside the widget’s data structure in order to ascertain the value. But you may also want to connect the adjustments of several widgets together, so that adjusting one adjusts the others. The most obvious example of this is connecting a scrollbar to a panning viewport or a scrolling text area. If each widget has its own way of setting or getting the adjustment value, then the programmer may have to write his own signal handlers to translate between the output of one widget’s signal and the "input" of another’s adjustment setting function. GTK– solves this problem using the Gtk::Adjustment object, which is a way for widgets to store and pass adjustment information in an abstract and flexible form. The most obvious use of Gtk::Adjustment is to store the configuration parameters and values of range widgets, such as scrollbars and scale controls. However, since Gtk::Adjustments are derived from Gtk::Object, they have some special powers beyond those of normal classes. Most importantly, they can emit signals, just like widgets, and these signals can be used not only to allow your program to react to user input on adjustable widgets, but also to propagate adjustment values transparently between j

adjustable widgets.

6.1 Creating an Adjustment

The Gtk::Adjustment constructor is as follows:

Gtk::Adjustment( gfloat value, gfloat lower, gfloat upper, gfloat step_increment=1, gfloat page_increment=10, gfloat page_size=0);

The value argument is the initial value you want to give to the adjustment, usually corresponding to the topmost or leftmost position of an adjustable widget. The lower argument specifies the lowest value which the adjustment can hold. 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. The page_size argument usually corresponds somehow to the visible area of a panning widget. The upper argument is used to represent the bottom most or right most coordinate in a panning widget’s child. Therefore it is not always the largest number that value can take, since the page_size of such widgets is usually non-zero.

6.2 Using Adjustments the Easy Way

The adjustable widgets can be roughly divided into those which use and require specific units for these values, and those which treat them as arbitrary numbers. The group which treats the values as arbitrary numbers includes the range widgets (scrollbars and scales, the progress bar widget, and the spin-button widget). These widgets are all the widgets which are typically "adjusted" directly by the user with the mouse or keyboard. They will treat thelower and upper values of an adjustment as a range within which the user can manipulate the adjustment’s value. By default, they will only modify the value of an adjustment. The other group includes the text widget, the viewport widget, the compound list widget, and the scrolled window widget. All of these widgets use pixel values for their adjustments. These are also all widgets which are typically "adjusted" indirectly using scrollbars. While all widgets which use adjustments can either create their own adjustments or use ones you supply, you’ll generally want to let this particular category of widgets create its own adjustments. Usually, they will eventually override all the values except the value itself in whatever adjustments you give them, but the results are, in general, undefined (meaning, you’ll have to read the source code to find out, and it may be different from widget to widget). Now, you’re probably thinking that since text widgets and viewports insist on setting everything except the value of their adjustments, while scrollbars will only touch the adjustment’s value, if you share an adjustment object between a scrollbar and a text widget, will manipulating the scrollbar will automagically adjust the text widget? Of course it will! You can set it up like this:

// creates its own adjustments Gtk::Text text(0, 0); // uses the newly-created adjustment for the scrollbar as well Gtk::VScrollbar vscrollbar (*(text.get_vadjustment()));

6.3 Adjustment Internals

OK, you say, that’s nice, but what if I want to create my own handlers to respond when the user adjusts a range widget or a spin button, and how do I get at the value of the adjustment in these handlers? To access the value of a j

Gtk::Adjustment, you can use the following method:

gfloat Gtk::Adjustment::get_value()

and its counterpart to set the value:

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

As mentioned earlier, Gtk::Adjustment is a subclass of Gtk::Object just like all the other GTK– widgets, and thus it is able to emit signals. This is, of course, why updates happen automagically when you share an ad- justment object between a scrollbar and another adjustable widget; all adjustable widgets connect signal handlers to their adjustment’s value_changed signal, as can your program. Here’s the definition of this signal in struct _GtkAdjustmentClass:

void value_changed ();

The various widgets that use the Gtk::Adjustment object will emit this signal on an adjustment whenever they change its value. This happens both when user input causes the slider to move on a range widget, and 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 (Gtk::Widget *picture) { picture->set_rotation (adj->value); ...

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

adj.value_changed.connect(bind(slot(&cb_rotate_picture),picture));

What about when a widget reconfigures the upper or lower fields of its adjustment, such as when a user adds more text to a text widget? In this case, it emits the changed signal, which looks like this:

void changed();

Range widgets typically connect a handler to this signal, which changes their appearance to reflect the change - for example, the size of the slider in a scrollbar will grow or shrink in inverse proportion to the difference between the lower and upper values of its adjustment. You probably won’t ever need to attach a handler to this signal, unless you’re writing a new type of range widget. However, if you change any of the values in a Gtk::Adjustment directly, you should emit this signal on it to reconfigure whatever widgets are using it, like this:

adjustment->changed();

Now go forth and adjust! gg

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 generally used for different purposes, they are quite similar in function and imple- mentation. 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, depending on which mouse button is used. As mentioned in 6 (Adjustments) above, all range widgets are associated with an adjustment object, from which they calculate the length of the slider and its position within the trough. When the user manipulates the slider, the range widget will change the value of the adjustment.

7.1 Scrollbar Widgets

These are your standard, run-of-the-mill scrollbars. These should be used only for scrolling some other widget, such as a list, a text box, or a viewport (and it’s generally easier to use the scrolled window widget in most cases). For other purposes, you should use scale widgets, as they are friendlier and more featureful. There are separate types for horizontal and vertical scrollbars. There really isn’t much to say about these. You create them with the following constructors:

Gtk::HScrollbar( Gtk::Adjustment &adjustment ); Gtk::HScrollbar();

Gtk::VScrollbar( GtkAdjustment &adjustment ); Gtk::VScrollbar(); and that’s about it (if you don’t believe me, look in the header files!). You can either pass a reference to an existing adjustment as argument, or no argument at all, in which case one will be created for you. Not specifying an argument might actually be useful in this case, if you wish to pass the newly-created adjustment to the constructor function of some other widget which will configure it for you, such as a text widget. You can fetch the object’s adjustment using the following method:

Gtk::Adjustment* GtkRange::get_adjustment() > Gtk::Scrollbar derives from Gtk::Range; it’s defined in

7.2 Scale Widgets

Scale widgets (or "sliders") are used to allow the user to visually select and manipulate a value within a specific range. You might want to use a scale widget, for example, to adjust the magnification level on a zoomed preview of a picture, or to control the brightness of a colour, or to specify the number of minutes of inactivity before a screensaver takes over the screen.

7.2.1 Creating a Scale Widget

As with scrollbars, there are separate widget types for horizontal and vertical scale widgets. (Most programmers seem to favour horizontal scale widgets). Since they work essentially the same way, there’s no need to treat them separately here. Here are the constructors for vertical and horizontal scale widgets, respectively: gg

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

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

As with scrollbars, you can either pass an existing adjustment as argument, or no argument at all, in which case an anonymous Gtk::Adjustment is created with all of its values set to 0.0 (which isn’t very useful in this case). In order to avoid confusing yourself, you probably want to create your adjustment with a page_size of 0.0 so that its upper value actually corresponds to the highest value the user can select. (If you’re already thoroughly confused, go back and read the section on 6 (Adjustments) again (you did read it already, didn’t you?) for an explanation of what exactly adjustments do and how to create and manipulate them).

7.2.2 Methods and Signals (well, methods, at least)

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(bool draw_value);

The value displayed by a scale widget is rounded to one decimal point by default, as is the value field in its Gtk::Adjustment. You can change this with:

void Gtk::Scale::set_digits(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. Finally, the value can be drawn in different positions relative to the trough:

void Gtk::Scale::set_value_pos(GtkPositionType pos);

The argument pos can be one of the following:

¯ GTK_POS_LEFT

¯ GTK_POS_RIGHT

¯ GTK_POS_TOP

¯ GTK_POS_BOTTOM

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. > The Gtk::Scale class is defined in

7.3 Common Functions

The Gtk::Range 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 methods and signals it defines are

only really used in writing derived widgets. There are, however, a few useful methods that are defined in

/range.h> and will work on all range widgets. gg

7.3.1 Setting the Update Policy

The update policy of a range widget defines at what points during user interaction it will change the value field of

its Gtk::Adjustment and emit the value_changed signal on this Gtk::Adjustment. The update policies, > defined in

¯ GTK_UPDATE_POLICY_CONTINUOUS - This is the default. The value_changed signal is emitted con- tinuously, i.e. whenever the slider is moved by even the tiniest amount.

¯ GTK_UPDATE_POLICY_DISCONTINUOUS -Thevalue_changed signal is only emitted once the slider has stopped moving and the user has released the mouse button.

¯ GTK_UPDATE_POLICY_DELAYED -Thevalue_changed signal is emitted when the user releases the mouse button, or if the slider stops moving for a short period of time.

The update policy of a range widget can be set using this method:

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

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

Gtk::Adjustment* Gtk::Range::get_adjustment(); void Gtk::Range::set_adjustment(Gtk::Adjustment *adjustment);

7.4 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 mouse 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. It may take a little while to get used to, but by default, scrollbars as well as scale widgets can take the keyboard focus in GTK–. If you think your users (or you!) will find this too confusing, you can always disable this by unsetting the GTK_CAN_FOCUS flag on the scrollbar, like this:

scrollbar.unset_flag(GTK_CAN_FOCUS);

The key bindings (which are, of course, only active when the widget has focus) 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).

7.4.1 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 Gtk::VScale widget, this is done with the Home and End keys, whereas with the Gtk::VScrollbar widget, it’s done by typing Control-Page Up and Control-Page Down. gg

7.4.2 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.TheHome and End keys move the slider to the ends of the trough. For the Gtk::HScale widget, moving the slider by page_increment is accomplished with Control-Left and Control-Right, while for Gtk::HScrollbar, it’s done with Control-Home and Control-End.

7.5 Example: Range widgets

This example displays a window with three range widgets all connected to the same adjustment, along with a couple of controls for adjusting some of the parameters mentioned above and in the section on adjustments, so you can see how they affect the way these widgets work for the user. Notice the use of subclassing in the example. We’ve made a "mini-widget" called LabeledOptionMenu which simplifies the creation of the two labelled option menus. Although, in this small example, we don’t really gain anything in terms of code size, for large programs the technique can quickly prove its worth. Source location: examples/rangewidgets/rangewidgets.cc

#include #include #include #include #include #include #include #include #include #include #include #include

// Gtk-- version of the range widgets example from the gtk+ tutorial

class LabeledOptionMenu : public Gtk::HBox { Gtk::Label m_label;

public: LabeledOptionMenu(const Gtk::string &menutitle, Gtk::Menu *menu, bool homogeneous=false, gint spacing=10); Gtk::Menu *m_menu; };

LabeledOptionMenu::LabeledOptionMenu(const Gtk::string &menutitle, Gtk::Menu *menu, bool homogeneous, gint spacing) : Gtk::HBox(homogeneous, spacing), m_label(menutitle), m_menu(menu) { pack_start(m_label, false, false, 0); Gtk::OptionMenu *om=manage(new Gtk::OptionMenu); gg

om->set_menu(m_menu); pack_start(*om); } class RangeControls : public Gtk::Window {

Gtk::VBox m_vbox1, m_vbox2, m_vbox3; Gtk::HBox m_hbox1, m_hbox2;

Gtk::Adjustment m_adj1;

Gtk::VScale m_vscale; Gtk::HScale m_hscale;

Gtk::HSeparator m_separator;

Gtk::Button m_buttonQuit; Gtk::CheckButton m_checkbutton;

Gtk::HScrollbar m_scrollbar; public:

RangeControls();

// callbacks void draw_value(Gtk::CheckButton *button); void menu_pos_select_cb(GtkPositionType type); void menu_update_select_cb(GtkUpdateType type); void digits_cb(Gtk::Adjustment *adj); void psize_cb(Gtk::Adjustment *adj); gint delete_event_impl(GdkEventAny*) { Gtk::Main::quit(); return 0; } };

RangeControls::RangeControls() : m_vbox1(false, 0), m_vbox2(false, 20), m_vbox3(false, 10),

m_hbox1(false, 10), m_hbox2(false, 10),

// 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). m_adj1(0.0, 0.0, 101.0, 0.1, 1.0, 1.0),

m_vscale(m_adj1),

m_hscale(m_adj1), gg

m_buttonQuit("Quit"), // a checkbutton to control whether the value is displayed or not m_checkbutton("Display value on scale widgets",0),

// reuse the same adjustment again m_scrollbar(m_adj1) // notice how this causes the scales to always be update // continuously when the scrollbar is moved { set_title("range controls");

m_vscale.set_update_policy(GTK_UPDATE_CONTINUOUS); m_vscale.set_digits(1); m_vscale.set_value_pos(GTK_POS_TOP); m_vscale.set_draw_value(true); m_hscale.set_update_policy(GTK_UPDATE_CONTINUOUS); m_hscale.set_digits(1); m_hscale.set_value_pos(GTK_POS_TOP); m_hscale.set_draw_value(true);

add(m_vbox1); m_vbox1.pack_start(m_vbox2); m_vbox2.set_border_width(10); m_vbox2.pack_start(m_hbox1); m_hbox1.pack_start(m_vscale); m_hbox1.pack_start(m_vbox3);

m_hscale.set_usize(200, 30); m_vbox3.pack_start(m_hscale);

m_scrollbar.set_update_policy(GTK_UPDATE_CONTINUOUS); m_vbox3.pack_start(m_scrollbar);

m_checkbutton.set_active(true); m_checkbutton.toggled.connect( bind(slot(this, &RangeControls::draw_value), &m_checkbutton)); m_vbox2.pack_start(m_checkbutton);

{ using namespace Gtk::Menu_Helpers;

Gtk::Menu *menu_vpos=manage(new Gtk::Menu); MenuList& list_vpos=menu_vpos->items(); list_vpos.push_back( MenuElem("Top",bind( slot(this,&RangeControls::menu_pos_select_cb),GTK_POS_TOP))); list_vpos.push_back( MenuElem("Bottom",bind( slot(this,&RangeControls::menu_pos_select_cb),GTK_POS_BOTTOM))); list_vpos.push_back( MenuElem("Left",bind( slot(this,&RangeControls::menu_pos_select_cb),GTK_POS_LEFT))); list_vpos.push_back( MenuElem("Right",bind( slot(this,&RangeControls::menu_pos_select_cb),GTK_POS_RIGHT))); gg

Gtk::Menu *menu_upd=manage(new Gtk::Menu); MenuList& list_upd=menu_upd->items(); list_upd.push_back( MenuElem("Continuous",bind( slot(this,&RangeControls::menu_update_select_cb),GTK_UPDATE_CONTINUOUS))); list_upd.push_back( MenuElem("Discontinuous",bind( slot(this,&RangeControls::menu_update_select_cb),GTK_UPDATE_DISCONTINUOUS))); list_upd.push_back( MenuElem("Delayed",bind( slot(this,&RangeControls::menu_update_select_cb),GTK_UPDATE_DELAYED)));

m_vbox2.pack_start( *manage(new LabeledOptionMenu("Scale Value Position:",menu_vpos))); m_vbox2.pack_start( *manage(new LabeledOptionMenu("Scale Update Policy:",menu_upd))); }

Gtk::HBox *lsbox1=manage(new Gtk::HBox(false,10)); lsbox1->pack_start(*manage(new Gtk::Label("Scale Digits:",0)),false,false); Gtk::Adjustment *adj1=manage(new Gtk::Adjustment(1.0, 0.0, 5.0)); Gtk::HScale *digits=manage(new Gtk::HScale(*adj1)); digits->set_digits(0); adj1->value_changed.connect(bind(slot(this,&RangeControls::digits_cb),adj1)); lsbox1->pack_start(*digits,true,true);

Gtk::HBox *lsbox2=manage(new Gtk::HBox(false,10)); lsbox2->pack_start(*manage(new Gtk::Label("Scrollbar Page Size:",0)),false,false); Gtk::Adjustment *adj2=manage(new Gtk::Adjustment(1.0, 1.0, 101.0)); Gtk::HScale *pgsize=manage(new Gtk::HScale(*adj2)); pgsize->set_digits(0); adj2->value_changed.connect(bind(slot(this,&RangeControls::psize_cb),adj2)); lsbox2->pack_start(*pgsize,true,true);

m_vbox2.pack_start(*lsbox1); m_vbox2.pack_start(*lsbox2); m_vbox1.pack_start(m_separator, false, true, 0); m_vbox1.pack_start(m_buttonQuit,false,false); m_buttonQuit.set_flags(GTK_CAN_DEFAULT); m_buttonQuit.grab_default(); m_buttonQuit.clicked.connect(Gtk::Main::quit.slot()); m_buttonQuit.set_border_width(10); show_all(); } void RangeControls::draw_value(Gtk::CheckButton *button) { m_vscale.set_draw_value(button->get_active()); m_hscale.set_draw_value(button->get_active()); } void RangeControls::menu_pos_select_cb(GtkPositionType postype) { m_vscale.set_value_pos(postype); g

m_hscale.set_value_pos(postype); }

void RangeControls::menu_update_select_cb(GtkUpdateType type) { m_vscale.set_update_policy(type); m_hscale.set_update_policy(type); }

void RangeControls::digits_cb(Gtk::Adjustment *adj) { gfloat val=adj->get_value(); m_vscale.set_digits(val); m_hscale.set_digits(val); }

void RangeControls::psize_cb(Gtk::Adjustment *adj) { Gtk::Adjustment *swadj=m_scrollbar.get_adjustment(); gfloat val=adj->get_value(); swadj->set_page_size(val); swadj->set_page_increment(val);

// note that we don’t have to emit the "changed" signal; // GTK-- does this for us }

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

myapp.run(); return 0; }

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 10.3 (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 g

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:

¯ GTK_JUSTIFY_LEFT

¯ GTK_JUSTIFY_RIGHT

¯ GTK_JUSTIFY_CENTER (the default)

¯ GTK_JUSTIFY_FILL

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

"__ __" g

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 demon- strate the label styles. It also gives some indication of what GTK– developers do in their off-hours. (The Frame widget is explained in the 10.1 (Frame) section.) Source location: examples/label/label.cc

#include #include #include #include #include

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" \ g

"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 la- bel.\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 la- bel. It " \ "should not be taking up the entire " /* big space to test ing */\ "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 la- bel. " \ "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); g

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:

¯ GTK_ARROW_UP

¯ GTK_ARROW_DOWN

¯ GTK_ARROW_LEFT

¯ GTK_ARROW_RIGHT g

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

¯ GTK_SHADOW_IN

¯ GTK_SHADOW_OUT (the default)

¯ GTK_SHADOW_ETCHED_IN

¯ GTK_SHADOW_ETCHED_OUT

Here’s a brief example illustrating the use of arrows: Source location: examples/arrow/arrow.cc

#include #include #include #include #include

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() g

/* 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(); g

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: g

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:

¯ GTK_PROGRESS_LEFT_TO_RIGHT

¯ GTK_PROGRESS_RIGHT_TO_LEFT

¯ GTK_PROGRESS_BOTTOM_TO_TOP

¯ GTK_PROGRESS_TOP_TO_BOTTOM

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:

¯ GTK_PROGRESS_CONTINUOUS

¯ GTK_PROGRESS_DISCRETE

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: g

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:

¯ %p - percentage

¯ %v - value

¯ %l - lower range value

¯ %u - upper range value

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); g

The last of these takes a number falling between the minimum and maximum values of the progress object’s adjust- ment, and returns the percentage to pass to set_percentage. Progress bars are often used with idle timeouts, described in the section 15 (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 #include #include #include #include #include

// 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); g

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(); g

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 ); g

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 trans- parent_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. ", " ...... ", "", " "}; g

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 #include #include #include #include

// 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. ", " ...... ", "", "" }; g

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; some- times 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 #include g

#include #include #include

//From 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

"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+ > ", "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*>", " OA1666

" p71=4 m69996kD8Z-66698&& ", " &i0ycm6n4 ogk17,0<6666g ", " N-k-<> >=01-kuu666> ", " ,6ky& &46-10ul,66, ", " Ou0<> o66y66By7=xu664 ", " <> +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(); } g

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 measure- ment!).

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); g

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 #include #include #include #include

// 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, g

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); g

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 #include #include #include #include

// 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; } g

};

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); g

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 ); g

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 #include #include #include #include #include #include

// 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); g

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 6 (Adjustment) object to hold information about the range of values they can have. Recall that an adjustment widget is created with the following constructor: g

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:

¯ value: initial value for the Spin Button

¯ lower: lower range value

¯ upper: upper range value

¯ step_increment: value to increment/decrement when pressing mouse button 1 on a button

¯ page_increment: value to increment/decrement when pressing mouse button 2 on a button

¯ page_size: unused

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 ); g

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.Ifincrement 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

¯ GTK_SHADOW_IN

¯ GTK_SHADOW_OUT

¯ GTK_SHADOW_ETCHED_IN

¯ GTK_SHADOW_ETCHED_OUT g

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 #include #include #include #include #include #include #include #include

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. */ g

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) ); g

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); g

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); g

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 gl;

gl.push_back("String 1"); gl.push_back("String 2"); g

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

combo.set_popdown_strings(glist);

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 contain- ing a Gtk::ColorSelection widget, a Gtk::HSeparator,andaGtk::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 g

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:

¯ Gtk::HBox* get_action_area();

¯ Gtk::Entry* get_selection_entry();

¯ Gtk::Label* get_selection_text(); g

¯ Gtk::Button* get_ok_button();

¯ Gtk::Button* get_cancel_button();

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 #include #include

//From 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; g

/* 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 */

9 Container widgets in GTK–

Some GUI toolkits focus on the ability to place arbitrary widgets on a display area. Container widgets are often some- thing of an afterthought in these toolkits; they require special handling and special, sometimes obtuse, programming techniques. GTK takes a different approach. Not only does it give you container widgets, almost every widget in it is a container of some sort. Widget containment and hierarchy are fundamental parts of the GTK design; understanding how GTK containment widgets work is therefore essential to understanding the GTK– toolkit. We’ve already covered many things about this aspect of GTK/GTK–. Now it’s time to discuss GTK–’s container widget system in detail.

9.1 The GTK– container widget system

Building a hierarchical widget system is not easy. Making it simple to use is downright difficult. GTK– breaks the problem down by defining two basic types of containers:

¯ Single-item containers can contain at most one widget. They all descend from the Gtk::Bin class.

¯ Multiple-item containers can contain zero or more widgets. They all descend from the Gtk::Container class.

It’s common for newcomers to GTK– to wonder what the purpose of the Gtk::Bin class is. Why not use multi-item containers for everything? Knowing the answer to this question is an important key to understanding GTK/GTK–. First, let’s consider the alternative: making all containers multi-item. This approach is taken by most toolkits that implement some sort of hierarchy for their widgets. This is possible because, in general, such toolkits only implement a single placement policy for their widgets: what, in GTK–, is called the fixed policy, which leaves it up to the programmer to place widgets at the proper screen coordinates. GTK–, by contrast, provides multiple placement policies, implemented primarily by its multiple-item container wid- gets. There are many of these: GTK– provides horizontal boxes, vertical boxes, tables, packboxes, fixed layout areas, and others. This helps the programmer to easily construct clean-looking interfaces, without even using a form-painter. Now, if all GTK– containers were multiple-item, we’d have to answer some questions about each one, such as:

¯ What should the placement policy be?

¯ Should the policy be selectable?

¯ Should we make it possible to define new policies? What’s the best way to do this? g

¯ What widgets should be containers?

.. and so forth. These are just a few of the problems that might arise if we tried to dispense with the concept of single-item containers. The existence of the Gtk::Bin class enables GTK to sidestep these thorny issues. A single-item container in GTK– is not a second-class citizen, and it’s not really handicapped in any way. In fact, a Gtk::Bin container isn’t really limited to containing just one widget. If we place, in a Gtk::Bin, one of the multiple-item containers, our Gtk::Bin can contain more than one widget after all. Furthermore, we can select the placement policy for those widgets by merely using the proper multi-item container widget. Making a Gtk::Bin widget is much easier than making a multi-item container. We need deal with only one child, so we don’t have to write code to handle multiple children, or provide any sort of access to them. We don’t need to worry about placing or displaying the children; that will be taken care of by a multi-item child. This means that it’s practical to make many widgets containers which in other toolkits wouldn’t be. Take the lowly button widget, for example. Very few - if any - toolkits consider buttons to be a container widget. GTK–, however, does, and it’s almost too easy not to: by simply making a button a derivative of Gtk::Bin, it doesn’t even have to provide code for displaying a label or picture; this can be done by a child pixmap or label widget. A Gtk::Button can even contain both at the same time, using any placement policy the programmer desires. Consider how impractical this would be without Gtk::Bin! Of course, sometimes you don’t want multiple children in a single-item container widget; one, or even zero, is enough. Most buttons only contain one child, that being a label or a picture. Because it doesn’t require its container widgets to support multiple children, GTK lets you avoid paying for what you don’t use. Now consider the GTK– window widget. We’ve already seen that GTK windows can contain at most one child. Now you know that, far from being a limitation, this is actually a strength of GTK, thanks to the Gtk::Bin concept. We summarise this section with a profound statement that composer Brian Eno once made:

"Let your limitations be your secret strengths."

... which GTK certainly does, in this case.

9.2 Using Gtk::Bin widgets

Gtk::Bin derives from a class called Gtk::Container, which is a base class for all widgets which contain other widgets. It provides virtual methods for operations such as adding and removing children and obtaining contained widgets, among other things. Unlike human parents, Gtk::Bin-derived widgets don’t have much to take care of when it comes to their only children. Gtk::Bin widgets use the following two methods to add and remove their child widgets:

void Gtk::Bin::add(Gtk::Widget& p0); void Gtk::Bin::remove();

Gtk::Bin also provides some convenience functions for adding labels and pixmaps (a common thing to do with Gtk::Bin widgets):

void add_label(const string &label, gfloat x=0.0, gfloat y=0.5); void add_pixmap(const Gdk_Pixmap &pixmap, const Gdk_Bitmap &bitmap); void add_pixlabel(const Gdk_Pixmap &pixmap, const Gdk_Bitmap &bitmap, const string &label, gfloat x=0.0, gfloat y=0.5); void add_pixlabel(const string &pixfile, const string &label, gfloat x=0.0, gfloat y=0.5);

These are similar to the constructors for the 8.6 (Pixmap) and 8.1 (Label) widgets; see those sections for details. g

9.3 Understanding the GTK– multiple-item widgets

Multiple-item widgets inherit from Gtk::Container; just as with Gtk::Bin, you use the add() and re- move() methods to add and remove contained widgets. Unlike Gtk::Bin::remove(),however,theremove() method for Gtk::Container takes an argument:

void Gtk::Container::add(Gtk::Widget& p0); void Gtk::Container::remove(Gtk::Widget& p0);

The argument for remove() specifies which widget to remove, since there can be more than one. Multiple-item containers have a bit more to do than Gtk::Bin widgets, since they can have more than one child (something which those of you with families can no doubt appreciate). The add() and remove() methods give you a way to put things in and take things out, but what if you need to access the contained widgets (which you probably will)? If you’re an accomplished C++ programmer, you’ll be happy to hear that most of the GTK– widgets which maintain lists of things do so STL-style (if you don’t know STL, learn it!). They don’t necessarily use actual STL containers (there are good reasons for this), but the lists they use look, feel, and act, for the most part, just like STL container classes. There is, however, a major difference between GTK– containers and STL containers. Normally, when you use a

vector, for example, you expect that whatever you put in, you’ll get out, unmodified. You wouldn’t make a > vector

¯ A child widget (zero or one), to be placed in the page

¯ A label for the page’s tab

(The GTK– notebook widget keeps other data for each page as well.) To insert a new page in a notebook, we can use one of the notebook helper classes, like this:

notebook->pages().push_back( Gtk::Notebook_Helpers::TabElem(*frame,bufferl));

(This line comes from the notebook example program.) Let’s see what’s going on here. Assume we have a pointer to a Notebook widget called notebook; we go from that to a member function called pages(), which returns an g

STL-like list object. On this we call the function push_back() (this should be familiar to those of you who know STL). The object that the pages() method returns is called a Notebook_Helpers::PageList. It’s one of the STL-

like containers that we keep referring to. Let’s take a look at some of the parts of this class (this has been heavily > edited for clarity; see

namespace Notebook_Helpers { class PageList { public: ... void push_back(const Element& e); ... Page* operator[](size_type l); }; };

There are two important things to notice here:

¯ The push_back() method takes as argument an Element object (helper);

¯ The overloaded [] operator returns .. a pointer to a Page!

So what does a PageList actually hold? It holds (as far as the programmer is concerned) Pages, objects which contain all the necessary data about a notebook page. The twist with using the Notebook widget (and most other GTK– multi-item containers) is that you aren’t allowed to construct Page objects, and then insert them; you have to insert Element objects instead. The reasons for this are numerous, and mainly involve the technical aspects of wrapping the GTK+ lists in C++ classes; the specifics don’t concern us here. This scheme of storing different objects than we put in has some important advantages:

¯ We can provide as many different Helper objects as desired, making it simple to construct complex widgets like Menus.

¯ Construction of the actual objects can be performed by the list at any appropriate time, enabling us to sidestep certain difficulties with GTK+.

¯ The definitions of the objects contained in the list can change; their interfaces need not concern the program- mer. For example, even if the Page object changes drastically, the programmer need not be concerned; the Elements need not change, and will continue to work as expected.

¯ New Element objects can be added at any time to support new features, without breaking existing code.

Not bad, eh? Perhaps the design isn’t so odd after all!

9.4 Using the GTK– multiple-item widgets

Each multiple-item container in GTK– (with a few exceptions) contains a GTK– list, which holds information for the contained elements. These lists work almost exactly like ordered STL containers - so much so, in fact, that rather than explaining them in detail, we can refer you to the STL documentation for most of their methods (saving ourselves a lot of typing in the process!). At minimum, GTK– container lists support iterators and the usual complement of insertion, deletion, and addition methods. You can always expect the following methods to be available for GTK– lists: g

¯ begin() returns a begin iterator

¯ end() returns an end iterator

¯ rbegin() returns a reverse begin iterator

¯ rend() returns a reverse end iterator

¯ size()

¯ max_size()

¯ empty()

¯ insert()

¯ push_front()

¯ push_back()

¯ pop_front()

¯ pop_back()

¯ clear()

¯ erase()

¯ remove()

¯ find()

¯ front()

¯ back()

Also, the [] operator is overloaded; note that it’s usually order N, so if performance is a consideration, or the list has a large number of elements, think carefully before using it. The element objects and list objects are defined, for each container, in a namespace whose name ends in _Helpers. For example, the helper namespace for the notebook widget is called Notebook_Helpers. Several element types are usually provided; the Notebook widget provides three:

¯ Gtk::Notebook_Helpers::Element

¯ Gtk::Notebook_Helpers::TabElem

¯ Gtk::Notebook_Helpers::MenuElem

TabElem and MenuElem inherit from Element; the insertion functions take an Element reference as argument. All multi-item containers have an Element object in their helper namespaces; usually there are additional "sub- element" objects available (like TabElem and MenuElem) which derive from Element. Element classes vary from container to container, since each container contains different kinds of objects; we’ll document these as we come to them. A common point of confusion with new GTK– users is over the nature of Elements. It’s very important to remember that Elementsarenot "real" objects; they exist only temporarily, and they’re never stored by multi-item widgets. They are used only as temporary "parameter-holders". Therefore, the following segment of code is illegal: g

MenuElem *m= new MenuElem("hello"); m->right_justify(); items().push_back(*m);

We constructed a new MenuElem helper object, and then tried to invoke right_justify() on it before adding it to the menu. The trouble is that there is no right_justify() method in the MenuElem class! The correct way to accomplish this would be:

items().push_back(MenuElem("hello")); items().back()->right_justify();

Here, we’ve constructed a MenuElem and inserted it into the menu by passing it to push_back(), causing the real menu item to be created. We’ve then called right_justify() on the object retrieved from the list. This is correct; the object retrieved from the list is not a MenuElem,butaMenuItem, which is a "real" menu item object, and therefore supports the right_justify() method as expected. (Fortunately, the right way is actually shorter than the wrong way, in this case!)

9.5 Container widgets: a rogues’ gallery

In the next two chapters, we’ll be looking at each of GTK–’s container widgets in detail. The Gtk::Bin container widgets we’ll be discussing are:

¯ Frames

¯ Aspect frames

¯ Event boxes

¯ The Alignment widget

¯ Viewports

¯ Scrolled windows

¯ Panes

We’ve already encountered some Gtk::Bin widgets: buttons (all types), and windows. Panes are a special case; they actually contain two child widgets, but since they don’t really maintain a list of children, we’ll discuss them along with the single-child widgets. The multiple-child containers we’ll discuss are:

¯ VBoxes and HBoxes

¯ Packers

¯ Tables

¯ The Layout & Fixed widgets

¯ Button boxes

¯ Trees & CTrees

¯ Lists & CLists gg

¯ Toolbars

¯ Menus

¯ Notebooks

Of that list, there are three widgets that do not use STL-like container lists: button boxes, the Layout widget, and the Fixed widget.

10 Single-item widgets

In this chapter, we discuss the single-item (i.e., derived from Gtk::Bin) container widgets, with the exception of the Button and Window widgets, which we’ve already covered. We also discuss the Gtk::Paned widget, which allows you to divide a window into two separate "panes". This widget actually contains two child widgets, but it doesn’t maintain a list, so we’ve included it here.

10.1 Frames

Frames can be used to enclose one or a group of widgets with a box which can optionally be labelled. The position of the label and the style of the box can be altered to suit. The Gtk::Frame constructor is:

Gtk::Frame(const nstring &label);

The label is by default placed in the upper left-hand corner of the frame. A value of NULL for the label argument will result in no label being displayed. The text of the label can be changed using:

void Gtk::Frame::set_label(const nstring &label);

The position of the label can be changed using:

void Gtk::Frame::set_label_align(gfloat xalign, gfloat yalign);

xalign and yalign take values between 0.0 and 1.0. xalign indicates the position of the label along the top horizontal of the frame. A value of 0.0 will left-justify the label, 1.0 right-justifies it, and 0.5 centres it. Values in- between also work; for example, 0.25 will place the label a quarter of the way across the top. yalign is ignored. The default value of xalign is 0.0. You can alter the frame’s appearance using:

void Gtk::Frame::set_shadow_type(GtkShadowType type);

where type can is one of

¯ GTK_SHADOW_NONE

¯ GTK_SHADOW_IN

¯ GTK_SHADOW_OUT gg

¯ GTK_SHADOW_ETCHED_IN (the default)

¯ GTK_SHADOW_ETCHED_OUT

The following code example illustrates the use of the Frame widget. Source location: examples/frame/frame.cc

#include #include #include #include

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::Frame* frame;

/* Set some window properties */ set_title("Frame Example"); set_usize(300, 300);

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

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

/* Create a Frame */ frame = manage( new Gtk::Frame() ); add(*frame);

/* Set the frames label */ frame->set_label( "GTK Frame Widget" );

/* Align the label at the right of the frame */ frame->set_label_align( 1.0, 0.0);

/* Set the style of the frame */ frame->set_shadow_type(GTK_SHADOW_ETCHED_OUT); gg

/* Show the frame */ frame->show();

/* show this window */ show ();

}

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); }

10.2 Aspect Frames

The aspect frame widget is like a frame widget, except that it also enforces the aspect ratio (the ratio of the width to the height) of the child widget to have a certain value, adding extra space if necessary. This is useful, for instance, if you want to display a photograph. To create a new aspect frame, use:

Gtk::AspectFrame(const string label, gfloat xalign, gfloat yalign, gfloat ratio, gint obey_child);

xalign and yalign specify the alignment factors. If obey_child is true, the aspect ratio of a child widget will match the aspect ratio of the ideal size it requests. Otherwise, it is given by ratio. To change the options of an existing aspect frame, you can use:

void Gtk::AspectFrame::set(gfloat xalign, gfloat yalign, gfloat ratio, gint obey_child);

The following program uses a Gtk::AspectFrame to present a drawing area whose aspect ratio will always be 2:1, no matter how the user resizes the top-level window. Source location: examples/aspectframe/aspectframe.cc gg

/* example-start aspectframe aspectframe.cc */

#include #include #include #include class AspectWindow: public Gtk::Window { Gtk::AspectFrame frame; Gtk::DrawingArea area; public: AspectWindow(); private: gint delete_event_impl(GdkEventAny*) { Gtk::Main::quit(); return 0; } };

AspectWindow::AspectWindow(): frame("2x1", /* label */ 0.5, /* center x */ 0.5, /* center y */ 2, /* xsize/ysize = 2 */ FALSE /* ignore child’s aspect */) { set_title("Aspect Frame"); set_border_width(10);

/* Add a child widget to the aspect frame */ /* Ask for a 200x200 window, but the AspectFrame will give us a 200x100 * window since we are forcing a 2x1 aspect ratio */ area.set_usize(200, 200); frame.add(area); /* Create an aspect_frame and add it to our toplevel window */ frame.show_all(); add(frame); } int main (int argc, char *argv[]) { Gtk::Main m(&argc, &argv); AspectWindow window;

window.show(); m.run();

return 0; } /* example-end */ gg

10.3 The EventBox

Some Gtk– widgets don’t have associated X windows; they draw on their parents’ windows. (Of course, you should not let your own children do this.) Because of this, they cannot receive events. Also, if they are incorrectly sized, they don’t clip, so you can get messy overwriting etc. If you require more from these widgets, the EventBox is for you. Although the name EventBox emphasises the event-handling function, the widget can also be used for clipping (and more; see the example below). The constructor for Gtk::EventBox is:

Gtk::EventBox();

A child widget can be added to the EventBox using:

event_box.add(child_widget);

The following example demonstrates both uses of an EventBox - a label is created that is clipped to a small box, and set up so that a mouse-click on the label causes the program to exit. Resizing the window reveals varying amounts of the label. Source location: examples/eventbox/eventbox.cc

/* example-start eventbox eventbox.c */

#include #include #include #include #include

class AppWindow : public Gtk::Window { Gtk::Tooltips tips;

public: AppWindow(); ~AppWindow();

virtual gint delete_event_impl (GdkEventAny*); gint callback(GdkEventButton*) { Gtk::Main::quit(); return 1; } };

AppWindow::AppWindow() : Gtk::Window(GTK_WINDOW_TOPLEVEL) { Gtk::EventBox *event_box; Gtk::Label *label;

set_title ("Event Box"); set_border_width (10); gg

/* Create an EventBox and add it to our toplevel window */

event_box = manage( new Gtk::EventBox () ); add (*event_box);

/* Create a long label */

label = manage( new Gtk::Label ("Click here to quit, quit, quit, quit, quit") ); event_box-> add (*label);

/* Clip it short. */ label->set_usize (110, 20);

/* And bind an action to it */ event_box->set_events (GDK_BUTTON_PRESS_MASK); event_box->button_press_event.connect(slot(*this, &AppWindow::callback)); tips.set_tip(*event_box,"Click me!");

/* Yet one more thing you need an X window for ... */

event_box->realize (); // event_box->get_window().set_cursor (gdk_cursor_new (GDK_HAND1));

show_all (); }

AppWindow::~AppWindow() {}

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

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

Gtk::Main main (argc, argv); AppWindow window;

Gtk::Main::run();

return(0); } /* example-end */

10.4 The Alignment widget

The Alignment widget allows you to place a widget within its window at a position and size relative to the size of the Alignment widget itself. It’s useful for centering a widget within the window. gg

There is only one method associated with Gtk::Alignment, other than the constructor:

Gtk::Alignment(gfloat xalign, gfloat yalign, gfloat xscale, gfloat yscale);

void Gtk::Alignment::set(GtkAlignment *alignment, gfloat xalign, gfloat yalign, gfloat xscale, gfloat yscale);

set() allows the alignment parameters of an exisiting Alignment widget to be altered. All four alignment parameters are floating point numbers which can range from 0.0 to 1.0. The xalign and yalign arguments affect the position of the widget placed within the Alignment widget. The xscale and yscale arguments affect the amount of space allocated to the widget. A child widget can be added to the Alignment widget using the usual add() method:

alignment.add(child_widget);

For an example of using the Alignment widget, refer to the example for the 8.4 (Progress Bar) widget.

10.5 Viewports

A viewport widget allows you to place a larger widget within it such that you can view a part of it at a time. It uses the ubiquitous 6 (adjustment object) to define the area that is currently in view. It’s fairly unlikely that you will ever need to use the Viewport widget directly. You are much more likely to use the 10.6 (Scrolled Windows) widget, which itself uses the Viewport, and gives you scrollbars. Here are the Gtk::Viewport constructors:

Gtk::Viewport(Gtk::Adjustment &hadjustment, Gtk::Adjustment &vadjustment); Gtk::Viewport();

You can specify the horizontal and vertical adjustment objects that the widget is to use when you create the widget, or you can let the viewport create its own, which it will do if you don’t pass any arguments to the constructor. You can get and set the adjustments after the widget has been created using the following four methods:

Gtk::Adjustment* Gtk::Viewport::get_hadjustment(); Gtk::Adjustment* Gtk::Viewport::get_vadjustment(); void Gtk::Viewport::set_hadjustment(GtkAdjustment &adjustment); void Gtk::Viewport::set_vadjustment(GtkAdjustment &adjustment);

The only other viewport function is used to alter its appearance:

void Gtk::Viewport::set_shadow_type(GtkShadowType type );

Possible values for the type parameter are: gg

¯ GTK_SHADOW_NONE

¯ GTK_SHADOW_IN

¯ GTK_SHADOW_OUT

¯ GTK_SHADOW_ETCHED_IN (the default)

¯ GTK_SHADOW_ETCHED_OUT

10.6 Scrolled Windows

Scrolled windows are used to create a scrollable area inside a real window. You can insert any type of widget into a scrolled window, and it will be accessible regardless of its size by using the scrollbars. Gtk::ScrolledWindow has the following constructors:

Gtk::ScrolledWindow(Gtk::Adjustment *hadjustment, Gtk::Adjustment *vadjustment); Gtk::ScrolledWindow();

As with the Viewport, you can supply Adjustments at creation time, or you can let the widget supply its own by passing no arguments. Scrolled windows have scrollbar policies which determine whether the scrollbars will be displayed or not; the policy can be set for each scrollbar using:

void Gtk::ScrolledWindow::set_policy(GtkPolicyType hscrollbar_policy, GtkPolicyType vscrollbar_policy);

The policy may be one of GTK_POLICY_AUTOMATIC or GTK_POLICY_ALWAYS. GTK_POLICY_AUTOMATIC will cause the scrolled window to display the scrollbar only if the contained widget is larger than the visible area; GTK_POLICY_ALWAYS will cause the scrollbar to always be displayed. Use the following method to add a child widget to the scrolled window:

void Gtk::ScrolledWindow::add_with_viewport(const Gtk::Widget &child);

Note that this differs from the usual add() method. Here is a simple example that packs 100 toggle buttons into a scrolled window. Try resizing the window, and notice how the scrollbars react. Source location: examples/scrolledwin/scrolledwin.cc

/* example-start scrolledwin scrolledwin.c */

#include #include #include #include #include #include #include

class Scrolledwin : public Gtk::Dialog gg

{ public: Scrolledwin(); ~Scrolledwin(); };

Scrolledwin::~Scrolledwin() {};

Scrolledwin::Scrolledwin() { char buffer[32]; int i, j; Gtk::ScrolledWindow *scrolled_window; Gtk::Table *table; Gtk::Button *button;

/* Create a new dialog window for the scrolled window to be * packed into. A dialog is just like a normal window except it has a * vbox and a horizontal separator packed into it. It’s just a shortcut * for creating dialogs */ destroy.connect(Gtk::Main::quit.slot());

set_title ("GtkScrolledWindow example"); set_border_width (0); set_usize(300, 300);

/* create a new scrolled window. */ scrolled_window = manage(new Gtk::ScrolledWindow());

scrolled_window->set_border_width (10);

/* the policy is one of GTK_POLICY AUTOMATIC, or GTK_POLICY_ALWAYS. * GTK_POLICY_AUTOMATIC will automatically decide whether you need * scrollbars, whereas GTK_POLICY_ALWAYS will always leave the scrollbars * there. The first one is the horizontal scrollbar, the second, * the vertical. */ scrolled_window->set_policy(GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); /* The dialog window is created with a vbox packed into it. */ get_vbox()->pack_start (*scrolled_window, TRUE, TRUE, 0); scrolled_window->show();

/* create a table of 10 by 10 squares. */ table = manage(new Gtk::Table(10, 10, FALSE));

/* set the spacing to 10 on x and 10 on y */ table->set_row_spacings (10); table->set_col_spacings (10);

/* pack the table into the scrolled window */ scrolled_window->add_with_viewport (*table); table->show();

/* this simply creates a grid of toggle buttons on the table * to demonstrate the scrolled window. */ for (i = 0; i < 10; i++) for (j = 0; j < 10; j++) { gg

sprintf (buffer, "button (%d,%d)\n", i, j); button = manage(new Gtk::ToggleButton(buffer)); table->attach(*button, i, i+1, j, j+1); button->show(); }

/* Add a "close" button to the bottom of the dialog */ button = manage(new Gtk::Button("close")); button->clicked.connect(destroy.slot());

/* this makes it so the button is the default. */ button->set_flags(GTK_CAN_DEFAULT); get_action_area()->pack_start (*button, TRUE, TRUE, 0);

/* This grabs this button to be the default button. Simply hitting * the "Enter" key will cause this button to activate. */ button->grab_default(); button->show ();

show(); }

int main (int argc, char *argv[]) { Gtk::Main myapp(argc, argv); Scrolledwin scrolledwin; myapp.run(); return(0); } /* example-end */

10.7 Paned Window Widgets

Panes are used to divide a window into halves, separated by a moveable divider. GTK– provides two of these widgets: Gtk::HPaned adds a horizontal divider, and Gtk::VPaned adds a vertical one. Other than the names and the orientations, there’s no difference between the two, so we’ll describe them together. Unlike the other widgets in this chapter, pane widgets contain not one, but two child widgets, one in each pane. Otherwise, they’re no different from the other single-item widgets; they do, however, have different add() methods. The pane constructors are:

Gtk::HPaned(); Gtk::VPaned();

To add child widgets to the panes, call

void Gtk::Paned::add1 (const Gtk::Widget &child); void Gtk::Paned::add2 (const Gtk::Widget &child);

For HPaned, add1() adds the child to the upper pane; for VPaned, add1() adds to the left-hand pane. add2() adds to the opposite pane. The appearance of the divider is adjustable. Dividers have a small, square handle which the user can use to move the divider; the size of this handle is adjustable. The gutter is a separation area between the two panes; the width of the gutter is also adjustable. These two parameters can be set using: gg

void Gtk::Paned::set_handle_size(guint16 size); void Gtk::Paned::set_gutter_size(guint16 size);

where size is in pixels. Typical values for these are 10 and 16, respectively. You can adjust the position of the divider using:

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

where position is in pixels. It’s a good idea to set the initial position using this method when you create the pane, since the default position isn’t always what you want. It’s example time again. For the example, we’ll create part of the user interface of an imaginary E-mail program. A window is divided into two portions vertically, with the top portion being a list of email messages and the bottom portion the text of the email message. A couple of things to notice: Text can’t be added to a Gtk::Text widget until it is realized (actually displayed on the screen). This could be done by calling Gtk::Widget::realize(), but as a demonstration of an alternate technique, we connect a handler to the realize signal to add the text. Also, we need to add the GTK_SHRINK option to some of the items in the table containing the text window and its scrollbars, so that when the bottom portion is made smaller, the correct portions shrink instead of being pushed off the bottom of the window. Source location: examples/paned/paned.cc

#include #include #include #include #include #include #include #include

class AppMessages: public Gtk::ScrolledWindow { public: AppMessages(); };

/* Create the list of "messages" */ AppMessages::AppMessages() : Gtk::ScrolledWindow() { Gtk::List *list;

/* Create a new scrolled window, with scrollbars only if needed */ set_policy(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

/* Create a new list and put it in the scrolled window */ list = manage(new Gtk::List()); add_with_viewport (*list);

/* Add some "messages" to the window */

for (int i=0; i<10; i++) list->items().push_back(*manage(new Gtk::ListItem("message"))); gg

list->show (); } class AppText : public Gtk::Table { Gtk::Text* text; public: AppText();

virtual void realize_impl(); };

/* Create a scrolled text area that displays a "message" */ AppText::AppText() : Gtk::Table(2,2,FALSE) { Gtk::Scrollbar *scrollbar;

/* Put a text widget in the upper left hand corner. Note the use of * GTK_SHRINK in the y direction */ text = manage( new Gtk::Text () ); attach (*text, 0, 1, 0, 1, GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0); text->show ();

/* Put a HScrollbar in the lower left hand corner */ scrollbar = manage( new Gtk::HScrollbar (*(text->get_hadjustment())) ); attach (*scrollbar, 0, 1, 1, 2, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0); scrollbar->show ();

/* And a VScrollbar in the upper right */ scrollbar = manage( new Gtk::VScrollbar (*(text->get_vadjustment())) ); attach (*scrollbar, 1, 2, 0, 1, GTK_FILL, GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 0); scrollbar->show (); }

/* Add some text to our text widget - this is a callback that is invoked when our window is realized. We could also force our window to be realized with gtk_widget_realize, but it would have to be part of a hierarchy first */ void AppText::realize_impl () { Gtk::Widget::realize_impl(); // always call this! text->freeze (); text->insert(Gtk::Text_Helpers::Context(), "From: [email protected]\n" "To: [email protected]\n" "Subject: Made it!\n" "\n" "We just got in this morning. The weather has been\n" "great - clear but cold, and there are lots of fun sights.\n" "Sojourner says hi. See you soon.\n" " -Path\n"); gg

text->thaw (); } 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::Paned *vpaned; Gtk::Widget *list; Gtk::Widget *text;

set_title ("Paned Windows"); set_border_width (10); set_usize (450, 400);

/* create a vpaned widget and add it to our toplevel window */

vpaned = manage( new Gtk::VPaned () ); add (*vpaned); vpaned->set_handle_size (10); vpaned->set_gutter_size (15);

/* Now create the contents of the two halves of the window */ list = manage( new AppMessages() ); vpaned->add1 (*list);

text = manage( new AppText() ); vpaned->add2 (*text);

show_all (); }

AppWindow::~AppWindow() {}

int main( int argc, char *argv[] ) { /* Initialise GTK */ Gtk::Main kit(&argc, &argv); gg

/* Create a new window */ AppWindow app;

/* Enter the event loop */ Gtk::Main::run ();

return(0); }

11 Packing widgets

When creating an application, you’ll want to put more than one widget inside a window. Our first helloworld example only used one widget, so we could simply use a Gtk::Container::add() call to "pack" the widget into the window. But when you want to put more than one widget into a window, how do you control where the widgets are positioned? This is where packing comes in.

11.1 Theory of Packing Boxes

Most packing is done by creating boxes as in the example above. These are invisible widget containers that we can pack our widgets into which come in two forms, a horizontal box, and a vertical box. When packing widgets into a horizontal box, the objects are inserted horizontally from left to right or right to left depending on the call used. In a vertical box, widgets are packed from top to bottom or vice versa. You may use any combination of boxes inside or beside other boxes to create the desired effect. To create a new horizontal box, we use a Gtk::HBox, and for vertical boxes, a Gtk::VBox.The Gtk::Box::pack_start() and Gtk::Box::pack_end() methods are used to place objects inside these containers. The Gtk::Box::pack_start() method will start at the top and work its way down in a vbox, and pack left to right in an hbox. Gtk::Box::pack_end() will do the opposite, packing from bottom to top in a vbox, and right to left in an hbox. Using these methods allows us to right justify or left justify our widgets; the methods may be mixed in any way to achieve the desired effect. We will use Gtk::Box::pack_start() in most of our examples. An object may be another container or a widget. In fact, many widgets are actually containers themselves, including the button, but we usually only place a label inside a button. By using these calls, GTK– knows where you want to place your widgets so it can do automatic resizing and other nifty things. There are also a number of options covering how your widgets should be packed. As you can imagine, this gives us a quite a bit of flexibility when placing and creating widgets.

11.2 Details of Boxes

Because of this flexibility, packing boxes in GTK– can be confusing at first. There are a lot of options, and it’s not immediately obvious how they all fit together. In the end, however, there are basically five different styles, as shown

in this picture:

> ÙØ ack bÓܽ:ÔÒg "ÎËÈACE = "½5"ÀËÈ ACE = "½¼"AÄÌ = Ô

> Each line contains one horizontal box (hbox) with several buttons. Each of the buttons is packed into the hbox the same way (i.e. same arguments to the Gtk::Box::pack_start() method). This is the declaration of Gtk::Box::pack_start() method: gg

void Gtk::Box::pack_start(const Gtk::Widget &child, bool expand = FALSE, bool fill = FALSE, gint padding = 0);

The first argument is the object you’re packing. The objects will all be buttons for now, so we’ll be packing buttons into boxes. If expand is set, the packed widgets will be spaced out evenly across the width/length of the hbox/vbox, but their sizes won’t change (there will be empty space between the widgets). fill has an effect only if expand is set; if it’s true, then the packed widgets will be resized so that there will be no space between them. padding specifies the width of a border-area to leave around the packed widget. Here’s the constructor for the box widgets:

Gtk::Box(bool homogeneous, gint spacing);

Passing true for homogeneous will force all of the contained widgets to be the same size. spacing is a (mini- mum) number of pixels to leave between each widget. What’s the difference between spacing (set when the box is created) and padding (set when elements are packed)? Spacing is added between objects; padding is added on either side of an object. The following figure should make it

clearer:

> ÙØ ack bÓܾ:ÔÒg "ÎËÈACE = "½5"ÀËÈ ACE = "½¼"AÄÌ = Ô

> Here is the code used to create the above images: Source location: examples/packbox/packbox.cc

#include #include #include #include #include #include #include #include

using std::cerr; using std::endl;

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

class PackBox : public Gtk::HBox { public: PackBox(bool homogeneous, gint spacing, bool expand, bool fill, gint padding); ~PackBox() { delete m_button6; }

Gtk::Button m_button1, m_button2, m_button3, m_button4, m_button5, *m_button6; char padstr[80]; }; gg

PackBox::PackBox(bool homogeneous, gint spacing, bool expand, bool fill, gint padding) : Gtk::HBox(homogeneous, spacing), m_button1("gtk_box_pack"), m_button2("(box,"), m_button3("button,"), m_button4(expand ? "TRUE," : "FALSE,"), m_button5(fill ? "TRUE," : "FALSE,") { pack_start(m_button1, expand, fill,padding); pack_start(m_button2, expand, fill,padding); pack_start(m_button3, expand, fill,padding); pack_start(m_button4, expand, fill,padding); pack_start(m_button5, expand, fill,padding);

sprintf(padstr, "%d);", padding);

m_button6 = new Gtk::Button(padstr); pack_start(*m_button6, expand, fill,padding); }

class PackBoxDemo : public Gtk::Window { public: Gtk::Button m_button; Gtk::VBox m_box1; Gtk::HBox m_boxQuit; Gtk::Button m_buttonQuit;

Gtk::HSeparator m_seperator1, m_seperator2, m_seperator3, m_seperator4, m_seperator5;

PackBoxDemo(int which); ~PackBoxDemo();

// You should always remember to connect the destroy signal to the // main window. This is very important for proper intuitive // behavior gint delete_event_impl(GdkEventAny*) { Gtk::Main::quit(); return 0; }

};

PackBoxDemo::PackBoxDemo(int which) : m_box1(false, 0), m_boxQuit(false, 0), m_buttonQuit("Quit") { Gtk::Label *m_label1, *m_label2; PackBox *m_packbox1, *m_packbox2, *m_packbox3, *m_packbox4, *m_packbox5;

switch(which) { gg

case 1: // create a new label. m_label1 = manage(new Gtk::Label("hbox(false, 0);"));

// Align the label to the left side. We’ll discuss this function and // others in the section on Widget Attributes. m_label1->set_alignment(0, 0);

// Pack the label into the vertical box (vbox box1). Remember that // widgets added to a vbox will be packed one on top of the other in // order. m_box1.pack_start(*m_label1, false, false, 0);

// Create a PackBox - homogeneous = FALSE, spacing = 0, // expand = FALSE, fill = FALSE, padding = 0 m_packbox1 = manage(new PackBox(false, 0, false, false, 0)); m_box1.pack_start(*m_packbox1, false, false, 0);

// Create a PackBox - homogeneous = FALSE, spacing = 0, // expand = TRUE, fill = FALSE, padding = 0 m_packbox2 = manage(new PackBox(false, 0, false, true, 0)); m_box1.pack_start(*m_packbox2, false, false, 0);

// Create a PackBox - homogeneous = FALSE, spacing = 0, // expand = TRUE, fill = TRUE, padding = 0 m_packbox3 = manage(new PackBox(false, 0, true, true, 0)); m_box1.pack_start(*m_packbox3, false, false, 0);

// pack the separator into the vbox. Remember each of these // widgets are being packed into a vbox, so they’ll be stacked // vertically. m_box1.pack_start(m_seperator1, false, true, 5);

// create another new label, and show it. m_label2 = manage(new Gtk::Label("hbox(true, 0);")); m_label2->set_alignment(0, 0); m_box1.pack_start(*m_label2, false, false, 0);

// Args are: homogeneous, spacing, expand, fill, padding m_packbox4 = manage(new PackBox(true, 0, true, false, 0)); m_box1.pack_start(*m_packbox4, false, false, 0);

// Args are: homogeneous, spacing, expand, fill, padding m_packbox5 = manage(new PackBox(true, 0, true, false, 0)); m_box1.pack_start(*m_packbox5, false, false, 0);

m_box1.pack_start(m_seperator2, false, true, 5);

break; case 2:

m_label1 = manage(new Gtk::Label("hbox(false, 10);")); m_label1->set_alignment(0, 0); m_box1.pack_start(*m_label1, false, false, 0); gg

m_packbox1 = manage(new PackBox(false, 10, true, false, 0)); m_box1.pack_start(*m_packbox1, false, false, 0);

m_packbox2 = manage(new PackBox(false, 10, true, true, 0)); m_box1.pack_start(*m_packbox2, false, false, 0);

m_box1.pack_start(m_seperator1, false, true, 5);

m_label2 = manage(new Gtk::Label("hbox(false, 10);")); m_label2->set_alignment(0, 0); m_box1.pack_start(*m_label2, false, false, 0);

m_packbox3 = manage(new PackBox(false, 0, true, false, 10)); m_box1.pack_start(*m_packbox3, false, false, 0);

m_packbox4 = manage(new PackBox(false, 0, true, true, 10)); m_box1.pack_start(*m_packbox4, false, false, 0);

m_box1.pack_start(m_seperator2, false, true, 5);

break; case 3:

// This demonstrates the ability to use Gtk::Box::pack_end() to // right justify widgets. First, we create a new box as before. m_packbox1 = manage(new PackBox(false, 0, false, false, 0)); // create the label that will be put at the end. m_label1 = manage(new Gtk::Label("end")); // pack it using pack_end(), so it is put on the right side // of the PackBox. m_packbox1->pack_end(*m_label1, false, false, 0);

m_box1.pack_start(*m_packbox1, false, false, 0);

// this explicitly sets the separator to 400 pixels wide by 5 pixels // high. This is so the hbox we created will also be 400 pixels wide, // and the "end" label will be separated from the other labels in the // hbox. Otherwise, all the widgets in the hbox would be packed as // close together as possible. m_seperator1.set_usize(400, 5);

// pack the separator into ourselves m_box1.pack_start(m_seperator1, false, true, 5); }

// setup the signal to destroy the window. Remember that this will send // the "destroy" signal to the window which will be caught by our signal // handler as defined above. m_buttonQuit.clicked.connect(Gtk::Main::quit.slot());

// pack the button into the quitbox. // The last 3 arguments to gtk_box_pack_start are: expand, fill, padding. gg

m_boxQuit.pack_start(m_buttonQuit, true, false, 0); m_box1.pack_start(m_boxQuit, false, false, 0);

// pack the vbox (box1) which now contains all our widgets, into the // main window. add(m_box1);

show_all();

}

PackBoxDemo::~PackBoxDemo() {}

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 myapp(&argc, &argv);

if (argc != 2) { cerr << "usage: packbox num, where num is 1, 2, or 3." << endl; // this just does cleanup in GTK, and exits with an exit status of 1. gtk_exit (1); }

PackBoxDemo packboxdemo(atoi(argv[1]));

myapp.run(); return 0; }

11.3 Packing Using Tables

Let’s take a look at another way of packing: tables. These can be extremely useful in certain situations. Using tables, we create a grid that we can place widgets in. The widgets may take up as many spaces as we specify. Here is the constructor for Gtk::Table:

Gtk::Table(gint rows, gint columns, bool homogeneous);

The first argument is the number of rows to make in the table, while the second, obviously, is the number of columns. If homogeneous is true, the table boxes are forced to all be the same size (i.e. the size of the largest widget in the table). The rows and columns are indexed starting at 0. If you specify rows =2andcolumns = 2, the layout would look something like this:

012 0+------+------+ gg

||| 1+------+------+ ||| 2+------+------+

Note that the coordinate system starts in the upper left hand corner. To place a widget into a box, use the following function:

void Gtk::Table::attach(Gtk::Widget &child, guint left_attach, guint right_attach, guint top_attach, guint bottom_attach, guint xoptions=(GTK_FILL|GTK_EXPAND), guint yoptions=(GTK_FILL|GTK_EXPAND), guint xpadding=0, guint ypadding=0);

The first argument is the widget you wish to place in the table. The left_attach and right_attach arguments specify where to place the widget, and how many boxes to use. For example, if you want a button in the lower-right cell of a 2x2 table, and want it to occupy that cell only,then left_attach would be 1, right_attach 2, top_attach 1, and bottom_attach 2. If, on the other hand, you wanted a widget to take up the entire top row of our 2x2 table, you’d set left_attach =0,right_attach =2,top_attach =0,andbottom_attach =1. xoptions and yoptions are used to specify packing options and may be bitwise ORed together to allow multiple options. These options are:

GTK_FILL If the table box is larger than the widget, and GTK_FILL is specified, the widget will expand to use all the room available.

GTK_SHRINK If the table widget was allocated less space than was requested (usually by the user resizing the window), then the widgets would normally just be pushed off the bottom of the window and disappear. If GTK_SHRINK is specified, the widgets will shrink with the table.

GTK_EXPAND This will cause the table to expand to use up any remaining space in the window.

The padding arguments work just as they do for boxes. Gtk::Table::set_row_spacing() and Gtk::Table::set_col_spacing() set the spacing between the rows at the specified row or column:

void Gtk::Table::set_row_spacing(gint row, gint spacing); void Gtk::Table::set_col_spacing(gint column, gint spacing);

Note that for columns, the space goes to the right of the column, and for rows, the space goes below the row. You can also set a consistent spacing of all rows and/or columns with: gg

void Gtk::Table::set_row_spacings(gint spacing ); void Gtk::Table::set_col_spacings(gint spacing );

Note that with these calls, the last row and last column do not get any spacing. In the following example, we make a window with three buttons in a 2x2 table. The first two buttons will be placed in

the upper row. A third button is placed in the lower row, spanning both columns. It should look something like this:

ÙØ abÐ e:ÔÒg "ÎËÈACE = "½5"ÀËÈ ACE = "½¼"AÄÌ = > Ø

<=CEÆÌEÊ> Source location: examples/table/table.cc

#include #include #include #include #include

// Gtk-- version of the table packing example from the gtk+ tutorial

using SigC::bind; using SigC::slot; using std::cout; using std::endl;

class MyWin : public Gtk::Window { public: MyWin();

Gtk::Table m_table; Gtk::Button m_b1, m_b2, m_bQuit;

void callback(char* data);

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

};

MyWin::MyWin() : m_table(2, 2, true), m_b1("button 1"), m_b2("button 2"), m_bQuit("Quit") { set_title("Table"); set_border_width(20);

add(m_table);

m_table.attach(m_b1, 0, 1, 0, 1); m_table.attach(m_b2, 1, 2, 0, 1); m_table.attach(m_bQuit, 0, 2, 1, 2); pg

m_b1.clicked.connect(bind(slot(this, &MyWin::callback), "button 1")); m_b2.clicked.connect(bind(slot(this, &MyWin::callback), "button 2")); m_bQuit.clicked.connect(Gtk::Main::quit.slot()); // the cast is needed to "help" template instantiation

show_all(); }

void MyWin::callback(char* data) { cout << "Hello again - " << data << " was pressed" << endl; }

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

myapp.run(); return 0; }

12 Multiple-item widgets

In this chapter, we discuss some of the multiple-item widgets in GTK–. The 12.5 (notebooks) and 12.4 (toolbars) sections in this chapter assume a decent understanding of the material in the 9 (container widgets) chapter; go back and read it if you haven’t already.

12.1 Fixed Container

The Fixed widget places its children at relative coordinates, which you specify. The widgets can also be moved. The constructor for Gtk::Fixed is:

Gtk::Fixed();

Place children in the Fixed container using

void Gtk::Fixed::put(const Gtk::Widget &widget, gint16 x, gint16 y);

You can then move them using

void Gtk::Fixed::move(const Gtk::Widget &widget, gint16 x, gint16 y); pg

Although the Fixed container is a multiple-item widget, it doesn’t provide STL-like access to its child widgets. If you’ll need access to the widgets you put in the Fixed container, it will probably be a good idea for you to maintain your own list of them. The following example illustrates how to use Gtk::Fixed: Source location: examples/fixed/fixed.cc

/* example-start fixed fixed.c */

#include #include #include #include

class AppWindow : public Gtk::Window { gint x; gint y;

public: AppWindow (); ~AppWindow ();

void move_button(Gtk::Fixed*,Gtk::Button*);

/* Here we connect the "destroy" event to a signal handler */ virtual gint delete_event_impl (GdkEventAny*); };

AppWindow::AppWindow() : Gtk::Window(GTK_WINDOW_TOPLEVEL), x(50), y(50) { Gtk::Fixed *fixed; Gtk::Button *button; gint i;

/* Create a new window */ set_title("Fixed Container");

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

/* Create a Fixed Container */ fixed = manage( new Gtk::Fixed() ); add(*fixed);

for (i = 1 ; i <= 3 ; i++) { /* Creates a new button with the label "Press me" */ button = manage( new Gtk::Button("Press me") );

/* When the button receives the "clicked" signal, it will call the * function move_button() passing it the Fixed Containter as its * argument. */ button->clicked.connect(bind(slot(this,&AppWindow::move_button),fixed,button)); pg

/* This packs the button into the fixed containers window. */ fixed->put (*button, i*50, i*50); }

show_all (); }

AppWindow::~AppWindow() {}

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

/* This callback function moves the button to a new position * in the Fixed container. */ void AppWindow::move_button( Gtk::Fixed *fixed, Gtk::Button *button ) { x = (x+30)%300; y = (y+50)%300; fixed->move(*button, x, y); }

int main( int argc, char *argv[] ) { /* Initialise GTK */ Gtk::Main app(&argc, &argv); AppWindow window;

/* Enter the event loop */ Gtk::Main::run ();

return(0); } /* example-end */

12.2 Layout Container

The Layout container is similar to the Fixed container except that it implements an infinite (where infinity is less than 2ˆ32) scrolling area. Xwindows has a limitation where windows can be at most 32767 pixels wide or tall. The Layout container gets around this limitation by doing some exotic stuff using window and bit gravities, so that you can have smooth scrolling even when you have many child widgets in your scrolling area. The Layout container operates similarly to the 10.5 (Viewport), but it contains multiple widgets instead of just one. If you’ll be placing a large number of widgets in the viewing area, the Layout container is a good choice. A Layout container is created using:

Gtk::Layout(Gtk::Adjustment &hadjustment, Gtk::Adjustment &vadjustment); Gtk::Layout(); pg

These are exactly like the Viewport constructors, and work the same. You can add and move widgets in the Layout container using the following two methods, which work the same as their counterparts in Gtk::Fixed:

void Gtk::Layout::put(const Gtk::Widget &widget, gint x, gint y);

void Gtk::Layout::move(const Gtk::Widget &widget, gint x, gint y);

Unlike the Viewport and Fixed widgets, the maximum size of the Layout widget can be changed. This is done using:

void Gtk::Layout::set_size(guint width, guint height);

Layout containers are one of the very few widgets in GTK– that actively repaint themselves as they are being changed (the vast majority of the GTK– widgets queue redrawing requests, which are processed when control returns to the gtk_main() function). Therefore, when you’re making a large number of changes in a Layout container, it will re- paint itself for each one. This is inefficient and unnecessary. To temporarily stop the Layout container from repainting itself, call

void Gtk::Layout::freeze();

Once you’re done making your changes, call

void Gtk::Layout::thaw();

Calling thaw() after a freeze() causes the Layout widget to repaint itself, making all your changes visible in one go. You can use the following four methods to get and set the Layout container’s adjustment widgets:

Gtk::Adjustment* Gtk::Layout::get_hadjustment(); Gtk::Adjustment* Gtk::Layout::get_vadjustment(); void Gtk::Layout::set_hadjustment(Gtk::Adjustment &adjustment); void Gtk::Layout::set_vadjustment(Gtk::Adjustment &adjustment);

As with the Fixed container, Layout has no STL-like list associated with it; if you need a list of its child widgets, you’re on your own.

12.3 Button boxes

Button boxes are a convenient way to quickly arrange a group of buttons. They come in both horizontal (Gtk::HButtonBox) and vertical (Gtk::VButtonBox) flavours; the two types are exactly alike, except in name and orientation. One interesting feature of button boxes is that all of them in a given program can share certain settings, such as inter- button spacing. The idea is that you can impose consistency on your interface this way. Methods which change a given setting for all button boxes have default in their names. The constructors for button boxes are: pg

Gtk::HButtonBox(); Gtk::VButtonBox();

Buttons are added to a button box using the add() method:

Gtk::ButtonBox::add(GtkWidget &child);

You can set the spacing between the buttons using

void Gtk::ButtonBox::set_spacing_default(gint spacing); gint Gtk::ButtonBox::get_spacing_default(); gint get_spacing(); void set_spacing(gint spacing); set_spacing_default() and get_spacing_default() operate on every button box in your program. Most of the button box parameters have default methods like this; we’ll only point them out here. You can set and get a minimum size for the buttons using

gint Gtk::ButtonBox::get_child_size_default_width(); gint Gtk::ButtonBox::get_child_size_default_height(); void Gtk::ButtonBox::set_child_size_default(gint min_width,gint min_height); gint Gtk::ButtonBox::get_child_size_width(); gint Gtk::ButtonBox::get_child_size_height(); void Gtk::ButtonBox::set_child_size(gint min_width,gint min_height);

You can also change the padding for each button in the button box using

gint Gtk::ButtonBox::get_child_ipadding_default_x(); gint Gtk::ButtonBox::get_child_ipadding_default_y(); void Gtk::ButtonBox::set_child_ipadding_default(gint ipad_x,gint ipad_y); gint Gtk::ButtonBox::get_child_ipadding_x(); gint Gtk::ButtonBox::get_child_ipadding_y(); void Gtk::ButtonBox::set_child_ipadding(gint ipad_x,gint ipad_y);

Button boxes support several layout styles. These can be retrieved and changed using:

GtkButtonBoxStyle Gtk::ButtonBox::get_layout_default(); void Gtk::ButtonBox::set_layout_default(GtkButtonBoxStyle layout); GtkButtonBoxStyle Gtk::ButtonBox::get_layout(); void Gtk::ButtonBox::set_layout(GtkButtonBoxStyle layout_style); where layout is one of

¯ GTK_BUTTONBOX_DEFAULT_STYLE

¯ GTK_BUTTONBOX_SPREAD

¯ GTK_BUTTONBOX_EDGE

¯ GTK_BUTTONBOX_START

¯ GTK_BUTTONBOX_END pg

Run the example to see what these styles do. There’s also a function to set the layout and spacing at the same time:

void set_layout_spacing(GtkButtonBoxStyle layout, gint spacing);

(There is no default version of set_layout_spacing().) Once again, button boxes don’t provide an STL-like list container. This example illustrates all the different layout settings for button boxes. Source location: examples/buttonbox/buttonbox.cc

/* example-start buttonbox buttonbox.c */

#include #include #include #include #include #include

class BBox : public Gtk::Frame { public: BBox (gint horizontal, const Gtk::string& title, gint spacing, gint child_w, gint child_h, GtkButtonBoxStyle layout); };

BBox::BBox (gint horizontal, const Gtk::string& title, gint spacing, gint child_w, gint child_h, GtkButtonBoxStyle layout) : Gtk::Frame(title) { Gtk::ButtonBox *bbox; Gtk::Button *button;

if (horizontal) bbox = manage( new Gtk::HButtonBox () ); else bbox = manage( new Gtk::VButtonBox () );

bbox->set_border_width (5);

add (*bbox);

/* Set the appearance of the Button Box */ bbox->set_layout (layout); pg

bbox->set_spacing (spacing); bbox->set_child_size (child_w, child_h);

button = manage( new Gtk::Button ("OK")); bbox->add (*button);

button = manage( new Gtk::Button ("Cancel")); bbox->add (*button);

button = manage( new Gtk::Button ("Help")); bbox->add (*button);

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

virtual gint delete_event_impl (GdkEventAny*); };

AppWindow::AppWindow() : Gtk::Window(GTK_WINDOW_TOPLEVEL) { Gtk::Box *main_vbox; Gtk::Box *vbox; Gtk::Box *hbox; Gtk::Frame *frame_horz; Gtk::Frame *frame_vert;

set_title ("Button Boxes"); set_border_width (10);

main_vbox = manage( new Gtk::VBox(FALSE, 0) ); add (*main_vbox);

frame_horz = manage( new Gtk::Frame ("Horizontal Button Boxes") ); main_vbox->pack_start (*frame_horz, TRUE, TRUE, 10);

vbox = manage( new Gtk::VBox (FALSE, 0) ); vbox -> set_border_width (10); frame_horz-> add (*vbox);

vbox->pack_start (*manage( new BBox (TRUE, "Spread (spacing 40)", 40, 85, 20, GTK_BUTTONBOX_SPREAD)), TRUE, TRUE, 0);

vbox->pack_start (*manage( new BBox (TRUE, "Edge (spacing 30)", 30, 85, 20, GTK_BUTTONBOX_EDGE)), TRUE, TRUE, 5);

vbox->pack_start (*manage( new BBox (TRUE, "Start (spacing 20)", 20, 85, 20, GTK_BUTTONBOX_START)), pg

TRUE, TRUE, 5);

vbox->pack_start (*manage( new BBox (TRUE, "End (spacing 10)", 10, 85, 20, GTK_BUTTONBOX_END)), TRUE, TRUE, 5);

frame_vert = manage( new Gtk::Frame ("Vertical Button Boxes") ); main_vbox->pack_start (*frame_vert, TRUE, TRUE, 10);

hbox = manage( new Gtk::HBox (FALSE, 0) ); hbox->set_border_width (10); frame_vert->add (*hbox);

hbox->pack_start (*manage( new BBox (FALSE, "Spread (spacing 5)", 5, 85, 20, GTK_BUTTONBOX_SPREAD)), TRUE, TRUE, 0);

hbox->pack_start (*manage( new BBox (FALSE, "Edge (spacing 30)", 30, 85, 20, GTK_BUTTONBOX_EDGE)), TRUE, TRUE, 5);

hbox->pack_start (*manage( new BBox (FALSE, "Start (spacing 20)", 20, 85, 20, GTK_BUTTONBOX_START)), TRUE, TRUE, 5);

hbox->pack_start (*manage( new BBox (FALSE, "End (spacing 10)", 10, 85, 20, GTK_BUTTONBOX_END)), TRUE, TRUE, 5);

show_all ();

}

AppWindow::~AppWindow() {} gint AppWindow::delete_event_impl (GdkEventAny*) { Gtk::Main::quit(); return 0; } int main( int argc, char *argv[] ) { Gtk::Main main (argc,argv); AppWindow window;

/* Enter the event loop */ Gtk::Main::run ();

return(0); } /* example-end */ pg

12.4 Toolbars

Toolbars are those rows of buttons one often sees just beneath the menubars of GUI applications. They’re meant to give the user quick access to often-used commands, some of which might be buried deep in the menubar hierarchy. Most people think of toolbars as a row of little square buttons with all-but-meaningless icons on them, such as those found in the products of certain very large software vendors. While GTK can’t force your icons to have obvious meanings, it can help you make your toolbars less opaque, because GTK toolbars can contain almost any type of widget. Still, toolbars do generally contain picture-buttons, so the GTK toolbar provides special support for this GUI idiom. In GTK–, you can use the toolbar helper objects to create special toolbar items having icons, labels, and tooltips. You can then let your users decide whether they want to see just the icons, or the icons and labels, or just the labels, as certain web-browsers let you do. This way, once your users have finally learnt the meaning of your pictograms, they can dispense with the captions, saving a bit of room in the window. (Picture-only toolbars also tend to look slicker in screenshots, as certain very large software vendors know all too well.) For those of you interested in making meaningful icons - which I hope includes nearly all of you that will be releasing GUI software to the public - there’s a classic work on the subject of symbols by the great Adrain Frutiger (designer of the typefaces Univers and Frutiger), entitled Signs and Symbols: Their Design and Meaning (English translation by Andrew Bluhm, published in the US by Watson-Guptill Publications, ISBN 0823048268), which probably everybody who designs anything graphic should read. At any rate, here’s the constructor for Gtk::Toolbar:

Gtk::Toolbar(GtkOrientation orientation, GtkToolbarStyle style );

where orientation may be one of

¯ GTK_ORIENTATION_HORIZONTAL

¯ GTK_ORIENTATION_VERTICAL

and style may be one of

¯ GTK_TOOLBAR_TEXT

¯ GTK_TOOLBAR_ICONS

¯ GTK_TOOLBAR_BOTH

As previously noted, you can insert two types of elements into a toolbar: toolbar items, and regular widgets. (Note that style applies only to the specially made toolbar items and not to button widgets inserted as widgets.) Both types of elements are inserted the same way, using classes from the Gtk::Toolbar_Helpers namespace. The various helper objects are:

¯ Element - used for inserting arbitrary widgets

¯ Space - a blank spot, used to separate groups of elements

¯ ButtonElem - a regular button element

¯ ToggleElem - a toggle-button element

¯ RadioElem - a radio-button element pg

Here’s the constructor for Element:

Element(Widget& w, const nstring &tooltip_text=0, const nstring &tooltip_private_text=0); w is the widget to insert, and tooltip_text is the text for the element’s tooltip. You can ignore tooltip_private_text. The constructors for ButtonElem and ToggleElem are exactly alike; each has three forms. Here are the But- tonElem constructors:

// text + icon ButtonElem(const nstring &text, Widget &content, SigC::Slot0 callback, const nstring &tooltip_text=0, const nstring &tooltip_private_text=0);

// icon only ButtonElem(Widget &content, SigC::Slot0 callback, const nstring &tooltip_text=0, const nstring &tooltip_private_text=0);

// text only ButtonElem(const nstring &text, SigC::Slot0 callback, const nstring &tooltip_text=0, const nstring &tooltip_private_text=0);

The only difference between these is whether they take an icon, text, or both as arguments. text is the text to display below the icon. content is the icon; note that any widget can be inserted here, but generally this will be a pixmap or other display widget. callback is the callback to use for the button. tooltip_text will be displayed in the button’s tooltip, and you can safely ignore tooltip_private_text. The RadioElem constructors are the same as those for ButtonElem and RadioElem, but they take an additional argument specifying the group for the radio button. Here they are:

// text + icon RadioElem(Gtk::RadioButton_Helpers::Group& group, const nstring& text, Widget& content, SigC::Slot0 callback=0, const nstring& tooltip_text=0, const nstring& tooltip_private_text=0);

// icon only RadioElem(Gtk::RadioButton_Helpers::Group& group, Widget& content, SigC::Slot0 callback=0, const nstring& tooltip_text=0, const nstring& tooltip_private_text=0);

// text only pg

RadioElem(Gtk::RadioButton_Helpers::Group& group, const nstring& text, SigC::Slot0 callback=0, const nstring& tooltip_text=0, const nstring& tooltip_private_text=0);

The group argument is the only addition here; it works exactly like the group argument for normal radio buttons. See the 5.4 (Radio Buttons) section for details. The toolbar’s contents are manipulated through an STL-like list, which you can obtain using the tools() method:

ToolList& tools();

For example, to add a text-only button tool to the toolbar, we could write

toolbar.tools().push_back(Gtk::Toolbar_Helpers::ButtonElem( "Crash",slot(&crash_cb),"Causes the program to dump core");

Since it’s inconvenient to have to type Gtk::Toolbar_Helpers all the time, you might want to add a using declaration. However, don’t add a global using namespace Gtk::Toolbar_Helpers declaration; place this only in some localised scope, to avoid clashes with other Helpers namespaces. Here’s an example program which displays a toolbar: Source location: examples/toolbar/toolbar.cc

#include #include #include #include

#include #include #include #include #include #include #include

using namespace SigC;

class MainWindowClass : public Gtk::Window { private: Gtk::HBox main_hbox; Gtk::HBox hbox; Gtk::Button button; Gtk::Toolbar toolbar;

void quit_pressed_cb(void); void toolbar_button_cb(char *);

public:

MainWindowClass (void); pg

~MainWindowClass (void);

}; void MainWindowClass::toolbar_button_cb(char *c) { printf("toolbar_button_cb : %s\n",c); }

MainWindowClass::MainWindowClass(void) : Gtk::Window(GTK_WINDOW_TOPLEVEL), main_hbox(FALSE,0), hbox(FALSE,0), button("Quit") {

set_usize(400,50);

add(main_hbox);

main_hbox.add(hbox);

hbox.pack_start(button,FALSE,FALSE,0); hbox.pack_start(toolbar,FALSE,FALSE,0);

button.clicked.connect( slot(this,&MainWindowClass::quit_pressed_cb) );

{ using namespace Gtk::Toolbar_Helpers; toolbar.tools().push_back(ButtonElem( "Click me", bind( slot(this,&MainWindowClass::toolbar_button_cb), "’Click me’ button"), "toolbar btn","")); toolbar.tools().push_back(Space());

toolbar.tools().push_back(ButtonElem( "Click me too", bind( slot(this, &MainWindowClass::toolbar_button_cb), "’Click me too’ button"), "other toolbar btn", ""));

toolbar.tools().push_back(ToggleElem( "Toggle me", bind( slot(this, &MainWindowClass::toolbar_button_cb), "This is from a toggle connector"), "toggle duh", ""));

Gtk::RadioButton::Group gr; toolbar.tools().push_back(RadioElem(gr,"R1")); toolbar.tools().push_back(RadioElem(gr,"R2")); toolbar.tools().push_back(RadioElem(gr,"R3")); }

toolbar.show(); button.show(); hbox.show(); main_hbox.show(); } pg

MainWindowClass::~MainWindowClass(void) {}

void MainWindowClass::quit_pressed_cb(void) { Gtk::Main::quit(); }

int main(gint argc, gchar **argv) { Gtk::Main kit(argc,argv); MainWindowClass main_window;

main_window.show();

kit.run();

return(0); }

12.5 Notebooks

A notebook consists of a set of stacked "pages", each of which can contain one widget. Labelled "tabs" are provided to allow the user to select a page. These are useful in a large number of situations; you can make a single dialog do a lot of work using a notebook. The Gtk::Notebook widget uses an STL-like list to contain the pages; these are constructed using helper objects. See the section on 9.4 (using multiple-item widgets) for information on how GTK– lists and helper objects work; the notebook widget is used there as an example. The constructor for Gtk::Notebook takes no arguments:

Gtk::Notebook();

To insert pages into a notebook, use the TabElem helper:

Gtk::Notebook_Helpers::TabElem(Widget& child,Widget& tab); Gtk::Notebook_Helpers::TabElem(Widget& child,const string& s);

In both constructors, child is the child widget to place in the page itself. tab, in the first constructor, is a widget to be placed in the tab; this will generally be a display widget such as an icon or a label. Most often, you’ll be placing labels in a tab, and the second constructor lets you pass a string for argument s; using this constructor will cause a label to be built from the string. You can access the notebook’s page list through the method

Gtk::Notebook_Helpers::PageList& Gtk::Notebook::pages();

A PageList contains a list of Pages, which provide some useful methods (shown unqualified):

Widget* get_child(); Widget* get_tab(); pg

void set_tab (Widget* tab=0); void set_tab (Widget& tab); void set_tab_text(const nstring& str=0);

bool get_expand(); bool get_fill(); GtkPackType get_pack(); void set_tab_packing(bool expand,bool fill,GtkPackType pack_type); get_child() and get_tab() return the child and tab widgets for the Page, respectively. set_tab() lets you change a tab’s widget. You can pass either a pointer to the widget, or the widget itself. set_tab_text sets the tab to contain a new label, the text of which will be str. set_tab_packing lets you change the packing parameters of the tab-list. If expand is set, then the tabs will be spaced out evenly across the edge they’re on; if fill is set, the tabs themselves will be sized such that there will be no space between them. pack_type can be either GTK_PACK_START or GTK_PACK_END. There are a number of things you can do to the notebook widget itself. GTK notebooks allow their tabs to be placed along any edge of the notebook’s display area; you can set or retrieve the placement using

void Gtk::Notebook::set_tab_pos(GtkPositionType pos); GtkPositionType Gtk::Notebook::get_tab_pos(); where pos is one of

¯ GTK_POS_LEFT

¯ GTK_POS_RIGHT

¯ GTK_POS_TOP

¯ GTK_POS_BOTTOM

Here are some other useful Gtk::Notebook methods (shown unqualified):

gint get_current_page_num();

Returns the index of the current page.

Gtk::Widget* get_nth_page(gint page_number);

Returns the child widget of page page_number.

gint page_num(const Gtk::Widget& child);

Returns the page number of the page containing the given child widget.

void set_page(gint page_number); void next_page(); void prev_page(); set_page selects page number page_number. next_page() and prev_page() cause the next and previous pages to be selected, respectively. Both functions wrap around if they are on the first or last pages. pg

Page* get_current();

Returns the Page object for the currently selected page.

bool get_show_tabs(); void set_show_tabs(bool show_tabs);

Set or retrieve the visibility of the tab-list.

bool get_show_border(); void set_show_border(bool show_border);

Notebooks have a border, which includes the tab-list; use set_show_border() to change its visibility.

void set_homogeneous_tabs(bool homogeneous);

Forces all of the tabs to be the same size.

void set_tab_border(gint border_width); void set_tab_hborder(guint tab_hborder); void set_tab_vborder(guint tab_vborder);

void set_scrollable(bool scrollable);

Sets whether the tab-list will be scrollable if there are more tabs than can be displayed all at once.

void popup_enable(); void popup_disable();

The notebook can have an optional popup menu which will display when the user right-clicks on the tab-list; the menu will contain a list of all the pages. This can be useful if there are a large number of pages in the notebook. You can enable or disable the popup menu with these functions. As you can see, there’s rather a lot to the notebook widget. We haven’t been exhaustive here; see the GTK– reference manual and source code for more information. It’s that time again. This example program demonstrates the use of the notebook widget. Source location: examples/notebook/notebook.cc

#include #include #include #include #include #include #include

struct Book : public Gtk::Notebook { void rotate_book (); void tabsborder_book (); void remove_book (); }; pg

/* This function rotates the position of the tabs */ void Book::rotate_book () { gint pos=(gint)(get_tab_pos()); ++pos; set_tab_pos ((GtkPositionType)(pos%4)); }

/* Add/Remove the page tabs and the borders */ void Book::tabsborder_book () { set_show_tabs (!get_show_tabs()); set_show_border (!get_show_border()); }

/* Remove a page from the notebook */ void Book::remove_book () { Gtk::Notebook::Page *page;

page = get_current(); pages().remove(page); /* Need to refresh the widget -- This forces the widget to redraw itself. */ draw(NULL); }

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

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

AppWindow::AppWindow() : Gtk::Window(GTK_WINDOW_TOPLEVEL) {

Gtk::Button *button; Gtk::Table *table; Book *notebook; Gtk::CheckButton *checkbutton;

int i; char bufferf[32]; char bufferl[32]; pg

set_border_width (10);

table = manage( new Gtk::Table(3,6,FALSE) ); add (*table);

/* Create a new notebook, place the position of the tabs */ notebook = manage( new Book() ); notebook->set_tab_pos (GTK_POS_TOP); table->attach (*notebook, 0,6,0,1);

/* Lets append a bunch of pages to the notebook */ for (i=0; i < 5; i++) { Gtk::Frame *frame; sprintf(bufferf, "Append Frame %d", i+1); sprintf(bufferl, "Page %d", i+1);

frame = manage( new Gtk::Frame (bufferf) ); frame->set_border_width (10); frame->set_usize (100, 75); frame->show ();

notebook->pages().push_back( Gtk::Notebook_Helpers::TabElem(*frame,bufferl)); }

/* Now lets add a page to a specific spot */ checkbutton = manage( new Gtk::CheckButton ("Check me please!") ); checkbutton->set_usize (100, 75); checkbutton->show ();

notebook->pages().insert(++(notebook->pages().begin()), Gtk::Notebook_Helpers::TabElem (*checkbutton, "Check me please!") );

/* Now finally lets prepend pages to the notebook */ for (i=0; i < 5; i++) { Gtk::Frame *frame; sprintf(bufferf, "Prepend Frame %d", i+1); sprintf(bufferl, "PPage %d", i+1);

frame = manage( new Gtk::Frame (bufferf) ); frame->set_border_width (10); frame->set_usize (100, 75); frame->show ();

notebook->pages().push_front( Gtk::Notebook_Helpers::TabElem(*frame,bufferl)); }

/* Set what page to start at (page 4) */ notebook->set_page (3); g

/* Create a bunch of buttons */ button = manage( new Gtk::Button("close") ); button->clicked.connect(Gtk::Main::quit.slot()); table->attach(*button, 0,1,1,2);

button = manage( new Gtk::Button("next page") ); button->clicked.connect(slot(notebook,&Gtk::Notebook::next_page)); table->attach(*button, 1,2,1,2);

button = manage( new Gtk::Button("prev page") ); button->clicked.connect(slot(notebook,&Gtk::Notebook::prev_page)); table->attach(*button, 2,3,1,2);

button = manage( new Gtk::Button("tab position") ); button->clicked.connect(slot(notebook,&Book::rotate_book)); table->attach(*button, 3,4,1,2);

button = manage( new Gtk::Button("tabs/border on/off") ); button->clicked.connect(slot(notebook,&Book::tabsborder_book)); table->attach(*button, 4,5,1,2);

button = manage( new Gtk::Button("remove page") ); button->clicked.connect(slot(notebook,&Book::remove_book)); table->attach(*button, 5,6,1,2);

show_all(); }

AppWindow::~AppWindow() {}

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

Gtk::Main::run();

return(0); }

13 Tree Widget (draft)

The purpose of tree widgets is to display hierarchically-organized data. The Gtk::Tree widget itself is a vertical container for widgets of type Gtk::TreeItem. Gtk::Tree itself is not terribly different from Gtk::List - both are derived directly from Gtk::Container, and the Gtk::Container methods work in the same way on Gtk::Tree widgets as on Gtk::List widgets. The difference is that Gtk::Tree widgets can be nested within other Gtk::Tree widgets. We’ll see how to do this shortly. The Gtk::Tree widget has its own window, and defaults to a white background, as does Gtk::List. Also, most of the Gtk::Tree methods work in the same way as the corresponding Gtk::List ones. However, Gtk::Tree is not derived from Gtk::List, so you cannot use them interchangeably. g

13.1 Creating a Tree

A Gtk::Tree is created in the usual way, using:

Gtk::Tree();

Like the Gtk::List widget, a Gtk::Tree will simply keep growing as more items are added to it, as well as when subtrees are expanded. For this reason, they are almost always packed into a Gtk::ScrolledWindow. You might want to use Gtk::Widget::set_usize() on the scrolled window to ensure that it is big enough to see the tree’s items, as the default size for Gtk::ScrolledWindow is quite small. Now that you have a tree, you’ll probably want to add some items to it. 13.5 (The Tree Item Widget) below explains the gory details of Gtk::TreeItem. For now, it’ll suffice to create one, using:

Gtk::TreeItem( const string &label );

You can then add it to the tree using one of the following (see 13.4.2 (Methods) below for more options):

void Gtk::Tree::append( const Gtk::Widget &tree_item );

void Gtk::Tree::prepend( const Gtk::Widget &tree_item );

Note that you must add items to a Gtk::Tree one at a time - there is no equivalent to Gtk::List::*_items().

13.2 Adding a Subtree

A subtree is created like any other Gtk::Tree widget. A subtree is added to another tree beneath a tree item, using:

void Gtk::TreeItem::set_subtree(const Gtk::Tree &subtree );

You do not need to call Gtk::Widget::show() on a subtree before or after adding it to a Gtk::TreeItem. However, you must have added the Gtk::TreeItem in question to a parent tree before calling Gtk::TreeItem::set_subtree(). This is because, technically, the parent of the subtree is not the Gtk::TreeItem which "owns" it, but rather the Gtk::Tree which holds that Gtk::TreeItem. When you add a subtree to a Gtk::TreeItem, a plus or minus sign appears beside it, which the user can click on to "expand" or "collapse" it, meaning, to show or hide its subtree. Gtk::TreeItems are collapsed by default. Note that when you collapse a Gtk::TreeItem, any selected items in its subtree remain selected, which may not be what the user expects.

13.3 Handling the Selection List

As with Gtk::List, the Gtk::Tree type has a selection field, and it is possible to control the behaviour of the tree (somewhat) by setting the selection type using:

void Gtk::Tree::set_selection_mode(GtkSelectionMode mode );

The selection field of a Gtk::Tree points to a linked list of all items that are currently selected, or NULL if the selection is empty. So to learn about the current selection we use:

SelectionList& selection() g

The selection_mode of the Gtk::List determines the selection facilities of a Gtk::List and therefore the contents of the selection. The selection_mode may be one of the following:

¯ GTK_SELECTION_SINGLE - The selection is either NULL or contains a GList pointer for a single selected item.

¯ GTK_SELECTION_BROWSE - The selection is NULL if the list contains no widgets or insensitive ones only, otherwise it contains a GList pointer for one GList structure, and therefore exactly one list item.

¯ GTK_SELECTION_MULTIPLE - The selection is NULL if no list items are selected or a GList pointer for the first selected item. That in turn points to a GList structure for the second selected item and so

¯ GTK_SELECTION_EXTENDED - The selection is always NULL.

The default is GTK_SELECTION_MULTIPLE. The "select_child", "unselect_child" (not exactly - see 13.4.1 (Signals) below for an explanation), and "selec- tion_changed" signals are emitted when list items are selected or unselected. However, in order to take advantage of these signals, you need to know which Gtk::Tree widget they will be emitted by. This is a source of potential confusion. All Gtk::Tree widgets have their own X window, and can therefore receive events such as mouse clicks (if their Gtk::TreeItems or their children don’t catch them first!). However, to make GTK_SELECTION_SINGLE and GTK_SELECTION_BROWSE selection types behave in a sane manner, the list of selected items is specific to the topmost Gtk::Tree widget in a hierarchy, known as the "root tree". Thus, accessing the selection directly in an arbitrary Gtk::Tree widget is not a good idea unless you know it’s the root tree. The GTK_TREE_SELECTION (Tree) macro does not yet have an equivalent in Gtk–, the program must remember the root tree. Note that the selection list can include items that are not in the subtree in question if the selection type is GTK_SELECTION_MULTIPLE. Finally, the "select_child" (and "unselect_child", in theory) signals are emitted by all trees, but the "selection_changed" signal is only emitted by the root tree. Consequently, if you want to handle the "select_child" signal for a tree and all its subtrees, you will have to connect it for every subtree.

13.4 Tree Widget Internals

The perils associated with obtaining the selection have already been mentioned. The other important fields of the tree can also be accessed with member functions, however some Gtk+ macros do not yet have a Gtk– equivalent. GTK_TREE_IS_ROOT_TREE (Tree) and GTK_TREE_ROOT_TREE (Tree) have no Gtk– equivalents, so remember to save a reference or pointer to the root tree. The children of a tree are all Gtk::TreeItems, they are accessed from their parent using:

ItemList& tree();

Gtk::TreeHelpers::ItemList is a wrapper around the Glib list of children which acts like an STL list. So, for example, the list of items can be iterated over using:

Gtk::Tree_Helpers::ItemList::iterator ItemIt;

for(ItemIt = a_tree->tree().begin(); ItemIt != a_tree->tree().end(); ItemIt++) { // operate on each item, access as *ItemIt } g

The Gtk+ tree_owner field is defined only in subtrees, where it points to the treeitem which holds the tree in question. It currently cannot be accessed in Gtk–. The level field indicates how deeply nested a particular tree is; root trees have level 0, and each successive level of subtrees has a level one greater than the parent level. This field is set only after a Gtk::Tree widget is actually mapped (i.e. drawn on the screen). Gtk::Tree::get_level() returns the tree’s level.

13.4.1 Signals

void Gtk::tree::selection_changed();

This signal will be emitted whenever the selection of a Gtk::Tree has changed. This happens when a child of the Gtk::Tree is selected or deselected.

void Gtk::Tree::select_child(Gtk::Widget& child);

This signal is emitted when a child of the Gtk::Tree is about to get selected. This happens on calls to Gtk::Tree::select_item(), Gtk::Tree::select_child(), on all button presses and calls to Gtk::Item::toggle(). It may some- times be indirectly triggered on other occasions where children get added to or removed from the Gtk::Tree.

void Gtk::Tree::unselect_child (Gtk::Widget& child);

This signal is emitted when a child of the Gtk::Tree is about to get deselected. As of Gtk– 1.0.4, this seems to only occur on calls to gtk_tree_unselect_item() or gtk_tree_unselect_child(), and perhaps on other occasions, but not when a button press deselects a child, nor on emission of the "toggle" signal by gtk_item_toggle().

13.4.2 Methods

Methods on ItemList are also given, which are used to manipulate the items in the tree.

Gtk::Tree();

Gtk::Tree ctor

void Gtk::Tree::tree().push_back(const Gtk::Widget &tree_item );

Append a tree item to a Gtk::Tree.

void Gtk::Tree::tree().push_front(const Gtk::Widget &tree_item );

Prepend a tree item to a Gtk::Tree.

iterator Gtk::Tree::tree().insert(iterator position, const Gtk::Widget &tree_item );

Insert a tree item into a Gtk::Tree at the position in the list specified by position.

template void Gtk::Tree::remove_items(iterator start, iterator stop); g

Remove a list of items from a Gtk::Tree. The iterator must be to Gtk_TreeItem* and be forward iteratable. Note that removing an item from a tree dereferences (and thus usually) destroys it and its subtree, if it has one, and all subtrees in that subtree. If you want to remove only one item, you can use gtk_container_remove(). Note that there is no STL equivalent to this function.

void Gtk::Tree::tree().erase(iterator start, iterator stop );

Remove the items from position start to position stop from a Gtk::Tree. The same warning about dereferencing applies here.

void Gtk::Tree::tree()[gint item]->select();

Emits the "select_item" signal for the child at position item, thus selecting the child (unless you unselect it in a signal handler).

void Gtk::Tree::tree()[gint item]->unselect();

Emits the "unselect_item" signal for the child at position item, thus unselecting the child.

gint Gtk::Tree::child_position(Gtk::TreeItem& child) const;

Returns the position of child in the item list. Can be used to select, unselect, etc. children by reference. Returns -1 if child is not in the tree.

void Gtk::Tree::set_selection_mode(GtkSelectionMode mode );

Sets the selection mode, which can be one of GTK_SELECTION_SINGLE (the default), GTK_SELECTION_BROWSE, GTK_SELECTION_MULTIPLE, or GTK_SELECTION_EXTENDED. This is only defined for root trees, which makes sense, since the root tree "owns" the selection. Setting it for subtrees has no effect at all; the value is simply ignored.

void Gtk::Tree::set_view_mode(GtkTreeViewMode mode );

Sets the "view mode", which can be either GTK_TREE_VIEW_LINE (the default) or GTK_TREE_VIEW_ITEM. The view mode propagates from a tree to its subtrees, and can’t be set exclusively to a subtree (this is not exactly true - see the example code comments). The term "view mode" is rather ambiguous - basically, it controls the way the highlight is drawn when one of a tree’s children is selected. If it’s GTK_TREE_VIEW_LINE, the entire Gtk::TreeItem widget is highlighted, while for GTK_TREE_VIEW_ITEM, only the child widget (i.e. usually the label) is highlighted.

void Gtk::Tree::set_view_lines(guint flag );

Controls whether connecting lines between tree items are drawn. flag is either TRUE, in which case they are, or FALSE, in which case they aren’t. The following are Gtk+ macros which have not yet been implemented in Gtk–. They can be applied using Tree::gtkobj(), however if you really need them ask the Gtk– development team, or write them yourself.

gint GTK_IS_ROOT_TREE (gpointer obj) g

Determine if a generic pointer refers to a ‘Gtk_Tree’ and is a root tree. Though this will accept any pointer, the results of passing it a pointer that does not refer to a Gtk_Tree are undefined and possibly harmful.

Gtk_Tree *GTK_TREE_ROOT_TREE (gpointer obj)

Return the root tree of a pointer to a ‘Gtk_Tree’ object. The above warning applies.

13.5 Tree Item Widget

The Gtk::TreeItem widget, like Gtk::ListItem, is derived from Gtk::Item, which in turn is derived from Gtk::Bin. Therefore, the item itself is a generic container holding exactly one child widget, which can be of any type. The Gtk::TreeItem widget has a number of extra members, but the only one we need be concerned with is the subtree. You can always obtain the subtree of a Gtk::TreeItem in a type-safe manner with the get_subtree() method. Gtk– does not have an interface to the other internals of Gtk_TreeItem, and you shouldn’t need to worry about them. A Gtk::TreeItem usually holds a label, so the constructor TreeItem(const string &label, gfloat x=0.0, gfloat y=0.5) is provided. The same effect can be achieved using code like the following:

tree_item = new Gtk::TreeItem(); label_widget = new Gtk::Label(label); label.set_alignment (0.0, 0.5);

tree_item.add (label_widget); label.show();

You are not forced to add a GtkLabel to a Gtk::TreeItem, you could instead add a GtkHBox or a GtkArrow, or even a GtkNotebook (though your app will likely be quite unpopular in this case). If you remove all the items from a subtree, it will be destroyed and unparented, unless you reference it beforehand, and the Gtk::TreeItem which owns it will be collapsed. So, if you want it to stick around, do something like the following:

tree->ref(); owner = tree->get_owner(); //!!!! No get_owner method tree->remove (item); if (tree->get_parent() == NULL){ owner.expand (); owner.set_subtree (tree); } else tree->unref();

Finally, drag-n-drop does work with Gtk::TreeItems. However, you have to make sure that the Gtk::TreeItem you want to make into a drag item or a drop site has not only been added to a Gtk::Tree, but that each successive parent widget has a parent itself, all the way back to a toplevel or dialog window, when you call Gtk::Widget::drag_source_set() or Gtk::Widget::drag_dest__set(). Otherwise, strange things will happen.

13.5.1 Signals

Gtk::TreeItem inherits the "select", "deselect", and "toggle" signals from GtkItem. In addition, it adds two signals of its own, "expand" and "collapse".

void Gtk::Item::select(); g

This signal is emitted when an item is about to be selected, either after it has been clicked on by the user, or when the program calls Gtk::Tree::item_select(), Gtk::Item::select(), or Gtk::Tree::select_child().

void Gtk::Item::deselect();

This signal is emitted when an item is about to be unselected, either after it has been clicked on by the user, or when the program calls Gtk::Tree::item_deselect() or Gtk::Item::deselect(). In the case of Gtk::TreeItems, it is also emitted by Gtk::Tree::unselect_child(), and sometimes Gtk::Tree::select_child().

void Gtk::Item::toggle();

This signal is emitted when the program calls Gtk::Item::toggle(). The effect it has when emitted on a Gtk::TreeItem is to call Gtk::Tree::select_child() (and never Gtk::Tree::unselect_child()) on the item’s parent tree, if the item has a parent tree. If it doesn’t, then the highlight is reversed on the item.

void Gtk::TreeItem::expand();

This signal is emitted when the tree item’s subtree is about to be expanded, that is, when the user clicks on the plus sign next to the item, or when the program calls Gtk::TreeItem::expand().

void Gtk::TreeItem::collapse();

This signal is emitted when the tree item’s subtree is about to be collapsed, that is, when the user clicks on the minus sign next to the item, or when the program calls Gtk::TreeItem::collapse().

13.5.2 Functions and Macros

static GtkType Gtk::TreeItem::get_type();

Returns the ‘Gtk::TreeItem’ type identifier.

Gtk::TreeItem();

Constructor for Gtk::TreeItem object.

Gtk::TreeItem(const string &label,gfloat x=0.0,gfloat y=0.5);

Creates a new Gtk::TreeItem object, having a single GtkLabel as the sole child.

void Gtk::Item::select();

This function emits the select signal.

void Gtk::Item::deselect();

This function emits the deselect signal.

void Gtk::TreeItem::set_subtree(Gtk::Widget& subtree);

This function adds subtree to the TreeItem, showing it if the TreeItem is expanded, or hiding it if collapsed. Again, remember that the TreeItem must have already been added to a tree for this to work. g

void Gtk::TreeItem::remove_subtree();

This removes all of the TreeItem’s subtree’s children (thus unreferencing and destroying it, any of its children’s sub- trees, and so on...), then removes the subtree itself, and hides the plus/minus sign. The members below are not yet implemented in Gtk::TreeItem.

void gtk_tree_item_expand( Gtk::TreeItem *tree_item );

This emits the "expand" signal on tree_item, which expands it.

void gtk_tree_item_collapse( Gtk::TreeItem *tree_item );

This emits the "collapse" signal on tree_item, which collapses it.

Gtk::TreeItem *GTK_TREE_ITEM (gpointer obj)

Cast a generic pointer to ‘Gtk::TreeItem*’.

Gtk::TreeItemClass *GTK_TREE_ITEM_CLASS (gpointer obj)

Cast a generic pointer to ‘Gtk::TreeItemClass’.

gint GTK_IS_TREE_ITEM (gpointer obj)

Determine if a generic pointer refers to a ‘Gtk::TreeItem’ object.

GtkWidget GTK_TREE_ITEM_SUBTREE (gpointer obj)

Returns a tree item’s subtree (obj should point to a ‘Gtk::TreeItem’ object).

13.6 Tree Example

This is somewhat like the tree example in testgtk.c, but a lot less complete (although much better commented). It puts up a window with a tree, and connects all the signals for the relevant objects, so you can see when they are emitted.

#include #include #include #include #include #include

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

static int cb_quit(GdkEventAny*) { Gtk::Main::quit(); return true; } g

/* for all the Gtk::Item:: and Gtk::TreeItem:: signals */ static void cb_itemsignal (Gtk::TreeItem *item, gchar *signame) { /* It’s a GtkBin, so it has one child, which we know to be a label, so get that */ Gtk::Label *label = dynamic_cast(item->get_child());

/* Get the text of the label */ Gtk::string name=label->get();

/* Get the level of the tree which the item is in */ g_print ("%s called for item %s->%p, level %d\n", signame, name.c_str(), item, dynamic_cast(item->get_parent())->get_level()); }

/* Note that this is never called */ static void cb_unselect_child (Gtk::Widget& child,Gtk::Tree *root_tree, Gtk::Tree *subtree) { g_print ("unselect_child called for root tree %p, subtree %p, child %p\n", root_tree, subtree, &child); }

/* Note that this is called every time the user clicks on an item, whether it is already selected or not. */ static void cb_select_child (Gtk::Widget& child,Gtk::Tree *root_tree, Gtk::Tree *subtree) { g_print ("select_child called for root tree %p, subtree %p, child %p\n", root_tree, subtree, &child); } static void cb_selection_changed (Gtk::Tree *tree) { g_print ("selection_change called for tree %p\n", tree); g_print ("selected objects are:\n");

Gtk::Tree::SelectionList &selection=tree->selection(); Gtk::Tree::SelectionList::iterator i=selection.begin();

while (i!=selection.end()) { /* Get a GtkWidget pointer from the list node */ Gtk::TreeItem *item = (*i); Gtk::Label *label = dynamic_cast(item->get_child()); Gtk::string name=label->get(); g_print ("\t%s on level %d\n", name.c_str(), dynamic_cast(item->get_parent())->get_level()); //g_print ("%p\n",item); ++i; } } int main (int argc, char *argv[]) g

{ Gtk::Window *window; Gtk::ScrolledWindow *scrolled_win; Gtk::Tree *tree; static gchar *itemnames[] = {"Foo", "Bar", "Baz", "Quux", "Maurice"}; gint i;

Gtk::Main myapp (argc, argv);

/* a generic toplevel window */ window = manage( new Gtk::Window (GTK_WINDOW_TOPLEVEL) ); window->delete_event.connect(slot(&cb_quit)); window->set_border_width(5);

/* A generic scrolled window */ scrolled_win = manage( new Gtk::ScrolledWindow () ); scrolled_win->set_policy (GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); scrolled_win->set_usize (150, 200); window->add (*scrolled_win); scrolled_win->show();

/* Create the root tree */ tree = manage( new Gtk::Tree() ); g_print ("root tree is %p\n", tree);

/* connect all GtkTree:: signals */ tree->select_child.connect(bind(slot(&cb_select_child), tree, tree)); tree->unselect_child.connect(bind(slot(&cb_unselect_child), tree, tree)); tree->selection_changed.connect(bind(slot(&cb_selection_changed),tree));

/* Add it to the scrolled window */ scrolled_win->add_with_viewport(*tree);

/* Set the selection mode */ tree->set_selection_mode (GTK_SELECTION_MULTIPLE);

/* Show it */ tree->show ();

for (i = 0; i < 5; i++){ Gtk::Tree *subtree; Gtk::TreeItem *item; gint j;

/* Create a tree item */ item = manage( new Gtk::TreeItem(itemnames[i]) );

/* Connect all GtkItem:: and GtkTreeItem:: signals */ item->select .connect( bind(slot(&cb_itemsignal),item,"select")); item->deselect.connect( bind(slot(&cb_itemsignal),item,"deselect")); item->toggle .connect( bind(slot(&cb_itemsignal),item,"toggle")); g

item->expand .connect( bind(slot(&cb_itemsignal),item,"expand")); item->collapse.connect( bind(slot(&cb_itemsignal),item,"collapse"));

/* Add it to the parent tree */ tree->append(*item);

/* Show it - this can be done at any time */ item->show();

/* Create this item’s subtree */ subtree = manage( new Gtk::Tree() ); g_print ("-> item %s->%p, subtree %p\n", itemnames[i], item, subtree);

/* This is still necessary if you want these signals to be called for the subtree’s children. Note that selection_change will be signalled for the root tree regardless. */ subtree->select_child.connect(bind(slot(&cb_select_child),tree,subtree)); subtree->unselect_child.connect(bind(slot(&cb_unselect_child),tree,subtree));

/* This has absolutely no effect, because it is completely ignored in subtrees */ subtree->set_selection_mode (GTK_SELECTION_SINGLE);

/* Neither does this, but for a rather different reason - the view_mode and view_line values of a tree are propagated to subtrees when they are mapped. So, setting it later on would actually have a (somewhat unpredictable) effect */ subtree->set_view_mode (GTK_TREE_VIEW_ITEM);

/* Set this item’s subtree - note that you cannot do this until AFTER the item has been added to its parent tree! */ item->set_subtree (*subtree); for (j = 0; j < 5; j++){ Gtk::TreeItem *subitem;

/* Create a subtree item, in much the same way */ subitem = manage( new Gtk::TreeItem(itemnames[j]) );

/* Connect all GtkItem:: and GtkTreeItem:: signals */ subitem->select .connect( bind(slot(&cb_itemsignal), subitem, "select")); subitem->deselect.connect( bind(slot(&cb_itemsignal), subitem, "deselect")); subitem->toggle .connect( bind(slot(&cb_itemsignal), subitem, "toggle")); subitem->expand .connect( bind(slot(&cb_itemsignal), subitem, "expand")); subitem->collapse.connect( bind(slot(&cb_itemsignal), subitem, "collapse"));

g_print ("-> -> item %s->%p\n", itemnames[j], subitem); g

/* Add it to its parent tree */ subtree->append (*subitem);

/* Show it */ subitem->show(); } }

/* Show the window and loop endlessly */ window->show ();

Gtk::Main::run(); return 0; }

14 Text Widget (draft)

The Text widget allows multiple lines of text to be displayed and edited. It supports both multi-coloured and multi-font text, allowing them to be mixed in any combination. It also has a wide set of key based text editing commands, which are compatible with Emacs. The text widget supports full cut-and-paste facilities, including the use of double- and triple-click to select a word and a whole line, respectively.

14.1 Creating and Configuring a Text box

The constructor for creating a new Text widget is:

Gtk::Text(Gtk::Adjustment &hadj, Gtk::Adjustment &vadj);

The arguments allow us to give the Text widget pointers to Adjustments that can be used to track the viewing position of the widget. Leaving out the arguments will cause the constructor to create its own Adjustments.

void Gtk::Text::set_adjustments(Gtk::Adjustment &hadj, Gtk::Adjustment &vadj);

The above function allows the horizontal and vertical adjustments of a text widget to be changed at any time. The text widget will not automatically create its own scrollbars when the amount of text to be displayed is too long for the display window. We therefore have to create and add them to the display layout ourselves.

Gtk::VScrollbar vscrollbar = Gtk::VScrollbar(GTK_TEXT(text)->vadj); hbox.pack_start(vscrollbar, FALSE, FALSE, 0); vscrollbar.show();

The above code snippet creates a new vertical scrollbar, and attaches it to the vertical adjustment of the text widget, text. It then packs it into a box, hbox, in the normal way. Unfortunately, Gtk::Text does not currently support horizontal scrollbars. g

There are two main ways in which a Text widget can be used: to allow the user to edit a body of text, or to allow us to display multiple lines of text to the user. In order for us to switch between these modes of operation, the text widget has the following function:

void Gtk::Editable::set_editable(gboolean editable);

The editable argument is a TRUE or FALSE value that specifies whether the user is permitted to edit the contents of the Text widget. When the text widget is editable, it will display a cursor at the current insertion point. You are not, however, restricted to just using the text widget in these two modes. You can toggle the editable state of the text widget at any time, and can insert text at any time. The text widget wraps lines of text that are too long to fit onto a single line of the display window. Its default behaviour is to break words across line breaks. This can be changed using the next function:

void Gtk::Text::set_line_wrap(gboolean line_wrap);

Using this function allows us to specify that the text widget should wrap long lines on word boundaries. The word_wrap argument is a TRUE or FALSE value.

14.2 Text Manipulation

The current insertion point of a Text widget can be set using

void Gtk::Text::set_point(guint index); where index is the position to set the insertion point. Analogous to this is the function for getting the current insertion point:

guint Gtk::Text::get_point();

A function that is useful in combination with the above two functions is

guint Gtk::Text::get_length(); which returns the current length of the text in the Text widget. The length is the number of characters that are within the text block of the widget, including characters such as newlines, which mark the ends of lines. In order to insert text at the current insertion point of a Text widget, the insert() method is used; it also allows us to specify background and foreground colors and a font for the text.

void Gtk::Text::insert(const GdkFont& font, GdkColor& fore, GdkColor& back, nstring& chars, gint length);

The font and colours of the text can also be specified using a context object:

void Gtk::Text_Helpers::Context();

The font and colours of the context can be set using: g

void Gtk::Text_Helpers::Context::set_foreground(const Gdk_Color& color) void Gtk::Text_Helpers::Context::set_background(const Gdk_Color& color) void Gtk::Text_Helpers::Context::set_font(const Gdk_Color& color)

These values can be cleared by calling the same methods without arguments. Corresponding "get" methods also exist for retrieving the values. The default context of the text widget can be set using:

void Gtk::Text::set_context(const Context& gc);

Again, the value can be cleared by calling with no argument. To insert text using a context, use:

void Gtk::Text::insert(const Context& gc, const string& text); or simply:

void Gtk::Text::insert(const string& text); to insert using the text widget’s current context. If the widget’s context is cleared, text with no specified style is inserted. So if you want a plain text widget you only have to use this member. The text widget is one of the few within GTK– that redraws itself dynamically. This means that all changes to the contents of the text widget take effect immediately. This may be undesirable when performing multiple changes to the text widget. In order to allow us to perform multiple updates to the text widget without it continuously redrawing, we can "freeze" the widget, which temporarily stops it from automatically redrawing itself every time it is changed. We can then "thaw" the widget after our updates are complete. The following two functions perform this freeze and thaw action:

void Gtk::Text::freeze(); void Gtk::Text::thaw();

Text is deleted from the text widget relative to the current insertion point by the following two functions. The return value is a TRUE or FALSE indicator of whether the operation was successful.

gint Gtk::Text::backward_delete(guint nchars);

gint Gtk::Text::forward_delete(guint nchars);

To retrieve blocks of text from the text widget, we can use the function

string Gtk::Editable::get_chars(gint start_pos, gint end_pos);

This is a member of the parent class of the text widget. A value of -1 as end_pos signifies the end of the text. The index of the text starts at 0.

14.3 Keyboard Shortcuts

The text widget has a number of pre-installed keyboard shortcuts for common editing, motion and selection functions. These are accessed using Control and Alt key combinations. In addition to these, holding down the Control key whilst using cursor key movement will move the cursor by words rather than characters. Holding down Shift whilst using cursor movement will extend the selection. Any resemblance to the Emacs keybindings is purely coincidental. :) g

14.3.1 Motion Shortcuts

¯ Ctrl-A Beginning of line

¯ Ctrl-E End of line

¯ Ctrl-N Next Line

¯ Ctrl-P Previous Line

¯ Ctrl-B Backward one character

¯ Ctrl-F Forward one character

¯ Alt-B Backward one word

¯ Alt-F Forward one word

14.3.2 Editing Shortcuts

¯ Ctrl-H Delete Backward Character (Backspace)

¯ Ctrl-D Delete Forward Character (Delete)

¯ Ctrl-W Delete Backward Word

¯ Alt-D Delete Forward Word

¯ Ctrl-K Delete to end of line

¯ Ctrl-U Delete line

14.3.3 Selection Shortcuts

¯ Ctrl-X Cut to clipboard

¯ Ctrl-C Copy to clipboard

¯ Ctrl-V Paste from clipboard

14.4 An example

Source location: examples/text/text.cc

#include #include #include #include #include #include #include #include #include #include #include

class AppWindow : public Gtk::Window { g

Gtk::Text text; Gtk::CheckButton edit_check; Gtk::CheckButton wrap_check; public: AppWindow(); ~AppWindow();

void text_toggle_editable () { text.set_editable(edit_check.get_active()); }

void text_toggle_word_wrap () { text.set_word_wrap(wrap_check.get_active()); }

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

}; void close_application( GtkWidget *widget, gpointer data ) { gtk_main_quit(); }

AppWindow::AppWindow() : Gtk::Window(GTK_WINDOW_TOPLEVEL), edit_check("Editable"), wrap_check("Wrap Words") { Gtk::VBox *box1; Gtk::VBox *box2; Gtk::HButtonBox *hbox; Gtk::Button *button; Gtk::HSeparator *separator; Gtk::Table *table; Gtk::VScrollbar *vscrollbar;

FILE *infile;

set_usize(600, 500); set_policy ( TRUE, TRUE, FALSE); set_title ("Text Widget Example"); set_border_width (0);

box1 = manage( new Gtk::VBox (FALSE, 0) ); add(*box1);

box2 = manage( new Gtk::VBox (FALSE, 10) ); box2->set_border_width (10); g

box1->pack_start (*box2, TRUE, TRUE, 0);

table = manage( new Gtk::Table (2, 2, FALSE) ); table->set_row_spacing (0, 2); table->set_col_spacing (0, 2); box2->pack_start (*table, TRUE, TRUE, 0);

/* Create the GtkText widget */ text.set_editable (TRUE); table->attach (text, 0, 1, 0, 1, GTK_EXPAND | GTK_SHRINK | GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);

/* Add a vertical scrollbar to the GtkText widget */ vscrollbar = manage( new Gtk::VScrollbar (*text.get_vadjustment())); table->attach ( *vscrollbar, 1, 2, 0, 1, GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);

/* Get the system colour map and allocate the colour red */ Gdk_Color red("red"); Gdk_Color white("white"); Gdk_Color black("black");

/* Load a fixed font */ Gdk_Font fixed_font("-misc-fixed-medium-r-*-*-*-140-*-*-*-*-*-*");

/* Realizing a widget creates a window for it, ready for us to in- sert some text */ text.realize ();

/* Freeze the text widget, ready for multiple updates */ text.freeze ();

/* Insert some coloured text */ text.insert ( Gdk_Font(), black, white, "Supports ", -1); text.insert ( Gdk_Font(), red, white, "colored ", -1); text.insert ( Gdk_Font(), black, white, "text and different ", -1); text.insert ( fixed_font, black, red , "fonts\n\n", -1);

/* Load the file text.c into the text window */

infile = fopen("text.cc", "r");

if (infile) { char buffer[1024]; int nchars; Gtk::Text::Context cx; cx.set_font(fixed_font); while (1) { nchars = fread(buffer, 1, 1024, infile); buffer[nchars]=’\0’; text.insert ( cx, buffer);

if (nchars < 1024) g

break; }

fclose (infile); }

/* Thaw the text widget, allowing the updates to become visible */ text.thaw ();

hbox = manage( new Gtk::HButtonBox () ); box2->pack_start ( *hbox, FALSE, FALSE, 0);

hbox->pack_start ( edit_check, FALSE, FALSE, 0); edit_check.toggled.connect(slot(this, &AppWindow::text_toggle_editable)); edit_check.set_active(TRUE);

hbox->pack_start (wrap_check, FALSE, TRUE, 0); wrap_check.toggled.connect(slot(this, &AppWindow::text_toggle_word_wrap)); wrap_check.set_active(FALSE);

separator = manage( new Gtk::HSeparator () ); box1->pack_start ( *separator, FALSE, TRUE, 0);

box2 = manage( new Gtk::VBox (FALSE, 10) ); box2->set_border_width (10); box1->pack_start ( *box2, FALSE, TRUE, 0);

button = manage( new Gtk::Button ("close") ); button->clicked.connect( Gtk::Main::quit.slot() ); box2->pack_start ( *button, TRUE, TRUE, 0); button->set_flags( GTK_CAN_DEFAULT); button->grab_default ();

show_all (); }

AppWindow::~AppWindow() {} int main (int argc, char *argv[]) { Gtk::Main m(argc, argv); AppWindow app;

Gtk::Main::run();

return(0); } ,

15 Timeouts, I/O and Idle Functions

15.1 Timeouts

You may be wondering how to make GTK– do useful work while it’s idling along (well, sleeping actually) in Gtk::Main(). Happily, you have several options. Using the following functions you can create a timeout function that will be called every few milliseconds.

Connection Gtk::Main::timeout.connect(const SlotType &sd, guint32 interval);

The first argument is a slot you wish to have called when the timeout occurs. The second argument is the number of milliseconds between calls to your function. You get back a Connection object that can be used to destroy the connection. Use

MyConnection.disconnect();

to destroy the connection. Another way of destroying the Connection is your callback function. It has to be of the type > Slot0

gint MyCallback() { cout << "Hello World!"; return true; }

You can stop the timeout function by returning zero or false from your callback function. Therefore, if you want your function to be called repeatedly, it should return a non-zero value, or true. Here’s an example of this technique: Source location: examples/timeout/timeout.cc

#include #include #include #include #include #include

class TimerExample : public Gtk::Window { // the usual stuff - nothing exciting Gtk::HBox m_box; Gtk::Button m_add_timer, m_del_timer, m_quit; gint m_t_nr;

// the start value for our timer static const gint COUNT_VALUE;

// the timeout value for the timers in [ms] static const gint TIMEOUT_VALUE;

// we need this to store our connections map m_timers;

// this is for storing our timer values ,

// each timer countsa back from COUNT_VALUE to 0 and // if removed when it reaches 0 map m_counters; public: TimerExample();

// the callback functions for add & remove button void add_timer_pressed(); void del_timer_pressed();

// the callback for the timer // note that is not of the type gint callback(void) // since we use bind() to add a data value of type gint to it gint timer_callback(gint timer_nr);

// this will end the application when its window is closed gint delete_event_impl(GdkEventAny*); }; const gint TimerExample::COUNT_VALUE = 5; const gint TimerExample::TIMEOUT_VALUE = 1500;

TimerExample::TimerExample() : m_add_timer("add a new timer"), m_del_timer("remove timer"), m_quit("Quit"), m_box(true,10), m_t_nr(0) { // connect the callbacks m_quit.pressed.connect(Gtk::Main::quit.slot()); m_add_timer.pressed.connect(slot(this,&TimerExample::add_timer_pressed)); m_del_timer.pressed.connect(slot(this,&TimerExample::del_timer_pressed));

// put buttons into container m_box.pack_start(m_add_timer); m_box.pack_start(m_del_timer); m_box.pack_start(m_quit);

// set border and display all set_border_width(10); add(m_box); show_all(); }

void TimerExample::add_timer_pressed() { // creation of a new object prevents long lines and // shows us a little how slots work // we have 0 parameters and gint as return value after calling bind SigC::Slot0 my_slot = bind(slot(this,&TimerExample::timer_callback),m_t_nr); ,

// now connect the slot to Gtk::Main::timeout Gtk::Connection conn = Gtk::Main::timeout.connect(my_slot,TIMEOUT_VALUE);

// memorize connection m_timers[m_t_nr] = conn;

// initialize timer count m_counters[m_t_nr] = COUNT_VALUE + 1;

// print some information on the console cout << "added timeout " << m_t_nr++ << endl; }

void TimerExample::del_timer_pressed() { // are there any timers ? if(m_timers.empty()) { // nope cout << "sorry, there are no timers left" << endl; } else { // get the nr of the first timer gint timer_nr = m_timers.begin()->first; // give a little information to the user cout << "removing timer " << timer_nr << endl; // delete the entry in the counter values m_counters.erase(timer_nr); // destroy the connection !!!!! // this is important since the connection is NOT destroyed when // the according Connection-Object is deleted // The purpose of the connection object is to give you the // possibility to destroy a connection without having to destroy // either the sender or the receiver // Try it and comment out the following line .... m_timers[timer_nr].disconnect(); // destroy the connection m_timers.erase(timer_nr); } }

gint TimerExample::timer_callback(gint timer_nr) { // print the timernr cout << "This is timer " << timer_nr; // decrement & check counter value if(--m_counters[timer_nr] == 0) { cout << " boom" << endl; // delete the counter entry m_counters.erase(timer_nr); // delete the connection entry m_timers.erase(timer_nr); // note that we do not need to call disconnect on the connection // since we Gtk::Main does this for us when we return 0 return 0; ,

}

// print the timer value cout << " - " << m_counters[timer_nr] << "/" << COUNT_VALUE << endl; return 1; }

// the intresting stuff ends here

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

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

TimerExample example;

app.run(); return 0; }

15.2 Monitoring I/O

A nifty feature of GDK (one of the libraries that underlying GTK–) is the ability to have it check for data on a file descriptor for you. This is especially useful for networking applications. The following function is used to do this:

Connection Gtk::Main::input.connect(const SlotType& sd, gint source, GdkInputCondition condition);

The first argument is a slot you wish to have called when then the specified event (see argument 3) occurs on the file

descriptor you specify using argument two. Argument three may be one or a combination (using j)of:

¯ GDK_INPUT_READ - Call your function when there is data ready for reading on your file descriptor.

¯ GDK_INPUT_WRITE - Call your function when the file descriptor is ready for writing.

¯ GDK_INPUT_EXCEPTION - Call your function when an exception happened on the file descriptor.

The return value is a Connection that may be used to stop monitoring this file descriptor using the disconnect following function. The callback function should be declared as follows:

void input_callback(gint source, GdkInputCondition condition); where source and condition are as specified above. As usual the slot is created with slot() and can be a member function of an object. ,

A little (and somewhat dirty) example follows as usual. To use the example just execute it from a terminal; it doesn’t create a window. It will create a pipe named testpipe in the current directory. Then start another shell and execute

cat >testpipe. The example will print each line you enter until you type quit. Source location: examples/input/input.cc

#include #include #include #include #include #include #include #include

using std::istream;

using std::auto_ptr;

using SigC::slot;

auto_ptr input;

// this will be our callback for read operations // there is not much to say. just read a string, // print it and quit the application if the string was quit void MyCallback(int, GdkInputCondition) { Gtk::string dummy; do { (*input) >> dummy; cout << dummy << endl; if(dummy == "quit") Gtk::Main::quit(); } while(input->fail()); }

int main (int argc, char *argv[]) { // the usual Gtk::Main object Gtk::Main app(argc, argv);

// create a fifo for testing purposes if (mkfifo("testfifo",0666) != 0) { cerr << "error creating fifo" << endl; return -1; }

// open the fifo input=new ifstream("testfifo");

// int fd = open("testfifo", 0); // if (fd == -1) { // cerr << "error opening fifo" << endl; // return -1; ,

// }

// assign the fifo’s filedescriptor to our ifstream object //This sucks; it will only ever work with libstdc++-v3, as // both istream::__filebuf_type and the basic_filebuf contructor // that takes an fd are libstdc++-v3 specific. //input=new istream(new ifstream::__filebuf_type(fd,"testfifo"));

// connect the callback function app.input.connect(slot(MyCallback), fd, GDK_INPUT_READ);

// and last but not least - run the application main loop app.run();

// now remove the temporary fifo if(unlink("testfifo")) cerr << "error removing fifo" << endl;

return 0; }

15.3 Idle Functions

What if you have a function you want called when nothing else is happening? Hook it up using the following:

Connection Gtk::Main::idle.connect(Slot0 idlefunc, gint priority);

This causes GTK– to call the specified function whenever nothing else is happening. You can add a priority (lower numbers are higher priorities). If you don’t supply a priority value, then GTK_PRIORITY_DEFAULT will be used. There are two ways to remove the callback: calling disconnect on the Connection object, or returning false (or 0) in the callback function, which should be declared as follows:

int idleFunc();

Since this is very similar to the functions above this explanation should be sufficient to understand what’s going on. However, here’s a little example: Source location: examples/idle/idle.cc

#include #include #include #include #include #include #include #include #include

class IdleExample : public Gtk::Window { // the usual stuff - nothing exciting Gtk::Button m_quit; ,

Gtk::Adjustment m_percentage_c; Gtk::ProgressBar m_progressbar_c; Gtk::Adjustment m_percentage_d; Gtk::ProgressBar m_progressbar_d; public: IdleExample();

// a timer-function gint timer_callback(); // a idle-function gint idle_callback();

gint delete_event_impl(GdkEventAny*); };

IdleExample::IdleExample() : m_quit("Quit"), m_percentage_c(0,0,100,0.5), m_progressbar_c(m_percentage_c), m_percentage_d(0,0,5000,0.5), m_progressbar_d(m_percentage_d) { // connect the callbacks m_quit.pressed.connect(Gtk::Main::quit.slot());

// put buttons into container Gtk::VBox *vbox = manage( new Gtk::VBox(false,5));

// adding a few widgets vbox->pack_start(* manage(new Gtk::Label("Formatting windows drive C:"))); vbox->pack_start(* manage(new Gtk::Label("100 MB"))); vbox->pack_start(m_progressbar_c); m_progressbar_c.set_show_text(true);

vbox->pack_start(* manage(new Gtk::Label("")));

vbox->pack_start(* manage(new Gtk::Label("Formatting windows drive D:"))); vbox->pack_start(* manage(new Gtk::Label("5000 MB"))); vbox->pack_start(m_progressbar_d); m_progressbar_d.set_show_text(true);

Gtk::HBox *hbox = manage( new Gtk::HBox(false,10)); hbox->pack_start(m_quit, true, false); vbox->pack_start(*hbox);

// set border and display all set_border_width(5); add(*vbox); show_all();

// formattinf drive c in timeout callback ;-) Gtk::Main::timeout.connect(slot(this,&IdleExample::timer_callback), 50); ,

// formatting drive d in idle callback ;-) Gtk::Main::idle.connect(slot(this,&IdleExample::idle_callback)); }

// increase the progressbar’s value and remove callback when done gint IdleExample::timer_callback() { float value = m_percentage_c.get_value(); m_percentage_c.set_value(value + 0.5); return value < 99.99; }

// increase the progressbar’s value and remove callback when done // note the diffrence in speed and also the impact of system load // try to increase system load and watch the drive d value gint IdleExample::idle_callback() { float value = m_percentage_d.get_value(); m_percentage_d.set_value(value + 0.5); return value < 4999.99; }

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

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

IdleExample example;

app.run(); return 0; }

This example points out the difference of idle and timeout functions a little. If you need functions that are called periodically, and speed is not very important, then you want timeout functions. If you want functions that are called as often as possible (like calculating a fractal in background), then use idle functions. Try executing the example and increasing the system load. The upper progress bar will increase steadily; the lower one will slow down. gg

16 Advanced Event and Signal Handling (draft)

16.1 Signal Functions

16.1.1 Connecting and Disconnecting Signal Handlers

guint gtk_signal_connect( GtkObject *object, const gchar *name, GtkSignalFunc func, gpointer func_data );

guint gtk_signal_connect_after( GtkObject *object, const gchar *name, GtkSignalFunc func, gpointer func_data );

guint gtk_signal_connect_object( GtkObject *object, const gchar *name, GtkSignalFunc func, GtkObject *slot_object );

guint gtk_signal_connect_object_after( GtkObject *object, const gchar *name, GtkSignalFunc func, GtkObject *slot_object );

guint gtk_signal_connect_full( GtkObject *object, const gchar *name, GtkSignalFunc func, GtkCallbackMarshal marshal, gpointer data, GtkDestroyNotify destroy_func, gint object_signal, gint after );

guint gtk_signal_connect_interp( GtkObject *object, const gchar *name, GtkCallbackMarshal func, gpointer data, GtkDestroyNotify destroy_func, gint after );

void gtk_signal_connect_object_while_alive( GtkObject *object, const gchar *signal, GtkSignalFunc func, GtkObject *alive_object );

void gtk_signal_connect_while_alive( GtkObject *object, const gchar *signal, GtkSignalFunc func, gpointer func_data, GtkObject *alive_object );

void gtk_signal_disconnect( GtkObject *object, guint handler_id ); gg

void gtk_signal_disconnect_by_func( GtkObject *object, GtkSignalFunc func, gpointer data );

16.1.2 Blocking and Unblocking Signal Handlers

void gtk_signal_handler_block( GtkObject *object, guint handler_id);

void gtk_signal_handler_block_by_func( GtkObject *object, GtkSignalFunc func, gpointer data );

void gtk_signal_handler_block_by_data( GtkObject *object, gpointer data );

void gtk_signal_handler_unblock( GtkObject *object, guint handler_id );

void gtk_signal_handler_unblock_by_func( GtkObject *object, GtkSignalFunc func, gpointer data );

void gtk_signal_handler_unblock_by_data( GtkObject *object, gpointer data );

16.1.3 Emitting and Stopping Signals

void gtk_signal_emit( GtkObject *object, guint signal_id, ... );

void gtk_signal_emit_by_name( GtkObject *object, const gchar *name, ... );

void gtk_signal_emitv( GtkObject *object, guint signal_id, GtkArg *params );

void gtk_signal_emitv_by_name( GtkObject *object, const gchar *name, GtkArg *params );

guint gtk_signal_n_emissions( GtkObject *object, guint signal_id );

guint gtk_signal_n_emissions_by_name( GtkObject *object, const gchar *name );

void gtk_signal_emit_stop( GtkObject *object, guint signal_id ); pgpp

void gtk_signal_emit_stop_by_name( GtkObject *object, const gchar *name );

16.2 Signal Emission and Propagation

Signal emission is the process wherby Gtk– runs all handlers for a specific object and signal. First, note that the return value from a signal emission is the return value of the last handler executed. Since event signals are all of type GTK_RUN_LAST, this will be the default (Gtk– supplied) default handler, unless you connect with gtk_signal_connect_after(). The way an event (say GTK_BUTTON_PRESS) is handled, is:

¯ Start with the widget where the event occured.

¯ Emit the generic "event" signal. If that signal handler returns a value of TRUE, stop all processing.

¯ Otherwise, emit a specific, "button_press_event" signal. If that returns TRUE, stop all processing.

¯ Otherwise, go to the widget’s parent, and repeat the above steps.

¯ Contimue until some signal handler returns TRUE, or until the top-level widget is reached.

Some consequences of the above are:

¯ Your handler’s return value will have no effect if there is a default handler, unless you connect with gtk_signal_connect_after().

¯ To prevent the default handler from being run, you need to connect with gtk_signal_connect() and use gtk_signal_emit_stop_by_name() - the return value only affects whether the signal is propagated, not the current emission.

17 Tips For Writing Gtk– Applications (draft)

This section is simply a gathering of wisdom, general style guidelines and hints to creating good Gtk– applications. Currently this section is very short, but hopefully it will get longer in future editions of this tutorial. Use GNU autoconf and automake! They are your friends :) Automake examines C files, determines how they depend on each other, and generates a Makefile so the files can be compiled in the correct order. Autoconf permits automatic configuration of software installation, handling a large number of system quirks to increase portability. I am planning to make a quick intro on them here. When writing C code, use only C comments (beginning with "/*" and ending with "*/"), and don’t use C++-style comments ("//"). Although many C compilers understand C++ comments, others don’t, and the ANSI C standard does not require that C++-style comments be processed as comments.

18 Contributing

This document, like so much other great software out there, was created for free by volunteers. If you are at all knowledgeable about any aspect of GTK– that does not already have documentation, please consider contributing to this document.

If you do decide to contribute, please post your contribution to the GTK– mailing list at

[email protected] >. Also, be aware that the entirety of this document is free, and any addition by you py g

provide must also be free. That is, people must be able to use any portion of your examples in their programs, and copies of this document (including your contribution) may be distributed freely.

19 Tutorial Copyright and Permissions Notice

The Gtk– Tutorial is Copyright (C) 1997 Ian Main. Copyright (C) 1998-1999 Tony Gale. Copyright (C) 1999 Karl Nelson, Guillaume Laurent. Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies. Permission is granted to copy and distribute modified versions of this document under the conditions for verbatim copying, provided that this copyright notice is included exactly as in the original, and that the entire resulting derived work is distributed under the terms of a permission notice identical to this one. Permission is granted to copy and distribute translations of this document into another language, under the above conditions for modified versions. If you are intending to incorporate this document into a published work, please contact the maintainer, and we will make an effort to ensure that you have the most up to date information available. There is no guarantee that this document up to its intended purpose. This is simply provided as a free resource. As such, the authors and maintainers of the information provided within can not make any guarantee that the information is even accurate.

A Gtk– Signals

As Gtk– is an object oriented widget set, it has a hierarchy of inheritance. This inheritance mechanism applies for signals. Therefore, you should refer to the widget hierarchy tree when using the signals listed in this section.

A.1 GtkObject

void GtkObject::destroy (GtkObject *, gpointer);

A.2 GtkWidget

void GtkWidget::show (GtkWidget *, gpointer); void GtkWidget::hide (GtkWidget *, gpointer); void GtkWidget::map (GtkWidget *, gpointer); void GtkWidget::unmap (GtkWidget *, gpointer); void GtkWidget::realize (GtkWidget *, gpointer); void GtkWidget::unrealize (GtkWidget *, gpointer); void GtkWidget::draw (GtkWidget *, g

ggpointer, gpointer); void GtkWidget::draw-focus (GtkWidget *, gpointer); void GtkWidget::draw-default (GtkWidget *, gpointer); void GtkWidget::size-request (GtkWidget *, ggpointer, gpointer); void GtkWidget::size-allocate (GtkWidget *, ggpointer, gpointer); void GtkWidget::state-changed (GtkWidget *, GtkStateType, gpointer); void GtkWidget::parent-set (GtkWidget *, GtkObject *, gpointer); void GtkWidget::style-set (GtkWidget *, GtkStyle *, gpointer); void GtkWidget::add-accelerator (GtkWidget *, gguint, GtkAccelGroup *, gguint, GdkModifierType, GtkAccelFlags, gpointer); void GtkWidget::remove-accelerator (GtkWidget *, GtkAccelGroup *, gguint, GdkModifierType, gpointer); bool GtkWidget::event (GtkWidget *, GdkEvent *, gpointer); bool GtkWidget::button-press-event (GtkWidget *, GdkEvent *, gpointer); bool GtkWidget::button-release-event (GtkWidget *, GdkEvent *, gpointer); bool GtkWidget::motion-notify-event (GtkWidget *, GdkEvent *, gpointer); bool GtkWidget::delete-event (GtkWidget *, GdkEvent *, gpointer); bool GtkWidget::destroy-event (GtkWidget *, GdkEvent *, gpointer); bool GtkWidget::expose-event (GtkWidget *, GdkEvent *, gpointer); bool GtkWidget::key-press-event (GtkWidget *, g

GdkEvent *, gpointer); bool GtkWidget::key-release-event (GtkWidget *, GdkEvent *, gpointer); bool GtkWidget::enter-notify-event (GtkWidget *, GdkEvent *, gpointer); bool GtkWidget::leave-notify-event (GtkWidget *, GdkEvent *, gpointer); bool GtkWidget::configure-event (GtkWidget *, GdkEvent *, gpointer); bool GtkWidget::focus-in-event (GtkWidget *, GdkEvent *, gpointer); bool GtkWidget::focus-out-event (GtkWidget *, GdkEvent *, gpointer); bool GtkWidget::map-event (GtkWidget *, GdkEvent *, gpointer); bool GtkWidget::unmap-event (GtkWidget *, GdkEvent *, gpointer); bool GtkWidget::property-notify-event (GtkWidget *, GdkEvent *, gpointer); bool GtkWidget::selection-clear-event (GtkWidget *, GdkEvent *, gpointer); bool GtkWidget::selection-request-event (GtkWidget *, GdkEvent *, gpointer); bool GtkWidget::selection-notify-event (GtkWidget *, GdkEvent *, gpointer); void GtkWidget::selection-get (GtkWidget *, GtkSelectionData *, gguint, gpointer); void GtkWidget::selection-received (GtkWidget *, GtkSelectionData *, gguint, gpointer); bool GtkWidget::proximity-in-event (GtkWidget *, GdkEvent *, gpointer); bool GtkWidget::proximity-out-event (GtkWidget *, GdkEvent *, gpointer); void GtkWidget::drag-begin (GtkWidget *, GdkDragContext *, gpointer); g

void GtkWidget::drag-end (GtkWidget *, GdkDragContext *, gpointer); void GtkWidget::drag-data-delete (GtkWidget *, GdkDragContext *, gpointer); void GtkWidget::drag-leave (GtkWidget *, GdkDragContext *, gguint, gpointer); bool GtkWidget::drag-motion (GtkWidget *, GdkDragContext *, ggint, ggint, gguint, gpointer); bool GtkWidget::drag-drop (GtkWidget *, GdkDragContext *, ggint, ggint, gguint, gpointer); void GtkWidget::drag-data-get (GtkWidget *, GdkDragContext *, GtkSelectionData *, gguint, gguint, gpointer); void GtkWidget::drag-data-received (GtkWidget *, GdkDragContext *, ggint, ggint, GtkSelectionData *, gguint, gguint, gpointer); bool GtkWidget::client-event (GtkWidget *, GdkEvent *, gpointer); bool GtkWidget::no-expose-event (GtkWidget *, GdkEvent *, gpointer); bool GtkWidget::visibility-notify-event (GtkWidget *, GdkEvent *, gpointer); void GtkWidget::debug-msg (GtkWidget *, GtkString *, gpointer);

A.3 GtkData

void GtkData::disconnect (GtkData *, gpointer); g

A.4 GtkContainer

void GtkContainer::add (GtkContainer *, GtkWidget *, gpointer); void GtkContainer::remove (GtkContainer *, GtkWidget *, gpointer); void GtkContainer::check-resize (GtkContainer *, gpointer); GtkDirectionType GtkContainer::focus (GtkContainer *, GtkDirectionType, gpointer); void GtkContainer::set-focus-child (GtkContainer *, GtkWidget *, gpointer);

A.5 GtkCalendar

void GtkCalendar::month-changed (GtkCalendar *, gpointer); void GtkCalendar::day-selected (GtkCalendar *, gpointer); void GtkCalendar::day-selected-double-click (GtkCalendar *, gpointer); void GtkCalendar::prev-month (GtkCalendar *, gpointer); void GtkCalendar::next-month (GtkCalendar *, gpointer); void GtkCalendar::prev-year (GtkCalendar *, gpointer); void GtkCalendar::next-year (GtkCalendar *, gpointer);

A.6 GtkEditable

void GtkEditable::changed (GtkEditable *, gpointer); void GtkEditable::insert-text (GtkEditable *, GtkString *, ggint, ggpointer, gpointer); void GtkEditable::delete-text (GtkEditable *, ggint, ggint, gpointer); void GtkEditable::activate (GtkEditable *, gpointer); void GtkEditable::set-editable (GtkEditable *, bool, gpointer); void GtkEditable::move-cursor (GtkEditable *, ggint, g

ggint, gpointer); void GtkEditable::move-word (GtkEditable *, ggint, gpointer); void GtkEditable::move-page (GtkEditable *, ggint, ggint, gpointer); void GtkEditable::move-to-row (GtkEditable *, ggint, gpointer); void GtkEditable::move-to-column (GtkEditable *, ggint, gpointer); void GtkEditable::kill-char (GtkEditable *, ggint, gpointer); void GtkEditable::kill-word (GtkEditable *, ggint, gpointer); void GtkEditable::kill-line (GtkEditable *, ggint, gpointer); void GtkEditable::cut-clipboard (GtkEditable *, gpointer); void GtkEditable::copy-clipboard (GtkEditable *, gpointer); void GtkEditable::paste-clipboard (GtkEditable *, gpointer);

A.7 GtkTipsQuery

void GtkTipsQuery::start-query (GtkTipsQuery *, gpointer); void GtkTipsQuery::stop-query (GtkTipsQuery *, gpointer); void GtkTipsQuery::widget-entered (GtkTipsQuery *, GtkWidget *, GtkString *, GtkString *, gpointer); bool GtkTipsQuery::widget-selected (GtkTipsQuery *, GtkWidget *, GtkString *, GtkString *, GdkEvent *, gpointer);

A.8 GtkCList

void GtkCList::select-row (GtkCList *, ggint, ggint, g

GdkEvent *, gpointer); void GtkCList::unselect-row (GtkCList *, ggint, ggint, GdkEvent *, gpointer); void GtkCList::row-move (GtkCList *, ggint, ggint, gpointer); void GtkCList::click-column (GtkCList *, ggint, gpointer); void GtkCList::resize-column (GtkCList *, ggint, ggint, gpointer); void GtkCList::toggle-focus-row (GtkCList *, gpointer); void GtkCList::select-all (GtkCList *, gpointer); void GtkCList::unselect-all (GtkCList *, gpointer); void GtkCList::undo-selection (GtkCList *, gpointer); void GtkCList::start-selection (GtkCList *, gpointer); void GtkCList::end-selection (GtkCList *, gpointer); void GtkCList::toggle-add-mode (GtkCList *, gpointer); void GtkCList::extend-selection (GtkCList *, GtkScrollType, ggfloat, bool, gpointer); void GtkCList::scroll-vertical (GtkCList *, GtkScrollType, ggfloat, gpointer); void GtkCList::scroll-horizontal (GtkCList *, GtkScrollType, ggfloat, gpointer); void GtkCList::abort-column-resize (GtkCList *, gpointer);

A.9 GtkNotebook

void GtkNotebook::switch-page (GtkNotebook *, ggpointer, gguint, gpointer); g

A.10 Gtk::List

void Gtk::List::selection-changed (Gtk::List *, gpointer); void Gtk::List::select-child (Gtk::List *, GtkWidget *, gpointer); void Gtk::List::unselect-child (Gtk::List *, GtkWidget *, gpointer);

A.11 GtkMenuShell

void GtkMenuShell::deactivate (GtkMenuShell *, gpointer); void GtkMenuShell::selection-done (GtkMenuShell *, gpointer); void GtkMenuShell::move-current (GtkMenuShell *, GtkMenuDirectionType, gpointer); void GtkMenuShell::activate-current (GtkMenuShell *, bool, gpointer); void GtkMenuShell::cancel (GtkMenuShell *, gpointer);

A.12 GtkToolbar

void GtkToolbar::orientation-changed (GtkToolbar *, ggint, gpointer); void GtkToolbar::style-changed (GtkToolbar *, ggint, gpointer);

A.13 Gtk::Tree

void Gtk::Tree::selection-changed (Gtk::Tree *, gpointer); void Gtk::Tree::select-child (Gtk::Tree *, GtkWidget *, gpointer); void Gtk::Tree::unselect-child (Gtk::Tree *, GtkWidget *, gpointer);

A.14 GtkButton

void GtkButton::pressed (GtkButton *, gpointer); void GtkButton::released (GtkButton *, gpointer); g

void GtkButton::clicked (GtkButton *, gpointer); void GtkButton::enter (GtkButton *, gpointer); void GtkButton::leave (GtkButton *, gpointer);

A.15 GtkItem

void GtkItem::select (GtkItem *, gpointer); void GtkItem::deselect (GtkItem *, gpointer); void GtkItem::toggle (GtkItem *, gpointer);

A.16 GtkWindow

void GtkWindow::set-focus (GtkWindow *, ggpointer, gpointer);

A.17 GtkHandleBox

void GtkHandleBox::child-attached (GtkHandleBox *, GtkWidget *, gpointer); void GtkHandleBox::child-detached (GtkHandleBox *, GtkWidget *, gpointer);

A.18 GtkToggleButton

void GtkToggleButton::toggled (GtkToggleButton *, gpointer);

A.19 GtkMenuItem

void GtkMenuItem::activate (GtkMenuItem *, gpointer); void GtkMenuItem::activate-item (GtkMenuItem *, gpointer);

A.20 Gtk::ListItem

void Gtk::ListItem::toggle-focus-row (Gtk::ListItem *, gpointer); void Gtk::ListItem::select-all (Gtk::ListItem *, gpointer); void Gtk::ListItem::unselect-all (Gtk::ListItem *, gpointer); g

void Gtk::ListItem::undo-selection (Gtk::ListItem *, gpointer); void Gtk::ListItem::start-selection (Gtk::ListItem *, gpointer); void Gtk::ListItem::end-selection (Gtk::ListItem *, gpointer); void Gtk::ListItem::toggle-add-mode (Gtk::ListItem *, gpointer); void Gtk::ListItem::extend-selection (Gtk::ListItem *, GtkEnum, ggfloat, bool, gpointer); void Gtk::ListItem::scroll-vertical (Gtk::ListItem *, GtkEnum, ggfloat, gpointer); void Gtk::ListItem::scroll-horizontal (Gtk::ListItem *, GtkEnum, ggfloat, gpointer);

A.21 Gtk::TreeItem

void Gtk::TreeItem::collapse (Gtk::TreeItem *, gpointer); void Gtk::TreeItem::expand (Gtk::TreeItem *, gpointer);

A.22 GtkCheckMenuItem

void GtkCheckMenuItem::toggled (GtkCheckMenuItem *, gpointer);

A.23 GtkInputDialog

void GtkInputDialog::enable-device (GtkInputDialog *, ggint, gpointer); void GtkInputDialog::disable-device (GtkInputDialog *, ggint, gpointer);

A.24 GtkColorSelection

void GtkColorSelection::color-changed (GtkColorSelection *, gpointer);

A.25 GtkStatusBar

void GtkStatusbar::text-pushed (GtkStatusbar *, gguint, yp

GtkString *, gpointer); void GtkStatusbar::text-popped (GtkStatusbar *, gguint, GtkString *, gpointer);

A.26 GtkCTree

void GtkCTree::tree-select-row (GtkCTree *, GtkCTreeNode *, ggint, gpointer); void GtkCTree::tree-unselect-row (GtkCTree *, GtkCTreeNode *, ggint, gpointer); void GtkCTree::tree-expand (GtkCTree *, GtkCTreeNode *, gpointer); void GtkCTree::tree-collapse (GtkCTree *, ggpointer, gpointer); void GtkCTree::tree-move (GtkCTree *, GtkCTreeNode *, GtkCTreeNode *, GtkCTreeNode *, gpointer); void GtkCTree::change-focus-row-expansion (GtkCTree *, GtkCTreeExpansionType, gpointer);

A.27 GtkCurve

void GtkCurve::curve-type-changed (GtkCurve *, gpointer);

A.28 GtkAdjustment

void GtkAdjustment::changed (GtkAdjustment *, gpointer); void GtkAdjustment::value-changed (GtkAdjustment *, gpointer);

B GDK Event Types

The follwing data types are passed into event handlers by Gtk–. For each data type listed, the signals that use this data type are listed.

¯ GdkEvent yp

– drag_end_event

¯ GdkEventType

¯ GdkEventAny

– delete_event – destroy_event – map_event – unmap_event – no_expose_event

¯ GdkEventExpose

– expose_event

¯ GdkEventNoExpose

¯ GdkEventVisibility

¯ GdkEventMotion

– motion_notify_event

¯ GdkEventButton

– button_press_event – button_release_event

¯ GdkEventKey

– key_press_event – key_release_event

¯ GdkEventCrossing

– enter_notify_event – leave_notify_event

¯ GdkEventFocus

– focus_in_event – focus_out_event

¯ GdkEventConfigure

– configure_event

¯ GdkEventProperty

– property_notify_event

¯ GdkEventSelection

– selection_clear_event – selection_request_event – selection_notify_event yp

¯ GdkEventProximity

– proximity_in_event – proximity_out_event

¯ GdkEventDragBegin

– drag_begin_event

¯ GdkEventDragRequest

– drag_request_event

¯ GdkEventDropEnter

– drop_enter_event

¯ GdkEventDropLeave

– drop_leave_event

¯ GdkEventDropDataAvailable

– drop_data_available_event

¯ GdkEventClient

– client_event

¯ GdkEventOther

– other_event

ThedatatypeGdkEventType is a special data type that is used by all the other data types as an indicator of the data type being passed to the signal handler. As you will see below, each of the event data structures has a member of this type. It is defined as an enumeration type as follows:

typedef enum { GDK_NOTHING = -1, GDK_DELETE = 0, GDK_DESTROY = 1, GDK_EXPOSE = 2, GDK_MOTION_NOTIFY = 3, GDK_BUTTON_PRESS = 4, GDK_2BUTTON_PRESS = 5, GDK_3BUTTON_PRESS = 6, GDK_BUTTON_RELEASE = 7, GDK_KEY_PRESS = 8, GDK_KEY_RELEASE = 9, GDK_ENTER_NOTIFY = 10, GDK_LEAVE_NOTIFY = 11, GDK_FOCUS_CHANGE = 12, GDK_CONFIGURE = 13, GDK_MAP = 14, GDK_UNMAP = 15, GDK_PROPERTY_NOTIFY = 16, GDK_SELECTION_CLEAR = 17, yp

GDK_SELECTION_REQUEST = 18, GDK_SELECTION_NOTIFY = 19, GDK_PROXIMITY_IN = 20, GDK_PROXIMITY_OUT = 21, GDK_DRAG_BEGIN = 22, GDK_DRAG_REQUEST = 23, GDK_DROP_ENTER = 24, GDK_DROP_LEAVE = 25, GDK_DROP_DATA_AVAIL = 26, GDK_CLIENT_EVENT = 27, GDK_VISIBILITY_NOTIFY = 28, GDK_NO_EXPOSE = 29, GDK_OTHER_EVENT = 9999 /* Deprecated, use filters instead */ } GdkEventType;

The other event type that is different from the others is GdkEvent itself. This is a union of all the other data types, which allows it to be cast to a specific event data type within a signal handler. So, the event data types are defined as follows:

struct _GdkEventAny { GdkEventType type; GdkWindow *window; gint8 send_event; };

struct _GdkEventExpose { GdkEventType type; GdkWindow *window; gint8 send_event; GdkRectangle area; gint count; /* If non-zero, how many more events follow. */ };

struct _GdkEventNoExpose { GdkEventType type; GdkWindow *window; gint8 send_event; /* XXX: does anyone need the X major_code or minor_code fields? */ };

struct _GdkEventVisibility { GdkEventType type; GdkWindow *window; gint8 send_event; GdkVisibilityState state; };

struct _GdkEventMotion { GdkEventType type; yp

GdkWindow *window; gint8 send_event; guint32 time; gdouble x; gdouble y; gdouble pressure; gdouble xtilt; gdouble ytilt; guint state; gint16 is_hint; GdkInputSource source; guint32 deviceid; gdouble x_root, y_root; }; struct _GdkEventButton { GdkEventType type; GdkWindow *window; gint8 send_event; guint32 time; gdouble x; gdouble y; gdouble pressure; gdouble xtilt; gdouble ytilt; guint state; guint button; GdkInputSource source; guint32 deviceid; gdouble x_root, y_root; }; struct _GdkEventKey { GdkEventType type; GdkWindow *window; gint8 send_event; guint32 time; guint state; guint keyval; gint length; gchar *string; }; struct _GdkEventCrossing { GdkEventType type; GdkWindow *window; gint8 send_event; GdkWindow *subwindow; GdkNotifyType detail; }; struct _GdkEventFocus yp

{ GdkEventType type; GdkWindow *window; gint8 send_event; gint16 in; }; struct _GdkEventConfigure { GdkEventType type; GdkWindow *window; gint8 send_event; gint16 x, y; gint16 width; gint16 height; }; struct _GdkEventProperty { GdkEventType type; GdkWindow *window; gint8 send_event; GdkAtom atom; guint32 time; guint state; }; struct _GdkEventSelection { GdkEventType type; GdkWindow *window; gint8 send_event; GdkAtom selection; GdkAtom target; GdkAtom property; guint32 requestor; guint32 time; };

/* This event type will be used pretty rarely. It only is important for XInput aware programs that are drawing their own cursor */ struct _GdkEventProximity { GdkEventType type; GdkWindow *window; gint8 send_event; guint32 time; GdkInputSource source; guint32 deviceid; }; struct _GdkEventDragRequest { GdkEventType type; yp

GdkWindow *window; gint8 send_event; guint32 requestor; union { struct { guint protocol_version:4; guint sendreply:1; guint willaccept:1; guint delete_data:1; /* Do *not* delete if link is sent, only if data is sent */ guint senddata:1; guint reserved:22; } flags; glong allflags; }u; guint8 isdrop; /* This gdk event can be generated by a couple of X events - this lets the app know whether the drop really occurred or we just set the data */

GdkPoint drop_coords; gchar *data_type; guint32 timestamp; }; struct _GdkEventDragBegin { GdkEventType type; GdkWindow *window; gint8 send_event; union { struct { guint protocol_version:4; guint reserved:28; } flags; glong allflags; }u; }; struct _GdkEventDropEnter { GdkEventType type; GdkWindow *window; gint8 send_event; guint32 requestor; union { struct { guint protocol_version:4; guint sendreply:1; guint extended_typelist:1; guint reserved:26; } flags; glong allflags; }u; }; yp

struct _GdkEventDropLeave { GdkEventType type; GdkWindow *window; gint8 send_event; guint32 requestor; union { struct { guint protocol_version:4; guint reserved:28; } flags; glong allflags; }u; }; struct _GdkEventDropDataAvailable { GdkEventType type; GdkWindow *window; gint8 send_event; guint32 requestor; union { struct { guint protocol_version:4; guint isdrop:1; guint reserved:25; } flags; glong allflags; }u; gchar *data_type; /* MIME type */ gulong data_numbytes; gpointer data; guint32 timestamp; GdkPoint coords; }; struct _GdkEventClient { GdkEventType type; GdkWindow *window; gint8 send_event; GdkAtom message_type; gushort data_format; union { char b[20]; short s[10]; long l[5]; } data; }; struct _GdkEventOther { GdkEventType type; GdkWindow *window; gint8 send_event; p

GdkXEvent *xevent; };

C Code Examples

Below are the code examples that are used in the above text which are not included in complete form elsewhere.

C.1 Tictactoe

C.1.1 tictactoe.h example_incl_scr(tictactoe/tictactoe.h)

C.1.2 tictactoe.c

Source location: examples/tictactoe/tictactoe.cc

/* example-start tictactoe tictactoe.c */

/* GTK - The GIMP Toolkit * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include #include #include "tictactoe.h"

TicTacToe::TicTacToe () { Gtk::Table *table; Gtk::ToggleButton *button; gint i,j;

table = manage( new Gtk::Table (3, 3, TRUE) ); add ( *table);

for (i=0;i<3; i++) p

for (j=0;j<3; j++) { buttons[i][j] = button = manage( new Gtk::ToggleButton ()); table-> attach (*button, i, i+1, j, j+1); button-> toggled.connect ( bind( slot(this, &TicTacToe::toggle), button)); button-> set_usize (20, 20); }

table-> show_all(); }

TicTacToe::~TicTacToe() {} void TicTacToe::clear () { freeze();

for (int i=0;i<3;i++) for (int j=0;j<3;j++) buttons[i][j]-> set_active (false);

thaw(); } void TicTacToe::toggle (Gtk::ToggleButton *button) { int i,k; if (frozen) return;

static int rwins[8][3] = { { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 }, {0,1,2},{0,1,2},{0,1,2}, {0,1,2},{0,1,2}}; static int cwins[8][3] = { { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 }, {0,0,0},{1,1,1},{2,2,2}, {0,1,2},{2,1,0}};

bool success, found;

for (k=0; k<8; k++) { success = true; found = false;

for (i=0;i<3;i++) { success &= buttons[rwins[k][i]][cwins[k][i]]-> get_active(); found |= buttons[rwins[k][i]][cwins[k][i]] == button; }

if (success && found) { p

tictactoe(); break; } } }

/* example-end */

C.1.3 ttt_test.c

Source location: examples/tictactoe/ttt_test.cc

#include #include #include "tictactoe.h"

void win (TicTacToe *ttt) { g_print ("Yay!\n"); ttt-> clear(); }

int main (int argc, char *argv[]) { Gtk::Window *window; TicTacToe *ttt;

Gtk::Main m(argc, argv);

ttt = manage( new TicTacToe () ); ttt-> tictactoe.connect ( bind (slot (&win), ttt) );

window = manage( new Gtk::Window () ); window-> set_title ("Tic-Tac-Toe"); window-> destroy.connect ( Gtk::Main::quit.slot() ); window-> set_border_width (10); window-> add (*ttt); window-> show_all ();

Gtk::Main::run ();

return 0; }

C.2 GtkDial

C.2.1 gtkdial.h

Source location: examples/gtkdial/gtkdial.h

/* example-start gtkdial gtkdial.h */ p

/* GTK - The GIMP Toolkit * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __GTK_DIAL_H__ #define __GTK_DIAL_H__

#include #include #include

#ifdef __cplusplus extern "C" { #endif /* __cplusplus */

#define GTK_DIAL(obj) GTK_CHECK_CAST (obj, gtk_dial_get_type (), GtkDial) #define GTK_DIAL_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, gtk_dial_get_type (), GtkDialClass) #define GTK_IS_DIAL(obj) GTK_CHECK_TYPE (obj, gtk_dial_get_type ())

typedef struct _GtkDial GtkDial; typedef struct _GtkDialClass GtkDialClass; struct _GtkDial { GtkWidget widget;

/* update policy (GTK_UPDATE_[CONTINUOUS/DELAYED/DISCONTINUOUS]) */ guint policy : 2;

/* Button currently pressed or 0 if none */ guint8 button;

/* Dimensions of dial components */ gint radius; gint pointer_width; p

/* ID of update timer, or 0 if none */ guint32 timer;

/* Current angle */ gfloat angle;

/* Old values from adjustment stored so we know when something changes */ gfloat old_value; gfloat old_lower; gfloat old_upper;

/* The adjustment object that stores the data for this dial */ GtkAdjustment *adjustment; };

struct _GtkDialClass { GtkWidgetClass parent_class; };

GtkWidget* gtk_dial_new (GtkAdjustment *adjustment); guint gtk_dial_get_type (void); GtkAdjustment* gtk_dial_get_adjustment (GtkDial *dial); void gtk_dial_set_update_policy (GtkDial *dial, GtkUpdateType policy);

void gtk_dial_set_adjustment (GtkDial *dial, GtkAdjustment *adjustment); #ifdef __cplusplus } #endif /* __cplusplus */

#endif /* __GTK_DIAL_H__ */ /* example-end */

C.2.2 gtkdial.c

Source location: examples/gtkdial/gtkdial.cc

/* example-start gtkdial gtkdial.c */

/* GTK - The GIMP Toolkit * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU p

* Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include #include #include

#include "gtkdial.h"

#define SCROLL_DELAY_LENGTH 300 #define DIAL_DEFAULT_SIZE 100

/* Forward declarations */ static void gtk_dial_class_init (GtkDialClass *klass); static void gtk_dial_init (GtkDial *dial); static void gtk_dial_destroy (GtkObject *object); static void gtk_dial_realize (GtkWidget *widget); static void gtk_dial_size_request (GtkWidget *widget, GtkRequisition *requisition); static void gtk_dial_size_allocate (GtkWidget *widget, GtkAllocation *allocation); static gint gtk_dial_expose (GtkWidget *widget, GdkEventExpose *event); static gint gtk_dial_button_press (GtkWidget *widget, GdkEventButton *event); static gint gtk_dial_button_release (GtkWidget *widget, GdkEventButton *event); static gint gtk_dial_motion_notify (GtkWidget *widget, GdkEventMotion *event); static gint gtk_dial_timer (GtkDial *dial); static void gtk_dial_update_mouse (GtkDial *dial, gint x, gint y); static void gtk_dial_update (GtkDial *dial); static void gtk_dial_adjustment_changed (GtkAdjustment *adjustment, gpointer data); static void gtk_dial_adjustment_value_changed (GtkAdjustment *adjustment, gpointer data);

/* Local data */ static GtkWidgetClass *parent_class = NULL; guint gtk_dial_get_type () { static guint dial_type = 0;

if (!dial_type) { p

GtkTypeInfo dial_info = { "GtkDial", sizeof (GtkDial), sizeof (GtkDialClass), (GtkClassInitFunc) gtk_dial_class_init, (GtkObjectInitFunc) gtk_dial_init, (GtkArgSetFunc) NULL, (GtkArgGetFunc) NULL, };

dial_type = gtk_type_unique (gtk_widget_get_type (), &dial_info); }

return dial_type; } static void gtk_dial_class_init (GtkDialClass *class) { GtkObjectClass *object_class; GtkWidgetClass *widget_class;

object_class = (GtkObjectClass*) class; widget_class = (GtkWidgetClass*) class;

parent_class = gtk_type_class (gtk_widget_get_type ());

object_class->destroy = gtk_dial_destroy;

widget_class->realize = gtk_dial_realize; widget_class->expose_event = gtk_dial_expose; widget_class->size_request = gtk_dial_size_request; widget_class->size_allocate = gtk_dial_size_allocate; widget_class->button_press_event = gtk_dial_button_press; widget_class->button_release_event = gtk_dial_button_release; widget_class->motion_notify_event = gtk_dial_motion_notify; } static void gtk_dial_init (GtkDial *dial) { dial->button = 0; dial->policy = GTK_UPDATE_CONTINUOUS; dial->timer = 0; dial->radius = 0; dial->pointer_width = 0; dial->angle = 0.0; dial->old_value = 0.0; dial->old_lower = 0.0; dial->old_upper = 0.0; dial->adjustment = NULL; }

GtkWidget* p

gtk_dial_new (GtkAdjustment *adjustment) { GtkDial *dial;

dial = gtk_type_new (gtk_dial_get_type ());

if (!adjustment) adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);

gtk_dial_set_adjustment (dial, adjustment);

return GTK_WIDGET (dial); } static void gtk_dial_destroy (GtkObject *object) { GtkDial *dial;

g_return_if_fail (object != NULL); g_return_if_fail (GTK_IS_DIAL (object));

dial = GTK_DIAL (object);

if (dial->adjustment) gtk_object_unref (GTK_OBJECT (dial->adjustment));

if (GTK_OBJECT_CLASS (parent_class)->destroy) (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); }

GtkAdjustment* gtk_dial_get_adjustment (GtkDial *dial) { g_return_val_if_fail (dial != NULL, NULL); g_return_val_if_fail (GTK_IS_DIAL (dial), NULL);

return dial->adjustment; } void gtk_dial_set_update_policy (GtkDial *dial, GtkUpdateType policy) { g_return_if_fail (dial != NULL); g_return_if_fail (GTK_IS_DIAL (dial));

dial->policy = policy; } void gtk_dial_set_adjustment (GtkDial *dial, GtkAdjustment *adjustment) { g_return_if_fail (dial != NULL); p

g_return_if_fail (GTK_IS_DIAL (dial));

if (dial->adjustment) { gtk_signal_disconnect_by_data (GTK_OBJECT (dial->adjustment), (gpointer) dial); gtk_object_unref (GTK_OBJECT (dial->adjustment)); }

dial->adjustment = adjustment; gtk_object_ref (GTK_OBJECT (dial->adjustment));

gtk_signal_connect (GTK_OBJECT (adjustment), "changed", (GtkSignalFunc) gtk_dial_adjustment_changed, (gpointer) dial); gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed", (GtkSignalFunc) gtk_dial_adjustment_value_changed, (gpointer) dial);

dial->old_value = adjustment->value; dial->old_lower = adjustment->lower; dial->old_upper = adjustment->upper;

gtk_dial_update (dial); } static void gtk_dial_realize (GtkWidget *widget) { GtkDial *dial; GdkWindowAttr attributes; gint attributes_mask;

g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_DIAL (widget));

GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); dial = GTK_DIAL (widget);

attributes.x = widget->allocation.x; attributes.y = widget->allocation.y; attributes.width = widget->allocation.width; attributes.height = widget->allocation.height; attributes.wclass = GDK_INPUT_OUTPUT; attributes.window_type = GDK_WINDOW_CHILD; attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK; attributes.visual = gtk_widget_get_visual (widget); attributes.colormap = gtk_widget_get_colormap (widget);

attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; widget->window = gdk_window_new (widget->parent->window, &at- tributes, attributes_mask); p

widget->style = gtk_style_attach (widget->style, widget->window);

gdk_window_set_user_data (widget->window, widget);

gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE); } static void gtk_dial_size_request (GtkWidget *widget, GtkRequisition *requisition) { requisition->width = DIAL_DEFAULT_SIZE; requisition->height = DIAL_DEFAULT_SIZE; } static void gtk_dial_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { GtkDial *dial;

g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_DIAL (widget)); g_return_if_fail (allocation != NULL);

widget->allocation = *allocation; dial = GTK_DIAL (widget);

if (GTK_WIDGET_REALIZED (widget)) {

gdk_window_move_resize (widget->window, allocation->x, allocation->y, allocation->width, allocation->height);

} dial->radius = MIN(allocation->width,allocation->height) * 0.45; dial->pointer_width = dial->radius / 5; } static gint gtk_dial_expose (GtkWidget *widget, GdkEventExpose *event) { GtkDial *dial; GdkPoint points[3]; gdouble s,c; gdouble theta; gint xc, yc; gint tick_length; gint i;

g_return_val_if_fail (widget != NULL, FALSE); g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE); g_return_val_if_fail (event != NULL, FALSE); p

if (event->count > 0) return FALSE;

dial = GTK_DIAL (widget);

gdk_window_clear_area (widget->window, 0, 0, widget->allocation.width, widget->allocation.height);

xc = widget->allocation.width/2; yc = widget->allocation.height/2;

/* Draw ticks */

for (i=0; i<25; i++) { theta = (i*M_PI/18. - M_PI/6.); s = sin(theta); c = cos(theta);

tick_length = (i%6 == 0) ? dial->pointer_width : dial->pointer_width/2;

gdk_draw_line (widget->window, widget->style->fg_gc[widget->state], xc + c*(dial->radius - tick_length), yc - s*(dial->radius - tick_length), xc + c*dial->radius, yc - s*dial->radius); }

/* Draw pointer */

s = sin(dial->angle); c = cos(dial->angle);

points[0].x = xc + s*dial->pointer_width/2; points[0].y = yc + c*dial->pointer_width/2; points[1].x = xc + c*dial->radius; points[1].y = yc - s*dial->radius; points[2].x = xc - s*dial->pointer_width/2; points[2].y = yc - c*dial->pointer_width/2;

gtk_draw_polygon (widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT, points, 3, TRUE);

return FALSE; } p

static gint gtk_dial_button_press (GtkWidget *widget, GdkEventButton *event) { GtkDial *dial; gint dx, dy; double s, c; double d_parallel; double d_perpendicular;

g_return_val_if_fail (widget != NULL, FALSE); g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE); g_return_val_if_fail (event != NULL, FALSE);

dial = GTK_DIAL (widget);

/* Determine if button press was within pointer region - we do this by computing the parallel and perpendicular distance of the point where the mouse was pressed from the line passing through the pointer */

dx = event->x - widget->allocation.width / 2; dy = widget->allocation.height / 2 - event->y;

s = sin(dial->angle); c = cos(dial->angle);

d_parallel = s*dy + c*dx; d_perpendicular = fabs(s*dx - c*dy);

if (!dial->button && (d_perpendicular < dial->pointer_width/2) && (d_parallel > - dial->pointer_width)) { gtk_grab_add (widget);

dial->button = event->button;

gtk_dial_update_mouse (dial, event->x, event->y); }

return FALSE; } static gint gtk_dial_button_release (GtkWidget *widget, GdkEventButton *event) { GtkDial *dial;

g_return_val_if_fail (widget != NULL, FALSE); g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE); g_return_val_if_fail (event != NULL, FALSE);

dial = GTK_DIAL (widget); p

if (dial->button == event->button) { gtk_grab_remove (widget);

dial->button = 0;

if (dial->policy == GTK_UPDATE_DELAYED) gtk_timeout_remove (dial->timer);

if ((dial->policy != GTK_UPDATE_CONTINUOUS) && (dial->old_value != dial->adjustment->value)) gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed"); }

return FALSE; } static gint gtk_dial_motion_notify (GtkWidget *widget, GdkEventMotion *event) { GtkDial *dial; GdkModifierType mods; gint x, y, mask;

g_return_val_if_fail (widget != NULL, FALSE); g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE); g_return_val_if_fail (event != NULL, FALSE);

dial = GTK_DIAL (widget);

if (dial->button != 0) { x = event->x; y = event->y;

if (event->is_hint || (event->window != widget->window)) gdk_window_get_pointer (widget->window, &x, &y, &mods);

switch (dial->button) { case 1: mask = GDK_BUTTON1_MASK; break; case 2: mask = GDK_BUTTON2_MASK; break; case 3: mask = GDK_BUTTON3_MASK; break; default: mask = 0; break; } p

if (mods & mask) gtk_dial_update_mouse (dial, x,y); }

return FALSE; } static gint gtk_dial_timer (GtkDial *dial) { g_return_val_if_fail (dial != NULL, FALSE); g_return_val_if_fail (GTK_IS_DIAL (dial), FALSE);

if (dial->policy == GTK_UPDATE_DELAYED) gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed");

return FALSE; } static void gtk_dial_update_mouse (GtkDial *dial, gint x, gint y) { gint xc, yc; gfloat old_value;

g_return_if_fail (dial != NULL); g_return_if_fail (GTK_IS_DIAL (dial));

xc = GTK_WIDGET(dial)->allocation.width / 2; yc = GTK_WIDGET(dial)->allocation.height / 2;

old_value = dial->adjustment->value; dial->angle = atan2(yc-y, x-xc);

if (dial->angle < -M_PI/2.) dial->angle += 2*M_PI;

if (dial->angle < -M_PI/6) dial->angle = -M_PI/6;

if (dial->angle > 7.*M_PI/6.) dial->angle = 7.*M_PI/6.;

dial->adjustment->value = dial->adjustment->lower + (7.*M_PI/6 - dial->angle) * (dial->adjustment->upper - dial->adjustment->lower) / (4.*M_PI/3.);

if (dial->adjustment->value != old_value) { if (dial->policy == GTK_UPDATE_CONTINUOUS) { gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed"); } else { p

gtk_widget_draw (GTK_WIDGET(dial), NULL);

if (dial->policy == GTK_UPDATE_DELAYED) { if (dial->timer) gtk_timeout_remove (dial->timer);

dial->timer = gtk_timeout_add (SCROLL_DELAY_LENGTH, (GtkFunction) gtk_dial_timer, (gpointer) dial); } } } } static void gtk_dial_update (GtkDial *dial) { gfloat new_value;

g_return_if_fail (dial != NULL); g_return_if_fail (GTK_IS_DIAL (dial));

new_value = dial->adjustment->value;

if (new_value < dial->adjustment->lower) new_value = dial->adjustment->lower;

if (new_value > dial->adjustment->upper) new_value = dial->adjustment->upper;

if (new_value != dial->adjustment->value) { dial->adjustment->value = new_value; gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed"); }

dial->angle = 7.*M_PI/6. - (new_value - dial->adjustment->lower) * 4.*M_PI/3. / (dial->adjustment->upper - dial->adjustment->lower);

gtk_widget_draw (GTK_WIDGET(dial), NULL); } static void gtk_dial_adjustment_changed (GtkAdjustment *adjustment, gpointer data) { GtkDial *dial;

g_return_if_fail (adjustment != NULL); g_return_if_fail (data != NULL);

dial = GTK_DIAL (data);

if ((dial->old_value != adjustment->value) || p

(dial->old_lower != adjustment->lower) || (dial->old_upper != adjustment->upper)) { gtk_dial_update (dial);

dial->old_value = adjustment->value; dial->old_lower = adjustment->lower; dial->old_upper = adjustment->upper; } }

static void gtk_dial_adjustment_value_changed (GtkAdjustment *adjustment, gpointer data) { GtkDial *dial;

g_return_if_fail (adjustment != NULL); g_return_if_fail (data != NULL);

dial = GTK_DIAL (data);

if (dial->old_value != adjustment->value) { gtk_dial_update (dial);

dial->old_value = adjustment->value; } } /* example-end */

C.3 Scribble

Source location: examples/scribble-simple/scribble-simple.cc

/* GTK - The GIMP Toolkit * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * p

* Revision history: * Modified for gtk-- by [email protected] * Modified for gdk-- by [email protected] * Modified (Added Erase function) by [email protected] * General cleanup by Karl Nelson. */

#include #include #include #include #include #include class ScribbleDrawingArea : public Gtk::DrawingArea { /* Backing pixmap for drawing area */

Gdk_Pixmap pixmap; Gdk_GC gc; Gdk_GC brush_gc; Gdk_Window win; Gdk_Visual visual;

virtual gint configure_event_impl (GdkEventConfigure *event); virtual gint expose_event_impl (GdkEventExpose *event); virtual gint button_press_event_impl (GdkEventButton *event); virtual gint motion_notify_event_impl (GdkEventMotion *event); void draw_brush (gdouble x, gdouble y); public: void erase();

ScribbleDrawingArea (); ~ScribbleDrawingArea ();

};

ScribbleDrawingArea::ScribbleDrawingArea() : Gtk::DrawingArea(), pixmap (0) { set_events (GDK_EXPOSURE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK); }

ScribbleDrawingArea::~ScribbleDrawingArea() {}

/* Create a new backing pixmap of the appropriate size */ int ScribbleDrawingArea::configure_event_impl (GdkEventConfigure * /* event */) { p

win = get_window(); visual = win.get_visual();

if (pixmap) pixmap.release();

gc = get_style()->get_white_gc(); pixmap.create(get_window(), width(), height());

pixmap.draw_rectangle (gc, true, 0, 0, width(), height());

return true; }

/* Redraw the screen from the backing pixmap */ int ScribbleDrawingArea::expose_event_impl (GdkEventExpose *event) {

gc = get_style()->get_fg_gc(get_state()); win.draw_pixmap(gc , pixmap, event->area.x, event->area.y, event->area.x, event->area.y, event->area.width, event->area.height);

return false; }

/* Draw a rectangle on the screen */ void ScribbleDrawingArea::draw_brush (gdouble x, gdouble y) { GdkRectangle update_rect; update_rect.x = (int)x - 5; update_rect.y = (int)y - 5; update_rect.width = 10; update_rect.height = 10;

// we can’t create the brush gc in the ctor because there was not window // so we will initialize it here once, and use it from then on. if (!brush_gc) { brush_gc = Gdk_GC(pixmap); Gdk_Color red("red"); get_colormap().alloc(red); brush_gc.set_foreground(red); } pixmap.draw_rectangle( brush_gc, true, update_rect.x, update_rect.y, update_rect.width, update_rect.height); draw(&update_rect); } gint ScribbleDrawingArea::button_press_event_impl (GdkEventButton *event) p

{ if (event->button == 1 && pixmap) draw_brush (event->x, event->y);

return true; } gint ScribbleDrawingArea::motion_notify_event_impl (GdkEventMotion *event) { int x, y; GdkModifierType state; Gdk_Window window(event->window); if (event->is_hint) window.get_pointer (x, y, state); else { x = (int)event->x; y = (int)event->y; state = (GdkModifierType) event->state; }

if (state & GDK_BUTTON1_MASK && pixmap) draw_brush (x, y);

return true; } void ScribbleDrawingArea::erase() {

// clear pixmap area to white. gc = get_style()->get_white_gc(); pixmap.draw_rectangle (gc, true, 0, 0, width(), height());

// request a refresh of whole area. draw(0); }

/*****************************************************************/ class ScribbleWindow : public Gtk::Window {

ScribbleDrawingArea drawing_area; Gtk::Button eraser; Gtk::Button button; Gtk::VBox vbox; public: ScribbleWindow (); ~ScribbleWindow (); };

ScribbleWindow::ScribbleWindow () : Gtk::Window(GTK_WINDOW_TOPLEVEL), vbox (false, 0),eraser("erase"), p

button ("quit") { add (vbox);

/* Create the drawing area */ drawing_area.size (400, 400); vbox.pack_start (drawing_area, true, true, 0);

/* Add the buttons */ vbox.pack_start (eraser, false, false, 0); vbox.pack_start (button, false, false, 0); eraser.clicked.connect(slot(drawing_area, &ScribbleDrawingArea::erase)); button.clicked.connect(destroy.slot()); destroy.connect(Gtk::Main::quit.slot());

drawing_area.show(); eraser.show(); button.show(); vbox.show(); }

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

window.show(); myapp.run();

return 0; }