gconf-0.11.0: System/Gnome/GConf/GConfClient.chs
{-# LANGUAGE CPP, FlexibleContexts #-}
-- -*-haskell-*-
-- GIMP Toolkit (GTK) GConf API
--
-- Author : Duncan Coutts
-- Created: 16 April 2004
--
-- Copyright (c) 2004 Duncan Coutts
--
-- This 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.
--
-- This library is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-- Library General Public License for more details.
--
-- | GConf is a system for maintaining program configuration information.
--
-- The main difference between GConf and traditional configuration file
-- API's is that GConf is \'live\'. Applications can be notified of changes
-- in their configuration, it allows desktop wide setting
-- to be propogated without restarting all applications, or multiple instances
-- of a single application to synchronise their configuration. It is similar
-- in many ways to the Win32 Registry with its directory-like structure.
--
-- This module only binds the client library which is the only interface that
-- normal applications should need. Only special GConf apps need lower level
-- access.
--
-- * Some low level functions have not been bound
-- eg @gconf_client_get_for_engine@
--
module System.Gnome.GConf.GConfClient (
-- * Data types
--
GConf, GConfPreloadType, GConfError,
-- * Creation operation
--
gconfGetDefault,
-- * Registering for change notifications
--
gconfAddDir, gconfRemoveDir,
gconfNotifyAdd, gconfNotifyRemove,
onValueChanged, afterValueChanged,
-- * Getting and setting configuration values
--
gconfGet, gconfSet,
-- gconfGetFloat,
-- gconfGetInt,
-- gconfGetString,
-- gconfGetBool,
-- gconfGetSchema,
-- gconfGetList,
-- gconfGetPair,
gconfGetWithoutDefault,
gconfGetDefaultFromSchema,
gconfUnset,
-- * Caching control
--
gconfClearCache, gconfPreload,
gconfSuggestSync,
-- * Navigation
--
gconfAllEntries,
gconfAllDirs,
gconfDirExists,
-- * GConfValue
--
GConfValueClass,
GConfPrimitiveValueClass,
GConfValue,
GConfValueDyn(..),
) where
import Control.Monad (liftM, when)
import Data.IORef (newIORef, readIORef, writeIORef)
import System.IO.Unsafe (unsafePerformIO)
import System.Glib.FFI
import System.Glib.UTFString
import System.Glib.GList
import System.Glib.GError (GErrorDomain, GErrorClass(..), propagateGError)
import System.Glib.GObject (constructNewGObject)
{#import System.Gnome.GConf.Types#}
{#import System.Gnome.GConf.Signals#}
{#import System.Gnome.GConf.GConfValue#}
{# context lib="gconf" prefix ="gconf_client" #}
{# enum GConfClientPreloadType as GConfPreloadType {underscoreToCase} #}
-- | The "GError" exceptions that can be thrown by functions in this module
{# enum GConfError {underscoreToCase} #}
gconfErrorDomain :: GErrorDomain
gconfErrorDomain = unsafePerformIO {#call unsafe gconf_error_quark#}
instance GErrorClass GConfError where
gerrorDomain _ = gconfErrorDomain
{# pointer *GConfEntry newtype #}
-- Operations
-- Creation operations
-- | Create a new GConf object using the default configuration engine.
--
gconfGetDefault :: IO GConf
gconfGetDefault = do
-- make sure the glib type system is initialised, which it might not be if
-- we're using gconf without using gtk and initGUI.
-- It is safe to call g_type_init more than once.
{# call g_type_init #}
constructNewGObject mkGConf {# call gconf_client_get_default #}
-- Registering for change notifications
-- | Add a directory to the list of directories the
-- GConf will watch. You should use gconfNotifyAdd to associate
-- change handlers with specific keys.
--
-- * Added directories may not overlap. That is, if you add \"\/foo\", you may
-- not add \"\/foo\/bar\". However you can add \"\/foo\" and \"\/bar\". You can
-- also add \"\/foo\" multiple times; if you add a directory multiple times, it
-- will not be removed until you call 'gconfRemoveDir' an equal number of
-- times.
--
-- * Note that the watch is recursive, all keys below the given directory will
-- be watched. So it would be a bad idea to watch the root \"\/\".
--
gconfAddDir :: GConf -> String -> IO ()
gconfAddDir gc key = gconfAddDirWithPreload gc key PreloadNone
-- | Like 'gconfAddDir' but with the option to specify a preload mode.
--
-- As a rule of thumb, if you plan to get the value of almost all the keys in a
-- directory, preloading that directory will probably enhance performance. If
-- you plan to use only half the keys, preloading is likely a bad idea.
--
-- * PreloadNone specifies that no preload occurs
-- * PreloadOnelevel loads the immediate children of the directory
-- * PreloadRecursive loads all children of the directory and its
-- subdirectories, recursively.
--
gconfAddDirWithPreload :: GConf -> String -> GConfPreloadType -> IO ()
gconfAddDirWithPreload gc key preload =
propagateGError $ \gerrorPtr ->
withCString key $ \strPtr ->
{# call gconf_client_add_dir #} gc strPtr
(fromIntegral $ fromEnum preload) gerrorPtr
-- | Remove a directory from the list created with 'gconfAddDir'. If any
-- notifications have been added below this directory with 'gconfNotifyAdd',
-- those notifications will be disabled until you re-add the removed directory.
--
-- * Note that if a directory has been added multiple times, you must remove it
-- the same number of times before the remove takes effect.
--
gconfRemoveDir :: GConf -> String -> IO ()
gconfRemoveDir gc key =
propagateGError $ \gerrorPtr ->
withCString key $ \strPtr ->
{# call gconf_client_remove_dir #} gc strPtr gerrorPtr
{#pointer GFreeFunc#}
foreign import ccall "wrapper" mkDestructor :: IO () -> IO GFreeFunc
type GConfClientNotifyFunc = Ptr () -> --GConfClient *client
{#type guint#} -> --guint cnxn_id
Ptr () -> --GConfEntry *entry
Ptr () -> --gpointer user_data
IO ()
foreign import ccall "wrapper" mkHandler_GConfClientNotifyFunc ::
GConfClientNotifyFunc -> IO (FunPtr GConfClientNotifyFunc)
connect_GConfClientNotifyFunc ::
GConf ->
String ->
(GConfEntry -> IO ()) ->
IO GConfConnectId
connect_GConfClientNotifyFunc gc key user = do
hPtr <- mkHandler_GConfClientNotifyFunc
(\_ _ entryPtr _ -> user (GConfEntry $ castPtr entryPtr))
dRef <- newIORef nullFunPtr
dPtr <- mkDestructor $ do
freeHaskellFunPtr hPtr
dPtr <- readIORef dRef
freeHaskellFunPtr dPtr
writeIORef dRef dPtr
cnxId <- propagateGError $ \gerrorPtr ->
withCString key $ \strPtr ->
{# call gconf_client_notify_add #} gc strPtr
(castFunPtr hPtr) nullPtr dPtr gerrorPtr
return $ GConfConnectId cnxId
newtype GConfConnectId = GConfConnectId {#type guint#}
gconfNotifyAdd :: GConfValueClass value =>
GConf ->
String ->
(String -> value -> IO ()) ->
IO GConfConnectId
gconfNotifyAdd gc key handler =
connect_GConfClientNotifyFunc gc key (convertValueChangedHandler handler)
where convertValueChangedHandler :: GConfValueClass value =>
(String -> value -> IO ()) ->
(GConfEntry -> IO ())
convertValueChangedHandler handler entry = do
keyStrPtr <- {# call unsafe gconf_entry_get_key #} entry
valuePtr <- {# call unsafe gconf_entry_get_value #} entry
key <- peekUTFString keyStrPtr
value <- marshalFromGConfValue (GConfValue valuePtr)
handler key value
gconfNotifyRemove :: GConf -> GConfConnectId -> IO ()
gconfNotifyRemove gc (GConfConnectId cxid) =
{# call gconf_client_notify_remove #} gc cxid
-- Getting and setting configuration values
-- | Gets the value of a configuration key.
--
gconfGet :: GConfValueClass value => GConf
-> String -- ^ Name of the key
-> IO value
gconfGet gc key = do
value <- propagateGError $ \gerrorPtr ->
withCString key $ \strPtr ->
{# call gconf_client_get #} gc strPtr gerrorPtr
marshalFromGConfValue (GConfValue value)
gconfGetInt :: GConf -> String -> IO Int
gconfGetInt = gconfGet
gconfGetBool :: GConf -> String -> IO Bool
gconfGetBool = gconfGet
gconfGetFloat :: GConf -> String -> IO Double
gconfGetFloat = gconfGet
gconfGetString :: GConf -> String -> IO String
gconfGetString = gconfGet
gconfGetPair :: GConfValueClass (a,b) =>
GConf -> String -> IO (a, b)
gconfGetPair = gconfGet
gconfGetList :: GConfValueClass [a] =>
GConf -> String -> IO [a]
gconfGetList = gconfGet
-- | Sets the value of a configuration key.
--
gconfSet :: GConfValueClass value => GConf
-> String -- ^ Name of the key
-> value -- ^ New value
-> IO ()
gconfSet gc key val = do
value@(GConfValue ptr) <- marshalToGConfValue val
if ptr == nullPtr
then gconfUnset gc key
else propagateGError $ \gerrorPtr ->
withCString key $ \strPtr ->
{# call gconf_client_set #} gc strPtr value gerrorPtr
gconfSetInt :: GConf -> String -> Int -> IO ()
gconfSetInt = gconfSet
gconfSetBool :: GConf -> String -> Bool -> IO ()
gconfSetBool = gconfSet
gconfSetFloat :: GConf -> String -> Double -> IO ()
gconfSetFloat = gconfSet
gconfSetString :: GConf -> String -> String -> IO ()
gconfSetString = gconfSet
gconfSetPair :: GConfValueClass (a,b) =>
GConf -> String -> (a, b) -> IO ()
gconfSetPair = gconfSet
gconfSetList :: GConfValueClass [a] =>
GConf -> String -> [a] -> IO ()
gconfSetList = gconfSet
-- | Gets the value of a configuration key.
--
-- Same as 'gconfGet', but doesn't look for a default value if the key is
-- unset.
--
gconfGetWithoutDefault :: GConfValueClass value =>
GConf -> String -> IO value
gconfGetWithoutDefault gc key = do
value <- propagateGError $ \gerrorPtr ->
withCString key $ \strPtr ->
{# call gconf_client_get_without_default #} gc strPtr gerrorPtr
marshalFromGConfValue (GConfValue value)
-- | Returns the default value stored in the key's schema, if the key has a
-- schema associated and the schema exists and the schema contains a default
-- value. Note that 'gconfSet' already returns the default value if no other
-- value is found, so normally you do not need this function.
--
gconfGetDefaultFromSchema :: GConfValueClass value =>
GConf -> String -> IO value
gconfGetDefaultFromSchema gc key = do
value <- propagateGError $ \gerrorPtr ->
withCString key $ \strPtr ->
{# call gconf_client_get_default_from_schema #} gc strPtr gerrorPtr
marshalFromGConfValue (GConfValue value)
-- | Unsets the value of key; if key is already unset, has no effect. An error
-- of note is 'GconfErrorOverridden', indicating that the system administrator
-- has \"forced\" a value for this key.
--
gconfUnset :: GConf -> String -> IO ()
gconfUnset gc key =
propagateGError $ \gerrorPtr ->
withCString key $ \strPtr -> do
{# call gconf_client_unset #} gc strPtr gerrorPtr
return ()
-- | Dumps everything out of the GConf client-side cache. If you know you're
-- done using the GConf for a while, you can call this function to save some
-- memory.
--
gconfClearCache :: GConf -> IO ()
gconfClearCache gc = {# call gconf_client_clear_cache #} gc
-- | Preloads a directory. Normally you do this when you call
-- 'gconfAddDirWithPreload', but if you've called 'gconfClearCache' there may
-- be a reason to do it again.
--
gconfPreload :: GConf -> String -> GConfPreloadType -> IO ()
gconfPreload gc key preload =
propagateGError $ \gerrorPtr ->
withCString key $ \strPtr ->
{# call gconf_client_preload #} gc strPtr
(fromIntegral $ fromEnum preload) gerrorPtr
-- | Suggests to gconfd that you've just finished a block of changes, and it
-- would be an optimal time to sync to permanent storage. This is only a
-- suggestion; and gconfd will eventually sync even if you don't call
-- 'gconfSuggestSync'. This function is just a "hint" provided to gconfd to
-- maximize efficiency and minimize data loss.
--
gconfSuggestSync :: GConf -> IO ()
gconfSuggestSync gc =
propagateGError $ \gerrorPtr ->
{# call gconf_client_suggest_sync #} gc gerrorPtr
-- |
--
gconfAllEntries :: GConf -> String -> IO [(String, GConfValueDyn)]
gconfAllEntries gc dir = do
gsList <- propagateGError $ \gerrorPtr ->
withCString dir $ \strPtr ->
{# call gconf_client_all_entries #} gc strPtr gerrorPtr
entryList <- fromGSList gsList
mapM (\entry -> do let entry' = GConfEntry entry
keyStrPtr <- {# call unsafe gconf_entry_get_key #} entry'
valuePtr <- {# call unsafe gconf_entry_get_value #} entry'
key <- peekUTFString keyStrPtr
value <- marshalFromGConfValue (GConfValue valuePtr)
-- gconf_entry_free is depreciated, use gconf_entry_unref
-- however gconf_entry_unref is not documented and docs
-- still say to use gconf_entry_free. Confusing.
{# call unsafe gconf_entry_free #} entry'
return (key,value))
entryList
-- |
--
gconfAllDirs :: GConf -> String -> IO [String]
gconfAllDirs gc dir = do
gsList <- withCString dir $ \strPtr ->
{# call gconf_client_all_dirs #} gc strPtr nullPtr
dirList <- fromGSList gsList
mapM (\strPtr -> do str <- peekUTFString strPtr
{# call unsafe g_free #} (castPtr strPtr)
return str)
dirList
-- |
--
gconfDirExists :: GConf -> String -> IO Bool
gconfDirExists gc dir =
withCString dir $ \strPtr ->
liftM toBool $ {# call gconf_client_dir_exists #} gc strPtr nullPtr
-- Signals
onValueChanged, afterValueChanged :: GConf ->
(String -> Maybe GConfValueDyn -> IO ()) ->
IO (ConnectId GConf)
onValueChanged gc handler =
connect_STRING_PTR__NONE "value_changed" False gc
(convertValueChangedHandler handler)
afterValueChanged gc handler =
connect_STRING_PTR__NONE "value_changed" True gc
(convertValueChangedHandler handler)
convertValueChangedHandler :: (String -> Maybe GConfValueDyn -> IO ()) ->
(String -> Ptr GConfValue -> IO ())
convertValueChangedHandler handler key ptr = do
value <- marshalFromGConfValue (GConfValue $ castPtr ptr)
handler key value