    {-# 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