-----------------------------------------------------------------------------
-- |
-- Module      :  XMonad.Prompt.Window
-- 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,
    allWindows,
    wsWindows,
    XWindowMap,

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

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
instance XPrompt WindowPrompt where
    showXPrompt Goto      = "Go to window: "
    showXPrompt Bring     = "Bring window: "
    showXPrompt BringToMaster
                          = "Bring window to master: "
    showXPrompt BringCopy = "Bring a copy: "
    commandToComplete _ c = c
    nextCompletion      _ = getNextCompletion

-- | Deprecated. Use windowPrompt instead.
windowPromptGoto, windowPromptBring, windowPromptBringCopy :: XPConfig -> X ()
windowPromptGoto c = windowPrompt c Goto windowMap
windowPromptBring c = windowPrompt c Bring windowMap
windowPromptBringCopy c = windowPrompt c BringCopy windowMap

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

-- | A helper to get the map of windows of the current workspace.
wsWindows :: XWindowMap
wsWindows = withWindowSet (return . W.index) >>= winmap
    where
      winmap = fmap M.fromList . mapM pair
      pair w = do name <- fmap show $ getName w
                  return (name, 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 c t winmap = do
  a <- case t of
         Goto  -> fmap gotoAction  winmap
         Bring -> fmap bringAction winmap
         BringCopy -> fmap bringCopyAction winmap
         BringToMaster -> fmap bringToMaster winmap
  wm <- winmap
  mkXPrompt t c (compList wm) a

    where
      winAction a m    = flip whenJust (windows . a) . flip M.lookup m
      gotoAction       = winAction W.focusWindow
      bringAction      = winAction bringWindow
      bringCopyAction  = winAction bringCopyWindow
      bringToMaster    = winAction (\w s -> W.shiftMaster . W.focusWindow w $ bringWindow w s)

      compList m s = return . filter (searchPredicate c s) . map fst . M.toList $ m

-- | Brings a copy of the specified window into the current workspace.
bringCopyWindow :: Window -> WindowSet -> WindowSet
bringCopyWindow w ws = copyWindow w (W.currentTag ws) ws