diff --git a/CHANGES b/CHANGES
--- a/CHANGES
+++ b/CHANGES
@@ -1,13 +1,112 @@
 User-visible changes in hledger-ui.
 See also hledger's change log.
 
-0.27.5 (2016/05/27)
 
-- GHC 8 compatibility
+# 1.0 (2016/10/26)
 
-0.27.4 (2016/5/19)
+## accounts screen
 
-- ensure we have the Show instance for functions, needed since regex-tdfa 1.2.2
+-   at depth 0, show accounts on one "All" line and show all transactions in the register
+
+-   0 now sets depth limit to 0 instead of clearing it
+
+-   always use --no-elide for a more regular accounts tree
+
+## register screen
+
+-   registers can now include/exclude subaccount transactions.
+
+    The register screen now includes subaccounts' transactions if the
+    accounts screen was in tree mode, or when showing an account
+    which was at the depth limit. Ie, it always shows the
+    transactions contributing to the balance displayed on the
+    accounts screen. As on the accounts screen, F toggles between
+    tree mode/subaccount txns included by default and flat
+    mode/subaccount txns excluded by default. (At least, it does when
+    it would make a difference.)
+
+-   register transactions are filtered by realness and status (#354).
+
+    Two fixes for the account transactions report when --real/--cleared/real:/status: 
+    are in effect, affecting hledger-ui and hledger-web:
+    
+    1.  exclude transactions which affect the current account via an excluded posting type.
+        Eg when --real is in effect, a transaction posting to the current account with only
+        virtual postings will not appear in the report.
+    
+    2.  when showing historical balances, don't count excluded posting types in the
+        starting balance. Eg with --real, the starting balance will be the sum of only the
+        non-virtual prior postings.
+        
+        This is complicated and there might be some ways to confuse it still, causing
+        wrongly included/excluded transactions or wrong historical balances/running totals
+        (transactions with both real and virtual postings to the current account, perhaps ?)
+
+-   show more accurate dates when postings have their own dates.
+
+    If postings to the register account matched by the register's
+    filter query have their own dates, we show the earliest of these
+    as the transaction date.
+
+## misc
+
+-   H toggles between showing "historical" or "period" balances (#392).
+
+    By default hledger-ui now shows historical balances, which
+    include transactions before the report start date (like hledger
+    balance --historical). Use the H key to toggle to "period" mode,
+    where balances start from 0 on the report start date.
+
+-   shift arrow keys allow quick period browsing
+
+    -   shift-down narrows to the next smaller standard period
+        (year/quarter/month/week/day), shift-up does the reverse
+    -   when narrowed to a standard period, shift-right/left moves to
+        the next/previous period
+    -   \`t\` sets the period to today.
+
+-   a runs the add command
+
+-   E runs $HLEDGER_UI_EDITOR or $EDITOR or a default editor (vi) on the journal file.
+
+    When using emacs or vi, if a transaction is selected the cursor will be positioned at its journal entry.
+
+-   / key sets the filter query; BACKSPACE/DELETE clears it
+
+-   Z toggles display of zero items (like --empty), and they are shown by default.
+
+    -E/--empty is now the default for hledger-ui, so accounts with 0 balance
+    and transactions posting 0 change are shown by default.  The Z key
+    toggles this, entering "nonzero" mode which hides zero items.
+
+-   R toggles inclusion of only real (non-virtual) postings
+
+-   U toggles inclusion of only uncleared transactions/postings
+
+-   I toggles balance assertions checking, useful for troubleshooting
+
+-   vi-style movement keys are now supported (for help, you must now use ? not h) (#357)
+
+-   ESC cancels minibuffer/help or clears the filter query and jumps to top screen
+
+-   ENTER has been reserved for later use
+
+-   reloading now preserves any options and modes in effect
+
+-   reloading on the error screen now updates the message rather than entering a new error screen
+
+-   the help dialog is more detailed, includes the hledger-ui manual, and uses the full terminal width if needed
+
+-   the header/footer content is more efficient; historical/period and tree/flat modes are now indicated in the footer
+
+-   date: query args on the command line now affect the report period.
+
+    A date2: arg or --date2 flag might also affect it (untested).
+
+-   hledger-ui now uses the quicker-building microlens
+
+
+
 
 0.27.3 (2016/1/12)
 
diff --git a/Hledger/UI/AccountsScreen.hs b/Hledger/UI/AccountsScreen.hs
--- a/Hledger/UI/AccountsScreen.hs
+++ b/Hledger/UI/AccountsScreen.hs
@@ -4,72 +4,71 @@
 {-# LANGUAGE RecordWildCards #-}
 
 module Hledger.UI.AccountsScreen
- (screen
- ,initAccountsScreen
+ (accountsScreen
+ ,asInit
  ,asSetSelectedAccount
  )
 where
 
-import Control.Lens ((^.))
--- import Control.Monad
+import Brick
+import Brick.Widgets.List
+import Brick.Widgets.Edit
+import Brick.Widgets.Border (borderAttr)
+import Control.Monad
 import Control.Monad.IO.Class (liftIO)
--- import Data.Default
 import Data.List
 import Data.Maybe
 import Data.Monoid
+import qualified Data.Text as T
 import Data.Time.Calendar (Day)
-import System.FilePath (takeFileName)
 import qualified Data.Vector as V
-import Graphics.Vty as Vty
-import Brick
-import Brick.Widgets.List
-import Brick.Widgets.Border (borderAttr)
--- import Brick.Widgets.Center
+import Graphics.Vty (Event(..),Key(..),Modifier(..))
+import Lens.Micro.Platform
+import System.Console.ANSI
+import System.FilePath (takeFileName)
 
 import Hledger
 import Hledger.Cli hiding (progname,prognameandversion,green)
--- import Hledger.Cli.CliOptions (defaultBalanceLineFormat)
 import Hledger.UI.UIOptions
--- import Hledger.UI.Theme
 import Hledger.UI.UITypes
+import Hledger.UI.UIState
 import Hledger.UI.UIUtils
-import qualified Hledger.UI.RegisterScreen as RS (screen, rsSetCurrentAccount)
-import qualified Hledger.UI.ErrorScreen as ES (screen)
+import Hledger.UI.Editor
+import Hledger.UI.RegisterScreen
+import Hledger.UI.ErrorScreen
 
-screen = AccountsScreen{
-   asState   = (list "accounts" V.empty 1, "")
-  ,sInitFn   = initAccountsScreen
-  ,sDrawFn   = drawAccountsScreen
-  ,sHandleFn = handleAccountsScreen
+accountsScreen :: Screen
+accountsScreen = AccountsScreen{
+   sInit   = asInit
+  ,sDraw   = asDraw
+  ,sHandle = asHandle
+  ,_asList            = list AccountsList V.empty 1
+  ,_asSelectedAccount = ""
   }
 
-asSetSelectedAccount a scr@AccountsScreen{asState=(l,_)} = scr{asState=(l,a)}
-asSetSelectedAccount _ scr = scr
-
-initAccountsScreen :: Day -> AppState -> AppState
-initAccountsScreen d st@AppState{
+asInit :: Day -> Bool -> UIState -> UIState
+asInit d reset ui@UIState{
   aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}},
   ajournal=j,
-  aScreen=s@AccountsScreen{asState=(_,selacct)}
+  aScreen=s@AccountsScreen{}
   } =
-  st{aopts=uopts', aScreen=s{asState=(l',selacct)}}
+  ui{aopts=uopts', aScreen=s & asList .~ newitems'}
    where
-    l = list (Name "accounts") (V.fromList displayitems) 1
+    newitems = list AccountsList (V.fromList displayitems) 1
 
-    -- keep the selection near the last known selected account if possible
-    l' | null selacct = l
-       | otherwise = maybe l (flip listMoveTo l) midx
+    -- keep the selection near the last selected account
+    -- (may need to move to the next leaf account when entering flat mode)
+    newitems' = listMoveTo selidx newitems
       where
-        midx = findIndex (\((a,_,_),_) -> a==selacctclipped) items
-        selacctclipped = case depth_ ropts of
-                          Nothing -> selacct
-                          Just d  -> clipAccountName d selacct
-
+        selidx = case (reset, listSelectedElement $ _asList s) of
+                   (True, _)               -> 0
+                   (_, Nothing)            -> 0
+                   (_, Just (_,AccountsScreenItem{asItemAccountName=a})) -> fromMaybe (fromMaybe 0 mprefixmatch) mexactmatch
+                     where
+                       mexactmatch  = findIndex ((a ==)                      . asItemAccountName) displayitems
+                       mprefixmatch = findIndex ((a `isAccountNamePrefixOf`) . asItemAccountName) displayitems
     uopts' = uopts{cliopts_=copts{reportopts_=ropts'}}
-    ropts' = ropts {
-      -- XXX balanceReport doesn't respect this yet
-      balancetype_=HistoricalBalance
-      }
+    ropts' = ropts{accountlistmode_=if flat_ ropts then ALFlat else ALTree}
 
     q = queryFromOpts d ropts
 
@@ -80,230 +79,275 @@
         valuedate = fromMaybe d $ queryEndDate False q
 
     -- run the report
-    (items,_total) = convert $ balanceReport ropts' q j
+    (items,_total) = convert $ report ropts' q j
+      where
+        -- still using the old balanceReport for change reports as it
+        -- does not include every account from before the report period
+        report | balancetype_ ropts == HistoricalBalance = singleBalanceReport
+               | otherwise                               = balanceReport
 
+
     -- pre-render the list items
-    displayitem ((fullacct, shortacct, indent), bal) =
-      (indent
-      ,fullacct
-      ,if flat_ ropts' then fullacct else shortacct
-      ,map showAmountWithoutPrice amts -- like showMixedAmountOneLineWithoutPrice
-      )
+    displayitem (fullacct, shortacct, indent, bal) =
+      AccountsScreenItem{asItemIndentLevel        = indent
+                        ,asItemAccountName        = fullacct
+                        ,asItemDisplayAccountName = replaceHiddenAccountsNameWith "All" $ if flat_ ropts' then fullacct else shortacct
+                        ,asItemRenderedAmounts    = map showAmountWithoutPrice amts -- like showMixedAmountOneLineWithoutPrice
+                        }
       where
         Mixed amts = normaliseMixedAmountSquashPricesForDisplay $ stripPrices bal
         stripPrices (Mixed as) = Mixed $ map stripprice as where stripprice a = a{aprice=NoPrice}
     displayitems = map displayitem items
 
 
-initAccountsScreen _ _ = error "init function called with wrong screen type, should not happen"
+asInit _ _ _ = 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,_)}} =
-  [ui]
-    where
-      toplabel = files
-              <+> str " accounts"
-              <+> borderQueryStr querystr
-              <+> cleared
-              <+> borderDepthStr mdepth
-              <+> str " ("
-              <+> cur
-              <+> str "/"
-              <+> total
-              <+> str ")"
-      files = case journalFilePaths j of
-                     [] -> str ""
-                     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 ""
-      cur = str (case l^.listSelectedL of
-                  Nothing -> "-"
-                  Just i -> show (i + 1))
-      total = str $ show $ V.length $ l^.listElementsL
+asDraw :: UIState -> [Widget Name]
+asDraw UIState{aopts=UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}
+                           ,ajournal=j
+                           ,aScreen=s@AccountsScreen{}
+                           ,aMode=mode
+                           } =
+  case mode of
+    Help       -> [helpDialog, maincontent]
+    -- Minibuffer e -> [minibuffer e, maincontent]
+    _          -> [maincontent]
+  where
+    maincontent = Widget Greedy Greedy $ do
+      c <- getContext
+      let
+        availwidth =
+          -- ltrace "availwidth" $
+          c^.availWidthL
+          - 2 -- XXX due to margin ? shouldn't be necessary (cf UIUtils)
+        displayitems = s ^. asList . listElementsL
+        maxacctwidthseen =
+          -- ltrace "maxacctwidthseen" $
+          V.maximum $
+          V.map (\AccountsScreenItem{..} -> asItemIndentLevel + textWidth asItemDisplayAccountName) $
+          -- V.filter (\(indent,_,_,_) -> (indent-1) <= fromMaybe 99999 mdepth) $
+          displayitems
+        maxbalwidthseen =
+          -- ltrace "maxbalwidthseen" $
+          V.maximum $ V.map (\AccountsScreenItem{..} -> sum (map strWidth asItemRenderedAmounts) + 2 * (length asItemRenderedAmounts - 1)) displayitems
+        maxbalwidth =
+          -- ltrace "maxbalwidth" $
+          max 0 (availwidth - 2 - 4) -- leave 2 whitespace plus least 4 for accts
+        balwidth =
+          -- ltrace "balwidth" $
+          min maxbalwidth maxbalwidthseen
+        maxacctwidth =
+          -- ltrace "maxacctwidth" $
+          availwidth - 2 - balwidth
+        acctwidth =
+          -- ltrace "acctwidth" $
+          min maxacctwidth maxacctwidthseen
 
-      bottomlabel = borderKeysStr [
-         -- ("up/down/pgup/pgdown/home/end", "move")
-         ("-=1234567890", "depth")
-        ,("F", "flat?")
-        ,("C", "cleared?")
-        ,("right/enter", "register")
-        ,("g", "reload")
-        ,("q", "quit")
-        ]
+        -- XXX how to minimise the balance column's jumping around
+        -- as you change the depth limit ?
 
-      ui = Widget Greedy Greedy $ do
-        c <- getContext
-        let
-          availwidth =
-            -- ltrace "availwidth" $
-            c^.availWidthL
-            - 2 -- XXX due to margin ? shouldn't be necessary (cf UIUtils)
-          displayitems = listElements l
-          maxacctwidthseen =
-            -- ltrace "maxacctwidthseen" $
-            V.maximum $
-            V.map (\(indent,_,displayacct,_) -> indent*2 + strWidth displayacct) $
-            -- V.filter (\(indent,_,_,_) -> (indent-1) <= fromMaybe 99999 mdepth) $
-            displayitems
-          maxbalwidthseen =
-            -- ltrace "maxbalwidthseen" $
-            V.maximum $ V.map (\(_,_,_,amts) -> sum (map strWidth amts) + 2 * (length amts-1)) displayitems
-          maxbalwidth =
-            -- ltrace "maxbalwidth" $
-            max 0 (availwidth - 2 - 4) -- leave 2 whitespace plus least 4 for accts
-          balwidth =
-            -- ltrace "balwidth" $
-            min maxbalwidth maxbalwidthseen
-          maxacctwidth =
-            -- ltrace "maxacctwidth" $
-            availwidth - 2 - balwidth
-          acctwidth =
-            -- ltrace "acctwidth" $
-            min maxacctwidth maxacctwidthseen
+        colwidths = (acctwidth, balwidth)
 
-          -- XXX how to minimise the balance column's jumping around
-          -- as you change the depth limit ?
+      render $ defaultLayout toplabel bottomlabel $ renderList (asDrawItem colwidths) True (_asList s)
 
-          colwidths = (acctwidth, balwidth)
+      where
+        ishistorical = balancetype_ ropts == HistoricalBalance
 
-        render $ defaultLayout toplabel bottomlabel $ renderList l (drawAccountsItem colwidths)
+        toplabel =
+              files
+          -- <+> withAttr (borderAttr <> "query") (str (if flat_ ropts then " flat" else ""))
+          <+> nonzero
+          <+> str (if ishistorical then " accounts" else " account changes")
+          -- <+> str (if ishistorical then " balances" else " changes")
+          <+> borderPeriodStr (if ishistorical then "at end of" else "in") (period_ ropts)
+          <+> borderQueryStr querystr
+          <+> togglefilters
+          <+> borderDepthStr mdepth
+          <+> str " ("
+          <+> cur
+          <+> str "/"
+          <+> total
+          <+> str ")"
+          <+> (if ignore_assertions_ copts
+               then withAttr (borderAttr <> "query") (str " ignoring balance assertions")
+               else str "")
+          where
+            files = case journalFilePaths j of
+                           [] -> str ""
+                           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_ ropts
+            mdepth = depth_ ropts
+            togglefilters =
+              case concat [
+                   uiShowClearedStatus $ clearedstatus_ ropts
+                  ,if real_ ropts then ["real"] else []
+                  ] of
+                [] -> str ""
+                fs -> str " from " <+> withAttr (borderAttr <> "query") (str $ intercalate ", " fs) <+> str " txns"
+            nonzero | empty_ ropts = str ""
+                    | otherwise    = withAttr (borderAttr <> "query") (str " nonzero")
+            cur = str (case _asList s ^. listSelectedL of
+                        Nothing -> "-"
+                        Just i -> show (i + 1))
+            total = str $ show $ V.length $ s ^. asList . listElementsL
 
-drawAccountsScreen _ = error "draw function called with wrong screen type, should not happen"
+        bottomlabel = case mode of
+                        Minibuffer ed -> minibuffer ed
+                        _             -> quickhelp
+          where
+            selectedstr = withAttr (borderAttr <> "query") . str
+            quickhelp = borderKeysStr' [
+               ("?", str "help")
+              ,("right", str "register")
+              ,("H"
+               ,if ishistorical
+                then selectedstr "historical" <+> str "/period"
+                else str "historical/" <+> selectedstr "period")
+              ,("F"
+               ,if flat_ ropts
+                then str "tree/" <+> selectedstr "flat"
+                else selectedstr "tree" <+> str "/flat")
+              ,("-+", str "depth")
+              --,("/", "filter")
+              --,("DEL", "unfilter")
+              --,("ESC", "cancel/top")
+              ,("a", str "add")
+--               ,("g", "reload")
+              ,("q", str "quit")
+              ]
 
-drawAccountsItem :: (Int,Int) -> Bool -> (Int, String, String, [String]) -> Widget
-drawAccountsItem (acctwidth, balwidth) selected (indent, _fullacct, displayacct, balamts) =
+asDraw _ = error "draw function called with wrong screen type, should not happen"
+
+asDrawItem :: (Int,Int) -> Bool -> AccountsScreenItem -> Widget Name
+asDrawItem (acctwidth, balwidth) selected AccountsScreenItem{..} =
   Widget Greedy Fixed $ do
     -- c <- getContext
       -- let showitem = intercalate "\n" . balanceReportItemAsText defreportopts fmt
     render $
-      addamts balamts $
-      str (fitString (Just acctwidth) (Just acctwidth) True True $ replicate (2*indent) ' ' ++ displayacct) <+>
+      addamts asItemRenderedAmounts $
+      str (T.unpack $ fitText (Just acctwidth) (Just acctwidth) True True $ T.replicate (asItemIndentLevel) " " <> asItemDisplayAccountName) <+>
       str "  " <+>
-      str (balspace balamts)
+      str (balspace asItemRenderedAmounts)
       where
         balspace as = replicate n ' '
           where n = max 0 (balwidth - (sum (map strWidth as) + 2 * (length as - 1)))
-        addamts :: [String] -> Widget -> Widget
+        addamts :: [String] -> Widget Name -> Widget Name
         addamts [] w = w
         addamts [a] w = (<+> renderamt a) w
         -- foldl' :: (b -> a -> b) -> b -> t a -> b
         -- foldl' (Widget -> String -> Widget) -> Widget -> [String] -> Widget
         addamts (a:as) w = foldl' addamt (addamts [a] w) as
-        addamt :: Widget -> String -> Widget
+        addamt :: Widget Name -> String -> Widget Name
         addamt w a = ((<+> renderamt a) . (<+> str ", ")) w
-        renderamt :: String -> Widget
+        renderamt :: String -> Widget Name
         renderamt a | '-' `elem` a = withAttr (sel $ "list" <> "balance" <> "negative") $ str a
                     | otherwise    = withAttr (sel $ "list" <> "balance" <> "positive") $ str a
         sel | selected  = (<> "selected")
             | otherwise = id
 
-handleAccountsScreen :: AppState -> Vty.Event -> EventM (Next AppState)
-handleAccountsScreen st@AppState{
-   aScreen=scr@AccountsScreen{asState=(l,selacct)}
+asHandle :: UIState -> Event -> EventM Name (Next UIState)
+asHandle ui0@UIState{
+   aScreen=scr@AccountsScreen{..}
+  ,aopts=UIOpts{cliopts_=copts}
   ,ajournal=j
-  } e = do
-    d <- liftIO getCurrentDay
-    -- c <- getContext
-    -- let h = c^.availHeightL
-    --     moveSel n l = listMoveBy n l
+  ,aMode=mode
+  } ev = do
+  d <- liftIO getCurrentDay
+  -- c <- getContext
+  -- let h = c^.availHeightL
+  --     moveSel n l = listMoveBy n l
 
-    -- before we go anywhere, remember the currently selected account.
-    -- (This is preserved across screen changes, unlike List's selection state)
-    let
-      selacct' = case listSelectedElement l of
-                  Just (_, (_, fullacct, _, _)) -> fullacct
-                  Nothing -> selacct
-      st' = st{aScreen=scr{asState=(l,selacct')}}
+  -- save the currently selected account, in case we leave this screen and lose the selection
+  let
+    selacct = case listSelectedElement _asList of
+                Just (_, AccountsScreenItem{..}) -> asItemAccountName
+                Nothing -> scr ^. asSelectedAccount
+    ui = ui0{aScreen=scr & asSelectedAccount .~ selacct}
 
-    case e of
-        Vty.EvKey Vty.KEsc []        -> halt st'
-        Vty.EvKey (Vty.KChar 'q') [] -> halt st'
-        -- Vty.EvKey (Vty.KChar 'l') [Vty.MCtrl] -> do
+  case mode of
+    Minibuffer ed ->
+      case ev of
+        EvKey KEsc   [] -> continue $ closeMinibuffer ui
+        EvKey KEnter [] -> continue $ regenerateScreens j d $ setFilter s $ closeMinibuffer ui
+                            where s = chomp $ unlines $ map strip $ getEditContents ed
+        ev              -> do ed' <- handleEditorEvent ev ed
+                              continue $ ui{aMode=Minibuffer ed'}
 
-        Vty.EvKey (Vty.KChar 'g') [] -> do
-          ej <- liftIO $ journalReload j  -- (ej, changed) <- liftIO $ journalReloadIfChanged copts j
-          case ej of
-            Right j' -> continue $ reload j' d st'
-            Left err -> continue $ screenEnter d ES.screen{esState=err} st'
+    Help ->
+      case ev of
+        EvKey (KChar 'q') [] -> halt ui
+        _                    -> helpHandle ui ev
 
-        Vty.EvKey (Vty.KChar '-') [] -> continue $ reload j d $ decDepth st'
-        Vty.EvKey (Vty.KChar '+') [] -> continue $ reload j d $ incDepth st'
-        Vty.EvKey (Vty.KChar '=') [] -> continue $ reload j d $ incDepth st'
-        Vty.EvKey (Vty.KChar '1') [] -> continue $ reload j d $ setDepth 1 st'
-        Vty.EvKey (Vty.KChar '2') [] -> continue $ reload j d $ setDepth 2 st'
-        Vty.EvKey (Vty.KChar '3') [] -> continue $ reload j d $ setDepth 3 st'
-        Vty.EvKey (Vty.KChar '4') [] -> continue $ reload j d $ setDepth 4 st'
-        Vty.EvKey (Vty.KChar '5') [] -> continue $ reload j d $ setDepth 5 st'
-        Vty.EvKey (Vty.KChar '6') [] -> continue $ reload j d $ setDepth 6 st'
-        Vty.EvKey (Vty.KChar '7') [] -> continue $ reload j d $ setDepth 7 st'
-        Vty.EvKey (Vty.KChar '8') [] -> continue $ reload j d $ setDepth 8 st'
-        Vty.EvKey (Vty.KChar '9') [] -> continue $ reload j d $ setDepth 9 st'
-        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.KLeft) []     -> continue $ popScreen st'
-        Vty.EvKey (k) [] | k `elem` [Vty.KRight, Vty.KEnter] -> do
-          let
-            scr = RS.rsSetCurrentAccount selacct' RS.screen
-            st'' = screenEnter d scr st'
-          vScrollToBeginning $ viewportScroll "register"
-          continue st''
+    Normal ->
+      case ev of
+        EvKey (KChar 'q') [] -> halt ui
+        -- EvKey (KChar 'l') [MCtrl] -> do
+        EvKey KEsc        [] -> continue $ resetScreens d ui
+        EvKey (KChar c)   [] | c `elem` ['?'] -> continue $ setMode Help ui
+        EvKey (KChar 'g') [] -> liftIO (uiReloadJournalIfChanged copts d j ui) >>= continue
+        EvKey (KChar 'I') [] -> continue $ uiCheckBalanceAssertions d (toggleIgnoreBalanceAssertions ui)
+        EvKey (KChar 'a') [] -> suspendAndResume $ clearScreen >> setCursorPosition 0 0 >> add copts j >> uiReloadJournalIfChanged copts d j ui
+        EvKey (KChar 'E') [] -> suspendAndResume $ void (runEditor endPos (journalFilePath j)) >> uiReloadJournalIfChanged copts d j ui
+        EvKey (KChar '0') [] -> continue $ regenerateScreens j d $ setDepth (Just 0) ui
+        EvKey (KChar '1') [] -> continue $ regenerateScreens j d $ setDepth (Just 1) ui
+        EvKey (KChar '2') [] -> continue $ regenerateScreens j d $ setDepth (Just 2) ui
+        EvKey (KChar '3') [] -> continue $ regenerateScreens j d $ setDepth (Just 3) ui
+        EvKey (KChar '4') [] -> continue $ regenerateScreens j d $ setDepth (Just 4) ui
+        EvKey (KChar '5') [] -> continue $ regenerateScreens j d $ setDepth (Just 5) ui
+        EvKey (KChar '6') [] -> continue $ regenerateScreens j d $ setDepth (Just 6) ui
+        EvKey (KChar '7') [] -> continue $ regenerateScreens j d $ setDepth (Just 7) ui
+        EvKey (KChar '8') [] -> continue $ regenerateScreens j d $ setDepth (Just 8) ui
+        EvKey (KChar '9') [] -> continue $ regenerateScreens j d $ setDepth (Just 9) ui
+        EvKey (KChar '-') [] -> continue $ regenerateScreens j d $ decDepth ui
+        EvKey (KChar '_') [] -> continue $ regenerateScreens j d $ decDepth ui
+        EvKey (KChar c)   [] | c `elem` ['+','='] -> continue $ regenerateScreens j d $ incDepth ui
+        EvKey (KChar 't') []    -> continue $ regenerateScreens j d $ setReportPeriod (DayPeriod d) ui
+        EvKey (KChar 'H') [] -> continue $ regenerateScreens j d $ toggleHistorical ui
+        EvKey (KChar 'F') [] -> continue $ regenerateScreens j d $ toggleFlat ui
+        EvKey (KChar 'Z') [] -> scrollTop >> (continue $ regenerateScreens j d $ toggleEmpty ui)
+        EvKey (KChar 'C') [] -> scrollTop >> (continue $ regenerateScreens j d $ toggleCleared ui)
+        EvKey (KChar 'U') [] -> scrollTop >> (continue $ regenerateScreens j d $ toggleUncleared ui)
+        EvKey (KChar 'R') [] -> scrollTop >> (continue $ regenerateScreens j d $ toggleReal ui)
+        EvKey (KDown)     [MShift] -> continue $ regenerateScreens j d $ shrinkReportPeriod d ui
+        EvKey (KUp)       [MShift] -> continue $ regenerateScreens j d $ growReportPeriod d ui
+        EvKey (KRight)    [MShift] -> continue $ regenerateScreens j d $ nextReportPeriod journalspan ui
+        EvKey (KLeft)     [MShift] -> continue $ regenerateScreens j d $ previousReportPeriod journalspan ui
+        EvKey (KChar '/') [] -> continue $ regenerateScreens j d $ showMinibuffer ui
+        EvKey k           [] | k `elem` [KBS, KDel] -> (continue $ regenerateScreens j d $ resetFilter ui)
+        EvKey k           [] | k `elem` [KLeft, KChar 'h']  -> continue $ popScreen ui
+        EvKey k           [] | k `elem` [KRight, KChar 'l'] -> scrollTopRegister >> continue (screenEnter d scr ui)
+          where
+            scr = rsSetAccount selacct isdepthclipped registerScreen
+            isdepthclipped = case getDepth ui of
+                                Just d  -> accountNameLevel selacct >= d
+                                Nothing -> False
 
         -- fall through to the list's event handler (handles up/down)
-        ev                       -> do
-                                     l' <- handleEvent ev l
-                                     continue $ st'{aScreen=scr{asState=(l',selacct')}}
-                                 -- 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
+        ev -> do
+                let ev' = case ev of
+                            EvKey (KChar 'k') [] -> EvKey (KUp) []
+                            EvKey (KChar 'j') [] -> EvKey (KDown) []
+                            _                    -> ev
+                newitems <- handleListEvent ev' _asList
+                continue $ ui{aScreen=scr & asList .~ newitems
+                                          & asSelectedAccount .~ selacct
+                                          }
+                -- continue =<< handleEventLensed ui someLens ev
 
--- | Decrement the current depth limit towards 0. If there was no depth limit,
--- set it to one less than the maximum account depth.
-decDepth :: AppState -> AppState
-decDepth st@AppState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts@ReportOpts{..}}}}
-  = st{aopts=uopts{cliopts_=copts{reportopts_=ropts{depth_=dec depth_}}}}
   where
-    dec (Just d) = Just $ max 0 (d-1)
-    dec Nothing  = Just $ maxDepth st - 1
+    -- Encourage a more stable scroll position when toggling list items.
+    -- We scroll to the top, and the viewport will automatically
+    -- scroll down just far enough to reveal the selection, which
+    -- usually leaves it at bottom of screen).
+    -- XXX better: scroll so selection is in middle of screen ?
+    scrollTop         = vScrollToBeginning $ viewportScroll AccountsViewport
+    scrollTopRegister = vScrollToBeginning $ viewportScroll RegisterViewport
+    journalspan = journalDateSpan False j
 
--- | Increment the current depth limit. If this makes it equal to the
--- the maximum account depth, remove the depth limit.
-incDepth :: AppState -> AppState
-incDepth st@AppState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts@ReportOpts{..}}}}
-  = st{aopts=uopts{cliopts_=copts{reportopts_=ropts{depth_=inc depth_}}}}
-  where
-    inc (Just d) | d < (maxDepth st - 1) = Just $ d+1
-    inc _ = Nothing
+asHandle _ _ = error "event handler called with wrong screen type, should not happen"
 
--- | Set the current depth limit to the specified depth, which should
--- be a positive number.  If it is zero, or equal to or greater than the
--- current maximum account depth, the depth limit will be removed.
--- (Slight inconsistency here: zero is currently a valid display depth
--- which can be reached using the - key.  But we need a key to remove
--- the depth limit, and 0 is it.)
-setDepth :: Int -> AppState -> AppState
-setDepth depth st@AppState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}}
-  = st{aopts=uopts{cliopts_=copts{reportopts_=ropts{depth_=mdepth'}}}}
-  where
-    mdepth' | depth < 0            = depth_ ropts
-            | depth == 0           = Nothing
-            | depth >= maxDepth st = Nothing
-            | otherwise            = Just depth
+asSetSelectedAccount a s@AccountsScreen{} = s & asSelectedAccount .~ a
+asSetSelectedAccount _ s = s
 
diff --git a/Hledger/UI/Editor.hs b/Hledger/UI/Editor.hs
new file mode 100644
--- /dev/null
+++ b/Hledger/UI/Editor.hs
@@ -0,0 +1,85 @@
+{- | Editor integration. -}
+
+-- {-# LANGUAGE OverloadedStrings #-}
+
+module Hledger.UI.Editor
+where
+
+import Control.Applicative ((<|>))
+import Data.List
+import Safe
+import System.Environment
+import System.Exit
+import System.FilePath
+import System.Process
+
+import Hledger
+
+-- | Editors we know how to create more specific command lines for.
+data EditorType = Emacs | EmacsClient | Vi | Other
+
+-- | A position we can move to in a text editor: a line and optional column number.
+-- 1 (or 0) means the first and -1 means the last (and -2 means the second last, etc.
+-- though this may not be well supported.)
+type TextPosition = (Int, Maybe Int)
+
+endPos :: Maybe TextPosition
+endPos = Just (-1,Nothing)
+
+-- | Try running the user's preferred text editor, or a default edit command,
+-- on the main journal file, blocking until it exits, and returning the exit code;
+-- or raise an error.
+runEditor :: Maybe TextPosition -> FilePath -> IO ExitCode
+runEditor mpos f = editorOpenPositionCommand mpos f >>= runCommand >>= waitForProcess
+
+-- Get the basic shell command to start the user's preferred text editor.
+-- This is the value of environment variable $HLEDGER_UI_EDITOR, or $EDITOR, or
+-- a default (emacsclient -a '' -nw, start/connect to an emacs daemon in terminal mode).
+editorCommand :: IO String
+editorCommand = do
+  hledger_ui_editor_env <- lookupEnv "HLEDGER_UI_EDITOR"
+  editor_env            <- lookupEnv "EDITOR"
+  let Just cmd =
+        hledger_ui_editor_env
+        <|> editor_env
+        <|> Just "emacsclient -a '' -nw"
+  return cmd
+
+-- | Get a shell command to start the user's preferred text editor, or a default,
+-- and optionally jump to a given position in the file. This will be the basic
+-- editor command, with the appropriate options added, if we know how.
+-- Currently we know how to do this for emacs and vi.
+-- Some examples:
+-- $EDITOR=notepad         -> "notepad FILE"
+-- $EDITOR=vi              -> "vi +LINE FILE"
+-- $EDITOR=vi, line -1     -> "vi + FILE"
+-- $EDITOR=emacs           -> "emacs +LINE:COL FILE"
+-- $EDITOR=emacs, line -1  -> "emacs FILE -f end-of-buffer"
+-- $EDITOR not set         -> "emacs -nw FILE -f end-of-buffer"
+--
+editorOpenPositionCommand :: Maybe TextPosition -> FilePath -> IO String
+editorOpenPositionCommand mpos f = do
+  cmd <- editorCommand
+  let f' = singleQuoteIfNeeded f
+  return $
+   case (identifyEditor cmd, mpos) of
+    (EmacsClient, Just (l,mc)) | l >= 0 -> cmd ++ " " ++ emacsposopt l mc ++ " " ++ f'
+    (EmacsClient, Just (l,mc)) | l < 0  -> cmd ++ " " ++ emacsposopt 999999999 mc ++ " " ++ f'
+    (Emacs, Just (l,mc))       | l >= 0 -> cmd ++ " " ++ emacsposopt l mc ++ " " ++ f'
+    (Emacs, Just (l,_))        | l < 0  -> cmd ++ " " ++ f' ++ " -f end-of-buffer"
+    (Vi, Just (l,_))                    -> cmd ++ " " ++ viposopt l ++ " " ++ f'
+    _                                   -> cmd ++ " " ++ f'
+    where
+      emacsposopt l mc = "+" ++ show l ++ maybe "" ((":"++).show) mc
+      viposopt l       = "+" ++ if l >= 0 then show l else ""
+
+-- Identify which text editor is used in the basic editor command, if possible.
+identifyEditor :: String -> EditorType
+identifyEditor cmd
+  | "emacsclient" `isPrefixOf` exe = EmacsClient
+  | "emacs" `isPrefixOf` exe       = Emacs
+  | exe `elem` ["vi","vim","ex","view","gvim","gview","evim","eview","rvim","rview","rgvim","rgview"]
+                                   = Vi
+  | otherwise                      = Other
+  where
+    exe = lowercase $ takeFileName $ headDef "" $ words' cmd
diff --git a/Hledger/UI/ErrorScreen.hs b/Hledger/UI/ErrorScreen.hs
--- a/Hledger/UI/ErrorScreen.hs
+++ b/Hledger/UI/ErrorScreen.hs
@@ -1,125 +1,160 @@
 -- The error screen, showing a current error condition (such as a parse error after reloading the journal)
 
-{-# LANGUAGE OverloadedStrings, FlexibleContexts #-}
+{-# LANGUAGE OverloadedStrings, FlexibleContexts, RecordWildCards #-}
 
 module Hledger.UI.ErrorScreen
- (screen)
+ (errorScreen
+ ,uiCheckBalanceAssertions
+ ,uiReloadJournal
+ ,uiReloadJournalIfChanged
+ )
 where
 
--- import Control.Lens ((^.))
+import Brick
+-- import Brick.Widgets.Border (borderAttr)
+import Control.Monad
 import Control.Monad.IO.Class (liftIO)
 import Data.Monoid
--- import Data.Maybe
 import Data.Time.Calendar (Day)
-import Graphics.Vty as Vty
-import Brick
--- import Brick.Widgets.List
--- import Brick.Widgets.Border
--- import Brick.Widgets.Border.Style
--- import Brick.Widgets.Center
--- import Text.Printf
+import Graphics.Vty (Event(..),Key(..))
+import Text.Megaparsec
 
--- import Hledger
 import Hledger.Cli hiding (progname,prognameandversion,green)
 import Hledger.UI.UIOptions
--- import Hledger.UI.Theme
 import Hledger.UI.UITypes
+import Hledger.UI.UIState
 import Hledger.UI.UIUtils
+import Hledger.UI.Editor
 
-screen = ErrorScreen{
-   esState  = ""
-  ,sInitFn    = initErrorScreen
-  ,sDrawFn    = drawErrorScreen
-  ,sHandleFn = handleErrorScreen
+errorScreen :: Screen
+errorScreen = ErrorScreen{
+   sInit    = esInit
+  ,sDraw    = esDraw
+  ,sHandle  = esHandle
+  ,esError  = ""
   }
 
-initErrorScreen :: Day -> AppState -> AppState
-initErrorScreen _ st@AppState{aScreen=ErrorScreen{}} = st
-initErrorScreen _ _ = error "init function called with wrong screen type, should not happen"
+esInit :: Day -> Bool -> UIState -> UIState
+esInit _ _ ui@UIState{aScreen=ErrorScreen{}} = ui
+esInit _ _ _ = error "init function called with wrong screen type, should not happen"
 
-drawErrorScreen :: AppState -> [Widget]
-drawErrorScreen AppState{ -- aopts=_uopts@UIOpts{cliopts_=_copts@CliOpts{reportopts_=_ropts@ReportOpts{query_=querystr}}},
-                             aScreen=ErrorScreen{esState=err}} = [ui]
+esDraw :: UIState -> [Widget Name]
+esDraw UIState{ --aopts=UIOpts{cliopts_=copts@CliOpts{}}
+               aScreen=ErrorScreen{..}
+              ,aMode=mode} =
+  case mode of
+    Help       -> [helpDialog, maincontent]
+    -- Minibuffer e -> [minibuffer e, maincontent]
+    _          -> [maincontent]
   where
-    toplabel = withAttr ("border" <> "bold") (str "Oops. Please fix this problem then press g to reload")
-            -- <+> str " transactions"
-            -- <+> borderQueryStr querystr -- no, account transactions report shows all transactions in the acct ?
-            -- <+> str " and subs"
-    --         <+> str " ("
-    --         <+> cur
-    --         <+> str "/"
-    --         <+> total
-    --         <+> str ")"
-    -- cur = str $ case l^.listSelectedL of
-    --              Nothing -> "-"
-    --              Just i -> show (i + 1)
-    -- total = str $ show $ length displayitems
-    -- displayitems = V.toList $ l^.listElementsL
-    bottomlabel = borderKeysStr [
-       -- ("up/down/pgup/pgdown/home/end", "move")
-       ("g", "reload")
-      -- ,("left", "return to accounts")
-      ]
-
-
-    -- query = query_ $ reportopts_ $ cliopts_ opts
+    maincontent = Widget Greedy Greedy $ do
+      render $ defaultLayout toplabel bottomlabel $ withAttr "error" $ str $ esError
+      where
+        toplabel =
+              withAttr ("border" <> "bold") (str "Oops. Please fix this problem then press g to reload")
+              -- <+> (if ignore_assertions_ copts then withAttr (borderAttr <> "query") (str " ignoring") else str " not ignoring")
 
-    ui = Widget Greedy Greedy $ do
+        bottomlabel = case mode of
+                        -- Minibuffer ed -> minibuffer ed
+                        _             -> quickhelp
+          where
+            quickhelp = borderKeysStr [
+               ("h", "help")
+              ,("ESC", "cancel/top")
+              ,("E", "editor")
+              ,("g", "reload")
+              ,("q", "quit")
+              ]
 
-      -- calculate column widths, based on current available width
-      -- c <- getContext
-      -- let
-      --   totalwidth = c^.availWidthL
-      --                - 2 -- XXX due to margin ? shouldn't be necessary (cf UIUtils)
+esDraw _ = error "draw function called with wrong screen type, should not happen"
 
-      render $ defaultLayout toplabel bottomlabel $ withAttr "error" $ str err
+esHandle :: UIState -> Event -> EventM Name (Next UIState)
+esHandle ui@UIState{
+   aScreen=ErrorScreen{..}
+  ,aopts=UIOpts{cliopts_=copts}
+  ,ajournal=j
+  ,aMode=mode
+  } ev =
+  case mode of
+    Help ->
+      case ev of
+        EvKey (KChar 'q') [] -> halt ui
+        _                    -> helpHandle ui ev
 
-drawErrorScreen _ = error "draw function called with wrong screen type, should not happen"
+    _ -> do
+      d <- liftIO getCurrentDay
+      case ev of
+        EvKey (KChar 'q') [] -> halt ui
+        EvKey KEsc        [] -> continue $ uiCheckBalanceAssertions d $ resetScreens d ui
+        EvKey (KChar c)   [] | c `elem` ['h','?'] -> continue $ setMode Help ui
+        EvKey (KChar 'E') [] -> suspendAndResume $ void (runEditor pos f) >> uiReloadJournalIfChanged copts d j (popScreen ui)
+          where
+            (pos,f) = case parsewithString hledgerparseerrorpositionp esError of
+                        Right (f,l,c) -> (Just (l, Just c),f)
+                        Left  _       -> (endPos, journalFilePath j)
+        EvKey (KChar 'g') [] -> liftIO (uiReloadJournalIfChanged copts d j (popScreen ui)) >>= continue . uiCheckBalanceAssertions d
+--           (ej, _) <- liftIO $ journalReloadIfChanged copts d j
+--           case ej of
+--             Left err -> continue ui{aScreen=s{esError=err}} -- show latest parse error
+--             Right j' -> continue $ regenerateScreens j' d $ popScreen ui  -- return to previous screen, and reload it
+        EvKey (KChar 'I') [] -> continue $ uiCheckBalanceAssertions d (popScreen $ toggleIgnoreBalanceAssertions ui)
+        _ -> continue ui
 
--- drawErrorItem :: (Int,Int,Int,Int,Int) -> Bool -> (String,String,String,String,String) -> Widget
--- drawErrorItem (datewidth,descwidth,acctswidth,changewidth,balwidth) selected (date,desc,accts,change,bal) =
---   Widget Greedy Fixed $ do
---     render $
---       str (fitString (Just datewidth) (Just datewidth) True True date) <+>
---       str "  " <+>
---       str (fitString (Just descwidth) (Just descwidth) True True desc) <+>
---       str "  " <+>
---       str (fitString (Just acctswidth) (Just acctswidth) True True accts) <+>
---       str "   " <+>
---       withAttr changeattr (str (fitString (Just changewidth) (Just changewidth) True False change)) <+>
---       str "   " <+>
---       withAttr balattr (str (fitString (Just balwidth) (Just balwidth) True False bal))
---   where
---     changeattr | '-' `elem` change = sel $ "list" <> "amount" <> "decrease"
---                | otherwise         = sel $ "list" <> "amount" <> "increase"
---     balattr    | '-' `elem` bal    = sel $ "list" <> "balance" <> "negative"
---                | otherwise         = sel $ "list" <> "balance" <> "positive"
---     sel | selected  = (<> "selected")
---         | otherwise = id
+esHandle _ _ = error "event handler called with wrong screen type, should not happen"
 
-handleErrorScreen :: AppState -> Vty.Event -> EventM (Next AppState)
-handleErrorScreen st@AppState{
-   aScreen=s@ErrorScreen{esState=_err}
-  ,aopts=UIOpts{cliopts_=_copts}
-  ,ajournal=j
-  } e = do
-  case e of
-    Vty.EvKey Vty.KEsc []        -> halt st
-    Vty.EvKey (Vty.KChar 'q') [] -> halt st
+-- | Parse the file name, line and column number from a hledger parse error message, if possible.
+-- Temporary, we should keep the original parse error location. XXX
+hledgerparseerrorpositionp :: ParsecT Dec String t (String, Int, Int)
+hledgerparseerrorpositionp = do
+  anyChar `manyTill` char '"'
+  f <- anyChar `manyTill` (oneOf ['"','\n'])
+  string " (line "
+  l <- read <$> some digitChar
+  string ", column "
+  c <- read <$> some digitChar
+  return (f, l, c)
 
-    Vty.EvKey (Vty.KChar 'g') [] -> do
-      d <- liftIO getCurrentDay
-      ej <- liftIO $ journalReload j -- (ej, changed) <- liftIO $ journalReloadIfChanged copts j
-      case ej of
-        Left err -> continue st{aScreen=s{esState=err}} -- show latest parse error
-        Right j' -> continue $ reload j' d $ popScreen st  -- return to previous screen, and reload it
+-- Unconditionally reload the journal, regenerating the current screen
+-- and all previous screens in the history.
+-- If reloading fails, enter the error screen, or if we're already
+-- on the error screen, update the error displayed.
+-- The provided CliOpts are used for reloading, and then saved
+-- in the UIState if reloading is successful (otherwise the
+-- ui state keeps its old cli opts.)
+-- Defined here so it can reference the error screen.
+uiReloadJournal :: CliOpts -> Day -> UIState -> IO UIState
+uiReloadJournal copts d ui = do
+  ej <- journalReload copts
+  return $ case ej of
+    Right j  -> regenerateScreens j d ui{aopts=(aopts ui){cliopts_=copts}}
+    Left err ->
+      case ui of
+        UIState{aScreen=s@ErrorScreen{}} -> ui{aScreen=s{esError=err}}
+        _                                -> screenEnter d errorScreen{esError=err} ui
 
-    -- Vty.EvKey (Vty.KLeft) []     -> continue $ popScreen st
-    -- Vty.EvKey (Vty.KRight) []    -> error (show curItem) where curItem = listSelectedElement is
-    -- fall through to the list's event handler (handles [pg]up/down)
-    _                       -> do continue st
-                                 -- is' <- handleEvent ev is
-                                 -- continue st{aScreen=s{rsState=is'}}
-                                 -- continue =<< handleEventLensed st someLens e
-handleErrorScreen _ _ = error "event handler called with wrong screen type, should not happen"
+-- Like uiReloadJournal, but does not bother re-parsing the journal if
+-- the file(s) have not changed since last loaded. Always regenerates
+-- the current and previous screens though, since opts or date may have changed.
+uiReloadJournalIfChanged :: CliOpts -> Day -> Journal -> UIState -> IO UIState
+uiReloadJournalIfChanged copts d j ui = do
+  (ej, _changed) <- journalReloadIfChanged copts d j
+  return $ case ej of
+    Right j' -> regenerateScreens j' d ui{aopts=(aopts ui){cliopts_=copts}}
+    Left err ->
+      case ui of
+        UIState{aScreen=s@ErrorScreen{}} -> ui{aScreen=s{esError=err}}
+        _                                -> screenEnter d errorScreen{esError=err} ui
 
+-- Re-check any balance assertions in the current journal, and if any
+-- fail, enter (or update) the error screen. Or if balance assertions
+-- are disabled, do nothing.
+uiCheckBalanceAssertions :: Day -> UIState -> UIState
+uiCheckBalanceAssertions d ui@UIState{aopts=UIOpts{cliopts_=copts}, ajournal=j}
+  | ignore_assertions_ copts = ui
+  | otherwise =
+    case journalCheckBalanceAssertions j of
+      Right _  -> ui
+      Left err ->
+        case ui of
+          UIState{aScreen=s@ErrorScreen{}} -> ui{aScreen=s{esError=err}}
+          _                                -> screenEnter d errorScreen{esError=err} ui
diff --git a/Hledger/UI/Main.hs b/Hledger/UI/Main.hs
--- a/Hledger/UI/Main.hs
+++ b/Hledger/UI/Main.hs
@@ -10,13 +10,15 @@
 module Hledger.UI.Main where
 
 -- import Control.Applicative
--- import Control.Lens ((^.))
+-- import Lens.Micro.Platform ((^.))
 import Control.Monad
 -- import Control.Monad.IO.Class (liftIO)
 -- import Data.Default
 -- import Data.Monoid              -- 
 import Data.List
 import Data.Maybe
+-- import Data.Text (Text)
+import qualified Data.Text as T
 -- import Data.Time.Calendar
 import Safe
 import System.Exit
@@ -30,8 +32,8 @@
 import Hledger.UI.UITypes
 -- import Hledger.UI.UIUtils
 import Hledger.UI.Theme
-import Hledger.UI.AccountsScreen as AS
-import Hledger.UI.RegisterScreen as RS
+import Hledger.UI.AccountsScreen
+import Hledger.UI.RegisterScreen
 
 ----------------------------------------------------------------------
 
@@ -42,8 +44,11 @@
   run opts
     where
       run opts
-        | "help" `inRawOpts` (rawopts_ $ cliopts_ opts)            = putStr (showModeHelp uimode) >> exitSuccess
-        | "version" `inRawOpts` (rawopts_ $ cliopts_ opts)         = putStrLn prognameandversion >> exitSuccess
+        | "h"               `inRawOpts` (rawopts_ $ cliopts_ opts) = putStr (showModeUsage uimode) >> exitSuccess
+        | "help"            `inRawOpts` (rawopts_ $ cliopts_ opts) = printHelpForTopic (topicForMode uimode) >> exitSuccess
+        | "man"             `inRawOpts` (rawopts_ $ cliopts_ opts) = runManForTopic (topicForMode uimode) >> exitSuccess
+        | "info"            `inRawOpts` (rawopts_ $ cliopts_ opts) = runInfoForTopic (topicForMode uimode) >> exitSuccess
+        | "version"         `inRawOpts` (rawopts_ $ cliopts_ opts) = putStrLn prognameandversion >> exitSuccess
         | "binary-filename" `inRawOpts` (rawopts_ $ cliopts_ opts) = putStrLn (binaryfilename progname)
         | otherwise                                                = withJournalDoUICommand opts runBrickUi
 
@@ -71,11 +76,18 @@
     uopts' = uopts{
       cliopts_=copts{
          reportopts_= ropts{
-            -- ensure depth_ also reflects depth: args
-            depth_=depthfromoptsandargs,
-            -- remove depth: args from query_
-            query_=unwords $ -- as in ReportOptions, with same limitations
-                   [v | (k,v) <- rawopts_ copts, k=="args", not $ "depth" `isPrefixOf` v]
+            -- incorporate any depth: query args into depth_,
+            -- any date: query args into period_
+            depth_ =depthfromoptsandargs,
+            period_=periodfromoptsandargs,
+            query_ =unwords -- as in ReportOptions, with same limitations
+                    [v | (k,v) <- rawopts_ copts, k=="args", not $ any (`isPrefixOf` v) ["depth","date"]],
+            -- always disable boring account name eliding, unlike the CLI, for a more regular tree
+            no_elide_=True,
+            -- show items with zero amount by default, unlike the CLI
+            empty_=True,
+            -- show historical balances by default, unlike the CLI
+            balancetype_=HistoricalBalance
             }
          }
       }
@@ -83,6 +95,9 @@
         q = queryFromOpts d ropts
         depthfromoptsandargs = case queryDepth q of 99999 -> Nothing
                                                     d     -> Just d
+        datespanfromargs = queryDateSpan (date2_ ropts) $ fst $ parseQuery d (T.pack $ query_ ropts)
+        periodfromoptsandargs =
+          dateSpanAsPeriod $ spansIntersect [periodAsDateSpan $ period_ ropts, datespanfromargs]
 
     -- XXX move this stuff into Options, UIOpts
     theme = maybe defaultTheme (fromMaybe defaultTheme . getTheme) $
@@ -90,48 +105,45 @@
     mregister = maybestringopt "register" $ rawopts_ copts
 
     (scr, prevscrs) = case mregister of
-      Nothing   -> (AS.screen, [])
+      Nothing   -> (accountsScreen, [])
       -- with --register, start on the register screen, and also put
       -- the accounts screen on the prev screens stack so you can exit
       -- to that as usual.
-      Just apat -> (rsSetCurrentAccount acct RS.screen, [ascr'])
+      Just apat -> (rsSetAccount acct False registerScreen, [ascr'])
         where
           acct = headDef
                  (error' $ "--register "++apat++" did not match any account")
-                 $ filter (regexMatches apat) $ journalAccountNames j
+                 $ filter (regexMatches apat . T.unpack) $ journalAccountNames j
           -- Initialising the accounts screen is awkward, requiring
-          -- another temporary AppState value..
+          -- another temporary UIState value..
           ascr' = aScreen $
-                  AS.initAccountsScreen d $
-                  AppState{
+                  asInit d True $
+                  UIState{
                     aopts=uopts'
                    ,ajournal=j
-                   ,aScreen=asSetSelectedAccount acct AS.screen
+                   ,aScreen=asSetSelectedAccount acct accountsScreen
                    ,aPrevScreens=[]
+                   ,aMode=Normal
                    }
   
-    st = (sInitFn scr) d
-         AppState{
+    ui = (sInit scr) d True
+         UIState{
             aopts=uopts'
            ,ajournal=j
            ,aScreen=scr
            ,aPrevScreens=prevscrs
+           ,aMode=Normal
            }
-         
-    app :: App (AppState) V.Event
-    app = App {
+
+    brickapp :: App (UIState) V.Event Name
+    brickapp = App {
         appLiftVtyEvent = id
       , appStartEvent   = return
       , appAttrMap      = const theme
       , appChooseCursor = showFirstCursor
-      , appHandleEvent  = \st ev -> sHandleFn (aScreen st) st ev
-      , appDraw         = \st -> sDrawFn (aScreen st) st
-         -- XXX bizarro. removing the st arg and parameter above,
-         -- which according to GHCI does not change the type,
-         -- causes "Exception: draw function called with wrong screen type"
-         -- on entering a register. Likewise, removing the st ev args and parameters
-         -- causes an exception on exiting a register.
+      , appHandleEvent  = \ui ev -> sHandle (aScreen ui) ui ev
+      , appDraw         = \ui    -> sDraw   (aScreen ui) ui
       }
 
-  void $ defaultMain app st
+  void $ defaultMain brickapp ui
 
diff --git a/Hledger/UI/RegisterScreen.hs b/Hledger/UI/RegisterScreen.hs
--- a/Hledger/UI/RegisterScreen.hs
+++ b/Hledger/UI/RegisterScreen.hs
@@ -1,129 +1,126 @@
 -- The account register screen, showing transactions in an account, like hledger-web's register.
 
-{-# LANGUAGE OverloadedStrings, FlexibleContexts #-}
+{-# LANGUAGE OverloadedStrings, FlexibleContexts, RecordWildCards #-}
 
 module Hledger.UI.RegisterScreen
- (screen
- ,rsSetCurrentAccount
+ (registerScreen
+ ,rsSetAccount
  )
 where
 
-import Control.Lens ((^.))
+import Lens.Micro.Platform ((^.))
+import Control.Monad
 import Control.Monad.IO.Class (liftIO)
 import Data.List
 import Data.List.Split (splitOn)
 import Data.Monoid
--- import Data.Maybe
+import Data.Maybe
+import qualified Data.Text as T
 import Data.Time.Calendar (Day)
 import qualified Data.Vector as V
-import Graphics.Vty as Vty
+import Graphics.Vty (Event(..),Key(..),Modifier(..))
 import Brick
 import Brick.Widgets.List
+import Brick.Widgets.Edit
 import Brick.Widgets.Border (borderAttr)
--- import Brick.Widgets.Center
--- import Text.Printf
+import System.Console.ANSI
 
+
 import Hledger
 import Hledger.Cli hiding (progname,prognameandversion,green)
 import Hledger.UI.UIOptions
 -- import Hledger.UI.Theme
 import Hledger.UI.UITypes
+import Hledger.UI.UIState
 import Hledger.UI.UIUtils
-import qualified Hledger.UI.TransactionScreen as TS (screen)
-import qualified Hledger.UI.ErrorScreen as ES (screen)
+import Hledger.UI.Editor
+import Hledger.UI.TransactionScreen
+import Hledger.UI.ErrorScreen
 
-screen = RegisterScreen{
-   rsState   = (list "register" V.empty 1, "")
-  ,sInitFn   = initRegisterScreen
-  ,sDrawFn   = drawRegisterScreen
-  ,sHandleFn = handleRegisterScreen
+registerScreen :: Screen
+registerScreen = RegisterScreen{
+   sInit   = rsInit
+  ,sDraw   = rsDraw
+  ,sHandle = rsHandle
+  ,rsList    = list RegisterList V.empty 1
+  ,rsAccount = ""
+  ,rsForceInclusive = False
   }
 
-rsSetCurrentAccount a scr@RegisterScreen{rsState=(l,_)} = scr{rsState=(l,a)}
-rsSetCurrentAccount _ scr = scr
+rsSetAccount :: AccountName -> Bool -> Screen -> Screen
+rsSetAccount a forceinclusive scr@RegisterScreen{} =
+  scr{rsAccount=replaceHiddenAccountsNameWith "*" a, rsForceInclusive=forceinclusive}
+rsSetAccount _ _ scr = scr
 
-initRegisterScreen :: Day -> AppState -> AppState
-initRegisterScreen d st@AppState{aopts=opts, ajournal=j, aScreen=s@RegisterScreen{rsState=(_,acct)}} =
-  st{aScreen=s{rsState=(l,acct)}}
+rsInit :: Day -> Bool -> UIState -> UIState
+rsInit d reset ui@UIState{aopts=UIOpts{cliopts_=CliOpts{reportopts_=ropts}}, ajournal=j, aScreen=s@RegisterScreen{..}} =
+  ui{aScreen=s{rsList=newitems'}}
   where
     -- gather arguments and queries
-    ropts = (reportopts_ $ cliopts_ opts)
-            {
-              depth_=Nothing,
-              balancetype_=HistoricalBalance
-            }
     -- XXX temp
-    thisacctq = Acct $ accountNameToAccountRegex acct -- includes subs
-    q = filterQuery (not . queryIsDepth) $ queryFromOpts d ropts
+    inclusive = not (flat_ ropts) || rsForceInclusive
+    thisacctq = Acct $ (if inclusive then accountNameToAccountRegex else accountNameToAccountOnlyRegex) rsAccount
+    ropts' = ropts{
+               depth_=Nothing
+              }
+    q = queryFromOpts d ropts'
+--    reportq = filterQuery (not . queryIsDepth) q
 
-    -- run a transactions report, most recent last
-    q' =
-      -- ltrace "q"
-      q
-    thisacctq' =
-      -- ltrace "thisacctq"
-      thisacctq
-    (_label,items') = accountTransactionsReport ropts j q' thisacctq'
-    items = reverse items'
+    (_label,items) = accountTransactionsReport ropts' j q thisacctq
+    items' = (if empty_ ropts' then id else filter (not . isZeroMixedAmount . fifth6)) $  -- without --empty, exclude no-change txns
+             reverse  -- most recent last
+             items
 
-    -- pre-render all items; these will be the List elements. This helps calculate column widths.
-    displayitem (t, _, _issplit, otheracctsstr, change, bal) =
-      (showDate $ tdate t
-      ,tdescription t
-      ,case splitOn ", " otheracctsstr of
-        [s] -> s
-        ss  -> intercalate ", " ss
-        -- _   -> "<split>"  -- should do this if accounts field width < 30
-      ,showMixedAmountOneLineWithoutPrice change
-      ,showMixedAmountOneLineWithoutPrice bal
-      ,t
-      )
-    displayitems = map displayitem items
+    -- generate pre-rendered list items. This helps calculate column widths.
+    displayitems = map displayitem items'
+      where
+        displayitem (t, _, _issplit, otheracctsstr, change, bal) =
+          RegisterScreenItem{rsItemDate          = showDate $ transactionRegisterDate q thisacctq t
+                            ,rsItemDescription   = T.unpack $ tdescription t
+                            ,rsItemOtherAccounts = case splitOn ", " otheracctsstr of
+                                                     [s] -> s
+                                                     ss  -> intercalate ", " ss
+                                                     -- _   -> "<split>"  -- should do this if accounts field width < 30
+                            ,rsItemChangeAmount  = showMixedAmountOneLineWithoutPrice change
+                            ,rsItemBalanceAmount = showMixedAmountOneLineWithoutPrice bal
+                            ,rsItemTransaction   = t
+                            }
 
-    -- build the List, moving the selection to the end
-    l = listMoveTo (length items) $
-        list (Name "register") (V.fromList displayitems) 1
+    -- build the List
+    newitems = list RegisterList (V.fromList displayitems) 1
 
-        -- (listName someList)
+    -- keep the selection on the previously selected transaction if possible,
+    -- (eg after toggling nonzero mode), otherwise select the last element.
+    newitems' = listMoveTo newselidx newitems
+      where
+        newselidx = case (reset, listSelectedElement rsList) of
+                      (True, _)    -> endidx
+                      (_, Nothing) -> endidx
+                      (_, Just (_,RegisterScreenItem{rsItemTransaction=Transaction{tindex=ti}}))
+                                   -> fromMaybe endidx $ findIndex ((==ti) . tindex . rsItemTransaction) displayitems
+        endidx = length displayitems
 
-initRegisterScreen _ _ = error "init function called with wrong screen type, should not happen"
+rsInit _ _ _ = 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}}
-                           ,aScreen=RegisterScreen{rsState=(l,acct)}} = [ui]
+rsDraw :: UIState -> [Widget Name]
+rsDraw UIState{aopts=UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}
+                            ,aScreen=RegisterScreen{..}
+                            ,aMode=mode
+                            } =
+  case mode of
+    Help       -> [helpDialog, maincontent]
+    -- Minibuffer e -> [minibuffer e, maincontent]
+    _          -> [maincontent]
   where
-    toplabel = withAttr ("border" <> "bold") (str acct)
-            <+> cleared
-            <+> str " transactions"
-            -- <+> borderQueryStr querystr -- no, account transactions report shows all transactions in the acct ?
-            -- <+> str " and subs"
-            <+> str " ("
-            <+> cur
-            <+> str "/"
-            <+> total
-            <+> str ")"
-    cleared = if (cleared_ $ reportopts_ $ cliopts_ uopts)
-              then withAttr (borderAttr <> "query") (str " cleared")
-              else str ""
-    cur = str $ case l^.listSelectedL of
-                 Nothing -> "-"
-                 Just i -> show (i + 1)
-    total = str $ show $ length displayitems
-    displayitems = V.toList $ l^.listElementsL
-
-    -- query = query_ $ reportopts_ $ cliopts_ opts
-
-    ui = Widget Greedy Greedy $ do
-
+    displayitems = V.toList $ rsList ^. listElementsL
+    maincontent = Widget Greedy Greedy $ do
       -- calculate column widths, based on current available width
       c <- getContext
       let
         totalwidth = c^.availWidthL
                      - 2 -- XXX due to margin ? shouldn't be necessary (cf UIUtils)
-
         -- the date column is fixed width
         datewidth = 10
-
         -- multi-commodity amounts rendered on one line can be
         -- arbitrarily wide.  Give the two amounts as much space as
         -- they need, while reserving a minimum of space for other
@@ -132,14 +129,13 @@
         whitespacewidth = 10 -- inter-column whitespace, fixed width
         minnonamtcolswidth = datewidth + 2 + 2 -- date column plus at least 2 for desc and accts
         maxamtswidth = max 0 (totalwidth - minnonamtcolswidth - whitespacewidth)
-        maxchangewidthseen = maximum' $ map (strWidth . fourth6) displayitems
-        maxbalwidthseen = maximum' $ map (strWidth . fifth6) displayitems
+        maxchangewidthseen = maximum' $ map (strWidth . rsItemChangeAmount) displayitems
+        maxbalwidthseen = maximum' $ map (strWidth . rsItemBalanceAmount) displayitems
         changewidthproportion = fromIntegral maxchangewidthseen / fromIntegral (maxchangewidthseen + maxbalwidthseen)
         maxchangewidth = round $ changewidthproportion * fromIntegral maxamtswidth
         maxbalwidth = maxamtswidth - maxchangewidth
         changewidth = min maxchangewidth maxchangewidthseen 
         balwidth = min maxbalwidth maxbalwidthseen
-
         -- assign the remaining space to the description and accounts columns
         -- maxdescacctswidth = totalwidth - (whitespacewidth - 4) - changewidth - balwidth
         maxdescacctswidth =
@@ -158,76 +154,162 @@
         acctswidth = maxdescacctswidth - descwidth
         colwidths = (datewidth,descwidth,acctswidth,changewidth,balwidth)
 
-        bottomlabel = borderKeysStr [
-           -- ("up/down/pgup/pgdown/home/end", "move")
-           ("left", "back")
-          ,("C", "cleared?")
-          ,("right/enter", "transaction")
-          ,("g", "reload")
-          ,("q", "quit")
-          ]
+      render $ defaultLayout toplabel bottomlabel $ renderList (rsDrawItem colwidths) True rsList
 
-      render $ defaultLayout toplabel bottomlabel $ renderList l (drawRegisterItem colwidths)
+      where
+        ishistorical = balancetype_ ropts == HistoricalBalance
+        inclusive = not (flat_ ropts) || rsForceInclusive
 
-drawRegisterScreen _ = error "draw function called with wrong screen type, should not happen"
+        toplabel =
+              withAttr ("border" <> "bold") (str $ T.unpack $ replaceHiddenAccountsNameWith "All" rsAccount)
+--           <+> withAttr (borderAttr <> "query") (str $ if inclusive then "" else " exclusive")
+          <+> togglefilters
+          <+> str " transactions"
+          -- <+> str (if ishistorical then " historical total" else " period total")
+          <+> borderQueryStr (query_ ropts)
+          -- <+> str " and subs"
+          <+> borderPeriodStr "in" (period_ ropts)
+          <+> str " ("
+          <+> cur
+          <+> str "/"
+          <+> total
+          <+> str ")"
+          <+> (if ignore_assertions_ copts then withAttr (borderAttr <> "query") (str " ignoring balance assertions") else str "")
+          where
+            togglefilters =
+              case concat [
+                   uiShowClearedStatus $ clearedstatus_ ropts
+                  ,if real_ ropts then ["real"] else []
+                  ,if empty_ ropts then [] else ["nonzero"]
+                  ] of
+                [] -> str ""
+                fs -> withAttr (borderAttr <> "query") (str $ " " ++ intercalate ", " fs)
+            cur = str $ case rsList ^. listSelectedL of
+                         Nothing -> "-"
+                         Just i -> show (i + 1)
+            total = str $ show $ length displayitems
 
-drawRegisterItem :: (Int,Int,Int,Int,Int) -> Bool -> (String,String,String,String,String,Transaction) -> Widget
-drawRegisterItem (datewidth,descwidth,acctswidth,changewidth,balwidth) selected (date,desc,accts,change,bal,_) =
+            -- query = query_ $ reportopts_ $ cliopts_ opts
+
+        bottomlabel = case mode of
+                        Minibuffer ed -> minibuffer ed
+                        _             -> quickhelp
+          where
+            selectedstr = withAttr (borderAttr <> "query") . str
+            quickhelp = borderKeysStr' [
+               ("?", str "help")
+              ,("left", str "back")
+              ,("right", str "transaction")
+              ,("H"
+               ,if ishistorical
+                then selectedstr "historical" <+> str "/period"
+                else str "historical/" <+> selectedstr "period")
+              ,("F"
+               ,if inclusive
+                then selectedstr "inclusive" <+> str "/exclusive"
+                else str "inclusive/" <+> selectedstr "exclusive")
+--               ,("a", "add")
+--               ,("g", "reload")
+--               ,("q", "quit")
+              ]
+
+rsDraw _ = error "draw function called with wrong screen type, should not happen"
+
+rsDrawItem :: (Int,Int,Int,Int,Int) -> Bool -> RegisterScreenItem -> Widget Name
+rsDrawItem (datewidth,descwidth,acctswidth,changewidth,balwidth) selected RegisterScreenItem{..} =
   Widget Greedy Fixed $ do
     render $
-      str (fitString (Just datewidth) (Just datewidth) True True date) <+>
+      str (fitString (Just datewidth) (Just datewidth) True True rsItemDate) <+>
       str "  " <+>
-      str (fitString (Just descwidth) (Just descwidth) True True desc) <+>
+      str (fitString (Just descwidth) (Just descwidth) True True rsItemDescription) <+>
       str "  " <+>
-      str (fitString (Just acctswidth) (Just acctswidth) True True accts) <+>
+      str (fitString (Just acctswidth) (Just acctswidth) True True rsItemOtherAccounts) <+>
       str "   " <+>
-      withAttr changeattr (str (fitString (Just changewidth) (Just changewidth) True False change)) <+>
+      withAttr changeattr (str (fitString (Just changewidth) (Just changewidth) True False rsItemChangeAmount)) <+>
       str "   " <+>
-      withAttr balattr (str (fitString (Just balwidth) (Just balwidth) True False bal))
+      withAttr balattr (str (fitString (Just balwidth) (Just balwidth) True False rsItemBalanceAmount))
   where
-    changeattr | '-' `elem` change = sel $ "list" <> "amount" <> "decrease"
-               | otherwise         = sel $ "list" <> "amount" <> "increase"
-    balattr    | '-' `elem` bal    = sel $ "list" <> "balance" <> "negative"
-               | otherwise         = sel $ "list" <> "balance" <> "positive"
+    changeattr | '-' `elem` rsItemChangeAmount = sel $ "list" <> "amount" <> "decrease"
+               | otherwise                     = sel $ "list" <> "amount" <> "increase"
+    balattr    | '-' `elem` rsItemBalanceAmount = sel $ "list" <> "balance" <> "negative"
+               | otherwise                      = sel $ "list" <> "balance" <> "positive"
     sel | selected  = (<> "selected")
         | otherwise = id
 
-handleRegisterScreen :: AppState -> Vty.Event -> EventM (Next AppState)
-handleRegisterScreen st@AppState{
-   aScreen=s@RegisterScreen{rsState=(l,acct)}
-  ,aopts=UIOpts{cliopts_=_copts}
+rsHandle :: UIState -> Event -> EventM Name (Next UIState)
+rsHandle ui@UIState{
+   aScreen=s@RegisterScreen{..}
+  ,aopts=UIOpts{cliopts_=copts}
   ,ajournal=j
-  } e = do
+  ,aMode=mode
+  } ev = do
   d <- liftIO getCurrentDay
-  case e of
-    Vty.EvKey Vty.KEsc []        -> halt st
-    Vty.EvKey (Vty.KChar 'q') [] -> halt st
 
-    Vty.EvKey (Vty.KChar 'g') [] -> do
-      ej <- liftIO $ journalReload j  -- (ej, changed) <- liftIO $ journalReloadIfChanged copts j
-      case ej of
-        Right j' -> continue $ reload j' d st
-        Left err -> continue $ screenEnter d ES.screen{esState=err} st
-
-    Vty.EvKey (Vty.KChar 'C') [] -> continue $ reload j d $ stToggleCleared st
-
-    Vty.EvKey (Vty.KLeft) []     -> continue $ popScreen st
+  case mode of
+    Minibuffer ed ->
+      case ev of
+        EvKey KEsc   [] -> continue $ closeMinibuffer ui
+        EvKey KEnter [] -> continue $ regenerateScreens j d $ setFilter s $ closeMinibuffer ui
+                            where s = chomp $ unlines $ map strip $ getEditContents ed
+        ev              -> do ed' <- handleEditorEvent ev ed
+                              continue $ ui{aMode=Minibuffer ed'}
 
-    Vty.EvKey (k) [] | k `elem` [Vty.KRight, Vty.KEnter] -> do
-      case listSelectedElement l of
-        Just (_, (_, _, _, _, _, t)) ->
-          let
-            ts = map sixth6 $ V.toList $ listElements l
-            numberedts = zip [1..] ts
-            i = fromIntegral $ maybe 0 (+1) $ elemIndex t ts -- XXX
-          in
-            continue $ screenEnter d TS.screen{tsState=((i,t),numberedts,acct)} st
-        Nothing -> continue st
+    Help ->
+      case ev of
+        EvKey (KChar 'q') [] -> halt ui
+        _                    -> helpHandle ui ev
 
-    -- fall through to the list's event handler (handles [pg]up/down)
-    ev                       -> do
-                                 l' <- handleEvent ev l
-                                 continue st{aScreen=s{rsState=(l',acct)}}
-                                 -- continue =<< handleEventLensed st someLens ev
+    Normal ->
+      case ev of
+        EvKey (KChar 'q') [] -> halt ui
+        EvKey KEsc        [] -> continue $ resetScreens d ui
+        EvKey (KChar c)   [] | c `elem` ['?'] -> continue $ setMode Help ui
+        EvKey (KChar 'g') [] -> liftIO (uiReloadJournalIfChanged copts d j ui) >>= continue
+        EvKey (KChar 'I') [] -> continue $ uiCheckBalanceAssertions d (toggleIgnoreBalanceAssertions ui)
+        EvKey (KChar 'a') [] -> suspendAndResume $ clearScreen >> setCursorPosition 0 0 >> add copts j >> uiReloadJournalIfChanged copts d j ui
+        EvKey (KChar 't') []    -> continue $ regenerateScreens j d $ setReportPeriod (DayPeriod d) ui
+        EvKey (KChar 'E') [] -> suspendAndResume $ void (runEditor pos f) >> uiReloadJournalIfChanged copts d j ui
+          where
+            (pos,f) = case listSelectedElement rsList of
+                        Nothing -> (endPos, journalFilePath j)
+                        Just (_, RegisterScreenItem{rsItemTransaction=Transaction{tsourcepos=GenericSourcePos f l c}}) -> (Just (l, Just c),f)
+        EvKey (KChar 'H') [] -> continue $ regenerateScreens j d $ toggleHistorical ui
+        EvKey (KChar 'F') [] -> scrollTop >> (continue $ regenerateScreens j d $ toggleFlat ui)
+        EvKey (KChar 'Z') [] -> scrollTop >> (continue $ regenerateScreens j d $ toggleEmpty ui)
+        EvKey (KChar 'C') [] -> scrollTop >> (continue $ regenerateScreens j d $ toggleCleared ui)
+        EvKey (KChar 'U') [] -> scrollTop >> (continue $ regenerateScreens j d $ toggleUncleared ui)
+        EvKey (KChar 'R') [] -> scrollTop >> (continue $ regenerateScreens j d $ toggleReal ui)
+        EvKey (KChar '/') [] -> (continue $ regenerateScreens j d $ showMinibuffer ui)
+        EvKey (KDown)     [MShift] -> continue $ regenerateScreens j d $ shrinkReportPeriod d ui
+        EvKey (KUp)       [MShift] -> continue $ regenerateScreens j d $ growReportPeriod d ui
+        EvKey (KRight)    [MShift] -> continue $ regenerateScreens j d $ nextReportPeriod journalspan ui
+        EvKey (KLeft)     [MShift] -> continue $ regenerateScreens j d $ previousReportPeriod journalspan ui
+        EvKey k           [] | k `elem` [KBS, KDel] -> (continue $ regenerateScreens j d $ resetFilter ui)
+        EvKey k           [] | k `elem` [KLeft, KChar 'h']  -> continue $ popScreen ui
+        EvKey k           [] | k `elem` [KRight, KChar 'l'] -> do
+          case listSelectedElement rsList of
+            Just (_, RegisterScreenItem{rsItemTransaction=t}) ->
+              let
+                ts = map rsItemTransaction $ V.toList $ listElements rsList
+                numberedts = zip [1..] ts
+                i = fromIntegral $ maybe 0 (+1) $ elemIndex t ts -- XXX
+              in
+                continue $ screenEnter d transactionScreen{tsTransaction=(i,t)
+                                                          ,tsTransactions=numberedts
+                                                          ,tsAccount=rsAccount} ui
+            Nothing -> continue ui
+        -- fall through to the list's event handler (handles [pg]up/down)
+        ev -> do
+                let ev' = case ev of
+                            EvKey (KChar 'k') [] -> EvKey (KUp) []
+                            EvKey (KChar 'j') [] -> EvKey (KDown) []
+                            _                    -> ev
+                newitems <- handleListEvent ev' rsList
+                continue ui{aScreen=s{rsList=newitems}}
+                -- continue =<< handleEventLensed ui someLens ev
+      where
+        -- Encourage a more stable scroll position when toggling list items (cf AccountsScreen.hs)
+        scrollTop = vScrollToBeginning $ viewportScroll RegisterViewport
+        journalspan = journalDateSpan False j
 
-handleRegisterScreen _ _ = error "event handler called with wrong screen type, should not happen"
+rsHandle _ _ = error "event handler called with wrong screen type, should not happen"
diff --git a/Hledger/UI/Theme.hs b/Hledger/UI/Theme.hs
--- a/Hledger/UI/Theme.hs
+++ b/Hledger/UI/Theme.hs
@@ -72,6 +72,7 @@
               (borderAttr <> "query", cyan `on` black & bold),
               (borderAttr <> "depth", yellow `on` black & bold),
               (borderAttr <> "keys", white `on` black & bold),
+              (borderAttr <> "minibuffer", white `on` black & bold),
               -- ("normal"                , black `on` white),
               ("list"                  , black `on` white),      -- regular list items
               ("list" <> "selected"    , white `on` blue & bold), -- selected list items
diff --git a/Hledger/UI/TransactionScreen.hs b/Hledger/UI/TransactionScreen.hs
--- a/Hledger/UI/TransactionScreen.hs
+++ b/Hledger/UI/TransactionScreen.hs
@@ -1,130 +1,178 @@
 -- The transaction screen, showing a single transaction's general journal entry.
 
-{-# LANGUAGE OverloadedStrings, TupleSections #-} -- , FlexibleContexts
+{-# LANGUAGE OverloadedStrings, TupleSections, RecordWildCards #-} -- , FlexibleContexts
 
 module Hledger.UI.TransactionScreen
- (screen
+ (transactionScreen
+ ,rsSelect
  )
 where
 
--- import Control.Lens ((^.))
+import Control.Monad
 import Control.Monad.IO.Class (liftIO)
--- import Data.List
--- import Data.List.Split (splitOn)
--- import Data.Ord
+import Data.List
 import Data.Monoid
--- import Data.Maybe
+import qualified Data.Text as T
 import Data.Time.Calendar (Day)
--- import qualified Data.Vector as V
-import Graphics.Vty as Vty
--- import Safe (headDef, lastDef)
+import Graphics.Vty (Event(..),Key(..))
 import Brick
 import Brick.Widgets.List (listMoveTo)
--- import Brick.Widgets.Border
--- import Brick.Widgets.Border.Style
--- import Brick.Widgets.Center
--- import Text.Printf
+import Brick.Widgets.Border (borderAttr)
 
 import Hledger
 import Hledger.Cli hiding (progname,prognameandversion,green)
 import Hledger.UI.UIOptions
 -- import Hledger.UI.Theme
 import Hledger.UI.UITypes
+import Hledger.UI.UIState
 import Hledger.UI.UIUtils
-import qualified Hledger.UI.ErrorScreen as ES (screen)
+import Hledger.UI.Editor
+import Hledger.UI.ErrorScreen
 
-screen = TransactionScreen{
-   tsState   = ((1,nulltransaction),[(1,nulltransaction)],"")
-  ,sInitFn   = initTransactionScreen
-  ,sDrawFn   = drawTransactionScreen
-  ,sHandleFn = handleTransactionScreen
+transactionScreen :: Screen
+transactionScreen = TransactionScreen{
+   sInit   = tsInit
+  ,sDraw   = tsDraw
+  ,sHandle = tsHandle
+  ,tsTransaction  = (1,nulltransaction)
+  ,tsTransactions = [(1,nulltransaction)]
+  ,tsAccount      = ""
   }
 
-initTransactionScreen :: Day -> AppState -> AppState
-initTransactionScreen _d st@AppState{aopts=_opts, ajournal=_j, aScreen=_s@TransactionScreen{tsState=_}} = st
-initTransactionScreen _ _ = error "init function called with wrong screen type, should not happen"
+tsInit :: Day -> Bool -> UIState -> UIState
+tsInit _d _reset ui@UIState{aopts=UIOpts{cliopts_=CliOpts{reportopts_=_ropts}}
+                                           ,ajournal=_j
+                                           ,aScreen=TransactionScreen{..}} = ui
+tsInit _ _ _ = 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]
+tsDraw :: UIState -> [Widget Name]
+tsDraw UIState{aopts=UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}
+                              ,aScreen=TransactionScreen{
+                                   tsTransaction=(i,t)
+                                  ,tsTransactions=nts
+                                  ,tsAccount=acct}
+                              ,aMode=mode} =
+  case mode of
+    Help       -> [helpDialog, maincontent]
+    -- Minibuffer e -> [minibuffer e, maincontent]
+    _          -> [maincontent]
   where
-    -- datedesc = show (tdate t) ++ " " ++ tdescription t
-    toplabel =
-      str "Transaction "
-      -- <+> withAttr ("border" <> "bold") (str $ "#" ++ show (tindex t))
-      -- <+> str (" ("++show i++" of "++show (length nts)++" in "++acct++")")
-      <+> (str $ "#" ++ show (tindex t))
-      <+> str " ("
-      <+> withAttr ("border" <> "bold") (str $ show i)
-      <+> str (" of "++show (length nts)++" in "++acct++")")
-    bottomlabel = borderKeysStr [
-       ("left", "back")
-      ,("up/down", "prev/next")
-      ,("g", "reload")
-      ,("q", "quit")
-      ]
-    ui = Widget Greedy Greedy $ do
-      render $ defaultLayout toplabel bottomlabel $ str $ showTransactionUnelidedOneLineAmounts t
-
-drawTransactionScreen _ = error "draw function called with wrong screen type, should not happen"
-
-handleTransactionScreen :: AppState -> Vty.Event -> EventM (Next AppState)
-handleTransactionScreen st@AppState{
-   aScreen=s@TransactionScreen{tsState=((i,t),nts,acct)}
-  ,aopts=UIOpts{cliopts_=CliOpts{reportopts_=ropts}}
-  ,ajournal=j
-  } e = do
-  d <- liftIO getCurrentDay
-  let
-    (iprev,tprev) = maybe (i,t) ((i-1),) $ lookup (i-1) nts
-    (inext,tnext) = maybe (i,t) ((i+1),) $ lookup (i+1) nts
-  case e of
-    Vty.EvKey Vty.KEsc []        -> halt st
-    Vty.EvKey (Vty.KChar 'q') [] -> halt st
-
-    Vty.EvKey (Vty.KChar 'g') [] -> do
-      d <- liftIO getCurrentDay
-      ej <- liftIO $ journalReload j  -- (ej, changed) <- liftIO $ journalReloadIfChanged copts j
-      case ej of
-        Right j' -> do
-          -- got to redo the register screen's transactions report, to get the latest transactions list for this screen
-          -- XXX duplicates initRegisterScreen
-          let
-            ropts' = ropts {depth_=Nothing
-                           ,balancetype_=HistoricalBalance
-                           }
-            q = filterQuery (not . queryIsDepth) $ queryFromOpts d ropts'
-            thisacctq = Acct $ accountNameToAccountRegex acct -- includes subs
-            items = reverse $ snd $ accountTransactionsReport ropts j' q thisacctq
-            ts = map first6 items
-            numberedts = zip [1..] ts
-            -- select the best current transaction from the new list
-            -- stay at the same index if possible, or if we are now past the end, select the last, otherwise select the first
-            (i',t') = case lookup i numberedts
-                      of Just t'' -> (i,t'')
-                         Nothing | null numberedts -> (0,nulltransaction)
-                                 | i > fst (last numberedts) -> last numberedts
-                                 | otherwise -> head numberedts
-            st' = st{aScreen=s{tsState=((i',t'),numberedts,acct)}}
-          continue $ reload j' d st'
-
-        Left err -> continue $ screenEnter d ES.screen{esState=err} st
-
-    -- Vty.EvKey (Vty.KChar 'C') [] -> continue $ reload j d $ stToggleCleared st
+    maincontent = Widget Greedy Greedy $ do
+      render $ defaultLayout toplabel bottomlabel $ str $
+        showTransactionUnelidedOneLineAmounts $
+        -- (if real_ ropts then filterTransactionPostings (Real True) else id) -- filter postings by --real
+        t
+      where
+        toplabel =
+          str "Transaction "
+          -- <+> withAttr ("border" <> "bold") (str $ "#" ++ show (tindex t))
+          -- <+> str (" ("++show i++" of "++show (length nts)++" in "++acct++")")
+          <+> (str $ "#" ++ show (tindex t))
+          <+> str " ("
+          <+> withAttr ("border" <> "bold") (str $ show i)
+          <+> str (" of "++show (length nts))
+          <+> togglefilters
+          <+> borderQueryStr (query_ ropts)
+          <+> str (" in "++T.unpack (replaceHiddenAccountsNameWith "All" acct)++")")
+          <+> (if ignore_assertions_ copts then withAttr (borderAttr <> "query") (str " ignoring balance assertions") else str "")
+          where
+            togglefilters =
+              case concat [
+                   uiShowClearedStatus $ clearedstatus_ ropts
+                  ,if real_ ropts then ["real"] else []
+                  ,if empty_ ropts then [] else ["nonzero"]
+                  ] of
+                [] -> str ""
+                fs -> withAttr (borderAttr <> "query") (str $ " " ++ intercalate ", " fs)
 
-    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)}}
+        bottomlabel = case mode of
+                        -- Minibuffer ed -> minibuffer ed
+                        _             -> quickhelp
+          where
+            quickhelp = borderKeysStr [
+               ("?", "help")
+              ,("left", "back")
+              ,("up/down", "prev/next")
+              --,("ESC", "cancel/top")
+              -- ,("a", "add")
+              ,("E", "editor")
+              ,("g", "reload")
+              ,("q", "quit")
+              ]
 
-    Vty.EvKey (Vty.KLeft) []     -> continue st''
-      where
-        st'@AppState{aScreen=scr} = popScreen st
-        st'' = st'{aScreen=rsSetSelectedTransaction (fromIntegral i) scr}
+tsDraw _ = error "draw function called with wrong screen type, should not happen"
 
-    _ev -> continue st
+tsHandle :: UIState -> Event -> EventM Name (Next UIState)
+tsHandle ui@UIState{aScreen=s@TransactionScreen{tsTransaction=(i,t)
+                                                ,tsTransactions=nts
+                                                ,tsAccount=acct}
+                    ,aopts=UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}
+                    ,ajournal=j
+                    ,aMode=mode
+                    }
+         ev =
+  case mode of
+    Help ->
+      case ev of
+        EvKey (KChar 'q') [] -> halt ui
+        _                    -> helpHandle ui ev
 
-handleTransactionScreen _ _ = error "event handler called with wrong screen type, should not happen"
+    _ -> do
+      d <- liftIO getCurrentDay
+      let
+        (iprev,tprev) = maybe (i,t) ((i-1),) $ lookup (i-1) nts
+        (inext,tnext) = maybe (i,t) ((i+1),) $ lookup (i+1) nts
+      case ev of
+        EvKey (KChar 'q') [] -> halt ui
+        EvKey KEsc        [] -> continue $ resetScreens d ui
+        EvKey (KChar c)   [] | c `elem` ['?'] -> continue $ setMode Help ui
+        EvKey (KChar 'E') [] -> suspendAndResume $ void (runEditor pos f) >> uiReloadJournalIfChanged copts d j ui
+          where
+            (pos,f) = let GenericSourcePos f l c = tsourcepos t in (Just (l, Just c),f)
+        EvKey (KChar 'g') [] -> do
+          d <- liftIO getCurrentDay
+          (ej, _) <- liftIO $ journalReloadIfChanged copts d j
+          case ej of
+            Left err -> continue $ screenEnter d errorScreen{esError=err} ui
+            Right j' -> do
+              -- got to redo the register screen's transactions report, to get the latest transactions list for this screen
+              -- XXX duplicates rsInit
+              let
+                ropts' = ropts {depth_=Nothing
+                               ,balancetype_=HistoricalBalance
+                               }
+                q = filterQuery (not . queryIsDepth) $ queryFromOpts d ropts'
+                thisacctq = Acct $ accountNameToAccountRegex acct -- includes subs
+                items = reverse $ snd $ accountTransactionsReport ropts j' q thisacctq
+                ts = map first6 items
+                numberedts = zip [1..] ts
+                -- select the best current transaction from the new list
+                -- stay at the same index if possible, or if we are now past the end, select the last, otherwise select the first
+                (i',t') = case lookup i numberedts
+                          of Just t'' -> (i,t'')
+                             Nothing | null numberedts -> (0,nulltransaction)
+                                     | i > fst (last numberedts) -> last numberedts
+                                     | otherwise -> head numberedts
+                ui' = ui{aScreen=s{tsTransaction=(i',t')
+                                  ,tsTransactions=numberedts
+                                  ,tsAccount=acct}}
+              continue $ regenerateScreens j' d ui'
+        EvKey (KChar 'I') [] -> continue $ uiCheckBalanceAssertions d (toggleIgnoreBalanceAssertions ui)
+        -- if allowing toggling here, we should refresh the txn list from the parent register screen
+        -- EvKey (KChar 'E') [] -> continue $ regenerateScreens j d $ stToggleEmpty ui
+        -- EvKey (KChar 'C') [] -> continue $ regenerateScreens j d $ stToggleCleared ui
+        -- EvKey (KChar 'R') [] -> continue $ regenerateScreens j d $ stToggleReal ui
+        EvKey k           [] | k `elem` [KUp, KChar 'k']   -> continue $ regenerateScreens j d ui{aScreen=s{tsTransaction=(iprev,tprev)}}
+        EvKey k           [] | k `elem` [KDown, KChar 'j'] -> continue $ regenerateScreens j d ui{aScreen=s{tsTransaction=(inext,tnext)}}
+        EvKey k           [] | k `elem` [KLeft, KChar 'h'] -> continue ui''
+          where
+            ui'@UIState{aScreen=scr} = popScreen ui
+            ui'' = ui'{aScreen=rsSelect (fromIntegral i) scr}
+        _ -> continue ui
 
-rsSetSelectedTransaction i scr@RegisterScreen{rsState=(l,a)} = scr{rsState=(l',a)}
-  where l' = listMoveTo (i-1) l
-rsSetSelectedTransaction _ scr = scr
+tsHandle _ _ = error "event handler called with wrong screen type, should not happen"
 
+-- | Select the nth item on the register screen.
+rsSelect i scr@RegisterScreen{..} = scr{rsList=l'}
+  where l' = listMoveTo (i-1) rsList
+rsSelect _ scr = scr
diff --git a/Hledger/UI/UIOptions.hs b/Hledger/UI/UIOptions.hs
--- a/Hledger/UI/UIOptions.hs
+++ b/Hledger/UI/UIOptions.hs
@@ -5,12 +5,11 @@
 
 module Hledger.UI.UIOptions
 where
+import Data.Default
 #if !MIN_VERSION_base(4,8,0)
 import Data.Functor.Compat ((<$>))
 #endif
 import Data.List (intercalate)
-import System.Console.CmdArgs
-import System.Console.CmdArgs.Explicit
 
 import Hledger.Cli hiding (progname,version,prognameandversion)
 import Hledger.UI.Theme (themeNames)
@@ -33,7 +32,7 @@
   -- ,flagReq ["drop"] (\s opts -> Right $ setopt "drop" s opts) "N" "with --flat, omit this many leading account name components"
   -- ,flagReq  ["format"] (\s opts -> Right $ setopt "format" s opts) "FORMATSTR" "use this custom line format"
   ,flagNone ["no-elide"] (\opts -> setboolopt "no-elide" opts) "don't compress empty parent accounts on one line"
-  ,flagNone ["value","V"] (setboolopt "value") "show amounts as their market value in their default valuation commodity (accounts screen)"
+  ,flagNone ["value","V"] (setboolopt "value") "show amounts as their current market value in their default valuation commodity (accounts screen)"
  ]
 
 --uimode :: Mode [([Char], [Char])]
diff --git a/Hledger/UI/UIState.hs b/Hledger/UI/UIState.hs
new file mode 100644
--- /dev/null
+++ b/Hledger/UI/UIState.hs
@@ -0,0 +1,216 @@
+{- | UIState operations. -}
+
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards   #-}
+
+module Hledger.UI.UIState
+where
+
+import Brick
+import Brick.Widgets.Edit
+import Data.List
+import Data.Text.Zipper (gotoEOL)
+import Data.Time.Calendar (Day)
+
+import Hledger
+import Hledger.Cli.CliOptions
+import Hledger.UI.UITypes
+import Hledger.UI.UIOptions
+
+-- | Toggle between showing only cleared items or all items.
+toggleCleared :: UIState -> UIState
+toggleCleared ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} =
+  ui{aopts=uopts{cliopts_=copts{reportopts_=toggleCleared ropts}}}
+  where
+    toggleCleared ropts@ReportOpts{clearedstatus_=Just Cleared} = ropts{clearedstatus_=Nothing}
+    toggleCleared ropts = ropts{clearedstatus_=Just Cleared}
+
+-- | Toggle between showing only pending items or all items.
+togglePending :: UIState -> UIState
+togglePending ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} =
+  ui{aopts=uopts{cliopts_=copts{reportopts_=togglePending ropts}}}
+  where
+    togglePending ropts@ReportOpts{clearedstatus_=Just Pending} = ropts{clearedstatus_=Nothing}
+    togglePending ropts = ropts{clearedstatus_=Just Pending}
+
+-- | Toggle between showing only uncleared items or all items.
+toggleUncleared :: UIState -> UIState
+toggleUncleared ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} =
+  ui{aopts=uopts{cliopts_=copts{reportopts_=toggleUncleared ropts}}}
+  where
+    toggleUncleared ropts@ReportOpts{clearedstatus_=Just Uncleared} = ropts{clearedstatus_=Nothing}
+    toggleUncleared ropts = ropts{clearedstatus_=Just Uncleared}
+
+-- | Toggle between showing all and showing only nonempty (more precisely, nonzero) items.
+toggleEmpty :: UIState -> UIState
+toggleEmpty ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} =
+  ui{aopts=uopts{cliopts_=copts{reportopts_=toggleEmpty ropts}}}
+  where
+    toggleEmpty ropts = ropts{empty_=not $ empty_ ropts}
+
+-- | Toggle between flat and tree mode. If in the third "default" mode, go to flat mode.
+toggleFlat :: UIState -> UIState
+toggleFlat ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} =
+  ui{aopts=uopts{cliopts_=copts{reportopts_=toggleFlatMode ropts}}}
+  where
+    toggleFlatMode ropts@ReportOpts{accountlistmode_=ALFlat} = ropts{accountlistmode_=ALTree}
+    toggleFlatMode ropts = ropts{accountlistmode_=ALFlat}
+
+-- | Toggle between historical balances and period balances.
+toggleHistorical :: UIState -> UIState
+toggleHistorical ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} =
+  ui{aopts=uopts{cliopts_=copts{reportopts_=ropts{balancetype_=b}}}}
+  where
+    b | balancetype_ ropts == HistoricalBalance = PeriodChange
+      | otherwise                               = HistoricalBalance
+
+-- | Toggle between showing all and showing only real (non-virtual) items.
+toggleReal :: UIState -> UIState
+toggleReal ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} =
+  ui{aopts=uopts{cliopts_=copts{reportopts_=toggleReal ropts}}}
+  where
+    toggleReal ropts = ropts{real_=not $ real_ ropts}
+
+-- | Toggle the ignoring of balance assertions.
+toggleIgnoreBalanceAssertions :: UIState -> UIState
+toggleIgnoreBalanceAssertions ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{}}} =
+  ui{aopts=uopts{cliopts_=copts{ignore_assertions_=not $ ignore_assertions_ copts}}}
+
+-- | Step through larger report periods, up to all.
+growReportPeriod :: Day -> UIState -> UIState
+growReportPeriod _d ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} =
+  ui{aopts=uopts{cliopts_=copts{reportopts_=ropts{period_=periodGrow $ period_ ropts}}}}
+
+-- | Step through smaller report periods, down to a day.
+shrinkReportPeriod :: Day -> UIState -> UIState
+shrinkReportPeriod d ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} =
+  ui{aopts=uopts{cliopts_=copts{reportopts_=ropts{period_=periodShrink d $ period_ ropts}}}}
+
+-- | Step the report start/end dates to the next period of same duration.
+nextReportPeriod :: DateSpan -> UIState -> UIState
+nextReportPeriod journalspan ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts@ReportOpts{period_=p}}}} =
+  ui{aopts=uopts{cliopts_=copts{reportopts_=ropts{period_=periodNextIn journalspan p}}}}
+
+-- | Step the report start/end dates to the next period of same duration.
+previousReportPeriod :: DateSpan -> UIState -> UIState
+previousReportPeriod journalspan ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts@ReportOpts{period_=p}}}} =
+  ui{aopts=uopts{cliopts_=copts{reportopts_=ropts{period_=periodPreviousIn journalspan p}}}}
+
+-- | Set the report period.
+setReportPeriod :: Period -> UIState -> UIState
+setReportPeriod p ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} =
+  ui{aopts=uopts{cliopts_=copts{reportopts_=ropts{period_=p}}}}
+
+-- | Apply a new filter query.
+setFilter :: String -> UIState -> UIState
+setFilter s ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} =
+  ui{aopts=uopts{cliopts_=copts{reportopts_=ropts{query_=s}}}}
+
+-- | Clear all filters/flags.
+resetFilter :: UIState -> UIState
+resetFilter ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} =
+  ui{aopts=uopts{cliopts_=copts{reportopts_=ropts{
+     accountlistmode_=ALTree
+    ,empty_=True
+    ,clearedstatus_=Nothing
+    ,real_=False
+    ,query_=""
+    --,period_=PeriodAll
+    }}}}
+
+resetDepth :: UIState -> UIState
+resetDepth ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} =
+  ui{aopts=uopts{cliopts_=copts{reportopts_=ropts{depth_=Nothing}}}}
+
+-- | Get the maximum account depth in the current journal.
+maxDepth :: UIState -> Int
+maxDepth UIState{ajournal=j} = maximum $ map accountNameLevel $ journalAccountNames j
+
+-- | Decrement the current depth limit towards 0. If there was no depth limit,
+-- set it to one less than the maximum account depth.
+decDepth :: UIState -> UIState
+decDepth ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts@ReportOpts{..}}}}
+  = ui{aopts=uopts{cliopts_=copts{reportopts_=ropts{depth_=dec depth_}}}}
+  where
+    dec (Just d) = Just $ max 0 (d-1)
+    dec Nothing  = Just $ maxDepth ui - 1
+
+-- | Increment the current depth limit. If this makes it equal to the
+-- the maximum account depth, remove the depth limit.
+incDepth :: UIState -> UIState
+incDepth ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts@ReportOpts{..}}}}
+  = ui{aopts=uopts{cliopts_=copts{reportopts_=ropts{depth_=inc depth_}}}}
+  where
+    inc (Just d) | d < (maxDepth ui - 1) = Just $ d+1
+    inc _ = Nothing
+
+-- | Set the current depth limit to the specified depth, or remove the depth limit.
+-- Also remove the depth limit if the specified depth is greater than the current
+-- maximum account depth. If the specified depth is negative, reset the depth limit
+-- to whatever was specified at uiartup.
+setDepth :: Maybe Int -> UIState -> UIState
+setDepth mdepth ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}}
+  = ui{aopts=uopts{cliopts_=copts{reportopts_=ropts{depth_=mdepth'}}}}
+  where
+    mdepth' = case mdepth of
+                Nothing                   -> Nothing
+                Just d | d < 0            -> depth_ ropts
+                       | d >= maxDepth ui -> Nothing
+                       | otherwise        -> mdepth
+
+getDepth :: UIState -> Maybe Int
+getDepth UIState{aopts=UIOpts{cliopts_=CliOpts{reportopts_=ropts}}} = depth_ ropts
+
+-- | Open the minibuffer, setting its content to the current query with the cursor at the end.
+showMinibuffer :: UIState -> UIState
+showMinibuffer ui = setMode (Minibuffer e) ui
+  where
+    e = applyEdit gotoEOL $ editor MinibufferEditor (str . unlines) (Just 1) oldq
+    oldq = query_ $ reportopts_ $ cliopts_ $ aopts ui
+
+-- | Close the minibuffer, discarding any edit in progress.
+closeMinibuffer :: UIState -> UIState
+closeMinibuffer = setMode Normal
+
+setMode :: Mode -> UIState -> UIState
+setMode m ui = ui{aMode=m}
+
+-- | Regenerate the content for the current and previous screens, from a new journal and current date.
+regenerateScreens :: Journal -> Day -> UIState -> UIState
+regenerateScreens j d ui@UIState{aScreen=s,aPrevScreens=ss} =
+  -- XXX clumsy due to entanglement of UIState and Screen.
+  -- sInit operates only on an appstate's current screen, so
+  -- remove all the screens from the appstate and then add them back
+  -- one at a time, regenerating as we go.
+  let
+    first:rest = reverse $ s:ss :: [Screen]
+    ui0 = ui{ajournal=j, aScreen=first, aPrevScreens=[]} :: UIState
+
+    ui1 = (sInit first) d False ui0 :: UIState
+    ui2 = foldl' (\ui s -> (sInit s) d False $ pushScreen s ui) ui1 rest :: UIState
+  in
+    ui2
+
+pushScreen :: Screen -> UIState -> UIState
+pushScreen scr ui = ui{aPrevScreens=(aScreen ui:aPrevScreens ui)
+                      ,aScreen=scr
+                      }
+
+popScreen :: UIState -> UIState
+popScreen ui@UIState{aPrevScreens=s:ss} = ui{aScreen=s, aPrevScreens=ss}
+popScreen ui = ui
+
+resetScreens :: Day -> UIState -> UIState
+resetScreens d ui@UIState{aScreen=s,aPrevScreens=ss} =
+  (sInit topscreen) d True $ resetDepth $ resetFilter $ closeMinibuffer ui{aScreen=topscreen, aPrevScreens=[]}
+  where
+    topscreen = case ss of _:_ -> last ss
+                           []  -> s
+
+-- | Enter a new screen, saving the old screen & state in the
+-- navigation history and initialising the new screen's state.
+screenEnter :: Day -> Screen -> UIState -> UIState
+screenEnter d scr ui = (sInit scr) d True $
+                       pushScreen scr
+                       ui
+
diff --git a/Hledger/UI/UITypes.hs b/Hledger/UI/UITypes.hs
--- a/Hledger/UI/UITypes.hs
+++ b/Hledger/UI/UITypes.hs
@@ -1,59 +1,161 @@
+{- |
+Overview:
+hledger-ui's UIState holds the currently active screen and any previously visited
+screens (and their states).
+The brick App delegates all event-handling and rendering
+to the UIState's active screen.
+Screens have their own screen state, render function, event handler, and app state
+update function, so they have full control.
+
+@
+Brick.defaultMain brickapp st
+  where
+    brickapp :: App (UIState) V.Event
+    brickapp = App {
+        appLiftVtyEvent = id
+      , appStartEvent   = return
+      , appAttrMap      = const theme
+      , appChooseCursor = showFirstCursor
+      , appHandleEvent  = \st ev -> sHandle (aScreen st) st ev
+      , appDraw         = \st    -> sDraw   (aScreen st) st
+      }
+    st :: UIState
+    st = (sInit s) d
+         UIState{
+            aopts=uopts'
+           ,ajournal=j
+           ,aScreen=s
+           ,aPrevScreens=prevscrs
+           ,aMinibuffer=Nothing
+           }
+@
+-}
+
+{-# LANGUAGE StandaloneDeriving #-}
+{-# LANGUAGE DeriveAnyClass     #-}
+{-# LANGUAGE OverloadedStrings  #-}
+{-# LANGUAGE TemplateHaskell    #-}
+
 module Hledger.UI.UITypes where
 
 import Data.Time.Calendar (Day)
-import qualified Graphics.Vty as V
+import Graphics.Vty (Event)
 import Brick
-import Brick.Widgets.List (List)
+import Brick.Widgets.List
+import Brick.Widgets.Edit (Editor)
+import Lens.Micro.Platform
 import Text.Show.Functions ()
   -- import the Show instance for functions. Warning, this also re-exports it
 
 import Hledger
 import Hledger.UI.UIOptions
 
-----------------------------------------------------------------------
+instance Show (List n a) where show _ = "<List>"
+instance Show (Editor n) where show _ = "<Editor>"
 
--- | hledger-ui's application state. This is part of, but distinct
--- from, brick's App.
-data AppState = AppState {
-   aopts :: UIOpts          -- ^ the command-line options and query currently in effect
-  ,ajournal :: Journal      -- ^ the journal being viewed
-  ,aScreen :: Screen        -- ^ the currently active screen
-  ,aPrevScreens :: [Screen] -- ^ previously visited screens, most recent first
+-- | hledger-ui's application state. This holds one or more stateful screens.
+-- As you navigate through screens, the old ones are saved in a stack.
+-- The app can be in one of several modes: normal screen operation,
+-- showing a help dialog, entering data in the minibuffer etc.
+data UIState = UIState {
+   aopts        :: UIOpts    -- ^ the command-line options and query arguments currently in effect
+  ,ajournal     :: Journal   -- ^ the journal being viewed
+  ,aPrevScreens :: [Screen]  -- ^ previously visited screens, most recent first
+  ,aScreen      :: Screen    -- ^ the currently active screen
+  ,aMode        :: Mode      -- ^ the currently active mode
   } deriving (Show)
 
--- | Types of screen available within the app, along with their state.
--- Screen types are distinguished by their constructor and their state
--- field, which must have unique names.
---
--- This type causes partial functions, so take care.
+-- | The mode modifies the screen's rendering and event handling.
+-- It resets to Normal when entering a new screen.
+data Mode =
+    Normal
+  | Help
+  | Minibuffer (Editor Name)
+  deriving (Show,Eq)
+
+-- Ignore the editor when comparing Modes.
+instance Eq (Editor n) where _ == _ = True
+
+-- Unique names required for widgets, viewports, cursor locations etc.
+data Name =
+    HelpDialog
+  | MinibufferEditor
+  | AccountsViewport
+  | AccountsList
+  | RegisterViewport
+  | RegisterList
+  deriving (Ord, Show, Eq)
+
+-- | hledger-ui screen types & instances.
+-- Each screen type has generically named initialisation, draw, and event handling functions,
+-- and zero or more uniquely named screen state fields, which hold the data for a particular
+-- instance of this screen. Note the latter create partial functions, which means that some invalid
+-- cases need to be handled, and also that their lenses are traversals, not single-value getters.
 data Screen =
     AccountsScreen {
-     asState :: (List (Int,String,String,[String]), AccountName)  -- ^ list widget holding (indent level, full account name, full or short account name to display, rendered amounts);
-                                                                  --   the full name of the currently selected account (or "")
-    ,sInitFn :: Day -> AppState -> AppState                       -- ^ function to initialise the screen's state on entry
-    ,sHandleFn :: AppState -> V.Event -> EventM (Next AppState)   -- ^ brick event handler to use for this screen
-    ,sDrawFn :: AppState -> [Widget]                              -- ^ brick renderer to use for this screen
+       sInit   :: Day -> Bool -> UIState -> UIState              -- ^ function to initialise or update this screen's state
+      ,sDraw   :: UIState -> [Widget Name]                             -- ^ brick renderer for this screen
+      ,sHandle :: UIState -> Event -> EventM Name (Next UIState)  -- ^ brick event handler for this screen
+      -- state fields.These ones have lenses:
+      ,_asList            :: List Name AccountsScreenItem  -- ^ list widget showing account names & balances
+      ,_asSelectedAccount :: AccountName              -- ^ a backup of the account name from the list widget's selected item (or "")
     }
   | RegisterScreen {
-     rsState :: (List (String,String,String,String,String,Transaction), AccountName)
-                                                                  -- ^ list widget holding (date, description, other accts, change amt, balance amt, and the full transaction);
-                                                                  --   the full name of the account we are showing a register for
-    ,sInitFn :: Day -> AppState -> AppState
-    ,sHandleFn :: AppState -> V.Event -> EventM (Next AppState)
-    ,sDrawFn :: AppState -> [Widget]
+       sInit   :: Day -> Bool -> UIState -> UIState
+      ,sDraw   :: UIState -> [Widget Name]
+      ,sHandle :: UIState -> Event -> EventM Name (Next UIState)
+      --
+      ,rsList    :: List Name RegisterScreenItem      -- ^ list widget showing transactions affecting this account
+      ,rsAccount :: AccountName                       -- ^ the account this register is for
+      ,rsForceInclusive :: Bool                       -- ^ should this register always include subaccount transactions,
+                                                      --   even when in flat mode ? (ie because entered from a
+                                                      --   depth-clipped accounts screen item)
     }
   | TransactionScreen {
-     tsState :: ((Integer,Transaction), [(Integer,Transaction)], AccountName)         -- ^ the (numbered) transaction we are viewing, a numbered list of transactions we can step through, and the account whose register we entered this screen from
-    ,sInitFn :: Day -> AppState -> AppState
-    ,sHandleFn :: AppState -> V.Event -> EventM (Next AppState)
-    ,sDrawFn :: AppState -> [Widget]
+       sInit   :: Day -> Bool -> UIState -> UIState
+      ,sDraw   :: UIState -> [Widget Name]
+      ,sHandle :: UIState -> Event -> EventM Name (Next UIState)
+      --
+      ,tsTransaction  :: NumberedTransaction          -- ^ the transaction we are currently viewing, and its position in the list
+      ,tsTransactions :: [NumberedTransaction]        -- ^ list of transactions we can step through
+      ,tsAccount      :: AccountName                  -- ^ the account whose register we entered this screen from
     }
   | ErrorScreen {
-     esState :: String                                            -- ^ error message to display
-    ,sInitFn :: Day -> AppState -> AppState
-    ,sHandleFn :: AppState -> V.Event -> EventM (Next AppState)
-    ,sDrawFn :: AppState -> [Widget]
+       sInit   :: Day -> Bool -> UIState -> UIState
+      ,sDraw   :: UIState -> [Widget Name]
+      ,sHandle :: UIState -> Event -> EventM Name (Next UIState)
+      --
+      ,esError :: String                              -- ^ error message to show
     }
   deriving (Show)
 
-instance Show (List a) where show _ = "<List>"
+-- | An item in the accounts screen's list of accounts and balances.
+data AccountsScreenItem = AccountsScreenItem {
+   asItemIndentLevel        :: Int          -- ^ indent level
+  ,asItemAccountName        :: AccountName  -- ^ full account name
+  ,asItemDisplayAccountName :: AccountName  -- ^ full or short account name to display
+  ,asItemRenderedAmounts    :: [String]     -- ^ rendered amounts
+  }
+
+-- | An item in the register screen's list of transactions in the current account.
+data RegisterScreenItem = RegisterScreenItem {
+   rsItemDate           :: String           -- ^ date
+  ,rsItemDescription    :: String           -- ^ description
+  ,rsItemOtherAccounts  :: String           -- ^ other accounts
+  ,rsItemChangeAmount   :: String           -- ^ the change to the current account from this transaction
+  ,rsItemBalanceAmount  :: String           -- ^ the balance or running total after this transaction
+  ,rsItemTransaction    :: Transaction      -- ^ the full transaction
+  }
+
+type NumberedTransaction = (Integer, Transaction)
+
+-- dummy monoid instance needed make lenses work with List fields not common across constructors
+--instance Monoid (List n a)
+--  where
+--    mempty        = list "" V.empty 1  -- XXX problem in 0.7, every list requires a unique Name
+--    mappend l1 l2 = l1 & listElementsL .~ (l1^.listElementsL <> l2^.listElementsL)
+
+concat <$> mapM makeLenses [
+   ''Screen
+  ]
+
diff --git a/Hledger/UI/UIUtils.hs b/Hledger/UI/UIUtils.hs
--- a/Hledger/UI/UIUtils.hs
+++ b/Hledger/UI/UIUtils.hs
@@ -1,97 +1,135 @@
+{- | Rendering & misc. helpers. -}
+
 {-# LANGUAGE OverloadedStrings #-}
 
-module Hledger.UI.UIUtils (
-  pushScreen
- ,popScreen
- ,screenEnter
- ,reload
- ,getViewportSize
- -- ,margin
- ,withBorderAttr
- ,topBottomBorderWithLabel
- ,topBottomBorderWithLabels
- ,defaultLayout
- ,borderQueryStr
- ,borderDepthStr
- ,borderKeysStr
- --
- ,stToggleCleared
- ) where
+module Hledger.UI.UIUtils
+where
 
-import Control.Lens ((^.))
--- import Control.Monad
--- import Control.Monad.IO.Class
--- import Data.Default
-import Data.List
-import Data.Monoid
-import Data.Time.Calendar (Day)
 import Brick
--- import Brick.Widgets.List
 import Brick.Widgets.Border
 import Brick.Widgets.Border.Style
-import Graphics.Vty as Vty
+-- import Brick.Widgets.Center
+import Brick.Widgets.Dialog
+import Brick.Widgets.Edit
+import Data.List
+import Data.Monoid
+import Graphics.Vty (Event(..),Key(..),Color,Attr,currentAttr)
+import Lens.Micro.Platform
+import System.Process
 
+import Hledger
 import Hledger.UI.UITypes
-import Hledger.Data.Types (Journal)
-import Hledger.UI.UIOptions
-import Hledger.Cli.CliOptions
-import Hledger.Reports.ReportOptions
-import Hledger.Utils (applyN)
--- import Hledger.Utils.Debug
+import Hledger.UI.UIState
 
-stToggleCleared :: AppState -> AppState
-stToggleCleared st@AppState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} =
-  st{aopts=uopts{cliopts_=copts{reportopts_=toggleCleared ropts}}}
 
--- | Toggle between showing all and showing only cleared items.
-toggleCleared :: ReportOpts -> ReportOpts
-toggleCleared ropts = ropts{cleared_=not $ cleared_ 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} =
-  -- clumsy due to entanglement of AppState and Screen.
-  -- sInitFn operates only on an appstate's current screen, so
-  -- remove all the screens from the appstate and then add them back
-  -- one at a time, regenerating as we go.
-  let
-    first:rest = reverse $ s:ss
-    st0 = st{ajournal=j, aScreen=first, aPrevScreens=[]}
-    st1 = (sInitFn first) d st0
-    st2 = foldl' (\st s -> (sInitFn s) d $ pushScreen s st) st1 rest
-  in
-    st2
+runInfo = runCommand "hledger-ui --info" >>= waitForProcess
+runMan  = runCommand "hledger-ui --man" >>= waitForProcess
+runHelp = runCommand "hledger-ui --help | less" >>= waitForProcess
 
-pushScreen :: Screen -> AppState -> AppState
-pushScreen scr st = st{aPrevScreens=(aScreen st:aPrevScreens st)
-                      ,aScreen=scr
-                      }
+-- ui
 
-popScreen :: AppState -> AppState
-popScreen st@AppState{aPrevScreens=s:ss} = st{aScreen=s, aPrevScreens=ss}
-popScreen st = st
+uiShowClearedStatus mc =
+ case mc of
+   Just Cleared   -> ["cleared"]
+   Just Pending   -> ["pending"]
+   Just Uncleared -> ["uncleared"]
+   Nothing        -> []
 
--- clearScreens :: AppState -> AppState
--- clearScreens st = st{aPrevScreens=[]}
+-- | Draw the help dialog, called when help mode is active.
+helpDialog :: Widget Name
+helpDialog =
+  Widget Fixed Fixed $ do
+    c <- getContext
+    render $
+      renderDialog (dialog (Just "Help (?/LEFT/ESC to close)") Nothing (c^.availWidthL)) $ -- (Just (0,[("ok",())]))
+      padTopBottom 1 $ padLeftRight 1 $
+        vBox [
+           hBox [
+              padLeftRight 1 $
+                vBox [
+                   str "NAVIGATION"
+                  ,renderKey ("UP/DOWN/k/j/PGUP/PGDN/HOME/END", "")
+                  ,str "  move selection"
+                  ,renderKey ("RIGHT/l", "more detail")
+                  ,renderKey ("LEFT/h", "previous screen")
+                  ,renderKey ("ESC", "cancel / reset to top")
+                  ,str " "
+                  ,str "MISC"
+                  ,renderKey ("?", "toggle help")
+                  ,renderKey ("a", "add transaction")
+                  ,renderKey ("E", "open editor")
+                  ,renderKey ("g", "reload data")
+                  ,renderKey ("I", "toggle balance assertions")
+                  ,renderKey ("q", "quit")
+                  ,str " "
+                  ,str "MANUAL"
+                  ,str "from help dialog:"
+                  ,renderKey ("t", "text")
+                  ,renderKey ("m", "man page")
+                  ,renderKey ("i", "info")
+                ]
+             ,padLeftRight 1 $
+                vBox [
+                   str "FILTERING"
+                  ,renderKey ("SHIFT-DOWN/UP", "shrink/grow report period")
+                  ,renderKey ("SHIFT-RIGHT/LEFT", "next/previous report period")
+                  ,renderKey ("t", "set report period to today")
+                  ,str " "
+                  ,renderKey ("/", "set a filter query")
+                  ,renderKey ("C", "toggle cleared/all")
+                  ,renderKey ("U", "toggle uncleared/all")
+                  ,renderKey ("R", "toggle real/all")
+                  ,renderKey ("Z", "toggle nonzero/all")
+                  ,renderKey ("DEL/BS", "remove filters")
+                  ,str " "
+                  ,str "accounts screen:"
+                  ,renderKey ("-+0123456789", "set depth limit")
+                  ,renderKey ("H", "toggle period balance (shows change) or\nhistorical balance (includes older postings)")
+                  ,renderKey ("F", "toggle tree (amounts include subaccounts) or\nflat mode (amounts exclude subaccounts\nexcept when account is depth-clipped)")
+                  ,str " "
+                  ,str "register screen:"
+                  ,renderKey ("H", "toggle period or historical total")
+                  ,renderKey ("F", "toggle subaccount transaction inclusion\n(and tree/flat mode)")
+                ]
+             ]
+--           ,vBox [
+--              str " "
+--             ,hCenter $ padLeftRight 1 $
+--               hCenter (str "MANUAL")
+--               <=>
+--               hCenter (hBox [
+--                  renderKey ("t", "text")
+--                 ,str " "
+--                 ,renderKey ("m", "man page")
+--                 ,str " "
+--                 ,renderKey ("i", "info")
+--                 ])
+--             ]
+          ]
+  where
+    renderKey (key,desc) = withAttr (borderAttr <> "keys") (str key) <+> str " " <+> str desc
 
--- | Enter a new screen, saving the old screen & state in the
--- navigation history and initialising the new screen's state.
-screenEnter :: Day -> Screen -> AppState -> AppState
-screenEnter d scr st = (sInitFn scr) d $
-                       pushScreen scr
-                       st
+-- | Event handler used when help mode is active.
+helpHandle :: UIState -> Event -> EventM Name (Next UIState)
+helpHandle ui ev =
+  case ev of
+    EvKey k [] | k `elem` [KEsc, KLeft, KChar 'h', KChar '?'] -> continue $ setMode Normal ui
+    EvKey (KChar 't') [] -> suspendAndResume $ runHelp >> return ui'
+    EvKey (KChar 'm') [] -> suspendAndResume $ runMan  >> return ui'
+    EvKey (KChar 'i') [] -> suspendAndResume $ runInfo >> return ui'
+    _ -> continue ui
+  where
+    ui' = setMode Normal ui
 
--- | In the EventM monad, get the named current viewport's width and height,
--- or (0,0) if the named viewport is not found.
-getViewportSize :: Name -> EventM (Int,Int)
-getViewportSize name = do
-  mvp <- lookupViewport name
-  let (w,h) = case mvp of
-        Just vp -> vp ^. vpSize
-        Nothing -> (0,0)
-  -- liftIO $ putStrLn $ show (w,h)
-  return (w,h)
+-- | Draw the minibuffer.
+minibuffer :: Editor Name -> Widget Name
+minibuffer ed =
+  forceAttr (borderAttr <> "minibuffer") $
+  hBox $
+  [txt "filter: ", renderEditor True ed]
 
+-- | Wrap a widget in the default hledger-ui screen layout.
+defaultLayout :: Widget Name -> Widget Name -> Widget Name -> Widget Name
 defaultLayout toplabel bottomlabel =
   topBottomBorderWithLabels (str " "<+>toplabel<+>str " ") (str " "<+>bottomlabel<+>str " ") .
   margin 1 0 Nothing
@@ -99,6 +137,43 @@
   -- padLeftRight 1 -- XXX should reduce inner widget's width by 2, but doesn't
                     -- "the layout adjusts... if you use the core combinators"
 
+borderQueryStr :: String -> Widget Name
+borderQueryStr ""  = str ""
+borderQueryStr qry = str " matching " <+> withAttr (borderAttr <> "query") (str qry)
+
+borderDepthStr :: Maybe Int -> Widget Name
+borderDepthStr Nothing  = str ""
+borderDepthStr (Just d) = str " to " <+> withAttr (borderAttr <> "query") (str $ "depth "++show d)
+
+borderPeriodStr :: String -> Period -> Widget Name
+borderPeriodStr _           PeriodAll = str ""
+borderPeriodStr preposition p         = str (" "++preposition++" ") <+> withAttr (borderAttr <> "query") (str $ showPeriod p)
+
+borderKeysStr :: [(String,String)] -> Widget Name
+borderKeysStr = borderKeysStr' . map (\(a,b) -> (a, str b))
+
+borderKeysStr' :: [(String,Widget Name)] -> Widget Name
+borderKeysStr' keydescs =
+  hBox $
+  intersperse sep $
+  [withAttr (borderAttr <> "keys") (str keys) <+> str ":" <+> desc | (keys, desc) <- keydescs]
+  where
+    -- sep = str " | "
+    sep = str " "
+
+-- temporary shenanigans:
+
+-- | Convert the special account name "*" (from balance report with depth limit 0) to something clearer.
+replaceHiddenAccountsNameWith :: AccountName -> AccountName -> AccountName
+replaceHiddenAccountsNameWith anew a | a == hiddenAccountsName = anew
+                                     | a == "*"                = anew
+                                     | otherwise               = a
+
+hiddenAccountsName = "..." -- for now
+
+-- generic
+
+topBottomBorderWithLabel :: Widget Name -> Widget Name -> Widget Name
 topBottomBorderWithLabel label = \wrapped ->
   Widget Greedy Greedy $ do
     c <- getContext
@@ -115,6 +190,7 @@
       <=>
       hBorder
 
+topBottomBorderWithLabels :: Widget Name -> Widget Name -> Widget Name -> Widget Name
 topBottomBorderWithLabels toplabel bottomlabel = \wrapped ->
   Widget Greedy Greedy $ do
     c <- getContext
@@ -132,6 +208,7 @@
       hBorderWithLabel bottomlabel
 
 -- XXX should be equivalent to the above, but isn't (page down goes offscreen)
+_topBottomBorderWithLabel2 :: Widget Name -> Widget Name -> Widget Name
 _topBottomBorderWithLabel2 label = \wrapped ->
  let debugmsg = ""
  in hBorderWithLabel (label <+> str debugmsg)
@@ -145,8 +222,8 @@
 -- thickness, using the current background colour or the specified
 -- colour.
 -- XXX May disrupt border style of inner widgets.
--- XXX Should reduce the available size visible to inner widget, but doesn't seem to (cf drawRegisterScreen2).
-margin :: Int -> Int -> Maybe Color -> Widget -> Widget
+-- XXX Should reduce the available size visible to inner widget, but doesn't seem to (cf rsDraw2).
+margin :: Int -> Int -> Maybe Color -> Widget Name -> Widget Name
 margin h v mcolour = \w ->
   Widget Greedy Greedy $ do
     c <- getContext
@@ -165,27 +242,6 @@
    -- withBorderStyle (borderStyleFromChar ' ') .
    -- applyN n border
 
+withBorderAttr :: Attr -> Widget Name -> Widget Name
 withBorderAttr attr = updateAttrMap (applyAttrMappings [(borderAttr, attr)])
-
--- _ui = vCenter $ vBox [ hCenter box
---                       , str " "
---                       , hCenter $ str "Press Esc to exit."
---                       ]
-
-borderQueryStr :: String -> Widget
-borderQueryStr ""  = str ""
-borderQueryStr qry = str " matching " <+> withAttr (borderAttr <> "query") (str qry)
-
-borderDepthStr :: Maybe Int -> Widget
-borderDepthStr Nothing  = str ""
-borderDepthStr (Just d) = str " to " <+> withAttr (borderAttr <> "depth") (str $ "depth "++show d)
-
-borderKeysStr :: [(String,String)] -> Widget
-borderKeysStr keydescs =
-  hBox $
-  intersperse sep $
-  [withAttr (borderAttr <> "keys") (str keys) <+> str ":" <+> str desc | (keys, desc) <- keydescs]
-  where
-    -- sep = str " | "
-    sep = str " "
 
diff --git a/doc/hledger-ui.1 b/doc/hledger-ui.1
new file mode 100644
--- /dev/null
+++ b/doc/hledger-ui.1
@@ -0,0 +1,453 @@
+
+.TH "hledger\-ui" "1" "October 2016" "hledger\-ui 1.0" "hledger User Manuals"
+
+
+
+.SH NAME
+.PP
+hledger\-ui \- curses\-style interface for the hledger accounting tool
+.SH SYNOPSIS
+.PP
+\f[C]hledger\-ui\ [OPTIONS]\ [QUERYARGS]\f[]
+.PD 0
+.P
+.PD
+\f[C]hledger\ ui\ \-\-\ [OPTIONS]\ [QUERYARGS]\f[]
+.SH DESCRIPTION
+.PP
+hledger is a cross\-platform program for tracking money, time, or any
+other commodity, using double\-entry accounting and a simple, editable
+file format.
+hledger is inspired by and largely compatible with ledger(1).
+.PP
+hledger\-ui is hledger\[aq]s curses\-style interface, providing an
+efficient full\-window text UI for viewing accounts and transactions,
+and some limited data entry capability.
+It is easier than hledger\[aq]s command\-line interface, and sometimes
+quicker and more convenient than the web interface.
+.PP
+Like hledger, it reads data from one or more files in hledger journal,
+timeclock, timedot, or CSV format specified with \f[C]\-f\f[], or
+\f[C]$LEDGER_FILE\f[], or \f[C]$HOME/.hledger.journal\f[] (on windows,
+perhaps \f[C]C:/Users/USER/.hledger.journal\f[]).
+For more about this see hledger(1), hledger_journal(5) etc.
+.SH OPTIONS
+.PP
+Note: if invoking hledger\-ui as a hledger subcommand, write
+\f[C]\-\-\f[] before options as shown above.
+.PP
+Any QUERYARGS are interpreted as a hledger search query which filters
+the data.
+.TP
+.B \f[C]\-\-flat\f[]
+show full account names, unindented
+.RS
+.RE
+.TP
+.B \f[C]\-\-register=ACCTREGEX\f[]
+start in the (first) matched account\[aq]s register screen
+.RS
+.RE
+.TP
+.B \f[C]\-\-theme=default|terminal|greenterm\f[]
+use this custom display theme
+.RS
+.RE
+.TP
+.B \f[C]\-V\ \-\-value\f[]
+show amounts as their current market value in their default valuation
+commodity (accounts screen only)
+.RS
+.RE
+.PP
+hledger general options:
+.TP
+.B \f[C]\-h\f[]
+show general usage (or after COMMAND, the command\[aq]s usage)
+.RS
+.RE
+.TP
+.B \f[C]\-\-help\f[]
+show the current program\[aq]s manual as plain text (or after an add\-on
+COMMAND, the add\-on\[aq]s manual)
+.RS
+.RE
+.TP
+.B \f[C]\-\-man\f[]
+show the current program\[aq]s manual with man
+.RS
+.RE
+.TP
+.B \f[C]\-\-info\f[]
+show the current program\[aq]s manual with info
+.RS
+.RE
+.TP
+.B \f[C]\-\-version\f[]
+show version
+.RS
+.RE
+.TP
+.B \f[C]\-\-debug[=N]\f[]
+show debug output (levels 1\-9, default: 1)
+.RS
+.RE
+.TP
+.B \f[C]\-f\ FILE\ \-\-file=FILE\f[]
+use a different input file.
+For stdin, use \-
+.RS
+.RE
+.TP
+.B \f[C]\-\-rules\-file=RULESFILE\f[]
+Conversion rules file to use when reading CSV (default: FILE.rules)
+.RS
+.RE
+.TP
+.B \f[C]\-\-alias=OLD=NEW\f[]
+display accounts named OLD as NEW
+.RS
+.RE
+.TP
+.B \f[C]\-I\ \-\-ignore\-assertions\f[]
+ignore any failing balance assertions in the journal
+.RS
+.RE
+.PP
+hledger reporting options:
+.TP
+.B \f[C]\-b\ \-\-begin=DATE\f[]
+include postings/txns on or after this date
+.RS
+.RE
+.TP
+.B \f[C]\-e\ \-\-end=DATE\f[]
+include postings/txns before this date
+.RS
+.RE
+.TP
+.B \f[C]\-D\ \-\-daily\f[]
+multiperiod/multicolumn report by day
+.RS
+.RE
+.TP
+.B \f[C]\-W\ \-\-weekly\f[]
+multiperiod/multicolumn report by week
+.RS
+.RE
+.TP
+.B \f[C]\-M\ \-\-monthly\f[]
+multiperiod/multicolumn report by month
+.RS
+.RE
+.TP
+.B \f[C]\-Q\ \-\-quarterly\f[]
+multiperiod/multicolumn report by quarter
+.RS
+.RE
+.TP
+.B \f[C]\-Y\ \-\-yearly\f[]
+multiperiod/multicolumn report by year
+.RS
+.RE
+.TP
+.B \f[C]\-p\ \-\-period=PERIODEXP\f[]
+set start date, end date, and/or reporting interval all at once
+(overrides the flags above)
+.RS
+.RE
+.TP
+.B \f[C]\-\-date2\f[]
+show, and match with \-b/\-e/\-p/date:, secondary dates instead
+.RS
+.RE
+.TP
+.B \f[C]\-C\ \-\-cleared\f[]
+include only cleared postings/txns
+.RS
+.RE
+.TP
+.B \f[C]\-\-pending\f[]
+include only pending postings/txns
+.RS
+.RE
+.TP
+.B \f[C]\-U\ \-\-uncleared\f[]
+include only uncleared (and pending) postings/txns
+.RS
+.RE
+.TP
+.B \f[C]\-R\ \-\-real\f[]
+include only non\-virtual postings
+.RS
+.RE
+.TP
+.B \f[C]\-\-depth=N\f[]
+hide accounts/postings deeper than N
+.RS
+.RE
+.TP
+.B \f[C]\-E\ \-\-empty\f[]
+show items with zero amount, normally hidden
+.RS
+.RE
+.TP
+.B \f[C]\-B\ \-\-cost\f[]
+show amounts in their cost price\[aq]s commodity
+.RS
+.RE
+.TP
+.B \f[C]\-\-pivot\ TAG\f[]
+will transform the journal before any other processing by replacing the
+account name of every posting having the tag TAG with content VALUE by
+the account name "TAG:VALUE".
+.RS
+.RE
+The TAG will only match if it is a full\-length match.
+The pivot will only happen if the TAG is on a posting, not if it is on
+the transaction.
+If the tag value is a multi:level:account:name the new account name will
+be "TAG:multi:level:account:name".
+.RS
+.RE
+.TP
+.B \f[C]\-\-anon\f[]
+show anonymized accounts and payees
+.RS
+.RE
+.SH KEYS
+.PP
+\f[C]?\f[] shows a help dialog listing all keys.
+(Some of these also appear in the quick help at the bottom of each
+screen.) Press \f[C]?\f[] again (or \f[C]ESCAPE\f[], or \f[C]LEFT\f[])
+to close it.
+The following keys work on most screens:
+.PP
+The cursor keys navigate: \f[C]right\f[] (or \f[C]enter\f[]) goes
+deeper, \f[C]left\f[] returns to the previous screen,
+\f[C]up\f[]/\f[C]down\f[]/\f[C]page\ up\f[]/\f[C]page\ down\f[]/\f[C]home\f[]/\f[C]end\f[]
+move up and down through lists.
+Vi\-style \f[C]h\f[]/\f[C]j\f[]/\f[C]k\f[]/\f[C]l\f[] movement keys are
+also supported.
+A tip: movement speed is limited by your keyboard repeat rate, to move
+faster you may want to adjust it.
+(If you\[aq]re on a mac, the Karabiner app is one way to do that.)
+.PP
+With shift pressed, the cursor keys adjust the report period, limiting
+the transactions to be shown (by default, all are shown).
+\f[C]shift\-down/up\f[] steps downward and upward through these standard
+report period durations: year, quarter, month, week, day.
+Then, \f[C]shift\-left/right\f[] moves to the previous/next period.
+\f[C]t\f[] sets the report period to today.
+(To set a non\-standard period, you can use \f[C]/\f[] and a
+\f[C]date:\f[] query).
+.PP
+\f[C]/\f[] lets you set a general filter query limiting the data shown,
+using the same query terms as in hledger and hledger\-web.
+While editing the query, you can use CTRL\-a/e/d/k, BS, cursor keys;
+press \f[C]ENTER\f[] to set it, or \f[C]ESCAPE\f[]to cancel.
+There are also keys for quickly adjusting some common filters like
+account depth and cleared/uncleared (see below).
+\f[C]BACKSPACE\f[] or \f[C]DELETE\f[] removes all filters, showing all
+transactions.
+.PP
+\f[C]ESCAPE\f[] removes all filters and jumps back to the top screen.
+Or, it cancels a minibuffer edit or help dialog in progress.
+.PP
+\f[C]g\f[] reloads from the data file(s) and updates the current screen
+and any previous screens.
+(With large files, this could cause a noticeable pause.)
+.PP
+\f[C]I\f[] toggles balance assertion checking.
+Disabling balance assertions temporarily can be useful for
+troubleshooting.
+.PP
+\f[C]a\f[] runs command\-line hledger\[aq]s add command, and reloads the
+updated file.
+This allows some basic data entry.
+.PP
+\f[C]E\f[] runs $HLEDGER_UI_EDITOR, or $EDITOR, or a default
+(\f[C]emacsclient\ \-a\ ""\ \-nw\f[]) on the journal file.
+With some editors (emacs, vi), the cursor will be positioned at the
+current transaction when invoked from the register and transaction
+screens, and at the error location (if possible) when invoked from the
+error screen.
+.PP
+\f[C]q\f[] quits the application.
+.PP
+Additional screen\-specific keys are described below.
+.SH SCREENS
+.SS Accounts screen
+.PP
+This is normally the first screen displayed.
+It lists accounts and their balances, like hledger\[aq]s balance
+command.
+By default, it shows all accounts and their latest ending balances
+(including the balances of subaccounts).
+if you specify a query on the command line, it shows just the matched
+accounts and the balances from matched transactions.
+.PP
+Account names are normally indented to show the hierarchy (tree mode).
+To see less detail, set a depth limit by pressing a number key,
+\f[C]1\f[] to \f[C]9\f[].
+\f[C]0\f[] shows even less detail, collapsing all accounts to a single
+total.
+\f[C]\-\f[] and \f[C]+\f[] (or \f[C]=\f[]) decrease and increase the
+depth limit.
+To remove the depth limit, set it higher than the maximum account depth,
+or press \f[C]ESCAPE\f[].
+.PP
+\f[C]F\f[] toggles flat mode, in which accounts are shown as a flat
+list, with their full names.
+In this mode, account balances exclude subaccounts, except for accounts
+at the depth limit (as with hledger\[aq]s balance command).
+.PP
+\f[C]H\f[] toggles between showing historical balances or period
+balances.
+Historical balances (the default) are ending balances at the end of the
+report period, taking into account all transactions before that date
+(filtered by the filter query if any), including transactions before the
+start of the report period.
+In other words, historical balances are what you would see on a bank
+statement for that account (unless disturbed by a filter query).
+Period balances ignore transactions before the report start date, so
+they show the change in balance during the report period.
+They are more useful eg when viewing a time log.
+.PP
+\f[C]C\f[] toggles cleared mode, in which uncleared transactions and
+postings are not shown.
+\f[C]U\f[] toggles uncleared mode, in which only uncleared
+transactions/postings are shown.
+.PP
+\f[C]R\f[] toggles real mode, in which virtual postings are ignored.
+.PP
+\f[C]Z\f[] toggles nonzero mode, in which only accounts with nonzero
+balances are shown (hledger\-ui shows zero items by default, unlike
+command\-line hledger).
+.PP
+Press \f[C]right\f[] or \f[C]enter\f[] to view an account\[aq]s
+transactions register.
+.SS Register screen
+.PP
+This screen shows the transactions affecting a particular account, like
+a check register.
+Each line represents one transaction and shows:
+.IP \[bu] 2
+the other account(s) involved, in abbreviated form.
+(If there are both real and virtual postings, it shows only the accounts
+affected by real postings.)
+.IP \[bu] 2
+the overall change to the current account\[aq]s balance; positive for an
+inflow to this account, negative for an outflow.
+.IP \[bu] 2
+the running historical total or period total for the current account,
+after the transaction.
+This can be toggled with \f[C]H\f[].
+Similar to the accounts screen, the historical total is affected by
+transactions (filtered by the filter query) before the report start
+date, while the period total is not.
+If the historical total is not disturbed by a filter query, it will be
+the running historical balance you would see on a bank register for the
+current account.
+.PP
+If the accounts screen was in tree mode, the register screen will
+include transactions from both the current account and its subaccounts.
+If the accounts screen was in flat mode, and a non\-depth\-clipped
+account was selected, the register screen will exclude transactions from
+subaccounts.
+In other words, the register always shows the transactions responsible
+for the period balance shown on the accounts screen.
+As on the accounts screen, this can be toggled with \f[C]F\f[].
+.PP
+\f[C]C\f[] toggles cleared mode, in which uncleared transactions and
+postings are not shown.
+\f[C]U\f[] toggles uncleared mode, in which only uncleared
+transactions/postings are shown.
+.PP
+\f[C]R\f[] toggles real mode, in which virtual postings are ignored.
+.PP
+\f[C]Z\f[] toggles nonzero mode, in which only transactions posting a
+nonzero change are shown (hledger\-ui shows zero items by default,
+unlike command\-line hledger).
+.PP
+Press \f[C]right\f[] (or \f[C]enter\f[]) to view the selected
+transaction in detail.
+.SS Transaction screen
+.PP
+This screen shows a single transaction, as a general journal entry,
+similar to hledger\[aq]s print command and journal format
+(hledger_journal(5)).
+.PP
+The transaction\[aq]s date(s) and any cleared flag, transaction code,
+description, comments, along with all of its account postings are shown.
+Simple transactions have two postings, but there can be more (or in
+certain cases, fewer).
+.PP
+\f[C]up\f[] and \f[C]down\f[] will step through all transactions listed
+in the previous account register screen.
+In the title bar, the numbers in parentheses show your position within
+that account register.
+They will vary depending on which account register you came from
+(remember most transactions appear in multiple account registers).
+The #N number preceding them is the transaction\[aq]s position within
+the complete unfiltered journal, which is a more stable id (at least
+until the next reload).
+.SS Error screen
+.PP
+This screen will appear if there is a problem, such as a parse error,
+when you press g to reload.
+Once you have fixed the problem, press g again to reload and resume
+normal operation.
+(Or, you can press escape to cancel the reload attempt.)
+.SH ENVIRONMENT
+.PP
+\f[B]COLUMNS\f[] The screen width to use.
+Default: the full terminal width.
+.PP
+\f[B]LEDGER_FILE\f[] The journal file path when not specified with
+\f[C]\-f\f[].
+Default: \f[C]~/.hledger.journal\f[] (on windows, perhaps
+\f[C]C:/Users/USER/.hledger.journal\f[]).
+.SH FILES
+.PP
+Reads data from one or more files in hledger journal, timeclock,
+timedot, or CSV format specified with \f[C]\-f\f[], or
+\f[C]$LEDGER_FILE\f[], or \f[C]$HOME/.hledger.journal\f[] (on windows,
+perhaps \f[C]C:/Users/USER/.hledger.journal\f[]).
+.SH BUGS
+.PP
+The need to precede options with \f[C]\-\-\f[] when invoked from hledger
+is awkward.
+.PP
+\f[C]\-f\-\f[] doesn\[aq]t work (hledger\-ui can\[aq]t read from stdin).
+.PP
+\f[C]\-V\f[] affects only the accounts screen.
+.PP
+When you press \f[C]g\f[], the current and all previous screens are
+regenerated, which may cause a noticeable pause.
+Also there is no visual indication that this is in progress.
+.PP
+The register screen\[aq]s switching between historic balance and running
+total based on query arguments may be confusing, and there is no column
+heading to indicate which is being displayed.
+
+
+.SH "REPORTING BUGS"
+Report bugs at http://bugs.hledger.org
+(or on the #hledger IRC channel or hledger mail list)
+
+.SH AUTHORS
+Simon Michael <simon@joyful.com> and contributors
+
+.SH COPYRIGHT
+
+Copyright (C) 2007-2016 Simon Michael.
+.br
+Released under GNU GPL v3 or later.
+
+.SH SEE ALSO
+hledger(1), hledger\-ui(1), hledger\-web(1), hledger\-api(1),
+hledger_csv(5), hledger_journal(5), hledger_timeclock(5), hledger_timedot(5),
+ledger(1)
+
+http://hledger.org
diff --git a/doc/hledger-ui.1.info b/doc/hledger-ui.1.info
new file mode 100644
--- /dev/null
+++ b/doc/hledger-ui.1.info
@@ -0,0 +1,367 @@
+This is hledger-ui/doc/hledger-ui.1.info, produced by makeinfo version
+4.8 from stdin.
+
+
+File: hledger-ui.1.info,  Node: Top,  Up: (dir)
+
+hledger-ui(1) hledger-ui 1.0
+****************************
+
+hledger-ui is hledger's curses-style interface, providing an efficient
+full-window text UI for viewing accounts and transactions, and some
+limited data entry capability. It is easier than hledger's command-line
+interface, and sometimes quicker and more convenient than the web
+interface.
+
+   Like hledger, it reads data from one or more files in hledger
+journal, timeclock, timedot, or CSV format specified with `-f', or
+`$LEDGER_FILE', or `$HOME/.hledger.journal' (on windows, perhaps
+`C:/Users/USER/.hledger.journal'). For more about this see hledger(1),
+hledger_journal(5) etc.
+
+* Menu:
+
+* OPTIONS::
+* KEYS::
+* SCREENS::
+
+
+File: hledger-ui.1.info,  Node: OPTIONS,  Next: KEYS,  Prev: Top,  Up: Top
+
+1 OPTIONS
+*********
+
+Note: if invoking hledger-ui as a hledger subcommand, write `--' before
+options as shown above.
+
+   Any QUERYARGS are interpreted as a hledger search query which filters
+the data.
+
+`--flat'
+     show full account names, unindented
+
+`--register=ACCTREGEX'
+     start in the (first) matched account's register screen
+
+`--theme=default|terminal|greenterm'
+     use this custom display theme
+
+`-V --value'
+     show amounts as their current market value in their default
+     valuation commodity (accounts screen only)
+
+   hledger general options:
+
+`-h'
+     show general usage (or after COMMAND, the command's usage)
+
+`--help'
+     show the current program's manual as plain text (or after an add-on
+     COMMAND, the add-on's manual)
+
+`--man'
+     show the current program's manual with man
+
+`--info'
+     show the current program's manual with info
+
+`--version'
+     show version
+
+`--debug[=N]'
+     show debug output (levels 1-9, default: 1)
+
+`-f FILE --file=FILE'
+     use a different input file. For stdin, use -
+
+`--rules-file=RULESFILE'
+     Conversion rules file to use when reading CSV (default: FILE.rules)
+
+`--alias=OLD=NEW'
+     display accounts named OLD as NEW
+
+`-I --ignore-assertions'
+     ignore any failing balance assertions in the journal
+
+   hledger reporting options:
+
+`-b --begin=DATE'
+     include postings/txns on or after this date
+
+`-e --end=DATE'
+     include postings/txns before this date
+
+`-D --daily'
+     multiperiod/multicolumn report by day
+
+`-W --weekly'
+     multiperiod/multicolumn report by week
+
+`-M --monthly'
+     multiperiod/multicolumn report by month
+
+`-Q --quarterly'
+     multiperiod/multicolumn report by quarter
+
+`-Y --yearly'
+     multiperiod/multicolumn report by year
+
+`-p --period=PERIODEXP'
+     set start date, end date, and/or reporting interval all at once
+     (overrides the flags above)
+
+`--date2'
+     show, and match with -b/-e/-p/date:, secondary dates instead
+
+`-C --cleared'
+     include only cleared postings/txns
+
+`--pending'
+     include only pending postings/txns
+
+`-U --uncleared'
+     include only uncleared (and pending) postings/txns
+
+`-R --real'
+     include only non-virtual postings
+
+`--depth=N'
+     hide accounts/postings deeper than N
+
+`-E --empty'
+     show items with zero amount, normally hidden
+
+`-B --cost'
+     show amounts in their cost price's commodity
+
+`--pivot TAG'
+     will transform the journal before any other processing by
+     replacing the account name of every posting having the tag TAG
+     with content VALUE by the account name "TAG:VALUE".  The TAG will
+     only match if it is a full-length match. The pivot will only
+     happen if the TAG is on a posting, not if it is on the transaction.
+     If the tag value is a multi:level:account:name the new account
+     name will be "TAG:multi:level:account:name".
+
+`--anon'
+     show anonymized accounts and payees
+
+
+File: hledger-ui.1.info,  Node: KEYS,  Next: SCREENS,  Prev: OPTIONS,  Up: Top
+
+2 KEYS
+******
+
+`?' shows a help dialog listing all keys. (Some of these also appear in
+the quick help at the bottom of each screen.) Press `?' again (or
+`ESCAPE', or `LEFT') to close it. The following keys work on most
+screens:
+
+   The cursor keys navigate: `right' (or `enter') goes deeper, `left'
+returns to the previous screen, `up'/`down'/`page up'/`page
+down'/`home'/`end' move up and down through lists. Vi-style
+`h'/`j'/`k'/`l' movement keys are also supported. A tip: movement speed
+is limited by your keyboard repeat rate, to move faster you may want to
+adjust it. (If you're on a mac, the Karabiner app is one way to do
+that.)
+
+   With shift pressed, the cursor keys adjust the report period,
+limiting the transactions to be shown (by default, all are shown).
+`shift-down/up' steps downward and upward through these standard report
+period durations: year, quarter, month, week, day. Then,
+`shift-left/right' moves to the previous/next period. `t' sets the
+report period to today. (To set a non-standard period, you can use `/'
+and a `date:' query).
+
+   `/' lets you set a general filter query limiting the data shown,
+using the same query terms as in hledger and hledger-web. While editing
+the query, you can use CTRL-a/e/d/k, BS, cursor keys; press `ENTER' to
+set it, or `ESCAPE'to cancel. There are also keys for quickly adjusting
+some common filters like account depth and cleared/uncleared (see
+below). `BACKSPACE' or `DELETE' removes all filters, showing all
+transactions.
+
+   `ESCAPE' removes all filters and jumps back to the top screen. Or,
+it cancels a minibuffer edit or help dialog in progress.
+
+   `g' reloads from the data file(s) and updates the current screen and
+any previous screens. (With large files, this could cause a noticeable
+pause.)
+
+   `I' toggles balance assertion checking. Disabling balance assertions
+temporarily can be useful for troubleshooting.
+
+   `a' runs command-line hledger's add command, and reloads the updated
+file. This allows some basic data entry.
+
+   `E' runs $HLEDGER_UI_EDITOR, or $EDITOR, or a default (`emacsclient
+-a "" -nw') on the journal file. With some editors (emacs, vi), the
+cursor will be positioned at the current transaction when invoked from
+the register and transaction screens, and at the error location (if
+possible) when invoked from the error screen.
+
+   `q' quits the application.
+
+   Additional screen-specific keys are described below.
+
+
+File: hledger-ui.1.info,  Node: SCREENS,  Prev: KEYS,  Up: Top
+
+3 SCREENS
+*********
+
+* Menu:
+
+* Accounts screen::
+* Register screen::
+* Transaction screen::
+* Error screen::
+
+
+File: hledger-ui.1.info,  Node: Accounts screen,  Next: Register screen,  Up: SCREENS
+
+3.1 Accounts screen
+===================
+
+This is normally the first screen displayed. It lists accounts and their
+balances, like hledger's balance command. By default, it shows all
+accounts and their latest ending balances (including the balances of
+subaccounts). if you specify a query on the command line, it shows just
+the matched accounts and the balances from matched transactions.
+
+   Account names are normally indented to show the hierarchy (tree
+mode).  To see less detail, set a depth limit by pressing a number key,
+`1' to `9'. `0' shows even less detail, collapsing all accounts to a
+single total. `-' and `+' (or `=') decrease and increase the depth
+limit. To remove the depth limit, set it higher than the maximum
+account depth, or press `ESCAPE'.
+
+   `F' toggles flat mode, in which accounts are shown as a flat list,
+with their full names. In this mode, account balances exclude
+subaccounts, except for accounts at the depth limit (as with hledger's
+balance command).
+
+   `H' toggles between showing historical balances or period balances.
+Historical balances (the default) are ending balances at the end of the
+report period, taking into account all transactions before that date
+(filtered by the filter query if any), including transactions before the
+start of the report period. In other words, historical balances are what
+you would see on a bank statement for that account (unless disturbed by
+a filter query). Period balances ignore transactions before the report
+start date, so they show the change in balance during the report period.
+They are more useful eg when viewing a time log.
+
+   `C' toggles cleared mode, in which uncleared transactions and
+postings are not shown. `U' toggles uncleared mode, in which only
+uncleared transactions/postings are shown.
+
+   `R' toggles real mode, in which virtual postings are ignored.
+
+   `Z' toggles nonzero mode, in which only accounts with nonzero
+balances are shown (hledger-ui shows zero items by default, unlike
+command-line hledger).
+
+   Press `right' or `enter' to view an account's transactions register.
+
+
+File: hledger-ui.1.info,  Node: Register screen,  Next: Transaction screen,  Prev: Accounts screen,  Up: SCREENS
+
+3.2 Register screen
+===================
+
+This screen shows the transactions affecting a particular account, like
+a check register. Each line represents one transaction and shows:
+
+   * the other account(s) involved, in abbreviated form. (If there are
+     both real and virtual postings, it shows only the accounts
+     affected by real postings.)
+
+   * the overall change to the current account's balance; positive for
+     an inflow to this account, negative for an outflow.
+
+   * the running historical total or period total for the current
+     account, after the transaction. This can be toggled with `H'.
+     Similar to the accounts screen, the historical total is affected
+     by transactions (filtered by the filter query) before the report
+     start date, while the period total is not. If the historical total
+     is not disturbed by a filter query, it will be the running
+     historical balance you would see on a bank register for the
+     current account.
+
+
+   If the accounts screen was in tree mode, the register screen will
+include transactions from both the current account and its subaccounts.
+If the accounts screen was in flat mode, and a non-depth-clipped account
+was selected, the register screen will exclude transactions from
+subaccounts. In other words, the register always shows the transactions
+responsible for the period balance shown on the accounts screen. As on
+the accounts screen, this can be toggled with `F'.
+
+   `C' toggles cleared mode, in which uncleared transactions and
+postings are not shown. `U' toggles uncleared mode, in which only
+uncleared transactions/postings are shown.
+
+   `R' toggles real mode, in which virtual postings are ignored.
+
+   `Z' toggles nonzero mode, in which only transactions posting a
+nonzero change are shown (hledger-ui shows zero items by default, unlike
+command-line hledger).
+
+   Press `right' (or `enter') to view the selected transaction in
+detail.
+
+
+File: hledger-ui.1.info,  Node: Transaction screen,  Next: Error screen,  Prev: Register screen,  Up: SCREENS
+
+3.3 Transaction screen
+======================
+
+This screen shows a single transaction, as a general journal entry,
+similar to hledger's print command and journal format
+(hledger_journal(5)).
+
+   The transaction's date(s) and any cleared flag, transaction code,
+description, comments, along with all of its account postings are shown.
+Simple transactions have two postings, but there can be more (or in
+certain cases, fewer).
+
+   `up' and `down' will step through all transactions listed in the
+previous account register screen. In the title bar, the numbers in
+parentheses show your position within that account register. They will
+vary depending on which account register you came from (remember most
+transactions appear in multiple account registers). The #N number
+preceding them is the transaction's position within the complete
+unfiltered journal, which is a more stable id (at least until the next
+reload).
+
+
+File: hledger-ui.1.info,  Node: Error screen,  Prev: Transaction screen,  Up: SCREENS
+
+3.4 Error screen
+================
+
+This screen will appear if there is a problem, such as a parse error,
+when you press g to reload. Once you have fixed the problem, press g
+again to reload and resume normal operation. (Or, you can press escape
+to cancel the reload attempt.)
+
+
+
+Tag Table:
+Node: Top88
+Node: OPTIONS823
+Ref: #options922
+Node: KEYS3786
+Ref: #keys3883
+Node: SCREENS6284
+Ref: #screens6371
+Node: Accounts screen6461
+Ref: #accounts-screen6591
+Node: Register screen8629
+Ref: #register-screen8786
+Node: Transaction screen10674
+Ref: #transaction-screen10834
+Node: Error screen11701
+Ref: #error-screen11825
+
+End Tag Table
diff --git a/doc/hledger-ui.1.txt b/doc/hledger-ui.1.txt
new file mode 100644
--- /dev/null
+++ b/doc/hledger-ui.1.txt
@@ -0,0 +1,351 @@
+
+hledger-ui(1)                hledger User Manuals                hledger-ui(1)
+
+
+
+NAME
+       hledger-ui - curses-style interface for the hledger accounting tool
+
+SYNOPSIS
+       hledger-ui [OPTIONS] [QUERYARGS]
+       hledger ui -- [OPTIONS] [QUERYARGS]
+
+DESCRIPTION
+       hledger  is  a  cross-platform program for tracking money, time, or any
+       other commodity, using double-entry accounting and a  simple,  editable
+       file  format.   hledger  is  inspired  by  and  largely compatible with
+       ledger(1).
+
+       hledger-ui is hledger's curses-style interface, providing an  efficient
+       full-window  text  UI  for  viewing accounts and transactions, and some
+       limited data entry  capability.   It  is  easier  than  hledger's  com-
+       mand-line interface, and sometimes quicker and more convenient than the
+       web interface.
+
+       Like hledger, it reads data from one or more files in hledger  journal,
+       timeclock,  timedot,  or CSV format specified with -f, or $LEDGER_FILE,
+       or       $HOME/.hledger.journal       (on       windows,        perhaps
+       C:/Users/USER/.hledger.journal).   For  more about this see hledger(1),
+       hledger_journal(5) etc.
+
+OPTIONS
+       Note: if invoking hledger-ui as a hledger subcommand, write  --  before
+       options as shown above.
+
+       Any  QUERYARGS  are interpreted as a hledger search query which filters
+       the data.
+
+       --flat show full account names, unindented
+
+       --register=ACCTREGEX
+              start in the (first) matched account's register screen
+
+       --theme=default|terminal|greenterm
+              use this custom display theme
+
+       -V --value
+              show amounts as their current market value in their default val-
+              uation commodity (accounts screen only)
+
+       hledger general options:
+
+       -h     show general usage (or after COMMAND, the command's usage)
+
+       --help show  the  current  program's  manual as plain text (or after an
+              add-on COMMAND, the add-on's manual)
+
+       --man  show the current program's manual with man
+
+       --info show the current program's manual with info
+
+       --version
+              show version
+
+       --debug[=N]
+              show debug output (levels 1-9, default: 1)
+
+       -f FILE --file=FILE
+              use a different input file.  For stdin, use -
+
+       --rules-file=RULESFILE
+              Conversion  rules  file  to  use  when  reading  CSV   (default:
+              FILE.rules)
+
+       --alias=OLD=NEW
+              display accounts named OLD as NEW
+
+       -I --ignore-assertions
+              ignore any failing balance assertions in the journal
+
+       hledger reporting options:
+
+       -b --begin=DATE
+              include postings/txns on or after this date
+
+       -e --end=DATE
+              include postings/txns before this date
+
+       -D --daily
+              multiperiod/multicolumn report by day
+
+       -W --weekly
+              multiperiod/multicolumn report by week
+
+       -M --monthly
+              multiperiod/multicolumn report by month
+
+       -Q --quarterly
+              multiperiod/multicolumn report by quarter
+
+       -Y --yearly
+              multiperiod/multicolumn report by year
+
+       -p --period=PERIODEXP
+              set  start date, end date, and/or reporting interval all at once
+              (overrides the flags above)
+
+       --date2
+              show, and match with -b/-e/-p/date:, secondary dates instead
+
+       -C --cleared
+              include only cleared postings/txns
+
+       --pending
+              include only pending postings/txns
+
+       -U --uncleared
+              include only uncleared (and pending) postings/txns
+
+       -R --real
+              include only non-virtual postings
+
+       --depth=N
+              hide accounts/postings deeper than N
+
+       -E --empty
+              show items with zero amount, normally hidden
+
+       -B --cost
+              show amounts in their cost price's commodity
+
+       --pivot TAG
+              will transform  the  journal  before  any  other  processing  by
+              replacing  the  account name of every posting having the tag TAG
+              with content VALUE by the account name "TAG:VALUE".
+       The TAG will only match if it is a full-length match.  The  pivot  will
+       only  happen  if  the TAG is on a posting, not if it is on the transac-
+       tion.  If the tag value is a multi:level:account:name the  new  account
+       name will be "TAG:multi:level:account:name".
+
+       --anon show anonymized accounts and payees
+
+KEYS
+       ?  shows a help dialog listing all keys.  (Some of these also appear in
+       the quick help at the bottom of each screen.) Press ? again (or ESCAPE,
+       or LEFT) to close it.  The following keys work on most screens:
+
+       The cursor keys navigate: right (or enter) goes deeper, left returns to
+       the previous screen,  up/down/page up/page down/home/end  move  up  and
+       down through lists.  Vi-style h/j/k/l movement keys are also supported.
+       A tip: movement speed is limited by your keyboard repeat rate, to  move
+       faster  you  may want to adjust it.  (If you're on a mac, the Karabiner
+       app is one way to do that.)
+
+       With shift pressed, the cursor keys adjust the report period,  limiting
+       the   transactions   to   be   shown   (by  default,  all  are  shown).
+       shift-down/up steps downward and upward through these  standard  report
+       period   durations:   year,   quarter,   month,   week,   day.    Then,
+       shift-left/right moves to the previous/next period.  t sets the  report
+       period  to  today.   (To set a non-standard period, you can use / and a
+       date: query).
+
+       / lets you set a general filter query limiting the  data  shown,  using
+       the  same query terms as in hledger and hledger-web.  While editing the
+       query, you can use CTRL-a/e/d/k, BS, cursor keys; press  ENTER  to  set
+       it, or ESCAPEto cancel.  There are also keys for quickly adjusting some
+       common filters like account depth and  cleared/uncleared  (see  below).
+       BACKSPACE or DELETE removes all filters, showing all transactions.
+
+       ESCAPE  removes  all  filters and jumps back to the top screen.  Or, it
+       cancels a minibuffer edit or help dialog in progress.
+
+       g reloads from the data file(s) and updates the current screen and  any
+       previous  screens.   (With  large  files, this could cause a noticeable
+       pause.)
+
+       I toggles balance assertion  checking.   Disabling  balance  assertions
+       temporarily can be useful for troubleshooting.
+
+       a  runs  command-line  hledger's  add  command, and reloads the updated
+       file.  This allows some basic data entry.
+
+       E  runs  $HLEDGER_UI_EDITOR,  or   $EDITOR,   or   a   default   (emac-
+       sclient -a "" -nw) on the journal file.  With some editors (emacs, vi),
+       the cursor will be positioned at the current transaction  when  invoked
+       from  the  register  and transaction screens, and at the error location
+       (if possible) when invoked from the error screen.
+
+       q quits the application.
+
+       Additional screen-specific keys are described below.
+
+SCREENS
+   Accounts screen
+       This is normally the first screen displayed.   It  lists  accounts  and
+       their  balances,  like hledger's balance command.  By default, it shows
+       all accounts and their latest ending balances (including  the  balances
+       of  subaccounts).  if you specify a query on the command line, it shows
+       just the matched accounts and the balances from matched transactions.
+
+       Account names are normally indented to show the hierarchy (tree  mode).
+       To see less detail, set a depth limit by pressing a number key, 1 to 9.
+       0 shows even less detail, collapsing all accounts to a single total.  -
+       and  +  (or  =)  decrease  and increase the depth limit.  To remove the
+       depth limit, set it higher than the maximum  account  depth,  or  press
+       ESCAPE.
+
+       F  toggles  flat mode, in which accounts are shown as a flat list, with
+       their full names.  In this mode, account balances exclude  subaccounts,
+       except  for accounts at the depth limit (as with hledger's balance com-
+       mand).
+
+       H toggles between showing historical balances or period balances.  His-
+       torical  balances  (the  default) are ending balances at the end of the
+       report period, taking into account all transactions  before  that  date
+       (filtered  by  the  filter query if any), including transactions before
+       the start of the report period.  In other  words,  historical  balances
+       are  what  you  would  see on a bank statement for that account (unless
+       disturbed by a filter  query).   Period  balances  ignore  transactions
+       before the report start date, so they show the change in balance during
+       the report period.  They are more useful eg when viewing a time log.
+
+       C toggles cleared mode, in which uncleared  transactions  and  postings
+       are  not  shown.   U  toggles  uncleared  mode, in which only uncleared
+       transactions/postings are shown.
+
+       R toggles real mode, in which virtual postings are ignored.
+
+       Z toggles nonzero mode, in which only accounts  with  nonzero  balances
+       are  shown (hledger-ui shows zero items by default, unlike command-line
+       hledger).
+
+       Press right or enter to view an account's transactions register.
+
+   Register screen
+       This screen shows the transactions affecting a particular account, like
+       a check register.  Each line represents one transaction and shows:
+
+       o the  other  account(s)  involved, in abbreviated form.  (If there are
+         both real and virtual postings, it shows only the  accounts  affected
+         by real postings.)
+
+       o the  overall change to the current account's balance; positive for an
+         inflow to this account, negative for an outflow.
+
+       o the running historical total or period total for the current account,
+         after  the  transaction.  This can be toggled with H.  Similar to the
+         accounts screen, the historical total  is  affected  by  transactions
+         (filtered  by  the  filter query) before the report start date, while
+         the period total is not.  If the historical total is not disturbed by
+         a  filter  query, it will be the running historical balance you would
+         see on a bank register for the current account.
+
+       If the accounts screen was in  tree  mode,  the  register  screen  will
+       include transactions from both the current account and its subaccounts.
+       If the accounts screen  was  in  flat  mode,  and  a  non-depth-clipped
+       account  was  selected,  the  register screen will exclude transactions
+       from subaccounts.  In other words, the register always shows the trans-
+       actions  responsible  for  the  period  balance  shown  on the accounts
+       screen.  As on the accounts screen, this can be toggled with F.
+
+       C toggles cleared mode, in which uncleared  transactions  and  postings
+       are  not  shown.   U  toggles  uncleared  mode, in which only uncleared
+       transactions/postings are shown.
+
+       R toggles real mode, in which virtual postings are ignored.
+
+       Z toggles nonzero mode, in which only transactions  posting  a  nonzero
+       change  are  shown (hledger-ui shows zero items by default, unlike com-
+       mand-line hledger).
+
+       Press right (or enter) to view the selected transaction in detail.
+
+   Transaction screen
+       This screen shows a single transaction, as  a  general  journal  entry,
+       similar  to  hledger's  print command and journal format (hledger_jour-
+       nal(5)).
+
+       The transaction's date(s)  and  any  cleared  flag,  transaction  code,
+       description,  comments,  along  with  all  of  its account postings are
+       shown.  Simple transactions have two postings, but there  can  be  more
+       (or in certain cases, fewer).
+
+       up  and  down will step through all transactions listed in the previous
+       account register screen.  In the title bar, the numbers in  parentheses
+       show  your  position  within  that  account  register.   They will vary
+       depending on which account register you came from (remember most trans-
+       actions appear in multiple account registers).  The #N number preceding
+       them is the transaction's position within the complete unfiltered jour-
+       nal, which is a more stable id (at least until the next reload).
+
+   Error screen
+       This  screen  will appear if there is a problem, such as a parse error,
+       when you press g to reload.  Once you have fixed the problem,  press  g
+       again to reload and resume normal operation.  (Or, you can press escape
+       to cancel the reload attempt.)
+
+ENVIRONMENT
+       COLUMNS The screen width to use.  Default: the full terminal width.
+
+       LEDGER_FILE The journal file path when not specified with -f.  Default:
+       ~/.hledger.journal  (on  windows,  perhaps C:/Users/USER/.hledger.jour-
+       nal).
+
+FILES
+       Reads data from one or more files in hledger journal, timeclock,  time-
+       dot,   or   CSV   format   specified   with  -f,  or  $LEDGER_FILE,  or
+       $HOME/.hledger.journal          (on          windows,           perhaps
+       C:/Users/USER/.hledger.journal).
+
+BUGS
+       The  need  to precede options with -- when invoked from hledger is awk-
+       ward.
+
+       -f- doesn't work (hledger-ui can't read from stdin).
+
+       -V affects only the accounts screen.
+
+       When you press g, the current and all previous screens are regenerated,
+       which may cause a noticeable pause.  Also there is no visual indication
+       that this is in progress.
+
+       The register screen's switching between historic  balance  and  running
+       total based on query arguments may be confusing, and there is no column
+       heading to indicate which is being displayed.
+
+
+
+REPORTING BUGS
+       Report bugs at http://bugs.hledger.org (or on the #hledger IRC  channel
+       or hledger mail list)
+
+
+AUTHORS
+       Simon Michael <simon@joyful.com> and contributors
+
+
+COPYRIGHT
+       Copyright (C) 2007-2016 Simon Michael.
+       Released under GNU GPL v3 or later.
+
+
+SEE ALSO
+       hledger(1),      hledger-ui(1),     hledger-web(1),     hledger-api(1),
+       hledger_csv(5), hledger_journal(5), hledger_timeclock(5), hledger_time-
+       dot(5), ledger(1)
+
+       http://hledger.org
+
+
+
+hledger-ui 1.0                   October 2016                    hledger-ui(1)
diff --git a/hledger-ui.cabal b/hledger-ui.cabal
--- a/hledger-ui.cabal
+++ b/hledger-ui.cabal
@@ -1,22 +1,22 @@
--- This file has been generated from package.yaml by hpack version 0.5.4.
+-- This file has been generated from package.yaml by hpack version 0.14.0.
 --
 -- see: https://github.com/sol/hpack
 
 name:           hledger-ui
-version:        0.27.5
+version:        1.0
 stability:      beta
 category:       Finance, Console
 synopsis:       Curses-style user interface for the hledger accounting tool
-description:
-    This is hledger's curses-style interface.
-    It is simpler and more convenient for browsing data than the command-line interface,
-    but lighter and faster than hledger-web.
-    hledger is a cross-platform program for tracking money, time, or
-    any other commodity, using double-entry accounting and a simple,
-    editable file format. It is inspired by and largely compatible
-    with ledger(1).  hledger provides command-line, curses and web
-    interfaces, and aims to be a reliable, practical tool for daily
-    use.
+description:    This is hledger's curses-style interface.
+                It is simpler and more convenient for browsing data than the command-line interface,
+                but lighter and faster than hledger-web.
+                .
+                hledger is a cross-platform program for tracking money, time, or
+                any other commodity, using double-entry accounting and a simple,
+                editable file format. It is inspired by and largely compatible
+                with ledger(1).  hledger provides command-line, curses and web
+                interfaces, and aims to be a reliable, practical tool for daily
+                use.
 license:        GPL
 license-file:   LICENSE
 author:         Simon Michael <simon@joyful.com>
@@ -25,68 +25,85 @@
 bug-reports:    http://bugs.hledger.org
 cabal-version:  >= 1.10
 build-type:     Simple
-tested-with:    GHC==7.8.4, GHC==7.10.3, GHC==8.0.1
+tested-with:    GHC==7.10.3, GHC==8.0
 
 extra-source-files:
     CHANGES
     README
 
+data-files:
+    doc/hledger-ui.1
+    doc/hledger-ui.1.info
+    doc/hledger-ui.1.txt
+
 source-repository head
   type: git
   location: https://github.com/simonmichael/hledger
 
+flag oldtime
+  description: If building with time < 1.5, also depend on old-locale. Set automatically by cabal.
+  manual: False
+  default: False
+
 flag threaded
   default: True
-  description:
-    Build with support for multithreaded execution
-
-flag old-locale
-  default: False
-  description:
-    A compatibility flag, set automatically by cabal.
-    If false then depend on time >= 1.5,
-    if true then depend on time < 1.5 together with old-locale.
+  description: Build with support for multithreaded execution
+  manual: False
 
 executable hledger-ui
   main-is: hledger-ui.hs
   hs-source-dirs:
       .
   ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-missing-signatures -fno-warn-type-defaults -fno-warn-orphans
-  if flag(threaded)
-    ghc-options: -threaded
-  cpp-options: -DVERSION="0.27.5"
+  cpp-options: -DVERSION="1.0"
   build-depends:
-      hledger >= 0.27.1 && < 0.28
-    , hledger-lib >= 0.27.1 && < 0.28
+      hledger >= 1.0 && < 1.1
+    , hledger-lib >= 1.0 && < 1.1
+    , ansi-terminal >= 0.6.2.3 && < 0.7
     , base >= 3 && < 5
     , base-compat >= 0.8.1
-    , brick >= 0.2 && < 0.7
     , cmdargs >= 0.8
     , containers
     , data-default
     , filepath
     , HUnit
-    , lens >= 4.12.3 && < 4.15
+    , microlens >= 0.4 && < 0.5
+    , microlens-platform >= 0.2.3.1 && < 0.4
+    , megaparsec >= 5
+    , pretty-show >=1.6.4
+    , process >= 1.2
     , safe >= 0.2
     , split >= 0.1 && < 0.3
+    , text >= 1.2 && < 1.3
+    , text-zipper >= 0.4 && < 0.9
     , transformers
     , vector
-    , vty >= 5.2 && < 5.6
-  if impl(ghc >= 7.4)
-    build-depends: pretty-show >= 1.6.4
-  if flag(old-locale)
-    build-depends: time < 1.5, old-locale
+  if os(windows)
+    buildable: False
   else
-    build-depends: time >= 1.5
+    build-depends:
+        brick >= 0.7 && < 0.9
+      , vty >= 5.5 && < 5.12
+  if flag(threaded)
+    ghc-options: -threaded
+  if flag(oldtime)
+    build-depends:
+        time < 1.5
+      , old-locale
+  else
+    build-depends:
+        time >= 1.5
   other-modules:
       Hledger.UI
-      Hledger.UI.Main
-      Hledger.UI.UIOptions
-      Hledger.UI.Theme
-      Hledger.UI.UITypes
-      Hledger.UI.UIUtils
       Hledger.UI.AccountsScreen
+      Hledger.UI.Editor
       Hledger.UI.ErrorScreen
+      Hledger.UI.Main
       Hledger.UI.RegisterScreen
+      Hledger.UI.Theme
       Hledger.UI.TransactionScreen
+      Hledger.UI.UIOptions
+      Hledger.UI.UIState
+      Hledger.UI.UITypes
+      Hledger.UI.UIUtils
   default-language: Haskell2010
diff --git a/hledger-ui.hs b/hledger-ui.hs
--- a/hledger-ui.hs
+++ b/hledger-ui.hs
@@ -1,2 +1,5 @@
 #!/usr/bin/env runhaskell
+module Main
+  (module Hledger.UI)
+where
 import Hledger.UI (main)
