    {-# OPTIONS_GHC -fglasgow-exts #-} -- For deriving Data/Typeable

    -----------------------------------------------------------------------------
    -- |
    -- Module      :  XMonad.Layout.BoringWindows
    -- Copyright   :  (c) 2008  David Roundy <droundy@darcs.net>
    -- License     :  BSD3-style (see LICENSE)
    --
    -- Maintainer  :  none
    -- Stability   :  unstable
    -- Portability :  unportable
    --
    -- BoringWindows is an extension to allow windows to be marked boring
    --
    -----------------------------------------------------------------------------

    module XMonad.Layout.BoringWindows (
                                       -- * Usage
                                       -- $usage
                                       boringWindows,
                                       markBoring, clearBoring,
                                       focusUp, focusDown
                                      ) where

    import XMonad hiding (Point)
    import qualified XMonad.StackSet as W
    import XMonad.Layout.LayoutModifier
    import XMonad.Util.Invisible

    data BoringMessage = FocusUp | FocusDown | IsBoring Window | ClearBoring
                           deriving ( Read, Show, Typeable )
    instance Message BoringMessage

    markBoring, clearBoring, focusUp, focusDown :: X ()
    markBoring = withFocused (sendMessage . IsBoring)
    clearBoring = sendMessage ClearBoring
    focusUp = sendMessage FocusUp
    focusDown = sendMessage FocusDown

    data BoringWindows a = BoringWindows (Invisible [] a) deriving ( Show, Read, Typeable )

    boringWindows :: (LayoutClass l a, Eq a) => l a -> ModifiedLayout BoringWindows l a
    boringWindows = ModifiedLayout (BoringWindows (I []))

    instance LayoutModifier BoringWindows Window where
        handleMessOrMaybeModifyIt (BoringWindows (I bs)) m
            | Just (IsBoring b) <- fromMessage m = return $ Just $ Left $ BoringWindows (I (b:bs))
            | Just ClearBoring <- fromMessage m = return $ Just $ Left $ BoringWindows (I [])
            | Just FocusUp <- fromMessage m = do windows $ W.modify' $ focusUp'
                                                 return Nothing
            | Just FocusDown <- fromMessage m =
                                do windows $ W.modify' (reverseStack . focusUp' . reverseStack)
                                   return Nothing
            where focusUp' (W.Stack t ls rs)
                      | (a,l:ls') <- skipBoring ls = W.Stack l ls' (a++t:rs)
                      | otherwise = case skipBoring (reverse (t:rs)++ls) of
                                    (a,x:xs) -> W.Stack x xs a
                                    _ -> W.Stack t ls rs
                  skipBoring [] = ([],[])
                  skipBoring (x:xs) | x `elem` bs = case skipBoring xs of
                                                    (a,b) -> (x:a,b)
                                    | otherwise = ([],x:xs)
        handleMessOrMaybeModifyIt _ _ = return Nothing

    -- | reverse a stack: up becomes down and down becomes up.
    reverseStack :: W.Stack a -> W.Stack a
    reverseStack (W.Stack t ls rs) = W.Stack t rs ls