{-# LANGUAGE ScopedTypeVariables #-} -- | This module implements a simple list box to which strings can be -- added at the end and deleted. module HTk.Toolkit.SimpleListBox( SimpleListBox, newSimpleListBox, -- :: String -> (value -> String) -> (Distance,Distance) -- -> IO (SimpleListBox value) -- Create a ListBox. The String gives the title for the box; the -- function argument gives the String's which -- are displayed; the integers give the width (characters) and height -- (rows) of the displayed section of the box. -- This implements Destroyable. SimpleListBoxItem, -- Instance of Object, Eq, Ord. addItemAtEnd, -- :: SimpleListBox value -> value -> IO (SimpleListBoxItem value) deleteItem, -- :: SimpleListBox value -> SimpleListBoxItem value -> IO () getItems, -- :: SimpleListBox value -> IO [value] bindSelection, -- :: SimpleListBox value -- -> IO (Event [SimpleListBoxItem value]),IO ()) -- Returns an event for selections in this list box. ([] can happen, -- for example, if the user selects or clicks an area into which no -- item has yet been added.) ) where import Data.Maybe import Control.Concurrent.MVar import Util.ExtendedPrelude import Util.Object import Util.Computation import Events.Events import HTk.Kernel.Core(GUIObject(..)) import HTk.Toplevel.HTk -- ------------------------------------------------------------------------- -- Datatypes -- ------------------------------------------------------------------------- data SimpleListBox val = SimpleListBox { frame :: Frame, -- contains the list box, and the scroll-bar. listBox :: ListBox String, mkString :: val -> String, contentsMVar :: MVar [SimpleListBoxItem val] } data SimpleListBoxItem val = SimpleListBoxItem { val :: val, oID :: ObjectID } -- ------------------------------------------------------------------------- -- Non-HTk Instances -- ------------------------------------------------------------------------- instance Object (SimpleListBox val) where objectID simpleListBox = objectID (toGUIObject simpleListBox) instance Destroyable (SimpleListBox val) where destroy simpleListBox = destroy (frame simpleListBox) instance Object (SimpleListBoxItem val) where objectID simpleListBoxItem = oID simpleListBoxItem instance Eq (SimpleListBoxItem val) where (==) = mapEq oID instance Ord (SimpleListBoxItem val) where compare = mapOrd oID -- ------------------------------------------------------------------------- -- HTk instances (needed for packing a list box) -- ------------------------------------------------------------------------- instance GUIObject (SimpleListBox val) where toGUIObject simpleListBox = toGUIObject (frame simpleListBox) cname _ = "SimpleListBox" instance Widget (SimpleListBox val) instance HasSize (SimpleListBox val) -- ------------------------------------------------------------------------- -- Functions -- ------------------------------------------------------------------------- newSimpleListBox :: Container par => par -> (val -> String) -> [Config (SimpleListBox val)] -> IO (SimpleListBox val) newSimpleListBox parent mkString configs = do frame <- newFrame parent [] listBox <- newListBox frame [value ([] :: [String]),bg "white"] pack listBox [Side AtLeft,Fill Y] scroll <- newScrollBar frame [] pack scroll [Side AtRight,Fill Y] listBox # scrollbar Vertical scroll listBox # selectMode Extended contentsMVar <- newMVar [] let simpleListBox = SimpleListBox { frame = frame, listBox = listBox, mkString = mkString, contentsMVar = contentsMVar } configure simpleListBox configs return simpleListBox addItemAtEnd :: SimpleListBox val -> val -> IO (SimpleListBoxItem val) addItemAtEnd simpleListBox (val1 :: val) = do -- We have to recompute the complete list of Strings, since -- Einar doesn't seem to have provided a function for adding a single -- item to a ListBox, and I can't be bothered to implement one. let mVar = contentsMVar simpleListBox mkS = mkString simpleListBox oID <- newObject let simpleListBoxItem = SimpleListBoxItem { val = val1, oID = oID } contents0 <- takeMVar mVar let contents1 :: [SimpleListBoxItem val] contents1 = contents0 ++ [simpleListBoxItem] newValue :: [String] newValue = map (mkS . val) contents1 (listBox simpleListBox) # value newValue putMVar mVar contents1 return simpleListBoxItem deleteItem :: SimpleListBox val -> SimpleListBoxItem val -> IO () deleteItem simpleListBox simpleListBoxItem = do let mVar = contentsMVar simpleListBox mkS = mkString simpleListBox contents0 <- takeMVar mVar let contents1 = deleteFirst (== simpleListBoxItem) contents0 (newValue :: [String]) = map (mkS . val) contents1 (listBox simpleListBox) # value newValue putMVar mVar contents1 done getItems :: SimpleListBox value -> IO [value] getItems simpleListBox = do contents <- readMVar (contentsMVar simpleListBox) return (map val contents) bindSelection :: SimpleListBox val -> IO (Event [SimpleListBoxItem val],IO ()) bindSelection simpleListBox = do (press,terminator) <- bindSimple (listBox simpleListBox) (ButtonPress (Just 1)) let event = press >>> do indexOpt <- getSelection (listBox simpleListBox) contents0 <- readMVar (contentsMVar simpleListBox) return (case indexOpt of Nothing -> [] Just items -> let max = length contents0 in mapMaybe (\ index -> if index >= max then Nothing -- can happen if events and a deletion -- get processed in the wrong order. else Just (contents0 !! index) ) items ) return (event,terminator)