diff options
28 files changed, 2309 insertions, 342 deletions
@@ -1,8 +1,12 @@ -> for version 1.0 -* add kiosk/locking mechanism (mostly done) -* XfconfBackend needs to do validation on channel and property names -* unit tests (mostly done) +* XfconfBackend needs to do better validation on channel and property names +* unit tests - some done, need: + - locking test + - RemoveChannel() test + - PropertyChanged notification test + - GetAllProperties() test + - tests for all the array and struct stuff * MCS settings migration code - special backend to read config entries - script/c code for users who want to migrate all their settings to @@ -16,10 +20,17 @@ - file system monitoring. if a backend file changes, the daemon needs to re-read the file, but must save the old property tree so it can figure out what has changed so it can send appropriate PropertyChanged signals + + what happens if a channel isn't loaded into memory and its backing file + changes? load it and send PropertyChanged for all properties? that + could be very bad. * separate XSETTINGS daemon that reads values from xfconfd - +* libxfce4mcs-client dummy implementation that forwards to libxfconf +* cmdline get/set/query tool so the config store can be scriptable -> for future: * transaction support * network support - allow remote displays and such +* do some caching in the client library? seems wasteful of memory, but it + could allow the daemon to ditch its copy of a channel's properties more + often. diff --git a/common/Makefile.am b/common/Makefile.am index d7c60ac..60078dd 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -5,6 +5,7 @@ libxfconf_common_la_SOURCES = \ xfconf-errors.c \ xfconf-marshal.c \ xfconf-marshal.h \ + xfconf-types.c \ xfconf-util.c \ xfconf-util.h diff --git a/common/xfconf-types.c b/common/xfconf-types.c new file mode 100644 index 0000000..43b3711 --- /dev/null +++ b/common/xfconf-types.c @@ -0,0 +1,183 @@ +/* + * xfconf + * + * Copyright (c) 2007 Brian Tarricone <bjt23@cornell.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License ONLY. + * + * This program 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 General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "xfconf/xfconf-types.h" + +#include <gobject/gvaluecollector.h> + + +static void +ushort_value_init(GValue *value) +{ + value->data[0].v_int = 0; +} + +static void +ushort_value_copy(const GValue *src_value, + GValue *dest_value) +{ + dest_value->data[0].v_int = src_value->data[0].v_int; +} + +static gchar * +ushort_value_collect(GValue *value, + guint n_collect_values, + GTypeCValue *collect_values, + guint collect_flags) +{ + value->data[0].v_int = collect_values[0].v_int; + return NULL; +} + +static gchar * +ushort_value_lcopy(const GValue *value, + guint n_collect_values, + GTypeCValue *collect_values, + guint collect_flags) +{ + guint16 *uint16_p = collect_values[0].v_pointer; + + if(!uint16_p) { + return g_strdup_printf("value location for `%s' passed as NULL", + G_VALUE_TYPE_NAME(value)); + } + + *uint16_p = value->data[0].v_int; + + return NULL; +} + + +GType +xfconf_uint16_get_type() +{ + static GType uint16_type = 0; + GTypeFundamentalInfo finfo = { 0 }; + GTypeInfo info = { 0, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, }; + static const GTypeValueTable value_table = { + ushort_value_init, + NULL, + ushort_value_copy, + NULL, + "i", + ushort_value_collect, + "p", + ushort_value_lcopy + }; + + if(!uint16_type) { + info.value_table = &value_table; + uint16_type = g_type_register_fundamental(g_type_fundamental_next(), + "XfconfUint16", &info, + &finfo, 0); + } + + return uint16_type; +} + +/** + * xfconf_g_value_get_uint16: + * @value: A #GValue. + * + * Retrieves a 16-bit unsigned value from @value. + * + * Returns: A guint16. + **/ +guint16 +xfconf_g_value_get_uint16(const GValue *value) +{ + g_return_val_if_fail(G_VALUE_HOLDS(value, XFCONF_TYPE_UINT16), 0); + return (guint16)value->data[0].v_int; +} + +/** + * xfconf_g_value_set_uint16: + * @value: A #GValue. + * @v_uint16: A guint16. + * + * Sets @value using an unsigned 16-bit integer. + **/ +void +xfconf_g_value_set_uint16(GValue *value, + guint16 v_uint16) +{ + g_return_if_fail(G_VALUE_HOLDS(value, XFCONF_TYPE_UINT16)); + value->data[0].v_int = v_uint16; +} + +GType +xfconf_int16_get_type() +{ + static GType int16_type = 0; + GTypeFundamentalInfo finfo = { 0 }; + GTypeInfo info = { 0, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, }; + static const GTypeValueTable value_table = { + ushort_value_init, + NULL, + ushort_value_copy, + NULL, + "i", + ushort_value_collect, + "p", + ushort_value_lcopy + }; + + if(!int16_type) { + info.value_table = &value_table; + int16_type = g_type_register_fundamental(g_type_fundamental_next(), + "XfconfInt16", &info, + &finfo, 0); + } + + return int16_type; +} + +/** + * xfconf_g_value_get_int16: + * @value: A #GValue. + * + * Retrieves a 16-bit signed value from @value. + * + * Returns: A gint16. + **/ +gint16 +xfconf_g_value_get_int16(const GValue *value) +{ + g_return_val_if_fail(G_VALUE_HOLDS(value, XFCONF_TYPE_INT16), 0); + return (gint16)value->data[0].v_int; +} + +/** + * xfconf_g_value_set_int16: + * @value: A #GValue. + * @v_int16: A gint16. + * + * Sets @value using a signed 16-bit integer. + **/ +void +xfconf_g_value_set_int16(GValue *value, + gint16 v_int16) +{ + g_return_if_fail(G_VALUE_HOLDS(value, XFCONF_TYPE_INT16)); + value->data[0].v_int = v_int16; +} diff --git a/common/xfconf-util.h b/common/xfconf-util.h index 14cf1be..65fb2c8 100644 --- a/common/xfconf-util.h +++ b/common/xfconf-util.h @@ -24,6 +24,8 @@ G_BEGIN_DECLS +#define XFCONF_DBUS_TYPE_G_DOUBLE_ARRAY (dbus_g_type_get_collection("GArray", G_TYPE_DOUBLE)) + void xfconf_g_value_free(GValue *value); gboolean xfconf_user_is_in_list(const gchar *list); diff --git a/docs/reference/tmpl/xfconf-backend.sgml b/docs/reference/tmpl/xfconf-backend.sgml index 1500b80..7004699 100644 --- a/docs/reference/tmpl/xfconf-backend.sgml +++ b/docs/reference/tmpl/xfconf-backend.sgml @@ -39,6 +39,12 @@ one could think of to store data. @_xb_reserved2: Reserved for future expansion. @_xb_reserved3: Reserved for future expansion. +<!-- ##### STRUCT XfconfBackend ##### --> +<para> + +</para> + + <!-- ##### FUNCTION xfconf_backend_initialize ##### --> <para> diff --git a/docs/reference/tmpl/xfconf-channel.sgml b/docs/reference/tmpl/xfconf-channel.sgml index 69f5b25..7ea9b30 100644 --- a/docs/reference/tmpl/xfconf-channel.sgml +++ b/docs/reference/tmpl/xfconf-channel.sgml @@ -94,7 +94,6 @@ configuration keys with the same names. @channel: @property: -@default_value: @Returns: @@ -109,7 +108,7 @@ configuration keys with the same names. @Returns: -<!-- ##### FUNCTION xfconf_channel_get_int64 ##### --> +<!-- ##### FUNCTION xfconf_channel_get_uint64 ##### --> <para> </para> @@ -160,7 +159,7 @@ configuration keys with the same names. @channel: @property: -@value: +@values: @Returns: @@ -175,7 +174,7 @@ configuration keys with the same names. @Returns: -<!-- ##### FUNCTION xfconf_channel_set_int64 ##### --> +<!-- ##### FUNCTION xfconf_channel_set_uint64 ##### --> <para> </para> @@ -208,3 +207,196 @@ configuration keys with the same names. @Returns: +<!-- ##### FUNCTION xfconf_channel_get_property ##### --> +<para> + +</para> + +@channel: +@property: +@value: +@Returns: + + +<!-- ##### FUNCTION xfconf_channel_set_property ##### --> +<para> + +</para> + +@channel: +@property: +@value: +@Returns: + + +<!-- ##### FUNCTION xfconf_channel_get_array ##### --> +<para> + +</para> + +@channel: +@property: +@first_value_type: +@Varargs: +@Returns: + + +<!-- ##### FUNCTION xfconf_channel_get_array_valist ##### --> +<para> + +</para> + +@channel: +@property: +@first_value_type: +@var_args: +@Returns: + + +<!-- ##### FUNCTION xfconf_channel_get_arrayv ##### --> +<para> + +</para> + +@channel: +@property: +@Returns: + + +<!-- ##### FUNCTION xfconf_channel_set_array ##### --> +<para> + +</para> + +@channel: +@property: +@first_value_type: +@Varargs: +@Returns: + + +<!-- ##### FUNCTION xfconf_channel_set_array_valist ##### --> +<para> + +</para> + +@channel: +@property: +@first_value_type: +@var_args: +@Returns: + + +<!-- ##### FUNCTION xfconf_channel_set_arrayv ##### --> +<para> + +</para> + +@channel: +@property: +@values: +@Returns: + + +<!-- ##### FUNCTION xfconf_channel_get_named_struct ##### --> +<para> + +</para> + +@channel: +@property: +@struct_name: +@value_struct: +@Returns: + + +<!-- ##### FUNCTION xfconf_channel_set_named_struct ##### --> +<para> + +</para> + +@channel: +@property: +@struct_name: +@value_struct: +@Returns: + + +<!-- ##### FUNCTION xfconf_channel_get_struct ##### --> +<para> + +</para> + +@channel: +@property: +@value_struct: +@first_member_type: +@Varargs: +@Returns: + + +<!-- ##### FUNCTION xfconf_channel_get_struct_valist ##### --> +<para> + +</para> + +@channel: +@property: +@value_struct: +@first_member_type: +@var_args: +@Returns: + + +<!-- ##### FUNCTION xfconf_channel_get_structv ##### --> +<para> + +</para> + +@channel: +@property: +@value_struct: +@n_members: +@member_types: +@Returns: + + +<!-- ##### FUNCTION xfconf_channel_set_struct ##### --> +<para> + +</para> + +@channel: +@property: +@value_struct: +@first_member_type: +@Varargs: +@Returns: + + +<!-- ##### FUNCTION xfconf_channel_set_struct_valist ##### --> +<para> + +</para> + +@channel: +@property: +@value_struct: +@first_member_type: +@var_args: +@Returns: + + +<!-- ##### FUNCTION xfconf_channel_set_structv ##### --> +<para> + +</para> + +@channel: +@property: +@value_struct: +@n_members: +@member_types: +@Returns: + + diff --git a/docs/reference/tmpl/xfconf-types.sgml b/docs/reference/tmpl/xfconf-types.sgml new file mode 100644 index 0000000..ff88f27 --- /dev/null +++ b/docs/reference/tmpl/xfconf-types.sgml @@ -0,0 +1,57 @@ +<!-- ##### SECTION Title ##### --> +Xfconf Types + +<!-- ##### SECTION Short_Description ##### --> +GObject types used by the Xfconf daemon and library + +<!-- ##### SECTION Long_Description ##### --> +<para> + +</para> + + +<!-- ##### SECTION See_Also ##### --> +<para> + +</para> + + +<!-- ##### SECTION Stability_Level ##### --> + + +<!-- ##### FUNCTION xfconf_g_value_get_int16 ##### --> +<para> + +</para> + +@value: +@Returns: + + +<!-- ##### FUNCTION xfconf_g_value_get_uint16 ##### --> +<para> + +</para> + +@value: +@Returns: + + +<!-- ##### FUNCTION xfconf_g_value_set_int16 ##### --> +<para> + +</para> + +@value: +@v_int16: + + +<!-- ##### FUNCTION xfconf_g_value_set_uint16 ##### --> +<para> + +</para> + +@value: +@v_uint16: + + diff --git a/docs/reference/tmpl/xfconf-unused.sgml b/docs/reference/tmpl/xfconf-unused.sgml index 157b816..a941f34 100644 --- a/docs/reference/tmpl/xfconf-unused.sgml +++ b/docs/reference/tmpl/xfconf-unused.sgml @@ -53,20 +53,29 @@ stamp-xfconf-marshal @obj: -<!-- ##### STRUCT XfconfBackend ##### --> +<!-- ##### FUNCTION xfconf_backend_get_error_quark ##### --> <para> </para> +@Returns: -<!-- ##### FUNCTION xfconf_backend_get_error_quark ##### --> +<!-- ##### FUNCTION xfconf_channel_begin_transaction ##### --> <para> </para> +@channel: @Returns: -<!-- ##### FUNCTION xfconf_channel_begin_transaction ##### --> +<!-- ##### FUNCTION xfconf_channel_cancel_transaction ##### --> +<para> + +</para> + +@channel: + +<!-- ##### FUNCTION xfconf_channel_commit_transaction ##### --> <para> </para> @@ -74,18 +83,23 @@ stamp-xfconf-marshal @channel: @Returns: -<!-- ##### FUNCTION xfconf_channel_cancel_transaction ##### --> +<!-- ##### FUNCTION xfconf_channel_get_int64 ##### --> <para> </para> @channel: +@property: +@default_value: +@Returns: -<!-- ##### FUNCTION xfconf_channel_commit_transaction ##### --> +<!-- ##### FUNCTION xfconf_channel_set_int64 ##### --> <para> </para> @channel: +@property: +@value: @Returns: diff --git a/docs/reference/tmpl/xfconf.sgml b/docs/reference/tmpl/xfconf.sgml index 5d63af8..1ce1fe3 100644 --- a/docs/reference/tmpl/xfconf.sgml +++ b/docs/reference/tmpl/xfconf.sgml @@ -1,8 +1,8 @@ <!-- ##### SECTION Title ##### --> -Xfconf Library Initialization +Xfconf Library Core <!-- ##### SECTION Short_Description ##### --> -Init routines for libxfconf +Init routines and core functionality for libxfconf <!-- ##### SECTION Long_Description ##### --> <para> @@ -38,3 +38,13 @@ resources. +<!-- ##### FUNCTION xfconf_named_struct_register ##### --> +<para> + +</para> + +@struct_name: +@n_members: +@member_types: + + diff --git a/docs/reference/xfconf-docs.sgml b/docs/reference/xfconf-docs.sgml index c9735fd..91ada57 100644 --- a/docs/reference/xfconf-docs.sgml +++ b/docs/reference/xfconf-docs.sgml @@ -7,7 +7,8 @@ </bookinfo> <chapter> - <title>Xfconf Errors</title> + <title>Xfconf Core Functionality</title> + <xi:include href="xml/xfconf-types.xml"/> <xi:include href="xml/xfconf-errors.xml"/> </chapter> diff --git a/docs/reference/xfconf-sections.txt b/docs/reference/xfconf-sections.txt index 228b9b4..26a2beb 100644 --- a/docs/reference/xfconf-sections.txt +++ b/docs/reference/xfconf-sections.txt @@ -8,15 +8,31 @@ xfconf_channel_get_all xfconf_channel_get_string xfconf_channel_get_string_list xfconf_channel_get_int -xfconf_channel_get_int64 +xfconf_channel_get_uint64 xfconf_channel_get_double xfconf_channel_get_bool xfconf_channel_set_string xfconf_channel_set_string_list xfconf_channel_set_int -xfconf_channel_set_int64 +xfconf_channel_set_uint64 xfconf_channel_set_double xfconf_channel_set_bool +xfconf_channel_get_property +xfconf_channel_set_property +xfconf_channel_get_array +xfconf_channel_get_array_valist +xfconf_channel_get_arrayv +xfconf_channel_set_array +xfconf_channel_set_array_valist +xfconf_channel_set_arrayv +xfconf_channel_get_named_struct +xfconf_channel_set_named_struct +xfconf_channel_get_struct +xfconf_channel_get_struct_valist +xfconf_channel_get_structv +xfconf_channel_set_struct +xfconf_channel_set_struct_valist +xfconf_channel_set_structv <SUBSECTION Standard> XFCONF_CHANNEL XFCONF_IS_CHANNEL @@ -30,6 +46,7 @@ XFCONF_CHANNEL_GET_CLASS <SECTION> <FILE>xfconf-backend</FILE> XfconfBackendInterface +XfconfBackend xfconf_backend_initialize xfconf_backend_set xfconf_backend_get @@ -49,6 +66,7 @@ xfconf_backend_get_type <FILE>xfconf</FILE> xfconf_init xfconf_shutdown +xfconf_named_struct_register </SECTION> <SECTION> @@ -56,3 +74,11 @@ xfconf_shutdown XFCONF_ERROR XfconfError </SECTION> + +<SECTION> +<FILE>xfconf-types</FILE> +xfconf_g_value_get_int16 +xfconf_g_value_get_uint16 +xfconf_g_value_set_int16 +xfconf_g_value_set_uint16 +</SECTION> diff --git a/docs/spec/backend.txt b/docs/spec/backend.txt new file mode 100644 index 0000000..421d737 --- /dev/null +++ b/docs/spec/backend.txt @@ -0,0 +1,43 @@ +Xfconf Backends +--------------- + +Xfconf backends act as the actual configuration store. A backend might +write flat files to disk, XML files to disk, rows to a relational +database, entries to an LDAP server, or any other possible mechanism +desired to persistently store the data. + +A backend must implement the XfconfBackend interface, which is described +in the API documentation. + +The backend will have to deal with a variety of data types. While some +of these types are wrapped directly in the client library (e.g., +xfconf_channel_set_uint64()), users of the library can also set other, +semi-arbitrary types, as well as array types. + +So, the backend must, at minimum, be able to understand the following GTypes: + +G_TYPE_STRING +G_TYPE_STRV (special GType for a C array of G_TYPE_STRING) +G_TYPE_UCHAR +G_TYPE_CHAR +XFCONF_TYPE_UINT16 +XFCONF_TYPE_INT16 +G_TYPE_UINT +G_TYPE_INT +G_TYPE_UINT64 +G_TYPE_INT64 +G_TYPE_FLOAT +G_TYPE_DOUBLE +G_TYPE_BOOLEAN + +In addition, the backend must be able to handle arrays of arbitrary +types from the above list (with the exception of G_TYPE_STRV). A single +array may hold multiple values of the same type, or of different types. +Because of this, array types are received by the backend as GValueArrays. +The GType of each value in the array must be checked, of course, as the +array elements may be of different types. + +If the backend receives a G_TYPE_STRV value, it may treat it specially +in storage, or may simply store it as an array of G_TYPE_STRING values. +However, the backend should always return it to clients as G_TYPE_STRV, +not as a G_TYPE_VALUE_ARRAY of G_TYPE_STRING. diff --git a/docs/spec/perchannel-xml.dtd b/docs/spec/perchannel-xml.dtd index c80767d..dff0013 100644 --- a/docs/spec/perchannel-xml.dtd +++ b/docs/spec/perchannel-xml.dtd @@ -1,17 +1,21 @@ <?xml version="1.0" encoding="UTF-8"?> <!ELEMENT channel property*> -<!ELEMENT property string*> <!-- <string> elems only if type="strlist" --> -<!ELEMENT string #PCDATA> +<!ELEMENT property (property|value)*> <!-- <value> elems only allowed + if type="array" --> +<!ELEMENT value EMPTY> <!ATTLIST channel name CDATA #REQUIRED> <!ATTLIST channel version CDATA #REQUIRED> <!-- currently "1.0" --> -<!ATTLIST channel locked (true|false) #IMPLIED> +<!ATTLIST channel locked CDATA #IMPLIED> <!-- only one of these --> +<!ATTLIST channel unlocked CDATA #IMPLIED> <!-- two are allowed --> <!ATTLIST property name CDATA #REQUIRED> -<!ATTLIST property type (string|strlist|int|int64|double|bool|empty) #REQUIRED> -<!ATTLIST property value CDATA #IMPLIED> <!-- not used if type="strlist" +<!ATTLIST property type (string|uchar|char|uint16|int16|uint|int|uint64|int64|float|double|bool|array|empty) #REQUIRED> +<!ATTLIST property value CDATA #IMPLIED> <!-- not used if type="array" or type="empty" --> -<!ATTLIST property locked (true|false) #IMPLIED> +<!ATTLIST property locked CDATA #IMPLIED> <!-- only one of these --> +<!ATTLIST property unlocked CDATA #IMPLIED> <!-- two are allowed --> -<!-- <string> element has no attributes --> +<!ATTLIST value type (string|uchar|char|uint16|int16|uint|int|uint64|int64|float|double|bool) #REQUIRED> +<!ATTLIST value value CDATA #REQUIRED> diff --git a/docs/spec/perchannel-xml.txt b/docs/spec/perchannel-xml.txt index b5fb0a2..0374feb 100644 --- a/docs/spec/perchannel-xml.txt +++ b/docs/spec/perchannel-xml.txt @@ -1,3 +1,5 @@ +XfconfBackendPerchannelXml file format, version 1.0 + -> High-level overview: The Per-channel XML configuration store backend is a simple @@ -11,10 +13,15 @@ <property name="allow-editing" type="bool" value="true"/> <property name="last-document" type="string" value="foo.txt"/> </property> - <property name="history" type="strlist"> - <string>foo.txt</string> - <string>bar.txt</string> - <string>baz.txt</string> + <property name="history" type="array"> + <value type="string" value="foo.txt"/> + <value type="string" value="bar.txt"/> + <value type="string" value="baz.txt"/> + </property> + <property name="random-stuff" type="array"> + <value type="int" value="345"/> + <value type="double" value="42.4"/> + <value type="string" value="cheese"/> </property> </channel> @@ -50,9 +57,10 @@ Attributes: + name(string): The name of the property (required). + type(string): The type of property. Must be one of: "string", - "strlist", "int", "int64", "double", "bool", "empty" (required). + "uchar", "char", "uint16", "int16", "uint", "int", "uint64", + "int64", "float", "double", "bool", "array", or "empty" (required). + value(string): The value of the property (required except for - type="strlist" and type="empty"). + type="array" and type="empty"). + locked(userlist): A list of users/groups who cannot modify this property (only allowed in config files in non-user-writable locations; mutually exclusive with the "unlocked" attribute; @@ -61,10 +69,13 @@ this property (only allowed in config files in non-user-writable locations; mutually exclusive with the "locked" attribute; optional, defaults to "*"). - * <string> - Only allowed inside <property> elements where type="strlist". Has - no attributes; the text between the opening and closing tags is - the property value. + * <value> + Only allowed inside <property> elements where type="array", and + indicates an array element value. + Attributes: + + type(string): The type of value. All values for type= on + <property> are supported except for "array" and "empty" (required). + + value(string): The element's value, encoded as a string (required). -> Nesting: diff --git a/gtk-doc.make b/gtk-doc.make index eeb23b4..8056cd6 100644 --- a/gtk-doc.make +++ b/gtk-doc.make @@ -53,6 +53,8 @@ docs: html-build.stamp scan-build.stamp: $(HFILE_GLOB) $(CFILE_GLOB) @echo 'gtk-doc: Scanning header files' @-chmod -R u+w $(srcdir) + cd $(srcdir) && \ + gtkdoc-scan --module=$(DOC_MODULE) --source-dir=$(DOC_SOURCE_DIR) --ignore-headers="$(IGNORE_HFILES)" $(SCAN_OPTIONS) $(EXTRA_HFILES) if grep -l '^..*$$' $(srcdir)/$(DOC_MODULE).types > /dev/null 2>&1 ; then \ CC="$(GTKDOC_CC)" LD="$(GTKDOC_LD)" CFLAGS="$(GTKDOC_CFLAGS)" LDFLAGS="$(GTKDOC_LIBS)" gtkdoc-scangobj $(SCANGOBJ_OPTIONS) --module=$(DOC_MODULE) --output-dir=$(srcdir) ; \ else \ @@ -61,11 +63,9 @@ scan-build.stamp: $(HFILE_GLOB) $(CFILE_GLOB) test -f $$i || touch $$i ; \ done \ fi - cd $(srcdir) && \ - gtkdoc-scan --module=$(DOC_MODULE) --source-dir=$(DOC_SOURCE_DIR) --ignore-headers="$(IGNORE_HFILES)" $(SCAN_OPTIONS) $(EXTRA_HFILES) touch scan-build.stamp -$(DOC_MODULE)-decl.txt $(SCANOBJ_FILES): scan-build.stamp +$(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(DOC_MODULE)-sections.txt $(DOC_MODULE)-overrides.txt: scan-build.stamp @true #### templates #### @@ -147,7 +147,7 @@ dist-hook: dist-check-gtkdoc dist-hook-local mkdir $(distdir)/html -cp $(srcdir)/tmpl/*.sgml $(distdir)/tmpl -cp $(srcdir)/xml/*.xml $(distdir)/xml - -cp $(srcdir)/html/* $(distdir)/html + cp $(srcdir)/html/* $(distdir)/html if test -f $(srcdir)/$(DOC_MODULE).types; then \ cp $(srcdir)/$(DOC_MODULE).types $(distdir)/$(DOC_MODULE).types; \ fi diff --git a/tests/t-get-properties.c b/tests/t-get-properties.c index cfe24a2..0b8ab99 100644 --- a/tests/t-get-properties.c +++ b/tests/t-get-properties.c @@ -38,8 +38,7 @@ main(int argc, { /* meh */ gchar **strlist = xfconf_channel_get_string_list(channel, - test_strlist_property, - NULL); + test_strlist_property); gint i; if(!strlist) { @@ -68,7 +67,7 @@ main(int argc, } TEST_OPERATION(xfconf_channel_get_int(channel, test_int_property, -1) == test_int); - TEST_OPERATION(xfconf_channel_get_int64(channel, test_int64_property, -1) == test_int64); + TEST_OPERATION(xfconf_channel_get_uint64(channel, test_uint64_property, -1) == test_uint64); /* FIXME: will this work everywhere? */ TEST_OPERATION(xfconf_channel_get_double(channel, test_double_property, 0.0) == test_double); TEST_OPERATION(xfconf_channel_get_bool(channel, test_bool_property, FALSE) == test_bool); diff --git a/tests/t-has-properties.c b/tests/t-has-properties.c index 12331ff..61c5580 100644 --- a/tests/t-has-properties.c +++ b/tests/t-has-properties.c @@ -33,7 +33,7 @@ main(int argc, TEST_OPERATION(xfconf_channel_has_property(channel, test_string_property)); TEST_OPERATION(xfconf_channel_has_property(channel, test_strlist_property)); TEST_OPERATION(xfconf_channel_has_property(channel, test_int_property)); - TEST_OPERATION(xfconf_channel_has_property(channel, test_int64_property)); + TEST_OPERATION(xfconf_channel_has_property(channel, test_uint64_property)); TEST_OPERATION(xfconf_channel_has_property(channel, test_double_property)); TEST_OPERATION(xfconf_channel_has_property(channel, test_bool_property)); diff --git a/tests/t-remove-properties.c b/tests/t-remove-properties.c index 194b602..1842235 100644 --- a/tests/t-remove-properties.c +++ b/tests/t-remove-properties.c @@ -42,9 +42,9 @@ main(int argc, xfconf_channel_remove_property(channel, test_int_property); TEST_OPERATION(!xfconf_channel_has_property(channel, test_int_property)); - TEST_OPERATION(xfconf_channel_has_property(channel, test_int64_property)); - xfconf_channel_remove_property(channel, test_int64_property); - TEST_OPERATION(!xfconf_channel_has_property(channel, test_int64_property)); + TEST_OPERATION(xfconf_channel_has_property(channel, test_uint64_property)); + xfconf_channel_remove_property(channel, test_uint64_property); + TEST_OPERATION(!xfconf_channel_has_property(channel, test_uint64_property)); TEST_OPERATION(xfconf_channel_has_property(channel, test_double_property)); xfconf_channel_remove_property(channel, test_double_property); diff --git a/tests/t-set-properties.c b/tests/t-set-properties.c index abf34ee..410cdc8 100644 --- a/tests/t-set-properties.c +++ b/tests/t-set-properties.c @@ -33,7 +33,7 @@ main(int argc, TEST_OPERATION(xfconf_channel_set_string(channel, test_string_property, test_string)); TEST_OPERATION(xfconf_channel_set_string_list(channel, test_strlist_property, test_strlist)); TEST_OPERATION(xfconf_channel_set_int(channel, test_int_property, test_int)); - TEST_OPERATION(xfconf_channel_set_int64(channel, test_int64_property, test_int64)); + TEST_OPERATION(xfconf_channel_set_uint64(channel, test_uint64_property, test_uint64)); TEST_OPERATION(xfconf_channel_set_double(channel, test_double_property, test_double)); TEST_OPERATION(xfconf_channel_set_bool(channel, test_bool_property, test_bool)); diff --git a/tests/tests-common.h b/tests/tests-common.h index 94959cf..06610c8 100644 --- a/tests/tests-common.h +++ b/tests/tests-common.h @@ -63,8 +63,8 @@ const gchar *test_strlist_property = "/test/stringtest/strlist"; const gchar *test_strlist[] = { "teststring1", "teststring2", NULL }; const gchar *test_int_property = "/test/inttest/int"; const gint test_int = 42; -const gchar *test_int64_property = "/test/int64test/int64"; -const gint64 test_int64 = 42000000000LL; +const gchar *test_uint64_property = "/test/uint64test/uint64"; +const guint64 test_uint64 = 42000000000LL; const gchar *test_double_property = "/test/doubletest/double"; const gdouble test_double = 42.4242; const gchar *test_bool_property = "/test/booltest/bool"; diff --git a/xfconf/Makefile.am b/xfconf/Makefile.am index 05d7fc9..6f02a4b 100644 --- a/xfconf/Makefile.am +++ b/xfconf/Makefile.am @@ -5,6 +5,7 @@ libxfconfincludedir = $(includedir)/xfce4/xfconf-$(LIBXFCONF_VERSION_API)/xfconf libxfconfinclude_HEADERS = \ xfconf-channel.h \ xfconf-errors.h \ + xfconf-types.h \ xfconf.h libxfconf_0_la_SOURCES = \ @@ -49,4 +50,3 @@ xfconf-dbus-bindings.h: $(top_srcdir)/common/xfconf-dbus.xml Makefile endif -EXTRA_DIST = xfconf-marshal.list diff --git a/xfconf/xfconf-channel.c b/xfconf/xfconf-channel.c index aa49185..71020b6 100644 --- a/xfconf/xfconf-channel.c +++ b/xfconf/xfconf-channel.c @@ -29,6 +29,9 @@ #include "xfconf-dbus-bindings.h" #include "xfconf-private.h" #include "xfconf-marshal.h" +#include "xfconf-types.h" + +#define ALIGN_VAL(val, align) ( ((val) + ((align) -1)) & ~((align) - 1) ) #ifdef XFCONF_ENABLE_CHECKS @@ -58,14 +61,14 @@ struct _XfconfChannel { GObject parent; - + gchar *channel_name; }; typedef struct _XfconfChannelClass { GObjectClass parent; - + /*< signals >*/ void (*property_changed)(XfconfChannel *channel, const gchar *property); @@ -86,14 +89,14 @@ enum static void xfconf_channel_class_init(XfconfChannelClass *klass); static void xfconf_channel_init(XfconfChannel *instance); -static void xfconf_channel_set_property(GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec); -static void xfconf_channel_get_property(GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec); +static void xfconf_channel_set_g_property(GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void xfconf_channel_get_g_property(GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); static void xfconf_channel_finalize(GObject *obj); static void xfconf_channel_property_changed(DBusGProxy *proxy, @@ -111,11 +114,11 @@ static void xfconf_channel_class_init(XfconfChannelClass *klass) { GObjectClass *object_class = (GObjectClass *)klass; - - object_class->set_property = xfconf_channel_set_property; - object_class->get_property = xfconf_channel_get_property; + + object_class->set_property = xfconf_channel_set_g_property; + object_class->get_property = xfconf_channel_get_g_property; object_class->finalize = xfconf_channel_finalize; - + /** * XfconfChannel::property-changed: * @channel: An #XfconfChannel. @@ -133,7 +136,7 @@ xfconf_channel_class_init(XfconfChannelClass *klass) g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); - + /** * XfconfChannel::channel-name: * @@ -146,7 +149,7 @@ xfconf_channel_class_init(XfconfChannelClass *klass) NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - + dbus_g_object_register_marshaller(xfconf_marshal_VOID__STRING_STRING, G_TYPE_NONE, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID); @@ -162,16 +165,16 @@ xfconf_channel_init(XfconfChannel *instance) } static void -xfconf_channel_set_property(GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) +xfconf_channel_set_g_property(GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) { switch(property_id) { case PROP_CHANNEL_NAME: XFCONF_CHANNEL(object)->channel_name = g_strdup(g_value_get_string(value)); break; - + default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; @@ -179,16 +182,16 @@ xfconf_channel_set_property(GObject *object, } static void -xfconf_channel_get_property(GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) +xfconf_channel_get_g_property(GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) { switch(property_id) { case PROP_CHANNEL_NAME: g_value_set_string(value, XFCONF_CHANNEL(object)->channel_name); break; - + default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; @@ -200,13 +203,13 @@ xfconf_channel_finalize(GObject *obj) { XfconfChannel *channel = XFCONF_CHANNEL(obj); DBusGProxy *proxy = _xfconf_get_dbus_g_proxy(); - + dbus_g_proxy_disconnect_signal(proxy, "PropertyChanged", G_CALLBACK(xfconf_channel_property_changed), channel); - + g_free(channel->channel_name); - + G_OBJECT_CLASS(xfconf_channel_parent_class)->finalize(obj); } @@ -221,10 +224,10 @@ xfconf_channel_property_changed(DBusGProxy *proxy, /* FIXME: optimise this by keeping track of all channels in a big hashtable * and using only a single instance of this callback for the class */ XfconfChannel *channel = XFCONF_CHANNEL(user_data); - + if(strcmp(channel_name, channel->channel_name)) return; - + g_signal_emit(G_OBJECT(channel), signals[SIG_PROPERTY_CHANGED], 0, property); } @@ -239,12 +242,12 @@ xfconf_channel_get_internal(XfconfChannel *channel, DBusGProxy *proxy = _xfconf_get_dbus_g_proxy(); gboolean ret; ERROR_DEFINE; - + ret = xfconf_client_get_property(proxy, channel->channel_name, property, value, ERROR); if(!ret) ERROR_CHECK; - + return ret; } @@ -283,14 +286,14 @@ xfconf_channel_has_property(XfconfChannel *channel, DBusGProxy *proxy = _xfconf_get_dbus_g_proxy(); gboolean exists = FALSE; ERROR_DEFINE; - + if(!xfconf_client_property_exists(proxy, channel->channel_name, property, &exists, ERROR)) { ERROR_CHECK; return FALSE; } - + return exists; } @@ -307,7 +310,7 @@ xfconf_channel_remove_property(XfconfChannel *channel, { DBusGProxy *proxy = _xfconf_get_dbus_g_proxy(); ERROR_DEFINE; - + if(!xfconf_client_remove_property(proxy, channel->channel_name, property, ERROR)) { @@ -333,14 +336,14 @@ xfconf_channel_get_all(XfconfChannel *channel) DBusGProxy *proxy = _xfconf_get_dbus_g_proxy(); GHashTable *properties = NULL; ERROR_DEFINE; - + if(!xfconf_client_get_all_properties(proxy, channel->channel_name, &properties, ERROR)) { ERROR_CHECK; return NULL; } - + return properties; } @@ -363,15 +366,18 @@ xfconf_channel_get_string(XfconfChannel *channel, { gchar *value = NULL; GValue val = { 0, }; - + g_return_val_if_fail(XFCONF_IS_CHANNEL(channel) && property, NULL); - + if(xfconf_channel_get_internal(channel, property, &val)) { - value = g_value_dup_string(&val); + if(G_VALUE_TYPE(&val) == G_TYPE_STRING) + value = g_value_dup_string(&val); g_value_unset(&val); - } else if(default_value) + } + + if(!value) value = g_strdup(default_value); - + return value; } @@ -379,46 +385,28 @@ xfconf_channel_get_string(XfconfChannel *channel, * xfconf_channel_get_string_list: * @channel: An #XfconfChannel. * @property: A property name. - * @default_value: A fallback value. * * Retrieves the string list value associated with @property on @channel. * * Returns: A newly-allocated string list which should be freed with - * g_strfreev() when no longer needed. If @property is not in - * @channel, a g_malloc()ed and g_strdup()ed copy of the strings in - * @default_value is returned. + * g_strfreev() when no longer needed. If @property is not in + * @channel, %NULL is returned. **/ gchar ** xfconf_channel_get_string_list(XfconfChannel *channel, - const gchar *property, - const gchar **default_value) + const gchar *property) { gchar **value = NULL; GValue val = { 0, }; - gint i = 0; - + g_return_val_if_fail(XFCONF_IS_CHANNEL(channel) && property, NULL); - + if(xfconf_channel_get_internal(channel, property, &val)) { if(G_VALUE_TYPE(&val) == G_TYPE_STRV) - return g_value_dup_boxed(&val); - else { - GPtrArray *arr = g_value_get_boxed(&val); - - value = g_new0(gchar *, arr->len + 1); - for(i = 0; i < arr->len; ++i) - value[i] = g_strdup(arr->pdata[i]); - - g_value_unset(&val); - } - } else if(default_value) { - while(default_value[i]) - ++i; - value = g_new0(gchar *, i + 1); - for(i = 0; default_value[i]; ++i) - value[i] = g_strdup(default_value[i]); + value = g_value_dup_boxed(&val); + g_value_unset(&val); } - + return value; } @@ -440,43 +428,45 @@ xfconf_channel_get_int(XfconfChannel *channel, { gint value = default_value; GValue val = { 0, }; - + g_return_val_if_fail(XFCONF_IS_CHANNEL(channel) && property, value); - + if(xfconf_channel_get_internal(channel, property, &val)) { - value = g_value_get_int(&val); + if(G_VALUE_TYPE(&val) == G_TYPE_INT) + value = g_value_get_int(&val); g_value_unset(&val); } - + return value; } /** - * xfconf_channel_get_int64: + * xfconf_channel_get_uint64: * @channel: An #XfconfChannel. * @property: A property name. * @default_value: A fallback value. * * Retrieves the 64-bit int value associated with @property on @channel. * - * Returns: The int64 value, or, if @property is not in @channel, + * Returns: The uint64 value, or, if @property is not in @channel, * @default_value is returned. **/ -gint64 -xfconf_channel_get_int64(XfconfChannel *channel, - const gchar *property, - gint64 default_value) +guint64 +xfconf_channel_get_uint64(XfconfChannel *channel, + const gchar *property, + guint64 default_value) { gint64 value = default_value; GValue val = { 0, }; - + g_return_val_if_fail(XFCONF_IS_CHANNEL(channel) && property, value); - + if(xfconf_channel_get_internal(channel, property, &val)) { - value = g_value_get_int64(&val); + if(G_VALUE_TYPE(&val) == G_TYPE_UINT64) + value = g_value_get_uint64(&val); g_value_unset(&val); } - + return value; } @@ -498,14 +488,15 @@ xfconf_channel_get_double(XfconfChannel *channel, { gdouble value = default_value; GValue val = { 0, }; - + g_return_val_if_fail(XFCONF_IS_CHANNEL(channel) && property, value); - + if(xfconf_channel_get_internal(channel, property, &val)) { - value = g_value_get_double(&val); + if(G_VALUE_TYPE(&val) == G_TYPE_DOUBLE) + value = g_value_get_double(&val); g_value_unset(&val); } - + return value; } @@ -527,14 +518,15 @@ xfconf_channel_get_bool(XfconfChannel *channel, { gboolean value = default_value; GValue val = { 0, }; - + g_return_val_if_fail(XFCONF_IS_CHANNEL(channel) && property, value); - + if(xfconf_channel_get_internal(channel, property, &val)) { - value = g_value_get_boolean(&val); + if(G_VALUE_TYPE(&val) == G_TYPE_BOOLEAN) + value = g_value_get_boolean(&val); g_value_unset(&val); } - + return value; } @@ -557,19 +549,19 @@ xfconf_channel_set_string(XfconfChannel *channel, GValue val = { 0, }; gboolean ret; ERROR_DEFINE; - + g_return_val_if_fail(XFCONF_IS_CHANNEL(channel) && property && value, FALSE); - + g_value_init(&val, G_TYPE_STRING); g_value_set_string(&val, value); - + ret = xfconf_client_set_property(proxy, channel->channel_name, property, &val, ERROR); if(!ret) ERROR_CHECK; - + g_value_unset(&val); - + return ret; } @@ -577,44 +569,35 @@ xfconf_channel_set_string(XfconfChannel *channel, * xfconf_channel_set_string_list: * @channel: An #XfconfChannel. * @property: A property name. - * @value: The value to set. + * @values: The value to set. * - * Sets @value for @property on @channel in the configuration store. + * Sets @values for @property on @channel in the configuration store. * * Returns: %TRUE on success, %FALSE if an error occured. **/ gboolean xfconf_channel_set_string_list(XfconfChannel *channel, const gchar *property, - const gchar **value) + const gchar **values) { DBusGProxy *proxy = _xfconf_get_dbus_g_proxy(); - GPtrArray *arr; GValue val = { 0, }; gboolean ret; - gint i, count = 0; ERROR_DEFINE; - - g_return_val_if_fail(XFCONF_IS_CHANNEL(channel) && property && value, FALSE); - - while(value[count]) - ++count; - - arr = g_ptr_array_sized_new(count); - for(i = 0; i < count; ++i) - g_ptr_array_add(arr, (gpointer)value[i]); - - g_value_init(&val, dbus_g_type_get_collection("GPtrArray", G_TYPE_STRING)); - g_value_set_boxed(&val, arr); - + + g_return_val_if_fail(XFCONF_IS_CHANNEL(channel) && property && values, + FALSE); + + g_value_init(&val, G_TYPE_STRV); + g_value_set_boxed(&val, values); + ret = xfconf_client_set_property(proxy, channel->channel_name, property, &val, ERROR); if(!ret) ERROR_CHECK; - + g_value_unset(&val); - g_ptr_array_free(arr, TRUE); - + return ret; } @@ -637,24 +620,24 @@ xfconf_channel_set_int(XfconfChannel *channel, GValue val = { 0, }; gboolean ret; ERROR_DEFINE; - + g_return_val_if_fail(XFCONF_IS_CHANNEL(channel) && property, FALSE); - + g_value_init(&val, G_TYPE_INT); g_value_set_int(&val, value); - + ret = xfconf_client_set_property(proxy, channel->channel_name, property, &val, ERROR); if(!ret) ERROR_CHECK; - + g_value_unset(&val); - + return ret; } /** - * xfconf_channel_set_int64: + * xfconf_channel_set_uint64: * @channel: An #XfconfChannel. * @property: A property name. * @value: The value to set. @@ -664,27 +647,27 @@ xfconf_channel_set_int(XfconfChannel *channel, * Returns: %TRUE on success, %FALSE if an error occured. **/ gboolean -xfconf_channel_set_int64(XfconfChannel *channel, - const gchar *property, - gint64 value) +xfconf_channel_set_uint64(XfconfChannel *channel, + const gchar *property, + guint64 value) { DBusGProxy *proxy = _xfconf_get_dbus_g_proxy(); GValue val = { 0, }; gboolean ret; ERROR_DEFINE; - + g_return_val_if_fail(XFCONF_IS_CHANNEL(channel) && property, FALSE); - - g_value_init(&val, G_TYPE_INT64); - g_value_set_int64(&val, value); - + + g_value_init(&val, G_TYPE_UINT64); + g_value_set_uint64(&val, value); + ret = xfconf_client_set_property(proxy, channel->channel_name, property, &val, ERROR); if(!ret) ERROR_CHECK; - + g_value_unset(&val); - + return ret; } @@ -707,19 +690,19 @@ xfconf_channel_set_double(XfconfChannel *channel, GValue val = { 0, }; gboolean ret; ERROR_DEFINE; - + g_return_val_if_fail(XFCONF_IS_CHANNEL(channel) && property, FALSE); - + g_value_init(&val, G_TYPE_DOUBLE); g_value_set_double(&val, value); - + ret = xfconf_client_set_property(proxy, channel->channel_name, property, &val, ERROR); if(!ret) ERROR_CHECK; - + g_value_unset(&val); - + return ret; } @@ -742,18 +725,962 @@ xfconf_channel_set_bool(XfconfChannel *channel, GValue val = { 0, }; gboolean ret; ERROR_DEFINE; - + g_return_val_if_fail(XFCONF_IS_CHANNEL(channel) && property, FALSE); - + g_value_init(&val, G_TYPE_BOOLEAN); g_value_set_boolean(&val, value); - + + ret = xfconf_client_set_property(proxy, channel->channel_name, property, + &val, ERROR); + if(!ret) + ERROR_CHECK; + + g_value_unset(&val); + + return ret; +} + +/** + * xfconf_channel_get_property: + * @channel: An #XfconfChannel. + * @property: A string property name. + * @value: A #GValue. + * + * Gets a property on @channel and stores it in @value. The caller is + * responsible for calling g_value_unset() when finished with @value. + * + * Returns: %TRUE if the property was retrieved successfully, + * %FALSE otherwise. + **/ +gboolean +xfconf_channel_get_property(XfconfChannel *channel, + const gchar *property, + GValue *value) +{ + GValue val1; + gboolean ret; + + g_return_val_if_fail(XFCONF_IS_CHANNEL(channel) && property && value, + FALSE); + + ret = xfconf_channel_get_internal(channel, property, &val1); + + if(ret) { + g_value_copy(&val1, g_value_init(value, G_VALUE_TYPE(&val1))); + g_value_unset(&val1); + return TRUE; + } + + return FALSE; +} + +/** + * xfconf_channel_set_property: + * @channel: An #XfconfChannel. + * @property: A string property name. + * @value: A #GValue. + * + * Sets the value stored in @value to a property on @channel. + * + * Note: The configuration store backend almost certainly supports + * only a restricted set of value types. + * + * Returns: %TRUE if the property was set successfully, %FALSE otherwise. + **/ +gboolean +xfconf_channel_set_property(XfconfChannel *channel, + const gchar *property, + const GValue *value) +{ + DBusGProxy *proxy = _xfconf_get_dbus_g_proxy(); + gboolean ret; + ERROR_DEFINE; + + g_return_val_if_fail(XFCONF_IS_CHANNEL(channel) && property && value, + FALSE); + + ret = xfconf_client_set_property(proxy, channel->channel_name, property, + value, ERROR); + if(!ret) + ERROR_CHECK; + + return ret; +} + +/** + * xfconf_channel_get_array: + * @channel: An #XfconfChannel. + * @property: A property string. + * @first_value_type: The type of the first argument in the array. + * @...: A variable argument list of types and values. + * + * Gets an array property on @channel. The @first_value_type + * argument specifies the type of the first value in the variable + * argument list. The variable argument list should alternate between + * pointers to locations to store the values, and the GType of the + * next value. The argument list should be terminated with + * G_TYPE_INVALID. + * + * Note: The configuration store backend almost certainly supports + * only a restricted set of value types. + * + * Returns: %TRUE if the property was retrieved successfully, + * %FALSE otherwise. + **/ +gboolean +xfconf_channel_get_array(XfconfChannel *channel, + const gchar *property, + GType first_value_type, + ...) +{ + va_list var_args; + gboolean ret; + + va_start(var_args, first_value_type); + ret = xfconf_channel_get_array_valist(channel, property, first_value_type, + var_args); + va_end(var_args); + + return ret; +} + +/** + * xfconf_channel_get_array_valist: + * @channel: An #XfconfChannel. + * @property: A property string. + * @first_value_type: The type of the first argument in the array. + * @var_args: A variable argument list of types and values. + * + * Gets an array property on @channel. See xfconf_channel_get_array() + * for details. + * + * Returns: %TRUE if the property was retrieved successfully, + * %FALSE otherwise. + **/ +gboolean +xfconf_channel_get_array_valist(XfconfChannel *channel, + const gchar *property, + GType first_value_type, + va_list var_args) +{ + gboolean ret = FALSE; + GValueArray *valarray = NULL; + GType cur_value_type; + GValue *value; + gint i; + + valarray = xfconf_channel_get_arrayv(channel, property); + if(!valarray) + return FALSE; + + for(cur_value_type = first_value_type, i = 0; + cur_value_type != G_TYPE_INVALID; + cur_value_type = va_arg(var_args, GType), ++i) + { + if(i > valarray->n_values - 1) { +#ifdef XFCONF_ENABLE_CHECKS + g_warning("Too many parameters passed, or config store doesn't " \ + "have enough elements in array (it only provided %d).", + valarray->n_values); +#endif + goto out; + } + + value = g_value_array_get_nth(valarray, i); + + if(G_VALUE_TYPE(value) != cur_value_type) { +#ifdef XFCONF_ENABLE_CHECKS + g_warning("Value types don't match (%d != %d) at parameter %d", + (int)G_VALUE_TYPE(value), (int)cur_value_type, i); +#endif + goto out; + } + +#define HANDLE_CASE(ctype, GTYPE, valtype) \ + case G_TYPE_ ## GTYPE: { \ + ctype *__val_p = va_arg(var_args, ctype *); \ + *__val_p = g_value_get_ ## valtype(value); \ + break; \ + } + + switch(cur_value_type) { + HANDLE_CASE(guchar, UCHAR, uchar) + HANDLE_CASE(gchar, CHAR, char) + HANDLE_CASE(guint32, UINT, uint) + HANDLE_CASE(gint32, INT, int) + HANDLE_CASE(guint64, UINT64, uint64) + HANDLE_CASE(gint64, INT64, int64) + HANDLE_CASE(gfloat, FLOAT, float) + HANDLE_CASE(gdouble, DOUBLE, double) + HANDLE_CASE(gboolean, BOOLEAN, boolean) +#undef HANDLE_CASE + + case G_TYPE_STRING: { + gchar **__val_p = va_arg(var_args, gchar **); + *__val_p = g_value_dup_string(value); + break; + } + + default: + if(XFCONF_TYPE_UINT16 == cur_value_type) { + guint16 *__val_p = va_arg(var_args, guint16 *); + *__val_p = xfconf_g_value_get_uint16(value); + } else if(XFCONF_TYPE_INT16 == cur_value_type) { + gint16 *__val_p = va_arg(var_args, gint16 *); + *__val_p = xfconf_g_value_get_int16(value); + } else if(G_TYPE_STRV == cur_value_type) { + gchar ***__val_p = va_arg(var_args, gchar ***); + *__val_p = g_value_dup_boxed(value); + } else { + g_warning("Unknown value type %d (%s) in value array.", + G_VALUE_TYPE(value), G_VALUE_TYPE_NAME(value)); + goto out; + } + break; + } + } + + if(i < valarray->n_values) { +#ifdef XFCONF_ENABLE_CHECKS + g_warning("Too few parameters passed, or config store has too " \ + "many elements in array (it provided %d).", + valarray->n_values); +#endif + goto out; + } + + ret = TRUE; + +out: + g_value_array_free(valarray); + + return ret; +} + +/** + * xfconf_channel_get_arrayv: + * @channel: An #XfconfChannel. + * @property: A property string. + * + * Gets an array property on @channel and returns it as + * a #GValueArray. + * + * Returns: A newly-allocated #GValueArray on success, or %NULL + * on failure. + **/ +GValueArray * +xfconf_channel_get_arrayv(XfconfChannel *channel, + const gchar *property) +{ + GValue val = { 0, }; + GValueArray *valarray = NULL; + + g_return_val_if_fail(XFCONF_IS_CHANNEL(channel) && property, NULL); + + if(!xfconf_channel_get_internal(channel, property, &val)) + return NULL; + + if(G_VALUE_TYPE(&val) != G_TYPE_VALUE_ARRAY) { +#ifdef XFCONF_ENABLE_CHECKS + g_warning("Type returned for xfconf_channel_get_arrayv() was not G_TYPE_VALUE_ARRAY"); +#endif + g_value_unset(&val); + return NULL; + } + + valarray = g_value_dup_boxed(&val); /* the copy here sucks */ + g_value_unset(&val); + + return valarray; +} + +/** + * xfconf_channel_set_array: + * @channel: An #XfconfChannel. + * @property: A property string. + * @first_value_type: The type of the first argument in the array. + * @...: A variable argument list of types and values. + * + * Sets an array property on @channel. The @first_value_type + * argument specifies the type of the first value in the variable + * argument list. Note that all values specified MUST be pointers + * to variables holding the correct value, and may not be, e.g., + * numeric constants. The argument list should be terminated with + * G_TYPE_INVALID. + * + * Note: The configuration store backend almost certainly supports + * only a restricted set of value types. + * + * Returns: %TRUE if the property was set successfully, %FALSE otherwise. + **/ +gboolean +xfconf_channel_set_array(XfconfChannel *channel, + const gchar *property, + GType first_value_type, + ...) +{ + gboolean ret; + va_list var_args; + + va_start(var_args, first_value_type); + ret = xfconf_channel_set_array_valist(channel, property, first_value_type, + var_args); + va_end(var_args); + + return ret; +} + +/** + * xfconf_channel_set_array_valist: + * @channel: An #XfconfChannel. + * @property: A property string. + * @first_value_type: The type of the first argument in the array. + * @var_args: A variable argument list of types and values. + * + * Sets an array property on @channel. See xfconf_channel_set_array() + * for details. + * + * Returns: %TRUE if the property was set successfully, %FALSE otherwise. + **/ +gboolean +xfconf_channel_set_array_valist(XfconfChannel *channel, + const gchar *property, + GType first_value_type, + va_list var_args) +{ + GValueArray *valarray; + GType cur_value_type; + GValue val = { 0, }; + gboolean ret = FALSE; + + g_return_val_if_fail(XFCONF_IS_CHANNEL(channel) && property + && G_TYPE_INVALID != first_value_type, FALSE); + + valarray = g_value_array_new(5); /* FIXME: arbitrary number... */ + + for(cur_value_type = first_value_type; + cur_value_type != G_TYPE_INVALID; + cur_value_type = va_arg(var_args, GType)) + { +#define HANDLE_CASE(ctype, GTYPE, valtype) \ + case G_TYPE_ ## GTYPE: { \ + ctype *__val = va_arg(var_args, ctype *); \ + g_value_init(&val, G_TYPE_ ## GTYPE); \ + g_value_set_ ## valtype(&val, *__val); \ + g_value_array_append(valarray, &val); \ + break; \ + } + + switch(cur_value_type) { + HANDLE_CASE(guchar, UCHAR, uchar) + HANDLE_CASE(gchar, CHAR, char) + HANDLE_CASE(guint32, UINT, uint) + HANDLE_CASE(gint32, INT, int) + HANDLE_CASE(guint64, UINT64, uint64) + HANDLE_CASE(gint64, INT64, int64) + HANDLE_CASE(gfloat, FLOAT, float) + HANDLE_CASE(gdouble, DOUBLE, double) + HANDLE_CASE(gboolean, BOOLEAN, boolean) + HANDLE_CASE(gchar *, STRING, string) +#undef HANDLE_CASE + + default: + if(XFCONF_TYPE_UINT16 == cur_value_type) { + guint16 *__val = va_arg(var_args, guint16 *); + g_value_init(&val, XFCONF_TYPE_UINT16); + xfconf_g_value_set_uint16(&val, *__val); + g_value_array_append(valarray, &val); + } else if(XFCONF_TYPE_INT16 == cur_value_type) { + gint16 *__val = va_arg(var_args, gint16 *); + g_value_init(&val, XFCONF_TYPE_INT16); + xfconf_g_value_set_int16(&val, *__val); + g_value_array_append(valarray, &val); + } else if(G_TYPE_STRV == cur_value_type) { + gchar **__val = va_arg(var_args, gchar **); + g_value_init(&val, G_TYPE_STRV); + g_value_set_boxed(&val, __val); + g_value_array_append(valarray, &val); + } else { + g_warning("Unknown value type %d (%s) in parameter list.", + G_VALUE_TYPE(&val), G_VALUE_TYPE_NAME(&val)); + goto out; + } + break; + } + + g_value_unset(&val); + } + + ret = xfconf_channel_set_arrayv(channel, property, valarray); + +out: + g_value_array_free(valarray); + + return ret; +} + +/** + * xfconf_channel_set_arrayv: + * @channel: An #XfconfChannel. + * @property: A property string. + * @values: A #GValueArray of values. + * + * Sets an array property on @channel, using the values in the + * provided @values array. + * + * Returns: %TRUE if the property was set successfully, %FALSE otherwise. + **/ +gboolean +xfconf_channel_set_arrayv(XfconfChannel *channel, + const gchar *property, + GValueArray *values) +{ + DBusGProxy *proxy = _xfconf_get_dbus_g_proxy(); + GValue val = { 0, }; + gboolean ret; + ERROR_DEFINE; + + g_return_val_if_fail(XFCONF_IS_CHANNEL(channel) && property && values, + FALSE); + + g_value_init(&val, G_TYPE_VALUE_ARRAY); + g_value_set_boxed(&val, values); + ret = xfconf_client_set_property(proxy, channel->channel_name, property, &val, ERROR); + if(!ret) ERROR_CHECK; - + g_value_unset(&val); - + + return ret; +} + +/** + * xfconf_channel_get_named_struct: + * @channel: An #XfconfChannel. + * @property: A string property name. + * @struct_name: A string struct name. + * @value_struct: A pointer to a struct. + * + * Gets a property from @channel and fills in @value_struct using + * the retrieved values. The @struct_name parameter is the same + * name that must have been used to register the struct's layout + * with xfconf_named_struct_register(). + * + * Returns: %TRUE if the property was retrieved successfully, + * %FALSE otherwise. + **/ +gboolean +xfconf_channel_get_named_struct(XfconfChannel *channel, + const gchar *property, + const gchar *struct_name, + gpointer value_struct) +{ + XfconfNamedStruct *ns = _xfconf_named_struct_lookup(struct_name); + + if(!ns) + return FALSE; + + return xfconf_channel_get_structv(channel, property, value_struct, + ns->n_members, ns->member_types); +} + +/** + * xfconf_channel_set_named_struct: + * @channel: An #XfconfChannel. + * @property: A string property name. + * @struct_name: A string struct name. + * @value_struct: A pointer to a struct. + * + * Sets a property on @channel using the members of @value_struct + * as the array of values. The @struct_name parameter is the same + * name that must have been used to register the struct's layout + * with xfconf_named_struct_register(). + * + * Returns: %TRUE if the property was set successfully, + * %FALSE otherwise. + **/ +gboolean +xfconf_channel_set_named_struct(XfconfChannel *channel, + const gchar *property, + const gchar *struct_name, + gpointer value_struct) +{ + XfconfNamedStruct *ns = _xfconf_named_struct_lookup(struct_name); + + if(!ns) + return FALSE; + + return xfconf_channel_set_structv(channel, property, value_struct, + ns->n_members, ns->member_types); +} + + +/** + * xfconf_channel_get_struct: + * @channel: An #XfconfChannel. + * @property: A string property name. + * @value_struct: A pointer to a struct in which to store values. + * @first_member_type: The GType of the first member of @value_struct. + * @...: A variable argument list of #GType<!-- -->s. + * + * Gets a property on @channel and stores it as members of the + * @value_struct struct. The @first_member_type argument + * specifies the #GType of the first member of the struct. The + * variable argument list specifies the #GType<!-- -->s of the + * rest of the struct members, and should be terminated with + * G_TYPE_INVALID. + * + * Note: This function takes your compiler's and platform's + * struct member alignment rules into account when storing values + * in @value_struct. Therefore, it cannot be used with structs that + * are declared as "packed" in such a way that the alignment rules + * are ignored by the compiler. + * + * Note: Struct members can only be non-pointer types such as int, + * boolean, double, etc. + * + * Returns: %TRUE if the property was retrieved successfully, + * %FALSE oherwise. + **/ +gboolean +xfconf_channel_get_struct(XfconfChannel *channel, + const gchar *property, + gpointer value_struct, + GType first_member_type, + ...) +{ + gboolean ret; + va_list var_args; + + va_start(var_args, first_member_type); + ret = xfconf_channel_get_struct_valist(channel, property, value_struct, + first_member_type, var_args); + va_end(var_args); + + return ret; +} + +/** + * xfconf_channel_get_struct_valist: + * @channel: An #XfconfChannel. + * @property: A string property name. + * @value_struct: A pointer to a struct in which to store values. + * @first_member_type: The GType of the first member of @value_struct. + * @var_args: A variable argument list of #GType<!-- -->s. + * + * Gets a property on @channel and stores it as members of the + * @value_struct struct. See xfconf_channel_get_struct() for details. + * + * Note: Struct members can only be non-pointer types such as int, + * boolean, double, etc. + * + * Returns: %TRUE if the property was retrieved successfully, + * %FALSE oherwise. + **/ +gboolean +xfconf_channel_get_struct_valist(XfconfChannel *channel, + const gchar *property, + gpointer value_struct, + GType first_member_type, + va_list var_args) +{ + GType cur_member_type; + GType *member_types; + gint n_members; + gsize cur_size = 5; /* FIXME: arbitrary... */ + gboolean ret; + + g_return_val_if_fail(XFCONF_IS_CHANNEL(channel) && property && value_struct + && G_TYPE_INVALID != first_member_type, FALSE); + + member_types = g_malloc(sizeof(GType) * cur_size); + + for(cur_member_type = first_member_type, n_members = 0; + cur_member_type != G_TYPE_INVALID; + cur_member_type = va_arg(var_args, GType), ++n_members) + { + if(n_members == cur_size) { + cur_size += 5; + member_types = g_realloc(member_types, sizeof(GType) * cur_size); + } + + member_types[n_members] = cur_member_type; + } + + ret = xfconf_channel_get_structv(channel, property, value_struct, + n_members, member_types); + g_free(member_types); + + return ret; +} + +/** + * xfconf_channel_get_structv: + * @channel: An #XfconfChannel. + * @property: A string property name. + * @value_struct: A pointer to a struct in which to store values. + * @n_members: The number of data members in the struct. + * @member_types: An array of @n_members #GType<!-- -->s. + * + * Gets a property on @channel and stores it as members of the + * @value_struct struct. The @member_types array should hold + * a #GType for each member of the struct. + * + * Note: Struct members can only be non-pointer types such as int, + * boolean, double, etc. + * + * Returns: %TRUE if the property was retrieved successfully, + * %FALSE oherwise. + **/ +gboolean +xfconf_channel_get_structv(XfconfChannel *channel, + const gchar *property, + gpointer value_struct, + guint n_members, + GType *member_types) +{ + GValueArray *valarray; + guint i; + GValue *value; + gboolean ret = FALSE; + gsize cur_offset = 0; + + g_return_val_if_fail(XFCONF_IS_CHANNEL(channel) && property && value_struct + && n_members && member_types, FALSE); + + valarray = xfconf_channel_get_arrayv(channel, property); + if(!valarray) + return FALSE; + + if(valarray->n_values != n_members) { +#ifdef XFCONF_ENABLE_CHECKS + g_warning("Returned value array does not match the number of struct " \ + "members (%d != %d)", valarray->n_values, n_members); +#endif + goto out; + } + + for(i = 0; i < n_members; ++i) { + typedef struct { guchar a; } DummyStruct; +#ifdef XFCONF_ENABLE_CHECKS +#define CHECK_VALUE_TYPES(value, GTYPE) G_STMT_START{ \ + if(G_VALUE_TYPE((value)) != (GTYPE)) { \ + g_warning("Returned value type does not match specified struct member type"); \ + goto out; \ + } \ +}G_STMT_END +#else +#define CHECK_VALUE_TYPES(value, GTYPE) G_STMT_START{ \ + if(G_VALUE_TYPE((value)) != (GTYPE)) \ + goto out; \ +}G_STMT_END +#endif + +#define SET_STRUCT_VAL(ctype, GTYPE, alignment, cvalgetter) G_STMT_START{ \ + ctype *__val_p; \ + value = g_value_array_get_nth(valarray, i); \ + CHECK_VALUE_TYPES(value, GTYPE); \ + cur_offset = ALIGN_VAL(cur_offset, alignment); \ + __val_p = (ctype *)(((guchar *)(&(((DummyStruct *)value_struct)->a)))+cur_offset); \ + *__val_p = cvalgetter(value); \ + cur_offset += sizeof(ctype); \ +}G_STMT_END + + switch(member_types[i]) { + case G_TYPE_STRING: + SET_STRUCT_VAL(gchar *, G_TYPE_STRING, ALIGNOF_GPOINTER, + g_value_dup_string); + break; + + case G_TYPE_UCHAR: + SET_STRUCT_VAL(guchar, G_TYPE_UCHAR, ALIGNOF_GUCHAR, + g_value_get_uchar); + break; + + case G_TYPE_CHAR: + SET_STRUCT_VAL(gchar, G_TYPE_CHAR, ALIGNOF_GCHAR, + g_value_get_char); + break; + + case G_TYPE_UINT: + SET_STRUCT_VAL(guint32, G_TYPE_UINT, ALIGNOF_GUINT32, + g_value_get_uint); + break; + + case G_TYPE_INT: + SET_STRUCT_VAL(gint32, G_TYPE_INT, ALIGNOF_GINT32, + g_value_get_int); + break; + + case G_TYPE_UINT64: + SET_STRUCT_VAL(guint64, G_TYPE_UINT64, ALIGNOF_GUINT64, + g_value_get_uint64); + break; + + case G_TYPE_INT64: + SET_STRUCT_VAL(gint64, G_TYPE_INT64, ALIGNOF_GINT64, + g_value_get_int64); + break; + + case G_TYPE_FLOAT: + SET_STRUCT_VAL(gfloat, G_TYPE_FLOAT, ALIGNOF_GFLOAT, + g_value_get_float); + break; + + case G_TYPE_DOUBLE: + SET_STRUCT_VAL(gdouble, G_TYPE_DOUBLE, ALIGNOF_GDOUBLE, + g_value_get_double); + break; + + case G_TYPE_BOOLEAN: + SET_STRUCT_VAL(gboolean, G_TYPE_BOOLEAN, ALIGNOF_GBOOLEAN, + g_value_get_boolean); + break; + + default: + if(XFCONF_TYPE_UINT16 == member_types[i]) { + SET_STRUCT_VAL(guint16, XFCONF_TYPE_UINT16, + ALIGNOF_GUINT16, xfconf_g_value_get_uint16); + } else if(XFCONF_TYPE_INT16 == member_types[i]) { + SET_STRUCT_VAL(gint16, XFCONF_TYPE_INT16, + ALIGNOF_GINT16, xfconf_g_value_get_int16); + } else { +#ifdef XFCONF_ENABLE_CHECKS + g_warning("Unable to handle value type %d (%s) when " \ + "setting a struct value", member_types[i], + g_type_name(member_types[i])); +#endif + goto out; + } + break; + } + } + + ret = TRUE; + +out: + g_value_array_free(valarray); + + return ret; +} + +/** + * xfconf_channel_set_struct: + * @channel: An #XfconfChannel. + * @property: A string property name. + * @value_struct: A pointer to a struct from which to take values. + * @first_member_type: The GType of the first member of @value_struct. + * @...: A variable argument list of #GType<!-- -->s. + * + * Sets a property on @channel using the members of @value_struct + * as a value array. The @first_member_type argument specifies + * the #GType of the first member of the struct. The variable + * argument list specifies the #GType<!-- -->s of the rest of the + * struct members, and should be terminated with G_TYPE_INVALID. + * + * Note: This function takes your compiler's and platform's + * struct member alignment rules into account when taking values + * in @value_struct. Therefore, it cannot be used with structs that + * are declared as "packed" such that the alignment rules are ignored + * by the compiler. + * + * Returns: %TRUE if the property was set successfully, + * %FALSE oherwise. + **/ +gboolean +xfconf_channel_set_struct(XfconfChannel *channel, + const gchar *property, + const gpointer value_struct, + GType first_member_type, + ...) +{ + gboolean ret; + va_list var_args; + + va_start(var_args, first_member_type); + ret = xfconf_channel_set_struct_valist(channel, property, value_struct, + first_member_type, var_args); + va_end(var_args); + + return ret; +} + +/** + * xfconf_channel_set_struct_valist: + * @channel: An #XfconfChannel. + * @property: A string property name. + * @value_struct: A pointer to a struct from which to take values. + * @first_member_type: The GType of the first member of @value_struct. + * @var_args: A variable argument list of #GType<!-- -->s. + * + * Sets a property on @channel using the members of @value_struct + * as a value array. See xfconf_channel_set_struct() for details. + * + * Returns: %TRUE if the property was set successfully, + * %FALSE oherwise. + **/ +gboolean +xfconf_channel_set_struct_valist(XfconfChannel *channel, + const gchar *property, + const gpointer value_struct, + GType first_member_type, + va_list var_args) +{ + GType cur_member_type; + GType *member_types; + gint n_members; + gsize cur_size = 5; /* FIXME: arbitrary... */ + gboolean ret; + + g_return_val_if_fail(XFCONF_IS_CHANNEL(channel) && property && value_struct + && G_TYPE_INVALID != first_member_type, FALSE); + + member_types = g_malloc(sizeof(GType) * cur_size); + + for(cur_member_type = first_member_type, n_members = 0; + cur_member_type != G_TYPE_INVALID; + cur_member_type = va_arg(var_args, GType), ++n_members) + { + if(n_members == cur_size) { + cur_size += 5; + member_types = g_realloc(member_types, sizeof(GType) * cur_size); + } + + member_types[n_members] = cur_member_type; + } + + ret = xfconf_channel_set_structv(channel, property, value_struct, + n_members, member_types); + g_free(member_types); + + return ret; +} + +/** + * xfconf_channel_set_structv: + * @channel: An #XfconfChannel. + * @property: A string property name. + * @value_struct: A pointer to a struct from which to take values. + * @n_members: The number of data members in the struct. + * @member_types: An array of @n_members #GType<!-- -->s. + * + * Sets a property on @channel using the members of @value_struct + * as a value array. The @member_types array should hold a #GType + * for each member of the struct. + * + * Returns: %TRUE if the property was set successfully, + * %FALSE oherwise. + **/ +gboolean +xfconf_channel_set_structv(XfconfChannel *channel, + const gchar *property, + const gpointer value_struct, + guint n_members, + GType *member_types) +{ + GValueArray *valarray; + guint i; + GValue val = { 0, }; + gboolean ret = FALSE; + gsize cur_offset = 0; + + g_return_val_if_fail(XFCONF_IS_CHANNEL(channel) && property && value_struct + && n_members && member_types, FALSE); + + valarray = g_value_array_new(n_members); + + for(i = 0; i < n_members; ++i) { + typedef struct { guchar a; } DummyStruct; + +#define GET_STRUCT_VAL(ctype, GTYPE, alignment, cvalsetter) G_STMT_START{ \ + ctype *__val_p; \ + g_value_init(&val, GTYPE); \ + cur_offset = ALIGN_VAL(cur_offset, alignment); \ + __val_p = (ctype *)(((guchar *)(&(((DummyStruct *)value_struct)->a)))+cur_offset); \ + cvalsetter(&val, *__val_p); \ + g_value_array_append(valarray, &val); \ + g_value_unset(&val); \ + cur_offset += sizeof(ctype); \ +}G_STMT_END + + switch(member_types[i]) { + case G_TYPE_STRING: + GET_STRUCT_VAL(gchar *, G_TYPE_STRING, ALIGNOF_GPOINTER, + g_value_set_string); + break; + + case G_TYPE_UCHAR: + GET_STRUCT_VAL(guchar, G_TYPE_UCHAR, ALIGNOF_GUCHAR, + g_value_set_uchar); + break; + + case G_TYPE_CHAR: + GET_STRUCT_VAL(gchar, G_TYPE_CHAR, ALIGNOF_GCHAR, + g_value_set_char); + break; + + case G_TYPE_UINT: + GET_STRUCT_VAL(guint32, G_TYPE_UINT, ALIGNOF_GUINT32, + g_value_set_uint); + break; + + case G_TYPE_INT: + GET_STRUCT_VAL(gint32, G_TYPE_INT, ALIGNOF_GINT32, + g_value_set_int); + break; + + case G_TYPE_UINT64: + GET_STRUCT_VAL(guint64, G_TYPE_UINT64, ALIGNOF_GUINT64, + g_value_set_uint64); + break; + + case G_TYPE_INT64: + GET_STRUCT_VAL(gint64, G_TYPE_INT64, ALIGNOF_GINT64, + g_value_set_int64); + break; + + case G_TYPE_FLOAT: + GET_STRUCT_VAL(gfloat, G_TYPE_FLOAT, ALIGNOF_GFLOAT, + g_value_set_float); + break; + + case G_TYPE_DOUBLE: + GET_STRUCT_VAL(gdouble, G_TYPE_DOUBLE, ALIGNOF_GDOUBLE, + g_value_set_double); + break; + + case G_TYPE_BOOLEAN: + GET_STRUCT_VAL(gboolean, G_TYPE_BOOLEAN, ALIGNOF_GBOOLEAN, + g_value_set_boolean); + break; + + default: + if(XFCONF_TYPE_UINT16 == member_types[i]) { + GET_STRUCT_VAL(guint16, XFCONF_TYPE_UINT16, + ALIGNOF_GUINT16, xfconf_g_value_set_uint16); + } else if(XFCONF_TYPE_INT16 == member_types[i]) { + GET_STRUCT_VAL(gint16, XFCONF_TYPE_INT16, + ALIGNOF_GINT16, xfconf_g_value_set_int16); + } else { +#ifdef XFCONF_ENABLE_CHECKS + g_warning("Unable to handle value type %d (%s) when " \ + "getting a struct value", member_types[i], + g_type_name(member_types[i])); +#endif + goto out; + } + break; + } + } + + ret = xfconf_channel_set_arrayv(channel, property, valarray); + +out: + g_value_array_free(valarray); + return ret; } diff --git a/xfconf/xfconf-channel.h b/xfconf/xfconf-channel.h index 0e8191b..6e51466 100644 --- a/xfconf/xfconf-channel.h +++ b/xfconf/xfconf-channel.h @@ -49,44 +49,140 @@ void xfconf_channel_remove_property(XfconfChannel *channel, GHashTable *xfconf_channel_get_all(XfconfChannel *channel); +/* basic types */ + gchar *xfconf_channel_get_string(XfconfChannel *channel, const gchar *property, const gchar *default_value); -gchar **xfconf_channel_get_string_list(XfconfChannel *channel, - const gchar *property, - const gchar **default_value); -gint xfconf_channel_get_int(XfconfChannel *channel, - const gchar *property, - gint default_value); -gint64 xfconf_channel_get_int64(XfconfChannel *channel, - const gchar *property, - gint64 default_value); -gdouble xfconf_channel_get_double(XfconfChannel *channel, - const gchar *property, - gdouble default_value); -gboolean xfconf_channel_get_bool(XfconfChannel *channel, - const gchar *property, - gboolean default_value); - gboolean xfconf_channel_set_string(XfconfChannel *channel, const gchar *property, const gchar *value); -gboolean xfconf_channel_set_string_list(XfconfChannel *channel, - const gchar *property, - const gchar **value); + +/* needed? +guint16 xfconf_channel_get_ushort(XfconfChannel *channel, + const gchar *property, + guint16 default_value); +gboolean xfconf_channel_set_ushort(XfconfChannel *channel, + const gchar *property, + guint16 value); +*/ + +gint32 xfconf_channel_get_int(XfconfChannel *channel, + const gchar *property, + gint32 default_value); gboolean xfconf_channel_set_int(XfconfChannel *channel, const gchar *property, - gint value); -gboolean xfconf_channel_set_int64(XfconfChannel *channel, + gint32 value); + +guint64 xfconf_channel_get_uint64(XfconfChannel *channel, + const gchar *property, + guint64 default_value); +gboolean xfconf_channel_set_uint64(XfconfChannel *channel, + const gchar *property, + guint64 value); + +gdouble xfconf_channel_get_double(XfconfChannel *channel, const gchar *property, - gint64 value); + gdouble default_value); gboolean xfconf_channel_set_double(XfconfChannel *channel, const gchar *property, gdouble value); + +gboolean xfconf_channel_get_bool(XfconfChannel *channel, + const gchar *property, + gboolean default_value); gboolean xfconf_channel_set_bool(XfconfChannel *channel, const gchar *property, gboolean value); +/* this is just convenience API for the array stuff, where + * all the values are G_TYPE_STRING */ +gchar **xfconf_channel_get_string_list(XfconfChannel *channel, + const gchar *property); +gboolean xfconf_channel_set_string_list(XfconfChannel *channel, + const gchar *property, + const gchar **values); + +/* really generic API - can set some value types that aren't + * supported by the basic type API, e.g., char, signed short, + * unsigned int, etc. no, you can't set arbitrary GTypes. */ +gboolean xfconf_channel_get_property(XfconfChannel *channel, + const gchar *property, + GValue *value); +gboolean xfconf_channel_set_property(XfconfChannel *channel, + const gchar *property, + const GValue *value); + +/* array types - arrays can be made up of values of arbitrary + * (and mixed) types, even some not supported by the basic + * type API */ + +gboolean xfconf_channel_get_array(XfconfChannel *channel, + const gchar *property, + GType first_value_type, + ...); +gboolean xfconf_channel_get_array_valist(XfconfChannel *channel, + const gchar *property, + GType first_value_type, + va_list var_args); +GValueArray *xfconf_channel_get_arrayv(XfconfChannel *channel, + const gchar *property); + +gboolean xfconf_channel_set_array(XfconfChannel *channel, + const gchar *property, + GType first_value_type, + ...); +gboolean xfconf_channel_set_array_valist(XfconfChannel *channel, + const gchar *property, + GType first_value_type, + va_list var_args); +gboolean xfconf_channel_set_arrayv(XfconfChannel *channel, + const gchar *property, + GValueArray *values); + +/* struct types */ + +gboolean xfconf_channel_get_named_struct(XfconfChannel *channel, + const gchar *property, + const gchar *struct_name, + gpointer value_struct); +gboolean xfconf_channel_set_named_struct(XfconfChannel *channel, + const gchar *property, + const gchar *struct_name, + gpointer value_struct); + +gboolean xfconf_channel_get_struct(XfconfChannel *channel, + const gchar *property, + gpointer value_struct, + GType first_member_type, + ...); +gboolean xfconf_channel_get_struct_valist(XfconfChannel *channel, + const gchar *property, + gpointer value_struct, + GType first_member_type, + va_list var_args); +gboolean xfconf_channel_get_structv(XfconfChannel *channel, + const gchar *property, + gpointer value_struct, + guint n_members, + GType *member_types); + +gboolean xfconf_channel_set_struct(XfconfChannel *channel, + const gchar *property, + const gpointer value_struct, + GType first_member_type, + ...); +gboolean xfconf_channel_set_struct_valist(XfconfChannel *channel, + const gchar *property, + const gpointer value_struct, + GType first_member_type, + va_list var_args); +gboolean xfconf_channel_set_structv(XfconfChannel *channel, + const gchar *property, + const gpointer value_struct, + guint n_members, + GType *member_types); + #if 0 /* future (maybe) */ //gboolean xfconf_channel_begin_transaction(XfconfChannel *channel); diff --git a/xfconf/xfconf-private.h b/xfconf/xfconf-private.h index 3ad1966..26674e2 100644 --- a/xfconf/xfconf-private.h +++ b/xfconf/xfconf-private.h @@ -20,8 +20,16 @@ #ifndef __XFCONF_PRIVATE_H__ #define __XFCONF_PRIVATE_H__ +typedef struct +{ + guint n_members; + GType *member_types; +} XfconfNamedStruct; + DBusGConnection *_xfconf_get_dbus_g_connection(); DBusGProxy *_xfconf_get_dbus_g_proxy(); DBusGProxy *_xfconf_get_gui_dbus_g_proxy(); +XfconfNamedStruct *_xfconf_named_struct_lookup(const gchar *struct_name); + #endif /* __XFCONF_PRIVATE_H__ */ diff --git a/xfconf/xfconf-types.h b/xfconf/xfconf-types.h new file mode 100644 index 0000000..0fb8546 --- /dev/null +++ b/xfconf/xfconf-types.h @@ -0,0 +1,48 @@ +/* + * xfconf + * + * Copyright (c) 2007 Brian Tarricone <bjt23@cornell.edu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License ONLY. + * + * This program 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 General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __XFCONF_TYPES_H__ +#define __XFCONF_TYPES_H__ + +#if !defined(LIBXFCONF_COMPILATION) && !defined(XFCONF_IN_XFCONF_H) +#error "Do not include xfconf-types.h, as this file may change or disappear in the future. Include <xfconf/xfconf.h> instead." +#endif + +#include <glib-object.h> + +#define XFCONF_TYPE_UINT16 (xfconf_uint16_get_type()) +#define XFCONF_TYPE_INT16 (xfconf_int16_get_type()) + +G_BEGIN_DECLS + +GType xfconf_uint16_get_type() G_GNUC_CONST; + +guint16 xfconf_g_value_get_uint16(const GValue *value); +void xfconf_g_value_set_uint16(GValue *value, + guint16 v_uint16); + +GType xfconf_int16_get_type() G_GNUC_CONST; + +gint16 xfconf_g_value_get_int16(const GValue *value); +void xfconf_g_value_set_int16(GValue *value, + gint16 v_int16); + +G_END_DECLS + +#endif diff --git a/xfconf/xfconf.c b/xfconf/xfconf.c index ac6ba48..578bf30 100644 --- a/xfconf/xfconf.c +++ b/xfconf/xfconf.c @@ -21,20 +21,27 @@ #include <config.h> #endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif + #include <glib-object.h> #include <dbus/dbus-glib.h> #include "xfconf.h" #include "xfconf-marshal.h" - +#include "xfconf-private.h" static guint xfconf_refcnt = 0; static DBusGConnection *dbus_conn = NULL; static DBusGProxy *dbus_proxy = NULL; static DBusGProxy *gui_dbus_proxy = NULL; +static GHashTable *named_structs = NULL; +/* private api */ + DBusGConnection * _xfconf_get_dbus_g_connection() { @@ -42,7 +49,7 @@ _xfconf_get_dbus_g_connection() g_critical("xfconf_init() must be called before attempting to use libxfconf!"); return NULL; } - + return dbus_conn; } @@ -53,7 +60,7 @@ _xfconf_get_dbus_g_proxy() g_critical("xfconf_init() must be called before attempting to use libxfconf!"); return NULL; } - + return dbus_proxy; } @@ -64,10 +71,27 @@ _xfconf_get_gui_dbus_g_proxy() g_critical("xfconf_init() must be called before attempting to use libxfconf!"); return NULL; } - + return gui_dbus_proxy; } +XfconfNamedStruct * +_xfconf_named_struct_lookup(const gchar *struct_name) +{ + return g_hash_table_lookup(named_structs, struct_name); +} + +static void +_xfconf_named_struct_free(XfconfNamedStruct *ns) +{ + g_free(ns->member_types); + g_free(ns); +} + + + +/* public api */ + /** * xfconf_init: * @error: An error return. @@ -85,21 +109,21 @@ xfconf_init(GError **error) ++xfconf_refcnt; return TRUE; } - + g_type_init(); - + dbus_g_error_domain_register(XFCONF_ERROR, "org.xfce.Xfconf.Error", XFCONF_TYPE_ERROR); - + dbus_conn = dbus_g_bus_get(DBUS_BUS_SESSION, error); if(!dbus_conn) return FALSE; - + dbus_proxy = dbus_g_proxy_new_for_name(dbus_conn, "org.xfce.Xfconf", "/org/xfce/Xfconf", "org.xfce.Xfconf"); - + dbus_g_object_register_marshaller((GClosureMarshal)xfconf_marshal_VOID__STRING_STRING, G_TYPE_NONE, G_TYPE_STRING, @@ -108,12 +132,16 @@ xfconf_init(GError **error) dbus_g_proxy_add_signal(dbus_proxy, "PropertyChanged", G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID); - + gui_dbus_proxy = dbus_g_proxy_new_for_name(dbus_conn, "org.xfce.Xfconf", "/org/xfce/Xfconf", "org.xfce.Xfconf.GUI"); - + + named_structs = g_hash_table_new_full(g_str_hash, g_str_equal, + (GDestroyNotify)g_free, + (GDestroyNotify)_xfconf_named_struct_free); + ++xfconf_refcnt; return TRUE; } @@ -130,13 +158,43 @@ xfconf_shutdown() { if(--xfconf_refcnt) return; - + + g_hash_table_destroy(named_structs); + named_structs = NULL; + g_object_unref(G_OBJECT(dbus_proxy)); dbus_proxy = NULL; - + g_object_unref(G_OBJECT(gui_dbus_proxy)); gui_dbus_proxy = NULL; - + dbus_g_connection_unref(dbus_conn); dbus_conn = NULL; } + +/** + * xfconf_named_struct_register: + * @struct_name: The unique name of the struct to register. + * @n_members: The number of data members in the struct. + * @member_types: An array of the #GType<!-- -->s of the struct members. + * + * Registers a named struct for use with xfconf_channel_get_named_struct() + * and xfconf_channel_set_named_struct(). + **/ +void +xfconf_named_struct_register(const gchar *struct_name, + guint n_members, + const GType *member_types) +{ + XfconfNamedStruct *ns; + + g_return_if_fail(struct_name && *struct_name && n_members && member_types + && !g_hash_table_lookup(named_structs, struct_name)); + + ns = g_new(XfconfNamedStruct, 1); + ns->n_members = n_members; + ns->member_types = g_new(GType, n_members); + memcpy(ns->member_types, member_types, sizeof(GType) * n_members); + + g_hash_table_insert(named_structs, g_strdup(struct_name), ns); +} diff --git a/xfconf/xfconf.h b/xfconf/xfconf.h index f1ad9fb..41acec8 100644 --- a/xfconf/xfconf.h +++ b/xfconf/xfconf.h @@ -32,6 +32,10 @@ G_BEGIN_DECLS gboolean xfconf_init(GError **error); void xfconf_shutdown(); +void xfconf_named_struct_register(const gchar *struct_name, + guint n_members, + const GType *member_types); + G_END_DECLS #endif /* __XFCONF_H__ */ diff --git a/xfconfd/xfconf-backend-perchannel-xml.c b/xfconfd/xfconf-backend-perchannel-xml.c index 085e92f..0caff83 100644 --- a/xfconfd/xfconf-backend-perchannel-xml.c +++ b/xfconfd/xfconf-backend-perchannel-xml.c @@ -57,6 +57,7 @@ #include "xfconf-backend-perchannel-xml.h" #include "xfconf-backend.h" #include "xfconf-util.h" +#include "xfconf/xfconf-types.h" #define FILE_VERSION_MAJOR "1" #define FILE_VERSION_MINOR "0" @@ -69,6 +70,24 @@ #define WRITE_TIMEOUT (5*1000) /* 5 secionds */ #define MAX_PROP_PATH (4096) +#ifdef CHAR_MIN +#define XFCONF_MINCHAR CHAR_MIN +#else +#define XFCONF_MINCHAR (-128) +#endif + +#ifdef CHAR_MAX +#define XFCONF_MAXCHAR CHAR_MAX +#else +#define XFCONF_MAXCHAR (127) +#endif + +#ifdef UCHAR_MAX +#define XFCONF_MAXUCHAR UCHAR_MAX +#else +#define XFCONF_MAXUCHAR (255) +#endif + struct _XfconfBackendPerchannelXml { GObject parent; @@ -98,13 +117,13 @@ typedef enum ELEM_NONE = 0, ELEM_CHANNEL, ELEM_PROPERTY, - ELEM_STRING, + ELEM_VALUE, } XmlParserElem; /* FIXME: due to the hierarchical nature of the file, i need to use a - * stack for strlist_property and strlist_value because a more than one - * strlist property can be open at once. the current xml file writer always - * puts the <string> elements right after the opening <property>, but it's + * stack for list_property and list_value because more than one array + * property can be open at once. the current xml file writer always + * puts the <value> elements right after the opening <property>, but it's * possible someone could edit the file so that's not the case anymore. */ typedef struct { @@ -115,9 +134,8 @@ typedef struct gchar *channel_name; gboolean channel_locked; gchar cur_path[MAX_PROP_PATH]; - gchar *cur_text; - gchar *strlist_property; - GValue *strlist_value; + gchar *list_property; + GValue *list_value; } XmlParserState; static void xfconf_backend_perchannel_xml_class_init(XfconfBackendPerchannelXmlClass *klass); @@ -759,6 +777,156 @@ xfconf_backend_perchannel_xml_create_channel(XfconfBackendPerchannelXml *xbpx, return properties; } +static GType +xfconf_string_type_to_gtype(const gchar *type) +{ + if(!strcmp(type, "string")) + return G_TYPE_STRING; + else if(!strcmp(type, "uchar")) + return G_TYPE_UCHAR; + else if(!strcmp(type, "char")) + return G_TYPE_CHAR; + else if(!strcmp(type, "uint16")) + return XFCONF_TYPE_UINT16; + else if(!strcmp(type, "int16")) + return XFCONF_TYPE_INT16; + else if(!strcmp(type, "uint")) + return G_TYPE_UINT; + else if(!strcmp(type, "int")) + return G_TYPE_INT; + else if(!strcmp(type, "uint64")) + return G_TYPE_UINT64; + else if(!strcmp(type, "int64")) + return G_TYPE_INT64; + else if(!strcmp(type, "float")) + return G_TYPE_FLOAT; + else if(!strcmp(type, "double")) + return G_TYPE_DOUBLE; + else if(!strcmp(type, "bool")) + return G_TYPE_BOOLEAN; + else if(!strcmp(type, "array")) + return G_TYPE_VALUE_ARRAY; + else if(!strcmp(type, "empty")) + return G_TYPE_NONE; + + return G_TYPE_INVALID; +} + +static gboolean +xfconf_string_to_value(const gchar *str, + GValue *value) +{ +#define CHECK_CONVERT_STATUS() \ + if(*str == 0 || *endptr != 0) \ + return FALSE +#define CHECK_CONVERT_VALUE(val, minval, maxval) \ + if((val) < (minval) || (val) > (maxval)) \ + return FALSE + +#define REAL_HANDLE_INT(minval, maxval, convertfunc, setfunc) \ + G_STMT_START{ \ + errno = 0; \ + intval = convertfunc(str, &endptr, 0); \ + if(0 == intval && ERANGE == errno) \ + return FALSE; \ + CHECK_CONVERT_STATUS(); \ + CHECK_CONVERT_VALUE(intval, minval, maxval); \ + setfunc(value, intval); \ + return TRUE; \ + }G_STMT_END + +#define HANDLE_UINT(minval, maxval, setfunc) REAL_HANDLE_INT(minval, maxval, strtoul, setfunc) +#define HANDLE_INT(minval, maxval, setfunc) REAL_HANDLE_INT(minval, maxval, strtol, setfunc) + + guint64 uintval; + gint64 intval; + gdouble dval; + gchar *endptr = NULL; + + switch(G_VALUE_TYPE(value)) { + case G_TYPE_STRING: + g_value_set_string(value, str); + return TRUE; + + case G_TYPE_UCHAR: + HANDLE_UINT(0, XFCONF_MAXUCHAR, g_value_set_uchar); + case G_TYPE_CHAR: + HANDLE_INT(XFCONF_MINCHAR, XFCONF_MAXCHAR, g_value_set_char); + case G_TYPE_UINT: + HANDLE_UINT(0, G_MAXUINT, g_value_set_uint); + case G_TYPE_INT: + HANDLE_INT(G_MININT, G_MAXINT, g_value_set_int); + + case G_TYPE_UINT64: + errno = 0; + uintval = g_ascii_strtoull(str, &endptr, 0); + if(0 == uintval && ERANGE == errno) + return FALSE; + CHECK_CONVERT_STATUS(); + g_value_set_uint64(value, uintval); + return TRUE; + + case G_TYPE_INT64: + errno = 0; + intval = g_ascii_strtoll(str, &endptr, 0); + if(0 == intval && ERANGE == errno) + return FALSE; + CHECK_CONVERT_STATUS(); + g_value_set_uint64(value, intval); + return TRUE; + + case G_TYPE_FLOAT: + errno = 0; + dval = g_ascii_strtod(str, &endptr); + if(0.0 == dval && ERANGE == errno) + return FALSE; + CHECK_CONVERT_STATUS(); + if(dval < G_MINFLOAT || dval > G_MAXFLOAT) + return FALSE; + g_value_set_float(value, (gfloat)dval); + return TRUE; + + case G_TYPE_DOUBLE: + errno = 0; + dval = g_ascii_strtod(str, &endptr); + if(0.0 == dval && ERANGE == errno) + return FALSE; + CHECK_CONVERT_STATUS(); + g_value_set_double(value, dval); + return TRUE; + + case G_TYPE_BOOLEAN: + if(!strcmp(str, "true")) { + g_value_set_boolean(value, TRUE); + return TRUE; + } else if(!strcmp(str, "false")) { + g_value_set_boolean(value, FALSE); + return TRUE; + } else + return FALSE; + + default: + if(XFCONF_TYPE_UINT16 == G_VALUE_TYPE(value)) { + HANDLE_INT(0, G_MAXUSHORT, xfconf_g_value_set_uint16); + return TRUE; + } else if(XFCONF_TYPE_INT16 == G_VALUE_TYPE(value)) { + HANDLE_INT(G_MINSHORT, G_MAXSHORT, xfconf_g_value_set_int16); + return TRUE; + } else if(G_TYPE_VALUE_ARRAY == G_VALUE_TYPE(value)) { + GValueArray *arr = g_value_array_new(3); + g_value_set_boxed(value, arr); + return TRUE; + } + return FALSE; + } + +#undef CHECK_CONVERT_STATUS +#undef CHECK_CONVERT_VALUE +#undef REAL_HANDLE_INT +#undef HANDLE_INT +#undef HANDLE_UINT +} + static void xfconf_backend_perchannel_xml_start_elem(GMarkupParseContext *context, const gchar *element_name, @@ -880,6 +1048,8 @@ xfconf_backend_perchannel_xml_start_elem(GMarkupParseContext *context, case ELEM_CHANNEL: case ELEM_PROPERTY: if(!strcmp(element_name, "property")) { + GType value_type; + for(i = 0; attribute_names[i]; ++i) { if(!strcmp(attribute_names[i], "name")) name = attribute_values[i]; @@ -958,44 +1128,32 @@ xfconf_backend_perchannel_xml_start_elem(GMarkupParseContext *context, } /* parse types and values */ - if(!strcmp(type, "string")) { - g_value_init(&prop->value, G_TYPE_STRING); - g_value_set_string(&prop->value, value); - } else if(!strcmp(type, "strlist")) { - GPtrArray *arr = g_ptr_array_new(); - g_value_init(&prop->value, - dbus_g_type_get_collection("GPtrArray", - G_TYPE_STRING)); - g_value_set_boxed(&prop->value, arr); - /* FIXME: use stacks here */ - state->strlist_property = g_strdup(fullpath); - state->strlist_value = &prop->value; - } else if(!strcmp(type, "int")) { - g_value_init(&prop->value, G_TYPE_INT); - g_value_set_int(&prop->value, atoi(value)); - } else if(!strcmp(type, "int64")) { - g_value_init(&prop->value, G_TYPE_INT64); - g_value_set_int64(&prop->value, g_ascii_strtoll(value, NULL, - 0)); - } else if(!strcmp(type, "double")) { - g_value_init(&prop->value, G_TYPE_DOUBLE); - g_value_set_double(&prop->value, g_ascii_strtod(value, - NULL)); - } else if(!strcmp(type, "bool")) { - g_value_init(&prop->value, G_TYPE_BOOLEAN); - if(!g_ascii_strcasecmp(value, "true")) - g_value_set_boolean(&prop->value, TRUE); - else - g_value_set_boolean(&prop->value, FALSE); - } else { - if(strcmp(type, "empty")) { - if(error) { - g_set_error(error, G_MARKUP_ERROR, - G_MARKUP_ERROR_INVALID_CONTENT, - "Invalid property type \"%s\"", type); - } - return; + value_type = xfconf_string_type_to_gtype(type); + if(G_TYPE_INVALID == value_type) { + if(error) { + g_set_error(error, G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + _("Invalid type for <%s>: \"%s\""), + element_name, type); + } + return; + } + + g_value_init(&prop->value, value_type); + if(!xfconf_string_to_value(value, &prop->value)) { + if(error) { + g_set_error(error, G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + _("Unable to parse value from \"%s\""), + value); } + return; + } + + if(G_TYPE_VALUE_ARRAY == value_type) { + /* FIXME: use stacks here */ + state->list_property = g_strdup(fullpath); + state->list_value = &prop->value; } if(prop) @@ -1004,11 +1162,67 @@ xfconf_backend_perchannel_xml_start_elem(GMarkupParseContext *context, g_strlcpy(state->cur_path, fullpath, MAX_PROP_PATH); state->cur_elem = ELEM_PROPERTY; } else if(ELEM_PROPERTY == state->cur_elem - && state->strlist_property /* FIXME: use stack */ - && state->strlist_value /* FIXME: use stack */ - && !strcmp(element_name, "string")) + && state->list_property /* FIXME: use stack */ + && state->list_value /* FIXME: use stack */ + && !strcmp(element_name, "value")) { - state->cur_elem = ELEM_STRING; + GValueArray *arr; + GValue val; + GType value_type = G_TYPE_INVALID; + + for(i = 0; attribute_names[i]; ++i) { + if(!strcmp(attribute_names[i], "type")) + type = attribute_values[i]; + else if(!strcmp(attribute_names[i], "value")) + value = attribute_values[i]; + else { + if(error) { + g_set_error(error, G_MARKUP_ERROR, + G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, + "Unknown attribute in <%s>: %s", + element_name, attribute_names[i]); + } + return; + } + } + + value_type = xfconf_string_type_to_gtype(attribute_values[i]); + if(G_TYPE_VALUE_ARRAY == value_type) { + if(error) { + g_set_error(error, G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + _("The type attribute of <value> cannot be an array")); + } + return; + } else if(G_TYPE_INVALID == value_type + || G_TYPE_NONE == value_type) + { + if(error) { + g_set_error(error, G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + _("Invalid type for <%s>: \"%s\""), + element_name, type); + } + return; + } + + g_value_init(&val, value_type); + if(!xfconf_string_to_value(value, &val)) { + if(error) { + g_set_error(error, G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + _("Unable to parse value from \"%s\""), + value); + } + g_value_unset(&val); + return; + } + + arr = g_value_get_boxed(state->list_value); + g_value_array_append(arr, &val); + g_value_unset(&val); + + state->cur_elem = ELEM_VALUE; } else { if(error) { g_set_error(error, G_MARKUP_ERROR, @@ -1021,11 +1235,11 @@ xfconf_backend_perchannel_xml_start_elem(GMarkupParseContext *context, } break; - case ELEM_STRING: + case ELEM_VALUE: if(error) { g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, - "No other elements are allowed inside a string element."); + "No other elements are allowed inside a <value> element."); } return; } @@ -1039,7 +1253,6 @@ xfconf_backend_perchannel_xml_end_elem(GMarkupParseContext *context, { XmlParserState *state = user_data; gchar *p; - GPtrArray *arr; switch(state->cur_elem) { case ELEM_CHANNEL: @@ -1049,8 +1262,8 @@ xfconf_backend_perchannel_xml_end_elem(GMarkupParseContext *context, case ELEM_PROPERTY: /* FIXME: use stacks here */ - state->strlist_property = NULL; - state->strlist_value = NULL; + state->list_property = NULL; + state->list_value = NULL; p = g_strrstr(state->cur_path, "/"); @@ -1066,11 +1279,7 @@ xfconf_backend_perchannel_xml_end_elem(GMarkupParseContext *context, } break; - case ELEM_STRING: - /* FIXME: use stack */ - arr = g_value_get_boxed(state->strlist_value); - g_ptr_array_add(arr, state->cur_text); - state->cur_text = NULL; + case ELEM_VALUE: state->cur_elem = ELEM_PROPERTY; break; @@ -1080,6 +1289,7 @@ xfconf_backend_perchannel_xml_end_elem(GMarkupParseContext *context, } } +#if 0 static inline gboolean check_is_whitespace(const gchar *str, gsize str_len) @@ -1106,14 +1316,14 @@ xfconf_backend_perchannel_xml_text_elem(GMarkupParseContext *context, { XmlParserState *state = user_data; - if(ELEM_STRING != state->cur_elem) { + if(ELEM_VALUE != state->cur_elem) { /* check to make sure it's not just whitespace */ if(check_is_whitespace(text, text_len)) return; if(error) { g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, - "Content only allowed in <string> elements"); + "Content only allowed in <value> elements"); } return; } @@ -1127,6 +1337,7 @@ xfconf_backend_perchannel_xml_text_elem(GMarkupParseContext *context, state->cur_text[cur_len + text_len] = 0; } } +#endif static gboolean xfconf_backend_perchannel_xml_merge_file(XfconfBackendPerchannelXml *xbpx, @@ -1142,7 +1353,7 @@ xfconf_backend_perchannel_xml_merge_file(XfconfBackendPerchannelXml *xbpx, GMarkupParser parser = { xfconf_backend_perchannel_xml_start_elem, xfconf_backend_perchannel_xml_end_elem, - xfconf_backend_perchannel_xml_text_elem, + /* xfconf_backend_perchannel_xml_text_elem, */ NULL, }; XmlParserState state; @@ -1256,7 +1467,6 @@ xfconf_backend_perchannel_xml_load_channel(XfconfBackendPerchannelXml *xbpx, prop->name = g_strdup("/"); properties = g_node_new(prop); - /* FIXME: handle locking */ for(l = system_files; l && !channel_locked; l = l->next) { xfconf_backend_perchannel_xml_merge_file(xbpx, l->data, TRUE, &properties, &channel_locked, @@ -1281,46 +1491,53 @@ out: } static gboolean -xfconf_backend_perchannel_xml_write_node(XfconfBackendPerchannelXml *xbpx, - FILE *fp, - GNode *node, - gint depth, - GError **error) +xfconf_format_xml_tag(GString *elem_str, + GValue *value, + gboolean is_array_value, + gchar spaces[MAX_PROP_PATH], + gboolean *is_array) { - XfconfProperty *prop = node->data; - GValue *value = &prop->value; - GString *elem_str; - gchar spaces[MAX_PROP_PATH]; - GNode *child; - gint i; - gboolean is_strlist = FALSE; - - if(depth * 2 > sizeof(spaces) + 1) - depth = sizeof(spaces) / 2 - 1; - - memset(spaces, ' ', depth * 2); - spaces[depth * 2] = 0; - - elem_str = g_string_sized_new(128); - g_string_append_printf(elem_str, "%s<property name=\"%s\"", spaces, - prop->name); - switch(G_VALUE_TYPE(value)) { case G_TYPE_STRING: g_string_append_printf(elem_str, " type=\"string\" value=\"%s\"", g_value_get_string(value)); break; + case G_TYPE_UCHAR: + g_string_append_printf(elem_str, " type=\"uchar\" value=\"%hhu\"", + g_value_get_uchar(value)); + break; + + case G_TYPE_CHAR: + g_string_append_printf(elem_str, " type=\"char\" value=\"%hhd\"", + g_value_get_uchar(value)); + break; + + case G_TYPE_UINT: + g_string_append_printf(elem_str, " type=\"uint\" value=\"%u\"", + g_value_get_uint(value)); + break; + case G_TYPE_INT: g_string_append_printf(elem_str, " type=\"int\" value=\"%d\"", g_value_get_int(value)); break; + case G_TYPE_UINT64: + g_string_append_printf(elem_str, " type=\"uint64\" value=\"%" G_GUINT64_FORMAT "\"", + g_value_get_uint64(value)); + break; + case G_TYPE_INT64: g_string_append_printf(elem_str, " type=\"int64\" value=\"%" G_GINT64_FORMAT "\"", g_value_get_int64(value)); break; + case G_TYPE_FLOAT: + g_string_append_printf(elem_str, " type=\"float\" value=\"%f\"", + (gdouble)g_value_get_float(value)); + break; + case G_TYPE_DOUBLE: g_string_append_printf(elem_str, " type=\"double\" value=\"%f\"", g_value_get_double(value)); @@ -1332,30 +1549,14 @@ xfconf_backend_perchannel_xml_write_node(XfconfBackendPerchannelXml *xbpx, break; default: - if(G_VALUE_TYPE(value) == dbus_g_type_get_collection("GPtrArray", - G_TYPE_STRING)) - { - GPtrArray *arr; - - is_strlist = TRUE; - - g_string_append(elem_str, " type=\"strlist\">\n"); - - arr = g_value_get_boxed(value); - for(i = 0; i < arr->len; ++i) { - gchar *value_str = g_markup_escape_text(arr->pdata[i], -1); - g_string_append_printf(elem_str, - "%s <string>%s</string>\n", - spaces, value_str); - g_free(value_str); - } - } else if(G_VALUE_TYPE(value) == G_TYPE_STRV) { + if(G_VALUE_TYPE(value) == G_TYPE_STRV) { gchar **strlist; gint i; - is_strlist = TRUE; + if(is_array_value) + return FALSE; - g_string_append(elem_str, " type=\"strlist\">\n"); + g_string_append(elem_str, " type=\"array\">\n"); strlist = g_value_get_boxed(value); for(i = 0; strlist[i]; ++i) { @@ -1365,19 +1566,83 @@ xfconf_backend_perchannel_xml_write_node(XfconfBackendPerchannelXml *xbpx, spaces, value_str); g_free(value_str); } - } else if(G_VALUE_TYPE(value) != 0) { - g_warning("Unknown value type %d (\"%s\"), treating as branch", - (int)G_VALUE_TYPE(value), G_VALUE_TYPE_NAME(value)); - g_value_unset(value); + + *is_array = TRUE; + } else if(G_VALUE_TYPE(value) == G_TYPE_VALUE_ARRAY) { + GValueArray *arr; + gint i; + + if(is_array_value) + return FALSE; + + g_string_append(elem_str, " type=\"array\">\n"); + + arr = g_value_get_boxed(value); + for(i = 0; i < arr->n_values; ++i) { + GValue *value1 = g_value_array_get_nth(arr, i); + gboolean dummy; + + g_string_append_printf(elem_str, "%s <value ", spaces); + if(!xfconf_format_xml_tag(elem_str, value1, TRUE, spaces, + &dummy)) + { + return FALSE; + } + g_string_append(elem_str, "/>\n"); + } + + *is_array = TRUE; + } else { + if(is_array_value) + return FALSE; + + if(G_VALUE_TYPE(value) != 0) { + g_warning("Unknown value type %d (\"%s\"), treating as branch", + (int)G_VALUE_TYPE(value), G_VALUE_TYPE_NAME(value)); + g_value_unset(value); + } + + is_array = FALSE; + g_string_append(elem_str, " type=\"empty\""); } break; } - if(!G_VALUE_TYPE(value)) - g_string_append(elem_str, " type=\"empty\""); + return TRUE; +} + +static gboolean +xfconf_backend_perchannel_xml_write_node(XfconfBackendPerchannelXml *xbpx, + FILE *fp, + GNode *node, + gint depth, + GError **error) +{ + XfconfProperty *prop = node->data; + GValue *value = &prop->value; + GString *elem_str; + gchar spaces[MAX_PROP_PATH]; + GNode *child; + gboolean is_array = FALSE; + + if(depth * 2 > sizeof(spaces) + 1) + depth = sizeof(spaces) / 2 - 1; + + memset(spaces, ' ', depth * 2); + spaces[depth * 2] = 0; + + elem_str = g_string_sized_new(128); + g_string_append_printf(elem_str, "%s<property name=\"%s\"", spaces, + prop->name); + + if(!xfconf_format_xml_tag(elem_str, value, FALSE, spaces, &is_array)) { + /* _flush_channel() will handle |error| */ + g_string_free(elem_str, TRUE); + return FALSE; + } child = g_node_first_child(node); - if(!is_strlist) { + if(!is_array) { if(child) g_string_append(elem_str, ">\n"); else @@ -1395,11 +1660,12 @@ xfconf_backend_perchannel_xml_write_node(XfconfBackendPerchannelXml *xbpx, if(!xfconf_backend_perchannel_xml_write_node(xbpx, fp, child, depth + 1, error)) { + /* _flush_channel() will handle |error| */ return FALSE; } } - if(is_strlist || g_node_first_child(node)) { + if(is_array || g_node_first_child(node)) { if(fputs(spaces, fp) == EOF || fputs("</property>\n", fp) == EOF) { /* _flush_channel() will handle |error| */ return FALSE; |