c++-gtk-utils
window.h
Go to the documentation of this file.
1 /* Copyright (C) 2005 to 2011 Chris Vine
2 
3 The library comprised in this file or of which this file is part is
4 distributed by Chris Vine under the GNU Lesser General Public
5 License as follows:
6 
7  This library is free software; you can redistribute it and/or
8  modify it under the terms of the GNU Lesser General Public License
9  as published by the Free Software Foundation; either version 2.1 of
10  the License, or (at your option) any later version.
11 
12  This library is distributed in the hope that it will be useful, but
13  WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  Lesser General Public License, version 2.1, for more details.
16 
17  You should have received a copy of the GNU Lesser General Public
18  License, version 2.1, along with this library (see the file LGPL.TXT
19  which came with this source code package in the c++-gtk-utils
20  sub-directory); if not, write to the Free Software Foundation, Inc.,
21  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 
23 */
24 
25 #ifndef CGU_WINDOW_H
26 #define CGU_WINDOW_H
27 
28 #include <gtk/gtk.h>
29 
31 
32 /**
33  * @class Cgu::WinBase window.h c++-gtk-utils/window.h
34  * @brief This is a class for managing the lifetime of top level
35  * widgets.
36  * @sa MainWidgetBase Application
37  *
38  * This class provides a base class for lifetime management of top
39  * level windows which can make GTK+ exception safe. It would be
40  * possible to use GTK+ to control the lifetime of the contained GTK+
41  * window object, through the delete event handler and the destroy
42  * signal. However in the case of a blocking window (say a dialog)
43  * this would result in an invalid object remaining in scope. It is
44  * better to use the C++ lifetime handling mechanisms to control
45  * object status, and this class does that. Where a Cgu::WinBase
46  * object is created as an auto (local) object its contained GTK+ top
47  * level window object will be valid while it remains in scope. For a
48  * Cgu::WinBase object which is not blocking (on which exec() is not
49  * called) and which is therefore created on free store the WinBase
50  * class will delete its own memory and destroy its contained GTK+ top
51  * level window object when it is closed (see below about the close()
52  * function).
53  *
54  * If a NULL pointer is passed as the last argument of its
55  * constructor, a new window object will be created with
56  * gtk_window_new(GTK_WINDOW_TOPLEVEL). However, alternatively a
57  * pre-formed object deriving from GtkWindow (such as a GtkDialog or
58  * GtkMessageDialog object) can be passed as that argument, in which
59  * case the class will manage that object.
60  *
61  * A window will block with a call to exec(), which returns an int
62  * value. The exec() method obtains its return value by calling the
63  * virtual protected function get_exec_val(), which by default returns
64  * 0 but can be overridden to return something more meaningful. If
65  * something other than an int needs to be returned, it will be
66  * necessary for a derived class to provide an extractor function to
67  * obtain the data from the object after exec() has returned.
68  *
69  * A convenience virtual protected on_delete_event() method is
70  * provided for any derived class which needs to respond to that
71  * signal. It does not return a value (it does not cause a destroy
72  * event to be emitted as in the case of a GTK+ delete event handler
73  * returning false).
74  *
75  * If the class is constructed on free store and exec() has not been
76  * called on it, it self-destroys and any memory allocated to it freed
77  * by calling the protected close() function. If exec() has been
78  * called for an object, a call to close() will only terminate the
79  * window event loop (that is, cause exec() to unblock), which is
80  * correct for an auto (local) object as it will destroy itself when
81  * it goes out of scope. By default (that is, if not overridden by
82  * any derived classes) on_delete_event() calls close().
83  *
84  * No special memory management for GTK+ widgets contained in the
85  * WinBase object (or in an object of a class derived from WinBase) is
86  * needed. The GTK+ object system takes care of that, and they will
87  * be released when the life of the WinBase object finishes. For this
88  * reason, the WinBase class can be used to make GTK+ exception safe:
89  * as its constructor does not throw, exceptions thrown in the
90  * constructors of classes derived from it will be correctly dealt
91  * with without causing GTK+ to leak memory if a widget is put into
92  * its container in the derived class's constructor immediately after
93  * creation on a "top down" basis, or it is placed in another widget
94  * class derived from MainWidgetBase.
95  *
96  * If a C string is passed to the caption argument of the constructor,
97  * then the window will display that text as its caption. A NULL
98  * pointer can be passed if no caption is required. Likewise if a
99  * pointer to a GdkPixbuf object is passed as the second parameter, it
100  * will be used to set a window icon in the window bar (if the user's
101  * window manager supports this).
102  *
103  * The constructor of WinBase does not call gtk_widget_show() because
104  * the constructor of a derived class may want to do things which
105  * require the window not to be visible. Accordingly the constructor
106  * of the derived class (or the library user) should call the
107  * WinBase::show_all() method(), or call
108  * gtk_widget_show()/gtk_widget_show_all() via WinBase::get_win().
109  *
110  * See @ref Threading for particulars about GTK+ thread safety (and so
111  * WinBase thread safety).
112  *
113  * WinBase objects can be used with widget heirarchies or top level
114  * windows created using GtkBuilder. See @ref GtkBuilder for
115  * particulars about that.
116  *
117  * A small compilable example is as follows:
118  *
119  * @anchor WinBaseExampleAnchor
120  * @code
121  * #include <gtk/gtk.h>
122  * #include <c++-gtk-utils/window.h>
123  *
124  *
125  * // *** class headers ***
126  *
127  * extern "C" void message_button_clicked(GtkWidget*, void*);
128  *
129  * class Message: public Cgu::WinBase {
130  * public:
131  * friend void message_button_clicked(GtkWidget*, void*);
132  * Message(const char* text);
133  * };
134  *
135  *
136  * // *** class implementation ***
137  *
138  * void message_button_clicked(GtkWidget*, void* data) {
139  * static_cast<Message*>(data)->close();
140  * }
141  *
142  * Message::Message(const char* text): Cgu::WinBase("Message", 0, true) {
143  * GtkWidget* box = gtk_vbox_new(false, 2);
144  * gtk_container_add(GTK_CONTAINER(get_win()), box);
145  * GtkWidget* label = gtk_label_new(text);
146  * gtk_box_pack_start(GTK_BOX(box), label,
147  * true, false, 0);
148  * GtkWidget* button_box = gtk_hbutton_box_new();
149  * gtk_box_pack_start(GTK_BOX(box), button_box,
150  * false, false, 0);
151  * GtkWidget* button = gtk_button_new_from_stock(GTK_STOCK_OK);
152  * gtk_container_add(GTK_CONTAINER(button_box), button);
153  * g_signal_connect(G_OBJECT(button), "clicked",
154  * G_CALLBACK(message_button_clicked), this);
155  * gtk_widget_set_can_default(button, true);
156  *
157  * gtk_widget_show_all(GTK_WIDGET(get_win()));
158  * }
159  *
160  *
161  * // *** user code ***
162  *
163  * int main(int argc, char* argv[]) {
164  * gtk_init(&argc, &argv);
165  * Message dialog("This is a message");
166  * dialog.exec();
167  * return 0;
168  * }
169  * @endcode
170  *
171  * See @ref Linkage for further discussion of the
172  * message_button_clicked() callback.
173  *
174  * @note The WinBase class will work fine if a GtkDialog object is
175  * passed to the last argument of the class's constructor formed from
176  * a call to gtk_dialog_new()/gtk_dialog_new_with_buttons(), but a few
177  * points should be noted:
178  *
179  * @note 1. If the response signal has been connected to, a delete
180  * event will cause a response signal to be emitted with the
181  * GTK_RESPONSE_DELETE_EVENT id, as well as causing the normal
182  * WinBase::on_delete_event() method to be called. If the response
183  * handler acts on the GTK_RESPONSE_DELETE_EVENT id by calling
184  * close(), then WinBase::on_delete_event() should normally be
185  * overridden to do nothing so that a double call to close() is not
186  * made (although c++-gtk-utils will check against and prevent such a
187  * double call of close() from a single delete event if that is not
188  * done).
189  *
190  * @note 2. It is usually best not to call gtk_dialog_run(). Instead,
191  * call WinBase::exec() for a blocking dialog, and override
192  * WinBase::get_exec_val() to return any required button response id.
193  *
194  * @note 3. If creating a GtkDialog object with
195  * gtk_dialog_new_with_buttons(), do not pass a GtkDialogFlags
196  * argument of GTK_DIALOG_DESTROY_WITH_PARENT. A GtkDialogFlags
197  * argument of GTK_DIALOG_MODAL can be passed, but alternatively to
198  * make the dialog modal the user can pass 'true' as the modal
199  * argument of the WinBase constructor and pass the parent widget to
200  * that constructor rather than to gtk_dialog_new_with_buttons() (so
201  * that GtkDialogFlags(0) is passed as the third argument of
202  * gtk_dialog_new_with_buttons()). Either approach will work, but the
203  * second has the advantage of consistency with the WinBase interface.
204  *
205  * @note 4. Likewise, if using gtk_dialog_new_with_buttons(), any
206  * dialog caption can be passed either to the title argument of that
207  * method or to the caption argument of the WinBase constructor.
208  *
209  * @note Similar principles apply to the management of objects derived from
210  * GtkDialog.
211  */
212 
213 namespace Cgu {
214 
215 #if GTK_CHECK_VERSION(2,99,0)
216 class Application;
217 #endif
218 
219 class WinBase {
220 
221  // main class object
222  GtkWindow* g_window_p;
223 
224  bool in_exec_loop;
225  bool is_modal;
226  bool close_guard;
227  GtkWindow* parent_p;
228 
229 #if GTK_CHECK_VERSION(2,99,0)
230  Application* app_p;
231  // only Cgu::Application can access these
232  void set_application(Application* app) {app_p = app;}
233  void unset_application() {app_p = 0;}
234 #endif
235 
236 protected:
237  /**
238  * A function for the use of derived classes which will cause the
239  * window to unblock if exec() has been called. If it is not a
240  * blocking window (ie exec() has not been called so it has been
241  * constructed on freestore), this function will cause the window to
242  * delete itself. By default it is called by on_delete_event().
243  * This method will not throw (assuming, in a case where the
244  * Cgu::WinBase object has been added to a Cgu::Application object,
245  * that merely iterating through a list does not throw, as it would
246  * not on any sane implementation), unless a derived class's
247  * destructor throws, which it should not do.
248  */
249  void close();
250 
251  /**
252  * Provides the value to be returned by exec(). This method will
253  * not throw (unless it is overridden by a derived class's method
254  * which throws).
255  * @return By default returns 0. It is intended to be overridden by
256  * derived classes where relevant to provide a more meaningful
257  * value.
258  */
259  virtual int get_exec_val() const;
260 
261  /**
262  * Called when there is a delete event on the managed window.
263  * Unless overridden by derived classes it just calls close().
264  */
265  virtual void on_delete_event();
266 public:
267 #if GTK_CHECK_VERSION(2,99,0)
268  friend class Application;
269 #endif
270 #ifndef DOXYGEN_PARSING
271  // this helper class avoids exposing GObject callbacks with C
272  // linkage to the global namespace
273  class CB;
274  friend class CB;
275 #endif
276 
277 /**
278  * This class cannot be copied. The copy constructor is deleted.
279  */
280  WinBase(const WinBase&) = delete;
281 
282 /**
283  * This class cannot be copied. The assignment operator is deleted.
284  */
285  WinBase& operator=(const WinBase&) = delete;
286 
287  /**
288  * Returns the GtkWindow object managed by this class. This method
289  * will not throw.
290  * @return The managed GtkWindow object.
291  */
292  GtkWindow* get_win() const {return g_window_p;}
293 
294  /**
295  * Makes the window block. Calls gtk_main(). It is usually a bad
296  * idea to call this method (and so have nested loops) on a
297  * non-modal dialog. To cause the window to unblock, call close().
298  * This method will not throw.
299  * @return The value returned by get_exec_val().
300  * @note If this library is compiled against GTK+3 and the WinBase
301  * object has been added to a Cgu::Application object, then this
302  * method returns immediately with a value of -1.
303  */
304  int exec();
305 
306  /**
307  * A convenience function which calls
308  * gtk_widget_show_all(GTK_WIDGET(get_win())). For classes which
309  * are intended to be derived from, it may be undesirable to call
310  * gtk_widget_show_all() in the class's constructor (a derived class
311  * may add widgets of its own in a way which requires the base
312  * class's widgets not to be realized). In such a case, calling
313  * gtk_widget_show_all() should be left to the user code. This is a
314  * function which makes that less verbose.
315  */
316  void show_all() {gtk_widget_show_all(GTK_WIDGET(get_win()));}
317 
318  /**
319  * The constructor will not throw.
320  * @param caption Window caption (optional).
321  * @param icon A pixbuf which will comprise the window icon (optional).
322  * @param modal Whether the window is to be modal. If this argument
323  * is false, the user may want to consider calling
324  * gtk_window_set_type_hint() if the window is a dialog before it
325  * becomes visible.
326  * @param parent The parent of a modal dialog or NULL (this argument
327  * is only relevant and acted on where the modal argument is true).
328  * The parent will be made insensitive while the modal dialog is in
329  * existence, and automatically made sensitive again when the modal
330  * dialog is finished with. gtk_window_set_transient_for() is also
331  * called, which means that the dialog will normally be kept on top
332  * of its parent and/or centered over it by the window manager. If
333  * the user does not want the parent to appear as insensitive, pass
334  * NULL to this argument and call gtk_window_set_transient_for() in
335  * the derived class's constructor.
336  * @param window A preformed GtkWindow object (such as a GtkDialog
337  * object), or NULL. If NULL, gtk_window_new(GTK_WINDOW_TOPLEVEL)
338  * will be called.
339  */
340  WinBase(const char* caption = 0, GdkPixbuf* icon = 0, bool modal = false,
341  GtkWindow* parent = 0, GtkWindow* window = 0);
342 
343  /**
344  * The destructor will not throw assuming, in a case where the
345  * Cgu::WinBase object has been added to a Cgu::Application object,
346  * that merely iterating through a list does not throw (as it would
347  * not on any sane implementation). Amongst other things, the
348  * destructor calls gtk_widget_destroy() on the managed top level
349  * window object.
350  */
351  virtual ~WinBase();
352 
353 #ifdef CGU_USE_GLIB_MEMORY_SLICES_NO_COMPAT
355 #endif
356 };
357 
358 } // namespace Cgu
359 
360 #endif