-----------------------------------------------------------------------------
-- |
-- Module      :  XMonad.Prompt.Window
-- Description :  A prompt for bringing windows to you, and bring you to windows.
-- Copyright   :  Devin Mullins <me@twifkak.com>
--                Andrea Rossato <andrea.rossato@unibz.it>
-- License     :  BSD-style (see LICENSE)
--
-- Maintainer  :  Devin  Mullins <me@twifkak.com>
--                Andrea Rossato <andrea.rossato@unibz.it>
-- Stability   :  unstable
-- Portability :  unportable
--
-- xprompt operations to bring windows to you, and bring you to windows.
--
-----------------------------------------------------------------------------

module XMonad.Prompt.Window
    (
    -- * Usage
    -- $usage
    WindowPrompt(..),
    windowPrompt,
    windowMultiPrompt,
    allWindows,
    allApplications,
    wsWindows,
    XWindowMap,

    -- * Deprecated
    windowPromptGoto,
    windowPromptBring,
    windowPromptBringCopy,
    ) where

import XMonad.Prelude (forM)
import qualified Data.Map as M

import qualified XMonad.StackSet as W
import XMonad
import XMonad.Prompt
import XMonad.Actions.CopyWindow
import XMonad.Actions.WindowBringer
import XMonad.Util.NamedWindows

-- $usage
-- WindowPrompt brings windows to you and you to windows. That is to
-- say, it pops up a prompt with window names, in case you forgot
-- where you left your XChat. It also offers helpers to build the
-- subset of windows which is used for the prompt completion.
--
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
--
-- > import XMonad.Prompt
-- > import XMonad.Prompt.Window
--
-- and in the keys definition:
--
-- > , ((modm .|. shiftMask, xK_g     ), windowPrompt def Goto wsWindows)
-- > , ((modm .|. shiftMask, xK_b     ), windowPrompt def Bring allWindows)
--
-- The autoComplete option is a handy complement here:
--
-- > , ((modm .|. shiftMask, xK_g     ), windowPrompt
-- >                                        def { autoComplete = Just 500000 }
-- >                                        Goto allWindows)
--
-- The \'500000\' is the number of microseconds to pause before sending you to
-- your new window. This is useful so that you don't accidentally send some
-- keystrokes to the selected client.
--
-- For detailed instruction on editing the key binding see
-- "XMonad.Doc.Extending#Editing_key_bindings".

-- Describe actions that can applied  on the selected window
data WindowPrompt = Goto | Bring | BringCopy | BringToMaster | WithWindow String (Window ->  X())
instance XPrompt WindowPrompt where
    showXPrompt :: WindowPrompt -> String
showXPrompt WindowPrompt
Goto      = String
"Go to window: "
    showXPrompt WindowPrompt
Bring     = String
"Bring window: "
    showXPrompt WindowPrompt
BringToMaster
                          = String
"Bring window to master: "
    showXPrompt WindowPrompt
BringCopy = String
"Bring a copy: "
    showXPrompt (WithWindow String
xs Window -> X ()
_) = String
xs
    commandToComplete :: WindowPrompt -> String -> String
commandToComplete WindowPrompt
_ String
c = String
c
    nextCompletion :: WindowPrompt -> String -> [String] -> String
nextCompletion      WindowPrompt
_ = String -> [String] -> String
getNextCompletion

-- | Internal type used for the multiple mode prompt.
data WindowModePrompt =
  WindowModePrompt WindowPrompt (M.Map String Window) (String -> String -> Bool)

instance XPrompt WindowModePrompt where
    showXPrompt :: WindowModePrompt -> String
showXPrompt (WindowModePrompt WindowPrompt
action Map String Window
_ String -> String -> Bool
_) =
        WindowPrompt -> String
forall t. XPrompt t => t -> String
showXPrompt WindowPrompt
action

    completionFunction :: WindowModePrompt -> ComplFunction
completionFunction (WindowModePrompt WindowPrompt
_ Map String Window
winmap String -> String -> Bool
predicate) =
        \String
