    -----------------------------------------------------------------------------
    -- |
    -- Module       : XMonad.Actions.MessageFeedback
    -- Copyright    : (c) Quentin Moser <quentin.moser@unifr.ch>
    -- License      : BSD3
    --
    -- Maintainer   : None
    -- Stability    : unstable
    -- Portability  : unportable
    --
    -- Alternative to 'XMonad.Operations.sendMessage' that provides knowledge
    -- of whether the message was handled, and utility functions based on
    -- this facility.
    -----------------------------------------------------------------------------

    module XMonad.Actions.MessageFeedback (
                                          -- * Usage
                                          -- $usage

                                            send
                                          , tryMessage
                                          , tryMessage_
                                          , tryInOrder
                                          , tryInOrder_
                                          , sm
                                          , sendSM
                                          , sendSM_
                                          ) where

    import XMonad.Core ( X (), Message, SomeMessage(..), LayoutClass(..), windowset, catchX )
    import XMonad.StackSet ( current, workspace, layout, tag )
    import XMonad.Operations ( updateLayout )

    import Control.Monad.State ( gets )
    import Data.Maybe ( isJust )
    import Control.Applicative ((<$>))

    -- $usage
    -- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
    --
    -- > import XMonad.Actions.MessageFeedback
    --
    -- You can then use this module's functions wherever an action is expected.
    --
    -- Note that most functions in this module have a return type of @X Bool@
    -- whereas configuration options will expect a @X ()@ action.
    -- For example, the key binding
    --
    -- > -- Shrink the master area of a tiled layout, or move the focused window
    -- > -- to the left in a WindowArranger-based layout
    -- > ((modKey, xK_Left), tryMessage Shrink (MoveLeft 50))
    --
    -- is mis-typed. For this reason, this module provides alternatives (ending with
    -- an underscore, e.g. tryMessage_) that discard their result and return an @X ()@.
    -- For example, to correct the previous example:
    --
    -- > ((modKey, xK_Left), tryMessage_ Shrink (MoveLeft 50))
    --

    -- | Behaves like 'XMonad.Operations.sendMessage', but returns True of the
    -- message was handled by the layout, False otherwise.
    send :: Message a => a -> X Bool
    send = sendSM . sm

    -- | Sends the first message, and if it was not handled, sends the second.
    -- Returns True if either message was handled, False otherwise.
    tryMessage :: (Message a, Message b) => a -> b -> X Bool
    tryMessage m1 m2 = do b <- send m1
                          if b then return True else send m2

    tryMessage_ :: (Message a, Message b) => a -> b -> X ()
    tryMessage_ m1 m2 = tryMessage m1 m2 >> return ()

    -- | Tries sending every message of the list in order until one of them
    -- is handled. Returns True if one of the messages was handled, False otherwise.
    tryInOrder :: [SomeMessage] -> X Bool
    tryInOrder [] = return False
    tryInOrder (m:ms) = do b <- sendSM m
                           if b then return True else tryInOrder ms

    tryInOrder_ :: [SomeMessage] -> X ()
    tryInOrder_ ms = tryInOrder ms >> return ()

    -- | Convenience shorthand for 'XMonad.Core.SomeMessage'.
    sm :: Message a => a -> SomeMessage
    sm = SomeMessage

    sendSM :: SomeMessage -> X Bool
    sendSM m = do w <- workspace . current <$> gets windowset
                  ml' <- handleMessage (layout w) m `catchX` return Nothing
                  updateLayout (tag w) ml'
                  return $ isJust ml'

    sendSM_ :: SomeMessage -> X ()
    sendSM_ m = sendSM m >> return ()