Model an mutable pile of objects. Objects can be made visible and invisible and can be moved about in the stacking order. It is implemented as an extensible array for fast update and access. Each entry has a unique identifier (EltName) that is an instance of Eq,Ord and Ix. Each entry also has an Ident which is the name of the object it is referring to (such as a widget Id). Elements have a boolean saying whether visible, and a pointer to the element above, below, the visible element above and the visible element below. Elements can be moved in the stacking order. raise p e mb moves e to the top of the pile if mb is Nothing or above a if mb is Just a. lower p e mb moves e to the bottom of the pile if mb is Nothing or below a if mb is Just a. Piles can also have Groups (see Group.lhs). A group models a group of elements that can be moved en-masse. Groups can also be moved above and below other groups. \begin{code} module PileArray ( module Group ,EltName,PileArray,EltData ,identElt -- :: EltData -> Ident ,pileArray -- :: Int -> IO PileArray ,getEltName -- :: PileArray -> IO EltName ,orderPileArray -- :: PileArray -> IO ([EltData],[EltData]) ,getEltData -- :: PileArray -> EltName -> IO (Maybe EltData) ,nameElt -- :: EltData -> EltName ,isVisibleElt -- :: EltData -> Bool ,aboveElt,belowElt,visAboveElt,visBelowElt -- :: EltData -> Maybe EltName ,topElt,bottomElt,visTopElt,visBottomElt -- :: PileArray -> IO (Maybe EltName) ,insertPileElt -- :: PileArray -> EltName -> Maybe EltName -- -> [GroupRef] -> IO () ,deletePileElt -- :: PileArray -> EltName -> IO () ,hidePileElt -- :: PileArray -> EltName -> IO () ,showPileElt -- :: PileArray -> EltName -> IO () ,raisePileElt -- :: PileArray -> EltName -> Maybe EltName -> IO () ,lowerPileElt -- :: PileArray -> EltName -> Maybe EltName -> IO () ,raisePileGp -- :: PileArray -> GroupRef -> Maybe EltName -> IO () ,lowerPileGp -- :: PileArray -> GroupRef -> Maybe EltName -> IO () ,raisePileGp' -- ::PileArray -> GroupRef -> Maybe GroupRef -> IO () ,lowerPileGp' -- ::PileArray -> GroupRef -> Maybe GroupRef -> IO () ) where import Array import IOExts import PlacePos import ExtArray import Monad import Maybe import Group import Identify data EltData = EltData {nameElt_ :: EltName, aboveElt_ :: Maybe EltName,belowElt_ :: Maybe EltName, visAboveElt_ :: Maybe EltName,visBelowElt_ :: Maybe EltName, visibleElt_ :: Bool, identElt :: Ident, groupRef :: [GroupRef]} instance Show EltData where show (EltData nm abv below visabv visbelow visible id _) = show ("EltData",nm,abv,below,(visabv,visbelow,visible,id)) ++ "\n" nameElt :: EltData -> EltName nameElt = nameElt_ isVisibleElt :: EltData -> Bool isVisibleElt = visibleElt_ aboveElt,belowElt,visAboveElt,visBelowElt :: EltData -> Maybe EltName aboveElt = aboveElt_ belowElt = belowElt_ visAboveElt = visAboveElt_ visBelowElt = visBelowElt_ data PileArray = PileArray {pileArray_ :: ExtArray EltData, posPointers_ :: IORef PosPointers} orderPileArray :: PileArray -> IO ([EltData],[EltData]) orderPileArray (PileArray a pv) = do ps <- readIORef pv es <- traverse' a belowElt_ (topName ps) print es vs <- traverse' a visBelowElt_ (visTopName ps) return (es, vs) traverse' :: ExtArray EltData -> (EltData -> Maybe EltName) -> Maybe EltName -> IO [EltData] traverse' a f Nothing = return [] traverse' a f (Just n) = do pMb <- lookupElt a n case pMb of Nothing -> do print ("traverse error",n) return [] Just p -> do ts <- traverse' a f (f p) return $ p:ts getEltName :: PileArray -> IO EltName getEltName (PileArray {pileArray_ = arr}) = do nm <- nextExtEntry arr return $ toElt nm getEltData :: PileArray -> EltName -> IO (Maybe EltData) getEltData (PileArray {pileArray_ = arr}) e = lookupElt arr e topElt,bottomElt,visTopElt,visBottomElt :: PileArray -> IO (Maybe EltName) topElt = getElt topName bottomElt = getElt bottomName visTopElt = getElt visTopName visBottomElt = getElt visBottomName getElt :: (PosPointers -> Maybe EltName) -> PileArray -> IO (Maybe EltName) getElt f (PileArray {posPointers_ = pv}) = do ps <- readIORef pv return $ f ps pileArray :: Int -> IO PileArray pileArray n = do ext <- newExtArray n v <- newIORef initPosPointers return $ PileArray ext v updGp :: EltName -> Maybe EltName -> Maybe EltName -> Maybe EltName -> Maybe EltName -> Bool -> GroupRef -> IO () updGp e mba mbb mbva mbvb visible gr = do updGp' (const True) e e e e mba mbb mbva mbvb visible gr updGp' :: (Ident -> Bool) -> EltName -> EltName -> EltName -> EltName -> Maybe EltName -> Maybe EltName -> Maybe EltName -> Maybe EltName -> Bool -> GroupRef -> IO () updGp' doG eT eB eVT eVB mba mbb mbva mbvb visible gr = do gp@(GroupData {groupName = gn, posPointers = (PosPointers {topName = top, bottomName = bottom, visTopName = vistop, visBottomName = visbottom}) ,groupMembers = gs}) <- readIORef gr when (doG gn) $ do let upd e v mb = if (isJust v && mb == v) || not (isJust v) then Just e else v writeIORef gr $ gp {posPointers = PosPointers (upd eT top mbb) (upd eB bottom mba) (if not visible then vistop else upd eVT vistop mbvb) (if not visible then visbottom else upd eVB visbottom mbva)} updDelGps :: EltData -> Bool -> Bool -> IO () updDelGps (EltData {aboveElt_ = mba,belowElt_ = mbb,visAboveElt_ = mbva,visBelowElt_ = mbvb, groupRef = grs,nameElt_ = e}) doalways dovis = do updDelGps' (const True) e e e e mba mbb mbva mbvb doalways dovis grs updDelGps' :: (Ident -> Bool) -> EltName -> EltName -> EltName -> EltName -> Maybe EltName -> Maybe EltName -> Maybe EltName -> Maybe EltName -> Bool -> Bool -> [GroupRef] -> IO () updDelGps' doGp eT eB eVT eVB mba mbb mbva mbvb doalways dovis grs = mapM_ updDelGp grs where updDelGp gr = do gp@(GroupData {groupName = gn, posPointers = (PosPointers {topName = top, bottomName = bottom, visTopName = vistop, visBottomName = visbottom})}) <- readIORef gr when (doGp gn) $ do let upd e v mb = if Just e == v then mb else v writeIORef gr $ gp {posPointers = PosPointers (if not doalways then top else upd eT top mbb) (if not doalways then bottom else upd eB bottom mba) (if not dovis then vistop else upd eVT vistop mbvb) (if not dovis then visbottom else upd eVB visbottom mbva)} showGpElt :: EltName -> Maybe EltName -> Maybe EltName -> GroupRef -> IO () showGpElt e mba mbb gr = do gp@(GroupData {groupMembers = gs, posPointers = ps@(PosPointers {visTopName = vistop, visBottomName = visbottom})}) <- readIORef gr let topv = maybe (Just e) (\v -> if v `elem` gs then vistop else Just e) mba bottomv = maybe (Just e) (\v -> if v `elem` gs then visbottom else Just e) mbb writeIORef gr $ gp {posPointers = ps {visTopName = topv,visBottomName = bottomv}} raiseOrLowerPileGp' :: Bool -> PileArray -> GroupRef -> Maybe GroupRef -> IO (Maybe EltName) raiseOrLowerPileGp' isRaise pile gr mbg = do mbe <- getWhatElt mbg raiseOrLowerPileGp isRaise pile gr mbe return mbe where getWhatElt Nothing = return Nothing getWhatElt (Just gr) = do gp@(GroupData {posPointers = ps}) <- readIORef gr return $ if isRaise then topName ps else bottomName ps raiseOrLowerPileGp :: Bool -> PileArray -> GroupRef -> Maybe EltName -> IO () raiseOrLowerPileGp isRaise pile gr mbn = do gp@(GroupData {groupName = gn, groupMembers = gs, posPointers = ps}) <- readIORef gr if maybe False (`elem` gs) mbn then do print "attempt to raise gp above one of its own members!" return () else handleOp (handle isRaise mbn gn ps) pile where handle isRaise mbn gn psg@(PosPointers {topName = topG,bottomName = bottomG, visTopName = vistopG,visBottomName = visbottomG}) ps@(PosPointers {topName = top,bottomName = bottom, visTopName = vistop,visBottomName = visbottom}) a = do let get = maybe (return Nothing) (lookupElt a) nt <- get topG nb <- get bottomG ntv <- get vistopG nbv <- get visbottomG case (nt,nb,ntv,nbv) of (Nothing,Nothing,_,_) -> do return ps (Just nte,Just nbe,ntveMb,nbveMb) -> let aboveElt = aboveElt_ nte belowElt = belowElt_ nbe nteL = nameElt_ nte nbeL = nameElt_ nbe nteVL = maybe (error "not visible") nameElt_ ntveMb nbeVL = maybe (error "not visible") nameElt_ nbveMb visAboveElt = maybe Nothing visAboveElt_ ntveMb visBelowElt = maybe Nothing visBelowElt_ nbveMb ntve = maybe (error "not visible") id ntveMb nbve = maybe (error "not visible") id nbveMb visibleElt = isJust ntveMb && isJust nbveMb allgs = groupRef nte -- assumes all members of this group are in all same -- parent groups in if (isRaise && isJust belowElt && belowElt == mbn) || (not isRaise && isJust aboveElt && aboveElt == mbn) then return ps else do (top,bottom) <- removeData top bottom aboveElt belowElt updAboveElt updBelowElt a (vistop,visbottom) <- if visibleElt then do updDelGps' (/= gn) nteL nbeL nteVL nbeVL aboveElt belowElt visAboveElt visBelowElt True True allgs removeData vistop visbottom visAboveElt visBelowElt updVisAboveElt updVisBelowElt a else do updDelGps' (/= gn) nteL nbeL nteVL nbeVL aboveElt belowElt visAboveElt visBelowElt True False allgs return (vistop,visbottom) (above',below',top',bottom') <- if isRaise then aboveData nteL nbeL mbn top bottom (lookupElt' a aboveElt_) updAboveElt updBelowElt a False else belowData nteL nbeL mbn top bottom (lookupElt' a belowElt_) updAboveElt updBelowElt a False (abovev,belowv,topv,bottomv) <- if visibleElt then if isRaise then aboveData nteVL nbeVL mbn vistop visbottom (lookupElt' a visAboveElt_) updVisAboveElt updVisBelowElt a True else belowData nteVL nbeVL mbn vistop visbottom (lookupElt' a visBelowElt_) updVisAboveElt updVisBelowElt a True else return (visAboveElt,visBelowElt,vistop,visbottom) mapM (updGp' (/= gn) nteL nbeL nteVL nbeVL above' below' abovev belowv visibleElt) allgs updElt a nteL (\e -> e {aboveElt_ = above'}) updElt a nbeL (\e -> e {belowElt_ = below'}) when visibleElt $ do updElt a nteVL (\e -> e {visAboveElt_ = abovev}) updElt a nbeVL (\e -> e {visBelowElt_ = belowv}) return $ PosPointers top' bottom' topv bottomv _ -> do print "error pilearray";return ps insertPileElt :: PileArray -> Ident -> EltName -> Maybe EltName -> [GroupRef] -> IO () deletePileElt :: PileArray -> EltName -> IO () hidePileElt :: PileArray -> EltName -> IO () showPileElt :: PileArray -> EltName -> IO () raisePileElt :: PileArray -> EltName -> Maybe EltName -> IO () lowerPileElt :: PileArray -> EltName -> Maybe EltName -> IO () raisePileGp :: PileArray -> GroupRef -> Maybe EltName -> IO () lowerPileGp :: PileArray -> GroupRef -> Maybe EltName -> IO () raisePileGp' ::PileArray -> GroupRef -> Maybe GroupRef -> IO (Maybe EltName) lowerPileGp' ::PileArray -> GroupRef -> Maybe GroupRef -> IO (Maybe EltName) raisePileGp' = raiseOrLowerPileGp' True lowerPileGp' = raiseOrLowerPileGp' False raisePileGp = raiseOrLowerPileGp True lowerPileGp = raiseOrLowerPileGp False insertPileElt pile ident n mbn mbg = handleOp (handle n mbn) pile where handle n mbn pos@(PosPointers {topName = top, bottomName = bottom, visTopName = vistop, visBottomName = visbottom}) a = do (above',below',top',bottom') <- aboveData n n mbn top bottom (lookupElt' a aboveElt_) updAboveElt updBelowElt a False (abovev,belowv,topv,bottomv) <- aboveData n n mbn vistop visbottom (lookupElt' a visAboveElt_) updVisAboveElt updVisBelowElt a True mapM (\g -> do updGp n above' below' abovev belowv True g insertInGroup g n) mbg writeElt a n (EltData n above' below' abovev belowv True ident mbg) return $ PosPointers top' bottom' topv bottomv deletePileElt pile n = handleOp (handle n ) pile where handle n pos@(PosPointers {topName = top, bottomName = bottom, visTopName = vistop, visBottomName = visbottom}) a = do maybeIO (return pos) (lookupElt a n) $ \e -> do (top',bottom') <- removeData top bottom (aboveElt_ e) (belowElt_ e) updAboveElt updBelowElt a (topv,bottomv) <- if visibleElt_ e then removeData vistop visbottom (visAboveElt_ e) (visBelowElt_ e) updVisAboveElt updVisBelowElt a else return (vistop,visbottom) updDelGps e True True mapM (flip deleteFromGroup n) (groupRef e) clearElt a n return $ PosPointers top' bottom' topv bottomv hidePileElt pile n = handleOp (handle n ) pile where handle n pos@(PosPointers {topName = top, bottomName = bottom, visTopName = vistop, visBottomName = visbottom}) a = do maybeIO (return pos) (lookupElt a n) $ \e -> if not (visibleElt_ e) then return pos else do (topv,bottomv) <- removeData vistop visbottom (visAboveElt_ e) (visBelowElt_ e) updVisAboveElt updVisBelowElt a updDelGps e False True writeElt a n (e {visibleElt_ = False,visAboveElt_ = Nothing,visBelowElt_ = Nothing}) return $ PosPointers top bottom topv bottomv showPileElt pile n = handleOp (handle n ) pile where handle n pos@(PosPointers {topName = top, bottomName = bottom, visTopName = vistop, visBottomName = visbottom}) a = do maybeIO (return pos) (lookupElt a n) $ \e -> if visibleElt_ e then return pos else do aboveD <- traverse a aboveElt_ (aboveElt_ e) belowD <- traverse a belowElt_ (belowElt_ e) let above = fmap nameElt_ aboveD let below = fmap nameElt_ belowD writeElt a n (e {visibleElt_ = True, visAboveElt_ = above, visBelowElt_ = below}) when (isJust above) $ updElt a (fromJust above) (updVisBelowElt $ Just n) when (isJust below) $ updElt a (fromJust below) (updVisAboveElt $ Just n) mapM (showGpElt n above below) (groupRef e) return $ PosPointers top bottom (maybe (Just n) (const vistop) above) (maybe (Just n) (const visbottom) below) raisePileElt pile n mbn = handleOp (raiseOrLower True n mbn) pile lowerPileElt pile n mbn = handleOp (raiseOrLower False n mbn) pile raiseOrLower :: Bool -> EltName -> Maybe EltName -> PosPointers -> ExtArray EltData -> IO PosPointers raiseOrLower isRaise n mbn pos@(PosPointers {topName = top, bottomName = bottom, visTopName = vistop, visBottomName = visbottom}) a = do maybeIO (return pos) (lookupElt a n) $ \e -> if (isRaise && isJust (belowElt_ e) && belowElt_ e == mbn) || (not isRaise && isJust (aboveElt_ e) && aboveElt_ e == mbn) then return pos else do (top,bottom) <- removeData top bottom (aboveElt_ e) (belowElt_ e) updAboveElt updBelowElt a (vistop,visbottom) <- if visibleElt_ e then do updDelGps e True True removeData vistop visbottom (visAboveElt_ e) (visBelowElt_ e) updVisAboveElt updVisBelowElt a else do updDelGps e True False return (vistop,visbottom) (above',below',top',bottom') <- if isRaise then aboveData n n mbn top bottom (lookupElt' a aboveElt_) updAboveElt updBelowElt a False else belowData n n mbn top bottom (lookupElt' a belowElt_) updAboveElt updBelowElt a False (abovev,belowv,topv,bottomv) <- if visibleElt_ e then if isRaise then aboveData n n mbn vistop visbottom (lookupElt' a visAboveElt_) updVisAboveElt updVisBelowElt a True else belowData n n mbn vistop visbottom (lookupElt' a visBelowElt_) updVisAboveElt updVisBelowElt a True else return (visAboveElt_ e,visBelowElt_ e,vistop,visbottom) mapM (updGp n above' below' abovev belowv (visibleElt_ e)) (groupRef e) writeElt a n (e {aboveElt_ = above',belowElt_ = below', visAboveElt_ = abovev,visBelowElt_ = belowv}) return $ PosPointers top' bottom' topv bottomv maybeIO :: IO a -> (IO (Maybe b)) -> (b -> IO a) -> IO a maybeIO doA act doB = do dMb <- act case dMb of Nothing -> doA Just d -> doB d updAboveElt :: Maybe EltName -> EltData -> EltData updAboveElt a p = p {aboveElt_ = a} updBelowElt :: Maybe EltName -> EltData -> EltData updBelowElt a p = p {belowElt_ = a} updVisBelowElt :: Maybe EltName -> EltData -> EltData updVisBelowElt a p = p {visBelowElt_ = a} updVisAboveElt :: Maybe EltName -> EltData -> EltData updVisAboveElt a p = p {visAboveElt_ = a} aboveData :: EltName -> EltName -> Maybe EltName -> Maybe EltName -> Maybe EltName -> (EltName -> IO (Maybe EltName)) -> (Maybe EltName -> EltData -> EltData) -> (Maybe EltName -> EltData -> EltData) -> ExtArray EltData -> Bool -> IO (Maybe EltName,Maybe EltName,Maybe EltName,Maybe EltName) aboveData nte nbe Nothing top bottom _ updAbove _ a _ = do when (isJust top) $ updElt a (fromJust top) (updAbove $ Just nbe) return (Nothing,top,Just nte,bottom `mplus` Just nbe) aboveData nte nbe (Just n') top bottom getAbove updAbove updBelow a justvis = do nd <- lookupElt a n' case nd of Nothing -> return (Nothing,top,Just nte,bottom `mplus` Just nbe) Just (EltData {visibleElt_ = False,belowElt_ = bel}) | justvis -> do mbd <- traverse a belowElt_ bel aboveData nte nbe (fmap nameElt_ mbd) top bottom getAbove updAbove updBelow a justvis Just _ -> do na <- getAbove n' updElt a n' (updAbove $ Just nbe) when (isJust na) $ updElt a (fromJust na) (updBelow $ Just nte) return (na,Just n', if Just n' == top || Nothing == top then Just nte else top, bottom `mplus` Just nbe) belowData :: EltName -> EltName -> Maybe EltName -> Maybe EltName -> Maybe EltName -> (EltName -> IO (Maybe EltName)) -> (Maybe EltName -> EltData -> EltData) -> (Maybe EltName -> EltData -> EltData) -> ExtArray EltData -> Bool -> IO (Maybe EltName,Maybe EltName,Maybe EltName,Maybe EltName) belowData nte nbe Nothing top bottom _ updAbove updBelow a _ = do when (isJust bottom) $ updElt a (fromJust bottom) (updBelow $ Just nte) return (bottom,Nothing,top `mplus` Just nte,Just nbe) belowData nte nbe (Just n') top bottom getBelow updAbove updBelow a justvis = do nd <- lookupElt a n' case nd of Nothing -> return (bottom,Nothing,top `mplus` Just nbe,Just nbe) Just (EltData {visibleElt_ = False,aboveElt_ = abov}) | justvis -> do mbd <- traverse a aboveElt_ abov belowData nte nbe (fmap nameElt_ mbd) top bottom getBelow updAbove updBelow a justvis Just _ -> do nb <- getBelow n' updElt a n' (updBelow $ Just nte) when (isJust nb) $ updElt a (fromJust nb) (updAbove $ Just nbe) return (Just n',nb, top `mplus` Just nte, if Just n' == bottom || Nothing == bottom then Just nbe else bottom) removeData :: Maybe EltName -> Maybe EltName -> Maybe EltName -> Maybe EltName -> (Maybe EltName -> EltData -> EltData) -> (Maybe EltName -> EltData -> EltData) -> ExtArray EltData -> IO (Maybe EltName,Maybe EltName) removeData top bottom na nb updAbove updBelow a = do case (na,nb) of (Nothing,Just n') -> do updElt a n' (updAbove Nothing) return $ (Just n',bottom) (Just n',Nothing) -> do updElt a n' (updBelow Nothing) return $ (top,Just n') (Just na',Just nb') -> do updElt a na' (updBelow nb) updElt a nb' (updAbove na) return $ (top,bottom) (Nothing,Nothing) -> do return $ (Nothing,Nothing) traverse :: ExtArray EltData -> (EltData -> Maybe EltName) -> Maybe EltName -> IO (Maybe EltData) traverse a f Nothing = return Nothing traverse a f (Just n) = do pMb <- lookupElt a n case pMb of Nothing -> do print ("traverse error",n) return Nothing Just p -> if visibleElt_ p then return $ Just p else traverse a f (f p) handleOp :: (PosPointers -> ExtArray EltData -> IO PosPointers) -> PileArray -> IO () handleOp f (PileArray {posPointers_ = pv,pileArray_ = pile}) = do ps <- readIORef pv f ps pile >>= writeIORef pv lookupElt :: ExtArray EltData -> EltName -> IO (Maybe EltData) lookupElt arr e = readExtArray arr $ fromElt e lookupElt' :: ExtArray EltData -> (EltData -> Maybe EltName) -> EltName -> IO (Maybe EltName) lookupElt' arr f e = do mbE <- lookupElt arr e return $ maybe Nothing f mbE writeElt :: ExtArray EltData -> EltName -> EltData -> IO () writeElt arr e ed = writeExtArray arr (fromElt e) ed clearElt :: ExtArray EltData -> EltName -> IO () clearElt arr e = clearExtArray arr (fromElt e) updElt :: ExtArray EltData -> EltName -> (EltData -> EltData) -> IO () updElt arr e f = updExtArray arr (fromElt e) f \end{code}