-----------------------------------------------------------------------------
-- |
-- Module      :  XMonad.Actions.FindEmptyWorkspace
-- Description :  Find an empty workspace.
-- Copyright   :  (c) Miikka Koskinen 2007
-- License     :  BSD3-style (see LICENSE)
--
-- Maintainer  :  arcatan@kapsi.fi
-- Stability   :  stable
-- Portability :  unportable
--
-- Find an empty workspace.
--
-----------------------------------------------------------------------------

module XMonad.Actions.FindEmptyWorkspace (
    -- * Usage
    -- $usage
    viewEmptyWorkspace, tagToEmptyWorkspace, sendToEmptyWorkspace
  ) where

import XMonad.Prelude
import XMonad
import XMonad.StackSet

-- $usage
--
-- To use, import this module into your @~\/.xmonad\/xmonad.hs@:
--
-- >   import XMonad.Actions.FindEmptyWorkspace
--
-- and add the desired keybindings, for example:
--
--  >   , ((modm,                xK_m    ), viewEmptyWorkspace)
--  >   , ((modm .|. shiftMask,  xK_m    ), tagToEmptyWorkspace)
--
-- Now you can jump to an empty workspace with @mod-m@. @Mod-shift-m@
-- will tag the current window to an empty workspace and view it.
--
-- For detailed instructions on editing your key bindings, see
-- "XMonad.Doc.Extending#Editing_key_bindings".

