CItems are improved tcl-tk canvas items.
In particular the CanvasItem class in WH.lhs requires the following methods:

class WidgetItem w => CanvasItem w where
 showItem :: w -> IO ()
 hideItem :: w -> IO ()
 transformItem :: Transform2 -> w -> IO ()
 raiseItem :: w -> Maybe Ident -> IO ()
 lowerItem :: w -> Maybe Ident -> IO ()

As tcl-tk items can't be shown and hidden but instead are visible from the 
moment they're created, we must add extra functionality to show and hide
items, and also to apply transforms to them.

to do: citems need a weak pointer mechanism like prim widgets to make sure
       they get destroyed.
\begin{code}
module CItem where
import StaticTypes
import FranPrim
import qualified FranCore as F
import Compatibility

import qualified TclPrimImpl as T
import WidgetExports
import WidgetSetImpl
import RectInfo

import Maybe
import Monad
import IOExts
import List

import AbstractInterface
\end{code}

A CItem keeps a copy of the GUI state when it was created, a reference to 
its parent, the widget itself, a boolean variable saying whether visible,
a reference to the embedded panel widget (if a canvas window item), a 
function to map (ie show) the item, the rect info maintaining tranformation
info (see RectInfo.lhs) and the current list of configuration options.
 
\begin{code}
data CItem w = CItem {
	citemGUI :: GUIDef,
	canvasWS :: T.Canvas,
        theChild :: T.CWidget w,
	visibleC :: IORef Bool,
        getWindow :: Maybe TheWin,
        mapChild :: IO (),
        rectInfo :: RectInfo,
        confs :: IORef [Config]
	}
\end{code}

Th reference to the panel item in a canvas window widget contains an action
to bind user input to the window, and raise and lower the window, as the 
relevant canvas item operations won't work on canvas window widgets.

\begin{code}
data TheWin = TheWin {
	bindTheWin :: (Action -> Listener UserAction -> IO (IO ())),
	raiseTheWin :: Maybe Ident -> IO (),
	lowerTheWin :: Maybe Ident -> IO ()
	}
\end{code}

To create a CItem, we pass in a function that creates an item given a 
parent canvas, the transformation info (RectInfo), the visibility ref, 
the list of current configs ref, and the initial configs ref.
This returns a tcl canvas widget and a function to map (show) the widget. 
The canvas widget should initially be invisible (ie we should have haskell
state for the object but no actual tcl object).

\begin{code}
type MkCW w = T.Canvas -> RectInfo -> IORef Bool -> IORef [Config]
	    -> [Config]   
	    -> IO (T.CWidget w,IO ()) 

mkCItem :: Maybe TheWin -- perhaps the panel item reference
	-> MkCW w -- a function to create the item
        -> T.Canvas -- the parent canvas
	-> [Config] -- configuration options for the canvas
	-> GUIDef -- the current GUI state
	-> IO (CItem w)
mkCItem mbw mkcw can cfgs guidef = do
 ri <- newRectInfo
 cv <- newIORef False
 csv <- newIORef cfgs
 (cw,redraw) <- mkcw can ri cv csv cfgs
 let ci = CItem guidef can cw cv mbw redraw ri csv 
 return ci
\end{code}

Useful functions to create canvas tcl items. There are three general types:
objects with a given vector size; objects that should pass through a list 
of points; objects that have no initial spatial information.

\begin{code}
mapVectorV :: F.Vector2B -> String -> w   
	   -> MkCW w
mapVectorV v =
 -- display the item transforming its vector size by the current transform.
 -- also transform it from the origin by the current translation
 mapAnyV (\r -> do t <- getFranTime;at v t >>= fmap (map T.ccoord). (resize2 r))
	 (\ri w doresize -> do 
		r <- addHIOB $ lift1 (\v -> resize2 ri v >>= doresize) v
 		addFinaliserW w r)

mapListV :: [F.Point2B] -> String -> w   
         -> MkCW w
mapListV ps = 
 -- display the item transforming all its points by the current transform
 mapAnyV (\r -> do t <- getFranTime;mapM (flip at t) ps >>= fmap (map T.ccoord) . (resize r))
	 (\ri w doresize -> do
		let psb = bListToListB ps
		r <- addHIOB $ lift1 (\vs -> resize ri vs >>= doresize) psb
		addFinaliserW w r)

