leksah-0.8.0.6: src/IDE/Core/State.hs
{-# OPTIONS_GHC -XFlexibleContexts -XTypeSynonymInstances -XMultiParamTypeClasses
-XScopedTypeVariables -XCPP -XDeriveDataTypeable #-}
-----------------------------------------------------------------------------
--
-- Module : IDE.Core.State
-- Copyright : (c) Juergen Nicklisch-Franken, Hamish Mackenzie
-- License : GNU-GPL
--
-- Maintainer : <maintainer at leksah.org>
-- Stability : provisional
-- Portability : portable
--
-- | The core state of ide. This module is imported from every other module,
-- | and all data structures of the state are declared here, to avoid circular
-- | module dependencies.
--
-------------------------------------------------------------------------------
module IDE.Core.State (
window
, errorRefs
, breakpointRefs
, contextRefs
, currentError
, currentBreak
, currentContext
, setCurrentError
, setCurrentBreak
, setCurrentContext
, isInterpreting
, isStartingOrClosing
, triggerEventIDE
, deactivatePane
-- * Convenience methods for accesing the IDE State
, readIDE
, modifyIDE
, modifyIDE_
, withIDE
, getIDE
, throwIDE
, reifyIDE
, reflectIDE
, catchIDE
, postSyncIDE
, postAsyncIDE
, forkIDE
, sysMessage
, MessageLevel(..)
, ideMessage
, logMessage
, withoutRecordingDo
--, deactivatePane
--, deactivatePaneIfActive
--, closePane
, activeProjectDir
#ifdef YI
, liftYiControl
, liftYi
#endif
, module IDE.Core.Types
, module IDE.Core.CTypes
, module IDE.Utils.Utils
, module Graphics.UI.Frame.Panes
, module Graphics.UI.Frame.ViewFrame
) where
import Graphics.UI.Gtk hiding (get)
import Graphics.UI.Gtk.SourceView.SourceView ()
import Data.IORef
import Control.Monad.Reader hiding (liftIO)
import Control.Exception
import Prelude hiding (catch)
import Control.Monad.State
import IDE.Core.Types
import Graphics.UI.Frame.Panes
import Graphics.UI.Frame.ViewFrame --hiding (notebookInsertOrdered)
import Control.Event
import System.IO
import Data.Maybe (isJust)
import System.FilePath (dropFileName)
import IDE.Core.CTypes
import Control.Concurrent (forkIO)
import IDE.Utils.Utils
import qualified Data.Map as Map (lookup)
import Data.Typeable(Typeable)
#ifdef YI
import qualified Yi as Yi
import qualified Yi.UI.Pango.Control as Yi
#endif
instance PaneMonad IDEM where
getFrameState = readIDE frameState
setFrameState v = modifyIDE_ (\ide -> ide{frameState = v})
runInIO f = reifyIDE (\ideRef -> return (\v -> reflectIDE (f v) ideRef))
panePathForGroup id = do
prefs <- readIDE prefs
case id `lookup` (categoryForPane prefs) of
Just group -> case group `lookup` (pathForCategory prefs) of
Nothing -> return (defaultPath prefs)
Just p -> return p
Nothing -> return (defaultPath prefs)
getThisPane = getPanePrim
-- getThisPane :: forall alpha beta . RecoverablePane alpha beta delta => Maybe PanePath -> delta alpha
getOrBuildThisPane ePpoPid = do
mbPane <- getPanePrim
case mbPane of
Nothing -> do
pp <- case ePpoPid of
Right pId -> getBestPathForId pId
Left ppp -> do
layout <- getLayout
return (getBestPanePath ppp layout)
nb <- getNotebook pp
buildPane pp nb builder
Just pane -> return (Just pane)
-- displayThisPane :: Bool -> delta alpha
displayThisPane pane shallGrabFocus = do
liftIO $ bringPaneToFront pane
when shallGrabFocus $ liftIO $ widgetGrabFocus $ getTopWidget pane
-- buildThisPane :: forall alpha beta . RecoverablePane alpha beta delta => PanePath ->
-- Notebook ->
-- (PanePath -> Notebook -> Window -> delta (alpha,Connections)) ->
-- delta alpha
buildThisPane panePath notebook builder = do
windows <- getWindows
(mbBuf,cids) <- builder panePath notebook (head windows)
case mbBuf of
Nothing -> return Nothing
Just buf -> do
panes' <- getPanesSt
paneMap' <- getPaneMapSt
let b1 = case Map.lookup (paneName buf) paneMap' of
Nothing -> True
Just it -> False
let b2 = case Map.lookup (paneName buf) panes' of
Nothing -> True
Just it -> False
if b1 && b2
then do
notebookInsertOrdered notebook (getTopWidget buf) (paneName buf) Nothing False
addPaneAdmin buf cids panePath
liftIO $ do
widgetSetName (getTopWidget buf) (paneName buf)
widgetShowAll (getTopWidget buf)
widgetGrabFocus (getTopWidget buf)
bringPaneToFront buf
return (Just buf)
else return Nothing
--activateThisPane :: forall alpha beta . RecoverablePane alpha beta delta => alpha -> Connections -> delta ()
activateThisPane pane conn = do
mbAP <- getActivePane
case mbAP of
Just (pn,_) | pn == paneName pane -> return ()
_ -> do
deactivatePaneWithout
triggerEventIDE (StatusbarChanged [CompartmentPane (Just (PaneC pane))])
liftIO $ bringPaneToFront pane
setActivePane (Just (paneName pane,conn))
trigger (Just (paneName pane))
(case mbAP of
Nothing -> Nothing
Just (pn,_) -> Just pn)
modifyIDE_ (\ide -> ide{recentPanes =
paneName pane : filter (/= paneName pane) (recentPanes ide)})
return ()
where
trigger :: Maybe String -> Maybe String -> IDEAction
trigger s1 s2 = do
triggerEventIDE (RecordHistory ((PaneSelected s1), PaneSelected s2))
triggerEventIDE (Sensitivity [(SensitivityEditor, False)])
return ()
--closeThisPane :: forall alpha beta . RecoverablePane alpha beta delta => alpha -> delta Bool
closeThisPane pane = do
(panePath,_) <- guiPropertiesFromName (paneName pane)
nb <- getNotebook panePath
mbI <- liftIO $notebookPageNum nb (getTopWidget pane)
case mbI of
Nothing -> liftIO $ do
error ("notebook page not found: unexpected " ++ paneName pane ++ " " ++ show panePath)
return False
Just i -> do
deactivatePaneIfActive pane
liftIO $ do
notebookRemovePage nb i
widgetDestroy (getTopWidget pane)
removePaneAdmin pane
modifyIDE_ (\ide -> ide{recentPanes = filter (/= paneName pane) (recentPanes ide)})
return True
data MessageLevel = Silent | Normal | High
deriving (Eq,Ord,Show)
-- Shall be replaced
sysMessage :: MonadIO m => MessageLevel -> String -> m ()
sysMessage ml str = liftIO $ do
putStrLn str
hFlush stdout
ideMessage :: MessageLevel -> String -> IDEAction
ideMessage level str = do
triggerEventIDE (LogMessage (str ++ "\n") LogTag)
liftIO $ sysMessage level str
logMessage :: String -> LogTag -> IDEAction
logMessage str tag = do
triggerEventIDE (LogMessage (str ++ "\n") tag)
return ()
-- with hslogger
---- ---------------------------------------------------------------------
---- Exception handling
----
data IDEException = IDEException String
deriving Typeable
instance Show IDEException where
show (IDEException str) = str
instance Exception IDEException
throwIDE str = throw (IDEException str)
-- Main window is always the first one in the list
window = head . windows
activeProjectDir :: IDEM FilePath
activeProjectDir = do
activePack' <- readIDE activePack
case activePack' of
Nothing -> return "."
Just pack -> return (dropFileName (ipdCabalFile pack))
errorRefs :: IDE -> [LogRef]
errorRefs = (filter ((\t -> t == ErrorRef || t == WarningRef) . logRefType)) . allLogRefs
breakpointRefs :: IDE -> [LogRef]
breakpointRefs = (filter ((== BreakpointRef) . logRefType)) . allLogRefs
contextRefs :: IDE -> [LogRef]
contextRefs = (filter ((== ContextRef) . logRefType)) . allLogRefs
currentError = (\(e,_,_)-> e) . currentEBC
currentBreak = (\(_,b,_)-> b) . currentEBC
currentContext = (\(_,_,c)-> c) . currentEBC
setCurrentError e = do
modifyIDE_ (\ide -> ide{currentEBC = (e, currentBreak ide, currentContext ide)})
triggerEventIDE (CurrentErrorChanged e) >> return ()
setCurrentBreak b = do
modifyIDE_ (\ide -> ide{currentEBC = (currentError ide, b, currentContext ide)})
triggerEventIDE (CurrentBreakChanged b) >> return ()
setCurrentContext c = modifyIDE_ (\ide -> ide{currentEBC = (currentError ide, currentBreak ide, c)})
isStartingOrClosing :: IDEState -> Bool
isStartingOrClosing IsStartingUp = True
isStartingOrClosing IsShuttingDown = True
isStartingOrClosing _ = False
isInterpreting :: IDEM Bool
isInterpreting = do
readIDE ghciState >>= \mb -> return (isJust mb)
triggerEventIDE :: IDEEvent -> IDEM IDEEvent
triggerEventIDE e = ask >>= \ideR -> triggerEvent ideR e
--
-- | A reader monad for a mutable reference to the IDE state
--
reifyIDE :: (IDERef -> IO a) -> IDEM a
reifyIDE = ReaderT
reflectIDE :: IDEM a -> IDERef -> IO a
reflectIDE c ideR = runReaderT c ideR
#ifdef YI
liftYiControl :: Yi.ControlM a -> IDEM a
liftYiControl f = do
control <- readIDE yiControl
liftIO $ Yi.runControl f control
liftYi :: Yi.YiM a -> IDEM a
liftYi = liftYiControl . Yi.liftYi
#endif
catchIDE :: Exception e => IDEM a -> (e -> IO a) -> IDEM a
catchIDE block handler = reifyIDE (\ideR -> catch (reflectIDE block ideR) handler)
forkIDE :: IDEAction -> IDEAction
forkIDE block = reifyIDE (\ideR -> forkIO (reflectIDE block ideR) >> return ())
postSyncIDE :: IDEM a -> IDEM a
postSyncIDE f = reifyIDE (\ideR -> postGUISync (reflectIDE f ideR))
postAsyncIDE :: IDEM () -> IDEM ()
postAsyncIDE f = reifyIDE (\ideR -> postGUIAsync (reflectIDE f ideR))
-- ---------------------------------------------------------------------
-- Convenience methods for accesing the IDE State
--
-- | Read an attribute of the contents
readIDE :: (IDE -> beta) -> IDEM beta
readIDE f = do
e <- ask
liftIO $ liftM f (readIORef e)
-- | Modify the contents, without returning a value
modifyIDE_ :: (IDE -> IDE) -> IDEM ()
modifyIDE_ f = let f' a = (f a,()) in do
e <- ask
liftIO (atomicModifyIORef e f')
-- | Variation on modifyIDE_ that lets you return a value
modifyIDE :: (IDE -> (IDE,beta)) -> IDEM beta
modifyIDE f = do
e <- ask
liftIO (atomicModifyIORef e f)
withIDE :: (IDE -> IO alpha) -> IDEM alpha
withIDE f = do
e <- ask
liftIO $ f =<< readIORef e
getIDE :: IDEM(IDE)
getIDE = do
e <- ask
st <- liftIO $ readIORef e
return st
withoutRecordingDo :: IDEAction -> IDEAction
withoutRecordingDo act = do
(b,l,n) <- readIDE guiHistory
if not b then do
modifyIDE_ (\ide -> ide{guiHistory = (True,l,n)})
act
(b,l,n) <- readIDE guiHistory
modifyIDE_ (\ide -> ide{guiHistory = (False,l,n)})
else act
-- ---------------------------------------------------------------------
-- Activating and deactivating Panes.
-- This is here and not in Views because it needs some dependencies
-- (e.g. Events for history)
--
deactivatePane :: IDEAction
deactivatePane = do
mbAP <- getActivePane
case mbAP of
Nothing -> return ()
Just (pn, _) -> do
deactivatePaneWithout
triggerEventIDE (RecordHistory (PaneSelected Nothing,
PaneSelected (Just pn)))
triggerEventIDE (Sensitivity [(SensitivityEditor, False)])
return ()
deactivatePaneWithout :: IDEAction
deactivatePaneWithout = do
triggerEventIDE (StatusbarChanged [CompartmentPane Nothing])
mbAP <- getActivePane
case mbAP of
Just (_,signals) -> liftIO $do
signalDisconnectAll signals
Nothing -> return ()
setActivePane Nothing
deactivatePaneIfActive :: RecoverablePane alpha beta IDEM => alpha -> IDEAction
deactivatePaneIfActive pane = do
mbActive <- getActivePane
case mbActive of
Nothing -> return ()
Just (n,_) -> if n == paneName pane
then deactivatePane
else return ()