packages feed

hscurses 1.4.2.0 → 1.5.0.0

raw patch · 20 files changed

+4125/−3327 lines, 20 filesdep +hscursesdep +prettydep +timedep −old-localedep −old-timedep ~basedep ~exceptionsdep ~mtlsetup-changednew-component:exe:contact-managernew-component:exe:key-testnew-component:exe:widget-test-editnew-component:exe:widget-test-tablenew-component:exe:widget-test-textnew-uploaderPVP ok

version bump matches the API change (PVP)

Dependencies added: hscurses, pretty, time

Dependencies removed: old-locale, old-time

Dependency ranges changed: base, exceptions, mtl, unix

API changes (from Hackage documentation)

- UI.HSCurses.Curses: bl :: Border -> Char
- UI.HSCurses.Curses: br :: Border -> Char
- UI.HSCurses.Curses: bs :: Border -> Char
- UI.HSCurses.Curses: instance Bits Attr
- UI.HSCurses.Curses: instance Eq Attr
- UI.HSCurses.Curses: instance Eq ButtonEvent
- UI.HSCurses.Curses: instance Eq Color
- UI.HSCurses.Curses: instance Eq Key
- UI.HSCurses.Curses: instance Eq Pair
- UI.HSCurses.Curses: instance Ix Color
- UI.HSCurses.Curses: instance Ix Pair
- UI.HSCurses.Curses: instance Num Attr
- UI.HSCurses.Curses: instance Ord Color
- UI.HSCurses.Curses: instance Ord Pair
- UI.HSCurses.Curses: instance Show Attr
- UI.HSCurses.Curses: instance Show ButtonEvent
- UI.HSCurses.Curses: instance Show Key
- UI.HSCurses.Curses: instance Show MouseEvent
- UI.HSCurses.Curses: instance Show Pair
- UI.HSCurses.Curses: instance Storable Attr
- UI.HSCurses.Curses: ls :: Border -> Char
- UI.HSCurses.Curses: mouseEventButton :: MouseEvent -> [ButtonEvent]
- UI.HSCurses.Curses: mouseEventId :: MouseEvent -> Int
- UI.HSCurses.Curses: mouseEventX :: MouseEvent -> Int
- UI.HSCurses.Curses: mouseEventY :: MouseEvent -> Int
- UI.HSCurses.Curses: mouseEventZ :: MouseEvent -> Int
- UI.HSCurses.Curses: rs :: Border -> Char
- UI.HSCurses.Curses: tl :: Border -> Char
- UI.HSCurses.Curses: tr :: Border -> Char
- UI.HSCurses.Curses: ts :: Border -> Char
- UI.HSCurses.CursesHelper: instance Eq Attribute
- UI.HSCurses.CursesHelper: instance Eq BackgroundColor
- UI.HSCurses.CursesHelper: instance Eq CursesStyle
- UI.HSCurses.CursesHelper: instance Eq ForegroundColor
- UI.HSCurses.CursesHelper: instance Eq Style
- UI.HSCurses.CursesHelper: instance Show Attribute
- UI.HSCurses.CursesHelper: instance Show BackgroundColor
- UI.HSCurses.CursesHelper: instance Show CursesStyle
- UI.HSCurses.CursesHelper: instance Show ForegroundColor
- UI.HSCurses.CursesHelper: instance Show Style
- UI.HSCurses.Widgets: dstyle_active :: DrawingStyle -> CursesStyle
- UI.HSCurses.Widgets: dstyle_focus :: DrawingStyle -> CursesStyle
- UI.HSCurses.Widgets: dstyle_normal :: DrawingStyle -> CursesStyle
- UI.HSCurses.Widgets: ew_content :: EditWidget -> String
- UI.HSCurses.Widgets: ew_history :: EditWidget -> [String]
- UI.HSCurses.Widgets: ew_historyIndex :: EditWidget -> Int
- UI.HSCurses.Widgets: ew_historySavedContent :: EditWidget -> Maybe String
- UI.HSCurses.Widgets: ew_options :: EditWidget -> EditWidgetOptions
- UI.HSCurses.Widgets: ew_xcursor :: EditWidget -> Int
- UI.HSCurses.Widgets: ew_xoffset :: EditWidget -> Int
- UI.HSCurses.Widgets: ewopt_keyHandlers :: EditWidgetOptions -> [(Key, KeyHandler EditWidget)]
- UI.HSCurses.Widgets: ewopt_minWidth :: EditWidgetOptions -> Int
- UI.HSCurses.Widgets: ewopt_style :: EditWidgetOptions -> DrawingStyle
- UI.HSCurses.Widgets: instance ActiveWidget EditWidget
- UI.HSCurses.Widgets: instance Eq Direction
- UI.HSCurses.Widgets: instance Eq DrawingHint
- UI.HSCurses.Widgets: instance Eq DrawingStyle
- UI.HSCurses.Widgets: instance Eq FillRow
- UI.HSCurses.Widgets: instance Eq HAlignment
- UI.HSCurses.Widgets: instance Eq TableWidgetOptions
- UI.HSCurses.Widgets: instance Eq TextWidget
- UI.HSCurses.Widgets: instance Eq TextWidgetOptions
- UI.HSCurses.Widgets: instance Eq TextWidgetSize
- UI.HSCurses.Widgets: instance Ord Direction
- UI.HSCurses.Widgets: instance Ord DrawingHint
- UI.HSCurses.Widgets: instance Show Direction
- UI.HSCurses.Widgets: instance Show DrawingHint
- UI.HSCurses.Widgets: instance Show DrawingStyle
- UI.HSCurses.Widgets: instance Show FillRow
- UI.HSCurses.Widgets: instance Show HAlignment
- UI.HSCurses.Widgets: instance Show TableWidgetOptions
- UI.HSCurses.Widgets: instance Show TextWidget
- UI.HSCurses.Widgets: instance Show TextWidgetOptions
- UI.HSCurses.Widgets: instance Show TextWidgetSize
- UI.HSCurses.Widgets: instance Widget EditWidget
- UI.HSCurses.Widgets: instance Widget EmptyWidget
- UI.HSCurses.Widgets: instance Widget OpaqueWidget
- UI.HSCurses.Widgets: instance Widget TableCell
- UI.HSCurses.Widgets: instance Widget TableWidget
- UI.HSCurses.Widgets: instance Widget TextWidget
- UI.HSCurses.Widgets: tbw_colOffset :: TableWidget -> Int
- UI.HSCurses.Widgets: tbw_options :: TableWidget -> TableWidgetOptions
- UI.HSCurses.Widgets: tbw_pos :: TableWidget -> Maybe Pos
- UI.HSCurses.Widgets: tbw_rows :: TableWidget -> [Row]
- UI.HSCurses.Widgets: tbwdisp_firstVis :: TableWidgetDisplayInfo -> Int
- UI.HSCurses.Widgets: tbwdisp_height :: TableWidgetDisplayInfo -> Int
- UI.HSCurses.Widgets: tbwdisp_heights :: TableWidgetDisplayInfo -> [Int]
- UI.HSCurses.Widgets: tbwdisp_lastVis :: TableWidgetDisplayInfo -> Int
- UI.HSCurses.Widgets: tbwdisp_nrows :: TableWidgetDisplayInfo -> Int
- UI.HSCurses.Widgets: tbwdisp_rightMargin :: TableWidgetDisplayInfo -> Maybe (Int, Size)
- UI.HSCurses.Widgets: tbwdisp_rows :: TableWidgetDisplayInfo -> [Row]
- UI.HSCurses.Widgets: tbwdisp_width :: TableWidgetDisplayInfo -> Int
- UI.HSCurses.Widgets: tbwdisp_widths :: TableWidgetDisplayInfo -> [Int]
- UI.HSCurses.Widgets: tbwopt_activeCols :: TableWidgetOptions -> [Int]
- UI.HSCurses.Widgets: tbwopt_fillCol :: TableWidgetOptions -> Maybe Int
- UI.HSCurses.Widgets: tbwopt_fillRow :: TableWidgetOptions -> FillRow
- UI.HSCurses.Widgets: tbwopt_minSize :: TableWidgetOptions -> Size
- UI.HSCurses.Widgets: tw_options :: TextWidget -> TextWidgetOptions
- UI.HSCurses.Widgets: tw_text :: TextWidget -> String
- UI.HSCurses.Widgets: tw_xoffset :: TextWidget -> Int
- UI.HSCurses.Widgets: tw_yoffset :: TextWidget -> Int
- UI.HSCurses.Widgets: twopt_halign :: TextWidgetOptions -> HAlignment
- UI.HSCurses.Widgets: twopt_size :: TextWidgetOptions -> TextWidgetSize
- UI.HSCurses.Widgets: twopt_style :: TextWidgetOptions -> DrawingStyle
+ UI.HSCurses.Curses: [bl] :: Border -> Char
+ UI.HSCurses.Curses: [br] :: Border -> Char
+ UI.HSCurses.Curses: [bs] :: Border -> Char
+ UI.HSCurses.Curses: [ls] :: Border -> Char
+ UI.HSCurses.Curses: [mouseEventButton] :: MouseEvent -> [ButtonEvent]
+ UI.HSCurses.Curses: [mouseEventId] :: MouseEvent -> CInt
+ UI.HSCurses.Curses: [mouseEventX] :: MouseEvent -> CInt
+ UI.HSCurses.Curses: [mouseEventY] :: MouseEvent -> CInt
+ UI.HSCurses.Curses: [mouseEventZ] :: MouseEvent -> CInt
+ UI.HSCurses.Curses: [rs] :: Border -> Char
+ UI.HSCurses.Curses: [tl] :: Border -> Char
+ UI.HSCurses.Curses: [tr] :: Border -> Char
+ UI.HSCurses.Curses: [ts] :: Border -> Char
+ UI.HSCurses.Curses: delScreen :: Screen -> IO ()
+ UI.HSCurses.Curses: getMouse :: MonadIO m => m (Maybe MouseEvent)
+ UI.HSCurses.Curses: instance GHC.Classes.Eq UI.HSCurses.Curses.Attr
+ UI.HSCurses.Curses: instance GHC.Classes.Eq UI.HSCurses.Curses.ButtonEvent
+ UI.HSCurses.Curses: instance GHC.Classes.Eq UI.HSCurses.Curses.Color
+ UI.HSCurses.Curses: instance GHC.Classes.Eq UI.HSCurses.Curses.Key
+ UI.HSCurses.Curses: instance GHC.Classes.Eq UI.HSCurses.Curses.Pair
+ UI.HSCurses.Curses: instance GHC.Classes.Ord UI.HSCurses.Curses.Color
+ UI.HSCurses.Curses: instance GHC.Classes.Ord UI.HSCurses.Curses.Pair
+ UI.HSCurses.Curses: instance GHC.Internal.Bits.Bits UI.HSCurses.Curses.Attr
+ UI.HSCurses.Curses: instance GHC.Internal.Foreign.Storable.Storable UI.HSCurses.Curses.Attr
+ UI.HSCurses.Curses: instance GHC.Internal.Foreign.Storable.Storable UI.HSCurses.Curses.MouseEvent
+ UI.HSCurses.Curses: instance GHC.Internal.Ix.Ix UI.HSCurses.Curses.Color
+ UI.HSCurses.Curses: instance GHC.Internal.Ix.Ix UI.HSCurses.Curses.Pair
+ UI.HSCurses.Curses: instance GHC.Internal.Num.Num UI.HSCurses.Curses.Attr
+ UI.HSCurses.Curses: instance GHC.Internal.Show.Show UI.HSCurses.Curses.Attr
+ UI.HSCurses.Curses: instance GHC.Internal.Show.Show UI.HSCurses.Curses.ButtonEvent
+ UI.HSCurses.Curses: instance GHC.Internal.Show.Show UI.HSCurses.Curses.Key
+ UI.HSCurses.Curses: instance GHC.Internal.Show.Show UI.HSCurses.Curses.MouseEvent
+ UI.HSCurses.Curses: instance GHC.Internal.Show.Show UI.HSCurses.Curses.Pair
+ UI.HSCurses.Curses: newTerm :: String -> FD -> FD -> IO Screen
+ UI.HSCurses.Curses: type Screen = Ptr ()
+ UI.HSCurses.Curses: withAllMouseEvents :: MonadIO m => m a -> m a
+ UI.HSCurses.CursesHelper: instance GHC.Classes.Eq UI.HSCurses.CursesHelper.Attribute
+ UI.HSCurses.CursesHelper: instance GHC.Classes.Eq UI.HSCurses.CursesHelper.BackgroundColor
+ UI.HSCurses.CursesHelper: instance GHC.Classes.Eq UI.HSCurses.CursesHelper.CursesStyle
+ UI.HSCurses.CursesHelper: instance GHC.Classes.Eq UI.HSCurses.CursesHelper.ForegroundColor
+ UI.HSCurses.CursesHelper: instance GHC.Classes.Eq UI.HSCurses.CursesHelper.Style
+ UI.HSCurses.CursesHelper: instance GHC.Internal.Show.Show UI.HSCurses.CursesHelper.Attribute
+ UI.HSCurses.CursesHelper: instance GHC.Internal.Show.Show UI.HSCurses.CursesHelper.BackgroundColor
+ UI.HSCurses.CursesHelper: instance GHC.Internal.Show.Show UI.HSCurses.CursesHelper.CursesStyle
+ UI.HSCurses.CursesHelper: instance GHC.Internal.Show.Show UI.HSCurses.CursesHelper.ForegroundColor
+ UI.HSCurses.CursesHelper: instance GHC.Internal.Show.Show UI.HSCurses.CursesHelper.Style
+ UI.HSCurses.Widgets: [dstyle_active] :: DrawingStyle -> CursesStyle
+ UI.HSCurses.Widgets: [dstyle_focus] :: DrawingStyle -> CursesStyle
+ UI.HSCurses.Widgets: [dstyle_normal] :: DrawingStyle -> CursesStyle
+ UI.HSCurses.Widgets: [ew_content] :: EditWidget -> String
+ UI.HSCurses.Widgets: [ew_historyIndex] :: EditWidget -> Int
+ UI.HSCurses.Widgets: [ew_historySavedContent] :: EditWidget -> Maybe String
+ UI.HSCurses.Widgets: [ew_history] :: EditWidget -> [String]
+ UI.HSCurses.Widgets: [ew_options] :: EditWidget -> EditWidgetOptions
+ UI.HSCurses.Widgets: [ew_xcursor] :: EditWidget -> Int
+ UI.HSCurses.Widgets: [ew_xoffset] :: EditWidget -> Int
+ UI.HSCurses.Widgets: [ewopt_keyHandlers] :: EditWidgetOptions -> [(Key, KeyHandler EditWidget)]
+ UI.HSCurses.Widgets: [ewopt_minWidth] :: EditWidgetOptions -> Int
+ UI.HSCurses.Widgets: [ewopt_style] :: EditWidgetOptions -> DrawingStyle
+ UI.HSCurses.Widgets: [tbw_colOffset] :: TableWidget -> Int
+ UI.HSCurses.Widgets: [tbw_options] :: TableWidget -> TableWidgetOptions
+ UI.HSCurses.Widgets: [tbw_pos] :: TableWidget -> Maybe Pos
+ UI.HSCurses.Widgets: [tbw_rows] :: TableWidget -> [Row]
+ UI.HSCurses.Widgets: [tbwdisp_firstVis] :: TableWidgetDisplayInfo -> Int
+ UI.HSCurses.Widgets: [tbwdisp_height] :: TableWidgetDisplayInfo -> Int
+ UI.HSCurses.Widgets: [tbwdisp_heights] :: TableWidgetDisplayInfo -> [Int]
+ UI.HSCurses.Widgets: [tbwdisp_lastVis] :: TableWidgetDisplayInfo -> Int
+ UI.HSCurses.Widgets: [tbwdisp_nrows] :: TableWidgetDisplayInfo -> Int
+ UI.HSCurses.Widgets: [tbwdisp_rightMargin] :: TableWidgetDisplayInfo -> Maybe (Int, Size)
+ UI.HSCurses.Widgets: [tbwdisp_rows] :: TableWidgetDisplayInfo -> [Row]
+ UI.HSCurses.Widgets: [tbwdisp_width] :: TableWidgetDisplayInfo -> Int
+ UI.HSCurses.Widgets: [tbwdisp_widths] :: TableWidgetDisplayInfo -> [Int]
+ UI.HSCurses.Widgets: [tbwopt_activeCols] :: TableWidgetOptions -> [Int]
+ UI.HSCurses.Widgets: [tbwopt_fillCol] :: TableWidgetOptions -> Maybe Int
+ UI.HSCurses.Widgets: [tbwopt_fillRow] :: TableWidgetOptions -> FillRow
+ UI.HSCurses.Widgets: [tbwopt_minSize] :: TableWidgetOptions -> Size
+ UI.HSCurses.Widgets: [tw_options] :: TextWidget -> TextWidgetOptions
+ UI.HSCurses.Widgets: [tw_text] :: TextWidget -> String
+ UI.HSCurses.Widgets: [tw_xoffset] :: TextWidget -> Int
+ UI.HSCurses.Widgets: [tw_yoffset] :: TextWidget -> Int
+ UI.HSCurses.Widgets: [twopt_halign] :: TextWidgetOptions -> HAlignment
+ UI.HSCurses.Widgets: [twopt_size] :: TextWidgetOptions -> TextWidgetSize
+ UI.HSCurses.Widgets: [twopt_style] :: TextWidgetOptions -> DrawingStyle
+ UI.HSCurses.Widgets: instance GHC.Classes.Eq UI.HSCurses.Widgets.Direction
+ UI.HSCurses.Widgets: instance GHC.Classes.Eq UI.HSCurses.Widgets.DrawingHint
+ UI.HSCurses.Widgets: instance GHC.Classes.Eq UI.HSCurses.Widgets.DrawingStyle
+ UI.HSCurses.Widgets: instance GHC.Classes.Eq UI.HSCurses.Widgets.FillRow
+ UI.HSCurses.Widgets: instance GHC.Classes.Eq UI.HSCurses.Widgets.HAlignment
+ UI.HSCurses.Widgets: instance GHC.Classes.Eq UI.HSCurses.Widgets.TableWidgetOptions
+ UI.HSCurses.Widgets: instance GHC.Classes.Eq UI.HSCurses.Widgets.TextWidget
+ UI.HSCurses.Widgets: instance GHC.Classes.Eq UI.HSCurses.Widgets.TextWidgetOptions
+ UI.HSCurses.Widgets: instance GHC.Classes.Eq UI.HSCurses.Widgets.TextWidgetSize
+ UI.HSCurses.Widgets: instance GHC.Classes.Ord UI.HSCurses.Widgets.Direction
+ UI.HSCurses.Widgets: instance GHC.Classes.Ord UI.HSCurses.Widgets.DrawingHint
+ UI.HSCurses.Widgets: instance GHC.Internal.Show.Show UI.HSCurses.Widgets.Direction
+ UI.HSCurses.Widgets: instance GHC.Internal.Show.Show UI.HSCurses.Widgets.DrawingHint
+ UI.HSCurses.Widgets: instance GHC.Internal.Show.Show UI.HSCurses.Widgets.DrawingStyle
+ UI.HSCurses.Widgets: instance GHC.Internal.Show.Show UI.HSCurses.Widgets.FillRow
+ UI.HSCurses.Widgets: instance GHC.Internal.Show.Show UI.HSCurses.Widgets.HAlignment
+ UI.HSCurses.Widgets: instance GHC.Internal.Show.Show UI.HSCurses.Widgets.TableWidgetOptions
+ UI.HSCurses.Widgets: instance GHC.Internal.Show.Show UI.HSCurses.Widgets.TextWidget
+ UI.HSCurses.Widgets: instance GHC.Internal.Show.Show UI.HSCurses.Widgets.TextWidgetOptions
+ UI.HSCurses.Widgets: instance GHC.Internal.Show.Show UI.HSCurses.Widgets.TextWidgetSize
+ UI.HSCurses.Widgets: instance UI.HSCurses.Widgets.ActiveWidget UI.HSCurses.Widgets.EditWidget
+ UI.HSCurses.Widgets: instance UI.HSCurses.Widgets.Widget UI.HSCurses.Widgets.EditWidget
+ UI.HSCurses.Widgets: instance UI.HSCurses.Widgets.Widget UI.HSCurses.Widgets.EmptyWidget
+ UI.HSCurses.Widgets: instance UI.HSCurses.Widgets.Widget UI.HSCurses.Widgets.OpaqueWidget
+ UI.HSCurses.Widgets: instance UI.HSCurses.Widgets.Widget UI.HSCurses.Widgets.TableCell
+ UI.HSCurses.Widgets: instance UI.HSCurses.Widgets.Widget UI.HSCurses.Widgets.TableWidget
+ UI.HSCurses.Widgets: instance UI.HSCurses.Widgets.Widget UI.HSCurses.Widgets.TextWidget
- UI.HSCurses.Curses: MouseEvent :: Int -> Int -> Int -> Int -> [ButtonEvent] -> MouseEvent
+ UI.HSCurses.Curses: MouseEvent :: CInt -> CInt -> CInt -> CInt -> [ButtonEvent] -> MouseEvent
- UI.HSCurses.Curses: type ChType = Word64
+ UI.HSCurses.Curses: type ChType = Word32
- UI.HSCurses.Curses: wAddStr :: Window -> [Char] -> IO ()
+ UI.HSCurses.Curses: wAddStr :: Window -> String -> IO ()
- UI.HSCurses.Widgets: type KeyHandler a = Pos -> Size -> a -> IO (Cont a)
+ UI.HSCurses.Widgets: type KeyHandler a = Pos -> Size -> a -> IO Cont a

Files

