------------------------------------------------------------------------------
-- |
-- Module: Xmobar.Config.Defaults
-- Copyright: (c) 2018, 2019 Jose Antonio Ortega Ruiz
-- License: BSD3-style (see LICENSE)
--
-- Maintainer: jao@gnu.org
-- Stability: unstable
-- Portability: portable
-- Created: Sun Nov 25, 2018 22:26
--
--
-- Default values for Xmobar configurations and functions to access
-- configuration files and directories.
--
------------------------------------------------------------------------------


module Xmobar.App.Config (defaultConfig,
                          xmobarConfigDir,
                          xmobarDataDir,
                          xmobarConfigFile) where

import Control.Monad (when)

import System.Environment
import System.Directory
import System.FilePath ((</>))
import System.Posix.Files (fileExist)

import Xmobar.Plugins.Date
import Xmobar.Plugins.StdinReader
import Xmobar.Config.Types
import Xmobar.Run.Runnable

-- | The default configuration values
defaultConfig :: Config
defaultConfig :: Config
defaultConfig =
    Config :: String
-> [String]
-> String
-> String
-> String
-> String
-> XPosition
-> Int
-> [Int]
-> Int
-> Border
-> String
-> Int
-> Int
-> Bool
-> Bool
-> Bool
-> Bool
-> Bool
-> Bool
-> String
-> [Runnable]
-> String
-> String
-> String
-> Bool
-> Config
Config { font :: String
font = String
"-misc-fixed-*-*-*-*-10-*-*-*-*-*-*-*"
           , additionalFonts :: [String]
additionalFonts = []
           , wmClass :: String
wmClass = String
"xmobar"
           , wmName :: String
wmName = String
"xmobar"
           , bgColor :: String
bgColor = String
"#000000"
           , fgColor :: String
fgColor = String
"#BFBFBF"
           , alpha :: Int
alpha   = Int
255
           , position :: XPosition
position = XPosition
Top
           , border :: Border
border = Border
NoBorder
           , borderColor :: String
borderColor = String
"#BFBFBF"
           , borderWidth :: Int
borderWidth = Int
1
           , textOffset :: Int
textOffset = -Int
1
           , iconOffset :: Int
iconOffset = -Int
1
           , textOffsets :: [Int]
textOffsets = []
           , hideOnStart :: Bool
hideOnStart = Bool
False
           , lowerOnStart :: Bool
lowerOnStart = Bool
True
           , persistent :: Bool
persistent = Bool
False
           , allDesktops :: Bool
allDesktops = Bool
True
           , overrideRedirect :: Bool
overrideRedirect = Bool
True
           , pickBroadest :: Bool
pickBroadest = Bool
False
           , iconRoot :: String
iconRoot = String
"."
           , commands :: [Runnable]
commands = [ Date -> Runnable
forall r. (Exec r, Read r, Show r) => r -> Runnable
Run (Date -> Runnable) -> Date -> Runnable
forall a b. (a -> b) -> a -> b
$ String -> String -> Int -> Date
Date String
"%a %b %_d %Y * %H:%M:%S" String
"theDate" Int
10
                        , StdinReader -> Runnable
forall r. (Exec r, Read r, Show r) => r -> Runnable
Run StdinReader
StdinReader]
           , sepChar :: String
sepChar = String
"%"
           , alignSep :: String
alignSep = String
"}{"
           , template :: String
template = String
"%StdinReader% }{ " String -> String -> String
forall a. [a] -> [a] -> [a]
++
                        String
"<fc=#00FF00>%uname%</fc> * <fc=#FF0000>%theDate%</fc>"
           , verbose :: Bool
verbose = Bool
False
           }

-- | Return the path to the xmobar configuration directory.  This
-- directory is where user configuration files are stored (e.g, the
-- xmobar.hs file).  You may also create a @lib@ subdirectory in the
-- configuration directory and the default recompile command will add
-- it to the GHC include path.
--
-- Several directories are considered.  In order of
-- preference:
--
--   1. The directory specified in the @XMOBAR_CONFIG_DIR@ environment variable.
--   2. The @~\/.xmobar@ directory.
--   3. The @XDG_CONFIG_HOME/xmobar@ directory.
--
-- The first directory that exists will be used.  If none of the
-- directories exist then (1) will be used if it is set, otherwise (2)
-- will be used.
xmobarConfigDir :: IO String
xmobarConfigDir :: IO String
xmobarConfigDir =
    Bool -> String -> [IO String] -> IO String
findFirstDirWithEnv Bool
False String
"XMOBAR_CONFIG_DIR"
      [ String -> IO String
getAppUserDataDirectory String
"xmobar"
      , XdgDirectory -> String -> IO String
getXdgDirectory XdgDirectory
XdgConfig String
"xmobar"
      ]

