/* Copyright 2002 The gtkmm Development Team
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*/
#include
#include
#include
#include //For PropertyProxyConnectionNode
#include
#include
#include // For std::move()
namespace
{
// Used by the Glib::ObjectBase default ctor. Using an explicitly defined
// char array rather than a string literal allows for fast pointer comparison,
// which is otherwise not guaranteed to work.
static const char anonymous_custom_type_name[] = "gtkmm__anonymous_custom_type";
using ObjectBaseDestroyNotifyFuncType = void (*)(void* data);
ObjectBaseDestroyNotifyFuncType ObjectBase_destroy_notify_funcptr;
// From function with C linkage, to protected static member function with C++ linkage
extern "C"
{
static void ObjectBase_destroy_notify_callback(void* data)
{
ObjectBase_destroy_notify_funcptr(data);
}
} // extern "C"
} // anonymous namespace
namespace Glib
{
/**** Glib::ObjectBase *****************************************************/
// Used only during the construction of named custom types.
struct ObjectBase::PrivImpl
{
// Pointers to the interfaces of custom types.
Class::interface_classes_type custom_interface_classes;
// Pointers to extra class init functions.
Class::class_init_funcs_type custom_class_init_functions;
// Pointer to the instance init function.
GInstanceInitFunc custom_instance_init_function = nullptr;
};
ObjectBase::ObjectBase()
: gobject_(nullptr),
custom_type_name_(anonymous_custom_type_name),
cpp_destruction_in_progress_(false)
{
}
ObjectBase::ObjectBase(const char* custom_type_name)
: gobject_(nullptr), custom_type_name_(custom_type_name),
cpp_destruction_in_progress_(false)
{
}
ObjectBase::ObjectBase(const std::type_info& custom_type_info)
: gobject_(nullptr), custom_type_name_(custom_type_info.name()),
cpp_destruction_in_progress_(false)
{
}
// initialize() actually initializes the wrapper. Glib::ObjectBase is used
// as virtual base class, which means the most-derived class' ctor invokes
// the Glib::ObjectBase ctor -- thus it's useless for Glib::Object.
//
void
ObjectBase::initialize(GObject* castitem)
{
if (gobject_)
{
// initialize() might be called twice when used with MI, e.g. by the ctors
// of Glib::Object and Glib::Interface. However, they must both refer to
// the same underlying GObject instance.
//
g_assert(gobject_ == castitem);
// TODO: Think about it. Will this really be called twice?
g_printerr("ObjectBase::initialize() called twice for the same GObject\n");
return; // Don't initialize the wrapper twice.
}
gobject_ = castitem;
_set_current_wrapper(castitem);
}
void
ObjectBase::initialize_move(GObject* castitem, Glib::ObjectBase* previous_wrapper)
{
if (gobject_)
{
g_assert(gobject_ == castitem);
// TODO: Think about it. Will this really be called twice?
g_printerr("ObjectBase::initialize_move() called twice for the same GObject\n");
return; // Don't initialize the wrapper twice.
}
gobject_ = castitem;
_move_current_wrapper(castitem, previous_wrapper);
custom_type_name_ = previous_wrapper->custom_type_name_;
cpp_destruction_in_progress_ = previous_wrapper->cpp_destruction_in_progress_;
// Clear the previous wrapper:
previous_wrapper->custom_type_name_ = nullptr;
previous_wrapper->cpp_destruction_in_progress_ = false;
}
ObjectBase::ObjectBase(ObjectBase&& src) noexcept
: sigc::trackable(std::move(src)), // not actually called because it's a virtual base
gobject_(nullptr),
custom_type_name_(src.custom_type_name_),
cpp_destruction_in_progress_(src.cpp_destruction_in_progress_)
{
}
ObjectBase&
ObjectBase::operator=(ObjectBase&& src) noexcept
{
if (this == &src)
return *this;
sigc::trackable::operator=(std::move(src));
if (gobject_)
{
// Remove the wrapper, without invoking destroy_notify_callback_():
g_object_steal_qdata(gobject_, Glib::quark_);
// Remove a reference, without deleting *this.
unreference();
gobject_ = nullptr;
}
initialize_move(src.gobject_, &src);
return *this;
}
ObjectBase::~ObjectBase() noexcept
{
// Normally, gobject_ should always be 0 at this point, because:
//
// a) Gtk::Object handles memory management on its own and always resets
// the gobject_ pointer in its destructor.
//
// b) Glib::Object instances that aren't Gtk::Objects will always be
// deleted by the destroy_notify_() virtual method. Calling delete
// on a Glib::Object is a programming error.
//
// The *only* situation where gobject_ is validly not 0 at this point
// happens if a derived class's ctor throws an exception. In that case
// we have to call g_object_unref() on our own.
//
if (GObject* const gobject = gobject_)
{
#ifdef GLIBMM_DEBUG_REFCOUNTING
g_warning("(Glib::ObjectBase::~ObjectBase): gobject_ = %p", (void*)gobject_);
#endif
gobject_ = nullptr;
#ifdef GLIBMM_DEBUG_REFCOUNTING
g_warning("(Glib::ObjectBase::~ObjectBase): before g_object_steal_qdata()");
#endif
// Remove the pointer to the wrapper from the underlying instance.
// This does _not_ cause invocation of the destroy_notify callback.
g_object_steal_qdata(gobject, quark_);
#ifdef GLIBMM_DEBUG_REFCOUNTING
g_warning("(Glib::ObjectBase::~ObjectBase): calling g_object_unref()");
#endif
g_object_unref(gobject);
}
}
void
ObjectBase::reference() const
{
GLIBMM_DEBUG_REFERENCE(this, gobject_);
g_object_ref(gobject_);
}
void
ObjectBase::unreference() const
{
GLIBMM_DEBUG_UNREFERENCE(this, gobject_);
g_object_unref(gobject_);
}
GObject*
ObjectBase::gobj_copy() const
{
reference();
return gobject_;
}
void
ObjectBase::_set_current_wrapper(GObject* object)
{
// Store a pointer to this wrapper in the underlying instance, so that we
// never create a second wrapper for the same underlying instance. Also,
// specify a callback that will tell us when it's time to delete this C++
// wrapper instance:
if (object)
{
if (!g_object_get_qdata(object, Glib::quark_))
{
ObjectBase_destroy_notify_funcptr = &destroy_notify_callback_;
g_object_set_qdata_full(object, Glib::quark_, this, &ObjectBase_destroy_notify_callback);
}
else
{
g_warning("This object, of type %s, already has a wrapper.\n"
"You should use wrap() instead of a constructor.",
G_OBJECT_TYPE_NAME(object));
}
}
}
void
ObjectBase::_move_current_wrapper(GObject* object, Glib::ObjectBase* previous_wrapper) noexcept
{
// See _set_current_wrapper().
ObjectBase* current_wrapper = _get_current_wrapper(object);
if (current_wrapper != previous_wrapper)
{
g_warning("%s: Unexpected previous wrapper, for object of type %s.\n"
"previous_wrapper=%p, current_wrapper=%p",
G_STRFUNC, G_OBJECT_TYPE_NAME(object), static_cast(previous_wrapper),
static_cast(current_wrapper));
}
// Remove the previous wrapper, without invoking destroy_notify_callback_():
g_object_steal_qdata(object, Glib::quark_);
// Set the new wrapper:
g_object_set_qdata_full(object, Glib::quark_, this, &ObjectBase_destroy_notify_callback);
// Clear the previous wrapper:
previous_wrapper->gobject_ = nullptr;
}
// static
ObjectBase*
ObjectBase::_get_current_wrapper(GObject* object)
{
if (object)
return static_cast(g_object_get_qdata(object, Glib::quark_));
else
return nullptr;
}
// static
void
ObjectBase::destroy_notify_callback_(void* data)
{
// GLIBMM_LIFECYCLE
// This method is called (indirectly) from g_object_run_dispose().
// Get the C++ instance associated with the C instance:
ObjectBase* cppObject =
static_cast(data); // Previously set with g_object_set_qdata_full().
#ifdef GLIBMM_DEBUG_REFCOUNTING
g_warning("ObjectBase::destroy_notify_callback_: cppObject = %p, gobject_ = %p, gtypename = %s\n",
(void*)cppObject, (void*)cppObject->gobject_, G_OBJECT_TYPE_NAME(cppObject->gobject_));
#endif
if (cppObject) // This will be nullptr if the C++ destructor has already run.
{
cppObject->destroy_notify_(); // Virtual - it does different things for Glib::ObjectBase and Gtk::Object.
}
}
void
ObjectBase::destroy_notify_()
{
// The C instance is about to be disposed, making it unusable. Now is a
// good time to delete the C++ wrapper of the C instance. There is no way
// to force the disposal of the GObject (though Gtk::Object::destroy_notify_()
// can call g_object_run_dispose()), so this is the *only* place where we delete
// the C++ wrapper.
//
// This will only happen after the last unreference(), which will be done by
// the RefPtr<> destructor. There should be no way to access the wrapper or
// the underlying object instance after that, so it's OK to delete this.
#ifdef GLIBMM_DEBUG_REFCOUNTING
g_warning("Glib::ObjectBase::destroy_notify_: gobject_ = %p", (void*)gobject_);
#endif
gobject_ = nullptr; // Make sure we don't unref it again in the dtor.
delete this;
}
bool
ObjectBase::is_anonymous_custom_() const
{
// Doing high-speed pointer comparison is OK here.
return (custom_type_name_ == anonymous_custom_type_name);
}
bool
ObjectBase::is_derived_() const
{
// gtkmmproc-generated classes initialize this to 0 by default.
return (custom_type_name_ != nullptr);
}
void
ObjectBase::set_manage()
{
// This is a private method and Gtk::manage() is a template function.
// Thus this will probably never run, unless you do something like:
//
// manage(static_cast(refptr.operator->()));
g_error("Glib::ObjectBase::set_manage(): "
"only Gtk::Object instances can be managed");
}
bool
ObjectBase::_cpp_destruction_is_in_progress() const
{
return cpp_destruction_in_progress_;
}
void
ObjectBase::set_property_value(const Glib::ustring& property_name, const Glib::ValueBase& value)
{
g_object_set_property(gobj(), property_name.c_str(), value.gobj());
}
void
ObjectBase::get_property_value(const Glib::ustring& property_name, Glib::ValueBase& value) const
{
g_object_get_property(const_cast(gobj()), property_name.c_str(), value.gobj());
}
sigc::connection
ObjectBase::connect_property_changed(
const Glib::ustring& property_name, const sigc::slot& slot)
{
// Create a proxy to hold our connection info
// This will be deleted by destroy_notify_handler.
auto pConnectionNode = new PropertyProxyConnectionNode(slot, gobj());
// connect it to glib
// pConnectionNode will be passed as the data argument to the callback.
return pConnectionNode->connect_changed(property_name);
}
sigc::connection
ObjectBase::connect_property_changed(
const Glib::ustring& property_name, sigc::slot&& slot)
{
// Create a proxy to hold our connection info
// This will be deleted by destroy_notify_handler.
auto pConnectionNode = new PropertyProxyConnectionNode(std::move(slot), gobj());
// connect it to glib
// pConnectionNode will be passed as the data argument to the callback.
return pConnectionNode->connect_changed(property_name);
}
void
ObjectBase::freeze_notify()
{
g_object_freeze_notify(gobj());
}
void
ObjectBase::thaw_notify()
{
g_object_thaw_notify(gobj());
}
GType ObjectBase::get_base_type()
{
return G_TYPE_OBJECT;
}
void ObjectBase::add_custom_interface_class(const Interface_Class* iface_class)
{
if (!priv_pimpl_)
priv_pimpl_ = std::make_unique();
priv_pimpl_->custom_interface_classes.emplace_back(iface_class);
}
void ObjectBase::add_custom_class_init_function(GClassInitFunc class_init_func, void* class_data)
{
if (!priv_pimpl_)
priv_pimpl_ = std::make_unique();
priv_pimpl_->custom_class_init_functions.emplace_back(
std::make_tuple(class_init_func, class_data));
}
void ObjectBase::set_custom_instance_init_function(GInstanceInitFunc instance_init_func)
{
if (!priv_pimpl_)
priv_pimpl_ = std::make_unique();
priv_pimpl_->custom_instance_init_function = instance_init_func;
}
const Class::interface_classes_type* ObjectBase::get_custom_interface_classes() const
{
return priv_pimpl_ ? &priv_pimpl_->custom_interface_classes : nullptr;
}
const Class::class_init_funcs_type* ObjectBase::get_custom_class_init_functions() const
{
return priv_pimpl_ ? &priv_pimpl_->custom_class_init_functions : nullptr;
}
GInstanceInitFunc ObjectBase::get_custom_instance_init_function() const
{
return priv_pimpl_ ? priv_pimpl_->custom_instance_init_function : nullptr;
}
void ObjectBase::custom_class_init_finished()
{
priv_pimpl_.reset();
}
/**** Global function *****************************************************/
bool
_gobject_cppinstance_already_deleted(GObject* gobject)
{
// This function is used to prevent calling wrap() on a GTK+ instance whose gtkmm instance has
// been deleted.
if (gobject)
return (bool)g_object_get_qdata(
gobject, Glib::quark_cpp_wrapper_deleted_); // true means that something is odd.
else
return false; // Nothing is particularly wrong.
}
} // namespace Glib