git.fiddlerwoaroof.com
Raw Blame History
{-# LANGUAGE RankNTypes #-}

import Control.Monad

import Data.List
import Data.Maybe
import Data.Monoid
import Data.Ratio ((%))
import Data.Word

import System.IO

import XMonad hiding ( (|||) )
import XMonad.Actions.CopyWindow(copy,copyWindow,kill1,killAllOtherCopies)
import XMonad.Actions.CycleWindows()
import XMonad.Actions.DynamicWorkspaces
import XMonad.Actions.GridSelect
import XMonad.Actions.PhysicalScreens
import XMonad.Actions.SpawnOn
import XMonad.Core()
import XMonad.Hooks.DynamicLog
import XMonad.Hooks.EwmhDesktops
import XMonad.Hooks.ICCCMFocus
import XMonad.Hooks.ManageDocks
import XMonad.Layout.Accordion
import XMonad.Layout.Circle
import XMonad.Layout.Combo()
import XMonad.Layout.Decoration
import XMonad.Layout.DragPane
import XMonad.Layout.Gaps
import XMonad.Layout.Grid
import XMonad.Layout.IM
import XMonad.Layout.LayoutCombinators
import XMonad.Layout.LayoutModifier()
import XMonad.Layout.Maximize
import XMonad.Layout.NoBorders
import XMonad.Layout.OneBig
import XMonad.Layout.PerWorkspace
import XMonad.Layout.Reflect()
import XMonad.Layout.Renamed
import XMonad.Layout.ResizableTile
import XMonad.Layout.Simplest
import XMonad.Layout.Spiral
import XMonad.Layout.SubLayouts()
import XMonad.Layout.Tabbed
import XMonad.Layout.ThreeColumns
import XMonad.Layout.TwoPane
import XMonad.Layout.WindowNavigation
import XMonad.Prompt
import XMonad.StackSet as W
import XMonad.Util.Dzen
import XMonad.Util.EZConfig(additionalKeys)
import XMonad.Util.Loggers
import XMonad.Util.NamedScratchpad
import XMonad.Util.Run(spawnPipe)
import XMonad.Util.Scratchpad

nmaster :: Int
nmaster = 1

ratio = 13/21

delta = 3/100

tiled :: Tall a
tiled = Tall nmaster delta ratio

myTabbed :: ModifiedLayout Rename (ModifiedLayout (Decoration TabbedDecoration DefaultShrinker) Simplest) Word64
myTabbed = renamed [Replace "Tabbed"] $ tabbedBottom shrinkText defaultTheme

threeLayout :: ThreeCol a
threeLayout = ThreeColMid nmaster delta ratio

writingLayout :: ModifiedLayout Rename (ModifiedLayout Gaps Full) a
writingLayout = renamed [Replace "Writing"] $ gaps [(L,500), (R, 500)] Full

imLayout :: ModifiedLayout Rename (ModifiedLayout AddRoster Accordion) Window
imLayout = renamed [Replace "im"] $ withIM myRatio empathyRoster Accordion where
    myRatio           = 1%6
    empathyRoster   = And (ClassName "Empathy") (Role "contact_list")


base :: NewSelect
  (ModifiedLayout Rename (Mirror ThreeCol))
  (NewSelect (ModifiedLayout Rename (Mirror Tall))
   (NewSelect Full
    (NewSelect Accordion
     (NewSelect Circle
      (NewSelect SpiralWithDir
       (NewSelect (ModifiedLayout Rename (ModifiedLayout (Decoration TabbedDecoration DefaultShrinker) Simplest))
        (NewSelect (ModifiedLayout Rename (Mirror Accordion))
         (NewSelect (ModifiedLayout Rename (ModifiedLayout Gaps Full))
          (NewSelect (ModifiedLayout Rename DragPane)
           (NewSelect (ModifiedLayout Rename Grid)
            (NewSelect TwoPane OneBig))))))))))) Word64
base = mthree ||| wide ||| Full ||| Accordion ||| Circle ||| spiral (6/7)
   ||| myTabbed ||| wideAccordion ||| writingLayout ||| rows ||| grid
   ||| TwoPane (3/100) (1/2) ||| OneBig (3/4) (3/4)
  where
    mthree = renamed [Replace "ThreeWide"] $ Mirror threeLayout
    wide = renamed [Replace "Wide"] $ Mirror tiled
    wideAccordion = renamed [Replace "WideAccordion"] $ Mirror Accordion
    grid = renamed [Replace "Gridding"] $ GridRatio (1/2)
    rows = renamed [Replace "WritingNew"] $ dragPane Vertical 1 0.5


{-
 -
 -}

tmp = onWorkspace "web" (myTabbed ||| Full ||| TwoPane (3/100) (1/2)) $
      onWorkspace "terminal" (tiled ||| threeLayout ||| Full) $
      onWorkspace "IM" imLayout $
      --onWorkspace "images" (gimpLayout ||| tiled ||| threeLayout ||| base) $
       tiled ||| threeLayout ||| base
 --where
 --  gimpLayout = renamed [Replace "gimp"] $ withIM (11/64) (Role "gimp-toolbox") $ ResizableTall 2 (1/118) (11/20) [1]

myLayout = avoidStruts $ smartBorders tmp

myDzenConfig :: DzenConfig
myDzenConfig = timeout 1 >=> onCurr (vCenter 100)
                         >=> onCurr (hCenter 300)
                         >=> XMonad.Util.Dzen.font "xft:Source Code Pro:size=20:antialias=true"

makeLayoutList :: [t] -> [(t,t)]
makeLayoutList = map (\ l -> (l, l))

--changeLayout :: Maybe String -> X ()
--changeLayout a =
--  case a of
--     Just r -> sendMessage $ JumpToLayout r
--     _ -> error "this shouldn't happen"

-- This is the currently used layout change
-- activated with Meta+;
-- It displays the relevant list of layouts for the current display
-- TODO: show new layout as a popup message
nchooseLayout :: GSConfig String -> X ()
nchooseLayout conf = do
   loName <- wrapped_loName
   wsName <- wrapped_wsName

   a <- gridselect conf $ makeLayoutList $
      cycleFront loName $ case wsName of
         "web" -> webList
         "terminal" -> terminalList
         "im" -> imList
         "images" -> imagesList
         _ -> defaultList

   case a of
      Just r -> do
         sendMessage $ JumpToLayout r
         dzenConfig myDzenConfig r
   return ()
 where
   wrapped_loName :: X String
   wrapped_loName =  liftM (fromMaybe "") logLayout

   wrapped_wsName :: X String 
   wrapped_wsName =  liftM (fromMaybe "") logCurrent

   cycleFront :: String -> [String] -> [String]
   cycleFront n l = n: Data.List.delete n l

   basicList = ["Accordion", "Full", "Tabbed", "Spiral", "Wide", "ThreeWide", "WideAccordion", "Writing", "WritingNew", "Gridding", "TwoPane", "OneBig"]
   defaultList = ["Tall", "ThreeCol"] ++ basicList
   webList = ["Tabbed", "Full", "TwoPane"]
   terminalList = ["Tall", "ThreeCol", "Full"]
   imList = ["im"]
   imagesList = ["gimp", "Tall", "ThreeCol"] ++ basicList



myPP :: PP
myPP = namedScratchpadFilterOutWorkspacePP $ sjanssenPP {
   ppCurrent = xmobarColor "grey" "white",
   ppHidden = xmobarColor "red" "black", -- . noScratchPad,
   ppHiddenNoWindows = id,
   ppTitle = xmobarColor "green" "" . shorten 126
}

myManageHook  :: Query (Endo (StackSet WorkspaceId (Layout Window) Window ScreenId ScreenDetail))
myManageHook = composeAll
   [
   (role =? "gimp-toolbox" <||> role =? "gimp-image-winow") --> (ask >>= doF . W.sink)
   , className =? "X-terminal-emulator"      --> doCopy "terminal"
   , className =? "Xmessage"  --> doFloat
   , className =? "Qasmixer" --> doFloat
   , className =? "Sonata" --> doFloat
   , className =? "feh" --> viewShift "images"
   , className =? "Display.im6" --> viewShift "images"
   , manageDocks
   ] <+> manageScratchPad
 where role = stringProperty "WM_WINDOW_ROLE"

manageScratchPad :: ManageHook
manageScratchPad = namedScratchpadManageHook scratchpads

scratchpads :: [NamedScratchpad]
scratchpads = [
  -- run htop in xterm, find it by title, use default floating window placement
  NS "glances" "xterm -e glances" (title =? "glances") floatStyle ,
  NS "mutt" "xterm -e mutt" (title =? "mutt") floatStyle ,
  NS "mcabber" "xterm -e mcabber" (title =? "mcabber") floatStyle ,
  NS "mpc" "stterm -c mpc -e ncmpcpp" (className =? "ncmpcpp") floatStyle ,

  -- run gvim, find by role
  NS "notes" "bash -c 'cd $HOME/mywiki; source /usr/local/share/chruby/chruby.sh; chruby 2.2.2; SOYWIKI_VIM=\"gvim --role notes\" /home/edwlan/.gem/ruby/2.2.2/bin/soywiki'" (role =? "notes") floatStyle
  ] where cls = stringProperty "WM_WINDOW_CLASS"
          role = stringProperty "WM_WINDOW_ROLE"
          floatStyle = customFloating $ W.RationalRect l t w h
          w = 1     -- terminal height, 10%
          h = 0.3333       -- terminal width, 100%
          t = 0.015  -- distance from top edge, 90%
          l = 0  -- distance from left edge, 0%

doCopy :: WorkspaceId -> ManageHook
doCopy i = doF . copyWin i =<< ask

copyWin :: forall a i l s sd . (Eq a, Eq i, Eq s) => i -> a -> StackSet i l a s sd -> StackSet i l a s sd
copyWin i a = copyWindow a i

viewShift :: WorkspaceId -> Query (Endo (StackSet WorkspaceId l Window ScreenId sd))
viewShift = doF . liftM2 (.) W.greedyView W.shift

dShow :: String -> X ()
dShow = dzenConfig myDzenConfig

copyNSwitch :: forall l a s sd a1. (Eq s, Eq a) => ((StackSet String l a s sd -> StackSet String l a s sd) -> X a1)
                 -> String -> X ()
copyNSwitch ws target = do
  ws $ copy target
  ws $ W.view target
  dShow target

shiftNSwitch :: forall l a s sd a1. (Eq s, Ord a) => ((StackSet String l a s sd -> StackSet String l a s sd) -> X a1)
                  -> String -> X ()
shiftNSwitch ws target = do
  ws $ shift target
  ws $ W.view target
  dShow target

switchWorkspace :: forall l a s sd a1. (Eq s, Ord a) => ((StackSet String l a s sd -> StackSet String l a s sd) -> X a1)
                  -> String -> X ()
switchWorkspace ws target = do
  windows $ W.greedyView target
  dShow target

maximizeSwitch :: X ()
maximizeSwitch = do
  withFocused $ sendMessage . maximizeRestore
  windows W.focusUp

maximizeFlop :: X ()
maximizeFlop = do
  windows W.focusUp
  withFocused $ sendMessage . maximizeRestore

makeSwitchComb mod keyList num action = ((getMod mod, keyList !! num), action)
  where
    getMod Nothing = mod4Mask
    getMod (Just mod) = mod4Mask .|. mod

workspaceNames = ["web", "2", "3", "4", "5", "6", "7", "terminal", "images"]

numKeys :: [KeySym]
numKeys = [xK_0, xK_1, xK_2, xK_3, xK_4, xK_5, xK_6, xK_7, xK_8, xK_9]

numPadKeys :: [KeySym]
numPadKeys = [xK_KP_Insert -- 0
             , xK_KP_End,  xK_KP_Down,  xK_KP_Page_Down -- 1, 2, 3
             , xK_KP_Left, xK_KP_Begin, xK_KP_Right     -- 4, 5, 6
             , xK_KP_Home, xK_KP_Up,    xK_KP_Page_Up   -- 7, 8, 9
             ]

combForNum keyList action mod n = makeSwitchComb mod keyList n $ action windows $ workspaceNames !! (n-1)

multiMap funs seq = concatMap (`map` seq) funs

workspaceKeys =
    bindWorkspaces [
      bindNum copyNSwitch shiftMask,
      bindNum shiftNSwitch controlMask,

      bindNumPad copyNSwitch shiftMask,
      bindNumPad shiftNSwitch controlMask,

      combForNum numKeys switchWorkspace Nothing,
      combForNum numPadKeys switchWorkspace Nothing
      ] ++ [
      ((mod4Mask, xK_0), withNthWorkspace greedyView 0),
      ((mod4Mask .|. shiftMask, xK_0), withNthWorkspace copy 0),
      ((mod4Mask, head numPadKeys), withNthWorkspace greedyView 0) 
      ]
    where
      bindNum action = combForNum numKeys action . Just
      bindNumPad action = combForNum numPadKeys action . Just
      bindWorkspaces = flip multiMap [1..9]

main :: IO ()
main = do
   xmproc <- spawnPipe "/home/edwlan/.local/bin/xmobar /home/edwlan/.xmobarrc"
   --xmproc1 <- spawnPipe "/home/edwlan/.cabal/bin/xmobar /home/edwlan/.xmobarrc1"
   xmonad $ ewmh defaultConfig {
         manageHook = myManageHook <+> manageSpawn <+> manageHook defaultConfig,
         --handleEventHook = handleTimerEvent,
         layoutHook = maximize myLayout,
         logHook = takeTopFocus >> dynamicLogWithPP myPP {
           ppOutput = hPutStrLn xmproc
         },
         modMask = mod4Mask,
         focusFollowsMouse = False,
         clickJustFocuses = False,
         XMonad.workspaces = ["web", "2", "3", "4", "5", "6", "7", "terminal", "images", "IM"]
      } `additionalKeys` (workspaceKeys ++
      [
         ((mod4Mask .|. controlMask .|. shiftMask, xK_h ), sendMessage $ Move L),
         ((mod4Mask .|. controlMask .|. shiftMask, xK_j ), sendMessage $ Move D),
         ((mod4Mask .|. controlMask .|. shiftMask, xK_k   ), sendMessage $ Move U),
         ((mod4Mask .|. controlMask .|. shiftMask, xK_l), sendMessage $ Move R),
         ((mod4Mask .|. controlMask, xK_backslash), maximizeFlop),
         ((mod4Mask .|. controlMask, xK_grave), shiftNSwitch windows "IM" ),
         ((mod4Mask .|. controlMask, xK_m), withWorkspace defaultXPConfig (windows . shift)),
         ((mod4Mask .|. controlMask, xK_q     ), spawn "if type xmonad; then xmonad --recompile && xmonad --restart; else xmessage xmonad not in \\$PATH: \"$PATH\"; fi"),
         ((mod4Mask .|. controlMask, xK_t), killAllOtherCopies),
         ((mod4Mask .|. shiftMask, xK_backslash), maximizeSwitch),
         ((mod4Mask .|. shiftMask, xK_BackSpace), removeWorkspace),
         ((mod4Mask .|. shiftMask, xK_grave), shiftNSwitch windows "IM" ),
         ((mod4Mask .|. shiftMask, xK_m), withWorkspace defaultXPConfig (windows . copy)),
         ((mod4Mask .|. shiftMask, xK_n), addWorkspacePrompt defaultXPConfig),
         ((mod4Mask .|. shiftMask, xK_Return), spawnHere "/usr/bin/x-terminal-emulator"),
         ((mod4Mask .|. shiftMask, xK_r), renameWorkspace defaultXPConfig),
         ((mod4Mask .|. shiftMask, xK_t), kill1),
         ((mod4Mask .|. shiftMask, xK_w), gridselectWorkspace defaultGSConfig (\ws -> greedyView ws . shift ws)),
         --((mod4Mask, xK_apostrophe), scratchpadSpawnActionTerminal "gvim"),
         ((mod4Mask, xK_backslash), withFocused (sendMessage . maximizeRestore)),
         ((mod4Mask, xK_b), sendMessage ToggleStruts),
         ((mod4Mask, xK_g), goToSelected defaultGSConfig),
         --((mod4Mask, xK_grave), scratchpadSpawnActionTerminal "urxvt"),
         {-((mod4Mask, xK_grave), switchWorkspace "IM" ),-}
         ((mod4Mask, xK_KP_Add), spawn "/usr/bin/zsh /home/edwlan/bin/dzen_mpc_status"),
         ((mod4Mask, xK_KP_Divide), spawn "/usr/bin/zsh /home/edwlan/bin/dmenu_play_mpd"),
         ((mod4Mask, xK_KP_Multiply), spawn "/usr/bin/zsh /home/edwlan/bin/dmenu_queue_mpd"),
         ((mod4Mask, xK_KP_Subtract), spawn "/usr/bin/zsh /home/edwlan/bin/dmenu_queueplay_mpd"),
         ((mod4Mask, xK_p), spawnHere "/home/edwlan/bin/yeganesh_run -f"),
         ((mod4Mask, xK_q), (withSelectedWindow $ windows . W.focusWindow) defaultGSConfig >> windows W.shiftMaster),
         ((mod4Mask, xK_semicolon), nchooseLayout defaultGSConfig),
         ((mod4Mask, xK_w), gridselectWorkspace defaultGSConfig greedyView),

         ((mod4Mask, xK_Left), onPrevNeighbour W.view), 
         ((mod4Mask, xK_Right), onNextNeighbour W.view)

      ]
      ++ [
         ((mod4Mask .|. mod1Mask, xK_1), namedScratchpadAction scratchpads "notes"),
         ((mod4Mask .|. mod1Mask, numPadKeys !! 1), namedScratchpadAction scratchpads "notes"),
         ((mod4Mask .|. mod1Mask, xK_2), namedScratchpadAction scratchpads "mutt"),
         ((mod4Mask .|. mod1Mask, numPadKeys !! 2), namedScratchpadAction scratchpads "mutt"),
         --((mod4Mask .|. mod1Mask, xK_3), namedScratchpadAction scratchpads "irc"),
         --((mod4Mask .|. mod1Mask, numPadKeys !! 3), namedScratchpadAction scratchpads "irc"),
         ((mod4Mask .|. mod1Mask, xK_4), namedScratchpadAction scratchpads "htop"),
         ((mod4Mask .|. mod1Mask, numPadKeys !! 4), namedScratchpadAction scratchpads "htop"),
         ((mod4Mask .|. mod1Mask, xK_5), namedScratchpadAction scratchpads "mpc"),
         ((mod4Mask .|. mod1Mask, numPadKeys !! 5), namedScratchpadAction scratchpads "mpc"),
         ((mod4Mask .|. mod1Mask, xK_6), namedScratchpadAction scratchpads "mcabber"),
         ((mod4Mask .|. mod1Mask, numPadKeys !! 6), namedScratchpadAction scratchpads "mcabber")
      ])