    {-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, TypeSynonymInstances #-}
    -----------------------------------------------------------------------------
    -- |
    -- Module      :  XMonad.Layout.Column
    -- Copyright   :  (c) 2009 Ilya Portnov
    -- License     :  BSD3-style (see LICENSE)
    --
    -- Maintainer  :  Ilya Portnov <portnov84@rambler.ru>
    -- Stability   :  unstable
    -- Portability :  unportable
    --
    -- Provides Column layout that places all windows in one column. Windows
    -- heights are calculated from equation: H1/H2 = H2/H3 = ... = q, where q is
    -- given. With Shrink/Expand messages you can change the q value.
    --
    -----------------------------------------------------------------------------

    module XMonad.Layout.Column (
                                 -- * Usage
                                 -- $usage
                                 Column (..)
                                ) where
    import XMonad
    import qualified XMonad.StackSet as W

    -- $usage
    -- This module defines layot named Column. It places all windows in one
    -- column. Windows heights are calculated from equation: H1/H2 = H2/H3 = ... =
    -- q, where `q' is given (thus, windows heights are members of geometric
    -- progression). With Shrink/Expand messages one can change the `q' value.
    --
    -- You can use this module by adding folowing in your @xmonad.hs@:
    --
    -- > import XMonad.Layout.Column
    --
    -- Then add layouts to your layoutHook:
    --
    -- > myLayoutHook = Column 1.6 ||| ...
    --
    -- In this example, each next window will have height 1.6 times less then
    -- previous window.

    data Column a = Column Float deriving (Read,Show)

    instance LayoutClass Column a where
        pureLayout = columnLayout
        pureMessage = columnMessage

    columnMessage :: Column a -> SomeMessage -> Maybe (Column a)
    columnMessage (Column q) m = fmap resize (fromMessage m)
        where resize Shrink = Column (q-0.1)
              resize Expand = Column (q+0.1)

    columnLayout :: Column a -> Rectangle -> W.Stack a -> [(a,Rectangle)]
    columnLayout (Column q) rect stack = zip ws rects
        where ws = W.integrate stack
              n = length ws
              heights = map (xn n rect q) [1..n]
              ys = [fromIntegral $ sum $ take k heights | k <- [0..n-1]]
              rects = map (mkRect rect) $ zip heights ys

    mkRect :: Rectangle -> (Dimension,Position) -> Rectangle
    mkRect (Rectangle xs ys ws _) (h,y) = Rectangle xs (ys+fromIntegral y) ws h

    xn :: Int -> Rectangle -> Float -> Int -> Dimension
    xn n (Rectangle _ _ _ h) q k = if q==1 then
                                      h `div` (fromIntegral n)
                                   else
                                      round ((fromIntegral h)*q^(n-k)*(1-q)/(1-q^n))