module Graphics.UI.Sifflet.EditArgsPanel
(
ArgSpecAction
, EditArgsPanel
, makeEditArgsPanel
, editArgsPanelRoot
, expandToFit
)
where
import Data.IORef
import Graphics.UI.Gtk (EventBox, hButtonBoxNew,
containerRemove,
ButtonBoxClass, buttonActivated,
widgetGetParent,
widgetQueueResize)
import Language.Sifflet.Expr
import Graphics.UI.Sifflet.Callback
import Graphics.UI.Sifflet.LittleGtk
import Language.Sifflet.Util (SuccFail(..), parseInt)
type ArgSpecAction = [ArgSpec] -> IO ()
type PanelRoot = EventBox
data EditArgsPanel =
EditArgsPanel {editArgsPanelRoot :: PanelRoot,
editArgsPanelAction :: ArgSpecAction}
type StateRef = IORef State
data State = State Model UI
newtype Model = Model [ArgSpec]
deriving (Show)
data UI = UI PanelRoot Table Label [ArgRow]
data ArgRow = ArgRow Entry Entry Button
makeEditArgsPanel :: CBMgr -> [ArgSpec] -> ArgSpecAction
-> IO EditArgsPanel
makeEditArgsPanel cbMgr argSpecs okayAction = do
root <- eventBoxNew
frame <- frameNew
frameSetLabel frame "Edit Arguments"
let panel = EditArgsPanel {editArgsPanelRoot = root,
editArgsPanelAction = okayAction}
vbox <- vBoxNew False 5
table <- tableNew (length argSpecs + 2) 3 False
status <- labelNew (Just "")
stateRef <- newIORef (State (Model argSpecs) (UI root table status []))
dressTable stateRef
btnBox <- hButtonBoxNew
fillButtonBox cbMgr btnBox stateRef panel
containerAdd root frame
containerAdd frame vbox
boxPackStartDefaults vbox table
boxPackStartDefaults vbox btnBox
boxPackStartDefaults vbox status
widgetShowAll root
return panel
dressTable :: StateRef -> IO ()
dressTable sref = do
State (Model args) ui@(UI root table status _rows) <- readIORef sref
argLabel <- labelNew (Just "Name")
inputsLabel <- labelNew (Just "Inlets")
tableAttachCell table argLabel 0 0
tableAttachCell table inputsLabel 0 1
let n = length args
argRows <- mapM (uncurry (argRowNew sref))
(zip (args ++ [ArgSpec "" 0]) [0 .. n])
mapM_ (uncurry (attachRow table))
(zip argRows [1 .. n + 1])
setStatusOK ui
widgetShowAll table
writeIORef sref (State (Model args) (UI root table status argRows))
expandToFit :: (WidgetClass v, WidgetClass w) => v -> w -> IO ()
expandToFit container widget = do
Requisition w1 h1 <- widgetSizeRequest container
Requisition w2 h2 <- widgetSizeRequest widget
let (w, h) = (max w1 w2, max h1 h2)
widgetSetSizeRequest container w h
widgetQueueResize container
stripTable :: StateRef -> IO ()
stripTable sref = do
State model ui@(UI root table status _rows) <- readIORef sref
let stripWidget widget =
do
containerRemove table widget
widgetDestroy widget
widgets <- containerGetChildren table
mapM_ stripWidget widgets
setStatusOK ui
writeIORef sref (State model (UI root table status []))
attachRow :: Table -> ArgRow -> Int -> IO ()
attachRow table (ArgRow nameEntry nEntry btn) nrow = do
tableAttachCell table nameEntry nrow 0
tableAttachCell table nEntry nrow 1
tableAttachCell table btn nrow 2
tableAttachCell :: (WidgetClass w) => Table -> w -> Int -> Int -> IO ()
tableAttachCell t w top left =
tableAttachDefaults t w left (left + 1) top (top + 1)
fillButtonBox :: (ButtonBoxClass b) =>
CBMgr -> b -> StateRef -> EditArgsPanel -> IO ()
fillButtonBox cbMgr btnBox sref panel =
let addButton (label, action) = do
b <- buttonNewWithLabel label
containerAdd btnBox b
cbMgr (AfterButtonClicked b action)
addButton' label action = do
b <- buttonNewWithLabel label
containerAdd btnBox b
_ <- on b buttonActivated action
return ()
applyAction :: IO ()
applyAction = applyArgRows sref (editArgsPanelAction panel)
okayAction =
applyAction >> closePanel panel
in do
addButton' "OK" okayAction
mapM_ addButton [
("Cancel", \ _ -> closePanel panel)]
closePanel :: EditArgsPanel -> IO ()
closePanel panel = widgetDestroy (editArgsPanelRoot panel)
argRowNew :: StateRef -> ArgSpec -> Int -> IO ArgRow
argRowNew sref (ArgSpec name inlets) n = do
e1 <- makeEntry name
e2 <- makeEntry (show inlets)
let (label, action) =
case name of
"" -> ("Add", addArg sref n)
_ -> ("Remove", removeArg sref n)
b <- buttonNewWithLabel label
_ <- on b buttonActivated action
return (ArgRow e1 e2 b)
addArg :: StateRef -> Int -> IO ()
addArg sref n = do
State (Model argSpecs) ui@(UI root _table _status argRows) <- readIORef sref
readResult <- readArgRow (argRows !! n)
case readResult of
Succ argTool ->
do
writeIORef sref (State (Model (argSpecs ++ [argTool])) ui)
stripTable sref
dressTable sref
mparent <- widgetGetParent root
case mparent of
Nothing -> return ()
Just parent -> expandToFit parent root
setStatusOK ui
Fail msg -> setStatus ui $ "Add: " ++ msg
readArgRow :: ArgRow -> IO (SuccFail ArgSpec)
readArgRow (ArgRow e1 e2 _) = do
name <- entryGetText e1
case name of
"" -> return $ Fail "blank name is not allowed"
_ -> do
inletsStr <- entryGetText e2
return $ case parseInt ("Inlets for " ++ name) inletsStr of
Succ inlets -> Succ (ArgSpec name inlets)
Fail msg -> Fail msg
removeArg :: StateRef -> Int -> IO ()
removeArg sref n = do
State (Model argSpecs) ui <- readIORef sref
writeIORef sref (State (Model (listRemove argSpecs n)) ui)
stripTable sref
dressTable sref
setStatusOK ui
listRemove :: [a] -> Int -> [a]
listRemove [] _ = error "listRemove: empty"
listRemove (_:xs) 0 = xs
listRemove (x:xs) n = x : (listRemove xs (n 1))
makeEntry :: String -> IO Entry
makeEntry text = do
entry <- entryNew
entrySetText entry text
return entry
applyArgRows :: StateRef -> ArgSpecAction -> IO ()
applyArgRows sref action = do
State _model ui@(UI _root _table _status argRows) <- readIORef sref
readResult <- readArgRows argRows
case readResult of
Succ newSpecs ->
do
setStatusOK ui
writeIORef sref (State (Model newSpecs) ui)
action newSpecs
Fail msg ->
setStatus ui ("Apply: " ++ msg) >>
return ()
readArgRows :: [ArgRow] -> IO (SuccFail [ArgSpec])
readArgRows [] = return (Succ [])
readArgRows (_:[]) = return (Succ [])
readArgRows (row:rows) = do
results <- readArgRows rows
case results of
Fail msg -> return $ Fail msg
Succ specs ->
do
result <- readArgRow row
case result of
Fail msg -> return $ Fail msg
Succ spec -> return $ Succ (spec:specs)
setStatusOK :: UI -> IO ()
setStatusOK ui = setStatus ui ""
setStatus :: UI -> String -> IO ()
setStatus (UI _ _ status _) msg =
labelSetText status msg