Some more gui monad operations.

\begin{code}

module GUI (module GUI,module GUIDef,GUIInfo) where
import IO
import FranCore
import BVars
import IOExts
import WH
import MutSet
import Identify
import WidgetExports (debugW,runLoop,GUIInfo,rootWinPrim,getWTime,quitW,
		      afterW,everyW,updateW,primRunW,primRunW_)
import GUIDef
import Conf
import Compatibility
import Monad
\end{code}

Run a GUI operation.
This involves creating a GUIDef, and running the toolkit event loop.
We run a closeDown action at the end.
We get the root window and make it the current window, setup fran,
and then run the GUI action. Updating behaviors afterwards

\begin{code}
start :: GUI () -> IO ()
start act = do
 info <- mkGUIDef (error "no GUIInfo yet") (error "no windows yet")
 runLoop (closeDown info) $ \g -> do
  win <- rootWinPrim g
  win <- mkSimplePrim win info 
  franInit
  let info' = info {guiInfo = g,currentWindow = win,rootWindowRef = win}
  primAction info' $ runGUI info' $ act
\end{code}

make a simple prim widget. 

\begin{code}
mkSimplePrim :: w -> GUIDef -> IO (PrimWidget w)
mkSimplePrim w info = do
 aref <- newIORef 0
 ref <- newIORef Nothing
 wp <- mkWeakIORef ref (return ())
 return $ PrimWidget aref info w ref wp neverE
\end{code}

quit - shutdown everything

\begin{code}
quit :: GUI ()
quit = withGUIInfo quitW 
\end{code}

set debugging info for the underlying toolkit

\begin{code}
debug :: Bool -> GUI ()
debug b = withGUIInfo $ debugW b
\end{code}

set timers

\begin{code}
after :: Double -> GUI () -> GUI (IO ())
after v act = withGUIDef $ \g -> do
  afterW v (primAction g (runGUI g act)) (guiInfo g)
\end{code}

close down cleanly by zapping all the fran global variables, and garbage 
collecting then running finalisers. This is important in hugs to stop
weak pointers doing funny stuff.

\begin{code} 
closeDown :: GUIDef -> IO ()
closeDown (GUIDef {nameSupply = nms,activeWidgets = mset}) = do
 franInit
 clearMutSet mset
 writeIORef nms 0
 performGC >> yield
 performGC >> yield
 performGC >> yield
\end{code}

a primitive action ie user input, causes the current time to be set.
the action is then run. the time remains constant in this running step.
we then get the time after the step and run all the fran workpools once.
every 10 seconds we garbage collect and run finalisers, to make sure
we're zapping weak pointers.

\begin{code}
primAction :: GUIDef -> IO () -> IO ()
primAction (GUIDef {guiInfo = gui,since = ref}) act = do
 t <- getWTime gui
 setFranTime t
 act
 t <- getWTime gui
 runFranOnce t
 tr <- readIORef ref
 when (t - 10 > tr) $ do
  writeIORef ref t
  performGC
  yield
\end{code}


Run some primitive code

\begin{code}
primRun :: [String] -> GUI String
primRun_ :: [String] -> GUI ()

primRun = withGUIInfo . primRunW
primRun_ = withGUIInfo . primRunW_

\end{code}

update behaviors and the display
\begin{code}
update :: GUI ()
update = withGUIDef $ \g -> primAction g (return ()) >> updateW (guiInfo g)
\end{code}

IORefs in the GUI monad.
They're here but not directly exported from frantk at the moment.
we'll see if there is any demand. there generally shouldn't be
as BVars should be capable of doing everything.

\begin{code}
type GUIRef a = IORef a

newState :: a -> GUI (GUIRef a)
newState a = liftIO $ newIORef a

readState :: GUIRef a -> GUI a
readState = liftIO . readIORef

writeState :: GUIRef a -> a -> GUI ()
writeState ref f = liftIO $ writeIORef ref f

modState :: GUIRef a -> (a -> a) -> GUI ()
modState ref f = liftIO $ updIORef ref f

type GUIArray a = IOArray Int a

newGUIArray :: Int -> a -> GUI (GUIArray a)
newGUIArray n x = liftIO  (newIOArray (0,n-1) x)

readGUIArray :: GUIArray a -> Int -> GUI a
readGUIArray arr i = liftIO  (readIOArray arr i)

writeGUIArray :: GUIArray a -> Int -> a -> GUI ()
writeGUIArray arr i x = liftIO  (writeIOArray arr i x)

modGUIArray :: GUIArray a -> Int -> (a->a) -> GUI ()
modGUIArray arr i f = do x <- readGUIArray arr i; writeGUIArray arr i (f x)
\end{code}

Update and use Styles

\begin{code}

getStyle :: GUI (Conf Style)
getStyle = do 
 (GUIDef {currentStyle = cs}) <- getGUIDef
 return cs

withStyle' :: [Conf Style] -> GUI a -> GUI a
withStyle' cs act = do
 oldc <- getStyle
 updGUIDef $ \g -> g {currentStyle = composeConfs $ cs ++ [oldc]}
 a <- act
 updGUIDef $ \g -> g {currentStyle = oldc}
 return a

\end{code}


We can define behavioral configuration options in terms of static ones.
Every time the behavior changes we update the config value (we can also
optimise by checking for equality and only updating when the value has
actually changed.)

\begin{code}
csetAllB :: WidgetItem w => [ConfigName] -> w -> [Conf w] -> GUI ()
csetAllB valids w cs =  do
 s <- getStyle
 case getConfigs valids s cs of
  (cs,as) -> do
    liftIO $ cset w $ reverse cs
    as w

csetAllB' :: WidgetItem w => [ConfigName] -> w -> [Conf w] -> Conf Style -> GUI ()
csetAllB' valids w cs s =  do
 case getConfigs valids s cs of
  (cs,as) -> do
    liftIO $ cset w $ reverse cs
    as w


csetB :: WidgetItem w =>  w -> [Conf w] -> GUI ()
csetB w cs =  do
 s <- getStyle
 case getConfigs [] emptyConf cs of
  (cs,as) -> do
    liftIO $ cset w $ reverse cs
    as w

setB' :: (WidgetItem w) => (a -> a -> Bool) 
		        -> w -> (a -> Conf w) -> Behavior a -> GUI ()
setB' eq w c (bh :: Behavior a) = do
 ref <- liftIO $ newIORef (const False)
 let set gui v = do
	f <- readIORef ref
        unless (f v) $ do
	  writeIORef ref (eq v)
	  runGUI gui $ csetB w [c v]
 gui <- getGUIDef 
 rm <- liftIO $ addIOB (lift1 (set gui) bh)
 liftIO $ addFinaliserW w rm
 return ()
          
setB :: (WidgetItem w,Eq a) =>  w -> (a -> Conf w) -> Behavior a -> GUI ()
setB = setB' (==)

confBeh :: (WidgetItem w,Eq a) => ConfigName -> (a -> Conf w) -> Behavior a -> Conf w
confBeh nm f bh = confGUI' nm $ \w -> setB w f bh

actB :: WidgetItem w => (w -> a -> IO ()) -> Behavior a -> w -> IO ()
actB f b w = do
 rm <- addIOB (lift1 (f w) b) 
 addFinaliserW w rm
 return ()
\end{code} 
