summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO19
-rw-r--r--common/Makefile.am1
-rw-r--r--common/xfconf-types.c183
-rw-r--r--common/xfconf-util.h2
-rw-r--r--docs/reference/tmpl/xfconf-backend.sgml6
-rw-r--r--docs/reference/tmpl/xfconf-channel.sgml200
-rw-r--r--docs/reference/tmpl/xfconf-types.sgml57
-rw-r--r--docs/reference/tmpl/xfconf-unused.sgml24
-rw-r--r--docs/reference/tmpl/xfconf.sgml14
-rw-r--r--docs/reference/xfconf-docs.sgml3
-rw-r--r--docs/reference/xfconf-sections.txt30
-rw-r--r--docs/spec/backend.txt43
-rw-r--r--docs/spec/perchannel-xml.dtd18
-rw-r--r--docs/spec/perchannel-xml.txt31
-rw-r--r--gtk-doc.make8
-rw-r--r--tests/t-get-properties.c5
-rw-r--r--tests/t-has-properties.c2
-rw-r--r--tests/t-remove-properties.c6
-rw-r--r--tests/t-set-properties.c2
-rw-r--r--tests/tests-common.h4
-rw-r--r--xfconf/Makefile.am2
-rw-r--r--xfconf/xfconf-channel.c1213
-rw-r--r--xfconf/xfconf-channel.h140
-rw-r--r--xfconf/xfconf-private.h8
-rw-r--r--xfconf/xfconf-types.h48
-rw-r--r--xfconf/xfconf.c86
-rw-r--r--xfconf/xfconf.h4
-rw-r--r--xfconfd/xfconf-backend-perchannel-xml.c492
28 files changed, 2309 insertions, 342 deletions
diff --git a/TODO b/TODO
index 51b47a3..f6d1043 100644
--- a/TODO
+++ b/TODO
@@ -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;