vision-0.0.5.0: src/Properties/Manager.hs
-- -*-haskell-*-
-- Vision (for the Voice): an XMMS2 client.
--
-- Author: Oleg Belozeorov
-- Created: 5 Jul. 2010
--
-- Copyright (C) 2010, 2011 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.
--
{-# LANGUAGE TupleSections #-}
module Properties.Manager
( initPropertyManager
, showPropertyManager
) where
import Control.Applicative
import Control.Monad
import Data.IORef
import qualified Data.Map as Map
import Data.Set (Set)
import qualified Data.Set as Set
import Data.Ord
import Data.Char
import System.IO.Unsafe
import Graphics.UI.Gtk
import Utils
import Compound
import Context
import UI
import Editor
import Properties.Property
import Properties.Model
data Manager
= Manager { pManager :: EditorDialog PropertyManager }
manager = pManager context
initPropertyManager = do
manager <- unsafeInterleaveIO $ makeEditorDialog
[(stockApply, ResponseApply)]
makePropertyManager $ \m -> do
let outerw = outer m
windowSetTitle outerw "Manage properties"
windowSetDefaultSize outerw 500 400
return $ augmentContext
Manager { pManager = manager }
showPropertyManager =
runEditorDialog manager getProperties setProperties False window
data PropertyManager
= PropertyManager { pStore :: ListStore Property
, pView :: TreeView
, pBox :: VBox
, pChanged :: IORef Bool
, pNames :: IORef (Set String)
}
instance CompoundWidget PropertyManager where
type Outer PropertyManager = VBox
outer = pBox
instance EditorWidget PropertyManager where
type Data PropertyManager = [Property]
getData = propertyManagerGetData
setData = propertyManagerSetData
clearData = propertyManagerClearData
setupView = propertyManagerSetupView
focusView = propertyManagerFocusView
getState = propertyManagerGetState
resetModified = propertyManagerResetModified
propertyManagerGetData =
listStoreToList . pStore
propertyManagerSetData pm props = do
let store = pStore pm
listStoreClear store
writeIORef (pNames pm) =<<
foldM (\names prop -> do
listStoreAppend store prop
return $ Set.insert (propName prop) names
) Set.empty props
propertyManagerClearData pm = do
listStoreClear $ pStore pm
writeIORef (pNames pm) Set.empty
propertyManagerSetupView pm =
treeViewSetCursor (pView pm) [0] Nothing
propertyManagerFocusView =
widgetGrabFocus . pView
propertyManagerGetState =
liftM (True, ) . readIORef . pChanged
propertyManagerResetModified =
flip writeIORef False . pChanged
makePropertyManager parent notify = do
names <- newIORef Set.empty
store <- listStoreNewDND [] Nothing Nothing
sorted <- treeModelSortNewWithModel store
let getByIter iter = do
[n] <- treeModelGetPath store iter
listStoreGetValue store n
compBy :: Ord a => (Property -> a) -> TreeIter -> TreeIter -> IO Ordering
compBy f a b =
comparing f <$> getByIter a <*> getByIter b
treeSortableSetDefaultSortFunc sorted $ Just $ compBy (map toLower . propName)
treeSortableSetSortFunc sorted 0 $ compBy (map toLower . propName)
treeSortableSetSortFunc sorted 1 $ compBy (map toLower . propKey)
treeSortableSetSortFunc sorted 2 $ compBy (fromEnum . propType)
treeSortableSetSortFunc sorted 3 $ compBy (fromEnum . propReadOnly)
treeSortableSetSortFunc sorted 4 $ compBy (fromEnum . propCustom)
view <- treeViewNewWithModel sorted
treeViewSetRulesHint view True
let showType p =
case propType p of
PropertyInt -> "integer"
PropertyString -> "string"
showBool acc p =
if acc p then "yes" else "no"
addColumn title id acc = do
column <- treeViewColumnNew
treeViewColumnSetSortColumnId column id
treeViewAppendColumn view column
treeViewColumnSetTitle column title
cell <- cellRendererTextNew
treeViewColumnPackStart column cell True
cellLayoutSetAttributes column cell store
(\p -> [ cellText := acc p ])
addColumn "Name" 0 propName
addColumn "Key" 1 propKey
addColumn "Type" 2 showType
addColumn "Read-only" 3 $ showBool propReadOnly
addColumn "Custom" 4 $ showBool propCustom
scroll <- scrolledWindowNew Nothing Nothing
scrolledWindowSetPolicy scroll PolicyAutomatic PolicyAutomatic
scrolledWindowSetShadowType scroll ShadowIn
containerAdd scroll view
changed <- newIORef False
let setChanged = do
writeIORef changed True
notify
addProperty prop = do
listStoreAppend store prop
modifyIORef names $ Set.insert (propName prop)
setChanged
return ()
exists name = Set.member name <$> readIORef names
edlg <- unsafeInterleaveIO $ makeEditorDialog []
(makePropertyEntry exists) (const $ return ())
addB <- buttonNewFromStock stockAdd
addB `onClicked`
runEditorDialog edlg (return nullProperty) addProperty True parent
delB <- buttonNewFromStock stockRemove
delB `onClicked` do
(path, _) <- treeViewGetCursor view
case path of
[n] -> do
[n'] <- treeModelSortConvertPathToChildPath sorted path
prop <- listStoreGetValue store n'
when (propCustom prop) $ do
listStoreRemove store n'
modifyIORef names $ Set.delete (propName prop)
s <- listStoreGetSize store
unless (s < 1) $
treeViewSetCursor view [min (s - 1) n] Nothing
setChanged
_ ->
return ()
view `on` cursorChanged $ do
(path, _) <- treeViewGetCursor view
case path of
[_] -> do
[n'] <- treeModelSortConvertPathToChildPath sorted path
prop <- listStoreGetValue store n'
widgetSetSensitive delB $ propCustom prop
_ ->
widgetSetSensitive delB False
bbox <- hBoxNew True 5
boxPackStartDefaults bbox addB
boxPackStartDefaults bbox delB
vbox <- vBoxNew False 5
containerSetBorderWidth vbox 7
boxPackStart vbox scroll PackGrow 0
boxPackStart vbox bbox PackNatural 0
return PropertyManager { pStore = store
, pView = view
, pBox = vbox
, pChanged = changed
, pNames = names
}
data PropertyEntry
= PropertyEntry
{ eTable :: Table
, eName :: Entry
, eKey :: Entry
, eType :: ComboBox
, eRO :: ComboBox
, eExists :: String -> IO Bool
}
instance CompoundWidget PropertyEntry where
type Outer PropertyEntry = Table
outer = eTable
instance EditorWidget PropertyEntry where
type Data PropertyEntry = Property
setData = propertyEntrySetData
getData = propertyEntryGetData
focusView = propertyEntryFocusView
getState = propertyEntryGetState
resetModified = const $ return ()
propertyEntrySetData e p = do
entrySetText (eName e) (propName p)
entrySetText (eKey e) (propKey p)
comboSet (eType e) Nothing (propType p)
comboSet (eRO e) Nothing (propReadOnly p)
propertyEntryGetData e = do
pname <- entryGetText $ eName e
pkey <- entryGetText $ eKey e
ptype <- comboGet $ eType e
pro <- comboGet $ eRO e
return Property { propName = trim pname
, propKey = trim pkey
, propType = ptype
, propReadOnly = pro
, propCustom = True
, propReadValue = Nothing
, propShowValue = Nothing
}
propertyEntryFocusView =
widgetGrabFocus . eName
propertyEntryGetState e = do
name <- trim <$> entryGetText (eName e)
key <- trim <$> entryGetText (eKey e)
(, True) <$> propertyEntryValid (eExists e) name key
propertyEntryValid exists name key = do
e <- exists name
return . not $ e || null name || null key
makePropertyEntry exists _ notify = do
table <- tableNew 4 2 False
containerSetBorderWidth table 7
tableSetColSpacings table 15
tableSetRowSpacings table 5
let addPair :: WidgetClass w => String -> w -> Int -> IO ()
addPair m w b = do
l <- labelNewWithMnemonic m
miscSetAlignment l 0.0 0.5
labelSetMnemonicWidget l w
tableAttach table l 0 1 b (b + 1) [Fill] [] 0 0
tableAttach table w 1 2 b (b + 1) [Expand, Fill] [] 0 0
nameE <- entryNew
addPair "_Name" nameE 0
keyE <- entryNew
addPair "_Key" keyE 1
typeC <- comboBoxNewText
comboBoxInsertText typeC (fromEnum PropertyString) "string"
comboBoxInsertText typeC (fromEnum PropertyInt) "integer"
typeRef <- newIORef PropertyString
typeCid <- setupCombo typeC typeRef
addPair "_Type" typeC 2
roC <- comboBoxNewText
comboBoxInsertText roC (fromEnum False) "no"
comboBoxInsertText roC (fromEnum True) "yes"
roRef <- newIORef False
roCid <- setupCombo roC roRef
addPair "_Read-only" roC 3
let check = do
notify
key <- trim <$> entryGetText keyE
case Map.lookup key builtinPropertyMap of
Just prop -> do
comboSet typeC (Just typeCid) $ propType prop
widgetSetSensitive typeC False
comboSet roC (Just roCid) $ propReadOnly prop
widgetSetSensitive roC False
Nothing -> do
comboSet typeC (Just typeCid) =<< readIORef typeRef
widgetSetSensitive typeC True
comboSet roC (Just roCid) =<< readIORef roRef
widgetSetSensitive roC True
checkInsert str pos = do
check
return $ length str + pos
checkDelete _ _ = check
nameE `afterInsertText` checkInsert
nameE `afterDeleteText` checkDelete
keyE `afterInsertText` checkInsert
keyE `afterDeleteText` checkDelete
return PropertyEntry { eTable = table
, eName = nameE
, eKey = keyE
, eType = typeC
, eRO = roC
, eExists = exists
}
nullProperty =
Property { propName = ""
, propKey = ""
, propType = PropertyString
, propReadOnly = False
, propCustom = True
, propReadValue = Nothing
, propShowValue = Nothing
}
comboGet combo =
toEnum <$> comboBoxGetActive combo
comboSet ::
Enum a =>
ComboBox ->
Maybe (ConnectId ComboBox) ->
a ->
IO ()
comboSet combo cid =
maybe id withSignalBlocked cid . comboBoxSetActive combo . fromEnum
setupCombo combo ref =
combo `on` changed $ (writeIORef ref =<< comboGet combo)