summaryrefslogtreecommitdiff
path: root/libs/glibmm2/glibmm/property.cc
diff options
context:
space:
mode:
Diffstat (limited to 'libs/glibmm2/glibmm/property.cc')
-rw-r--r--libs/glibmm2/glibmm/property.cc198
1 files changed, 198 insertions, 0 deletions
diff --git a/libs/glibmm2/glibmm/property.cc b/libs/glibmm2/glibmm/property.cc
new file mode 100644
index 0000000000..e801e0c1bd
--- /dev/null
+++ b/libs/glibmm2/glibmm/property.cc
@@ -0,0 +1,198 @@
+// -*- c++ -*-
+/* $Id$ */
+
+/* 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 Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <glibmm/property.h>
+#include <glibmm/object.h>
+#include <cstddef>
+
+// Temporary hack till GLib gets fixed.
+#undef G_STRLOC
+#define G_STRLOC __FILE__ ":" G_STRINGIFY(__LINE__)
+
+
+namespace
+{
+
+// OK guys, please don't kill me for that. Let me explain what happens here.
+//
+// The task:
+// ---------
+// a) Autogenerate a property ID number for each custom property. This is an
+// unsigned integer, which doesn't have to be assigned continuously. I.e.,
+// it can be everything but 0.
+// b) If more than one object of the same class is instantiated, then of course
+// the already installed properties must be used. That means, a property ID
+// must not be associated with a single Glib::Property<> instance. Rather,
+// the ID has to be associated with the class somehow.
+// c) With only a GObject pointer and a property ID (and perhaps GParamSpec*
+// if necessary), it must be possible to acquire a reference to the property
+// wrapper instance.
+//
+// The current solution:
+// ---------------------
+// a) Assign an ID to a Glib::PropertyBase by calculating its offset in bytes
+// relative to the beginning of the object's memory. dynamic_cast<void*>
+// is used to retrieve a pointer to the very beginning of an instance.
+// b) Recalculate a specific PropertyBase pointer by adding the property ID
+// (i.e. the byte offset) to the object start pointer. The result is then
+// just casted to PropertyBase*.
+//
+// Drawbacks:
+// ----------
+// a) It's a low-level hack. Should be portable, yes, but we can only do very
+// limited error checking.
+// b) All Glib::Property<> instances are absolutely required to be direct data
+// members of the class that implements the property. That seems a natural
+// thing to do, but it's questionable whether it should be a requirement.
+//
+// Advantages:
+// -----------
+// a) Although low-level, it's extremely easy to implement. The nasty code is
+// concentrated in only two non-exposed utility functions, and it works
+// just fine.
+// b) It's efficient, and the memory footprint is very small too.
+// c) I actually tried other ways, too, but ran into dead-ends everywhere.
+// It's probably possible to implement this without calculating offsets,
+// but it'll be very complicated, and involve a lot of qdata pointers to
+// property tables andwhatnot.
+//
+// We can reimplement this later if necessary.
+
+unsigned int property_to_id(Glib::ObjectBase& object, Glib::PropertyBase& property)
+{
+ void *const base_ptr = dynamic_cast<void*>(&object);
+ void *const prop_ptr = &property;
+
+ const ptrdiff_t offset = static_cast<guint8*>(prop_ptr) - static_cast<guint8*>(base_ptr);
+
+ g_return_val_if_fail(offset > 0 && offset < G_MAXINT, 0);
+
+ return static_cast<unsigned int>(offset);
+}
+
+Glib::PropertyBase& property_from_id(Glib::ObjectBase& object, unsigned int property_id)
+{
+ void *const base_ptr = dynamic_cast<void*>(&object);
+ void *const prop_ptr = static_cast<guint8*>(base_ptr) + property_id;
+
+ return *static_cast<Glib::PropertyBase*>(prop_ptr);
+}
+
+} // anonymous namespace
+
+
+namespace Glib
+{
+
+void custom_get_property_callback(GObject* object, unsigned int property_id,
+ GValue* value, GParamSpec* param_spec)
+{
+ if(Glib::ObjectBase *const wrapper = Glib::ObjectBase::_get_current_wrapper(object))
+ {
+ PropertyBase& property = property_from_id(*wrapper, property_id);
+
+ if((property.object_ == wrapper) && (property.param_spec_ == param_spec))
+ g_value_copy(property.value_.gobj(), value);
+ else
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, param_spec);
+ }
+}
+
+void custom_set_property_callback(GObject* object, unsigned int property_id,
+ const GValue* value, GParamSpec* param_spec)
+{
+ if(Glib::ObjectBase *const wrapper = Glib::ObjectBase::_get_current_wrapper(object))
+ {
+ PropertyBase& property = property_from_id(*wrapper, property_id);
+
+ if((property.object_ == wrapper) && (property.param_spec_ == param_spec))
+ {
+ g_value_copy(value, property.value_.gobj());
+ g_object_notify(object, g_param_spec_get_name(param_spec));
+ }
+ else
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, param_spec);
+ }
+}
+
+
+/**** Glib::PropertyBase ***************************************************/
+
+PropertyBase::PropertyBase(Glib::Object& object, GType value_type)
+:
+ object_ (&object),
+ value_ (),
+ param_spec_ (0)
+{
+ value_.init(value_type);
+}
+
+PropertyBase::~PropertyBase()
+{
+ if(param_spec_)
+ g_param_spec_unref(param_spec_);
+}
+
+bool PropertyBase::lookup_property(const Glib::ustring& name)
+{
+ g_assert(param_spec_ == 0);
+
+ param_spec_ = g_object_class_find_property(G_OBJECT_GET_CLASS(object_->gobj()), name.c_str());
+
+ if(param_spec_)
+ {
+ g_assert(G_PARAM_SPEC_VALUE_TYPE(param_spec_) == G_VALUE_TYPE(value_.gobj()));
+ g_param_spec_ref(param_spec_);
+ }
+
+ return (param_spec_ != 0);
+}
+
+void PropertyBase::install_property(GParamSpec* param_spec)
+{
+ g_return_if_fail(param_spec != 0);
+
+ const unsigned int property_id = property_to_id(*object_, *this);
+
+ g_object_class_install_property(G_OBJECT_GET_CLASS(object_->gobj()), property_id, param_spec);
+
+ param_spec_ = param_spec;
+ g_param_spec_ref(param_spec_);
+}
+
+const char* PropertyBase::get_name_internal() const
+{
+ const char *const name = g_param_spec_get_name(param_spec_);
+ g_return_val_if_fail(name != 0, "");
+ return name;
+}
+
+Glib::ustring PropertyBase::get_name() const
+{
+ return Glib::ustring(get_name_internal());
+}
+
+void PropertyBase::notify()
+{
+ g_object_notify(object_->gobj(), g_param_spec_get_name(param_spec_));
+}
+
+} // namespace Glib
+