ChangeLog view
@@ -1,4 +1,12 @@-1.4.2.0 (2015-02-12_+1.5.0.0 (2025-01-19)+  - Support for mouse events+  - Support for 'newterm' and 'delscreen'+  - Made debug logging usable for library users by compiling with the 'debug'+    flag+  - Replaced 'old-time' and 'old-locale' dependencies with just 'time'+  - Tested GHC versions >=8.4++1.4.2.0 (2015-02-12)   - support for werase and winsch (thanks to Ryan Newton)   - add wnoutrefresh for more efficient updates of multiple windows (Ryan Newton)   - use getmaxyx for scrSize,  remove non-recommended approach of reading
− README
@@ -1,72 +0,0 @@-               hscurses -- A Haskell Binding to ncurses-               =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+--hscurses is a Haskell binding to the ncurses library, a library of-functions that manage an application's display on character-cell-terminals. hscurses also provides some basic widgets implemented on-top of the ncurses binding, such as a text input widget and a table-widget.--The hscurses library has been reported to work on Linux x86 using GHC-6.12.1, 7.0.3 and 7.2.1.--Building and installing the hscurses library with GHC--------------------------------------------------------Requirements:--- GNU m4-- GHC >= 6.8-- ncurses--Build and installation steps:--  runhaskell Setup.hs configure-  runhaskell Setup.hs build-  runhaskell Setup.hs install--In order to generate the API documentation, you need haddock-(http://www.haskell.org/haddock). The command is then:--  runhaskell Setup.hs haddock--Using the hscurses library:------------------------------Just add the flag `-package hscurses' to the compiler flags. At the-moment, user documentation is only available through Haddock API-documentation.--Windows support:-------------------Windows support relies on pdcurses (http://pdcurses.sourceforge.net/),-which is already packaged for MinGW-(http://sourceforge.net/projects/mingw/files/MinGW/PDCurses/PDCurses-3.4-1/)-and can be installed with mingw-get.--Thanks to José Romildo Malaquias (malaquias@gmail.com) for porting-hscurses to the windows platform!--Copyright:-------------   John Meacham <john @ repetae . net>, 2002-2004.-   Tuomo Valkonen <tuomov @ iki.fi>, 2004.-   Don Stewart <http://www.cse.unsw.edu.au/~dons>, 2004.-   Stefan Wehr <http://www.stefanwehr.de>, 2004-2011.--History:-----------John Meacham started the binding for his chat client Ginsu-(http://repetae.net/john/computer/ginsu/).--Tuomo Valkonen integrated to code into Riot-(http://modeemi.fi/~tuomov/riot/), with minor modifications.--Don Stewart improved the code for the Yi editor-(http://www.cse.unsw.edu.au/~dons/yi.html).--Stefan Wehr turned the binding into a standalone library. He also-added some basic widgets.
+ README.md view
@@ -0,0 +1,113 @@+# hscurses -- A Haskell Binding to ncurses++`hscurses` is a Haskell binding to the ncurses library, a library of functions+that manage an application's display on character-cell terminals. `hscurses`+also provides some basic widgets implemented on top of the `ncurses` binding,+such as a text input widget and a table widget.++The `hscurses` library has been reported to work on Linux x86 using GHC 6.12.1,+7.0.3, 7.2.1, and GHC major versions starting from GHC 8.4. See the GitHub+actions for GHC versions that we actively test against.++## Building the `hscurses` library++Requirements:++- `cabal-install` + GHC installation+- `ncurses` with headers++Build:++    cabal build++The repository contains a couple of example and testing applications in the+[example](./example) and [tests](./tests) directories. To run them, you have to+enable the `examples` Cabal flag; for instance:++    cabal run -f examples widget-test-edit++or:++    cabal run -f examples contact-manager -- example/contacts++In order to generate the API documentation, you need+[haddock](http://www.haskell.org/haddock). The command is then:++    cabal haddock++## Building and installing the `hscurses` library (old)++Requirements:++- GNU `m4`+- GHC >= 6.8+- `ncurses` with headers++Build and installation steps:++    runhaskell Setup.hs configure+    runhaskell Setup.hs build+    runhaskell Setup.hs install++In order to generate the API documentation, you need haddock+(http://www.haskell.org/haddock). The command is then:++    runhaskell Setup.hs haddock++## Using the `hscurses` library++Just add the package `hscurses` to your dependencies and make sure you have an+installation of `ncurses`. At the moment, user documentation is only available+through Haddock API documentation.++For basic usage check out the code in the [example](./example) and+[./tests](tests) directories.++### Debug logging++`hscurses` ships with very basic logging functionality. The default behavior is+not to compile with support for this, but you can use the Cabal flag `debug` to+manually enable this. Logs will be written to `.hscurses.logs` in the directory+you're running the `cabal` command from. Example:++    cabal run -f examples -f debug contact-manager -- example/contacts++## Windows support++Windows support relies on [pdcurses](http://pdcurses.sourceforge.net/), which is+already [packaged for MinGW](http://sourceforge.net/projects/mingw/files/MinGW/PDCurses/PDCurses-3.4-1/)+and can be installed with `mingw-get`.++Thanks to José Romildo Malaquias (malaquias@gmail.com) for porting `hscurses` to+the Windows platform!++There is also a detailed installation instruction by+[Ilan Godik](https://medium.com/@NightRa):+https://medium.com/@NightRa/installing-hscurses-on-windows-830532d3268a++## Development notes++- We used [cabal-fmt](https://github.com/phadej/cabal-fmt) the cabal file(s).+- To consolidate the code style a tiny bit, the Haskell code formatter+  [fourmolu](https://fourmolu.github.io/) was used; see +  [its configuration](./fourmolu.yaml) for details.++## Copyright++    John Meacham <john @ repetae . net>, 2002-2004.+    Tuomo Valkonen <tuomov @ iki.fi>, 2004.+    Don Stewart <http://www.cse.unsw.edu.au/~dons>, 2004.+    Stefan Wehr <http://www.stefanwehr.de>, 2004-2011.++## History++John Meacham started the binding for his chat client+[Ginsu](http://repetae.net/john/computer/ginsu/).++Tuomo Valkonen integrated to code into [Riot](http://modeemi.fi/~tuomov/riot/),+with minor modifications.++Don Stewart improved the code for the [Yi editor](http://www.cse.unsw.edu.au/~dons/yi.html).++Stefan Wehr turned the binding into a standalone library. He also added some+basic widgets.
Setup.hs view
@@ -1,2 +1,3 @@ import Distribution.Simple+ main = defaultMainWithHooks autoconfUserHooks
UI/HSCurses/CWString.hsc view
@@ -17,8 +17,7 @@   module UI.HSCurses.CWString (--    -- * utf8 versions+    -- utf8 versions     withUTF8String,     withUTF8StringLen,     newUTF8String,@@ -26,7 +25,7 @@     peekUTF8String,     peekUTF8StringLen, -    -- * WChar stuff+        -- WChar stuff #ifdef HAVE_WCHAR_H     withCWString,     withCWStringLen,@@ -39,19 +38,18 @@     CWString,     CWStringLen, #endif-    -- * Locale versions++    -- Locale versions     withLCString,     withLCStringLen,     newLCString,     newLCStringLen,     peekLCStringLen,     peekLCString,---  charIsRepresentable--    ) where+) where -import Data.Char            ( ord, chr )-import Data.Bits            ( Bits((.|.), (.&.), shift) )+import Data.Bits (Bits (shift, (.&.), (.|.)))+import Data.Char (chr, ord) import Foreign.C.String  #if __GLASGOW_HASKELL__ < 603@@ -87,54 +85,59 @@ -- support functions wNUL :: CWChar wNUL = 0+ #ifndef __GLASGOW_HASKELL__+ pairLength :: String -> CString -> CStringLen-pairLength  = flip (,) . length+pairLength = flip (,) . length  cwCharsToChars :: [CWChar] -> [Char]-cwCharsToChars xs  = map castCWCharToChar xs+cwCharsToChars xs = map castCWCharToChar xs charsToCWChars :: [Char] -> [CWChar]-charsToCWChars xs  = map castCharToCWChar xs+charsToCWChars xs = map castCharToCWChar xs  #endif+ -- __STDC_ISO_10646__  castCWCharToChar :: CWChar -> Char-castCWCharToChar ch = chr (fromIntegral ch )+castCWCharToChar ch = chr (fromIntegral ch)  castCharToCWChar :: Char -> CWChar castCharToCWChar ch = fromIntegral (ord ch) --- exported functions-peekCWString    :: CWString -> IO String+peekCWString :: CWString -> IO String #ifndef __GLASGOW_HASKELL__-peekCString cp  = do cs <- peekArray0 wNUL cp; return (cwCharsToChars cs)+peekCString cp = do cs <- peekArray0 wNUL cp; return (cwCharsToChars cs) #else peekCWString cp = loop 0   where     loop i = do         val <- peekElemOff cp i-        if val == wNUL then return [] else do-            rest <- loop (i+1)-            return (castCWCharToChar val : rest)+        if val == wNUL+            then return []+            else do+                rest <- loop (i + 1)+                return (castCWCharToChar val : rest) #endif -peekCWStringLen           :: CWStringLen -> IO String+peekCWStringLen :: CWStringLen -> IO String #ifndef __GLASGOW_HASKELL__-peekCWStringLen (cp, len)  = do cs <- peekArray len cp; return (cwCharsToChars cs)+peekCWStringLen (cp, len) = do cs <- peekArray len cp; return (cwCharsToChars cs) #else peekCWStringLen (cp, len) = loop 0   where-    loop i | i == len  = return []-           | otherwise = do-                val <- peekElemOff cp i-                rest <- loop (i+1)-                return (castCWCharToChar val : rest)+    loop i+        | i == len = return []+        | otherwise = do+            val <- peekElemOff cp i+            rest <- loop (i + 1)+            return (castCWCharToChar val : rest) #endif  newCWString :: String -> IO CWString #ifndef __GLASGOW_HASKELL__-newCWString  = newArray0 wNUL . charsToCWChars+newCWString = newArray0 wNUL . charsToCWChars #else newCWString str = do   ptr <- mallocArray0 (length str)@@ -145,10 +148,12 @@   return ptr #endif -newCWStringLen     :: String -> IO CWStringLen+newCWStringLen :: String -> IO CWStringLen #ifndef __GLASGOW_HASKELL__-newCWStringLen str  = do a <- newArray (charsToCWChars str)-                        return (pairLength str a)+newCWStringLen str = do+    a <- newArray (charsToCWChars str)+    return+    (pairLength str a) #else newCWStringLen str = do   ptr <- mallocArray0 len@@ -163,7 +168,7 @@  withCWString :: String -> (CWString -> IO a) -> IO a #ifndef __GLASGOW_HASKELL__-withCWString  = withArray0 wNUL . charsToCWChars+withCWString = withArray0 wNUL . charsToCWChars #else withCWString str f =   allocaArray0 (length str) $ \ptr ->@@ -175,9 +180,9 @@       f ptr #endif -withCWStringLen         :: String -> (CWStringLen -> IO a) -> IO a+withCWStringLen :: String -> (CWStringLen -> IO a) -> IO a #ifndef __GLASGOW_HASKELL__-withCWStringLen str act  = withArray (charsToCWChars str) $ act . pairLength str+withCWStringLen str act = withArray (charsToCWChars str) $ act . pairLength str #else withCWStringLen str f =   allocaArray len $ \ptr ->@@ -197,72 +202,69 @@ wcharIsUnicode = False #endif -newtype MBState = MBState { _mbstate :: (Ptr MBState)}+newtype MBState = MBState {_mbstate :: (Ptr MBState)}  withMBState :: (MBState -> IO a) -> IO a-withMBState act = allocaBytes (#const sizeof(mbstate_t)) (\mb -> c_memset mb 0 (#const sizeof(mbstate_t)) >> act (MBState mb))+withMBState act = allocaBytes (# const sizeof (mbstate_t)) (\mb -> c_memset mb 0 (# const sizeof (mbstate_t)) >> act (MBState mb))  clearMBState :: MBState -> IO ()-clearMBState (MBState mb) = c_memset mb 0 (#const sizeof(mbstate_t)) >> return ()+clearMBState (MBState mb) = c_memset mb 0 (# const sizeof (mbstate_t)) >> return ()  wcsrtombs :: CWString -> (CString, CSize) -> IO CSize-wcsrtombs wcs (cs,len) =-    alloca (\p ->-        poke p wcs >> withMBState (\mb ->-            wcsrtombs' p cs len mb))-    where-        wcsrtombs'  p cs' len' mb = do-            x <- c_wcsrtombs cs p len' mb-            case x of-                -1 -> do-                    sp <- peek p-                    poke sp ((fi (ord '?'))::CWChar)-                    poke p wcs-                    clearMBState mb-                    wcsrtombs' p cs' len' mb-                e | e >= 0 && e <= len' -> do-                    let ep = advancePtr cs' (fi e)-                    poke ep (fi (0::Int))-                    return x-                e -> error $ "HSCurses.CWString.wcsrtombs: impossible case: "++show e+wcsrtombs wcs (cs, len) =+    alloca+        ( \p ->+            poke p wcs+                >> withMBState+                    ( \mb ->+                        wcsrtombs' p cs len mb+                    )+        )+  where+    wcsrtombs' p cs' len' mb = do+        x <- c_wcsrtombs cs p len' mb+        case x of+            -1 -> do+                sp <- peek p+                poke sp ((fi (ord '?')) :: CWChar)+                poke p wcs+                clearMBState mb+                wcsrtombs' p cs' len' mb+            e | e >= 0 && e <= len' -> do+                let ep = advancePtr cs' (fi e)+                poke ep (fi (0 :: Int))+                return x+            e -> error $ "HSCurses.CWString.wcsrtombs: impossible case: " ++ show e  foreign import ccall unsafe hs_get_mb_cur_max :: IO Int  mb_cur_max :: Int mb_cur_max = unsafePerformIO hs_get_mb_cur_max -{--charIsRepresentable :: Char -> IO Bool-charIsRepresentable ch = fmap (/= -1) $ allocaBytes mb_cur_max (\cs -> c_wctomb cs (fi $ ord ch))--}--{--foreign import ccall unsafe "stdlib.h wctomb" c_wctomb :: CString -> CWChar -> IO CInt--}- foreign import ccall unsafe "stdlib.h wcsrtombs"-        c_wcsrtombs :: CString -> (Ptr (Ptr CWChar)) -> CSize -> MBState -> IO CSize+    c_wcsrtombs :: CString -> (Ptr (Ptr CWChar)) -> CSize -> MBState -> IO CSize  foreign import ccall unsafe "string.h memset"-        c_memset :: Ptr a -> CInt -> CSize -> IO (Ptr a)+    c_memset :: Ptr a -> CInt -> CSize -> IO (Ptr a)  foreign import ccall unsafe "stdlib.h mbstowcs"-        c_mbstowcs :: CWString -> CString -> CSize -> IO CSize--mbstowcs :: CWString-         -> Foreign.C.String.CString-         -> Foreign.C.Types.CSize -> IO Foreign.C.Types.CSize+    c_mbstowcs :: CWString -> CString -> CSize -> IO CSize +mbstowcs ::+    CWString ->+    Foreign.C.String.CString ->+    Foreign.C.Types.CSize ->+    IO Foreign.C.Types.CSize mbstowcs a b s = throwIf (== -1) (const "mbstowcs") $ c_mbstowcs a b s -peekLCString    :: CString -> IO String-peekLCString cp  = do+peekLCString :: CString -> IO String+peekLCString cp = do     sz <- mbstowcs nullPtr cp 0     allocaArray (fi $ sz + 1) (\wcp -> mbstowcs wcp cp (sz + 1) >> peekCWString wcp)  -- TODO fix for embeded NULs-peekLCStringLen           :: CStringLen -> IO String-peekLCStringLen (cp, len)  =  allocaBytes (len + 1) $ \ncp -> do+peekLCStringLen :: CStringLen -> IO String+peekLCStringLen (cp, len) = allocaBytes (len + 1) $ \ncp -> do     copyBytes ncp cp len     pokeElemOff ncp len 0     peekLCString ncp@@ -273,45 +275,40 @@         cs <- mallocArray0 alen         wcsrtombs wcs (cs, fi alen)         return cs--    where alen = mb_cur_max * length s+  where+    alen = mb_cur_max * length s -newLCStringLen     :: String -> IO CStringLen-newLCStringLen str  = newLCString str >>= \cs -> return (pairLength1 str cs)+newLCStringLen :: String -> IO CStringLen+newLCStringLen str = newLCString str >>= \cs -> return (pairLength1 str cs)  withLCString :: String -> (CString -> IO a) -> IO a withLCString s a =     withCWString s $ \wcs ->         allocaArray0 alen $ \cs ->-            wcsrtombs wcs (cs,fi alen) >> a cs--    where alen = mb_cur_max * length s+            wcsrtombs wcs (cs, fi alen) >> a cs+  where+    alen = mb_cur_max * length s  withLCStringLen :: String -> (CStringLen -> IO a) -> IO a withLCStringLen s a =     withCWString s $ \wcs ->         allocaArray0 alen $ \cs -> do-            sz <- wcsrtombs wcs (cs,fi alen)-            a (cs,fi sz)--    where alen = mb_cur_max * length s+            sz <- wcsrtombs wcs (cs, fi alen)+            a (cs, fi sz)+  where+    alen = mb_cur_max * length s  pairLength1 :: String -> CString -> CStringLen-pairLength1  = flip (,) . length+pairLength1 = flip (,) . length  #else -- ----------------------------------------------------------- -- no CF_WCHAR_SUPPORT (OpenBSD) -{--charIsRepresentable :: Char -> IO Bool-charIsRepresentable ch = return $ isLatin1 ch--}- withLCString :: String -> (Foreign.C.String.CString -> IO a) -> IO a withLCString = withCString -withLCStringLen  :: String -> (Foreign.C.String.CStringLen -> IO a) -> IO a+withLCStringLen :: String -> (Foreign.C.String.CStringLen -> IO a) -> IO a withLCStringLen = withCStringLen  newLCString :: String -> IO Foreign.C.String.CString@@ -356,31 +353,42 @@  toUTF :: String -> String toUTF [] = []-toUTF (x:xs) | ord x<=0x007F = x:toUTF xs-             | ord x<=0x07FF = chr (0xC0 .|. ((ord x `shift` (-6)) .&. 0x1F)):-                               chr (0x80 .|. (ord x .&. 0x3F)):-                               toUTF xs-             | otherwise     = chr (0xE0 .|. ((ord x `shift` (-12)) .&. 0x0F)):-                               chr (0x80 .|. ((ord x `shift` (-6)) .&. 0x3F)):-                               chr (0x80 .|. (ord x .&. 0x3F)):-                               toUTF xs+toUTF (x : xs)+    | ord x <= 0x007F = x : toUTF xs+    | ord x <= 0x07FF =+        chr (0xC0 .|. ((ord x `shift` (-6)) .&. 0x1F))+            : chr (0x80 .|. (ord x .&. 0x3F))+            : toUTF xs+    | otherwise =+        chr (0xE0 .|. ((ord x `shift` (-12)) .&. 0x0F))+            : chr (0x80 .|. ((ord x `shift` (-6)) .&. 0x3F))+            : chr (0x80 .|. (ord x .&. 0x3F))+            : toUTF xs  fromUTF :: String -> String fromUTF [] = []-fromUTF (al@(x:xs)) | ord x<=0x7F = x:fromUTF xs-                     | ord x<=0xBF = err-                     | ord x<=0xDF = twoBytes al-                     | ord x<=0xEF = threeBytes al-                     | otherwise   = err+fromUTF (al@(x : xs))+    | ord x <= 0x7F = x : fromUTF xs+    | ord x <= 0xBF = err+    | ord x <= 0xDF = twoBytes al+    | ord x <= 0xEF = threeBytes al+    | otherwise = err   where-    twoBytes (x1:x2:xs') = chr (((ord x1 .&. 0x1F) `shift` 6) .|.-                               (ord x2 .&. 0x3F)):fromUTF xs'+    twoBytes (x1 : x2 : xs') =+        chr+            ( ((ord x1 .&. 0x1F) `shift` 6)+                .|. (ord x2 .&. 0x3F)+            )+            : fromUTF xs'     twoBytes _ = error "fromUTF: illegal two byte sequence" -    threeBytes (x1:x2:x3:xs') = chr (((ord x1 .&. 0x0F) `shift` 12) .|.-                                    ((ord x2 .&. 0x3F) `shift` 6) .|.-                                    (ord x3 .&. 0x3F)):fromUTF xs'+    threeBytes (x1 : x2 : x3 : xs') =+        chr+            ( ((ord x1 .&. 0x0F) `shift` 12)+                .|. ((ord x2 .&. 0x3F) `shift` 6)+                .|. (ord x3 .&. 0x3F)+            )+            : fromUTF xs'     threeBytes _ = error "fromUTF: illegal three byte sequence"      err = error "fromUTF: illegal UTF-8 character"-
UI/HSCurses/Curses.hsc view
@@ -1,1552 +1,1722 @@-{-# LANGUAGE ForeignFunctionInterface, GeneralizedNewtypeDeriving #-}--- glaexts needed for newtype deriving---- Copyright (c) 2002-2004 John Meacham (john at repetae dot net)--- Copyright (c) 2004 Don Stewart - http://www.cse.unsw.edu.au/~dons--- Copyright (c) 2005-2011 Stefan Wehr - http://www.stefanwehr.de------ This library is free software; you can redistribute it and/or--- modify it under the terms of the GNU Lesser General Public--- License as published by the Free Software Foundation; either--- version 2.1 of the License, or (at your option) any later version.------ This library is distributed in the hope that it will be useful,--- but WITHOUT ANY WARRANTY; without even the implied warranty of--- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU--- Lesser General Public License for more details.------ You should have received a copy of the GNU Lesser General Public--- License along with this library; if not, write to the Free Software--- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA--------- | Binding to the [wn]curses library. From the ncurses man page:------ >      The curses library routines give the user a terminal-inde---- >      pendent method of updating character screens with  reason---- >      able  optimization.------ Sections of the quoted documentation are from the OpenBSD man pages, which--- are distributed under a BSD license.------ A useful reference is:---        /Writing Programs with NCURSES/, by Eric S. Raymond and Zeyd---        M. Ben-Halim, <http://dickey.his.com/ncurses/>------ N.B attrs don't work with Irix curses.h. This should be fixed.-----module UI.HSCurses.Curses (--    -- * Basic Functions-    stdScr,             -- :: Window-    initScr,            -- :: IO Window-    initCurses,         -- :: IO ()-    resetParams,-    endWin,             -- :: IO ()-    scrSize,            -- :: IO (Int, Int)--    -- * Windows and Pads-    Window,             -- data Window deriving Eq-    Border(..),         -- data Border-    touchWin,-    newPad, pRefresh, delWin, newWin, wRefresh, wnoutRefresh, wBorder, defaultBorder,---    -- * Refresh Routines-    refresh,            -- :: IO ()-    update,-    resizeTerminal,-    timeout,            -- :: Int -> IO ()-    noqiflush,           -- :: IO ()--    -- * Navigation-    move,               -- :: Int -> Int -> IO ()-    getYX,--    -- * Input-    getCh, getch, decodeKey, ungetCh, keyResizeCode,--    -- * Input Options-    cBreak,             -- :: Bool -> IO ()-    raw,                -- :: Bool -> IO ()-    echo,               -- :: Bool -> IO ()-    intrFlush,          -- :: Bool -> IO ()-    keypad,             -- :: Window -> Bool -> IO ()-    noDelay,            -- :: Window -> Bool -> IO ()--    -- * Output-    wAddStr,       -- :: Window -> String -> IO ()-    addLn,         -- :: IO ()-    mvWAddStr,-    mvAddCh,       -- :: Int -> Int -> ChType -> IO ()-    wMove,-    bkgrndSet,     -- :: Attr -> Pair -> IO ()-    erase,         -- :: IO ()-    wclear,        -- :: Window -> IO ()-    werase,-    clrToEol,      -- :: IO ()-    wClrToEol,-    beep,-    waddch,-    winsch,-    waddchnstr,    -- :: Window -> CString -> CInt -> IO CInt--    -- * Output Options-    clearOk,-    leaveOk,-    nl,                 -- :: Bool -> IO ()--    -- * Cursor Routines-    CursorVisibility(..), cursSet,--    -- * Color Support-    hasColors,      -- :: IO Bool-    startColor,     -- :: IO ()-    useDefaultColors,   -- :: IO ()-    Pair(..),       -- newtype Pair = Pair Int deriving (Eq, Ord, Ix)-    colorPairs,     -- :: IO Int-    Color(..),      -- newtype Color = Color Int deriving (Eq, Ord, Ix)-    colors,         -- :: IO Int-    color,          -- :: String -> Maybe Color---    black, red, green, yellow, blue, magenta, cyan, white, -- :: Color-    initPair,       -- :: Pair -> Color -> Color -> IO ()-    pairContent,    -- :: Pair -> IO (Color, Color)-    canChangeColor, -- :: IO Bool-    initColor,      -- :: Color -> (Int, Int, Int) -> IO ()-    colorContent,   -- :: Color -> IO (Int, Int, Int)-    defaultBackground, defaultForeground,--    -- * Attributes-    attrPlus,-    Attr,-    attr0, -- :: Attr--    isAltCharset, isBlink, isBold, isDim, isHorizontal, isInvis,-    isLeft, isLow, isProtect, isReverse, isRight, isStandout, isTop,-    isUnderline, isVertical,-        -- :: Attr -> Bool--    setAltCharset, setBlink, setBold, setDim, setHorizontal, setInvis,-    setLeft, setLow, setProtect, setReverse, setRight, setStandout,-    setTop, setUnderline, setVertical,-        -- :: Attr -> Bool -> Attr--    attrSet, -- :: Attr -> Pair -> IO ()-    attrOn, attrOff,--    standout,standend,-    attrDim, attrBold,-    attrDimOn, attrDimOff,-    attrBoldOn, attrBoldOff,-    wAttrOn,-    wAttrOff,-    wAttrSet, wAttrGet,--    -- * Mouse Routines-    withMouseEventMask,-    ButtonEvent(..),-    MouseEvent(..),--    -- * Keys-    Key(..),-    cERR,-    cKEY_UP,-    cKEY_DOWN,-    cKEY_LEFT,-    cKEY_RIGHT,-    cTRUE,---    cACS_BLOCK,--    -- * Lines-    vline,-    ulCorner, llCorner, urCorner, lrCorner, rTee, lTee, bTee, tTee, hLine,-    vLine, plus, s1, s9, diamond, ckBoard, degree, plMinus, bullet,-    lArrow, rArrow, dArrow, uArrow, board, lantern, block,-    s3, s7, lEqual, gEqual, pi, nEqual, sterling,--    -- * Signals-    cursesSigWinch,--    -- * Misc-    cursesTest,-    throwIfErr, throwIfErr_,-    errI,       -- :: IO CInt -> IO ()-    flushinp,-    recognize,-    ChType,-    NBool--  ) where--#include <HSCurses.h>--#if HAVE_SIGNAL_H-# include <signal.h>-#endif--import UI.HSCurses.CWString       ( withLCStringLen )-import UI.HSCurses.Logging--import Prelude hiding           ( pi )-import Data.Char-import Data.List-import Data.Ix                  ( Ix )--import System.IO.Unsafe ( unsafePerformIO )--import Control.Monad ( when, liftM, void )-import Control.Monad.Trans-import Control.Concurrent--#if !MIN_VERSION_base(4,7,0)-import Foreign hiding ( unsafePerformIO, void )-#else-import Foreign hiding ( void )-#endif-import Foreign.C.String-import Foreign.C.Types-import Foreign.C.Error--#ifndef mingw32_HOST_OS-import System.Posix.Signals-#endif--#if __GLASGOW_HASKELL__ < 603-import Data.Bits-#endif--------------------------------------------------------------------------------- | @initCurses fn@ does all initialization necessary for a Curses---   application.----initCurses :: IO ()-initCurses = do-    initScr-    b <- hasColors-    when b $ startColor >> useDefaultColors--resetParams :: IO ()-resetParams = do-    raw True    -- raw mode please-    echo False-    nl False-    intrFlush True-    leaveOk False-    keypad stdScr True-#ifdef NCURSES_EXT_FUNCS-    defineKey (#const KEY_UP) "\x1b[1;2A"-    defineKey (#const KEY_DOWN) "\x1b[1;2B"-    defineKey (#const KEY_SLEFT) "\x1b[1;2D"-    defineKey (#const KEY_SRIGHT) "\x1b[1;2C"-    defineKey (#const KEY_B2) "\x1b[E"  -- xterm seems to emit B2, not BEG-    defineKey (#const KEY_END) "\x1b[F"-    defineKey (#const KEY_END) "\x1b[4~"-    defineKey (#const KEY_HOME) "\x1b[H"-    defineKey (#const KEY_HOME) "\x1b[1~"-#endif----------------------------------------------------------------------------fi :: (Integral a, Num b) => a -> b-fi = fromIntegral--throwIfErr :: (Eq a, Show a, Num a) => String -> IO a -> IO a---throwIfErr name act = do---    res <- act---    if res == (cERR)---        then ioError (userError ("Curses: "++name++" failed"))---        else return res-throwIfErr s = throwIf (== (#const ERR)) (\a -> "Curses[" ++ show a ++ "]:"  ++ s)--throwIfErr_ :: (Eq a, Show a, Num a) => String -> IO a -> IO ()-throwIfErr_ name act = void $ throwIfErr name act--errI :: IO CInt -> IO ()-errI f = do r <- f-            if r == cERR then do _ <- endwin-                                 error "curses returned an error"-             else return ()----------------------------------------------------------------------------type WindowTag = ()-type Window = Ptr WindowTag-type ChType = #type chtype-type NBool = #type bool--{-# NOINLINE stdScr #-}------ | The standard screen----stdScr :: Window-stdScr = unsafePerformIO (peek stdscr)-foreign import ccall "static HSCurses.h &stdscr"-    stdscr :: Ptr Window------- | initscr is normally the first curses routine to call when--- initializing a program. curs_initscr(3):------ >     To initialize the routines, the routine initscr or newterm--- >     must be called before any of the other routines that  deal--- >     with  windows  and  screens  are used.------ >     The initscr code determines the terminal type and initial---- >     izes all curses data structures.  initscr also causes  the--- >     first  call  to  refresh  to  clear the screen.  If errors--- >     occur, initscr writes  an  appropriate  error  message  to--- >     standard error and exits; otherwise, a pointer is returned--- >     to stdscr.----initScr :: IO Window-initScr = throwIfNull "initscr" initscr-foreign import ccall unsafe "HSCurses.h initscr" initscr :: IO Window------- | > The cbreak routine---   > disables line buffering and erase/kill  character-process----   > ing  (interrupt  and  flow  control  characters  are unaf----   > fected), making characters typed by the  user  immediately---   > available  to  the  program.  The nocbreak routine returns---   > the terminal to normal (cooked) mode.----cBreak :: Bool -> IO ()-cBreak True  = throwIfErr_ "cbreak"   cbreak-cBreak False = throwIfErr_ "nocbreak" nocbreak--foreign import ccall unsafe "HSCurses.h cbreak"     cbreak :: IO CInt-foreign import ccall unsafe "HSCurses.h nocbreak" nocbreak :: IO CInt------- |>    The  raw and noraw routines place the terminal into or out--- >     of raw mode.  Raw mode is similar to cbreak mode, in  that--- >     characters  typed  are  immediately  passed through to the--- >     user program.  The differences are that in raw  mode,  the--- >     interrupt,  quit, suspend, and flow control characters are--- >     all passed through uninterpreted, instead of generating  a--- >     signal.   The  behavior  of the BREAK key depends on other--- >     bits in the tty driver that are not set by curses.----raw :: Bool -> IO ()-raw False = throwIfErr_ "noraw" noraw-raw True  = throwIfErr_ "raw"   raw_c--foreign import ccall unsafe "HSCurses.h noraw" noraw :: IO CInt-foreign import ccall unsafe "HSCurses.h raw"   raw_c :: IO CInt------- |>      The  echo  and  noecho routines control whether characters--- >       typed by the user are echoed by getch as they  are  typed.--- >       Echoing  by  the  tty  driver is always disabled, but ini---- >       tially getch is in echo  mode,  so  characters  typed  are--- >       echoed.  Authors of most interactive programs prefer to do--- >       their own echoing in a controlled area of the  screen,  or--- >       not  to  echo  at  all, so they disable echoing by calling--- >       noecho.  [See curs_getch(3) for a discussion of how  these--- >       routines interact with cbreak and nocbreak.]--- >----echo :: Bool -> IO ()-echo False = throwIfErr_ "noecho" noecho-echo True  = throwIfErr_ "echo"   echo_c--foreign import ccall unsafe "HSCurses.h noecho" noecho :: IO CInt-foreign import ccall unsafe "HSCurses.h echo" echo_c :: IO CInt------- |>       The  nl  and  nonl routines control whether the underlying--- >        display device translates the return key into  newline  on--- >        input,  and  whether it translates newline into return and--- >        line-feed on output (in either case, the call  addch('\n')--- >        does the equivalent of return and line feed on the virtual--- >        screen).  Initially, these translations do occur.  If  you--- >        disable  them using nonl, curses will be able to make bet---- >        ter use of the line-feed capability, resulting  in  faster--- >        cursor  motion.   Also, curses will then be able to detect--- >        the return key.--- >-nl :: Bool -> IO ()-nl True  = throwIfErr_ "nl"   nl_c-nl False = throwIfErr_ "nonl" nonl--foreign import ccall unsafe "HSCurses.h nl" nl_c :: IO CInt-foreign import ccall unsafe "HSCurses.h nonl" nonl :: IO CInt---- |>       If  the intrflush option is enabled, (bf is TRUE), when an--- >        interrupt key  is  pressed  on  the  keyboard  (interrupt,--- >        break,  quit)  all  output in the tty driver queue will be--- >        flushed, giving the  effect  of  faster  response  to  the--- >        interrupt,  but  causing  curses to have the wrong idea of--- >        what is on the  screen.   Disabling  (bf  is  FALSE),  the--- >        option  prevents the flush.--- >-intrFlush :: Bool -> IO ()-intrFlush bf =-    throwIfErr_ "intrflush" $ intrflush stdScr (if bf then 1 else 0)-foreign import ccall unsafe "HSCurses.h intrflush" intrflush :: Window -> (#type bool) -> IO CInt------- | Enable the keypad of the user's terminal.----keypad :: Window -> Bool -> IO ()-keypad win bf = throwIfErr_ "keypad" $ keypad_c win (if bf then 1 else 0)-foreign import ccall unsafe "HSCurses.h keypad"-    keypad_c :: Window -> (#type bool) -> IO CInt--noDelay :: Window -> Bool -> IO ()-noDelay win bf =-    throwIfErr_ "nodelay" $ nodelay win (if bf then 1 else 0)--foreign import ccall unsafe "HSCurses.h nodelay"-    nodelay :: Window -> (#type bool) -> IO CInt------- |   Normally, the hardware cursor is left at the  location  of---     the  window  cursor  being  refreshed.  The leaveok option---     allows the cursor to be left wherever the  update  happens---     to leave it.  It is useful for applications where the cur----     sor is not used, since it  reduces  the  need  for  cursor---     motions.   If  possible, the cursor is made invisible when---     this option is enabled.----leaveOk  :: Bool -> IO CInt-leaveOk True  = leaveok_c stdScr 1-leaveOk False = leaveok_c stdScr 0--foreign import ccall unsafe "HSCurses.h leaveok"-    leaveok_c :: Window -> (#type bool) -> IO CInt--clearOk :: Bool -> IO CInt-clearOk True  = clearok_c stdScr 1-clearOk False = clearok_c stdScr 0--foreign import ccall unsafe "HSCurses.h clearok"-    clearok_c :: Window -> (#type bool) -> IO CInt----------------------------------------------------------------------------foreign import ccall unsafe "HSCurses.h use_default_colors"-    useDefaultColors :: IO ()--defaultBackground, defaultForeground :: Color-defaultBackground = Color (-1)-defaultForeground = Color (-1)----------------------------------------------------------------------------#ifdef NCURSES_EXT_FUNCS-defineKey :: CInt -> String -> IO ()-defineKey k s =  withCString s (\s' -> define_key s' k) >> return ()--foreign import ccall unsafe "HSCurses.h define_key"-    define_key :: Ptr CChar -> CInt -> IO ()-#endif------- | >  The program must call endwin for each terminal being used before---   >  exiting from curses.----endWin :: IO ()-endWin = throwIfErr_ "endwin" endwin-foreign import ccall unsafe "HSCurses.h endwin" endwin :: IO CInt--------------------------------------------------------------------------------- | get the dimensions of the screen (lines,cols)----scrSize :: IO (Int, Int)--- Note, per the documentation:---    http://invisible-island.net/ncurses/ncurses-intro.html#caution--- It is not recommended to peek at the LINES and COLS global variables.  This code--- was previously doing exactly that, but now it is fixed to use getmaxyx.---   -Ryan Newton [2013.03.31]  -scrSize = do-    yfp <- mallocForeignPtr -    xfp <- mallocForeignPtr -    withForeignPtr yfp $ \yp -> -     withForeignPtr xfp $ \xp -> do-       getMaxYX stdScr yp xp-       y <- peek yp-       x <- peek xp-       return (fromIntegral y, fromIntegral x)-    -foreign import ccall "HSCurses.h getmaxyx_fun" getMaxYX-    :: Window -> Ptr CInt -> Ptr CInt -> IO ()------- | refresh curses windows and lines. curs_refresh(3)----refresh :: IO ()-refresh = throwIfErr_ "refresh" refresh_c--foreign import ccall unsafe "HSCurses.h refresh"-    refresh_c :: IO CInt------- | wRefresh refreshes the specified window, copying the data--- | from the virtual screen to the physical screen.----wRefresh :: Window -> IO ()-wRefresh w = throwIfErr_ "wrefresh" $ wrefresh_c w--foreign import ccall unsafe "HSCurses.h wrefresh"-    wrefresh_c :: Window -> IO CInt---- | Stage an update to a window, but don't actually do the refresh until update is--- called.  This allows multiple windows to be updated together more smoothly.----wnoutRefresh :: Window -> IO ()-wnoutRefresh w = throwIfErr_ "wnoutrefresh" $ wnoutrefresh_c w--foreign import ccall safe "HSCurses.h wnoutrefresh"-  wnoutrefresh_c :: Window -> IO CInt------- | Do an actual update. Used after endWin on linux to restore the terminal----update :: IO ()-update = throwIfErr_ "update" update_c--foreign import ccall unsafe "HSCurses.h doupdate" update_c :: IO CInt--foreign import ccall unsafe "static curses.h timeout" timeout_c :: CInt -> IO ()---- | Set a delay in milliseconds.-timeout :: Int -> IO ()-timeout = timeout_c . fromIntegral----------------------------------------------------------------------------hasColors :: IO Bool-hasColors = liftM (/= 0) has_colors-foreign import ccall unsafe "HSCurses.h has_colors" has_colors :: IO (#type bool)------- | Initialise the color settings, also sets the screen to the--- default colors (white on black)----startColor :: IO ()-startColor = throwIfErr_ "start_color" start_color-foreign import ccall unsafe start_color :: IO CInt--newtype Pair = Pair Int deriving (Eq, Ord, Ix, Show)------- | colorPairs defines the maximum number of color-pairs the terminal--- can support).----colorPairs :: IO Int-colorPairs = fmap fromIntegral $ peek colorPairsPtr--foreign import ccall "HSCurses.h &COLOR_PAIRS"-        colorPairsPtr :: Ptr CInt--newtype Color = Color Int deriving (Eq, Ord, Ix)--colors :: IO Int-colors = liftM fromIntegral $ peek colorsPtr--foreign import ccall "HSCurses.h &COLORS" colorsPtr :: Ptr CInt----black, red, green, yellow, blue, magenta, cyan, white :: Color--color :: String -> Maybe Color-color "default"  = Just $ Color (-1)-color "black"    = Just $ Color (#const COLOR_BLACK)-color "red"      = Just $ Color (#const COLOR_RED)-color "green"    = Just $ Color (#const COLOR_GREEN)-color "yellow"   = Just $ Color (#const COLOR_YELLOW)-color "blue"     = Just $ Color (#const COLOR_BLUE)-color "magenta"  = Just $ Color (#const COLOR_MAGENTA)-color "cyan"     = Just $ Color (#const COLOR_CYAN)-color "white"    = Just $ Color (#const COLOR_WHITE)-color _ =  Nothing-------- |   curses support color attributes  on  terminals  with  that---     capability.   To  use  these  routines start_color must be---     called, usually right after initscr.   Colors  are  always---     used  in pairs (referred to as color-pairs).  A color-pair---     consists of a foreground  color  (for  characters)  and  a---     background color (for the blank field on which the charac----     ters are displayed).  A programmer  initializes  a  color----     pair  with  the routine init_pair.  After it has been ini----     tialized, COLOR_PAIR(n), a macro  defined  in  <curses.h>,---     can be used as a new video attribute.------     If  a  terminal  is capable of redefining colors, the pro----     grammer can use the routine init_color to change the defi----     nition   of   a   color.------     The init_pair routine changes the definition of  a  color----     pair.   It takes three arguments: the number of the color----     pair to be changed, the foreground color number,  and  the---     background color number.  For portable applications:------     -    The value of the first argument must be between 1 and---          COLOR_PAIRS-1.------     -    The value of the second and third arguments  must  be---          between  0  and  COLORS (the 0 color pair is wired to---          white on black and cannot be changed).-------initPair :: Pair -> Color -> Color -> IO ()-initPair (Pair p) (Color f) (Color b) =-    throwIfErr_ "init_pair" $-        init_pair (fromIntegral p) (fromIntegral f) (fromIntegral b)--foreign import ccall unsafe-    init_pair :: CShort -> CShort -> CShort -> IO CInt---pairContent :: Pair -> IO (Color, Color)-pairContent (Pair p) =-    alloca $ \fPtr ->-    alloca $ \bPtr -> do-        throwIfErr "pair_content" $ pair_content (fromIntegral p) fPtr bPtr-        f <- peek fPtr-        b <- peek bPtr-        return (Color (fromIntegral f), Color (fromIntegral b))--foreign import ccall unsafe pair_content :: CShort -> Ptr CShort -> Ptr CShort -> IO CInt--canChangeColor :: IO Bool-canChangeColor = liftM (/= 0) can_change_color--foreign import ccall unsafe can_change_color :: IO (#type bool)--initColor :: Color -> (Int, Int, Int) -> IO ()-initColor (Color c) (r, g, b) =-    throwIfErr_ "init_color" $-        init_color (fromIntegral c) (fromIntegral r) (fromIntegral g) (fromIntegral b)-foreign import ccall unsafe init_color :: CShort -> CShort -> CShort -> CShort -> IO CInt--colorContent :: Color -> IO (Int, Int, Int)-colorContent (Color c) =-    alloca $ \rPtr ->-    alloca $ \gPtr ->-    alloca $ \bPtr -> do-        throwIfErr "color_content" $ color_content (fromIntegral c) rPtr gPtr bPtr-        r <- peek rPtr-        g <- peek gPtr-        b <- peek bPtr-        return (fromIntegral r, fromIntegral g, fromIntegral b)--foreign import ccall unsafe-    color_content :: CShort -> Ptr CShort -> Ptr CShort -> Ptr CShort -> IO CInt--foreign import ccall unsafe "HSCurses.h hs_curses_color_pair" colorPair :: Pair -> ChType------------------ Attributes----------------foreign import ccall unsafe "HSCurses.h attr_set"-    attr_set :: Attr -> CShort -> Ptr a -> IO Int---- foreign import ccall unsafe "HSCurses.h attr_get" :: Attr -> CShort -> Ptr a -> IO Int--foreign import ccall unsafe "HSCurses.h wattr_set"-    wattr_set :: Window -> Attr -> CInt -> Ptr a -> IO CInt--foreign import ccall unsafe "HSCurses.h wattr_get"-    wattr_get :: Window -> Ptr Attr -> Ptr CShort -> Ptr a -> IO CInt--foreign import ccall "HSCurses.h attr_on" attr_on :: (#type attr_t) -> Ptr a -> IO Int-foreign import ccall "HSCurses.h attr_off" attr_off :: (#type attr_t) -> Ptr a -> IO Int-foreign import ccall "HSCurses.h attron" attron :: Int -> IO Int-foreign import ccall "HSCurses.h attroff" attroff :: Int -> IO Int-foreign import ccall unsafe "HSCurses.h wattron" wattron :: Window -> CInt -> IO CInt-foreign import ccall unsafe "HSCurses.h wattroff" wattroff :: Window -> CInt -> IO CInt-foreign import ccall standout :: IO Int-foreign import ccall standend :: IO Int------- |----wAttrSet :: Window -> (Attr,Pair) -> IO ()-wAttrSet w (a,(Pair p)) = throwIfErr_ "wattr_set" $-    wattr_set w a (fromIntegral p) nullPtr------- | manipulate the current attributes of the named window. see curs_attr(3)----wAttrGet :: Window -> IO (Attr,Pair)-wAttrGet w =-    alloca $ \pa ->-        alloca $ \pp -> do-            throwIfErr_ "wattr_get" $ wattr_get w pa pp nullPtr-            a <- peek pa-            p <- peek pp-            return (a,Pair $ fromIntegral p)---newtype Attr = Attr (#type attr_t) deriving (Eq,Storable,Bits, Num, Show)------- | Normal display (no highlight)----attr0 :: Attr-#ifdef WA_NORMAL-attr0 = Attr (#const WA_NORMAL)-#else-attr0 = Attr (#const A_NORMAL)-#endif--isAltCharset, isBlink, isBold, isDim, isHorizontal, isInvis, isLeft,-    isLow, isProtect, isReverse, isRight, isStandout, isTop,-    isUnderline, isVertical :: Attr -> Bool--isAltCharset = isAttr (#const WA_ALTCHARSET)-isBlink      = isAttr (#const WA_BLINK)-isBold       = isAttr (#const WA_BOLD)-isDim        = isAttr (#const WA_DIM)-isHorizontal = isAttr (#const WA_HORIZONTAL)-isInvis      = isAttr (#const WA_INVIS)-isLeft       = isAttr (#const WA_LEFT)-isLow        = isAttr (#const WA_LOW)-isProtect    = isAttr (#const WA_PROTECT)-isReverse    = isAttr (#const WA_REVERSE)-isRight      = isAttr (#const WA_RIGHT)-isStandout   = isAttr (#const WA_STANDOUT)-isTop        = isAttr (#const WA_TOP)-isUnderline  = isAttr (#const WA_UNDERLINE)-isVertical   = isAttr (#const WA_VERTICAL)--isAttr :: (#type attr_t) -> Attr -> Bool-isAttr b (Attr a) = a .&. b /= 0------- | Setting attributes----setAltCharset, setBlink, setBold, setDim, setHorizontal, setInvis,-    setLeft, setLow, setProtect, setReverse, setRight, setStandout,-    setTop, setUnderline, setVertical :: Attr -> Bool -> Attr--setAltCharset = setAttr (#const WA_ALTCHARSET)-setBlink      = setAttr (#const WA_BLINK)-setBold       = setAttr (#const WA_BOLD)-setDim        = setAttr (#const WA_DIM)-setHorizontal = setAttr (#const WA_HORIZONTAL)-setInvis      = setAttr (#const WA_INVIS)-setLeft       = setAttr (#const WA_LEFT)-setLow        = setAttr (#const WA_LOW)-setProtect    = setAttr (#const WA_PROTECT)-setReverse    = setAttr (#const WA_REVERSE)-setRight      = setAttr (#const WA_RIGHT)-setStandout   = setAttr (#const WA_STANDOUT)-setTop        = setAttr (#const WA_TOP)-setUnderline  = setAttr (#const WA_UNDERLINE)-setVertical   = setAttr (#const WA_VERTICAL)--setAttr :: (#type attr_t) -> Attr -> Bool -> Attr-setAttr b (Attr a) False = Attr (a .&. complement b)-setAttr b (Attr a) True  = Attr (a .|.            b)--attrPlus :: Attr -> Attr -> Attr-attrPlus (Attr a) (Attr b) = Attr (a .|. b)--attrSet :: Attr -> Pair -> IO ()-attrSet attr (Pair p) = throwIfErr_ "attrset" $-    attr_set attr (fromIntegral p) nullPtr--attrOn :: Attr -> IO ()-attrOn (Attr attr) = throwIfErr_ "attr_on" $-    attr_on attr nullPtr---attrOff :: Attr -> IO ()-attrOff (Attr attr) = throwIfErr_ "attr_off" $-    attr_off attr nullPtr----wAttrOn :: Window -> Int -> IO ()-wAttrOn w x = throwIfErr_ "wattron" $ wattron w (fi x)--wAttrOff :: Window -> Int -> IO ()-wAttrOff w x = throwIfErr_ "wattroff" $ wattroff w (fi x)--attrDimOn :: IO ()-attrDimOn  = throwIfErr_ "attron A_DIM" $-    attron (#const A_DIM)--attrDimOff :: IO ()-attrDimOff = throwIfErr_ "attroff A_DIM" $-    attroff (#const A_DIM)--attrBoldOn :: IO ()-attrBoldOn  = throwIfErr_ "attron A_BOLD" $-    attron (#const A_BOLD)--attrBoldOff :: IO ()-attrBoldOff = throwIfErr_ "attroff A_BOLD" $-    attroff (#const A_BOLD)---attrDim :: Int-attrDim = (#const A_DIM)-attrBold :: Int-attrBold = (#const A_BOLD)------------------------------------------------------------------------------ | Raw NCurses routine.-foreign import ccall safe-    waddch :: Window -> ChType -> IO CInt---- | Raw NCurses routine.-foreign import ccall safe-    winsch :: Window -> ChType -> IO CInt---- | Raw NCurses routine.-foreign import ccall safe-    waddchnstr :: Window -> CString -> CInt -> IO CInt--foreign import ccall safe "static curses.h mvaddch" mvaddch_c :: CInt -> CInt -> ChType -> IO ()--mvWAddStr :: Window -> Int -> Int -> String -> IO ()-mvWAddStr w y x str = wMove w y x >> wAddStr w str--mvAddCh :: Int -> Int -> ChType -> IO ()-mvAddCh l m n = mvaddch_c (fromIntegral l) (fromIntegral m) (fromIntegral n)--addLn :: IO ()-addLn = wAddStr stdScr "\n"------- | normalise the string, stripping \\r and making control chars--- printable. Called over all output(?)--{--normalise :: String -> String-normalise []        = []-normalise ('\r':cs) = normalise cs-normalise (c:cs) | isControl c   = '@' : normalise cs-                 | otherwise     = c   : normalise cs-{-# INLINE normalise #-}--}--{--normalise s = map f . filter (/= '\r') s-    where-        f c | isPrint c  = c-        f c = '@'--}----------------------------------------------------------------------------#if defined(HAVE_WCHAR_H) && (defined(HAVE_LIBNCURSESW) || defined(HAVE_LIBPDCURSESW))----wAddStr :: Window -> String -> IO ()---wAddStr w str = throwIfErr_ ("waddnwstr: " ++ show str) $ withCWStringLen (normalise str) (\(ws,len) -> waddnwstr w ws (fi len))--foreign import ccall unsafe-    waddnwstr :: Window -> CWString -> CInt -> IO CInt--wAddStr :: Window -> String -> IO ()-wAddStr win str = do-    let-        convStr f = case f [] of-            [] -> return ()-            s  -> throwIfErr_ "waddnstr" $-                withCWStringLen  (s) (\(ws,len) ->  (waddnwstr win ws (fi len)))-        loop []        acc = convStr acc-        loop (ch:str') acc = recognize-            ch-            (loop str' (acc . (ch:)))-            (\ch' -> do-                convStr acc-                throwIfErr "waddch" $ waddch win ch'-                loop str' id)-    loop str id--#else------- This is heavily called, and does a lot of allocs.  We walk over all--- the string accumulating a list of characters to be drawn.------ Got it down to:------      wAddStr Yi.Curses 20.0   38.1---      wAddStr Yi.Curses 10.0   32.5------ TODO make this way less expensive. That accum sucks.--- use difference lists for O(1) append----wAddStr :: Window -> [Char] -> IO ()-wAddStr _   [] = return ()-wAddStr win s  = throwIfErr_ ("waddnstr: <" ++ s ++ ">") $-    withLCStringLen (s) (\(ws,len) -> waddnstr win ws (fi len))--foreign import ccall safe-    waddnstr :: Window -> CString -> CInt -> IO CInt--#endif--foreign import ccall safe-    vline  :: Char -> Int -> IO ()--------------------------------------------------------------------------------- what ?-----#let translate_attr attr =                              \-    "(if a .&. %lu /= 0 then %lu else 0) .|.",          \-    (unsigned long) WA_##attr, (unsigned long) A_##attr--bkgrndSet :: Attr -> Pair -> IO ()-bkgrndSet (Attr a) p = bkgdset $-    fromIntegral (ord ' ') .|.-    #translate_attr ALTCHARSET-    #translate_attr BLINK-    #translate_attr BOLD-    #translate_attr DIM-    #translate_attr INVIS-    #translate_attr PROTECT-    #translate_attr REVERSE-    #translate_attr STANDOUT-    #translate_attr UNDERLINE-    colorPair p--foreign import ccall unsafe bkgdset :: ChType -> IO ()---- | Copy blanks to every position in the screen.-erase :: IO ()-erase = throwIfErr_ "erase" $ werase_c  stdScr-foreign import ccall unsafe "werase" werase_c :: Window -> IO CInt---- | Copy blanks to every position in a window.-werase :: Window -> IO ()-werase w = throwIfErr_ "werase" $ werase_c w---- | Copy blanks to a window and set clearOk for that window.-wclear :: Window -> IO ()-wclear w = throwIfErr_ "wclear" $ wclear_c  w-foreign import ccall unsafe "wclear" wclear_c :: Window -> IO CInt--clrToEol :: IO ()-clrToEol = throwIfErr_ "clrtoeol" clrtoeol-foreign import ccall unsafe clrtoeol :: IO CInt------- | >    move the cursor associated with the window---   >    to line y and column x.  This routine does  not  move  the---   >    physical  cursor  of the terminal until refresh is called.---   >    The position specified is relative to the upper  left-hand---   >    corner of the window, which is (0,0).------ Note that 'move_c' may be a macro.----move :: Int -> Int -> IO ()-move y x = throwIfErr_ "move" $ move_c (fromIntegral y) (fromIntegral x)--foreign import ccall unsafe "move"-    move_c :: CInt -> CInt -> IO CInt------- | >    move the cursor associated with the window---   >    to line y and column x.  This routine does  not  move  the---   >    physical  cursor  of the terminal until refresh is called.---   >    The position specified is relative to the upper  left-hand---   >    corner of the window, which is (0,0).----wMove :: Window -> Int -> Int -> IO ()-wMove w y x = throwIfErr_ "wmove" $ wmove w (fi y) (fi x)--foreign import ccall unsafe-    wmove :: Window -> CInt -> CInt -> IO CInt----------------------- Cursor routines---------------------data CursorVisibility = CursorInvisible | CursorVisible | CursorVeryVisible--vis_c :: CursorVisibility -> CInt-vis_c vis = case vis of-    CursorInvisible   -> 0-    CursorVisible     -> 1-    CursorVeryVisible -> 2--c_vis :: CInt -> CursorVisibility-c_vis 0 = CursorInvisible-c_vis 1 = CursorVisible-c_vis 2 = CursorVeryVisible-c_vis n = error ("Illegal C value for cursor visibility: " ++ show n)------- | Set the cursor state------ >       The curs_set routine sets  the  cursor  state  is  set  to--- >       invisible, normal, or very visible for visibility equal to--- >       0, 1, or 2 respectively.  If  the  terminal  supports  the--- >       visibility   requested,   the  previous  cursor  state  is--- >       returned; otherwise, ERR is returned.----cursSet :: CursorVisibility -> IO CursorVisibility-cursSet CursorInvisible =-    do leaveOk True-       old <- curs_set 0-       return $ c_vis old-cursSet v =-    do leaveOk False-       old <- curs_set (vis_c v)-       return $ c_vis old--foreign import ccall unsafe "HSCurses.h curs_set"-    curs_set :: CInt -> IO CInt------- | Get the current cursor coordinates----getYX :: Window -> IO (Int, Int)-getYX w =-    alloca $ \py ->                 -- allocate two ints on the stack-        alloca $ \px -> do-            nomacro_getyx w py px   -- writes current cursor coords-            y <- peek py-            x <- peek px-            return (fromIntegral y, fromIntegral x)------- | Get the current cursor coords, written into the two argument ints.------ >    The getyx macro places the current cursor position of the given--- >    window in the two integer variables y and x.------      void getyx(WINDOW *win, int y, int x);----foreign import ccall unsafe "HSCursesUtils.h hscurses_nomacro_getyx"-        nomacro_getyx :: Window -> Ptr CInt -> Ptr CInt -> IO ()-----------------------------------------------------------------------------touchWin :: Window -> IO ()-touchWin w = throwIfErr_ "touchwin" $ touchwin w-foreign import ccall touchwin :: Window -> IO CInt--newPad :: Int -> Int -> IO Window-newPad nlines ncols = throwIfNull "newpad" $-    newpad (fromIntegral nlines) (fromIntegral ncols)--pRefresh :: Window -> Int -> Int -> Int -> Int -> Int -> Int -> IO ()-pRefresh pad pminrow pmincol sminrow smincol smaxrow smaxcol =-    throwIfErr_ "prefresh" $-        prefresh pad (fromIntegral pminrow)-                     (fromIntegral pmincol)-                     (fromIntegral sminrow)-                     (fromIntegral smincol)-                     (fromIntegral smaxrow)-                     (fromIntegral smaxcol)--delWin :: Window -> IO ()-delWin w = throwIfErr_ "delwin" $ delwin w--data Border = Border {-      ls :: Char-    , rs :: Char-    , ts :: Char-    , bs :: Char-    , tl :: Char-    , tr :: Char-    , bl :: Char-    , br :: Char-}--defaultBorder :: Border-defaultBorder = Border '\0' '\0' '\0' '\0' '\0' '\0' '\0' '\0'------- | >    Draw a border around the edges of a window. defaultBorder is---   >    a record  representing all 0 parameters to wrecord.----wBorder :: Window -> Border -> IO ()-wBorder w (Border ls rs ts bs tl tr bl br) = throwIfErr_ "wborder" $-                                             wborder w ls' rs' ts' bs' tl' tr' bl' br'-    where ls' = castCharToCChar ls-          rs' = castCharToCChar rs-          ts' = castCharToCChar ts-          bs' = castCharToCChar bs-          tl' = castCharToCChar tl-          tr' = castCharToCChar tr-          bl' = castCharToCChar bl-          br' = castCharToCChar br-foreign import ccall unsafe-    prefresh :: Window -> CInt -> CInt -> CInt -> CInt -> CInt -> CInt -> IO CInt--foreign import ccall unsafe-    newpad :: CInt -> CInt -> IO Window--foreign import ccall unsafe-    delwin :: Window -> IO CInt--foreign import ccall unsafe-    wborder :: Window -> CChar -> CChar -> CChar -> CChar -> CChar -> CChar -> CChar -> CChar -> IO CInt--newWin :: Int -> Int -> Int -> Int -> IO Window-newWin nlines ncolumn begin_y begin_x = throwIfNull "newwin" $-    newwin (fi nlines) (fi ncolumn) (fi begin_y) (fi begin_x)--foreign import ccall unsafe-    newwin :: CInt -> CInt -> CInt -> CInt -> IO Window--wClrToEol :: Window -> IO ()-wClrToEol w = throwIfErr_ "wclrtoeol" $ wclrtoeol w--foreign import ccall unsafe wclrtoeol :: Window -> IO CInt------- | >      The getch, wgetch, mvgetch and mvwgetch, routines read a---   >      character  from the window.-----#if defined(HAVE_LIBPDCURSES) || defined (HAVE_LIBPDCURSESW)-foreign import ccall unsafe "HSCurses.h hscurses_nomacro_getch" getch :: IO CInt-#else-foreign import ccall unsafe "HSCurses.h getch" getch :: IO CInt-#endif----foreign import ccall unsafe def_prog_mode :: IO CInt---foreign import ccall unsafe reset_prog_mode :: IO CInt-foreign import ccall unsafe flushinp :: IO CInt--foreign import ccall unsafe "HSCurses.h noqiflush" noqiflush :: IO ()--foreign import ccall unsafe "HSCurses.h beep" c_beep :: IO CInt-foreign import ccall unsafe "HSCurses.h flash" c_flash :: IO CInt--beep :: IO ()-beep = do-    br <- c_beep-    when (br /= (#const OK)) (c_flash >> return ())-------------------------------------------------------------------------------- | Map curses keys to key abstraction-----data Key-    = KeyChar Char | KeyBreak | KeyDown | KeyUp | KeyLeft | KeyRight-    | KeyHome | KeyBackspace | KeyF Int | KeyDL | KeyIL | KeyDC-    | KeyIC | KeyEIC | KeyClear | KeyEOS | KeyEOL | KeySF | KeySR-    | KeyNPage | KeyPPage | KeySTab | KeyCTab | KeyCATab | KeyEnter-    | KeySReset | KeyReset | KeyPrint | KeyLL | KeyA1 | KeyA3-    | KeyB2 | KeyC1 | KeyC3 | KeyBTab | KeyBeg | KeyCancel | KeyClose-    | KeyCommand | KeyCopy | KeyCreate | KeyEnd | KeyExit | KeyFind-    | KeyHelp | KeyMark | KeyMessage | KeyMove | KeyNext | KeyOpen-    | KeyOptions | KeyPrevious | KeyRedo | KeyReference | KeyRefresh-    | KeyReplace | KeyRestart | KeyResume | KeySave | KeySBeg-    | KeySCancel | KeySCommand | KeySCopy | KeySCreate | KeySDC-    | KeySDL | KeySelect | KeySEnd | KeySEOL | KeySExit | KeySFind-    | KeySHelp | KeySHome | KeySIC | KeySLeft | KeySMessage | KeySMove-    | KeySNext | KeySOptions | KeySPrevious | KeySPrint | KeySRedo-    | KeySReplace | KeySRight | KeySRsume | KeySSave | KeySSuspend-    | KeySUndo | KeySuspend | KeyUndo | KeyResize | KeyMouse | KeyUnknown Int-    deriving (Eq,Show)--decodeKey :: CInt -> Key-decodeKey key = case key of-    _ | key >= 0 && key <= 255 -> KeyChar (chr (fromIntegral key))-    (#const KEY_BREAK)         -> KeyBreak-    (#const KEY_DOWN)          -> KeyDown-    (#const KEY_UP)            -> KeyUp-    (#const KEY_LEFT)          -> KeyLeft-    (#const KEY_RIGHT)         -> KeyRight-    (#const KEY_HOME)          -> KeyHome-    (#const KEY_BACKSPACE)     -> KeyBackspace-    _ | key >= (#const KEY_F0) && key <= (#const KEY_F(63))-                               -> KeyF (fromIntegral (key - #const KEY_F0))-    (#const KEY_DL)            -> KeyDL-    (#const KEY_IL)            -> KeyIL-    (#const KEY_DC)            -> KeyDC-    (#const KEY_IC)            -> KeyIC-    (#const KEY_EIC)           -> KeyEIC-    (#const KEY_CLEAR)         -> KeyClear-    (#const KEY_EOS)           -> KeyEOS-    (#const KEY_EOL)           -> KeyEOL-    (#const KEY_SF)            -> KeySF-    (#const KEY_SR)            -> KeySR-    (#const KEY_NPAGE)         -> KeyNPage-    (#const KEY_PPAGE)         -> KeyPPage-    (#const KEY_STAB)          -> KeySTab-    (#const KEY_CTAB)          -> KeyCTab-    (#const KEY_CATAB)         -> KeyCATab-    (#const KEY_ENTER)         -> KeyEnter-    (#const KEY_SRESET)        -> KeySReset-    (#const KEY_RESET)         -> KeyReset-    (#const KEY_PRINT)         -> KeyPrint-    (#const KEY_LL)            -> KeyLL-    (#const KEY_A1)            -> KeyA1-    (#const KEY_A3)            -> KeyA3-    (#const KEY_B2)            -> KeyB2-    (#const KEY_C1)            -> KeyC1-    (#const KEY_C3)            -> KeyC3-    (#const KEY_BTAB)          -> KeyBTab-    (#const KEY_BEG)           -> KeyBeg-    (#const KEY_CANCEL)        -> KeyCancel-    (#const KEY_CLOSE)         -> KeyClose-    (#const KEY_COMMAND)       -> KeyCommand-    (#const KEY_COPY)          -> KeyCopy-    (#const KEY_CREATE)        -> KeyCreate-    (#const KEY_END)           -> KeyEnd-    (#const KEY_EXIT)          -> KeyExit-    (#const KEY_FIND)          -> KeyFind-    (#const KEY_HELP)          -> KeyHelp-    (#const KEY_MARK)          -> KeyMark-    (#const KEY_MESSAGE)       -> KeyMessage-    (#const KEY_MOVE)          -> KeyMove-    (#const KEY_NEXT)          -> KeyNext-    (#const KEY_OPEN)          -> KeyOpen-    (#const KEY_OPTIONS)       -> KeyOptions-    (#const KEY_PREVIOUS)      -> KeyPrevious-    (#const KEY_REDO)          -> KeyRedo-    (#const KEY_REFERENCE)     -> KeyReference-    (#const KEY_REFRESH)       -> KeyRefresh-    (#const KEY_REPLACE)       -> KeyReplace-    (#const KEY_RESTART)       -> KeyRestart-    (#const KEY_RESUME)        -> KeyResume-    (#const KEY_SAVE)          -> KeySave-    (#const KEY_SBEG)          -> KeySBeg-    (#const KEY_SCANCEL)       -> KeySCancel-    (#const KEY_SCOMMAND)      -> KeySCommand-    (#const KEY_SCOPY)         -> KeySCopy-    (#const KEY_SCREATE)       -> KeySCreate-    (#const KEY_SDC)           -> KeySDC-    (#const KEY_SDL)           -> KeySDL-    (#const KEY_SELECT)        -> KeySelect-    (#const KEY_SEND)          -> KeySEnd-    (#const KEY_SEOL)          -> KeySEOL-    (#const KEY_SEXIT)         -> KeySExit-    (#const KEY_SFIND)         -> KeySFind-    (#const KEY_SHELP)         -> KeySHelp-    (#const KEY_SHOME)         -> KeySHome-    (#const KEY_SIC)           -> KeySIC-    (#const KEY_SLEFT)         -> KeySLeft-    (#const KEY_SMESSAGE)      -> KeySMessage-    (#const KEY_SMOVE)         -> KeySMove-    (#const KEY_SNEXT)         -> KeySNext-    (#const KEY_SOPTIONS)      -> KeySOptions-    (#const KEY_SPREVIOUS)     -> KeySPrevious-    (#const KEY_SPRINT)        -> KeySPrint-    (#const KEY_SREDO)         -> KeySRedo-    (#const KEY_SREPLACE)      -> KeySReplace-    (#const KEY_SRIGHT)        -> KeySRight-    (#const KEY_SRSUME)        -> KeySRsume-    (#const KEY_SSAVE)         -> KeySSave-    (#const KEY_SSUSPEND)      -> KeySSuspend-    (#const KEY_SUNDO)         -> KeySUndo-    (#const KEY_SUSPEND)       -> KeySuspend-    (#const KEY_UNDO)          -> KeyUndo-#ifdef KEY_RESIZE-    (#const KEY_RESIZE)        -> KeyResize-#endif-#ifdef KEY_MOUSE-    (#const KEY_MOUSE)         -> KeyMouse-#endif-    _                          -> KeyUnknown (fromIntegral key)--keyResizeCode :: Maybe CInt-#ifdef KEY_RESIZE-keyResizeCode = Just (#const KEY_RESIZE)-#else-keyResizeCode = Nothing-#endif--cERR :: CInt-cERR = #const ERR--cKEY_UP, cKEY_DOWN, cKEY_LEFT, cKEY_RIGHT :: ChType-cKEY_UP = #const KEY_UP-cKEY_DOWN = #const KEY_DOWN-cKEY_LEFT = #const KEY_LEFT-cKEY_RIGHT = #const KEY_RIGHT---- cACS_BLOCK :: ChType--- cACS_BLOCK = #const ACS_BLOCK--cTRUE :: NBool-cTRUE = #const TRUE----- ------------------------------------------------------------------------ get char------- ncurses ungetch and Haskell's threadWaitRead do not work together well.--- So I decided to implement my own input queue.-ungetCh :: (Integral a) => a -> IO ()-ungetCh i =-    do debug "ungetCh called"-       writeChan inputBuf (BufDirect (fi i))--data BufData = BufDirect CInt  -- data directly available-             | DataViaGetch    -- data can be obtained by calling getch--inputBuf :: Chan BufData-inputBuf = unsafePerformIO newChan-{-# NOINLINE inputBuf #-}--getchToInputBuf :: IO ()-getchToInputBuf =-    do threadWaitRead (fi (0::Int))-       {- From the (n)curses manpage:-       Programmers  concerned  about portability should be prepared for either-       of two cases: (a) signal receipt does not interrupt getch;  (b)  signal-       receipt  interrupts getch and causes it to return ERR with errno set to-       EINTR.  Under the ncurses implementation, handled signals never  inter$B!>(B-       rupt getch.-       -}-       -- we only signalize that getch can now called without getting blocked.-       -- directly calling `getch' might result in losing the character just-       -- read (race condition).-       debug "now input available on stdin"-       writeChan inputBuf DataViaGetch------ | read a character from the window----getCh :: IO Key-getCh =-    do debug "getCh called"-       tid <- forkIO getchToInputBuf-       d <- readChan inputBuf-       killThread tid  -- we can kill the thread savely, because the thread does-                       -- not read any data via getch-       v <- case d of-              BufDirect x ->-                do debug "getCh: getting data directly from buffer"-                   return x-              DataViaGetch ->-                do debug "getCh: getting data via getch"-                   getch -- won't block!-       case v of-         (#const ERR) -> -- NO CODE IN THIS LINE-             do e <- getErrno-                if e `elem` [eAGAIN, eINTR]-                   then do debug "Curses.getCh returned eAGAIN or eINTR"-                           getCh-                   else throwErrno "HSCurses.Curses.getch"-         k -> let k' = decodeKey k-                  in do debug ("getCh: result = " ++ show k')-                        return k'---resizeTerminal :: Int -> Int -> IO ()--#ifdef HAVE_RESIZETERM-resizeTerminal a b = throwIfErr_ "resizeterm"  $ resizeterm (fi a) (fi b)--foreign import ccall unsafe "HSCurses.h resizeterm"-    resizeterm :: CInt -> CInt -> IO CInt-#else-resizeTerminal _ _ = return ()-#endif------- | The SIGWINCH signal is sent whenever the terminal size changes.--- This signal is not available on all platforms, so it is a |Maybe| value.-----#ifdef mingw32_HOST_OS-type Signal = CInt-#endif--cursesSigWinch :: Maybe Signal-#ifdef SIGWINCH-cursesSigWinch = Just (#const SIGWINCH)-#else-cursesSigWinch = Nothing-#endif----------------- Test case---------------cursesTest :: IO ()-cursesTest = do-    initScr-    hc <- hasColors-    when hc startColor-    ccc <- canChangeColor-    (ys,xs) <- scrSize-    cp <- colorPairs-    cs <- colors-    endWin-    putStrLn $ "ScreenSize: " ++ show (xs,ys)-    putStrLn $ "hasColors: " ++ show hc-    putStrLn $ "canChangeColor: " ++ show ccc-    putStrLn $ "colorPairs: " ++ show cp-    putStrLn $ "colors: " ++ show cs------------------------- Mouse Routines--------------------data MouseEvent = MouseEvent {-    mouseEventId :: Int,-    mouseEventX :: Int,-    mouseEventY :: Int,-    mouseEventZ :: Int,-    mouseEventButton :: [ButtonEvent]-   } deriving(Show)--data ButtonEvent = ButtonPressed Int | ButtonReleased Int | ButtonClicked Int |-    ButtonDoubleClicked Int | ButtonTripleClicked Int | ButtonShift | ButtonControl | ButtonAlt-                deriving(Eq,Show)--withMouseEventMask :: MonadIO m => [ButtonEvent] -> m a -> m a--#ifdef KEY_MOUSE--foreign import ccall unsafe "HSCurses.h mousemask"-    mousemask :: (#type mmask_t) -> Ptr (#type mmask_t) -> IO (#type mmask_t)--withMouseEventMask bes action = do-    ov <- liftIO $ alloca (\a ->  mousemask (besToMouseMask bes) a >> peek a)-    r <- action-    liftIO $ mousemask ov nullPtr-    return r--besToMouseMask :: [ButtonEvent] -> (#type mmask_t)-besToMouseMask bes = foldl' (.|.) 0 (map cb bes) where-    cb (ButtonPressed 1) = (#const BUTTON1_PRESSED)-    cb (ButtonPressed 2) = (#const BUTTON2_PRESSED)-    cb (ButtonPressed 3) = (#const BUTTON3_PRESSED)-    cb (ButtonPressed 4) = (#const BUTTON4_PRESSED)-    cb (ButtonReleased 1) = (#const BUTTON1_RELEASED)-    cb (ButtonReleased 2) = (#const BUTTON2_RELEASED)-    cb (ButtonReleased 3) = (#const BUTTON3_RELEASED)-    cb (ButtonReleased 4) = (#const BUTTON4_RELEASED)-    cb (ButtonClicked 1) = (#const BUTTON1_CLICKED)-    cb (ButtonClicked 2) = (#const BUTTON2_CLICKED)-    cb (ButtonClicked 3) = (#const BUTTON3_CLICKED)-    cb (ButtonClicked 4) = (#const BUTTON4_CLICKED)-    cb ButtonShift = (#const BUTTON_SHIFT)-    cb ButtonAlt = (#const BUTTON_ALT)-#ifdef BUTTON_CTRL-    cb ButtonControl = (#const BUTTON_CTRL)-#else-    cb ButtonControl = (#const BUTTON_CONTROL)-#endif-    cb _ = 0---#else-withMouseEventMask _ a = a--#endif-----ulCorner, llCorner, urCorner, lrCorner, rTee, lTee, bTee, tTee, hLine,-    vLine, plus, s1, s9, diamond, ckBoard, degree, plMinus, bullet,-    lArrow, rArrow, dArrow, uArrow, board, lantern, block,-    s3, s7, lEqual, gEqual, pi, nEqual, sterling-    :: Char--ulCorner = chr 0x250C-llCorner = chr 0x2514-urCorner = chr 0x2510-lrCorner = chr 0x2518-rTee     = chr 0x2524-lTee     = chr 0x251C-bTee     = chr 0x2534-tTee     = chr 0x252C-hLine    = chr 0x2500-vLine    = chr 0x2502-plus     = chr 0x253C-s1       = chr 0x23BA -- was: 0xF800-s9       = chr 0x23BD -- was: 0xF804-diamond  = chr 0x25C6-ckBoard  = chr 0x2592-degree   = chr 0x00B0-plMinus  = chr 0x00B1-bullet   = chr 0x00B7-lArrow   = chr 0x2190-rArrow   = chr 0x2192-dArrow   = chr 0x2193-uArrow   = chr 0x2191-board    = chr 0x2591-lantern  = chr 0x256C-block    = chr 0x2588-s3       = chr 0x23BB -- was: 0xF801-s7       = chr 0x23BC -- was: 0xF803-lEqual   = chr 0x2264-gEqual   = chr 0x2265-pi       = chr 0x03C0-nEqual   = chr 0x2260-sterling = chr 0x00A3---recognize :: Char -> IO a -> (ChType -> IO a) -> IO a-recognize _ch noConvert _convert = noConvert -- Handle the most common case first.------- ------------------------------------------------------------------------ code graveyard-----{---addStr :: String -> IO ()-addStr str =-    throwIfErr_ "addstr" $-    withCStringConv (readIORef cursesOutConv) str addstr-foreign import ccall unsafe addstr :: Ptr CChar -> IO CInt--addStrLn :: String -> IO ()-addStrLn str = addStr str >> addLn------- | add a string of characters to a curses window and advance cursor--- curs_addstr(3)----wAddStr :: Window -> String -> IO ()-wAddStr w str = throwIfErr_ "waddstr" $-    withCStringConv (readIORef cursesOutConv) str (waddstr w)--foreign import ccall unsafe waddstr :: Window -> Ptr CChar -> IO CInt---addGraphStr :: String -> IO ()-addGraphStr str = do-    conv <- readIORef cursesOutConv-    let-        convStr f = case f [] of-            [] -> return ()-            s  -> throwIfErr_ "addstr" $-                withCStringConv (return conv) s addstr-        loop []        acc = convStr acc-        loop (ch:str') acc = recognize-            ch-            (loop str' (acc . (ch:)))-            (\ch' -> do-                convStr acc-                throwIfErr "addch" $ addch ch'-                loop str' id)-    loop str id--addGraphStrLn :: String -> IO ()-addGraphStrLn str = do addGraphStr str; addLn---}---- vim: sw=4 ts=4+{-# LANGUAGE ForeignFunctionInterface #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE TypeApplications #-}++-- Copyright (c) 2002-2004 John Meacham (john at repetae dot net)+-- Copyright (c) 2004 Don Stewart - http://www.cse.unsw.edu.au/~dons+-- Copyright (c) 2005-2011 Stefan Wehr - http://www.stefanwehr.de+--+-- This library is free software; you can redistribute it and/or+-- modify it under the terms of the GNU Lesser General Public+-- License as published by the Free Software Foundation; either+-- version 2.1 of the License, or (at your option) any later version.+--+-- This library is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU+-- Lesser General Public License for more details.+--+-- You should have received a copy of the GNU Lesser General Public+-- License along with this library; if not, write to the Free Software+-- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA++-- | Binding to the [wn]curses library. From the ncurses man page:+--+-- >      The curses library routines give the user a terminal-inde-+-- >      pendent method of updating character screens with  reason-+-- >      able  optimization.+--+-- Sections of the quoted documentation are from the OpenBSD man pages, which+-- are distributed under a BSD license.+--+-- A useful reference is:+--        /Writing Programs with NCURSES/, by Eric S. Raymond and Zeyd+--        M. Ben-Halim, <http://dickey.his.com/ncurses/>+--+-- N.B attrs don't work with Irix curses.h. This should be fixed.+module UI.HSCurses.Curses (+    -- Basic Functions+    stdScr,+    initScr,+    initCurses,+    resetParams,+    endWin,+    scrSize,+    newTerm,+    delScreen,++    -- Windows, screens and Pads+    Screen,+    Window,+    Border (..),+    touchWin,+    newPad,+    pRefresh,+    delWin,+    newWin,+    wRefresh,+    wnoutRefresh,+    wBorder,+    defaultBorder,++    -- Refresh routines+    refresh,+    update,+    resizeTerminal,+    timeout,+    noqiflush,++    -- Navigation+    move,+    getYX,++    -- Input+    getCh,+    getch,+    decodeKey,+    ungetCh,+    keyResizeCode,++    -- * Input Options+    cBreak, -- :: Bool -> IO ()+    raw, -- :: Bool -> IO ()+    echo, -- :: Bool -> IO ()+    intrFlush, -- :: Bool -> IO ()+    keypad, -- :: Window -> Bool -> IO ()+    noDelay, -- :: Window -> Bool -> IO ()++    -- * Output+    wAddStr, -- :: Window -> String -> IO ()+    addLn, -- :: IO ()+    mvWAddStr,+    mvAddCh, -- :: Int -> Int -> ChType -> IO ()+    wMove,+    bkgrndSet, -- :: Attr -> Pair -> IO ()+    erase, -- :: IO ()+    wclear, -- :: Window -> IO ()+    werase,+    clrToEol, -- :: IO ()+    wClrToEol,+    beep,+    waddch,+    winsch,+    waddchnstr, -- :: Window -> CString -> CInt -> IO CInt++    -- * Output Options+    clearOk,+    leaveOk,+    nl, -- :: Bool -> IO ()++    -- * Cursor Routines+    CursorVisibility (..),+    cursSet,++    -- * Color Support+    hasColors, -- :: IO Bool+    startColor, -- :: IO ()+    useDefaultColors, -- :: IO ()+    Pair (..), -- newtype Pair = Pair Int deriving (Eq, Ord, Ix)+    colorPairs, -- :: IO Int+    Color (..), -- newtype Color = Color Int deriving (Eq, Ord, Ix)+    colors, -- :: IO Int+    color, -- :: String -> Maybe Color+    --    black, red, green, yellow, blue, magenta, cyan, white, -- :: Color+    initPair, -- :: Pair -> Color -> Color -> IO ()+    pairContent, -- :: Pair -> IO (Color, Color)+    canChangeColor, -- :: IO Bool+    initColor, -- :: Color -> (Int, Int, Int) -> IO ()+    colorContent, -- :: Color -> IO (Int, Int, Int)+    defaultBackground,+    defaultForeground,++    -- * Attributes+    attrPlus,+    Attr,+    attr0, -- :: Attr+    isAltCharset,+    isBlink,+    isBold,+    isDim,+    isHorizontal,+    isInvis,+    isLeft,+    isLow,+    isProtect,+    isReverse,+    isRight,+    isStandout,+    isTop,+    isUnderline,+    isVertical,+    -- :: Attr -> Bool++    setAltCharset,+    setBlink,+    setBold,+    setDim,+    setHorizontal,+    setInvis,+    setLeft,+    setLow,+    setProtect,+    setReverse,+    setRight,+    setStandout,+    setTop,+    setUnderline,+    setVertical,+    -- :: Attr -> Bool -> Attr++    attrSet, -- :: Attr -> Pair -> IO ()+    attrOn,+    attrOff,+    standout,+    standend,+    attrDim,+    attrBold,+    attrDimOn,+    attrDimOff,+    attrBoldOn,+    attrBoldOff,+    wAttrOn,+    wAttrOff,+    wAttrSet,+    wAttrGet,++    -- * Mouse Routines+    getMouse,+    withMouseEventMask,+    withAllMouseEvents,+    ButtonEvent (..),+    MouseEvent (..),++    -- * Keys+    Key (..),+    cERR,+    cKEY_UP,+    cKEY_DOWN,+    cKEY_LEFT,+    cKEY_RIGHT,+    cTRUE,+    --    cACS_BLOCK,++    -- * Lines+    vline,+    ulCorner,+    llCorner,+    urCorner,+    lrCorner,+    rTee,+    lTee,+    bTee,+    tTee,+    hLine,+    vLine,+    plus,+    s1,+    s9,+    diamond,+    ckBoard,+    degree,+    plMinus,+    bullet,+    lArrow,+    rArrow,+    dArrow,+    uArrow,+    board,+    lantern,+    block,+    s3,+    s7,+    lEqual,+    gEqual,+    pi,+    nEqual,+    sterling,++    -- * Signals+    cursesSigWinch,++    -- * Misc+    cursesTest,+    throwIfErr,+    throwIfErr_,+    errI, -- :: IO CInt -> IO ()+    flushinp,+    recognize,+    ChType,+    NBool,+) where++#include <HSCurses.h>++#if HAVE_SIGNAL_H+#include <signal.h>+#endif++import UI.HSCurses.Logging++#if !(defined(HAVE_WCHAR_H) && (defined(HAVE_LIBNCURSESW) || defined(HAVE_LIBPDCURSESW)))+-- FIXME: this is needed for CI builds on macOS; we can probably replace the+-- whole CWString module with just the "new" function in Foreign.C.String+import UI.HSCurses.CWString (withLCStringLen)+#endif++import Data.Char+import Data.Ix (Ix)+import Prelude hiding (pi)++-- foldl' was put into Prelude in base 4.20+#if !MIN_VERSION_base(4,20,0)+import Data.List (foldl')+#endif++import System.IO.Unsafe (unsafePerformIO)++import Control.Concurrent+import Control.Monad (liftM, void, when)+import Control.Monad.Trans++#if !MIN_VERSION_base(4,7,0)+import Foreign hiding (unsafePerformIO, void)+#else+import Foreign hiding (void)+#endif++import Foreign.C.Error+import Foreign.C.String+import Foreign.C.Types++import GHC.IO.FD (FD (..))++#ifndef mingw32_HOST_OS+import System.Posix.Signals+#endif++#if __GLASGOW_HASKELL__ < 603+import Data.Bits+#endif+++-- | 'initCurses' does all initialization necessary for a Curses application.+initCurses :: IO ()+initCurses = do+    initScr+    b <- hasColors+    when b $ startColor >> useDefaultColors++resetParams :: IO ()+resetParams = do+    raw True -- raw mode please+    echo False+    nl False+    intrFlush True+    leaveOk False+    keypad stdScr True+#ifdef NCURSES_EXT_FUNCS+    defineKey (# const KEY_UP) "\x1b[1;2A"+    defineKey (# const KEY_DOWN) "\x1b[1;2B"+    defineKey (# const KEY_SLEFT) "\x1b[1;2D"+    defineKey (# const KEY_SRIGHT) "\x1b[1;2C"+    defineKey (# const KEY_B2) "\x1b[E" -- xterm seems to emit B2, not BEG+    defineKey (# const KEY_END) "\x1b[F"+    defineKey (# const KEY_END) "\x1b[4~"+    defineKey (# const KEY_HOME) "\x1b[H"+    defineKey (# const KEY_HOME) "\x1b[1~"+#endif++fi :: (Integral a, Num b) => a -> b+fi = fromIntegral++throwIfErr :: (Eq a, Show a, Num a) => String -> IO a -> IO a+throwIfErr s = throwIf (== (# const ERR)) (\a -> "Curses[" ++ show a ++ "]:" ++ s)++throwIfErr_ :: (Eq a, Show a, Num a) => String -> IO a -> IO ()+throwIfErr_ name act = void $ throwIfErr name act++errI :: IO CInt -> IO ()+errI f = do+    r <- f+    if r == cERR+        then do+            _ <- endwin+            error "curses returned an error"+        else return ()++type WindowTag = ()+type Window = Ptr WindowTag+type ChType = #type chtype+type NBool = #type bool+++-- | The standard screen+stdScr :: Window+stdScr = unsafePerformIO (peek stdscr)+{-# NOINLINE stdScr #-}++foreign import ccall "static HSCurses.h &stdscr"+    stdscr :: Ptr Window++-- | 'initScr' is normally the first curses routine to call when initializing a+-- program. curs_initscr(3):+--+-- > To initialize the routines, the routine initscr or newterm must be called+-- > before any of the other routines that deal with windows and screens are+-- > used.+-- >+-- > The initscr code determines the terminal type and initial- izes all curses+-- > data structures. initscr also causes the first call to refresh to clear the+-- > screen. If errors occur, initscr writes an appropriate error message to+-- > standard error and exits; otherwise, a pointer is returned to stdscr.+initScr :: IO Window+initScr = throwIfNull "initscr" initscr++foreign import ccall unsafe "HSCurses.h initscr" initscr :: IO Window++-- This seems like the easiest way to get a FILE (see: man FILE)+-- from an FD+type FILE = Ptr ()++foreign import ccall unsafe "fdopen"+    fdopen :: CInt -> CString -> IO FILE++fdOpen :: FD -> String -> IO FILE+fdOpen fd mode =+    withCString mode $ \mode' -> do+        fdopen (fdFD fd) mode'++type Screen = Ptr ()++-- | A program that outputs to more than one terminal should use the 'newTerm'+-- routine for each terminal instead of 'initScr'. A program that needs to+-- inspect capabilities, so it can continue to run in a line-oriented mode if+-- the terminal cannot support a screen-oriented program, would also use+-- 'newTerm'. curs_initscr(3X):+--+-- >    The routine newterm should be called once for each terminal. It returns+-- >    a variable of type SCREEN * which should be saved as a reference to that+-- >    terminal. newterm's arguments are+-- >+-- >    - the type of the terminal to be used in place of $TERM,+-- >+-- >    - an output stream connected to the terminal, and+-- >+-- >    - an input stream connected to the terminal+-- >+-- >    If the type parameter is NULL, $TERM will be used.+newTerm :: String -> FD -> FD -> IO Screen+newTerm typ out in' =+    withCString typ $ \typ' -> do+        fout <- fdOpen out "rw"+        fin <- fdOpen in' "r"+        throwIfNull "newterm" $ newterm typ' fout fin++foreign import ccall unsafe "HSCurses.h newterm"+    newterm :: CString -> FILE -> FILE -> IO Screen++foreign import ccall unsafe "HSCurses.h delscreen"+    delScreen :: Screen -> IO ()++-- | > The cbreak routine disables line buffering and erase/kill+--   > character-processing (interrupt and flow control characters are+--   > unaffected), making characters typed by the user immediately available to+--   > the program. The nocbreak routine returns the terminal to normal (cooked)+--   > mode.+cBreak :: Bool -> IO ()+cBreak True = throwIfErr_ "cbreak" cbreak+cBreak False = throwIfErr_ "nocbreak" nocbreak++foreign import ccall unsafe "HSCurses.h cbreak"     cbreak :: IO CInt+foreign import ccall unsafe "HSCurses.h nocbreak" nocbreak :: IO CInt++-- | > The raw and noraw routines place the terminal into or out of raw mode.+--   > Raw mode is similar to cbreak mode, in that characters typed are+--   > immediately passed through to the user program. The differences are that+--   > in raw mode, the interrupt, quit, suspend, and flow control characters+--   > are all passed through uninterpreted, instead of generating a signal. The+--   > behavior of the BREAK key depends on other bits in the tty driver that+--   > are not set by curses.+raw :: Bool -> IO ()+raw False = throwIfErr_ "noraw" noraw+raw True = throwIfErr_ "raw" raw_c++foreign import ccall unsafe "hscurses.h noraw" noraw :: IO CInt+foreign import ccall unsafe "HSCurses.h raw" raw_c :: IO CInt++-- | > The echo and noecho routines control whether characters typed by the user+--   > are echoed by getch as they are typed. Echoing by the tty driver is+--   > always disabled, but ini- tially getch is in echo mode, so characters+--   > typed are echoed. Authors of most interactive programs prefer to do their+--   > own echoing in a controlled area of the screen, or not to echo at all, so+--   > they disable echoing by calling noecho. [See curs_getch(3) for a+--   > discussion of how these routines interact with cbreak and nocbreak.]+echo :: Bool -> IO ()+echo False = throwIfErr_ "noecho" noecho+echo True = throwIfErr_ "echo" echo_c++foreign import ccall unsafe "HSCurses.h noecho" noecho :: IO CInt+foreign import ccall unsafe "HSCurses.h echo" echo_c :: IO CInt++-- | > The nl and nonl routines control whether the underlying display device+--   > translates the return key into newline on input, and whether it+--   > translates newline into return and line-feed on output (in either case,+--   > the call addch('\n') does the equivalent of return and line feed on the+--   > virtual screen). Initially, these translations do occur. If you disable+--   > them using nonl, curses will be able to make bet- ter use of the+--   > line-feed capability, resulting in faster cursor motion. Also, curses+--   > will then be able to detect the return key.+nl :: Bool -> IO ()+nl True = throwIfErr_ "nl" nl_c+nl False = throwIfErr_ "nonl" nonl++foreign import ccall unsafe "HSCurses.h nl" nl_c :: IO CInt+foreign import ccall unsafe "HSCurses.h nonl" nonl :: IO CInt++-- | > If the intrflush option is enabled, (bf is TRUE), when an interrupt key+--   > is pressed on the keyboard (interrupt, break, quit) all output in the tty+--   > driver queue will be flushed, giving the effect of faster response to the+--   > interrupt, but causing curses to have the wrong idea of what is on the+--   > screen. Disabling (bf is FALSE), the option prevents the flush.+intrFlush :: Bool -> IO ()+intrFlush bf =+    throwIfErr_ "intrflush" $ intrflush stdScr (if bf then 1 else 0)++foreign import ccall unsafe "HSCurses.h intrflush"+    intrflush :: Window -> (#type bool) -> IO CInt++-- | Enable the keypad of the user's terminal.+keypad :: Window -> Bool -> IO ()+keypad win bf = throwIfErr_ "keypad" $ keypad_c win (if bf then 1 else 0)++foreign import ccall unsafe "HSCurses.h keypad"+    keypad_c :: Window -> (#type bool) -> IO CInt++noDelay :: Window -> Bool -> IO ()+noDelay win bf =+    throwIfErr_ "nodelay" $ nodelay win (if bf then 1 else 0)++foreign import ccall unsafe "HSCurses.h nodelay"+    nodelay :: Window -> (#type bool) -> IO CInt++-- | Normally, the hardware cursor is left at the location of the window cursor+-- being refreshed. The leaveok option allows the cursor to be left wherever the+-- update happens to leave it. It is useful for applications where the cursor is+-- not used, since it reduces the need for cursor motions. If possible, the+-- cursor is made invisible when this option is enabled.+leaveOk :: Bool -> IO CInt+leaveOk True = leaveok_c stdScr 1+leaveOk False = leaveok_c stdScr 0++foreign import ccall unsafe "HSCurses.h leaveok"+    leaveok_c :: Window -> (#type bool) -> IO CInt++clearOk :: Bool -> IO CInt+clearOk True = clearok_c stdScr 1+clearOk False = clearok_c stdScr 0++foreign import ccall unsafe "HSCurses.h clearok"+    clearok_c :: Window -> (#type bool) -> IO CInt++foreign import ccall unsafe "HSCurses.h use_default_colors"+    useDefaultColors :: IO ()++defaultBackground, defaultForeground :: Color+defaultBackground = Color (-1)+defaultForeground = Color (-1)++#ifdef NCURSES_EXT_FUNCS+defineKey :: CInt -> String -> IO ()+defineKey k s = withCString s (\s' -> define_key s' k) >> return ()++foreign import ccall unsafe "HSCurses.h define_key"+    define_key :: Ptr CChar -> CInt -> IO ()+#endif++-- | >  The program must call endwin for each terminal being used before+--   >  exiting from curses.+endWin :: IO ()+endWin = throwIfErr_ "endwin" endwin++foreign import ccall unsafe "HSCurses.h endwin" endwin :: IO CInt++-- | Get the dimensions of the screen (lines, cols).+scrSize :: IO (Int, Int)+-- Note, per the documentation:+--    http://invisible-island.net/ncurses/ncurses-intro.html#caution+-- It is not recommended to peek at the LINES and COLS global variables.  This code+-- was previously doing exactly that, but now it is fixed to use getmaxyx.+--   -Ryan Newton [2013.03.31]+scrSize = do+    yfp <- mallocForeignPtr+    xfp <- mallocForeignPtr+    withForeignPtr yfp $ \yp ->+        withForeignPtr xfp $ \xp -> do+            getMaxYX stdScr yp xp+            y <- peek yp+            x <- peek xp+            return (fromIntegral y, fromIntegral x)+    +foreign import ccall "HSCurses.h getmaxyx_fun"+    getMaxYX ::+        Window -> Ptr CInt -> Ptr CInt -> IO ()++-- | Refresh curses windows and lines. curs_refresh(3)+refresh :: IO ()+refresh = throwIfErr_ "refresh" refresh_c++foreign import ccall unsafe "HSCurses.h refresh"+    refresh_c :: IO CInt++-- | Refresh the specified window, copying the data from the virtual screen to+-- the physical screen.+wRefresh :: Window -> IO ()+wRefresh w = throwIfErr_ "wrefresh" $ wrefresh_c w++foreign import ccall unsafe "HSCurses.h wrefresh"+    wrefresh_c :: Window -> IO CInt++-- | Stage an update to a window, but don't actually do the refresh until update+-- is called. This allows multiple windows to be updated together more smoothly.+wnoutRefresh :: Window -> IO ()+wnoutRefresh w = throwIfErr_ "wnoutrefresh" $ wnoutrefresh_c w++foreign import ccall safe "HSCurses.h wnoutrefresh"+    wnoutrefresh_c :: Window -> IO CInt++-- | Do an actual update. Used after endWin on linux to restore the terminal.+update :: IO ()+update = throwIfErr_ "update" update_c++foreign import ccall unsafe "HSCurses.h doupdate" update_c :: IO CInt++foreign import ccall unsafe "static curses.h timeout" timeout_c :: CInt -> IO ()++-- | Set a delay in milliseconds.+timeout :: Int -> IO ()+timeout = timeout_c . fromIntegral++hasColors :: IO Bool+hasColors = liftM (/= 0) has_colors++foreign import ccall unsafe "HSCurses.h has_colors" has_colors :: IO (#type bool)++-- | Initialise the color settings. Also sets the screen to the default colors+-- (white on black).+startColor :: IO ()+startColor = throwIfErr_ "start_color" start_color++foreign import ccall unsafe start_color :: IO CInt++newtype Pair = Pair Int deriving (Eq, Ord, Ix, Show)++-- | Defines the maximum number of color-pairs the terminal can support).+colorPairs :: IO Int+colorPairs = fmap fromIntegral $ peek colorPairsPtr++foreign import ccall "HSCurses.h &COLOR_PAIRS"+    colorPairsPtr :: Ptr CInt++newtype Color = Color Int deriving (Eq, Ord, Ix)++colors :: IO Int+colors = liftM fromIntegral $ peek colorsPtr++foreign import ccall "HSCurses.h &COLORS" colorsPtr :: Ptr CInt++color :: String -> Maybe Color+color "default" = Just $ Color (-1)+color "black" = Just $ Color (# const COLOR_BLACK)+color "red" = Just $ Color (# const COLOR_RED)+color "green" = Just $ Color (# const COLOR_GREEN)+color "yellow" = Just $ Color (# const COLOR_YELLOW)+color "blue" = Just $ Color (# const COLOR_BLUE)+color "magenta" = Just $ Color (# const COLOR_MAGENTA)+color "cyan" = Just $ Color (# const COLOR_CYAN)+color "white" = Just $ Color (# const COLOR_WHITE)+color _ = Nothing++-- | curses support color attributes on terminals with that capability. To use+-- these routines start_color must be called, usually right after initscr.+-- Colors are always used in pairs (referred to as color-pairs). A color-pair+-- consists of a foreground color (for characters) and a background color (for+-- the blank field on which the charac- ters are displayed). A programmer+-- initializes a color- pair with the routine init_pair. After it has been ini-+-- tialized, COLOR_PAIR(n), a macro defined in <curses.h>, can be used as a new+-- video attribute.+--+-- If a terminal is capable of redefining colors, the pro- grammer can use the+-- routine init_color to change the defi- nition of a color.+--+-- The init_pair routine changes the definition of a color- pair. It takes three+-- arguments: the number of the color- pair to be changed, the foreground color+-- number, and the background color number. For portable applications:+--+-- - The value of the first argument must be between 1 and COLOR_PAIRS-1.+--+-- - The value of the second and third arguments must be between 0 and COLORS+--   (the 0 color pair is wired to white on black and cannot be changed).+initPair :: Pair -> Color -> Color -> IO ()+initPair (Pair p) (Color f) (Color b) =+    throwIfErr_ "init_pair" $+        init_pair (fromIntegral p) (fromIntegral f) (fromIntegral b)++foreign import ccall unsafe+    init_pair :: CShort -> CShort -> CShort -> IO CInt++pairContent :: Pair -> IO (Color, Color)+pairContent (Pair p) =+    alloca $ \fPtr ->+        alloca $ \bPtr -> do+            throwIfErr "pair_content" $ pair_content (fromIntegral p) fPtr bPtr+            f <- peek fPtr+            b <- peek bPtr+            return (Color (fromIntegral f), Color (fromIntegral b))++foreign import ccall unsafe+    pair_content :: CShort -> Ptr CShort -> Ptr CShort -> IO CInt++canChangeColor :: IO Bool+canChangeColor = liftM (/= 0) can_change_color++foreign import ccall unsafe can_change_color :: IO (#type bool)++initColor :: Color -> (Int, Int, Int) -> IO ()+initColor (Color c) (r, g, b) =+    throwIfErr_ "init_color" $+        init_color (fromIntegral c) (fromIntegral r) (fromIntegral g) (fromIntegral b)++foreign import ccall unsafe+    init_color :: CShort -> CShort -> CShort -> CShort -> IO CInt++colorContent :: Color -> IO (Int, Int, Int)+colorContent (Color c) =+    alloca $ \rPtr ->+        alloca $ \gPtr ->+            alloca $ \bPtr -> do+                throwIfErr "color_content" $ color_content (fromIntegral c) rPtr gPtr bPtr+                r <- peek rPtr+                g <- peek gPtr+                b <- peek bPtr+                return (fromIntegral r, fromIntegral g, fromIntegral b)++foreign import ccall unsafe+    color_content :: CShort -> Ptr CShort -> Ptr CShort -> Ptr CShort -> IO CInt++foreign import ccall unsafe "HSCurses.h hs_curses_color_pair" colorPair :: Pair -> ChType++-----------------------------------------------------------------------------+-- * Attributes+-----------------------------------------------------------------------------++foreign import ccall unsafe "HSCurses.h attr_set"+    attr_set :: Attr -> CShort -> Ptr a -> IO Int++foreign import ccall unsafe "HSCurses.h wattr_set"+    wattr_set :: Window -> Attr -> CInt -> Ptr a -> IO CInt++foreign import ccall unsafe "HSCurses.h wattr_get"+    wattr_get :: Window -> Ptr Attr -> Ptr CShort -> Ptr a -> IO CInt++foreign import ccall "HSCurses.h attr_on" attr_on :: (#type attr_t) -> Ptr a -> IO Int+foreign import ccall "HSCurses.h attr_off" attr_off :: (#type attr_t) -> Ptr a -> IO Int+foreign import ccall "HSCurses.h attron" attron :: Int -> IO Int+foreign import ccall "HSCurses.h attroff" attroff :: Int -> IO Int+foreign import ccall unsafe "HSCurses.h wattron" wattron :: Window -> CInt -> IO CInt+foreign import ccall unsafe "HSCurses.h wattroff" wattroff :: Window -> CInt -> IO CInt+foreign import ccall standout :: IO Int+foreign import ccall standend :: IO Int++wAttrSet :: Window -> (Attr, Pair) -> IO ()+wAttrSet w (a, (Pair p)) =+    throwIfErr_ "wattr_set" $+        wattr_set w a (fromIntegral p) nullPtr++-- | Manipulate the current attributes of the named window. see curs_attr(3)+wAttrGet :: Window -> IO (Attr, Pair)+wAttrGet w =+    alloca $ \pa ->+        alloca $ \pp -> do+            throwIfErr_ "wattr_get" $ wattr_get w pa pp nullPtr+            a <- peek pa+            p <- peek pp+            return (a, Pair $ fromIntegral p)++newtype Attr = Attr (#type attr_t)+    deriving (Eq, Storable, Bits, Num, Show)++-- | Normal display (no highlight)+attr0 :: Attr+#ifdef WA_NORMAL+attr0 = Attr (#const WA_NORMAL)+#else+attr0 = Attr (#const A_NORMAL)+#endif++isAltCharset, isBlink, isBold, isDim, isHorizontal, isInvis, isLeft,+    isLow, isProtect, isReverse, isRight, isStandout, isTop,+    isUnderline, isVertical :: Attr -> Bool++isAltCharset = isAttr (#const WA_ALTCHARSET)+isBlink      = isAttr (#const WA_BLINK)+isBold       = isAttr (#const WA_BOLD)+isDim        = isAttr (#const WA_DIM)+isHorizontal = isAttr (#const WA_HORIZONTAL)+isInvis      = isAttr (#const WA_INVIS)+isLeft       = isAttr (#const WA_LEFT)+isLow        = isAttr (#const WA_LOW)+isProtect    = isAttr (#const WA_PROTECT)+isReverse    = isAttr (#const WA_REVERSE)+isRight      = isAttr (#const WA_RIGHT)+isStandout   = isAttr (#const WA_STANDOUT)+isTop        = isAttr (#const WA_TOP)+isUnderline  = isAttr (#const WA_UNDERLINE)+isVertical   = isAttr (#const WA_VERTICAL)++isAttr :: (#type attr_t) -> Attr -> Bool+isAttr b (Attr a) = a .&. b /= 0++setAltCharset, setBlink, setBold, setDim, setHorizontal, setInvis,+    setLeft, setLow, setProtect, setReverse, setRight, setStandout,+    setTop, setUnderline, setVertical :: Attr -> Bool -> Attr++setAltCharset = setAttr (#const WA_ALTCHARSET)+setBlink      = setAttr (#const WA_BLINK)+setBold       = setAttr (#const WA_BOLD)+setDim        = setAttr (#const WA_DIM)+setHorizontal = setAttr (#const WA_HORIZONTAL)+setInvis      = setAttr (#const WA_INVIS)+setLeft       = setAttr (#const WA_LEFT)+setLow        = setAttr (#const WA_LOW)+setProtect    = setAttr (#const WA_PROTECT)+setReverse    = setAttr (#const WA_REVERSE)+setRight      = setAttr (#const WA_RIGHT)+setStandout   = setAttr (#const WA_STANDOUT)+setTop        = setAttr (#const WA_TOP)+setUnderline  = setAttr (#const WA_UNDERLINE)+setVertical   = setAttr (#const WA_VERTICAL)++setAttr :: (#type attr_t) -> Attr -> Bool -> Attr+setAttr b (Attr a) False = Attr (a .&. complement b)+setAttr b (Attr a) True  = Attr (a .|. b)++attrPlus :: Attr -> Attr -> Attr+attrPlus (Attr a) (Attr b) = Attr (a .|. b)++attrSet :: Attr -> Pair -> IO ()+attrSet attr (Pair p) = throwIfErr_ "attrset" $+    attr_set attr (fromIntegral p) nullPtr++attrOn :: Attr -> IO ()+attrOn (Attr attr) = throwIfErr_ "attr_on" $+    attr_on attr nullPtr++attrOff :: Attr -> IO ()+attrOff (Attr attr) = throwIfErr_ "attr_off" $+    attr_off attr nullPtr++wAttrOn :: Window -> Int -> IO ()+wAttrOn w x = throwIfErr_ "wattron" $ wattron w (fi x)++wAttrOff :: Window -> Int -> IO ()+wAttrOff w x = throwIfErr_ "wattroff" $ wattroff w (fi x)++attrDimOn :: IO ()+attrDimOn  = throwIfErr_ "attron A_DIM" $+    attron (#const A_DIM)++attrDimOff :: IO ()+attrDimOff = throwIfErr_ "attroff A_DIM" $+    attroff (#const A_DIM)++attrBoldOn :: IO ()+attrBoldOn  = throwIfErr_ "attron A_BOLD" $+    attron (#const A_BOLD)++attrBoldOff :: IO ()+attrBoldOff = throwIfErr_ "attroff A_BOLD" $+    attroff (#const A_BOLD)++attrDim :: Int+attrDim = (#const A_DIM)+attrBold :: Int+attrBold = (#const A_BOLD)++-- | Raw NCurses routine.+foreign import ccall safe+    waddch :: Window -> ChType -> IO CInt++-- | Raw NCurses routine.+foreign import ccall safe+    winsch :: Window -> ChType -> IO CInt++-- | Raw NCurses routine.+foreign import ccall safe+    waddchnstr :: Window -> CString -> CInt -> IO CInt++foreign import ccall safe "static curses.h mvaddch"+    mvaddch_c :: CInt -> CInt -> ChType -> IO ()++mvWAddStr :: Window -> Int -> Int -> String -> IO ()+mvWAddStr w y x str = wMove w y x >> wAddStr w str++mvAddCh :: Int -> Int -> ChType -> IO ()+mvAddCh l m n = mvaddch_c (fromIntegral l) (fromIntegral m) (fromIntegral n)++addLn :: IO ()+addLn = wAddStr stdScr "\n"++#if defined(HAVE_WCHAR_H) && (defined(HAVE_LIBNCURSESW) || defined(HAVE_LIBPDCURSESW))++foreign import ccall unsafe+    waddnwstr :: Window -> CWString -> CInt -> IO CInt++wAddStr :: Window -> String -> IO ()+wAddStr win str = do+    let convStr f = case f [] of+            [] -> return ()+            s ->+                throwIfErr_ "waddnstr" $+                    withCWStringLen (s) (\(ws, len) -> (waddnwstr win ws (fi len)))+        loop [] acc = convStr acc+        loop (ch : str') acc =+            recognize+                ch+                (loop str' (acc . (ch :)))+                ( \ch' -> do+                    convStr acc+                    throwIfErr "waddch" $ waddch win ch'+                    loop str' id+                )+    loop str id++#else++-- This is heavily called, and does a lot of allocs.  We walk over all+-- the string accumulating a list of characters to be drawn.+--+-- Got it down to:+--+--      wAddStr Yi.Curses 20.0   38.1+--      wAddStr Yi.Curses 10.0   32.5+--+-- TODO make this way less expensive. That accum sucks.+-- use difference lists for O(1) append+wAddStr :: Window -> [Char] -> IO ()+wAddStr _ [] = return ()+wAddStr win s =+    throwIfErr_ ("waddnstr: <" ++ s ++ ">") $+        withLCStringLen (s) (\(ws, len) -> waddnstr win ws (fi len))++foreign import ccall safe+    waddnstr :: Window -> CString -> CInt -> IO CInt++#endif++foreign import ccall safe+    vline :: Char -> Int -> IO ()++--+-- what ?+--++#let translate_attr attr =                              \+    "(if a .&. %lu /= 0 then %lu else 0) .|.",          \+    (unsigned long) WA_##attr, (unsigned long) A_##attr++bkgrndSet :: Attr -> Pair -> IO ()+bkgrndSet (Attr a) p = bkgdset $+    fromIntegral (ord ' ') .|.+    #translate_attr ALTCHARSET+    #translate_attr BLINK+    #translate_attr BOLD+    #translate_attr DIM+    #translate_attr INVIS+    #translate_attr PROTECT+    #translate_attr REVERSE+    #translate_attr STANDOUT+    #translate_attr UNDERLINE+    colorPair p++foreign import ccall unsafe bkgdset :: ChType -> IO ()++-- | Copy blanks to every position in the screen.+erase :: IO ()+erase = throwIfErr_ "erase" $ werase_c stdScr++foreign import ccall unsafe "werase" werase_c :: Window -> IO CInt++-- | Copy blanks to every position in a window.+werase :: Window -> IO ()+werase w = throwIfErr_ "werase" $ werase_c w++-- | Copy blanks to a window and set clearOk for that window.+wclear :: Window -> IO ()+wclear w = throwIfErr_ "wclear" $ wclear_c w++foreign import ccall unsafe "wclear" wclear_c :: Window -> IO CInt++clrToEol :: IO ()+clrToEol = throwIfErr_ "clrtoeol" clrtoeol++foreign import ccall unsafe clrtoeol :: IO CInt++-- | > move the cursor associated with the window to line y and column x. This+--   > routine does not move the physical cursor of the terminal until refresh+--   > is called. The position specified is relative to the upper left-hand+--   > corner of the window, which is (0,0).+--+-- Note that 'move_c' may be a macro.+move :: Int -> Int -> IO ()+move y x = throwIfErr_ "move" $ move_c (fromIntegral y) (fromIntegral x)++foreign import ccall unsafe "move"+    move_c :: CInt -> CInt -> IO CInt++-- | >    move the cursor associated with the window+--   >    to line y and column x.  This routine does  not  move  the+--   >    physical  cursor  of the terminal until refresh is called.+--   >    The position specified is relative to the upper  left-hand+--   >    corner of the window, which is (0,0).+wMove :: Window -> Int -> Int -> IO ()+wMove w y x = throwIfErr_ "wmove" $ wmove w (fi y) (fi x)++foreign import ccall unsafe+    wmove :: Window -> CInt -> CInt -> IO CInt++-----------------------------------------------------------------------------+-- * Cursor routines+-----------------------------------------------------------------------------++data CursorVisibility+    = CursorInvisible+    | CursorVisible+    | CursorVeryVisible++vis_c :: CursorVisibility -> CInt+vis_c vis = case vis of+    CursorInvisible -> 0+    CursorVisible -> 1+    CursorVeryVisible -> 2++c_vis :: CInt -> CursorVisibility+c_vis 0 = CursorInvisible+c_vis 1 = CursorVisible+c_vis 2 = CursorVeryVisible+c_vis n = error ("Illegal C value for cursor visibility: " ++ show n)++-- | Set the cursor state.+--+-- > The curs_set routine sets the cursor state is set to invisible, normal, or+-- > very visible for visibility equal to 0, 1, or 2 respectively. If the+-- > terminal supports the visibility requested, the previous cursor state is+-- > returned; otherwise, ERR is returned.+cursSet :: CursorVisibility -> IO CursorVisibility+cursSet CursorInvisible = do+    leaveOk True+    old <- curs_set 0+    return $ c_vis old+cursSet v = do+    leaveOk False+    old <- curs_set (vis_c v)+    return $ c_vis old++foreign import ccall unsafe "HSCurses.h curs_set"+    curs_set :: CInt -> IO CInt++-- | Get the current cursor coordinates.+getYX :: Window -> IO (Int, Int)+getYX w =+    alloca $ \py ->+        -- allocate two ints on the stack+        alloca $ \px -> do+            nomacro_getyx w py px -- writes current cursor coords+            y <- peek py+            x <- peek px+            return (fromIntegral y, fromIntegral x)++-- | Get the current cursor coords, written into the two argument ints.+--+-- > The getyx macro places the current cursor position of the given window in+-- > the two integer variables y and x.+foreign import ccall unsafe "HSCursesUtils.h hscurses_nomacro_getyx"+    nomacro_getyx :: Window -> Ptr CInt -> Ptr CInt -> IO ()++touchWin :: Window -> IO ()+touchWin w = throwIfErr_ "touchwin" $ touchwin w++foreign import ccall touchwin :: Window -> IO CInt++newPad :: Int -> Int -> IO Window+newPad nlines ncols =+    throwIfNull "newpad" $+        newpad (fromIntegral nlines) (fromIntegral ncols)++pRefresh :: Window -> Int -> Int -> Int -> Int -> Int -> Int -> IO ()+pRefresh pad pminrow pmincol sminrow smincol smaxrow smaxcol =+    throwIfErr_ "prefresh" $+        prefresh+            pad+            (fromIntegral pminrow)+            (fromIntegral pmincol)+            (fromIntegral sminrow)+            (fromIntegral smincol)+            (fromIntegral smaxrow)+            (fromIntegral smaxcol)++delWin :: Window -> IO ()+delWin w = throwIfErr_ "delwin" $ delwin w++data Border = Border+    { ls :: Char+    , rs :: Char+    , ts :: Char+    , bs :: Char+    , tl :: Char+    , tr :: Char+    , bl :: Char+    , br :: Char+    }++defaultBorder :: Border+defaultBorder = Border '\0' '\0' '\0' '\0' '\0' '\0' '\0' '\0'++-- | Draw a border around the edges of a window. 'defaultBorder' is a record+-- representing all 0 parameters to wrecord.+wBorder :: Window -> Border -> IO ()+wBorder w (Border ls rs ts bs tl tr bl br) =+    throwIfErr_ "wborder" $+        wborder w ls' rs' ts' bs' tl' tr' bl' br'+  where+    ls' = castCharToCChar ls+    rs' = castCharToCChar rs+    ts' = castCharToCChar ts+    bs' = castCharToCChar bs+    tl' = castCharToCChar tl+    tr' = castCharToCChar tr+    bl' = castCharToCChar bl+    br' = castCharToCChar br++foreign import ccall unsafe+    prefresh :: Window -> CInt -> CInt -> CInt -> CInt -> CInt -> CInt -> IO CInt++foreign import ccall unsafe+    newpad :: CInt -> CInt -> IO Window++foreign import ccall unsafe+    delwin :: Window -> IO CInt++foreign import ccall unsafe+    wborder :: Window -> CChar -> CChar -> CChar -> CChar -> CChar -> CChar -> CChar -> CChar -> IO CInt++newWin :: Int -> Int -> Int -> Int -> IO Window+newWin nlines ncolumn begin_y begin_x =+    throwIfNull "newwin" $+        newwin (fi nlines) (fi ncolumn) (fi begin_y) (fi begin_x)++foreign import ccall unsafe+    newwin :: CInt -> CInt -> CInt -> CInt -> IO Window++wClrToEol :: Window -> IO ()+wClrToEol w = throwIfErr_ "wclrtoeol" $ wclrtoeol w++foreign import ccall unsafe wclrtoeol :: Window -> IO CInt++-- | >      The getch, wgetch, mvgetch and mvwgetch, routines read a+--   >      character  from the window.+#if defined(HAVE_LIBPDCURSES) || defined (HAVE_LIBPDCURSESW)+foreign import ccall unsafe "HSCurses.h hscurses_nomacro_getch" getch :: IO CInt+#else+foreign import ccall unsafe "HSCurses.h getch" getch :: IO CInt+#endif++foreign import ccall unsafe flushinp :: IO CInt++foreign import ccall unsafe "HSCurses.h noqiflush" noqiflush :: IO ()++foreign import ccall unsafe "HSCurses.h beep" c_beep :: IO CInt+foreign import ccall unsafe "HSCurses.h flash" c_flash :: IO CInt++beep :: IO ()+beep = do+    br <- c_beep+    when (br /= (# const OK)) (c_flash >> return ())++-- | A mapping of curses keys to Haskell values.+data Key+    = KeyChar Char+    | KeyBreak+    | KeyDown+    | KeyUp+    | KeyLeft+    | KeyRight+    | KeyHome+    | KeyBackspace+    | KeyF Int+    | KeyDL+    | KeyIL+    | KeyDC+    | KeyIC+    | KeyEIC+    | KeyClear+    | KeyEOS+    | KeyEOL+    | KeySF+    | KeySR+    | KeyNPage+    | KeyPPage+    | KeySTab+    | KeyCTab+    | KeyCATab+    | KeyEnter+    | KeySReset+    | KeyReset+    | KeyPrint+    | KeyLL+    | KeyA1+    | KeyA3+    | KeyB2+    | KeyC1+    | KeyC3+    | KeyBTab+    | KeyBeg+    | KeyCancel+    | KeyClose+    | KeyCommand+    | KeyCopy+    | KeyCreate+    | KeyEnd+    | KeyExit+    | KeyFind+    | KeyHelp+    | KeyMark+    | KeyMessage+    | KeyMove+    | KeyNext+    | KeyOpen+    | KeyOptions+    | KeyPrevious+    | KeyRedo+    | KeyReference+    | KeyRefresh+    | KeyReplace+    | KeyRestart+    | KeyResume+    | KeySave+    | KeySBeg+    | KeySCancel+    | KeySCommand+    | KeySCopy+    | KeySCreate+    | KeySDC+    | KeySDL+    | KeySelect+    | KeySEnd+    | KeySEOL+    | KeySExit+    | KeySFind+    | KeySHelp+    | KeySHome+    | KeySIC+    | KeySLeft+    | KeySMessage+    | KeySMove+    | KeySNext+    | KeySOptions+    | KeySPrevious+    | KeySPrint+    | KeySRedo+    | KeySReplace+    | KeySRight+    | KeySRsume+    | KeySSave+    | KeySSuspend+    | KeySUndo+    | KeySuspend+    | KeyUndo+    | KeyResize+    | KeyMouse+    | KeyUnknown Int+    deriving (Eq, Show)++decodeKey :: CInt -> Key+decodeKey key = case key of+    _ | key >= 0 && key <= 255 -> KeyChar (chr (fromIntegral key))+    (#const KEY_BREAK)         -> KeyBreak+    (#const KEY_DOWN)          -> KeyDown+    (#const KEY_UP)            -> KeyUp+    (#const KEY_LEFT)          -> KeyLeft+    (#const KEY_RIGHT)         -> KeyRight+    (#const KEY_HOME)          -> KeyHome+    (#const KEY_BACKSPACE)     -> KeyBackspace+    _ | key >= (#const KEY_F0) && key <= (#const KEY_F(63))+                               -> KeyF (fromIntegral (key - #const KEY_F0))+    (#const KEY_DL)            -> KeyDL+    (#const KEY_IL)            -> KeyIL+    (#const KEY_DC)            -> KeyDC+    (#const KEY_IC)            -> KeyIC+    (#const KEY_EIC)           -> KeyEIC+    (#const KEY_CLEAR)         -> KeyClear+    (#const KEY_EOS)           -> KeyEOS+    (#const KEY_EOL)           -> KeyEOL+    (#const KEY_SF)            -> KeySF+    (#const KEY_SR)            -> KeySR+    (#const KEY_NPAGE)         -> KeyNPage+    (#const KEY_PPAGE)         -> KeyPPage+    (#const KEY_STAB)          -> KeySTab+    (#const KEY_CTAB)          -> KeyCTab+    (#const KEY_CATAB)         -> KeyCATab+    (#const KEY_ENTER)         -> KeyEnter+    (#const KEY_SRESET)        -> KeySReset+    (#const KEY_RESET)         -> KeyReset+    (#const KEY_PRINT)         -> KeyPrint+    (#const KEY_LL)            -> KeyLL+    (#const KEY_A1)            -> KeyA1+    (#const KEY_A3)            -> KeyA3+    (#const KEY_B2)            -> KeyB2+    (#const KEY_C1)            -> KeyC1+    (#const KEY_C3)            -> KeyC3+    (#const KEY_BTAB)          -> KeyBTab+    (#const KEY_BEG)           -> KeyBeg+    (#const KEY_CANCEL)        -> KeyCancel+    (#const KEY_CLOSE)         -> KeyClose+    (#const KEY_COMMAND)       -> KeyCommand+    (#const KEY_COPY)          -> KeyCopy+    (#const KEY_CREATE)        -> KeyCreate+    (#const KEY_END)           -> KeyEnd+    (#const KEY_EXIT)          -> KeyExit+    (#const KEY_FIND)          -> KeyFind+    (#const KEY_HELP)          -> KeyHelp+    (#const KEY_MARK)          -> KeyMark+    (#const KEY_MESSAGE)       -> KeyMessage+    (#const KEY_MOVE)          -> KeyMove+    (#const KEY_NEXT)          -> KeyNext+    (#const KEY_OPEN)          -> KeyOpen+    (#const KEY_OPTIONS)       -> KeyOptions+    (#const KEY_PREVIOUS)      -> KeyPrevious+    (#const KEY_REDO)          -> KeyRedo+    (#const KEY_REFERENCE)     -> KeyReference+    (#const KEY_REFRESH)       -> KeyRefresh+    (#const KEY_REPLACE)       -> KeyReplace+    (#const KEY_RESTART)       -> KeyRestart+    (#const KEY_RESUME)        -> KeyResume+    (#const KEY_SAVE)          -> KeySave+    (#const KEY_SBEG)          -> KeySBeg+    (#const KEY_SCANCEL)       -> KeySCancel+    (#const KEY_SCOMMAND)      -> KeySCommand+    (#const KEY_SCOPY)         -> KeySCopy+    (#const KEY_SCREATE)       -> KeySCreate+    (#const KEY_SDC)           -> KeySDC+    (#const KEY_SDL)           -> KeySDL+    (#const KEY_SELECT)        -> KeySelect+    (#const KEY_SEND)          -> KeySEnd+    (#const KEY_SEOL)          -> KeySEOL+    (#const KEY_SEXIT)         -> KeySExit+    (#const KEY_SFIND)         -> KeySFind+    (#const KEY_SHELP)         -> KeySHelp+    (#const KEY_SHOME)         -> KeySHome+    (#const KEY_SIC)           -> KeySIC+    (#const KEY_SLEFT)         -> KeySLeft+    (#const KEY_SMESSAGE)      -> KeySMessage+    (#const KEY_SMOVE)         -> KeySMove+    (#const KEY_SNEXT)         -> KeySNext+    (#const KEY_SOPTIONS)      -> KeySOptions+    (#const KEY_SPREVIOUS)     -> KeySPrevious+    (#const KEY_SPRINT)        -> KeySPrint+    (#const KEY_SREDO)         -> KeySRedo+    (#const KEY_SREPLACE)      -> KeySReplace+    (#const KEY_SRIGHT)        -> KeySRight+    (#const KEY_SRSUME)        -> KeySRsume+    (#const KEY_SSAVE)         -> KeySSave+    (#const KEY_SSUSPEND)      -> KeySSuspend+    (#const KEY_SUNDO)         -> KeySUndo+    (#const KEY_SUSPEND)       -> KeySuspend+    (#const KEY_UNDO)          -> KeyUndo+#ifdef KEY_RESIZE+    (#const KEY_RESIZE)        -> KeyResize+#endif+#ifdef KEY_MOUSE+    (#const KEY_MOUSE)         -> KeyMouse+#endif+    _                          -> KeyUnknown (fromIntegral key)++keyResizeCode :: Maybe CInt+#ifdef KEY_RESIZE+keyResizeCode = Just (#const KEY_RESIZE)+#else+keyResizeCode = Nothing+#endif++cERR :: CInt+cERR = #const ERR++cKEY_UP, cKEY_DOWN, cKEY_LEFT, cKEY_RIGHT :: ChType+cKEY_UP = #const KEY_UP+cKEY_DOWN = #const KEY_DOWN+cKEY_LEFT = #const KEY_LEFT+cKEY_RIGHT = #const KEY_RIGHT++cTRUE :: NBool+cTRUE = #const TRUE++-- ncurses ungetch and Haskell's threadWaitRead do not work together well.+-- So I decided to implement my own input queue.+ungetCh :: (Integral a) => a -> IO ()+ungetCh i = do+    debug "ungetCh called"+    writeChan inputBuf (BufDirect (fi i))++data BufData+    = -- | Data directly available+      BufDirect CInt+    | -- | Data can be obtained by calling getch+      DataViaGetch++inputBuf :: Chan BufData+inputBuf = unsafePerformIO newChan+{-# NOINLINE inputBuf #-}++getchToInputBuf :: IO ()+getchToInputBuf = do+    threadWaitRead (fi (0 :: Int))+    {- From the (n)curses manpage:+    Programmers  concerned  about portability should be prepared for either+    of two cases: (a) signal receipt does not interrupt getch;  (b)  signal+    receipt  interrupts getch and causes it to return ERR with errno set to+    EINTR.  Under the ncurses implementation, handled signals never  inter$B!>(B+    rupt getch.+    -}+    -- we only signalize that getch can now called without getting blocked.+    -- directly calling `getch' might result in losing the character just+    -- read (race condition).+    debug "now input available on stdin"+    writeChan inputBuf DataViaGetch++-- | Read a single character from the window.+getCh :: IO Key+getCh = do+  debug "getCh called"+  tid <- forkIO getchToInputBuf+  d <- readChan inputBuf+  -- we can kill the thread safely, because the thread does not read any data+  -- via getch+  killThread tid+  v <- case d of+         BufDirect x ->+           do debug "getCh: getting data directly from buffer"+              return x+         DataViaGetch ->+           do debug "getCh: getting data via getch"+              getch -- won't block!+  case v of+    (#const ERR) -> -- NO CODE IN THIS LINE+        do e <- getErrno+           if e `elem` [eAGAIN, eINTR]+              then do debug "Curses.getCh returned eAGAIN or eINTR"+                      getCh+              else throwErrno "HSCurses.Curses.getch"+    k -> let k' = decodeKey k+             in do debug ("getCh: result = " ++ show k')+                   return k'++resizeTerminal :: Int -> Int -> IO ()++#ifdef HAVE_RESIZETERM+resizeTerminal a b = throwIfErr_ "resizeterm"  $ resizeterm (fi a) (fi b)++foreign import ccall unsafe "HSCurses.h resizeterm"+    resizeterm :: CInt -> CInt -> IO CInt+#else+resizeTerminal _ _ = return ()+#endif++-- | The SIGWINCH signal is sent whenever the terminal size changes. This signal+-- is not available on all platforms, so it is a |Maybe| value.++#ifdef mingw32_HOST_OS+type Signal = CInt+#endif++cursesSigWinch :: Maybe Signal+#ifdef SIGWINCH+cursesSigWinch = Just (#const SIGWINCH)+#else+cursesSigWinch = Nothing+#endif++-- | A test case printing out some common attributes.+cursesTest :: IO ()+cursesTest = do+    initScr+    hc <- hasColors+    when hc startColor+    ccc <- canChangeColor+    (ys,xs) <- scrSize+    cp <- colorPairs+    cs <- colors+    endWin+    putStrLn $ "ScreenSize: " ++ show (xs,ys)+    putStrLn $ "hasColors: " ++ show hc+    putStrLn $ "canChangeColor: " ++ show ccc+    putStrLn $ "colorPairs: " ++ show cp+    putStrLn $ "colors: " ++ show cs++-----------------------------------------------------------------------------+-- * Mouse routines+-----------------------------------------------------------------------------++data MouseEvent = MouseEvent+    { mouseEventId :: CInt+    , mouseEventX :: CInt+    , mouseEventY :: CInt+    , mouseEventZ :: CInt+    , mouseEventButton :: [ButtonEvent]+    }+    deriving (Show)++instance Storable MouseEvent where+    sizeOf _ = (#size MEVENT)+    alignment _ = (#alignment MEVENT)+    peek ptr = do+        id' <- (#peek MEVENT, id) ptr+        x <- (#peek MEVENT, x) ptr+        y <- (#peek MEVENT, y) ptr+        z <- (#peek MEVENT, z) ptr+        bstate :: (#type mmask_t) <- (#peek MEVENT, bstate) ptr+        pure $! MouseEvent id' x y z (besFromMouseMask bstate)+    poke ptr (MouseEvent id' x y z bstate) = do+        (#poke MEVENT, id) ptr id'+        (#poke MEVENT, x) ptr x+        (#poke MEVENT, y) ptr y+        (#poke MEVENT, z) ptr z+        (#poke MEVENT, bstate) ptr (besToMouseMask bstate)++foreign import ccall unsafe "HSCurses.h getmouse"+    getmouse :: Ptr MouseEvent -> IO CInt++getMouse :: (MonadIO m) => m (Maybe MouseEvent)+getMouse = liftIO $ alloca $ \ptr -> do+    res <- getmouse ptr+    if res == (# const OK)+        then Just <$> peek ptr+        else pure Nothing++data ButtonEvent+    = ButtonPressed Int+    | ButtonReleased Int+    | ButtonClicked Int+    | ButtonDoubleClicked Int+    | ButtonTripleClicked Int+    | ButtonShift+    | ButtonControl+    | ButtonAlt+    deriving (Eq, Show)++withMouseEventMask :: (MonadIO m) => [ButtonEvent] -> m a -> m a+withAllMouseEvents :: (MonadIO m) => m a -> m a++#ifdef KEY_MOUSE++foreign import ccall unsafe "HSCurses.h mousemask"+    mousemask :: (#type mmask_t) -> Ptr (#type mmask_t) -> IO (#type mmask_t)++-- TODO: bracket instead?+withMouseEventMask bes action = do+    ov <- liftIO $ alloca (\a ->  mousemask (besToMouseMask bes) a >> peek a)+    r <- action+    liftIO $ mousemask ov nullPtr+    return r++withAllMouseEvents action = do+    ov <- liftIO $ alloca (\a ->  mousemask (#const ALL_MOUSE_EVENTS) a >> peek a)+    r <- action+    liftIO $ mousemask ov nullPtr+    return r++besToMouseMask :: [ButtonEvent] -> (#type mmask_t)+besToMouseMask bes = foldl' (.|.) 0 (map cb bes) where+    cb (ButtonPressed 1) = (#const BUTTON1_PRESSED)+    cb (ButtonPressed 2) = (#const BUTTON2_PRESSED)+    cb (ButtonPressed 3) = (#const BUTTON3_PRESSED)+    cb (ButtonPressed 4) = (#const BUTTON4_PRESSED)+    cb (ButtonReleased 1) = (#const BUTTON1_RELEASED)+    cb (ButtonReleased 2) = (#const BUTTON2_RELEASED)+    cb (ButtonReleased 3) = (#const BUTTON3_RELEASED)+    cb (ButtonReleased 4) = (#const BUTTON4_RELEASED)+    cb (ButtonClicked 1) = (#const BUTTON1_CLICKED)+    cb (ButtonClicked 2) = (#const BUTTON2_CLICKED)+    cb (ButtonClicked 3) = (#const BUTTON3_CLICKED)+    cb (ButtonClicked 4) = (#const BUTTON4_CLICKED)+    cb (ButtonDoubleClicked 1) = (#const BUTTON1_DOUBLE_CLICKED)+    cb (ButtonDoubleClicked 2) = (#const BUTTON2_DOUBLE_CLICKED)+    cb (ButtonDoubleClicked 3) = (#const BUTTON3_DOUBLE_CLICKED)+    cb (ButtonDoubleClicked 4) = (#const BUTTON4_DOUBLE_CLICKED)+    cb (ButtonTripleClicked 1) = (#const BUTTON1_TRIPLE_CLICKED)+    cb (ButtonTripleClicked 2) = (#const BUTTON2_TRIPLE_CLICKED)+    cb (ButtonTripleClicked 3) = (#const BUTTON3_TRIPLE_CLICKED)+    cb (ButtonTripleClicked 4) = (#const BUTTON4_TRIPLE_CLICKED)+#if NCURSES_MOUSE_VERSION > 1+    cb (ButtonPressed 5) = (#const BUTTON5_PRESSED)+    cb (ButtonReleased 5) = (#const BUTTON5_RELEASED)+    cb (ButtonClicked 5) = (#const BUTTON5_CLICKED)+    cb (ButtonDoubleClicked 5) = (#const BUTTON5_DOUBLE_CLICKED)+    cb (ButtonTripleClicked 5) = (#const BUTTON5_TRIPLE_CLICKED)+#endif+    cb ButtonShift = (#const BUTTON_SHIFT)+    cb ButtonAlt = (#const BUTTON_ALT)+#ifdef BUTTON_CTRL+    cb ButtonControl = (#const BUTTON_CTRL)+#else+    cb ButtonControl = (#const BUTTON_CONTROL)+#endif+    cb _ = 0++besFromMouseMask :: (#type mmask_t) -> [ButtonEvent]+besFromMouseMask mmask =+    foldl'+        (\evts (c, evt) -> if mmask .&. c /= 0 then evt : evts else evts)+        mempty+        mappings+  where+    mappings =+        [ ((#const BUTTON1_PRESSED), ButtonPressed 1)+        , ((#const BUTTON2_PRESSED), ButtonPressed 2)+        , ((#const BUTTON3_PRESSED), ButtonPressed 3)+        , ((#const BUTTON4_PRESSED), ButtonPressed 4)+        , ((#const BUTTON1_RELEASED), ButtonReleased 1)+        , ((#const BUTTON2_RELEASED), ButtonReleased 2)+        , ((#const BUTTON3_RELEASED), ButtonReleased 3)+        , ((#const BUTTON4_RELEASED), ButtonReleased 4)+        , ((#const BUTTON1_CLICKED), ButtonClicked 1)+        , ((#const BUTTON2_CLICKED), ButtonClicked 2)+        , ((#const BUTTON3_CLICKED), ButtonClicked 3)+        , ((#const BUTTON4_CLICKED), ButtonClicked 4)+        , ((#const BUTTON1_DOUBLE_CLICKED), ButtonDoubleClicked 1)+        , ((#const BUTTON2_DOUBLE_CLICKED), ButtonDoubleClicked 2)+        , ((#const BUTTON3_DOUBLE_CLICKED), ButtonDoubleClicked 3)+        , ((#const BUTTON4_DOUBLE_CLICKED), ButtonDoubleClicked 4)+        , ((#const BUTTON1_TRIPLE_CLICKED), ButtonTripleClicked 1)+        , ((#const BUTTON2_TRIPLE_CLICKED), ButtonTripleClicked 2)+        , ((#const BUTTON3_TRIPLE_CLICKED), ButtonTripleClicked 3)+        , ((#const BUTTON4_TRIPLE_CLICKED), ButtonTripleClicked 4)+#if NCURSES_MOUSE_VERSION > 1+        , ((#const BUTTON5_PRESSED), ButtonPressed 5)+        , ((#const BUTTON5_RELEASED), ButtonReleased 5)+        , ((#const BUTTON5_CLICKED), ButtonClicked 5)+        , ((#const BUTTON5_DOUBLE_CLICKED), ButtonDoubleClicked 5)+        , ((#const BUTTON5_TRIPLE_CLICKED), ButtonTripleClicked 5)+#endif+        , ((#const BUTTON_SHIFT), ButtonShift)+        , ((#const BUTTON_ALT), ButtonAlt)+#ifdef BUTTON_CTRL+        , ((#const BUTTON_CTRL), ButtonControl)+#else+        , ((#const BUTTON_CONTROL), ButtonControl)+#endif+        ]++#else++withMouseEventMask _ a = a+withAllMouseEvents = id++#endif++ulCorner :: Char+ulCorner = chr 0x250C++llCorner :: Char+llCorner = chr 0x2514++urCorner :: Char+urCorner = chr 0x2510++lrCorner :: Char+lrCorner = chr 0x2518++rTee :: Char+rTee = chr 0x2524++lTee :: Char+lTee = chr 0x251C++bTee :: Char+bTee = chr 0x2534++tTee :: Char+tTee = chr 0x252C++hLine :: Char+hLine = chr 0x2500++vLine :: Char+vLine = chr 0x2502++plus :: Char+plus = chr 0x253C++s1 :: Char+s1 = chr 0x23BA -- was: 0xF800++s9 :: Char+s9 = chr 0x23BD -- was: 0xF804++diamond :: Char+diamond = chr 0x25C6++ckBoard :: Char+ckBoard = chr 0x2592++degree :: Char+degree = chr 0x00B0++plMinus :: Char+plMinus = chr 0x00B1++bullet :: Char+bullet = chr 0x00B7++lArrow :: Char+lArrow = chr 0x2190++rArrow :: Char+rArrow = chr 0x2192++dArrow :: Char+dArrow = chr 0x2193++uArrow :: Char+uArrow = chr 0x2191++board :: Char+board = chr 0x2591++lantern :: Char+lantern = chr 0x256C++block :: Char+block = chr 0x2588++s3 :: Char+s3 = chr 0x23BB -- was: 0xF801++s7 :: Char+s7 = chr 0x23BC -- was: 0xF803++lEqual :: Char+lEqual = chr 0x2264++gEqual :: Char+gEqual = chr 0x2265++pi :: Char+pi = chr 0x03C0++nEqual :: Char+nEqual = chr 0x2260++sterling :: Char+sterling = chr 0x00A3++recognize :: Char -> IO a -> (ChType -> IO a) -> IO a+recognize _ch noConvert _convert = noConvert -- Handle the most common case first.
UI/HSCurses/CursesHelper.hs view
@@ -27,43 +27,65 @@ -- License along with this library; if not, write to the Free Software -- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA - module UI.HSCurses.CursesHelper (--        -- * UI initialisation-        start, end, suspend, resizeui,+    -- UI initialisation+    start,+    end,+    suspend,+    resizeui, -        -- * Input-        getKey,+    -- Input+    getKey, -        -- * Drawing-        drawLine, drawCursor,+    -- Drawing+    drawLine,+    drawCursor, -        -- * Navigation-        gotoTop,+    -- Navigation+    gotoTop, -        -- * Colors-        ForegroundColor(..), BackgroundColor(..),-        defaultColor, black, red, green, yellow, blue, magenta, cyan, white,+    -- Colors+    ForegroundColor (..),+    BackgroundColor (..),+    defaultColor,+    black,+    red,+    green,+    yellow,+    blue,+    magenta,+    cyan,+    white, -        -- * Attributes-        Attribute(..), convertAttributes,+    -- Attributes+    Attribute (..),+    convertAttributes, -        -- * Style-        Style(..), CursesStyle, mkCursesStyle, changeCursesStyle,-        setStyle, wSetStyle, resetStyle, wResetStyle, convertStyles,-        defaultStyle, defaultCursesStyle, withStyle, wWithStyle,+    -- Style+    Style (..),+    CursesStyle,+    mkCursesStyle,+    changeCursesStyle,+    setStyle,+    wSetStyle,+    resetStyle,+    wResetStyle,+    convertStyles,+    defaultStyle,+    defaultCursesStyle,+    withStyle,+    wWithStyle, -        -- * Keys-        displayKey,+    -- Keys+    displayKey, -        -- * Helpers-        withCursor, withProgram-  )   where+    -- Helpers+    withCursor,+    withProgram,+) where -import UI.HSCurses.Curses hiding ( refresh, Window )+import UI.HSCurses.Curses as Curses import UI.HSCurses.Logging-import qualified UI.HSCurses.Curses as Curses  import Data.Char import Data.Maybe@@ -73,49 +95,44 @@ import Control.Monad.Catch (MonadCatch, bracket, bracket_) #define MonadMask MonadCatch #endif++import Control.Monad import Control.Monad.Trans  #ifndef mingw32_HOST_OS import System.Posix.Signals #endif ---------- -- | @start@ initializes the UI and grabs the keyboard. -- -- This function installs a handler for the SIGWINCH signal -- which writes the KEY_RESIZE key to the input queue (if KEY_RESIZE and -- and SIGWINCH are both available).--- start :: IO () start = do-    Curses.initCurses                   -- initialise the screen+    Curses.initCurses -- initialise the screen     Curses.resetParams-    Curses.keypad Curses.stdScr True    -- grab the keyboard+    Curses.keypad Curses.stdScr True -- grab the keyboard     case (Curses.cursesSigWinch, Curses.keyResizeCode) of #ifndef mingw32_HOST_OS-      (Just sig, Just key) ->-          do installHandler sig (Catch $ sigwinch sig key) Nothing-             return ()+        (Just sig, Just key) ->+            void $ installHandler sig (Catch $ sigwinch sig key) Nothing #endif-      _ -> debug ("cannot install SIGWINCH handler: signal=" ++-                  show Curses.cursesSigWinch ++ ", KEY_RESIZE=" ++-                  show Curses.keyResizeCode)+        _ ->+            debug $+                "cannot install SIGWINCH handler: signal="+                    ++ show Curses.cursesSigWinch+                    ++ ", KEY_RESIZE="+                    ++ show Curses.keyResizeCode #ifndef mingw32_HOST_OS-    where sigwinch sig key =-              do debug "SIGWINCH signal received"-                 Curses.ungetCh key-                 installHandler sig (Catch $ sigwinch sig key) Nothing-                 return ()+  where+    sigwinch sig key = do+        debug "SIGWINCH signal received"+        Curses.ungetCh key+        void $ installHandler sig (Catch $ sigwinch sig key) Nothing #endif ----- -- | Clean up and go home.--- end :: IO () end = do Curses.endWin -- Refresh is needed on linux. grr.@@ -123,9 +140,7 @@          Curses.update #endif --- -- | Suspend the program.--- suspend :: IO () #ifndef mingw32_HOST_OS suspend = raiseSignal sigTSTP@@ -133,78 +148,60 @@ suspend = return () #endif --- -- | @getKey refresh@ reads a key. -- -- The @refresh@ function is used to redraw the screen when the terminal size -- changes (see the documentatio of @start@ for a discussion of the problem).----getKey :: MonadIO m => m () -> m Key+getKey :: (MonadIO m) => m () -> m Key getKey refresh = do     k <- liftIO $ Curses.getCh     debug ("getKey: " ++ show k)     case k of-      KeyResize ->-          do refresh-             getKey refresh-      _ -> return k-+        KeyResize ->+            do+                refresh+                getKey refresh+        _ -> return k --- -- | @drawLine n s@ draws @n@ characters of string @s@.--- drawLine :: Int -> String -> IO () -- lazy version is faster than calculating length of s drawLine w s = Curses.wAddStr Curses.stdScr $! take w (s ++ repeat ' ') --- -- | Draw the cursor at the given position.----drawCursor :: (Int,Int) -> (Int, Int) -> IO ()-drawCursor (o_y,o_x) (y,x) = withCursor Curses.CursorVisible $ do+drawCursor :: (Int, Int) -> (Int, Int) -> IO ()+drawCursor (o_y, o_x) (y, x) = withCursor Curses.CursorVisible $ do     gotoTop-    (h,w) <- scrSize-    Curses.wMove Curses.stdScr (min (h-1) (o_y + y)) (min (w-1) (o_x + x))+    (h, w) <- scrSize+    Curses.wMove Curses.stdScr (min (h - 1) (o_y + y)) (min (w - 1) (o_x + x)) --- -- | Move cursor to origin of stdScr.--- gotoTop :: IO () gotoTop = Curses.wMove Curses.stdScr 0 0 ---- -- | Resize the window -- From "Writing Programs with NCURSES", by Eric S. Raymond and -- Zeyd M. Ben-Halim-------resizeui :: IO (Int,Int)+resizeui :: IO (Int, Int) resizeui = do     Curses.endWin     Curses.refresh     Curses.scrSize ------------------------------------------------------------------------------ -- | Basic colors.--- defaultColor :: Curses.Color defaultColor = fromJust $ Curses.color "default"  black, red, green, yellow, blue, magenta, cyan, white :: Curses.Color-black     = fromJust $ Curses.color "black"-red       = fromJust $ Curses.color "red"-green     = fromJust $ Curses.color "green"-yellow    = fromJust $ Curses.color "yellow"-blue      = fromJust $ Curses.color "blue"-magenta   = fromJust $ Curses.color "magenta"-cyan      = fromJust $ Curses.color "cyan"-white     = fromJust $ Curses.color "white"+black = fromJust $ Curses.color "black"+red = fromJust $ Curses.color "red"+green = fromJust $ Curses.color "green"+yellow = fromJust $ Curses.color "yellow"+blue = fromJust $ Curses.color "blue"+magenta = fromJust $ Curses.color "magenta"+cyan = fromJust $ Curses.color "cyan"+white = fromJust $ Curses.color "white" --- -- | Converts a list of 'Curses.Color' pairs (foreground color and --   background color) into the curses representation 'Curses.Pair'. --@@ -212,20 +209,28 @@ -- -- (not visible outside this module) colorsToPairs :: [(Curses.Color, Curses.Color)] -> IO [Curses.Pair]-colorsToPairs cs =-    do p <- Curses.colorPairs-       let nColors = length cs-           blackWhite = p < nColors-       if blackWhite-          then trace ("Terminal does not support enough colors. Number of " ++-                      " colors requested: " ++ show nColors ++-                      ". Number of colors supported: " ++ show p)-                 return $ take nColors (repeat (Curses.Pair 0))-          else mapM toPairs (zip [1..] cs)-     where toPairs (n, (fg, bg)) =-               let p = Curses.Pair n-               in do Curses.initPair p fg bg-                     return p+colorsToPairs cs = do+    p <- Curses.colorPairs+    let nColors = length cs+        blackWhite = p < nColors+    if blackWhite+        then+            trace+                ( "Terminal does not support enough colors. Number of "+                    ++ " colors requested: "+                    ++ show nColors+                    ++ ". Number of colors supported: "+                    ++ show p+                )+                return+                $ take nColors (repeat (Curses.Pair 0))+        else mapM toPairs (zip [1 ..] cs)+  where+    toPairs (n, (fg, bg)) =+        let p = Curses.Pair n+         in do+                Curses.initPair p fg bg+                return p  ------------------------------------------------------------------------ -- Nicer, user-visible color defs.@@ -234,9 +239,7 @@ -- from erroneously constructing bright colors for dark backgrounds, -- which doesn't work. --- -- | Foreground colors.--- data ForegroundColor     = BlackF     | GreyF@@ -257,9 +260,7 @@     | DefaultF     deriving (Eq, Show) --- -- | Background colors.--- data BackgroundColor     = BlackB     | DarkRedB@@ -272,107 +273,94 @@     | DefaultB     deriving (Eq, Show) --- -- | Mapping abstract colours to ncurses attributes and colours -- -- (not visible outside this module)- convertBg :: BackgroundColor -> ([Attribute], Curses.Color) convertBg c = case c of-    BlackB      -> ([], black)-    DarkRedB    -> ([], red)-    DarkGreenB  -> ([], green)-    BrownB      -> ([], yellow)-    DarkBlueB   -> ([], blue)-    PurpleB     -> ([], magenta)-    DarkCyanB   -> ([], cyan)-    WhiteB      -> ([], white)-    DefaultB    -> ([], defaultColor)+    BlackB -> ([], black)+    DarkRedB -> ([], red)+    DarkGreenB -> ([], green)+    BrownB -> ([], yellow)+    DarkBlueB -> ([], blue)+    PurpleB -> ([], magenta)+    DarkCyanB -> ([], cyan)+    WhiteB -> ([], white)+    DefaultB -> ([], defaultColor)  convertFg :: ForegroundColor -> ([Attribute], Curses.Color) convertFg c = case c of-    BlackF       -> ([], black)-    GreyF        -> ([Bold], black)-    DarkRedF     -> ([], red)-    RedF         -> ([Bold], red)-    DarkGreenF   -> ([], green)-    GreenF       -> ([Bold], green)-    BrownF       -> ([], yellow)-    YellowF      -> ([Bold], yellow)-    DarkBlueF    -> ([], blue)-    BlueF        -> ([Bold], blue)-    PurpleF      -> ([], magenta)-    MagentaF     -> ([Bold], magenta)-    DarkCyanF    -> ([], cyan)-    CyanF        -> ([Bold], cyan)-    WhiteF       -> ([], white)+    BlackF -> ([], black)+    GreyF -> ([Bold], black)+    DarkRedF -> ([], red)+    RedF -> ([Bold], red)+    DarkGreenF -> ([], green)+    GreenF -> ([Bold], green)+    BrownF -> ([], yellow)+    YellowF -> ([Bold], yellow)+    DarkBlueF -> ([], blue)+    BlueF -> ([Bold], blue)+    PurpleF -> ([], magenta)+    MagentaF -> ([Bold], magenta)+    DarkCyanF -> ([], cyan)+    CyanF -> ([Bold], cyan)+    WhiteF -> ([], white)     BrightWhiteF -> ([Bold], white)-    DefaultF     -> ([], defaultColor)-+    DefaultF -> ([], defaultColor) ---------------------------------------------------------------------------- -- | Abstractions for some commonly used attributes.----data Attribute = Bold-               | Underline-               | Dim-               | Reverse-               | Blink-               deriving (Eq, Show)+data Attribute+    = Bold+    | Underline+    | Dim+    | Reverse+    | Blink+    deriving (Eq, Show) --- -- | Converts an abstract attribute list into its curses representation.--- convertAttributes :: [Attribute] -> Curses.Attr convertAttributes =     foldr setAttrs Curses.attr0-    where setAttrs Bold = setBoldA-          setAttrs Underline = setUnderlineA-          setAttrs Dim = setDimA-          setAttrs Reverse = setReverseA-          setAttrs Blink = setBlinkA+  where+    setAttrs Bold = setBoldA+    setAttrs Underline = setUnderlineA+    setAttrs Dim = setDimA+    setAttrs Reverse = setReverseA+    setAttrs Blink = setBlinkA -setBoldA, setUnderlineA, setDimA,-  setReverseA, setBlinkA :: Curses.Attr -> Curses.Attr+setBoldA+    , setUnderlineA+    , setDimA+    , setReverseA+    , setBlinkA ::+        Curses.Attr -> Curses.Attr setBoldA = flip Curses.setBold True setUnderlineA = flip Curses.setUnderline True setDimA = flip Curses.setDim True-setReverseA = flip Curses.setReverse   True+setReverseA = flip Curses.setReverse True setBlinkA = flip Curses.setBlink True ------------------------------------------------------------------------------- | A humand-readable style.----data Style = Style ForegroundColor BackgroundColor-           | AttributeStyle [Attribute] ForegroundColor BackgroundColor-           | ColorlessStyle [Attribute]-           deriving (Eq, Show)+-- | A human-readable style.+data Style+    = Style ForegroundColor BackgroundColor+    | AttributeStyle [Attribute] ForegroundColor BackgroundColor+    | ColorlessStyle [Attribute]+    deriving (Eq, Show)  defaultStyle :: Style defaultStyle = Style DefaultF DefaultB --- -- | A style which uses the internal curses representations for --   attributes and colors.----data CursesStyle = CursesStyle Curses.Attr Curses.Pair-                 | ColorlessCursesStyle Curses.Attr-                 deriving (Eq, Show)--{--instance Show CursesStyle where-    show (CursesStyle _ _) = "CursesStyle"-    show (ColorlessCursesStyle _) = "ColorlessCursesStyle"--}+data CursesStyle+    = CursesStyle Curses.Attr Curses.Pair+    | ColorlessCursesStyle Curses.Attr+    deriving (Eq, Show)  mkCursesStyle :: [Attribute] -> CursesStyle mkCursesStyle attrs = ColorlessCursesStyle (convertAttributes attrs) --- -- | Changes the attributes of the given CursesStyle.--- changeCursesStyle :: CursesStyle -> [Attribute] -> CursesStyle changeCursesStyle (CursesStyle _ p) attrs =     CursesStyle (convertAttributes attrs) p@@ -381,26 +369,22 @@ defaultCursesStyle :: CursesStyle defaultCursesStyle = CursesStyle Curses.attr0 (Curses.Pair 0) --- -- | Reset the screen to normal values--- resetStyle :: IO () resetStyle = wResetStyle Curses.stdScr  wResetStyle :: Curses.Window -> IO () wResetStyle = flip wSetStyle defaultCursesStyle --- -- | Manipulate the current style of the standard screen--- setStyle :: CursesStyle -> IO () setStyle = wSetStyle Curses.stdScr  wSetStyle :: Curses.Window -> CursesStyle -> IO () wSetStyle window (CursesStyle a p) = Curses.wAttrSet window (a, p)-wSetStyle window (ColorlessCursesStyle a) =-    do (_, p) <- Curses.wAttrGet window-       Curses.wAttrSet window (a, p)+wSetStyle window (ColorlessCursesStyle a) = do+    (_, p) <- Curses.wAttrGet window+    Curses.wAttrSet window (a, p)  withStyle :: (MonadIO m, MonadMask m) => CursesStyle -> m a -> m a withStyle = wWithStyle Curses.stdScr@@ -408,58 +392,57 @@ wWithStyle :: (MonadIO m, MonadMask m) => Curses.Window -> CursesStyle -> m a -> m a wWithStyle window style action =     bracket-        (liftIO $ do old <- Curses.wAttrGet window    -- before-                     wSetStyle window style-                     return old)+        ( liftIO $ do+            old <- Curses.wAttrGet window -- before+            wSetStyle window style+            return old+        )         (\old -> liftIO $ Curses.wAttrSet window old) -- after-        (\_ -> action)                                -- do this+        (\_ -> action) -- do this --- -- | Converts a list of human-readable styles into the corresponding --   curses representation. -- --   This function should be called exactly once at application startup --   for all styles of the application. convertStyles :: [Style] -> IO [CursesStyle]-convertStyles styleList =-    do let (attrs, cs) = unzip $ map convertStyle styleList-           cursesAttrs = map convertAttributes attrs-       cursesPairs <- colorsToPairs' cs-       let res = zipWith toCursesStyle cursesAttrs cursesPairs-       trace ("convertStyles: " ++ show (zip styleList res)) (return res)-    where convertStyle (Style fg bg) = convertStyle (AttributeStyle [] fg bg)-          convertStyle (AttributeStyle attrs fg bg) =-              let (afg, cfg) = convertFg fg-                  (abg, cbg) = convertBg bg-              in (afg ++ abg ++ attrs, Just (cfg, cbg))-          convertStyle (ColorlessStyle attrs) = (attrs, Nothing)-          colorsToPairs' cs =-              do pairs <- colorsToPairs (catMaybes cs)-                 return $ mergeNothing cs pairs-          mergeNothing (Just _:crest) (p:prest) = Just p-                                                  : mergeNothing crest prest-          mergeNothing (Nothing:crest) ps = Nothing : mergeNothing crest ps-          mergeNothing [] [] = []-          toCursesStyle cursesAttrs Nothing =-              ColorlessCursesStyle cursesAttrs-          toCursesStyle cursesAttrs (Just cursesPair) =-              CursesStyle cursesAttrs cursesPair-------------------------------------------------------------------------------- | Converting keys to humand-readable strings---+convertStyles styleList = do+    let (attrs, cs) = unzip $ map convertStyle styleList+        cursesAttrs = map convertAttributes attrs+    cursesPairs <- colorsToPairs' cs+    let res = zipWith toCursesStyle cursesAttrs cursesPairs+    trace ("convertStyles: " ++ show (zip styleList res)) (return res)+  where+    convertStyle (Style fg bg) = convertStyle (AttributeStyle [] fg bg)+    convertStyle (AttributeStyle attrs fg bg) =+        let (afg, cfg) = convertFg fg+            (abg, cbg) = convertBg bg+         in (afg ++ abg ++ attrs, Just (cfg, cbg))+    convertStyle (ColorlessStyle attrs) = (attrs, Nothing)+    colorsToPairs' cs = do+        pairs <- colorsToPairs (catMaybes cs)+        return $ mergeNothing cs pairs+    mergeNothing (Just _ : crest) (p : prest) =+        Just p+            : mergeNothing crest prest+    mergeNothing (Nothing : crest) ps = Nothing : mergeNothing crest ps+    mergeNothing _ _ = []+    toCursesStyle cursesAttrs Nothing =+        ColorlessCursesStyle cursesAttrs+    toCursesStyle cursesAttrs (Just cursesPair) =+        CursesStyle cursesAttrs cursesPair +-- | Converting keys to human-readable strings displayKey :: Key -> String displayKey (KeyChar ' ') = "<Space>" displayKey (KeyChar '\t') = "<Tab>" displayKey (KeyChar '\r') = "<Enter>" displayKey (KeyChar c)     | isPrint c = [c]-displayKey (KeyChar c)  -- Control-    | ord '\^A' <= ord c && ord c <= ord '\^Z'-        = let c' = chr $ ord c - ord '\^A' + ord 'a'-              in '^':[toUpper c']+displayKey (KeyChar c) -- Control+    | ord '\^A' <= ord c && ord c <= ord '\^Z' =+        let c' = chr $ ord c - ord '\^A' + ord 'a'+         in '^' : [toUpper c'] displayKey (KeyChar c) = show c displayKey KeyDown = "<Down>" displayKey KeyUp = "<Up>"@@ -479,19 +462,18 @@  ------------------------------------------------------------------------ ----- | Other helpers+-- Other helpers -- ------ | set the cursor, and do action---+-- | Set the cursor, and do action withCursor :: (MonadIO m, MonadMask m) => CursorVisibility -> m a -> m a withCursor nv action =     bracket-        (liftIO $ Curses.cursSet nv)             -- before-        (\vis -> liftIO $ Curses.cursSet vis)    -- after-        (\_ -> action)                           -- do this+        (liftIO $ Curses.cursSet nv) -- before+        (\vis -> liftIO $ Curses.cursSet vis) -- after+        (\_ -> action) -- do this  withProgram :: (MonadIO m, MonadMask m) => m a -> m a-withProgram action = withCursor CursorVisible $-    bracket_ (liftIO endWin) (liftIO flushinp) action+withProgram action =+    withCursor CursorVisible $+        bracket_ (liftIO endWin) (liftIO flushinp) action
UI/HSCurses/IConv.hsc view
@@ -23,34 +23,25 @@ -- | Iconv binding  #if HAVE_ICONV_H-# include <iconv.h>+#include <iconv.h> #endif -module UI.HSCurses.IConv {-(-    IConv,-    iconv,-    iconv_,-    with_iconv,-    to_unicode,-    from_unicode,-    to_unicode_,-    from_unicode_-  )-} where+module UI.HSCurses.IConv where -import UI.HSCurses.CWString          ( peekUTF8StringLen, withUTF8StringLen )+import UI.HSCurses.CWString (peekUTF8StringLen, withUTF8StringLen) -import System.IO.Unsafe ( unsafePerformIO )+import System.IO.Unsafe (unsafePerformIO)  #if !MIN_VERSION_base(4,7,0)-import Foreign hiding ( unsafePerformIO )+import Foreign hiding (unsafePerformIO) #else import Foreign #endif +import Control.Exception (Exception, bracket, try) import Foreign.C-import Control.Exception    ( Exception, try, bracket ) -type IConv = Ptr () --(#type iconv_t)+type IConv = Ptr () -- (#type iconv_t)  err_ptr :: Ptr b -> Bool err_ptr p = p == (plusPtr nullPtr (-1))@@ -58,23 +49,25 @@ throw_if_not_2_big :: String -> IO CSize -> IO CSize throw_if_not_2_big s r_ = do     r <- r_-    if r == fromIntegral (-1 :: Int) then do-        errno <- getErrno-        if errno /= e2BIG then-	    throwErrno s-	  else-	    return r-      else-        return r+    if r == fromIntegral (-1 :: Int)+        then do+            errno <- getErrno+            if errno /= e2BIG+                then+                    throwErrno s+                else+                    return r+        else+            return r  iconv_open :: String -> String -> IO IConv iconv_open to from =     withCString to $         \cto -> withCString from $-            \cfrom -> do-	        throwErrnoIf err_ptr "iconv_open"-		    $ c_iconv_open cto cfrom-+            \cfrom ->+                do+                    throwErrnoIf err_ptr "iconv_open"+                    $ c_iconv_open cto cfrom  iconv_close :: IConv -> IO () iconv_close ic =@@ -90,22 +83,27 @@             alloca $ \outbuf_ptr ->                 alloca $ \outbytesleft_ptr ->                     allocaBytes outbuf_size $ \outbuf -> do-      poke (inbytesleft_ptr :: Ptr CSize) (fromIntegral inbuf_bytes)-      poke inbuf_ptr inbuf-      let loop acc = do-          poke (outbytesleft_ptr :: Ptr CSize) (fromIntegral outbuf_size)-          poke outbuf_ptr outbuf-          ret <- throw_if_not_2_big "c_iconv" $-              c_iconv ic inbuf_ptr inbytesleft_ptr-                         outbuf_ptr outbytesleft_ptr-          left <- peek outbytesleft_ptr-          res <- get_string_fn (castPtr outbuf, outbuf_size - fromIntegral left)-          if ret == fromIntegral (-1 :: Int) then-              loop (acc++res)-            else-              return (acc++res)-      loop []-+                        poke (inbytesleft_ptr :: Ptr CSize) (fromIntegral inbuf_bytes)+                        poke inbuf_ptr inbuf+                        let loop acc = do+                                poke (outbytesleft_ptr :: Ptr CSize) (fromIntegral outbuf_size)+                                poke outbuf_ptr outbuf+                                ret <-+                                    throw_if_not_2_big "c_iconv" $+                                        c_iconv+                                            ic+                                            inbuf_ptr+                                            inbytesleft_ptr+                                            outbuf_ptr+                                            outbytesleft_ptr+                                left <- peek outbytesleft_ptr+                                res <- get_string_fn (castPtr outbuf, outbuf_size - fromIntegral left)+                                if ret == fromIntegral (-1 :: Int)+                                    then+                                        loop (acc ++ res)+                                    else+                                        return (acc ++ res)+                        loop []  with_iconv :: String -> String -> (IConv -> IO a) -> IO a with_iconv to from fn =@@ -116,24 +114,12 @@     withCStringLen str $ do_iconv peekCStringLen ic  -- between 8-bit encodings only-iconv :: Exception e => String -> String -> String -> Either e String+iconv :: (Exception e) => String -> String -> String -> Either e String iconv to from str =     unsafePerformIO $ try $ with_iconv to from (iconv_ str) - #ifdef HAVE_WCHAR_H-{--type CUni = (#type wchar_t)-cuni_size = (#size wchar_t)-unicode_charset = "WCHAR_T" -chartocuni :: Char -> CUni-chartocuni = fromIntegral . ord--cunitochar :: CUni -> Char-cunitochar = chr . fromIntegral--}- cuni_charset :: [Char] cuni_charset = "WCHAR_T" @@ -144,14 +130,13 @@         error "Conversion result contains remainder bytes."       else         peekCWStringLen (buf, chars)-        --liftM (map cunitochar) $ peekArray chars buf  with_cuni :: String -> ((Ptr (#type wchar_t), Int) -> IO String) -> IO String with_cuni str f =     withCWStringLen str $ \(s, l) -> f (s, l*(#size wchar_t))-    --withArray (map chartocuni str) $ \s -> f (s, l*cuni_size)  #else+ -- no CF_WCHAR_SUPPORT  -- Due to endianness problems, it is easiest to do this through UTF-8@@ -169,43 +154,46 @@  to_unicode_ :: String -> String -> IO String to_unicode_ from str =-     with_iconv cuni_charset from $-      \ic -> withCStringLen str $ do_iconv peek_cuni ic+    with_iconv cuni_charset from $+        \ic -> withCStringLen str $ do_iconv peek_cuni ic -to_unicode :: Exception e => String -> String -> Either e String+to_unicode :: (Exception e) => String -> String -> Either e String to_unicode from str =     unsafePerformIO $ try $ to_unicode_ from str  from_unicode_ :: String -> String -> IO String from_unicode_ to str =-     with_iconv to cuni_charset $-      \ic -> with_cuni str $ do_iconv peekCStringLen ic+    with_iconv to cuni_charset $+        \ic -> with_cuni str $ do_iconv peekCStringLen ic -from_unicode :: Exception e => String -> String -> Either e String+from_unicode :: (Exception e) => String -> String -> Either e String from_unicode from str =     unsafePerformIO $ try $ from_unicode_ from str - #ifndef ICONV_LIB_PREFIX -foreign import ccall unsafe "iconv.h iconv_open" c_iconv_open-    :: CString -> CString -> IO IConv--foreign import ccall unsafe "iconv.h iconv_close" c_iconv_close-    :: IConv -> IO CInt+foreign import ccall unsafe "iconv.h iconv_open"+    c_iconv_open ::+        CString -> CString -> IO IConv -foreign import ccall unsafe "iconv.h iconv" c_iconv-    :: IConv -> Ptr a -> Ptr CSize -> Ptr b -> Ptr CSize -> IO CSize+foreign import ccall unsafe "iconv.h iconv_close"+    c_iconv_close ::+        IConv -> IO CInt +foreign import ccall unsafe "iconv.h iconv"+    c_iconv ::+        IConv -> Ptr a -> Ptr CSize -> Ptr b -> Ptr CSize -> IO CSize #else -foreign import ccall unsafe "iconv.h libiconv_open" c_iconv_open-    :: CString -> CString -> IO IConv--foreign import ccall unsafe "iconv.h libiconv_close" c_iconv_close-    :: IConv -> IO CInt+foreign import ccall unsafe "iconv.h libiconv_open"+    c_iconv_open ::+        CString -> CString -> IO IConv -foreign import ccall unsafe "iconv.h libiconv" c_iconv-    :: IConv -> Ptr a -> Ptr CSize -> Ptr b -> Ptr CSize -> IO CSize+foreign import ccall unsafe "iconv.h libiconv_close"+    c_iconv_close ::+        IConv -> IO CInt +foreign import ccall unsafe "iconv.h libiconv"+    c_iconv ::+        IConv -> Ptr a -> Ptr CSize -> Ptr b -> Ptr CSize -> IO CSize #endif
UI/HSCurses/Logging.hs view
@@ -16,27 +16,22 @@ -- License along with this library; if not, write to the Free Software -- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA -module UI.HSCurses.Logging (trace,debug) where+module UI.HSCurses.Logging (trace, debug) where  import Control.Monad.Trans -#define __DEBUG__ 0--#if __DEBUG__+#if DEBUG -import Data.IORef import System.IO import System.IO.Unsafe (unsafePerformIO)-import System.Locale-import System.Time+import qualified Data.Time as Time  #endif - trace :: String -> a -> a-debug :: MonadIO m => String -> m ()+debug :: (MonadIO m) => String -> m () -#if __DEBUG__+#if DEBUG  logFile :: Handle logFile = unsafePerformIO $ do h <- openFile ".hscurses.log" AppendMode@@ -45,33 +40,25 @@ {-# NOINLINE logFile #-}  formatTime :: IO String-formatTime =-    do let fmt = "%Y-%m-%d %H:%M:%S"-       clockT <- getClockTime-       calTime <- toCalendarTime clockT-       let maxSdecLen = 5-           sdec' = show $ ctPicosec calTime-           sdec = if length sdec' > maxSdecLen-                     then take maxSdecLen sdec'-                     else sdec'-       return (formatCalendarTime defaultTimeLocale fmt calTime-               ++ ":" ++ sdec)--trace s x =-    unsafePerformIO $ do debug s-                         return x--debug s = liftIO $ debug_ logFile s+formatTime = do+  let fmt = "%Y-%m-%d %H:%M:%S%03Q"+  now <- Time.getZonedTime+  return $ Time.formatTime Time.defaultTimeLocale fmt now +debug_ :: Handle -> String -> IO () debug_ f s =     do ts <- formatTime        hPutStrLn f ("[" ++ ts ++ "] " ++ s)        hFlush f +trace s x =+    unsafePerformIO $ do debug s+                         return x+debug s = liftIO $ debug_ logFile s+ #else  trace _ x = x- debug _ = return ()  #endif
UI/HSCurses/Widgets.hs view
@@ -1,956 +1,1133 @@-{-# LANGUAGE ScopedTypeVariables, ExistentialQuantification, CPP #-}---- Copyright (c) 2005-2011 Stefan Wehr - http://www.stefanwehr.de------ This library is free software; you can redistribute it and/or--- modify it under the terms of the GNU Lesser General Public--- License as published by the Free Software Foundation; either--- version 2.1 of the License, or (at your option) any later version.------ This library is distributed in the hope that it will be useful,--- but WITHOUT ANY WARRANTY; without even the implied warranty of--- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU--- Lesser General Public License for more details.------ You should have received a copy of the GNU Lesser General Public--- License along with this library; if not, write to the Free Software--- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA--module UI.HSCurses.Widgets where--import Control.Exception (assert)-#if MIN_VERSION_exceptions(0,6,0)-import Control.Monad.Catch (MonadMask)-#else-import Control.Monad.Catch (MonadCatch)-#define MonadMask MonadCatch-#endif-import Control.Monad.Trans-import Data.Char-import Data.List-import Data.Maybe--import UI.HSCurses.Logging-import qualified UI.HSCurses.Curses as Curses-import qualified UI.HSCurses.CursesHelper as CursesH--type Pos = (Int, Int)-type Offset = (Int, Int)--type Size = (Int, -- height-             Int  -- width-            )--getHeight :: Size -> Int-getHeight = fst--getWidth :: Size -> Int-getWidth = snd--getYOffset :: Offset -> Int-getYOffset = fst--getXOffset :: Offset -> Int-getXOffset = snd--getYPos :: Pos -> Int-getYPos = fst--getXPos :: Pos -> Int-getXPos = snd--data Direction = DirLeft | DirRight | DirUp | DirDown-               deriving (Eq, Show, Ord)--data HAlignment = AlignLeft | AlignCenter | AlignRight-               deriving (Eq, Show)--data Cont a = Cont a | Done a--class Widget a where-    draw      :: Pos -> Size -> DrawingHint -> a -> IO ()-    minSize   :: a -> Size--class Widget a => ActiveWidget a where-    activate  :: (MonadIO m, MonadMask m) => m () -> Pos -> Size -> a ->-                 m (a, String)--type KeyHandler a = Pos -> Size -> a -> IO (Cont a)--mkKeyHandler :: (Pos -> Size -> a -> a)-                -> KeyHandler a-mkKeyHandler f pos sz w = return (Cont (f pos sz w))------- Drawing-----data DrawingHint = DHNormal-                 | DHFocus-                 | DHActive-                   deriving (Eq, Show, Ord)--data DrawingStyle = DStyle-    { dstyle_normal      :: CursesH.CursesStyle-    , dstyle_focus       :: CursesH.CursesStyle-    , dstyle_active      :: CursesH.CursesStyle-    } deriving (Eq, Show)---mkDrawingStyle :: CursesH.CursesStyle -> DrawingStyle-mkDrawingStyle defStyle =-    let revStyle = CursesH.changeCursesStyle defStyle [CursesH.Reverse]-        in DStyle { dstyle_normal = defStyle-                  , dstyle_focus = revStyle-                  , dstyle_active = revStyle-                  }--defaultDrawingStyle :: DrawingStyle-defaultDrawingStyle = mkDrawingStyle CursesH.defaultCursesStyle-_draw :: DrawingHint -> DrawingStyle -> IO a -> IO a-_draw DHActive sty io = CursesH.withStyle (dstyle_active sty) io-_draw DHNormal sty io = CursesH.withStyle (dstyle_normal sty) io-_draw DHFocus sty io = CursesH.withStyle (dstyle_focus sty) io------- Helper functions for scrolling-----scrollFactor :: Double-scrollFactor = 0.8--scrollBy :: Int -> Int-scrollBy displayLen =-    let amount = floor ((fromInteger . toInteger) displayLen * scrollFactor)-    in max (displayLen - 1) (min 1 amount)---- returns the new offset for scrolling in forward direction--- dataLen: total number of data items--- offset: the index of the first data item shown on the current page--- displayLen: the number of data items that is shown in one page-scrollForward :: Int -> Int -> Int -> Int-scrollForward dataLen offset displayLen =-    if offset + displayLen >= dataLen-       then offset-       else min (offset + scrollBy displayLen) (dataLen - displayLen)---- returns the new offset for scrolling in backward direction.--- parameters as for scrollForward-scrollBackward :: t -> Int -> Int -> Int-scrollBackward _ offset displayLen =-    if offset == 0-       then offset-       else max (offset - scrollBy displayLen) 0-------- EmptyWidget-----data EmptyWidget = EmptyWidget Size--instance Widget EmptyWidget where-    draw _ _ _ _ = return ()-    minSize (EmptyWidget sz) = sz-------- An opaque widget-----data OpaqueWidget = OpaqueWidget Size--instance Widget OpaqueWidget where-    draw (y,x) (h,w) _ _ =-        let draw' n =-                do Curses.wMove Curses.stdScr (y+n) x-                   CursesH.drawLine w ""-        in do mapM draw' (take h [0..])-              Curses.refresh-    minSize (OpaqueWidget sz) = sz------- Widget for text input-----data EditWidget = EditWidget-    { ew_content       :: String,-      ew_xoffset       :: Int, -- content!!xoffset is the 1st char shown-      ew_xcursor       :: Int, -- cursor position-      ew_history       :: [String],-      ew_historyIndex  :: Int,-      ew_historySavedContent :: Maybe String,-      ew_options       :: EditWidgetOptions-    }--ew_contentPos :: EditWidget -> Int-ew_contentPos ew = ew_xcursor ew + ew_xoffset ew--instance Widget EditWidget where-    draw = drawEditWidget-    minSize ew = (1, ewopt_minWidth $ ew_options ew)--instance ActiveWidget EditWidget where-    activate = activateEditWidget--data EditWidgetOptions = EWOptions-    { ewopt_keyHandlers    :: [(Curses.Key, KeyHandler EditWidget)],-      ewopt_minWidth       :: Int,-      ewopt_style          :: DrawingStyle-    }--defaultEWOptions :: EditWidgetOptions-defaultEWOptions = EWOptions-                   { ewopt_keyHandlers = editWidgetKeyHandlers,-                     ewopt_minWidth = 8,-                     ewopt_style = defaultDrawingStyle-                   }--newEditWidget :: EditWidgetOptions -> String -> EditWidget-newEditWidget opts =-    editWidgetSetContent-      (EditWidget-       { ew_content = "",-         ew_xoffset = 0,-         ew_xcursor = 0,-         ew_history = [],-         ew_historyIndex = -1,-         ew_historySavedContent = Nothing,-         ew_options = opts-       })----editWidgetGoLeft :: Pos-                                                -> Size-                                                -> EditWidget-                                                -> IO (Cont EditWidget)-editWidgetGoLeft = mkKeyHandler editWidgetGoLeft'-editWidgetGoRight :: Pos-                                                 -> Size-                                                 -> EditWidget-                                                 -> IO (Cont EditWidget)-editWidgetGoRight = mkKeyHandler editWidgetGoRight'-editWidgetDeleteLeft :: Pos-                                                    -> Size-                                                    -> EditWidget-                                                    -> IO (Cont EditWidget)-editWidgetDeleteLeft = mkKeyHandler editWidgetDeleteLeft'-editWidgetDeleteUnderCursor :: Pos-                                                           -> Size-                                                           -> EditWidget-                                                           -> IO (Cont EditWidget)-editWidgetDeleteUnderCursor = mkKeyHandler editWidgetDeleteUnderCursor'-editWidgetDeleteToEnd :: Pos-                                                     -> Size-                                                     -> EditWidget-                                                     -> IO (Cont EditWidget)-editWidgetDeleteToEnd = mkKeyHandler editWidgetDeleteToEnd'-editWidgetGoHome :: Pos-                                                -> Size-                                                -> EditWidget-                                                -> IO (Cont EditWidget)-editWidgetGoHome = mkKeyHandler editWidgetGoHome'-editWidgetGoEnd :: Pos-                                               -> Size-                                               -> EditWidget-                                               -> IO (Cont EditWidget)-editWidgetGoEnd = mkKeyHandler editWidgetGoEnd'-editWidgetHistoryUp :: Pos-                                                   -> Size-                                                   -> EditWidget-                                                   -> IO (Cont EditWidget)-editWidgetHistoryUp = mkKeyHandler editWidgetHistoryUp'-editWidgetHistoryDown :: Pos-                                                     -> Size-                                                     -> EditWidget-                                                     -> IO (Cont EditWidget)-editWidgetHistoryDown = mkKeyHandler editWidgetHistoryDown'--editWidgetKeyHandlers :: [(Curses.Key,-                                                       Pos-                                                       -> Size-                                                       -> EditWidget-                                                       -> IO (Cont EditWidget))]-editWidgetKeyHandlers =-    [(Curses.KeyLeft, editWidgetGoLeft),-     (Curses.KeyRight, editWidgetGoRight),-     (Curses.KeyBackspace, editWidgetDeleteLeft),-     (Curses.KeyChar '\^D', editWidgetDeleteUnderCursor),-     (Curses.KeyDC, editWidgetDeleteUnderCursor),-     (Curses.KeyChar '\^K', editWidgetDeleteToEnd),-     (Curses.KeyHome, editWidgetGoHome),-     (Curses.KeyChar '\^A', editWidgetGoHome),-     (Curses.KeyEnd, editWidgetGoEnd),-     (Curses.KeyChar '\^E', editWidgetGoEnd),-     (Curses.KeyChar '\r', editWidgetFinish),-     (Curses.KeyChar '\t', editWidgetFinish),-     (Curses.KeyUp, editWidgetHistoryUp),-     (Curses.KeyDown, editWidgetHistoryDown)-    ]--editWidgetGetContent :: EditWidget -> String-editWidgetGetContent ew = ew_content ew-editWidgetSetContent :: EditWidget-                                                    -> String-                                                    -> EditWidget-editWidgetSetContent ew s =-    addToHistory (ew { ew_content = s, ew_xoffset = 0, ew_xcursor = 0 }) s--editWidgetGetOptions :: EditWidget-                                                    -> EditWidgetOptions-editWidgetGetOptions ew = ew_options ew-editWidgetSetOptions :: EditWidget-                                                    -> EditWidgetOptions-                                                    -> EditWidget-editWidgetSetOptions ew opts = ew { ew_options = opts }--drawEditWidget :: Pos -> Size -> DrawingHint -> EditWidget -> IO ()-drawEditWidget (y, x) (_, width) hint ew =-    _draw hint (ewopt_style . ew_options $ ew) $-    do Curses.wMove Curses.stdScr y x-       CursesH.drawLine width (drop (ew_xoffset ew) $ ew_content ew)-       Curses.refresh--activateEditWidget :: (MonadIO m, MonadMask m) => m () -> Pos -> Size-                   -> EditWidget -> m (EditWidget, String)-activateEditWidget refresh pos@(y, x) sz@(_, width) ew =-    CursesH.withCursor Curses.CursorVisible $ processKey ew-    where-    processKey ex =-        do liftIO $ drawLocal ex-           k <- CursesH.getKey refresh-           case lookup k (ewopt_keyHandlers $ ew_options ex) of-             Nothing ->-                 case k of-                   Curses.KeyChar c | isAscii c && isPrint c-                       -> processKey $ insertChar ex c-                   _   -> processKey ex-             Just f  ->-                 do x' <- liftIO $ f pos sz ex-                    case x' of-                      Cont ex' -> processKey ex'-                      Done ex' -> do liftIO $ drawEditWidget pos sz DHActive ex'-                                     return (ex', editWidgetGetContent ex')-    insertChar ew' c =-        let pos' = ew_contentPos ew'-            oldContent = ew_content ew'-            newContent = take pos' oldContent ++ (c : drop pos' oldContent)-            in editWidgetGoRight' pos' sz (ew' { ew_content = newContent })-    drawLocal ew' = _draw DHActive  (ewopt_style . ew_options $ ew') $-        do Curses.wMove Curses.stdScr y x-           CursesH.drawLine width (drop (ew_xoffset ew') $ ew_content ew')-           Curses.wMove Curses.stdScr y (x + ew_xcursor ew')-           Curses.refresh--editWidgetGoLeft' :: t -> t1 -> EditWidget -> EditWidget-editWidgetGoLeft' _ _ ew =-    let newXcursor = max (ew_xcursor ew - 1) 0-        newXoffset = if ew_xcursor ew == 0-                        then max (ew_xoffset ew - 1) 0-                        else ew_xoffset ew-        in ew { ew_xoffset = newXoffset,-                ew_xcursor = newXcursor }--editWidgetGoRight' :: t -> (t1, Int) -> EditWidget -> EditWidget-editWidgetGoRight' _ (_, width) ew =-    let len = length (ew_content ew)-        lastChar = len - ew_xoffset ew - 1-        newXcursor = minimum [ew_xcursor ew + 1, lastChar + 1, width - 1]-        newXoffset = if ew_xcursor ew == width - 1-                        then min (ew_xoffset ew + 1) (len - width + 1)-                        else ew_xoffset ew-        in ew { ew_xoffset = newXoffset,-                ew_xcursor = newXcursor }--editWidgetDeleteLeft' :: Pos -> Size -> EditWidget -> EditWidget-editWidgetDeleteLeft' pos sz ew =-    let cpos = ew_contentPos ew - 1-        oldContent = ew_content ew-        newContent = take cpos oldContent ++ drop (cpos+1) oldContent-        ew' = editWidgetGoLeft' pos sz (ew { ew_content = newContent })-        in if ew_xcursor ew == 0 && ew_xoffset ew /= 0-              then editWidgetGoRight' pos sz (editWidgetGoLeft' pos sz ew')-              else ew'--editWidgetDeleteUnderCursor' :: t -> t1 -> EditWidget -> EditWidget-editWidgetDeleteUnderCursor' _ _ ew =-    let pos = ew_contentPos ew-        oldContent = ew_content ew-        newContent = take pos oldContent ++ drop (pos+1) oldContent-        in ew { ew_content = newContent }--editWidgetDeleteToEnd' :: t -> t1 -> EditWidget -> EditWidget-editWidgetDeleteToEnd' _ _ ew =-    let pos = ew_contentPos ew-        oldContent = ew_content ew-        newContent = take pos oldContent-        in ew { ew_content = newContent }--editWidgetGoHome' :: t -> t1 -> EditWidget -> EditWidget-editWidgetGoHome' _ _ ew =-    ew { ew_xcursor = 0,-         ew_xoffset = 0 }--editWidgetGoEnd' :: Pos -> Size -> EditWidget -> EditWidget-editWidgetGoEnd' pos sz ew =-    let cpos = ew_contentPos ew-        len = length (ew_content ew)-        in if cpos == len-              then ew-              else editWidgetGoEnd' pos sz (editWidgetGoRight' pos sz ew)--editWidgetFinish :: (Monad m) => t -> t1 -> EditWidget -> m (Cont EditWidget)-editWidgetFinish _ _ ew =  return (Done (addToHistory ew (ew_content ew)))--maxHistoryLength :: Int-maxHistoryLength = 50--addToHistory :: EditWidget -> [Char] -> EditWidget-addToHistory ew s =-    let newHist = if not (null s)-                     then take maxHistoryLength (s : ew_history ew)-                     else ew_history ew-        in ew { ew_history = newHist, ew_historyIndex = -1,-                ew_historySavedContent = Nothing }--editWidgetHistoryUp' :: t -> t1 -> EditWidget -> EditWidget-editWidgetHistoryUp' _ _ ew = editWidgetHistory (+) ew--editWidgetHistoryDown' :: t -> t1 -> EditWidget -> EditWidget-editWidgetHistoryDown' _ _ ew = editWidgetHistory (-) ew---- ew_historyList: list of history items, i.e. non-null strings which were---   entered into the widget and confirmed with ENTER or which were added---   via editWidgetSetContent.--- ew_historyIndex: the index of the history item shown in the widget. The---   value -1 means that the value saved in ew_historySavedContent should---   be shown.-editWidgetHistory :: (Num t) => (Int -> t -> Int) -> EditWidget -> EditWidget-editWidgetHistory op ew =-    let i = ew_historyIndex ew-        l = ew_history ew-        j =  i `op` 1-        in if j >= 0 && j < length l-              then let savedContent =-                           case ew_historySavedContent ew of-                             Nothing -> Just (ew_content ew)-                             x -> x-                       in ew { ew_historyIndex = j, ew_content = l!!j,-                               ew_historySavedContent = savedContent,-                               ew_xcursor = 0, ew_xoffset = 0 }-              else if j == -1-                      then case ew_historySavedContent ew of-                             Nothing -> ew-                             Just x -> ew { ew_content = x,-                                            ew_historyIndex = j,-                                            ew_xcursor = 0,-                                            ew_xoffset = 0  }-                   else ew--------- Text widget-----data TextWidget = TextWidget-    { tw_text           :: String,-      tw_yoffset        :: Int,-      tw_xoffset        :: Int,-      tw_options        :: TextWidgetOptions-    }-    deriving (Eq, Show)--instance Widget TextWidget where-    draw = drawTextWidget-    minSize tw =-        case twopt_size $ tw_options tw of-          TWSizeDefault -> let l = lines (tw_text tw)-                           in (length l, if null l then 0 else maximum (map length l))-          TWSizeFixed sz -> sz--data TextWidgetSize = TWSizeDefault    -- minimal size determined by content-                    | TWSizeFixed Size -- minimal size is fixed, content is-                                       -- possibly cut off-                      deriving (Eq, Show)-                                 {--                    | Autowrap   -- minimal width determined by content,-                                 -- but lines are wrapped if necessary-                                 -}--data TextWidgetOptions = TWOptions-    { twopt_size   :: TextWidgetSize,-      twopt_style  :: DrawingStyle,-      twopt_halign :: HAlignment }-    deriving (Eq, Show)--defaultTWOptions :: TextWidgetOptions-defaultTWOptions = TWOptions-                { twopt_size = TWSizeDefault,-                  twopt_style = defaultDrawingStyle,-                  twopt_halign = AlignLeft }--newTextWidget :: TextWidgetOptions -> String -> TextWidget-newTextWidget opts s = TextWidget-                       { tw_text = s,-                         tw_yoffset = 0,-                         tw_xoffset = 0,-                         tw_options = opts-                       }--drawTextWidget :: Pos -> Size -> DrawingHint -> TextWidget -> IO ()-drawTextWidget (y, x) (height, width) hint tw =-    let ly = take height $ drop (tw_yoffset tw) (lines (tw_text tw))-        l = take height $ (map (drop (tw_xoffset tw)) ly ++ repeat [])-        l' = map (align (twopt_halign $ tw_options tw) width ' ') l-    in --trace ("drawing text widget at " ++ show pos ++ " with size " ++ show sz) $-       do _draw hint (twopt_style . tw_options $ tw)-                      (mapM drawLine $ zip l' [0..])-          Curses.refresh-    where drawLine (s, i) =-              do Curses.wMove Curses.stdScr (y + i) x-                 CursesH.drawLine width s--textWidgetGetText :: TextWidget -> String-textWidgetGetText = tw_text--textWidgetSetText :: TextWidget -> String -> TextWidget-textWidgetSetText tw s = tw { tw_text = s }--textWidgetScrollDown :: Size -> TextWidget -> TextWidget-textWidgetScrollDown (h, _) tw =-    let dataLen = length $ lines (tw_text tw)-        offset = tw_yoffset tw-        in tw { tw_yoffset = scrollForward dataLen offset h }--textWidgetScrollUp :: Size -> TextWidget -> TextWidget-textWidgetScrollUp (h, _) tw =-    let dataLen = length $ lines (tw_text tw)-        offset = tw_yoffset tw-        in tw { tw_yoffset = scrollBackward dataLen offset h }--textWidgetScrollLeft :: Size -> TextWidget -> TextWidget-textWidgetScrollLeft (_, w) tw =-    let dataLen = length $ lines (tw_text tw)-        offset = tw_xoffset tw-        in tw { tw_xoffset = scrollBackward dataLen offset w }--textWidgetScrollRight :: Size -> TextWidget -> TextWidget-textWidgetScrollRight (_, w) tw =-    let dataLen = length $ lines (tw_text tw)-        offset = tw_xoffset tw-        in tw { tw_xoffset = scrollForward dataLen offset w }-------- Table widget-----data TableCell = forall w. Widget w => TableCell w-               | forall w. ActiveWidget w => ActiveTableCell w--isActive :: TableCell -> Bool-isActive (TableCell _) = False-isActive (ActiveTableCell _) = True--instance Widget TableCell where-    draw pos sz hint (TableCell w) = draw pos sz hint w-    draw pos sz hint (ActiveTableCell w) = draw pos sz hint w-    minSize (TableCell w) = minSize w-    minSize (ActiveTableCell w) = minSize w--_activateTableCell :: (MonadIO m, MonadMask m) => m () -> Pos -> Size-                   -> TableCell -> m (TableCell, String)-_activateTableCell _ _ _ (TableCell _) =-    error "_activateTableCell: cannot activate non-active cell!"-_activateTableCell refresh pos sz (ActiveTableCell w) =-    do (new, res) <- activate refresh pos sz w-       return (ActiveTableCell new, res)--type Row = [TableCell]--singletonRow :: TableCell -> Row-singletonRow tc = [tc]--getCellWidget :: TableWidget -> (Int, Int) -> TableCell-getCellWidget tbw (row, col) = (tbw_rows tbw) !! row !! col--setCellWidget :: TableWidget -> (Int, Int) -> TableCell -> TableWidget-setCellWidget tbw (rowIndex, colIndex) w =-    let rows = tbw_rows tbw-        row = rows !! rowIndex-        newRow = listReplace row w colIndex-        newRows = listReplace rows newRow rowIndex-        in tbw { tbw_rows = newRows }--data TableWidget = TableWidget-    { tbw_rows     :: [Row],-      tbw_colOffset  :: Int,-      tbw_pos      :: Maybe Pos,-      tbw_options  :: TableWidgetOptions }--data FillRow = First | Last | None deriving (Eq,Show)--data TableWidgetOptions = TBWOptions-    { tbwopt_fillCol    :: Maybe Int,-      tbwopt_fillRow    :: FillRow,-      tbwopt_activeCols :: [Int],-      tbwopt_minSize    :: Size }-    deriving (Eq, Show)--defaultTBWOptions :: TableWidgetOptions-defaultTBWOptions = TBWOptions-                    { tbwopt_fillCol = Nothing,-                      tbwopt_fillRow = None,-                      tbwopt_activeCols = [],-                      tbwopt_minSize = (4, 10) }--instance Widget TableWidget where-    draw      = drawTableWidget-    minSize   = tbwopt_minSize . tbw_options--newTableWidget :: TableWidgetOptions -> [Row] -> TableWidget-newTableWidget opts rows = TableWidget-                              { tbw_rows = rows,-                                tbw_colOffset = 0,-                                tbw_pos = findFirstActiveCell rows opts,-                                tbw_options = opts }--data TableWidgetDisplayInfo =-    TBWDisplayInfo-    { tbwdisp_height     :: Int   -- height of the display area-    , tbwdisp_width      :: Int   -- width of the display area-    , tbwdisp_firstVis   :: Int   -- index of the first row visible-    , tbwdisp_lastVis    :: Int   -- index of the last row visible-    , tbwdisp_rows       :: [Row] -- the rows which are visible-    , tbwdisp_nrows      :: Int   -- the number of rows visible-    , tbwdisp_heights    :: [Int] -- the heights of the visible rows-    , tbwdisp_widths     :: [Int] -- the widths of the visible rows-      -- free space at the right side (xoffset, size)-    , tbwdisp_rightMargin :: Maybe (Int, Size)-    }--tableWidgetDisplayInfo :: Size -> TableWidget -> TableWidgetDisplayInfo-tableWidgetDisplayInfo (height, width) tbw =-    assert (isQuadratic (tbw_rows tbw)) $-    let allRows = tbw_rows tbw-        ncols = length (allRows!!0)-        colOffset = tbw_colOffset tbw-        allHeights = minSpaces getHeight allRows-        heights' = drop colOffset allHeights-        nrows = getNRows heights' 0 0-        rows = take nrows $ drop colOffset allRows-        (heights, heightDummy) =-            let hs = take nrows heights'-                s = sum hs-                d = height - s-                in case tbwopt_fillRow $ tbw_options tbw of-                     First -> (applyToFirst (+d) hs, 0)-                     Last -> (applyToLast (+d) hs, 0)-                     None -> (hs, d)-        widths' = minSpaces getWidth (transpose $ tbw_rows tbw)-        (widths, rightMargin) =-            if sum widths' > width-               then error ("table to wide: width=" ++ show (sum widths') ++-                           ", available width=" ++ show width)-               else case tbwopt_fillCol $ tbw_options tbw of-                      Just i | i >= 0 && i < ncols-                                 -> (take i widths' ++-                                     let rest = drop i widths'-                                     in (head rest + width - sum widths') : tail rest-                                    , Nothing)-                      _ -> let diff = width - sum widths'-                               msz = (height, diff)-                               m = if diff > 0 then Just (sum widths', msz)-                                   else Nothing-                           in (widths', m)-        dummyHeights = if heightDummy == 0 then [] else [heightDummy]-        dummyRows = if heightDummy == 0 then []-                    else [map (\w -> TableCell (OpaqueWidget (heightDummy, w)))-                          widths]-        in TBWDisplayInfo-               { tbwdisp_height = height-               , tbwdisp_width = width-               , tbwdisp_firstVis = colOffset-               , tbwdisp_lastVis = colOffset + nrows - 1-               , tbwdisp_rows = rows ++ dummyRows-               , tbwdisp_nrows = nrows + length dummyRows-               , tbwdisp_heights = heights ++ dummyHeights-               , tbwdisp_widths = widths-               , tbwdisp_rightMargin = rightMargin-               }-    where-        minSpaces f ls =-            snd $ mapAccumL-                    (\acc ws ->-                       (acc, acc + maximum (map (f . minSize) ws)))-                    0 ls-        getNRows (h:hs) n acc | h + n <= height = getNRows hs (h+n) (acc+1)-        getNRows _ _ acc = acc-        isQuadratic [] = True-        isQuadratic (x:xs) = isQuadratic' xs (length x)-        isQuadratic' (x:xs) n = length x == n && isQuadratic' xs n-        isQuadratic' [] _ = True-        applyToFirst _ [] = []-        applyToFirst f (x:xs) = f x : xs-        applyToLast _ [] = []-        applyToLast f l =-            let (h, t) = (head $ reverse l, tail $ reverse l)-                in reverse $ f h : t--getCellInfo :: Pos -> Size -> TableWidget -> (Int,Int) -> (Pos, Size)-getCellInfo (y,x) sz tbw (row, col) =-    let info = tableWidgetDisplayInfo sz tbw-        heights = tbwdisp_heights info-        widths = tbwdisp_widths info-        h = heights !! row-        w = widths !! col-        yoff = sum $ take row heights-        xoff = sum $ take col widths-        in ((y+yoff, x+xoff), (h, w))--drawTableWidget :: Pos -> Size -> DrawingHint -> TableWidget -> IO ()-drawTableWidget (y, x) sz hint tbw =-    let info = tableWidgetDisplayInfo sz tbw-        heights = tbwdisp_heights info-        widths = tbwdisp_widths info-        firstVis = tbwdisp_firstVis info-        rows = tbwdisp_rows info-        rightMargin = tbwdisp_rightMargin info-        in do drawRows rows heights widths 0 firstVis hint-              case rightMargin of-                Nothing -> return ()-                Just (xoff,s) -> draw (y,x+xoff) s hint (OpaqueWidget s)-              Curses.refresh-    where drawRows :: [Row] -> [Int] -> [Int] -> Int -> Int-                   -> DrawingHint -> IO ()-          drawRows [] _ _ _ _ _ = return ()-          drawRows (r:rs) (h:hs) widths yoffset rowIndex hint' =-            do drawCols r h widths yoffset 0 (rowIndex, 0) hint'-               drawRows rs hs widths (yoffset + h) (rowIndex + 1) hint'-          drawCols :: Row -> Int -> [Int] -> Int -> Int -> (Int, Int)-                   -> DrawingHint -> IO ()-          drawCols [] _ _ _ _ _ _ = return ()-          drawCols (c:cs) h (w:ws) yoffset xoffset (rowIndex, colIndex) hint' =-            let hint'' = case tbw_pos tbw of-                           Just (z, a) | z == rowIndex && a == colIndex-                               -> DHFocus-                           _ -> hint'-            in do draw (y+yoffset, x+xoffset) (h,w) hint'' c-                  drawCols cs h ws yoffset (xoffset + w)-                           (rowIndex, colIndex+1) hint'---tableWidgetScrollDown :: Size -> TableWidget -> TableWidget-tableWidgetScrollDown (h, _) tbw =-    let dataLen = length $ tbw_rows tbw-        offset = tbw_colOffset tbw-        newOffset = scrollForward dataLen offset h-        newTbw = tbw { tbw_colOffset = newOffset }-        in case tbw_pos newTbw of-             Nothing -> newTbw-             Just (y,x) -> newTbw { tbw_pos = Just (max newOffset y, x) }--tableWidgetScrollUp :: Size -> TableWidget -> TableWidget-tableWidgetScrollUp sz@(h,_) tbw =-    let dataLen = length $ tbw_rows tbw-        offset = tbw_colOffset tbw-        newOffset = scrollBackward dataLen offset h-        newTbw = tbw { tbw_colOffset = newOffset }-        newLastVis = tbwdisp_lastVis (tableWidgetDisplayInfo sz newTbw)-        in case tbw_pos newTbw of-             Nothing -> newTbw-             Just (y,x) ->-                 newTbw { tbw_pos = Just (min newLastVis y, x) }--tableWidgetActivateCurrent :: (MonadIO m, MonadMask m) => m () -> Pos -> Size-                           -> DrawingHint -> TableWidget-                           -> m (TableWidget, Maybe String)-tableWidgetActivateCurrent refresh (y, x) sz _ tbw =-    case tbw_pos tbw of-      Nothing -> do debug "tableWidgetActivateCurrent: pos=Nothing"-                    return (tbw, Nothing)-      Just p -> let w = getCellWidget tbw p-                    in if not $ isActive w-                       then do debug "tableWidgetActivateCurrent: not active"-                               return (tbw, Nothing)-                       else activate' w p-    where-    activate' widget colyx@(coly, colx) =-        let info = tableWidgetDisplayInfo sz tbw-            vcol = colx-            vrow = coly - tbwdisp_firstVis info-            heights = tbwdisp_heights info-            widths = tbwdisp_widths info-            h = heights !! vrow-            w = widths !! vcol-            yoffset = sum (take vrow heights)-            xoffset = sum (take vcol widths)-        in do (new, res) <- _activateTableCell refresh (y+yoffset, x+xoffset)-                              (h, w) widget-              return (setCellWidget tbw colyx new, Just res)--tableWidgetGoLeft :: Size -> TableWidget -> TableWidget-tableWidgetGoLeft =  tableWidgetMove DirLeft--tableWidgetGoRight :: Size -> TableWidget -> TableWidget-tableWidgetGoRight =  tableWidgetMove DirRight--tableWidgetGoUp :: Size -> TableWidget -> TableWidget-tableWidgetGoUp =  tableWidgetMove DirUp--tableWidgetGoDown :: Size -> TableWidget -> TableWidget-tableWidgetGoDown =  tableWidgetMove DirDown--tableWidgetMove :: Direction-                -> (Int, Int)-                -> TableWidget-                -> TableWidget-tableWidgetMove dir sz tbw =-    let pos = tbw_pos tbw-        opts = tbw_options tbw-        nrows = length (tbw_rows tbw)-        in case pos of-             Nothing -> tbw-             Just p -> case findNextActiveCell opts nrows p dir of-                         Nothing -> tbw-                         newP@(Just (y, _)) ->-                             tableWidgetMakeVisible (tbw {tbw_pos=newP}) sz y--tableWidgetMakeVisible :: TableWidget-                       -> (Int, Int)-                       -> Int-                       -> TableWidget-tableWidgetMakeVisible tbw sz@(_,_) y =-    let info = tableWidgetDisplayInfo sz tbw-        firstVis = tbwdisp_firstVis info-        lastVis = tbwdisp_lastVis info-        in if y < firstVis-              then tableWidgetMakeVisible (tableWidgetScrollUp sz tbw) sz y-              else if y > lastVis-                      then tableWidgetMakeVisible-                               (tableWidgetScrollDown sz tbw) sz y-                      else tbw--findFirstActiveCell :: [Row] -> TableWidgetOptions -> Maybe Pos-findFirstActiveCell rows opts =-    let nrows = length rows-        firstActiveCells = map (\y -> findNextActiveCell opts nrows-                                                         (y, -1) DirRight)-                               [0..nrows-1]-        in case catMaybes firstActiveCells of-             [] -> Nothing-             (x:_) -> Just x--findNextActiveCell :: TableWidgetOptions -> Int  -> Pos -> Direction-                   -> Maybe Pos-findNextActiveCell opts nrows (y,x) dir =---    trace ("findNextActiveCell (opts=" ++ show opts ++ ", nrows=" ++ show nrows---           ++ ", pos=" ++ show pos ++ ", dir=" ++ show dir) $-    let rows = [0..(nrows - 1)]-        cols = sort (tbwopt_activeCols opts)-        horiz f = case f cols x rows y of-                    Nothing -> Nothing-                    Just z -> Just (y, z)-        vert f = case f rows y cols x of-                   Nothing -> Nothing-                   Just z -> Just (z, x)-        res = case dir of-                DirLeft-> horiz goLeft-                DirRight -> horiz goRight-                DirUp -> vert goLeft-                DirDown -> vert goRight-        in --trace ("result of findNextActiveCell: " ++ show res)-           res-    where goLeft _ _ rows a | not (a `elem` rows) = Nothing-          goLeft cols b _ _ =-              case reverse (takeWhile (<b) cols) of-                [] -> Nothing-                (c:_) -> Just c-          goRight _ _ rows a | not (a `elem` rows) = Nothing-          goRight cols a _ _ =-              case dropWhile (a>=) cols of-                [] -> Nothing-                (b:_) -> Just b--tableWidgetDeleteRow :: Int -> TableWidget -> TableWidget-tableWidgetDeleteRow n tbw =-    let rows = tbw_rows tbw-        rows' = deleteAt n rows-        pos' =-           case tbw_pos tbw of-             Nothing -> Nothing-             Just (row,col) ->-                 let row' = min row (length rows' - 1)-                     in if row' >= 0 then Just (row', col)-                        else Nothing-        in tbw { tbw_rows = rows', tbw_pos = pos' }------- BorderWidget----------- Selection Widget---------- Utility functions------- | Join a list by some delimiter-joinLists :: [[a]] -> [a] -> [a]-joinLists l s = if (null l) then [] else foldr1 (\x -> \y -> x ++ s ++ y) l---- | Split a list by some delimiter-splitList :: Eq a => [a] -> [a] -> [[a]]-splitList d l =-    unfoldr (\x -> if (null x)-                      then Nothing-                      else Just $ nextToken d [] (snd $ splitAt (length d) x))-            (d++l)-  where nextToken _ r [] = (r, [])-        nextToken e r m@(h:t) | (e `isPrefixOf` m) = (r, m)-                              | otherwise = nextToken e (r++[h]) t--listReplace :: [a] -> a -> Int -> [a]-listReplace l a i =-    case splitAt i l of-      (_, []) -> error ("listReplace: index to large. index="++show i++-                        ", length="++show (length l))-      ([], _) | i < 0 -> error ("listReplace: negative index. index="++-                                show i)-      (xs,(_:ys)) -> xs ++ (a:ys)----alignRows :: [[String]] -> Char -> String -> [String]-alignRows :: [[[a]]] -> a -> [a] -> [[a]]-alignRows rows fill delim =-    let widths = foldr maxWidths (repeat 0) rows-        in map (alignRow widths) rows-    where-    maxWidths ::  [[a]] -> [Int] -> [Int]-    maxWidths row acc = map (uncurry max) (zip acc (map length row))-    alignRow widths row = concatMap (uncurry alignCell) (zip widths row)-    alignCell width cell =-        let diff = width - length cell-            in cell ++ (take diff $ repeat fill) ++ delim---align :: HAlignment -> Int -> a -> [a] -> [a]-align a w f l =-    let space = w - length l-        in case a of-             AlignLeft -> l ++ (fill space)-             AlignRight -> (fill space) ++ l-             AlignCenter ->-                 let left = space `div` 2-                     right = left + (space `mod` 2)-                     in fill left ++ l ++ fill right-    where fill n = take n (repeat f)--deleteAt :: Int -> [a] -> [a]-deleteAt n l = if n >= 0 && n < length l-                  then let (a,b) = splitAt n l in a ++ (tail b)-                  else error ("deleteAt: illegal index: " ++ show n)+{-# LANGUAGE CPP #-}+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE ScopedTypeVariables #-}++-- Copyright (c) 2005-2011 Stefan Wehr - http://www.stefanwehr.de+--+-- This library is free software; you can redistribute it and/or+-- modify it under the terms of the GNU Lesser General Public+-- License as published by the Free Software Foundation; either+-- version 2.1 of the License, or (at your option) any later version.+--+-- This library is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU+-- Lesser General Public License for more details.+--+-- You should have received a copy of the GNU Lesser General Public+-- License along with this library; if not, write to the Free Software+-- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA++module UI.HSCurses.Widgets where++import Control.Exception (assert)+#if MIN_VERSION_exceptions(0,6,0)+import Control.Monad.Catch (MonadMask)+#else+import Control.Monad.Catch (MonadCatch)+#define MonadMask MonadCatch+#endif+import Control.Monad.Trans+import Data.Char+import Data.List+import Data.List.NonEmpty (NonEmpty ((:|)))+import qualified Data.List.NonEmpty as NonEmpty+import Data.Maybe++import qualified UI.HSCurses.Curses as Curses+import qualified UI.HSCurses.CursesHelper as CursesH+import UI.HSCurses.Logging++type Pos = (Int, Int)+type Offset = (Int, Int)++type Size =+    ( Int -- height+    , Int -- width+    )++getHeight :: Size -> Int+getHeight = fst++getWidth :: Size -> Int+getWidth = snd++getYOffset :: Offset -> Int+getYOffset = fst++getXOffset :: Offset -> Int+getXOffset = snd++getYPos :: Pos -> Int+getYPos = fst++getXPos :: Pos -> Int+getXPos = snd++data Direction = DirLeft | DirRight | DirUp | DirDown+    deriving (Eq, Show, Ord)++data HAlignment = AlignLeft | AlignCenter | AlignRight+    deriving (Eq, Show)++data Cont a = Cont a | Done a++class Widget a where+    draw :: Pos -> Size -> DrawingHint -> a -> IO ()+    minSize :: a -> Size++class (Widget a) => ActiveWidget a where+    activate ::+        (MonadIO m, MonadMask m) =>+        m () ->+        Pos ->+        Size ->+        a ->+        m (a, String)++type KeyHandler a = Pos -> Size -> a -> IO (Cont a)++mkKeyHandler ::+    (Pos -> Size -> a -> a) ->+    KeyHandler a+mkKeyHandler f pos sz w = return (Cont (f pos sz w))++--+-- Drawing+--++data DrawingHint+    = DHNormal+    | DHFocus+    | DHActive+    deriving (Eq, Show, Ord)++data DrawingStyle = DStyle+    { dstyle_normal :: CursesH.CursesStyle+    , dstyle_focus :: CursesH.CursesStyle+    , dstyle_active :: CursesH.CursesStyle+    }+    deriving (Eq, Show)++mkDrawingStyle :: CursesH.CursesStyle -> DrawingStyle+mkDrawingStyle defStyle =+    let revStyle = CursesH.changeCursesStyle defStyle [CursesH.Reverse]+     in DStyle+            { dstyle_normal = defStyle+            , dstyle_focus = revStyle+            , dstyle_active = revStyle+            }++defaultDrawingStyle :: DrawingStyle+defaultDrawingStyle = mkDrawingStyle CursesH.defaultCursesStyle+_draw :: DrawingHint -> DrawingStyle -> IO a -> IO a+_draw DHActive sty io = CursesH.withStyle (dstyle_active sty) io+_draw DHNormal sty io = CursesH.withStyle (dstyle_normal sty) io+_draw DHFocus sty io = CursesH.withStyle (dstyle_focus sty) io++--+-- Helper functions for scrolling+--++scrollFactor :: Double+scrollFactor = 0.8++scrollBy :: Int -> Int+scrollBy displayLen =+    let amount = floor ((fromInteger . toInteger) displayLen * scrollFactor)+     in max (displayLen - 1) (min 1 amount)++-- returns the new offset for scrolling in forward direction+-- dataLen: total number of data items+-- offset: the index of the first data item shown on the current page+-- displayLen: the number of data items that is shown in one page+scrollForward :: Int -> Int -> Int -> Int+scrollForward dataLen offset displayLen =+    if offset + displayLen >= dataLen+        then offset+        else min (offset + scrollBy displayLen) (dataLen - displayLen)++-- returns the new offset for scrolling in backward direction.+-- parameters as for scrollForward+scrollBackward :: t -> Int -> Int -> Int+scrollBackward _ offset displayLen =+    if offset == 0+        then offset+        else max (offset - scrollBy displayLen) 0++--+-- EmptyWidget+--++data EmptyWidget = EmptyWidget Size++instance Widget EmptyWidget where+    draw _ _ _ _ = return ()+    minSize (EmptyWidget sz) = sz++--+-- An opaque widget+--++data OpaqueWidget = OpaqueWidget Size++instance Widget OpaqueWidget where+    draw (y, x) (h, w) _ _ =+        let draw' n =+                do+                    Curses.wMove Curses.stdScr (y + n) x+                    CursesH.drawLine w ""+         in do+                mapM draw' (take h [0 ..])+                Curses.refresh+    minSize (OpaqueWidget sz) = sz++--+-- Widget for text input+--++data EditWidget = EditWidget+    { ew_content :: String+    , ew_xoffset :: Int -- content!!xoffset is the 1st char shown+    , ew_xcursor :: Int -- cursor position+    , ew_history :: [String]+    , ew_historyIndex :: Int+    , ew_historySavedContent :: Maybe String+    , ew_options :: EditWidgetOptions+    }++ew_contentPos :: EditWidget -> Int+ew_contentPos ew = ew_xcursor ew + ew_xoffset ew++instance Widget EditWidget where+    draw = drawEditWidget+    minSize ew = (1, ewopt_minWidth $ ew_options ew)++instance ActiveWidget EditWidget where+    activate = activateEditWidget++data EditWidgetOptions = EWOptions+    { ewopt_keyHandlers :: [(Curses.Key, KeyHandler EditWidget)]+    , ewopt_minWidth :: Int+    , ewopt_style :: DrawingStyle+    }++defaultEWOptions :: EditWidgetOptions+defaultEWOptions =+    EWOptions+        { ewopt_keyHandlers = editWidgetKeyHandlers+        , ewopt_minWidth = 8+        , ewopt_style = defaultDrawingStyle+        }++newEditWidget :: EditWidgetOptions -> String -> EditWidget+newEditWidget opts =+    editWidgetSetContent+        ( EditWidget+            { ew_content = ""+            , ew_xoffset = 0+            , ew_xcursor = 0+            , ew_history = []+            , ew_historyIndex = -1+            , ew_historySavedContent = Nothing+            , ew_options = opts+            }+        )++editWidgetGoLeft ::+    Pos ->+    Size ->+    EditWidget ->+    IO (Cont EditWidget)+editWidgetGoLeft = mkKeyHandler editWidgetGoLeft'+editWidgetGoRight ::+    Pos ->+    Size ->+    EditWidget ->+    IO (Cont EditWidget)+editWidgetGoRight = mkKeyHandler editWidgetGoRight'+editWidgetDeleteLeft ::+    Pos ->+    Size ->+    EditWidget ->+    IO (Cont EditWidget)+editWidgetDeleteLeft = mkKeyHandler editWidgetDeleteLeft'+editWidgetDeleteUnderCursor ::+    Pos ->+    Size ->+    EditWidget ->+    IO (Cont EditWidget)+editWidgetDeleteUnderCursor = mkKeyHandler editWidgetDeleteUnderCursor'+editWidgetDeleteToEnd ::+    Pos ->+    Size ->+    EditWidget ->+    IO (Cont EditWidget)+editWidgetDeleteToEnd = mkKeyHandler editWidgetDeleteToEnd'+editWidgetGoHome ::+    Pos ->+    Size ->+    EditWidget ->+    IO (Cont EditWidget)+editWidgetGoHome = mkKeyHandler editWidgetGoHome'+editWidgetGoEnd ::+    Pos ->+    Size ->+    EditWidget ->+    IO (Cont EditWidget)+editWidgetGoEnd = mkKeyHandler editWidgetGoEnd'+editWidgetHistoryUp ::+    Pos ->+    Size ->+    EditWidget ->+    IO (Cont EditWidget)+editWidgetHistoryUp = mkKeyHandler editWidgetHistoryUp'+editWidgetHistoryDown ::+    Pos ->+    Size ->+    EditWidget ->+    IO (Cont EditWidget)+editWidgetHistoryDown = mkKeyHandler editWidgetHistoryDown'++editWidgetKeyHandlers ::+    [ ( Curses.Key+      , Pos ->+        Size ->+        EditWidget ->+        IO (Cont EditWidget)+      )+    ]+editWidgetKeyHandlers =+    [ (Curses.KeyLeft, editWidgetGoLeft)+    , (Curses.KeyRight, editWidgetGoRight)+    , (Curses.KeyBackspace, editWidgetDeleteLeft)+    , (Curses.KeyChar '\^D', editWidgetDeleteUnderCursor)+    , (Curses.KeyDC, editWidgetDeleteUnderCursor)+    , (Curses.KeyChar '\^K', editWidgetDeleteToEnd)+    , (Curses.KeyHome, editWidgetGoHome)+    , (Curses.KeyChar '\^A', editWidgetGoHome)+    , (Curses.KeyEnd, editWidgetGoEnd)+    , (Curses.KeyChar '\^E', editWidgetGoEnd)+    , (Curses.KeyChar '\r', editWidgetFinish)+    , (Curses.KeyChar '\t', editWidgetFinish)+    , (Curses.KeyUp, editWidgetHistoryUp)+    , (Curses.KeyDown, editWidgetHistoryDown)+    ]++editWidgetGetContent :: EditWidget -> String+editWidgetGetContent ew = ew_content ew+editWidgetSetContent ::+    EditWidget ->+    String ->+    EditWidget+editWidgetSetContent ew s =+    addToHistory (ew {ew_content = s, ew_xoffset = 0, ew_xcursor = 0}) s++editWidgetGetOptions ::+    EditWidget ->+    EditWidgetOptions+editWidgetGetOptions ew = ew_options ew+editWidgetSetOptions ::+    EditWidget ->+    EditWidgetOptions ->+    EditWidget+editWidgetSetOptions ew opts = ew {ew_options = opts}++drawEditWidget :: Pos -> Size -> DrawingHint -> EditWidget -> IO ()+drawEditWidget (y, x) (_, width) hint ew =+    _draw hint (ewopt_style . ew_options $ ew) $+        do+            Curses.wMove Curses.stdScr y x+            CursesH.drawLine width (drop (ew_xoffset ew) $ ew_content ew)+            Curses.refresh++activateEditWidget ::+    (MonadIO m, MonadMask m) =>+    m () ->+    Pos ->+    Size ->+    EditWidget ->+    m (EditWidget, String)+activateEditWidget refresh pos@(y, x) sz@(_, width) ew =+    CursesH.withCursor Curses.CursorVisible $ processKey ew+  where+    processKey ex =+        do+            liftIO $ drawLocal ex+            k <- CursesH.getKey refresh+            case lookup k (ewopt_keyHandlers $ ew_options ex) of+                Nothing ->+                    case k of+                        Curses.KeyChar c+                            | isAscii c && isPrint c ->+                                processKey $ insertChar ex c+                        _ -> processKey ex+                Just f ->+                    do+                        x' <- liftIO $ f pos sz ex+                        case x' of+                            Cont ex' -> processKey ex'+                            Done ex' -> do+                                liftIO $ drawEditWidget pos sz DHActive ex'+                                return (ex', editWidgetGetContent ex')+    insertChar ew' c =+        let pos' = ew_contentPos ew'+            oldContent = ew_content ew'+            newContent = take pos' oldContent ++ (c : drop pos' oldContent)+         in editWidgetGoRight' pos' sz (ew' {ew_content = newContent})+    drawLocal ew' = _draw DHActive (ewopt_style . ew_options $ ew') $+        do+            Curses.wMove Curses.stdScr y x+            CursesH.drawLine width (drop (ew_xoffset ew') $ ew_content ew')+            Curses.wMove Curses.stdScr y (x + ew_xcursor ew')+            Curses.refresh++editWidgetGoLeft' :: t -> t1 -> EditWidget -> EditWidget+editWidgetGoLeft' _ _ ew =+    let newXcursor = max (ew_xcursor ew - 1) 0+        newXoffset =+            if ew_xcursor ew == 0+                then max (ew_xoffset ew - 1) 0+                else ew_xoffset ew+     in ew+            { ew_xoffset = newXoffset+            , ew_xcursor = newXcursor+            }++editWidgetGoRight' :: t -> (t1, Int) -> EditWidget -> EditWidget+editWidgetGoRight' _ (_, width) ew =+    let len = length (ew_content ew)+        lastChar = len - ew_xoffset ew - 1+        newXcursor = minimum [ew_xcursor ew + 1, lastChar + 1, width - 1]+        newXoffset =+            if ew_xcursor ew == width - 1+                then min (ew_xoffset ew + 1) (len - width + 1)+                else ew_xoffset ew+     in ew+            { ew_xoffset = newXoffset+            , ew_xcursor = newXcursor+            }++editWidgetDeleteLeft' :: Pos -> Size -> EditWidget -> EditWidget+editWidgetDeleteLeft' pos sz ew =+    let cpos = ew_contentPos ew - 1+        oldContent = ew_content ew+        newContent = take cpos oldContent ++ drop (cpos + 1) oldContent+        ew' = editWidgetGoLeft' pos sz (ew {ew_content = newContent})+     in if ew_xcursor ew == 0 && ew_xoffset ew /= 0+            then editWidgetGoRight' pos sz (editWidgetGoLeft' pos sz ew')+            else ew'++editWidgetDeleteUnderCursor' :: t -> t1 -> EditWidget -> EditWidget+editWidgetDeleteUnderCursor' _ _ ew =+    let pos = ew_contentPos ew+        oldContent = ew_content ew+        newContent = take pos oldContent ++ drop (pos + 1) oldContent+     in ew {ew_content = newContent}++editWidgetDeleteToEnd' :: t -> t1 -> EditWidget -> EditWidget+editWidgetDeleteToEnd' _ _ ew =+    let pos = ew_contentPos ew+        oldContent = ew_content ew+        newContent = take pos oldContent+     in ew {ew_content = newContent}++editWidgetGoHome' :: t -> t1 -> EditWidget -> EditWidget+editWidgetGoHome' _ _ ew =+    ew+        { ew_xcursor = 0+        , ew_xoffset = 0+        }++editWidgetGoEnd' :: Pos -> Size -> EditWidget -> EditWidget+editWidgetGoEnd' pos sz ew =+    let cpos = ew_contentPos ew+        len = length (ew_content ew)+     in if cpos == len+            then ew+            else editWidgetGoEnd' pos sz (editWidgetGoRight' pos sz ew)++editWidgetFinish :: (Monad m) => t -> t1 -> EditWidget -> m (Cont EditWidget)+editWidgetFinish _ _ ew = return (Done (addToHistory ew (ew_content ew)))++maxHistoryLength :: Int+maxHistoryLength = 50++addToHistory :: EditWidget -> [Char] -> EditWidget+addToHistory ew s =+    let newHist =+            if not (null s)+                then take maxHistoryLength (s : ew_history ew)+                else ew_history ew+     in ew+            { ew_history = newHist+            , ew_historyIndex = -1+            , ew_historySavedContent = Nothing+            }++editWidgetHistoryUp' :: t -> t1 -> EditWidget -> EditWidget+editWidgetHistoryUp' _ _ ew = editWidgetHistory (+) ew++editWidgetHistoryDown' :: t -> t1 -> EditWidget -> EditWidget+editWidgetHistoryDown' _ _ ew = editWidgetHistory (-) ew++-- ew_historyList: list of history items, i.e. non-null strings which were+--   entered into the widget and confirmed with ENTER or which were added+--   via editWidgetSetContent.+-- ew_historyIndex: the index of the history item shown in the widget. The+--   value -1 means that the value saved in ew_historySavedContent should+--   be shown.+editWidgetHistory :: (Num t) => (Int -> t -> Int) -> EditWidget -> EditWidget+editWidgetHistory op ew =+    let i = ew_historyIndex ew+        l = ew_history ew+        j = i `op` 1+     in if j >= 0 && j < length l+            then+                let savedContent =+                        case ew_historySavedContent ew of+                            Nothing -> Just (ew_content ew)+                            x -> x+                 in ew+                        { ew_historyIndex = j+                        , ew_content = l !! j+                        , ew_historySavedContent = savedContent+                        , ew_xcursor = 0+                        , ew_xoffset = 0+                        }+            else+                if j == -1+                    then case ew_historySavedContent ew of+                        Nothing -> ew+                        Just x ->+                            ew+                                { ew_content = x+                                , ew_historyIndex = j+                                , ew_xcursor = 0+                                , ew_xoffset = 0+                                }+                    else ew++--+-- Text widget+--++data TextWidget = TextWidget+    { tw_text :: String+    , tw_yoffset :: Int+    , tw_xoffset :: Int+    , tw_options :: TextWidgetOptions+    }+    deriving (Eq, Show)++instance Widget TextWidget where+    draw = drawTextWidget+    minSize tw =+        case twopt_size $ tw_options tw of+            TWSizeDefault ->+                let l = lines (tw_text tw)+                 in (length l, if null l then 0 else maximum (map length l))+            TWSizeFixed sz -> sz++data TextWidgetSize+    = TWSizeDefault -- minimal size determined by content+    | TWSizeFixed Size -- minimal size is fixed, content is+    -- possibly cut off+    deriving (Eq, Show)++{-+\| Autowrap   -- minimal width determined by content,+             -- but lines are wrapped if necessary+             -}++data TextWidgetOptions = TWOptions+    { twopt_size :: TextWidgetSize+    , twopt_style :: DrawingStyle+    , twopt_halign :: HAlignment+    }+    deriving (Eq, Show)++defaultTWOptions :: TextWidgetOptions+defaultTWOptions =+    TWOptions+        { twopt_size = TWSizeDefault+        , twopt_style = defaultDrawingStyle+        , twopt_halign = AlignLeft+        }++newTextWidget :: TextWidgetOptions -> String -> TextWidget+newTextWidget opts s =+    TextWidget+        { tw_text = s+        , tw_yoffset = 0+        , tw_xoffset = 0+        , tw_options = opts+        }++drawTextWidget :: Pos -> Size -> DrawingHint -> TextWidget -> IO ()+drawTextWidget (y, x) (height, width) hint tw =+    let ly = take height $ drop (tw_yoffset tw) (lines (tw_text tw))+        l = take height $ (map (drop (tw_xoffset tw)) ly ++ repeat [])+        l' = map (align (twopt_halign $ tw_options tw) width ' ') l+     in -- trace ("drawing text widget at " ++ show pos ++ " with size " ++ show sz) $+        do+            _draw+                hint+                (twopt_style . tw_options $ tw)+                (mapM drawLine $ zip l' [0 ..])+            Curses.refresh+  where+    drawLine (s, i) =+        do+            Curses.wMove Curses.stdScr (y + i) x+            CursesH.drawLine width s++textWidgetGetText :: TextWidget -> String+textWidgetGetText = tw_text++textWidgetSetText :: TextWidget -> String -> TextWidget+textWidgetSetText tw s = tw {tw_text = s}++textWidgetScrollDown :: Size -> TextWidget -> TextWidget+textWidgetScrollDown (h, _) tw =+    let dataLen = length $ lines (tw_text tw)+        offset = tw_yoffset tw+     in tw {tw_yoffset = scrollForward dataLen offset h}++textWidgetScrollUp :: Size -> TextWidget -> TextWidget+textWidgetScrollUp (h, _) tw =+    let dataLen = length $ lines (tw_text tw)+        offset = tw_yoffset tw+     in tw {tw_yoffset = scrollBackward dataLen offset h}++textWidgetScrollLeft :: Size -> TextWidget -> TextWidget+textWidgetScrollLeft (_, w) tw =+    let dataLen = length $ lines (tw_text tw)+        offset = tw_xoffset tw+     in tw {tw_xoffset = scrollBackward dataLen offset w}++textWidgetScrollRight :: Size -> TextWidget -> TextWidget+textWidgetScrollRight (_, w) tw =+    let dataLen = length $ lines (tw_text tw)+        offset = tw_xoffset tw+     in tw {tw_xoffset = scrollForward dataLen offset w}++--+-- Table widget+--++data TableCell+    = forall w. (Widget w) => TableCell w+    | forall w. (ActiveWidget w) => ActiveTableCell w++isActive :: TableCell -> Bool+isActive (TableCell _) = False+isActive (ActiveTableCell _) = True++instance Widget TableCell where+    draw pos sz hint (TableCell w) = draw pos sz hint w+    draw pos sz hint (ActiveTableCell w) = draw pos sz hint w+    minSize (TableCell w) = minSize w+    minSize (ActiveTableCell w) = minSize w++_activateTableCell ::+    (MonadIO m, MonadMask m) =>+    m () ->+    Pos ->+    Size ->+    TableCell ->+    m (TableCell, String)+_activateTableCell _ _ _ (TableCell _) =+    error "_activateTableCell: cannot activate non-active cell!"+_activateTableCell refresh pos sz (ActiveTableCell w) =+    do+        (new, res) <- activate refresh pos sz w+        return (ActiveTableCell new, res)++type Row = [TableCell]++singletonRow :: TableCell -> Row+singletonRow tc = [tc]++getCellWidget :: TableWidget -> (Int, Int) -> TableCell+getCellWidget tbw (row, col) = (tbw_rows tbw) !! row !! col++setCellWidget :: TableWidget -> (Int, Int) -> TableCell -> TableWidget+setCellWidget tbw (rowIndex, colIndex) w =+    let rows = tbw_rows tbw+        row = rows !! rowIndex+        newRow = listReplace row w colIndex+        newRows = listReplace rows newRow rowIndex+     in tbw {tbw_rows = newRows}++data TableWidget = TableWidget+    { tbw_rows :: [Row]+    , tbw_colOffset :: Int+    , tbw_pos :: Maybe Pos+    , tbw_options :: TableWidgetOptions+    }++data FillRow = First | Last | None deriving (Eq, Show)++data TableWidgetOptions = TBWOptions+    { tbwopt_fillCol :: Maybe Int+    , tbwopt_fillRow :: FillRow+    , tbwopt_activeCols :: [Int]+    , tbwopt_minSize :: Size+    }+    deriving (Eq, Show)++defaultTBWOptions :: TableWidgetOptions+defaultTBWOptions =+    TBWOptions+        { tbwopt_fillCol = Nothing+        , tbwopt_fillRow = None+        , tbwopt_activeCols = []+        , tbwopt_minSize = (4, 10)+        }++instance Widget TableWidget where+    draw = drawTableWidget+    minSize = tbwopt_minSize . tbw_options++newTableWidget :: TableWidgetOptions -> [Row] -> TableWidget+newTableWidget opts rows =+    TableWidget+        { tbw_rows = rows+        , tbw_colOffset = 0+        , tbw_pos = findFirstActiveCell rows opts+        , tbw_options = opts+        }++data TableWidgetDisplayInfo+    = TBWDisplayInfo+    { tbwdisp_height :: Int -- height of the display area+    , tbwdisp_width :: Int -- width of the display area+    , tbwdisp_firstVis :: Int -- index of the first row visible+    , tbwdisp_lastVis :: Int -- index of the last row visible+    , tbwdisp_rows :: [Row] -- the rows which are visible+    , tbwdisp_nrows :: Int -- the number of rows visible+    , tbwdisp_heights :: [Int] -- the heights of the visible rows+    , tbwdisp_widths :: [Int] -- the widths of the visible rows+    -- free space at the right side (xoffset, size)+    , tbwdisp_rightMargin :: Maybe (Int, Size)+    }++tableWidgetDisplayInfo :: Size -> TableWidget -> TableWidgetDisplayInfo+tableWidgetDisplayInfo (height, width) tbw =+    assert (isQuadratic (tbw_rows tbw)) $+        let allRows = tbw_rows tbw+            ncols = length (allRows !! 0)+            colOffset = tbw_colOffset tbw+            allHeights = minSpaces getHeight allRows+            heights' = drop colOffset allHeights+            nrows = getNRows heights' 0 0+            rows = take nrows $ drop colOffset allRows+            (heights, heightDummy) =+                let hs = take nrows heights'+                    s = sum hs+                    d = height - s+                 in case tbwopt_fillRow $ tbw_options tbw of+                        First -> (applyToFirst (+ d) hs, 0)+                        Last -> (applyToLast (+ d) hs, 0)+                        None -> (hs, d)+            widths' = minSpaces getWidth (transpose $ tbw_rows tbw)+            (widths, rightMargin) =+                if sum widths' > width+                    then+                        error+                            ( "table too wide: width="+                                ++ show (sum widths')+                                ++ ", available width="+                                ++ show width+                            )+                    else case tbwopt_fillCol $ tbw_options tbw of+                        Just i+                            | i >= 0 && i < ncols ->+                                ( take i widths'+                                    ++ case drop i widths' of+                                        [] -> error "rest unexpectedly empty"+                                        (w : ws) -> (w + width - sum widths') : ws+                                , Nothing+                                )+                        _ ->+                            let diff = width - sum widths'+                                msz = (height, diff)+                                m =+                                    if diff > 0+                                        then Just (sum widths', msz)+                                        else Nothing+                             in (widths', m)+            dummyHeights = if heightDummy == 0 then [] else [heightDummy]+            dummyRows =+                if heightDummy == 0+                    then []+                    else+                        [ map+                            (\w -> TableCell (OpaqueWidget (heightDummy, w)))+                            widths+                        ]+         in TBWDisplayInfo+                { tbwdisp_height = height+                , tbwdisp_width = width+                , tbwdisp_firstVis = colOffset+                , tbwdisp_lastVis = colOffset + nrows - 1+                , tbwdisp_rows = rows ++ dummyRows+                , tbwdisp_nrows = nrows + length dummyRows+                , tbwdisp_heights = heights ++ dummyHeights+                , tbwdisp_widths = widths+                , tbwdisp_rightMargin = rightMargin+                }+  where+    minSpaces f ls =+        snd $+            mapAccumL+                ( \acc ws ->+                    (acc, acc + maximum (map (f . minSize) ws))+                )+                0+                ls+    getNRows (h : hs) n acc | h + n <= height = getNRows hs (h + n) (acc + 1)+    getNRows _ _ acc = acc+    isQuadratic [] = True+    isQuadratic (x : xs) = isQuadratic' xs (length x)+    isQuadratic' (x : xs) n = length x == n && isQuadratic' xs n+    isQuadratic' [] _ = True+    applyToFirst _ [] = []+    applyToFirst f (x : xs) = f x : xs+    applyToLast _ [] = []+    applyToLast f (x : xs) =+        let rev = NonEmpty.reverse $ x :| xs+            (h, t) = (NonEmpty.head rev, NonEmpty.tail rev)+         in reverse $ f h : t++getCellInfo :: Pos -> Size -> TableWidget -> (Int, Int) -> (Pos, Size)+getCellInfo (y, x) sz tbw (row, col) =+    let info = tableWidgetDisplayInfo sz tbw+        heights = tbwdisp_heights info+        widths = tbwdisp_widths info+        h = heights !! row+        w = widths !! col+        yoff = sum $ take row heights+        xoff = sum $ take col widths+     in ((y + yoff, x + xoff), (h, w))++drawTableWidget :: Pos -> Size -> DrawingHint -> TableWidget -> IO ()+drawTableWidget (y, x) sz hint tbw =+    let info = tableWidgetDisplayInfo sz tbw+        heights = tbwdisp_heights info+        widths = tbwdisp_widths info+        firstVis = tbwdisp_firstVis info+        rows = tbwdisp_rows info+        rightMargin = tbwdisp_rightMargin info+     in do+            drawRows rows heights widths 0 firstVis hint+            case rightMargin of+                Nothing -> return ()+                Just (xoff, s) -> draw (y, x + xoff) s hint (OpaqueWidget s)+            Curses.refresh+  where+    drawRows ::+        [Row] ->+        [Int] ->+        [Int] ->+        Int ->+        Int ->+        DrawingHint ->+        IO ()+    drawRows [] _ _ _ _ _ = return ()+    drawRows (r : rs) (h : hs) widths yoffset rowIndex hint' =+        do+            drawCols r h widths yoffset 0 (rowIndex, 0) hint'+            drawRows rs hs widths (yoffset + h) (rowIndex + 1) hint'+    drawRows _ _ _ _ _ _ = return ()+    drawCols ::+        Row ->+        Int ->+        [Int] ->+        Int ->+        Int ->+        (Int, Int) ->+        DrawingHint ->+        IO ()+    drawCols [] _ _ _ _ _ _ = return ()+    drawCols (c : cs) h (w : ws) yoffset xoffset (rowIndex, colIndex) hint' =+        let hint'' = case tbw_pos tbw of+                Just (z, a)+                    | z == rowIndex && a == colIndex ->+                        DHFocus+                _ -> hint'+         in do+                draw (y + yoffset, x + xoffset) (h, w) hint'' c+                drawCols+                    cs+                    h+                    ws+                    yoffset+                    (xoffset + w)+                    (rowIndex, colIndex + 1)+                    hint'+    drawCols _ _ _ _ _ _ _ = return ()++tableWidgetScrollDown :: Size -> TableWidget -> TableWidget+tableWidgetScrollDown (h, _) tbw =+    let dataLen = length $ tbw_rows tbw+        offset = tbw_colOffset tbw+        newOffset = scrollForward dataLen offset h+        newTbw = tbw {tbw_colOffset = newOffset}+     in case tbw_pos newTbw of+            Nothing -> newTbw+            Just (y, x) -> newTbw {tbw_pos = Just (max newOffset y, x)}++tableWidgetScrollUp :: Size -> TableWidget -> TableWidget+tableWidgetScrollUp sz@(h, _) tbw =+    let dataLen = length $ tbw_rows tbw+        offset = tbw_colOffset tbw+        newOffset = scrollBackward dataLen offset h+        newTbw = tbw {tbw_colOffset = newOffset}+        newLastVis = tbwdisp_lastVis (tableWidgetDisplayInfo sz newTbw)+     in case tbw_pos newTbw of+            Nothing -> newTbw+            Just (y, x) ->+                newTbw {tbw_pos = Just (min newLastVis y, x)}++tableWidgetActivateCurrent ::+    (MonadIO m, MonadMask m) =>+    m () ->+    Pos ->+    Size ->+    DrawingHint ->+    TableWidget ->+    m (TableWidget, Maybe String)+tableWidgetActivateCurrent refresh (y, x) sz _ tbw =+    case tbw_pos tbw of+        Nothing -> do+            debug "tableWidgetActivateCurrent: pos=Nothing"+            return (tbw, Nothing)+        Just p ->+            let w = getCellWidget tbw p+             in if not $ isActive w+                    then do+                        debug "tableWidgetActivateCurrent: not active"+                        return (tbw, Nothing)+                    else activate' w p+  where+    activate' widget colyx@(coly, colx) =+        let info = tableWidgetDisplayInfo sz tbw+            vcol = colx+            vrow = coly - tbwdisp_firstVis info+            heights = tbwdisp_heights info+            widths = tbwdisp_widths info+            h = heights !! vrow+            w = widths !! vcol+            yoffset = sum (take vrow heights)+            xoffset = sum (take vcol widths)+         in do+                (new, res) <-+                    _activateTableCell+                        refresh+                        (y + yoffset, x + xoffset)+                        (h, w)+                        widget+                return (setCellWidget tbw colyx new, Just res)++tableWidgetGoLeft :: Size -> TableWidget -> TableWidget+tableWidgetGoLeft = tableWidgetMove DirLeft++tableWidgetGoRight :: Size -> TableWidget -> TableWidget+tableWidgetGoRight = tableWidgetMove DirRight++tableWidgetGoUp :: Size -> TableWidget -> TableWidget+tableWidgetGoUp = tableWidgetMove DirUp++tableWidgetGoDown :: Size -> TableWidget -> TableWidget+tableWidgetGoDown = tableWidgetMove DirDown++tableWidgetMove ::+    Direction ->+    (Int, Int) ->+    TableWidget ->+    TableWidget+tableWidgetMove dir sz tbw =+    let pos = tbw_pos tbw+        opts = tbw_options tbw+        nrows = length (tbw_rows tbw)+     in case pos of+            Nothing -> tbw+            Just p -> case findNextActiveCell opts nrows p dir of+                Nothing -> tbw+                newP@(Just (y, _)) ->+                    tableWidgetMakeVisible (tbw {tbw_pos = newP}) sz y++tableWidgetMakeVisible ::+    TableWidget ->+    (Int, Int) ->+    Int ->+    TableWidget+tableWidgetMakeVisible tbw sz@(_, _) y =+    let info = tableWidgetDisplayInfo sz tbw+        firstVis = tbwdisp_firstVis info+        lastVis = tbwdisp_lastVis info+     in if y < firstVis+            then tableWidgetMakeVisible (tableWidgetScrollUp sz tbw) sz y+            else+                if y > lastVis+                    then+                        tableWidgetMakeVisible+                            (tableWidgetScrollDown sz tbw)+                            sz+                            y+                    else tbw++findFirstActiveCell :: [Row] -> TableWidgetOptions -> Maybe Pos+findFirstActiveCell rows opts =+    let nrows = length rows+        firstActiveCells =+            map+                ( \y ->+                    findNextActiveCell+                        opts+                        nrows+                        (y, -1)+                        DirRight+                )+                [0 .. nrows - 1]+     in case catMaybes firstActiveCells of+            [] -> Nothing+            (x : _) -> Just x++findNextActiveCell ::+    TableWidgetOptions ->+    Int ->+    Pos ->+    Direction ->+    Maybe Pos+findNextActiveCell opts nrows (y, x) dir =+    --    trace ("findNextActiveCell (opts=" ++ show opts ++ ", nrows=" ++ show nrows+    --           ++ ", pos=" ++ show pos ++ ", dir=" ++ show dir) $+    let rows = [0 .. (nrows - 1)]+        cols = sort (tbwopt_activeCols opts)+        horiz f = case f cols x rows y of+            Nothing -> Nothing+            Just z -> Just (y, z)+        vert f = case f rows y cols x of+            Nothing -> Nothing+            Just z -> Just (z, x)+        res = case dir of+            DirLeft -> horiz goLeft+            DirRight -> horiz goRight+            DirUp -> vert goLeft+            DirDown -> vert goRight+     in -- trace ("result of findNextActiveCell: " ++ show res)+        res+  where+    goLeft _ _ rows a | not (a `elem` rows) = Nothing+    goLeft cols b _ _ =+        case reverse (takeWhile (< b) cols) of+            [] -> Nothing+            (c : _) -> Just c+    goRight _ _ rows a | not (a `elem` rows) = Nothing+    goRight cols a _ _ =+        case dropWhile (a >=) cols of+            [] -> Nothing+            (b : _) -> Just b++tableWidgetDeleteRow :: Int -> TableWidget -> TableWidget+tableWidgetDeleteRow n tbw =+    let rows = tbw_rows tbw+        rows' = deleteAt n rows+        pos' =+            case tbw_pos tbw of+                Nothing -> Nothing+                Just (row, col) ->+                    let row' = min row (length rows' - 1)+                     in if row' >= 0+                            then Just (row', col)+                            else Nothing+     in tbw {tbw_rows = rows', tbw_pos = pos'}++--+-- BorderWidget+--++--+-- Selection Widget+--++--+-- Utility functions+--++-- | Join a list by some delimiter+joinLists :: [[a]] -> [a] -> [a]+joinLists l s = if (null l) then [] else foldr1 (\x -> \y -> x ++ s ++ y) l++-- | Split a list by some delimiter+splitList :: (Eq a) => [a] -> [a] -> [[a]]+splitList d l =+    unfoldr+        ( \x ->+            if (null x)+                then Nothing+                else Just $ nextToken d [] (snd $ splitAt (length d) x)+        )+        (d ++ l)+  where+    nextToken _ r [] = (r, [])+    nextToken e r m@(h : t)+        | (e `isPrefixOf` m) = (r, m)+        | otherwise = nextToken e (r ++ [h]) t++listReplace :: [a] -> a -> Int -> [a]+listReplace l a i =+    case splitAt i l of+        (_, []) ->+            error+                ( "listReplace: index to large. index="+                    ++ show i+                    ++ ", length="+                    ++ show (length l)+                )+        ([], _)+            | i < 0 ->+                error+                    ( "listReplace: negative index. index="+                        ++ show i+                    )+        (xs, (_ : ys)) -> xs ++ (a : ys)++-- alignRows :: [[String]] -> Char -> String -> [String]+alignRows :: [[[a]]] -> a -> [a] -> [[a]]+alignRows rows fill delim =+    let widths = foldr maxWidths (repeat 0) rows+     in map (alignRow widths) rows+  where+    maxWidths :: [[a]] -> [Int] -> [Int]+    maxWidths row acc = map (uncurry max) (zip acc (map length row))+    alignRow widths row = concatMap (uncurry alignCell) (zip widths row)+    alignCell width cell =+        let diff = width - length cell+         in cell ++ (take diff $ repeat fill) ++ delim++align :: HAlignment -> Int -> a -> [a] -> [a]+align a w f l =+    let space = w - length l+     in case a of+            AlignLeft -> l ++ (fill space)+            AlignRight -> (fill space) ++ l+            AlignCenter ->+                let left = space `div` 2+                    right = left + (space `mod` 2)+                 in fill left ++ l ++ fill right+  where+    fill n = take n (repeat f)++deleteAt :: Int -> [a] -> [a]+deleteAt n l =+    if n >= 0 && n < length l+        then+            let (a, b) = splitAt n l+             in case b of+                    [] -> error "deleteAt: impossible"+                    (_ : rest) -> a ++ rest+        else error ("deleteAt: illegal index: " ++ show n)
example/ContactManager.hs view
@@ -27,21 +27,23 @@ {- TODO: -* save-* add+\* save+\* add -} +import Prelude hiding ((<>))+ import Control.Exception import Control.Monad.State-import Data.List ( sort )+import Data.List (sort) import System.Environment (getArgs, getProgName) import System.Exit (exitFailure) import Text.PrettyPrint.HughesPJ -import UI.HSCurses.Logging-import UI.HSCurses.Widgets import qualified UI.HSCurses.Curses as Curses import qualified UI.HSCurses.CursesHelper as CursesH+import UI.HSCurses.Logging+import UI.HSCurses.Widgets  type Name = String type Email = String@@ -53,40 +55,41 @@ type PhoneNumber = String  data Contact = Contact-    { lastName         :: Name-    , firstName        :: Name-    , emailAddress     :: Email-    , address          :: Address-    , zipCode          :: ZIPCode-    , city             :: City-    , province         :: Province-    , country          :: Country-    , phoneNumber      :: PhoneNumber+    { lastName :: Name+    , firstName :: Name+    , emailAddress :: Email+    , address :: Address+    , zipCode :: ZIPCode+    , city :: City+    , province :: Province+    , country :: Country+    , phoneNumber :: PhoneNumber     }-    deriving (Show,Read,Eq,Ord)+    deriving (Show, Read, Eq, Ord) -emptyContact = Contact-               { lastName         = ""-               , firstName        = ""-               , emailAddress     = ""-               , address          = ""-               , zipCode          = ""-               , city             = ""-               , province         = ""-               , country          = ""-               , phoneNumber      = ""-               }+emptyContact =+    Contact+        { lastName = ""+        , firstName = ""+        , emailAddress = ""+        , address = ""+        , zipCode = ""+        , city = ""+        , province = ""+        , country = ""+        , phoneNumber = ""+        }  pprContact c =     pprLine (combine (lastName c) ", " (firstName c)) $-    pprLine (address c) $-    pprLine (combine (zipCode c) " " (city c)) $-    pprLine (province c) $-    pprLine (country c) $-    pprLine (phoneNumber c) $-    pprLine (emailAddress c) $-    empty-    where+        pprLine (address c) $+            pprLine (combine (zipCode c) " " (city c)) $+                pprLine (province c) $+                    pprLine (country c) $+                        pprLine (phoneNumber c) $+                            pprLine (emailAddress c) $+                                empty+  where     pprLine :: String -> Doc -> Doc     pprLine [] = (<>) empty     pprLine s = ($$) (text s)@@ -104,82 +107,104 @@     , ("City", city c)     , ("State/Province", province c)     , ("Country", country c)-    , ("Phone", phoneNumber c) ]+    , ("Phone", phoneNumber c)+    ]  readContacts :: FilePath -> IO [Contact] readContacts f =-    do s <- readFile f-       case reads s of-         [(contacts, [])] -> return (sort contacts)-         _ -> error ("corrupt contact file: " ++ f)+    do+        s <- readFile f+        case reads s of+            [(contacts, [])] -> return (sort contacts)+            _ -> error ("corrupt contact file: " ++ f)  writeContacts :: FilePath -> [Contact] -> IO () writeContacts f contacts =     writeFile f (show contacts)  sampleContacts =-    [ emptyContact { lastName = "Wehr"-                   , firstName = "Stefan"-                   , emailAddress = "mail AT stefanwehr DOT de"-                   , address = "28 Loch Maree St"-                   , zipCode = "2032"-                   , city = "Kingsford"-                   , province = "NSW"-                   , country = "Australia" }-    , emptyContact { lastName = "Thorpe"-                   , firstName = "Ian"-                   , emailAddress = "ian@aol7.com.au"-                   , city = "Perth"-                   , country = "Australia" }-    , emptyContact { lastName = "Gates"-                   , firstName = "Bill"-                   , emailAddress = "billy@microsoft.com" }-    , emptyContact { lastName = "Stewart"-                   , firstName = "Don"-                   , address = "CSE, UNSW, 501-16, k17 building"-                   , city = "Sydney"-                   , country = "Australia" } ]-+    [ emptyContact+        { lastName = "Wehr"+        , firstName = "Stefan"+        , emailAddress = "mail AT stefanwehr DOT de"+        , address = "28 Loch Maree St"+        , zipCode = "2032"+        , city = "Kingsford"+        , province = "NSW"+        , country = "Australia"+        }+    , emptyContact+        { lastName = "Thorpe"+        , firstName = "Ian"+        , emailAddress = "ian@aol7.com.au"+        , city = "Perth"+        , country = "Australia"+        }+    , emptyContact+        { lastName = "Gates"+        , firstName = "Bill"+        , emailAddress = "billy@microsoft.com"+        }+    , emptyContact+        { lastName = "Stewart"+        , firstName = "Don"+        , address = "CSE, UNSW, 501-16, k17 building"+        , city = "Sydney"+        , country = "Australia"+        }+    ]  title = "contact-manager"  help = "q:quit, d:delete, a:add"  data CMState = CMState-    { cm_styles  :: [CursesH.CursesStyle]+    { cm_styles :: [CursesH.CursesStyle]     , cm_contacts :: [Contact]     } type CM = StateT CMState IO  runCM :: [CursesH.CursesStyle] -> [Contact] -> CM a -> IO a-runCM stys contacts cm = evalStateT cm (CMState { cm_styles = stys-                                                , cm_contacts = contacts })+runCM stys contacts cm =+    evalStateT+        cm+        ( CMState+            { cm_styles = stys+            , cm_contacts = contacts+            }+        )  nthStyle :: Int -> CM CursesH.CursesStyle nthStyle n =-    do cs <- gets cm_styles-       return $ cs !! n+    do+        cs <- gets cm_styles+        return $ cs !! n  getSize = liftIO $ Curses.scrSize -styles = [ CursesH.defaultStyle-         , CursesH.AttributeStyle [CursesH.Bold] CursesH.GreenF CursesH.DarkBlueB-         ]-+styles =+    [ CursesH.defaultStyle+    , CursesH.AttributeStyle [CursesH.Bold] CursesH.GreenF CursesH.DarkBlueB+    ]  defStyle = nthStyle 0 lineStyle = nthStyle 1  lineDrawingStyle =-    do sty <- lineStyle-       return $ mkDrawingStyle sty+    do+        sty <- lineStyle+        return $ mkDrawingStyle sty  lineOptions =-    do sz <- getSize-       ds <- lineDrawingStyle-       return $ TWOptions { twopt_size = TWSizeFixed (1, getWidth sz),-                            twopt_style = ds,-                            twopt_halign = AlignLeft }+    do+        sz <- getSize+        ds <- lineDrawingStyle+        return $+            TWOptions+                { twopt_size = TWSizeFixed (1, getWidth sz)+                , twopt_style = ds+                , twopt_halign = AlignLeft+                }  type ToplineWidget = TextWidget type MidlineWidget = TextWidget@@ -190,37 +215,46 @@ type ContactEditWidget = TableWidget  mkToplineWidget =-    do opts <- lineOptions-       return $ newTextWidget (opts { twopt_halign = AlignCenter })-                  title+    do+        opts <- lineOptions+        return $+            newTextWidget+                (opts {twopt_halign = AlignCenter})+                title  mkMidlineWidget :: ContactListWidget -> CM MidlineWidget mkMidlineWidget listWidget =-    do opts <- lineOptions-       contacts <- gets cm_contacts-       let s = case tbw_pos listWidget of-                 Nothing -> show (length contacts)-                 Just (row, _) -> show (1+row) ++ "/" ++ show (length contacts)-       return $ newTextWidget  (opts { twopt_halign = AlignRight }) s+    do+        opts <- lineOptions+        contacts <- gets cm_contacts+        let s = case tbw_pos listWidget of+                Nothing -> show (length contacts)+                Just (row, _) -> show (1 + row) ++ "/" ++ show (length contacts)+        return $ newTextWidget (opts {twopt_halign = AlignRight}) s  mkBotlineWidget =-    do opts <- lineOptions-       return $ newTextWidget opts help+    do+        opts <- lineOptions+        return $ newTextWidget opts help  -- We need to insert a dummy widget at the lower-right corner of the window, -- i.e. at the lower-right corner of the message line. Otherwise, an -- error occurs because drawing a character to this position moves the -- cursor to the next line, which doesn't exist. mkMsglineWidget =-    do sz <- getSize-       let width = getWidth sz-           opts = TWOptions { twopt_size = TWSizeFixed (1, width - 1),-                              twopt_style = defaultDrawingStyle,-                              twopt_halign = AlignLeft }-           tw = newTextWidget opts "msgline"-           row = [TableCell tw, TableCell $ EmptyWidget (1,1)]-           tabOpts = defaultTBWOptions { tbwopt_minSize = (1, width) }-       return $ newTableWidget tabOpts [row]+    do+        sz <- getSize+        let width = getWidth sz+            opts =+                TWOptions+                    { twopt_size = TWSizeFixed (1, width - 1)+                    , twopt_style = defaultDrawingStyle+                    , twopt_halign = AlignLeft+                    }+            tw = newTextWidget opts "msgline"+            row = [TableCell tw, TableCell $ EmptyWidget (1, 1)]+            tabOpts = defaultTBWOptions {tbwopt_minSize = (1, width)}+        return $ newTableWidget tabOpts [row]  nlines = 4 @@ -228,84 +262,108 @@  contactDetailsHeight (h, _) =     let n = h - nlines-        in n `div` 2 + (n `mod` 2)+     in n `div` 2 + (n `mod` 2)  contactListOptions =-    do sz <- getSize-       return $ TBWOptions-                  { tbwopt_fillCol = Nothing,-                    tbwopt_fillRow = None,-                    tbwopt_activeCols = [0],-                    tbwopt_minSize = (contactListHeight sz, getWidth sz) }+    do+        sz <- getSize+        return $+            TBWOptions+                { tbwopt_fillCol = Nothing+                , tbwopt_fillRow = None+                , tbwopt_activeCols = [0]+                , tbwopt_minSize = (contactListHeight sz, getWidth sz)+                }  contactDetailsOptions =-    do sz <- getSize-       return $ TWOptions { twopt_size = TWSizeFixed (contactDetailsHeight sz,-                                                         getWidth sz),-                            twopt_style = defaultDrawingStyle,-                            twopt_halign = AlignLeft }+    do+        sz <- getSize+        return $+            TWOptions+                { twopt_size =+                    TWSizeFixed+                        ( contactDetailsHeight sz+                        , getWidth sz+                        )+                , twopt_style = defaultDrawingStyle+                , twopt_halign = AlignLeft+                }  mkContactListWidget :: CM ContactListWidget mkContactListWidget =-    do contacts <- gets cm_contacts-       sz <- getSize-       let lines = alignRows (map contactLine contacts) ' ' "  "-           rows = map (contactRow $ getWidth sz) lines-       opts <- contactListOptions-       return $ newTableWidget opts rows-    where contactLine c = [lastName c, firstName c, emailAddress c]-          contactRow w s = [TableCell $ newTextWidget-                            (defaultTWOptions { twopt_size = TWSizeFixed (1, w) }) s]-          lastRow = [TableCell (EmptyWidget (0,0))]+    do+        contacts <- gets cm_contacts+        sz <- getSize+        let lines = alignRows (map contactLine contacts) ' ' "  "+            rows = map (contactRow $ getWidth sz) lines+        opts <- contactListOptions+        return $ newTableWidget opts rows+  where+    contactLine c = [lastName c, firstName c, emailAddress c]+    contactRow w s =+        [ TableCell $+            newTextWidget+                (defaultTWOptions {twopt_size = TWSizeFixed (1, w)})+                s+        ]+    lastRow = [TableCell (EmptyWidget (0, 0))]  mkContactDetailsWidget :: ContactListWidget -> CM ContactDetailsWidget mkContactDetailsWidget listWidget =-    do contacts <- gets cm_contacts-       let contact = case tbw_pos listWidget of-                       Nothing -> ""-                       Just (row, _) ->-                           let c = contacts !! row-                               in show $ pprContact c-       opts <- contactDetailsOptions-       return $ newTextWidget opts contact+    do+        contacts <- gets cm_contacts+        let contact = case tbw_pos listWidget of+                Nothing -> ""+                Just (row, _) ->+                    let c = contacts !! row+                     in show $ pprContact c+        opts <- contactDetailsOptions+        return $ newTextWidget opts contact  mkContactEditWidget :: Contact -> CM ContactEditWidget mkContactEditWidget contact =     let l = contactToLabelValueList contact         rows = map mkRow l-        in do sz <- getSize-              let opts = TBWOptions-                         { tbwopt_fillCol = Just 1,-                           tbwopt_fillRow = None,-                           tbwopt_activeCols = [1],-                           tbwopt_minSize = (getHeight sz - 3, getWidth sz) }-              return $ newTableWidget opts rows-    where mkRow (label, value) =-              let labelW = newTextWidget defaultTWOptions label-                  valueW = newEditWidget defaultEWOptions value-                  in [TableCell labelW, ActiveTableCell valueW]+     in do+            sz <- getSize+            let opts =+                    TBWOptions+                        { tbwopt_fillCol = Just 1+                        , tbwopt_fillRow = None+                        , tbwopt_activeCols = [1]+                        , tbwopt_minSize = (getHeight sz - 3, getWidth sz)+                        }+            return $ newTableWidget opts rows+  where+    mkRow (label, value) =+        let labelW = newTextWidget defaultTWOptions label+            valueW = newEditWidget defaultEWOptions value+         in [TableCell labelW, ActiveTableCell valueW]  mkMainEditWidget contact =-    do tlw <- mkToplineWidget-       blw <- mkBotlineWidget-       msglw <- mkMsglineWidget-       ew <- mkContactEditWidget contact-       return $ MainEditWidget tlw blw msglw ew+    do+        tlw <- mkToplineWidget+        blw <- mkBotlineWidget+        msglw <- mkMsglineWidget+        ew <- mkContactEditWidget contact+        return $ MainEditWidget tlw blw msglw ew  data MainEditWidget = MainEditWidget     { toplineEditWidget :: ToplineWidget     , botlineEditWidget :: BotlineWidget     , msglineEditWidget :: MsglineWidget-    , contactEditWidget :: ContactEditWidget }+    , contactEditWidget :: ContactEditWidget+    }  mkMainWidget =-    do tlw <- mkToplineWidget-       clw <- mkContactListWidget-       mlw <- mkMidlineWidget clw-       cdw <- mkContactDetailsWidget clw-       blw <- mkBotlineWidget-       msglw <- mkMsglineWidget-       return $ MainWidget tlw mlw blw msglw clw cdw+    do+        tlw <- mkToplineWidget+        clw <- mkContactListWidget+        mlw <- mkMidlineWidget clw+        cdw <- mkContactDetailsWidget clw+        blw <- mkBotlineWidget+        msglw <- mkMsglineWidget+        return $ MainWidget tlw mlw blw msglw clw cdw  instance Widget MainEditWidget where     draw pos sz hint w = draw pos sz hint (mkRealMainEditWidget (Just sz) w)@@ -313,15 +371,17 @@  mkRealMainEditWidget :: (Maybe Size) -> MainEditWidget -> TableWidget mkRealMainEditWidget msz w =-    let cells = [ TableCell $ toplineEditWidget w-                , TableCell $ contactEditWidget w-                , TableCell $ botlineEditWidget w-                , TableCell $ msglineEditWidget w ]+    let cells =+            [ TableCell $ toplineEditWidget w+            , TableCell $ contactEditWidget w+            , TableCell $ botlineEditWidget w+            , TableCell $ msglineEditWidget w+            ]         rows = map singletonRow cells         opts = case msz of-                 Nothing -> defaultTBWOptions-                 Just sz -> defaultTBWOptions { tbwopt_minSize = sz }-        in newTableWidget opts rows+            Nothing -> defaultTBWOptions+            Just sz -> defaultTBWOptions {tbwopt_minSize = sz}+     in newTableWidget opts rows  data MainWidget = MainWidget     { toplineWidget :: ToplineWidget@@ -329,48 +389,63 @@     , botlineWidget :: BotlineWidget     , msglineWidget :: MsglineWidget     , contactListWidget :: ContactListWidget-    , contactDetailsWidget :: ContactDetailsWidget }+    , contactDetailsWidget :: ContactDetailsWidget+    }  instance Widget MainWidget where     draw pos sz hint w = draw pos sz hint (mkRealMainWidget (Just sz) w)     minSize w = minSize (mkRealMainWidget Nothing w)  mkRealMainWidget msz w =-    let cells = [ TableCell $ toplineWidget w-                , TableCell $ contactListWidget w-                , TableCell $ midlineWidget w-                , TableCell $ contactDetailsWidget w-                , TableCell $ botlineWidget w-                , TableCell $ msglineWidget w ]+    let cells =+            [ TableCell $ toplineWidget w+            , TableCell $ contactListWidget w+            , TableCell $ midlineWidget w+            , TableCell $ contactDetailsWidget w+            , TableCell $ botlineWidget w+            , TableCell $ msglineWidget w+            ]         rows = map singletonRow cells         opts = case msz of-                 Nothing -> defaultTBWOptions-                 Just sz -> defaultTBWOptions { tbwopt_minSize = sz }-        in newTableWidget opts rows+            Nothing -> defaultTBWOptions+            Just sz -> defaultTBWOptions {tbwopt_minSize = sz}+     in newTableWidget opts rows  updateStateDependentWidgets :: MainWidget -> ContactListWidget -> CM MainWidget updateStateDependentWidgets w listWidget =-    do detailsWidget <- mkContactDetailsWidget listWidget-       midlineWidget <- mkMidlineWidget listWidget-       return $ w { contactListWidget = listWidget-                  , contactDetailsWidget = detailsWidget-                  , midlineWidget = midlineWidget }+    do+        detailsWidget <- mkContactDetailsWidget listWidget+        midlineWidget <- mkMidlineWidget listWidget+        return $+            w+                { contactListWidget = listWidget+                , contactDetailsWidget = detailsWidget+                , midlineWidget = midlineWidget+                }  move :: Direction -> MainWidget -> CM MainWidget move dir w =-    do sz <- getSize-       let listWidget = tableWidgetMove dir sz (contactListWidget w)-       updateStateDependentWidgets w listWidget+    do+        sz <- getSize+        let listWidget = tableWidgetMove dir sz (contactListWidget w)+        updateStateDependentWidgets w listWidget  delete w =     let lw = contactListWidget w-        in case tbw_pos lw of-             Nothing -> return w-             Just (row,_) ->-                 let lw' = tableWidgetDeleteRow row lw-                     in do modify (\s -> s { cm_contacts =-                                             deleteAt row (cm_contacts s) })-                           updateStateDependentWidgets w lw'+     in case tbw_pos lw of+            Nothing -> return w+            Just (row, _) ->+                let lw' = tableWidgetDeleteRow row lw+                 in do+                        modify+                            ( \s ->+                                s+                                    { cm_contacts =+                                        deleteAt row (cm_contacts s)+                                    }+                            )+                        updateStateDependentWidgets w lw'+ {- editEventloop w ewm =     do k <- CursesH.getKey (resize mkMainEditWidget)@@ -390,64 +465,76 @@  edit w =     let lw = contactListWidget w-        in case tbw_pos lw of-             Nothing -> return w-             Just (row,_) ->-                 do contacts <- gets cm_contacts+     in case tbw_pos lw of+            Nothing -> return w+            Just (row, _) ->+                do+                    contacts <- gets cm_contacts                     let c = contacts !! row                     ew <- mkMainEditWidget c                     redraw ew                     return w-                    --editEventloop w ew -resize :: Widget w => CM w -> CM ()+-- editEventloop w ew++resize :: (Widget w) => CM w -> CM () resize f =-    do liftIO $ do Curses.endWin-                   Curses.resetParams-                   Curses.cursSet Curses.CursorInvisible-                   Curses.refresh-       w <- f-       redraw w+    do+        liftIO $ do+            Curses.endWin+            Curses.resetParams+            Curses.cursSet Curses.CursorInvisible+            Curses.refresh+        w <- f+        redraw w -redraw :: Widget w => w -> CM ()+redraw :: (Widget w) => w -> CM () redraw w =-    do sz <- getSize-       liftIO $ draw (0, 0) sz DHNormal w-       liftIO $ Curses.refresh+    do+        sz <- getSize+        liftIO $ draw (0, 0) sz DHNormal w+        liftIO $ Curses.refresh  eventloop w =-    do k <- CursesH.getKey (resize mkMainWidget)-       debug ("Got key " ++ show k)-       case k of-         Curses.KeyChar 'q' -> return ()-         Curses.KeyChar 'd' -> process $ delete w-         Curses.KeyChar 'e' -> process $ edit w-         Curses.KeyUp       -> process $ move DirUp w-         Curses.KeyDown     -> process $ move DirDown w-         _ -> eventloop w-    where process f =-              do w' <- f-                 redraw w'-                 eventloop w'+    do+        k <- CursesH.getKey (resize mkMainWidget)+        debug ("Got key " ++ show k)+        case k of+            Curses.KeyChar 'q' -> return ()+            Curses.KeyChar 'd' -> process $ delete w+            Curses.KeyChar 'e' -> process $ edit w+            Curses.KeyUp -> process $ move DirUp w+            Curses.KeyDown -> process $ move DirDown w+            _ -> eventloop w+  where+    process f =+        do+            w' <- f+            redraw w'+            eventloop w' cmMain :: CM () cmMain =-    do w <- mkMainWidget-       redraw w-       eventloop w-+    do+        w <- mkMainWidget+        redraw w+        eventloop w  main :: IO () main =-    do args <- getArgs-       contacts <--             if length args /= 1-                then do p <- getProgName-                        putStrLn ("Usage: " ++ p ++ " contact-file")-                        exitFailure-                else readContacts (args!!0)-       runCurses contacts `finally` CursesH.end-    where runCurses contacts =-              do CursesH.start-                 cstyles <- CursesH.convertStyles styles-                 Curses.cursSet Curses.CursorInvisible-                 runCM cstyles contacts cmMain+    do+        args <- getArgs+        contacts <-+            if length args /= 1+                then do+                    p <- getProgName+                    putStrLn ("Usage: " ++ p ++ " contact-file")+                    exitFailure+                else readContacts (args !! 0)+        runCurses contacts `finally` CursesH.end+  where+    runCurses contacts =+        do+            CursesH.start+            cstyles <- CursesH.convertStyles styles+            Curses.cursSet Curses.CursorInvisible+            runCM cstyles contacts cmMain
− example/Setup.hs
@@ -1,3 +0,0 @@-#!/usr/bin/env runhaskell-import Distribution.Simple-main = defaultMainWithHooks simpleUserHooks
− example/cm.cabal
@@ -1,14 +0,0 @@-Name:          contact-manager-Synopsis:      Primitive addressbook-Version:       0.1-License:       GPL-License-file:  ../LICENSE-Author:   Stefan Wehr <http://www.informatik.uni-freiburg.de/~wehr>--Build-Depends:       base>3, hscurses >= 1.2, mtl, pretty-Build-Type:          Simple--Executable:          cm-Main-Is:             ContactManager.hs-ghc-options:         -funbox-strict-fields -threaded-
− example/contacts
@@ -1,1 +0,0 @@-[Contact {lastName = "Wehr", firstName = "Stefan", emailAddress = "mail AT stefanwehr DOT de", address = "28 Loch Maree St", zipCode = "2032", city = "Kingsford", province = "NSW", country = "Australia", phoneNumber = ""},Contact {lastName = "Thorpe", firstName = "Ian", emailAddress = "ian@aol7.com.au", address = "", zipCode = "", city = "Perth", province = "", country = "Australia", phoneNumber = ""},Contact {lastName = "Gates", firstName = "Bill", emailAddress = "billy@microsoft.com", address = "", zipCode = "", city = "", province = "", country = "", phoneNumber = ""},Contact {lastName = "Stewart", firstName = "Don", emailAddress = "", address = "CSE, UNSW, 501-16, k17 building", zipCode = "", city = "Sydney", province = "", country = "Australia", phoneNumber = ""}]
− example/contacts2
@@ -1,1 +0,0 @@-[Contact {lastName = "Wehr", firstName = "Stefan", emailAddress = "mail AT stefanwehr DOT de", address = "28 Loch Maree St", zipCode = "2032", city = "Kingsford", province = "NSW", country = "Australia", phoneNumber = ""},Contact {lastName = "Thorpe", firstName = "Ian", emailAddress = "ian@aol7.com.au", address = "", zipCode = "", city = "Perth", province = "", country = "Australia", phoneNumber = ""},Contact {lastName = "Gates", firstName = "Bill", emailAddress = "billy@microsoft.com", address = "", zipCode = "", city = "", province = "", country = "", phoneNumber = ""},Contact {lastName = "Stewart", firstName = "Don", emailAddress = "", address = "CSE, UNSW, 501-16, k17 building", zipCode = "", city = "Sydney", province = "", country = "Australia", phoneNumber = ""}]
hscurses.cabal view
@@ -1,55 +1,177 @@-Name:           hscurses-Version:        1.4.2.0-License:        LGPL-License-file:   LICENSE-Author:         John Meacham <john at repetae dot net>-                Tuomo Valkonen <tuomov at iki.fi>-                Don Stewart <http://www.cse.unsw.edu.au/~dons>-                Stefan Wehr <http://www.stefanwehr.de>-Copyright:      Stefan Wehr 2004 - 2011-                Don Stewart 2004-                Tuomo Valkonen 2004-                John Meacham 2002-2004-Maintainer:     Stefan Wehr <http://www.stefanwehr.de>-Stability:      Stable-Category:       User-interface-Synopsis:       NCurses bindings for Haskell-Description:-    Binding to NCurses, a library of functions that manage an-    application's display on character-cell terminals. Additionally,-    it contains some basic widgets such as a text input widget and-    a table widget.-Homepage:       https://github.com/skogsbaer/hscurses-Cabal-version: >= 1.6-Build-Type:     Configure-Tested-with:    GHC==7.6.1, GHC==7.8-Data-files:-    README, TODO, ChangeLog,-    example/contacts2, example/Setup.hs, example/ContactManager.hs,-    example/cm.cabal, example/contacts, cbits/config.h.in-    configure, configure.ac, hscurses.buildinfo.in,-    cbits/HSCurses.h, cbits/HSCursesUtils.h+name:               hscurses+version:            1.5.0.0+license:            LGPL+license-file:       LICENSE+author:+  John Meacham <john at repetae dot net>+  Tuomo Valkonen <tuomov at iki.fi>+  Don Stewart <http://www.cse.unsw.edu.au/~dons>+  Stefan Wehr <http://www.stefanwehr.de> -Source-Repository head-  Type:           git-  Location:       git://github.com/skogsbaer/hscurses.git+extra-source-files: ChangeLog+copyright:+  Stefan Wehr 2004 - 2011+  Don Stewart 2004+  Tuomo Valkonen 2004+  John Meacham 2002-2004 -Library-  Build-depends:  base == 4.*, exceptions, mtl,-                  old-time < 1.2, old-locale == 1.0.*+maintainer:         Stefan Wehr <http://www.stefanwehr.de>+stability:          Stable+category:           User-interface+synopsis:           NCurses bindings for Haskell+description:+  Binding to NCurses, a library of functions that manage an+  application's display on character-cell terminals. Additionally,+  it contains some basic widgets such as a text input widget and+  a table widget.++homepage:           https://github.com/skogsbaer/hscurses+cabal-version:      >=1.10+build-type:         Configure+tested-with:+  GHC ==7.6.1+   || ==7.8+   || ==8.4.4+   || ==8.6.5+   || ==8.8.4+   || ==8.10.7+   || ==9.4.8+   || ==9.6.6+   || ==9.8.4+   || ==9.10.1+   || ==9.12.1++data-files:+  cbits/config.h.in+  cbits/HSCurses.h+  cbits/HSCursesUtils.h+  configure+  configure.ac+  hscurses.buildinfo.in+  README.md+  TODO++source-repository head+  type:     git+  location: https://github.com/skogsbaer/hscurses++flag examples+  description: Build test and example binaries+  default:     False+  manual:      True++flag debug+  description: Compile with logging/tracing functions+  default:     False+  manual:      True++library+  build-depends:+      base        >=4   && <5+    , exceptions  <0.11+    , mtl         <2.4+    , time        <1.13++  extra-libraries:    ncurses+   if !os(windows)-    Build-depends: unix >= 2.4-  Exposed-modules:-      UI.HSCurses.Curses, UI.HSCurses.CursesHelper, UI.HSCurses.Widgets,-      UI.HSCurses.Logging-  Other-modules:-      UI.HSCurses.CWString, UI.HSCurses.IConv-  C-sources:-      cbits/HSCursesUtils.c-  Includes:-      HSCurses.h, HSCursesUtils.h-  Extensions:-      CPP, ForeignFunctionInterface, GeneralizedNewtypeDeriving,-      ScopedTypeVariables, ExistentialQuantification-  Include-dirs:     cbits .-  Ghc-options:      -funbox-strict-fields -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing+    build-depends: unix <2.9++  if flag(debug)+    cpp-options: -DDEBUG=1++  exposed-modules:+    UI.HSCurses.Curses+    UI.HSCurses.CursesHelper+    UI.HSCurses.Logging+    UI.HSCurses.Widgets++  other-modules:+    UI.HSCurses.CWString+    UI.HSCurses.IConv++  c-sources:          cbits/HSCursesUtils.c+  includes:+    HSCurses.h+    HSCursesUtils.h++  default-extensions:+    CPP+    ExistentialQuantification+    ForeignFunctionInterface+    GeneralizedNewtypeDeriving+    ScopedTypeVariables++  include-dirs:       cbits+  ghc-options:+    -funbox-strict-fields -Wall -fno-warn-unused-do-bind+    -fno-warn-name-shadowing++  default-language:   Haskell2010++executable contact-manager+  if flag(examples)+    build-depends:+        base      >=4.0 && <5+      , hscurses+      , mtl       >=2.0 && <2.4+      , pretty    >=1.0 && <1.2++  else+    buildable: False++  hs-source-dirs:   example+  default-language: Haskell2010+  main-is:          ContactManager.hs++executable key-test+  if flag(examples)+    build-depends:+        base      >=4.0 && <5+      , hscurses++  else+    buildable: False++  hs-source-dirs:   tests/key-test+  default-language: Haskell2010+  main-is:          KeyTest.hs++executable widget-test-text+  if flag(examples)+    build-depends:+        base      >=4.0 && <5+      , hscurses++  else+    buildable: False++  hs-source-dirs:   tests/widget-test+  default-language: Haskell2010+  main-is:          TextTest.hs++executable widget-test-table+  if flag(examples)+    build-depends:+        base      >=4.0 && <5+      , hscurses++  else+    buildable: False++  hs-source-dirs:   tests/widget-test+  default-language: Haskell2010+  main-is:          TableTest.hs++executable widget-test-edit+  if flag(examples)+    build-depends:+        base      >=4.0+      , hscurses++  else+    buildable: False++  hs-source-dirs:   tests/widget-test+  default-language: Haskell2010+  main-is:          EditTest.hs
+ tests/key-test/KeyTest.hs view
@@ -0,0 +1,34 @@+import UI.HSCurses.Curses+import qualified UI.HSCurses.CursesHelper as CursesH++import Control.Exception+import Control.Monad (forever)+import Data.Char+import System.Exit++draw s = do+    (h, w) <- scrSize+    CursesH.gotoTop+    CursesH.drawLine w s+    refresh++done = return ()++main :: IO ()+main =+    finally+        ( do+            CursesH.start+            draw "Press any key, click the mouse, or use the mouse wheel"+            withAllMouseEvents $ do+                forever $ do+                    c <- CursesH.getKey done+                    case c of+                        KeyChar 'q' -> exitWith ExitSuccess+                        KeyMouse -> do+                            draw "mouse\n"+                            me <- getMouse+                            draw (show me)+                        x -> draw ("Last key: " ++ CursesH.displayKey x ++ " (" ++ show x ++ ")")+        )+        CursesH.end
+ tests/widget-test/EditTest.hs view
@@ -0,0 +1,59 @@+import qualified UI.HSCurses.Curses as Curses+import qualified UI.HSCurses.CursesHelper as CursesH+import UI.HSCurses.Widgets++import Control.Exception+import System.Exit++draw s = do+    (h, w) <- Curses.scrSize+    CursesH.gotoTop+    CursesH.drawLine w s+    Curses.refresh++done = return ()++forever :: (Monad m) => m t -> m t+forever x = x >> forever x++exit ew _ _ = return (Done ew)++options stys =+    let dsty = (mkDrawingStyle (stys !! 1)) {dstyle_active = stys !! 1}+     in defaultEWOptions {ewopt_style = dsty}++editWidget stys = newEditWidget (options stys) ""++edit ew = do+    (ew', s) <- activateEditWidget done (1, 10) (1, 10) ew+    Curses.wMove Curses.stdScr 5 0+    CursesH.drawLine 60 ("saved: " ++ s)+    Curses.refresh+    return ew'++loop ew = do+    c <- CursesH.getKey done+    ew' <- case c of+        Curses.KeyChar 'q' -> exitWith ExitSuccess+        Curses.KeyChar 'e' -> edit ew+        _ -> return ew+    loop ew'++styles =+    [ CursesH.defaultStyle+    , CursesH.Style CursesH.CyanF CursesH.PurpleB+    ]++main :: IO ()+main =+    do+        CursesH.start+        cstyles <- CursesH.convertStyles styles+        Curses.cursSet Curses.CursorInvisible+        CursesH.gotoTop+        CursesH.drawLine 20 "Hit 'e'!"+        Curses.wMove Curses.stdScr 1 0+        CursesH.drawLine 9 "Input: "+        Curses.refresh+        loop (editWidget cstyles)+        `finally` CursesH.end
+ tests/widget-test/TableTest.hs view
@@ -0,0 +1,116 @@+import Control.Exception+import System.Exit+import qualified UI.HSCurses.Curses as Curses+import qualified UI.HSCurses.CursesHelper as CursesH+import UI.HSCurses.Widgets++row1 sty = map (TableCell . newTextWidget defaultTWOptions) ["1", "eins", "one"]+row2 sty =+    TableCell+        ( newTextWidget+            ( defaultTWOptions+                { twopt_size =+                    TWSizeFixed (2, 10)+                , twopt_style =+                    mkDrawingStyle (sty !! 2)+                }+            )+            "2"+        )+        : map (TableCell . newTextWidget defaultTWOptions) ["zwei", "two"]+row3 sty =+    map (TableCell . newTextWidget defaultTWOptions) ["3", "drei"]+        ++ [ ActiveTableCell $+                newEditWidget+                    ( defaultEWOptions+                        { ewopt_style =+                            mkDrawingStyle (sty !! 1)+                        }+                    )+                    ""+           ]+row4 sty =+    map+        (TableCell . newTextWidget defaultTWOptions)+        ["4", "vier", "four"]+row5 sty =+    map (TableCell . newTextWidget defaultTWOptions) ["5", "fuenf"]+        ++ [ TableCell+                ( newTextWidget+                    ( defaultTWOptions+                        { twopt_size =+                            TWSizeFixed (1, 6)+                        , twopt_style =+                            mkDrawingStyle (sty !! 3)+                        }+                    )+                    "five56XXXXX"+                )+           ]++rows sty = [row1 sty, row2 sty, row3 sty, row4 sty, row5 sty]++tableWidget sty = newTableWidget (TBWOptions (Just 1) None [0, 2] (10, 10)) (rows sty)++tableSize = (2, 50)+tablePos = (1, 0)++msgSize = (1, 40)+msgPos = (10, 0)++text = "0        1         2         3         4         5"++done = return ()++loop tbw msg = do+    drawTableWidget tablePos tableSize DHNormal tbw+    drawTextWidget msgPos msgSize DHNormal msg+    c <- CursesH.getKey done+    case c of+        Curses.KeyChar 'q' -> exitWith ExitSuccess+        Curses.KeyChar ' ' -> loop (tableWidgetScrollDown tableSize tbw) msg+        Curses.KeyChar '-' -> loop (tableWidgetScrollUp tableSize tbw) msg+        Curses.KeyRight -> loop (tableWidgetGoRight tableSize tbw) msg+        Curses.KeyLeft -> loop (tableWidgetGoLeft tableSize tbw) msg+        Curses.KeyUp -> loop (tableWidgetGoUp tableSize tbw) msg+        Curses.KeyDown -> loop (tableWidgetGoDown tableSize tbw) msg+        Curses.KeyChar '\r' -> do+            (new, res) <-+                tableWidgetActivateCurrent+                    done+                    tablePos+                    tableSize+                    DHNormal+                    tbw+            let msg' = case res of+                    Nothing ->+                        textWidgetSetText+                            msg+                            "could not activate current cell"+                    Just s ->+                        textWidgetSetText+                            msg+                            ("new content: <" ++ s ++ ">")+            loop new msg'+        _ -> loop tbw msg++styles =+    [ CursesH.defaultStyle+    , CursesH.Style CursesH.WhiteF CursesH.PurpleB+    , CursesH.AttributeStyle [CursesH.Dim] CursesH.CyanF CursesH.WhiteB+    , CursesH.ColorlessStyle [CursesH.Bold]+    ]++main :: IO ()+main =+    do+        CursesH.start+        cstyles <- CursesH.convertStyles styles+        Curses.cursSet Curses.CursorInvisible+        drawTextWidget+            (0, 0)+            (1, 60)+            DHFocus+            (newTextWidget defaultTWOptions text)+        loop (tableWidget cstyles) (newTextWidget defaultTWOptions "")+        `finally` CursesH.end
+ tests/widget-test/TextTest.hs view
@@ -0,0 +1,37 @@+import Control.Exception+import System.Exit+import qualified UI.HSCurses.Curses as Curses+import qualified UI.HSCurses.CursesHelper as CursesH+import UI.HSCurses.Widgets++twOptions sty = defaultTWOptions {twopt_style = mkDrawingStyle sty}++text sty = newTextWidget (twOptions sty) (reverse "1\n12\n123\n1234\n12345\n123456\n1234567\n12345678\n123456789\n1234567890")++textSize = (4, 4)++loop tw = do+    drawTextWidget (0, 0) textSize DHNormal tw+    c <- CursesH.getKey done+    case c of+        Curses.KeyChar 'q' -> exitWith ExitSuccess+        Curses.KeyChar ' ' -> loop $ textWidgetScrollDown textSize tw+        Curses.KeyChar '-' -> loop $ textWidgetScrollUp textSize tw+        Curses.KeyChar ',' -> loop $ textWidgetScrollLeft textSize tw+        Curses.KeyChar '.' -> loop $ textWidgetScrollRight textSize tw+        _ -> loop tw++done :: IO ()+done = return ()++styles = [CursesH.defaultStyle, CursesH.Style CursesH.WhiteF CursesH.PurpleB]++main :: IO ()+main =+    do+        CursesH.start+        cstyles <- CursesH.convertStyles styles+        Curses.cursSet Curses.CursorInvisible+        CursesH.gotoTop+        loop (text (cstyles !! 1))+        `finally` CursesH.end