{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, DeriveDataTypeable #-}
-----------------------------------------------------------------------------
-- |
-- Module : XMonad.Layout.MessageControl
-- Copyright : (c) 2008 Quentin Moser
-- License : BSD3
--
-- Maintainer : <quentin.moser@unifr.ch>
-- Stability : unstable
-- Portability : unportable
--
-- Provides message \"escaping\" and filtering facilities which
-- help control complex nested layouts.
-----------------------------------------------------------------------------
module XMonad.Layout.MessageControl (
-- * Usage
-- $usage
Ignore()
, ignore
, UnEscape()
, unEscape
, EscapedMessage(Escape)
, escape
) where
import XMonad.Core (Message, SomeMessage(..)
, fromMessage, LayoutClass(..))
import XMonad.StackSet (Workspace(..))
import XMonad.Layout.LayoutModifier (LayoutModifier(..), ModifiedLayout(..))
import Data.Typeable (Typeable)
import Control.Applicative ((<$>))
import Control.Arrow (second)
-- $usage
-- You can use this module by importing it into your @~\/.xmonad\/xmonad.hs@ file:
--
-- > import XMonad.Layout.MessageEscape
--
-- Then, if you use a modified layout where the modifier would intercept
-- a message, but you'd want to be able to send it to the inner layout
-- only, add the 'unEscape' modifier to the inner layout like so:
--
-- > import XMonad.Layout.Master (mastered)
-- > import XMonad.Layout.Tabbed (simpleTabbed)
-- > import XMonad.Layout.LayoutCombinators ((|||))
-- >
-- > myLayout = Tall ||| unEscape (mastered 0.01 0.5 $ Full ||| simpleTabbed)
--
-- you can now send a message to the inner layout with
-- @sendMessage $ escape message@, e.g.
--
-- > -- Change the inner layout
-- > ((modm .|. controlMask, xK_space), sendMessage $ escape NextLayout)
--
-- If you want unescaped messages to be handled /only/ by the enclosing
-- layout, use the 'ignore' modifier:
--
-- > myLayout = Tall ||| (ignore NextLayout $ ignore (JumpToLayout "") $
-- > unEscape $ mastered 0.01 0.5
-- > $ Full ||| simpleTabbed)
--
-- /IMPORTANT NOTE:/ The standard '(|||)' operator from "XMonad.Layout"
-- does not behave correctly with 'ignore'. Make sure you use the one
-- from "XMonad.Layout.LayoutCombinators".
-- | the Ignore layout modifier. Prevents its inner layout from receiving
-- messages of a certain type.
data Ignore m l w = I (l w)
deriving (Show, Read)
instance (Message m, LayoutClass l w) => LayoutClass (Ignore m l) w where
runLayout ws r = second (I <$>) <$> runLayout (unILayout ws) r
where unILayout :: Workspace i (Ignore m l w) w -> Workspace i (l w) w
unILayout w@(Workspace { layout = (I l) }) = w { layout = l }
handleMessage l@(I l') sm
= case fromMessageAs sm l of
Just _ -> return Nothing
Nothing -> (I <$>) <$> handleMessage l' sm
where fromMessageAs :: Message m' => SomeMessage -> Ignore m' l w -> Maybe m'
fromMessageAs a _ = fromMessage a
description (I l) = "Ignore "++description l
-- | the UnEscape layout modifier. Listens to 'EscapedMessage's and sends
-- their nested message to the inner layout.
data UnEscape w = UE
deriving (Show, Read)
instance LayoutModifier UnEscape a where
handleMessOrMaybeModifyIt _ mess
= return $ case fromMessage mess of
Just (Escape mess') -> Just $ Right mess'
Nothing -> Nothing
-- | Data type for an escaped message. Send with 'escape'.
newtype EscapedMessage = Escape SomeMessage
deriving Typeable
instance Message EscapedMessage
-- | Creates an 'EscapedMessage'.
escape :: Message m => m -> EscapedMessage
escape = Escape . SomeMessage
-- | Applies the UnEscape layout modifier to a layout.
unEscape :: LayoutClass l w => l w -> ModifiedLayout UnEscape l w
unEscape l = ModifiedLayout UE l
-- | Applies the Ignore layout modifier to a layout, blocking
-- all messages of the same type as the one passed as its first argument.
ignore :: (Message m, LayoutClass l w)
=> m -> l w -> (Ignore m l w)
ignore _ l = I l