    -----------------------------------------------------------------------------
    -- |
    -- Module      :  XMonad.Actions.OnScreen
    -- Copyright   :  (c) 2009 Nils Schweinsberg
    -- License     :  BSD3-style (see LICENSE)
    --
    -- Maintainer  :  Nils Schweinsberg <mail@n-sch.de>
    -- Stability   :  unstable
    -- Portability :  unportable
    --
    -- Control workspaces on different screens (in xinerama mode).
    --
    -----------------------------------------------------------------------------

    module XMonad.Actions.OnScreen (
        -- * Usage
        -- $usage
        onScreen
        , viewOnScreen
        , greedyViewOnScreen
        , onlyOnScreen
        ) where

    import XMonad.StackSet
    import Control.Monad(guard)
    import Data.List
    import Data.Maybe(fromMaybe)
    import Data.Function(on)

    -- $usage
    --
    -- This module provides an easy way to control, what you see on other screens in
    -- xinerama mode without having to focus them. Put this into your
    -- @~\/.xmonad\/xmonad.hs@:
    --
    -- > import XMonad.Actions.OnScreen
    --
    -- Then add the appropriate keybindings, for example replace your current keys
    -- to switch the workspaces with this at the bottom of your keybindings:
    --
    -- >     ++
    -- >     [ ((m .|. modm, k), windows (f i))
    -- >       | (i, k) <- zip (workspaces conf) ([xK_1 .. xK_9] ++ [xK_0])
    -- >       , (f, m) <- [ (viewOnScreen 0, 0)
    -- >                   , (viewOnScreen 1, controlMask)
    -- >                   , (greedyView, controlMask .|. shiftMask) ]
    -- >     ]
    --
    -- This will provide you with the following keybindings:
    --
    --      * modkey + 1-0:
    --      Switch to workspace 1-0 on screen 0
    --
    --      * modkey + control + 1-0:
    --      Switch to workspace 1-0 on screen 1
    --
    --      * modkey + control + shift + 1-0:
    --      Default greedyView behaviour
    --
    --
    -- A more basic version inside the default keybindings would be:
    --
    -- >        , ((modm .|. controlMask, xK_1) windows (viewOnScreen 0 "1"))
    --
    -- where 0 is the first screen and "1" the workspace with the tag "1".
    --
    -- For detailed instructions on editing your key bindings, see
    -- "XMonad.Doc.Extending#Editing_key_bindings".

    -- | Switch to the (hidden) workspace with index 'i' on the screen 'sc'.
    -- A default function (for example 'view' or 'greedyView') will be run if 'sc' is
    -- the current screen, no valid screen id or workspace 'i' is already visible.
    onScreen :: (Eq sid, Eq i)
              => (i -> StackSet i l a sid sd -> StackSet i l a sid sd) -- ^ default action
              -> sid                      -- ^ screen id
              -> i                        -- ^ index of the workspace
              -> StackSet i l a sid sd    -- ^ current stack
              -> StackSet i l a sid sd
    onScreen defFunc sc i st = fromMaybe (defFunc i st) $ do
        -- on unfocused current screen
        guard $ screen (current st) /= sc
        x <- find ((i==) . tag    ) (hidden  st)
        s <- find ((sc==) . screen) (screens st)
        o <- find ((sc==) . screen) (visible st)
        let newScreen = s { workspace = x }
        return st { visible   = newScreen : deleteBy ((==) `on` screen) newScreen (visible st)
                  , hidden    = workspace o : deleteBy ((==) `on` tag) x (hidden st)
                  }

    -- | Switch to workspace 'i' on screen 'sc'. If 'i' is visible use 'greedyView'
    -- to switch the current workspace with workspace 'i'.
    greedyViewOnScreen :: (Eq sid, Eq i)
        => sid                          -- ^ screen id
        -> i                            -- ^ index of the workspace
        -> StackSet i l a sid sd        -- ^ current stack
        -> StackSet i l a sid sd
    greedyViewOnScreen = onScreen greedyView

    -- | Switch to workspace 'i' on screen 'sc'. If 'i' is visible use 'view' to
    -- switch focus to the workspace 'i'.
    viewOnScreen :: (Eq sid, Eq i)
        => sid                          -- ^ screen id
        -> i                            -- ^ index of the workspace
        -> StackSet i l a sid sd        -- ^ current stack
        -> StackSet i l a sid sd
    viewOnScreen = onScreen view

    -- | Switch to workspace 'i' on screen 'sc'. If 'i' is visible do nothing.
    onlyOnScreen :: (Eq sid, Eq i)
        => sid                          -- ^ screen id
        -> i                            -- ^ index of the workspace
        -> StackSet i l a sid sd        -- ^ current stack
        -> StackSet i l a sid sd
    onlyOnScreen = onScreen doNothing
      where doNothing _ st = st