content: {-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, TypeSynonymInstances #-}
  -----------------------------------------------------------------------------
  -- |
  -- Module      :  XMonad.Layout.OneBig
  -- Copyright   :  (c) 2009 Ilya Portnov
  -- License     :  BSD3-style (see LICENSE)
  --
  -- Maintainer  :  Ilya Portnov <portnov84@rambler.ru>
  -- Stability   :  unstable
  -- Portability :  unportable
  --
  -- Provides layout named OneBig. It places one (master) window at top left corner of screen, and other (slave) windows at top
  --
  -----------------------------------------------------------------------------
  
  module XMonad.Layout.OneBig (
                               -- * Usage
                               -- $usage
                               OneBig (..)
                              ) where
  import XMonad
  import qualified XMonad.StackSet as W
  
  -- $usage
  -- This module defines layout named OneBig. It places one (master)
  -- window at top left, and other (slave) windows at right and at
  -- bottom of master. It tries to give equal space for each slave
  -- window.
  --
  -- You can use this module by adding following in your @xmonad.hs@:
  --
  -- > import XMonad.Layout.OneBig
  --
  -- Then add layouts to your layoutHook:
  --
  -- > myLayoutHook = OneBig (3/4) (3/4) ||| ...
  --
  -- In this example, master window will occupy 3/4 of screen width and
  -- 3/4 of screen height.
  
  -- | Data type for layout
  data OneBig a = OneBig Float Float deriving (Read,Show)
  
  instance LayoutClass OneBig a where
    pureLayout = oneBigLayout
    pureMessage = oneBigMessage
  
  -- | Processes Shrink/Expand messages
  oneBigMessage :: OneBig a -> SomeMessage -> Maybe (OneBig a)
  oneBigMessage (OneBig cx cy) m = fmap resize (fromMessage m)
      where resize Shrink = OneBig (cx-delta) (cy-delta)
            resize Expand = OneBig (cx+delta) (cy+delta)
            delta = 3/100
  
  -- | Main layout function
  oneBigLayout :: OneBig a -> Rectangle -> W.Stack a -> [(a, Rectangle)]
  oneBigLayout (OneBig cx cy) rect stack = [(master,masterRect)]
                                        ++ (divideBottom bottomRect bottomWs)
                                        ++ (divideRight rightRect rightWs)
        where ws = W.integrate stack
              n = length ws
              ht (Rectangle _ _ _ hh) = hh
              wd (Rectangle _ _ ww _) = ww
              h' = round (fromIntegral (ht rect)*cy)
              w = wd rect
              m = calcBottomWs n w h'
              master = head ws
              other  = tail ws
              bottomWs = take m other
              rightWs = drop m other
              masterRect = cmaster n m cx cy rect
              bottomRect = cbottom cy rect
              rightRect  = cright cx cy rect
  
  -- | Calculate how many windows must be placed at bottom
  calcBottomWs :: Int -> Dimension -> Dimension -> Int
  calcBottomWs n w h' = case n of
                          1 -> 0
                          2 -> 1
                          3 -> 2
                          4 -> 2
                          _ -> (fromIntegral w)*(n-1) `div` fromIntegral (h'+(fromIntegral w))
  
  -- | Calculate rectangle for master window
  cmaster:: Int -> Int -> Float -> Float -> Rectangle -> Rectangle
  cmaster n m cx cy (Rectangle x y sw sh) = Rectangle x y w h
      where w = if (n > m+1) then
                  round (fromIntegral sw*cx)
                else
                  sw
            h = if (n > 1) then
                  round (fromIntegral sh*cy)
                else
                  sh
  
  -- | Calculate rectangle for bottom windows
  cbottom:: Float -> Rectangle -> Rectangle
  cbottom cy (Rectangle sx sy sw sh) = Rectangle sx y sw h
      where h = round (fromIntegral sh*(1-cy))
            y = round (fromIntegral sh*cy+(fromIntegral sy))
  
  -- | Calculate rectangle for right windows
  cright:: Float -> Float -> Rectangle -> Rectangle
  cright cx cy (Rectangle sx sy sw sh) = Rectangle x sy w h
      where w = round (fromIntegral sw*(1-cx))
            x = round (fromIntegral sw*cx+(fromIntegral sx))
            h = round (fromIntegral sh*cy)
  
  -- | Divide bottom rectangle between windows
  divideBottom :: Rectangle -> [a] -> [(a, Rectangle)]
  divideBottom (Rectangle x y w h) ws = zip ws rects
      where n = length ws
            oneW = fromIntegral w `div` n
            oneRect = Rectangle x y (fromIntegral oneW) h
            rects = take n $ iterate (shiftR (fromIntegral oneW)) oneRect
  
  -- | Divide right rectangle between windows
  divideRight :: Rectangle -> [a] -> [(a, Rectangle)]
  divideRight (Rectangle x y w h) ws = if (n==0) then [] else zip ws rects
      where n = length ws
            oneH = fromIntegral h `div` n
            oneRect = Rectangle x y w (fromIntegral oneH)
            rects = take n $ iterate (shiftB (fromIntegral oneH)) oneRect
  
  -- | Shift rectangle right
  shiftR :: Position -> Rectangle -> Rectangle
  shiftR s (Rectangle x y w h) = Rectangle (x+s) y w h
  
  -- | Shift rectangle bottom
  shiftB :: Position -> Rectangle -> Rectangle
  shiftB s (Rectangle x y w h) = Rectangle x (y+s) w h
  
  
  