diff --git a/src/gui/Language/Astview/Gui/Actions.hs b/src/gui/Language/Astview/Gui/Actions.hs index 1fe5887..09a6505 100644 --- a/src/gui/Language/Astview/Gui/Actions.hs +++ b/src/gui/Language/Astview/Gui/Actions.hs @@ -12,6 +12,7 @@ import Language.Astview.DataTree(flatten) import Prelude hiding (span,writeFile) import Data.List (find) import Control.Monad (when,unless,void,zipWithM_) +import Control.Monad.IO.Class(liftIO) import Data.Char (toLower) import System.IO (withFile,IOMode(..),hPutStr,hClose) import System.FilePath (takeExtension,takeFileName) @@ -28,77 +29,83 @@ import Graphics.UI.Gtk.SourceView -- * filemenu menu actions -- ------------------------------------------------------------------- -clearTreeView :: TreeView -> IO () -clearTreeView t = do - c <- treeViewGetColumn t 0 - case c of - Just col-> treeViewRemoveColumn t col - Nothing -> return 0 +clearTreeView :: AstAction () +clearTreeView = do + t <- getTreeView + liftIO $ do + c <- treeViewGetColumn t 0 + case c of + Just col-> treeViewRemoveColumn t col + Nothing -> return 0 return () -- | resets the GUI, actionEmptyGUI :: AstAction () -actionEmptyGUI ref = do - g <- getGui ref - clearTreeView =<< getTreeView ref - flip textBufferSetText ("" :: String) =<< getSourceBuffer ref - windowSetTitleSuffix (window g) unsavedDoc +actionEmptyGUI = do + g <- getGui + sb <- getSourceBuffer + clearTreeView + liftIO $ do + flip textBufferSetText ("" :: String) sb + windowSetTitleSuffix (window g) unsavedDoc -- | updates the sourceview with a given file and parses the file actionLoadHeadless :: FilePath -> AstAction () -actionLoadHeadless file ref = do - setCurrentFile file ref - w <- getWindow ref - windowSetTitleSuffix w (takeFileName file) - buffer <- getSourceBuffer ref - textBufferSetText buffer =<< withFile file ReadMode (fmap BS.unpack . BS.hGetContents) - deleteStar ref - actionReparse ref +actionLoadHeadless file = do + setCurrentFile file + w <- getWindow + liftIO $ windowSetTitleSuffix w (takeFileName file) + buffer <- getSourceBuffer + liftIO $ textBufferSetText buffer =<< withFile file ReadMode (fmap BS.unpack . BS.hGetContents) + deleteStar + actionReparse -- |tries to find a language based on the extension of -- current file name getLanguageByExtension :: AstAction (Maybe Language) -getLanguageByExtension ref = do - file <- getCurrentFile ref - languages <- getKnownLanguages ref +getLanguageByExtension = do + file <- getCurrentFile + languages <- getKnownLanguages return $ find (elem (takeExtension file) . exts) languages getLanguage :: AstAction (Maybe Language) -getLanguage ref = do - maybeLang <- getActiveLanguage ref +getLanguage = do + maybeLang <- getActiveLanguage case maybeLang of - Nothing -> getLanguageByExtension ref + Nothing -> getLanguageByExtension Just lang -> return $ Just lang actionGetAst :: Language -> AstAction (Either Error Ast) -actionGetAst l ref = do - plain <- getText =<< getSourceBuffer ref - flattening <- getFlattenLists ref +actionGetAst l = do + plain <- getText + flattening <- getFlattenLists return $ (if flattening then flatten else id) <$> parse l plain -- | parses the contents of the sourceview with the selected language actionParse :: Language -> AstAction (Tree String) -actionParse l ref = do - buffer <- getSourceBuffer ref - view <- getTreeView ref - sourceBufferSetHighlightSyntax buffer True - setupSyntaxHighlighting buffer l - tree <- buildTree <$> actionGetAst l ref - clearTreeView view - model <- treeStoreNew [tree] - treeViewSetModel view model - col <- treeViewColumnNew - renderer <- cellRendererTextNew - cellLayoutPackStart col renderer True - fontsize <- getFontsize ref - cellLayoutSetAttributes - col - renderer - model - (\row -> [ cellText := row - , cellTextSize := (fromInteger . toInteger) fontsize - ] ) - treeViewAppendColumn view col +actionParse l = do + buffer <- getSourceBuffer + view <- getTreeView + liftIO $ do + sourceBufferSetHighlightSyntax buffer True + setupSyntaxHighlighting buffer l + tree <- buildTree <$> actionGetAst l + clearTreeView + fontsize <- getFontsize + liftIO $ do + model <- treeStoreNew [tree] + treeViewSetModel view model + col <- treeViewColumnNew + renderer <- cellRendererTextNew + cellLayoutPackStart col renderer True + cellLayoutSetAttributes + col + renderer + model + (\row -> [ cellText := row + , cellTextSize := (fromInteger . toInteger) fontsize + ] ) + treeViewAppendColumn view col return tree -- |constructs the tree which will be presented by our gtk-treeview @@ -124,55 +131,59 @@ setupSyntaxHighlighting buffer language = do -- |saves current file if a file is active or calls "save as"-dialog actionSave :: AstAction () -actionSave ref = do - file <- getCurrentFile ref - text <- getText =<< getSourceBuffer ref +actionSave = do + file <- getCurrentFile + text <- getText case file of - "Unsaved document" -> actionDlgSave ref + "Unsaved document" -> actionDlgSave _ -> do - deleteStar ref + deleteStar writeFile file text -- |sets up a simple filechooser dialog, whose response to Ok -- is given by argument function -actionMkDialog :: FileChooserAction -> (FileChooserDialog -> t -> IO ()) -> t -> IO() -actionMkDialog fileChooser actionOnOkay ref = do - dia <- fileChooserDialogNew +actionMkDialog :: FileChooserAction -> (FileChooserDialog -> AstAction ()) -> AstAction() +actionMkDialog fileChooser actionOnOkay = do + dia <- liftIO $ fileChooserDialogNew (Just ("astview" :: String)) Nothing fileChooser [] - zipWithM_ (dialogAddButton dia) [stockCancel ,stockOpen] - [ResponseCancel,ResponseOk] + liftIO $ zipWithM_ (dialogAddButton dia) + [stockCancel,stockOpen] [ResponseCancel,ResponseOk] - widgetShowAll dia - response <- dialogRun dia + liftIO $ widgetShowAll dia + response <- liftIO $ dialogRun dia case response of ResponseCancel -> return () - ResponseOk -> actionOnOkay dia ref + ResponseOk -> actionOnOkay dia _ -> return () - widgetHide dia + liftIO $ widgetHide dia -- |lanches the "save as"-dialog actionSaveAs :: AstAction () actionSaveAs = actionMkDialog FileChooserActionSave onOkay where - onOkay dia ref = do - maybeFile <- fileChooserGetFilename dia + + onOkay :: FileChooserDialog -> AstAction () + onOkay dia = do + maybeFile <- liftIO $ fileChooserGetFilename dia case maybeFile of Nothing-> return () Just file -> do - setCurrentFile file ref - writeFile file =<< getText =<< getSourceBuffer ref + setCurrentFile file + writeFile file =<< getText -- |removes @*@ from window title if existing and updates state deleteStar :: AstAction () -deleteStar ref = do - w <- getWindow ref - (t :: String) <- get w windowTitle - bufferChanged <- getChanged ref - when bufferChanged $ set w [windowTitle := tail t] - setChanged False ref +deleteStar = do + w <- getWindow + bufferChanged <- getChanged + liftIO $ do + (t :: String) <- get w windowTitle + when bufferChanged $ + set w [windowTitle := tail t] + setChanged False -- ------------------------------------------------------------------- -- ** editmenu menu actions @@ -180,64 +191,67 @@ deleteStar ref = do -- |moves selected source to clipboard (cut) actionCutSource :: AstAction () -actionCutSource ref = do - actionCopySource ref - actionDeleteSource ref - return () +actionCutSource = do + actionCopySource + actionDeleteSource -- |copies selected source to clipboard actionCopySource :: AstAction () -actionCopySource ref = do - buffer <- getSourceBuffer ref - (start,end) <- textBufferGetSelectionBounds buffer - clipBoard <- clipboardGet selectionClipboard - s :: String <- textBufferGetText buffer start end True - clipboardSetText clipBoard s +actionCopySource = do + buffer <- getSourceBuffer + liftIO $ do + (start,end) <- textBufferGetSelectionBounds buffer + clipBoard <- clipboardGet selectionClipboard + s :: String <- textBufferGetText buffer start end True + clipboardSetText clipBoard s -- |pastes text from clipboard at current cursor position actionPasteSource :: AstAction () -actionPasteSource ref = do - buffer <- getSourceBuffer ref - clipBoard <- clipboardGet selectionClipboard - clipboardRequestText clipBoard (insertAt buffer) where - insertAt :: SourceBuffer -> Maybe String -> IO () - insertAt buff m = whenJust m (textBufferInsertAtCursor buff) +actionPasteSource = do + buffer <- getSourceBuffer + liftIO $ do + clipBoard <- clipboardGet selectionClipboard + clipboardRequestText clipBoard (insertAt buffer) where + + insertAt :: SourceBuffer -> Maybe String -> IO () + insertAt buff m = whenJust m (textBufferInsertAtCursor buff) -- |deletes selected source actionDeleteSource :: AstAction () -actionDeleteSource ref = void $ do - buffer <- getSourceBuffer ref - textBufferDeleteSelection buffer False False +actionDeleteSource = void $ do + buffer <- getSourceBuffer + liftIO $ textBufferDeleteSelection buffer False False -- |launches a dialog which displays the text position associated to -- last clicked tree node. actionJumpToTextLoc :: AstAction () -actionJumpToTextLoc ref = do - maybeLang <- getLanguage ref +actionJumpToTextLoc = do + maybeLang <- getLanguage case maybeLang of Nothing -> return () Just lang -> do - astOrError <- actionGetAst lang ref + astOrError <- actionGetAst lang case astOrError of Left _ -> return () Right (Ast ast) -> do - gtkPath <- getPath ref + gtkPath <- getPath unless (null gtkPath) $ do let astPath = tail gtkPath loc = ast `at` astPath case loc of Nothing -> return () - Just l -> actionSelectSrcLoc l ref + Just l -> actionSelectSrcLoc l -- |selects the given source location in gui textview actionSelectSrcLoc :: SrcSpan -> AstAction () -actionSelectSrcLoc (SrcSpan (SrcPos bl br) (SrcPos el er)) ref = do - textBuffer <- getSourceBuffer ref - let getIter line row = textBufferGetIterAtLineOffset textBuffer (line-1) (0 `max` row-1) - -- we need to subtract 1 since lines and offsets start with 0 - begin <- getIter bl br - end <- getIter el er - textBufferSelectRange textBuffer begin end +actionSelectSrcLoc (SrcSpan (SrcPos bl br) (SrcPos el er)) = do + textBuffer <- getSourceBuffer + liftIO $ do + let getIter line row = textBufferGetIterAtLineOffset textBuffer (line-1) (0 `max` row-1) + -- we need to subtract 1 since lines and offsets start with 0 + begin <- getIter bl br + end <- getIter el er + textBufferSelectRange textBuffer begin end at :: Tree AstNode -> Path -> Maybe SrcSpan at (Node n _ ) [] = srcspan n @@ -254,32 +268,34 @@ at (Node _ cs) (i:is) = get i cs >>= \tree -> tree `at` is where -- |returns the current cursor position in a source view. -- return type: (line,row) getCursorPosition :: AstAction SrcSpan -getCursorPosition ref = do - (startIter,endIter) <- textBufferGetSelectionBounds =<< getSourceBuffer ref - lineStart <- textIterGetLine startIter - rowStart <- textIterGetLineOffset startIter - lineEnd <- textIterGetLine endIter - rowEnd <- textIterGetLineOffset endIter - return $ span (lineStart+1) (rowStart+1) (lineEnd+1) (rowEnd+1) +getCursorPosition = do + buffer <- getSourceBuffer + liftIO $ do + (startIter,endIter) <- textBufferGetSelectionBounds buffer + lineStart <- textIterGetLine startIter + rowStart <- textIterGetLineOffset startIter + lineEnd <- textIterGetLine endIter + rowEnd <- textIterGetLineOffset endIter + return $ span (lineStart+1) (rowStart+1) (lineEnd+1) (rowEnd+1) -- |opens tree position associated with current cursor position. actionJumpToSrcLoc :: AstAction () -actionJumpToSrcLoc ref = do - treePath <- actionGetAssociatedPath ref +actionJumpToSrcLoc = do + treePath <- actionGetAssociatedPath case treePath of - Just p -> activatePath p ref + Just p -> activatePath p Nothing -> return () -- |returns the shortest path in tree which is associated with the -- current selected source location. actionGetAssociatedPath :: AstAction (Maybe Path) -actionGetAssociatedPath ref = do - sele <- getCursorPosition ref - maybeLang <- getLanguage ref +actionGetAssociatedPath = do + sele <- getCursorPosition + maybeLang <- getLanguage case maybeLang of Nothing -> return Nothing Just lang -> do - astOrError <- actionGetAst lang ref + astOrError <- actionGetAst lang case astOrError of Left _ -> return Nothing Right ast -> @@ -288,11 +304,12 @@ actionGetAssociatedPath ref = do -- |select tree path activatePath :: Path -> AstAction () -activatePath p ref = do - view <- getTreeView ref - treeViewExpandToPath view p - treeViewExpandRow view p True - treeViewSetCursor view p Nothing +activatePath p = do + view <- getTreeView + liftIO $ do + treeViewExpandToPath view p + treeViewExpandRow view p True + treeViewSetCursor view p Nothing -- ------------------------------------------------------------------- -- ** other actions @@ -300,67 +317,76 @@ activatePath p ref = do -- | adds '*' to window title if file changed and sets state actionBufferChanged :: AstAction () -actionBufferChanged ref = do - w <- fmap window (getGui ref) - t <- get w windowTitle - c <- getChanged ref - unless c $ set w [windowTitle := '*':t] - setChanged True ref +actionBufferChanged = do + w <- fmap window getGui + t <- liftIO $ get w windowTitle + c <- getChanged + unless c $ liftIO $ set w [windowTitle := '*':t] + cp <- getCursorPosition + setCursor cp + setChanged True -- | destroys window widget actionQuit :: AstAction () -actionQuit ref = do - isChanged <- getChanged ref - when isChanged $ actionQuitWorker ref - actionQuitForce ref +actionQuit = do + isChanged <- getChanged + when isChanged $ actionQuitWorker + actionQuitForce -- |ends program with force actionQuitForce :: AstAction () -actionQuitForce ref = do - widgetDestroy =<< fmap window (getGui ref) +actionQuitForce = do + w <- getWindow + liftIO $ widgetDestroy w actionQuitWorker :: AstAction () -actionQuitWorker ref = do - file <- getCurrentFile ref - dialog <- messageDialogNew Nothing [] MessageQuestion ButtonsYesNo +actionQuitWorker = do + file <- getCurrentFile + + dialog <- liftIO $ messageDialogNew Nothing [] MessageQuestion ButtonsYesNo ("Save changes to document \""++takeFileName file ++ "\" before closing?") - containerSetBorderWidth dialog 2 - widgetShowAll dialog - response <- dialogRun dialog + response <- liftIO $ do + containerSetBorderWidth dialog 2 + widgetShowAll dialog + dialogRun dialog case response of - ResponseYes -> actionSave ref - _ -> actionQuitForce ref - widgetHide dialog + ResponseYes -> actionSave + _ -> actionQuitForce + liftIO $ widgetHide dialog -- | launches open dialog actionDlgOpen :: AstAction () actionDlgOpen = actionMkDialog FileChooserActionOpen onOkay where - onOkay dia ref = whenJustM (fileChooserGetFilename dia) $ \file -> - actionLoadHeadless file ref + + onOkay :: FileChooserDialog -> AstAction () + onOkay dia = whenJustM (liftIO $ fileChooserGetFilename dia) actionLoadHeadless -- | launches save dialog actionDlgSave :: AstAction () actionDlgSave = actionMkDialog FileChooserActionSave onOkay where - onOkay dia ref = do - maybeFile <- fileChooserGetFilename dia + + onOkay :: FileChooserDialog -> AstAction () + onOkay dia = do + maybeFile <- liftIO $ fileChooserGetFilename dia case maybeFile of Nothing-> return () Just file -> do - g <- getGui ref - setChanged False ref - setCurrentFile file ref - writeFile file =<< getText =<< getSourceBuffer ref - set (window g) [windowTitle := takeFileName file] + g <- getGui + setChanged False + setCurrentFile file + writeFile file =<< getText + liftIO $ set (window g) [windowTitle := takeFileName file] -- |applies current parser to sourcebuffer actionReparse :: AstAction () -actionReparse ref = - whenJustM (getLanguage ref) $ \l -> void $ actionParse l ref +actionReparse = + whenJustM getLanguage (void . actionParse) actionGetPath :: AstAction Path -actionGetPath ref = do - rows <- treeSelectionGetSelectedRows =<< treeViewGetSelection =<< getTreeView ref +actionGetPath = do + tv <- getTreeView + rows <- liftIO (treeSelectionGetSelectedRows =<< treeViewGetSelection tv) return $ case rows of [] -> [] (p:_) -> p @@ -381,11 +407,13 @@ whenJustM val action = do whenJust m action -- |returns the text in given text buffer -getText :: TextBufferClass c => c -> IO String -getText tb = do - start <- textBufferGetStartIter tb - end <- textBufferGetEndIter tb - textBufferGetText tb start end True +getText :: AstAction String +getText = do + tb <- getSourceBuffer + liftIO $ do + start <- textBufferGetStartIter tb + end <- textBufferGetEndIter tb + textBufferGetText tb start end True -- |uses the given string to set the title of given window with -- suffix "-astview". Window titles should only be set by this @@ -394,5 +422,6 @@ windowSetTitleSuffix :: WindowClass w => w -> String -> IO () windowSetTitleSuffix win title = set win [windowTitle := title++" - astview" ] -- |safe function to write files -writeFile :: FilePath -> String -> IO () -writeFile f str = withFile f WriteMode (\h -> hPutStr h str >> hClose h) +writeFile :: FilePath -> String -> AstAction () +writeFile f str = liftIO $ + withFile f WriteMode (\h -> hPutStr h str >> hClose h) diff --git a/src/gui/Language/Astview/Gui/Init.hs b/src/gui/Language/Astview/Gui/Init.hs index 5fcc263..e4c6fb9 100644 --- a/src/gui/Language/Astview/Gui/Init.hs +++ b/src/gui/Language/Astview/Gui/Init.hs @@ -1,21 +1,26 @@ {- provides 'setupGUI' the main gui initialization -function. (using the module Language.Astview.Menu to build the menu bar) +function. - -} -module Language.Astview.Gui.Init(setupGUI) where +module Language.Astview.Gui.Init(setupGui,hooks) where -import Language.Astview.Gui.Types -import Language.Astview.Gui.Actions -import Language.Astview.Gui.Menu -import Language.Astview.Languages(languages) +import Language.Astview.Gui.Actions +import Language.Astview.Gui.Menu +import Language.Astview.Gui.Types +import Language.Astview.Languages (languages) -import Control.Monad.Trans (liftIO) -import Data.IORef -import System.FilePath (()) +import Control.Monad.Reader +import Control.Monad.Trans (liftIO) +import Data.IORef -import Graphics.UI.Gtk hiding (Language) -import Graphics.UI.Gtk.SourceView -import Paths_astview (getDataFileName) +import Graphics.UI.Gtk hiding (Language) +import Graphics.UI.Gtk.SourceView + +setupGui :: Builder -> IO (IORef AstState) +setupGui builder = do + gui <- builderToGui builder + let initState = AstState (defaultValue { knownLanguages = languages}) gui defaultValue + newIORef initState -- |builds initial gui state from builder file builderToGui :: Builder -> IO GUI @@ -25,25 +30,6 @@ builderToGui builder = do tb <- buildSourceView =<< builderGetObjectStr builder castToScrolledWindow "swSource" return $ GUI win treeview tb --- |creates initial program state and provides an IORef to that -buildState :: Builder -> IO (IORef AstState) -buildState builder = do - g <- builderToGui builder - let astSt = AstState st g defaultValue - st = defaultValue { knownLanguages = languages} - newIORef astSt - --- | initiates gui and returns initial program state -setupGUI :: IO (IORef AstState) -setupGUI = do - initGUI - builder <- builderNew - builderAddFromFile builder =<< getDataFileName ("data" "astview.xml") - r <- buildState builder - initMenu builder r - hooks r - return r - -- | setup the GtkSourceView and add it to the ScrollPane. return the -- underlying textbuffer buildSourceView :: ScrolledWindow -> IO SourceBuffer @@ -58,42 +44,46 @@ buildSourceView sw = do containerAdd sw sourceView return sourceBuffer --- ** hooks +-- * hooks --- | adds actions to widgets defined in type 'Gui'. (see 'hookNonGuiStateWidgets') +-- | adds actions to widgets defined in type 'Gui'. hooks :: AstAction (ConnectId Window) -hooks ref = do - textbuffer <- getSourceBuffer ref - storeLastActiveTextPosition textbuffer ref - - tree <- getTreeView ref - storeLastActiveTreePosition tree ref +hooks = do + storeLastActiveTextPosition + storeLastActiveTreePosition + closeAstviewOnWindowClosed + close - win <- getWindow ref - closeAstviewOnWindowClosed win ref - close win ref - -type Hook a = a -> AstAction (ConnectId a) +type Hook a = AstAction (ConnectId a) -- |stores the last active cursor position in text to the program state storeLastActiveTextPosition :: Hook SourceBuffer -storeLastActiveTextPosition buffer ref = buffer `on` bufferChanged $ do - actionBufferChanged ref - cp <- getCursorPosition ref - setCursor cp ref +storeLastActiveTextPosition = do + ioref <- ask + buffer <- getSourceBuffer + + liftIO $ buffer `on` bufferChanged $ do + runReaderT actionBufferChanged ioref -- |stores the path to the last selected tree cell to the program state storeLastActiveTreePosition :: Hook TreeView -storeLastActiveTreePosition tree ref = - tree `on` cursorChanged $ do +storeLastActiveTreePosition = do + ioref <- ask + tree <- getTreeView + + liftIO $ tree `on` cursorChanged $ do (p,_) <- treeViewGetCursor tree - setTreePath p ref + runReaderT (setTreePath p) ioref -- |softly terminate application on main window closed closeAstviewOnWindowClosed :: Hook Window -closeAstviewOnWindowClosed w ref = - w `on` deleteEvent $ tryEvent $ liftIO $ actionQuit ref +closeAstviewOnWindowClosed = do + ioref <- ask + w <- getWindow + liftIO $ w `on` deleteEvent $ tryEvent $ liftIO $ runReaderT actionQuit ioref -- |terminate application on main window closed close :: Hook Window -close w _ = w `on` objectDestroy $ mainQuit +close = do + w <- getWindow + liftIO $ w `on` objectDestroy $ mainQuit diff --git a/src/gui/Language/Astview/Gui/Menu.hs b/src/gui/Language/Astview/Gui/Menu.hs index 545c459..c7a3bf8 100644 --- a/src/gui/Language/Astview/Gui/Menu.hs +++ b/src/gui/Language/Astview/Gui/Menu.hs @@ -3,43 +3,46 @@ to the respective MenuItems. -} module Language.Astview.Gui.Menu (initMenu,connect,builderGetObjectStr) where -import Language.Astview.Gui.Types -import Language.Astview.Gui.Actions -import Language.Astview.Languages(languages) -import Language.Astview.Language - -import Graphics.UI.Gtk hiding (Language) -import Paths_astview (getDataFileName) -import System.FilePath (()) -import Data.List(intercalate) -import Data.Monoid ((<>)) -import Control.Monad(forM_) -import System.Glib.UTFString (stringToGlib) +import Language.Astview.Gui.Actions +import Language.Astview.Gui.Types +import Language.Astview.Language +import Language.Astview.Languages (languages) + +import Control.Monad (forM_) +import Control.Monad.IO.Class (liftIO) +import Control.Monad.Reader +import Data.List (intercalate) +import Data.Monoid ((<>)) +import Graphics.UI.Gtk hiding (Language) +import Paths_astview (getDataFileName) +import System.FilePath (()) +import System.Glib.UTFString (stringToGlib) -- |sets up the menu and binds menu items to logic initMenu :: Builder -> AstAction () -initMenu builder ref = do - uiManager <- uiManagerNew - menuDeclFile <- getDataFileName ("data" "menu.xml") - uiManagerAddUiFromFile uiManager menuDeclFile - uiManagerBuildLanguagesMenu uiManager ref - - actionGroup <- actionGroupNew ("ActionGroup" :: String) - initMenuFile actionGroup ref - initMenuEdit actionGroup ref - initMenuNavigate actionGroup ref - initMenuLanguages actionGroup ref - initMenuHelp actionGroup builder ref - - uiManagerInsertActionGroup uiManager actionGroup 0 - maybeMenubar <- uiManagerGetWidget uiManager ("/ui/menubar" :: String) - let menubar = case maybeMenubar of - Nothing -> error $ "Could not parse menu bar declaration from " - ++ show menuDeclFile - Just m -> m - vboxMain <- builderGetObjectStr builder castToBox "vboxMain" - vboxMain `set` [ containerChild := menubar ] - boxReorderChild vboxMain menubar 0 +initMenu builder = do + uiManager <- liftIO uiManagerNew + menuDeclFile <- liftIO $ getDataFileName ("data" "menu.xml") + liftIO $ uiManagerAddUiFromFile uiManager menuDeclFile + uiManagerBuildLanguagesMenu uiManager + + actionGroup <- liftIO $ actionGroupNew ("ActionGroup" :: String) + initMenuFile actionGroup + initMenuEdit actionGroup + initMenuNavigate actionGroup + initMenuLanguages actionGroup + initMenuHelp actionGroup builder + + liftIO $ do + uiManagerInsertActionGroup uiManager actionGroup 0 + maybeMenubar <- uiManagerGetWidget uiManager ("/ui/menubar" :: String) + let menubar = case maybeMenubar of + Nothing -> error $ "Could not parse menu bar declaration from " + ++ show menuDeclFile + Just m -> m + vboxMain <- builderGetObjectStr builder castToBox "vboxMain" + vboxMain `set` [ containerChild := menubar ] + boxReorderChild vboxMain menubar 0 -- |creates a menu item for every element of 'knownLanguages' in menu "Languages". -- @@ -48,16 +51,17 @@ initMenu builder ref = do -- new languages by just adding it to the list of languages without even -- touching any gui component. uiManagerBuildLanguagesMenu :: UIManager -> AstAction () -uiManagerBuildLanguagesMenu uiManager ref = do - langs <- getKnownLanguages ref - forM_ langs $ \lang -> do - mergeId <- uiManagerNewMergeId uiManager - let ident = "actionLanguage"++name lang - uiManagerAddUi uiManager mergeId "/ui/menubar/Languages/LangsSep" - (ident :: String) - (Just ident) - [UiManagerMenuitem] - False +uiManagerBuildLanguagesMenu uiManager = do + langs <- getKnownLanguages + liftIO $ do + forM_ langs $ \lang -> do + mergeId <- uiManagerNewMergeId uiManager + let ident = "actionLanguage"++name lang + uiManagerAddUi uiManager mergeId "/ui/menubar/Languages/LangsSep" + (ident :: String) + (Just ident) + [UiManagerMenuitem] + False -- |the association between the gui functions from 'Actions' -- and the gtk identifier from xml file. @@ -86,85 +90,108 @@ menuActions = menuFile ++ menuEdit ++ menuNavigate where -- |associate the menu action with the respective -- gui function from module Actions connect :: Action -> AstAction (ConnectId Action) -connect action ref = do - name <- actionGetName action - case lookup name menuActions of - Nothing -> error $ "No action associated with "++ show name - Just f -> action `on` actionActivated $ f ref +connect action = do + st <- ask + liftIO $ do + name <- actionGetName action + case lookup name menuActions of + Nothing -> error $ "No action associated with "++ show name + Just f -> action `on` actionActivated $ runReaderT f st -- * the menu File initMenuFile :: ActionGroup -> AstAction () -initMenuFile actionGroup ref = do - actionFile <- actionNewStr "actionMenuFile" "File" Nothing Nothing - - actionNew <- actionNewStr "actionNew" "New" Nothing (Just stockNew) - actionOpen <- actionNewStr "actionOpen" "Open" Nothing (Just stockOpen) - actionSave <- actionNewStr "actionSave" "Save" Nothing (Just stockSave) - actionSaveAs <- actionNewStr "actionSaveAs" "Save As" Nothing (Just stockSaveAs) - actionQuit <- actionNewStr "actionQuit" "Quit" Nothing (Just stockQuit) - actionGroupAddAction actionGroup actionFile - mapM_ (\action -> do {action `connect` ref ; addAction actionGroup action Nothing}) - [actionNew,actionOpen,actionSave,actionSaveAs,actionQuit] +initMenuFile actionGroup = do + actions <- liftIO $ do + actionFile <- actionNewStr "actionMenuFile" "File" Nothing Nothing + + actionNew <- actionNewStr "actionNew" "New" Nothing (Just stockNew) + actionOpen <- actionNewStr "actionOpen" "Open" Nothing (Just stockOpen) + actionSave <- actionNewStr "actionSave" "Save" Nothing (Just stockSave) + actionSaveAs <- actionNewStr "actionSaveAs" "Save As" Nothing (Just stockSaveAs) + actionQuit <- actionNewStr "actionQuit" "Quit" Nothing (Just stockQuit) + actionGroupAddAction actionGroup actionFile + return [actionNew,actionOpen,actionSave,actionSaveAs,actionQuit] + + forM_ actions $ \action -> do + connect action + liftIO $ addAction actionGroup action Nothing + -- * the menu Edit initMenuEdit :: ActionGroup -> AstAction () -initMenuEdit actionGroup ref = do - actionEdit <- actionNewStr "actionMenuEdit" "Edit" Nothing Nothing +initMenuEdit actionGroup = do + + actionReparse <- liftIO $ actionNewStr "actionReparse" "Reparse" Nothing (Just stockRefresh) + actions <- liftIO $ do + actionEdit <- actionNewStr "actionMenuEdit" "Edit" Nothing Nothing + + actionCut <- actionNewStr "actionCut" "Cut" Nothing (Just stockCut) + actionCopy <- actionNewStr "actionCopy" "Copy" Nothing (Just stockCopy) + actionPaste <- actionNewStr "actionPaste" "Paste" Nothing (Just stockPaste) + actionDelete <- actionNewStr "actionDelete" "Delete" Nothing (Just stockRemove) + actionGroupAddAction actionGroup actionEdit + return [actionCut,actionCopy,actionPaste,actionDelete,actionReparse] - actionCut <- actionNewStr "actionCut" "Cut" Nothing (Just stockCut) - actionCopy <- actionNewStr "actionCopy" "Copy" Nothing (Just stockCopy) - actionPaste <- actionNewStr "actionPaste" "Paste" Nothing (Just stockPaste) - actionDelete <- actionNewStr "actionDelete" "Delete" Nothing (Just stockRemove) - actionReparse <- actionNewStr "actionReparse" "Reparse" Nothing (Just stockRefresh) - actionGroupAddAction actionGroup actionEdit - mapM_ (\action -> do {action `connect` ref ; addAction actionGroup action Nothing}) - [actionCut,actionCopy,actionPaste,actionDelete,actionReparse] + forM_ actions $ \action -> do + connect action + liftIO $ addAction actionGroup action Nothing - actionSetAccelPath actionReparse ("p" :: String) - initMenuItemFlatten actionGroup ref + liftIO $ actionSetAccelPath actionReparse ("p" :: String) + initMenuItemFlatten actionGroup -- |bind the check menu for flattening lists to the boolean value in the state. initMenuItemFlatten :: ActionGroup -> AstAction () -initMenuItemFlatten actionGroup ref = do - isFlat <- getFlattenLists ref +initMenuItemFlatten actionGroup = do + isFlat <- getFlattenLists + st <- ask let actionToggleFlatten = ToggleActionEntry "actionFlatten" "Flatten lists in tree?" - Nothing Nothing Nothing f isFlat + Nothing Nothing Nothing (runReaderT f st) isFlat + + f :: AstAction () f = do - isFlat <- getFlattenLists ref - setFlattenLists (not isFlat) ref - actionReparse ref - actionGroupAddToggleActions actionGroup [actionToggleFlatten] + isFlat <- getFlattenLists + setFlattenLists (not isFlat) + actionReparse + + liftIO $ actionGroupAddToggleActions actionGroup [actionToggleFlatten] -- * the menu Navigate initMenuNavigate :: ActionGroup -> AstAction () -initMenuNavigate actionGroup ref = do - actionNavigate <- actionNewStr "actionMenuNavigate" "Navigate" Nothing Nothing +initMenuNavigate actionGroup = do + actions <- liftIO $ do + actionNavigate <- actionNewStr "actionMenuNavigate" "Navigate" Nothing Nothing + + actionTreeLoc <- actionNewStr "actionTreeLoc" ">>>" Nothing Nothing + actionTextLoc <- actionNewStr "actionTextLoc" "<<<" Nothing Nothing + actionGroupAddAction actionGroup actionNavigate + return [actionTreeLoc,actionTextLoc] + + forM_ actions $ \action -> do + connect action + liftIO $ addAction actionGroup action Nothing - actionTreeLoc <- actionNewStr "actionTreeLoc" ">>>" Nothing Nothing - actionTextLoc <- actionNewStr "actionTextLoc" "<<<" Nothing Nothing - actionGroupAddAction actionGroup actionNavigate - mapM_ (\action -> do {action `connect` ref ; addAction actionGroup action Nothing}) - [actionTreeLoc,actionTextLoc] -- * the menu Languages -- |sets up the menu @Languages@ and binds actions to the menu items. initMenuLanguages :: ActionGroup -> AstAction () -initMenuLanguages actionGroup ref = do - actionLangs <- actionNewStr "actionMenuLanguages" "Languages" Nothing Nothing - actionGroupAddAction actionGroup actionLangs - langs <- getKnownLanguages ref - let auto = RadioActionEntry - "actionLanguageAuto" - "Automatically select languages" - Nothing Nothing Nothing 0 - raes = auto:languagesToRadioActionEntry langs - actionGroupAddRadioActions actionGroup raes 0 (`onRadioChange` ref) +initMenuLanguages actionGroup = do + langs <- getKnownLanguages + st <- ask + liftIO $ do + actionLangs <- actionNewStr "actionMenuLanguages" "Languages" Nothing Nothing + actionGroupAddAction actionGroup actionLangs + let auto = RadioActionEntry + "actionLanguageAuto" + "Automatically select languages" + Nothing Nothing Nothing 0 + raes = auto:languagesToRadioActionEntry langs + actionGroupAddRadioActions actionGroup raes 0 (\a -> runReaderT (onRadioChange a) st) -- |creates a 'RadioActionEntry' for every language languagesToRadioActionEntry :: [Language] -> [RadioActionEntry] @@ -177,15 +204,15 @@ languagesToRadioActionEntry languages = zipWith mkRadioActionEntry languages [1. -- |bind functionality to RadioAction onRadioChange :: RadioAction -> AstAction () -onRadioChange action ref = do - i <- radioActionGetCurrentValue action +onRadioChange action = do + i <- liftIO $ radioActionGetCurrentValue action if i == 0 then - setActiveLanguage Nothing ref + setActiveLanguage Nothing else let lang = languages !! (i-1) in - setActiveLanguage (Just lang) ref - actionReparse ref + setActiveLanguage (Just lang) + actionReparse -- |produces a string containing the languages' name and -- the associated file extensions @@ -199,7 +226,7 @@ makeLanguageLabel language = -- * the language Help initMenuHelp :: ActionGroup -> Builder -> AstAction () -initMenuHelp actionGroup _ _ = do +initMenuHelp actionGroup _ = liftIO $ do actionHelp <- actionNewStr "actionMenuHelp" "Help" Nothing Nothing actionAbout <- actionNewStr "actionAbout" "About" Nothing (Just stockAbout) actionGroupAddAction actionGroup actionHelp diff --git a/src/gui/Language/Astview/Gui/Types.hs b/src/gui/Language/Astview/Gui/Types.hs index 9ca0afd..54f1eb0 100644 --- a/src/gui/Language/Astview/Gui/Types.hs +++ b/src/gui/Language/Astview/Gui/Types.hs @@ -4,6 +4,7 @@ module Language.Astview.Gui.Types where import Data.Label import Data.IORef +import Control.Monad.Reader import Graphics.UI.Gtk hiding (Language,get,set) import Graphics.UI.Gtk.SourceView (SourceBuffer) @@ -13,7 +14,7 @@ import Language.Astview.Language(Language,SrcSpan,Path,position) class Default a where defaultValue :: a -type AstAction a = IORef AstState -> IO a +type AstAction a = ReaderT (IORef AstState) IO a -- |union of internal program state and gui data AstState = AstState @@ -74,55 +75,62 @@ mkLabels [ ''AstState , ''GUI ] -getSourceBuffer :: AstAction SourceBuffer -getSourceBuffer = fmap (sb . gui) . readIORef -getTreeView :: AstAction TreeView -getTreeView = fmap (tv . gui) . readIORef getAstState :: AstAction AstState -getAstState = readIORef +getAstState = do + ioRef <- ask + liftIO (readIORef ioRef) + +getSourceBuffer :: AstAction SourceBuffer +getSourceBuffer = (sb . gui) <$> getAstState + +getTreeView :: AstAction TreeView +getTreeView = (tv . gui) <$> getAstState getGui :: AstAction GUI -getGui = fmap gui . readIORef +getGui = gui <$> getAstState getState :: AstAction State -getState = fmap state . readIORef +getState = state <$> getAstState getKnownLanguages :: AstAction [Language] -getKnownLanguages = fmap (knownLanguages . state) . readIORef +getKnownLanguages = (knownLanguages . state) <$> getAstState getChanged :: AstAction Bool -getChanged = fmap (textchanged . state) . readIORef +getChanged = (textchanged . state) <$> getAstState getCursor :: AstAction SrcSpan -getCursor = fmap (lastSelectionInText . state) . readIORef +getCursor = (lastSelectionInText . state) <$> getAstState getPath :: AstAction TreePath -getPath = fmap (lastSelectionInTree. state) . readIORef +getPath = (lastSelectionInTree . state) <$> getAstState + getCurrentFile :: AstAction String -getCurrentFile = fmap (currentFile . state) . readIORef +getCurrentFile = (currentFile . state) <$> getAstState getActiveLanguage :: AstAction (Maybe Language) -getActiveLanguage = fmap (activeLanguage . state) . readIORef +getActiveLanguage = (activeLanguage . state) <$> getAstState getWindow :: AstAction Window -getWindow = fmap (window . gui) . readIORef +getWindow = (window . gui) <$> getAstState getFlattenLists :: AstAction Bool -getFlattenLists = fmap (flattenLists . options) . readIORef +getFlattenLists = (flattenLists . options) <$> getAstState getFontsize :: AstAction Int -getFontsize = fmap (fsize . options) . readIORef +getFontsize = (fsize . options) <$> getAstState -- * setter functions lensSetIoRef :: (AstState :-> a) -> (a :-> b) -> b -> AstAction () -lensSetIoRef outerLens innerLens value ref = modifyIORef ref m where +lensSetIoRef outerLens innerLens value = do + ref <- ask + liftIO $ modifyIORef ref m where - m :: AstState -> AstState - m = modify outerLens (set innerLens value) + m :: AstState -> AstState + m = modify outerLens (set innerLens value) -- |stores the given cursor selection setCursor :: SrcSpan -> AstAction () diff --git a/src/gui/Main.hs b/src/gui/Main.hs index 895ab46..dbdad95 100644 --- a/src/gui/Main.hs +++ b/src/gui/Main.hs @@ -1,23 +1,40 @@ module Main where -import System.Environment(getArgs) -import Graphics.UI.Gtk hiding (get) +import Control.Monad.Reader +import Graphics.UI.Gtk hiding (get) +import Paths_astview (getDataFileName) +import System.Environment (getArgs) +import System.FilePath (()) + +import Language.Astview.Gui.Actions (actionEmptyGUI, + actionLoadHeadless) +import Language.Astview.Gui.Init (hooks, setupGui) +import Language.Astview.Gui.Menu (initMenu) +import Language.Astview.Gui.Types + -import Language.Astview.Gui.Actions (actionEmptyGUI,actionLoadHeadless) -import Language.Astview.Gui.Types(getGui,window) -import Language.Astview.Gui.Init(setupGUI) -- | loads LanguageRegistration, inits GTK-GUI, checks for a -- CLI-argument (one file to parse) and finally starts the GTK-GUI main :: IO () main = do - ref <- setupGUI + initGUI + builder <- builderNew + builderAddFromFile builder =<< getDataFileName ("data" "astview.xml") + ioref <- setupGui builder args <- getArgs - case args of - [] -> actionEmptyGUI ref - [a] -> actionLoadHeadless a ref - _ -> error "Zero or one argument expected" + flip runReaderT ioref $ do + + initMenu builder + hooks + + case args of + [] -> actionEmptyGUI + [file] -> actionLoadHeadless file + _ -> error "Zero or one argument expected" - widgetShowAll =<< fmap window (getGui ref) - mainGUI + w <- getWindow + liftIO $ do + widgetShowAll w + mainGUI