/* Copyright 2010 The glibmm 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
namespace Glib
{
/****************** VariantBase ***********************************/
VariantBase::VariantBase(GVariant* castitem, bool make_a_copy /* = false */)
{
if (castitem)
{
if (g_variant_is_floating(castitem))
g_variant_ref_sink(castitem);
if (make_a_copy)
g_variant_ref(castitem);
}
gobject_ = castitem;
}
VariantBase::operator bool() const
{
return gobj();
}
void
VariantBase::init(const GVariant* cobject, bool take_a_reference)
{
if (gobject_)
g_variant_unref(gobject_);
gobject_ = const_cast(cobject);
if (take_a_reference)
g_variant_ref(gobject_);
}
bool VariantBase::operator==(const VariantBase& other) const
{
return equal(other);
}
bool VariantBase::operator!=(const VariantBase& other) const
{
return !equal(other);
}
void
VariantBase::get_normal_form(VariantBase& result) const
{
GVariant* const g_value = g_variant_get_normal_form(const_cast(gobj()));
// The C function never returns NULL, according to its documenation,
// so we don't need a bool return value.
result.init(g_value); // g_value is already referenced.
}
void
VariantBase::byteswap(VariantBase& result) const
{
GVariant* const g_value = g_variant_byteswap(const_cast(gobj()));
result.init(g_value); // g_value is already referenced.
}
bool
VariantBase::is_castable_to(const VariantType& supertype) const
{
const std::string subtype_string = get_type_string();
const std::string supertype_string = supertype.get_string();
// The following code is similar to g_variant_type_is_subtype_of(), with
// these differences:
// - Both types are assumed to be definite types (no indefinite types,
// no 'r', '?' or '*').
// - VARIANT_TYPE_OBJECT_PATH (o) and VARIANT_TYPE_SIGNATURE (g) can be cast to
// VARIANT_TYPE_STRING (s), (Variant::variant_type()).
// - VARIANT_TYPE_STRING (s), VARIANT_TYPE_OBJECT_PATH (o) and
// VARIANT_TYPE_SIGNATURE (g) can be cast to VARIANT_TYPE_BYTESTRING (ay),
// (Variant::variant_type()).
// - VARIANT_TYPE_HANDLE (h) can be cast to VARIANT_TYPE_INT32 (i),
// (Variant::variant_type()).
std::size_t subtype_index = 0;
std::size_t supertype_index = 0;
const std::size_t supertype_size = supertype_string.size();
while (supertype_index < supertype_size)
{
const char supertype_char = supertype_string[supertype_index++];
const char subtype_char = subtype_string[subtype_index++];
if (supertype_char == subtype_char)
continue;
switch (supertype_char)
{
case 's':
if (!(subtype_char == 'o' || subtype_char == 'g'))
return false;
break;
case 'a':
if (!(supertype_string[supertype_index] == 'y' &&
(subtype_char == 's' || subtype_char == 'o' || subtype_char == 'g')))
return false;
supertype_index++;
break;
case 'i':
if (!(subtype_char == 'h'))
return false;
break;
default:
return false;
}
}
return true;
}
/****************** VariantStringBase ***********************************/
VariantStringBase::VariantStringBase() : VariantBase()
{
}
VariantStringBase::VariantStringBase(GVariant* castitem, bool take_a_reference)
: VariantBase(castitem, take_a_reference)
{
}
// static
void
VariantStringBase::create_object_path(VariantStringBase& output, const std::string& object_path)
{
GVariant* result = nullptr;
result = g_variant_new_object_path(object_path.c_str());
g_variant_ref_sink(result);
output.init(result);
}
// static
void
VariantStringBase::create_signature(VariantStringBase& output, const std::string& signature)
{
GVariant* result = nullptr;
result = g_variant_new_signature(signature.c_str());
g_variant_ref_sink(result);
output.init(result);
}
/****************** VariantContainerBase ***********************************/
VariantContainerBase::VariantContainerBase() : VariantBase()
{
}
VariantContainerBase::VariantContainerBase(GVariant* castitem, bool take_a_reference)
: VariantBase(castitem, take_a_reference)
{
}
// static
VariantContainerBase
VariantContainerBase::create_tuple(const std::vector& children)
{
using var_ptr = GVariant*;
var_ptr* const var_array = new var_ptr[children.size()];
for (std::vector::size_type i = 0; i < children.size(); i++)
var_array[i] = const_cast(children[i].gobj());
VariantContainerBase result =
VariantContainerBase(g_variant_new_tuple(var_array, children.size()));
delete[] var_array;
return result;
}
// static
VariantContainerBase
VariantContainerBase::create_tuple(const VariantBase& child)
{
std::vector vec;
vec.emplace_back(child);
return create_tuple(vec);
}
// static
VariantContainerBase
VariantContainerBase::create_maybe(const VariantType& child_type, const VariantBase& child)
{
GVariant* g_variant = g_variant_new_maybe(child_type.gobj(), const_cast(child.gobj()));
VariantContainerBase result = VariantContainerBase(g_variant);
return result;
}
void
VariantContainerBase::get_child(VariantBase& child, gsize index) const
{
if (index >= get_n_children())
throw std::out_of_range("VariantContainerBase::get_child(): Index out of bounds.");
GVariant* const gvariant = g_variant_get_child_value(gobject_, index);
child.init(gvariant);
}
// VariantContainerBase has no method variant_type()
template <>
VariantContainerBase
VariantBase::cast_dynamic(const VariantBase& v)
{
if (!v.gobj())
return VariantContainerBase();
if (v.get_type().is_container())
{
return VariantContainerBase(const_cast(v.gobj()), true);
}
else
{
// std::cerr << "vtype=" << v.get_type_string() << std::endl;
throw std::bad_cast();
}
}
bool
VariantContainerBase::get_maybe(VariantBase& maybe) const
{
GVariant* const g_value = g_variant_get_maybe(const_cast(gobj()));
if (g_value)
{
maybe.init(g_value); // g_value is already referenced.
return true;
}
else
{
return false;
}
}
VariantIter
VariantContainerBase::get_iter(const VariantType& container_variant_type) const
{
// Get the GVariantIter.
// If the underlying GVariant can be cast to the type of the container,
// use the actual type (which may differ from container_variant_type, if
// the GVariant contains strings, object paths or DBus type signatures),
// otherwise let g_variant_get() report what's wrong with the type.
GVariantIter* g_iter = nullptr;
g_variant_get(const_cast(gobj()),
is_castable_to(container_variant_type) ? get_type_string().c_str()
: container_variant_type.get_string().c_str(),
&g_iter);
return VariantIter(g_iter);
}
/****************** Specializations ***********************************/
/*--------------------Variant---------------------*/
Variant::Variant() : VariantContainerBase()
{
}
Variant::Variant(GVariant* castitem, bool take_a_reference)
: VariantContainerBase(castitem, take_a_reference)
{
}
// static
const VariantType&
Variant::variant_type()
{
return VARIANT_TYPE_VARIANT;
}
Variant
Variant::create(const VariantBase& data)
{
auto result = Variant(g_variant_new_variant(const_cast(data.gobj())));
return result;
}
void
Variant::get(VariantBase& variant) const
{
GVariant* const gvariant = g_variant_get_variant(gobject_);
variant.init(gvariant);
}
/*--------------------Variant---------------------*/
Variant::Variant() : VariantStringBase()
{
}
Variant::Variant(GVariant* castitem, bool take_a_reference)
: VariantStringBase(castitem, take_a_reference)
{
}
// static
const VariantType&
Variant::variant_type()
{
return VARIANT_TYPE_STRING;
}
Variant
Variant::create(const Glib::ustring& data)
{
auto result = Variant(g_variant_new_string(data.c_str()));
return result;
}
Glib::ustring
Variant::get() const
{
return convert_const_gchar_ptr_to_ustring(g_variant_get_string(gobject_, nullptr));
}
/*--------------------Variant---------------------*/
Variant::Variant() : VariantStringBase()
{
}
Variant::Variant(GVariant* castitem, bool take_a_reference)
: VariantStringBase(castitem, take_a_reference)
{
}
// static
const VariantType&
Variant::variant_type()
{
return VARIANT_TYPE_OBJECT_PATH;
}
Variant
Variant::create(const Glib::DBusObjectPathString& data)
{
auto result = Variant(g_variant_new_object_path(data.c_str()));
return result;
}
Glib::DBusObjectPathString
Variant::get() const
{
const char* s = g_variant_get_string(gobject_, nullptr);
return s ? CppType(s) : CppType();
}
/*--------------------Variant---------------------*/
Variant::Variant() : VariantStringBase()
{
}
Variant::Variant(GVariant* castitem, bool take_a_reference)
: VariantStringBase(castitem, take_a_reference)
{
}
// static
const VariantType&
Variant::variant_type()
{
return VARIANT_TYPE_SIGNATURE;
}
Variant
Variant::create(const Glib::DBusSignatureString& data)
{
auto result = Variant(g_variant_new_signature(data.c_str()));
return result;
}
Glib::DBusSignatureString
Variant::get() const
{
const char* s = g_variant_get_string(gobject_, nullptr);
return s ? CppType(s) : CppType();
}
/*--------------------Variant---------------------*/
Variant::Variant() : VariantStringBase()
{
}
Variant::Variant(GVariant* castitem, bool take_a_reference)
: VariantStringBase(castitem, take_a_reference)
{
}
// static
const VariantType&
Variant::variant_type()
{
return VARIANT_TYPE_BYTESTRING;
}
Variant
Variant::create(const std::string& data)
{
auto result = Variant(g_variant_new_bytestring(data.c_str()));
return result;
}
std::string
Variant::get() const
{
const VariantType vtype = get_type();
const char* pch = nullptr;
if (vtype.equal(VARIANT_TYPE_BYTESTRING))
pch = g_variant_get_bytestring(gobject_);
else // g_variant_get_string() can handle strings, object paths, and signatures.
pch = g_variant_get_string(gobject_, nullptr);
return convert_const_gchar_ptr_to_stdstring(pch);
}
/*--------------------Variant< std::vector >---------------------*/
using type_vec_ustring = std::vector;
Variant::Variant() : VariantContainerBase()
{
}
Variant::Variant(GVariant* castitem, bool take_a_reference)
: VariantContainerBase(castitem, take_a_reference)
{
}
// static
const VariantType&
Variant::variant_type()
{
return VARIANT_TYPE_STRING_ARRAY;
}
Variant
Variant::create(const type_vec_ustring& data)
{
// Get the variant type of the elements.
VariantType element_variant_type = Variant::variant_type();
// Get the variant type of the array.
VariantType array_variant_type = Variant::variant_type();
// Create a GVariantBuilder to build the array.
GVariantBuilder* builder = g_variant_builder_new(array_variant_type.gobj());
// Add the elements of the vector into the builder.
for (const auto& str : data)
g_variant_builder_add(builder, element_variant_type.get_string().c_str(), str.c_str());
// Create the variant using the builder.
auto result =
Variant(g_variant_new(array_variant_type.get_string().c_str(), builder));
g_variant_builder_unref(builder);
return result;
}
Glib::ustring
Variant::get_child(gsize index) const
{
if (index >= get_n_children())
throw std::out_of_range(
"Variant< std::vector >::get_child(): Index out of bounds.");
GVariant* gvariant = g_variant_get_child_value(const_cast(gobj()), index);
return Glib::Variant(gvariant).get();
}
type_vec_ustring
Variant::get() const
{
// g_variant_get_strv() can be used only if the type is VARIANT_TYPE_STRING_ARRAY,
// but the Variant can alternatively hold an array of object paths or DBus type signatures.
type_vec_ustring result;
for (gsize i = 0, n_children = get_n_children(); i < n_children; ++i)
{
GVariant* gvariant = g_variant_get_child_value(const_cast(gobj()), i);
result.emplace_back(Glib::Variant(gvariant).get());
}
return result;
}
VariantIter
Variant::get_iter() const
{
return VariantContainerBase::get_iter(variant_type());
}
/*--------------------Variant>---------------------*/
using type_vec_opstring = std::vector;
Variant::Variant() : VariantContainerBase()
{
}
Variant::Variant(GVariant* castitem, bool take_a_reference)
: VariantContainerBase(castitem, take_a_reference)
{
}
// static
const VariantType&
Variant::variant_type()
{
return VARIANT_TYPE_OBJECT_PATH_ARRAY;
}
// static
Variant
Variant::create(const type_vec_opstring& data)
{
// Get the variant type of the elements.
VariantType element_variant_type = Variant::variant_type();
// Get the variant type of the array.
VariantType array_variant_type = Variant::variant_type();
// Create a GVariantBuilder to build the array.
GVariantBuilder* builder = g_variant_builder_new(array_variant_type.gobj());
// Add the elements of the vector into the builder.
for (const auto& str : data)
g_variant_builder_add(builder, element_variant_type.get_string().c_str(), str.c_str());
// Create the variant using the builder.
auto result =
Variant(g_variant_new(array_variant_type.get_string().c_str(), builder));
g_variant_builder_unref(builder);
return result;
}
Glib::DBusObjectPathString
Variant::get_child(gsize index) const
{
if (index >= get_n_children())
throw std::out_of_range(
"Variant< std::vector >::get_child(): Index out of bounds.");
GVariant* gvariant = g_variant_get_child_value(const_cast(gobj()), index);
return Glib::Variant(gvariant).get();
}
type_vec_opstring
Variant::get() const
{
gsize n_children = 0;
const gchar** children = g_variant_get_objv(const_cast(gobj()), &n_children);
type_vec_opstring result = type_vec_opstring(children, children+n_children);
g_free(children);
return result;
}
VariantIter
Variant::get_iter() const
{
return VariantContainerBase::get_iter(variant_type());
}
/*--------------------Variant< std::vector >---------------------*/
using type_vec_string = std::vector;
Variant::Variant() : VariantContainerBase()
{
}
Variant::Variant(GVariant* castitem, bool take_a_reference)
: VariantContainerBase(castitem, take_a_reference)
{
}
// static
const VariantType&
Variant::variant_type()
{
return VARIANT_TYPE_BYTESTRING_ARRAY;
}
Variant
Variant::create(const type_vec_string& data)
{
// Create a string array to add the strings of the vector to.
char** str_array = g_new(char*, data.size() + 1);
// Add the elements of the vector into the string array.
for (type_vec_string::size_type i = 0; i < data.size(); i++)
str_array[i] = g_strdup(data[i].c_str());
// Terminate the string array.
str_array[data.size()] = nullptr;
// Create the variant using g_variant_new_bytestring_array() (passing the
// newly constructed array.
auto result = Variant(g_variant_new_bytestring_array(str_array, data.size()));
g_strfreev(str_array);
return result;
}
Variant
Variant::create_from_object_paths(const type_vec_string& data)
{
// Create a string array to add the strings of the vector to.
char** str_array = g_new(char*, data.size() + 1);
// Add the elements of the vector into the string array.
for (type_vec_string::size_type i = 0; i < data.size(); i++)
str_array[i] = g_strdup(data[i].c_str());
// Terminate the string array.
str_array[data.size()] = nullptr;
// Create the variant using g_variant_new_objv() (passing the
// newly constructed array.
auto result = Variant(g_variant_new_objv(str_array, data.size()));
g_strfreev(str_array);
return result;
}
std::string
Variant::get_child(gsize index) const
{
if (index >= get_n_children())
throw std::out_of_range(
"Variant< std::vector >::get_child(): Index out of bounds.");
GVariant* gvariant = g_variant_get_child_value(const_cast(gobj()), index);
return Glib::Variant(gvariant).get();
}
type_vec_string
Variant::get() const
{
// g_variant_get_bytestring_array() can be used only if the type is VARIANT_TYPE_BYTESTRING_ARRAY,
// but the Variant can alternatively hold an array of strings, object paths or DBus type
// signatures.
type_vec_string result;
for (gsize i = 0, n_children = get_n_children(); i < n_children; ++i)
{
GVariant* gvariant = g_variant_get_child_value(const_cast(gobj()), i);
result.emplace_back(Glib::Variant(gvariant).get());
}
return result;
}
VariantIter
Variant::get_iter() const
{
return VariantContainerBase::get_iter(variant_type());
}
/*---------------------Value---------------------*/
void Value::set(CppType data)
{
set_variant(data.gobj());
}
Value::CppType Value::get() const
{
return CppType(get_variant(), true);
}
} // namespace Glib