10 import qualified Graphics
.UI
.Gtk
as G
11 -- import qualified System.Glib as G
12 import System
.Glib
.Attributes
(AttrOp
(..))
14 import Control
.Monad
.Prompt
23 backgroundColor
:: G
.Color
24 backgroundColor
= G
.Color
0xE0E0 0xE0E0 0xE0E0
27 frameColor
= G
.Color
0x4040 0x4040 0x4040
29 mainUI
:: [Strategy
] -> IO ()
31 G
.unsafeInitGUIForThreadedRTS
35 data Globals
= Globals
{
36 gBoard
:: G
.DrawingArea
,
37 gStatusbar
:: G
.Statusbar
,
43 sStrategy
:: Strategy
,
44 sBoard
:: Maybe Board
,
45 sStop
:: Maybe (IO ()),
46 sPreviousConfigItem
:: Maybe G
.RadioMenuItem
49 initState strats
= State
{
51 sStrategy
= head strats
,
54 sPreviousConfigItem
= Nothing
57 mkMainWindow
:: [Strategy
] -> IO ()
58 mkMainWindow strats
= do
60 win `G
.set`
[G
.windowTitle
:= "Haskell PGMS"]
61 win `G
.onDestroy` G
.mainQuit
63 vbox
<- G
.vBoxNew
False 0
64 win `G
.containerAdd` vbox
66 menubar
<- G
.menuBarNew
67 vbox `G
.containerAdd` menubar
69 board
<- G
.drawingAreaNew
70 vbox `G
.containerAdd` board
72 statusbar
<- G
.statusbarNew
73 vbox `G
.containerAdd` statusbar
75 state
<- newIORef
(initState strats
)
77 let globals
= Globals
{ gBoard
= board
,
78 gStatusbar
= statusbar
,
81 configureBoard globals
83 runItem
<- G
.menuItemNewWithLabel
"Run!"
84 menubar `G
.menuShellAppend` runItem
86 runItem `G
.onActivateLeaf` runGame globals
88 difficultyItem
<- G
.menuItemNewWithLabel
"Difficulty"
89 menubar `G
.menuShellAppend` difficultyItem
90 difficultyMenu
<- G
.menuNew
91 difficultyItem `G
.menuItemSetSubmenu` difficultyMenu
92 Just prev
<- foldM (\prev
(name
, cfg
) -> do
93 item <- maybe G
.radioMenuItemNewWithLabel
94 G
.radioMenuItemNewWithLabelFromWidget
96 item `G
.onActivateLeaf` selectConfig
item cfg globals
97 difficultyMenu `G
.menuShellAppend`
item
100 G
.separatorMenuItemNew
>>= G
.menuShellAppend difficultyMenu
101 customItem
<- G
.radioMenuItemNewWithLabelFromWidget prev
"Custom..."
102 customItem `G
.onActivateLeaf` customConfig customItem globals
103 difficultyMenu `G
.menuShellAppend` customItem
105 strategyItem
<- G
.menuItemNewWithLabel
"Strategy"
106 menubar `G
.menuShellAppend` strategyItem
107 strategyMenu
<- G
.menuNew
108 strategyItem `G
.menuItemSetSubmenu` strategyMenu
109 foldM (\prev strat
-> do
110 item <- maybe G
.radioMenuItemNewWithLabel
111 G
.radioMenuItemNewWithLabelFromWidget
113 strategyMenu `G
.menuShellAppend`
item
114 item `G
.onActivateLeaf` selectStrategy strat globals
120 selectStrategy
:: Strategy
-> Globals
-> IO ()
121 selectStrategy strat g
= do
122 modifyIORef
(gState g
) $ \s
-> s
{ sStrategy
= strat
}
124 G
.widgetQueueDraw
(gBoard g
)
126 reset
:: Globals
-> IO ()
128 s
<- readIORef
(gState g
)
129 writeIORef
(gState g
) s
{ sBoard
= Nothing
, sStop
= Nothing
}
130 maybe (return ()) id (sStop s
)
132 configureBoard
:: Globals
-> IO ()
133 configureBoard g
= do
136 iconFile
<- findFile
"icons.png"
137 icons
<- G
.pixbufNewFromFile iconFile
138 iconSize
<- G
.pixbufGetWidth icons
139 G
.widgetSetSizeRequest area
(pX maxSize
* iconSize
+ 2)
140 (pY maxSize
* iconSize
+ 2)
142 area `G
.onExpose`
\_
-> do
143 s
<- readIORef
(gState g
)
144 let board
= maybe empty id (sBoard s
)
145 makeArray
= listArray (Pos
1 1, cSize
(sConfig s
)) . repeat
148 bMines
= makeArray
False,
149 bView
= makeArray Hidden
,
151 drawBoard iconSize icons area
(sConfig s
) board
155 drawBoard
:: Int -> G
.Pixbuf
-> G
.DrawingArea
-> Config
-> Board
-> IO ()
156 drawBoard iconSize icons area cfg board
= do
157 (w
, h
) <- G
.widgetGetSize area
158 let Pos sx sy
= cSize cfg
159 ox
= (w
- sx
* iconSize
) `
div`
2
160 oy
= (h
- sy
* iconSize
) `
div`
2
161 draw
<- G
.widgetGetDrawWindow area
163 gc
<- G
.gcNewWithValues draw G
.newGCValues
164 let drawCell
(Pos x y
) n
= G
.drawPixbuf draw gc icons
165 0 (n
* iconSize
) (ox
+ (x
-1)*iconSize
) (oy
+ (y
-1)*iconSize
)
166 iconSize iconSize G
.RgbDitherNone
0 0
168 forM_
(assocs (bView board
)) $ \(p
, cell
) -> case cell
of
169 Exposed n
-> drawCell p n
170 Hidden
-> drawCell p
(9 + fromEnum (bMines board
! p
))
171 Marked
-> drawCell p
(11 + fromEnum (bMines board
! p
))
172 Exploded
-> drawCell p
13
174 G
.gcSetValues gc G
.newGCValues
{ G
.foreground
= backgroundColor
}
175 G
.drawRectangle draw gc
False (ox
- 1) (oy
- 1)
176 (sx
* iconSize
+ 1) (sy
* iconSize
+ 1)
177 G
.gcSetValues gc G
.newGCValues
{ G
.foreground
= frameColor
}
178 G
.drawRectangle draw gc
False (ox
- 2) (oy
- 2)
179 (sx
* iconSize
+ 3) (sy
* iconSize
+ 3)
181 runGame
:: Globals
-> IO ()
183 s
<- readIORef
(gState g
)
184 maybe runGame
' (\_
-> return False) (sStop s
)
191 s
<- readIORef
(gState g
)
192 runPromptC finish handle
(playGameP
(sConfig s
) gen1
193 (sRun
(sStrategy s
) gen2
))
195 handle
:: Play a
-> (a
-> IO Bool) -> IO Bool
196 handle
(Start b
) c
= do
200 handle
(Update p b
) c
= do
203 handle
(Trace s b
) c
= do
208 finish
:: (Result
String, Board
) -> IO Bool
212 finish
(Lost
, b
) = do
215 finish
(Unfinished s
, b
) = do
216 msg
("Unfinished: " ++ s
)
219 cont
:: (a
-> IO Bool) -> a
-> IO ()
221 hdl
<- flip G
.timeoutAdd
120 $ do
222 modifyIORef
(gState g
) $ \s
-> s
{ sStop
= Nothing
}
224 modifyIORef
(gState g
) $ \s
-> s
{ sStop
= Just
(G
.timeoutRemove hdl
) }
226 setBoard
:: Board
-> IO Bool
228 modifyIORef
(gState g
) $ \s
-> s
{ sBoard
= Just b
}
229 G
.widgetQueueDraw
(gBoard g
)
232 msg
:: String -> IO G
.MessageId
234 G
.statusbarPush
(gStatusbar g
) 1 s
236 configs
:: [(String, Config
)]
237 configs
= [("Beginner", beginner
),
238 ("Intermediate", intermediate
),
241 selectConfig
:: G
.RadioMenuItem
-> Config
-> Globals
-> IO ()
242 selectConfig
item cfg g
= do
243 active
<- G
.checkMenuItemGetActive
item
244 if not active
then do
245 modifyIORef
(gState g
) $ \s
-> s
{ sPreviousConfigItem
= Just
item }
247 s
<- readIORef
(gState g
)
248 when (sConfig s
/= cfg
) $ do
249 writeIORef
(gState g
) s
{ sConfig
= cfg
}
251 G
.widgetQueueDraw
(gBoard g
)
252 modifyIORef
(gState g
) $ \s
-> s
{ sPreviousConfigItem
= Nothing
}
254 customConfig
:: G
.RadioMenuItem
-> Globals
-> IO ()
255 customConfig
item g
= do
256 active
<- G
.checkMenuItemGetActive
item
257 when active
$ customConfig
' item g
259 customConfig
' :: G
.RadioMenuItem
-> Globals
-> IO ()
260 customConfig
' item g
= do
261 s
<- readIORef
(gState g
)
262 let Config
{ cSize
= Pos sx sy
, cMines
= m
} = sConfig s
265 G
.dialogAddButton dia G
.stockCancel G
.ResponseCancel
266 okButton
<- G
.dialogAddButton dia G
.stockOk G
.ResponseOk
267 G
.dialogSetDefaultResponse dia G
.ResponseCancel
268 G
.windowSetTitle dia
"Custom config"
270 upper
<- G
.dialogGetUpper dia
271 table
<- G
.tableNew
2 3 False
272 upper `G
.containerAdd` table
273 let fs
= [("width", 2, sx
, pX maxSize
),
274 ("height", 2, sy
, pY maxSize
),
275 ("mines", 1, m
, 999)]
276 fields
<- forM
(zip [0..] fs
) $ \(c
, (n
, l
, v
, h
)) -> do
277 label
<- G
.labelNew
(Just n
)
278 G
.tableAttach table label
0 1 c
(c
+1) [G
.Fill
] [] 5 2
279 G
.miscSetAlignment label
0 0.5
280 adj
<- G
.adjustmentNew
(fromIntegral v
) l
(fromIntegral h
) 1 10 10
281 button
<- G
.spinButtonNew adj
0.5 0
282 G
.tableAttach table button
1 2 c
(c
+1) [G
.Expand
, G
.Fill
] [] 5 2
286 res
<- G
.dialogRun dia
287 [width
, height
, mines
] <- forM fields
$ \f -> do
288 round `
liftM` G
.spinButtonGetValue f
289 let cfg
' = Config
{ cSize
= Pos width height
,
290 cMines
= min (width
* height
- 1) mines
}
293 G
.ResponseOk
-> selectConfig
item cfg
' g
294 _
-> maybe (return ()) (`G
.checkMenuItemSetActive`
True)
295 (sPreviousConfigItem s
)