packages feed

coin-1.0: src/Coin/UI/Widgets/MultiList.hs

{-
 *  Programmer:	Piotr Borek
 *  E-mail:     piotrborek@op.pl
 *  Copyright 2015 Piotr Borek
 *
 *  Distributed under the terms of the GPL (GNU Public License)
 *
 *  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 2 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.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-}

module Coin.UI.Widgets.MultiList (
    MultiListWidget,
    multiListNew,
    multiListButtonNew,
    multiListButtonShow,
    multiListButtonOnActivated,
    multiListOptionClear,
    multiListOptionAdd,
    multiListOptionAdd',
    multiListOptionSetCursor,
    multiListOptionGetCursor,
    multiListOptionToList
) where

import qualified System.Glib.Types as Gtk
import qualified Graphics.UI.Gtk as Gtk
import Graphics.UI.Gtk ( AttrOp(..) )

import Control.Monad
import Data.IORef

import Coin.UI.Widgets.SimpleListView
import Coin.UI.Utils.CssUtils

data MultiListData = MultiListData {
    multiListButton      :: Gtk.Button,
    multiListWindow      :: Gtk.ScrolledWindow,
    multiListView        :: SimpleListView,
    multiListConnections :: IORef [Gtk.ConnectId SimpleListView]
}

data MultiListWidget = MultiListWidget {
    multiListContainer :: Gtk.VBox,
    multiListWidgets   :: IORef [MultiListData],
    multiListCounter   :: IORef Int
}

instance Gtk.GObjectClass MultiListWidget where
    toGObject = Gtk.toGObject . multiListContainer
    unsafeCastGObject = undefined

instance Gtk.WidgetClass MultiListWidget

multiListNew :: IO MultiListWidget
multiListNew = do
    box <- Gtk.vBoxNew False 0
    widgets <- newIORef []
    counter <- newIORef 0
    return $ MultiListWidget box widgets counter

multiListButtonNew :: MultiListWidget -> String -> IO Int
multiListButtonNew multiList text = do
    let buttonCss =
           [ "button {"
           , "    padding: 5px;"
           , "    border-radius: 8px;"
           , "    font-weight: bold;"
           , "}"
           ]

    index <- readIORef $ multiListCounter multiList

    button <- Gtk.buttonNew
    Gtk.set button [ Gtk.buttonLabel := text, Gtk.buttonXalign := 0.0, cssStyle := buttonCss]
    void $ Gtk.on button Gtk.buttonActivated $ multiListButtonShow multiList index
    listView <- simpleListViewNew []

    let vbox = multiListContainer multiList
    Gtk.boxPackStart vbox button Gtk.PackNatural 0

    scrolledWindow <- Gtk.scrolledWindowNew Nothing Nothing
    Gtk.scrolledWindowAddWithViewport scrolledWindow listView

    Gtk.boxPackStart vbox scrolledWindow Gtk.PackGrow 0

    connections <- newIORef []
    modifyIORef' (multiListWidgets multiList) $ \w ->
        w ++ [MultiListData button scrolledWindow listView connections]

    modifyIORef' (multiListCounter multiList) (+1)

    return index

multiListButtonShow :: MultiListWidget -> Int -> IO ()
multiListButtonShow multiList index = do
    widgets <- readIORef $ multiListWidgets multiList
    forM_ widgets $ \d -> Gtk.widgetHide $ multiListWindow d
    let d = widgets !! index
    Gtk.widgetShow $ multiListWindow d

multiListButtonOnActivated :: MultiListWidget -> Int -> IO () -> IO ()
multiListButtonOnActivated multiList index action = do
    widgets <- readIORef $ multiListWidgets multiList
    let d = widgets !! index
    void $ Gtk.on (multiListButton d) Gtk.buttonActivated action

multiListOptionClear :: MultiListWidget -> Int -> IO ()
multiListOptionClear multiList index = do
    widgets <- readIORef $ multiListWidgets multiList
    let d = widgets !! index

    connections <- readIORef $ multiListConnections d
    forM_ connections Gtk.signalDisconnect
    modifyIORef' (multiListConnections d) $ const []

    simpleListViewClear $ multiListView d

multiListOptionAdd' :: MultiListWidget -> Int -> [(String, IO ())] -> IO ()
multiListOptionAdd' multiList index tuples =
    forM_ tuples $ \(text, action) -> multiListOptionAdd multiList index text action

multiListOptionAdd :: MultiListWidget -> Int -> String -> IO () -> IO ()
multiListOptionAdd multiList index text action = do
    widgets <- readIORef $ multiListWidgets multiList
    let d = widgets !! index
    let list = multiListView d
    listIndex <- simpleListViewAppend list text
    connID <- Gtk.on list Gtk.cursorChanged $ do
        (path, _) <- Gtk.treeViewGetCursor list
        when (path == [listIndex]) $ do
            forM_ widgets $ \dta ->
                when (list /= multiListView dta) $ do
                    selection <- Gtk.treeViewGetSelection $ multiListView dta
                    Gtk.treeViewSetCursor (multiListView dta) [] Nothing
                    Gtk.treeSelectionUnselectAll selection
            action

    modifyIORef' (multiListConnections d) (connID:)

multiListOptionSetCursor :: MultiListWidget -> Int -> Int -> IO ()
multiListOptionSetCursor multiList buttonIndex listIndex = do
    widgets <- readIORef $ multiListWidgets multiList
    let d = widgets !! buttonIndex
    let list = multiListView d
    multiListButtonShow multiList buttonIndex
    Gtk.treeViewSetCursor list [listIndex] Nothing

multiListOptionGetCursor :: MultiListWidget -> IO (Maybe (Int, Int))
multiListOptionGetCursor multiList = do
    widgets <- readIORef $ multiListWidgets multiList

    foldM (\rval (idx, widget) ->
               case rval of
                   Just _  -> return rval
                   Nothing -> do
                       (path, _) <- Gtk.treeViewGetCursor $ multiListView widget
                       case path of
                           [path'] -> return $ Just (idx, path')
                           _       -> return Nothing
          )
          Nothing
          (zip [0..] widgets)

multiListOptionToList :: MultiListWidget -> Int -> IO [String]
multiListOptionToList multiList index = do
    widgets <- readIORef $ multiListWidgets multiList
    let d = widgets !! index
    simpleListViewToList $ multiListView d