2
-- GIMP Toolkit (GTK) GError API
4
-- Author : Duncan Coutts
6
-- Created: 2 July 2004
8
-- Version $Revision: 1.3 $ from $Date: 2005/08/29 21:14:35 $
10
-- Copyright (C) 2004 Duncan Coutts
11
-- parts derived from Structs.hsc Copyright (c) 1999..2002 Axel Simon
13
-- This library is free software; you can redistribute it and/or
14
-- modify it under the terms of the GNU Lesser General Public
15
-- License as published by the Free Software Foundation; either
16
-- version 2.1 of the License, or (at your option) any later version.
18
-- This library is distributed in the hope that it will be useful,
19
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
20
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21
-- Lesser General Public License for more details.
24
-- Maintainer : gtk2hs-users@lists.sourceforge.net
25
-- Stability : provisional
26
-- Portability : portable (depends on GHC)
28
-- Error Reporting, glib's system for reporting errors.
30
-- 'GError's are used by glib to report recoverable runtime errors.
32
-- This module provides functions for checking glib\/gtk functions that report
33
-- 'GError's. It also provides functions for throwing and catching 'GError's as
34
-- Haskell exceptions.
36
module System.Glib.GError (
45
-- * Catching GError exceptions
46
-- | To catch GError exceptions thrown by Gtk2Hs functions use the
47
-- catchGError* or handleGError* functions. They work in a similar way to
48
-- the standard 'Control.Exception.catch' and 'Control.Exception.handle'
51
-- 'catchGError'\/'handleGError' catches all GError exceptions, you provide
52
-- a handler function that gets given the GError if an exception was thrown.
53
-- This is the most general but is probably not what you want most of the
54
-- time. It just gives you the raw error code rather than a Haskell
55
-- enumeration of the error codes. Most of the time you will only want to
56
-- catch a specific error or any error from a specific error domain. To
57
-- catch just a single specific error use
58
-- 'catchGErrorJust'\/'handleGErrorJust'. To catch any error in a particular
59
-- error domain use 'catchGErrorJustDomain'\/'handleGErrorJustDomain'
63
catchGErrorJustDomain,
67
handleGErrorJustDomain,
72
-- * Checking for GErrors returned by glib\/gtk functions
73
-- | * Note, these functions are only useful to implementors
75
-- If you are wrapping a new API that reports 'GError's you should probably
76
-- use 'propagateGError' to convert the GError into an exception. You should
77
-- also note in the documentation for the function that it throws GError
78
-- exceptions and the Haskell enumeration for the expected glib GError
79
-- domain(s), so that users know what exceptions they might want to catch.
81
-- If you think it is more appropriate to use an alternate return value (eg
82
-- Either\/Maybe) then you should use 'checkGError' or 'checkGErrorWithCont'.
94
import System.Glib.UTFString
95
import Control.Exception
98
{# context lib="gtk" prefix ="gtk" #}
100
-- | A GError consists of a domain, code and a human readable message.
101
data GError = GError !GErrorDomain !GErrorCode !GErrorMessage
102
# if __GLASGOW_HASKELL__>=600
105
{-# NOINLINE gerrorTypeRep #-}
106
gerrorTypeRep :: TypeRep
107
gerrorTypeRep = mkAppTy (mkTyCon "Graphics.UI.Gtk.GError.GError") []
108
instance Typeable GError where
109
typeOf _ = gerrorTypeRep
112
type GQuark = {#type GQuark #}
114
-- | A code used to identify the \'namespace\' of the error. Within each error
115
-- domain all the error codes are defined in an enumeration. Each gtk\/gnome
116
-- module that uses GErrors has its own error domain. The rationale behind
117
-- using error domains is so that each module can organise its own error codes
118
-- without having to coordinate on a global error code list.
119
type GErrorDomain = GQuark
121
-- | A code to identify a specific error within a given 'GErrorDomain'. Most of
122
-- time you will not need to deal with this raw code since there is an
123
-- enumeration type for each error domain. Of course which enumeraton to use
124
-- depends on the error domain, but if you use 'catchGErrorJustDomain' or
125
-- 'handleGErrorJustDomain', this is worked out for you automatically.
126
type GErrorCode = Int
128
-- | A human readable error message.
129
type GErrorMessage = String
131
instance Storable GError where
132
sizeOf _ = {#sizeof GError #}
133
alignment _ = alignment (undefined:: GQuark)
135
(domain :: GQuark) <- {#get GError->domain #} ptr
136
(code :: {#type gint #}) <- {#get GError->code #} ptr
137
(msgPtr :: CString) <- {#get GError->message #} ptr
138
msg <- peekUTFString msgPtr
139
return $ GError (fromIntegral domain) (fromIntegral code) msg
140
poke _ = error "GError::poke: not implemented"
142
-- | Each error domain's error enumeration type should be an instance of this
143
-- class. This class helps to hide the raw error and domain codes from the
144
-- user. This interface should be implemented by calling the approrpiate
145
-- @{error_domain}_error_quark@. It is safe to use 'unsafePerformIO' for this.
147
-- Example for 'PixbufError':
149
-- > instance GErrorClass PixbufError where
150
-- > gerrorDomain _ = unsafePerformIO {#call unsafe pixbuf_error_quark#}
152
class Enum err => GErrorClass err where
153
gerrorDomain :: err -> GErrorDomain -- ^ This must not use the value of its parameter
154
-- so that it is safe to pass 'undefined'.
156
-- | Glib functions which report 'GError's take as a parameter a @GError **error@.
157
-- Use this function to supply such a parameter. It checks if an error was
158
-- reported and if so throws it as a Haskell exception.
162
-- > propagateGError $ \gerrorPtr ->
163
-- > {# call g_some_function_that_might_return_an_error #} a b gerrorPtr
165
propagateGError :: (Ptr (Ptr ()) -> IO a) -> IO a
166
propagateGError action = checkGError action throwGError
168
-- | Like 'propagateGError' but instead of throwing the GError as an exception
169
-- handles the error immediately using the supplied error handler.
174
-- > (\gerrorPtr -> {# call g_some_function_that_might_return_an_error #} a b gerrorPtr)
175
-- > (\(GError domain code msg) -> ...)
177
checkGError :: (Ptr (Ptr ()) -> IO a) -> (GError -> IO a) -> IO a
178
checkGError action handler =
179
alloca $ \(errPtrPtr :: Ptr (Ptr GError)) -> do
180
poke errPtrPtr nullPtr
181
result <- action (castPtr errPtrPtr)
182
errPtr <- peek errPtrPtr
185
else do gerror <- peek errPtr
186
{# call unsafe g_error_free #} (castPtr errPtr)
189
-- | Like 'checkGError' but with an extra continuation applied to the result.
190
-- This can be useful when something needs to be done after making the call
191
-- to the function that can raise an error but is should only be done if there
196
-- > checkGErrorWithCont (\gerrorPtr ->
197
-- > {# call g_some_function_that_might_return_an_error #} a b gerrorPtr)
198
-- > (\(GError domain code msg) -> ...) -- what to do in case of error
199
-- > (\result -> ...) -- what to do after if no error
201
checkGErrorWithCont :: (Ptr (Ptr ()) -> IO b) -> (GError -> IO a) -> (b -> IO a) -> IO a
202
checkGErrorWithCont action handler cont =
203
alloca $ \(errPtrPtr :: Ptr (Ptr GError)) -> do
204
poke errPtrPtr nullPtr
205
result <- action (castPtr errPtrPtr)
206
errPtr <- peek errPtrPtr
209
else do gerror <- peek errPtr
210
{# call unsafe g_error_free #} (castPtr errPtr)
213
-- | Use this if you need to explicitly throw a GError or re-throw an existing
214
-- GError that you do not wish to handle.
215
throwGError :: GError -> IO a
216
throwGError gerror = evaluate (throwDyn gerror)
218
-- | This will catch any GError exception. The handler function will receive the
219
-- raw GError. This is probably only useful when you want to take some action
220
-- that does not depend on which GError exception has occured, otherwise it
221
-- would be better to use either 'catchGErrorJust' or 'catchGErrorJustDomain'.
227
-- > (\(GError dom code msg) -> fail msg)
229
catchGError :: IO a -- ^ The computation to run
230
-> (GError -> IO a) -- ^ Handler to invoke if an exception is raised
232
catchGError action handler = catchDyn action handler
234
-- | This will catch just a specific GError exception. If you need to catch a
235
-- range of related errors, 'catchGErrorJustDomain' is probably more
236
-- appropriate. Example:
238
-- > do image <- catchGErrorJust PixbufErrorCorruptImage
240
-- > (\errorMessage -> do log errorMessage
241
-- > return mssingImagePlaceholder)
243
catchGErrorJust :: GErrorClass err => err -- ^ The error to catch
244
-> IO a -- ^ The computation to run
245
-> (GErrorMessage -> IO a) -- ^ Handler to invoke if an exception is raised
247
catchGErrorJust code action handler = catchGError action handler'
248
where handler' gerror@(GError domain code' msg)
249
| fromIntegral domain == gerrorDomain code
250
&& code' == fromEnum code = handler msg
251
| otherwise = throwGError gerror
253
-- | Catch all GErrors from a particular error domain. The handler function
254
-- should just deal with one error enumeration type. If you need to catch
255
-- errors from more than one error domain, use this function twice with an
256
-- appropriate handler functions for each.
258
-- > catchGErrorJustDomain
260
-- > (\err message -> case err of
261
-- > PixbufErrorCorruptImage -> ...
262
-- > PixbufErrorInsufficientMemory -> ...
263
-- > PixbufErrorUnknownType -> ...
266
catchGErrorJustDomain :: GErrorClass err => IO a -- ^ The computation to run
267
-> (err -> GErrorMessage -> IO a) -- ^ Handler to invoke if an exception is raised
269
catchGErrorJustDomain action (handler :: err -> GErrorMessage -> IO a) =
270
catchGError action handler'
271
where handler' gerror@(GError domain code msg)
272
| fromIntegral domain == gerrorDomain (undefined::err) = handler (toEnum code) msg
273
| otherwise = throwGError gerror
275
-- | A verson of 'catchGError' with the arguments swapped around.
277
-- > handleGError (\(GError dom code msg) -> ...) $
280
handleGError :: (GError -> IO a) -> IO a -> IO a
281
handleGError = flip catchGError
283
-- | A verson of 'handleGErrorJust' with the arguments swapped around.
284
handleGErrorJust :: GErrorClass err => err -> (GErrorMessage -> IO a) -> IO a -> IO a
285
handleGErrorJust code = flip (catchGErrorJust code)
287
-- | A verson of 'handleGErrorJustDomain' with the arguments swapped around.
288
handleGErrorJustDomain :: GErrorClass err => (err -> GErrorMessage -> IO a) -> IO a -> IO a
289
handleGErrorJustDomain = flip catchGErrorJustDomain
291
-- | Catch all GError exceptions and convert them into a general failure.
292
failOnGError :: IO a -> IO a
293
failOnGError action = catchGError action (\(GError dom code msg) -> fail msg)