[project @ 2001-08-01 13:53:07 by simonmar]
[ghc-base.git] / Text / Html / BlockTable.hs
diff --git a/Text/Html/BlockTable.hs b/Text/Html/BlockTable.hs
new file mode 100644 (file)
index 0000000..8211c4e
--- /dev/null
@@ -0,0 +1,176 @@
+-----------------------------------------------------------------------------
+-- 
+-- Module      :  Text.Html.BlockTable
+-- Copyright   :  (c) Andy Gill, and the Oregon Graduate Institute of 
+--               Science and Technology, 1999-2001
+-- License     :  BSD-style (see the file libraries/core/LICENSE)
+-- 
+-- Maintainer  :  Andy Gill <andy@galconn.com>
+-- Stability   :  experimental
+-- Portability :  portable
+--
+-- $Id: BlockTable.hs,v 1.1 2001/08/01 13:53:07 simonmar Exp $
+--
+-- An Html combinator library
+--
+-----------------------------------------------------------------------------
+
+module Text.Html.BlockTable (
+
+-- Datatypes:
+
+      BlockTable,             -- abstract
+
+-- Contruction Functions: 
+
+      single,
+      above,
+      beside,
+
+-- Investigation Functions: 
+
+      getMatrix,
+      showsTable,
+      showTable,
+
+      ) where
+
+import Prelude
+
+infixr 4 `beside`
+infixr 3 `above`
+
+-- These combinators can be used to build formated 2D tables.
+-- The specific target useage is for HTML table generation.
+
+{-
+   Examples of use:
+
+       > table1 :: BlockTable String
+       > table1 = single "Hello"       +-----+
+                                       |Hello|
+         This is a 1x1 cell            +-----+
+         Note: single has type
+        
+               single :: a -> BlockTable a
+       
+         So the cells can contain anything.
+       
+       > table2 :: BlockTable String
+       > table2 = single "World"       +-----+
+                                       |World|
+                                       +-----+
+
+
+       > table3 :: BlockTable String
+       > table3 = table1 %-% table2    +-----%-----+
+                                       |Hello%World|
+        % is used to indicate          +-----%-----+
+        the join edge between
+        the two Tables.  
+
+       > table4 :: BlockTable String
+       > table4 = table3 %/% table2    +-----+-----+
+                                       |Hello|World|
+         Notice the padding on the     %%%%%%%%%%%%%
+         smaller (bottom) cell to      |World      |
+         force the table to be a       +-----------+
+         rectangle.
+
+       > table5 :: BlockTable String
+       > table5 = table1 %-% table4    +-----%-----+-----+
+                                       |Hello%Hello|World|
+         Notice the padding on the     |     %-----+-----+
+         leftmost cell, again to       |     %World      |
+         force the table to be a       +-----%-----------+
+         rectangle.
+   Now the table can be rendered with processTable, for example:
+       Main> processTable table5
+       [[("Hello",(1,2)),
+         ("Hello",(1,1)),
+         ("World",(1,1))],
+        [("World",(2,1))]] :: [[([Char],(Int,Int))]]
+       Main> 
+-}
+
+-- ---------------------------------------------------------------------------
+-- Contruction Functions
+
+-- Perhaps one day I'll write the Show instance
+-- to show boxes aka the above ascii renditions.
+
+instance (Show a) => Show (BlockTable a) where
+      showsPrec p = showsTable
+
+type TableI a = [[(a,(Int,Int))]] -> [[(a,(Int,Int))]]
+
+data BlockTable a = Table (Int -> Int -> TableI a) Int Int
+
+
+-- You can create a (1x1) table entry
+
+single :: a -> BlockTable a
+single a = Table (\ x y z -> [(a,(x+1,y+1))] : z) 1 1
+
+
+-- You can compose tables, horizonally and vertically
+
+above  :: BlockTable a -> BlockTable a -> BlockTable a
+beside :: BlockTable a -> BlockTable a -> BlockTable a
+
+t1 `above` t2 = trans (combine (trans t1) (trans t2) (.))
+
+t1 `beside` t2 = combine t1 t2 (\ lst1 lst2 r ->
+    let
+      -- Note this depends on the fact that
+      -- that the result has the same number
+      -- of lines as the y dimention; one list
+      -- per line. This is not true in general
+      -- but is always true for these combinators.
+      -- I should assert this!
+      -- I should even prove this.
+      beside (x:xs) (y:ys) = (x ++ y) : beside xs ys
+      beside (x:xs) []     = x        : xs ++ r
+      beside []     (y:ys) = y        : ys ++ r
+      beside []     []     =                  r
+    in
+      beside (lst1 []) (lst2 []))
+
+-- trans flips (transposes) over the x and y axis of
+-- the table. It is only used internally, and typically
+-- in pairs, ie. (flip ... munge ... (un)flip).
+
+trans :: BlockTable a -> BlockTable a
+trans (Table f1 x1 y1) = Table (flip f1) y1 x1
+
+combine :: BlockTable a 
+      -> BlockTable b 
+      -> (TableI a -> TableI b -> TableI c) 
+      -> BlockTable c
+combine (Table f1 x1 y1) (Table f2 x2 y2) comb = Table new_fn (x1+x2) max_y
+    where
+      max_y = max y1 y2
+      new_fn x y =
+         case compare y1 y2 of
+          EQ -> comb (f1 0 y)             (f2 x y)
+          GT -> comb (f1 0 y)             (f2 x (y + y1 - y2))
+          LT -> comb (f1 0 (y + y2 - y1)) (f2 x y)
+
+-- ---------------------------------------------------------------------------
+-- Investigation Functions
+
+-- This is the other thing you can do with a Table;
+-- turn it into a 2D list, tagged with the (x,y)
+-- sizes of each cell in the table.
+
+getMatrix :: BlockTable a -> [[(a,(Int,Int))]]
+getMatrix (Table r _ _) = r 0 0 []
+
+-- You can also look at a table
+
+showsTable :: (Show a) => BlockTable a -> ShowS
+showsTable table = shows (getMatrix table)
+
+showTable :: (Show a) => BlockTable a -> String
+showTable table = showsTable table ""