~ubuntu-branches/ubuntu/lucid/gtk2hs/lucid

« back to all changes in this revision

Viewing changes to glib/System/Glib/GError.chs.pp

  • Committer: Bazaar Package Importer
  • Author(s): Liyang HU
  • Date: 2006-07-22 21:31:58 UTC
  • Revision ID: james.westby@ubuntu.com-20060722213158-he81wo6uam30m9aw
Tags: upstream-0.9.10
ImportĀ upstreamĀ versionĀ 0.9.10

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
-- -*-haskell-*-
 
2
--  GIMP Toolkit (GTK) GError API
 
3
--
 
4
--  Author : Duncan Coutts
 
5
--
 
6
--  Created: 2 July 2004
 
7
--
 
8
--  Version $Revision: 1.3 $ from $Date: 2005/08/29 21:14:35 $
 
9
--
 
10
--  Copyright (C) 2004 Duncan Coutts
 
11
--  parts derived from Structs.hsc Copyright (c) 1999..2002 Axel Simon
 
12
--
 
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.
 
17
--
 
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.
 
22
--
 
23
-- |
 
24
-- Maintainer  : gtk2hs-users@lists.sourceforge.net
 
25
-- Stability   : provisional
 
26
-- Portability : portable (depends on GHC)
 
27
--
 
28
--  Error Reporting, glib's system for reporting errors.
 
29
--
 
30
--  'GError's are used by glib to report recoverable runtime errors.
 
31
--
 
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.
 
35
--
 
36
module System.Glib.GError (
 
37
 
 
38
  -- * Data types
 
39
  --
 
40
  GError(..),
 
41
  GErrorDomain,
 
42
  GErrorCode,
 
43
  GErrorMessage,
 
44
  
 
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'
 
49
  -- functions.
 
50
  --
 
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'
 
60
  --
 
61
  catchGError,
 
62
  catchGErrorJust,
 
63
  catchGErrorJustDomain,
 
64
  
 
65
  handleGError,
 
66
  handleGErrorJust,
 
67
  handleGErrorJustDomain,
 
68
  
 
69
  failOnGError,
 
70
  throwGError,
 
71
 
 
72
  -- * Checking for GErrors returned by glib\/gtk functions
 
73
  -- | * Note, these functions are only useful to implementors
 
74
  --
 
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.
 
80
  --
 
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'.
 
83
 
 
84
  GErrorClass(..),
 
85
  propagateGError,
 
86
  checkGError,
 
87
  checkGErrorWithCont
 
88
  
 
89
  ) where
 
90
 
 
91
import Monad (when)
 
92
import Foreign
 
93
import Foreign.C
 
94
import System.Glib.UTFString
 
95
import Control.Exception
 
96
import Data.Dynamic
 
97
 
 
98
{# context lib="gtk" prefix ="gtk" #}
 
99
 
 
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
 
103
  deriving Typeable
 
104
#else
 
105
{-# NOINLINE gerrorTypeRep #-}
 
106
gerrorTypeRep :: TypeRep
 
107
gerrorTypeRep = mkAppTy (mkTyCon "Graphics.UI.Gtk.GError.GError") []
 
108
instance Typeable GError where
 
109
  typeOf _ = gerrorTypeRep
 
110
#endif
 
111
 
 
112
type GQuark = {#type GQuark #}
 
113
 
 
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
 
120
 
 
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
 
127
 
 
128
-- | A human readable error message.
 
129
type GErrorMessage = String
 
130
                                                                                           
 
131
instance Storable GError where
 
132
  sizeOf _ = {#sizeof GError #}
 
133
  alignment _ = alignment (undefined:: GQuark)
 
134
  peek ptr = do
 
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"
 
141
 
 
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.
 
146
--
 
147
-- Example for 'PixbufError':
 
148
--
 
149
-- > instance GErrorClass PixbufError where
 
150
-- >   gerrorDomain _ = unsafePerformIO {#call unsafe pixbuf_error_quark#}
 
151
--
 
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'.
 
155
 
 
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.
 
159
--   
 
160
-- Example of use:
 
161
--
 
162
-- > propagateGError $ \gerrorPtr ->
 
163
-- > {# call g_some_function_that_might_return_an_error #} a b gerrorPtr
 
164
--
 
165
propagateGError :: (Ptr (Ptr ()) -> IO a) -> IO a
 
166
propagateGError action = checkGError action throwGError
 
167
 
 
168
-- | Like 'propagateGError' but instead of throwing the GError as an exception
 
169
--   handles the error immediately using the supplied error handler.
 
170
--
 
171
-- Example of use:
 
172
--
 
173
-- > checkGError
 
174
-- >   (\gerrorPtr -> {# call g_some_function_that_might_return_an_error #} a b gerrorPtr)
 
175
-- >   (\(GError domain code msg) -> ...)
 
176
--
 
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
 
183
  if errPtr == nullPtr
 
184
    then return result
 
185
    else do gerror <- peek errPtr
 
186
            {# call unsafe g_error_free #} (castPtr errPtr)
 
187
            handler gerror
 
188
 
 
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
 
192
--   was no error.
 
193
--
 
194
-- Example of use:
 
195
--
 
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
 
200
--
 
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
 
207
  if errPtr == nullPtr
 
208
    then cont result
 
209
    else do gerror <- peek errPtr
 
210
            {# call unsafe g_error_free #} (castPtr errPtr)
 
211
            handler gerror
 
212
 
 
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)
 
217
 
 
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'.
 
222
--   For example:
 
223
--
 
224
-- > catchGError
 
225
-- >   (do ...
 
226
-- >       ...)
 
227
-- >   (\(GError dom code msg) -> fail msg)
 
228
--   
 
229
catchGError :: IO a            -- ^ The computation to run
 
230
            -> (GError -> IO a) -- ^ Handler to invoke if an exception is raised
 
231
            -> IO a
 
232
catchGError action handler = catchDyn action handler
 
233
 
 
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:
 
237
--
 
238
-- > do image <- catchGErrorJust PixbufErrorCorruptImage
 
239
-- >               loadImage
 
240
-- >               (\errorMessage -> do log errorMessage
 
241
-- >                                    return mssingImagePlaceholder)
 
242
--
 
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
 
246
                -> IO a
 
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
 
252
 
 
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.
 
257
--
 
258
-- > catchGErrorJustDomain
 
259
-- >   loadImage
 
260
-- >   (\err message -> case err of
 
261
-- >       PixbufErrorCorruptImage -> ...
 
262
-- >       PixbufErrorInsufficientMemory -> ...
 
263
-- >       PixbufErrorUnknownType -> ...
 
264
-- >       _ -> ...)
 
265
--
 
266
catchGErrorJustDomain :: GErrorClass err => IO a        -- ^ The computation to run
 
267
                      -> (err -> GErrorMessage -> IO a) -- ^ Handler to invoke if an exception is raised
 
268
                      -> IO a
 
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
 
274
 
 
275
-- | A verson of 'catchGError' with the arguments swapped around.
 
276
--
 
277
-- > handleGError (\(GError dom code msg) -> ...) $
 
278
-- >   ...
 
279
--   
 
280
handleGError :: (GError -> IO a) -> IO a -> IO a
 
281
handleGError = flip catchGError
 
282
 
 
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)
 
286
 
 
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
 
290
 
 
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)