-- | Find the first hidden empty workspace in a StackSet. Returns
-- Nothing if all workspaces are in use. Function searches currently
-- focused workspace, other visible workspaces (when in Xinerama) and
-- hidden workspaces in this order.
findEmptyWorkspace :: StackSet i l a s sd -> Maybe (Workspace i l a)
findEmptyWorkspace :: forall i l a s sd. StackSet i l a s sd -> Maybe (Workspace i l a)
findEmptyWorkspace = (Workspace i l a -> Bool)
-> [Workspace i l a] -> Maybe (Workspace i l a)
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find (Maybe (Stack a) -> Bool
forall a. Maybe a -> Bool
isNothing (Maybe (Stack a) -> Bool)
-> (Workspace i l a -> Maybe (Stack a)) -> Workspace i l a -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Workspace i l a -> Maybe (Stack a)
forall i l a. Workspace i l a -> Maybe (Stack a)
stack) ([Workspace i l a] -> Maybe (Workspace i l a))
-> (StackSet i l a s sd -> [Workspace i l a])
-> StackSet i l a s sd
-> Maybe (Workspace i l a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. StackSet i l a s sd -> [Workspace i l a]
forall {i} {l} {a} {sid} {sd}.
StackSet i l a sid sd -> [Workspace i l a]
allWorkspaces
  where
    allWorkspaces :: StackSet i l a sid sd -> [Workspace i l a]
allWorkspaces StackSet i l a sid sd
ss = (Screen i l a sid sd -> Workspace i l a
forall i l a sid sd. Screen i l a sid sd -> Workspace i l a
workspace (Screen i l a sid sd -> Workspace i l a)
-> (StackSet i l a sid sd -> Screen i l a sid sd)
-> StackSet i l a sid sd
-> Workspace i l a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. StackSet i l a sid sd -> Screen i l a sid sd
forall i l a sid sd. StackSet i l a sid sd -> Screen i l a sid sd
current) StackSet i l a sid sd
ss Workspace i l a -> [Workspace i l a] -> [Workspace i l a]
forall a. a -> [a] -> [a]
:
                       ((Screen i l a sid sd -> Workspace i l a)
-> [Screen i l a sid sd] -> [Workspace i l a]
forall a b. (a -> b) -> [a] -> [b]
map Screen i l a sid sd -> Workspace i l a
forall i l a sid sd. Screen i l a sid sd -> Workspace i l a
workspace ([Screen i l a sid sd] -> [Workspace i l a])
-> (StackSet i l a sid sd -> [Screen i l a sid sd])
-> StackSet i l a sid sd
-> [Workspace i l a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. StackSet i l a sid sd -> [Screen i l a sid sd]
forall i l a sid sd. StackSet i l a sid sd -> [Screen i l a sid sd]
visible) StackSet i l a sid sd
ss [Workspace i l a] -> [Workspace i l a] -> [Workspace i l a]
forall a. [a] -> [a] -> [a]
++ StackSet i l a sid sd -> [Workspace i l a]
forall {i} {l} {a} {sid} {sd}.
StackSet i l a sid sd -> [Workspace i l a]
hidden StackSet i l a sid sd
ss

withEmptyWorkspace :: (WorkspaceId -> X ()) -> X ()
withEmptyWorkspace :: (WorkspaceId -> X ()) -> X ()
withEmptyWorkspace WorkspaceId -> X ()
f = do
    WindowSet
ws <- (XState -> WindowSet) -> X WindowSet
forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
gets XState -> WindowSet
windowset
    Maybe (Workspace WorkspaceId (Layout Window) Window)
-> (Workspace WorkspaceId (Layout Window) Window -> X ()) -> X ()
forall (m :: * -> *) a. Monad m => Maybe a -> (a -> m ()) -> m ()
whenJust (WindowSet -> Maybe (Workspace WorkspaceId (Layout Window) Window)
forall i l a s sd. StackSet i l a s sd -> Maybe (Workspace i l a)
findEmptyWorkspace WindowSet
ws) (WorkspaceId -> X ()
f (WorkspaceId -> X ())
-> (Workspace WorkspaceId (Layout Window) Window -> WorkspaceId)
-> Workspace WorkspaceId (Layout Window) Window
-> X ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Workspace WorkspaceId (Layout Window) Window -> WorkspaceId
forall i l a. Workspace i l a -> i
tag)

-- | Find and view an empty workspace. Do nothing if all workspaces are
-- in use.
viewEmptyWorkspace :: X ()
viewEmptyWorkspace :: X ()
viewEmptyWorkspace = (WorkspaceId -> X ()) -> X ()
withEmptyWorkspace ((WindowSet -> WindowSet) -> X ()
windows ((WindowSet -> WindowSet) -> X ())
-> (WorkspaceId -> WindowSet -> WindowSet) -> WorkspaceId -> X ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. WorkspaceId -> WindowSet -> WindowSet
forall s i l a sd.
(Eq s, Eq i) =>
i -> StackSet i l a s sd -> StackSet i l a s sd
view)

-- | Tag current window to an empty workspace and view it. Do nothing if
-- all workspaces are in use.
tagToEmptyWorkspace :: X ()
tagToEmptyWorkspace :: X ()
tagToEmptyWorkspace = (WorkspaceId -> X ()) -> X ()
withEmptyWorkspace ((WorkspaceId -> X ()) -> X ()) -> (WorkspaceId -> X ()) -> X ()
forall a b. (a -> b) -> a -> b
$ \WorkspaceId
w -> (WindowSet -> WindowSet) -> X ()
windows ((WindowSet -> WindowSet) -> X ())
-> (WindowSet -> WindowSet) -> X ()
forall a b. (a -> b) -> a -> b
$ WorkspaceId -> WindowSet -> WindowSet
forall s i l a sd.
(Eq s, Eq i) =>
i -> StackSet i l a s sd -> StackSet i l a s sd
view WorkspaceId
w (WindowSet -> WindowSet)
-> (WindowSet -> WindowSet) -> WindowSet -> WindowSet
forall b c a. (b -> c) -> (a -> b) -> a -> c
. WorkspaceId -> WindowSet -> WindowSet
forall a s i l sd.
(Ord a, Eq s, Eq i) =>
i -> StackSet i l a s sd -> StackSet i l a s sd
shift WorkspaceId
w

-- | Send current window to an empty workspace. Do nothing if
-- all workspaces are in use.
sendToEmptyWorkspace :: X ()
sendToEmptyWorkspace :: X ()
sendToEmptyWorkspace = (WorkspaceId -> X ()) -> X ()
withEmptyWorkspace ((WorkspaceId -> X ()) -> X ()) -> (WorkspaceId -> X ()) -> X ()
forall a b. (a -> b) -> a -> b
$ \WorkspaceId
w -> (WindowSet -> WindowSet) -> X ()
windows ((WindowSet -> WindowSet) -> X ())
-> (WindowSet -> WindowSet) -> X ()
forall a b. (a -> b) -> a -> b
$ WorkspaceId -> WindowSet -> WindowSet
forall a s i l sd.
(Ord a, Eq s, Eq i) =>
i -> StackSet i l a s sd -> StackSet i l a s sd
shift WorkspaceId
w