1{-# LANGUAGE CPP #-}
2-- -*-haskell-*-
3--  GIMP Toolkit (GTK) CustomStore TreeModel
4--
5--  Author : Duncan Coutts, Axel Simon
6--
7--  Created: 11 Feburary 2006
8--
9--  Copyright (C) 2005 Duncan Coutts, Axel Simon
10--
11--  This library is free software; you can redistribute it and/or
12--  modify it under the terms of the GNU Lesser General Public
13--  License as published by the Free Software Foundation; either
14--  version 2.1 of the License, or (at your option) any later version.
15--
16--  This library is distributed in the hope that it will be useful,
17--  but WITHOUT ANY WARRANTY; without even the implied warranty of
18--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19--  Lesser General Public License for more details.
20--
21-- |
22-- Maintainer  : gtk2hs-users@lists.sourceforge.net
23-- Stability   : provisional
24-- Portability : portable (depends on GHC)
25--
26-- Standard model to store hierarchical data.
27--
28module Graphics.UI.Gtk.ModelView.TreeStore (
29
30-- * Types
31  TreeStore,
32
33-- * Constructors
34  treeStoreNew,
35  treeStoreNewDND,
36
37-- * Implementation of Interfaces
38  treeStoreDefaultDragSourceIface,
39  treeStoreDefaultDragDestIface,
40
41-- * Methods
42  treeStoreGetValue,
43  treeStoreGetTree,
44  treeStoreLookup,
45
46  treeStoreSetValue,
47
48  treeStoreInsert,
49  treeStoreInsertTree,
50  treeStoreInsertForest,
51
52  treeStoreRemove,
53  treeStoreClear,
54
55  treeStoreChange,
56  treeStoreChangeM,
57  ) where
58
59import Data.Bits
60import Data.Word (Word32)
61import Data.Maybe ( fromMaybe, isJust )
62import Data.Tree
63import Control.Monad ( when )
64import Control.Exception (assert)
65import Data.IORef
66import Graphics.UI.Gtk.ModelView.Types
67import Graphics.UI.Gtk.Types (GObjectClass(..))
68import Graphics.UI.Gtk.ModelView.CustomStore
69import Graphics.UI.Gtk.ModelView.TreeModel
70import Graphics.UI.Gtk.ModelView.TreeDrag
71import Control.Monad.Trans ( liftIO )
72
73--------------------------------------------
74-- internal model data types
75--
76
77-- | A store for hierarchical data.
78--
79newtype TreeStore a = TreeStore (CustomStore (IORef (Store a)) a)
80
81instance TypedTreeModelClass TreeStore
82instance TreeModelClass (TreeStore a)
83instance GObjectClass (TreeStore a) where
84  toGObject (TreeStore tm) = toGObject tm
85  unsafeCastGObject = TreeStore . unsafeCastGObject
86
87-- | Maximum number of nodes on each level.
88--
89-- * These numbers determine how many bits in a 'TreeIter' are devoted to
90--   each level. Hence, these numbers reflect log2 of the maximum number
91--   of nodes at a level, rounded up.
92--
93type Depth = [Int]
94
95data Store a = Store {
96  depth :: Depth,
97  content :: Cache a
98}
99
100-- | Create a new list store.
101--
102-- * The given rose tree determines the initial content and may be the empty
103--   list. Each 'Tree' in the forest corresponds to one top-level node.
104--
105-- * The TreeStore maintains the initially given Forest and aligns the 'TreePath'
106--   bits to fit in 96-bit length 'TreeIter' storage.
107--
108-- * Additionally, a cache is used to achieve higher performance if operating on
109--   recently used TreePaths.
110--
111-- * __Note:__ due to the limited amount of bits available in TreeIter storage, only
112--   limited depth forests can be used with this implementation, the result of too deep
113--   Forests is an undefined behaviour while trying to retrieve the deeply nested nodes.
114--   For example: assuming the average requiement is 8 bits per tree level (max number of
115--   children at the level is 255), then we can only use 12 levels deep trees (96/8) -
116--   any further levels in a TreePath will not be encoded in the corresponding TreeIter
117--   storage.
118--
119treeStoreNew :: Forest a -> IO (TreeStore a)
120treeStoreNew forest = treeStoreNewDND forest
121                        (Just treeStoreDefaultDragSourceIface)
122                        (Just treeStoreDefaultDragDestIface)
123
124-- | Create a new list store.
125--
126-- * In addition to 'treeStoreNew', this function takes an two interfaces
127--   to implement user-defined drag-and-drop functionality.
128--
129treeStoreNewDND :: Forest a -- ^ the inital tree stored in this model
130  -> Maybe (DragSourceIface TreeStore a) -- ^ an optional interface for drags
131  -> Maybe (DragDestIface TreeStore a) -- ^ an optional interface to handle drops
132  -> IO (TreeStore a)
133treeStoreNewDND forest mDSource mDDest = do
134  storeRef <- newIORef Store {
135      depth = calcForestDepth forest,
136      content = storeToCache forest
137    }
138  let withStore f = readIORef storeRef >>= return . f
139      withStoreUpdateCache f = do
140        store <- readIORef storeRef
141        let (result, cache') = f store
142        writeIORef storeRef store { content = cache' }
143        return result
144
145  customStoreNew storeRef TreeStore TreeModelIface {
146    treeModelIfaceGetFlags = return [],
147
148    treeModelIfaceGetIter = \path -> withStore $
149      \Store { depth = d } -> fromPath d path,
150
151    treeModelIfaceGetPath = \iter -> withStore $
152      \Store { depth = d } -> toPath d iter,
153
154    treeModelIfaceGetRow  = \iter -> withStoreUpdateCache $
155      \Store { depth = d, content = cache } ->
156        case checkSuccess d iter cache of
157          (True, cache'@((_, (Node { rootLabel = val }:_)):_)) ->
158            (val, cache')
159          _ -> error "TreeStore.getRow: iter does not refer to a valid entry",
160
161    treeModelIfaceIterNext = \iter -> withStoreUpdateCache $
162      \Store { depth = d, content = cache } -> iterNext d iter cache,
163
164    treeModelIfaceIterChildren = \mIter -> withStoreUpdateCache $
165      \Store { depth = d, content = cache } ->
166      let iter = fromMaybe invalidIter mIter
167       in iterNthChild d 0 iter cache,
168
169    treeModelIfaceIterHasChild = \iter -> withStoreUpdateCache $
170      \Store { depth = d, content = cache } ->
171       let (mIter, cache') = iterNthChild d 0 iter cache
172        in (isJust mIter, cache'),
173
174    treeModelIfaceIterNChildren = \mIter -> withStoreUpdateCache $
175      \Store { depth = d, content = cache } ->
176      let iter = fromMaybe invalidIter mIter
177       in iterNChildren d iter cache,
178
179    treeModelIfaceIterNthChild = \mIter idx  -> withStoreUpdateCache $
180      \Store { depth = d, content = cache } ->
181      let iter = fromMaybe invalidIter mIter
182       in iterNthChild d idx iter cache,
183
184    treeModelIfaceIterParent = \iter -> withStore $
185      \Store { depth = d } -> iterParent d iter,
186
187    treeModelIfaceRefNode = \_ -> return (),
188    treeModelIfaceUnrefNode = \_ -> return ()
189   } mDSource mDDest
190
191
192-- | Default drag functions for
193-- 'Graphics.UI.Gtk.ModelView.TreeStore'. These functions allow the rows of
194-- the model to serve as drag source. Any row is allowed to be dragged and the
195-- data set in the 'SelectionDataM' object is set with 'treeSetRowDragData',
196-- i.e. it contains the model and the 'TreePath' to the row.
197treeStoreDefaultDragSourceIface :: DragSourceIface TreeStore row
198treeStoreDefaultDragSourceIface = DragSourceIface {
199    treeDragSourceRowDraggable = \_ _-> return True,
200    treeDragSourceDragDataGet = treeSetRowDragData,
201    treeDragSourceDragDataDelete = \model dest@(_:_) -> do
202            liftIO $ treeStoreRemove model dest
203            return True
204
205  }
206
207-- | Default drop functions for 'Graphics.UI.Gtk.ModelView.TreeStore'. These
208--   functions accept a row and insert the row into the new location if it is
209--   dragged into a tree view
210-- that uses the same model.
211treeStoreDefaultDragDestIface :: DragDestIface TreeStore row
212treeStoreDefaultDragDestIface = DragDestIface {
213    treeDragDestRowDropPossible = \model dest -> do
214      mModelPath <- treeGetRowDragData
215      case mModelPath of
216        Nothing -> return False
217        Just (model', source) -> return (toTreeModel model==toTreeModel model'),
218    treeDragDestDragDataReceived = \model dest@(_:_) -> do
219      mModelPath <- treeGetRowDragData
220      case mModelPath of
221        Nothing -> return False
222        Just (model', source@(_:_)) ->
223          if toTreeModel model/=toTreeModel model' then return False
224          else liftIO $ do
225            row <- treeStoreGetTree model source
226            treeStoreInsertTree model (init dest) (last dest) row
227            return True
228  }
229
230--------------------------------------------
231-- low level bit-twiddling utility functions
232--
233
234bitsNeeded :: Word32 -> Int
235bitsNeeded n = bitsNeeded' 0 n
236  where bitsNeeded' b 0 = b
237        bitsNeeded' b n = bitsNeeded' (b+1) (n `shiftR` 1)
238
239getBitSlice :: TreeIter -> Int -> Int -> Word32
240getBitSlice (TreeIter _ a b c) off count =
241      getBitSliceWord a  off     count
242  .|. getBitSliceWord b (off-32) count
243  .|. getBitSliceWord c (off-64) count
244
245  where getBitSliceWord :: Word32 -> Int -> Int -> Word32
246        getBitSliceWord word off count =
247          word `shift` (-off) .&. (1 `shiftL` count - 1)
248
249setBitSlice :: TreeIter -> Int -> Int -> Word32 -> TreeIter
250setBitSlice (TreeIter stamp a b c) off count value =
251  assert (value < 1 `shiftL` count) $
252  TreeIter stamp
253           (setBitSliceWord a  off     count value)
254           (setBitSliceWord b (off-32) count value)
255           (setBitSliceWord c (off-64) count value)
256
257  where setBitSliceWord :: Word32 -> Int -> Int -> Word32 -> Word32
258        setBitSliceWord word off count value =
259          let mask = (1 `shiftL` count - 1) `shift` off
260           in (word .&. complement mask) .|. (value `shift` off)
261
262
263--iterPrefixEqual :: TreeIter -> TreeIter -> Int -> Bool
264--iterPrefixEqual (TreeIter _ a1 b1 c1) (TreeIter _ a2 b2 c2) pos
265--  | pos>64 = let mask = 1 `shiftL` (pos-64) - 1 in
266--             a1==a2 && b1==b2 && (c1 .&. mask) == (c2 .&. mask)
267--  | pos>32 = let mask = 1 `shiftL` (pos-32) - 1 in
268--             a1==a2 && (b1 .&. mask) == (b2 .&. mask)
269--  | otherwise = let mask = 1 `shiftL` pos - 1 in
270--                (a1 .&. mask) == (a2 .&. mask)
271
272-- | The invalid tree iterator.
273--
274invalidIter :: TreeIter
275invalidIter = TreeIter 0 0 0 0
276
277--showIterBits (TreeIter _ a b c) = [showBits a, showBits b, showBits c]
278--
279--showBits :: Bits a => a -> String
280--showBits a = [ if testBit a i then '1' else '0' | i <- [0..bitSize a - 1] ]
281
282-- | Calculate the maximum number of nodes on a per-level basis.
283--
284calcForestDepth :: Forest a -> Depth
285calcForestDepth f = map bitsNeeded $
286                    takeWhile (/=0) $
287                    foldr calcTreeDepth (repeat 0) f
288  where
289  calcTreeDepth Node { subForest = f } (d:ds) =
290      (d+1): zipWith max ds (foldr calcTreeDepth (repeat 0) f)
291
292
293-- | Convert an iterator into a path.
294--
295toPath :: Depth -> TreeIter -> TreePath
296toPath d iter = gP 0 d
297  where
298  gP pos [] = []
299  gP pos (d:ds) = let idx = getBitSlice iter pos d in
300                  if idx==0 then [] else fromIntegral (idx-1) : gP (pos+d) ds
301
302-- | Try to convert a path into a 'TreeIter'.
303--
304fromPath :: Depth -> TreePath -> Maybe TreeIter
305fromPath = fP 0 invalidIter
306  where
307  fP pos ti _ [] = Just ti -- the remaining bits are zero anyway
308  fP pos ti [] _ = Nothing
309  fP pos ti (d:ds) (p:ps) = let idx = fromIntegral (p+1) in
310    if idx >= bit d then Nothing else
311    fP (pos+d) (setBitSlice ti pos d idx) ds ps
312
313
314-- | The 'Cache' type synonym is only used iternally. What it represents
315--   the stack during a (fictional) lookup operations.
316--   The topmost frame is the node
317--   for which this lookup was started and the innermost frame (the last
318--   element of the list) contains the root of the tree.
319--
320type Cache a = [(TreeIter, Forest a)]
321
322
323-- | Create a traversal structure that allows a pre-order traversal in linear
324--   time.
325--
326-- * The returned structure points at the root of the first level which doesn't
327--   really exist, but serves to indicate that it is before the very first
328--   node.
329--
330storeToCache :: Forest a -> Cache a
331storeToCache [] = []
332storeToCache forest = [(invalidIter, [Node root forest])]
333  where
334  root = error "TreeStore.storeToCache: accessed non-exitent root of tree"
335
336-- | Extract the store from the cache data structure.
337cacheToStore :: Cache a -> Forest a
338cacheToStore [] = []
339cacheToStore cache = case last cache of (_, [Node _ forest]) -> forest
340
341-- | Advance the traversal structure to the given 'TreeIter'.
342--
343advanceCache :: Depth -> TreeIter -> Cache a -> Cache a
344advanceCache depth goal [] = []
345advanceCache depth goal cache@((rootIter,_):_) =
346  moveToSameLevel 0 depth
347  where
348  moveToSameLevel pos [] = cache
349  moveToSameLevel pos (d:ds) =
350    let
351      goalIdx = getBitSlice goal pos d
352      curIdx = getBitSlice rootIter pos d
353      isNonZero pos d (ti,_) = getBitSlice ti pos d/=0
354    in
355    if goalIdx==curIdx then moveToSameLevel (pos+d) ds else
356    if goalIdx==0 then dropWhile (isNonZero pos d) cache else
357    if curIdx==0 then moveToChild pos (d:ds) cache else
358    if goalIdx<curIdx then
359      moveToChild pos (d:ds) (dropWhile (isNonZero pos d) cache)
360    else let
361      -- advance the current iterator to coincide with the goal iterator
362      -- at this level
363      moveWithinLevel pos d ((ti,forest):parents) = let
364          diff = fromIntegral (goalIdx-curIdx)
365          (dropped, remain) = splitAt diff forest
366          advance = length dropped
367          ti' = setBitSlice ti pos d (curIdx+fromIntegral advance)
368        in
369        if advance==diff then moveToChild (pos+d) ds ((ti',remain):parents)
370        else (ti',remain):parents -- node not found
371    in moveWithinLevel pos d $ case ds of
372        [] -> cache
373        (d':_) -> dropWhile (isNonZero (pos+d) d') cache
374
375  -- Descend into the topmost forest to find the goal iterator. The position
376  -- and the remainding depths specify the index in the cache that is zero.
377  -- All indices in front of pos coincide with that of the goal iterator.
378  moveToChild :: Int -> Depth -> Cache a -> Cache a
379  moveToChild pos [] cache = cache -- we can't set more than the leaf
380  moveToChild pos (d:ds) cache@((ti,forest):parents)
381    | getBitSlice goal pos d == 0 = cache
382    | otherwise = case forest of
383      [] -> cache -- impossible request
384      Node { subForest = children }:_ ->
385        let
386          childIdx :: Int
387          childIdx = fromIntegral (getBitSlice goal pos d)-1
388          (dropped, remain) = splitAt childIdx children
389          advanced = length dropped
390          ti' = setBitSlice ti pos d (fromIntegral advanced+1)
391        in if advanced<childIdx then ((ti',remain):cache) else
392           moveToChild (pos+d) ds ((ti',remain):cache)
393
394-- | Advance to the given iterator and return weather this was successful.
395--
396checkSuccess :: Depth -> TreeIter -> Cache a -> (Bool, Cache a)
397checkSuccess depth iter cache = case advanceCache depth iter cache of
398    cache'@((cur,sibs):_) -> (cmp cur iter && not (null sibs), cache')
399    [] -> (False, [])
400  where
401  cmp (TreeIter _ a1 b1 c1) (TreeIter _ a2 b2 c2) =
402      a1==a2 && b1==b2 && c2==c2
403
404-- | Get the leaf index of this iterator.
405--
406-- * Due to the way we construct the 'TreeIter's, we can check which the last
407--   level of an iterator is: The bit sequence of level n is zero if n is
408--   greater or equal to the level that the iterator refers to. The returned
409--   triple is (pos, leaf, zero) such that pos..pos+leaf denotes the leaf
410--   index and pos+leaf..pos+leaf+zero denotes the bit field that is zero.
411--
412getTreeIterLeaf :: Depth -> TreeIter -> (Int, Int, Int)
413getTreeIterLeaf ds ti = gTIL 0 0 ds
414  where
415  gTIL pos dCur (dNext:ds)
416    | getBitSlice ti (pos+dCur) dNext==0 = (pos,dCur,dNext)
417    | otherwise = gTIL (pos+dCur) dNext ds
418  gTIL pos d [] = (pos, d, 0)
419
420-- | Move an iterator forwards on the same level.
421--
422iterNext :: Depth -> TreeIter -> Cache a -> (Maybe TreeIter, Cache a)
423iterNext depth iter cache = let
424    (pos,leaf,_child) = getTreeIterLeaf depth iter
425    curIdx = getBitSlice iter pos leaf
426    nextIdx = curIdx+1
427    nextIter = setBitSlice iter pos leaf nextIdx
428  in
429  if nextIdx==bit leaf then (Nothing, cache) else
430  case checkSuccess depth nextIter cache of
431    (True, cache) -> (Just nextIter, cache)
432    (False, cache) -> (Nothing, cache)
433
434-- | Move down to the child of the given iterator.
435--
436iterNthChild :: Depth -> Int -> TreeIter -> Cache a  ->
437                (Maybe TreeIter, Cache a)
438iterNthChild depth childIdx_ iter cache = let
439    (pos,leaf,child) = getTreeIterLeaf depth iter
440    childIdx = fromIntegral childIdx_+1
441    nextIter = setBitSlice iter (pos+leaf) child childIdx
442  in
443  if childIdx>=bit child then (Nothing, cache) else
444  case checkSuccess depth nextIter cache of
445    (True, cache) -> (Just nextIter, cache)
446    (False, cache) -> (Nothing, cache)
447
448-- | Descend to the first child.
449--
450iterNChildren :: Depth -> TreeIter -> Cache a -> (Int, Cache a)
451iterNChildren depth iter cache = case checkSuccess depth iter cache of
452  (True, cache@((_,Node { subForest = forest}:_):_)) -> (length forest, cache)
453  (_, cache) -> (0, cache)
454
455
456-- | Ascend to parent.
457--
458iterParent :: Depth -> TreeIter -> Maybe TreeIter
459iterParent depth iter = let
460    (pos,leaf,_child) = getTreeIterLeaf depth iter
461  in if pos==0 then Nothing else
462     if getBitSlice iter pos leaf==0 then Nothing else
463     Just (setBitSlice iter pos leaf 0)
464
465-- | Insert nodes into the store.
466--
467-- * The given list of nodes is inserted into given parent at @pos@.
468--   If the parent existed, the function returns @Just path@ where @path@
469--   is the position of the newly inserted elements. If @pos@ is negative
470--   or greater or equal to the number of children of the node at @path@,
471--   the new nodes are appended to the list.
472--
473treeStoreInsertForest ::
474    TreeStore a -- ^ the store
475 -> TreePath    -- ^ @path@ - the position of the parent
476 -> Int         -- ^ @pos@ - the index of the new tree
477 -> Forest a    -- ^ the list of trees to be inserted
478 -> IO ()
479treeStoreInsertForest (TreeStore model) path pos nodes = do
480  customStoreInvalidateIters model
481  (idx, toggle) <- atomicModifyIORef (customStoreGetPrivate model) $
482    \store@Store { depth = d, content = cache } ->
483    case insertIntoForest (cacheToStore cache) nodes path pos of
484      Nothing -> error ("treeStoreInsertForest: path does not exist " ++ show path)
485      Just (newForest, idx, toggle) ->
486       let depth = calcForestDepth newForest
487        in (Store { depth = depth,
488                    content = storeToCache newForest },
489           (idx, toggle))
490  Store { depth = depth } <- readIORef (customStoreGetPrivate model)
491  let rpath = reverse path
492  stamp <- customStoreGetStamp model
493  sequence_ [ let p' = reverse p
494                  Just iter = fromPath depth p'
495               in treeModelRowInserted model p' (treeIterSetStamp iter stamp)
496            | (i, node) <- zip [idx..] nodes
497            , p <- paths (i : rpath) node ]
498  let Just iter = fromPath depth path
499  when toggle $ treeModelRowHasChildToggled model path
500                (treeIterSetStamp iter stamp)
501
502  where paths :: TreePath -> Tree a -> [TreePath]
503        paths path Node { subForest = ts } =
504          path : concat [ paths (n:path) t | (n, t) <- zip [0..] ts ]
505
506-- | Insert a node into the store.
507--
508treeStoreInsertTree ::
509    TreeStore a -- ^ the store
510 -> TreePath    -- ^ @path@ - the position of the parent
511 -> Int         -- ^ @pos@ - the index of the new tree
512 -> Tree a      -- ^ the value to be inserted
513 -> IO ()
514treeStoreInsertTree store path pos node =
515  treeStoreInsertForest store path pos [node]
516
517-- | Insert a single node into the store.
518--
519-- * This function inserts a single node without children into the tree.
520--   Its arguments are similar to those of 'treeStoreInsert'.
521--
522treeStoreInsert ::
523    TreeStore a -- ^ the store
524 -> TreePath    -- ^ @path@ - the position of the parent
525 -> Int         -- ^ @pos@ - the index of the new tree
526 -> a           -- ^ the value to be inserted
527 -> IO ()
528treeStoreInsert store path pos node =
529  treeStoreInsertForest store path pos [Node node []]
530
531-- | Insert nodes into a forest.
532--
533-- * If the parent was found, returns the new tree, the child number
534--   and a flag denoting if these new nodes were the first children
535--   of the parent.
536--
537insertIntoForest :: Forest a -> Forest a -> TreePath -> Int ->
538                    Maybe (Forest a, Int, Bool)
539insertIntoForest forest nodes [] pos
540  | pos<0 = Just (forest++nodes, length forest, null forest)
541  | otherwise = Just (prev++nodes++next, length prev, null forest)
542    where (prev, next) = splitAt pos forest
543insertIntoForest forest nodes (p:ps) pos = case splitAt p forest of
544  (prev, []) -> Nothing
545  (prev, Node { rootLabel = val,
546                subForest = for}:next) ->
547    case insertIntoForest for nodes ps pos of
548      Nothing -> Nothing
549      Just (for, pos, toggle) -> Just (prev++Node { rootLabel = val,
550                                                    subForest = for }:next,
551                                       pos, toggle)
552
553-- | Remove a node from the store.
554--
555-- * The node denoted by the path is removed, along with all its children.
556--   The function returns @True@ if the given node was found.
557--
558treeStoreRemove :: TreeStore a -> TreePath -> IO Bool
559  --TODO: eliminate this special case without segfaulting!
560treeStoreRemove (TreeStore model) [] = return False
561treeStoreRemove (TreeStore model) path = do
562  customStoreInvalidateIters model
563  (found, toggle) <- atomicModifyIORef (customStoreGetPrivate model) $
564    \store@Store { depth = d, content = cache } ->
565    if null cache then (store, (False, False)) else
566    case deleteFromForest (cacheToStore cache) path of
567      Nothing -> (store, (False, False))
568      Just (newForest, toggle) ->
569        (Store { depth = d, -- this might be a space leak
570                 content = storeToCache newForest }, (True, toggle))
571  when found $ do
572    when (toggle && not (null path)) $ do
573      Store { depth = depth } <- readIORef (customStoreGetPrivate model)
574      let parent = init path
575          Just iter = fromPath depth parent
576      treeModelRowHasChildToggled model parent iter
577    treeModelRowDeleted model path
578  return found
579
580treeStoreClear :: TreeStore a -> IO ()
581treeStoreClear (TreeStore model) = do
582  customStoreInvalidateIters model
583  Store { content = cache } <- readIORef (customStoreGetPrivate model)
584  let forest = cacheToStore cache
585  writeIORef (customStoreGetPrivate model) Store {
586      depth = calcForestDepth [],
587      content = storeToCache []
588    }
589  let loop (-1) = return ()
590      loop   n  = treeModelRowDeleted model [n] >> loop (n-1)
591  loop (length forest - 1)
592
593-- | Remove a node from a rose tree.
594--
595-- * Returns the new tree if the node was found. The returned flag is
596--   @True@ if deleting the node left the parent without any children.
597--
598deleteFromForest :: Forest a -> TreePath -> Maybe (Forest a, Bool)
599deleteFromForest forest [] = Just ([], False)
600deleteFromForest forest (p:ps) =
601  case splitAt p forest of
602    (prev, kill@Node { rootLabel = val,
603                       subForest = for}:next) ->
604      if null ps then Just (prev++next, null prev && null next) else
605      case deleteFromForest for ps of
606        Nothing -> Nothing
607        Just (for,toggle) -> Just (prev++Node {rootLabel = val,
608                                               subForest = for }:next, toggle)
609    (prev, []) -> Nothing
610
611
612-- | Set a node in the store.
613--
614treeStoreSetValue :: TreeStore a -> TreePath -> a -> IO ()
615treeStoreSetValue store path value = treeStoreChangeM store path (\_ -> return value)
616                                  >> return ()
617
618
619-- | Change a node in the store.
620--
621-- * Returns @True@ if the node was found. For a monadic version, see
622--   'treeStoreChangeM'.
623--
624treeStoreChange :: TreeStore a -> TreePath -> (a -> a) -> IO Bool
625treeStoreChange store path func = treeStoreChangeM store path (return . func)
626
627
628-- | Change a node in the store.
629--
630-- * Returns @True@ if the node was found. For a purely functional version, see
631--   'treeStoreChange'.
632--
633treeStoreChangeM :: TreeStore a -> TreePath -> (a -> IO a) -> IO Bool
634treeStoreChangeM (TreeStore model) path act = do
635  customStoreInvalidateIters model
636  store@Store { depth = d, content = cache } <-
637      readIORef (customStoreGetPrivate model)
638  (store'@Store { depth = d, content = cache }, found) <- do
639    mRes <- changeForest (cacheToStore cache) act path
640    return $ case mRes of
641      Nothing -> (store, False)
642      Just newForest -> (Store { depth = d,
643                                 content = storeToCache newForest }, True)
644  writeIORef (customStoreGetPrivate model) store'
645  let Just iter = fromPath d path
646  stamp <- customStoreGetStamp model
647  when found $ treeModelRowChanged model path (treeIterSetStamp iter stamp)
648  return found
649
650-- | Change a node in the forest.
651--
652-- * Returns @True@ if the given node was found.
653--
654changeForest :: Forest a -> (a -> IO a) -> TreePath -> IO (Maybe (Forest a))
655changeForest forest act [] = return Nothing
656changeForest forest act (p:ps) = case splitAt p forest of
657  (prev, []) -> return Nothing
658  (prev, Node { rootLabel = val,
659                subForest = for}:next) ->
660    if null ps then do
661      val' <- act val
662      return (Just (prev++Node { rootLabel = val',
663                                 subForest = for }:next))
664    else do
665      mFor <- changeForest for act ps
666      case mFor of
667        Nothing -> return Nothing
668        Just for -> return $ Just (prev++Node { rootLabel = val,
669                                                subForest = for }:next)
670
671-- | Extract one node from the current model. Fails if the given
672--   'TreePath' refers to a non-existent node.
673--
674treeStoreGetValue :: TreeStore a -> TreePath -> IO a
675treeStoreGetValue model path = fmap rootLabel (treeStoreGetTree model path)
676
677-- | Extract a subtree from the current model. Fails if the given
678--   'TreePath' refers to a non-existent node.
679--
680treeStoreGetTree :: TreeStore a -> TreePath -> IO (Tree a)
681treeStoreGetTree (TreeStore model) path = do
682  store@Store { depth = d, content = cache } <-
683      readIORef (customStoreGetPrivate model)
684  case fromPath d path of
685    (Just iter) -> do
686      let (res, cache') = checkSuccess d iter cache
687      writeIORef (customStoreGetPrivate model) store { content = cache' }
688      case cache' of
689        ((_,node:_):_) | res -> return node
690        _ -> fail ("treeStoreGetTree: path does not exist " ++ show path)
691    _ -> fail ("treeStoreGetTree: path does not exist " ++ show path)
692
693-- | Extract a subtree from the current model. Like 'treeStoreGetTree'
694--   but returns @Nothing@ if the path refers to a non-existant node.
695--
696treeStoreLookup :: TreeStore a -> TreePath -> IO (Maybe (Tree a))
697treeStoreLookup (TreeStore model) path = do
698  store@Store { depth = d, content = cache } <-
699      readIORef (customStoreGetPrivate model)
700  case fromPath d path of
701    (Just iter) -> do
702      let (res, cache') = checkSuccess d iter cache
703      writeIORef (customStoreGetPrivate model) store { content = cache' }
704      case cache' of
705        ((_,node:_):_) | res -> return (Just node)
706        _ -> return Nothing
707    _ -> return Nothing
708