mapFreeV :: String -> w -> MkCW w
mapFreeV =
 -- translate the the item by the current translation
 mapAnyV (\ri -> do (mov,_,_) <- currentTransform ri
	   	    return [vectorToWorld' mov])
	 (\_ _ _ -> return ())
\end{code}

The generic creation function. Note that we use the config ref in order to 
redraw the widget. This must therefore get updated on every cset operation.

\begin{code}	 
mapAnyV :: (RectInfo -> IO [T.CCoord]) 
             -- given the current transform, generate a list of actual tcl
             -- coordinates
             -- turn the item specific data into an initial list of coords
        -> (RectInfo -> T.CWidget w -> ([T.Coord] -> IO ()) -> IO ())
             -- handle resizing of the widget
	-> String -> w  -- specific info which type of widget to create 
	-> MkCW w -- a make widget function
mapAnyV resizeAny addIOB mknm mkw can ri visref cfgsref cs = do
   w <- T.totcl_cwidget can mkw 
   let doresize vs = do vis <- readIORef visref;when vis $ T.setCoords w vs 
   addIOB ri w doresize
   let display = do 
        ps <- resizeAny ri
	cfgs <- readIORef cfgsref
	T.tcl_mapcwidget w ps cfgs mknm
	return ()
   return (w,display)

resize2 :: RectInfo -> Vector2 -> IO [T.Coord]
resize2 w v = resize w [origin2,v2p v]

resize :: RectInfo -> [Point2] -> IO [T.Coord]
resize ri ps = do
 (mov,sca,_) <- currentTransform ri
 return $ fmap (\p -> pointToWorld $ (translateUscaleRotate2 mov sca 0) *% p)
               ps
\end{code}

CItems are widget items and canvas items, and can have actions bound to them

\begin{code}
instance WidgetItem (CItem w) where
 cset ci@(CItem {confs = cfgV}) cs = do
   -- update the haskell cache of config values
   updIORef cfgV $ overideCfgs cs
   whenVisible (return ()) (flip cset cs) ci
 destroy ci = whenVisible (return ()) destroy ci
 uniqueId (CItem {theChild = w}) = uniqueId w
 addFinaliserW (CItem {theChild = w}) f = addFinaliserW w f

instance CanvasItem (CItem w) where
 hideItem = hideCItem
 showItem = showCItem
 transformItem = transformCItem
 raiseItem = raiseCItem 
 lowerItem = lowerCItem

instance Bindable (CItem w) where
 bind a act l = 
   -- bind to the canvas item or the panel item within the canvas widget,
   -- if there is one (see GUI.lhs for more on primAction)
   maybe (bind (theChild a) act (wrapIOL (primAction (citemGUI a)) l))
	 (\w -> bindTheWin w act l)
	 (getWindow a) 
\end{code}

The actual canvas operation implementation

\begin{code}

-- when visible apply an op to the widget otherwise perform a default action

whenVisible :: IO () -> (T.CWidget w -> IO ()) -> CItem w -> IO ()
whenVisible def app (CItem {visibleC = vis,theChild = c}) = do
  v <- readIORef vis
  if not v then def else app c


-- to hide an item check if it is visible, if so remove it and set the
-- visibility ref
hideCItem :: CItem w -> IO ()
hideCItem ci@(CItem {visibleC = vis}) = 
   whenVisible (return ()) 
     (\c -> do
	T.removeObject c
	writeIORef vis False
      )
     ci

-- to show an item, if it is not visible remap it, and set the visibility
-- ref to True
showCItem :: CItem w -> IO ()
showCItem c = whenVisible 
    (do mapChild c
        writeIORef (visibleC c) True)
    (const $ return ()) c


-- to raise or lower an item, raise or lower the citem or the 
-- panel item in a canvas window item

raiseCItem,lowerCItem :: CItem w -> Maybe Ident -> IO ()
raiseCItem = raiseOrLower T.raiseObject raiseTheWin 
lowerCItem = raiseOrLower T.lowerObject lowerTheWin 

raiseOrLower :: (T.CWidget w -> Maybe String -> IO ()) 
	     -> (TheWin -> Maybe Ident -> IO ())
	     -> CItem w -> Maybe Ident -> IO ()
raiseOrLower changeO changeP c@(CItem {getWindow = w}) mb = do
 maybe (whenVisible (return ()) 
                    (\w -> do
			changeO w (fmap getWPath' mb)
		    ) c)
       (flip changeP mb)
       w
\end{code}

to transform a canvs item we need to take the Fran absolute transform
and turn it into an offset movement and scale. We do this using relativise
(from RectInfo.lhs). 
If we've not moved or been rescaled do nothing, otherwise, if the widget
is visible apply the transform, and record the new bounding box of the
widget.

Note that tcl canvas items don't support rotate.

\begin{code}
transformCItem :: Transform2 -> CItem w -> IO ()
transformCItem t c = do
   t'@(mov,sca,_) <- relativise (rectInfo c) $ factorTransform2 t
   unless (isZero mov && sca == 1.0) $
	whenVisible (return ()) 
	    (\cw -> do 
		doTransform cw c t' mov
                (a,b,w,h) <- T.bboxObjects [cw]
                setBBoxR (rectInfo c) $ 
		      rectFromCorners (point2XY (fromInt a) (fromInt b))
			 	      (point2XY (fromInt w) (fromInt h)))
	    c

-- check if a vector is a zero vector ie no movement
isZero :: Vector2 -> Bool
isZero (Vector2XY x y) | x == 0 && y == 0 = True
isZero _ = False
\end{code}

to perform the actual transform move the object by the vector and
then resize it.

\begin{code}
doTransform :: T.CWidget w -> CItem w -> (Vector2,RealVal,RealVal) 
	    -> Vector2 -> IO ()
doTransform w ri t@(mov,sca,rot) mov0 = do
 case mov of
    Vector2XY 0 0 -> return ()
    Vector2XY a b -> T.moveObject w (round a,round b)
 unless (sca == 1.0) $ resizeIt w ri sca mov0

\end{code}

to resize an object, if its a canvas window item then set the width and height
of the canvas item. otherwise scale it.

note that resizing will currently therefore have no effect on text or bitmaps
\begin{code}
resizeIt :: T.CWidget w -> CItem w -> RealVal -> Vector2 -> IO ()  
resizeIt cw c sca mov = do
 r <- getBBoxR (rectInfo c)
 case getWindow c of
  Just _ -> do
    let (w,h) = vectorToWorld $ uscale2 sca *% rectSize r
    cset cw ["width" T.%% show w,"height" T.%% show h]
  Nothing ->
    T.scaleObject cw 
	(case origin2 .+^ mov of Point2XY a b -> (round a,round b)) 
	sca sca
\end{code}
