tabloid-0.3: TableController.hs
module TableController
(
Controller
, test
, new
, view
, push
) where
import qualified Data.ByteString.Lazy.Char8 as L
import qualified Graphics.UI.Gtk.ModelView as MV
import qualified LoadSaveController as LSC
import qualified TableView as View
import Graphics.UI.Gtk
import Data.Maybe
import Data.List
import SimpleRegex
import Control.Monad
import Control.Applicative
import WindowedApp
import Component
type Controller = Ref C
type StringList = MV.ListStore [L.ByteString]
view :: C -> Widget
view = View.mainWidget . gui
new :: IO Controller
new = do
lsc <- LSC.new (Just "jira")
lsv <- (lsc .> LSC.view)
l <- MV.listStoreNew []
v <- View.new l lsv
lsc .< (LSC.onSave (Just (Just <$> toJira l)))
onClicked (View.searchPB v) (searchTableBackward (View.searchE v) l (View.treeView v))
onClicked (View.searchNB v) (searchTableForward (View.searchE v) l (View.treeView v))
View.add_col (View.treeView v) l (L.unpack . (!! 0)) "Line" 0
this <- newRef (C v l 1 Nothing)
onToggled (View.groupB v) (this .<< updateGrouping)
onValueSpinned (View.groupE v) (this .<< updateGrouping)
return this
push this rows =
this .<< \s ->
case groupCol s of
Nothing ->
pushRaw (zipWith (:) (map (L.pack . show) [1..]) rows) s
Just (c, _) ->
pushRaw (zipWith (:) (map (L.pack . show) [1..]) rows) (s {groupCol = Just (c, rows)})
-- internal functions
groupRows [] _ = []
groupRows (row:rows) col = row : sameGroup ++ groupRows rest col
where cell = row !! col
(sameGroup, rest) = partition ((== cell) . (!! col)) rows
updateGrouping state@(C g listM mc gCol) =
do
col <- spinButtonGetValueAsInt (View.groupE g)
grouping <- toggleButtonGetActive (View.groupB g)
if grouping then
do
rows <- MV.listStoreToList listM
case gCol of
Nothing ->
pushRaw rows (state { groupCol = Just (col, rows)})
Just (ccol, crows) ->
pushRaw crows (state { groupCol = Just (col, crows)})
else
case gCol of
Nothing ->
return (state { groupCol = Nothing })
Just (ccols, crows) ->
pushRaw crows (state { groupCol = Nothing})
searchTableBackward entry liststore treeview = do
selI <- getSelectedRow treeview
rows' <- MV.listStoreToList liststore
let rows = reverse $ zip [0..] (take selI rows')
searchRowRange rows entry treeview
searchTableForward entry liststore treeview = do
selI <- getSelectedRow treeview
rows' <- MV.listStoreToList liststore
let rows = zip [(selI + 1) ..] (drop (selI + 1) rows')
searchRowRange rows entry treeview
searchRowRange rows entry treeview = do
searchTerm <- entryGetText entry
let sI = filter (any (isInfixOf searchTerm) . (map L.unpack) . snd) rows
case sI of
((nexti, _):_) -> do
MV.treeViewSetCursor treeview [nexti] Nothing
_ ->
beep
getSelectedRow treeV = do
tsel <- MV.treeViewGetSelection treeV
sel <- MV.treeSelectionGetSelectedRows tsel
return $ case sel of
((fst_sel:_):_) -> fst_sel
_ -> 0
{-
toCsv :: StringList -> IO L.ByteString
toCsv liststore = do
cont <- MV.listStoreToList liststore
return (L.unlines (lineToCsv <$> cont))
where
lineToCsv :: [L.ByteString] -> L.ByteString
lineToCsv ls =
let els = doQuotes <$> ls
in L.concat (intersperse (L.pack ";") els)
doQuotes :: L.ByteString -> L.ByteString
doQuotes cont =
let escaped = L.foldl' (\s c ->
s `L.append`
(if c == '"'
then L.pack ['\\', c]
else L.pack [c]))
L.empty
cont
in (L.pack "\"") `L.append` escaped `L.append` (L.pack "\"")
-}
toJira :: StringList -> IO L.ByteString
toJira liststore = do
cont <- MV.listStoreToList liststore
return (L.unlines (lineToCsv <$> cont))
where
lineToCsv :: [L.ByteString] -> L.ByteString
lineToCsv ls =
let els = escape <$> ls
in (L.pack "| ") `L.append`
L.concat (intersperse (L.pack " | ") els) `L.append` (L.pack " |")
escape :: L.ByteString -> L.ByteString
escape cont = L.foldl' (\s c ->
s `L.append`
(if c == '|'
then L.pack ['\\', c]
else L.pack [c]))
L.empty
cont
pushRaw :: [[L.ByteString]] -> C -> IO C
pushRaw rows c@(C g listM mc groupCol) =
let
addRows n [] = return n
addRows n (r:rs) = do
let r' = length r
when (r' > n) $ forM_ [n .. r' - 1]
(\i ->
View.add_col (View.treeView g)
listM (at i) ("$" ++ show i) i)
(MV.listStoreAppend listM) r
addRows (max n r') rs
at :: Int -> [L.ByteString] -> String
at i xs
| (length xs) <= i = ""
| otherwise = L.unpack $ xs !! i
in
do
(MV.listStoreClear listM)
rowcount <- spinButtonGetValueAsInt (View.maxLines g)
let groupedRows = maybe selRows (groupRows selRows . fst) groupCol
selRows = (take rowcount rows)
cs <- addRows mc groupedRows
spinButtonSetRange (View.groupE g) 0 (fromIntegral (cs - 1))
return (c { cols = cs })
data C = C
{
gui :: View.ViewState
, listModel :: StringList
, cols :: Int
, groupCol :: Maybe (Int, [[L.ByteString]])
}
-- tests
test = windowedApp "TableController test" $ do
t <- new :: IO Controller
t `push` [[(L.pack . show) (c `rem` 3)| c <- [r .. r + 10]] | r <- [0 .. 9]]
t .> view