    -----------------------------------------------------------------------------
    -- |
    -- Module     :  XMonad.Actions.Workscreen
    -- Copyright  :  (c) 2012 kedals0
    -- License    :  BSD3-style (see LICENSE)
    --
    -- Maintainer :  Dal <kedasl0@gmail.com>
    -- Stability  :  unstable
    -- Portability:  unportable
    --
    -- A workscreen permits to display a set of workspaces on several
    -- screens. In xinerama mode, when a workscreen is viewed, workspaces
    -- associated to all screens are visible.
    --
    -- The first workspace of a workscreen is displayed on first screen,
    -- second on second screen, etc. Workspace position can be easily
    -- changed. If the current workscreen is called again, workspaces are
    -- shifted.
    --
    -- This also permits to see all workspaces of a workscreen even if just
    -- one screen is present, and to move windows from workspace to workscreen.
    -----------------------------------------------------------------------------
    {-# LANGUAGE DeriveDataTypeable #-}

    module XMonad.Actions.Workscreen (
      -- * Usage
      -- $usage
      configWorkscreen
      ,viewWorkscreen
      ,Workscreen(..)
      ,shiftToWorkscreen
      ,fromWorkspace
      ,expandWorkspace
      ) where

    import XMonad hiding (workspaces)
    import qualified XMonad.StackSet as W
    import qualified XMonad.Util.ExtensibleState as XS
    import XMonad.Actions.OnScreen

    -- $usage
    -- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
    --
    -- > import XMonad.Actions.Workscreen
    -- > myWorkspaces = let myOldWorkspaces = ["adm","work","mail"]
    -- >                in Workscreen.expandWorkspace 2 myOldWorkspaces
    -- > myStartupHook = do Workscreen.configWorkscreen (Workscreen.fromWorkspace 2 myWorkspaces)
    -- >                    return ()
    --
    -- Then, replace normal workspace view and shift keybinding:
    --
    -- > [((m .|. modm, k), f i)
    -- >      | (i, k) <- zip [0..] [1..12]
    -- >      , (f, m) <- [(Workscreen.viewWorkscreen, 0), (Workscreen.shiftToWorkscreen, shiftMask)]]
    --
    -- For detailed instructions on editing your key bindings, see
    -- "XMonad.Doc.Extending#Editing_key_bindings".

    data Workscreen = Workscreen{workscreenId::Int,workspaces::[WorkspaceId]} deriving (Show,Typeable)
    type WorkscreenId=Int

    data WorkscreenStorage = WorkscreenStorage WorkscreenId [Workscreen] deriving (Show,Typeable)
    instance ExtensionClass WorkscreenStorage where
      initialValue = WorkscreenStorage 0 []

    -- | Helper to group workspaces. Multiply workspace by screens number.
    expandWorkspace :: Int -> [WorkspaceId] -> [WorkspaceId]
    expandWorkspace nscr ws = concat $ map expandId ws
      where expandId wsId = let t = wsId ++ "_"
                            in map ((++) t . show ) [1..nscr]

    -- | Create workscreen list from workspace list. Group workspaces to
    -- packets of screens number size.
    fromWorkspace :: Int -> [WorkspaceId] -> [Workscreen]
    fromWorkspace n ws = map (\(a,b) -> Workscreen a b) $ zip [0..] (fromWorkspace' n ws)
    fromWorkspace' :: Int -> [WorkspaceId] -> [[WorkspaceId]]
    fromWorkspace' _ [] = []
    fromWorkspace' n ws = take n ws : fromWorkspace' n (drop n ws)

    -- | Initial configuration of workscreens
    configWorkscreen :: [Workscreen] -> X ()
    configWorkscreen wscrn = XS.put (WorkscreenStorage 0 wscrn)

    -- | View workscreen of index @WorkscreenId@. If current workscreen is asked
    -- workscreen, workscreen's workspaces are shifted.
    viewWorkscreen :: WorkscreenId -> X ()
    viewWorkscreen wscrId = do (WorkscreenStorage c a) <- XS.get
                               let wscr = if wscrId == c
                                              then Workscreen wscrId $ shiftWs (workspaces $ a !! wscrId)
                                              else a !! wscrId
                                   (x,_:ys) = splitAt wscrId a
                                   newWorkscreenStorage = WorkscreenStorage wscrId (x ++ [wscr] ++ ys)
                               windows (viewWorkscreen' wscr)
                               XS.put newWorkscreenStorage

    viewWorkscreen' :: Workscreen -> WindowSet -> WindowSet
    viewWorkscreen' (Workscreen _ ws) = \s -> foldl wsToSc' s (zip [0..] ws)
      where wsToSc' s (scr,wsId) = greedyViewOnScreen scr wsId s

    shiftWs :: [WorkspaceId] -> [WorkspaceId]
    shiftWs a = drop 1 a ++ take 1 a

    -- | Shift a window on the first workspace of workscreen
    -- @WorkscreenId@.
    shiftToWorkscreen :: WorkscreenId -> X ()
    shiftToWorkscreen wscrId = do (WorkscreenStorage _ a) <- XS.get
                                  let ws = head . workspaces $ a !! wscrId
                                  windows $ W.shift ws