    {-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
    ----------------------------------------------------------------------------
    -- |
    -- Module      :  XMonad.Layout.WindowSwitcherDecoration
    -- Copyright   :  (c) Jan Vornberger 2009
    --                    Alejandro Serrano 2010
    -- License     :  BSD3-style (see LICENSE)
    --
    -- Maintainer  :  jan.vornberger@informatik.uni-oldenburg.de
    -- Stability   :  unstable
    -- Portability :  not portable
    --
    -- A decoration that allows to switch the position of windows by dragging
    -- them onto each other.
    --
    -----------------------------------------------------------------------------

    module XMonad.Layout.WindowSwitcherDecoration
        ( -- * Usage:
          -- $usage
          windowSwitcherDecoration,
          windowSwitcherDecorationWithButtons,
          windowSwitcherDecorationWithImageButtons,
          WindowSwitcherDecoration, ImageWindowSwitcherDecoration,
        ) where

    import XMonad
    import XMonad.Layout.Decoration
    import XMonad.Layout.DecorationAddons
    import XMonad.Layout.ImageButtonDecoration
    import XMonad.Layout.DraggingVisualizer
    import qualified XMonad.StackSet as S
    import Control.Monad
    import Foreign.C.Types(CInt)

    -- $usage
    -- You can use this module with the following in your
    -- @~\/.xmonad\/xmonad.hs@:
    --
    -- > import XMonad.Layout.WindowSwitcherDecoration
    -- > import XMonad.Layout.DraggingVisualizer
    --
    -- Then edit your @layoutHook@ by adding the WindowSwitcherDecoration to
    -- your layout:
    --
    -- > myL = windowSwitcherDecoration shrinkText def (draggingVisualizer $ layoutHook def)
    -- > main = xmonad def { layoutHook = myL }
    --
    -- There is also a version of the decoration that contains buttons like
    -- "XMonad.Layout.ButtonDecoration". To use that version, you will need to
    -- import "XMonad.Layout.DecorationAddons" as well and modify your @layoutHook@
    -- in the following way:
    --
    -- > import XMonad.Layout.DecorationAddons
    -- >
    -- > myL = windowSwitcherDecorationWithButtons shrinkText defaultThemeWithButtons (draggingVisualizer $ layoutHook def)
    -- > main = xmonad def { layoutHook = myL }
    --
    -- Additionaly, there is a version of the decoration that contains image buttons like
    -- "XMonad.Layout.ImageButtonDecoration". To use that version, you will need to
    -- import "XMonad.Layout.ImageButtonDecoration" as well and modify your @layoutHook@
    -- in the following way:
    --
    -- > import XMonad.Layout.ImageButtonDecoration
    -- >
    -- > myL = windowSwitcherDecorationWithImageButtons shrinkText defaultThemeWithImageButtons (draggingVisualizer $ layoutHook def)
    -- > main = xmonad def { layoutHook = myL }
    --

    windowSwitcherDecoration :: (Eq a, Shrinker s) => s -> Theme
               -> l a -> ModifiedLayout (Decoration WindowSwitcherDecoration s) l a
    windowSwitcherDecoration s c = decoration s c $ WSD False

    windowSwitcherDecorationWithButtons :: (Eq a, Shrinker s) => s -> Theme
               -> l a -> ModifiedLayout (Decoration WindowSwitcherDecoration s) l a
    windowSwitcherDecorationWithButtons s c = decoration s c $ WSD True

    data WindowSwitcherDecoration a = WSD Bool deriving (Show, Read)

    instance Eq a => DecorationStyle WindowSwitcherDecoration a where
        describeDeco _ = "WindowSwitcherDeco"

        decorationCatchClicksHook (WSD withButtons) mainw dFL dFR = if withButtons
                                                                        then titleBarButtonHandler mainw dFL dFR
                                                                        else return False
        decorationWhileDraggingHook _ ex ey (mainw, r) x y = handleTiledDraggingInProgress ex ey (mainw, r) x y
        decorationAfterDraggingHook _ (mainw, _) decoWin = do focus mainw
                                                              hasCrossed <- handleScreenCrossing mainw decoWin
                                                              unless hasCrossed $ do sendMessage $ DraggingStopped
                                                                                     performWindowSwitching mainw

    -- Note: the image button code is duplicated from the above
    -- because the title bar handle is different

    windowSwitcherDecorationWithImageButtons :: (Eq a, Shrinker s) => s -> Theme
               -> l a -> ModifiedLayout (Decoration ImageWindowSwitcherDecoration s) l a
    windowSwitcherDecorationWithImageButtons s c = decoration s c $ IWSD True

    data ImageWindowSwitcherDecoration a = IWSD Bool deriving (Show, Read)

    instance Eq a => DecorationStyle ImageWindowSwitcherDecoration a where
        describeDeco _ = "ImageWindowSwitcherDeco"

        decorationCatchClicksHook (IWSD withButtons) mainw dFL dFR = if withButtons
                                                                        then imageTitleBarButtonHandler mainw dFL dFR
                                                                        else return False
        decorationWhileDraggingHook _ ex ey (mainw, r) x y = handleTiledDraggingInProgress ex ey (mainw, r) x y
        decorationAfterDraggingHook _ (mainw, _) decoWin = do focus mainw
                                                              hasCrossed <- handleScreenCrossing mainw decoWin
                                                              unless hasCrossed $ do sendMessage $ DraggingStopped
                                                                                     performWindowSwitching mainw

    handleTiledDraggingInProgress :: CInt -> CInt -> (Window, Rectangle) -> Position -> Position -> X ()
    handleTiledDraggingInProgress ex ey (mainw, r) x y = do
        let rect = Rectangle (x - (fi ex - rect_x r))
                             (y - (fi ey - rect_y r))
                             (rect_width  r)
                             (rect_height r)
        sendMessage $ DraggingWindow mainw rect

    performWindowSwitching :: Window -> X ()
    performWindowSwitching win =
        withDisplay $ \d -> do
           root <- asks theRoot
           (_, _, selWin, _, _, _, _, _) <- io $ queryPointer d root
           ws <- gets windowset
           let allWindows = S.index ws
           -- do a little double check to be sure
           if (win `elem` allWindows) && (selWin `elem` allWindows)
                then do
                    let allWindowsSwitched = map (switchEntries win selWin) allWindows
                    let (ls, t:rs) = break (win ==) allWindowsSwitched
                    let newStack = S.Stack t (reverse ls) rs
                    windows $ S.modify' $ \_ -> newStack
                else return ()
        where
            switchEntries a b x
                | x == a    = b
                | x == b    = a
                | otherwise = x