----------------------------------------------------------------------------- -- -- Module : Main.hs -- Copyright : (c) Neil Mitchell 2007 -- License : -- -- Maintainer : -- Stability : unstable -- Portability : not portable, uses Gtk2Hs -- ----------------------------------------------------------------------------- module Main where import PropLang.Gtk import PropLang.Variable import PropLang.Value import PropLang.Event import Control.Monad import Data.Char import Data.Maybe import System.IO import Control.Concurrent import Graphics.UI.Gtk hiding (Action, Window, ComboBox, MenuItem, TextView, ToolButton, FontButton, Event, onClicked, onChanged) import Graphics.UI.Gtk.Glade import System.Exit --import System.Posix.Signals import System.Process import Config import Data import Evaluator import Prof import Util main :: IO () main = do initPropLang confInit window <- getWindow "res/guihaskell.glade" "wndMain" prefWindow <- getWindow "res/prefdialog.glade" "wndPref" aboutWindow <- getWindow "res/about.glade" "wndAbout" running <- newVarName "evaluator_on_off" True filename <- newVarWithName "selected_filename" (newConfValueWithDefault Nothing "selected_filename") tags <- newVar [] history <- newVar ([], []) -- Configuration variables profCFlags <- newVarWithName "profiler_cflags_conf" (newConfValueWithDefault "-prof -auto-all" "profCFlags") profRFlags <- newVarWithName "profiler_rflags_conf" (newConfValueWithDefault "+RTS -p" "profRFlags") executable <- newVarWithName "executable_name" (newConfValueWithDefault "foobar.exe" "executable") font <- newVarWithName "textview_font" (newConfValueWithDefault "Monospace 12" "textview_font") -- Evaluator variables current <- newVarWithName "current_evaluator" (newConfValueWithDefault GHCi "current_evaluator") states <- newVarName "evaluator_states" empty let f x = getCtrl window x g x = getCtrl prefWindow x dat = Data window (f "txtOut") (f "txtIn") (f "txtSelect") (f "sb") (f "tbRun") (f "tbStop") (f "tbRestart") (f "tbOpen") (f "tbRecent") (f "tbProfile") (f "tbPref") (f "cbCompiler") (f "fbFont") (f "miFile") (f "miOpen") (f "miQuit") (f "miEdit") (f "miCut") (f "miCopy") (f "miPaste") (f "miView") (f "miTools") (f "miRun") (f "miProfile") (f "miPref") (f "midHelp") (f "miAbout") prefWindow (g "txtExecutable") (g "txtProfCFlags") (g "txtProfRFlags") (g "tbClose") aboutWindow running filename tags history profCFlags profRFlags executable font current states startEvaluator dat Nothing setupRelations dat setupFonts dat showWindowMain window -- -- Run an open file dialog and -- return the selection -- runFileDialog :: IO (Maybe FilePath) runFileDialog = do dialog <- fileChooserDialogNew Nothing Nothing FileChooserActionOpen [("Okay", ResponseOk), ("Cancel", ResponseCancel)] response <- dialogRun dialog widgetHide dialog handleResponse dialog response where handleResponse dialog response = case response of ResponseOk -> fileChooserGetFilename dialog _ -> return Nothing -- -- Defines the actions for GUI elements -- setupRelations :: Data -> IO () setupRelations dat@Data -- This is ugly, but at least it's readable { window =window , tbRun =tbRun , tbStop =tbStop , tbRestart =tbRestart , tbOpen =tbOpen , tbPref =tbPref , tbProfile =tbProfile , cbCompiler =cbCompiler , fbFont =fbFont , txtIn =txtIn , txtSelect =txtSelect , miFile =miFile , miOpen =miOpen , miQuit =miQuit , miRun =miRun , miProfile =miProfile , miPref =miPref , miHelp =miHelp , miAbout =miAbout , wndPref =wndPref , txtExecutable =txtExecutable , txtProfCFlags =txtProfCFlags , txtProfRFlags =txtProfRFlags , tbClose =tbClose , wndAbout =wndAbout , running =running , filename =filename , executable =executable , profCFlags =profCFlags , profRFlags =profRFlags , font =font , current =current } = do -- Evaluator events tbRun!onClicked += fireCommand dat tbRestart!onClicked += refreshCommand dat tbOpen!onClicked += (runFileDialog >>= setCurrentFile dat >> startWithFile dat) tbStop!onClicked += stopCommand dat onEnterKey txtIn $ fireCommand dat -- Evaluator selection injectWith (cbCompiler!text) current show current =< with1 (cbCompiler!text) (\x -> if null x then Hugs else read x) current += switchEvaluator dat -- Filename selection injectWith (txtSelect!text) filename (maybe "" id) tie (txtSelect!text) filename (\t -> if null t then Nothing else Just t) (maybe "" id) -- Evaluator runtime status tbRun!enabled =< with1 running not tbStop!enabled =<= running -- Tools tbProfile!onClicked += (runProf dat) -- Config events tbPref!onClicked += (showWindow wndPref) miPref!onActivated += (showWindow wndPref) -- Hack tbClose!onClicked += (widgetHide $ getWindowRaw wndPref) fbFont!text -<- font fbFont!text =<>= font fbFont!text += setupFonts dat -- Need "-<-" first to populate GUI at startup -- Then tie them txtExecutable!text -<- executable txtExecutable!text =<>= executable txtProfCFlags!text -<- profCFlags txtProfCFlags!text =<>= profCFlags txtProfRFlags!text -<- profRFlags txtProfRFlags!text =<>= profRFlags -- Fixed some of the key event bugs. This should work. -- The enter key still doesn't work though... --enterKey <- newVarWithName "enter_key_pressed?" -- (newPredValue "" ((==) "Return")) --enterKey =<= (txtIn!key) --enterKey += (fireCommand dat) upKey <- newVarWithName "arrow_up_pressed?" (newPredValue "" ((==) "Up")) upKey =<= (txtIn!key) upKey += (nextHistory dat) downKey <- newVarWithName "arrow_down_pressed?" (newPredValue "" ((==) "Down")) downKey =<= (txtIn!key) downKey += (previousHistory dat) -- Menus miOpen!onActivated += (raise $ tbOpen!onClicked) miQuit!onActivated += mainQuit -- TODO: This should also run "main" too like in most IDEs, -- but concurrency makes that weird miRun!onActivated += refreshCommand dat miProfile!onActivated += (runProf dat) miAbout!onActivated += (showWindow wndAbout) return () -- -- Sends entered text to processes -- fireCommand :: Data -> IO () fireCommand dat@Data{txtOut=txtOut, txtIn=txtIn} = do handles <- getHandles dat hist <- getVar $ history dat case handles of Nothing -> errorMessage dat "Can't send command; compiler not running." Just (Handles inp _ _ _) -> do s <- getVar (txtIn!text) running dat -< True appendText dat (s ++ "\n") forkIO (hPutStrLn inp s) txtIn!text -< "" if null $ dropWhile isSpace s then history dat -< ([], fst hist ++ snd hist) else history dat -< ([], s : fst hist ++ snd hist) refreshCommand :: Data -> IO () refreshCommand dat = do path <- getVar $ filename dat case path of Nothing -> startEvaluator dat Nothing Just _ -> startWithFile dat -- -- Stop the currently running process -- stopCommand :: Data -> IO () stopCommand dat = do handles <- getHandles dat case handles of Nothing -> errorMessage dat "No compiler running to stop." Just (Handles inp pid oid eid) -> do killThread oid killThread eid terminateProcess pid waitForProcess pid setHandles dat Nothing startEvaluator dat Nothing -- Get the next oldest command from history nextHistory :: Data -> IO () nextHistory dat@Data {txtIn=txtIn} = do h <- getVar $ history dat case h of (xs, y:ys) -> do (txtIn!text) -< y history dat -< (y:xs, ys) _ -> return () -- Get a command that's one newer previousHistory :: Data -> IO () previousHistory dat@Data {txtIn=txtIn} = do h <- getVar $ history dat case h of (x:x2:xs, ys) -> do (txtIn!text) -< x2 history dat -< (x2:xs, x:ys) _ -> do (txtIn!text) -< "" history dat -< ([], fst h ++ snd h) {- stopCommand :: Data -> IO () stopCommand dat = do handles <- getHandles dat case handles of Nothing -> return () Just (pid, inp) -> do signalProcess sigINT pid -}