hs-pgms-0.1: src/GUI.hs
-- |
-- Module : GUI
-- Copyright : (c) 2008 Bertram Felgenhauer
-- License : BSD3
--
-- Maintainer : Bertram Felgenhauer <int-e@gmx.de>
-- Stability : experimental
-- Portability : portable
--
-- This module is part of Haskell PGMS.
--
{-# LANGUAGE GADTs, BangPatterns #-}
module GUI (
mainGUI,
) where
import Mine
import Util
import GUI.Common
import GUI.Config
import GUI.Stats
import qualified Graphics.UI.Gtk as G
import System.Glib.Attributes (AttrOp (..))
import Control.Monad
import Control.Monad.Prompt
import Data.Maybe
import Data.IORef
import Data.Array.IArray
import System.Random
backgroundColor :: G.Color
backgroundColor = G.Color 0xE0E0 0xE0E0 0xE0E0
frameColor :: G.Color
frameColor = G.Color 0x4040 0x4040 0x4040
mainGUI :: [Strategy] -> IO ()
mainGUI strats = do
G.unsafeInitGUIForThreadedRTS
mkMainWindow strats
G.mainGUI
mkMainWindow :: [Strategy] -> IO ()
mkMainWindow strats = do
win <- G.windowNew
win `G.set` [G.windowTitle := "Haskell PGMS"]
win `G.onDestroy` G.mainQuit
vbox <- G.vBoxNew False 0
win `G.containerAdd` vbox
menubar <- G.menuBarNew
vbox `G.containerAdd` menubar
board <- G.drawingAreaNew
vbox `G.containerAdd` board
statusbar <- G.statusbarNew
vbox `G.containerAdd` statusbar
state <- newIORef (initState strats)
let globals = Globals { gBoard = board,
gStatusbar = statusbar,
gState = state }
configureBoard globals
runItem <- G.menuItemNewWithLabel "Run"
menubar `G.menuShellAppend` runItem
runMenu <- G.menuNew
runItem `G.menuItemSetSubmenu` runMenu
runRunItem <- G.menuItemNewWithLabel "Run"
runMenu `G.menuShellAppend` runRunItem
runRunItem `G.onActivateLeaf` runGame globals
runStatsItem <- G.menuItemNewWithLabel "Statistics..."
runMenu `G.menuShellAppend` runStatsItem
runStatsItem `G.onActivateLeaf` runStats globals
G.separatorMenuItemNew >>= G.menuShellAppend runMenu
runQuitItem <- G.menuItemNewWithLabel "Quit"
runMenu `G.menuShellAppend` runQuitItem
runQuitItem `G.onActivateLeaf` G.widgetDestroy win
difficultyItem <- G.menuItemNewWithLabel "Difficulty"
menubar `G.menuShellAppend` difficultyItem
difficultyMenu <- G.menuNew
difficultyItem `G.menuItemSetSubmenu` difficultyMenu
Just prev <- foldM (\prev (name, cfg) -> do
item <- maybe G.radioMenuItemNewWithLabel
G.radioMenuItemNewWithLabelFromWidget
prev name
item `G.onActivateLeaf` selectConfig item cfg globals
difficultyMenu `G.menuShellAppend` item
return (Just item)
) Nothing configs
G.separatorMenuItemNew >>= G.menuShellAppend difficultyMenu
customItem <- G.radioMenuItemNewWithLabelFromWidget prev "Custom..."
customItem `G.onActivateLeaf` customConfig customItem globals
difficultyMenu `G.menuShellAppend` customItem
strategyItem <- G.menuItemNewWithLabel "Strategy"
menubar `G.menuShellAppend` strategyItem
strategyMenu <- G.menuNew
strategyItem `G.menuItemSetSubmenu` strategyMenu
foldM (\prev strat -> do
item <- maybe G.radioMenuItemNewWithLabel
G.radioMenuItemNewWithLabelFromWidget
prev (sName strat)
strategyMenu `G.menuShellAppend` item
item `G.onActivateLeaf` selectStrategy strat globals
return (Just item)
) Nothing strats
G.widgetShowAll win
configureBoard :: Globals -> IO ()
configureBoard g = do
let area = gBoard g
iconFile <- findFile "icons.png"
icons <- G.pixbufNewFromFile iconFile
iconSize <- G.pixbufGetWidth icons
G.widgetSetSizeRequest area (pX maxSize * iconSize + 2)
(pY maxSize * iconSize + 2)
area `G.onExpose` \_ -> do
s <- readIORef (gState g)
let board = maybe empty id (sBoard s)
makeArray e = listArray (Pos 1 1, cSize (sConfig s)) (repeat e)
empty = Board {
bConfig = sConfig s,
bMines = makeArray False,
bView = makeArray Hidden,
bTodo = 0 }
drawBoard iconSize icons area (sConfig s) board
return True
return ()
drawBoard :: Int -> G.Pixbuf -> G.DrawingArea -> Config -> Board -> IO ()
drawBoard iconSize icons area cfg board = do
(w, h) <- G.widgetGetSize area
let Pos sX sY = cSize cfg
ox = (w - sX * iconSize) `div` 2
oy = (h - sY * iconSize) `div` 2
draw <- G.widgetGetDrawWindow area
gc <- G.gcNewWithValues draw G.newGCValues
let drawCell (Pos x y) n = G.drawPixbuf draw gc icons
0 (n * iconSize) (ox + (x-1)*iconSize) (oy + (y-1)*iconSize)
iconSize iconSize G.RgbDitherNone 0 0
forM_ (assocs (bView board)) $ \(p, cell) -> case cell of
Exposed n -> drawCell p n
Hidden -> drawCell p (9 + fromEnum (bMines board ! p))
Marked -> drawCell p (11 + fromEnum (bMines board ! p))
Exploded -> drawCell p 13
G.gcSetValues gc G.newGCValues { G.foreground = backgroundColor }
G.drawRectangle draw gc False (ox - 1) (oy - 1)
(sX * iconSize + 1) (sY * iconSize + 1)
G.gcSetValues gc G.newGCValues { G.foreground = frameColor }
G.drawRectangle draw gc False (ox - 2) (oy - 2)
(sX * iconSize + 3) (sY * iconSize + 3)
runGame :: Globals -> IO ()
runGame g = do
s <- readIORef (gState g)
maybe runGame' (\_ -> return False) (sStop s)
return ()
where
runGame' :: IO Bool
runGame' = do
gen1 <- newStdGen
gen2 <- newStdGen
s <- readIORef (gState g)
runPromptC finish handle (playGameP (sConfig s) gen1
(sRun (sStrategy s) gen2))
handle :: Play a -> (a -> IO Bool) -> IO Bool
handle (Start b) c = do
msg "Start!"
cont c ()
setBoard b
handle (Update p b) c = do
cont c ()
setBoard b
handle (Trace s b) c = do
cont c ()
msg $ "Trace: " ++ s
setBoard b
finish :: (Result String, Board) -> IO Bool
finish (Won, b) = do
msg "Won!"
setBoard b
finish (Lost, b) = do
msg "Lost!"
setBoard b
finish (Unfinished s, b) = do
msg ("Unfinished: " ++ s)
setBoard b
cont :: (a -> IO Bool) -> a -> IO ()
cont c r = do
hdl <- flip G.timeoutAdd 120 $ do
modifyIORef (gState g) $ \s -> s { sStop = Nothing }
c r
modifyIORef (gState g) $ \s -> s { sStop = Just (G.timeoutRemove hdl) }
setBoard :: Board -> IO Bool
setBoard b = do
modifyIORef (gState g) $ \s -> s { sBoard = Just b }
G.widgetQueueDraw (gBoard g)
return False
msg :: String -> IO G.MessageId
msg s = do
G.statusbarPush (gStatusbar g) 1 s