/* generate_extra_defs.cc
*
* Copyright (C) 2001 The Free Software Foundation
*
* 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 "generate_extra_defs.h"
#include
#include
#include
namespace
{
void add_signal_flag_if(std::string& strFlags, const char* strFlag,
const GSignalQuery& signalQuery, GSignalFlags flag)
{
if (signalQuery.signal_flags & flag)
{
if (!strFlags.empty())
strFlags += ", ";
strFlags += strFlag;
}
}
} // anonymous namespace
std::string
get_property_with_node_name(
GParamSpec* pParamSpec, const std::string& strObjectName, const std::string& strNodeName)
{
std::string strResult;
// Name and type:
const std::string strName = g_param_spec_get_name(pParamSpec);
const std::string strTypeName = G_PARAM_SPEC_TYPE_NAME(pParamSpec);
const gchar* pchBlurb = g_param_spec_get_blurb(pParamSpec);
std::string strDocs = (pchBlurb) ? pchBlurb : "";
// Quick hack to get rid of nested double quotes:
std::replace(strDocs.begin(), strDocs.end(), '"', '\'');
strResult += "(" + strNodeName + " " + strName + "\n";
strResult += " (of-object \"" + strObjectName + "\")\n";
strResult += " (prop-type \"" + strTypeName + "\")\n";
strResult += " (docs \"" + strDocs + "\")\n";
// Flags:
GParamFlags flags = pParamSpec->flags;
bool bReadable = (flags & G_PARAM_READABLE) == G_PARAM_READABLE;
bool bWritable = (flags & G_PARAM_WRITABLE) == G_PARAM_WRITABLE;
bool bConstructOnly = (flags & G_PARAM_CONSTRUCT_ONLY) == G_PARAM_CONSTRUCT_ONLY;
bool bDeprecated = (flags & G_PARAM_DEPRECATED) == G_PARAM_DEPRECATED;
//#t and #f aren't documented, but I guess that it's correct based on the example in the .defs
// spec.
const std::string strTrue = "#t";
const std::string strFalse = "#f";
strResult += " (readable " + (bReadable ? strTrue : strFalse) + ")\n";
strResult += " (writable " + (bWritable ? strTrue : strFalse) + ")\n";
strResult += " (construct-only " + (bConstructOnly ? strTrue : strFalse) + ")\n";
if (bDeprecated)
strResult += " (deprecated #t)\n"; // Default: not deprecated
// Default value:
const GValue* defValue = g_param_spec_get_default_value(pParamSpec);
std::string defString;
bool defValueExists = false;
if (G_VALUE_HOLDS_STRING(defValue))
{
defValueExists = true;
const char* defCString = g_value_get_string(defValue);
if (defCString)
{
// Replace newlines with \n.
// A string default value can contain newline characters.
// gmmproc removes all newlines when it reads .defs files.
defString = std::regex_replace(defCString, std::regex("\n"), "\\n");
}
else
defString = ""; // A NULL string pointer becomes an empty string.
}
else if (G_VALUE_HOLDS_FLOAT(defValue) || G_VALUE_HOLDS_DOUBLE(defValue))
{
// g_value_transform() can transform a floating point value to a terrible
// string, especially if the value is huge.
defValueExists = true;
const double defDouble = G_VALUE_HOLDS_FLOAT(defValue) ?
g_value_get_float(defValue) : g_value_get_double(defValue);
std::ostringstream defStringStream;
defStringStream << defDouble;
defString = defStringStream.str();
}
else
{
GValue defStringValue = G_VALUE_INIT;
g_value_init(&defStringValue, G_TYPE_STRING);
if (g_value_transform(defValue, &defStringValue))
{
const char* defCString = g_value_get_string(&defStringValue);
if (defCString)
{
defValueExists = true;
defString = defCString;
}
}
g_value_unset(&defStringValue);
}
if (defValueExists)
strResult += " (default-value \"" + defString + "\")\n";
strResult += ")\n\n"; // close (strNodeName
return strResult;
}
// Until the glib bug https://bugzilla.gnome.org/show_bug.cgi?id=465631
// is fixed, get_properties() must be called for a GObject before it's
// called for a GInterface.
std::string
get_properties(GType gtype)
{
std::string strResult;
std::string strObjectName = g_type_name(gtype);
// Get the list of properties:
GParamSpec** ppParamSpec = nullptr;
guint iCount = 0;
if (G_TYPE_IS_OBJECT(gtype))
{
GObjectClass* pGClass = G_OBJECT_CLASS(g_type_class_ref(gtype));
ppParamSpec = g_object_class_list_properties(pGClass, &iCount);
g_type_class_unref(pGClass);
if (!ppParamSpec)
{
strResult += ";; Warning: g_object_class_list_properties() returned NULL for " +
std::string(g_type_name(gtype)) + "\n";
}
}
else if (G_TYPE_IS_INTERFACE(gtype))
{
gpointer pGInterface = g_type_default_interface_ref(gtype);
if (pGInterface)
{
ppParamSpec = g_object_interface_list_properties(pGInterface, &iCount);
g_type_default_interface_unref(pGInterface);
if (!ppParamSpec)
{
strResult += ";; Warning: g_object_interface_list_properties() returned NULL for " +
std::string(g_type_name(gtype)) + "\n";
}
}
else
strResult += ";; Warning: g_type_default_interface_ref() returned NULL for " +
std::string(g_type_name(gtype)) + "\n";
}
// This extra check avoids an occasional crash
if (!ppParamSpec)
iCount = 0;
for (guint i = 0; i < iCount; i++)
{
GParamSpec* pParamSpec = ppParamSpec[i];
// Generate the property if the specified gtype actually owns the property.
// (Generally all properties, including any base classes' properties are
// retrieved by g_object_interface_list_properties() for a given gtype.
// The base classes' properties should not be generated).
if (pParamSpec && pParamSpec->owner_type == gtype)
{
strResult += get_property_with_node_name(pParamSpec, strObjectName, "define-property");
}
}
g_free(ppParamSpec);
return strResult;
}
bool
gtype_is_a_pointer(GType gtype)
{
return (g_type_is_a(gtype, G_TYPE_OBJECT) || g_type_is_a(gtype, G_TYPE_BOXED));
}
std::string get_type_name(
GType gtype, GTypeIsAPointerFunc is_a_pointer_func) // Adds a * if necessary.
{
std::string strTypeName = g_type_name(gtype);
if (is_a_pointer_func && is_a_pointer_func(gtype))
strTypeName += "*"; // Add * to show that it's a pointer.
else if (g_type_is_a(gtype, G_TYPE_STRING))
strTypeName = "gchar*"; // g_type_name() returns "gchararray".
return strTypeName;
}
std::string
get_type_name_parameter(GType gtype, GTypeIsAPointerFunc is_a_pointer_func)
{
std::string strTypeName = get_type_name(gtype, is_a_pointer_func);
// All signal parameters that are registered as GTK_TYPE_STRING are actually const gchar*.
if (strTypeName == "gchar*")
strTypeName = "const-gchar*";
return strTypeName;
}
std::string
get_type_name_signal(GType gtype, GTypeIsAPointerFunc is_a_pointer_func)
{
return get_type_name_parameter(
gtype, is_a_pointer_func); // At the moment, it needs the same stuff.
}
std::string
get_signals(GType gtype, GTypeIsAPointerFunc is_a_pointer_func)
{
std::string strResult;
std::string strObjectName = g_type_name(gtype);
gpointer gclass_ref = nullptr;
gpointer ginterface_ref = nullptr;
if (G_TYPE_IS_OBJECT(gtype))
gclass_ref = g_type_class_ref(gtype); // Ensures that class_init() is called.
else if (G_TYPE_IS_INTERFACE(gtype))
ginterface_ref = g_type_default_interface_ref(gtype); // install signals.
// Get the list of signals:
guint iCount = 0;
guint* pIDs = g_signal_list_ids(gtype, &iCount);
// Loop through the list of signals:
if (pIDs)
{
for (guint i = 0; i < iCount; i++)
{
guint signal_id = pIDs[i];
// Name:
std::string strName = g_signal_name(signal_id);
strResult += "(define-signal " + strName + "\n";
strResult += " (of-object \"" + strObjectName + "\")\n";
// Other information about the signal:
GSignalQuery signalQuery = {
0, nullptr, 0, GSignalFlags(0), 0, 0, nullptr,
};
g_signal_query(signal_id, &signalQuery);
// Return type:
std::string strReturnTypeName =
get_type_name_signal(signalQuery.return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE,
is_a_pointer_func); // The type is mangled with a flag. Hacky.
// bool bReturnTypeHasStaticScope = (signalQuery.return_type & G_SIGNAL_TYPE_STATIC_SCOPE) ==
// G_SIGNAL_TYPE_STATIC_SCOPE;
strResult += " (return-type \"" + strReturnTypeName + "\")\n";
// Flags:
std::string strFlags;
add_signal_flag_if(strFlags, "Run First", signalQuery, G_SIGNAL_RUN_FIRST);
add_signal_flag_if(strFlags, "Run Last", signalQuery, G_SIGNAL_RUN_LAST);
add_signal_flag_if(strFlags, "Run Cleanup", signalQuery, G_SIGNAL_RUN_CLEANUP);
add_signal_flag_if(strFlags, "No Recurse", signalQuery, G_SIGNAL_NO_RECURSE);
add_signal_flag_if(strFlags, "Action", signalQuery, G_SIGNAL_ACTION);
add_signal_flag_if(strFlags, "No Hooks", signalQuery, G_SIGNAL_NO_HOOKS);
add_signal_flag_if(strFlags, "Must Collect", signalQuery, G_SIGNAL_MUST_COLLECT);
strResult += " (flags \"" + strFlags + "\")\n";
if (signalQuery.signal_flags & G_SIGNAL_DETAILED)
strResult += " (detailed #t)\n"; // Default: not detailed
if (signalQuery.signal_flags & G_SIGNAL_DEPRECATED)
strResult += " (deprecated #t)\n"; // Default: not deprecated
// Loop through the list of parameters:
const GType* pParameters = signalQuery.param_types;
if (pParameters)
{
strResult += " (parameters\n";
for (unsigned j = 0; j < signalQuery.n_params; j++)
{
GType typeParamMangled = pParameters[j];
// Parameter name:
// We can't get the real parameter name from the GObject system. It's not registered with
// g_signal_new().
gchar* pchNum = g_strdup_printf("%d", j);
std::string strParamName = "p" + std::string(pchNum);
g_free(pchNum);
pchNum = nullptr;
// Just like above, for the return type:
std::string strTypeName =
get_type_name_signal(typeParamMangled & ~G_SIGNAL_TYPE_STATIC_SCOPE,
is_a_pointer_func); // The type is mangled with a flag. Hacky.
// bool bTypeHasStaticScope = (typeParamMangled & G_SIGNAL_TYPE_STATIC_SCOPE) ==
// G_SIGNAL_TYPE_STATIC_SCOPE;
strResult += " '(\"" + strTypeName + "\" \"" + strParamName + "\")\n";
}
strResult += " )\n"; // close (parameters
}
strResult += ")\n\n"; // close (define-signal
}
}
g_free(pIDs);
if (gclass_ref)
g_type_class_unref(gclass_ref); // to match the g_type_class_ref() above.
else if (ginterface_ref)
g_type_default_interface_unref(ginterface_ref); // for interface ref above.
return strResult;
}
std::string
get_defs(GType gtype, GTypeIsAPointerFunc is_a_pointer_func)
{
std::string strObjectName = g_type_name(gtype);
std::string strDefs;
if (G_TYPE_IS_OBJECT(gtype) || G_TYPE_IS_INTERFACE(gtype))
{
strDefs = ";; From " + strObjectName + "\n\n";
strDefs += get_signals(gtype, is_a_pointer_func);
strDefs += get_properties(gtype);
}
else
strDefs = ";; " + strObjectName +
" is neither a GObject nor a GInterface. Not checked for signals and properties.\n\n";
return strDefs;
}