fltkhs-0.1.0.0: src/Examples/table-sort.hs
{-# LANGUAGE CPP #-}
module Main where
import qualified Graphics.UI.FLTK.LowLevel.FL as FL
import Graphics.UI.FLTK.LowLevel.Fl_Enumerations
import Graphics.UI.FLTK.LowLevel.Fl_Types
import Control.Monad
import Graphics.UI.FLTK.LowLevel.FLTKHS
import Data.IORef
import Data.List
import Data.Function
import System.Process
dirCommand :: ([Char], [[Char]])
dirHeaders :: [[Char]]
#ifdef ming32_HOST_OS
dirCommand = ("dir", [])
dirHeaders = ["Perms", "#L", "Own", "Group", "Size", "Date", "", "", "Filename"]
#else
dirCommand = ("ls", ["-l"])
dirHeaders = ["Date", "Time", "Size", "Filename", "", "", "", "", ""]
#endif
margin :: Int
margin = 20
headerFontFace :: Font
headerFontFace = helveticaBold
headerFontSize :: FontSize
headerFontSize = FontSize 16
rowFontFace :: Font
rowFontFace = helvetica
rowFontSize :: FontSize
rowFontSize = FontSize 16
data TableState = TableState {
sortReverse :: IORef Bool,
sortLastCol :: IORef Int,
rowData :: IORef [[String]]
}
drawSortArrow :: Rectangle -> Bool -> IO ()
drawSortArrow (Rectangle (Position (X x') (Y y')) (Size (Width w') (Height h'))) sortReverse' =
let xlft = x' + (w'-6) - 8
xctr = x' + (w'-6) - 4
xrit = x' + (w'-6) - 0
ytop = y' + (truncate ((fromIntegral h' / 2) :: Double)) - 4
ybot = y' + (truncate ((fromIntegral h' / 2) :: Double)) + 4
in
if sortReverse'
then do
flcSetColor whiteColor
flcLine (Position (X xrit) (Y ytop)) (Position (X xctr) (Y ybot))
flcSetColorWithC 41
flcLine (Position (X xlft) (Y ytop)) (Position (X xrit) (Y ytop))
flcLine (Position (X xlft) (Y ytop)) (Position (X xctr) (Y ybot))
else do
flcSetColor whiteColor
flcLine (Position (X xrit) (Y ybot)) (Position (X xctr) (Y ybot))
flcLine (Position (X xrit) (Y ybot)) (Position (X xlft) (Y ybot))
flcSetColorWithC 41
flcLine (Position (X xlft) (Y ybot)) (Position (X xctr) (Y ytop))
drawCell :: TableState -> Ref TableRow -> TableContext -> TableCoordinate -> Rectangle -> IO ()
drawCell tableState table tc (TableCoordinate (Row row') (Column col')) rectangle' =
let (x',y',w',h') = fromRectangle rectangle'
in do
sortReverse' <- readIORef (sortReverse tableState)
sortLastCol' <- readIORef (sortLastCol tableState)
rowData' <- readIORef (rowData tableState)
numCols <- getCols table
numRows <- getRows table
if (row' < numRows && col' < numCols)
then case tc of
ContextColHeader -> do
flcPushClip rectangle'
flcDrawBox ThinUpBox rectangle' backgroundColor
if (col' < 9)
then do
flcSetColor blackColor
flcDrawInBox
(dirHeaders !! col')
(toRectangle ((x'+2),y',w',h'))
alignLeft
Nothing
Nothing
if (col' == sortLastCol')
then drawSortArrow rectangle' sortReverse'
else return ()
else return ()
flcPopClip
ContextCell -> do
flcPushClip rectangle'
bgColor <- do
isSelected' <- getRowSelected table row'
case isSelected' of
Right is' -> if is'
then getSelectionColor table
else return whiteColor
Left _ -> error $ "Row: " ++ (show row') ++ " is out of range."
flcSetColor bgColor
flcRectf rectangle'
flcSetFont rowFontFace rowFontSize
flcSetColor blackColor
let currentRow = rowData' !! row'
flcDrawInBox
(indexOr "" col' currentRow)
(toRectangle $ (x'+2,y',w',h'))
alignLeft
Nothing
Nothing
flcSetColor light2Color
flcRect rectangle'
flcPopClip
_ -> return ()
else return ()
setIndex :: Int -> [a] -> (a -> a) -> [a]
setIndex idx' xs f =
map
(
\(i,e) -> if (i == idx')
then f e
else e
)
(zip [0..] xs)
indexOr :: a -> Int -> [a] -> a
indexOr fallback idx xs =
if (idx < length xs)
then xs !! idx
else fallback
eventCallback :: TableState -> Ref TableRow -> IO ()
eventCallback tableState table = do
(Column col') <- callbackCol table
context' <- callbackContext table
case context' of
ContextColHeader -> do
event' <- FL.event
mouseButton' <- FL.eventButton
if (event' == Release && mouseButton' == Mouse_Left)
then do
sortLastCol' <- readIORef (sortLastCol tableState)
if (sortLastCol' == col')
then readIORef (sortReverse tableState) >>= writeIORef (sortReverse tableState) . toggle
else writeIORef (sortReverse tableState) False
sortReverse' <- readIORef (sortReverse tableState)
rowData' <- readIORef (rowData tableState) >>= return . zip [(0 :: Int)..]
let sorted = sortBy (compare `on` (indexOr "" col'. snd)) rowData'
writeIORef
(rowData tableState)
(if sortReverse'
then (reverse $ map snd sorted)
else map snd sorted)
writeIORef (sortLastCol tableState) col'
redraw table
else return ()
_ -> return ()
where toggle True = False
toggle False = True
autowidth :: Ref TableRow -> Int -> [[String]] -> IO ()
autowidth table pad rowData' = do
flcSetFont headerFontFace headerFontSize
mapM_
(\(colNum, colName) -> do
(Size (Width w') _) <- flcMeasure colName Nothing
setColWidth table colNum (w' + pad)
)
(zip [0 ..] dirHeaders)
flcSetFont rowFontFace rowFontSize
mapM_
(\row' -> do
mapM_
(\(colIdx,col) -> do
(Size (Width wc') _) <- flcMeasure col Nothing
colWidth' <- getColWidth table colIdx
if (wc' + pad > colWidth')
then setColWidth table colIdx (wc' + pad)
else return ()
)
(zip [0..] row')
)
rowData'
-- need to do { table_resized(); redraw(); }
-- but table_resized() is unexposed.
-- setting the row_header flag induces this.
getRowHeader table >>= setRowHeader table
resize_window :: Ref DoubleWindow -> Ref TableRow -> IO ()
resize_window window table = do
let width = (4 :: Int)
numCols <- getCols table
colWidthTotal <- liftM sum $ mapM (getColWidth table) [0..(numCols - 1)]
let totalWidth = width + colWidthTotal + (margin * 2)
appWidth <- FL.w
if (totalWidth < 200 || totalWidth > appWidth)
then return ()
else do
x' <- getX window
y' <- getY window
h' <- getH window
resize window $ toRectangle (x',y',totalWidth,h')
main :: IO ()
main = do
window <- doubleWindowNew
(Size (Width 900) (Height 500))
Nothing
(Just "Table Sorting")
windowW <- getW window
windowH <- getH window
rows <- uncurry readProcess dirCommand "" >>= return . map words . lines
rowData' <- newIORef rows
sortReverse' <- newIORef False
sortLastCol' <- newIORef (-1)
let tableState = TableState sortReverse' sortLastCol' rowData'
begin window
table <- tableRowNew
(Rectangle
(Position (X margin) (Y margin))
(Size (Width $ windowW - margin * 2)
(Height $ windowH - margin * 2)))
Nothing
defaultCustomWidgetFuncs
(defaultCustomTableFuncs {
drawCellCustom = Just $ drawCell tableState
})
setColHeader table True
setColResize table True
setSelectionColor table yellowColor
setWhen table [WhenRelease]
readIORef rowData' >>= setRows table . length
readIORef rowData' >>= setCols table . maximum . map length
setRowHeightAll table 18
readIORef rowData' >>= autowidth table 20
setTooltip table "Click on column headings to toggle column sorting"
setColor table whiteColor
setCallback table (eventCallback tableState)
end window
setResizable window table
resize_window window table
_ <- showWidget window
_ <- FL.run
return ()