e801e0c1bd47f85cc2191b01391ab877441569e8
[ardour.git] / libs / glibmm2 / glibmm / property.cc
1 // -*- c++ -*-
2 /* $Id$ */
3
4 /* Copyright 2002 The gtkmm Development Team
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the Free
18  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  */
20
21 #include <glibmm/property.h>
22 #include <glibmm/object.h>
23 #include <cstddef>
24
25 // Temporary hack till GLib gets fixed.
26 #undef  G_STRLOC
27 #define G_STRLOC __FILE__ ":" G_STRINGIFY(__LINE__)
28
29
30 namespace
31 {
32
33 // OK guys, please don't kill me for that.  Let me explain what happens here.
34 //
35 // The task:
36 // ---------
37 // a) Autogenerate a property ID number for each custom property.  This is an
38 //    unsigned integer, which doesn't have to be assigned continuously.  I.e.,
39 //    it can be everything but 0.
40 // b) If more than one object of the same class is instantiated, then of course
41 //    the already installed properties must be used.  That means, a property ID
42 //    must not be associated with a single Glib::Property<> instance.  Rather,
43 //    the ID has to be associated with the class somehow.
44 // c) With only a GObject pointer and a property ID (and perhaps GParamSpec*
45 //    if necessary), it must be possible to acquire a reference to the property
46 //    wrapper instance.
47 //
48 // The current solution:
49 // ---------------------
50 // a) Assign an ID to a Glib::PropertyBase by calculating its offset in bytes
51 //    relative to the beginning of the object's memory.  dynamic_cast<void*>
52 //    is used to retrieve a pointer to the very beginning of an instance.
53 // b) Recalculate a specific PropertyBase pointer by adding the property ID
54 //    (i.e. the byte offset) to the object start pointer.  The result is then
55 //    just casted to PropertyBase*.
56 //
57 // Drawbacks:
58 // ----------
59 // a) It's a low-level hack.  Should be portable, yes, but we can only do very
60 //    limited error checking.
61 // b) All Glib::Property<> instances are absolutely required to be direct data
62 //    members of the class that implements the property.  That seems a natural
63 //    thing to do, but it's questionable whether it should be a requirement.
64 //
65 // Advantages:
66 // -----------
67 // a) Although low-level, it's extremely easy to implement.  The nasty code is
68 //    concentrated in only two non-exposed utility functions, and it works
69 //    just fine.
70 // b) It's efficient, and the memory footprint is very small too.
71 // c) I actually tried other ways, too, but ran into dead-ends everywhere.
72 //    It's probably possible to implement this without calculating offsets,
73 //    but it'll be very complicated, and involve a lot of qdata pointers to
74 //    property tables andwhatnot.
75 //
76 // We can reimplement this later if necessary.
77
78 unsigned int property_to_id(Glib::ObjectBase& object, Glib::PropertyBase& property)
79 {
80   void *const base_ptr = dynamic_cast<void*>(&object);
81   void *const prop_ptr = &property;
82
83   const ptrdiff_t offset = static_cast<guint8*>(prop_ptr) - static_cast<guint8*>(base_ptr);
84
85   g_return_val_if_fail(offset > 0 && offset < G_MAXINT, 0);
86
87   return static_cast<unsigned int>(offset);
88 }
89
90 Glib::PropertyBase& property_from_id(Glib::ObjectBase& object, unsigned int property_id)
91 {
92   void *const base_ptr = dynamic_cast<void*>(&object);
93   void *const prop_ptr = static_cast<guint8*>(base_ptr) + property_id;
94
95   return *static_cast<Glib::PropertyBase*>(prop_ptr);
96 }
97
98 } // anonymous namespace
99
100
101 namespace Glib
102 {
103
104 void custom_get_property_callback(GObject* object, unsigned int property_id,
105                                   GValue* value, GParamSpec* param_spec)
106 {
107   if(Glib::ObjectBase *const wrapper = Glib::ObjectBase::_get_current_wrapper(object))
108   {
109     PropertyBase& property = property_from_id(*wrapper, property_id);
110
111     if((property.object_ == wrapper) && (property.param_spec_ == param_spec))
112       g_value_copy(property.value_.gobj(), value);
113     else
114       G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, param_spec);
115   }
116 }
117
118 void custom_set_property_callback(GObject* object, unsigned int property_id,
119                                   const GValue* value, GParamSpec* param_spec)
120 {
121   if(Glib::ObjectBase *const wrapper = Glib::ObjectBase::_get_current_wrapper(object))
122   {
123     PropertyBase& property = property_from_id(*wrapper, property_id);
124
125     if((property.object_ == wrapper) && (property.param_spec_ == param_spec))
126     {
127       g_value_copy(value, property.value_.gobj());
128       g_object_notify(object, g_param_spec_get_name(param_spec));
129     }
130     else
131       G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, param_spec);
132   }
133 }
134
135
136 /**** Glib::PropertyBase ***************************************************/
137
138 PropertyBase::PropertyBase(Glib::Object& object, GType value_type)
139 :
140   object_     (&object),
141   value_      (),
142   param_spec_ (0)
143 {
144   value_.init(value_type);
145 }
146
147 PropertyBase::~PropertyBase()
148 {
149   if(param_spec_)
150     g_param_spec_unref(param_spec_);
151 }
152
153 bool PropertyBase::lookup_property(const Glib::ustring& name)
154 {
155   g_assert(param_spec_ == 0);
156
157   param_spec_ = g_object_class_find_property(G_OBJECT_GET_CLASS(object_->gobj()), name.c_str());
158
159   if(param_spec_)
160   {
161     g_assert(G_PARAM_SPEC_VALUE_TYPE(param_spec_) == G_VALUE_TYPE(value_.gobj()));
162     g_param_spec_ref(param_spec_);
163   }
164
165   return (param_spec_ != 0);
166 }
167
168 void PropertyBase::install_property(GParamSpec* param_spec)
169 {
170   g_return_if_fail(param_spec != 0);
171
172   const unsigned int property_id = property_to_id(*object_, *this);
173
174   g_object_class_install_property(G_OBJECT_GET_CLASS(object_->gobj()), property_id, param_spec);
175
176   param_spec_ = param_spec;
177   g_param_spec_ref(param_spec_);
178 }
179
180 const char* PropertyBase::get_name_internal() const
181 {
182   const char *const name = g_param_spec_get_name(param_spec_);
183   g_return_val_if_fail(name != 0, "");
184   return name;
185 }
186
187 Glib::ustring PropertyBase::get_name() const
188 {
189   return Glib::ustring(get_name_internal());
190 }
191
192 void PropertyBase::notify()
193 {
194   g_object_notify(object_->gobj(), g_param_spec_get_name(param_spec_));
195 }
196
197 } // namespace Glib
198