Merged revisions 6293,6296-6306,6308 via svnmerge from
[ardour.git] / libs / gtkmm2 / gtk / src / object.ccg
1 /* $Id: object.ccg,v 1.8 2005/12/10 11:54:53 murrayc Exp $ */
2
3 /* Copyright 1998-2002 The gtkmm Development Team
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the Free
17  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19
20 #include <glibmm/quark.h>
21 #include <gtk/gtkobject.h>
22
23
24 namespace Gtk
25 {
26
27 Object::Object(const Glib::ConstructParams& construct_params)
28 :
29   Glib::Object(construct_params)
30 {
31    gobject_disposed_ = false;
32
33   _init_unmanage(); //We don't like the GTK+ default memory management - we want to be in control._)
34 }
35
36 Object::Object(GtkObject* castitem)
37 :
38   Glib::Object((GObject*) castitem)
39 {
40   gobject_disposed_ = false;
41
42    _init_unmanage(); //We don't like the GTK+ default memory management - we want to be in control.
43 }
44
45 void Object::_init_unmanage(bool /* is_toplevel = false */)
46 {
47   //GTKMM_LIFECYCLE
48
49   if(gobject_)
50   {
51     //Glib::Object::Object has already stored a pointer to this C++ instance in the underlying C instance,
52     //and connected a callback which will, in turn, call our destroy_notify_(),
53     //so will will know if GTK+ disposes of the underlying instance.
54
55     // Most GTK+ objects are floating, by default. This means that the container widget controls their lifetime.
56     // We'll change this:
57     if(g_object_is_floating (gobject_)) //Top-level Windows and Dialogs can not be manag()ed, so there is no need to do this.
58     {
59       GLIBMM_DEBUG_REFERENCE(this, gobject_);
60       g_object_ref_sink(gobject_); //Stops it from being floating - we will make this optional ( see Gtk::manage() ),
61
62       #ifdef GLIBMM_DEBUG_REFCOUNTING
63       g_warning("gtkmm after sink: C++ instance: %p, C instance: %p, refcount=%d\n", (void*)(Glib::ObjectBase*)this, (void*)gobject_, G_OBJECT(gobject_)->ref_count);
64       g_warning("    c instance gtype: %s\n", G_OBJECT_TYPE_NAME(gobject_));
65       #endif
66
67      referenced_ = true; //Not managed.
68     }
69     else
70     {
71        //This widget is already not floating. It's probably already been added to a GTK+ container, and has just had Glib::wrap() called on it.
72        //It's not floating because containers call g_object_sink() on child widgets to take control of them.
73        //We just ref() it so that we can unref it later.
74        //GLIBMM_DEBUG_REFERENCE(this, gobject_);
75        //g_object_ref(gobject_);
76     
77        //Alternatively, it might be a top-level window (e.g. a Dialog). We would then be doing one too-many refs(),
78        //We do an extra unref() in Window::_destroy_c_instance() to take care of that.
79
80        referenced_ = false; //Managed. We should not try to unfloat GtkObjects that we did not instantiate.
81     }
82   }
83 }
84
85 void Object::_destroy_c_instance()
86 {
87   #ifdef GLIBMM_DEBUG_REFCOUNTING
88   g_warning("Gtk::Object::_destroy_c_instance() this=%10X, gobject_=%10X\n", this, gobject_);
89     if(gobject_)
90       g_warning("  gtypename: %s\n", G_OBJECT_TYPE_NAME(gobject_));
91   #endif
92
93   cpp_destruction_in_progress_ = true;
94
95   // remove our hook.
96   GtkObject* object = gobj();
97
98   if (object)
99   { 
100     g_assert(GTK_IS_OBJECT(object));
101
102     disconnect_cpp_wrapper();
103     //Unfortunately this means that our dispose callback will not be called, because the qdata has been removed.
104     //So we'll connect the callback again, just so that gobject_disposed_ gets set for use later in this same method.
105     //See below:
106    
107
108     //This probably isn't a problem now:
109     //If we are killing the C++ instance before the C instance, then this might lead to strange behaviour.
110     //If this is a problem, then you'll have to use a managed() object, which will die only upon GTK+'s request.
111
112     //We can't do anything with the gobject_ if it's already been disposed.
113     //This prevents us from unref-ing it again, or destroying it again after GTK+ has told us that it has been disposed.
114     if (!gobject_disposed_)
115     {
116       if(referenced_)
117       {
118         //It's not manage()ed so we just unref to destroy it
119         #ifdef GLIBMM_DEBUG_REFCOUNTING
120         g_warning("final unref: gtypename: %s, refcount: %d\n", G_OBJECT_TYPE_NAME(object), ((GObject*)object)->ref_count);
121         #endif
122
123         //Because we called disconnect_cpp_wrapper() our dispose callback will not be called, because the qdata has been removed.
124         //So we'll connect a callback again, just so that gobject_disposed_ gets set for use later in this same method.
125         gulong connection_id_destroy = g_signal_connect (object, "destroy", G_CALLBACK (&callback_destroy_), this);
126
127         GLIBMM_DEBUG_UNREFERENCE(this, object);
128         g_object_unref(object);
129
130         if(!gobject_disposed_) //or if(g_signal_handler_is_connected(object, connection_id_destroy))
131           g_signal_handler_disconnect(object, connection_id_destroy);
132
133         //destroy_notify() should have been called after the final g_object_unref() or gtk_object_destroy(), so gobject_disposed_ should now be true.
134
135         //If the C instance still isn't dead then insist, by calling gtk_object_destroy().
136         //This is necessary because even a manage()d widget is refed when added to a container.
137         // <danielk> That's simply not true.  But references might be taken elsewhere,
138         // and gtk_object_destroy() just tells everyone "drop your refs, please!".
139         if (!gobject_disposed_)
140         {
141           #ifdef GLIBMM_DEBUG_REFCOUNTING
142           g_warning("Gtk::Object::_destroy_c_instance(): Calling gtk_object_destroy(): gobject_=%10X, gtypename=%s\n", object, G_OBJECT_TYPE_NAME(object));
143           #endif
144
145           g_assert(GTK_IS_OBJECT(object));
146           gtk_object_destroy(object); //Container widgets can respond to this.
147         }
148       }
149       else
150       {
151         //It's manag()ed, but the coder decided to delete it before deleting its parent.
152         //That should be OK because the Container can respond to that.
153         #ifdef GLIBMM_DEBUG_REFCOUNTING
154         g_warning("Gtk::Object::_destroy_c_instance(): Calling gtk_object_destroy(): gobject_=%10X\n", gobject_);
155         #endif
156
157         if (!gobject_disposed_)
158         {
159           g_assert(GTK_IS_OBJECT(object));
160           gtk_object_destroy(object);
161         }
162       }
163     }
164
165     //Glib::Object::~Object() will not g_object_unref() it too. because gobject_ is now 0.
166   }  
167 }
168
169 Object::~Object()
170 {
171   #ifdef GLIBMM_DEBUG_REFCOUNTING
172   g_warning("Gtk::Object::~Object() gobject_=%10X\n", gobject_);
173   #endif
174
175   //This has probably been called already from Gtk::Object::_destroy(), which is called from derived destructors.
176   _destroy_c_instance();
177 }
178
179 void Object::disconnect_cpp_wrapper()
180 {
181   //GTKMM_LIFECYCLE:
182
183   #ifdef GLIBMM_DEBUG_REFCOUNTING
184   g_warning("Gtk::Object::disconnect_cpp_wrapper() this=%10X, gobject_=%10X\n", this, gobject_);
185     if(gobject_)
186       g_warning("  gtypename: %s\n", G_OBJECT_TYPE_NAME(gobject_));
187   #endif
188
189   if(gobj())
190   {
191     //Prevent gtk vfuncs and default signal handlers from calling our instance methods:
192     g_object_steal_qdata((GObject*)gobj(), Glib::quark_); //It will no longer be possible to get the C++ instance from the C instance.
193
194     //Allow us to prevent generation of a new C++ wrapper during destruction:
195     g_object_set_qdata((GObject*)gobj(), Glib::quark_cpp_wrapper_deleted_, (gpointer)true);
196
197     //Prevent C++ instance from using GTK+ object:
198     gobject_ = 0;
199
200     //TODO: Disconnect any signals, using gtk methods.
201     //We'll have to keep a record of all the connections.
202   }
203 }
204
205 void Object::destroy_notify_()
206 {
207   //Overriden.
208   //GTKMM_LIFECYCLE
209
210   #ifdef GLIBMM_DEBUG_REFCOUNTING
211   g_warning("Gtk::Object::destroy_notify_: this=%10X, gobject_=%10X\n", this, gobject_);
212   if(gobject_)
213     g_warning("  gtypename=%s\n", G_OBJECT_TYPE_NAME(gobject_));
214   #endif
215
216   //Remember that it's been disposed (which only happens once):
217   //This also stops us from destroying it again in the destructor when it calls destroy_().
218   gobject_disposed_ = true;
219
220   if(!cpp_destruction_in_progress_) //This function might have been called as a side-effect of destroy() when it called gtk_object_destroy().
221   {
222     if (!referenced_) //If it's manage()ed.
223     {
224       #ifdef GLIBMM_DEBUG_REFCOUNTING
225       g_warning("Gtk::Object::destroy_notify_: before delete this.\n");
226       #endif
227       delete this; //Free the C++ instance.
228     }
229     else  //It's not managed, but the C gobject_ just died before the C++ instance..
230     {
231       #ifdef GLIBMM_DEBUG_REFCOUNTING
232       g_warning("Gtk::Object::destroy_notify_: setting gobject_ to 0\n");
233       #endif
234       gobject_ = 0;
235     }
236   }
237
238 }
239
240 void Object::destroy_()
241 {
242   //Called from destructors.
243   //GTKMM_LIFECYCLE
244
245   #ifdef GLIBMM_DEBUG_REFCOUNTING
246   g_warning("Gtk::Object::destroy_(): gobject_: %10X\n", gobject_);
247   if(gobject_)
248    g_warning("  gtypename: %s\n", G_OBJECT_TYPE_NAME(gobject_));
249   #endif
250
251   if ( !cpp_destruction_in_progress_ ) //see comment below.
252   {
253     //Prevent destroy_notify_() from running as a possible side-effect of gtk_object_destroy.
254     //We can't predict whether destroy_notify_() will really be run, so we'll disconnect the C++ instance here.
255     cpp_destruction_in_progress_ = true;
256
257     //destroy the C instance:
258     _destroy_c_instance();
259   }
260
261   //The C++ destructor will be reached later. This function was called by a destructor.
262 }
263
264 void Object::set_manage() 
265 {
266   //GTKMM_LIFECYCLE
267   //This object will not be unref()ed by gtkmm, though it could be destroyed if the coder deletes the C++ instance early.
268   //This method is overriden in Gtk::Window because they can not be manage()ed.
269
270   if (!referenced_) return; //It's already managed.
271
272   // remove our reference
273   if (gobject_->ref_count >= 1) //This should only happen just after creation. We don't use "==1" because GtkButton starts with a refcount of 2 when using a mnemonic.
274   {
275     //g_warning("Object::set_manage(), making object floating: %s\n", G_OBJECT_TYPE_NAME(gobject_));
276
277     // Cowardly refuse to remove last reference make floating instead. //TODO: What does that comment mean?
278     #ifdef GLIBMM_DEBUG_REFCOUNTING
279       g_warning("Object::set_manage(): setting GTK_FLOATING: gobject_ = %p", (void*) gobj());
280       g_warning("  gtypename=%s\n", G_OBJECT_TYPE_NAME(gobj()));
281     #endif
282     //deprecated: GTK_OBJECT_SET_FLAGS(gobj(), GTK_FLOATING);
283     g_object_force_floating(gobject_);
284   }
285   else
286   {
287     g_warning("Object::set_manage(). Refcount seems to be 0. %s\n", G_OBJECT_TYPE_NAME(gobject_));
288
289     //DEF_GLIBMM_DEBUG_UNREF(this, gobj())
290     //g_object_unref(gobj());
291   }
292
293   //g_warning("Object::set_manage(): end: %s", G_OBJECT_TYPE_NAME(gobject_));
294   //g_warning("  refcount=%d", G_OBJECT(gobj())->ref_count);
295
296   referenced_ = false;
297 }
298
299 void Object::callback_destroy_(GObject*, void* data) //static
300
301   //This is only used for a short time, then disconnected.
302
303   Object* cppObject = static_cast<Object*>(data);
304   if(cppObject) //This will be 0 if the C++ destructor has already run.
305   {
306     cppObject->gobject_disposed_ = true;
307   }
308 }
309
310 bool Object::is_managed_() const
311 {
312   return !referenced_;
313 }
314
315 } // namespace Gtk
316