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