1/* Copyright 1998-2002 The gtkmm Development Team 2 * 3 * This library is free software; you can redistribute it and/or 4 * modify it under the terms of the GNU Lesser General Public 5 * License as published by the Free Software Foundation; either 6 * version 2.1 of the License, or (at your option) any later version. 7 * 8 * This library is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 * Lesser General Public License for more details. 12 * 13 * You should have received a copy of the GNU Lesser General Public 14 * License along with this library; if not, write to the Free Software 15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 16 */ 17 18#include <glibmm/vectorutils.h> 19 20#include <gtkmm/adjustment.h> 21#include <gtkmm/scrolledwindow.h> 22#include <gtk/gtk.h> 23 24 25namespace 26{ 27 28// Marks a widget that has been automatically created and added by gtk_container_add(). 29// The only reason we must use this GQuark or a similar trick, is that we want 30// to restore the state of a managed removed object. 31// gtk_container_remove() does not do that. 32GQuark quark_auto_added_widget = g_quark_from_static_string("gtkmm_Container::auto_added_widget"); 33 34static void container_foreach_callback(GtkWidget* widget_gobj, void* data) 35{ 36 try 37 { 38 const auto & slot = *static_cast<Gtk::Container::ForeachSlot*>(data); 39 const auto widget = Glib::wrap(widget_gobj); 40 41 g_return_if_fail(widget != nullptr); 42 43 slot(*widget); 44 } 45 catch(...) 46 { 47 Glib::exception_handlers_invoke(); 48 } 49} 50 51 52//Copy of generated callback: 53static void Container_signal_remove_callback_normal(GtkContainer* self, GtkWidget* p0,void* data) 54{ 55 using namespace Gtk; 56 typedef sigc::slot< void,Widget* > SlotType; 57 58 // Do not try to call a signal on a disassociated wrapper. 59 if(Glib::ObjectBase::_get_current_wrapper((GObject*) self)) 60 { 61 try 62 { 63 if(sigc::slot_base *const slot = Glib::SignalProxyNormal::data_to_slot(data)) 64 (*static_cast<SlotType*>(slot))(Glib::wrap(p0) 65); 66 } 67 catch(...) 68 { 69 Glib::exception_handlers_invoke(); 70 } 71 } 72} 73 74static void Container_signal_remove_callback(GtkContainer* self, GtkWidget* p0, void* data) 75{ 76 //GTKMM_LIFECYCLE 77 78#ifdef GLIBMM_DEBUG_REFCOUNTING 79 g_warning("Container_signal_remove_callback()"); 80#endif 81 82 //Don't call wrap() on a GTK+ instance whose gtkmm instance has been deleted - just call the original C callback. 83 const bool gtkmm_child_already_deleted = Glib::_gobject_cppinstance_already_deleted((GObject*)p0); 84 85 if(!gtkmm_child_already_deleted) 86 { 87 //Call the regular, generated callback: 88 Container_signal_remove_callback_normal(self, p0, data); 89 } 90 else 91 { 92 //Do nothing. 93 } 94} 95 96} // anonymous namespace 97 98 99namespace Gtk 100{ 101 102//Copy of generated callback: 103void Container_Class::remove_callback_normal(GtkContainer* self, GtkWidget* p0) 104{ 105 const auto obj = dynamic_cast<CppObjectType*>( 106 Glib::ObjectBase::_get_current_wrapper((GObject*)self)); 107 108 // Non-gtkmmproc-generated custom classes implicitly call the default 109 // Glib::ObjectBase constructor, which sets is_derived_. But gtkmmproc- 110 // generated classes can use this optimisation, which avoids the unnecessary 111 // parameter conversions if there is no possibility of the virtual function 112 // being overridden: 113 if(obj && obj->is_derived_()) 114 { 115 try // Trap C++ exceptions which would normally be lost because this is a C callback. 116 { 117 // Call the virtual member method, which derived classes might override. 118 obj->on_remove(Glib::wrap(p0) 119); 120 } 121 catch(...) 122 { 123 Glib::exception_handlers_invoke(); 124 } 125 } 126 else 127 { 128 const auto base = static_cast<BaseClassType*>( 129 g_type_class_peek_parent(G_OBJECT_GET_CLASS(self)) // Get the parent class of the object class (The original underlying C class). 130 ); 131 132 // Call the original underlying C function: 133 if(base && base->remove) 134 (*base->remove)(self, p0); 135 } 136} 137 138//Custom hand-coded callback: 139void Container_Class::remove_callback(GtkContainer* self, GtkWidget* p0) 140{ 141 //GTKMM_LIFECYCLE 142 143#ifdef GLIBMM_DEBUG_REFCOUNTING 144 g_warning("Container_Class::remove_callback_custom() C self=%p: C child=%p\n", (void*)self, (void*)p0); 145 g_warning("gtypename self: %s\n, gtypename child: %s", G_OBJECT_TYPE_NAME(G_OBJECT(self)), G_OBJECT_TYPE_NAME(G_OBJECT(p0))); 146#endif 147 148 //Don't call wrap() on a GTK+ instance whose gtkmm instance has been deleted - just call the original C callback. 149 const bool gtkmm_child_already_deleted = Glib::_gobject_cppinstance_already_deleted((GObject*)p0); 150 151 if(!gtkmm_child_already_deleted) 152 { 153 //Call the regular, generated callback: 154 Container_Class::remove_callback_normal(self, p0); 155 } 156 else 157 { 158 BaseClassType *const base = 159 static_cast<BaseClassType*>(g_type_class_peek_parent(G_OBJECT_GET_CLASS(self))); 160 161 // Do not try to call the default implementation of the virtual function 162 // GtkContainerClass::remove(), because it unhelpfully informs us that 163 // it isn't implemented. This leaves us with no generic means to check 164 // whether it is implemented. 165 if(base && G_TYPE_FROM_CLASS(base) != GTK_TYPE_CONTAINER && base->remove) 166 (*base->remove)(self, p0); 167 } 168} 169 170void Container::foreach(const Container::ForeachSlot& slot) 171{ 172 ForeachSlot slot_copy (slot); 173 gtk_container_foreach(gobj(), &container_foreach_callback, &slot_copy); 174} 175 176void Container::forall(const Container::ForeachSlot& slot) 177{ 178 ForeachSlot slot_copy (slot); 179 gtk_container_forall(gobj(), &container_foreach_callback, &slot_copy); 180} 181 182// Why not let both add() and remove() be virtual, and override them in ScrolledWindow? 183// Because a virtual add() has an unwanted side-effect in Gtk::Stack. 184// See https://bugzilla.gnome.org/show_bug.cgi?id=724732 185// And a virtual remove() can be problematic because ScrolledWindow and other 186// subclasses of Bin have a remove() overload with no parameter. 187 188void Container::add(Widget& widget) 189{ 190 auto scrolled_window = dynamic_cast<ScrolledWindow*>(this); 191 if (scrolled_window) 192 { 193 auto old_child = gtk_bin_get_child(scrolled_window->Bin::gobj()); // Normally nullptr 194 gtk_container_add(gobj(), widget.gobj()); 195 auto child = gtk_bin_get_child(scrolled_window->Bin::gobj()); 196 if (child && child != old_child) 197 { 198 // If the child is not the inserted widget, gtk_scrolled_window_add() has 199 // created and inserted a GtkViewport. Mark this GtkViewport as automatically 200 // added. Container::remove() needs this information. 201 const gpointer qdata_value = (child != widget.gobj()) ? child : nullptr; 202 g_object_set_qdata(G_OBJECT(child), quark_auto_added_widget, qdata_value); 203 } 204 } 205 else 206 gtk_container_add(gobj(), widget.gobj()); 207} 208 209#ifndef GTKMM_DISABLE_DEPRECATED 210G_GNUC_BEGIN_IGNORE_DEPRECATIONS 211bool Container::has_focus_chain() const 212{ 213 return gtk_container_get_focus_chain(const_cast<GtkContainer*>(gobj()), nullptr); 214} 215 216std::vector<Widget*> Container::get_focus_chain() 217{ 218 GList* list = nullptr; 219 gtk_container_get_focus_chain(gobj(), &list); 220 221 return Glib::ListHandler<Widget*>::list_to_vector(list, Glib::OWNERSHIP_SHALLOW); 222} 223 224std::vector<const Widget*> Container::get_focus_chain() const 225{ 226 GList* list = nullptr; 227 gtk_container_get_focus_chain(const_cast<GtkContainer*>(gobj()), &list); 228 229 return Glib::ListHandler<const Widget*>::list_to_vector(list, Glib::OWNERSHIP_SHALLOW); 230} 231G_GNUC_END_IGNORE_DEPRECATIONS 232#endif // GTKMM_DISABLE_DEPRECATED 233 234void Container::show_all_children(bool recursive) 235{ 236 // Plain C version if this turns out to be performance-critical: 237 //GtkCallback callback = (GtkCallback) ((recursive) ? >k_widget_show_all : >k_widget_show); 238 //gtk_container_foreach(gobj(), callback, 0); 239 240 // This could also be done with get_children() and an explicit loop, 241 // if any problems should arise. In gtkmm-1.2, foreach() is known to 242 // cause segfaults eventually, but it seems to work now. 243 244 foreach(sigc::mem_fun((recursive) ? &Widget::show_all : &Widget::show)); 245} 246 247void Container::remove(Widget& widget) 248{ 249 auto child_to_float = &widget; 250 if (g_object_get_qdata(widget.Glib::Object::gobj(), quark_auto_added_widget)) 251 { 252 // If widget was created by gtk_scrolled_window_add(), 253 // don't restore the floating state of that widget. Instead, restore the 254 // state of that widget's child, which was added by add(). 255 auto scrolled_window = dynamic_cast<ScrolledWindow*>(this); 256 if (scrolled_window) 257 { 258 auto bin = dynamic_cast<Bin*>(&widget); 259 if (bin) 260 child_to_float = bin->get_child(); 261 } 262 } 263 264 // If this is a managed widget, then do an extra ref so that it will not be 265 // destroyed when it is removed, and restore the floating state of the ref. 266 // This should leave it in the same state as when it was instantiated, 267 // before being added to the container. 268 if (child_to_float && child_to_float->is_managed_()) 269 { 270 child_to_float->reference(); 271 g_object_force_floating(child_to_float->Glib::Object::gobj()); 272 } 273 274 // gtk_container_remove() removes what gtk_container_add() added, even if 275 // gtk_container_add() added an extra GtkViewport in a GtkScrolledWindow. 276 // See https://bugzilla.gnome.org/show_bug.cgi?id=710471 277 gtk_container_remove(gobj(), widget.gobj()); 278} 279 280} // namespace Gtk 281