4 import Graphics.UI.WX as WX
5 import Graphics.UI.WXCore hiding (Colour)
6 import Graphics.UI.WXCore.Draw
9 --import Text.XML.HaXml.XmlContent
14 import Common (removeQuotes)
17 Circle { shapeStyle :: ShapeStyle, shapeRadius :: Double }
18 | Polygon { shapeStyle :: ShapeStyle, shapePerimeter :: [DoublePoint] }
20 | Lines { shapeStyle :: ShapeStyle, shapePerimeter :: [DoublePoint] }
21 -- no fill for open shape
22 | Points { shapeStyle :: ShapeStyle, shapePoints :: [DoublePoint] }
23 | Rectangle { shapeStyle :: ShapeStyle
24 , shapeUpperLeft :: DoublePoint, shapeLowerRight :: DoublePoint }
25 | Arc { shapeStyle :: ShapeStyle, shapeRadius :: Double
26 , shapeStart :: Double, shapeEnd :: Double
27 , shapeCenter :: DoublePoint }
28 | Ellipse { shapeStyle :: ShapeStyle, shapeCenter :: DoublePoint
29 , shapeHRadius :: Double, shapeVRadius :: Double }
30 | EllipticArc { shapeStyle :: ShapeStyle, shapeCenter :: DoublePoint
31 , shapeHRadius :: Double, shapeVRadius :: Double
32 , shapeStart :: Double, shapeEnd :: Double }
33 | RoundRec { shapeStyle :: ShapeStyle, shapeRadius :: Double
34 , shapeUpperLeft :: DoublePoint, shapeLowerRight :: DoublePoint }
35 | Text { shapeStyle :: ShapeStyle, shapeText :: String }
36 | Composite { shapeSegments :: [Shape] } -- drawn in given order
37 | TextInEllipse { shapeStyle :: ShapeStyle, shapeText :: String }
38 deriving (Eq, Show, Read)
40 data ShapeStyle = ShapeStyle
41 { styleStrokeWidth :: Int
42 , styleStrokeColour :: Colour
45 deriving (Eq, Show, Read)
47 instance Parse Shape where
51 `discard` isWord "{" `apply` field "shapeStyle"
52 `discard` isWord "," `apply` field "shapeRadius"
55 , do{ isWord "Polygon"
57 `discard` isWord "{" `apply` field "shapeStyle"
58 `discard` isWord "," `apply` field "shapePerimeter"
63 `discard` isWord "{" `apply` field "shapeStyle"
64 `discard` isWord "," `apply` field "shapePerimeter"
69 `discard` isWord "{" `apply` field "shapeStyle"
70 `discard` isWord "," `apply` field "shapePoints"
73 , do{ isWord "Rectangle"
75 `discard` isWord "{" `apply` field "shapeStyle"
76 `discard` isWord "," `apply` field "shapeUpperLeft"
77 `discard` isWord "," `apply` field "shapeLowerRight"
82 `discard` isWord "{" `apply` field "shapeStyle"
83 `discard` isWord "," `apply` field "shapeRadius"
84 `discard` isWord "," `apply` field "shapeStart"
85 `discard` isWord "," `apply` field "shapeEnd"
86 `discard` isWord "," `apply` field "shapeCenter"
89 , do{ isWord "Ellipse"
91 `discard` isWord "{" `apply` field "shapeStyle"
92 `discard` isWord "," `apply` field "shapeCenter"
93 `discard` isWord "," `apply` field "shapeHRadius"
94 `discard` isWord "," `apply` field "shapeVRadius"
97 , do{ isWord "EllipticArc"
99 `discard` isWord "{" `apply` field "shapeStyle"
100 `discard` isWord "," `apply` field "shapeCenter"
101 `discard` isWord "," `apply` field "shapeHRadius"
102 `discard` isWord "," `apply` field "shapeVRadius"
103 `discard` isWord "," `apply` field "shapeStart"
104 `discard` isWord "," `apply` field "shapeEnd"
107 , do{ isWord "RoundRec"
109 `discard` isWord "{" `apply` field "shapeStyle"
110 `discard` isWord "," `apply` field "shapeRadius"
111 `discard` isWord "," `apply` field "shapeUpperLeft"
112 `discard` isWord "," `apply` field "shapeLowerRight"
117 `discard` isWord "{" `apply` field "shapeStyle"
118 `discard` isWord "," `apply` field "shapeText"
121 , do{ isWord "Composite"
123 `discard` isWord "{" `apply` field "shapeSegments"
126 , do{ isWord "TextInEllipse"
127 ; return TextInEllipse
128 `discard` isWord "{" `apply` field "shapeStyle"
129 `discard` isWord "," `apply` field "shapeText"
132 ] `adjustErr` (++"\nexpected a Shape (Circle,Polygon,Lines,Points,Rectangle,Arc,Ellipse,EllipticArc,RoundRec,Text,Composite,TextInEllipse)")
134 instance Parse ShapeStyle where
135 parse = do{ isWord "ShapeStyle"
137 `discard` isWord "{" `apply` field "styleStrokeWidth"
138 `discard` isWord "," `apply` field "styleStrokeColour"
139 `discard` isWord "," `apply` field "styleFill"
144 instance HTypeable Shape where
145 toHType s = Defined "Shape" [] [ Constr "Circle" [] []
146 , Constr "Polygon" [] []
147 , Constr "Lines" [] []
148 , Constr "Composite" [] []
150 instance XmlContent Shape where
151 toContents s@(Circle{}) =
152 [ mkElemC "Circle" (toContents (shapeStyle s)
153 ++ [mkElemC "radius" (toContents (shapeRadius s))]) ]
154 toContents s@(Polygon{}) =
155 [ mkElemC "Polygon" (toContents (shapeStyle s)
156 ++ [mkElemC "perimeter" (concatMap toContents
157 (shapePerimeter s))]) ]
158 toContents s@(Lines{}) =
159 [ mkElemC "Lines" (toContents (shapeStyle s)
160 ++ [mkElemC "perimeter" (concatMap toContents
161 (shapePerimeter s))]) ]
162 toContents s@(Composite{}) =
163 [ mkElemC "Composite" (concatMap toContents (shapeSegments s)) ]
165 { e@(Elem t _ _) <- element ["Circle","Polygon","Lines","Composite"]
167 "Circle" -> interior e $
168 do{ style <- parseContents
169 ; r <- inElement "radius" parseContents
170 ; return (Circle {shapeStyle=style, shapeRadius=r})
172 "Polygon" -> interior e $
173 do{ style <- parseContents
174 ; p <- inElement "perimeter" $ many1 parseContents
175 ; return (Polygon {shapeStyle=style, shapePerimeter=p})
177 "Lines" -> interior e $
178 do{ style <- parseContents
179 ; p <- inElement "perimeter" $ many1 parseContents
180 ; return (Lines {shapeStyle=style, shapePerimeter=p})
182 "Composite" -> interior e $ do{ ss <- many1 parseContents
183 ; return (Composite {shapeSegments=ss})
187 instance HTypeable ShapeStyle where
188 toHType s = Defined "ShapeStyle" [] [Constr "ShapeStyle" [] []]
189 instance XmlContent ShapeStyle where
191 [ mkElemC "ShapeStyle"
192 [ mkElemC "StrokeWidth" (toContents (styleStrokeWidth s))
193 , mkElemC "StrokeColour" (toContents (styleStrokeColour s))
194 , mkElemC "Fill" (toContents (styleFill s))
197 parseContents = inElement "ShapeStyle" $ do
198 { w <- inElement "StrokeWidth" parseContents
199 ; c <- inElement "StrokeColour" parseContents
200 ; f <- inElement "Fill" parseContents
201 ; return (ShapeStyle { styleStrokeWidth=w, styleStrokeColour=c
206 logicalDraw :: Size -> DC () -> DoublePoint -> Shape -> [Prop (DC ())] -> IO ()
207 logicalDraw ppi dc centre shape options =
209 Circle {} -> WX.circle dc (logicalToScreenPoint ppi centre)
210 (logicalToScreenX ppi (shapeRadius shape))
211 (style2options (shapeStyle shape)++options)
213 Polygon {} -> WX.polygon dc (map (logicalToScreenPoint ppi
215 (shapePerimeter shape))
216 (style2options (shapeStyle shape)++options)
218 Lines {} -> logicalLineSegments ppi dc (map (translate centre)
219 (shapePerimeter shape))
220 (style2options (shapeStyle shape)++options)
222 Points {} -> mapM_ (\p -> WX.drawPoint dc
223 (logicalToScreenPoint ppi $ translate centre p)
224 (style2options (shapeStyle shape)++options)
228 Rectangle {} -> WX.drawRect dc
229 (rectFrom (shapeUpperLeft shape) (shapeLowerRight shape) )
230 (style2options (shapeStyle shape)++options)
232 Arc {} -> WX.arc dc (logicalToScreenPoint ppi . translate centre $ shapeCenter shape)
233 (logicalToScreenX ppi (shapeRadius shape))
236 (style2options (shapeStyle shape)++options)
238 Ellipse {} -> WX.ellipse dc
239 (doRect (shapeCenter shape) (shapeHRadius shape) (shapeVRadius shape))
240 (style2options (shapeStyle shape)++options)
242 EllipticArc {} -> WX.ellipticArc
244 (doRect (shapeCenter shape) (shapeHRadius shape) (shapeVRadius shape))
247 (style2options (shapeStyle shape)++options)
249 RoundRec {} -> WX.roundedRect dc
250 (rectFrom (shapeUpperLeft shape) (shapeLowerRight shape) )
252 (style2options (shapeStyle shape)++options)
254 Text {} -> do textSize <- getTextExtent dc (shapeText shape)
255 let upperCorner = logicalToScreenPoint ppi . subtractDoublePoint centre
257 . screenToLogicalPoint ppi $ pointFromSize textSize
258 WX.drawText dc (removeQuotes $ shapeText shape)
260 (style2options (shapeStyle shape)++options)
262 Composite {} -> mapM_ (\s-> logicalDraw ppi dc centre s options)
263 (shapeSegments shape)
266 do let txt = removeQuotes (shapeText shape)
267 textSize <- getTextExtent dc (shapeText shape)
268 let upperCorner = logicalToScreenPoint ppi . subtractDoublePoint centre
270 . screenToLogicalPoint ppi $ pointFromSize textSize
272 (rect (pointSub upperCorner $ Point 5 5)
273 $ Size (sizeW textSize + 10) (sizeH textSize + 10) )
274 (style2options (shapeStyle shape)++options)
275 WX.drawText dc txt upperCorner
276 (style2options (shapeStyle shape)++options)
278 where rectFrom :: DoublePoint -> DoublePoint -> Rect
279 rectFrom upperLeftCorner lowerRightCorner =
280 rectBetween (logicalToScreenPoint ppi $ translate centre upperLeftCorner)
281 (logicalToScreenPoint ppi $ translate centre lowerRightCorner)
282 doRect :: DoublePoint -> Double -> Double -> Rect
283 doRect ellipseCenter hRadius vRadius =
284 rectBetween (logicalToScreenPoint ppi . translate centre
285 $ subtractDoublePoint ellipseCenter $ DoublePoint hRadius vRadius)
286 (logicalToScreenPoint ppi . translate centre
287 $ translate ellipseCenter $ DoublePoint hRadius vRadius)
289 logicalLineSegments :: Size -> DC () -> [DoublePoint] -> [Prop (DC ())] -> IO ()
290 logicalLineSegments _ _ [_p] _options = return ()
291 logicalLineSegments ppi dc (fromPoint:toPoint:ps) options =
292 do{ line dc (logicalToScreenPoint ppi fromPoint)
293 (logicalToScreenPoint ppi toPoint) options
294 ; logicalLineSegments ppi dc (toPoint:ps) options
298 circle = Circle { shapeStyle = defaultShapeStyle
299 , shapeRadius = kNODE_RADIUS }
301 style2options :: ShapeStyle -> [Prop (DC ())]
303 [ penWidth := styleStrokeWidth sty
304 , penColor := wxcolor (styleStrokeColour sty)
305 , brushKind := BrushSolid
306 , brushColor := wxcolor (styleFill sty)
309 defaultShapeStyle :: ShapeStyle
311 ShapeStyle { styleStrokeWidth = 1
312 , styleStrokeColour = licorice
313 , styleFill = nodeColor }