    {-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, TypeSynonymInstances #-}
    -----------------------------------------------------------------------------
    -- |
    -- Module      :  XMonad.Layout.FixedColumn
    -- Copyright   :  (c) 2008 Justin Bogner <mail@justinbogner.com>
    -- License     :  BSD3-style (as xmonad)
    --
    -- Maintainer  :  Justin Bogner <mail@justinbogner.com>
    -- Stability   :  unstable
    -- Portability :  unportable
    --
    -- A layout much like Tall, but using a multiple of a window's minimum
    -- resize amount instead of a percentage of screen to decide where to
    -- split. This is useful when you usually leave a text editor or
    -- terminal in the master pane and like it to be 80 columns wide.
    --
    -----------------------------------------------------------------------------

    module XMonad.Layout.FixedColumn (
        -- * Usage
        -- $usage
            FixedColumn(..)
    ) where

    import Control.Monad (msum)
    import Data.Maybe (fromMaybe)
    import Graphics.X11.Xlib (Window, rect_width)
    import Graphics.X11.Xlib.Extras ( getWMNormalHints
                                    , getWindowAttributes
                                    , sh_base_size
                                    , sh_resize_inc
                                    , wa_border_width)

    import XMonad.Core (X, LayoutClass(..), fromMessage, io, withDisplay)
    import XMonad.Layout (Resize(..), IncMasterN(..), tile)
    import XMonad.StackSet as W

    -- $usage
    -- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
    --
    -- > import XMonad.Layout.FixedColumn
    --
    -- Then edit your @layoutHook@ by adding the FixedColumn layout:
    --
    -- > myLayout = FixedColumn 1 20 80 10 ||| Full ||| etc..
    -- > main = xmonad def { layoutHook = myLayout }
    --
    -- For more detailed instructions on editing the layoutHook see:
    --
    -- "XMonad.Doc.Extending#Editing_the_layout_hook"

    -- | A tiling mode based on preserving a nice fixed width
    --   window. Supports 'Shrink', 'Expand' and 'IncMasterN'.
    data FixedColumn a = FixedColumn !Int -- Number of windows in the master pane
                                     !Int -- Number to increment by when resizing
                                     !Int -- Default width of master pane
                                     !Int -- Column width for normal windows
                            deriving (ReadPrec [FixedColumn a]
    ReadPrec (FixedColumn a)
    Int -> ReadS (FixedColumn a)
    ReadS [FixedColumn a]
    (Int -> ReadS (FixedColumn a))
    -> ReadS [FixedColumn a]
    -> ReadPrec (FixedColumn a)
    -> ReadPrec [FixedColumn a]
    -> Read (FixedColumn a)
    forall a. ReadPrec [FixedColumn a]
    forall a. ReadPrec (FixedColumn a)
    forall a. Int -> ReadS (FixedColumn a)
    forall a. ReadS [FixedColumn a]
    forall a.
    (Int -> ReadS a)
    -> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
    readListPrec :: ReadPrec [FixedColumn a]
    $creadListPrec :: forall a. ReadPrec [FixedColumn a]
    readPrec :: ReadPrec (FixedColumn a)
    $creadPrec :: forall a. ReadPrec (FixedColumn a)
    readList :: ReadS [FixedColumn a]
    $creadList :: forall a. ReadS [FixedColumn a]
    readsPrec :: Int -> ReadS (FixedColumn a)
    $creadsPrec :: forall a. Int -> ReadS (FixedColumn a)
    Read, Int -> FixedColumn a -> ShowS
    [FixedColumn a] -> ShowS
    FixedColumn a -> String
    (Int -> FixedColumn a -> ShowS)
    -> (FixedColumn a -> String)
    -> ([FixedColumn a] -> ShowS)
    -> Show (FixedColumn a)
    forall a. Int -> FixedColumn a -> ShowS
    forall a. [FixedColumn a] -> ShowS
    forall a. FixedColumn a -> String
    forall a.
    (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
    showList :: [FixedColumn a] -> ShowS
    $cshowList :: forall a. [FixedColumn a] -> ShowS
    show :: FixedColumn a -> String
    $cshow :: forall a. FixedColumn a -> String
    showsPrec :: Int -> FixedColumn a -> ShowS
    $cshowsPrec :: forall a. Int -> FixedColumn a -> ShowS
    Show)

    instance LayoutClass FixedColumn Window where
        doLayout :: FixedColumn Window
    -> Rectangle
    -> Stack Window
    -> X ([(Window, Rectangle)], Maybe (FixedColumn Window))
    doLayout (FixedColumn nmaster :: Int
    nmaster _ ncol :: Int
    ncol fallback :: Int
    fallback) r :: Rectangle
    r s :: Stack Window
    s = do
                [Int]
    fws <- (Window -> X Int) -> [Window] -> X [Int]
    forall (t :: * -> *) (m :: * -> *) a b.
    (Traversable t, Monad m) =>
    (a -> m b) -> t a -> m (t b)
    mapM (Int -> Int -> Window -> X Int
    widthCols Int
    fallback Int
    ncol) [Window]
    ws
                let frac :: Rational
    frac = [Int] -> Int
    forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
    maximum (Int -> [Int] -> [Int]
    forall a. Int -> [a] -> [a]
    take Int
    nmaster [Int]
    fws) Int -> Dimension -> Rational
    forall a a a. (Fractional a, Integral a, Integral a) => a -> a -> a
    // Rectangle -> Dimension
    rect_width Rectangle
    r
                    rs :: [Rectangle]
    rs   = Rational -> Rectangle -> Int -> Int -> [Rectangle]
    tile Rational
    frac Rectangle
    r Int
    nmaster ([Window] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length [Window]
    ws)
                ([(Window, Rectangle)], Maybe (FixedColumn Window))
    -> X ([(Window, Rectangle)], Maybe (FixedColumn Window))
    forall (m :: * -> *) a. Monad m => a -> m a
    return (([(Window, Rectangle)], Maybe (FixedColumn Window))
     -> X ([(Window, Rectangle)], Maybe (FixedColumn Window)))
    -> ([(Window, Rectangle)], Maybe (FixedColumn Window))
    -> X ([(Window, Rectangle)], Maybe (FixedColumn Window))
    forall a b. (a -> b) -> a -> b
    $ ([Window] -> [Rectangle] -> [(Window, Rectangle)]
    forall a b. [a] -> [b] -> [(a, b)]
    zip [Window]
    ws [Rectangle]
    rs, Maybe (FixedColumn Window)
    forall a. Maybe a
    Nothing)
            where ws :: [Window]
    ws     = Stack Window -> [Window]
    forall a. Stack a -> [a]
    W.integrate Stack Window
    s
                  x :: a
    x // :: a -> a -> a
    // y :: a
    y = a -> a
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral a
    x a -> a -> a
    forall a. Fractional a => a -> a -> a
    / a -> a
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral a
    y

        pureMessage :: FixedColumn Window -> SomeMessage -> Maybe (FixedColumn Window)
    pureMessage (FixedColumn nmaster :: Int
    nmaster delta :: Int
    delta ncol :: Int
    ncol fallback :: Int
    fallback) m :: SomeMessage
    m =
                [Maybe (FixedColumn Window)] -> Maybe (FixedColumn Window)
    forall (t :: * -> *) (m :: * -> *) a.
    (Foldable t, MonadPlus m) =>
    t (m a) -> m a
    msum [(Resize -> FixedColumn Window)
    -> Maybe Resize -> Maybe (FixedColumn Window)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap Resize -> FixedColumn Window
    forall a. Resize -> FixedColumn a
    resize     (SomeMessage -> Maybe Resize
    forall m. Message m => SomeMessage -> Maybe m
    fromMessage SomeMessage
    m)
                     ,(IncMasterN -> FixedColumn Window)
    -> Maybe IncMasterN -> Maybe (FixedColumn Window)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap IncMasterN -> FixedColumn Window
    forall a. IncMasterN -> FixedColumn a
    incmastern (SomeMessage -> Maybe IncMasterN
    forall m. Message m => SomeMessage -> Maybe m
    fromMessage SomeMessage
    m)]
            where resize :: Resize -> FixedColumn a
    resize Shrink
                      = Int -> Int -> Int -> Int -> FixedColumn a
    forall a. Int -> Int -> Int -> Int -> FixedColumn a
    FixedColumn Int
    nmaster Int
    delta (Int -> Int -> Int
    forall a. Ord a => a -> a -> a
    max 0 (Int -> Int) -> Int -> Int
    forall a b. (a -> b) -> a -> b
    $ Int
    ncol Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    delta) Int
    fallback
                  resize Expand
                      = Int -> Int -> Int -> Int -> FixedColumn a
    forall a. Int -> Int -> Int -> Int -> FixedColumn a
    FixedColumn Int
    nmaster Int
    delta (Int
    ncol Int -> Int -> Int
    forall a. Num a => a -> a -> a
    + Int
    delta) Int
    fallback
                  incmastern :: IncMasterN -> FixedColumn a
    incmastern (IncMasterN d :: Int
    d)
                      = Int -> Int -> Int -> Int -> FixedColumn a
    forall a. Int -> Int -> Int -> Int -> FixedColumn a
    FixedColumn (Int -> Int -> Int
    forall a. Ord a => a -> a -> a
    max 0 (Int
    nmasterInt -> Int -> Int
    forall a. Num a => a -> a -> a
    +Int
    d)) Int
    delta Int
    ncol Int
    fallback

        description :: FixedColumn Window -> String
    description _ = "FixedColumn"

    -- | Determine the width of @w@ given that we would like it to be @n@
    --   columns wide, using @inc@ as a resize increment for windows that
    --   don't have one
    widthCols :: Int -> Int -> Window -> X Int
    widthCols :: Int -> Int -> Window -> X Int
    widthCols inc :: Int
    inc n :: Int
    n w :: Window
    w = (Display -> X Int) -> X Int
    forall a. (Display -> X a) -> X a
    withDisplay ((Display -> X Int) -> X Int) -> (Display -> X Int) -> X Int
    forall a b. (a -> b) -> a -> b
    $ \d :: Display
    d -> IO Int -> X Int
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    io (IO Int -> X Int) -> IO Int -> X Int
    forall a b. (a -> b) -> a -> b
    $ do
        SizeHints
    sh <- Display -> Window -> IO SizeHints
    getWMNormalHints Display
    d Window
    w
        Int
    bw <- (WindowAttributes -> Int) -> IO WindowAttributes -> IO Int
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap (CInt -> Int
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (CInt -> Int)
    -> (WindowAttributes -> CInt) -> WindowAttributes -> Int
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . WindowAttributes -> CInt
    wa_border_width) (IO WindowAttributes -> IO Int) -> IO WindowAttributes -> IO Int
    forall a b. (a -> b) -> a -> b
    $ Display -> Window -> IO WindowAttributes
    getWindowAttributes Display
    d Window
    w
        let widthHint :: (SizeHints -> m (a, b)) -> m b
    widthHint f :: SizeHints -> m (a, b)
    f = SizeHints -> m (a, b)
    f SizeHints
    sh m (a, b) -> ((a, b) -> m b) -> m b
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>= b -> m b
    forall (m :: * -> *) a. Monad m => a -> m a
    return (b -> m b) -> ((a, b) -> b) -> (a, b) -> m b
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . a -> b
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (a -> b) -> ((a, b) -> a) -> (a, b) -> b
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (a, b) -> a
    forall a b. (a, b) -> a
    fst
            oneCol :: Int
    oneCol      = Int -> Maybe Int -> Int
    forall a. a -> Maybe a -> a
    fromMaybe Int
    inc (Maybe Int -> Int) -> Maybe Int -> Int
    forall a b. (a -> b) -> a -> b
    $ (SizeHints -> Maybe (Dimension, Dimension)) -> Maybe Int
    forall (m :: * -> *) a b b.
    (Monad m, Integral a, Num b) =>
    (SizeHints -> m (a, b)) -> m b
    widthHint SizeHints -> Maybe (Dimension, Dimension)
    sh_resize_inc
            base :: Int
    base        = Int -> Maybe Int -> Int
    forall a. a -> Maybe a -> a
    fromMaybe 0 (Maybe Int -> Int) -> Maybe Int -> Int
    forall a b. (a -> b) -> a -> b
    $ (SizeHints -> Maybe (Dimension, Dimension)) -> Maybe Int
    forall (m :: * -> *) a b b.
    (Monad m, Integral a, Num b) =>
    (SizeHints -> m (a, b)) -> m b
    widthHint SizeHints -> Maybe (Dimension, Dimension)
    sh_base_size
        Int -> IO Int
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Int -> IO Int) -> Int -> IO Int
    forall a b. (a -> b) -> a -> b
    $ 2 Int -> Int -> Int
    forall a. Num a => a -> a -> a
    * Int
    bw Int -> Int -> Int
    forall a. Num a => a -> a -> a
    + Int
    base Int -> Int -> Int
    forall a. Num a => a -> a -> a
    + Int
    n Int -> Int -> Int
    forall a. Num a => a -> a -> a
    * Int
    oneCol