{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE RecordWildCards #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  XMonad.Actions.CopyWindow
-- Description :  Duplicate a window on multiple workspaces.
-- Copyright   :  (c) David Roundy <droundy@darcs.net>, Ivan Veselov <veselov@gmail.com>, Lanny Ripple <lan3ny@gmail.com>
-- License     :  BSD3-style (see LICENSE)
--
-- Maintainer  :  ???
-- Stability   :  unstable
-- Portability :  unportable
--
-- Provides bindings to duplicate a window on multiple workspaces,
-- providing dwm-like tagging functionality.
--
-----------------------------------------------------------------------------

module XMonad.Actions.CopyWindow (
                                 -- * Usage
                                 -- $usage
                                 copy, copyToAll, copyWindow, runOrCopy
                                 , killAllOtherCopies, kill1, taggedWindows, copiesOfOn
                                 -- * Highlight workspaces containing copies in logHook
                                 -- $logHook
                                 , wsContainingCopies, copiesPP
                                ) where

import XMonad
import XMonad.Prelude
import Control.Arrow ((&&&))
import qualified Data.List as L

import XMonad.Actions.WindowGo
import XMonad.Hooks.StatusBar.PP (PP(..), WS(..), isHidden)
import qualified XMonad.StackSet as W

-- $usage
--
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@ file:
--
-- > import XMonad.Actions.CopyWindow
--
-- Then add something like this to your keybindings:
--
-- > -- mod-[1..9] @@ Switch to workspace N
-- > -- mod-shift-[1..9] @@ Move client to workspace N
-- > -- mod-control-shift-[1..9] @@ Copy client to workspace N
-- > [((m .|. modm, k), windows $ f i)
-- >     | (i, k) <- zip (workspaces x) [xK_1 ..]
-- >     , (f, m) <- [(W.view, 0), (W.shift, shiftMask), (copy, shiftMask .|. controlMask)]]
--
-- To use the above key bindings you need also to import
-- "XMonad.StackSet":
--
-- > import qualified XMonad.StackSet as W
--
-- You may also wish to redefine the binding to kill a window so it only
-- removes it from the current workspace, if it's present elsewhere:
--
-- >  , ((modm .|. shiftMask, xK_c     ), kill1) -- @@ Close the focused window
--
-- Instead of copying a window from one workspace to another maybe you don't
-- want to have to remember where you placed it.  For that consider:
--
-- >  , ((modm, xK_b    ), runOrCopy "firefox" (className =? "Firefox")) -- @@ run or copy firefox
--
-- Another possibility which this extension provides is 'making window
-- always visible' (i.e. always on current workspace), similar to corresponding
-- metacity functionality. This behaviour is emulated through copying given
-- window to all the workspaces and then removing it when it's unneeded on
-- all workspaces any more.
--
-- Here is the example of keybindings which provide these actions:
--
-- >  , ((modm, xK_v ), windows copyToAll) -- @@ Make focused window always visible
-- >  , ((modm .|. shiftMask, xK_v ),  killAllOtherCopies) -- @@ Toggle window state back
--
-- For detailed instructions on editing your key bindings, see
-- "XMonad.Doc.Extending#Editing_key_bindings".

-- $logHook
--
-- To distinguish workspaces containing copies of the focused window, use 'copiesPP'.
-- 'copiesPP' takes a pretty printer and makes it aware of copies of the focused window.
-- It can be applied when creating a 'XMonad.Hooks.StatusBar.StatusBarConfig'.
--
-- A sample config looks like this:
--
-- > mySB = statusBarProp "xmobar" (copiesPP (pad . xmobarColor "red" "black") xmobarPP)
-- > main = xmonad $ withEasySB mySB defToggleStrutsKey def

-- | Take a pretty printer and make it aware of copies by using the provided function
-- to show hidden workspaces that contain copies of the focused window.
copiesPP :: (WorkspaceId -> String) -> PP -> X PP
copiesPP :: (WorkspaceId -> WorkspaceId) -> PP -> X PP
copiesPP WorkspaceId -> WorkspaceId
wtoS PP
pp = do
    [WorkspaceId]
copies <- X [WorkspaceId]
wsContainingCopies
    let check :: WS -> Bool
check WS{[Window]
WindowSet
WindowSpace
PP
wsPP :: WS -> PP
wsWS :: WS -> WindowSpace
wsWindowSet :: WS -> WindowSet
wsUrgents :: WS -> [Window]
wsPP :: PP
wsWS :: WindowSpace
wsWindowSet :: WindowSet
wsUrgents :: [Window]
..} = WindowSpace -> WorkspaceId
forall i l a. Workspace i l a -> i
W.tag WindowSpace
wsWS WorkspaceId -> [WorkspaceId] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [WorkspaceId]
copies
    let printer :: ReaderT WS Maybe (WorkspaceId -> WorkspaceId)
printer = ((WS -> Bool) -> ReaderT WS Maybe Bool
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks (WS -> Bool
isHidden (WS -> Bool) -> (WS -> Bool) -> WS -> Bool
forall (m :: * -> *). Monad m => m Bool -> m Bool -> m Bool
<&&> WS -> Bool
check) ReaderT WS Maybe Bool
-> (Bool -> ReaderT WS Maybe ()) -> ReaderT WS Maybe ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Bool -> ReaderT WS Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard) ReaderT WS Maybe ()
-> (WorkspaceId -> WorkspaceId)
-> ReaderT WS Maybe (WorkspaceId -> WorkspaceId)
forall (f :: * -> *) a b. Functor f => f a -> b -> f b
$> WorkspaceId -> WorkspaceId
wtoS
    PP -> X PP
forall (m :: * -> *) a. Monad m => a -> m a
return PP
pp{ ppPrinters :: ReaderT WS Maybe (WorkspaceId -> WorkspaceId)
ppPrinters = ReaderT WS Maybe (WorkspaceId -> WorkspaceId)
printer ReaderT WS Maybe (WorkspaceId -> WorkspaceId)
-> ReaderT WS Maybe (WorkspaceId -> WorkspaceId)
-> ReaderT WS Maybe (WorkspaceId -> WorkspaceId)
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> PP -> ReaderT WS Maybe (WorkspaceId -> WorkspaceId)
ppPrinters PP
pp }

-- | Copy the focused window to a workspace.
copy :: (Eq s, Eq i, Eq a) => i -> W.StackSet i l a s sd -> W.StackSet i l a s sd
copy :: forall s i a l sd.
(Eq s, Eq i, Eq a) =>
i -> StackSet i l a s sd -> StackSet i l a s sd
copy i
n StackSet i l a s sd
s | Just a
w <- StackSet i l a s sd -> Maybe a
forall i l a s sd. StackSet i l a s sd -> Maybe a
W.peek StackSet i l a s sd
s = a -> i -> StackSet i l a s sd -> StackSet i l a s sd
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 a
w i
n StackSet i l a s sd
s
         | Bool
otherwise = StackSet i l a s sd
s

-- | Copy the focused window to all workspaces.
copyToAll :: (Eq s, Eq i, Eq a) => W.StackSet i l a s sd -> W.StackSet i l a s sd
copyToAll :: forall s i a l sd.
(Eq s, Eq i, Eq a) =>
StackSet i l a s sd -> StackSet i l a s sd
copyToAll StackSet i l a s sd
s = (Workspace i l a -> StackSet i l a s sd -> StackSet i l a s sd)
-> StackSet i l a s sd -> [Workspace i l a] -> StackSet i l a s sd
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (i -> StackSet i l a s sd -> StackSet i l a s sd
forall s i a l sd.
(Eq s, Eq i, Eq a) =>
i -> StackSet i l a s sd -> StackSet i l a s sd
copy (i -> StackSet i l a s sd -> StackSet i l a s sd)
-> (Workspace i l a -> i)
-> Workspace i l a
-> StackSet i l a s sd
-> StackSet i l a s sd
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Workspace i l a -> i
forall i l a. Workspace i l a -> i
W.tag) StackSet i l a s sd
s (StackSet i l a s sd -> [Workspace i l a]
forall i l a s sd. StackSet i l a s sd -> [Workspace i l a]
W.workspaces StackSet i l a s sd
s)

-- | Copy an arbitrary window to a workspace.
copyWindow :: (Eq a, Eq i, Eq s) => a -> i -> W.StackSet i l a s sd -> W.StackSet i l a s sd
copyWindow :: 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 a
w i
n = StackSet i l a s sd -> StackSet i l a s sd
forall {s} {l} {sd}.
Eq s =>
StackSet i l a s sd -> StackSet i l a s sd
copy'
    where copy' :: StackSet i l a s sd -> StackSet i l a s sd
copy' StackSet i l a s sd
s = if i
n i -> StackSet i l a s sd -> Bool
forall i l a s sd. Eq i => i -> StackSet i l a s sd -> Bool
`W.tagMember` StackSet i l a s sd
s
                    then i -> StackSet i l a s sd -> StackSet i l a s sd
forall s i l a sd.
(Eq s, Eq i) =>
i -> StackSet i l a s sd -> StackSet i l a s sd
W.view (StackSet i l a s sd -> i
forall i l a s sd. StackSet i l a s sd -> i
W.currentTag StackSet i l a s sd
s) (StackSet i l a s sd -> StackSet i l a s sd)
-> StackSet i l a s sd -> StackSet i l a s sd
forall a b. (a -> b) -> a -> b
$ a -> StackSet i l a s sd -> StackSet i l a s sd
forall {a} {i} {l} {s} {sd}.
Eq a =>
a -> StackSet i l a s sd -> StackSet i l a s sd
insertUp' a
w (StackSet i l a s sd -> StackSet i l a s sd)
-> StackSet i l a s sd -> StackSet i l a s sd
forall a b. (a -> b) -> a -> b
$ i -> StackSet i l a s sd -> StackSet i l a s sd
forall s i l a sd.
(Eq s, Eq i) =>
i -> StackSet i l a s sd -> StackSet i l a s sd
W.view i
n StackSet i l a s sd
s
                    else StackSet i l a s sd
s
          insertUp' :: a -> StackSet i l a s sd -> StackSet i l a s sd
insertUp' a
a = Maybe (Stack a)
-> (Stack a -> Maybe (Stack a))
-> StackSet i l a s sd
-> StackSet i l a s sd
forall a i l s sd.
Maybe (Stack a)
-> (Stack a -> Maybe (Stack a))
-> StackSet i l a s sd
-> StackSet i l a s sd
W.modify (Stack a -> Maybe (Stack a)
forall a. a -> Maybe a
Just (Stack a -> Maybe (Stack a)) -> Stack a -> Maybe (Stack a)
forall a b. (a -> b) -> a -> b
$ a -> [a] -> [a] -> Stack a
forall a. a -> [a] -> [a] -> Stack a
W.Stack a
a [] [])
                          (\(W.Stack a
t [a]
l [a]
r) -> if a
a a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` a
ta -> [a] -> [a]
forall a. a -> [a] -> [a]
:[a]
l[a] -> [a] -> [a]
forall a. [a] -> [a] -> [a]
++[a]
r
                                             then Stack a -> Maybe (Stack a)
forall a. a -> Maybe a
Just (Stack a -> Maybe (Stack a)) -> Stack a -> Maybe (Stack a)
forall a b. (a -> b) -> a -> b
$ a -> [a] -> [a] -> Stack a
forall a. a -> [a] -> [a] -> Stack a
W.Stack a
t [a]
l [a]
r
                                             else Stack a -> Maybe (Stack a)
forall a. a -> Maybe a
Just (Stack a -> Maybe (Stack a)) -> Stack a -> Maybe (Stack a)
forall a b. (a -> b) -> a -> b
$ a -> [a] -> [a] -> Stack a
forall a. a -> [a] -> [a] -> Stack a
W.Stack a
a (a -> [a] -> [a]
forall a. Eq a => a -> [a] -> [a]
L.delete a
a [a]
l) (a -> [a] -> [a]
forall a. Eq a => a -> [a] -> [a]
L.delete a
a (a
ta -> [a] -> [a]
forall a. a -> [a] -> [a]
:[a]
r)))


-- | runOrCopy will run the provided shell command unless it can
--  find a specified window in which case it will copy the window to
--  the current workspace.  Similar to (i.e., stolen from) "XMonad.Actions.WindowGo".
runOrCopy :: String -> Query Bool -> X ()
runOrCopy :: WorkspaceId -> Query Bool -> X ()
runOrCopy = X () -> Query Bool -> X ()
copyMaybe (X () -> Query Bool -> X ())
-> (WorkspaceId -> X ()) -> WorkspaceId -> Query Bool -> X ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. WorkspaceId -> X ()
forall (m :: * -> *). MonadIO m => WorkspaceId -> m ()
spawn

-- | Copy a window if it exists, run the first argument otherwise.
copyMaybe :: X () -> Query Bool -> X ()
copyMaybe :: X () -> Query Bool -> X ()
copyMaybe X ()
f Query Bool
qry = Query Bool -> ManageHook -> X () -> X ()
ifWindow Query Bool
qry ManageHook
forall {l} {sd}.
Query (Endo (StackSet WorkspaceId l Window ScreenId sd))
copyWin X ()
f
    where copyWin :: Query (Endo (StackSet WorkspaceId l Window ScreenId sd))
copyWin = Query Window
forall r (m :: * -> *). MonadReader r m => m r
ask Query Window
-> (Window
    -> Query (Endo (StackSet WorkspaceId l Window ScreenId sd)))
-> Query (Endo (StackSet WorkspaceId l Window ScreenId sd))
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \Window
w -> (StackSet WorkspaceId l Window ScreenId sd
 -> StackSet WorkspaceId l Window ScreenId sd)
-> Query (Endo (StackSet WorkspaceId l Window ScreenId sd))
forall s. (s -> s) -> Query (Endo s)
doF (\StackSet WorkspaceId l Window ScreenId sd
ws -> Window
-> WorkspaceId
-> StackSet WorkspaceId l Window ScreenId sd
-> StackSet WorkspaceId l Window ScreenId sd
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 (StackSet WorkspaceId l Window ScreenId sd -> WorkspaceId
forall i l a s sd. StackSet i l a s sd -> i
W.currentTag StackSet WorkspaceId l Window ScreenId sd
ws) StackSet WorkspaceId l Window ScreenId sd
ws)

-- | Remove the focused window from this workspace.  If it's present in no
-- other workspace, then kill it instead. If we do kill it, we'll get a
-- delete notify back from X.
--
-- There are two ways to delete a window. Either just kill it, or if it
-- supports the delete protocol, send a delete event (e.g. firefox).
kill1 :: X ()
kill1 :: X ()
kill1 = do WindowSet
ss <- (XState -> WindowSet) -> X WindowSet
forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
gets XState -> WindowSet
windowset
           Maybe Window -> (Window -> X ()) -> X ()
forall (m :: * -> *) a. Monad m => Maybe a -> (a -> m ()) -> m ()
whenJust (WindowSet -> Maybe Window
forall i l a s sd. StackSet i l a s sd -> Maybe a
W.peek WindowSet
ss) ((Window -> X ()) -> X ()) -> (Window -> X ()) -> X ()
forall a b. (a -> b) -> a -> b
$ \Window
w -> if Window -> WindowSet -> Bool
forall a i l s sd. Eq a => a -> StackSet i l a s sd -> Bool
W.member Window
w (WindowSet -> Bool) -> WindowSet -> Bool
forall a b. (a -> b) -> a -> b
$ Window -> WindowSet -> WindowSet
forall {a} {i} {l} {s} {sd}.
Eq a =>
a -> StackSet i l a s sd -> StackSet i l a s sd
delete'' Window
w WindowSet
ss
                                      then (WindowSet -> WindowSet) -> X ()
windows ((WindowSet -> WindowSet) -> X ())
-> (WindowSet -> WindowSet) -> X ()
forall a b. (a -> b) -> a -> b
$ Window -> WindowSet -> WindowSet
forall {a} {i} {l} {s} {sd}.
Eq a =>
a -> StackSet i l a s sd -> StackSet i l a s sd
delete'' Window
w
                                      else X ()
kill
    where delete'' :: a -> StackSet i l a s sd -> StackSet i l a s sd
delete'' a
w = Maybe (Stack a)
-> (Stack a -> Maybe (Stack a))
-> StackSet i l a s sd
-> StackSet i l a s sd
forall a i l s sd.
Maybe (Stack a)
-> (Stack a -> Maybe (Stack a))
-> StackSet i l a s sd
-> StackSet i l a s sd
W.modify Maybe (Stack a)
forall a. Maybe a
Nothing ((a -> Bool) -> Stack a -> Maybe (Stack a)
forall a. (a -> Bool) -> Stack a -> Maybe (Stack a)
W.filter (a -> a -> Bool
forall a. Eq a => a -> a -> Bool
/= a
w))

-- | Kill all other copies of focused window (if they're present).
-- 'All other' means here 'copies which are not on the current workspace'.
killAllOtherCopies :: X ()
killAllOtherCopies :: X ()
killAllOtherCopies = do WindowSet
ss <- (XState -> WindowSet) -> X WindowSet
forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
gets XState -> WindowSet
windowset
                        Maybe Window -> (Window -> X ()) -> X ()
forall (m :: * -> *) a. Monad m => Maybe a -> (a -> m ()) -> m ()
whenJust (WindowSet -> Maybe Window
forall i l a s sd. StackSet i l a s sd -> Maybe a
W.peek WindowSet
ss) ((Window -> X ()) -> X ()) -> (Window -> X ()) -> X ()
forall a b. (a -> b) -> a -> b
$ \Window
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
W.view (WindowSet -> WorkspaceId
forall i l a s sd. StackSet i l a s sd -> i
W.currentTag WindowSet
ss) (WindowSet -> WindowSet)
-> (WindowSet -> WindowSet) -> WindowSet -> WindowSet
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
                                                   Window -> WindowSet -> WindowSet
forall {sid} {i} {a} {l} {sd}.
(Eq sid, Eq i, Eq a) =>
a -> StackSet i l a sid sd -> StackSet i l a sid sd
delFromAllButCurrent Window
w
    where
      delFromAllButCurrent :: a -> StackSet i l a sid sd -> StackSet i l a sid sd
delFromAllButCurrent a
w StackSet i l a sid sd
ss = (Workspace i l a -> StackSet i l a sid sd -> StackSet i l a sid sd)
-> StackSet i l a sid sd
-> [Workspace i l a]
-> StackSet i l a sid sd
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (a -> i -> StackSet i l a sid sd -> StackSet i l a sid sd
forall {s} {i} {a} {l} {sd}.
(Eq s, Eq i, Eq a) =>
a -> i -> StackSet i l a s sd -> StackSet i l a s sd
delWinFromWorkspace a
w (i -> StackSet i l a sid sd -> StackSet i l a sid sd)
-> (Workspace i l a -> i)
-> Workspace i l a
-> StackSet i l a sid sd
-> StackSet i l a sid sd
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Workspace i l a -> i
forall i l a. Workspace i l a -> i
W.tag)
                                  StackSet i l a sid sd
ss
                                  (StackSet i l a sid sd -> [Workspace i l a]
forall i l a s sd. StackSet i l a s sd -> [Workspace i l a]
W.hidden 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
W.workspace (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]
W.visible StackSet i l a sid sd
ss))
      delWinFromWorkspace :: a -> i -> StackSet i l a s sd -> StackSet i l a s sd
delWinFromWorkspace a
w i
wid = i
-> (StackSet i l a s sd -> StackSet i l a s sd)
-> StackSet i l a s sd
-> StackSet i l a s sd
forall {s} {i} {s} {l} {a} {sd} {l} {a} {sd}.
(Eq s, Eq i, Eq s) =>
i
-> (StackSet i l a s sd -> StackSet i l a s sd)
-> StackSet i l a s sd
-> StackSet i l a s sd
viewing i
wid ((StackSet i l a s sd -> StackSet i l a s sd)
 -> StackSet i l a s sd -> StackSet i l a s sd)
-> (StackSet i l a s sd -> StackSet i l a s sd)
-> StackSet i l a s sd
-> StackSet i l a s sd
forall a b. (a -> b) -> a -> b
$ Maybe (Stack a)
-> (Stack a -> Maybe (Stack a))
-> StackSet i l a s sd
-> StackSet i l a s sd
forall a i l s sd.
Maybe (Stack a)
-> (Stack a -> Maybe (Stack a))
-> StackSet i l a s sd
-> StackSet i l a s sd
W.modify Maybe (Stack a)
forall a. Maybe a
Nothing ((a -> Bool) -> Stack a -> Maybe (Stack a)
forall a. (a -> Bool) -> Stack a -> Maybe (Stack a)
W.filter (a -> a -> Bool
forall a. Eq a => a -> a -> Bool
/= a
w))

      viewing :: i
-> (StackSet i l a s sd -> StackSet i l a s sd)
-> StackSet i l a s sd
-> StackSet i l a s sd
viewing i
wis StackSet i l a s sd -> StackSet i l a s sd
f StackSet i l a s sd
ss = i -> StackSet i l a s sd -> StackSet i l a s sd
forall s i l a sd.
(Eq s, Eq i) =>
i -> StackSet i l a s sd -> StackSet i l a s sd
W.view (StackSet i l a s sd -> i
forall i l a s sd. StackSet i l a s sd -> i
W.currentTag StackSet i l a s sd
ss) (StackSet i l a s sd -> StackSet i l a s sd)
-> StackSet i l a s sd -> StackSet i l a s sd
forall a b. (a -> b) -> a -> b
$ StackSet i l a s sd -> StackSet i l a s sd
f (StackSet i l a s sd -> StackSet i l a s sd)
-> StackSet i l a s sd -> StackSet i l a s sd
forall a b. (a -> b) -> a -> b
$ i -> StackSet i l a s sd -> StackSet i l a s sd
forall s i l a sd.
(Eq s, Eq i) =>
i -> StackSet i l a s sd -> StackSet i l a s sd
W.view i
wis StackSet i l a s sd
ss

-- | A list of hidden workspaces containing a copy of the focused window.
wsContainingCopies :: X [WorkspaceId]
wsContainingCopies :: X [WorkspaceId]
wsContainingCopies = do
    WindowSet
ws <- (XState -> WindowSet) -> X WindowSet
forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
gets XState -> WindowSet
windowset
    [WorkspaceId] -> X [WorkspaceId]
forall (m :: * -> *) a. Monad m => a -> m a
return ([WorkspaceId] -> X [WorkspaceId])
-> [WorkspaceId] -> X [WorkspaceId]
forall a b. (a -> b) -> a -> b
$ Maybe Window -> [(WorkspaceId, [Window])] -> [WorkspaceId]
forall a i. Eq a => Maybe a -> [(i, [a])] -> [i]
copiesOfOn (WindowSet -> Maybe Window
forall i l a s sd. StackSet i l a s sd -> Maybe a
W.peek WindowSet
ws) ([WindowSpace] -> [(WorkspaceId, [Window])]
forall i l a. [Workspace i l a] -> [(i, [a])]
taggedWindows ([WindowSpace] -> [(WorkspaceId, [Window])])
-> [WindowSpace] -> [(WorkspaceId, [Window])]
forall a b. (a -> b) -> a -> b
$ WindowSet -> [WindowSpace]
forall i l a s sd. StackSet i l a s sd -> [Workspace i l a]
W.hidden WindowSet
ws)

-- | Get a list of tuples (tag, [Window]) for each workspace.
taggedWindows :: [W.Workspace i l a] -> [(i, [a])]
taggedWindows :: forall i l a. [Workspace i l a] -> [(i, [a])]
taggedWindows = (Workspace i l a -> (i, [a])) -> [Workspace i l a] -> [(i, [a])]
forall a b. (a -> b) -> [a] -> [b]
map ((Workspace i l a -> (i, [a])) -> [Workspace i l a] -> [(i, [a])])
-> (Workspace i l a -> (i, [a])) -> [Workspace i l a] -> [(i, [a])]
forall a b. (a -> b) -> a -> b
$ Workspace i l a -> i
forall i l a. Workspace i l a -> i
W.tag (Workspace i l a -> i)
-> (Workspace i l a -> [a]) -> Workspace i l a -> (i, [a])
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& Maybe (Stack a) -> [a]
forall a. Maybe (Stack a) -> [a]
W.integrate' (Maybe (Stack a) -> [a])
-> (Workspace i l a -> Maybe (Stack a)) -> Workspace i l a -> [a]
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)
W.stack

-- | Get tags with copies of the focused window (if present.)
copiesOfOn :: (Eq a) => Maybe a -> [(i, [a])] -> [i]
copiesOfOn :: forall a i. Eq a => Maybe a -> [(i, [a])] -> [i]
copiesOfOn Maybe a
foc [(i, [a])]
tw = [i] -> (a -> [i]) -> Maybe a -> [i]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe [] a -> [i]
hasCopyOf Maybe a
foc
  where hasCopyOf :: a -> [i]
hasCopyOf a
f = ((i, [a]) -> i) -> [(i, [a])] -> [i]
forall a b. (a -> b) -> [a] -> [b]
map (i, [a]) -> i
forall a b. (a, b) -> a
fst ([(i, [a])] -> [i]) -> [(i, [a])] -> [i]
forall a b. (a -> b) -> a -> b
$ ((i, [a]) -> Bool) -> [(i, [a])] -> [(i, [a])]
forall a. (a -> Bool) -> [a] -> [a]
filter ((a
f a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ) ([a] -> Bool) -> ((i, [a]) -> [a]) -> (i, [a]) -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (i, [a]) -> [a]
forall a b. (a, b) -> b
snd) [(i, [a])]
tw