c++-gtk-utils
window.h
Go to the documentation of this file.
00001 /* Copyright (C) 2005 to 2011 Chris Vine
00002 
00003 The library comprised in this file or of which this file is part is
00004 distributed by Chris Vine under the GNU Lesser General Public
00005 License as follows:
00006 
00007    This library is free software; you can redistribute it and/or
00008    modify it under the terms of the GNU Lesser General Public License
00009    as published by the Free Software Foundation; either version 2.1 of
00010    the License, or (at your option) any later version.
00011 
00012    This library is distributed in the hope that it will be useful, but
00013    WITHOUT ANY WARRANTY; without even the implied warranty of
00014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015    Lesser General Public License, version 2.1, for more details.
00016 
00017    You should have received a copy of the GNU Lesser General Public
00018    License, version 2.1, along with this library (see the file LGPL.TXT
00019    which came with this source code package in the c++-gtk-utils
00020    sub-directory); if not, write to the Free Software Foundation, Inc.,
00021    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
00022 
00023 */
00024 
00025 #ifndef CGU_WINDOW_H
00026 #define CGU_WINDOW_H
00027 
00028 #include <gtk/gtk.h>
00029 
00030 #include <c++-gtk-utils/cgu_config.h>
00031 
00032 /** 
00033  * @class Cgu::WinBase window.h c++-gtk-utils/window.h
00034  * @brief This is a class for managing the lifetime of top level
00035  * widgets.
00036  * @sa MainWidgetBase Application
00037  *
00038  * This class provides a base class for lifetime management of top
00039  * level windows which can make GTK+ exception safe.  It would be
00040  * possible to use GTK+ to control the lifetime of the contained GTK+
00041  * window object, through the delete event handler and the destroy
00042  * signal.  However in the case of a blocking window (say a dialog)
00043  * this would result in an invalid object remaining in scope.  It is
00044  * better to use the C++ lifetime handling mechanisms to control
00045  * object status, and this class does that.  Where a Cgu::WinBase
00046  * object is created as an auto (local) object its contained GTK+ top
00047  * level window object will be valid while it remains in scope.  For a
00048  * Cgu::WinBase object which is not blocking (on which exec() is not
00049  * called) and which is therefore created on free store the WinBase
00050  * class will delete its own memory and destroy its contained GTK+ top
00051  * level window object when it is closed (see below about the close()
00052  * function).
00053  *
00054  * If a NULL pointer is passed as the last argument of its
00055  * constructor, a new window object will be created with
00056  * gtk_window_new(GTK_WINDOW_TOPLEVEL).  However, alternatively a
00057  * pre-formed object deriving from GtkWindow (such as a GtkDialog or
00058  * GtkMessageDialog object) can be passed as that argument, in which
00059  * case the class will manage that object.
00060  *
00061  * A window will block with a call to exec(), which returns an int
00062  * value.  The exec() method obtains its return value by calling the
00063  * virtual protected function get_exec_val(), which by default returns
00064  * 0 but can be overridden to return something more meaningful.  If
00065  * something other than an int needs to be returned, it will be
00066  * necessary for a derived class to provide an extractor function to
00067  * obtain the data from the object after exec() has returned.
00068  *
00069  * A convenience virtual protected on_delete_event() method is
00070  * provided for any derived class which needs to respond to that
00071  * signal.  It does not return a value (it does not cause a destroy
00072  * event to be emitted as in the case of a GTK+ delete event handler
00073  * returning false).
00074  *
00075  * If the class is constructed on free store and exec() has not been
00076  * called on it, it self-destroys and any memory allocated to it freed
00077  * by calling the protected close() function.  If exec() has been
00078  * called for an object, a call to close() will only terminate the
00079  * window event loop (that is, cause exec() to unblock), which is
00080  * correct for an auto (local) object as it will destroy itself when
00081  * it goes out of scope.  By default (that is, if not overridden by
00082  * any derived classes) on_delete_event() calls close().
00083  *
00084  * No special memory management for GTK+ widgets contained in the
00085  * WinBase object (or in an object of a class derived from WinBase) is
00086  * needed.  The GTK+ object system takes care of that, and they will
00087  * be released when the life of the WinBase object finishes.  For this
00088  * reason, the WinBase class can be used to make GTK+ exception safe:
00089  * as its constructor does not throw, exceptions thrown in the
00090  * constructors of classes derived from it will be correctly dealt
00091  * with without causing GTK+ to leak memory if a widget is put into
00092  * its container in the derived class's constructor immediately after
00093  * creation on a "top down" basis, or it is placed in another widget
00094  * class derived from MainWidgetBase.
00095  *
00096  * If a C string is passed to the caption argument of the constructor,
00097  * then the window will display that text as its caption.  A NULL
00098  * pointer can be passed if no caption is required.  Likewise if a
00099  * pointer to a GdkPixbuf object is passed as the second parameter, it
00100  * will be used to set a window icon in the window bar (if the user's
00101  * window manager supports this).
00102  *
00103  * The constructor of WinBase does not call gtk_widget_show() because
00104  * the constructor of a derived class may want to do things which
00105  * require the window not to be visible.  Accordingly the constructor
00106  * of the derived class (or the library user) should call the
00107  * WinBase::show_all() method(), or call
00108  * gtk_widget_show()/gtk_widget_show_all() via WinBase::get_win().
00109  *
00110  * See @ref Threading for particulars about GTK+ thread safety (and so
00111  * WinBase thread safety).
00112  *
00113  * WinBase objects can be used with widget heirarchies or top level
00114  * windows created using GtkBuilder.  See @ref GtkBuilder for
00115  * particulars about that.
00116  *
00117  * A small compilable example is as follows:
00118  *
00119  * @anchor WinBaseExampleAnchor
00120  * @code
00121  *   #include <gtk/gtk.h>
00122  *   #include <c++-gtk-utils/window.h>
00123  *
00124  *
00125  *   // *** class headers ***
00126  *
00127  *   extern "C" void message_button_clicked(GtkWidget*, void*);
00128  *
00129  *   class Message: public Cgu::WinBase {
00130  *   public:
00131  *     friend void message_button_clicked(GtkWidget*, void*);
00132  *     Message(const char* text);
00133  *   };
00134  *
00135  *   
00136  *   // *** class implementation ***
00137  *
00138  *   void message_button_clicked(GtkWidget*, void* data) {
00139  *     static_cast<Message*>(data)->close();
00140  *   }
00141  *
00142  *   Message::Message(const char* text): Cgu::WinBase("Message", 0, true) {
00143  *     GtkWidget* box = gtk_vbox_new(false, 2);
00144  *     gtk_container_add(GTK_CONTAINER(get_win()), box);
00145  *     GtkWidget* label = gtk_label_new(text);
00146  *     gtk_box_pack_start(GTK_BOX(box), label,
00147  *                        true, false, 0);
00148  *     GtkWidget* button_box = gtk_hbutton_box_new();
00149  *     gtk_box_pack_start(GTK_BOX(box), button_box,
00150  *                        false, false, 0);
00151  *     GtkWidget* button = gtk_button_new_from_stock(GTK_STOCK_OK);
00152  *     gtk_container_add(GTK_CONTAINER(button_box), button);
00153  *     g_signal_connect(G_OBJECT(button), "clicked",
00154  *                      G_CALLBACK(message_button_clicked), this);
00155  *     gtk_widget_set_can_default(button, true);
00156  *
00157  *     gtk_widget_show_all(GTK_WIDGET(get_win()));
00158  *   }
00159  *
00160  *
00161  *   // *** user code ***
00162  *
00163  *   int main(int argc, char* argv[]) {
00164  *     gtk_init(&argc, &argv);
00165  *     Message dialog("This is a message");
00166  *     dialog.exec();
00167  *     return 0;
00168  *   }
00169  * @endcode
00170  *
00171  * See @ref Linkage for further discussion of the
00172  * message_button_clicked() callback.
00173  *
00174  * @note The WinBase class will work fine if a GtkDialog object is
00175  * passed to the last argument of the class's constructor formed from
00176  * a call to gtk_dialog_new()/gtk_dialog_new_with_buttons(), but a few
00177  * points should be noted:
00178  *
00179  * @note 1. If the response signal has been connected to, a delete
00180  * event will cause a response signal to be emitted with the
00181  * GTK_RESPONSE_DELETE_EVENT id, as well as causing the normal
00182  * WinBase::on_delete_event() method to be called.  If the response
00183  * handler acts on the GTK_RESPONSE_DELETE_EVENT id by calling
00184  * close(), then WinBase::on_delete_event() should normally be
00185  * overridden to do nothing so that a double call to close() is not
00186  * made (although c++-gtk-utils will check against and prevent such a
00187  * double call of close() from a single delete event if that is not
00188  * done).
00189  *
00190  * @note 2. It is usually best not to call gtk_dialog_run().  Instead,
00191  * call WinBase::exec() for a blocking dialog, and override
00192  * WinBase::get_exec_val() to return any required button response id.
00193  *
00194  * @note 3. If creating a GtkDialog object with
00195  * gtk_dialog_new_with_buttons(), do not pass a GtkDialogFlags
00196  * argument of GTK_DIALOG_DESTROY_WITH_PARENT.  A GtkDialogFlags
00197  * argument of GTK_DIALOG_MODAL can be passed, but alternatively to
00198  * make the dialog modal the user can pass 'true' as the modal
00199  * argument of the WinBase constructor and pass the parent widget to
00200  * that constructor rather than to gtk_dialog_new_with_buttons() (so
00201  * that GtkDialogFlags(0) is passed as the third argument of
00202  * gtk_dialog_new_with_buttons()).  Either approach will work, but the
00203  * second has the advantage of consistency with the WinBase interface.
00204  *
00205  * @note 4. Likewise, if using gtk_dialog_new_with_buttons(), any
00206  * dialog caption can be passed either to the title argument of that
00207  * method or to the caption argument of the WinBase constructor.
00208  *
00209  * @note Similar principles apply to the management of objects derived from
00210  * GtkDialog.
00211  */
00212 
00213 namespace Cgu {
00214 
00215 #if GTK_CHECK_VERSION(2,99,0)
00216 class Application;
00217 #endif
00218 
00219 class WinBase {
00220 
00221   // main class object
00222   GtkWindow* g_window_p;
00223 
00224   bool in_exec_loop;
00225   bool is_modal;
00226   bool close_guard;
00227   GtkWindow* parent_p;
00228 
00229 #if GTK_CHECK_VERSION(2,99,0)
00230   Application* app_p;
00231   // only Cgu::Application can access these
00232   void set_application(Application* app)  {app_p = app;}
00233   void unset_application() {app_p = 0;}
00234 #endif
00235 
00236 protected:
00237   /**
00238    * A function for the use of derived classes which will cause the
00239    * window to unblock if exec() has been called.  If it is not a
00240    * blocking window (ie exec() has not been called so it has been
00241    * constructed on freestore), this function will cause the window to
00242    * delete itself.  By default it is called by on_delete_event().
00243    * This method will not throw (assuming, in a case where the
00244    * Cgu::WinBase object has been added to a Cgu::Application object,
00245    * that merely iterating through a list does not throw, as it would
00246    * not on any sane implementation), unless a derived class's
00247    * destructor throws, which it should not do.
00248    */
00249   void close();
00250 
00251   /**
00252    * Provides the value to be returned by exec().  This method will
00253    * not throw (unless it is overridden by a derived class's method
00254    * which throws).
00255    * @return By default returns 0.  It is intended to be overridden by
00256    * derived classes where relevant to provide a more meaningful
00257    * value.
00258    */
00259   virtual int get_exec_val() const;
00260 
00261   /**
00262    * Called when there is a delete event on the managed window.
00263    * Unless overridden by derived classes it just calls close().
00264    */
00265   virtual void on_delete_event();
00266 public:
00267 #if GTK_CHECK_VERSION(2,99,0)
00268   friend class Application;
00269 #endif
00270 #ifndef DOXYGEN_PARSING
00271   // this helper class avoids exposing GObject callbacks with C
00272   // linkage to the global namespace
00273   class CB;
00274   friend class CB;
00275 #endif
00276 
00277 /**
00278  * This class cannot be copied.  The copy constructor is deleted.
00279  */
00280   WinBase(const WinBase&) = delete;
00281 
00282 /**
00283  * This class cannot be copied.  The assignment operator is deleted.
00284  */
00285   WinBase& operator=(const WinBase&) = delete;
00286 
00287   /**
00288    * Returns the GtkWindow object managed by this class.  This method
00289    * will not throw.
00290    * @return The managed GtkWindow object.
00291    */
00292   GtkWindow* get_win() const {return g_window_p;}
00293 
00294   /**
00295    * Makes the window block. Calls gtk_main().  It is usually a bad
00296    * idea to call this method (and so have nested loops) on a
00297    * non-modal dialog.  To cause the window to unblock, call close().
00298    * This method will not throw.
00299    * @return The value returned by get_exec_val().
00300    * @note If this library is compiled against GTK+3 and the WinBase
00301    * object has been added to a Cgu::Application object, then this
00302    * method returns immediately with a value of -1.
00303    */
00304   int exec();
00305 
00306   /**
00307    * A convenience function which calls
00308    * gtk_widget_show_all(GTK_WIDGET(get_win())).  For classes which
00309    * are intended to be derived from, it may be undesirable to call
00310    * gtk_widget_show_all() in the class's constructor (a derived class
00311    * may add widgets of its own in a way which requires the base
00312    * class's widgets not to be realized).  In such a case, calling
00313    * gtk_widget_show_all() should be left to the user code.  This is a
00314    * function which makes that less verbose.
00315    */
00316    void show_all() {gtk_widget_show_all(GTK_WIDGET(get_win()));}
00317 
00318   /**
00319    * The constructor will not throw.
00320    * @param caption Window caption (optional).
00321    * @param icon A pixbuf which will comprise the window icon (optional).
00322    * @param modal Whether the window is to be modal.  If this argument
00323    * is false, the user may want to consider calling
00324    * gtk_window_set_type_hint() if the window is a dialog before it
00325    * becomes visible.
00326    * @param parent The parent of a modal dialog or NULL (this argument
00327    * is only relevant and acted on where the modal argument is true).
00328    * The parent will be made insensitive while the modal dialog is in
00329    * existence, and automatically made sensitive again when the modal
00330    * dialog is finished with.  gtk_window_set_transient_for() is also
00331    * called, which means that the dialog will normally be kept on top
00332    * of its parent and/or centered over it by the window manager.  If
00333    * the user does not want the parent to appear as insensitive, pass
00334    * NULL to this argument and call gtk_window_set_transient_for() in
00335    * the derived class's constructor.
00336    * @param window A preformed GtkWindow object (such as a GtkDialog
00337    * object), or NULL.  If NULL, gtk_window_new(GTK_WINDOW_TOPLEVEL)
00338    * will be called.
00339    */
00340   WinBase(const char* caption = 0, GdkPixbuf* icon = 0, bool modal = false,
00341           GtkWindow* parent = 0, GtkWindow* window = 0);
00342 
00343   /**
00344    * The destructor will not throw assuming, in a case where the
00345    * Cgu::WinBase object has been added to a Cgu::Application object,
00346    * that merely iterating through a list does not throw (as it would
00347    * not on any sane implementation).  Amongst other things, the
00348    * destructor calls gtk_widget_destroy() on the managed top level
00349    * window object.
00350    */
00351   virtual ~WinBase();
00352 
00353 #ifdef CGU_USE_GLIB_MEMORY_SLICES_NO_COMPAT
00354   CGU_GLIB_MEMORY_SLICES_FUNCS
00355 #endif
00356 };
00357 
00358 } // namespace Cgu
00359 
00360 #endif