{-# LANGUAGE FlexibleInstances, TemplateHaskell, UndecidableInstances #-} module Hbro.Config where -- {{{ Imports import qualified Hbro.Keys as Key import Hbro.IPC import Hbro.Util -- import Control.Conditional import Control.Lens hiding(set) import Control.Monad.Base import Control.Monad.Error hiding(forM_, mapM_) -- import Control.Monad.Writer hiding(forM_, mapM_) -- import Data.Foldable (forM_, mapM_) -- import Data.Functor import Data.List import Data.Map (Map) import qualified Data.Map as M import Graphics.UI.Gtk.Gdk.EventM import Graphics.UI.Gtk.WebKit.WebNavigationAction import Network.URI as N hiding(parseURI, parseURIReference) import Prelude hiding(mapM_) -- }}} -- {{{ Types data ResourceAction = Load | Download data Verbosity = Quiet | Normal | Verbose deriving(Eq, Show) -- | Custom settings provided by the user data Config m = Config { _homePage :: URI, -- ^ Startup page -- Parameters _verbosity :: Verbosity, -- ^ Logs verbosity -- Hooks _keyBindings :: Map Key.Mode (Key.Bindings m), -- ^ Key bindings _onDownload :: URI -> String -> Int -> m (), -- ^ Callback triggered when a download is requested _onKeyStroke :: [Key.Stroke] -> m (), -- ^ Callback triggered when a key is pressed _onLinkClicked :: MouseButton -> URI -> m (), -- ^ Callback triggered when a link is clicked _onLoadRequested :: URI -> m (), -- ^ Callback triggered when a load is requested _onLoadFinished :: m (), -- ^ Callback triggered when a load is finished _onNewWindow :: URI -> m (), -- ^ Callback triggered when a new window is requested _onResourceOpened :: URI -> String -> m ResourceAction, -- ^ Callback triggered when opening a non HTML resource _onTitleChanged :: String -> m (), -- ^ Callback triggered when document title is changed _commands :: CommandsMap m -- ^ Commands recognized through IPC system } makeLenses ''Config instance Show (Config m) where show c = "Home page = " ++ (show $ c^.homePage) ++ "\nVerbosity = " ++ (show $ c^.verbosity) -- | 'MonadReader' for 'Config' class (Monad m) => ConfigReader n m | m -> n where readConfig :: Simple Lens (Config n) a -> m a instance ConfigReader n ((->) (Config n)) where readConfig l = view l -- | 'MonadWriter' for 'Config' class (Monad m) => ConfigWriter n m | m -> n where writeConfig :: Simple Lens (Config n) a -> a -> m () -- | 'MonadState' for 'Config' type ConfigState n m = (ConfigReader n m, ConfigWriter n m) modifyConfig :: (ConfigState n m) => Simple Lens (Config n) a -> (a -> a) -> m () modifyConfig l f = writeConfig l . f =<< readConfig l instance Eq NavigationReason where a == b = (fromEnum a) == (fromEnum b) instance Show NavigationReason where show WebNavigationReasonLinkClicked = "Link clicked" show WebNavigationReasonFormSubmitted = "Form submitted" show WebNavigationReasonBackForward = "Back/forward" show WebNavigationReasonReload = "Reload" show WebNavigationReasonFormResubmitted = "Form resubmitted" show WebNavigationReasonOther = "Other" -- }}} -- | Run an action unless verbosity is 'Quiet' unlessQuiet :: (MonadBase IO m, ConfigReader n m) => m () -> m () unlessQuiet f = do quiet' <- readConfig verbosity case quiet' of Quiet -> return () _ -> f -- | Run an action when verbosity is 'Verbose' whenLoud :: (MonadBase IO m, ConfigReader n m) => m () -> m () whenLoud f = do verbose' <- readConfig verbosity case verbose' of Verbose -> f _ -> return () log, logV :: (MonadBase IO m, ConfigReader n m) => String -> m () log = unlessQuiet . io . putStrLn logV = whenLoud . io . putStrLn -- | Bind a keystrokes chain to a callback, in a given mode bind :: (MonadBase IO m, ConfigState m m) => Key.Mode -> String -> m () -> m () bind mode strokes action = case newBindings of Just b -> do oldValue <- readConfig keyBindings let newValue = M.insertWith Key.merge mode b oldValue void $ writeConfig keyBindings newValue return () _ -> return () where newBindings = Key.mkBinding strokes action