diff --git a/hledger-lib/Hledger/Data/Journal.hs b/hledger-lib/Hledger/Data/Journal.hs index 69e784ae1..ea5a8c782 100644 --- a/hledger-lib/Hledger/Data/Journal.hs +++ b/hledger-lib/Hledger/Data/Journal.hs @@ -24,6 +24,7 @@ module Hledger.Data.Journal ( filterJournalPostings, filterJournalAmounts, filterTransactionAmounts, + filterTransactionPostings, filterPostingAmount, -- * Querying journalAccountNames, @@ -313,6 +314,10 @@ filterTransactionAmounts q t@Transaction{tpostings=ps} = t{tpostings=map (filter filterPostingAmount :: Query -> Posting -> Posting filterPostingAmount q p@Posting{pamount=Mixed as} = p{pamount=Mixed $ filter (q `matchesAmount`) as} +filterTransactionPostings :: Query -> Transaction -> Transaction +filterTransactionPostings m t@Transaction{tpostings=ps} = t{tpostings=filter (m `matchesPosting`) ps} + + {- ------------------------------------------------------------------------------- -- filtering V1 diff --git a/hledger-lib/Hledger/Reports/TransactionsReports.hs b/hledger-lib/Hledger/Reports/TransactionsReports.hs index f4bdcd21f..6c8974ab7 100644 --- a/hledger-lib/Hledger/Reports/TransactionsReports.hs +++ b/hledger-lib/Hledger/Reports/TransactionsReports.hs @@ -218,9 +218,6 @@ summarisePostingAccounts ps = displayps | null realps = ps | otherwise = realps -filterTransactionPostings :: Query -> Transaction -> Transaction -filterTransactionPostings m t@Transaction{tpostings=ps} = t{tpostings=filter (m `matchesPosting`) ps} - ------------------------------------------------------------------------------- -- | Split a transactions report whose items may involve several commodities, diff --git a/hledger-ui/Hledger/UI/AccountsScreen.hs b/hledger-ui/Hledger/UI/AccountsScreen.hs index 35d2a0c13..5957f0f25 100644 --- a/hledger-ui/Hledger/UI/AccountsScreen.hs +++ b/hledger-ui/Hledger/UI/AccountsScreen.hs @@ -100,13 +100,15 @@ initAccountsScreen d st@AppState{ initAccountsScreen _ _ = error "init function called with wrong screen type, should not happen" drawAccountsScreen :: AppState -> [Widget] -drawAccountsScreen _st@AppState{aopts=uopts, ajournal=j, aScreen=AccountsScreen{asState=(l,_)}} = +drawAccountsScreen AppState{aopts=UIOpts{cliopts_=CliOpts{reportopts_=ropts}} + ,ajournal=j + ,aScreen=AccountsScreen{asState=(l,_)}} = [ui] where toplabel = files <+> str " accounts" <+> borderQueryStr querystr - <+> cleared + <+> togglefilters <+> borderDepthStr mdepth <+> str " (" <+> cur @@ -118,11 +120,15 @@ drawAccountsScreen _st@AppState{aopts=uopts, ajournal=j, aScreen=AccountsScreen{ f:_ -> withAttr ("border" <> "bold") $ str $ takeFileName f -- [f,_:[]] -> (withAttr ("border" <> "bold") $ str $ takeFileName f) <+> str " (& 1 included file)" -- f:fs -> (withAttr ("border" <> "bold") $ str $ takeFileName f) <+> str (" (& " ++ show (length fs) ++ " included files)") - querystr = query_ $ reportopts_ $ cliopts_ uopts - mdepth = depth_ $ reportopts_ $ cliopts_ uopts - cleared = if (cleared_ $ reportopts_ $ cliopts_ uopts) - then str " with " <+> withAttr (borderAttr <> "query") (str "cleared") <+> str " txns" - else str "" + querystr = query_ ropts + mdepth = depth_ ropts + togglefilters = + case concat [ + if cleared_ ropts then ["cleared"] else [] + ,if real_ ropts then ["real"] else [] + ] of + [] -> str "" + fs -> str " with " <+> withAttr (borderAttr <> "query") (str $ intercalate ", " fs) <+> str " txns" cur = str (case l^.listSelectedL of Nothing -> "-" Just i -> show (i + 1)) @@ -133,6 +139,7 @@ drawAccountsScreen _st@AppState{aopts=uopts, ajournal=j, aScreen=AccountsScreen{ ("-=1234567890", "depth") ,("F", "flat?") ,("C", "cleared?") + ,("R", "real?") ,("right/enter", "register") ,("g", "reload") ,("q", "quit") @@ -249,6 +256,7 @@ handleAccountsScreen st@AppState{ Vty.EvKey (Vty.KChar '0') [] -> continue $ reload j d $ setDepth 0 st' Vty.EvKey (Vty.KChar 'F') [] -> continue $ reload j d $ stToggleFlat st' Vty.EvKey (Vty.KChar 'C') [] -> continue $ reload j d $ stToggleCleared st' + Vty.EvKey (Vty.KChar 'R') [] -> continue $ reload j d $ stToggleReal st' Vty.EvKey (Vty.KLeft) [] -> continue $ popScreen st' Vty.EvKey (k) [] | k `elem` [Vty.KRight, Vty.KEnter] -> do let @@ -264,15 +272,6 @@ handleAccountsScreen st@AppState{ -- continue =<< handleEventLensed st' someLens ev handleAccountsScreen _ _ = error "event handler called with wrong screen type, should not happen" -stToggleFlat :: AppState -> AppState -stToggleFlat st@AppState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} = - st{aopts=uopts{cliopts_=copts{reportopts_=toggleFlatMode ropts}}} - --- | Toggle between flat and tree mode. If in the third "default" mode, go to flat mode. -toggleFlatMode :: ReportOpts -> ReportOpts -toggleFlatMode ropts@ReportOpts{accountlistmode_=ALFlat} = ropts{accountlistmode_=ALTree} -toggleFlatMode ropts = ropts{accountlistmode_=ALFlat} - -- | Get the maximum account depth in the current journal. maxDepth :: AppState -> Int maxDepth AppState{ajournal=j} = maximum $ map accountNameLevel $ journalAccountNames j diff --git a/hledger-ui/Hledger/UI/RegisterScreen.hs b/hledger-ui/Hledger/UI/RegisterScreen.hs index a94f3adb0..3e59bde74 100644 --- a/hledger-ui/Hledger/UI/RegisterScreen.hs +++ b/hledger-ui/Hledger/UI/RegisterScreen.hs @@ -85,11 +85,11 @@ initRegisterScreen d st@AppState{aopts=opts, ajournal=j, aScreen=s@RegisterScree initRegisterScreen _ _ = error "init function called with wrong screen type, should not happen" drawRegisterScreen :: AppState -> [Widget] -drawRegisterScreen AppState{aopts=uopts -- @UIOpts{cliopts_=_copts@CliOpts{reportopts_=_ropts@ReportOpts{query_=querystr}} +drawRegisterScreen AppState{aopts=UIOpts{cliopts_=CliOpts{reportopts_=ropts}} ,aScreen=RegisterScreen{rsState=(l,acct)}} = [ui] where toplabel = withAttr ("border" <> "bold") (str $ T.unpack acct) - <+> cleared + <+> togglefilters <+> str " transactions" -- <+> borderQueryStr querystr -- no, account transactions report shows all transactions in the acct ? -- <+> str " and subs" @@ -98,9 +98,13 @@ drawRegisterScreen AppState{aopts=uopts -- @UIOpts{cliopts_=_copts@CliOpts{repor <+> str "/" <+> total <+> str ")" - cleared = if (cleared_ $ reportopts_ $ cliopts_ uopts) - then withAttr (borderAttr <> "query") (str " cleared") - else str "" + togglefilters = + case concat [ + if cleared_ ropts then ["cleared"] else [] + ,if real_ ropts then ["real"] else [] + ] of + [] -> str "" + fs -> withAttr (borderAttr <> "query") (str $ " " ++ intercalate ", " fs) cur = str $ case l^.listSelectedL of Nothing -> "-" Just i -> show (i + 1) @@ -158,6 +162,7 @@ drawRegisterScreen AppState{aopts=uopts -- @UIOpts{cliopts_=_copts@CliOpts{repor -- ("up/down/pgup/pgdown/home/end", "move") ("left", "back") ,("C", "cleared?") + ,("R", "real?") ,("right/enter", "transaction") ,("g", "reload") ,("q", "quit") @@ -206,6 +211,7 @@ handleRegisterScreen st@AppState{ Left err -> continue $ screenEnter d ES.screen{esState=err} st Vty.EvKey (Vty.KChar 'C') [] -> continue $ reload j d $ stToggleCleared st + Vty.EvKey (Vty.KChar 'R') [] -> continue $ reload j d $ stToggleReal st Vty.EvKey (Vty.KLeft) [] -> continue $ popScreen st diff --git a/hledger-ui/Hledger/UI/TransactionScreen.hs b/hledger-ui/Hledger/UI/TransactionScreen.hs index b0249f4ee..a63c480e1 100644 --- a/hledger-ui/Hledger/UI/TransactionScreen.hs +++ b/hledger-ui/Hledger/UI/TransactionScreen.hs @@ -9,7 +9,7 @@ where -- import Lens.Micro ((^.)) import Control.Monad.IO.Class (liftIO) --- import Data.List +import Data.List -- import Data.List.Split (splitOn) -- import Data.Ord import Data.Monoid @@ -22,7 +22,7 @@ import Graphics.Vty as Vty -- import Safe (headDef, lastDef) import Brick import Brick.Widgets.List (listMoveTo) --- import Brick.Widgets.Border +import Brick.Widgets.Border (borderAttr) -- import Brick.Widgets.Border.Style -- import Brick.Widgets.Center -- import Text.Printf @@ -43,12 +43,22 @@ screen = TransactionScreen{ } initTransactionScreen :: Day -> AppState -> AppState -initTransactionScreen _d st@AppState{aopts=_opts, ajournal=_j, aScreen=_s@TransactionScreen{tsState=_}} = st +initTransactionScreen d st@AppState{aopts=UIOpts{cliopts_=CliOpts{reportopts_=ropts}} + ,ajournal=_j + ,aScreen=s@TransactionScreen{tsState=((n,t),nts,a)}} = + st{aScreen=s{tsState=((n, t'),nts,a)}} + where + -- re-filter the postings, eg because real/virtual was toggled. + -- get the original transaction from the list passed from the register screen. + t' = case lookup n nts of + Just torig -> filterTransactionPostings (queryFromOpts d ropts) torig + Nothing -> t -- shouldn't happen + initTransactionScreen _ _ = error "init function called with wrong screen type, should not happen" drawTransactionScreen :: AppState -> [Widget] -drawTransactionScreen AppState{ -- aopts=_uopts@UIOpts{cliopts_=_copts@CliOpts{reportopts_=_ropts@ReportOpts{query_=querystr}}}, - aScreen=TransactionScreen{tsState=((i,t),nts,acct)}} = [ui] +drawTransactionScreen AppState{aopts=UIOpts{cliopts_=CliOpts{reportopts_=ropts}} + ,aScreen=TransactionScreen{tsState=((i,t),nts,acct)}} = [ui] where -- datedesc = show (tdate t) ++ " " ++ tdescription t toplabel = @@ -59,9 +69,18 @@ drawTransactionScreen AppState{ -- aopts=_uopts@UIOpts{cliopts_=_copts@CliOpts{r <+> str " (" <+> withAttr ("border" <> "bold") (str $ show i) <+> str (" of "++show (length nts)++" in "++T.unpack acct++")") + <+> togglefilters + togglefilters = + case concat [ + if cleared_ ropts then ["cleared"] else [] + ,if real_ ropts then ["real"] else [] + ] of + [] -> str "" + fs -> withAttr (borderAttr <> "query") (str $ " " ++ intercalate ", " fs) <+> str " postings" bottomlabel = borderKeysStr [ ("left", "back") ,("up/down", "prev/next") + ,("R", "real?") ,("g", "reload") ,("q", "quit") ] @@ -114,6 +133,10 @@ handleTransactionScreen st@AppState{ -- Vty.EvKey (Vty.KChar 'C') [] -> continue $ reload j d $ stToggleCleared st + Vty.EvKey (Vty.KChar 'R') [] -> + -- just show/hide the real postings in this transaction, don't bother updating parent screens + continue $ reload j d $ stToggleReal st + Vty.EvKey (Vty.KUp) [] -> continue $ reload j d st{aScreen=s{tsState=((iprev,tprev),nts,acct)}} Vty.EvKey (Vty.KDown) [] -> continue $ reload j d st{aScreen=s{tsState=((inext,tnext),nts,acct)}} diff --git a/hledger-ui/Hledger/UI/UIUtils.hs b/hledger-ui/Hledger/UI/UIUtils.hs index 01761c4ba..74ca5e104 100644 --- a/hledger-ui/Hledger/UI/UIUtils.hs +++ b/hledger-ui/Hledger/UI/UIUtils.hs @@ -16,6 +16,8 @@ module Hledger.UI.UIUtils ( ,borderKeysStr -- ,stToggleCleared + ,stToggleFlat + ,stToggleReal ) where import Lens.Micro ((^.)) @@ -47,6 +49,23 @@ stToggleCleared st@AppState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts toggleCleared :: ReportOpts -> ReportOpts toggleCleared ropts = ropts{cleared_=not $ cleared_ ropts} +stToggleFlat :: AppState -> AppState +stToggleFlat st@AppState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} = + st{aopts=uopts{cliopts_=copts{reportopts_=toggleFlatMode ropts}}} + +-- | Toggle between flat and tree mode. If in the third "default" mode, go to flat mode. +toggleFlatMode :: ReportOpts -> ReportOpts +toggleFlatMode ropts@ReportOpts{accountlistmode_=ALFlat} = ropts{accountlistmode_=ALTree} +toggleFlatMode ropts = ropts{accountlistmode_=ALFlat} + +stToggleReal :: AppState -> AppState +stToggleReal st@AppState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} = + st{aopts=uopts{cliopts_=copts{reportopts_=toggleReal ropts}}} + +-- | Toggle between showing all and showing only real (non-virtual) items. +toggleReal :: ReportOpts -> ReportOpts +toggleReal ropts = ropts{real_=not $ real_ ropts} + -- | Regenerate the content for the current and previous screens, from a new journal and current date. reload :: Journal -> Day -> AppState -> AppState reload j d st@AppState{aScreen=s,aPrevScreens=ss} =