c++-gtk-utils
|
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 59 Temple Place - Suite 330, Boston, MA, 02111-1307, 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 this which 00105 * requires the widget not to be visible. Accordingly the constructor 00106 * of the derived class (or the library user) should call 00107 * gtk_widget_show()/gtk_widget_show_all() via WinBase::get_win()). 00108 * 00109 * See @ref Threading for particulars about GTK+ thread safety (and so 00110 * WinBase thread safety). 00111 * 00112 * WinBase objects can be used with widget heirarchies or top level 00113 * windows created using GtkBuilder. See @ref GtkBuilder for 00114 * particulars about that. 00115 * 00116 * A small compilable example is as follows: 00117 * 00118 * @anchor WinBaseExampleAnchor 00119 * @code 00120 * #include <gtk/gtk.h> 00121 * #include <c++-gtk-utils/window.h> 00122 * 00123 * 00124 * // *** class headers *** 00125 * 00126 * extern "C" void message_button_clicked(GtkWidget*, void*); 00127 * 00128 * class Message: public Cgu::WinBase { 00129 * public: 00130 * friend void message_button_clicked(GtkWidget*, void*); 00131 * Message(const char* text); 00132 * }; 00133 * 00134 * 00135 * // *** class implementation *** 00136 * 00137 * void message_button_clicked(GtkWidget*, void* data) { 00138 * static_cast<Message*>(data)->close(); 00139 * } 00140 * 00141 * Message::Message(const char* text): Cgu::WinBase("Message", 0, true) { 00142 * GtkWidget* box = gtk_vbox_new(false, 2); 00143 * gtk_container_add(GTK_CONTAINER(get_win()), box); 00144 * GtkWidget* label = gtk_label_new(text); 00145 * gtk_box_pack_start(GTK_BOX(box), label, 00146 * true, false, 0); 00147 * GtkWidget* button_box = gtk_hbutton_box_new(); 00148 * gtk_box_pack_start(GTK_BOX(box), button_box, 00149 * false, false, 0); 00150 * GtkWidget* button = gtk_button_new_from_stock(GTK_STOCK_OK); 00151 * gtk_container_add(GTK_CONTAINER(button_box), button); 00152 * g_signal_connect(G_OBJECT(button), "clicked", 00153 * G_CALLBACK(message_button_clicked), this); 00154 * gtk_widget_set_can_default(button, true); 00155 * 00156 * gtk_widget_show_all(GTK_WIDGET(get_win())); 00157 * } 00158 * 00159 * 00160 * // *** user code *** 00161 * 00162 * int main(int argc, char* argv[]) { 00163 * gtk_init(&argc, &argv); 00164 * Message dialog("This is a message"); 00165 * dialog.exec(); 00166 * return 0; 00167 * } 00168 * @endcode 00169 * 00170 * See @ref Linkage for further discussion of the 00171 * message_button_clicked() callback. 00172 * 00173 * @note The WinBase class will work fine if a GtkDialog object is 00174 * passed to the last argument of the class's constructor formed from 00175 * a call to gtk_dialog_new()/gtk_dialog_new_with_buttons(), but a few 00176 * points should be noted: 00177 * 00178 * @note 1. If the response signal has been connected to, a delete 00179 * event will cause a response signal to be emitted with the 00180 * GTK_RESPONSE_DELETE_EVENT id, as well as causing the normal 00181 * WinBase::on_delete_event() method to be called. If the response 00182 * handler acts on the GTK_RESPONSE_DELETE_EVENT id by calling 00183 * close(), then WinBase::on_delete_event() should normally be 00184 * overridden to do nothing so that a double call to close() is not 00185 * made (although c++-gtk-utils will check against and prevent such a 00186 * double call of close() from a single delete event if that is not 00187 * done). 00188 * 00189 * @note 2. It is usually best not to call gtk_dialog_run(). Instead, 00190 * call WinBase::exec() for a blocking dialog, and override 00191 * WinBase::get_exec_val() to return any required button response id. 00192 * 00193 * @note 3. If creating a GtkDialog object with 00194 * gtk_dialog_new_with_buttons(), do not pass a GtkDialogFlags 00195 * argument of GTK_DIALOG_DESTROY_WITH_PARENT. A GtkDialogFlags 00196 * argument of GTK_DIALOG_MODAL can be passed, but alternatively to 00197 * make the dialog modal the user can pass 'true' as the modal 00198 * argument of the WinBase constructor and pass the parent widget to 00199 * that constructor rather than to gtk_dialog_new_with_buttons() (so 00200 * that GtkDialogFlags(0) is passed as the third argument of 00201 * gtk_dialog_new_with_buttons()). Either approach will work, but the 00202 * second has the advantage of consistency with the WinBase interface. 00203 * 00204 * @note 4. Likewise, if using gtk_dialog_new_with_buttons(), any 00205 * dialog caption can be passed either to the title argument of that 00206 * method or to the caption argument of the WinBase constructor. 00207 * 00208 * @note Similar principles apply to the management of objects derived from 00209 * GtkDialog. 00210 */ 00211 00212 namespace Cgu { 00213 00214 #if GTK_CHECK_VERSION(2,99,0) 00215 class Application; 00216 #endif 00217 00218 class WinBase { 00219 00220 // main class object 00221 GtkWindow* g_window_p; 00222 00223 bool in_exec_loop; 00224 bool is_modal; 00225 bool close_guard; 00226 GtkWindow* parent_p; 00227 00228 #if GTK_CHECK_VERSION(2,99,0) 00229 Application* app_p; 00230 // only Cgu::Application can access these 00231 void set_application(Application* app) {app_p = app;} 00232 void unset_application() {app_p = 0;} 00233 #endif 00234 00235 protected: 00236 /** 00237 * A function for the use of derived classes which will cause the 00238 * window to unblock if exec() has been called. If it is not a 00239 * blocking window (ie exec() has not been called so it has been 00240 * constructed on freestore), this function will cause the window to 00241 * delete itself. By default it is called by on_delete_event(). 00242 * This method will not throw (assuming, in a case where the 00243 * Cgu::WinBase object has been added to a Cgu::Application object, 00244 * that merely iterating through a list does not throw, as it would 00245 * not on any sane implementation), unless a derived class's 00246 * destructor throws, which it should not do. 00247 */ 00248 void close(); 00249 00250 /** 00251 * Provides the value to be returned by exec(). This method will 00252 * not throw (unless it is overridden by a derived class's method 00253 * which throws). 00254 * @return By default returns 0. It is intended to be overridden by 00255 * derived classes where relevant to provide a more meaningful 00256 * value. 00257 */ 00258 virtual int get_exec_val() const; 00259 00260 /** 00261 * Called when there is a delete event on the managed window. 00262 * Unless overridden by derived classes it just calls close(). 00263 */ 00264 virtual void on_delete_event(); 00265 public: 00266 #if GTK_CHECK_VERSION(2,99,0) 00267 friend class Application; 00268 #endif 00269 #ifndef DOXYGEN_PARSING 00270 // this helper class avoids exposing GObject callbacks with C 00271 // linkage to the global namespace 00272 class CB; 00273 friend class CB; 00274 #endif 00275 00276 /** 00277 * This class cannot be copied. The copy constructor is deleted. 00278 */ 00279 WinBase(const WinBase&) = delete; 00280 00281 /** 00282 * This class cannot be copied. The assignment operator is deleted. 00283 */ 00284 WinBase& operator=(const WinBase&) = delete; 00285 00286 /** 00287 * Returns the GtkWindow object managed by this class. This method 00288 * will not throw. 00289 * @return The managed GtkWindow object. 00290 */ 00291 GtkWindow* get_win() const {return g_window_p;} 00292 00293 /** 00294 * Makes the window block. Calls gtk_main(). It is usually a bad 00295 * idea to call this method (and so have nested loops) on a 00296 * non-modal dialog. To cause the window to unblock, call close(). 00297 * This method will not throw. 00298 * @return The value returned by get_exec_val(). 00299 * @note If this library is compiled against GTK+3 and the WinBase 00300 * object has been added to a Cgu::Application object, then this 00301 * method returns immediately with a value of -1. 00302 */ 00303 int exec(); 00304 00305 /** 00306 * A convenience function which calls 00307 * gtk_widget_show_all(GTK_WIDGET(get_win())). For classes which 00308 * are intended to be derived from, it may be undesirable to call 00309 * gtk_widget_show_all() in the class's constructor (a derived class 00310 * may add widgets of its own in a way which requires the base 00311 * class's widgets not to be realized). In such a case, calling 00312 * gtk_widget_show_all() should be left to the user code. This is a 00313 * function which makes that less verbose. 00314 */ 00315 void show_all() {gtk_widget_show_all(GTK_WIDGET(get_win()));} 00316 00317 /** 00318 * The constructor will not throw. 00319 * @param caption Window caption (optional). 00320 * @param icon A pixbuf which will comprise the window icon (optional). 00321 * @param modal Whether the window is to be modal. If this argument 00322 * is false, the user may want to consider calling 00323 * gtk_window_set_type_hint() if the window is a dialog before it 00324 * becomes visible. 00325 * @param parent The parent of a modal dialog or NULL (this argument 00326 * is only relevant and acted on where the modal argument is true). 00327 * The parent will be made insensitive while the modal dialog is in 00328 * existence, and automatically made sensitive again when the modal 00329 * dialog is finished with. gtk_window_set_transient_for() is also 00330 * called, which means that the dialog will normally be kept on top 00331 * of its parent and/or centered over it by the window manager. If 00332 * the user does not want the parent to appear as insensitive, pass 00333 * NULL to this argument and call gtk_window_set_transient_for() in 00334 * the derived class's constructor. 00335 * @param window A preformed GtkWindow object (such as a GtkDialog 00336 * object), or NULL. If NULL, gtk_window_new(GTK_WINDOW_TOPLEVEL) 00337 * will be called. 00338 */ 00339 WinBase(const char* caption = 0, GdkPixbuf* icon = 0, bool modal = false, 00340 GtkWindow* parent = 0, GtkWindow* window = 0); 00341 00342 /** 00343 * The destructor will not throw assuming, in a case where the 00344 * Cgu::WinBase object has been added to a Cgu::Application object, 00345 * that merely iterating through a list does not throw (as it would 00346 * not on any sane implementation). Amongst other things, the 00347 * destructor calls gtk_widget_destroy() on the managed top level 00348 * window object. 00349 */ 00350 virtual ~WinBase(); 00351 00352 #ifdef CGU_USE_GLIB_MEMORY_SLICES_NO_COMPAT 00353 CGU_GLIB_MEMORY_SLICES_FUNCS 00354 #endif 00355 }; 00356 00357 } // namespace Cgu 00358 00359 #endif