content: {-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, TypeSynonymInstances #-}
  
  -----------------------------------------------------------------------------
  -- |
  -- Module       : XMonad.Layout.MagicFocus
  -- Copyright    : (c) Peter De Wachter <pdewacht@gmail.com>
  -- License      : BSD
  --
  -- Maintainer   : Peter De Wachter <pdewacht@gmail.com>
  -- Stability    : unstable
  -- Portability  : unportable
  --
  -- Automagically put the focused window in the master area.
  -----------------------------------------------------------------------------
  
  module XMonad.Layout.MagicFocus
      (-- * Usage
       -- $usage
       magicFocus,
       promoteWarp,
       promoteWarp',
       followOnlyIf,
       disableFollowOnWS,
       MagicFocus,
      ) where
  
  import XMonad
  import qualified XMonad.StackSet as W
  import XMonad.Layout.LayoutModifier
  
  import XMonad.Actions.UpdatePointer (updatePointer)
  import Data.Monoid(All(..))
  import qualified Data.Map as M
  
  -- $usage
  -- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
  --
  -- > import XMonad.Layout.MagicFocus
  --
  -- Then edit your @layoutHook@ by adding the magicFocus layout
  -- modifier:
  --
  -- > myLayout = magicFocus (Tall 1 (3/100) (1/2)) ||| Full ||| etc..
  -- > main = xmonad def { layoutHook = myLayout,
  -- >                     handleEventHook = promoteWarp }
  --
  -- For more detailed instructions on editing the layoutHook see:
  --
  -- "XMonad.Doc.Extending#Editing_the_layout_hook"
  
  -- | Create a new layout which automagically puts the focused window
  --   in the master area.
  magicFocus :: l a -> ModifiedLayout MagicFocus l a
  magicFocus = ModifiedLayout MagicFocus
  
  data MagicFocus a = MagicFocus deriving (Show, Read)
  
  instance LayoutModifier MagicFocus Window where
    modifyLayout MagicFocus (W.Workspace i l s) r =
      withWindowSet $ \wset ->
        runLayout (W.Workspace i l (s >>= \st -> Just $ swap st (W.peek wset))) r
  
  swap :: (Eq a) => W.Stack a -> Maybe a -> W.Stack a
  swap (W.Stack f u d) focused
      | Just f == focused = W.Stack f [] (reverse u ++ d)
      | otherwise         = W.Stack f u d
  
  -- | An eventHook that overrides the normal focusFollowsMouse. When the mouse
  -- it moved to another window, that window is replaced as the master, and the
  -- mouse is warped to inside the new master.
  --
  -- It prevents infinite loops when focusFollowsMouse is true (the default), and
  -- MagicFocus is in use when changing focus with the mouse.
  --
  -- This eventHook does nothing when there are floating windows on the current
  -- workspace.
  promoteWarp :: Event -> X All
  promoteWarp = promoteWarp' (0.5, 0.5) (0.85, 0.85)
  
  -- | promoteWarp' allows you to specify an arbitrary pair of arguments to
  -- pass to 'updatePointer' when the mouse enters another window.
  promoteWarp' :: (Rational, Rational) -> (Rational, Rational) -> Event -> X All
  promoteWarp' refPos ratio e@(CrossingEvent {ev_window = w, ev_event_type = t})
      | t == enterNotify && ev_mode   e == notifyNormal = do
          ws <- gets windowset
          let foc = W.peek ws
              st = W.integrate' . W.stack . W.workspace $ W.current ws
              wsFloats = M.filterWithKey (\k _ -> k `elem` st) $ W.floating ws
          if Just w /= foc && M.null wsFloats then do
              windows (W.swapMaster . W.focusWindow w)
              updatePointer refPos ratio
              return $ All False
            else return $ All True
  promoteWarp' _ _ _ = return $ All True
  
  -- | Another event hook to override the focusFollowsMouse and make the pointer
  -- only follow if a given condition is satisfied. This could be used to disable
  -- focusFollowsMouse only for given workspaces or layouts.
  -- Beware that your focusFollowsMouse setting is ignored if you use this event hook.
  followOnlyIf :: X Bool -> Event -> X All
  followOnlyIf cond e@(CrossingEvent {ev_window = w, ev_event_type = t})
      | t == enterNotify && ev_mode e == notifyNormal
      = whenX cond (focus w) >> return (All False)
  followOnlyIf _ _ = return $ All True
  
  -- | Disables focusFollow on the given workspaces:
  disableFollowOnWS :: [WorkspaceId] -> X Bool
  disableFollowOnWS wses = (`notElem` wses) `fmap` gets (W.currentTag . windowset)
  