-----------------------------------------------------------------------------
-- |
-- Module      :  XMonad.Actions.FlexibleResize
-- Copyright   :  (c) Lukas Mai
-- License     :  BSD3-style (see LICENSE)
--
-- Maintainer  :  <l.mai@web.de>
-- Stability   :  unstable
-- Portability :  unportable
--
-- Resize floating windows from any corner.
--
-----------------------------------------------------------------------------

module XMonad.Actions.FlexibleResize (
        -- * Usage
        -- $usage
        XMonad.Actions.FlexibleResize.mouseResizeWindow,
        XMonad.Actions.FlexibleResize.mouseResizeEdgeWindow
) where

import XMonad
import XMonad.Util.XUtils (fi)
import Foreign.C.Types

-- $usage
-- To use, first import this module into your @~\/.xmonad\/xmonad.hs@ file:
--
-- > import qualified XMonad.Actions.FlexibleResize as Flex
--
-- Then add an appropriate mouse binding:
--
-- >     , ((modm, button3), (\w -> focus w >> Flex.mouseResizeWindow w))
--
-- For detailed instructions on editing your mouse bindings, see
-- "XMonad.Doc.Extending#Editing_mouse_bindings".

-- | Resize a floating window from whichever corner the mouse is
--   closest to.
mouseResizeWindow
  :: Window -- ^ The window to resize.
  -> X ()
mouseResizeWindow = mouseResizeEdgeWindow 0


-- | Resize a floating window from whichever corner or edge the mouse is
--   closest to.
mouseResizeEdgeWindow
  :: Rational -- ^ The size of the area where only one edge is resized.
  -> Window   -- ^ The window to resize.
  -> X ()
mouseResizeEdgeWindow edge w = whenX (isClient w) $ withDisplay $ \d -> do
    io $ raiseWindow d w
    wa <- io $ getWindowAttributes d w
    sh <- io $ getWMNormalHints d w
    (_, _, _, _, _, ix, iy, _) <- io $ queryPointer d w
    let
        [pos_x, pos_y, width, height] = map (fi . ($ wa)) [wa_x, wa_y, wa_width, wa_height]
        west  = findPos ix width
        north = findPos iy height
        (cx, fx, gx) = mkSel west  width  pos_x
        (cy, fy, gy) = mkSel north height pos_y
    io $ warpPointer d none w 0 0 0 0 cx cy
    mouseDrag (\ex ey -> do let (nw,nh) = applySizeHintsContents sh (gx ex, gy ey)
                            io $ moveResizeWindow d w (fx nw) (fy nh) nw nh)
              (float w)
    where
    findPos :: CInt -> Position -> Maybe Bool
    findPos m s = if p < 0.5 - edge/2
                  then Just True
                  else if p < 0.5 + edge/2
                       then Nothing
                       else Just False
                  where p = fi m / fi s
    mkSel :: Maybe Bool -> Position -> Position -> (Position, Dimension -> Position, Position -> Dimension)
    mkSel b k p = case b of
                      Just True ->  (0, (fi k + fi p -).fi, (fi k + fi p -).fi)
                      Nothing ->    (k `div` 2, const p, const $ fi k)
                      Just False -> (k, const p, subtract (fi p) . fi)