{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, TypeSynonymInstances, FlexibleContexts #-} ----------------------------------------------------------------------------- -- | -- Module : XMonad.Layout.IM -- Copyright : (c) Roman Cheplyaka, Ivan N. Veselov -- License : BSD-style (see LICENSE) -- -- Maintainer : Roman Cheplyaka -- Stability : unstable -- Portability : unportable -- -- Layout modfier suitable for workspace with multi-windowed instant messenger -- (like Psi or Tkabber). -- ----------------------------------------------------------------------------- module XMonad.Layout.IM ( -- * Usage -- $usage -- * Hints -- $hints -- * TODO -- $todo Property(..), IM(..), withIM, gridIM, ) where import XMonad import qualified XMonad.StackSet as S import XMonad.Layout.Grid import XMonad.Layout.LayoutModifier import XMonad.Util.WindowProperties -- $usage -- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@: -- -- > import XMonad.Layout.IM -- > import Data.Ratio ((%)) -- -- Then edit your @layoutHook@ by adding IM modifier to layout which you prefer -- for managing your chat windows (Grid in this example, another useful choice -- to consider is Tabbed layout). -- -- > myLayout = withIM (1%7) (ClassName "Tkabber") Grid ||| Full ||| etc.. -- > main = xmonad defaultConfig { layoutHook = myLayout } -- -- Here @1%7@ is the part of the screen which your roster will occupy, -- @ClassName \"Tkabber\"@ tells xmonad which window is actually your roster. -- -- Screenshot: -- -- For more detailed instructions on editing the layoutHook see: -- -- "XMonad.Doc.Extending#Editing_the_layout_hook" -- $hints -- -- To launch IM layout automatically on your IM workspace use "XMonad.Layout.PerWorkspace". -- -- By default the roster window will appear on the left side. -- To place roster window on the right side, use @reflectHoriz@ from -- "XMonad.Layout.Reflect" module. -- $todo -- This item are questionable. Please let me know if you find them useful. -- -- * shrink\/expand -- -- | Data type for LayoutModifier which converts given layout to IM-layout -- (with dedicated space for the roster and original layout for chat windows) data AddRoster a = AddRoster Rational Property deriving (Read, Show) instance LayoutModifier AddRoster Window where modifyLayout (AddRoster ratio prop) = applyIM ratio prop modifierDescription _ = "IM" -- | Modifier which converts given layout to IM-layout (with dedicated -- space for roster and original layout for chat windows) withIM :: LayoutClass l a => Rational -> Property -> l a -> ModifiedLayout AddRoster l a withIM ratio prop = ModifiedLayout $ AddRoster ratio prop -- | IM layout modifier applied to the Grid layout gridIM :: Rational -> Property -> ModifiedLayout AddRoster Grid a gridIM ratio prop = withIM ratio prop Grid -- | Internal function for adding space for the roster specified by -- the property and running original layout for all chat windows applyIM :: (LayoutClass l Window) => Rational -> Property -> S.Workspace WorkspaceId (l Window) Window -> Rectangle -> X ([(Window, Rectangle)], Maybe (l Window)) applyIM ratio prop wksp rect = do let stack = S.stack wksp let ws = S.integrate' $ stack let (masterRect, slaveRect) = splitHorizontallyBy ratio rect master <- findM (hasProperty prop) ws case master of Just w -> do let filteredStack = stack >>= S.filter (w /=) wrs <- runLayout (wksp {S.stack = filteredStack}) slaveRect return ((w, masterRect) : fst wrs, snd wrs) Nothing -> runLayout wksp rect -- | Like find, but works with monadic computation instead of pure function. findM :: Monad m => (a -> m Bool) -> [a] -> m (Maybe a) findM _ [] = return Nothing findM f (x:xs) = do { b <- f x; if b then return (Just x) else findM f xs } -- | This is for compatibility with old configs only and will be removed in future versions! data IM a = IM Rational Property deriving (Read, Show) instance LayoutClass IM Window where description _ = "IM" doLayout (IM r prop) rect stack = do let ws = S.integrate stack let (masterRect, slaveRect) = splitHorizontallyBy r rect master <- findM (hasProperty prop) ws let positions = case master of Just w -> (w, masterRect) : arrange defaultRatio slaveRect (filter (w /=) ws) Nothing -> arrange defaultRatio rect ws return (positions, Nothing)