content: -----------------------------------------------------------------------------
  -- |
  -- 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 def { 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) []
  