content: {-# LANGUAGE DeriveDataTypeable #-}
  
  ----------------------------------------------------------------------------
  -- |
  -- Module      :  XMonad.Util.PositionStore
  -- Copyright   :  (c) Jan Vornberger 2009
  -- License     :  BSD3-style (see LICENSE)
  --
  -- Maintainer  :  jan.vornberger@informatik.uni-oldenburg.de
  -- Stability   :  unstable
  -- Portability :  not portable
  --
  -- A utility module to store information about position and size of a window.
  -- See "XMonad.Layout.PositionStoreFloat" for a layout that makes use of this.
  --
  -----------------------------------------------------------------------------
  
  module XMonad.Util.PositionStore (
          getPosStore,
          modifyPosStore,
  
          posStoreInsert,
          posStoreMove,
          posStoreQuery,
          posStoreRemove,
          PositionStore,
      ) where
  
  import XMonad
  import qualified XMonad.Util.ExtensibleState as XS
  import qualified Data.Map as M
  
  -- Store window positions relative to the upper left screen edge
  -- and windows sizes as well as positions as fractions of the screen size.
  -- This way windows can be easily relocated and scaled when switching screens.
  
  data PositionStore = PS (M.Map Window PosStoreRectangle)
                              deriving (Read,Show,Typeable)
  data PosStoreRectangle = PSRectangle Double Double Double Double
                              deriving (Read,Show,Typeable)
  
  instance ExtensionClass PositionStore where
    initialValue = PS M.empty
    extensionType = PersistentExtension
  
  getPosStore :: X (PositionStore)
  getPosStore = XS.get
  
  modifyPosStore :: (PositionStore -> PositionStore) -> X ()
  modifyPosStore = XS.modify
  
  posStoreInsert :: PositionStore -> Window -> Rectangle -> Rectangle -> PositionStore
  posStoreInsert (PS posStoreMap) w (Rectangle x y wh ht) (Rectangle srX srY srWh srHt) =
      let offsetX = x - srX
          offsetY = y - srY
      in PS $ M.insert w (PSRectangle (fromIntegral offsetX / fromIntegral srWh)
                                                 (fromIntegral offsetY / fromIntegral srHt)
                                                 (fromIntegral wh / fromIntegral srWh)
                                                 (fromIntegral ht / fromIntegral srHt)) posStoreMap
  
  posStoreRemove :: PositionStore -> Window -> PositionStore
  posStoreRemove (PS posStoreMap) w = PS $ M.delete w posStoreMap
  
  posStoreQuery :: PositionStore -> Window -> Rectangle -> Maybe Rectangle
  posStoreQuery (PS posStoreMap) w (Rectangle srX srY srWh srHt) = do
      (PSRectangle x y wh ht) <- M.lookup w posStoreMap
      let realWh = fromIntegral srWh * wh
          realHt = fromIntegral srHt * ht
          realOffsetX = fromIntegral srWh * x
          realOffsetY = fromIntegral srHt * y
      return (Rectangle (srX + round realOffsetX) (srY + round realOffsetY)
                          (round realWh) (round realHt))
  
  posStoreMove :: PositionStore -> Window -> Position -> Position -> Rectangle -> Rectangle -> PositionStore
  posStoreMove posStore w x y oldSr newSr =
      case (posStoreQuery posStore w oldSr) of
          Nothing -> posStore     -- not in store, can't move -> do nothing
          Just (Rectangle _ _ wh ht) -> posStoreInsert posStore w (Rectangle x y wh ht) newSr
  