packages feed

matsuri-0.0.4: Main.hs

{-# LANGUAGE RecordWildCards #-}

-- | Main.hs
-- Main program module.

import Buffers
import Config
import Cmd
import Help
import Jabber
import UI
import Utils

import Network
import Network.XMPP.MUC
import Control.Concurrent
import Control.Concurrent.MVar
import qualified Data.Map as M
import Graphics.Vty
import Graphics.Vty.Widgets.All
import qualified Widgets.ListBox as L
import qualified Widgets.EditBox as E


-- | Read configs and start main loop.
main = withSocketsDo $ do
    -- read config
    (config, buffers) <- readConfig ".matsurirc"
    vty <- mkVty
    mev <- newEmptyMVar
    -- main loop
    forkIO $ loopGetEvent mev vty
    DisplayRegion w h <- display_bounds $ terminal vty
    let ui = resizeUI (fromIntegral w) (fromIntegral h) $ defaultUI buffers
    rerender mev vty ui config buffers
    -- disconnect all accounts
    mapM_ disconnect $ [acc | BufAccount acc <- (M.elems buffers)]
    -- shutdown && exiting
    shutdown vty
  where
    loopGetEvent mev vty = do
        event <- next_event vty
        putMVar mev (VtyEvent event)
        loopGetEvent mev vty


-- | Get event and process it.
mainLoop mev vty st@(UIState {..}) config buffers = do
    event <- takeMVar mev
    case event of
      InsBuffer k buffer -> insElem' k buffer
      ---
      NewMsg k msg -> case getBuf k buffers of
        BufChat chat ->
            let contents = msg:(chatContents chat)
            in insElem' k $ BufChat chat{chatContents=contents}
        BufRoom room ->
            let contents = msg:(roomContents room)
            in insElem' k $ BufRoom room{roomContents=contents}
        _ -> skip
      ---
      NewStatus k status' -> case getBuf k buffers of
        BufChat chat -> insElem' k $ BufChat chat{status=status'}
        _ -> skip
      ---
      NewRoomMsg k ev -> case getBuf k buffers of
        BufRoom room -> insElem' k (updRoom ev room)
        _ -> skip
      ---
      RoomPresence k presence -> case getBuf k buffers of
        BufRoom room -> do
            (cnt, occs) <- updateRoomOccupants presence
                                               (roomOccupants room)
                                               (roomNick room)
            let buf = BufRoom room{ roomContents=cnt++(roomContents room)
                                  , roomOccupants=occs }
            insElem' k buf
        _ -> skip
      ---
      RoomList k list -> case getBuf k buffers of
        BufRoom room -> do
            w <- getW'
            info <- withTime " == " (showRoomList list w)
            let cnts = (InfoMsg info):(roomContents room)
            insElem' k $ BufRoom room{roomContents=cnts}
        _ -> skip
      ---
      VtyEvent event' -> case event' of
        -- roster
        EvKey (KASCII 'p') [MCtrl] -> rerender' $ st { roster = L.moveUp roster }
        EvKey (KASCII 'n') [MCtrl] -> rerender' $ st { roster = L.moveDn roster }
        EvKey KEnter [] | E.contents edit == "" ->
          case getBuf cur buffers of
            BufAccount acc ->
                let buf = BufAccount acc{accCollapsed = not $ accCollapsed acc}
                in insElem' cur buf
            BufGroup grp ->
                let buf = BufGroup grp{grpCollapsed = not $ grpCollapsed grp}
                in insElem' cur buf
            _ -> skip
        -- show/hide
        EvKey (KASCII 'v') [MCtrl] ->
          let isShow = getCF "show_roster" config
              isShow' = if null isShow then "1"
                                       else ""
              config' = setCF "show_roster" isShow' config
          in rerender mev vty st config' buffers

        -- editbox
        EvKey (KASCII  c ) []      -> rerender' $ st { edit = E.insert c edit }
        EvKey (KASCII 'a') [MCtrl] -> rerender' $ st { edit = E.moveToHome edit }
        EvKey (KASCII 'f') [MCtrl] -> rerender' $ st { edit = E.moveRight edit }
        EvKey (KASCII 'b') [MCtrl] -> rerender' $ st { edit = E.moveLeft edit }
        EvKey (KASCII 'f') [MMeta] -> rerender' $ st { edit = E.moveWordRight edit }
        EvKey (KASCII 'b') [MMeta] -> rerender' $ st { edit = E.moveWordLeft edit }
        EvKey (KASCII 'e') [MCtrl] -> rerender' $ st { edit = E.moveToEnd edit }
        EvKey KBS          []      -> rerender' $ st { edit = E.backSpace edit }
        EvKey KDel         []      -> rerender' $ st { edit = E.delete edit }
        -- parse command || send message
        EvKey KEnter [] -> case E.contents edit of
          content | words content == ["/quit"] -> return ()
          ('/':'/':msg) -> sendMsg ('/':msg)
          ('/':cmd) -> do
              w <- getW'
              buffers' <- doCmd mev config buffers acc cur cmd w
              let st' = st { edit = E.empty edit
                           , roster = mkListBox roster buffers' }
              rerender mev vty st' config buffers'
          msg -> sendMsg msg
        -- other
        EvResize w h               -> rerender' $ resizeUI w h st
        EvKey (KASCII 'q') [MCtrl] -> return ()
        _                          -> skip
  where
    skip = mainLoop mev vty st config buffers
    rerender' st = rerender mev vty st config buffers
    -- send msg to chat
    sendMsg msg = case getBuf cur buffers of
      -- fix copypaste?
      BufChat chat -> do
          buffer <- sendChatMessage acc chat msg
          let buffers' = insElem cur buffer buffers
              st' = st { edit = E.empty edit }
          rerender mev vty st' config buffers'
      BufRoom room -> do
          sendRoomMessage acc room msg
          let st' = st { edit = E.empty edit }
          rerender mev vty st' config buffers
      _ -> skip
    -- insert new element in buffer (or simply replace old element)
    -- and re-create roster tree
    insElem' k buffer =
        let buffers' = insElem k buffer buffers
            st' = st { roster = mkListBox roster buffers' }
        in rerender mev vty st' config buffers'
    -- current account
    acc = case getBuf cur buffers of
        BufAccount acc' -> acc'
        BufGroup grp -> getAcc' (grpName grp)
        BufChat chat -> getAcc' (chatName chat)
        BufRoom room -> getAcc' (roomName room)
      where getAcc' name = getAcc (takeWhile (/='|') name) buffers
    -- current buffer
    cur = L.cur roster
    -- get textbox width
    getW' = do
        w <- getW vty
        let roster_width = if null $ getCF "show_roster" config
                           then 0
                           else read $ getCF "roster_width" config
            textbox_width = w - roster_width - 1
        return textbox_width


rerender mev vty st config buffers
  = mkImage vty (mkUI st config buffers) >>=
    update vty . pic_for_image >>
    mainLoop mev vty st config buffers