content: {-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, DeriveDataTypeable, TypeSynonymInstances, PatternGuards #-}
  
  -----------------------------------------------------------------------------
  -- |
  -- Module      :  XMonad.Layout.Gaps
  -- Copyright   :  (c) 2008 Brent Yorgey
  -- License     :  BSD3
  --
  -- Maintainer  :  <byorgey@gmail.com>
  -- Stability   :  unstable
  -- Portability :  unportable
  --
  -- Create manually-sized gaps along edges of the screen which will not
  -- be used for tiling, along with support for toggling gaps on and
  -- off.
  --
  -- Note that "XMonad.Hooks.ManageDocks" is the preferred solution for
  -- leaving space for your dock-type applications (status bars,
  -- toolbars, docks, etc.), since it automatically sets up appropriate
  -- gaps, allows them to be toggled, etc.  However, this module may
  -- still be useful in some situations where the automated approach of
  -- ManageDocks does not work; for example, to work with a dock-type
  -- application that does not properly set the STRUTS property, or to
  -- leave part of the screen blank which is truncated by a projector,
  -- and so on.
  -----------------------------------------------------------------------------
  
  module XMonad.Layout.Gaps (
                                 -- * Usage
                                 -- $usage
                            Direction2D(..), Gaps,
                            GapSpec, gaps, gaps', GapMessage(..)
  
                            ) where
  
  import XMonad.Core
  import Graphics.X11 (Rectangle(..))
  
  import XMonad.Layout.LayoutModifier
  import XMonad.Util.Types (Direction2D(..))
  import XMonad.Util.XUtils (fi)
  
  import Data.List (delete)
  
  -- $usage
  -- You can use this module by importing it into your @~\/.xmonad\/xmonad.hs@ file:
  --
  -- > import XMonad.Layout.Gaps
  --
  -- and applying the 'gaps' modifier to your layouts as follows (for
  -- example):
  --
  -- > layoutHook = gaps [(U,18), (R,23)] $ Tall 1 (3/100) (1/2) ||| Full  -- leave gaps at the top and right
  --
  -- You can additionally add some keybindings to toggle or modify the gaps,
  -- for example:
  --
  -- > , ((modm .|. controlMask, xK_g), sendMessage $ ToggleGaps)  -- toggle all gaps
  -- > , ((modm .|. controlMask, xK_t), sendMessage $ ToggleGap U) -- toggle the top gap
  -- > , ((modm .|. controlMask, xK_w), sendMessage $ IncGap 5 R)  -- increment the right-hand gap
  -- > , ((modm .|. controlMask, xK_q), sendMessage $ DecGap 5 R)  -- decrement the right-hand gap
  --
  -- If you want complete control over all gaps, you could include
  -- something like this in your keybindings, assuming in this case you
  -- are using 'XMonad.Util.EZConfig.mkKeymap' or
  -- 'XMonad.Util.EZConfig.additionalKeysP' from "XMonad.Util.EZConfig"
  -- for string keybinding specifications:
  --
  -- > ++
  -- > [ ("M-g " ++ f ++ " " ++ k, sendMessage $ m d)
  -- >     | (k, d) <- [("a",L), ("s",D), ("w",U), ("d",R)]
  -- >     , (f, m) <- [("v", ToggleGap), ("h", IncGap 10), ("f", DecGap 10)]
  -- > ]
  --
  -- Given the above keybinding definition, for example, you could type
  -- @M-g, v, a@ to toggle the top gap.
  --
  -- To configure gaps differently per-screen, use
  -- "XMonad.Layout.PerScreen" (coming soon).
  
  -- | A manual gap configuration.  Each side of the screen on which a
  --   gap is enabled is paired with a size in pixels.
  type GapSpec = [(Direction2D,Int)]
  
  -- | The gap state.  The first component is the configuration (which
  --   gaps are allowed, and their current size), the second is the gaps
  --   which are currently active.
  data Gaps a = Gaps GapSpec [Direction2D]
    deriving (Show, Read)
  
  -- | Messages which can be sent to a gap modifier.
  data GapMessage = ToggleGaps              -- ^ Toggle all gaps.
                  | ToggleGap  !Direction2D    -- ^ Toggle a single gap.
                  | IncGap !Int !Direction2D    -- ^ Increase a gap by a certain number of pixels.
                  | DecGap !Int !Direction2D    -- ^ Decrease a gap.
    deriving (Typeable)
  
  instance Message GapMessage
  
  instance LayoutModifier Gaps a where
      modifyLayout g w r = runLayout w (applyGaps g r)
  
      pureMess (Gaps conf cur) m
        | Just ToggleGaps    <- fromMessage m
          = Just $ Gaps conf (toggleGaps conf cur)
        | Just (ToggleGap d) <- fromMessage m
          = Just $ Gaps conf (toggleGap conf cur d)
        | Just (IncGap i d)  <- fromMessage m
          = Just $ Gaps (incGap conf d i) cur
        | Just (DecGap i d)  <- fromMessage m
          = Just $ Gaps (incGap conf d (-i)) cur
        | otherwise = Nothing
  
  applyGaps :: Gaps a -> Rectangle -> Rectangle
  applyGaps gs r = foldr applyGap r (activeGaps gs)
    where
      applyGap (U,z) (Rectangle x y w h) = Rectangle x (y + fi z) w (h - fi z)
      applyGap (D,z) (Rectangle x y w h) = Rectangle x y w (h - fi z)
      applyGap (L,z) (Rectangle x y w h) = Rectangle (x + fi z) y (w - fi z) h
      applyGap (R,z) (Rectangle x y w h) = Rectangle x y (w - fi z) h
  
  activeGaps :: Gaps a -> GapSpec
  activeGaps (Gaps conf cur) = filter ((`elem` cur) . fst) conf
  
  toggleGaps :: GapSpec -> [Direction2D] -> [Direction2D]
  toggleGaps conf [] = map fst conf
  toggleGaps _    _  = []
  
  toggleGap :: GapSpec -> [Direction2D] -> Direction2D -> [Direction2D]
  toggleGap conf cur d | d `elem` cur            = delete d cur
                       | d `elem` (map fst conf) = d:cur
                       | otherwise               = cur
  
  incGap :: GapSpec -> Direction2D -> Int -> GapSpec
  incGap gs d i = map (\(dir,j) -> if dir == d then (dir,max (j+i) 0) else (dir,j)) gs
  
  -- | Add togglable manual gaps to a layout.
  gaps :: GapSpec   -- ^ The gaps to allow, paired with their initial sizes.
       -> l a       -- ^ The layout to modify.
       -> ModifiedLayout Gaps l a
  gaps g = ModifiedLayout (Gaps g (map fst g))
  
  -- | Add togglable manual gaps to a layout, explicitly specifying the initial states.
  gaps' :: [((Direction2D,Int),Bool)] -- ^ The gaps to allow and their initial states.
        -> l a                        -- ^ The layout to modify.
        -> ModifiedLayout Gaps l a
  gaps' g = ModifiedLayout (Gaps (map fst g) [d | ((d,_),v) <- g, v])
  