s -> [String] -> IO [String]
forall (m :: * -> *) a. Monad m => a -> m a
return ([String] -> IO [String])
-> (Map String Window -> [String])
-> Map String Window
-> IO [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String -> Bool) -> [String] -> [String]
forall a. (a -> Bool) -> [a] -> [a]
filter (String -> String -> Bool
predicate String
s) ([String] -> [String])
-> (Map String Window -> [String]) -> Map String Window -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((String, Window) -> String) -> [(String, Window)] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (String, Window) -> String
forall a b. (a, b) -> a
fst ([(String, Window)] -> [String])
-> (Map String Window -> [(String, Window)])
-> Map String Window
-> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Map String Window -> [(String, Window)]
forall k a. Map k a -> [(k, a)]
M.toList (Map String Window -> IO [String])
-> Map String Window -> IO [String]
forall a b. (a -> b) -> a -> b
$ Map String Window
winmap

    modeAction :: WindowModePrompt -> String -> String -> X ()
modeAction (WindowModePrompt WindowPrompt
action Map String Window
winmap String -> String -> Bool
_) String
buf String
auto = do
        let name :: String
name = if String -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
auto then String
buf else String
auto
            a :: String -> X ()
a = case WindowPrompt
action of
                  WindowPrompt
Goto           -> String -> X ()
gotoAction
                  WindowPrompt
Bring          -> String -> X ()
bringAction
                  WindowPrompt
BringCopy      -> String -> X ()
bringCopyAction
                  WindowPrompt
BringToMaster  -> String -> X ()
bringToMaster
                  WithWindow String
_ Window -> X ()
f -> (Window -> X ()) -> String -> X ()
forall (m :: * -> *). Monad m => (Window -> m ()) -> String -> m ()
withWindow Window -> X ()
f
        String -> X ()
a String
name
      where
        withWindow :: (Window -> m ()) -> String -> m ()
withWindow Window -> m ()
f     = (Maybe Window -> (Window -> m ()) -> m ())
-> (Window -> m ()) -> Maybe Window -> m ()
forall a b c. (a -> b -> c) -> b -> a -> c
flip Maybe Window -> (Window -> m ()) -> m ()
forall (m :: * -> *) a. Monad m => Maybe a -> (a -> m ()) -> m ()
whenJust Window -> m ()
f (Maybe Window -> m ())
-> (String -> Maybe Window) -> String -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String -> Map String Window -> Maybe Window)
-> Map String Window -> String -> Maybe Window
forall a b c. (a -> b -> c) -> b -> a -> c
flip String -> Map String Window -> Maybe Window
forall k a. Ord k => k -> Map k a -> Maybe a
M.lookup Map String Window
winmap
        winAction :: (Window -> WindowSet -> WindowSet) -> String -> X ()
winAction Window -> WindowSet -> WindowSet
a      = (Window -> X ()) -> String -> X ()
forall (m :: * -> *). Monad m => (Window -> m ()) -> String -> m ()
withWindow ((WindowSet -> WindowSet) -> X ()
windows ((WindowSet -> WindowSet) -> X ())
-> (Window -> WindowSet -> WindowSet) -> Window -> X ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Window -> WindowSet -> WindowSet
a)
        gotoAction :: String -> X ()
gotoAction       = (Window -> WindowSet -> WindowSet) -> String -> X ()
winAction Window -> WindowSet -> WindowSet
forall s a i l sd.
(Eq s, Eq a, Eq i) =>
a -> StackSet i l a s sd -> StackSet i l a s sd
W.focusWindow
        bringAction :: String -> X ()
bringAction      = (Window -> WindowSet -> WindowSet) -> String -> X ()
winAction Window -> WindowSet -> WindowSet
bringWindow
        bringCopyAction :: String -> X ()
bringCopyAction  = (Window -> WindowSet -> WindowSet) -> String -> X ()
winAction Window -> WindowSet -> WindowSet
bringCopyWindow
        bringToMaster :: String -> X ()
