{-# LANGUAGE GADTs #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiWayIf #-} {-# LANGUAGE BinaryLiterals #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE DeriveDataTypeable #-} module Phoityne.IO.GUI.GTK.TextEditor ( TextEditorData(..) , LineTextDoubleClickedHandler , CodeNoteCloseEventHandler , CodeTextKeyPressEventHandler , CodeBufferChangedEventHandler , CodeBufferDeleteRangeEventHandler , CodeBufferInsertTextEventHandler , setupCodeNote , activateCodeNote , highLightBreakPoint , offLightBreakPoint , addBreakPointTagAtLine , isCurrentTextEditor , getCodeViewContent , isTextEditorModified , setTextEditorModified , getCodeTextLineNumber , blockIndentTextEditor , blockUnIndentTextEditor , blockCommentTextEditor , blockUnCommentTextEditor , setContent2TextEditor , setCursorOnTextEditor , deleteRangeOnTextEditor , insertText2TextEditor , getSelectedText , deleteBreakPointTag , deleteBreakPointTagAtLine , updateBreakPointTag , searchEndIter ) where -- モジュール import Phoityne.Constant import Phoityne.IO.GUI.GTK.Constant import Phoityne.IO.GUI.GTK.Utility -- システム import System.Log.Logger import GHC.Float import Graphics.UI.Gtk import Control.Monad import Control.Monad.IO.Class import Data.String.Utils import qualified Control.Exception as E import qualified Data.Text as T import qualified Data.ByteString as BS -- | -- -- type LineTextDoubleClickedHandler = Bool -- isTagExists -> Int -- lineNo -> IO () -- | -- -- type HSCode = BS.ByteString -- | -- -- type CodeNoteCloseEventHandler = TextEditorData -> IO () -- | -- -- data TextEditorData = TextEditorData { lineTextEditorData :: TextView , codeTextEditorData :: TextView , boxTextEditorData :: HBox , tabLabelTextEditorData :: Label } deriving (Eq) -- | -- -- type CodeTextKeyPressEventHandler = String -> Bool -> Bool -> Bool -> Int -> IO Bool -- | -- -- type CodeBufferChangedEventHandler = IO () -- | -- -- type CodeBufferDeleteRangeEventHandler = (Int, Int) -> (Int, Int) -> String -> IO () -- | -- -- type CodeBufferInsertTextEventHandler = (Int, Int) -> String -> IO () -- | -- -- searchEndIter :: TextEditorData -> Int -> Int -> String -> IO (Int, Int) searchEndIter (TextEditorData _ codeView _ _) startLineNo startColNo str = do buf <- textViewGetBuffer codeView iter <- textBufferGetIterAtLineOffset buf startLineNo startColNo forwardTextIter iter $ length str lineNo <- textIterGetLine iter colNo <- textIterGetLineOffset iter return (lineNo, colNo) -- | -- -- setupCodeNote :: Builder -> String -- ファイル名 -> FilePath -- ファイルパス -> HSCode -- コンテンツ -> CodeNoteCloseEventHandler -> LineTextDoubleClickedHandler -> Maybe Int -- スクロールライン -> CodeTextKeyPressEventHandler -> CodeBufferChangedEventHandler -> CodeBufferDeleteRangeEventHandler -> CodeBufferInsertTextEventHandler -> IO TextEditorData setupCodeNote builder name path code evh lineViewEvh _ codeKeyEVH bufEVH delRangeEVH insertTextEVH = do img <- imageNewFromIcon (T.pack "window-close") 8 withIcon img where withIcon Nothing = do criticalM _LOG_NAME $ "[setupCodeNote] close icon not found." E.throwIO . userError $ "[setupCodeNote] close icon not found." withIcon (Just image) = do -- ページタブのラベルとクローズボタンの生成 box <- hBoxNew False 0 label <- labelNew $ Just name fontDesc <- fontDescriptionNew fontDescriptionSetFamily fontDesc (_FONT_DESC) fontDescriptionSetSize fontDesc 9 widgetOverrideFont label $ Just fontDesc closeButton <- toolButtonNew (Just image) (Nothing::Maybe T.Text) boxPackStart box label PackNatural 0 boxPackStart box closeButton PackNatural 0 widgetShowAll box menuWidget <- labelNew $ Just path widgetOverrideFont menuWidget $ Just fontDesc -- ノートブックに新規ページの生成 note <- builderGetObject builder castToNotebook _NAME_CODE_NOTE textEditor <- createTextEditor builder code lineViewEvh label codeKeyEVH bufEVH delRangeEVH insertTextEVH _ <- notebookAppendPageMenu note (boxTextEditorData textEditor) box menuWidget notebookSetTabReorderable note (boxTextEditorData textEditor) True _ <- onToolButtonClicked closeButton (codeNoteCloseEventHandler note textEditor evh) setFont note widgetShowAll note -- activateCodeNote builder textEditor lineNo return textEditor -- | -- -- createTextEditor :: Builder -> HSCode -> LineTextDoubleClickedHandler -> Label -> CodeTextKeyPressEventHandler -> CodeBufferChangedEventHandler -> CodeBufferDeleteRangeEventHandler -> CodeBufferInsertTextEventHandler -> IO TextEditorData createTextEditor builder code evh tabLabel codeKeyEVH bufEVH delRangeEVH insertTextEVH = do tagTable <- builderGetObject builder castToTextTagTable _NAME_TAG_TABLE lineBuf <- textBufferNew $ Just tagTable codeBuf <- textBufferNew $ Just tagTable lineTextView <- textViewNewWithBuffer lineBuf codeTextView <- textViewNewWithBuffer codeBuf textTagTableLookup tagTable (T.unpack _BREAK_POINT_HIGHLIGHT_TAG) >>= \case Just _ -> return () Nothing -> do tag <- textTagNew (Just _BREAK_POINT_HIGHLIGHT_TAG) set tag [textTagBackground := "blue", textTagForeground := "white", textTagBackgroundSet := True] textTagTableAdd tagTable tag box <- hBoxNew False 0 lineScroll <- scrolledWindowNew Nothing Nothing scrolledWindowSetPlacement lineScroll CornerTopRight codeScroll <- scrolledWindowNew Nothing Nothing ajust <- scrolledWindowGetVAdjustment codeScroll scrolledWindowSetVAdjustment lineScroll ajust set lineScroll [ scrolledWindowHscrollbarPolicy := PolicyNever , scrolledWindowVscrollbarPolicy := PolicyAutomatic] textViewSetBorderWindowSize codeTextView TextWindowTop 5 textViewSetBorderWindowSize codeTextView TextWindowBottom 5 textViewSetBorderWindowSize codeTextView TextWindowLeft 5 textViewSetBorderWindowSize codeTextView TextWindowRight 5 textViewSetBorderWindowSize lineTextView TextWindowTop 5 textViewSetBorderWindowSize lineTextView TextWindowBottom 5 --textViewSetBorderWindowSize lineTextView TextWindowLeft 10 --textViewSetBorderWindowSize lineTextView TextWindowRight 5 textViewSetEditable lineTextView True textViewSetCursorVisible lineTextView False textViewSetJustification lineTextView JustifyRight widgetModifyBg lineTextView StateNormal $ Color 0xd8d8 0xd8d8 0xd8d8 containerAdd lineScroll lineTextView containerAdd codeScroll codeTextView boxPackStart box lineScroll PackNatural 0 boxPackStart box codeScroll PackGrow 0 setFont lineTextView setFont codeTextView _ <- on lineTextView buttonPressEvent $ lineTextDoubleClickedHandler lineTextView evh codeBuf <- textViewGetBuffer codeTextView lineBuf <- textViewGetBuffer lineTextView textBufferSetByteString codeBuf code lineCount <- textBufferGetLineCount codeBuf let lineStr = createLineNoText [1..lineCount] "" textBufferSetText lineBuf lineStr fontDesc <- fontDescriptionNew fontDescriptionSetFamily fontDesc (_FONT_DESC) fontDescriptionSetSize fontDesc 9 widgetOverrideFont (castToWidget lineTextView) $ Just fontDesc widgetOverrideFont (castToWidget codeTextView) $ Just fontDesc {- -- 行番号の垂直スクロールバーを消す。お試し。 widgetSetSizeRequest (castToWidget lineScroll) 0 (-1) scrolledWindowGetVScrollbar lineScroll >>= \case Nothing -> return () Just s -> widgetHide (castToWidget s) -} saveBt <- builderGetObject builder castToToolButton _NAME_TOOL_BT_SAVE textBufferSetModified codeBuf False _ <- on codeBuf bufferChanged $ codeTextBufferChangedHandler lineBuf codeBuf bufEVH _ <- on codeBuf modifiedChanged $ codeTextModifiedHandler codeBuf tabLabel saveBt _ <- on codeTextView keyPressEvent $ codeTextKeyPressEventHandler lineTextView codeTextView codeKeyEVH _ <- on codeTextView buttonPressEvent $ codeTextButtonPressEventHandler codeTextView _ <- on codeBuf deleteRange $ codeTextDeleteRangeEventHandler codeTextView delRangeEVH _ <- on codeBuf bufferInsertText $ codeTextInsertTextEventHandler codeTextView insertTextEVH return $ TextEditorData lineTextView codeTextView box tabLabel where createLineNoText [] acc = acc createLineNoText (x:[]) acc = acc ++ " " ++ show x ++ " " createLineNoText (x:xs) acc = acc ++ " " ++ show x ++ " \n" ++ createLineNoText xs acc codeTextModifiedHandler codeBuf label saveBt = do textBufferGetModified codeBuf >>= \case False -> do str <- labelGetText label labelSetText label $ init . init $ (str :: String) widgetSetSensitive saveBt False True -> do str <- labelGetText label labelSetText label $ str ++ (" *"::String) widgetSetSensitive saveBt True codeTextBufferChangedHandler lineBuf codeBuf _ = do ll <- textBufferGetLineCount lineBuf cl <- textBufferGetLineCount codeBuf updateLineNo lineBuf codeBuf ll cl updateLineNo lineBuf _ ll cl | ll == cl = return () | ll < cl = do let lineTxt = "\n" ++ createLineNoText [ll+1 .. cl] "" endIter <- textBufferGetEndIter lineBuf textBufferInsert lineBuf endIter lineTxt | otherwise = do startIter <- textBufferGetIterAtLine lineBuf (cl-1) textIterForwardToLineEnd startIter endIter <- textBufferGetEndIter lineBuf textBufferDelete lineBuf startIter endIter codeTextDeleteRangeEventHandler view evh si ei = do slineNo <- textIterGetLine si sColNo <- textIterGetLineOffset si elineNo <- textIterGetLine ei eColNo <- textIterGetLineOffset ei buf <- textViewGetBuffer view str <- textBufferGetText buf si ei False evh (slineNo, sColNo) (elineNo, eColNo) str codeTextInsertTextEventHandler _ evh si str = do startLineNo <- textIterGetLine si startColNo <- textIterGetLineOffset si evh (startLineNo, startColNo) str -- | -- Event Handler -- codeNoteCloseEventHandler :: Notebook -> TextEditorData -> CodeNoteCloseEventHandler -> IO () codeNoteCloseEventHandler note wid evh = do notebookPageNum note (boxTextEditorData wid) >>= \case Just idx -> notebookRemovePage note idx Nothing -> errorM _LOG_NAME $ "[codeNoteCloseEventHandler]unexpected error." evh wid -- | -- -- activateCodeNote :: Builder -> TextEditorData -> Int -> IO () activateCodeNote builder wid lineNo = do note <- builderGetObject builder castToNotebook _NAME_CODE_NOTE notebookPageNum note (boxTextEditorData wid) >>= \case Just idx -> notebookSetCurrentPage note idx Nothing -> errorM _LOG_NAME $ "[activateCodeNote]invalid node page." window <- builderGetObject builder castToWindow _WINDOW_NAME widgetShowAll window forceEvent let codeTextView = codeTextEditorData wid codeBuf <- textViewGetBuffer codeTextView iter <- textBufferGetIterAtLine codeBuf lineNo _ <- textViewScrollToIter codeTextView iter 0.0 (Just (0.0, 0.5)) widgetGrabFocus codeTextView -- | -- Event Handler -- lineTextDoubleClickedHandler :: TextView -> LineTextDoubleClickedHandler -> EventM EButton Bool lineTextDoubleClickedHandler self evh = eventClick >>= \case DoubleClick -> doubleClicked >> return True _ -> return False where doubleClicked = do (_, posYd) <- eventCoordinates liftIO $ do scrollY <- textViewGetVadjustment self >>= adjustmentGetValue let posY = scrollY + posYd (iter, _) <- textViewGetLineAtY self $ double2Int posY isTagExists <- textIterBeginsTag iter Nothing lineNo <- textIterGetLine iter evh isTagExists lineNo -- | -- -- updateBreakPointTag :: TextEditorData -> Bool -> Int -> IO () updateBreakPointTag (TextEditorData lineView _ _ _) True lineNo = deleteBreakPointTagAtLine lineView lineNo updateBreakPointTag (TextEditorData lineView _ _ _) False lineNo = addBreakPointTagAtLine lineView lineNo -- | -- -- addBreakPointTagAtLine :: TextView -> Int -> IO () addBreakPointTagAtLine self line = do buf <- textViewGetBuffer self iter <- textBufferGetIterAtLine buf line addBreakPointTagAtIter self iter -- | -- -- addBreakPointTagAtIter :: TextView -> TextIter -> IO () addBreakPointTagAtIter self startIter = do textBuf <- textIterGetBuffer startIter tagTable <- textBufferGetTagTable textBuf tag <- textTagNew Nothing set tag [textTagBackground := "blue", textTagForeground := "white", textTagBackgroundSet := True] textTagTableAdd tagTable tag endIter <- textIterCopy startIter _ <- textViewForwardDisplayLineEnd self endIter textBufferApplyTag textBuf tag startIter endIter -- | -- 指定行についているタグをすべて削除する -- deleteBreakPointTagAtLine :: TextView -> Int -> IO () deleteBreakPointTagAtLine textView line = do buf <- textViewGetBuffer textView iter <- textBufferGetIterAtLine buf line deleteBreakPointTagAtIter iter -- | -- TextIterの位置についているタグをすべて削除する -- deleteBreakPointTagAtIter :: TextIter -> IO () deleteBreakPointTagAtIter iter = do textBuf <- textIterGetBuffer iter tagTable <- textBufferGetTagTable textBuf tags <- textIterGetTags iter mapM_ (textTagTableRemove tagTable) tags -- | -- -- deleteBreakPointTag :: TextEditorData -> Int -> IO () deleteBreakPointTag (TextEditorData lineView _ _ _) lineNo = do textBuf <- textViewGetBuffer lineView tagTable <- textBufferGetTagTable textBuf iter <- textBufferGetIterAtLine textBuf (lineNo - 1) tags <- textIterGetTags iter mapM_ (textTagTableRemove tagTable) tags -- | -- -- highLightBreakPoint :: TextEditorData -> Int -> Int -> Int -> Int -> IO () highLightBreakPoint textEditor sl sc el ec = do let codeTextView = codeTextEditorData textEditor codeBuf <- textViewGetBuffer codeTextView --tagTable <- textBufferGetTagTable codeBuf startLine <- normalizeLine codeBuf sl startCol <- normalizeColumn codeBuf startLine sc endLine <- normalizeLine codeBuf el endCol <- normalizeColumn codeBuf endLine ec startIter <- textBufferGetIterAtLineOffset codeBuf startLine startCol endIter <- textBufferGetIterAtLineOffset codeBuf endLine (endCol + 1) textBufferPlaceCursor codeBuf endIter textBufferSelectRange codeBuf startIter endIter where normalizeLine buf l = do nl <- normalizeLine_ buf l return $ if 0 > nl then 0 else nl normalizeLine_ buf l = do allLines <- textBufferGetLineCount buf return $ if l > allLines then allLines - 1 else l - 1 normalizeColumn buf line col = do nc <- normalizeColumn_ buf line col return $ if 0 > nc then 0 else nc normalizeColumn_ buf line col = do iter <- textBufferGetIterAtLine buf line allChars <- textIterGetCharsInLine iter -- -1 for delimiters let allChars' = allChars - 1 return $ if col > allChars' then allChars' - 1 else col - 1 -- | -- -- offLightBreakPoint :: TextEditorData -> IO () offLightBreakPoint textEditor = do let codeTextView = codeTextEditorData textEditor codeBuf <- textViewGetBuffer codeTextView startIter <- textBufferGetStartIter codeBuf endIter <- textBufferGetEndIter codeBuf textBufferRemoveTagByName codeBuf _BREAK_POINT_HIGHLIGHT_TAG startIter endIter return () -- | -- -- isCurrentTextEditor :: Builder -> TextEditorData -> IO Bool isCurrentTextEditor bulder (TextEditorData _ _ box _) = do getCurrentTextEditorHBox bulder >>= \case Nothing -> return False Just b -> return $ box == b -- | -- -- getCurrentTextEditorHBox :: Builder -> IO (Maybe HBox) getCurrentTextEditorHBox builder = do note <- builderGetObject builder castToNotebook _NAME_CODE_NOTE notebookGetCurrentPage note >>= \case (-1) -> return Nothing idx -> notebookGetNthPage note idx >>= \case Nothing -> return Nothing Just w -> return . Just $ castToHBox w -- | -- -- getCodeViewContent :: TextEditorData -> IO BS.ByteString getCodeViewContent (TextEditorData _ codeView _ _) = do buf <- textViewGetBuffer codeView textBufferSetModified buf False startIter <- textBufferGetStartIter buf endIter <- textBufferGetEndIter buf textBufferGetByteString buf startIter endIter False -- | -- -- isTextEditorModified :: TextEditorData -> IO Bool isTextEditorModified (TextEditorData _ codeView _ _) = do buf <- textViewGetBuffer codeView textBufferGetModified buf -- | -- -- setTextEditorModified :: TextEditorData -> Bool -> IO () setTextEditorModified (TextEditorData _ codeView _ _) isModified = do buf <- textViewGetBuffer codeView textBufferSetModified buf isModified -- | -- -- codeTextButtonPressEventHandler :: TextView -> EventM EButton Bool codeTextButtonPressEventHandler textView = eventButton >>= \case RightButton -> liftIO $ do buf <- textViewGetBuffer textView clip <- widgetGetClipboard textView selectionPrimary textBufferHasSelection buf >>= \case False -> textBufferPasteClipboardAtCursor buf clip True >> return True True -> textBufferCopyClipboard buf clip >> return True _ -> return False -- | -- Event Handler -- codeTextKeyPressEventHandler :: TextView -> TextView -> CodeTextKeyPressEventHandler -> EventM EKey Bool codeTextKeyPressEventHandler lineTextView codeTextView evh = do name <- eventKeyName mods <- eventModifier liftIO $ do buf <- textViewGetBuffer codeTextView mark <- textBufferGetInsert buf iter <- textBufferGetIterAtMark buf mark lineNo <- textIterGetLine iter buf <- textViewGetBuffer lineTextView iter <- textBufferGetIterAtLine buf lineNo isTagExists <- textIterBeginsTag iter Nothing evh (T.unpack name) (elem Shift mods) (elem Control mods) isTagExists lineNo -- | -- -- getCodeTextLineNumber :: TextEditorData -> IO (Int, Int) getCodeTextLineNumber (TextEditorData _ codeView _ _) = do buf <- textViewGetBuffer codeView mark <- textBufferGetInsert buf iter <- textBufferGetIterAtMark buf mark lineNo <- textIterGetLine iter colNo <- textIterGetLineOffset iter return (lineNo, colNo) -- | -- -- blockIndentTextEditor :: TextEditorData -> IO () blockIndentTextEditor te@(TextEditorData _ codeView _ _) = do (startLine, endLine) <- getSelectedLineNumbers te buf <- textViewGetBuffer codeView insertIndent buf startLine endLine where insertIndent buf lineNo endNo = do iter <- textBufferGetIterAtLine buf lineNo textBufferInsert buf iter _INDENT_SPACE if lineNo == endNo then return () else insertIndent buf (lineNo+1) endNo -- | -- -- blockUnIndentTextEditor :: TextEditorData -> IO () blockUnIndentTextEditor te@(TextEditorData _ codeView _ _) = do (startLine, endLine) <- getSelectedLineNumbers te buf <- textViewGetBuffer codeView deleteIndent buf startLine endLine where deleteIndent buf lineNo endNo = do iter <- textBufferGetIterAtLine buf lineNo next <- textIterCopy iter forwardTextIter next 2 >>= \case Nothing -> return () Just endIter -> do indent <- textBufferGetText buf iter endIter False when (_INDENT_SPACE == indent) $ textBufferDelete buf iter endIter if lineNo == endNo then return () else deleteIndent buf (lineNo+1) endNo -- | -- -- blockCommentTextEditor :: TextEditorData -> IO () blockCommentTextEditor te@(TextEditorData _ codeView _ _) = do (startLine, endLine) <- getSelectedLineNumbers te buf <- textViewGetBuffer codeView insertComment buf startLine endLine where insertComment buf lineNo endNo = do iter <- textBufferGetIterAtLine buf lineNo cnt <- getSpaceCount buf iter when (cnt /= (-1)) $ do forwardTextIter iter cnt >>= \case Nothing -> return () Just startIter -> textBufferInsert buf startIter _COMMENT_TAG if lineNo == endNo then return () else insertComment buf (lineNo+1) endNo getSpaceCount buf iter = do isEndLine <- textIterEndsLine iter isEndBuf <- textIterIsEnd iter if isEndLine || isEndBuf then return (-1) else do endIter <- textIterCopy iter _ <- textIterForwardToLineEnd endIter str <- textBufferGetText buf iter endIter False return $ length $ takeWhile (== ' ') str -- | -- -- blockUnCommentTextEditor :: TextEditorData -> IO () blockUnCommentTextEditor te@(TextEditorData _ codeView _ _) = do (startLine, endLine) <- getSelectedLineNumbers te buf <- textViewGetBuffer codeView deleteComment buf startLine endLine where deleteComment buf lineNo endNo = do iter <- textBufferGetIterAtLine buf lineNo (isCmnt, idx) <- getCommentInfo buf iter iterMay <- forwardTextIter iter idx when isCmnt $ deleteCommentTag buf iterMay if lineNo == endNo then return () else deleteComment buf (lineNo+1) endNo deleteCommentTag _ Nothing = return () deleteCommentTag buf (Just iter) = do next <- textIterCopy iter forwardTextIter next (length _COMMENT_TAG) >>= \case Nothing -> return () Just endIter -> textBufferDelete buf iter endIter getCommentInfo :: TextBuffer -> TextIter -> IO (Bool, Int) getCommentInfo buf iter = do endIter <- textIterCopy iter textIterForwardToLineEnd endIter >>= \case False -> return (False, 0) -- 最終行はFalseとなる問題あり。 True -> do str <- textBufferGetText buf iter endIter False if startswith _COMMENT_TAG (strip str) then return (True, length (head (split _COMMENT_TAG str))) else return (False, length (takeWhile (== ' ') str)) -- | -- -- getSelectedLineNumbers :: TextEditorData -> IO (Int, Int) getSelectedLineNumbers te@(TextEditorData _ codeView _ _) = do buf <- textViewGetBuffer codeView textBufferHasSelection buf >>= \case True -> do (startIter, endIter) <- textBufferGetSelectionBounds buf s <- textIterGetLine startIter e <- textIterGetLine endIter return (s, e) False -> do (l, _) <- getCodeTextLineNumber te return (l, l) -- | -- -- setContent2TextEditor :: TextEditorData -> BS.ByteString -> IO () setContent2TextEditor (TextEditorData _ codeView _ _) bs = do buf <- textViewGetBuffer codeView textBufferSetByteString buf bs textBufferSetModified buf False -- | -- -- setCursorOnTextEditor :: TextEditorData -> Int -> Int -> IO () setCursorOnTextEditor (TextEditorData _ codeView _ _) lineNo colNo = do buf <- textViewGetBuffer codeView iter <- textBufferGetIterAtLineOffset buf lineNo colNo textBufferPlaceCursor buf iter mark <- textBufferGetInsert buf textViewScrollToMark codeView mark 0.0 (Just (0.0, 0.5)) -- | -- -- insertText2TextEditor :: TextEditorData -> Int -> Int -> String -> IO () insertText2TextEditor (TextEditorData _ codeView _ _) startLineNo startColNo str = do buf <- textViewGetBuffer codeView iter <- textBufferGetIterAtLineOffset buf startLineNo startColNo textBufferInsert buf iter str -- | -- -- deleteRangeOnTextEditor :: TextEditorData -> Int -> Int -> Int -> Int -> IO () deleteRangeOnTextEditor (TextEditorData _ codeView _ _) startLineNo startColNo endLineNo endColNo = do buf <- textViewGetBuffer codeView startIter <- textBufferGetIterAtLineOffset buf startLineNo startColNo endIter <- textBufferGetIterAtLineOffset buf endLineNo endColNo textBufferDelete buf startIter endIter -- | -- -- getSelectedText :: TextEditorData -> IO String getSelectedText (TextEditorData _ codeView _ _) = do buf <- textViewGetBuffer codeView textBufferHasSelection buf >>= \case True -> do (startIter, endIter) <- textBufferGetSelectionBounds buf textBufferGetText buf startIter endIter False False -> return ""