gtk-helpers (empty) → 0.0.7
raw patch · 25 files changed
+1452/−0 lines, 25 filesdep +arraydep +basedep +giosetup-changed
Dependencies added: array, base, gio, glib, gtk, mtl, process, template-haskell
Files
- LICENSE +30/−0
- Setup.hs +3/−0
- gtk-helpers.cabal +48/−0
- src/Data/Board/GameBoardIO.hs +89/−0
- src/Game/Board/BasicTurnGame.hs +61/−0
- src/Graphics/UI/Gtk/Board/BoardLink.hs +67/−0
- src/Graphics/UI/Gtk/Board/TiledBoard.hs +403/−0
- src/Graphics/UI/Gtk/Entry/FormatEntry.hs +76/−0
- src/Graphics/UI/Gtk/Entry/HighlightedEntry.hs +71/−0
- src/Graphics/UI/Gtk/Extra/Builder.hs +39/−0
- src/Graphics/UI/Gtk/Extra/BuilderTH.hs +64/−0
- src/Graphics/UI/Gtk/Helpers/Combo.hs +44/−0
- src/Graphics/UI/Gtk/Helpers/FileDialog.hs +42/−0
- src/Graphics/UI/Gtk/Helpers/MenuItem.hs +6/−0
- src/Graphics/UI/Gtk/Helpers/MessageDialog.hs +10/−0
- src/Graphics/UI/Gtk/Helpers/ModelViewNotebookSync.hs +60/−0
- src/Graphics/UI/Gtk/Helpers/ModelViewPath.hs +14/−0
- src/Graphics/UI/Gtk/Helpers/Multiline/TextBuffer.hs +19/−0
- src/Graphics/UI/Gtk/Helpers/Pixbuf.hs +61/−0
- src/Graphics/UI/Gtk/Helpers/TreeView.hs +58/−0
- src/Graphics/UI/Gtk/Layout/BackgroundContainer.hs +65/−0
- src/Graphics/UI/Gtk/Layout/DummyBin.hs +19/−0
- src/Graphics/UI/Gtk/Layout/EitherWidget.hs +41/−0
- src/Graphics/UI/Gtk/Layout/MaybeWidget.hs +45/−0
- src/System/Application.hs +17/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Ivan Perez (Keera Studios) 2010-2012++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Ivan Perez nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,3 @@+#!/usr/bin/env runhaskell+import Distribution.Simple+main = defaultMain
+ gtk-helpers.cabal view
@@ -0,0 +1,48 @@+Name: gtk-helpers+Version: 0.0.7+Synopsis: A collection of auxiliary operations and widgets related to Gtk+-- Description: +Homepage: http://keera.es/blog/community+License: BSD3+License-file: LICENSE+Author: Ivan Perez+Maintainer: ivan.perez@keera.es+-- Copyright: +Stability: Experimental+Category: Graphics+Build-type: Simple+-- Extra-source-files: +Cabal-version: >=1.2++Library+ Build-depends: base >= 4 && < 5,+ template-haskell, process, gtk, gio, glib, mtl, array++ hs-source-dirs: src/++ -- Build-tools: + ghc-options: -Wall++ -- Exposed-modules: + exposed-modules: Data.Board.GameBoardIO+ , Game.Board.BasicTurnGame+ , Graphics.UI.Gtk.Board.TiledBoard+ , Graphics.UI.Gtk.Board.BoardLink+ , Graphics.UI.Gtk.Entry.FormatEntry+ , Graphics.UI.Gtk.Entry.HighlightedEntry+ , Graphics.UI.Gtk.Extra.Builder+ , Graphics.UI.Gtk.Extra.BuilderTH+ , Graphics.UI.Gtk.Helpers.Combo+ , Graphics.UI.Gtk.Helpers.FileDialog+ , Graphics.UI.Gtk.Helpers.MenuItem+ , Graphics.UI.Gtk.Helpers.MessageDialog+ , Graphics.UI.Gtk.Helpers.ModelViewNotebookSync+ , Graphics.UI.Gtk.Helpers.ModelViewPath+ , Graphics.UI.Gtk.Helpers.Multiline.TextBuffer+ , Graphics.UI.Gtk.Helpers.Pixbuf+ , Graphics.UI.Gtk.Helpers.TreeView+ , Graphics.UI.Gtk.Layout.BackgroundContainer+ , Graphics.UI.Gtk.Layout.DummyBin+ , Graphics.UI.Gtk.Layout.EitherWidget+ , Graphics.UI.Gtk.Layout.MaybeWidget+ , System.Application
+ src/Data/Board/GameBoardIO.hs view
@@ -0,0 +1,89 @@+module Data.Board.GameBoardIO where++import Control.Monad+import Data.Array.IO+import Data.Maybe++data GameBoard index e = GameBoard (IOArray (index,index) (Maybe e))++gameBoardNew :: (Ix index) => [(index, index, e)] -> IO (GameBoard index e)+gameBoardNew es = do+ gameBoardNewWithBoundaries boundaries es+ where indexList = map (\(i,j,_) -> (i,j)) es+ boundaries = listBoundaries indexList++gameBoardNewWithBoundaries :: (Ix index) => ((index, index), (index, index)) -> [(index, index, e)] -> IO (GameBoard index e)+gameBoardNewWithBoundaries boundaries es = do+ array <- newArray boundaries Nothing+ mapM_ (\(i,j,e) -> writeArray array (i,j) (Just e)) es+ return $ GameBoard array++gameBoardNewEmptySquare :: (Num index, Ix index) => index -> index -> IO (GameBoard index e)+gameBoardNewEmptySquare iX jX = do+ array <- newArray ((0,0),(iX,jX)) Nothing+ return $ GameBoard array++gameBoardNewEmpty :: Ix index => [(index, index)] -> IO (GameBoard index e)+gameBoardNewEmpty es = do+ array <- newArray (listBoundaries es) Nothing+ return $ GameBoard array++listBoundaries :: Ix index => [(index,index)] -> ((index,index),(index,index))+listBoundaries ((ix,jx):es) = foldr updateBoundaries ((ix,jx),(ix,jx)) es+ where updateBoundaries (x,y) ((minX,minY),(maxX,maxY)) = ((minX',minY'),(maxX',maxY'))+ where minX' = if x < minX then x else minX+ minY' = if y < minY then y else minY+ maxX' = if x > maxX then x else maxX+ maxY' = if y > maxY then y else maxY++gameBoardSetPiece :: Ix index => (index, index) -> e -> GameBoard index e -> IO () -- GameBoard index e)+gameBoardSetPiece pos e (GameBoard board) =+ writeArray board pos (Just e)++gameBoardGetPiece :: Ix index => (index, index) -> GameBoard index e -> IO (Maybe e)+gameBoardGetPiece pos (GameBoard board) =+ readArray board pos++gameBoardRemovePiece :: Ix index => (index, index) -> GameBoard index e -> IO () -- GameBoard index e)+gameBoardRemovePiece pos (GameBoard board) =+ writeArray board pos Nothing++gameBoardMovePiece :: Ix index => (index, index) -> (index, index) -> GameBoard index e -> IO () -- GameBoard index e)+gameBoardMovePiece posO posD gb = do+ piece <- gameBoardGetPiece posO gb+ maybe (return ()) (\piece' -> do+ gameBoardRemovePiece posO gb+ gameBoardSetPiece posD piece' gb) piece++gameBoardFoldM :: (Ix index) => GameBoard index a -> (b -> ((index,index), a) -> IO b) -> b -> IO b+gameBoardFoldM (GameBoard array) f def = do+ assocs <- getAssocs array+ let assocs' = map (\(x,y) -> (x,fromJust y)) $ filter (isJust . snd) assocs+ foldM f def assocs'++gameBoardMapM_ :: (Ix index) => GameBoard index a -> ((index,index) -> a -> IO ()) -> IO ()+gameBoardMapM_ (GameBoard array) f =+ arrayMapM_ array f'+ where f' x e = maybe (return ()) (f x) e++arrayMapM_ :: (Ix index) => IOArray index a -> (index -> a -> IO ()) -> IO ()+arrayMapM_ array f = do+ assocs <- getAssocs array+ mapM_ (uncurry f) assocs++gameBoardClear :: (Ix index) => GameBoard index a -> IO()+gameBoardClear board@(GameBoard array) = do+ ((xm, ym), (xM, yM)) <- getBounds array+ forM_ (range (xm, xM)) $ \x -> + forM_ (range (ym, yM)) $ \y ->+ gameBoardRemovePiece (x,y) board++gameBoardGetBoundaries :: (Ix index) => GameBoard index a -> IO ((index,index),(index,index))+gameBoardGetBoundaries (GameBoard array) = getBounds array++gameBoardClone :: (Ix index) => GameBoard index a -> IO (GameBoard index a)+gameBoardClone (GameBoard array) = do+ bounds <- getBounds array+ assocs <- getAssocs array+ let assocs' = [(ix,iy,e) | ((ix,iy), Just e) <- assocs]+ gameBoardNewWithBoundaries bounds assocs'
+ src/Game/Board/BasicTurnGame.hs view
@@ -0,0 +1,61 @@+{-# Language MultiParamTypeClasses, FunctionalDependencies #-}++module Game.Board.BasicTurnGame where++import Data.Ix+import Data.Maybe++data GameChange index player piece + = AddPiece (index, index) player piece+ | RemovePiece (index, index)+ | MovePiece (index, index) (index, index)++-- FIXME: Wrong data structure => use unmutable matrices+hasPiece :: Ix index => GameState index tile player piece -> (index, index) -> Bool+hasPiece game ix = isJust (getPieceAt game ix)++getPieceAt :: Ix index => GameState index tile player piece -> (index, index) -> Maybe (player, piece)+getPieceAt game (posX, posY) = + listToMaybe [(player, piece) | (x, y, player, piece) <- boardPieces' game+ , x == posX+ , y == posY]+ ++data GameState index tile player piece = GameState+ { curPlayer' :: player+ , boardPos :: [(index, index, tile)]+ , boardPieces' :: [(index, index, player, piece)]+ }++class PlayableGame a index tile player piece | a -> index, a -> tile, a -> player, a -> piece where++ curPlayer :: a -> player+ allPieces :: a -> [(index, index, player, piece)]+ allPos :: a -> [(index, index, tile)]++ moveEnabled :: a -> Bool+ moveEnabled _ = False++ canMove :: a -> player -> (index, index) -> Bool+ canMove _ _ _ = False++ canMoveTo :: a -> player -> (index, index) -> (index, index) -> Bool+ canMoveTo _ _ _ _ = False++ move :: a -> player -> (index, index) -> (index, index) -> [GameChange index player piece]+ move _ _ _ _ = []++ activateEnabled :: a -> Bool+ activateEnabled _ = False++ canActivate :: a -> player -> (index, index) -> Bool+ canActivate _ _ _ = False++ activate :: a -> player -> (index, index) -> [GameChange index player piece]+ activate _ _ _ = []++ applyChange :: a -> GameChange index player piece -> a+ applyChange g _ = g++ applyChanges :: a -> [GameChange index player piece] -> a+ applyChanges a ls = foldl applyChange a ls
+ src/Graphics/UI/Gtk/Board/BoardLink.hs view
@@ -0,0 +1,67 @@+{-# Language MultiParamTypeClasses, FunctionalDependencies #-}++module Graphics.UI.Gtk.Board.BoardLink where++import Control.Monad+import Data.IORef+import Data.Ix+import Data.Maybe+import Game.Board.BasicTurnGame+import Graphics.UI.Gtk+import Graphics.UI.Gtk.Board.TiledBoard+++attachGameRules :: (PlayableGame pg index tile player piece, Ix index)+ => Game pg index tile player piece -> IO (Board index tile (player, piece))+attachGameRules game = do+ board <- boardNew (allPos $ gameS game) (tileF $ visual game) (pieceF $ visual game)++ let (r,g,b) = bgColor (visual game)+ (r', g', b') = (fromIntegral r, fromIntegral g, fromIntegral b)+ mapM_ (\s -> widgetModifyBg board s (Color r' g' b')) [StateNormal, StateActive, StatePrelight, StateSelected]+ when (isJust (bg $ visual game)) $ boardSetBackground board (bg $ visual game)++ vgRef <- newIORef game++ -- Set the initial board state+ mapM_ (\(x,y) -> boardSetPiece x y board) $ [((x,y),(pl,pc)) | (x,y,pl,pc) <- allPieces (gameS game)]++ board `boardOnPieceDragStart` \pos -> do+ visualGame <- readIORef vgRef+ let game' = gameS visualGame+ return (moveEnabled game' && canMove game' (curPlayer game') pos)++ board `boardOnPieceDragOver` \posF posT -> do+ visualGame <- readIORef vgRef+ let game' = gameS visualGame+ return (moveEnabled game' && canMoveTo game' (curPlayer game') posF posT)++ board `boardOnPieceDragDrop` \posF posT -> do+ visualGame <- readIORef vgRef+ let game' = gameS visualGame+ moves = move game' (curPlayer game') posF posT+ game'' = foldl applyChange game' moves+ writeIORef vgRef (visualGame { gameS = game'' })+ forM_ moves (applyBoardChange board)+ + when (moveEnabled (gameS game)) $ boardEnableDrag board++ return board++applyBoardChange :: Ix index => Board index tile (player, piece) -> GameChange index player piece -> IO ()+applyBoardChange board (AddPiece pos player piece) = boardSetPiece pos (player, piece) board+applyBoardChange board (RemovePiece pos) = boardRemovePiece pos board+applyBoardChange board (MovePiece posO posD) = boardMovePiece posO posD board++data VisualGameAspects index tile player piece = VisualGameAspects+ { tileF :: PixmapsFor tile+ , pieceF :: PixmapsFor (player, piece)+ , bgColor :: (Int, Int, Int)+ , bg :: Maybe (Pixbuf, SizeAdjustment)+ } +++data Game pg index tile player piece = Game+ { visual :: VisualGameAspects index tile player piece+ , gameS :: pg+ }
+ src/Graphics/UI/Gtk/Board/TiledBoard.hs view
@@ -0,0 +1,403 @@+module Graphics.UI.Gtk.Board.TiledBoard where++import Control.Monad (when, void)+import Control.Monad.Trans (liftIO)+import Data.Array.IO+import Data.IORef+import Data.Maybe (isJust, fromJust)+import Graphics.UI.Gtk+import Graphics.UI.Gtk.Gdk.GC (gcNew)+import System.Glib.Types++-- Local imports+import Data.Board.GameBoardIO++data Board index tile piece = Board+ { boardDrawingArea :: DrawingArea+ , boardTiles :: GameBoard index tile+ , boardPieces :: GameBoard index piece+ , tilePixmaps :: PixmapsFor tile+ , piecePixmaps :: PixmapsFor piece+ , tileSize :: (Int, Int)+ , background :: IORef (Maybe (Pixbuf, SizeAdjustment))+ , overlay :: IORef (Maybe (Pixbuf, SizeAdjustment))+ , dragEnabled :: IORef Bool+ , draggingFrom :: IORef (Maybe (index, index))+ , draggingTo :: IORef (Maybe (index, index))+ , draggingMouseOrig :: IORef (Maybe (Int, Int))+ , draggingMousePos :: IORef (Maybe (Int, Int))+ , movingStatus :: IORef (Maybe (MovingStatus index))+ }++data MovingStatus index = MovingStatus+ { movingFrom :: (index, index)+ , movingTo :: (index, index)+ , stepsPerUnit :: Double+ , timePerUnit :: Double+ , movingStep :: Double+ }++instance WidgetClass (Board index tile piece)+instance ObjectClass (Board index tile piece)+instance GObjectClass (Board index tile piece) where+ toGObject = toGObject . boardDrawingArea+ unsafeCastGObject x = Board (unsafeCastGObject x) undefined undefined undefined undefined undefined undefined undefined undefined undefined undefined undefined undefined undefined++type PixmapsFor a = a -> Pixbuf++data SizeAdjustment = SizeAdjustment++boardNew :: Ix index+ => [(index,index,tile)] -> PixmapsFor tile -> PixmapsFor piece+ -> IO (Board index tile piece)+boardNew tileList tilePixs piecePixs = do+ da <- drawingAreaNew++ tb <- gameBoardNew tileList+ pb <- gameBoardNewEmpty (map (\(x,y,_) -> (x,y)) tileList)++ ts <- getTileSize tileList tilePixs++ bg <- newIORef Nothing+ ov <- newIORef Nothing++ dragging <- newIORef False+ draggingF <- newIORef Nothing+ draggingT <- newIORef Nothing+ draggingO <- newIORef Nothing+ draggingP <- newIORef Nothing++ movingSt <- newIORef Nothing++ let board = Board da tb pb tilePixs piecePixs ts bg ov+ dragging draggingF draggingT draggingO draggingP+ movingSt++ (pixW, pixH) <- boardGetPixelSize board++ da `on` realize $ widgetSetSizeRequest da pixW pixH+ da `on` exposeEvent $ liftIO (boardRefresh board) >> return False++ return board++getTileSize :: [(index, index, tile)] -> PixmapsFor tile -> IO (Int, Int)+getTileSize [] _ = return (0,0)+getTileSize ((_,_,t):_) pixs = do+ let pb = pixs t+ w <- pixbufGetWidth pb+ h <- pixbufGetHeight pb+ return (w,h)++boardGetPiece :: Ix index => (index, index) -> Board index tile piece -> IO (Maybe piece)+boardGetPiece pos board = gameBoardGetPiece pos (boardPieces board)++boardSetPiece :: Ix index => (index, index) -> piece -> Board index tile piece -> IO ()+boardSetPiece pos piece board = do+ boardSetPieceNoRefresh pos piece board+ boardInvalidate board+-- boardRefresh board++boardSetPieceNoRefresh :: Ix index => (index, index) -> piece -> Board index tile piece -> IO ()+boardSetPieceNoRefresh pos piece board = do+ -- check that there's a tile there+ posOk <- fmap isJust $ gameBoardGetPiece pos $ boardTiles board+ when posOk $ do+ -- if there is, place the piece on the pieces board+ gameBoardSetPiece pos piece (boardPieces board) ++boardRemovePiece :: Ix index => (index, index) -> Board index tile piece -> IO ()+boardRemovePiece pos board = do+ -- check that there's a tile there+ posOk <- fmap isJust $ gameBoardGetPiece pos $ boardTiles board+ when posOk $ do+ -- if there is, remove the piece from the pieces board+ gameBoardRemovePiece pos (boardPieces board) + -- refresh the image+ boardInvalidate board+ -- boardRefresh board++boardMovePiece :: Ix index => (index, index) -> (index, index) -> Board index tile piece -> IO ()+boardMovePiece posO posD board = do+ -- check that there's a tile there+ posOrigOk <- fmap isJust $ gameBoardGetPiece posO $ boardTiles board+ posDestOk <- fmap isJust $ gameBoardGetPiece posD $ boardTiles board+ when (posOrigOk && posDestOk) $ do+ -- Move the piece+ gameBoardMovePiece posO posD $ boardPieces board+ -- Refresh the UI+ boardInvalidate board+ -- boardRefresh board++boardInvalidate :: Ix index => Board index tile piece -> IO ()+boardInvalidate = widgetQueueDraw ++boardRefresh :: Ix index => Board index tile piece -> IO ()+boardRefresh board = do + realized <- widgetGetRealized board+ when realized $ do+ dw <- widgetGetDrawWindow (boardDrawingArea board)+ + (w,h) <- widgetGetSize (boardDrawingArea board)++ bg <- readIORef (background board)+ ov <- readIORef (overlay board)+ + drawWindowBeginPaintRect dw (Rectangle 0 0 w h)+ + -- Clear Drawing area+ drawWindowClear dw+ gc <- gcNew dw+ when (isJust bg) $ do+ ((posBgX, posBgY), bg') <- uncurry (adjustPixbuf (w,h)) (fromJust bg)+ drawPixbuf dw gc bg' 0 0 posBgX posBgY (-1) (-1) RgbDitherNone (-1) (-1)++ -- Dragging status (used to determine what to draw)+ posM <- readIORef (draggingFrom board)+ mpOrig <- readIORef (draggingMouseOrig board)+ mpPos <- readIORef (draggingMousePos board)++ -- Draw tiles+ drawPixmaps dw (tileSize board) (boardTiles board) (tilePixmaps board)+ + -- Draw Pieces+ piecesBoard <- if isJust posM && isJust mpOrig && isJust mpPos+ then do pieces' <- gameBoardClone $ boardPieces board+ gameBoardRemovePiece (fromJust posM) pieces'+ return pieces'+ else return $ boardPieces board+ + -- drawPixmaps dw (tileSize board) (boardPieces board) (piecePixmaps board)+ drawPixmaps dw (tileSize board) piecesBoard (piecePixmaps board)++ -- Draw moving piece+ when (isJust posM && isJust mpOrig && isJust mpPos) $ do+ pieceM <- boardGetPiece (fromJust posM) board+ when (isJust pieceM) $ do+ let pb = piecePixmaps board (fromJust pieceM)+ let (mpPosX, mpPosY) = fromJust mpPos+ (mpOrigX, mpOrigY) = fromJust mpOrig+ (x,y) = (mpPosX - mpOrigX, mpPosY - mpOrigY)+ drawPixbuf dw gc pb 0 0 x y (-1) (-1) RgbDitherNone (-1) (-1)++ when (isJust ov) $ do+ ((posOvX, posOvY), ov') <- uncurry (adjustPixbuf (w,h)) (fromJust ov)+ drawPixbuf dw gc ov' 0 0 posOvX posOvY (-1) (-1) RgbDitherNone (-1) (-1)+ + drawWindowEndPaint dw++-- FIXME: To be completed+adjustPixbuf :: (Int, Int) -> Pixbuf -> SizeAdjustment -> IO ((Int, Int), Pixbuf)+adjustPixbuf _ pb _ = return ((0,0), pb)++mouseMotionHandler :: Ix index => Board index tile piece -> ((index, index) -> EventM EMotion Bool) -> EventM EMotion Bool+mouseMotionHandler board p = do+ coords <- eventCoordinates+ pos <- liftIO $ getMouseCoordinates board coords+ maybe (return False) p pos++mouseButtonHandler :: Ix index => Board index tile piece -> ((index, index) -> EventM EButton Bool) -> EventM EButton Bool+mouseButtonHandler board p = do+ coords <- eventCoordinates+ pos <- liftIO $ getMouseCoordinates board coords+ maybe (return False) p pos++getMouseCoordinates :: Ix index => Board index tile piece -> (Double, Double) -> IO (Maybe (index, index))+getMouseCoordinates board (x,y) = do+ let (tileW, tileH) = tileSize board+ tileCol = round x `div` tileW+ tileRow = round y `div` tileH+ let (GameBoard array) = boardTiles board+ ((xm, ym), (xM, yM)) <- getBounds $ array+ let xs = range (xm, xM)+ ys = range (ym, yM)+ if (inRange (0, length xs - 1) tileCol && inRange (0, length ys - 1) tileRow)+ then return $ Just ((head (drop tileCol xs)), (head (drop tileRow ys)))+ else return Nothing++clickHandler :: Ix index => Board index tile piece -> ((index, index) -> IO ()) -> EventM EButton Bool+clickHandler board p = do+ (x,y) <- eventCoordinates+ liftIO $ do+ let (tileW, tileH) = tileSize board+ tileCol = round x `div` tileW+ tileRow = round y `div` tileH+ let (GameBoard array) = boardTiles board+ ((xm, ym), (xM, yM)) <- getBounds $ array+ let xs = range (xm, xM)+ ys = range (ym, yM)+ when (inRange (0, length xs - 1) tileCol && inRange (0, length ys - 1) tileRow) $+ p ((head (drop tileCol xs)), (head (drop tileRow ys)))+ return False+ +boardGetPixelSize :: Ix index => Board index tile piece -> IO (Int, Int)+boardGetPixelSize board = do+ let (GameBoard array) = boardTiles board++ ((xm, ym), (xM, yM)) <- getBounds $ array+ let htiles = rangeSize (xm, xM)+ vtiles = rangeSize (ym, yM)+ (tileW, tileH) = tileSize board++ return (htiles * tileW, vtiles * tileH)++drawPixmaps :: (Ix index, DrawableClass d) => d -> (Int, Int) -> GameBoard index e -> PixmapsFor e -> IO()+drawPixmaps d tileSize@(tw, th) gameBoard@(GameBoard array) pixmaps = do+ gc <- gcNew d+ ((xm, ym), (xM, yM)) <- gameBoardGetBoundaries gameBoard++ let paintPixmap (x,y) elem = do+ let pixmap = pixmaps elem+ ix = index (xm, xM) x+ iy = index (ym, yM) y+ posX = ix * tw+ posY = iy * th+ drawPixbuf d gc pixmap 0 0 posX posY (-1) (-1) RgbDitherNone (-1) (-1)+ + gameBoardMapM_ gameBoard paintPixmap++boardFoldM :: (Ix index) => Board index tile piece -> (b -> ((index,index), piece) -> IO b) -> b -> IO b+boardFoldM board f def = gameBoardFoldM (boardPieces board) f def++boardClear :: Ix index => Board index tile piece -> IO ()+boardClear board = do+ gameBoardClear (boardPieces board)+ boardInvalidate board++boardLoad :: Ix index => Board index tile piece -> [((index, index), piece)] -> IO()+boardLoad board pieces = do+ gameBoardClear (boardPieces board)+ mapM_ (\(pos, piece) -> boardSetPieceNoRefresh pos piece board) pieces+ boardRefresh board++boardOnClick :: Ix index => Board index tile piece -> ((index, index) -> IO ()) -> IO()+boardOnClick board p = boardOnPress board (\c -> liftIO (p c) >> return False)++boardOnPress :: Ix index => Board index tile piece -> ((index, index) -> EventM EButton Bool) -> IO()+boardOnPress board f = void $ do+ widgetAddEvents (boardDrawingArea board) [ButtonPressMask]+ (boardDrawingArea board) `on` buttonPressEvent $ mouseButtonHandler board f++boardOnRelease :: Ix index => Board index tile piece -> ((index, index) -> EventM EButton Bool) -> IO()+boardOnRelease board f = void $ do+ widgetAddEvents (boardDrawingArea board) [ButtonPressMask, ButtonReleaseMask]+ (boardDrawingArea board) `on` buttonReleaseEvent $ mouseButtonHandler board f++boardOnMotion :: Ix index => Board index tile piece -> ((index, index) -> EventM EMotion Bool) -> IO()+boardOnMotion board f = void $ do+ widgetAddEvents board [PointerMotionMask]+ (boardDrawingArea board) `on` motionNotifyEvent $ mouseMotionHandler board f++boardSetBackground :: Ix index => Board index tile piece -> Maybe (Pixbuf, SizeAdjustment) -> IO()+boardSetBackground board bg = do+ writeIORef (background board) bg+ boardInvalidate board++boardSetOverlay :: Ix index => Board index tile piece -> Maybe (Pixbuf, SizeAdjustment) -> IO()+boardSetOverlay board bg = do+ writeIORef (overlay board) bg+ boardInvalidate board++boardEnableDrag :: Ix index => Board index tile piece -> IO()+boardEnableDrag board = writeIORef (dragEnabled board) True++boardDisableDrag :: Ix index => Board index tile piece -> IO()+boardDisableDrag board = do+ writeIORef (dragEnabled board) False+ boardInvalidate board++boardStartDrag :: Ix index => Board index tile piece -> (index, index) -> IO()+boardStartDrag board ix@(i,j) = do+ writeIORef (draggingFrom board) (Just ix)+ ((xm, ym), (xM, yM)) <- gameBoardGetBoundaries $ boardPieces board+ let (w,h) = tileSize board+ x = round ((0.5 + fromIntegral (rangeSize (xm, i))) * fromIntegral w)+ y = round ((0.5 + fromIntegral (rangeSize (ym, j))) * fromIntegral h)+ writeIORef (draggingMouseOrig board) (Just (x, y))++boardStopDrag :: Ix index => Board index tile piece -> IO()+boardStopDrag board = do+ writeIORef (draggingFrom board) Nothing+ writeIORef (draggingTo board) Nothing+ boardInvalidate board++boardOnPieceDragStart :: Ix index => Board index tile piece -> ((index, index) -> IO Bool) -> IO()+boardOnPieceDragStart board f = boardOnPress board $ \ix -> do+ (x,y) <- eventCoordinates+ returning False $ liftIO $ do+ drag <- readIORef (dragEnabled board)+ when drag $ do+ canDragThis <- f ix+ let from = if canDragThis then Just ix else Nothing+ orig = if canDragThis then Just (relativePos board ix (round x, round y)) else Nothing+ writeIORef (draggingFrom board) from+ writeIORef (draggingMouseOrig board) orig+ boardInvalidate board++boardOnPieceDragOver :: Ix index => Board index tile piece -> ((index, index) -> (index, index) -> IO Bool) -> IO()+boardOnPieceDragOver board f = boardOnMotion board $ \ix -> do+ (x,y) <- eventCoordinates+ returning False $ liftIO $ do+ drag <- readIORef (dragEnabled board)+ origM <- readIORef (draggingFrom board)+ when (drag && isJust origM) $ do+ canDropHere <- f (fromJust origM) ix+ let newDest = if canDropHere then Just ix else Nothing+ writeIORef (draggingTo board) newDest+ writeIORef (draggingMousePos board) (Just (round x, round y))+ boardInvalidate board++boardOnPieceDragDrop :: Ix index => Board index tile piece -> ((index, index) -> (index, index) -> IO ()) -> IO()+boardOnPieceDragDrop board f = void $ do+ widgetAddEvents (boardDrawingArea board) [ButtonPressMask, ButtonReleaseMask]+ (boardDrawingArea board) `on` buttonReleaseEvent $ returning False $ liftIO $ do+ drag <- readIORef (dragEnabled board)+ origM <- readIORef (draggingFrom board)+ destM <- readIORef (draggingTo board)+ let notSame = origM /= destM+ when (drag && isJust origM) $ do++ -- No longer dragging+ writeIORef (draggingFrom board) Nothing+ writeIORef (draggingTo board) Nothing+ writeIORef (draggingMouseOrig board) Nothing+ writeIORef (draggingMousePos board) Nothing++ -- When possible, call the handler+ when (isJust destM && notSame) $ f (fromJust origM) (fromJust destM)++ -- In any case, the board must be repainted+ boardInvalidate board++boardIsDragging :: Ix index => Board index tile piece -> IO Bool+boardIsDragging = fmap isJust . readIORef . draggingFrom++relativePos :: Ix index => Board index tile piece -> (index, index) -> (Int, Int) -> (Int, Int)+relativePos board (ix,iy) (x,y) = (x', y')+ where (w,h) = tileSize board+ x' = x `mod` w+ y' = y `mod` h++returning :: Monad m => a -> m b -> m a+returning v f = f >> return v++-- boardLiveMove :: Ix index => (index, index) -> (index, index) -> Board index tile piece -> IO ()+-- boardLiveMove posO posD board = do+-- evs <- widgetGetEvents board+-- +-- let evs' = undefined+-- +-- movingParams <- undefined+-- +-- let timeDelay = undefined+-- +-- writeIORef (movingStatus board) (Just movingParams)+-- +-- -- Set delay and time handler+-- timeoutAdd (do undefined -- advance moving one step+-- boardInvalidate board+-- -- return True if not the end, otherwise return False+-- )+-- timeDelay+-- +-- -- Time Handler includes resetting the event handers+-- return ()
+ src/Graphics/UI/Gtk/Entry/FormatEntry.hs view
@@ -0,0 +1,76 @@+module Graphics.UI.Gtk.Entry.FormatEntry+ ( FormatEntry+ , formatEntryNew+ , formatEntryNewWithFunction+ , formatEntrySetColor+ , formatEntryGetColor+ , formatEntrySetCheckFunction+ , formatEntryGetCheckFunction+ , formatEntryHasCorrectFormat+ , formatEntryColor+ , formatEntryCheckFunction+ )+ where++import Control.Monad.Trans (liftIO)+import Graphics.UI.Gtk+import Graphics.UI.Gtk.Entry.HighlightedEntry+import System.Glib.Types+import Data.IORef++-- Higlighted Entries+data FormatEntry = FormatEntry HighlightedEntry (IORef FormatEntryParams)+type FormatEntryParams = String -> Bool++formatEntryNew :: IO FormatEntry+formatEntryNew = formatEntryNewWithFunction (const True)++formatEntryNewWithFunction :: (String -> Bool) -> IO FormatEntry+formatEntryNewWithFunction checkF = do+ entry <- highlightedEntryNew+ defaultParamsRef <- newIORef checkF+ let formatEntry = FormatEntry entry defaultParamsRef+ formatEntry `on` editableChanged $ refreshEntry formatEntry+ return formatEntry++instance GObjectClass FormatEntry where+ toGObject (FormatEntry entry _) = toGObject entry+ unsafeCastGObject o = FormatEntry (unsafeCastGObject o) undefined+instance ObjectClass FormatEntry+instance WidgetClass FormatEntry+instance EntryClass FormatEntry+instance EditableClass FormatEntry++formatEntrySetColor :: FormatEntry -> Color -> IO ()+formatEntrySetColor (FormatEntry e _) color = highlightedEntrySetColor e color++formatEntryGetColor :: FormatEntry -> IO Color+formatEntryGetColor (FormatEntry e _) = highlightedEntryGetColor e++formatEntrySetCheckFunction :: FormatEntry -> (String -> Bool) -> IO ()+formatEntrySetCheckFunction fe@(FormatEntry _ params) checkF = do+ writeIORef params checkF+ refreshEntry fe++formatEntryGetCheckFunction :: FormatEntry -> IO (String -> Bool)+formatEntryGetCheckFunction (FormatEntry _ params) =+ readIORef params++-- Repaints the entry using the current color, or resets the+-- default style if no warning has to be given+refreshEntry :: FormatEntry -> IO()+refreshEntry f@(FormatEntry entry params) = do+ correct <- formatEntryHasCorrectFormat f+ highlightedEntrySetStatus entry (not correct)++formatEntryColor :: Attr FormatEntry Color+formatEntryColor = newAttr formatEntryGetColor formatEntrySetColor++formatEntryCheckFunction :: Attr FormatEntry (String -> Bool)+formatEntryCheckFunction = newAttr formatEntryGetCheckFunction formatEntrySetCheckFunction++formatEntryHasCorrectFormat :: FormatEntry -> IO Bool+formatEntryHasCorrectFormat f = do+ text <- entryGetText f+ func <- formatEntryGetCheckFunction f+ return $ func text
+ src/Graphics/UI/Gtk/Entry/HighlightedEntry.hs view
@@ -0,0 +1,71 @@+module Graphics.UI.Gtk.Entry.HighlightedEntry+ ( HighlightedEntry+ , highlightedEntryNew+ , highlightedEntrySetColor+ , highlightedEntryGetColor+ , highlightedEntrySetStatus+ , highlightedEntryGetStatus+ , highlightedEntryColor+ , highlightedEntryStatus+ )+ where++import Control.Monad.Trans (liftIO)+import Graphics.UI.Gtk+import System.Glib.Types+import Data.IORef++-- Higlighted Entries+data HighlightedEntry = HighlightedEntry Entry (IORef HighlightedEntryParams)+type HighlightedEntryParams = (Color, Bool)++highlightedEntryNew :: IO HighlightedEntry+highlightedEntryNew = do+ entry <- entryNew+ defaultParamsRef <- newIORef (Color 65000 32000 32000, False)+ return $ HighlightedEntry entry defaultParamsRef++instance GObjectClass HighlightedEntry where+ toGObject (HighlightedEntry entry _) = toGObject entry+ unsafeCastGObject o = HighlightedEntry (unsafeCastGObject o) undefined+instance ObjectClass HighlightedEntry+instance WidgetClass HighlightedEntry+instance EntryClass HighlightedEntry++highlightedEntrySetStatus :: HighlightedEntry -> Bool -> IO ()+highlightedEntrySetStatus he@(HighlightedEntry _ params) status = do+ modifyIORef params (\(c,_) -> (c,status))+ refreshBaseColor he++highlightedEntryGetStatus :: HighlightedEntry -> IO Bool+highlightedEntryGetStatus (HighlightedEntry _ params) = do+ (_,status) <- readIORef params+ return status++highlightedEntrySetColor :: HighlightedEntry -> Color -> IO ()+highlightedEntrySetColor he@(HighlightedEntry _ params) color = do+ modifyIORef params (\(_, s) -> (color, s))+ refreshBaseColor he++highlightedEntryGetColor :: HighlightedEntry -> IO Color+highlightedEntryGetColor (HighlightedEntry _ params) = do+ (color, _) <- readIORef params+ return color++-- Repaints the entry using the current color, or resets the+-- default style if no warning has to be given+refreshBaseColor :: HighlightedEntry -> IO()+refreshBaseColor (HighlightedEntry entry params) = do+ (color, status) <- readIORef params+ if status+ then mapM_ (\s -> widgetModifyBase entry s color) sensitiveStates+ else mapM_ (widgetRestoreBase entry) sensitiveStates+ where sensitiveStates = [ StateNormal, StateActive+ , StateSelected, StatePrelight+ ]++highlightedEntryStatus :: Attr HighlightedEntry Bool+highlightedEntryStatus = newAttr highlightedEntryGetStatus highlightedEntrySetStatus++highlightedEntryColor :: Attr HighlightedEntry Color+highlightedEntryColor = newAttr highlightedEntryGetColor highlightedEntrySetColor
+ src/Graphics/UI/Gtk/Extra/Builder.hs view
@@ -0,0 +1,39 @@+-- | A collection of small functions to help retrieve+-- information from Glade files.++module Graphics.UI.Gtk.Extra.Builder+ ( fromBuilder+ , loadInterface+ )+ where++import Graphics.UI.Gtk++-- | Graphics.UI.Gtk.builderGetObject with the arguments flipped+-- (Builder goes last).+fromBuilder :: (GObjectClass cls) =>+ (GObject -> cls) -> String -> Builder -> IO cls+fromBuilder f s b = builderGetObject b f s++-- | Returns a builder from which the objects in this part of the interface+-- can be accessed.+loadInterface :: String -> IO Builder+loadInterface fn = builderNew `incidentallyM` (`builderAddFromFile` fn)+-- It can be written in point-free style, but I'm not sure it's+-- clearer+-- loadInterface = incidentallyM builderNew . flip builderAddFromFile++-- -- | Returns a builder from which the objects in this part of the interface+-- --+-- -- can be accessed.+-- loadInterface :: String -> IO Builder+-- loadInterface builderPath = do+-- builder <- builderNew+-- builderAddFromFile builder builderPath+-- return builder++-- | Sequences two monadic computations and returns the+-- result of the first.+incidentallyM :: Monad m => m a -> (a -> m b) -> m a +incidentallyM op f = op >>= (\x -> (f x >> return x))+
+ src/Graphics/UI/Gtk/Extra/BuilderTH.hs view
@@ -0,0 +1,64 @@+-- | This module allows you to access Glade objects from+-- a Gtk Builder by providing the builder, the type of the+-- object and the name (the cast operation is deduced+-- automatically from the type name).+--+-- It uses Graphics.UI.Gtk.Builder.onBuilder to access a+-- glade object using TH.++module Graphics.UI.Gtk.Extra.BuilderTH+ ( gtkBuilderAccessor+ , gtkViewAccessor+ , fromBuilder+ )+ where++import Graphics.UI.Gtk.Extra.Builder (fromBuilder)+import Language.Haskell.TH.Syntax+import Language.Haskell.TH.Lib++-- | Accessor for Glade objects from Gtk Builders by name and+-- type.+gtkBuilderAccessor :: String -> String -> Q [Dec]+gtkBuilderAccessor name kind = sequenceQ+ -- Declaration+ [ sigD funcName+ -- Builder -> IO Kind+ (appT (appT arrowT (conT (mkName "Builder"))) + (appT (conT (mkName "IO")) (conT (mkName kind))))+ -- Implementation+ , funD funcName + -- castedOnBuilder objectName+ [clause [] (normalB (appE castedAccess + (litE (stringL name)))) []]+ ]++ where castedAccess = appE (varE (mkName "fromBuilder")) casting+ casting = varE (mkName ("castTo" ++ kind))+ funcName = mkName name++-- | Accessor for Glade objects from Gtk Builders encapsulated in+-- Views, by name and -- type.+gtkViewAccessor :: String -> String -> String -> String -> Q [Dec]+gtkViewAccessor builderModule uiAccessor name kind = sequenceQ+ -- Declaration+ [ sigD funcName+ -- Builder -> IO Kind+ (appT (appT arrowT (conT (mkName "View"))) + (appT (conT (mkName "IO")) (conT (mkName kind))))+ -- Implementation+ , funD funcName + -- castedOnBuilder objectName+ [clause [varP builderName]+ (normalB (appE (varE funcNameInBuilder)+ (appE (varE (mkName uiAccessor))+ (varE builderName)+ )+ )) []]+ ]++ where castedAccess = appE (varE (mkName "fromBuilder")) casting+ casting = varE (mkName ("castTo" ++ kind))+ funcName = mkName name+ funcNameInBuilder = mkName $ builderModule ++ ('.' : name) + builderName = mkName "b"
+ src/Graphics/UI/Gtk/Helpers/Combo.hs view
@@ -0,0 +1,44 @@+module Graphics.UI.Gtk.Helpers.Combo where++import Data.List+import Data.Maybe+import Graphics.UI.Gtk++addTextColumn :: (TreeModelClass (model row), TypedTreeModelClass model)+ => ComboBox -> model row -> (row -> Maybe String) -> IO()+addTextColumn cb st f = do++ comboBoxSetModel cb (Just st)+ renderer <- cellRendererTextNew+ cellLayoutPackStart cb renderer True+ cellLayoutSetAttributes cb renderer st $ map (cellText :=).maybeToList.f+ return ()++type TypedComboBox a = (ComboBox, ListStore a)++typedComboBoxCombo :: TypedComboBox a -> ComboBox+typedComboBoxCombo = fst++typedComboBoxStore :: TypedComboBox a -> ListStore a+typedComboBoxStore = snd++typedComboBoxGetSelected :: TypedComboBox a -> IO (Maybe a)+typedComboBoxGetSelected (cb, ls) = do+ sel <- get cb comboBoxActive+ list <- listStoreToList ls+ if sel < 0 || length list <= sel+ then return Nothing+ else return $ Just $ list!!sel++typedComboBoxGetSelectedUnsafe :: TypedComboBox a -> IO a+typedComboBoxGetSelectedUnsafe (cb, ls) = do+ sel <- get cb comboBoxActive+ list <- listStoreToList ls+ return $ list!!sel++typedComboBoxSetSelected :: (Eq a) => TypedComboBox a -> a -> IO ()+typedComboBoxSetSelected (cb, ls) x = do+ list <- listStoreToList ls+ case elemIndex x list of+ Nothing -> return ()+ Just ix -> set cb [ comboBoxActive := ix ]
+ src/Graphics/UI/Gtk/Helpers/FileDialog.hs view
@@ -0,0 +1,42 @@+module Graphics.UI.Gtk.Helpers.FileDialog+ ( openOpenFileDialog+ )+ where++import Graphics.UI.Gtk++type Ext = ([String], String)++-- Auxiliary functions: open file dialogs+openOpenFileDialog :: String -> [Ext] -> IO (Maybe String)+openOpenFileDialog title exts = do++ dialog <- fileChooserDialogNew+ (Just title) --dialog title+ Nothing+ FileChooserActionOpen --the kind of dialog we want+ [("gtk-cancel" --The buttons to display+ ,ResponseCancel)+ ,("gtk-ok"+ , ResponseAccept)]++ ffs <- extsToFilters exts+ mapM_ (fileChooserAddFilter dialog) ffs++ widgetShow dialog+ result <- dialogRun dialog+ res <- case result of+ ResponseAccept -> fileChooserGetFilename dialog+ _ -> return Nothing+ widgetDestroy dialog+ return res++extsToFilters :: [Ext] -> IO [FileFilter]+extsToFilters = mapM extToFilter++extToFilter :: Ext -> IO FileFilter+extToFilter (pats, name) = do+ ff <- fileFilterNew+ fileFilterSetName ff name+ mapM_ (fileFilterAddPattern ff) pats+ return ff
+ src/Graphics/UI/Gtk/Helpers/MenuItem.hs view
@@ -0,0 +1,6 @@+module Graphics.UI.Gtk.Helpers.MenuItem where++import Graphics.UI.Gtk++menuItemGetLabel :: MenuItemClass self => self -> IO (Maybe Label)+menuItemGetLabel = fmap (fmap castToLabel) . binGetChild
+ src/Graphics/UI/Gtk/Helpers/MessageDialog.hs view
@@ -0,0 +1,10 @@+module Graphics.UI.Gtk.Helpers.MessageDialog where++import Graphics.UI.Gtk+import Control.Monad++popupError :: String -> String -> IO ()+popupError t s = do+ md <- messageDialogNew Nothing [DialogModal] MessageError ButtonsOk s+ set md [ windowTitle := t ]+ void $ dialogRun md >> widgetDestroy md
+ src/Graphics/UI/Gtk/Helpers/ModelViewNotebookSync.hs view
@@ -0,0 +1,60 @@+-- | This module provides functions to alter the view in a notebook and a+-- maybewidget with a notebook inside based on what is selected in a model view+-- (icon view, list view, tree view)+--+-- This pattern appears very often in my programs. I like to use model views to+-- let the user choose what to see (instead of using the tab pages).+module Graphics.UI.Gtk.Helpers.ModelViewNotebookSync where++import Graphics.UI.Gtk+import Graphics.UI.Gtk.Helpers.ModelViewPath+import Graphics.UI.Gtk.Layout.MaybeWidget++-- | Keeps the page of a notebook in sync with the selection in a model view.+-- It uses an auxiliary function to determine which page to select.+modelViewNotebookSync :: (ViewWithPathSelection a c, NotebookClass b)+ => a -> b -> (TreePath -> Int) -> IO()+modelViewNotebookSync modelView notebook syncF = do+ (path, _) <- modelViewGetCursor modelView+ let page = syncF path+ notebookSetCurrentPage notebook page++-- | Keeps the page of a notebook in sync with the selection in a model view.+-- It selects the page with the same number as the element selected in the+-- model view. If there's more than one selection, or if the model view is not+-- flat and the selected element is not at the first level, then it selects nothing.+modelViewNotebookSyncId :: (ViewWithPathSelection a c, NotebookClass b) => a -> b -> IO()+modelViewNotebookSyncId modelView notebook = do+ (path, _) <- modelViewGetCursor modelView+ case path of+ [page] -> notebookSetCurrentPage notebook page+ _ -> return ()++-- | Keeps the page of a notebook inside a maybe widget in sync with the+-- selection in a model view.+--+-- It uses an auxiliary function to determine which page to select. If the function+-- returns Nothing, then the maybe widget is deactivated.+modelViewMaybeNotebookSync :: (ViewWithPathSelection a c, NotebookClass b)+ => a -> MaybeWidget b -> (TreePath -> Maybe Int) -> IO()+modelViewMaybeNotebookSync modelView maybeNotebook syncF = do+ (path, _) <- modelViewGetCursor modelView+ case syncF path of+ Nothing -> set maybeNotebook [ maybeWidgetActivated := False]+ Just p -> do set maybeNotebook [ maybeWidgetActivated := True ]+ notebookSetCurrentPage (maybeWidgetGetWidget maybeNotebook) p++-- | Keeps the page of a notebook inside a maybe widget in sync with the+-- selection in a model view.+--+-- It selects the page with the same number as the element selected in the+-- model view. If there's more than one selection, or if the model view is not+-- flat and the selected element is not at the first level, then it selects nothing.+modelViewMaybeNotebookSyncId :: (ViewWithPathSelection a c, NotebookClass b)+ => a -> MaybeWidget b -> IO()+modelViewMaybeNotebookSyncId modelView maybeNotebook = do+ (path, _) <- modelViewGetCursor modelView+ case path of+ [page] -> do set maybeNotebook [ maybeWidgetActivated := True ]+ notebookSetCurrentPage (maybeWidgetGetWidget maybeNotebook) page+ _ -> set maybeNotebook [ maybeWidgetActivated := False ]
+ src/Graphics/UI/Gtk/Helpers/ModelViewPath.hs view
@@ -0,0 +1,14 @@+{-# LANGUAGE FunctionalDependencies, MultiParamTypeClasses #-}+module Graphics.UI.Gtk.Helpers.ModelViewPath where++import Graphics.UI.Gtk++class ViewWithPathSelection a b | a -> b where+ modelViewGetCursor :: a -> IO (TreePath, Maybe b)++instance ViewWithPathSelection IconView CellRenderer where+ modelViewGetCursor = iconViewGetCursor++instance ViewWithPathSelection TreeView TreeViewColumn where+ modelViewGetCursor = treeViewGetCursor+
+ src/Graphics/UI/Gtk/Helpers/Multiline/TextBuffer.hs view
@@ -0,0 +1,19 @@+-- | Auxiliary functions userful to manipulate TextBuffers+module Graphics.UI.Gtk.Helpers.Multiline.TextBuffer where++-- External imports+import Control.Monad+import Graphics.UI.Gtk++-- | Updates a text buffer only if it's necessary (to avoid extra events)+textBufferUpdateText :: TextBuffer -> String -> IO ()+textBufferUpdateText bf s = do+ tx <- textBufferGetAllText bf True+ when (tx /= s) $ textBufferSetText bf s++-- | Gets all the text from a text buffer+textBufferGetAllText :: TextBuffer -> Bool -> IO String+textBufferGetAllText bf hidden = do+ si <- textBufferGetStartIter bf+ ei <- textBufferGetEndIter bf+ textBufferGetText bf si ei hidden
+ src/Graphics/UI/Gtk/Helpers/Pixbuf.hs view
@@ -0,0 +1,61 @@+module Graphics.UI.Gtk.Helpers.Pixbuf where++import Graphics.UI.Gtk+import Data.Word++safePixbufComposite :: Pixbuf -> Pixbuf+ -> Int -> Int -- position+ -> Int -> Int -- something else?+ -> InterpType+ -> Word8+ -> IO ()+safePixbufComposite pbO pbD x y x' y' interp alpha = do+ w <- pixbufGetWidth pbO+ h <- pixbufGetHeight pbO+ w' <- pixbufGetWidth pbD+ h' <- pixbufGetHeight pbD+ safePixbufComposite' pbO pbD x y x' y' w h w' h' interp alpha++safePixbufComposite' :: Pixbuf -> Pixbuf+ -> Int -> Int -- position+ -> Int -> Int -- something else?+ -> Int -> Int -- original image size+ -> Int -> Int -- destination image size+ -> InterpType+ -> Word8+ -> IO ()+safePixbufComposite' pbO pbD x y x' y' w h w' h' interp alpha+ | x + w < 0 || y + h < 0 || w < 0 || h < 0+ = return ()+ | x < 0 || y < 0+ = do let diffX = if x > 0 then 0 else abs x+ diffY = if y > 0 then 0 else abs y+ newX = if x < 0 then 0 else x+ newY = if y < 0 then 0 else y+ newX' = if x < 0 then 0 else x'+ newY' = if y < 0 then 0 else y'+ pbO' <- pixbufNew ColorspaceRgb True 8 (w - diffX) (h - diffY)+ pixbufCopyArea pbO diffX diffY (w - diffX) (h - diffY) pbO' 0 0+ safePixbufComposite' pbO' pbD+ newX newY+ newX' newY'+ (w - diffX) (h - diffY)+ w' h'+ interp alpha+ | otherwise+ = do let realw = if (x + w) > w' then w' - x else w+ realh = if (y + h) > h' then h' - y else h+ pixbufComposite pbO pbD x y realw realh (fI x') (fI y') 1 1 interp alpha+ where fI = fromIntegral++pixbufNewEmpty :: Int -> Int -> IO Pixbuf+pixbufNewEmpty w h = do+ pb <- pixbufNew ColorspaceRgb True 8 w h+ pixbufFill pb 0 0 0 1+ return pb++pixbufNewWithBGColor :: Bool -> Int -> Int -> (Word8, Word8, Word8) -> IO Pixbuf+pixbufNewWithBGColor alpha w h (r,g,b) = do+ pb <- pixbufNew ColorspaceRgb alpha 8 w h+ pixbufFill pb r g b 255+ return pb
+ src/Graphics/UI/Gtk/Helpers/TreeView.hs view
@@ -0,0 +1,58 @@+module Graphics.UI.Gtk.Helpers.TreeView where++-- External imports+import Control.Monad+import Data.Maybe+import Graphics.UI.Gtk++type TypedTreeView a = (TreeView, ListStore a)++addTextColumn :: (TreeModelClass (model row), TypedTreeModelClass model)+ => TreeView -> model row -> (row -> Maybe String) -> IO()+addTextColumn tv st f = void $ do+ col <- treeViewColumnNew+ renderer <- cellRendererTextNew+ cellLayoutPackStart col renderer True+ cellLayoutSetAttributes col renderer st $ map (cellText :=).maybeToList.f+ treeViewAppendColumn tv col++addPixbufColumn :: (TreeModelClass (model row), TypedTreeModelClass model)+ => TreeView -> model row -> (row -> Maybe Pixbuf) -> IO()+addPixbufColumn tv st f = void $ do+ icon <- treeViewColumnNew+ renderIcon <- cellRendererPixbufNew+ cellLayoutPackStart icon renderIcon True+ cellLayoutSetAttributes icon renderIcon st $ map (cellPixbuf :=).maybeToList.f+ treeViewAppendColumn tv icon++addPixbufTextDoubleColumn :: (TreeModelClass (model row), TypedTreeModelClass model)+ => TreeView -> model row -> (row -> (Maybe Pixbuf, Maybe String)) -> IO()+addPixbufTextDoubleColumn tv st f = void $ do+ col <- treeViewColumnNew++ renderIcon <- cellRendererPixbufNew+ cellLayoutPackStart col renderIcon False+ cellLayoutSetAttributes col renderIcon st $ map (cellPixbuf :=).maybeToList.fst.f++ renderer <- cellRendererTextNew+ cellLayoutPackEnd col renderer True+ cellLayoutSetAttributes col renderer st $ map (cellText :=).maybeToList.snd.f++ treeViewAppendColumn tv col++synchroniseTreeViewAndNotebook :: TreeView -> Notebook -> ([TreePath] -> Maybe Int) -> IO()+synchroniseTreeViewAndNotebook tv nb f = do+ tr <- treeViewGetSelectedPath tv+ maybe (return ()) (\v -> set nb [ notebookPage := v ]) (f tr)++treeViewGetSelectedPath :: TreeViewClass tv => tv -> IO [[Int]]+treeViewGetSelectedPath =+ treeSelectionGetSelectedRows <=< treeViewGetSelection++treeViewGetSelected :: TreeViewClass tv => tv -> ListStore a -> IO (Maybe a)+treeViewGetSelected tv ls = do+ tr <- treeViewGetSelectedPath tv+ case tr of+ [[x]] -> fmap Just $ listStoreGetValue ls x+ _ -> return Nothing+
+ src/Graphics/UI/Gtk/Layout/BackgroundContainer.hs view
@@ -0,0 +1,65 @@+module Graphics.UI.Gtk.Layout.BackgroundContainer where++import Control.Monad.Trans (liftIO)+import Data.IORef+import Graphics.UI.Gtk+import Graphics.UI.Gtk.Gdk.GC+import System.Glib.Types++data BackgroundContainer = BackgroundContainer EventBox (IORef (Maybe Pixbuf))++instance WidgetClass BackgroundContainer+instance ObjectClass BackgroundContainer+instance GObjectClass BackgroundContainer where+ toGObject (BackgroundContainer ev _) = toGObject ev+ unsafeCastGObject ev = (BackgroundContainer (unsafeCastGObject ev) undefined)++instance EventBoxClass BackgroundContainer+instance ContainerClass BackgroundContainer+instance BinClass BackgroundContainer++backgroundContainerNew :: IO BackgroundContainer+backgroundContainerNew = do+ ev <- eventBoxNew+ ref <- newIORef Nothing+ return $ BackgroundContainer ev ref++backgroundContainerNewWithPicture :: FilePath -> IO BackgroundContainer+backgroundContainerNewWithPicture fp = do+ ev <- eventBoxNew+ pb <- pixbufNewFromFile fp+ ref <- newIORef (Just pb)+ let wdgt = BackgroundContainer ev ref+ wdgt `on` exposeEvent $ liftIO (backgroundExpose wdgt) >> return False+ return wdgt++backgroundContainerNewWithPixbuf :: Pixbuf -> IO BackgroundContainer+backgroundContainerNewWithPixbuf pb = do+ ev <- eventBoxNew+ ref <- newIORef (Just pb)+ let wdgt = BackgroundContainer ev ref+ wdgt `on` exposeEvent $ liftIO (backgroundExpose wdgt) >> return False+ return wdgt++backgroundExpose :: BackgroundContainer -> IO ()+backgroundExpose (BackgroundContainer ev ref) = do+ dw <- widgetGetDrawWindow ev+ drawWindowClear dw+ pixbufM <- readIORef ref+ case pixbufM of+ Nothing -> return ()+ Just pb -> do sz@(w,h) <- widgetGetSize ev+ pb' <- pixbufScaleSimple pb w h InterpBilinear+ drawWindowBeginPaintRect dw (Rectangle 0 0 w h)+ gc <- gcNew dw+ drawPixbuf dw gc pb' 0 0 0 0 (-1) (-1) RgbDitherNone (-1) (-1)+ drawWindowEndPaint dw++backgroundSetPicture :: BackgroundContainer -> Maybe FilePath -> IO()+backgroundSetPicture (BackgroundContainer ev ref) fpM = do+ pbM <- maybe (return Nothing) (fmap Just . pixbufNewFromFile) fpM+ writeIORef ref pbM++backgroundSetPixbuf :: BackgroundContainer -> Maybe Pixbuf -> IO()+backgroundSetPixbuf (BackgroundContainer ev ref) pbM =+ writeIORef ref pbM
+ src/Graphics/UI/Gtk/Layout/DummyBin.hs view
@@ -0,0 +1,19 @@+module Graphics.UI.Gtk.Layout.DummyBin where++import Graphics.UI.Gtk+import System.Glib.Types++data DummyBin = DummyBin EventBox++instance WidgetClass DummyBin+instance ObjectClass DummyBin+instance GObjectClass DummyBin where+ toGObject (DummyBin x) = toGObject x+ unsafeCastGObject = DummyBin . unsafeCastGObject++dummyBinNew :: IO DummyBin+dummyBinNew = do+ evbox <- eventBoxNew+ return $ DummyBin evbox++instance ContainerClass DummyBin where
+ src/Graphics/UI/Gtk/Layout/EitherWidget.hs view
@@ -0,0 +1,41 @@+module Graphics.UI.Gtk.Layout.EitherWidget where++import Control.Monad+import Data.IORef+import Graphics.UI.Gtk+import System.Glib.Types++data EitherWidget a b = EitherWidget Notebook (IORef EitherWidgetParams)+type EitherWidgetParams = Bool++instance WidgetClass (EitherWidget a b)+instance ObjectClass (EitherWidget a b)+instance GObjectClass (EitherWidget a b) where+ toGObject (EitherWidget nb _) = toGObject nb+ unsafeCastGObject o = EitherWidget (unsafeCastGObject o) undefined+ +eitherWidgetNew :: (WidgetClass a, WidgetClass b) => a -> b -> IO (EitherWidget a b)+eitherWidgetNew wL wR = do+ nb <- notebookNew+ _ <- notebookAppendPage nb wL ""+ _ <- notebookAppendPage nb wR ""+ notebookSetShowTabs nb False+ params <- newIORef True+ return $ EitherWidget nb params++eitherWidgetLeftActivated :: Attr (EitherWidget a b) Bool+eitherWidgetLeftActivated = newAttr getter setter+ where getter (EitherWidget _ paramsR) = readIORef paramsR+ setter (EitherWidget nb paramsR) v = do+ params <- readIORef paramsR+ when (v /= params) $ do let upd = if v then 0 else 1+ notebookSetCurrentPage nb upd+ writeIORef paramsR v++eitherWidgetRightActivated :: Attr (EitherWidget a b) Bool+eitherWidgetRightActivated = newAttr getter setter+ where getter w = fmap not $ get w eitherWidgetLeftActivated+ setter w v = set w [ eitherWidgetLeftActivated := not v ]++eitherWidgetToggle :: EitherWidget a b -> IO()+eitherWidgetToggle w = set w [ eitherWidgetLeftActivated :~ not ]
+ src/Graphics/UI/Gtk/Layout/MaybeWidget.hs view
@@ -0,0 +1,45 @@+module Graphics.UI.Gtk.Layout.MaybeWidget where++import Control.Monad+import Data.IORef+import Graphics.UI.Gtk+import System.Glib.Types++data MaybeWidget a = MaybeWidget Notebook a Label (IORef MaybeWidgetParams)+type MaybeWidgetParams = Bool++instance WidgetClass (MaybeWidget a)+instance ObjectClass (MaybeWidget a)+instance GObjectClass (MaybeWidget a) where+ toGObject (MaybeWidget nb _ _ _) = toGObject nb+ unsafeCastGObject o = MaybeWidget (unsafeCastGObject o) undefined undefined undefined+ +maybeWidgetNewWithLabel :: (WidgetClass a) => a -> Maybe String -> IO (MaybeWidget a)+maybeWidgetNewWithLabel w label = do+ lblW <- labelNew label+ nb <- notebookNew+ _ <- notebookAppendPage nb lblW ""+ _ <- notebookAppendPage nb w ""+ notebookSetShowTabs nb False+ params <- newIORef False+ return $ MaybeWidget nb w lblW params++maybeWidgetGetWidget :: MaybeWidget a -> a+maybeWidgetGetWidget (MaybeWidget _ a _ _) = a+ +maybeWidgetLabelText :: Attr (MaybeWidget a) String+maybeWidgetLabelText = newAttr getter setter+ where getter (MaybeWidget _ _ lblW _) = get lblW labelLabel+ setter (MaybeWidget _ _ lblW _) s = set lblW [ labelLabel := s ]++maybeWidgetActivated :: Attr (MaybeWidget a) Bool+maybeWidgetActivated = newAttr getter setter+ where getter (MaybeWidget _ _ _ paramsR) = readIORef paramsR+ setter (MaybeWidget nb _ _ paramsR) v = do+ params <- readIORef paramsR+ when (v /= params) $ do let upd = if v then 1 else 0+ notebookSetCurrentPage nb upd+ writeIORef paramsR v++maybeWidgetToggle :: MaybeWidget a -> IO()+maybeWidgetToggle w = set w [ maybeWidgetActivated :~ not ]
+ src/System/Application.hs view
@@ -0,0 +1,17 @@+module System.Application where++-- External imports+import Control.Monad+-- import Control.Monad.Extra+import System.GIO.File.AppInfo+import System.Process++-- FIXME: This uses runProcess instead of appInfoLaunchUris because+-- the latter segfaults in my machine+openUrlBySystemTool :: String -> IO Bool+openUrlBySystemTool url = do+ infos <- appInfoGetAllForType "text/html"+ unless (null infos) $ void $ do+ let exe = appInfoGetExecutable $ head infos+ runProcess exe [url] Nothing Nothing Nothing Nothing Nothing+ return (not (null infos))