packages feed

vision-0.0.5.0: src/Playlist/Format/Config.hs

-- -*-haskell-*-
--  Vision (for the Voice): an XMMS2 client.
--
--  Author:  Oleg Belozeorov
--  Created: 1 Jul. 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.
--

{-# LANGUAGE TupleSections #-}
{-# OPTIONS_GHC -fno-warn-incomplete-patterns #-}

module Playlist.Format.Config
  ( FormatView
  , makePlaylistFormatView
  ) where

import Control.Monad

import Data.IORef

import System.IO.Unsafe

import Graphics.UI.Gtk

import Utils
import Compound
import Editor


data FormatView
  = FormatView { fBox          :: HBox
               , fStore        :: ListStore String
               , fView         :: TreeView
               , fChanged      :: IORef Bool
               , fCids         :: [ConnectId (ListStore String)]
               }

instance CompoundWidget FormatView where
  type Outer FormatView = HBox
  outer = fBox

instance EditorWidget FormatView where
  type Data FormatView = [String]
  setData       = formatViewSetData
  getData       = formatViewGetData
  clearData     = formatViewClearData
  setupView     = formatViewSetupView
  focusView     = formatViewFocusView
  getState      = formatViewGetState
  resetModified = formatViewResetModified

formatViewSetData f d =
  withoutChanged f $ do
    let store = fStore f
    listStoreClear store
    mapM_ (listStoreAppend store) d

formatViewGetData =
  listStoreToList . fStore

formatViewClearData f =
  withoutChanged f . listStoreClear $ fStore f

formatViewSetupView f =
  treeViewSetCursor (fView f) [0] Nothing

formatViewFocusView =
  widgetGrabFocus . fView

formatViewGetState =
  liftM (True, ) . readIORef . fChanged

formatViewResetModified =
  flip writeIORef False . fChanged

withoutChanged FormatView { fCids = cids } =
  bracket_ (mapM_ signalBlock cids) (mapM_ signalUnblock cids)

makePlaylistFormatView parent onChanged = do
  box <- hBoxNew False 5
  containerSetBorderWidth box 7

  bbox <- vButtonBoxNew
  buttonBoxSetLayout bbox ButtonboxStart
  boxSetSpacing bbox 5
  boxPackEnd box bbox PackNatural 0

  scroll <- scrolledWindowNew Nothing Nothing
  scrolledWindowSetPolicy scroll PolicyAutomatic PolicyAutomatic
  scrolledWindowSetShadowType scroll ShadowIn
  boxPackStartDefaults box scroll

  let mkButton m = do
        button <- buttonNewWithMnemonic m
        widgetSetCanFocus button False
        containerAdd bbox button
        return button

  addB    <- mkButton "_Add"
  editB   <- mkButton "_Edit"
  deleteB <- mkButton "_Delete"

  changedRef <- newIORef False
  store      <- listStoreNew []

  view  <- treeViewNewWithModel store
  treeViewSetRulesHint view True
  treeViewSetHeadersVisible view False
  treeViewSetReorderable view True
  containerAdd scroll view

  column <- treeViewColumnNew
  treeViewAppendColumn view column

  cell <- cellRendererTextNew
  treeViewColumnPackStart column cell True
  cellLayoutSetAttributes column cell store $ \t -> [ cellText := t ]

  sel <- treeViewGetSelection view
  treeSelectionSetMode sel SelectionBrowse
  sel `onSelectionChanged` do
    n <- treeSelectionCountSelectedRows sel
    let enable = n > 0
    widgetSetSensitive editB enable
    widgetSetSensitive deleteB enable

  let handleChange path _ = do
        treeViewSetCursor view path Nothing
        handleChange'
      handleChange' = do
        writeIORef changedRef True
        onChanged

  insId <- store `on` rowInserted $ handleChange
  chgId <- store `on` rowChanged  $ handleChange
  delId <- store `on` rowDeleted  $ \[n] -> do
    size <- listStoreGetSize store
    case size of
      0 -> return ()
      k -> treeViewSetCursor view [if k > n then n else k - 1] Nothing
    handleChange'

  editor <- unsafeInterleaveIO $ makeEditorDialog []
            makeFormatEditor $ \fe -> do
    let outerw = outer fe
    windowSetTitle outerw "Edit format"
    windowSetDefaultSize outerw 500 400

  let startEditing pos =
        runEditorDialog editor
        (case pos of
            Just pos -> listStoreGetValue store pos
            Nothing  -> return "")
        (\text -> case pos of
            Just pos -> do
              oldText <- listStoreGetValue store pos
              unless (text == oldText) $
                listStoreSetValue store pos text
            Nothing  -> do
              listStoreAppend store text
              return ())
        True parent

  view `onRowActivated` \[n] _ -> startEditing $ Just n
  addB    `onClicked` startEditing Nothing
  editB   `onClicked` do
    ([n], _) <- treeViewGetCursor view
    startEditing $ Just n
  deleteB `onClicked` do
    ([n], _) <- treeViewGetCursor view
    listStoreRemove store n

  return FormatView { fBox     = box
                    , fStore   = store
                    , fView    = view
                    , fChanged = changedRef
                    , fCids    = [insId, chgId, delId]
                    }


data FormatEditor
  = FormatEditor
    { eScroll :: ScrolledWindow
    , eBuff   :: TextBuffer
    , eView   :: TextView
    , eCid    :: ConnectId TextBuffer
    }

instance CompoundWidget FormatEditor where
  type Outer FormatEditor = ScrolledWindow
  outer = eScroll

instance EditorWidget FormatEditor where
  type Data FormatEditor = String
  setData       = formatEditorSetData
  getData       = formatEditorGetData
  clearData     = formatEditorClearData
  setupView     = formatEditorSetupView
  focusView     = formatEditorFocusView
  getState      = formatEditorGetState
  resetModified = formatEditorResetModified

formatEditorSetData =
  textBufferSetText . eBuff

formatEditorGetData FormatEditor { eBuff = buff } = do
  start <- textBufferGetStartIter buff
  end   <- textBufferGetEndIter buff
  textBufferGetText buff start end False

formatEditorClearData =
  flip textBufferSetText "" . eBuff

formatEditorSetupView FormatEditor { eBuff = buff } = do
  start <- textBufferGetStartIter buff
  textBufferPlaceCursor buff start

formatEditorFocusView =
  widgetGrabFocus . eView

formatEditorGetState =
  liftM (True, ) . textBufferGetModified . eBuff

formatEditorResetModified e =
  withSignalBlocked (eCid e) $ textBufferSetModified (eBuff e) False

makeFormatEditor _ onStateChanged = do
  buff <- textBufferNew Nothing
  cid <- buff `on` modifiedChanged $ onStateChanged

  view <- textViewNewWithBuffer buff

  scroll <- scrolledWindowNew Nothing Nothing
  scrolledWindowSetPolicy scroll PolicyAutomatic PolicyAutomatic
  scrolledWindowSetShadowType scroll ShadowIn
  containerSetBorderWidth scroll 7
  containerAdd scroll view

  return FormatEditor { eScroll = scroll
                      , eBuff   = buff
                      , eView   = view
                      , eCid    = cid
                      }