content: {-# LANGUAGE MultiParamTypeClasses, DeriveDataTypeable, TypeSynonymInstances, FlexibleContexts, PatternGuards #-}
  ----------------------------------------------------------------------------
  -- |
  -- Module      :  XMonad.Layout.Minimize
  -- Copyright   :  (c) Jan Vornberger 2009, Alejandro Serrano 2010
  -- License     :  BSD3-style (see LICENSE)
  --
  -- Maintainer  :  jan.vornberger@informatik.uni-oldenburg.de
  -- Stability   :  unstable
  -- Portability :  not portable
  --
  -- Makes it possible to minimize windows, temporarily removing them
  -- from the layout until they are restored.
  --
  -----------------------------------------------------------------------------
  
  module XMonad.Layout.Minimize (
          -- * Usage
          -- $usage
          minimize,
          minimizeWindow,
          MinimizeMsg(RestoreMinimizedWin,RestoreNextMinimizedWin,MinimizeWin),
          Minimize,
          getMinimizedWindows,
          getCurrentMinimizedWindows,
          setMinimized,
          setNotMinimized
      ) where
  
  import XMonad
  import qualified XMonad.StackSet as W
  import XMonad.Layout.LayoutModifier
  import XMonad.Layout.BoringWindows as BW
  import XMonad.Util.WindowProperties (getProp32)
  import Data.List
  import qualified Data.Map as M
  import qualified XMonad.Util.ExtensibleState as XS
  import Data.Maybe
  import Foreign.C.Types (CLong)
  
  -- $usage
  -- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
  --
  -- > import XMonad.Layout.Minimize
  --
  -- Then edit your @layoutHook@ by adding the Minimize layout modifier:
  --
  -- > myLayout = minimize (Tall 1 (3/100) (1/2)) ||| Full ||| etc..
  -- > main = xmonad def { layoutHook = myLayout }
  --
  -- For more detailed instructions on editing the layoutHook see:
  --
  -- "XMonad.Doc.Extending#Editing_the_layout_hook"
  --
  -- In the key-bindings, do something like:
  --
  -- >        , ((modm,               xK_m     ), withFocused minimizeWindow)
  -- >        , ((modm .|. shiftMask, xK_m     ), sendMessage RestoreNextMinimizedWin)
  --
  -- The first action will minimize the focused window, while the second one will restore
  -- the next minimized window.
  --
  -- For detailed instruction on editing the key binding see:
  --
  -- "XMonad.Doc.Extending#Editing_key_bindings".
  --
  -- The module is designed to work together with "XMonad.Layout.BoringWindows" so
  -- that minimized windows will be skipped over when switching the focused window with
  -- the keyboard. Include 'BW.boringWindows' in your layout hook and see the
  -- documentation of "XMonad.Layout.BoringWindows" on how to modify your keybindings.
  --
  -- Also see "XMonad.Hooks.Minimize" if you want to be able to minimize
  -- and restore windows from your taskbar.
  
  data Minimize a = Minimize [Window] (M.Map Window W.RationalRect) deriving ( Read, Show )
  minimize :: LayoutClass l Window => l Window -> ModifiedLayout Minimize l Window
  minimize = ModifiedLayout $ Minimize [] M.empty
  
  data MinimizeMsg = MinimizeWin Window
                      | RestoreMinimizedWin Window
                      | RestoreNextMinimizedWin
                      | MinimizedSave
                      deriving (Typeable, Eq)
  instance Message MinimizeMsg
  
  minimizeWindow :: Window -> X ()
  minimizeWindow w = sendMessage (MinimizeWin w) >> BW.focusDown
  
  setMinimizedState :: Window -> Int -> (CLong -> [CLong] -> [CLong]) -> X ()
  setMinimizedState win st f = do
      setWMState win st
      withDisplay $ \dpy -> do
          wm_state <- getAtom "_NET_WM_STATE"
          mini <- getAtom "_NET_WM_STATE_HIDDEN"
          wstate <- fromMaybe [] `fmap` getProp32 wm_state win
          let ptype = 4 -- The atom property type for changeProperty
              fi_mini = fromIntegral mini
          io $ changeProperty32 dpy win wm_state ptype propModeReplace (f fi_mini wstate)
  
  setMinimized :: Window -> X ()
  setMinimized win = setMinimizedState win iconicState (:)
  
  setNotMinimized :: Window -> X ()
  setNotMinimized win = setMinimizedState win normalState delete
  
  data MinimizedWindows = MinimizedWindows [Window] deriving (Read, Show, Typeable)
  instance ExtensionClass MinimizedWindows where
      initialValue = MinimizedWindows []
      extensionType = PersistentExtension
  
  getMinimizedWindows l = do
      res <- handleMessage l (SomeMessage MinimizedSave) `catchX` return Nothing
      case res of
           Just _ -> do
               MinimizedWindows wins <- XS.get
               return wins
           _ -> return []
  
  getCurrentMinimizedWindows = gets (W.layout . W.workspace . W.current . windowset) >>= getMinimizedWindows
  
  -- filter the list such that those non-existent ones are avoided
  correctWins ls ws = filter (`elem` (W.allWindows ws)) ls
  
  instance LayoutModifier Minimize Window where
      modifierDescription _ = "Minimize"
  
      modifyLayout (Minimize minimized _) wksp rect = do
          let stack = W.stack wksp
              filtStack = stack >>=W.filter (\w -> not (w `elem` minimized))
          runLayout (wksp {W.stack = filtStack}) rect
  
      handleMess ol@(Minimize minimized unfloated) m
          | Just (MinimizeWin w) <- fromMessage m, not (w `elem` minimized) = do
                  setMinimized w
                  ws <- gets windowset
                  case M.lookup w (W.floating ws) of
                    Nothing -> return $ Just $ Minimize (w:minimized) unfloated
                    Just r -> do
                      modify (\s -> s { windowset = W.sink w ws})
                      return $ Just $ Minimize (w:minimized) (M.insert w r unfloated)
          | Just (RestoreMinimizedWin w) <- fromMessage m = do
              setNotMinimized w
              case M.lookup w unfloated of
                Nothing -> return $ Just $ Minimize (minimized \\ [w]) unfloated
                Just r -> do
                  ws <- gets windowset
                  modify (\s -> s { windowset = W.float w r ws})
                  return $ Just $ Minimize (minimized \\ [w]) (M.delete w unfloated)
          | Just RestoreNextMinimizedWin <- fromMessage m = do
            ws <- gets windowset
            let cminimized = correctWins minimized ws
            -- should check if the window is valid (still exists)
            if not (null cminimized)
              then case M.lookup (head cminimized) unfloated of
                Nothing -> do
                  let w = head cminimized
                  setNotMinimized w
                  modify (\s -> s { windowset = W.focusWindow w ws})
                  return $ Just $ Minimize (tail cminimized) unfloated
                Just r -> do
                  let w = head cminimized
                  setNotMinimized w
                  modify (\s -> s { windowset = (W.focusWindow w . W.float w r) ws})
                  return $ Just $ Minimize (tail cminimized) (M.delete w unfloated)
              else return Nothing
          | Just MinimizedSave <- fromMessage m = do
              -- just save the minimized list to the data
              ws <- gets windowset
              XS.put $ MinimizedWindows (correctWins minimized ws)
              return $ Just ol
          | Just BW.UpdateBoring <- fromMessage m = do
              ws <- gets (W.workspace . W.current . windowset)
              flip sendMessageWithNoRefresh ws $ BW.Replace "Minimize" minimized
              return Nothing
          | otherwise = return Nothing
  