-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDemo.hs
135 lines (104 loc) · 3.59 KB
/
Demo.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
-- Example of an XDot drawing with clickable nodes
import System.Environment
import Control.Monad
import Control.Monad.Trans.Class
import Data.IORef
import qualified Data.Text.Lazy.IO as L
import Data.GraphViz
import qualified Data.GraphViz.Types.Generalised as G
import qualified Data.GraphViz.Types.Graph as R
import Data.GraphViz.Commands.IO
import Graphics.XDot.Parser
import Graphics.XDot.Viewer
import Graphics.XDot.Types hiding (w, h)
import Graphics.UI.Gtk hiding (Box, Signal, Dot, Point, Rectangle, Object)
import Graphics.Rendering.Cairo
import qualified Graphics.UI.Gtk.Gdk.Events as E
data State = State
{ objects :: ([(Object String, Operation)], Rectangle)
, bounds :: [(Object String, Rectangle)]
, mousePos :: Point
, hover :: Object String
}
main :: IO ()
main = do
args <- getArgs
quitWithoutGraphviz "Cannot continue: Graphviz is not installed"
if length args == 1 then run $ head args else error "Usage: Demo file.dot"
run :: String -> IO ()
run file = do
dotText <- L.readFile file
-- If dg is a G.DotGraph it fails when there's a subgraph in it
let dg = parseDotGraph dotText :: R.DotGraph String
-- You can choose another graphviz command by changing Dot to Neato, TwoPi, Circo or Fdp
xdg <- graphvizWithHandle Dot dg (XDot Nothing) hGetDot
let objs = (getOperations xdg, getSize xdg)
--putStrLn $ show xdg
--putStrLn $ show objs
state <- newIORef $ State objs [] (0,0) None
initGUI
window <- windowNew
canvas <- drawingAreaNew
set window [ windowTitle := "XDot Demo"
, containerChild := canvas
]
on canvas draw $ do
redraw canvas state
on canvas motionNotifyEvent $ do
(x,y) <- eventCoordinates
lift $ do
modifyIORef state (\s -> s {mousePos = (x,y)})
tick canvas state
return True
on canvas buttonPressEvent $ do
button <- eventButton
eClick <- eventClick
lift $ do
when (button == LeftButton && eClick == SingleClick) $
click state dg
return True
widgetShowAll window
on window destroyEvent $ lift $ mainQuit >> return True
mainGUI
click :: IORef State -> R.DotGraph String -> IO ()
click state _dg = do
s <- readIORef state
case hover s of
Node t -> putStrLn $ "Node clicked: " ++ t
Edge f t -> putStrLn $ "Edge clicked: " ++ f ++ " -> " ++ t
_ -> return ()
tick :: WidgetClass w => w -> IORef State -> IO ()
tick canvas state = do
oldS <- readIORef state
let oldHover = hover oldS
modifyIORef state $ \s' -> (
let (mx, my) = mousePos s'
check (name', (x,y,w,h)) =
if x <= mx && mx <= x + w &&
y <= my && my <= y + h
then name' else None
validOne (None:xs) = validOne xs
validOne (x:_) = x
validOne _ = None
in s' {hover = validOne $ map check (bounds s')}
)
s <- readIORef state
unless (oldHover == hover s) $ widgetQueueDraw canvas
redraw :: WidgetClass w => w -> IORef State -> Render ()
redraw canvas state = do
s <- liftIO $ readIORef state
rw <- liftIO $ widgetGetAllocatedWidth canvas
rh <- liftIO $ widgetGetAllocatedHeight canvas
let (ops, size'@(_,_,sw,sh)) = objects s
-- Proportional scaling
let scalex = min (fromIntegral rw / sw) (fromIntegral rh / sh)
scaley = scalex
offsetx = 0.5 * fromIntegral rw
offsety = 0.5 * fromIntegral rh
save
translate offsetx offsety
scale scalex scaley
result <- drawAll (hover s) size' ops
restore
let boundingBoxes = map (\(o, (x,y,w,h)) -> (o, (x*scalex+offsetx,y*scaley+offsety,w*scalex,h*scaley))) result
liftIO $ modifyIORef state (\s' -> s' {bounds = boundingBoxes})