nautilus/libnautilus-private/nautilus-preferences.c
Darin Adler a72923da4f reviewed by: John Sullivan <sullivan@eazel.com>
Add a new command-line option "--no-desktop" as requested by
	fcrozat on behalf of people who want to run Nautilus under KDE as
	well as under GNOME. The option has no effect if Nautilus is
	already running, but if it's the first invocation of Nautilus,
	then no desktop-related preferences show up and the desktop is
	not shown regardless of the setting of those preferences.

	* src/nautilus-main.c: (main): Add the no-desktop option. Make the
	desktop-related preferences invisible if it's on, and pass it down
	to the NautilusApplication.

	* src/nautilus-application.h:
	* src/nautilus-application.c: (nautilus_application_startup):
	Don't start the desktop if no_desktop is passed in. Otherwise
	decide based on the preference.

	* libnautilus-extensions/nautilus-preferences-box.c:
	(preferences_box_find_row): Mark this private function static.
	(preferences_box_populate_pane): Get rid of code that would
	translate the pane_name twice.
	(nautilus_preferences_dialog_new): Simplified this code a bit.

	* libnautilus-extensions/nautilus-preferences.h:
	* libnautilus-extensions/nautilus-preferences.c:
	(preferences_make_user_level_filtered_key),
	(nautilus_preferences_get_is_invisible),
	(nautilus_preferences_set_is_invisible),
	(preferences_key_make_for_getter),
	(nautilus_preferences_visible_in_current_user_level),
	(nautilus_preferences_is_visible): Added new flag to make a
	preference invisible.

	* src/nautilus-first-time-druid.h:
	* src/nautilus-first-time-druid.c: (set_up_gmc_transition_page),
	(nautilus_first_time_druid_show): Removed code that used to make
	the druid default based on the desktop preference.

	Fixed the Tinderbox.

	* libnautilus-extensions/nautilus-global-preferences.c:
	(default_default_folder_viewer_callback): Allow NULL because that
	happens during self-check.
2001-04-24 00:46:13 +00:00

1850 lines
48 KiB
C

