2010-11-25 10:10:38 +03:00
|
|
|
{-|
|
|
|
|
hledger-vty - a hledger add-on providing a curses-style interface.
|
2011-04-18 21:36:28 +04:00
|
|
|
Copyright (c) 2007-2011 Simon Michael <simon@joyful.com>
|
2010-11-25 10:10:38 +03:00
|
|
|
Released under GPL version 3 or later.
|
2008-12-08 20:27:16 +03:00
|
|
|
-}
|
|
|
|
|
2011-06-21 01:33:26 +04:00
|
|
|
module Hledger.Vty.Main (main) where
|
2010-11-25 10:10:38 +03:00
|
|
|
|
2011-06-06 22:59:24 +04:00
|
|
|
import Control.Monad
|
|
|
|
import Data.List
|
|
|
|
import Data.Maybe
|
2011-06-14 03:28:39 +04:00
|
|
|
import Data.Time.Calendar
|
2008-12-08 20:27:16 +03:00
|
|
|
import Graphics.Vty
|
2011-08-16 02:50:09 +04:00
|
|
|
import Safe
|
2011-08-22 18:55:39 +04:00
|
|
|
import System.Exit
|
2010-11-25 10:10:38 +03:00
|
|
|
|
2011-07-18 03:05:56 +04:00
|
|
|
import Hledger
|
2015-08-12 05:08:33 +03:00
|
|
|
import Hledger.Cli hiding (progname,prognameandversion,green)
|
2011-08-16 02:50:09 +04:00
|
|
|
import Hledger.Vty.Options
|
2010-11-25 10:10:38 +03:00
|
|
|
|
|
|
|
|
|
|
|
main :: IO ()
|
|
|
|
main = do
|
2011-08-16 02:50:09 +04:00
|
|
|
opts <- getHledgerVtyOpts
|
2015-08-12 05:08:33 +03:00
|
|
|
-- when (debug_ $ cliopts_ opts) $ printf "%s\n" prognameandversion >> printf "opts: %s\n" (show opts)
|
2011-08-16 02:50:09 +04:00
|
|
|
runWith opts
|
|
|
|
|
|
|
|
runWith :: VtyOpts -> IO ()
|
|
|
|
runWith opts = run opts
|
2010-11-25 10:10:38 +03:00
|
|
|
where
|
2011-08-16 02:50:09 +04:00
|
|
|
run opts
|
2015-08-12 05:08:33 +03:00
|
|
|
| "help" `inRawOpts` (rawopts_ $ cliopts_ opts) = putStr (showModeHelp vtymode) >> exitSuccess
|
|
|
|
| "version" `inRawOpts` (rawopts_ $ cliopts_ opts) = putStrLn prognameandversion >> exitSuccess
|
|
|
|
| "binary-filename" `inRawOpts` (rawopts_ $ cliopts_ opts) = putStrLn (binaryfilename progname)
|
2011-08-16 02:50:09 +04:00
|
|
|
| otherwise = withJournalDo' opts vty
|
|
|
|
|
|
|
|
withJournalDo' :: VtyOpts -> (VtyOpts -> Journal -> IO ()) -> IO ()
|
|
|
|
withJournalDo' opts cmd = do
|
2015-08-12 05:08:33 +03:00
|
|
|
-- journalFilePathFromOpts (cliopts_ opts) >>= readJournalFile Nothing >>=
|
|
|
|
-- either error' (cmd opts . journalApplyAliases (aliasesFromOpts $ cliopts_ opts))
|
|
|
|
-- XXX head should be safe for now
|
|
|
|
(head `fmap` journalFilePathFromOpts (cliopts_ opts)) >>= readJournalFile Nothing Nothing True >>=
|
2011-08-16 02:50:09 +04:00
|
|
|
either error' (cmd opts . journalApplyAliases (aliasesFromOpts $ cliopts_ opts))
|
2008-12-08 20:27:16 +03:00
|
|
|
|
2009-04-03 01:02:07 +04:00
|
|
|
helpmsg = "(b)alance, (r)egister, (p)rint, (right) to drill down, (left) to back up, (q)uit"
|
2008-12-08 20:27:16 +03:00
|
|
|
|
2009-06-05 13:44:20 +04:00
|
|
|
instance Show Vty where show = const "a Vty"
|
2008-12-08 20:27:16 +03:00
|
|
|
|
2010-05-20 03:50:41 +04:00
|
|
|
-- | The application state when running the vty command.
|
2008-12-08 20:27:16 +03:00
|
|
|
data AppState = AppState {
|
2008-12-10 10:56:00 +03:00
|
|
|
av :: Vty -- ^ the vty context
|
2011-08-16 02:50:09 +04:00
|
|
|
,aw :: Int -- ^ window width
|
|
|
|
,ah :: Int -- ^ window height
|
2008-12-10 10:56:00 +03:00
|
|
|
,amsg :: String -- ^ status message
|
2011-08-16 02:50:09 +04:00
|
|
|
,aopts :: VtyOpts -- ^ command-line opts
|
2010-04-16 01:41:18 +04:00
|
|
|
,aargs :: [String] -- ^ command-line args at startup
|
2010-05-23 21:41:25 +04:00
|
|
|
,ajournal :: Journal -- ^ parsed journal
|
2008-12-10 10:56:00 +03:00
|
|
|
,abuf :: [String] -- ^ lines of the current buffered view
|
|
|
|
,alocs :: [Loc] -- ^ user's navigation trail within the UI
|
|
|
|
-- ^ never null, head is current location
|
2008-12-08 20:27:16 +03:00
|
|
|
} deriving (Show)
|
|
|
|
|
2008-12-10 10:56:00 +03:00
|
|
|
-- | A location within the user interface.
|
2008-12-08 20:27:16 +03:00
|
|
|
data Loc = Loc {
|
2008-12-10 10:56:00 +03:00
|
|
|
scr :: Screen -- ^ one of the available screens
|
|
|
|
,sy :: Int -- ^ viewport y scroll position
|
|
|
|
,cy :: Int -- ^ cursor y position
|
2010-04-16 01:41:18 +04:00
|
|
|
,largs :: [String] -- ^ command-line args, possibly narrowed for this location
|
2008-12-08 20:27:16 +03:00
|
|
|
} deriving (Show)
|
|
|
|
|
2008-12-10 10:56:00 +03:00
|
|
|
-- | The screens available within the user interface.
|
|
|
|
data Screen = BalanceScreen -- ^ like hledger balance, shows accounts
|
2009-04-03 14:58:05 +04:00
|
|
|
| RegisterScreen -- ^ like hledger register, shows transaction-postings
|
2010-07-13 10:30:06 +04:00
|
|
|
| PrintScreen -- ^ like hledger print, shows journal transactions
|
2011-08-07 20:24:09 +04:00
|
|
|
-- | LedgerScreen -- ^ shows the raw journal entries
|
2008-12-08 20:27:16 +03:00
|
|
|
deriving (Eq,Show)
|
|
|
|
|
2010-05-20 03:50:41 +04:00
|
|
|
-- | Run the vty (curses-style) ui.
|
2011-08-16 02:50:09 +04:00
|
|
|
vty :: VtyOpts -> Journal -> IO ()
|
|
|
|
vty opts j = do
|
2015-08-12 05:08:33 +03:00
|
|
|
cfg <- standardIOConfig
|
|
|
|
v <- mkVty cfg
|
|
|
|
|
|
|
|
-- let line0 = string (defAttr ` withForeColor ` green) "first line"
|
|
|
|
-- line1 = string (defAttr ` withBackColor ` blue) "second line"
|
|
|
|
-- img = line0 <-> line1
|
|
|
|
-- pic = picForImage img
|
|
|
|
-- update vty pic
|
|
|
|
-- e <- nextEvent vty
|
|
|
|
-- shutdown vty
|
|
|
|
-- print ("Last event was: " ++ show e)
|
|
|
|
|
|
|
|
Output{displayBounds=getdisplayregion} <- outputForConfig cfg
|
|
|
|
(w,h) <- getdisplayregion
|
|
|
|
d <- getCurrentDay
|
|
|
|
let a = enter d BalanceScreen (words' $ query_ $ reportopts_ $ cliopts_ opts)
|
2008-12-08 20:27:16 +03:00
|
|
|
AppState {
|
|
|
|
av=v
|
2015-08-12 05:08:33 +03:00
|
|
|
,aw=w
|
|
|
|
,ah=h
|
2008-12-08 20:27:16 +03:00
|
|
|
,amsg=helpmsg
|
2011-08-07 21:10:34 +04:00
|
|
|
,aopts=opts
|
2015-08-12 05:08:33 +03:00
|
|
|
,aargs=words' $ query_ $ reportopts_ $ cliopts_ opts
|
2010-05-23 21:41:25 +04:00
|
|
|
,ajournal=j
|
2008-12-08 20:27:16 +03:00
|
|
|
,abuf=[]
|
|
|
|
,alocs=[]
|
|
|
|
}
|
2014-09-11 00:07:53 +04:00
|
|
|
go a
|
2008-12-08 20:27:16 +03:00
|
|
|
|
|
|
|
-- | Update the screen, wait for the next event, repeat.
|
|
|
|
go :: AppState -> IO ()
|
2009-12-11 00:25:49 +03:00
|
|
|
go a@AppState{av=av,aopts=opts} = do
|
2011-08-16 02:50:09 +04:00
|
|
|
when (not $ debug_vty_ opts) $ update av (renderScreen a)
|
2015-08-12 05:08:33 +03:00
|
|
|
k <- nextEvent av
|
2011-06-14 03:28:39 +04:00
|
|
|
d <- getCurrentDay
|
2014-09-11 00:07:53 +04:00
|
|
|
case k of
|
2015-08-12 05:08:33 +03:00
|
|
|
EvResize x y -> go $ resize' x y a
|
|
|
|
EvKey (KChar 'l') [MCtrl] -> refresh av >> go a{amsg=helpmsg}
|
|
|
|
EvKey (KChar 'b') [] -> go $ resetTrailAndEnter d BalanceScreen a
|
|
|
|
EvKey (KChar 'r') [] -> go $ resetTrailAndEnter d RegisterScreen a
|
|
|
|
EvKey (KChar 'p') [] -> go $ resetTrailAndEnter d PrintScreen a
|
2011-06-14 03:28:39 +04:00
|
|
|
EvKey KRight [] -> go $ drilldown d a
|
|
|
|
EvKey KEnter [] -> go $ drilldown d a
|
|
|
|
EvKey KLeft [] -> go $ backout d a
|
2008-12-08 20:27:16 +03:00
|
|
|
EvKey KUp [] -> go $ moveUpAndPushEdge a
|
|
|
|
EvKey KDown [] -> go $ moveDownAndPushEdge a
|
|
|
|
EvKey KHome [] -> go $ moveToTop a
|
|
|
|
EvKey KUp [MCtrl] -> go $ moveToTop a
|
|
|
|
EvKey KUp [MShift] -> go $ moveToTop a
|
|
|
|
EvKey KEnd [] -> go $ moveToBottom a
|
|
|
|
EvKey KDown [MCtrl] -> go $ moveToBottom a
|
|
|
|
EvKey KDown [MShift] -> go $ moveToBottom a
|
|
|
|
EvKey KPageUp [] -> go $ prevpage a
|
|
|
|
EvKey KBS [] -> go $ prevpage a
|
2015-08-12 05:08:33 +03:00
|
|
|
EvKey (KChar ' ') [MShift] -> go $ prevpage a
|
2008-12-08 20:27:16 +03:00
|
|
|
EvKey KPageDown [] -> go $ nextpage a
|
2015-08-12 05:08:33 +03:00
|
|
|
EvKey (KChar ' ') [] -> go $ nextpage a
|
|
|
|
EvKey (KChar 'q') [] -> shutdown av >> return ()
|
2008-12-08 20:27:16 +03:00
|
|
|
-- EvKey KEsc [] -> shutdown av >> return ()
|
|
|
|
_ -> go a
|
|
|
|
|
|
|
|
-- app state modifiers
|
|
|
|
|
|
|
|
-- | The number of lines currently available for the main data display area.
|
|
|
|
pageHeight :: AppState -> Int
|
|
|
|
pageHeight a = ah a - 1
|
|
|
|
|
|
|
|
setLocCursorY, setLocScrollY :: Int -> Loc -> Loc
|
|
|
|
setLocCursorY y l = l{cy=y}
|
|
|
|
setLocScrollY y l = l{sy=y}
|
|
|
|
|
|
|
|
cursorY, scrollY, posY :: AppState -> Int
|
|
|
|
cursorY = cy . loc
|
|
|
|
scrollY = sy . loc
|
|
|
|
posY a = scrollY a + cursorY a
|
|
|
|
|
|
|
|
setCursorY, setScrollY, setPosY :: Int -> AppState -> AppState
|
2010-09-05 22:18:50 +04:00
|
|
|
setCursorY _ AppState{alocs=[]} = error' "shouldn't happen" -- silence warnings
|
2008-12-08 20:27:16 +03:00
|
|
|
setCursorY y a@AppState{alocs=(l:locs)} = a{alocs=(l':locs)} where l' = setLocCursorY y l
|
2009-06-05 13:44:20 +04:00
|
|
|
|
2010-09-05 22:18:50 +04:00
|
|
|
setScrollY _ AppState{alocs=[]} = error' "shouldn't happen" -- silence warnings
|
2008-12-08 20:27:16 +03:00
|
|
|
setScrollY y a@AppState{alocs=(l:locs)} = a{alocs=(l':locs)} where l' = setLocScrollY y l
|
2009-06-05 13:44:20 +04:00
|
|
|
|
2010-09-05 22:18:50 +04:00
|
|
|
setPosY _ AppState{alocs=[]} = error' "shouldn't happen" -- silence warnings
|
2008-12-08 20:27:16 +03:00
|
|
|
setPosY y a@AppState{alocs=(l:locs)} = a{alocs=(l':locs)}
|
2014-09-11 00:07:53 +04:00
|
|
|
where
|
2008-12-08 20:27:16 +03:00
|
|
|
l' = setLocScrollY sy $ setLocCursorY cy l
|
|
|
|
ph = pageHeight a
|
|
|
|
cy = y `mod` ph
|
|
|
|
sy = y - cy
|
|
|
|
|
2011-07-18 02:14:07 +04:00
|
|
|
updateCursorY, updateScrollY {-, updatePosY-} :: (Int -> Int) -> AppState -> AppState
|
2008-12-08 20:27:16 +03:00
|
|
|
updateCursorY f a = setCursorY (f $ cursorY a) a
|
|
|
|
updateScrollY f a = setScrollY (f $ scrollY a) a
|
2011-07-18 02:14:07 +04:00
|
|
|
-- updatePosY f a = setPosY (f $ posY a) a
|
2008-12-08 20:27:16 +03:00
|
|
|
|
2015-08-12 05:08:33 +03:00
|
|
|
resize' :: Int -> Int -> AppState -> AppState
|
|
|
|
resize' x y a = setCursorY cy' a{aw=x,ah=y}
|
2008-12-08 20:27:16 +03:00
|
|
|
where
|
|
|
|
cy = cursorY a
|
|
|
|
cy' = min cy (y-2)
|
|
|
|
|
2008-12-10 10:56:00 +03:00
|
|
|
moveToTop :: AppState -> AppState
|
2009-09-22 19:56:59 +04:00
|
|
|
moveToTop = setPosY 0
|
2008-12-08 20:27:16 +03:00
|
|
|
|
2008-12-10 10:56:00 +03:00
|
|
|
moveToBottom :: AppState -> AppState
|
2008-12-08 20:27:16 +03:00
|
|
|
moveToBottom a = setPosY (length $ abuf a) a
|
|
|
|
|
2008-12-10 10:56:00 +03:00
|
|
|
moveUpAndPushEdge :: AppState -> AppState
|
2009-06-05 13:44:20 +04:00
|
|
|
moveUpAndPushEdge a
|
2008-12-08 20:27:16 +03:00
|
|
|
| cy > 0 = updateCursorY (subtract 1) a
|
|
|
|
| sy > 0 = updateScrollY (subtract 1) a
|
|
|
|
| otherwise = a
|
2009-06-05 13:44:20 +04:00
|
|
|
where Loc{sy=sy,cy=cy} = head $ alocs a
|
2008-12-08 20:27:16 +03:00
|
|
|
|
2008-12-10 10:56:00 +03:00
|
|
|
moveDownAndPushEdge :: AppState -> AppState
|
2009-06-05 13:44:20 +04:00
|
|
|
moveDownAndPushEdge a
|
2008-12-08 20:27:16 +03:00
|
|
|
| sy+cy >= bh = a
|
|
|
|
| cy < ph-1 = updateCursorY (+1) a
|
|
|
|
| otherwise = updateScrollY (+1) a
|
2014-09-11 00:07:53 +04:00
|
|
|
where
|
2009-06-05 13:44:20 +04:00
|
|
|
Loc{sy=sy,cy=cy} = head $ alocs a
|
2008-12-08 20:27:16 +03:00
|
|
|
ph = pageHeight a
|
|
|
|
bh = length $ abuf a
|
|
|
|
|
|
|
|
-- | Scroll down by page height or until we can just see the last line,
|
|
|
|
-- without moving the cursor, or if we are already scrolled as far as
|
|
|
|
-- possible then move the cursor to the last line.
|
2008-12-10 10:56:00 +03:00
|
|
|
nextpage :: AppState -> AppState
|
2008-12-08 20:27:16 +03:00
|
|
|
nextpage (a@AppState{abuf=b})
|
|
|
|
| sy < bh-jump = setScrollY sy' a
|
|
|
|
| otherwise = setCursorY (bh-sy) a
|
|
|
|
where
|
|
|
|
sy = scrollY a
|
|
|
|
jump = pageHeight a - 1
|
|
|
|
bh = length b
|
|
|
|
sy' = min (sy+jump) (bh-jump)
|
|
|
|
|
|
|
|
-- | Scroll up by page height or until we can just see the first line,
|
|
|
|
-- without moving the cursor, or if we are scrolled as far as possible
|
|
|
|
-- then move the cursor to the first line.
|
2008-12-10 10:56:00 +03:00
|
|
|
prevpage :: AppState -> AppState
|
2009-06-05 13:44:20 +04:00
|
|
|
prevpage a
|
2008-12-08 20:27:16 +03:00
|
|
|
| sy > 0 = setScrollY sy' a
|
|
|
|
| otherwise = setCursorY 0 a
|
|
|
|
where
|
|
|
|
sy = scrollY a
|
|
|
|
jump = pageHeight a - 1
|
|
|
|
sy' = max (sy-jump) 0
|
|
|
|
|
|
|
|
-- | Push a new UI location on to the stack.
|
|
|
|
pushLoc :: Loc -> AppState -> AppState
|
|
|
|
pushLoc l a = a{alocs=(l:alocs a)}
|
|
|
|
|
|
|
|
popLoc :: AppState -> AppState
|
|
|
|
popLoc a@AppState{alocs=locs}
|
|
|
|
| length locs > 1 = a{alocs=drop 1 locs}
|
|
|
|
| otherwise = a
|
|
|
|
|
|
|
|
clearLocs :: AppState -> AppState
|
|
|
|
clearLocs a = a{alocs=[]}
|
|
|
|
|
2011-07-18 02:14:07 +04:00
|
|
|
-- exit :: AppState -> AppState
|
|
|
|
-- exit = popLoc
|
2008-12-08 20:27:16 +03:00
|
|
|
|
|
|
|
loc :: AppState -> Loc
|
|
|
|
loc = head . alocs
|
|
|
|
|
2010-04-16 01:41:18 +04:00
|
|
|
-- | Get the filter pattern args in effect for the current ui location.
|
|
|
|
currentArgs :: AppState -> [String]
|
|
|
|
currentArgs (AppState {alocs=Loc{largs=as}:_}) = as
|
|
|
|
currentArgs (AppState {aargs=as}) = as
|
|
|
|
|
2008-12-08 20:27:16 +03:00
|
|
|
screen :: AppState -> Screen
|
2010-04-16 01:41:18 +04:00
|
|
|
screen a = scr where (Loc scr _ _ _) = loc a
|
2008-12-08 20:27:16 +03:00
|
|
|
|
2010-04-16 01:49:07 +04:00
|
|
|
-- | Enter a new screen, with possibly new args, adding the new ui location to the stack.
|
2011-06-14 03:28:39 +04:00
|
|
|
enter :: Day -> Screen -> [String] -> AppState -> AppState
|
|
|
|
enter d scr@BalanceScreen args a = updateData d $ pushLoc Loc{scr=scr,sy=0,cy=0,largs=args} a
|
|
|
|
enter d scr@RegisterScreen args a = updateData d $ pushLoc Loc{scr=scr,sy=0,cy=0,largs=args} a
|
|
|
|
enter d scr@PrintScreen args a = updateData d $ pushLoc Loc{scr=scr,sy=0,cy=0,largs=args} a
|
2008-12-08 20:27:16 +03:00
|
|
|
|
2011-06-14 03:28:39 +04:00
|
|
|
resetTrailAndEnter :: Day -> Screen -> AppState -> AppState
|
|
|
|
resetTrailAndEnter d scr a = enter d scr (aargs a) $ clearLocs a
|
2008-12-08 20:27:16 +03:00
|
|
|
|
2008-12-10 10:56:00 +03:00
|
|
|
-- | Regenerate the display data appropriate for the current screen.
|
2011-06-14 03:28:39 +04:00
|
|
|
updateData :: Day -> AppState -> AppState
|
|
|
|
updateData d a@AppState{aopts=opts,ajournal=j} =
|
2009-06-05 13:44:20 +04:00
|
|
|
case screen a of
|
2015-08-12 05:08:33 +03:00
|
|
|
BalanceScreen -> a{abuf=lines $ balanceReportAsText ropts $ balanceReport ropts q j}
|
|
|
|
RegisterScreen -> a{abuf=lines $ postingsReportAsText cliopts $ postingsReport ropts q j}
|
|
|
|
PrintScreen -> a{abuf=lines $ entriesReportAsText $ entriesReport ropts q j}
|
|
|
|
where q = queryFromOpts d ropts{query_=unwords' $ currentArgs a}
|
|
|
|
ropts = reportopts_ cliopts
|
|
|
|
cliopts = cliopts_ opts
|
2008-12-08 20:27:16 +03:00
|
|
|
|
2011-06-14 03:28:39 +04:00
|
|
|
backout :: Day -> AppState -> AppState
|
|
|
|
backout d a | screen a == BalanceScreen = a
|
|
|
|
| otherwise = updateData d $ popLoc a
|
2008-12-08 20:27:16 +03:00
|
|
|
|
2011-06-14 03:28:39 +04:00
|
|
|
drilldown :: Day -> AppState -> AppState
|
|
|
|
drilldown d a =
|
2009-06-05 13:44:20 +04:00
|
|
|
case screen a of
|
2011-06-14 03:28:39 +04:00
|
|
|
BalanceScreen -> enter d RegisterScreen [currentAccountName a] a
|
|
|
|
RegisterScreen -> scrollToTransaction e $ enter d PrintScreen (currentArgs a) a
|
2009-06-05 13:44:20 +04:00
|
|
|
PrintScreen -> a
|
2009-12-16 11:07:26 +03:00
|
|
|
where e = currentTransaction a
|
2008-12-08 20:27:16 +03:00
|
|
|
|
|
|
|
-- | Get the account name currently highlighted by the cursor on the
|
|
|
|
-- balance screen. Results undefined while on other screens.
|
|
|
|
currentAccountName :: AppState -> AccountName
|
|
|
|
currentAccountName a = accountNameAt (abuf a) (posY a)
|
|
|
|
|
|
|
|
-- | Get the full name of the account being displayed at a specific line
|
|
|
|
-- within the balance command's output.
|
|
|
|
accountNameAt :: [String] -> Int -> AccountName
|
|
|
|
accountNameAt buf lineno = accountNameFromComponents anamecomponents
|
|
|
|
where
|
|
|
|
namestohere = map (drop 22) $ take (lineno+1) buf
|
|
|
|
(indented, nonindented) = span (" " `isPrefixOf`) $ reverse namestohere
|
|
|
|
thisbranch = indented ++ take 1 nonindented
|
|
|
|
anamecomponents = reverse $ map strip $ dropsiblings thisbranch
|
|
|
|
dropsiblings :: [AccountName] -> [AccountName]
|
|
|
|
dropsiblings [] = []
|
2009-09-23 13:22:53 +04:00
|
|
|
dropsiblings (x:xs) = x : dropsiblings xs'
|
2008-12-08 20:27:16 +03:00
|
|
|
where
|
|
|
|
xs' = dropWhile moreindented xs
|
|
|
|
moreindented = (>= myindent) . indentof
|
|
|
|
myindent = indentof x
|
|
|
|
indentof = length . takeWhile (==' ')
|
|
|
|
|
|
|
|
-- | If on the print screen, move the cursor to highlight the specified entry
|
2008-12-10 10:56:00 +03:00
|
|
|
-- (or a reasonable guess). Doesn't work.
|
2009-12-19 08:57:54 +03:00
|
|
|
scrollToTransaction :: Maybe Transaction -> AppState -> AppState
|
|
|
|
scrollToTransaction Nothing a = a
|
|
|
|
scrollToTransaction (Just t) a@AppState{abuf=buf} = setCursorY cy $ setScrollY sy a
|
2008-12-08 20:27:16 +03:00
|
|
|
where
|
2009-12-19 08:57:54 +03:00
|
|
|
entryfirstline = head $ lines $ showTransaction t
|
2008-12-08 20:27:16 +03:00
|
|
|
halfph = pageHeight a `div` 2
|
|
|
|
y = fromMaybe 0 $ findIndex (== entryfirstline) buf
|
|
|
|
sy = max 0 $ y - halfph
|
|
|
|
cy = y - sy
|
|
|
|
|
2009-12-19 08:57:54 +03:00
|
|
|
-- | Get the transaction containing the posting currently highlighted by
|
|
|
|
-- the cursor on the register screen (or best guess). Results undefined
|
|
|
|
-- while on other screens.
|
|
|
|
currentTransaction :: AppState -> Maybe Transaction
|
2010-05-23 21:41:25 +04:00
|
|
|
currentTransaction a@AppState{ajournal=j,abuf=buf} = ptransaction p
|
2008-12-08 20:27:16 +03:00
|
|
|
where
|
2010-05-23 21:41:25 +04:00
|
|
|
p = headDef nullposting $ filter ismatch $ journalPostings j
|
2009-12-19 08:57:54 +03:00
|
|
|
ismatch p = postingDate p == parsedate (take 10 datedesc)
|
2015-08-12 05:08:33 +03:00
|
|
|
&& take 70 (showPostingWithBalanceForVty p nullmixedamt) == (datedesc ++ acctamt)
|
2010-02-17 00:16:30 +03:00
|
|
|
datedesc = take 32 $ fromMaybe "" $ find (not . (" " `isPrefixOf`)) $ headDef "" rest : reverse above
|
|
|
|
acctamt = drop 32 $ headDef "" rest
|
2008-12-08 20:27:16 +03:00
|
|
|
(above,rest) = splitAt y buf
|
|
|
|
y = posY a
|
|
|
|
|
2015-08-12 05:08:33 +03:00
|
|
|
showPostingWithBalanceForVty p b =
|
|
|
|
postingsReportItemAsText defcliopts $
|
|
|
|
mkpostingsReportItem False False PrimaryDate Nothing p b
|
|
|
|
|
2008-12-08 20:27:16 +03:00
|
|
|
-- renderers
|
|
|
|
|
|
|
|
renderScreen :: AppState -> Picture
|
|
|
|
renderScreen (a@AppState{aw=w,ah=h,abuf=buf,amsg=msg}) =
|
2015-08-12 05:08:33 +03:00
|
|
|
Picture {picCursor = Cursor (fromIntegral cx) (fromIntegral cy)
|
|
|
|
,picLayers = [mainimg
|
|
|
|
<->
|
|
|
|
renderStatus w msg
|
|
|
|
]
|
|
|
|
,picBackground = Background ' ' defAttr
|
2009-09-03 18:29:34 +04:00
|
|
|
}
|
2014-09-11 00:07:53 +04:00
|
|
|
where
|
2008-12-08 20:27:16 +03:00
|
|
|
(cx, cy) = (0, cursorY a)
|
|
|
|
sy = scrollY a
|
2009-12-11 00:25:49 +03:00
|
|
|
-- mainimg = (renderString attr $ unlines $ above)
|
|
|
|
-- <->
|
|
|
|
-- (renderString reverseattr $ thisline)
|
|
|
|
-- <->
|
|
|
|
-- (renderString attr $ unlines $ below)
|
|
|
|
-- (above,(thisline:below))
|
|
|
|
-- | null ls = ([],[""])
|
|
|
|
-- | otherwise = splitAt y ls
|
|
|
|
-- ls = lines $ fitto w (h-1) $ unlines $ drop as $ buf
|
|
|
|
-- trying for more speed
|
2015-08-12 05:08:33 +03:00
|
|
|
mainimg = vertCat (map (string defaultattr) above)
|
2008-12-08 20:27:16 +03:00
|
|
|
<->
|
2009-09-22 20:51:27 +04:00
|
|
|
string currentlineattr thisline
|
2008-12-08 20:27:16 +03:00
|
|
|
<->
|
2015-08-12 05:08:33 +03:00
|
|
|
vertCat (map (string defaultattr) below)
|
2008-12-08 20:27:16 +03:00
|
|
|
(thisline,below) | null rest = (blankline,[])
|
|
|
|
| otherwise = (head rest, tail rest)
|
|
|
|
(above,rest) = splitAt cy linestorender
|
|
|
|
linestorender = map padclipline $ take (h-1) $ drop sy $ buf ++ replicate h blankline
|
2009-09-22 19:56:59 +04:00
|
|
|
padclipline = take w . (++ blankline)
|
2008-12-08 20:27:16 +03:00
|
|
|
blankline = replicate w ' '
|
|
|
|
|
2011-07-18 02:14:07 +04:00
|
|
|
-- padClipString :: Int -> Int -> String -> [String]
|
|
|
|
-- padClipString h w s = rows
|
|
|
|
-- where
|
|
|
|
-- rows = map padclipline $ take h $ lines s ++ replicate h blankline
|
|
|
|
-- padclipline = take w . (++ blankline)
|
|
|
|
-- blankline = replicate w ' '
|
|
|
|
|
|
|
|
-- renderString :: Attr -> String -> Image
|
2015-08-12 05:08:33 +03:00
|
|
|
-- renderString attr s = vertCat $ map (string attr) rows
|
2011-07-18 02:14:07 +04:00
|
|
|
-- where
|
|
|
|
-- rows = lines $ fitto w h s
|
|
|
|
-- w = maximum $ map length ls
|
|
|
|
-- h = length ls
|
|
|
|
-- ls = lines s
|
2008-12-08 20:27:16 +03:00
|
|
|
|
|
|
|
renderStatus :: Int -> String -> Image
|
2009-09-22 19:56:59 +04:00
|
|
|
renderStatus w = string statusattr . take w . (++ repeat ' ')
|
2008-12-08 20:27:16 +03:00
|
|
|
|
2009-12-11 00:25:49 +03:00
|
|
|
-- the all-important theming engine!
|
2008-12-08 20:27:16 +03:00
|
|
|
|
2009-06-05 13:44:20 +04:00
|
|
|
theme = Restrained
|
|
|
|
|
|
|
|
data UITheme = Restrained | Colorful | Blood
|
2008-12-08 20:27:16 +03:00
|
|
|
|
2014-09-11 00:07:53 +04:00
|
|
|
(defaultattr,
|
|
|
|
currentlineattr,
|
2008-12-08 20:27:16 +03:00
|
|
|
statusattr
|
2009-06-05 13:44:20 +04:00
|
|
|
) = case theme of
|
2015-08-12 05:08:33 +03:00
|
|
|
Restrained -> (defAttr
|
|
|
|
,defAttr `withStyle` bold
|
|
|
|
,defAttr `withStyle` reverseVideo
|
2009-06-05 13:44:20 +04:00
|
|
|
)
|
2015-08-12 05:08:33 +03:00
|
|
|
Colorful -> (defAttr `withStyle` reverseVideo
|
|
|
|
,defAttr `withForeColor` white `withBackColor` red
|
|
|
|
,defAttr `withForeColor` black `withBackColor` green
|
2009-06-05 13:44:20 +04:00
|
|
|
)
|
2015-08-12 05:08:33 +03:00
|
|
|
Blood -> (defAttr `withStyle` reverseVideo
|
|
|
|
,defAttr `withForeColor` white `withBackColor` red
|
|
|
|
,defAttr `withStyle` reverseVideo
|
2009-06-05 13:44:20 +04:00
|
|
|
)
|
2008-12-08 20:27:16 +03:00
|
|
|
|
2015-08-12 05:08:33 +03:00
|
|
|
-- halfbrightattr = defAttr `withStyle` dim
|
|
|
|
-- reverseattr = defAttr `withStyle` reverseVideo
|
|
|
|
-- redattr = defAttr `withForeColor` red
|
|
|
|
-- greenattr = defAttr `withForeColor` green
|
|
|
|
-- reverseredattr = defAttr `withStyle` reverseVideo `withForeColor` red
|
|
|
|
-- reversegreenattr= defAttr `withStyle` reverseVideo `withForeColor` green
|