{-# LANGUAGE MagicHash #-} {-# OPTIONS_HADDOCK hide #-} -- -*-haskell-*- -- GIMP Toolkit (GTK) CustomStore TreeModel -- -- Author : Duncan Coutts -- -- Created: 31 March 2006 -- -- Copyright (C) 2006-2007 Duncan Coutts, Axel Simon -- -- This library is free software; you can redistribute it and/or -- modify it under the terms of the GNU Lesser General Public -- License as published by the Free Software Foundation; either -- version 2.1 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 -- Lesser General Public License for more details. -- -- #hide -- | -- Maintainer : gtk2hs-users@lists.sourceforge.net -- Stability : provisional -- Portability : portable (depends on GHC) -- -- Common types and classes for the ModelView modules. -- module Graphics.UI.Gtk.ModelView.Types ( TypedTreeModel(..), TypedTreeModelClass, toTypedTreeModel, unsafeTreeModelToGeneric, TypedTreeModelSort(..), unsafeTreeModelSortToGeneric, TypedTreeModelFilter(..), unsafeTreeModelFilterToGeneric, -- TreeIter TreeIter(..), receiveTreeIter, peekTreeIter, treeIterSetStamp, -- TreePath TreePath, NativeTreePath(..), newTreePath, withTreePath, peekTreePath, fromTreePath, stringToTreePath, -- Columns ColumnAccess(..), ColumnId(..), -- Storing the model in a ComboBox comboQuark, ) where import GHC.Exts (unsafeCoerce#) import System.Glib.FFI import System.Glib.GValue (GValue) import System.Glib.GObject (Quark, quarkFromString) {#import Graphics.UI.Gtk.Types#} (TreeModel, TreeModelSort, TreeModelFilter, Pixbuf) import Data.Char ( isDigit ) import Control.Monad ( liftM ) {# context lib="gtk" prefix="gtk" #} newtype TypedTreeModel row = TypedTreeModel (ForeignPtr (TypedTreeModel row)) class TypedTreeModelClass model where dummy :: model a -> a dummy _ = error "not used" -- this is to get the right kind for model :: * -> * -- TODO: when haddock is fixed we can use an explicit kind annotation toTypedTreeModel :: TypedTreeModelClass model => model row -> TypedTreeModel row toTypedTreeModel = unsafeCoerce# unsafeTreeModelToGeneric :: TreeModel -> model row unsafeTreeModelToGeneric = unsafeCoerce# instance TypedTreeModelClass TypedTreeModel newtype TypedTreeModelSort row = TypedTreeModelSort (ForeignPtr (TypedTreeModelSort row)) unsafeTreeModelSortToGeneric :: TreeModelSort -> TypedTreeModelSort row unsafeTreeModelSortToGeneric = unsafeCoerce# instance TypedTreeModelClass TypedTreeModelSort newtype TypedTreeModelFilter row = TypedTreeModelFilter (ForeignPtr (TypedTreeModelFilter row)) unsafeTreeModelFilterToGeneric :: TreeModelFilter -> TypedTreeModelFilter row unsafeTreeModelFilterToGeneric = unsafeCoerce# instance TypedTreeModelClass TypedTreeModelFilter -- | Tree Iterator: a pointer to an entry in a -- 'Graphics.UI.Gtk.ModelView.TreeModel'. The constructor of this structure is -- public for the sake of creating custom tree models. The first value is a -- time stamp that is handled by the functions that interface with Gtk. The -- time stamps are used to print warnings if programmers use an iter to a -- model that has changed meanwhile. The other three fields are used by the -- custom model implementation to implement an indexing scheme. The precise -- use of the three words is therefore implementation specific. See also -- 'TreePath'. -- data TreeIter = TreeIter {-# UNPACK #-} !CInt !Word !Word !Word deriving Show {#pointer *TreeIter as TreeIterPtr -> TreeIter #} instance Storable TreeIter where sizeOf _ = {# sizeof TreeIter #} alignment _ = alignment (undefined :: CInt) peek ptr = do stamp <- {# get TreeIter->stamp #} ptr user_data <- {# get TreeIter->user_data #} ptr user_data2 <- {# get TreeIter->user_data2 #} ptr user_data3 <- {# get TreeIter->user_data3 #} ptr return (TreeIter stamp (ptrToWord user_data) (ptrToWord user_data2) (ptrToWord user_data3)) where ptrToWord :: Ptr a -> Word ptrToWord ptr = fromIntegral (ptr `minusPtr` nullPtr) poke ptr (TreeIter stamp user_data user_data2 user_data3) = do {# set TreeIter->stamp #} ptr stamp {# set TreeIter->user_data #} ptr (wordToPtr user_data) {# set TreeIter->user_data2 #} ptr (wordToPtr user_data2) {# set TreeIter->user_data3 #} ptr (wordToPtr user_data3) where wordToPtr :: Word -> Ptr a wordToPtr word = nullPtr `plusPtr` fromIntegral word -- Pass a pointer to a structure large enough to hold a GtkTreeIter -- structure. If the function returns true, read the tree iter and -- return it. receiveTreeIter :: (Ptr TreeIter -> IO CInt) -> IO (Maybe TreeIter) receiveTreeIter body = alloca $ \iterPtr -> do result <- body iterPtr if toBool result then liftM Just (peek iterPtr) else return Nothing -- Note that this function does throw an error if the pointer is NULL rather -- than returning some random tree iterator. peekTreeIter :: Ptr TreeIter -> IO TreeIter peekTreeIter ptr | ptr==nullPtr = fail "peekTreeIter: ptr is NULL, tree iterator is invalid" | otherwise = peek ptr -- update the stamp of a tree iter treeIterSetStamp :: TreeIter -> CInt -> TreeIter treeIterSetStamp (TreeIter _ a b c) s = (TreeIter s a b c) -- | TreePath : a list of indices to specify a subtree or node in a -- 'Graphics.UI.Gtk.ModelView.TreeModel.TreeModel'. The node that correspond -- to a given 'TreePath' might change if nodes are removed or added and a -- 'TreePath' may refer to a different or even non-existent node after a -- modification of the model. In contrast, a 'TreeIter' is a more compact -- representation of a 'TreePath' which becomes invalid after each -- modification of the underlying model. An intelligent index that is adjusted -- with each update of the model to point to the same node (whenever possible) -- is 'Graphics.UI.Gtk.ModelView.TreeRowReference.TreeRowReference'. -- type TreePath = [Int] {#pointer * TreePath as NativeTreePath newtype#} nativeTreePathFree :: NativeTreePath -> IO () nativeTreePathFree = {# call unsafe tree_path_free #} newTreePath :: TreePath -> IO NativeTreePath newTreePath path = do nativePath <- liftM NativeTreePath {# call unsafe tree_path_new #} mapM_ ({#call unsafe tree_path_append_index#} nativePath . fromIntegral) path return nativePath withTreePath :: TreePath -> (NativeTreePath -> IO a) -> IO a withTreePath tp act = do nativePath <- newTreePath tp res <- act nativePath nativeTreePathFree nativePath return res nativeTreePathGetIndices :: NativeTreePath -> IO [Int] nativeTreePathGetIndices tp = do depth <- liftM fromIntegral $ {# call unsafe tree_path_get_depth #} tp arrayPtr <- {# call unsafe tree_path_get_indices #} tp if (depth==0 || arrayPtr==nullPtr) then return [] else liftM (map fromIntegral) $ peekArray depth arrayPtr -- | Convert the given pointer to a tree path. peekTreePath :: Ptr NativeTreePath -> IO TreePath peekTreePath tpPtr | tpPtr==nullPtr = return [] | otherwise = nativeTreePathGetIndices (NativeTreePath tpPtr) -- | Convert the given pointer to a tree path. Frees the pointer. fromTreePath :: Ptr NativeTreePath -> IO TreePath fromTreePath tpPtr | tpPtr==nullPtr = return [] | otherwise = do path <- nativeTreePathGetIndices (NativeTreePath tpPtr) nativeTreePathFree (NativeTreePath tpPtr) return path -- | Convert a comma or colon separated string into a 'TreePath'. Any -- non-digit characters are assumed to separate indices, thus, the function -- always is always successful. stringToTreePath :: String -> TreePath stringToTreePath "" = [] stringToTreePath path = getNum 0 (dropWhile (not . isDigit) path) where getNum acc ('0':xs) = getNum (10*acc) xs getNum acc ('1':xs) = getNum (10*acc+1) xs getNum acc ('2':xs) = getNum (10*acc+2) xs getNum acc ('3':xs) = getNum (10*acc+3) xs getNum acc ('4':xs) = getNum (10*acc+4) xs getNum acc ('5':xs) = getNum (10*acc+5) xs getNum acc ('6':xs) = getNum (10*acc+6) xs getNum acc ('7':xs) = getNum (10*acc+7) xs getNum acc ('8':xs) = getNum (10*acc+8) xs getNum acc ('9':xs) = getNum (10*acc+9) xs getNum acc xs = acc:stringToTreePath (dropWhile (not . isDigit) xs) -- | Accessing a row for a specific value. Used for 'ColumnMap'. data ColumnAccess row = CAInvalid | CAInt (row -> Int) | CABool (row -> Bool) | CAString (row -> String) | CAPixbuf (row -> Pixbuf) -- | The type of a tree column. data ColumnId row ty = ColumnId (GValue -> IO ty) ((row -> ty) -> ColumnAccess row) Int -- it shouldn't matter if the following function is actually inlined {-# NOINLINE comboQuark #-} comboQuark :: Quark comboQuark = unsafePerformIO $ quarkFromString "comboBoxHaskellStringModelQuark"