    -----------------------------------------------------------------------------
    -- |
    -- Module      :  XMonad.Hooks.ToggleHook
    -- Description :  Hook and keybindings for toggling hook behavior.
    -- Copyright   :  Ben Boeckel <mathstuf@gmail.com>
    -- License     :  BSD-style (see LICENSE)
    --
    -- Maintainer  :  Ben Boeckel <mathstuf@gmail.com>
    -- Stability   :  unstable
    -- Portability :  unportable
    --
    -- Hook and keybindings for toggling hook behavior.
    -----------------------------------------------------------------------------

    module XMonad.Hooks.ToggleHook ( -- * Usage
                                     -- $usage

                                     -- * The hook
                                     toggleHook
                                   , toggleHook'

                                     -- * Actions
                                   , hookNext
                                   , toggleHookNext
                                   , hookAllNew
                                   , toggleHookAllNew

                                     -- * Queries
                                   , willHook
                                   , willHookNext
                                   , willHookAllNew

                                     -- * Status bar utilities
                                     -- $pp
                                   , willHookNextPP
                                   , willHookAllNewPP
                                   , runLogHook ) where

    import Prelude hiding (all)

    import XMonad
    import XMonad.Prelude (guard, join)
    import qualified XMonad.Util.ExtensibleState as XS

    import Control.Arrow (first, second)

    import Data.Map

    {- Helper functions -}

    _set :: String -> ((a -> a) -> (Bool, Bool) -> (Bool, Bool)) -> a -> X ()
    _set :: forall a.
    String -> ((a -> a) -> (Bool, Bool) -> (Bool, Bool)) -> a -> X ()
    _set String
    n (a -> a) -> (Bool, Bool) -> (Bool, Bool)
    f a
    b = String -> ((Bool, Bool) -> (Bool, Bool)) -> X ()
    modify' String
    n ((a -> a) -> (Bool, Bool) -> (Bool, Bool)
    f ((a -> a) -> (Bool, Bool) -> (Bool, Bool))
    -> (a -> a) -> (Bool, Bool) -> (Bool, Bool)
    forall a b. (a -> b) -> a -> b
    $ a -> a -> a
    forall a b. a -> b -> a
    const a
    b)

    _toggle :: String -> ((Bool -> Bool) -> (Bool, Bool) -> (Bool, Bool)) -> X ()
    _toggle :: String -> ((Bool -> Bool) -> (Bool, Bool) -> (Bool, Bool)) -> X ()
    _toggle String
    n (Bool -> Bool) -> (Bool, Bool) -> (Bool, Bool)
    f = String -> ((Bool, Bool) -> (Bool, Bool)) -> X ()
    modify' String
    n ((Bool -> Bool) -> (Bool, Bool) -> (Bool, Bool)
    f Bool -> Bool
    not)

    _get :: String -> ((Bool, Bool) -> a) -> X a
    _get :: forall a. String -> ((Bool, Bool) -> a) -> X a
    _get String
    n (Bool, Bool) -> a
    f = (HookState -> a) -> X a
    forall a (m :: * -> *) b.
    (ExtensionClass a, XLike m) =>
    (a -> b) -> m b
    XS.gets ((HookState -> a) -> X a) -> (HookState -> a) -> X a
    forall a b. (a -> b) -> a -> b
    $ (Bool, Bool) -> a
    f ((Bool, Bool) -> a)
    -> (HookState -> (Bool, Bool)) -> HookState -> a
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ((Bool, Bool) -> String -> Map String (Bool, Bool) -> (Bool, Bool)
    forall k a. Ord k => a -> k -> Map k a -> a
    findWithDefault (Bool
    False, Bool
    False) String
    n (Map String (Bool, Bool) -> (Bool, Bool))
    -> (HookState -> Map String (Bool, Bool))
    -> HookState
    -> (Bool, Bool)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . HookState -> Map String (Bool, Bool)
    hooks)

    _pp :: String -> ((Bool, Bool) -> Bool) -> String -> (String -> String) -> X (Maybe String)
    _pp :: String
    -> ((Bool, Bool) -> Bool)
    -> String
    -> (String -> String)
    -> X (Maybe String)
    _pp String
    n (Bool, Bool) -> Bool
    f String
    s String -> String
    st = (\Bool
    b -> Bool -> Maybe ()
    forall (f :: * -> *). Alternative f => Bool -> f ()
    guard Bool
    b Maybe () -> Maybe String -> Maybe String
    forall a b. Maybe a -> Maybe b -> Maybe b
    forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
    >> String -> Maybe String
    forall a. a -> Maybe a
    Just (String -> String
    st String
    s)) (Bool -> Maybe String) -> X Bool -> X (Maybe String)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> String -> ((Bool, Bool) -> Bool) -> X Bool
    forall a. String -> ((Bool, Bool) -> a) -> X a
    _get String
    n (Bool, Bool) -> Bool
    f

    {- The current state is kept here -}

    newtype HookState = HookState { HookState -> Map String (Bool, Bool)
    hooks :: Map String (Bool, Bool) } deriving (ReadPrec [HookState]
    ReadPrec HookState
    Int -> ReadS HookState
    ReadS [HookState]
    (Int -> ReadS HookState)
    -> ReadS [HookState]
    -> ReadPrec HookState
    -> ReadPrec [HookState]
    -> Read HookState
    forall a.
    (Int -> ReadS a)
    -> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
    $creadsPrec :: Int -> ReadS HookState
    readsPrec :: Int -> ReadS HookState
    $creadList :: ReadS [HookState]
    readList :: ReadS [HookState]
    $creadPrec :: ReadPrec HookState
    readPrec :: ReadPrec HookState
    $creadListPrec :: ReadPrec [HookState]
    readListPrec :: ReadPrec [HookState]
    Read, Int -> HookState -> String -> String
    [HookState] -> String -> String
    HookState -> String
    (Int -> HookState -> String -> String)
    -> (HookState -> String)
    -> ([HookState] -> String -> String)
    -> Show HookState
    forall a.
    (Int -> a -> String -> String)
    -> (a -> String) -> ([a] -> String -> String) -> Show a
    $cshowsPrec :: Int -> HookState -> String -> String
    showsPrec :: Int -> HookState -> String -> String
    $cshow :: HookState -> String
    show :: HookState -> String
    $cshowList :: [HookState] -> String -> String
    showList :: [HookState] -> String -> String
    Show)

    instance ExtensionClass HookState where
        initialValue :: HookState
    initialValue = Map String (Bool, Bool) -> HookState
    HookState Map String (Bool, Bool)
    forall k a. Map k a
    empty
        extensionType :: HookState -> StateExtension
    extensionType = HookState -> StateExtension
    forall a. (Read a, Show a, ExtensionClass a) => a -> StateExtension
    PersistentExtension

    modify' :: String -> ((Bool, Bool) -> (Bool, Bool)) -> X ()
    modify' :: String -> ((Bool, Bool) -> (Bool, Bool)) -> X ()
    modify' String
    n (Bool, Bool) -> (Bool, Bool)
    f = (HookState -> HookState) -> X ()
    forall a (m :: * -> *).
    (ExtensionClass a, XLike m) =>
    (a -> a) -> m ()
    XS.modify (Map String (Bool, Bool) -> HookState
    HookState (Map String (Bool, Bool) -> HookState)
    -> (HookState -> Map String (Bool, Bool)) -> HookState -> HookState
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Map String (Bool, Bool) -> Map String (Bool, Bool)
    setter (Map String (Bool, Bool) -> Map String (Bool, Bool))
    -> (HookState -> Map String (Bool, Bool))
    -> HookState
    -> Map String (Bool, Bool)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . HookState -> Map String (Bool, Bool)
    hooks)
        where
            setter :: Map String (Bool, Bool) -> Map String (Bool, Bool)
    setter Map String (Bool, Bool)
    m = String
    -> (Bool, Bool)
    -> Map String (Bool, Bool)
    -> Map String (Bool, Bool)
    forall k a. Ord k => k -> a -> Map k a -> Map k a
    insert String
    n ((Bool, Bool) -> (Bool, Bool)
    f ((Bool, Bool) -> String -> Map String (Bool, Bool) -> (Bool, Bool)
    forall k a. Ord k => a -> k -> Map k a -> a
    findWithDefault (Bool
    False, Bool
    False) String
    n Map String (Bool, Bool)
    m)) Map String (Bool, Bool)
    m

    -- $usage
    -- This module provides actions (that can be set as keybindings)
    -- to be able to cause hooks to be occur on a conditional basis.
    --
    -- You can use it by including the following in your @xmonad.hs@:
    --
    -- > import XMonad.Hooks.ToggleHook
    --
    -- and adding 'toggleHook name hook' to your 'ManageHook' where @name@ is the
    -- name of the hook and @hook@ is the hook to execute based on the state.
    --
    -- > myManageHook = toggleHook "float" doFloat <> manageHook def
    --
    -- Additionally, toggleHook' is provided to toggle between two hooks (rather
    -- than on/off).
    --
    -- > myManageHook = toggleHook' "oldfocus" (const id) W.focusWindow <> manageHook def
    --
    -- The 'hookNext' and 'toggleHookNext' functions can be used in key
    -- bindings to set whether the hook is applied or not.
    --
    -- > , ((modm, xK_e), toggleHookNext "float")
    --
    -- 'hookAllNew' and 'toggleHookAllNew' are similar but float all
    -- spawned windows until disabled again.
    --
    -- > , ((modm, xK_r), toggleHookAllNew "float")

    -- | This 'ManageHook' will selectively apply a hook as set
    -- by 'hookNext' and 'hookAllNew'.
    toggleHook :: String -> ManageHook -> ManageHook
    toggleHook :: String -> ManageHook -> ManageHook
    toggleHook String
    n ManageHook
    h = String -> ManageHook -> ManageHook -> ManageHook
    toggleHook' String
    n ManageHook
    h ManageHook
    forall m. Monoid m => m
    idHook

    toggleHook' :: String -> ManageHook -> ManageHook -> ManageHook
    toggleHook' :: String -> ManageHook -> ManageHook -> ManageHook
    toggleHook' String
    n ManageHook
    th ManageHook
    fh = do m <- X (Map String (Bool, Bool)) -> Query (Map String (Bool, Bool))
    forall a. X a -> Query a
    liftX (X (Map String (Bool, Bool)) -> Query (Map String (Bool, Bool)))
    -> X (Map String (Bool, Bool)) -> Query (Map String (Bool, Bool))
    forall a b. (a -> b) -> a -> b
    $ (HookState -> Map String (Bool, Bool))
    -> X (Map String (Bool, Bool))
    forall a (m :: * -> *) b.
    (ExtensionClass a, XLike m) =>
    (a -> b) -> m b
    XS.gets HookState -> Map String (Bool, Bool)
    hooks
                             (next, all) <- return $ findWithDefault (False, False) n m
                             liftX $ XS.put $ HookState $ insert n (False, all) m
                             if next || all then th else fh

    -- | @hookNext name True@ arranges for the next spawned window to
    -- have the hook @name@ applied, @hookNext name False@ cancels it.
    hookNext :: String -> Bool -> X ()
    hookNext :: String -> Bool -> X ()
    hookNext String
    n = String
    -> ((Bool -> Bool) -> (Bool, Bool) -> (Bool, Bool)) -> Bool -> X ()
    forall a.
    String -> ((a -> a) -> (Bool, Bool) -> (Bool, Bool)) -> a -> X ()
    _set String
    n (Bool -> Bool) -> (Bool, Bool) -> (Bool, Bool)
    forall b c d. (b -> c) -> (b, d) -> (c, d)
    forall (a :: * -> * -> *) b c d.
    Arrow a =>
    a b c -> a (b, d) (c, d)
    first

    toggleHookNext :: String -> X ()
    toggleHookNext :: String -> X ()
    toggleHookNext String
    n = String -> ((Bool -> Bool) -> (Bool, Bool) -> (Bool, Bool)) -> X ()
    _toggle String
    n (Bool -> Bool) -> (Bool, Bool) -> (Bool, Bool)
    forall b c d. (b -> c) -> (b, d) -> (c, d)
    forall (a :: * -> * -> *) b c d.
    Arrow a =>
    a b c -> a (b, d) (c, d)
    first

    -- | @hookAllNew name True@ arranges for new windows to
    -- have the hook @name@ applied, @hookAllNew name False@ cancels it
    hookAllNew :: String -> Bool -> X ()
    hookAllNew :: String -> Bool -> X ()
    hookAllNew String
    n = String
    -> ((Bool -> Bool) -> (Bool, Bool) -> (Bool, Bool)) -> Bool -> X ()
    forall a.
    String -> ((a -> a) -> (Bool, Bool) -> (Bool, Bool)) -> a -> X ()
    _set String
    n (Bool -> Bool) -> (Bool, Bool) -> (Bool, Bool)
    forall b c d. (b -> c) -> (d, b) -> (d, c)
    forall (a :: * -> * -> *) b c d.
    Arrow a =>
    a b c -> a (d, b) (d, c)
    second

    toggleHookAllNew :: String -> X ()
    toggleHookAllNew :: String -> X ()
    toggleHookAllNew String
    n = String -> ((Bool -> Bool) -> (Bool, Bool) -> (Bool, Bool)) -> X ()
    _toggle String
    n (Bool -> Bool) -> (Bool, Bool) -> (Bool, Bool)
    forall b c d. (b -> c) -> (d, b) -> (d, c)
    forall (a :: * -> * -> *) b c d.
    Arrow a =>
    a b c -> a (d, b) (d, c)
    second

    -- | Query what will happen at the next ManageHook call for the hook @name@.
    willHook :: String -> X Bool
    willHook :: String -> X Bool
    willHook String
    n = String -> X Bool
    willHookNext String
    n X Bool -> X Bool -> X Bool
    forall (m :: * -> *). Monad m => m Bool -> m Bool -> m Bool
    <||> String -> X Bool
    willHookAllNew String
    n

    -- | Whether the next window will trigger the hook @name@.
    willHookNext :: String -> X Bool
    willHookNext :: String -> X Bool
    willHookNext String
    n = String -> ((Bool, Bool) -> Bool) -> X Bool
    forall a. String -> ((Bool, Bool) -> a) -> X a
    _get String
    n (Bool, Bool) -> Bool
    forall a b. (a, b) -> a
    fst

    -- | Whether new windows will trigger the hook @name@.
    willHookAllNew :: String -> X Bool
    willHookAllNew :: String -> X Bool
    willHookAllNew String
    n = String -> ((Bool, Bool) -> Bool) -> X Bool
    forall a. String -> ((Bool, Bool) -> a) -> X a
    _get String
    n (Bool, Bool) -> Bool
    forall a b. (a, b) -> b
    snd

    -- $pp
    -- The following functions are used to display the current
    -- state of 'hookNext' and 'hookAllNew' in your
    -- "XMonad.Hooks.StatusBar". 'willHookNextPP' and
    -- 'willHookAllNewPP' should be added to the
    -- 'XMonad.Hooks.StatusBar.PP.ppExtras' field of your
    -- "XMonad.Hooks.StatusBar.PP".
    --
    -- Use 'runLogHook' to refresh the output of your 'logHook', so
    -- that the effects of a 'hookNext'/... will be visible
    -- immediately:
    --
    -- > , ((modm, xK_e), toggleHookNext "float" >> runLogHook)
    --
    -- The @String -> String@ parameters to 'willHookNextPP' and
    -- 'willHookAllNewPP' will be applied to their output, you
    -- can use them to set the text color, etc., or you can just
    -- pass them 'id'.

    willHookNextPP :: String -> (String -> String) -> X (Maybe String)
    willHookNextPP :: String -> (String -> String) -> X (Maybe String)
    willHookNextPP String
    n = String
    -> ((Bool, Bool) -> Bool)
    -> String
    -> (String -> String)
    -> X (Maybe String)
    _pp String
    n (Bool, Bool) -> Bool
    forall a b. (a, b) -> a
    fst String
    "Next"

    willHookAllNewPP :: String -> (String -> String) -> X (Maybe String)
    willHookAllNewPP :: String -> (String -> String) -> X (Maybe String)
    willHookAllNewPP String
    n = String
    -> ((Bool, Bool) -> Bool)
    -> String
    -> (String -> String)
    -> X (Maybe String)
    _pp String
    n (Bool, Bool) -> Bool
    forall a b. (a, b) -> b
    snd String
    "All"

    runLogHook :: X ()
    runLogHook :: X ()
    runLogHook = X (X ()) -> X ()
    forall (m :: * -> *) a. Monad m => m (m a) -> m a
    join (X (X ()) -> X ()) -> X (X ()) -> X ()
    forall a b. (a -> b) -> a -> b
    $ (XConf -> X ()) -> X (X ())
    forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
    asks ((XConf -> X ()) -> X (X ())) -> (XConf -> X ()) -> X (X ())
    forall a b. (a -> b) -> a -> b
    $ XConfig Layout -> X ()
    forall (l :: * -> *). XConfig l -> X ()
    logHook (XConfig Layout -> X ())
    -> (XConf -> XConfig Layout) -> XConf -> X ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . XConf -> XConfig Layout
    config