-- | Return the path to the xmobar data directory.  This directory is
-- used by Xmobar to store data files such as the run-time state file
-- and the configuration binary generated by GHC.
--
-- Several directories are considered.  In order of preference:
--
--   1. The directory specified in the @XMOBAR_DATA_DIR@ environment variable.
--   2. The @XDG_DATA_HOME/xmobar@ directory.
--   3. The @~\/.xmobar@ directory.
--
-- The first directory that exists will be used.  If none of the
-- directories exist then (1) will be used if it is set, otherwise (2)
-- will be used.  Either way, a directory will be created if
-- necessary.
xmobarDataDir :: IO String
xmobarDataDir :: IO String
xmobarDataDir =
    Bool -> String -> [IO String] -> IO String
findFirstDirWithEnv Bool
True String
"XMOBAR_DATA_DIR"
      [ XdgDirectory -> String -> IO String
getXdgDirectory XdgDirectory
XdgData String
"xmobar"
      , String -> IO String
getAppUserDataDirectory String
"xmobar"
      ]

-- | Helper function that will find the first existing directory and
-- return its path.  If none of the directories can be found,
-- optionally create and return the first from the list.  If the list
-- is empty this function returns the historical @~\/.xmobar@
-- directory.
findFirstDirOf :: Bool -> [IO FilePath] -> IO FilePath
findFirstDirOf :: Bool -> [IO String] -> IO String
findFirstDirOf Bool
create [] = Bool -> [IO String] -> IO String
findFirstDirOf Bool
create [String -> IO String
getAppUserDataDirectory String
"xmobar"]
findFirstDirOf Bool
create [IO String]
possibles = do
    Maybe String
found <- [IO String] -> IO (Maybe String)
go [IO String]
possibles
    case Maybe String
found of
      Just String
path -> String -> IO String
forall (m :: * -> *) a. Monad m => a -> m a
return String
path
      Maybe String
Nothing ->  do
        String
primary <- [IO String] -> IO String
forall a. [a] -> a
head [IO String]
possibles
        Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when Bool
create (Bool -> String -> IO ()
createDirectoryIfMissing Bool
True String
primary)
        String -> IO String
forall (m :: * -> *) a. Monad m => a -> m a
return String
primary
  where
    go :: [IO String] -> IO (Maybe String)
go [] = Maybe String -> IO (Maybe String)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe String
forall a. Maybe a
Nothing
    go (IO String
x:[IO String]
xs) = do
      Bool
exists <- IO String
x IO String -> (String -> IO Bool) -> IO Bool
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= String -> IO Bool
doesDirectoryExist
      if Bool
exists then IO String
x IO String -> (String -> IO (Maybe String)) -> IO (Maybe String)
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Maybe String -> IO (Maybe String)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe String -> IO (Maybe String))
-> (String -> Maybe String) -> String -> IO (Maybe String)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Maybe String
forall a. a -> Maybe a
Just else [IO String] -> IO (Maybe String)
go [IO String]
xs

-- | Simple wrapper around @findFirstDirOf@ that allows the primary
-- path to be specified by an environment variable.
findFirstDirWithEnv :: Bool -> String -> [IO FilePath] -> IO FilePath
findFirstDirWithEnv :: Bool -> String -> [IO String] -> IO String
findFirstDirWithEnv Bool
create String
envName [IO String]
paths = do
    Maybe String
envPath' <- String -> IO (Maybe String)
lookupEnv String
envName
    case Maybe String
envPath' of
      Maybe String
Nothing -> Bool -> [IO String] -> IO String
findFirstDirOf Bool
create [IO String]
paths
      Just String
envPath -> Bool -> [IO String] -> IO String
findFirstDirOf Bool
create (String -> IO String
forall (m :: * -> *) a. Monad m => a -> m a
return String
envPathIO String -> [IO String] -> [IO String]
forall a. a -> [a] -> [a]
:[IO String]
paths)

xmobarConfigFile :: IO (Maybe FilePath)
xmobarConfigFile :: IO (Maybe String)
xmobarConfigFile =
  [IO String] -> IO (Maybe String)
ffirst [ String -> IO String
xdg String
"xmobar.hs", String -> IO String
xdg String
"xmobarrc", String -> IO String
home String
".xmobarrc"]
  where xdg :: String -> IO String
xdg String
p = (String -> String) -> IO String -> IO String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (String -> String -> String
</> String
p) IO String
xmobarConfigDir
        home :: String -> IO String
home String
p = (String -> String) -> IO String -> IO String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (String -> String -> String
</> String
p) IO String
getHomeDirectory
        ffirst :: [IO String] -> IO (Maybe String)
ffirst [] = Maybe String -> IO (Maybe String)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe String
forall a. Maybe a
Nothing
        ffirst (IO String
f:[IO String]
fs) =
          IO String
f IO String -> (String -> IO Bool) -> IO Bool
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= String -> IO Bool
fileExist IO Bool -> (Bool -> IO (Maybe String)) -> IO (Maybe String)
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \Bool
e -> if Bool
e then (String -> Maybe String) -> IO String -> IO (Maybe String)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap String -> Maybe String
forall a. a -> Maybe a
Just IO String
f else [IO String] -> IO (Maybe String)
ffirst [IO String]
fs