bringToMaster    = (Window -> WindowSet -> WindowSet) -> String -> X ()
winAction (\Window
w WindowSet
s -> WindowSet -> WindowSet
forall i l a s sd. StackSet i l a s sd -> StackSet i l a s sd
W.shiftMaster (WindowSet -> WindowSet)
-> (WindowSet -> WindowSet) -> WindowSet -> WindowSet
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Window -> WindowSet -> WindowSet
forall s a i l sd.
(Eq s, Eq a, Eq i) =>
a -> StackSet i l a s sd -> StackSet i l a s sd
W.focusWindow Window
w (WindowSet -> WindowSet) -> WindowSet -> WindowSet
forall a b. (a -> b) -> a -> b
$ Window -> WindowSet -> WindowSet
bringWindow Window
w WindowSet
s)

-- | Deprecated. Use windowPrompt instead.
{-# DEPRECATED windowPromptGoto      "Use windowPrompt instead." #-}
{-# DEPRECATED windowPromptBring     "Use windowPrompt instead." #-}
{-# DEPRECATED windowPromptBringCopy "Use windowPrompt instead." #-}
windowPromptGoto, windowPromptBring, windowPromptBringCopy :: XPConfig -> X ()
windowPromptGoto :: XPConfig -> X ()
windowPromptGoto XPConfig
c = XPConfig -> WindowPrompt -> XWindowMap -> X ()
windowPrompt XPConfig
c WindowPrompt
Goto XWindowMap
windowMap
windowPromptBring :: XPConfig -> X ()
windowPromptBring XPConfig
c = XPConfig -> WindowPrompt -> XWindowMap -> X ()
windowPrompt XPConfig
c WindowPrompt
Bring XWindowMap
windowMap
windowPromptBringCopy :: XPConfig -> X ()
windowPromptBringCopy XPConfig
c = XPConfig -> WindowPrompt -> XWindowMap -> X ()
windowPrompt XPConfig
c WindowPrompt
BringCopy XWindowMap
windowMap

-- | A helper to get the map of all windows.
allWindows :: XWindowMap
allWindows :: XWindowMap
allWindows = XWindowMap
windowMap

-- | A helper to get the map of all applications
allApplications :: XWindowMap
allApplications :: XWindowMap
allApplications = XWindowMap
windowAppMap

-- | A helper to get the map of windows of the current workspace.
wsWindows :: XWindowMap
wsWindows :: XWindowMap
wsWindows = (WindowSet -> X [Window]) -> X [Window]
forall a. (WindowSet -> X a) -> X a
withWindowSet ([Window] -> X [Window]
forall (m :: * -> *) a. Monad m => a -> m a
return ([Window] -> X [Window])
-> (WindowSet -> [Window]) -> WindowSet -> X [Window]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. WindowSet -> [Window]
forall i l a s sd. StackSet i l a s sd -> [a]
W.index) X [Window] -> ([Window] -> XWindowMap) -> XWindowMap
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= [Window] -> XWindowMap
winmap
    where
      winmap :: [Window] -> XWindowMap
winmap = ([(String, Window)] -> Map String Window)
-> X [(String, Window)] -> XWindowMap
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [(String, Window)] -> Map String Window
forall k a. Ord k => [(k, a)] -> Map k a
M.fromList (X [(String, Window)] -> XWindowMap)
-> ([Window] -> X [(String, Window)]) -> [Window] -> XWindowMap
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Window -> X (String, Window)) -> [Window] -> X [(String, Window)]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM Window -> X (String, Window)
pair
      pair :: Window -> X (String, Window)
pair Window
w = do String
name <- NamedWindow -> String
forall a. Show a => a -> String
show (NamedWindow -> String) -> X NamedWindow -> X String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Window -> X NamedWindow
getName Window
w
                  (String, Window) -> X (String, Window)
forall (m :: * -> *) a. Monad m => a -> m a
return (String
name, Window
w)

-- | A Map where keys are pretty printable window names and values are
-- Xmonad windows identifier.
type XWindowMap = X (M.Map String Window)

-- | Pops open a prompt with window titles belonging to
-- winmap. Choose one, and an action is applied on the
-- selected window, according to WindowPrompt.
windowPrompt :: XPConfig -> WindowPrompt -> XWindowMap -> X ()
windowPrompt :: XPConfig -> WindowPrompt -> XWindowMap -> X ()
windowPrompt XPConfig
c WindowPrompt
t XWindowMap
winmap = do
  Map String Window
wm <- XWindowMap
winmap
  let mode :: WindowModePrompt
mode     = WindowPrompt
-> Map String Window
-> (String -> String -> Bool)
-> WindowModePrompt
WindowModePrompt WindowPrompt
t Map String Window
wm (XPConfig -> String -> String -> Bool
searchPredicate XPConfig
c)
      action :: String -> String -> X ()
action   = WindowModePrompt -> String -> String -> X ()
forall t. XPrompt t => t -> String -> String -> X ()
modeAction WindowModePrompt
mode
      compList :: ComplFunction
compList = WindowModePrompt -> ComplFunction
forall t. XPrompt t => t -> ComplFunction
completionFunction WindowModePrompt
mode
  WindowPrompt
-> XPConfig -> ComplFunction -> (String -> X ()) -> X ()
forall p.
XPrompt p =>
p -> XPConfig -> ComplFunction -> (String -> X ()) -> X ()
mkXPrompt WindowPrompt
t XPConfig
c ComplFunction
compList (\String
s -> String -> String -> X ()
action String
s String
s)

-- | Like 'windowPrompt', but uses the multiple modes feature of
-- @Prompt@ (via 'mkXPromptWithModes').
--
-- Given a list of actions along with the windows they should work
-- with, display the appropriate prompt with the ability to switch
-- between them using the @changeModeKey@.
--
-- For example, to have a prompt that first shows you all windows, but
-- allows you to narrow the list down to just the windows on the
-- current workspace:
--
-- > windowMultiPrompt config [(Goto, allWindows), (Goto, wsWindows)]
windowMultiPrompt :: XPConfig -> [(WindowPrompt, XWindowMap)] -> X ()
windowMultiPrompt :: XPConfig -> [(WindowPrompt, XWindowMap)] -> X ()
windowMultiPrompt XPConfig
c [(WindowPrompt, XWindowMap)]
modes = do
  [XPType]
modes' <- [(WindowPrompt, XWindowMap)]
-> ((WindowPrompt, XWindowMap) -> X XPType) -> X [XPType]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
t a -> (a -> m b) -> m (t b)
forM [(WindowPrompt, XWindowMap)]
modes (((WindowPrompt, XWindowMap) -> X XPType) -> X [XPType])
-> ((WindowPrompt, XWindowMap) -> X XPType) -> X [XPType]
forall a b. (a -> b) -> a -> b
$ \(WindowPrompt
t, XWindowMap
wm) -> do
    Map String Window
wm' <- XWindowMap
wm
    XPType -> X XPType
forall (m :: * -> *) a. Monad m => a -> m a
return (XPType -> X XPType)
-> (WindowModePrompt -> XPType) -> WindowModePrompt -> X XPType
forall b c a. (b -> c) -> (a -> b) -> a -> c
. WindowModePrompt -> XPType
forall p. XPrompt p => p -> XPType
XPT (WindowModePrompt -> X XPType) -> WindowModePrompt -> X XPType
forall a b. (a -> b) -> a -> b
$ WindowPrompt
-> Map String Window
-> (String -> String -> Bool)
-> WindowModePrompt
WindowModePrompt WindowPrompt
t Map String Window
wm' (XPConfig -> String -> String -> Bool
searchPredicate XPConfig
c)

  [XPType] -> XPConfig -> X ()
mkXPromptWithModes [XPType]
modes' XPConfig
c

-- | Brings a copy of the specified window into the current workspace.
bringCopyWindow :: Window -> WindowSet -> WindowSet
bringCopyWindow :: Window -> WindowSet -> WindowSet
bringCopyWindow Window
w WindowSet
ws = Window -> String -> WindowSet -> WindowSet
forall a i s l sd.
(Eq a, Eq i, Eq s) =>
a -> i -> StackSet i l a s sd -> StackSet i l a s sd
copyWindow Window
w (WindowSet -> String
forall i l a s sd. StackSet i l a s sd -> i
W.currentTag WindowSet
ws) WindowSet
ws