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