packages feed

vision-0.0.2.4: src/Location/Model.hs

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

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

module Location.Model
  ( initModel
  , Item (..)
  , makeItem
  , locationStore
  , sortModel
  , getCurrentLocation
  , onLocation
  , LocationChange (..)
  , updateLocation
  , canGo
  , itemByIter
  , itemByPath
  , setSortOrder
  , getSortOrder
  ) where

import Control.Concurrent.MVar
import Control.Applicative

import Data.List
import Data.Ord

import Graphics.UI.Gtk

import XMMS2.Client

import Context
import Utils
import Handler


data Location
  = Location { lCurrent :: String
             , lBack    :: [String]
             , lForward :: [String]
             }

makeLocation =
  Location { lCurrent = ""
           , lBack    = []
           , lForward = []
           }

data LocationChange
  = Back
  | Forward
  | Go String
  | Refresh
  | Up

changeLocation Back l =
  case lBack l of
    (u : us) ->
      (l { lCurrent = u
         , lBack    = us
         , lForward = lCurrent l : lForward l
         }, Just u)
    _ ->
      (l, Nothing)
changeLocation Forward l =
  case lForward l of
    (u : us) ->
      (l { lCurrent = u
         , lBack    = lCurrent l : lBack l
         , lForward = us
         }, Just u)
    _ ->
      (l, Nothing)
changeLocation (Go u) l =
  (l { lCurrent = u
     , lBack    = case lCurrent l of
         [] -> lBack l
         u' -> u' : lBack l
     , lForward = []
     }, Just u)
changeLocation Refresh l =
  (l, Just $ lCurrent l)
changeLocation Up l =
  let u = reverse . dropWhile (/= '/') . tail . reverse $ lCurrent l
  in changeLocation (Go u) l


data State
  = State { sLocation :: Location
          , sOrder    :: SortType
          }

makeState order =
  State { sLocation = makeLocation
        , sOrder    = order
        }

data Item
  = Item { iName  :: String
         , iPath  :: String
         , iIsDir :: Bool
         }

makeItem x =
  Item { iName  = name
       , iPath  = path
       , iIsDir = entryIsDir x
       }
  where name = last $ split path
        path = decodeURL $ entryPath x

compareItems SortDescending a b
  | iIsDir a == iIsDir b = comparing iName b a
  | iIsDir a             = LT
  | iIsDir b             = GT
compareItems SortAscending a b
  | iIsDir a == iIsDir b = comparing iName a b
  | iIsDir a             = LT
  | iIsDir b             = GT


data Model
  = Model { mState      :: MVar State
          , mStore      :: ListStore Item
          , mSort       :: TypedTreeModelSort Item
          , mOnLocation :: HandlerMVar ()
          }

locationStore = mStore context
sortModel = mSort context
onLocation = onHandler $ mOnLocation context
state = mState context

getCurrentLocation =
  withMVar state $ return . lCurrent . sLocation

updateLocation change = do
  r <- modifyMVar state $ \s -> do
    let (l, u) = changeLocation change $ sLocation s
    return (s { sLocation = l }, u)
  onLocation $ invoke ()
  return r

canGo = withMVar state $ \s ->
  let Location { lCurrent = c
               , lBack    = b
               , lForward = f
               } = sLocation s
  in return (not $ null b,
             not $ null f,
             not $ null c || isSuffixOf "//" c,
             not $ null c)


initModel order = do
  context <- initContext order
  let ?context = context

  setSortOrder order

  return ?context


initContext order = do
  state      <- newMVar $ makeState order
  store      <- listStoreNewDND [] Nothing Nothing
  sort       <- treeModelSortNewWithModel store
  onLocation <- makeHandlerMVar
  return $ augmentContext
    Model { mState      = state
          , mStore      = store
          , mSort       = sort
          , mOnLocation = onLocation
          }

split [] =  []
split s  =
  let (p, s') = break (== '/') s in
  p : case s' of
        []      -> []
        (_:s'') -> split s''

itemByIter' iter = do
  [n]   <- treeModelGetPath locationStore iter
  listStoreGetValue locationStore n

itemByIter iter = do
  citer <- treeModelSortConvertIterToChildIter sortModel iter
  itemByIter' citer

itemByPath path = do
  [n] <- treeModelSortConvertPathToChildPath sortModel path
  listStoreGetValue locationStore n

compareIters order a b =
  compareItems order <$> itemByIter' a <*> itemByIter' b

getSortOrder =
  withMVar state $ return . sOrder

setSortOrder order = do
  modifyMVar_ state $ \s ->
    return s { sOrder = order }
  treeSortableSetDefaultSortFunc sortModel $ compareIters order