vision-0.0.4.0: src/Utils.hs
-- -*-haskell-*-
-- Vision (for the Voice): an XMMS2 client.
--
-- Author: Oleg Belozeorov
-- Created: 17 Jun. 2010
--
-- Copyright (C) 2010 Oleg Belozeorov
--
-- This program is free software; you can redistribute it and/or
-- modify it under the terms of the GNU General Public License as
-- published by the Free Software Foundation; either version 3 of
-- the License, or (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-- General Public License for more details.
--
module Utils
( HandlerMVar
, makeHandlerMVar
, onHandler
, encodeURL
, decodeURL
, trd
, trim
, bracket_
, catchResult
, setupTreeViewPopup
, dialogAddButtonCR
, withJust
, withSignalBlocked
, hideOnDeleteEvent
, eqBy
, tryModifyMVar_
, EntryIcon (..)
, onIconPress
, secondaryIconSensitive
) where
import Prelude hiding (catch)
import Control.Monad.CatchIO hiding (Handler)
import Control.Monad
import Control.Monad.Trans
import Control.Applicative
import Control.Concurrent.MVar
import Data.Char hiding (Control)
import Codec.Binary.UTF8.String
import Foreign.C.Types
import Graphics.UI.Gtk
import System.Glib.Properties
import XMMS2.Client
import Handler
type HandlerMVar a = MVar (Handler IO a)
makeHandlerMVar = newMVar make
onHandler = modifyMVar
encodeURL = encodeURL' . encodeString
encodeURL' [] = []
encodeURL' (' ':cs) = '+' : encodeURL' cs
encodeURL' (c:cs)
| goodChar c =
c : encodeURL' cs
| otherwise =
let code = ord c
c1 = intToDigit $ code `div` 16
c2 = intToDigit $ code `rem` 16 in
'%' : c1 : c2 : encodeURL' cs
goodChar ':' = True
goodChar '/' = True
goodChar '-' = True
goodChar '.' = True
goodChar '_' = True
goodChar c = isAsciiUpper c || isAsciiLower c || isDigit c
decodeURL = decodeString . decodeURL'
decodeURL' [] = []
decodeURL' ('+' : cs) = ' ' : decodeURL' cs
decodeURL' ('%' : cs) = let (c, cs') = decodeByte cs in c : decodeURL' cs'
decodeURL' (c : cs) = c : decodeURL' cs
decodeByte (d1 : d0 : cs)
| isHexDigit d1 && isHexDigit d0 = (chr $ digitToInt d1 * 16 + digitToInt d0, cs)
decodeByte _ = error "invalid URL"
trd (_, _, c) = c
trim = f . f where f = reverse . dropWhile isSpace
bracket_ f g = bracket f (const g) . const
catchResult def conv =
(conv <$> result) `catch` \(_ :: XMMSException) -> return def
setupTreeViewPopup view popup = do
view `on` popupMenuSignal $ (menuPopup popup Nothing >> return True)
sel <- treeViewGetSelection view
selMode <- treeSelectionGetMode sel
let cond =
case selMode of
SelectionMultiple ->
(2 >) <$> liftIO (treeSelectionCountSelectedRows sel)
SelectionNone ->
return False
_ ->
return True
setCursor = do
doIt <- cond
when doIt $ do
(x, y) <- eventCoordinates
maybePath <- liftIO $ treeViewGetPathAtPos view (round x, round y)
case maybePath of
Just (path, _, _) ->
liftIO $ treeViewSetCursor view path Nothing
Nothing ->
return ()
view `on` buttonPressEvent $ tryEvent $ do
RightButton <- eventButton
SingleClick <- eventClick
stamp <- eventTime
setCursor
liftIO $ menuPopup popup $ Just (RightButton, stamp)
dialogAddButtonCR dialog label response = do
button <- dialogAddButton dialog label response
dialog `on` keyPressEvent $ tryEvent $ do
"Return" <- eventKeyName
[Control] <- eventModifier
liftIO $ do
doIt <- button `get` widgetSensitive
when doIt $ do
widgetActivate button
return ()
fmaybe :: b -> Maybe a -> (a -> b) -> b
fmaybe = flip . maybe
withJust :: Monad m => Maybe a -> (a -> m b) -> m ()
withJust m f = fmaybe (return ()) m $ \a -> f a >> return ()
withSignalBlocked s f =
block $ do
signalBlock s
r <- unblock f `onException` signalUnblock s
signalUnblock s
return r
hideOnDeleteEvent window =
window `on` deleteEvent $ do
liftIO $ widgetHide window
return True
eqBy f a b = f a == f b
{-# INLINE tryModifyMVar_ #-}
tryModifyMVar_ :: MVar a -> (a -> IO a) -> IO ()
tryModifyMVar_ m io =
block $ do
maybeA <- tryTakeMVar m
case maybeA of
Just a -> do
a' <- unblock (io a) `onException` putMVar m a
putMVar m a'
Nothing ->
return ()
deriving instance MonadCatchIO (ResultM c a)
data EntryIcon
= PrimaryIcon
| SecondaryIcon
deriving (Enum, Eq)
onIconPress
:: EntryClass ec
=> ec
-> (EntryIcon -> IO ())
-> IO (ConnectId ec)
onIconPress entry handler =
connectGeneric "icon-press" False entry $ \_ pos _ ->
handler $ toEnum $ fromIntegral (pos :: CInt)
secondaryIconSensitive =
newAttrFromBoolProperty "secondary-icon-sensitive"