content: ----------------------------------------------------------------------------
  -- |
  -- Module      :  XMonad.Hooks.PositionStoreHooks
  -- Copyright   :  (c) Jan Vornberger 2009
  -- License     :  BSD3-style (see LICENSE)
  --
  -- Maintainer  :  jan.vornberger@informatik.uni-oldenburg.de
  -- Stability   :  unstable
  -- Portability :  not portable
  --
  -- This module contains two hooks for the
  -- PositionStore (see "XMonad.Util.PositionStore") - a ManageHook and
  -- an EventHook.
  --
  -- The ManageHook can be used to fill the PositionStore with position and size
  -- information about new windows. The advantage of using this hook is, that the
  -- information is recorded independent of the currently active layout. So the
  -- floating shape of the window can later be restored even if it was opened in a
  -- tiled layout initially.
  --
  -- For windows, that do not request a particular position, a random position will
  -- be assigned. This prevents windows from piling up exactly on top of each other.
  --
  -- The EventHook makes sure that windows are deleted from the PositionStore
  -- when they are closed.
  --
  -----------------------------------------------------------------------------
  
  module XMonad.Hooks.PositionStoreHooks (
      -- * Usage
      -- $usage
      positionStoreManageHook,
      positionStoreEventHook
      ) where
  
  import XMonad
  import qualified XMonad.StackSet as W
  import XMonad.Util.PositionStore
  import XMonad.Hooks.ManageDocks
  import XMonad.Layout.Decoration
  
  import System.Random(randomRIO)
  import Control.Applicative((<$>))
  import Control.Monad(when)
  import Data.Maybe
  import Data.Monoid
  import qualified Data.Set as S
  
  -- $usage
  -- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
  --
  -- > import XMonad.Hooks.PositionStoreHooks
  --
  -- and adding 'positionStoreManageHook' to your 'ManageHook' as well
  -- as 'positionStoreEventHook' to your event hooks. To be accurate
  -- about window sizes, the module needs to know if any decoration is in effect.
  -- This is specified with the first argument: Supply 'Nothing' for no decoration,
  -- otherwise use 'Just def' or similar to inform the module about the
  -- decoration theme used.
  --
  -- > myManageHook = positionStoreManageHook Nothing <+> manageHook def
  -- > myHandleEventHook = positionStoreEventHook
  -- >
  -- > main = xmonad def { manageHook = myManageHook
  -- >                   , handleEventHook = myHandleEventHook
  -- >                   }
  --
  
  positionStoreManageHook :: Maybe Theme -> ManageHook
  positionStoreManageHook mDecoTheme = ask >>= liftX . positionStoreInit mDecoTheme >> idHook
  
  positionStoreInit :: Maybe Theme -> Window -> X ()
  positionStoreInit mDecoTheme w  = withDisplay $ \d -> do
          let decoH = maybe 0 decoHeight mDecoTheme   -- take decoration into account, which - in its current
                                                      -- form - makes windows smaller to make room for it
          wa <- io $ getWindowAttributes d w
          ws <- gets windowset
          arbitraryOffsetX <- randomIntOffset
          arbitraryOffsetY <- randomIntOffset
          if (wa_x wa == 0) && (wa_y wa == 0)
              then do
                  let sr@(Rectangle srX srY _ _) = screenRect . W.screenDetail . W.current $ ws
                  modifyPosStore (\ps -> posStoreInsert ps w
                                          (Rectangle (srX + fi arbitraryOffsetX)
                                                     (srY + fi arbitraryOffsetY)
                                                      (fi $ wa_width wa)
                                                      (decoH + fi (wa_height wa))) sr )
              else do
                  sc <- fromMaybe (W.current ws) <$> pointScreen (fi $ wa_x wa) (fi $ wa_y wa)
                  let sr = screenRect . W.screenDetail $ sc
                  sr' <- fmap ($ sr) (calcGap $ S.fromList [minBound .. maxBound])    -- take docks into account, accepting
                                                                                      -- a somewhat unfortunate inter-dependency
                                                                                      -- with 'XMonad.Hooks.ManageDocks'
                  modifyPosStore (\ps -> posStoreInsert ps w
                                          (Rectangle (fi $ wa_x wa) (fi (wa_y wa) - fi decoH)
                                              (fi $ wa_width wa) (decoH + fi (wa_height wa))) sr' )
      where
          randomIntOffset :: X (Int)
          randomIntOffset = io $ randomRIO (42, 242)
  
  positionStoreEventHook :: Event -> X All
  positionStoreEventHook (DestroyWindowEvent {ev_window = w, ev_event_type = et}) = do
      when (et == destroyNotify) $ do
          modifyPosStore (\ps -> posStoreRemove ps w)
      return (All True)
  positionStoreEventHook _ = return (All True)
  