Allure-0.4.2: src/Display/Gtk.hs
module Display.Gtk
(displayId, startup, shutdown, display, nextEvent, Session) where
import Control.Monad
import Control.Concurrent
import Graphics.UI.Gtk.Gdk.Events -- TODO: replace, deprecated
import Graphics.UI.Gtk
import qualified Data.List as L
import Data.IORef
import qualified Data.Map as M
import qualified Data.ByteString.Char8 as BS
import Area
import Loc
import Geometry
import qualified Keys as K (Key(..), keyTranslate)
import qualified Color
displayId :: String
displayId = "gtk"
data Session = Session
{ schan :: Chan String
, stags :: M.Map Color.Attr TextTag
, sview :: TextView
}
startup :: (Session -> IO ()) -> IO ()
startup k =
do
-- initGUI
unsafeInitGUIForThreadedRTS
w <- windowNew
ttt <- textTagTableNew
-- text attributes
tts <- fmap M.fromList $
mapM (\ ak -> do
tt <- textTagNew Nothing
textTagTableAdd ttt tt
doAttr tt ak
return (ak, tt))
[ (f, b) | f <- [minBound..maxBound], b <- Color.legalBG ]
-- text buffer
tb <- textBufferNew (Just ttt)
textBufferSetText tb (unlines (replicate 25 (replicate 80 ' ')))
-- create text view
tv <- textViewNewWithBuffer tb
containerAdd w tv
textViewSetEditable tv False
textViewSetCursorVisible tv False
-- font
f <- fontDescriptionNew
fontDescriptionSetFamily f "Monospace"
fontDescriptionSetSize f 12
widgetModifyFont tv (Just f)
currentfont <- newIORef f
onButtonPress tv (\ e -> case e of
Button { Graphics.UI.Gtk.Gdk.Events.eventButton = RightButton } ->
do
fsd <- fontSelectionDialogNew "Choose font"
cf <- readIORef currentfont
fds <- fontDescriptionToString cf
fontSelectionDialogSetFontName fsd fds
fontSelectionDialogSetPreviewText fsd "+##@##-...|"
resp <- dialogRun fsd
when (resp == ResponseOk) $
do
fn <- fontSelectionDialogGetFontName fsd
case fn of
Just fn' -> do
fd <- fontDescriptionFromString fn'
writeIORef currentfont fd
widgetModifyFont tv (Just fd)
Nothing -> return ()
widgetDestroy fsd
return True
_ -> return False)
let black = Color minBound minBound minBound -- Color.defBG == Color.Black
white = Color 0xC500 0xBC00 0xB800 -- Color.defFG == Color.White
widgetModifyBase tv StateNormal black
widgetModifyText tv StateNormal white
ec <- newChan
forkIO $ k (Session ec tts tv)
onKeyPress tv
(\ e -> do
writeChan ec (Graphics.UI.Gtk.Gdk.Events.eventKeyName e)
return True)
onDestroy w mainQuit -- set quit handler
widgetShowAll w
yield
mainGUI
shutdown :: Session -> IO ()
shutdown _ = mainQuit
display :: Area -> Session -> (Loc -> (Color.Attr, Char)) -> String -> String
-> IO ()
display (x0, y0, x1, y1) session f msg status =
postGUIAsync $ do
tb <- textViewGetBuffer (sview session)
let fLine y = let (as, cs) = unzip [ f (toLoc (x1 + 1) (x, y))
| x <- [x0..x1] ]
in ((y, as), BS.pack cs)
memo = L.map fLine [y0..y1]
attrs = L.map fst memo
chars = L.map snd memo
bs = [BS.pack msg, BS.pack "\n", BS.unlines chars, BS.pack status]
textBufferSetByteString tb (BS.concat bs)
mapM_ (setTo tb (stags session) x0) attrs
setTo :: TextBuffer -> M.Map Color.Attr TextTag -> X -> (Y, [Color.Attr])
-> IO ()
setTo _ _ _ (_, []) = return ()
setTo tb tts lx (ly, attr:attrs) = do
ib <- textBufferGetIterAtLineOffset tb (ly + 1) lx
ie <- textIterCopy ib
let setIter :: Color.Attr -> Int -> [Color.Attr] -> IO ()
setIter previous repetitions [] = do
textIterForwardChars ie repetitions
when (previous /= Color.defaultAttr) $
textBufferApplyTag tb (tts M.! previous) ib ie
setIter previous repetitions (a:as)
| a == previous =
setIter a (repetitions + 1) as
| otherwise = do
textIterForwardChars ie repetitions
when (previous /= Color.defaultAttr) $
textBufferApplyTag tb (tts M.! previous) ib ie
textIterForwardChars ib repetitions
setIter a 1 as
setIter attr 1 attrs
-- | reads until a non-dead key encountered
readUndeadChan :: Chan String -> IO String
readUndeadChan ch =
do
x <- readChan ch
if dead x then readUndeadChan ch else return x
where
dead x =
case x of
"Shift_R" -> True
"Shift_L" -> True
"Control_L" -> True
"Control_R" -> True
"Super_L" -> True
"Super_R" -> True
"Menu" -> True
"Alt_L" -> True
"Alt_R" -> True
"ISO_Level2_Shift" -> True
"ISO_Level3_Shift" -> True
"ISO_Level2_Latch" -> True
"ISO_Level3_Latch" -> True
"Num_Lock" -> True
"Caps_Lock" -> True
_ -> False
nextEvent :: Session -> IO K.Key
nextEvent session =
do
e <- readUndeadChan (schan session)
return (K.keyTranslate e)
doAttr :: TextTag -> Color.Attr -> IO ()
doAttr tt (fg, bg)
| (fg, bg) == Color.defaultAttr = return ()
| fg == Color.defFG = set tt [textTagBackground := Color.colorToRGB bg]
| bg == Color.defBG = set tt [textTagForeground := Color.colorToRGB fg]
| otherwise = set tt [textTagForeground := Color.colorToRGB fg,
textTagBackground := Color.colorToRGB bg]