+ * g_action_parse_detailed_name:
+ * @detailed_name: a detailed action name
+ * @action_name: (out): the action name
+ * @target_value: (out): the target value, or %NULL for no target
+ * @error: a pointer to a %NULL #GError, or %NULL
+ *
+ * Parses a detailed action name into its separate name and target
+ * components.
+ *
+ * Detailed action names can have three formats.
+ *
+ * The first format is used to represent an action name with no target
+ * value and consists of just an action name containing no whitespace
+ * nor the characters ':', '(' or ')'. For example: "app.action".
+ *
+ * The second format is used to represent an action with a target value
+ * that is a non-empty string consisting only of alphanumerics, plus '-'
+ * and '.'. In that case, the action name and target value are
+ * separated by a double colon ("::"). For example:
+ * "app.action::target".
+ *
+ * The third format is used to represent an action with any type of
+ * target value, including strings. The target value follows the action
+ * name, surrounded in parens. For example: "app.action(42)". The
+ * target value is parsed using g_variant_parse(). If a tuple-typed
+ * value is desired, it must be specified in the same way, resulting in
+ * two sets of parens, for example: "app.action((1,2,3))". A string
+ * target can be specified this way as well: "app.action('target')".
+ * For strings, this third format must be used if * target value is
+ * empty or contains characters other than alphanumerics, '-' and '.'.
+ *
+ * Returns: %TRUE if successful, else %FALSE with @error set
+ *
+ * Since: 2.38
+ **/
+gboolean
+g_action_parse_detailed_name (const gchar *detailed_name,
+ gchar **action_name,
+ GVariant **target_value,
+ GError **error)
+{
+ const gchar *target;
+ gsize target_len;
+ gsize base_len;
+
+ /* For historical (compatibility) reasons, this function accepts some
+ * cases of invalid action names as long as they don't interfere with
+ * the separation of the action from the target value.
+ *
+ * We decide which format we have based on which we see first between
+ * '::' '(' and '\0'.
+ */
+
+ if (*detailed_name == '\0' || *detailed_name == ' ')
+ goto bad_fmt;
+
+ base_len = strcspn (detailed_name, ": ()");
+ target = detailed_name + base_len;
+ target_len = strlen (target);
+
+ switch (target[0])
+ {
+ case ' ':
+ case ')':
+ goto bad_fmt;
+
+ case ':':
+ if (target[1] != ':')
+ goto bad_fmt;
+
+ *target_value = g_variant_ref_sink (g_variant_new_string (target + 2));
+ break;
+
+ case '(':
+ {
+ if (target[target_len - 1] != ')')
+ goto bad_fmt;
+
+ *target_value = g_variant_parse (NULL, target + 1, target + target_len - 1, NULL, error);
+ if (*target_value == NULL)
+ goto bad_fmt;
+ }
+ break;
+
+ case '\0':
+ *target_value = NULL;
+ break;
+ }
+
+ *action_name = g_strndup (detailed_name, base_len);
+
+ return TRUE;
+
+bad_fmt:
+ if (error)
+ {
+ if (*error == NULL)
+ g_set_error (error, G_VARIANT_PARSE_ERROR, G_VARIANT_PARSE_ERROR_FAILED,
+ "Detailed action name '%s' has invalid format", detailed_name);
+ else
+ g_prefix_error (error, "Detailed action name '%s' has invalid format: ", detailed_name);
+ }
+
+ return FALSE;
+}
+
+/**
+ * g_action_print_detailed_name:
+ * @action_name: a valid action name
+ * @target_value: (allow-none): a #GVariant target value, or %NULL
+ *
+ * Formats a detailed action name from @action_name and @target_value.