    -----------------------------------------------------------------------------
    -- |
    -- Module      :  XMonad.Hooks.InsertPosition
    -- Copyright   :  (c) 2009 Adam Vogt
    -- License     :  BSD-style (see xmonad/LICENSE)
    --
    -- Maintainer  :  vogt.adam@gmail.com
    -- Stability   :  unstable
    -- Portability :  portable
    --
    -- Configure where new windows should be added and which window should be
    -- focused.
    --
    -----------------------------------------------------------------------------

    module XMonad.Hooks.InsertPosition (
        -- * Usage
        -- $usage
        insertPosition
        ,Focus(..), Position(..)
        ) where

    import XMonad(ManageHook, MonadReader(ask))
    import qualified XMonad.StackSet as W
    import Control.Applicative((<$>))
    import Data.Maybe(fromMaybe)
    import Data.List(find)
    import Data.Monoid(Endo(Endo))

    -- $usage
    -- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
    --
    -- > import XMonad.Hooks.InsertPosition
    -- > xmonad defaultConfig { manageHook = insertPosition Master Newer <+> myManageHook }
    --
    -- You should you put the manageHooks that use 'doShift' to take effect
    -- /before/ 'insertPosition', so that the window order will be consistent.
    -- Because ManageHooks compose from right to left (like function composition
    -- '.'), this means that 'insertPosition' should be the leftmost ManageHook.

    data Position = Master | End | Above | Below
    data Focus = Newer | Older

    -- | insertPosition. A manage hook for placing new windows. XMonad's default is
    -- the same as using: @insertPosition Above Newer@.
    insertPosition :: Position -> Focus -> ManageHook
    insertPosition pos foc = Endo . g <$> ask
      where
        g w = viewingWs w (updateFocus w . ins w . W.delete w)
        ins w = (\f ws -> fromMaybe id (W.focusWindow <$> W.peek ws) $ f ws) $
            case pos of
                Master -> W.insertUp w . W.focusMaster
                End    -> insertDown w . W.modify' focusLast'
                Above  -> W.insertUp w
                Below  -> insertDown w
        updateFocus =
            case foc of
                Older -> const id
                Newer -> W.focusWindow

    -- | Modify the StackSet when the workspace containing w is focused
    viewingWs :: (Eq a, Eq s, Eq i, Show i) =>a-> (W.StackSet i l a s sd -> W.StackSet i l a s sd)-> W.StackSet i l a s sd-> W.StackSet i l a s sd
    viewingWs w f = do
        i <- W.tag . W.workspace . W.current
        ws <- find (elem w . W.integrate' . W.stack) . W.workspaces
        maybe id (fmap (W.view i . f) . W.view . W.tag) ws

    -- | 'insertDown' and 'focusLast' belong in XMonad.StackSet?
    insertDown :: (Eq a) => a -> W.StackSet i l a s sd -> W.StackSet i l a s sd
    insertDown w = W.swapDown . W.insertUp w

    focusLast' ::  W.Stack a -> W.Stack a
    focusLast' st = let ws = W.integrate st
        in W.Stack (last ws) (tail $ reverse ws) []