{-# OPTIONS_GHC -XScopedTypeVariables -XDeriveDataTypeable -XMultiParamTypeClasses -XTypeSynonymInstances #-} ----------------------------------------------------------------------------- -- -- Module : IDE.Find -- Copyright : (c) Juergen Nicklisch-Franken, Hamish Mackenzie -- License : GNU-GPL -- -- Maintainer : -- Stability : provisional -- Portability : portablea -- -- | The toolbar for searching and replacing in a text buffer -- ------------------------------------------------------------------------------- module IDE.Find ( toggleFindbar , constructFindReplace , hideFindbar , showFindbar , focusFindEntry , editFindInc , editGotoLine , FindState(..) , getFindState , setFindState , editFind , showToolbar , hideToolbar , toggleToolbar ) where import Graphics.UI.Gtk hiding (get) import Graphics.UI.Gtk.Gdk.Events import Data.Maybe import Control.Monad.Reader import IDE.Core.State import IDE.Pane.SourceBuffer import Data.Char (toLower) import Text.Regex.Posix.String (compile) import Text.Regex.Posix (Regex(..), compNewline, compExtended, execBlank, compIgnoreCase) import Data.Bits ((.|.)) import Data.List (nub, find, isPrefixOf) import Data.Array ((!)) import Text.Regex.Base.RegexLike (matchAll) import Text.Regex (subRegex) import IDE.Tool (runTool) import Control.Concurrent (forkIO) import IDE.Pane.Grep import GHC.Unicode (isAlphaNum) import IDE.Package (getPackageDescriptionAndPath) import Distribution.PackageDescription (allBuildInfo, hsSourceDirs) data FindState = FindState { entryStr :: String , entryHist :: [String] , replaceStr :: String , replaceHist :: [String] , caseSensitive :: Bool , entireWord :: Bool , wrapAround :: Bool , regex :: Bool , lineNr :: Int} deriving(Eq,Ord,Show,Read) getFindState :: IDEM FindState getFindState = do (fb,ls) <- needFindbar liftIO $ do lineNr <- getLineEntry fb >>= (\e -> spinButtonGetValueAsInt (castToSpinButton e)) replaceStr <- getReplaceEntry fb >>= (\e -> entryGetText (castToEntry e)) entryStr <- getFindEntry fb >>= (\e -> entryGetText (castToEntry e)) entryHist <- listStoreToList ls entireWord <- getEntireWord fb wrapAround <- getWrapAround fb caseSensitive <- getCaseSensitive fb regex <- getRegex fb return FindState{ entryStr = entryStr , entryHist = entryHist , replaceStr = replaceStr , replaceHist = [] , caseSensitive = caseSensitive , entireWord = entireWord , wrapAround = wrapAround , regex = regex , lineNr = lineNr} setFindState :: FindState -> IDEAction setFindState fs = do (fb,ls) <- needFindbar liftIO $ do getLineEntry fb >>= (\e -> spinButtonSetValue (castToSpinButton e) (fromIntegral (lineNr fs))) getReplaceEntry fb >>= (\e -> entrySetText (castToEntry e) (replaceStr fs)) getFindEntry fb >>= (\e -> entrySetText (castToEntry e) (entryStr fs)) listStoreClear ls mapM_ (\s -> listStoreAppend ls s) (entryHist fs) setEntireWord fb (entireWord fs) setWrapAround fb (wrapAround fs) setCaseSensitive fb (caseSensitive fs) setRegex fb (regex fs) hideToolbar :: IDEAction hideToolbar = do (_,mbtb) <- readIDE toolbar case mbtb of Nothing -> return () Just tb -> do modifyIDE_ (\ide -> ide{toolbar = (False,snd (toolbar ide))}) liftIO $ widgetHideAll tb showToolbar :: IDEAction showToolbar = do (_,mbtb) <- readIDE toolbar case mbtb of Nothing -> return () Just tb -> do modifyIDE_ (\ide -> ide{toolbar = (True,snd (toolbar ide))}) liftIO $ widgetShowAll tb toggleToolbar :: IDEAction toggleToolbar = do toolbar' <- readIDE toolbar if fst toolbar' then hideToolbar else showToolbar hideFindbar :: IDEAction hideFindbar = do (_,mbfb) <- readIDE findbar modifyIDE_ (\ide -> ide{findbar = (False,mbfb)}) case mbfb of Nothing -> return () Just (fb,_) -> liftIO $ widgetHideAll fb showFindbar :: IDEAction showFindbar = do (_,mbfb) <- readIDE findbar modifyIDE_ (\ide -> ide{findbar = (True,mbfb)}) case mbfb of Nothing -> return () Just (fb,_) -> liftIO $ widgetShowAll fb focusFindEntry :: IDEAction focusFindEntry = do (fb,_) <- needFindbar liftIO $ do entry <- getFindEntry fb widgetGrabFocus entry toggleFindbar :: IDEAction toggleFindbar = do findbar <- readIDE findbar if fst findbar then hideFindbar else showFindbar constructFindReplace :: IDEM Toolbar constructFindReplace = reifyIDE $ \ ideR -> do toolbar <- toolbarNew tooltips <- tooltipsNew toolbarSetStyle toolbar ToolbarIcons toolbarSetIconSize toolbar IconSizeSmallToolbar closeButton <- toolButtonNewFromStock "gtk-close" toolbarInsert toolbar closeButton 0 spinTool <- toolItemNew spinL <- spinButtonNewWithRange 1.0 1000.0 10.0 widgetSetName spinL "gotoLineEntry" containerAdd spinTool spinL widgetSetName spinTool "gotoLineEntryTool" toolbarInsert toolbar spinTool 0 labelTool3 <- toolItemNew label3 <- labelNew (Just "Goto Line :") containerAdd labelTool3 label3 toolbarInsert toolbar labelTool3 0 sep1 <- separatorToolItemNew toolbarInsert toolbar sep1 0 grepButton <- toolButtonNew (Nothing :: Maybe Widget) (Just "Grep") toolbarInsert toolbar grepButton 0 grepButton `onToolButtonClicked` (reflectIDE (doGrep toolbar) ideR) tooltipsSetTip tooltips grepButton "Search in multiple files" "" sep1 <- separatorToolItemNew toolbarInsert toolbar sep1 0 replaceAllButton <- toolButtonNew (Nothing :: Maybe Widget) (Just "Replace All") toolbarInsert toolbar replaceAllButton 0 replaceButton <- toolButtonNewFromStock "gtk-find-and-replace" toolbarInsert toolbar replaceButton 0 replaceTool <- toolItemNew rentry <- entryNew widgetSetName rentry "replaceEntry" containerAdd replaceTool rentry widgetSetName replaceTool "replaceTool" toolbarInsert toolbar replaceTool 0 labelTool2 <- toolItemNew label2 <- labelNew (Just "Replace: ") containerAdd labelTool2 label2 toolbarInsert toolbar labelTool2 0 sep2 <- separatorToolItemNew toolbarInsert toolbar sep2 0 nextButton <- toolButtonNewFromStock "gtk-go-forward" toolbarInsert toolbar nextButton 0 tooltipsSetTip tooltips nextButton "Search for the next match in the current file" "" nextButton `onToolButtonClicked` (doSearch toolbar Forward ideR ) wrapAroundButton <- toggleToolButtonNew toolButtonSetLabel wrapAroundButton (Just "Wrap") widgetSetName wrapAroundButton "wrapAroundButton" toolbarInsert toolbar wrapAroundButton 0 tooltipsSetTip tooltips wrapAroundButton "When selected searching will continue from the top when no more matches are found" "" previousButton <- toolButtonNewFromStock "gtk-go-back" toolbarInsert toolbar previousButton 0 tooltipsSetTip tooltips previousButton "Search for the previous match in the current file" "" previousButton `onToolButtonClicked` (doSearch toolbar Backward ideR ) entryTool <- toolItemNew entry <- entryNew widgetSetName entry "searchEntry" containerAdd entryTool entry widgetSetName entryTool "searchEntryTool" toolItemSetExpand entryTool True toolbarInsert toolbar entryTool 0 store <- listStoreNew [] customStoreSetColumn store (makeColumnIdString 0) id completion <- entryCompletionNew entrySetCompletion entry completion set completion [entryCompletionModel := Just store] cell <- cellRendererTextNew cellLayoutPackStart completion cell True cellLayoutSetAttributes completion cell store (\cd -> [cellText := cd]) entryCompletionSetMatchFunc completion (matchFunc store) on completion matchSelected $ \ model iter -> do txt <- treeModelGetValue model iter (makeColumnIdString 0) entrySetText entry txt doSearch toolbar Forward ideR return True regexButton <- toggleToolButtonNew toolButtonSetLabel regexButton (Just "Regex") widgetSetName regexButton "regexButton" toolbarInsert toolbar regexButton 0 regexButton `onToolButtonClicked` (doSearch toolbar Insert ideR) tooltipsSetTip tooltips regexButton "When selected the search string is used as a regular expression" "" entireWordButton <- toggleToolButtonNew toolButtonSetLabel entireWordButton (Just "Words") widgetSetName entireWordButton "entireWordButton" toolbarInsert toolbar entireWordButton 0 entireWordButton `onToolButtonClicked` (doSearch toolbar Insert ideR) tooltipsSetTip tooltips entireWordButton "When selected only entire words are matched" "" caseSensitiveButton <- toggleToolButtonNew toolButtonSetLabel caseSensitiveButton (Just "c. S.") widgetSetName caseSensitiveButton "caseSensitiveButton" toolbarInsert toolbar caseSensitiveButton 0 caseSensitiveButton `onToolButtonClicked` (doSearch toolbar Insert ideR) tooltipsSetTip tooltips caseSensitiveButton "When selected the search is case sensitive" "" labelTool <- toolItemNew label <- labelNew (Just "Find: ") containerAdd labelTool label toolbarInsert toolbar labelTool 0 entry `afterInsertText` (\t i -> do doSearch toolbar Insert ideR return i) entry `afterDeleteText` (\ _ _ -> doSearch toolbar Delete ideR ) entry `afterKeyPress` (\ e -> case e of k@(Key _ _ _ _ _ _ _ _ _ _) | eventKeyName k == "Down" -> do doSearch toolbar Forward ideR return True | eventKeyName k == "Up" -> do doSearch toolbar Backward ideR return True | eventKeyName k == "Escape" -> do getOut ideR return True | otherwise -> return False _ -> return False) entry `onEntryActivate` (doSearch toolbar Forward ideR) replaceButton `onToolButtonClicked` replace toolbar Forward ideR replaceAllButton `onToolButtonClicked` replaceAll toolbar Forward ideR spinL `afterFocusIn` (\ _ -> (reflectIDE (inActiveBufContext True $ \_ gtkbuf currentBuffer _ -> do max <- textBufferGetLineCount gtkbuf spinButtonSetRange spinL 1.0 (fromIntegral max) return True) ideR)) spinL `afterEntryActivate` (reflectIDE (inActiveBufContext () $ \_ gtkbuf currentBuffer _ -> do line <- spinButtonGetValueAsInt spinL iter <- textBufferGetStartIter gtkbuf textIterSetLine iter (line - 1) textBufferPlaceCursor gtkbuf iter textViewScrollToIter (sourceView currentBuffer) iter 0.2 Nothing return ()) ideR ) closeButton `onToolButtonClicked` do reflectIDE hideFindbar ideR set toolbar [ toolbarChildHomogeneous spinTool := False ] set toolbar [ toolbarChildHomogeneous wrapAroundButton := False ] set toolbar [ toolbarChildHomogeneous entireWordButton := False ] set toolbar [ toolbarChildHomogeneous caseSensitiveButton := False ] set toolbar [ toolbarChildHomogeneous regexButton := False ] set toolbar [ toolbarChildHomogeneous replaceAllButton := False ] set toolbar [ toolbarChildHomogeneous labelTool := False ] set toolbar [ toolbarChildHomogeneous labelTool2 := False ] set toolbar [ toolbarChildHomogeneous labelTool3 := False ] reflectIDE (modifyIDE_ (\ide -> ide{findbar = (False,Just (toolbar,store))})) ideR return toolbar where getOut = reflectIDE $ do hideFindbar mbbuf <- maybeActiveBuf case mbbuf of Nothing -> return () Just buf -> do liftIO $ widgetGrabFocus (sourceView buf) return () doSearch :: Toolbar -> SearchHint -> IDERef -> IO () doSearch fb hint ideR = do entry <- getFindEntry fb search <- entryGetText (castToEntry entry) entireWord <- getEntireWord fb caseSensitive <- getCaseSensitive fb wrapAround <- getWrapAround fb regex <- getRegex fb mbExpAndMatchIndex <- liftIO $ regexAndMatchIndex caseSensitive entireWord regex search case mbExpAndMatchIndex of Just (exp, matchIndex) -> do res <- reflectIDE (editFind entireWord caseSensitive wrapAround regex search "" hint) ideR if res || null search then do widgetModifyBase entry StateNormal white widgetModifyText entry StateNormal black else do widgetModifyBase entry StateNormal red widgetModifyText entry StateNormal white Nothing -> do if null search then do widgetModifyBase entry StateNormal white widgetModifyText entry StateNormal black else do widgetModifyBase entry StateNormal orange widgetModifyText entry StateNormal black reflectIDE (addToHist search) ideR return () doGrep :: Toolbar -> IDEAction doGrep fb = do ideR <- ask entry <- liftIO $ getFindEntry fb search <- liftIO $ entryGetText (castToEntry entry) entireWord <- liftIO $ getEntireWord fb caseSensitive <- liftIO $ getCaseSensitive fb wrapAround <- liftIO $ getWrapAround fb regex <- liftIO $ getRegex fb let (regexString, _) = regexStringAndMatchIndex entireWord regex search mbPD <- getPackageDescriptionAndPath case mbPD of Nothing -> ideMessage Normal "No package description" Just (pd,cabalPath) -> do let srcPaths = nub $ concatMap hsSourceDirs $ allBuildInfo pd liftIO $ forkIO $ do (output, pid) <- runTool "grep" ((if caseSensitive then [] else ["-i"]) ++ ["-r", "-E", "-n", "--exclude=*~", regexString] ++ srcPaths) reflectIDE (setGrepResults output) ideR return () matchFunc :: ListStore String -> String -> TreeIter -> IO Bool matchFunc model str iter = do tp <- treeModelGetPath model iter r <- case tp of (i:_) -> do row <- listStoreGetValue model i return (isPrefixOf (map toLower str) (map toLower row) && length str < length row) otherwise -> return False return r addToHist :: String -> IDEAction addToHist str = if null str then return () else do (_,ls) <- needFindbar liftIO $ do entryHist <- listStoreToList ls when (null (filter (\e -> (str `isPrefixOf` e)) entryHist)) $ do let newList = take 12 (str : filter (\e -> not (e `isPrefixOf` str)) entryHist) listStoreClear ls mapM_ (\s -> listStoreAppend ls s) newList replace :: Toolbar -> SearchHint -> IDERef -> IO () replace fb hint ideR = do entry <- getFindEntry fb search <- entryGetText (castToEntry entry) rentry <- getReplaceEntry fb replace <- entryGetText (castToEntry rentry) entireWord <- getEntireWord fb caseSensitive <- getCaseSensitive fb wrapAround <- getWrapAround fb regex <- getRegex fb found <- reflectIDE (editReplace entireWord caseSensitive wrapAround regex search replace hint) ideR return () replaceAll :: Toolbar -> SearchHint -> IDERef -> IO () replaceAll fb hint ideR = do entry <- getFindEntry fb search <- entryGetText (castToEntry entry) rentry <- getReplaceEntry fb replace <- entryGetText (castToEntry rentry) entireWord <- getEntireWord fb caseSensitive <- getCaseSensitive fb wrapAround <- getWrapAround fb regex <- getRegex fb found <- reflectIDE (editReplaceAll entireWord caseSensitive wrapAround regex search replace hint) ideR return () editFind :: Bool -> Bool -> Bool -> Bool -> String -> String -> SearchHint -> IDEM Bool editFind entireWord caseSensitive wrapAround regex search dummy hint = do mbExpAndMatchIndex <- liftIO $ regexAndMatchIndex caseSensitive entireWord regex search case mbExpAndMatchIndex of Nothing -> return False Just (exp, matchIndex) -> editFind' exp matchIndex wrapAround dummy hint editFind' :: Regex -> Int -> Bool -> String -> SearchHint -> IDEM Bool editFind' exp matchIndex wrapAround dummy hint = inActiveBufContext' False $ \_ gtkbuf currentBuffer _ -> liftIO $ do i1 <- textBufferGetStartIter gtkbuf i2 <- textBufferGetEndIter gtkbuf textBufferRemoveTagByName gtkbuf "found" i1 i2 startMark <- textBufferGetInsert gtkbuf st1 <- textBufferGetIterAtMark gtkbuf startMark mbsr2 <- liftIO $ do if hint == Backward then do textIterBackwardChar st1 textIterBackwardChar st1 mbsr <- backSearch exp matchIndex st1 case mbsr of Nothing -> if wrapAround then do backSearch exp matchIndex i2 else return Nothing Just (start,end) -> return (Just (start,end)) else do if hint == Forward then textIterForwardChar st1 else return True mbsr <- forwardSearch exp matchIndex st1 case mbsr of Nothing -> if wrapAround then do forwardSearch exp matchIndex i1 else return Nothing Just (start,end) -> return (Just (start,end)) case mbsr2 of Just (start,end) -> do --found --widgetGrabFocus sourceView textViewScrollToIter (sourceView currentBuffer) start 0.2 Nothing textBufferApplyTagByName gtkbuf "found" start end textBufferPlaceCursor gtkbuf start return True Nothing -> return False where backSearch exp matchIndex iter = do gtkbuf <- textIterGetBuffer iter offset <- textIterGetOffset iter findMatch exp matchIndex gtkbuf (<= offset) True forwardSearch exp matchIndex iter = do gtkbuf <- textIterGetBuffer iter offset <- textIterGetOffset iter findMatch exp matchIndex gtkbuf (>= offset) False regexAndMatchIndex :: Bool -> Bool -> Bool -> String -> IO (Maybe (Regex, Int)) regexAndMatchIndex caseSensitive entireWord regex string = do if null string then return Nothing else do let (regexString, index) = regexStringAndMatchIndex entireWord regex string mbRegex <- compileRegex caseSensitive regexString return $ maybe Nothing (Just . (flip (,) index)) mbRegex regexStringAndMatchIndex :: Bool -> Bool -> String -> (String, Int) regexStringAndMatchIndex entireWord regex string = -- Escape non regex string let regexString = if regex then string else foldl (\s c -> s ++ if isAlphaNum c then [c] else ['\\', c]) "" string in -- Regular expression with word filter if needed if entireWord then ("(^|[^a-zA-Z0-9])(" ++ regexString ++ ")($|[^a-zA-Z0-9])", 2) else (regexString, 0) findMatch :: Regex -> Int -> TextBuffer -> (Int -> Bool) -> Bool -> IO (Maybe (TextIter, TextIter)) findMatch exp matchIndex gtkbuf offsetPred findLast = do iterStart <- textBufferGetStartIter gtkbuf iterEnd <- textBufferGetEndIter gtkbuf text <- textBufferGetText gtkbuf iterStart iterEnd True let matches = (if findLast then reverse else id) (matchAll exp text) case find (offsetPred . fst . (!matchIndex)) matches of Just matches -> do iter1 <- textIterCopy iterStart textIterForwardChars iter1 (fst (matches!matchIndex)) iter2 <- textIterCopy iter1 textIterForwardChars iter2 (snd (matches!matchIndex)) return $ Just (iter1, iter2) Nothing -> return Nothing editReplace :: Bool -> Bool -> Bool -> Bool -> String -> String -> SearchHint -> IDEM Bool editReplace entireWord caseSensitive wrapAround regex search replace hint = editReplace' entireWord caseSensitive wrapAround regex search replace hint True editReplace' :: Bool -> Bool -> Bool -> Bool -> String -> String -> SearchHint -> Bool -> IDEM Bool editReplace' entireWord caseSensitive wrapAround regex search replace hint mayRepeat = inActiveBufContext' False $ \_ gtkbuf currentBuffer _ -> do insertMark <- liftIO $ textBufferGetInsert gtkbuf iter <- liftIO $ textBufferGetIterAtMark gtkbuf insertMark offset <- liftIO $ textIterGetOffset iter mbExpAndMatchIndex <- liftIO $ regexAndMatchIndex caseSensitive entireWord regex search case mbExpAndMatchIndex of Just (exp, matchIndex) -> do match <- liftIO $ findMatch exp matchIndex gtkbuf (== offset) False case match of Just (iterStart, iterEnd) -> do old <- liftIO $ textIterGetText iterStart iterEnd mbText <- liftIO $ replacementText regex old replace case mbText of Just text -> do liftIO $ textBufferDelete gtkbuf iterStart iterEnd liftIO $ textBufferInsert gtkbuf iterStart text Nothing -> do sysMessage Normal "Should never happen. findMatch worked but repleacementText failed" return () editFind entireWord caseSensitive wrapAround regex search "" hint Nothing -> do r <- editFind entireWord caseSensitive wrapAround regex search "" hint if r then editReplace' entireWord caseSensitive wrapAround regex search replace hint False else return False Nothing -> return False where replacementText False old replace = return $ Just replace replacementText True old replace = do mbExp <- compileRegex caseSensitive search case mbExp of Just exp -> return $ Just $ subRegex exp old replace Nothing -> return Nothing editReplaceAll :: Bool -> Bool -> Bool -> Bool -> String -> String -> SearchHint -> IDEM Bool editReplaceAll entireWord caseSensitive wrapAround regex search replace hint = do res <- editReplace' entireWord caseSensitive False regex search replace hint True if res then editReplaceAll entireWord caseSensitive False regex search replace hint else return False compileRegex :: Bool -> String -> IO (Maybe Regex) compileRegex caseSense searchString = do res <- compile ((if caseSense then id else (compIgnoreCase .|.)) (compExtended .|. compNewline)) execBlank searchString case res of Left err -> do sysMessage Normal (show err) return Nothing Right regex -> return $ Just regex red = Color 64000 10000 10000 orange = Color 64000 48000 0 white = Color 64000 64000 64000 black = Color 0 0 0 needFindbar :: IDEM (Toolbar,ListStore String) needFindbar = do (_,mbfb) <- readIDE findbar case mbfb of Nothing -> throwIDE "Find>>needFindbar: Findbar not initialized" Just p -> return p editFindInc :: SearchHint -> IDEAction editFindInc hint = do ideR <- ask (fb,_) <- needFindbar case hint of Initial -> inActiveBufContext' () $ \_ gtkbuf currentBuffer _ -> liftIO $ do hasSelection <- textBufferHasSelection gtkbuf when hasSelection $ do (i1,i2) <- textBufferGetSelectionBounds gtkbuf text <- textBufferGetText gtkbuf i1 i2 False findEntry <- getFindEntry fb entrySetText (castToEntry findEntry) text return () _ -> return () showFindbar reifyIDE $ \ideR -> do entry <- getFindEntry fb widgetGrabFocus entry case hint of Forward -> doSearch fb Forward ideR Backward -> doSearch fb Backward ideR _ -> return () editGotoLine :: IDEAction editGotoLine = do showFindbar (fb,_) <- needFindbar entry <- liftIO $ getLineEntry fb liftIO $ widgetGrabFocus entry getLineEntry, getReplaceEntry, getFindEntry :: Toolbar -> IO Widget getLineEntry = getWidget "gotoLineEntryTool" getReplaceEntry = getWidget "replaceTool" getFindEntry = getWidget "searchEntryTool" getWidget :: String -> Toolbar -> IO Widget getWidget str tb = do widgets <- containerGetChildren tb entryL <- filterM (\w -> liftM (== str) (widgetGetName w) ) widgets case entryL of [w] -> do mbw <- binGetChild (castToBin w) case mbw of Nothing -> throwIDE "Find>>getWidget not found(1)" Just w -> return w _ -> throwIDE "Find>>getWidget not found(2)" getEntireWord, getWrapAround, getCaseSensitive, getRegex :: Toolbar -> IO Bool getEntireWord = getSelection "entireWordButton" getWrapAround = getSelection "wrapAroundButton" getCaseSensitive = getSelection "caseSensitiveButton" getRegex = getSelection "regexButton" getSelection :: String -> Toolbar -> IO Bool getSelection str tb = do widgets <- containerGetChildren tb entryL <- filterM (\w -> liftM (== str) (widgetGetName w) ) widgets case entryL of [w] -> toggleToolButtonGetActive (castToToggleToolButton w) _ -> throwIDE "Find>>getIt widget not found" setEntireWord, setWrapAround, setCaseSensitive, setRegex :: Toolbar -> Bool -> IO () setEntireWord = setSelection "entireWordButton" setWrapAround = setSelection "wrapAroundButton" setCaseSensitive = setSelection "caseSensitiveButton" setRegex = setSelection "regexButton" setSelection :: String -> Toolbar -> Bool -> IO () setSelection str tb bool = do widgets <- containerGetChildren tb entryL <- filterM (\w -> liftM (== str) (widgetGetName w) ) widgets case entryL of [w] -> toggleToolButtonSetActive (castToToggleToolButton w) bool _ -> throwIDE "Find>>getIt widget not found"