module Rahm.Desktop.Marking ( markCurrentWindow, jumpToMark, setAlternateWindows, getAlternateWindows, setAlternateWorkspace, getAlternateWorkspace, getMarkedLocations, markAllLocations, farLeftWindow, farRightWindow, windowLocation ) where import Prelude hiding (head) import Data.Maybe (fromMaybe) import Control.Monad.Trans (lift) import Data.Ord (Down(..)) import Control.Exception import Control.Monad (when, (<=<)) import Control.Monad.Trans.Maybe import Data.Char (isAlpha, isDigit, ord) import Data.IORef import Data.List (sortOn, sort, sortBy, find) import Data.List.Safe (head) import Data.Map (Map) import Data.Maybe (catMaybes) import Data.Sequence (Seq(..)) import Rahm.Desktop.Common import Rahm.Desktop.History import Rahm.Desktop.Hooks.WindowChange import Rahm.Desktop.Windows (mapWindows, findWindow, getLocationWorkspace) import Rahm.Desktop.Workspaces import System.Environment import System.FilePath import System.IO import XMonad import XMonad.StackSet hiding (focus) import qualified Data.Map as Map import qualified Data.Sequence as Seq import qualified XMonad.Util.ExtensibleState as XS {- Submodule that handles marking windows so they can be jumped back to. -} type Mark = String data MarkState = MarkState { markStateMap :: Map Mark [Location] , alternateWindows :: [Window] , alternateWorkspaces :: Map Window WorkspaceId } deriving (Read, Show) instance ExtensionClass MarkState where initialValue = MarkState Map.empty [] Map.empty extensionType = PersistentExtension -- greedyFocus :: Window -> X () -- greedyFocus win = do -- ws <- withWindowSet $ \ss -> -- return $ getLocationWorkspace =<< findWindow ss win -- -- mapM_ (windows . greedyView . tag) ws -- focus win setAlternateWorkspace :: Window -> WorkspaceId -> X () setAlternateWorkspace win wid = XS.modify $ \m -> m { alternateWorkspaces = Map.insert win wid (alternateWorkspaces m) } getAlternateWorkspace :: Window -> X (Maybe WorkspaceId) getAlternateWorkspace window = Map.lookup window . alternateWorkspaces <$> XS.get setAlternateWindows :: [Window] -> X () setAlternateWindows wins = XS.modify (\m -> m { alternateWindows = wins }) getAlternateWindows :: X [Window] getAlternateWindows = alternateWindows <$> XS.get withMaybeFocused :: (Maybe Window -> X a) -> X a withMaybeFocused f = withWindowSet $ f . peek markAllLocations :: Mark -> [Location] -> X () markAllLocations mark locs = XS.modify $ \m -> m { markStateMap = Map.insert mark locs (markStateMap m) } markCurrentWindow :: Mark -> X () markCurrentWindow mark = do ws <- getCurrentWorkspace withFocused $ \win -> XS.modify $ \state@MarkState {markStateMap = ms} -> state { markStateMap = Map.insertWith (++) mark [Location ws $ Just win] ms } jumpToMark :: Mark -> X () jumpToMark mark = do MarkState {markStateMap = m} <- XS.get mapM_ focusLocation $ head =<< Map.lookup mark m getMarkedLocations :: Mark -> X [Location] getMarkedLocations mark = do MarkState {markStateMap = m} <- XS.get return (fromMaybe [] $ Map.lookup mark m) setFocusedWindow :: a -> StackSet i l a s sd -> StackSet i l a s sd setFocusedWindow window (StackSet (Screen (Workspace t l stack) a b) vis hid float) = let newStack = case stack of Nothing -> Nothing Just (Stack _ up down) -> Just (Stack window up down) in StackSet (Screen (Workspace t l newStack) a b) vis hid float swapWithFocused :: (Ord a) => a -> StackSet i l a s sd -> StackSet i l a s sd swapWithFocused winToSwap stackSet = case peek stackSet of Nothing -> stackSet Just focused -> do setFocusedWindow winToSwap $ mapWindows ( \w -> if w == winToSwap then focused else w) stackSet windowRect :: Window -> X (Maybe Rectangle) windowRect win = withDisplay $ \dpy -> (do (_, x, y, w, h, bw, _) <- io $ getGeometry dpy win return $ Just $ Rectangle x y (w + 2 * bw) (h + 2 * bw)) `catchX` return Nothing getWindowsAndRects :: X [(Window, Rectangle)] getWindowsAndRects = catMaybes <$> (mapM (\w -> fmap (w,) <$> windowRect w) =<< withWindowSet (return . allWindows)) windowLocation :: Window -> MaybeT X Location windowLocation win = do tag <- MaybeT $ withWindowSet $ return . findTag win return (Location tag (Just win)) farLeftWindow :: MaybeT X Location farLeftWindow = do rects <- lift $ sortOn (\(_, Rectangle x _ _ _) -> x) <$> getWindowsAndRects case rects of ((w, _) : _) -> windowLocation w _ -> MaybeT (return Nothing) farRightWindow :: MaybeT X Location farRightWindow = do rects <- lift $ sortOn (Down . \(_, Rectangle x _ _ _) -> x) <$> getWindowsAndRects case rects of ((w, _) : _) -> windowLocation w _ -> MaybeT (return Nothing)