/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/* nautilus-preferences.c - Preference peek/poke/notify implementation.
Copyright (C) 1999, 2000 Eazel, Inc.
The Gnome Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The Gnome Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the Gnome Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
Authors: Ramiro Estrugo <ramiro@eazel.com>
*/
#include <config.h>
#include "nautilus-preferences.h"
#include "nautilus-gconf-extensions.h"
#include "nautilus-lib-self-check-functions.h"
#include <eel/eel-enumeration.h>
#include <eel/eel-glib-extensions.h>
#include <eel/eel-string-list.h>
#include <eel/eel-string.h>
#include <gconf/gconf-client.h>
#include <gconf/gconf.h>
#include <gtk/gtksignal.h>
#include <libgnome/gnome-defs.h>
#include <libgnome/gnome-i18n.h>
#include <libgnomeui/gnome-dialog.h>
#include <libgnomeui/gnome-dialog-util.h>
#define DEFAULT_USER_LEVEL NAUTILUS_USER_LEVEL_INTERMEDIATE
#define USER_LEVEL_KEY "/apps/nautilus/user_level"
/* An enumeration used for updating auto-storage variables in a type-specific way.
* FIXME: there is another enumeration like this in nautilus-global-preferences.c,
* used for different purposes but in a related way. Should we combine them?
*/
typedef enum
{
PREFERENCE_BOOLEAN = 1,
PREFERENCE_INTEGER,
PREFERENCE_STRING,
PREFERENCE_STRING_LIST
} PreferenceType;
/*
* PreferencesEntry:
*
* A structure to manage preference hash table nodes.
* Preferences are hash tables. The hash key is the preference name
* (a string). The hash value is a pointer of the following struct:
*/
typedef struct {
char *name;
char *description;
PreferenceType type;
gboolean invisible;
GList *callback_list;
gboolean callbacks_blocked;
GList *auto_storage_list;
int gconf_connection_id;
char *enumeration_id;
GConfValue *cached_value;
} PreferencesEntry;
/*
* PreferencesCallbackEntry:
*
* A structure to manage callback lists. A callback list is a GList.
* The callback_data in each list node is a pointer to the following
* struct:
*/
typedef struct {
NautilusPreferencesCallback callback;
gpointer callback_data;
} PreferencesCallbackEntry;
static const char *user_level_names_for_display[] =
{
N_("Beginner"),
N_("Intermediate"),
N_("Advanced")
};
static const char *user_level_names_for_storage[] =
{
"novice",
"intermediate",
"advanced"
};
static const char * preferences_peek_storage_path (void);
static const char * preferences_peek_defaults_path (void);
static const char * preferences_peek_visibility_path (void);
static gboolean preferences_preference_is_internal (const char *name);
static gboolean preferences_preference_is_user_level (const char *name);
static gboolean preferences_preference_is_default (const char *name);
static char * preferences_key_make (const char *name);
static char * preferences_key_make_for_getter (const char *name);
static char * preferences_key_make_for_default (const char *name,
int user_level);
static char * preferences_key_make_for_default_getter (const char *name,
int user_level);
static char * preferences_key_make_for_visibility (const char *name);
static void preferences_user_level_changed_notice (GConfClient *client,
guint connection_id,
GConfEntry *gconf_entry,
gpointer user_data);
static void preferences_something_changed_notice (GConfClient *client,
guint connection_id,
GConfEntry *gconf_entry,
gpointer user_data);
static void preferences_global_table_check_changes_function (gpointer key,
gpointer value,
gpointer callback_data);
static GHashTable *preferences_global_table_get_global (void);
static void preferences_callback_entry_free (PreferencesCallbackEntry *callback_entry);
static void preferences_entry_update_auto_storage (PreferencesEntry *entry);
static void preferences_global_table_free (void);
static const char * preferences_peek_user_level_name_for_storage (int user_level);
static PreferencesEntry * preferences_global_table_lookup_or_insert (const char *name);
static guint user_level_changed_connection_id = NAUTILUS_GCONF_UNDEFINED_CONNECTION;
static GHashTable *global_table = NULL;
static char *storage_path = NULL;
static char *defaults_path = NULL;
static char *visiblity_path = NULL;
static gboolean initialized = FALSE;
static const char *
preferences_peek_storage_path (void)
{
g_return_val_if_fail (storage_path != NULL, NULL);
return storage_path;
}
static void
preferences_set_storage_path (const char *new_storage_path)
{
g_return_if_fail (eel_strlen (new_storage_path) > 0);
/* Make sure the path is indeed different */
if (eel_str_is_equal (new_storage_path, storage_path)) {
return;
}
/* Free the preference hash table */
preferences_global_table_free ();
/* Stop monitoring the old path */
nautilus_gconf_monitor_remove (storage_path);
storage_path = g_strdup (new_storage_path);
/* Start monitoring the new path */
nautilus_gconf_monitor_add (storage_path);
}
static gboolean
preferences_is_initialized (void)
{
return initialized;
}
static const char *
preferences_peek_defaults_path (void)
{
if (defaults_path == NULL) {
defaults_path = g_strdup_printf ("%s/defaults", preferences_peek_storage_path ());
}
return defaults_path;
}
static const char *
preferences_peek_visibility_path (void)
{
if (visiblity_path == NULL) {
visiblity_path = g_strdup_printf ("%s/visibility", preferences_peek_storage_path ());
}
return visiblity_path;
}
static const char *
preferences_peek_user_level_name_for_storage (int user_level)
{
user_level = nautilus_preferences_user_level_clamp (user_level);
return user_level_names_for_storage[user_level];
}
/* If the preference name begind with a "/", we interpret
* it as a straight gconf key. */
static gboolean
preferences_preference_is_internal (const char *name)
{
g_return_val_if_fail (name != NULL, FALSE);
if (eel_str_has_prefix (name, "/")) {
return FALSE;
}
return TRUE;
}
static gboolean
preferences_preference_is_user_level (const char *name)
{
g_return_val_if_fail (name != NULL, FALSE);
return eel_str_is_equal (name, USER_LEVEL_KEY)
|| eel_str_is_equal (name, "user_level");
}
static char *
preferences_key_make (const char *name)
{
g_return_val_if_fail (name != NULL, NULL);
if (!preferences_preference_is_internal (name)) {
return g_strdup (name);
}
/* Otherwise, we prefix it with the path */
return g_strdup_printf ("%s/%s", preferences_peek_storage_path (), name);
}
static char *
preferences_key_make_for_default (const char *name,
int user_level)
{
char *key;
char *default_key = NULL;
g_return_val_if_fail (name != NULL, NULL);
user_level = nautilus_preferences_user_level_clamp (user_level);
key = preferences_key_make (name);
default_key = g_strdup_printf ("%s/%s%s",
preferences_peek_defaults_path (),
preferences_peek_user_level_name_for_storage (user_level),
key);
g_free (key);
return default_key;
}
static char *
preferences_key_make_for_default_getter (const char *name,
int user_level)
{
char *default_key_for_getter = NULL;
gboolean done;
g_return_val_if_fail (name != NULL, NULL);
user_level = nautilus_preferences_user_level_clamp (user_level);
done = FALSE;
while (!done) {
default_key_for_getter = preferences_key_make_for_default (name, user_level);
done = (user_level == 0) || (!nautilus_gconf_is_default (default_key_for_getter));
if (!done) {
g_free (default_key_for_getter);
user_level--;
}
}
return default_key_for_getter;
}
static char *
preferences_key_make_for_visibility (const char *name)
{
char *default_key;
char *key;
g_return_val_if_fail (name != NULL, NULL);
key = preferences_key_make (name);
default_key = g_strdup_printf ("%s%s", preferences_peek_visibility_path (), key);
g_free (key);
return default_key;
}
static gboolean
preferences_preference_is_default (const char *name)
{
gboolean result;
char *key;
g_return_val_if_fail (name != NULL, FALSE);
key = preferences_key_make (name);
result = nautilus_gconf_is_default (key);
g_free (key);
return result;
}
static char *
preferences_make_user_level_filtered_key (const char *name)
{
char *key;
g_return_val_if_fail (name != NULL, NULL);
if (nautilus_preferences_visible_in_current_user_level (name)) {
key = preferences_key_make (name);
} else {
key = preferences_key_make_for_default (name, nautilus_preferences_get_user_level ());
}
return key;
}
static void
preferences_block_callbacks (const char *name)
{
PreferencesEntry *entry;
g_return_if_fail (name != NULL);
g_return_if_fail (preferences_is_initialized ());
entry = preferences_global_table_lookup_or_insert (name);
g_assert (entry != NULL);
entry->callbacks_blocked = TRUE;
}
static void
preferences_unblock_callbacks (const char *name)
{
PreferencesEntry *entry;
g_return_if_fail (name != NULL);
g_return_if_fail (preferences_is_initialized ());
entry = preferences_global_table_lookup_or_insert (name);
g_assert (entry != NULL);
entry->callbacks_blocked = FALSE;
}
/* Public preferences functions */
int
nautilus_preferences_get_visible_user_level (const char *name)
{
int result;
char *visible_key;
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (preferences_is_initialized (), FALSE);
visible_key = preferences_key_make_for_visibility (name);
result = nautilus_gconf_get_integer (visible_key);
g_free (visible_key);
return result;
}
void
nautilus_preferences_set_visible_user_level (const char *name,
int visible_user_level)
{
char *visible_key;
g_return_if_fail (name != NULL);
g_return_if_fail (preferences_is_initialized ());
visible_key = preferences_key_make_for_visibility (name);
nautilus_gconf_set_integer (visible_key, visible_user_level);
g_free (visible_key);
}
gboolean
nautilus_preferences_get_is_invisible (const char *name)
{
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (preferences_is_initialized (), FALSE);
return preferences_global_table_lookup_or_insert (name)->invisible;
}
void
nautilus_preferences_set_is_invisible (const char *name,
gboolean is_invisible)
{
g_return_if_fail (name != NULL);
g_return_if_fail (preferences_is_initialized ());
preferences_global_table_lookup_or_insert (name)->invisible = is_invisible;
}
void
nautilus_preferences_set_boolean (const char *name,
gboolean boolean_value)
{
char *key;
g_return_if_fail (name != NULL);
g_return_if_fail (preferences_is_initialized ());
key = preferences_key_make (name);
nautilus_gconf_set_boolean (key, boolean_value);
g_free (key);
nautilus_gconf_suggest_sync ();
}
static char *
preferences_key_make_for_getter (const char *name)
{
char *key;
g_return_val_if_fail (name != NULL, NULL);
g_return_val_if_fail (preferences_is_initialized (), NULL);
if (preferences_preference_is_default (name) || !nautilus_preferences_visible_in_current_user_level (name)) {
key = preferences_key_make_for_default_getter (name, nautilus_preferences_get_user_level ());
} else {
key = preferences_make_user_level_filtered_key (name);
}
return key;
}
gboolean
nautilus_preferences_get_boolean (const char *name)
{
gboolean result;
char *key;
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (preferences_is_initialized (), FALSE);
key = preferences_key_make_for_getter (name);
result = nautilus_gconf_get_boolean (key);
g_free (key);
return result;
}
void
nautilus_preferences_set_integer (const char *name,
int int_value)
{
char *key;
int old_value;
g_return_if_fail (name != NULL);
g_return_if_fail (preferences_is_initialized ());
key = preferences_key_make (name);
old_value = nautilus_preferences_get_integer (name);
if (int_value != old_value) {
nautilus_gconf_set_integer (key, int_value);
nautilus_gconf_suggest_sync ();
}
g_free (key);
}
int
nautilus_preferences_get_integer (const char *name)
{
int result;
char *key;
g_return_val_if_fail (name != NULL, 0);
g_return_val_if_fail (preferences_is_initialized (), 0);
key = preferences_key_make_for_getter (name);
result = nautilus_gconf_get_integer (key);
g_free (key);
return result;
}
void
nautilus_preferences_set (const char *name,
const char *string_value)
{
char *key;
char *old_value;
g_return_if_fail (name != NULL);
g_return_if_fail (preferences_is_initialized ());
key = preferences_key_make (name);
old_value = nautilus_preferences_get (name);
if (strcmp (string_value, old_value) != 0) {
nautilus_gconf_set_string (key, string_value);
nautilus_gconf_suggest_sync ();
}
g_free (key);
}
char *
nautilus_preferences_get (const char *name)
{
char *result;
char *key;
g_return_val_if_fail (name != NULL, NULL);
g_return_val_if_fail (preferences_is_initialized (), NULL);
key = preferences_key_make_for_getter (name);
result = nautilus_gconf_get_string (key);
g_free (key);
if (result == NULL) {
result = g_strdup ("");
}
return result;
}
void
nautilus_preferences_set_string_list (const char *name,
const EelStringList *string_list_value)
{
char *key;
GSList *slist;
g_return_if_fail (name != NULL);
g_return_if_fail (preferences_is_initialized ());
slist = eel_string_list_as_g_slist (string_list_value);
key = preferences_key_make (name);
nautilus_gconf_set_string_list (key, slist);
g_free (key);
eel_g_slist_free_deep (slist);
nautilus_gconf_suggest_sync ();
}
static gboolean
string_list_is_valid (const EelStringList *string_list,
const char *enumeration_id)
{
guint i;
char *nth;
gboolean bad;
g_return_val_if_fail (string_list != NULL, FALSE);
g_return_val_if_fail (enumeration_id != NULL, FALSE);
bad = FALSE;
for (i = 0; i < eel_string_list_get_length (string_list) && !bad; i++) {
nth = eel_string_list_nth (string_list, i);
bad = !eel_enumeration_id_contains_name (enumeration_id, nth);
g_free (nth);
}
return !bad;
}
EelStringList *
nautilus_preferences_get_string_list (const char *name)
{
EelStringList *result;
char *key;
GSList *slist;
PreferencesEntry *entry;
char *default_key;
g_return_val_if_fail (name != NULL, NULL);
g_return_val_if_fail (preferences_is_initialized (), NULL);
key = preferences_key_make_for_getter (name);
slist = nautilus_gconf_get_string_list (key);
g_free (key);
result = eel_string_list_new_from_g_slist (slist, TRUE);
eel_g_slist_free_deep (slist);
entry = preferences_global_table_lookup_or_insert (name);
g_assert (entry != NULL);
/* No enumeration_id so we're done */
if (entry->enumeration_id == NULL) {
return result;
}
/* Do a sanity check on the validity of the values */
if (string_list_is_valid (result, entry->enumeration_id)) {
return result;
}
/* Forget the bad value and use the default instead */
eel_string_list_free (result);
default_key = preferences_key_make_for_default_getter (name,
nautilus_preferences_get_user_level ());
slist = nautilus_gconf_get_string_list (default_key);
g_free (default_key);
result = eel_string_list_new_from_g_slist (slist, TRUE);
eel_g_slist_free_deep (slist);
/* Go the extra mile and fix the problem for the user */
preferences_block_callbacks (name);
nautilus_preferences_set_string_list (name, result);
preferences_unblock_callbacks (name);
return result;
}
int
nautilus_preferences_get_user_level (void)
{
char *user_level;
int result;
g_return_val_if_fail (preferences_is_initialized (), 0);
user_level = nautilus_gconf_get_string (USER_LEVEL_KEY);
if (eel_str_is_equal (user_level, "advanced")) {
result = NAUTILUS_USER_LEVEL_ADVANCED;
} else if (eel_str_is_equal (user_level, "intermediate")) {
result = NAUTILUS_USER_LEVEL_INTERMEDIATE;
} else if (eel_str_is_equal (user_level, "novice")) {
result = NAUTILUS_USER_LEVEL_NOVICE;
} else {
result = DEFAULT_USER_LEVEL;
}
g_free (user_level);
return result;
}
void
nautilus_preferences_set_user_level (int user_level)
{
g_return_if_fail (preferences_is_initialized ());
g_return_if_fail (nautilus_preferences_user_level_is_valid (user_level));
user_level = nautilus_preferences_user_level_clamp (user_level);
nautilus_gconf_set_string (USER_LEVEL_KEY, user_level_names_for_storage[user_level]);
nautilus_gconf_suggest_sync ();
}
void
nautilus_preferences_default_set_integer (const char *name,
int user_level,
int int_value)
{
char *default_key;
g_return_if_fail (name != NULL);
g_return_if_fail (preferences_is_initialized ());
default_key = preferences_key_make_for_default (name, user_level);
nautilus_gconf_set_integer (default_key, int_value);
g_free (default_key);
}
int
nautilus_preferences_default_get_integer (const char *name,
int user_level)
{
int result;
char *default_key;
g_return_val_if_fail (name != NULL, 0);
g_return_val_if_fail (preferences_is_initialized (), 0);
default_key = preferences_key_make_for_default (name, user_level);
result = nautilus_gconf_get_integer (default_key);
g_free (default_key);
return result;
}
void
nautilus_preferences_default_set_boolean (const char *name,
int user_level,
gboolean boolean_value)
{
char *default_key;
g_return_if_fail (name != NULL);
g_return_if_fail (preferences_is_initialized ());
default_key = preferences_key_make_for_default (name, user_level);
nautilus_gconf_set_boolean (default_key, boolean_value);
g_free (default_key);
}
gboolean
nautilus_preferences_default_get_boolean (const char *name,
int user_level)
{
gboolean result;
char *default_key;
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (preferences_is_initialized (), FALSE);
default_key = preferences_key_make_for_default (name, user_level);
result = nautilus_gconf_get_boolean (default_key);
g_free (default_key);
return result;
}
void
nautilus_preferences_default_set_string (const char *name,
int user_level,
const char *string_value)
{
char *default_key;
g_return_if_fail (name != NULL);
g_return_if_fail (preferences_is_initialized ());
default_key = preferences_key_make_for_default (name, user_level);
nautilus_gconf_set_string (default_key, string_value);
g_free (default_key);
}
char *
nautilus_preferences_default_get_string (const char *name,
int user_level)
{
char *result;
char *default_key;
g_return_val_if_fail (name != NULL, NULL);
g_return_val_if_fail (preferences_is_initialized (), NULL);
default_key = preferences_key_make_for_default (name, user_level);
result = nautilus_gconf_get_string (default_key);
g_free (default_key);
return result;
}
void
nautilus_preferences_default_set_string_list (const char *name,
int user_level,
const EelStringList *string_list_value)
{
char *default_key;
GSList *slist;
g_return_if_fail (name != NULL);
g_return_if_fail (preferences_is_initialized ());
slist = eel_string_list_as_g_slist (string_list_value);
default_key = preferences_key_make_for_default (name, user_level);
nautilus_gconf_set_string_list (default_key, slist);
g_free (default_key);
eel_g_slist_free_deep (slist);
}
EelStringList *
nautilus_preferences_default_get_string_list (const char *name,
int user_level)
{
EelStringList *result;
char *default_key;
GSList *slist;
g_return_val_if_fail (name != NULL, NULL);
g_return_val_if_fail (preferences_is_initialized (), NULL);
default_key = preferences_key_make_for_default (name, user_level);
slist = nautilus_gconf_get_string_list (default_key);
g_free (default_key);
result = eel_string_list_new_from_g_slist (slist, TRUE);
eel_g_slist_free_deep (slist);
return result;
}
/**
* preferences_callback_entry_invoke_function
*
* A function that invokes a callback from the given struct. It is meant to be fed to
* g_list_foreach ()
* @data: The list data privately maintained by the GList.
* @callback_data: The callback_data privately maintained by the GList.
**/
static void
preferences_callback_entry_invoke_function (gpointer data,
gpointer callback_data)
{
PreferencesCallbackEntry *callback_entry;
g_return_if_fail (data != NULL);
callback_entry = data;
(* callback_entry->callback) (callback_entry->callback_data);
}
/**
* preferences_entry_invoke_callbacks_if_needed
*
* @entry: A PreferencesEntry
*
* This function checks the cached value in the entry with the current
* value of the preference. If the value has changed, then callbacks
* are invoked and auto storage updated.
*
* We need this check because even though the GConf value of a preference
* could indeed have changed, its representation on the Nautilus side
* of things could still be the same. The best example of this is
* user level changes, where the value of the preference on the Nautilus
* end of things is determined by visibility.
**/
static void
preferences_entry_invoke_callbacks_if_needed (PreferencesEntry *entry)
{
GConfValue *new_value;
char *getter_key;
g_return_if_fail (entry != NULL);
getter_key = preferences_key_make_for_getter (entry->name);
new_value = nautilus_gconf_get_value (getter_key);
g_free (getter_key);
/* If the values are the same, then we dont need to invoke any callbacks */
if (nautilus_gconf_value_is_equal (entry->cached_value, new_value)) {
nautilus_gconf_value_free (new_value);
return;
}
/* Update the auto storage preferences */
if (entry->auto_storage_list != NULL) {
preferences_entry_update_auto_storage (entry);
}
/* Store the new cached value */
nautilus_gconf_value_free (entry->cached_value);
entry->cached_value = new_value;
/* Dont invoke callbacks if the entry is blocked */
if (entry->callbacks_blocked) {
return;
}
/* Invoke callbacks for this entry if any */
if (entry->callback_list != NULL) {
g_list_foreach (entry->callback_list,
preferences_callback_entry_invoke_function,
NULL);
}
}
static void
update_auto_string (gpointer data, gpointer callback_data)
{
char **storage;
const char *value;
g_return_if_fail (data != NULL);
g_return_if_fail (callback_data != NULL);
storage = (char **)data;
value = (const char *)callback_data;
g_free (*storage);
*(char **)storage = g_strdup (value);
}
static void
update_auto_string_list (gpointer data, gpointer callback_data)
{
EelStringList **storage;
const EelStringList *value;
g_return_if_fail (data != NULL);
g_return_if_fail (callback_data != NULL);
storage = (EelStringList **)data;
value = (const EelStringList *)callback_data;
eel_string_list_free (*storage);
*(EelStringList **)storage = eel_string_list_copy (value);
}
static void
update_auto_integer_or_boolean (gpointer data, gpointer callback_data)
{
g_return_if_fail (data != NULL);
*(int *)data = GPOINTER_TO_INT (callback_data);
}
static void
preferences_entry_update_auto_storage (PreferencesEntry *entry)
{
char *new_string_value;
EelStringList *new_string_list_value;
int new_int_value;
gboolean new_boolean_value;
switch (entry->type) {
case PREFERENCE_STRING:
new_string_value = nautilus_preferences_get (entry->name);
g_list_foreach (entry->auto_storage_list,
update_auto_string,
new_string_value);
g_free (new_string_value);
break;
case PREFERENCE_STRING_LIST:
new_string_list_value = nautilus_preferences_get_string_list (entry->name);
g_list_foreach (entry->auto_storage_list,
update_auto_string_list,
new_string_list_value);
eel_string_list_free (new_string_list_value);
break;
case PREFERENCE_INTEGER:
new_int_value = nautilus_preferences_get_integer (entry->name);
g_list_foreach (entry->auto_storage_list,
update_auto_integer_or_boolean,
GINT_TO_POINTER (new_int_value));
break;
case PREFERENCE_BOOLEAN:
new_boolean_value = nautilus_preferences_get_boolean (entry->name);
g_list_foreach (entry->auto_storage_list,
update_auto_integer_or_boolean,
GINT_TO_POINTER (new_boolean_value));
break;
default:
g_warning ("unexpected preferences type %d in preferences_entry_update_auto_storage", entry->type);
}
}
static void
preferences_something_changed_notice (GConfClient *client,
guint connection_id,
GConfEntry *entry,
gpointer notice_data)
{
g_return_if_fail (entry != NULL);
g_return_if_fail (entry->key != NULL);
g_return_if_fail (notice_data != NULL);
preferences_entry_invoke_callbacks_if_needed (notice_data);
}
static void
preferences_global_table_check_changes_function (gpointer key,
gpointer value,
gpointer user_data)
{
PreferencesEntry *entry;
g_return_if_fail (key != NULL);
g_return_if_fail (value != NULL);
entry = value;
g_return_if_fail (entry->name != NULL);
/* We dont worry about the 'user_level' itself for recursive reasons */
if (preferences_preference_is_user_level (entry->name)) {
return;
}
preferences_entry_invoke_callbacks_if_needed (entry);
}
static void
preferences_entry_update_cached_value (PreferencesEntry *entry)
{
char *getter_key;
g_return_if_fail (entry != NULL);
nautilus_gconf_value_free (entry->cached_value);
getter_key = preferences_key_make_for_getter (entry->name);
entry->cached_value = nautilus_gconf_get_value (getter_key);
g_free (getter_key);
}
static void
preferences_user_level_changed_notice (GConfClient *client,
guint connection_id,
GConfEntry *gconf_entry,
gpointer user_data)
{
g_return_if_fail (gconf_entry != NULL);
g_return_if_fail (gconf_entry->key != NULL);
g_return_if_fail (eel_str_has_suffix (gconf_entry->key, "user_level"));
g_hash_table_foreach (preferences_global_table_get_global (),
preferences_global_table_check_changes_function,
NULL);
}
static void
preferences_entry_ensure_gconf_connection (PreferencesEntry *entry)
{
char *key;
/*
* We install only one gconf notification for each preference entry.
* Otherwise, we would invoke the installed callbacks more than once
* per registered callback.
*/
if (entry->gconf_connection_id != NAUTILUS_GCONF_UNDEFINED_CONNECTION) {
return;
}
g_return_if_fail (entry->name != NULL);
key = preferences_key_make (entry->name);
entry->gconf_connection_id = nautilus_gconf_notification_add (key,
preferences_something_changed_notice,
entry);
g_free (key);
g_return_if_fail (entry->gconf_connection_id != NAUTILUS_GCONF_UNDEFINED_CONNECTION);
/* Update the cached value.
* From now onwards the cached value will be updated
* each time preferences_something_changed_notice() triggers
* so that it can be later compared with new values to
* determine if the gconf value is different from the
* Nautilus value.
*/
preferences_entry_update_cached_value (entry);
}
/**
* preferences_entry_add_callback
*
* Add a callback to a pref node. Callbacks are fired whenever
* the pref value changes.
* @preferences_entry: The hash node.
* @callback: The user-supplied callback.
* @callback_data: The user-supplied closure.
**/
static void
preferences_entry_add_callback (PreferencesEntry *entry,
NautilusPreferencesCallback callback,
gpointer callback_data)
{
PreferencesCallbackEntry *callback_entry;
g_return_if_fail (entry != NULL);
g_return_if_fail (callback != NULL);
callback_entry = g_new0 (PreferencesCallbackEntry, 1);
callback_entry->callback = callback;
callback_entry->callback_data = callback_data;
g_return_if_fail (callback_entry != NULL);
entry->callback_list = g_list_append (entry->callback_list, callback_entry);
preferences_entry_ensure_gconf_connection (entry);
}
/**
* preferences_entry_add_auto_storage
*
* Add an auto-storage variable to a pref node. The variable will
* be updated to match the pref value whenever the pref
* the pref value changes.
* @preferences_entry: The hash node.
* @storage: The user-supplied location at which to store the value.
* @type: Which type of variable this is.
**/
static void
preferences_entry_add_auto_storage (PreferencesEntry *entry,
gpointer storage,
PreferenceType type)
{
g_return_if_fail (entry != NULL);
g_return_if_fail (storage != NULL);
g_return_if_fail (entry->type == 0 || entry->type == type);
g_return_if_fail (g_list_find (entry->auto_storage_list, storage) == NULL);
entry->type = type;
entry->auto_storage_list = g_list_append (entry->auto_storage_list, storage);
preferences_entry_ensure_gconf_connection (entry);
}
static void
preferences_entry_check_remove_connection (PreferencesEntry *entry)
{
/*
* If there are no callbacks or auto-storage variables left in the entry,
* remove the gconf notification.
*/
if (entry->callback_list != NULL || entry->auto_storage_list != NULL) {
return;
}
nautilus_gconf_notification_remove (entry->gconf_connection_id);
entry->gconf_connection_id = NAUTILUS_GCONF_UNDEFINED_CONNECTION;
}
/**
* preferences_entry_remove_callback
*
* remove a callback from a pref entry. Both the callback and the callback_data must
* match in order for a callback to be removed from the entry.
* @preferences_entry: The hash entry.
* @callback: The user-supplied callback.
* @callback_data: The user-supplied closure.
**/
static void
preferences_entry_remove_callback (PreferencesEntry *entry,
NautilusPreferencesCallback callback,
gpointer callback_data)
{
GList *new_list;
const GList *node;
g_return_if_fail (entry != NULL);
g_return_if_fail (callback != NULL);
g_return_if_fail (entry->callback_list != NULL);
new_list = g_list_copy (entry->callback_list);
for (node = new_list; node != NULL; node = node->next) {
PreferencesCallbackEntry *callback_entry = node->data;
g_return_if_fail (callback_entry != NULL);
if (callback_entry->callback == callback &&
callback_entry->callback_data == callback_data) {
entry->callback_list = g_list_remove (entry->callback_list,
callback_entry);
preferences_callback_entry_free (callback_entry);
}
}
g_list_free (new_list);
preferences_entry_check_remove_connection (entry);
}
/**
* preferences_entry_remove_auto_storage
*
* remove an auto-storage variable from a pref entry.
* @preferences_entry: The hash entry.
* @storage: The user-supplied location.
**/
static void
preferences_entry_remove_auto_storage (PreferencesEntry *entry,
gpointer storage)
{
GList *new_list;
const GList *node;
gpointer storage_in_entry;
g_return_if_fail (entry != NULL);
g_return_if_fail (storage != NULL);
g_return_if_fail (entry->auto_storage_list != NULL);
new_list = g_list_copy (entry->auto_storage_list);
for (node = new_list; node != NULL; node = node->next) {
storage_in_entry = node->data;
g_return_if_fail (storage_in_entry != NULL);
if (storage_in_entry == storage) {
entry->auto_storage_list = g_list_remove (entry->auto_storage_list,
storage);
switch (entry->type) {
case PREFERENCE_STRING:
update_auto_string (storage, NULL);
break;
case PREFERENCE_STRING_LIST:
update_auto_string_list (storage, NULL);
break;
case PREFERENCE_BOOLEAN:
case PREFERENCE_INTEGER:
update_auto_integer_or_boolean (storage, 0);
break;
default:
g_warning ("unexpected preference type %d in preferences_entry_remove_auto_storage", entry->type);
}
}
}
g_list_free (new_list);
preferences_entry_check_remove_connection (entry);
}
/**
* preferences_callback_entry_free
*
* Free a callback info struct.
* @preferences_callback_entry: The struct to free.
**/
static void
preferences_callback_entry_free (PreferencesCallbackEntry *callback_entry)
{
g_return_if_fail (callback_entry != NULL);
callback_entry->callback = NULL;
callback_entry->callback_data = NULL;
g_free (callback_entry);
}
/**
* preferences_callback_entry_free_func
*
* A function that frees a callback info struct. It is meant to be fed to
* g_list_foreach ()
* @data: The list data privately maintained by the GList.
* @callback_data: The callback_data privately maintained by the GList.
**/
static void
preferences_callback_entry_free_func (gpointer data,
gpointer callback_data)
{
g_return_if_fail (data != NULL);
preferences_callback_entry_free (data);
}
/**
* preferences_entry_free
*
* Free a preference hash node's members along with the node itself.
* @preferences_hash_node: The node to free.
**/
static void
preferences_entry_free (PreferencesEntry *entry)
{
g_return_if_fail (entry != NULL);
nautilus_gconf_notification_remove (entry->gconf_connection_id);
entry->gconf_connection_id = NAUTILUS_GCONF_UNDEFINED_CONNECTION;
g_list_free (entry->auto_storage_list);
eel_g_list_free_deep_custom (entry->callback_list,
preferences_callback_entry_free_func,
NULL);
entry->auto_storage_list = NULL;
entry->callback_list = NULL;
g_free (entry->name);
g_free (entry->description);
g_free (entry->enumeration_id);
nautilus_gconf_value_free (entry->cached_value);
g_free (entry);
}
/**
* preferences_entry_free_func
*
* A function that frees a pref hash node. It is meant to be fed to
* g_hash_table_foreach ()
* @key: The hash key privately maintained by the GHashTable.
* @value: The hash value privately maintained by the GHashTable.
* @callback_data: The callback_data privately maintained by the GHashTable.
**/
static void
preferences_entry_free_func (gpointer key,
gpointer value,
gpointer callback_data)
{
g_assert (value != NULL);
preferences_entry_free (value);
}
static void
preferences_global_table_free (void)
{
if (global_table == NULL) {
return;
}
g_hash_table_foreach (global_table, preferences_entry_free_func, NULL);
g_hash_table_destroy (global_table);
global_table = NULL;
g_free (storage_path);
storage_path = NULL;
g_free (defaults_path);
defaults_path = NULL;
g_free (visiblity_path);
visiblity_path = NULL;
}
static GHashTable *
preferences_global_table_get_global (void)
{
static gboolean at_exit_handler_added = FALSE;
if (global_table == NULL) {
global_table = g_hash_table_new (g_str_hash, g_str_equal);
if (!at_exit_handler_added) {
at_exit_handler_added = TRUE;
g_atexit (preferences_global_table_free);
}
}
return global_table;
}
static PreferencesEntry *
preferences_global_table_lookup (const char *name)
{
g_return_val_if_fail (name != NULL, NULL);
g_return_val_if_fail (preferences_global_table_get_global () != NULL, NULL);
return g_hash_table_lookup (preferences_global_table_get_global (), name);
}
static PreferencesEntry *
preferences_global_table_insert (const char *name)
{
PreferencesEntry *entry;
g_return_val_if_fail (name != NULL, NULL);
g_return_val_if_fail (preferences_global_table_get_global () != NULL, NULL);
g_return_val_if_fail (preferences_global_table_lookup (name) == NULL, NULL);
entry = g_new0 (PreferencesEntry, 1);
entry->name = g_strdup (name);
g_hash_table_insert (preferences_global_table_get_global (), entry->name, entry);
g_return_val_if_fail (entry == preferences_global_table_lookup (name), NULL);
/* Update the cached value for the first time.
*
* We need to do this because checks for value changes
* happen not only as a result of callbacks triggering, but
* also as a result of user_level changes. When a user level
* changes, all the preferences entries are iterated to invoke
* callbacks for those that changed as a result.
*
* See preferences_global_table_check_changes_function().
*/
preferences_entry_update_cached_value (entry);
return entry;
}
static PreferencesEntry *
preferences_global_table_lookup_or_insert (const char *name)
{
PreferencesEntry *entry;
g_return_val_if_fail (name != NULL, NULL);
entry = preferences_global_table_lookup (name);
if (entry != NULL) {
return entry;
}
entry = preferences_global_table_insert (name);
g_assert (entry != NULL);
return entry;
}
void
nautilus_preferences_add_callback (const char *name,
NautilusPreferencesCallback callback,
gpointer callback_data)
{
PreferencesEntry *entry;
g_return_if_fail (name != NULL);
g_return_if_fail (callback != NULL);
g_return_if_fail (preferences_is_initialized ());
entry = preferences_global_table_lookup_or_insert (name);
g_assert (entry != NULL);
preferences_entry_add_callback (entry, callback, callback_data);
}
void
nautilus_preferences_add_auto_string (const char *name,
const char **storage)
{
PreferencesEntry *entry;
char *value;
g_return_if_fail (name != NULL);
g_return_if_fail (storage != NULL);
g_return_if_fail (preferences_is_initialized ());
entry = preferences_global_table_lookup_or_insert (name);
g_assert (entry != NULL);
preferences_entry_add_auto_storage (entry, storage, PREFERENCE_STRING);
value = nautilus_preferences_get (entry->name);
update_auto_string (storage, value);
g_free (value);
}
void
nautilus_preferences_add_auto_string_list (const char *name,
const EelStringList **storage)
{
PreferencesEntry *entry;
EelStringList *value;
g_return_if_fail (name != NULL);
g_return_if_fail (storage != NULL);
g_return_if_fail (preferences_is_initialized ());
entry = preferences_global_table_lookup_or_insert (name);
g_assert (entry != NULL);
preferences_entry_add_auto_storage (entry, storage, PREFERENCE_STRING_LIST);
value = nautilus_preferences_get_string_list (entry->name);
update_auto_string_list (storage, value);
eel_string_list_free (value);
}
void
nautilus_preferences_add_auto_integer (const char *name,
int *storage)
{
PreferencesEntry *entry;
int value;
g_return_if_fail (name != NULL);
g_return_if_fail (storage != NULL);
g_return_if_fail (preferences_is_initialized ());
entry = preferences_global_table_lookup_or_insert (name);
g_assert (entry != NULL);
preferences_entry_add_auto_storage (entry, storage, PREFERENCE_INTEGER);
value = nautilus_preferences_get_integer (entry->name);
update_auto_integer_or_boolean (storage, GINT_TO_POINTER (value));
}
void
nautilus_preferences_add_auto_boolean (const char *name,
gboolean *storage)
{
PreferencesEntry *entry;
gboolean value;
g_return_if_fail (name != NULL);
g_return_if_fail (storage != NULL);
g_return_if_fail (preferences_is_initialized ());
entry = preferences_global_table_lookup_or_insert (name);
g_assert (entry != NULL);
preferences_entry_add_auto_storage (entry, storage, PREFERENCE_BOOLEAN);
value = nautilus_preferences_get_boolean (entry->name);
update_auto_integer_or_boolean (storage, GINT_TO_POINTER (value));
}
void
nautilus_preferences_remove_auto_string (const char *name,
const char **storage)
{
PreferencesEntry *entry;
g_return_if_fail (name != NULL);
g_return_if_fail (storage != NULL);
g_return_if_fail (preferences_is_initialized ());
entry = preferences_global_table_lookup (name);
if (entry == NULL) {
g_warning ("Trying to remove auto-string for %s without adding it first.", name);
return;
}
preferences_entry_remove_auto_storage (entry, storage);
}
void
nautilus_preferences_remove_auto_string_list (const char *name,
const EelStringList **storage)
{
PreferencesEntry *entry;
g_return_if_fail (name != NULL);
g_return_if_fail (storage != NULL);
g_return_if_fail (preferences_is_initialized ());
entry = preferences_global_table_lookup (name);
if (entry == NULL) {
g_warning ("Trying to remove auto-string for %s without adding it first.", name);
return;
}
preferences_entry_remove_auto_storage (entry, storage);
}
void
nautilus_preferences_remove_auto_integer (const char *name,
int *storage)
{
PreferencesEntry *entry;
g_return_if_fail (name != NULL);
g_return_if_fail (storage != NULL);
g_return_if_fail (preferences_is_initialized ());
entry = preferences_global_table_lookup (name);
if (entry == NULL) {
g_warning ("Trying to remove auto-integer for %s without adding it first.", name);
return;
}
preferences_entry_remove_auto_storage (entry, storage);
}
void
nautilus_preferences_remove_auto_boolean (const char *name,
gboolean *storage)
{
PreferencesEntry *entry;
g_return_if_fail (name != NULL);
g_return_if_fail (storage != NULL);
g_return_if_fail (preferences_is_initialized ());
entry = preferences_global_table_lookup (name);
if (entry == NULL) {
g_warning ("Trying to remove auto-boolean for %s without adding it first.", name);
return;
}
preferences_entry_remove_auto_storage (entry, storage);
}
typedef struct
{
char *name;
NautilusPreferencesCallback callback;
gpointer callback_data;
} WhileAliveData;
static void
preferences_while_alive_disconnector (GtkObject *object, gpointer callback_data)
{
WhileAliveData *data;
g_return_if_fail (GTK_IS_OBJECT (object));
g_return_if_fail (callback_data != NULL);
data = callback_data;
nautilus_preferences_remove_callback (data->name,
data->callback,
data->callback_data);
g_free (data->name);
g_free (data);
}
void
nautilus_preferences_add_callback_while_alive (const char *name,
NautilusPreferencesCallback callback,
gpointer callback_data,
GtkObject *alive_object)
{
WhileAliveData *data;
g_return_if_fail (name != NULL);
g_return_if_fail (callback != NULL);
g_return_if_fail (GTK_IS_OBJECT (alive_object));
g_return_if_fail (preferences_is_initialized ());
data = g_new (WhileAliveData, 1);
data->name = g_strdup (name);
data->callback = callback;
data->callback_data = callback_data;
nautilus_preferences_add_callback (name, callback, callback_data);
gtk_signal_connect (alive_object,
"destroy",
GTK_SIGNAL_FUNC (preferences_while_alive_disconnector),
data);
}
void
nautilus_preferences_remove_callback (const char *name,
NautilusPreferencesCallback callback,
gpointer callback_data)
{
PreferencesEntry *entry;
g_return_if_fail (name != NULL);
g_return_if_fail (callback != NULL);
g_return_if_fail (preferences_is_initialized ());
entry = preferences_global_table_lookup (name);
if (entry == NULL) {
g_warning ("Trying to remove a callback for %s without adding it first.", name);
return;
}
preferences_entry_remove_callback (entry, callback, callback_data);
}
void
nautilus_preferences_set_description (const char *name,
const char *description)
{
PreferencesEntry *entry;
g_return_if_fail (name != NULL);
g_return_if_fail (description != NULL);
g_return_if_fail (preferences_is_initialized ());
entry = preferences_global_table_lookup_or_insert (name);
g_assert (entry != NULL);
g_free (entry->description);
entry->description = g_strdup (description);
}
char *
nautilus_preferences_get_description (const char *name)
{
PreferencesEntry *entry;
g_return_val_if_fail (name != NULL, NULL);
g_return_val_if_fail (preferences_is_initialized (), NULL);
entry = preferences_global_table_lookup_or_insert (name);
return g_strdup (entry->description ? entry->description : "");
}
void
nautilus_preferences_set_enumeration_id (const char *name,
const char *enumeration_id)
{
PreferencesEntry *entry;
g_return_if_fail (name != NULL);
g_return_if_fail (enumeration_id != NULL);
g_return_if_fail (preferences_is_initialized ());
entry = preferences_global_table_lookup_or_insert (name);
g_assert (entry != NULL);
g_free (entry->enumeration_id);
entry->enumeration_id = g_strdup (enumeration_id);
}
char *
nautilus_preferences_get_enumeration_id (const char *name)
{
PreferencesEntry *entry;
g_return_val_if_fail (name != NULL, NULL);
g_return_val_if_fail (preferences_is_initialized (), NULL);
entry = preferences_global_table_lookup_or_insert (name);
return entry->enumeration_id ? g_strdup (entry->enumeration_id) : NULL;
}
char *
nautilus_preferences_get_user_level_name_for_display (int user_level)
{
g_return_val_if_fail (preferences_is_initialized (), NULL);
user_level = nautilus_preferences_user_level_clamp (user_level);
return g_strdup (gettext (user_level_names_for_display[user_level]));
}
char *
nautilus_preferences_get_user_level_name_for_storage (int user_level)
{
g_return_val_if_fail (preferences_is_initialized (), NULL);
return g_strdup (preferences_peek_user_level_name_for_storage (user_level));
}
int
nautilus_preferences_user_level_clamp (int user_level)
{
g_return_val_if_fail (preferences_is_initialized (), 0);
return CLAMP (user_level, NAUTILUS_USER_LEVEL_NOVICE, NAUTILUS_USER_LEVEL_ADVANCED);
}
gboolean
nautilus_preferences_user_level_is_valid (int user_level)
{
g_return_val_if_fail (preferences_is_initialized (), FALSE);
return user_level == nautilus_preferences_user_level_clamp (user_level);
}
gboolean
nautilus_preferences_monitor_directory (const char *directory)
{
g_return_val_if_fail (preferences_is_initialized (), FALSE);
return nautilus_gconf_monitor_add (directory);
}
gboolean
nautilus_preferences_visible_in_current_user_level (const char *name)
{
int user_level;
int visible_user_level;
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (preferences_is_initialized (), FALSE);
user_level = nautilus_preferences_get_user_level ();
visible_user_level = nautilus_preferences_get_visible_user_level (name);
return visible_user_level <= user_level;
}
gboolean
nautilus_preferences_is_visible (const char *name)
{
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (preferences_is_initialized (), FALSE);
if (!nautilus_preferences_visible_in_current_user_level (name)) {
return FALSE;
}
return !preferences_global_table_lookup_or_insert (name)->invisible;
}
static void
preferences_remove_user_level_notice (void)
{
nautilus_gconf_notification_remove (user_level_changed_connection_id);
user_level_changed_connection_id = NAUTILUS_GCONF_UNDEFINED_CONNECTION;
}
void
nautilus_preferences_initialize (const char *path)
{
g_return_if_fail (eel_strlen (path) > 0);
if (initialized) {
return;
}
initialized = TRUE;
user_level_changed_connection_id = nautilus_gconf_notification_add (USER_LEVEL_KEY,
preferences_user_level_changed_notice,
NULL);
g_atexit (preferences_remove_user_level_notice);
preferences_set_storage_path (path);
}
#if !defined (NAUTILUS_OMIT_SELF_CHECK)
#define CONSTANT_STORAGE_PATH "/apps/self-check-nautilus"
#define CHECK_BOOLEAN(name__, value__) \
G_STMT_START { \
nautilus_preferences_set_boolean ((name__), (value__)); \
EEL_CHECK_BOOLEAN_RESULT (nautilus_preferences_get_boolean (name__), (value__)); \
} G_STMT_END
#define CHECK_INTEGER(name__, value__) \
G_STMT_START { \
nautilus_preferences_set_integer ((name__), (value__)); \
EEL_CHECK_INTEGER_RESULT (nautilus_preferences_get_integer (name__), (value__)); \
} G_STMT_END
#define CHECK_STRING(name__, value__) \
G_STMT_START { \
nautilus_preferences_set ((name__), (value__)); \
EEL_CHECK_STRING_RESULT (nautilus_preferences_get (name__), (value__)); \
} G_STMT_END
void
nautilus_self_check_preferences (void)
{
int original_user_level;
original_user_level = nautilus_preferences_get_user_level ();
EEL_CHECK_INTEGER_RESULT (nautilus_preferences_get_integer ("self-check/neverset/i"), 0);
EEL_CHECK_STRING_RESULT (nautilus_preferences_get ("self-check/neverset/s"), "");
EEL_CHECK_BOOLEAN_RESULT (nautilus_preferences_get_boolean ("self-check/neverset/b"), FALSE);
nautilus_preferences_set_user_level (0);
CHECK_INTEGER ("self-check/i", 666);
CHECK_BOOLEAN ("self-check/b", TRUE);
CHECK_STRING ("self-check/s", "foo");
/* Restore the original user level */
nautilus_preferences_set_user_level (original_user_level);
}
#endif /* !NAUTILUS_OMIT_SELF_CHECK */