content: ----------------------------------------------------------------------------
  -- |
  -- Module      :  XMonad.Layout.DecorationAddons
  -- Copyright   :  (c) Jan Vornberger 2009
  -- License     :  BSD3-style (see LICENSE)
  --
  -- Maintainer  :  jan.vornberger@informatik.uni-oldenburg.de
  -- Stability   :  unstable
  -- Portability :  not portable
  --
  -- Various stuff that can be added to the decoration. Most of it
  -- is intended to be used by other modules. See
  -- "XMonad.Layout.ButtonDecoration" for a module that makes use of this.
  --
  -----------------------------------------------------------------------------
  
  module XMonad.Layout.DecorationAddons (
                                      titleBarButtonHandler
                                     ,defaultThemeWithButtons
                                     ,handleScreenCrossing
                                     ) where
  
  import XMonad
  import qualified XMonad.StackSet as W
  import XMonad.Layout.Decoration
  import XMonad.Actions.WindowMenu
  import XMonad.Layout.Minimize
  import XMonad.Layout.Maximize
  import XMonad.Hooks.ManageDocks
  import XMonad.Util.Font
  import XMonad.Util.PositionStore
  
  import Control.Applicative((<$>))
  import Data.Maybe
  import qualified Data.Set as S
  
  minimizeButtonOffset :: Int
  minimizeButtonOffset = 48
  
  maximizeButtonOffset :: Int
  maximizeButtonOffset = 25
  
  closeButtonOffset :: Int
  closeButtonOffset = 10
  
  buttonSize :: Int
  buttonSize = 10
  
  -- | A function intended to be plugged into the 'decorationCatchClicksHook' of a decoration.
  -- It will intercept clicks on the buttons of the decoration and invoke the associated action.
  -- To actually see the buttons, you will need to use a theme that includes them.
  -- See 'defaultThemeWithButtons' below.
  titleBarButtonHandler :: Window -> Int -> Int -> X Bool
  titleBarButtonHandler mainw distFromLeft distFromRight = do
      let action = if (fi distFromLeft <= 3 * buttonSize)
                          then focus mainw >> windowMenu >> return True
                    else if (fi distFromRight >= closeButtonOffset &&
                             fi distFromRight <= closeButtonOffset + buttonSize)
                                then focus mainw >> kill >> return True
                    else if (fi distFromRight >= maximizeButtonOffset &&
                             fi distFromRight <= maximizeButtonOffset + (2 * buttonSize))
                               then focus mainw >> sendMessage (maximizeRestore mainw) >> return True
                    else if (fi distFromRight >= minimizeButtonOffset &&
                             fi distFromRight <= minimizeButtonOffset + buttonSize)
                               then focus mainw >> minimizeWindow mainw >> return True
                    else return False
      action
  
  -- | Intended to be used together with 'titleBarButtonHandler'. See above.
  defaultThemeWithButtons :: Theme
  defaultThemeWithButtons = def {
                              windowTitleAddons = [ (" (M)", AlignLeft)
                                                  , ("_"   , AlignRightOffset minimizeButtonOffset)
                                                  , ("[]"  , AlignRightOffset maximizeButtonOffset)
                                                  , ("X"   , AlignRightOffset closeButtonOffset)
                                                  ]
                              }
  
  -- | A function intended to be plugged into the 'decorationAfterDraggingHook' of a decoration.
  -- It will check if the window has been dragged onto another screen and shift it there.
  -- The PositionStore is also updated accordingly, as this is designed to be used together
  -- with "XMonad.Layout.PositionStoreFloat".
  handleScreenCrossing :: Window -> Window -> X Bool
  handleScreenCrossing w decoWin = withDisplay $ \d -> do
      root <- asks theRoot
      (_, _, _, px, py, _, _, _) <- io $ queryPointer d root
      ws <- gets windowset
      sc <- fromMaybe (W.current ws) <$> pointScreen (fi px) (fi py)
      maybeWksp <- screenWorkspace $ W.screen sc
      let targetWksp = maybeWksp >>= \wksp ->
                          W.findTag w ws >>= \currentWksp ->
                          if (currentWksp /= wksp)
                              then Just wksp
                              else Nothing
      case targetWksp of
          Just wksp -> do
                          -- find out window under cursor on target workspace
                          -- apparently we have to switch to the workspace first
                          -- to make this work, which unforunately introduces some flicker
                          windows $ \ws' -> W.view wksp ws'
                          (_, _, selWin, _, _, _, _, _) <- io $ queryPointer d root
  
                          -- adjust PositionStore
                          let oldScreenRect = screenRect . W.screenDetail $ W.current ws
                              newScreenRect = screenRect . W.screenDetail $ sc
                          {-- somewhat ugly hack to get proper ScreenRect,
                              creates unwanted inter-dependencies
                              TODO: get ScreenRects in a proper way --}
                          oldScreenRect' <- fmap ($ oldScreenRect) (calcGap $ S.fromList [minBound .. maxBound])
                          newScreenRect' <- fmap ($ newScreenRect) (calcGap $ S.fromList [minBound .. maxBound])
                          wa <- io $ getWindowAttributes d decoWin
                          modifyPosStore (\ps ->
                              posStoreMove ps w (fi $ wa_x wa) (fi $ wa_y wa)
                                  oldScreenRect' newScreenRect')
  
                          -- set focus correctly so the window will be inserted
                          -- at the correct position on the target workspace
                          -- and then shift the window
                          windows $ \ws' -> W.shiftWin wksp w . W.focusWindow selWin $ ws'
  
                          -- return True to signal that screen crossing has taken place
                          return True
          Nothing -> return False
  