manatee-mplayer-0.0.2: Manatee/Extension/Mplayer/PlaylistView.hs
-- Author: Andy Stewart <lazycat.manatee@gmail.com>
-- Maintainer: Andy Stewart <lazycat.manatee@gmail.com>
--
-- Copyright (C) 2010 Andy Stewart, all rights reserved.
--
-- 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
-- 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, see <http://www.gnu.org/licenses/>.
{-# LANGUAGE DeriveDataTypeable, ScopedTypeVariables #-}
{-# LANGUAGE OverloadedStrings #-}
module Manatee.Extension.Mplayer.PlaylistView where
import Control.Applicative
import Control.Concurrent.STM
import Control.Monad
import Data.Map (Map)
import Data.Text.Lazy (Text)
import Data.Typeable
import Graphics.UI.Gtk hiding (Statusbar, statusbarNew, get)
import Graphics.UI.Gtk.Gdk.SerializedEvent
import Manatee.Core.PageView
import Manatee.Core.Types
import Manatee.Extension.Mplayer.DBus
import Manatee.Extension.Mplayer.PlaylistBuffer
import Manatee.Toolkit.General.Basic
import Manatee.Toolkit.General.Functor
import Manatee.Toolkit.General.Maybe
import Manatee.Toolkit.General.STM
import Manatee.Toolkit.Gio.Gio
import Manatee.Toolkit.Gtk.Gtk
import Manatee.Toolkit.Gtk.ModelView
import Manatee.Toolkit.Gtk.ScrolledWindow
import System.GIO.File.ContentType
import System.Posix.Process
import System.Random
import qualified Data.Map as M
data PlaylistView =
PlaylistView {playlistViewPlugId :: TVar PagePlugId
,playlistViewScrolledWindow :: ScrolledWindow
,playlistViewBuffer :: PlaylistBuffer
,playlistViewTreeView :: TreeView
,playlistViewListStore :: ListStore MultimediaInfo
,playlistViewSortModel :: TypedTreeModelSort MultimediaInfo
,playlistViewCurrentPlayPath :: TVar TreePath
,playlistViewPlayMode :: TVar PlayMode}
deriving Typeable
instance PageBuffer PlaylistBuffer where
pageBufferGetName = readTVarIO . playlistBufferName
pageBufferSetName a = writeTVarIO (playlistBufferName a)
pageBufferClient = playlistBufferClient
pageBufferCreateView a pId = PageViewWrap <$> playlistViewNew a pId
pageBufferMode = playlistBufferMode
instance PageView PlaylistView where
pageViewBuffer = PageBufferWrap . playlistViewBuffer
pageViewPlugId = playlistViewPlugId
pageViewFocus = treeViewFocus . playlistViewTreeView
pageViewScrolledWindow = playlistViewScrolledWindow
pageViewHandleKeyAction = playlistViewHandleKeyAction
pageViewScrollToTop = playlistViewScrollToTop
pageViewScrollToBottom = playlistViewScrollToBottom
pageViewScrollVerticalPage = playlistViewScrollVerticalPage
pageViewScrollVerticalStep = playlistViewScrollVerticalStep
data PlayMode = SingleMode
| ListMode
| RandomMode
deriving (Show, Eq, Ord)
-- | Internal new function.
playlistViewNew :: PlaylistBuffer -> PagePlugId -> IO PlaylistView
playlistViewNew buffer plugId = do
-- Create plug id.
pId <- newTVarIO plugId
-- Create UI frame.
scrolledWindow <- scrolledWindowNew_
-- Tree view.
treeView <- treeViewNew
treeViewSetEnableTreeLines treeView True
scrolledWindow `containerAdd` treeView
-- List store.
listStore <- listStoreNew []
-- Sort model.
sortModel <- treeModelSortNewWithModel listStore
-- Current play path.
path <- newTVarIO [0]
-- Play mode.
playMode <- newTVarIO ListMode
-- Playlist view.
let playlistView = PlaylistView pId scrolledWindow buffer treeView listStore sortModel path playMode
-- Build match rule listen DBus message.
playlistViewBuildMatchRule playlistView
-- Draw view.
playlistViewDraw playlistView
return playlistView
-- | Build match rule.
playlistViewBuildMatchRule :: PlaylistView -> IO ()
playlistViewBuildMatchRule view =
mkMplayerClientMatchRule
(pageViewClient view)
(PlayFinished,
\ _ -> do
mode <- readTVarIO (playlistViewPlayMode view)
case mode of
SingleMode -> playlistViewPlayCurrent view
ListMode -> playlistViewPlayNext view
RandomMode -> playlistViewPlayRandom view)
-- | Switch play mode.
playlistViewSwitchPlayMode :: PlaylistView -> IO ()
playlistViewSwitchPlayMode view = do
mode <- readTVarIO (playlistViewPlayMode view)
let newMode = case mode of
SingleMode -> ListMode
ListMode -> RandomMode
RandomMode -> SingleMode
writeTVarIO (playlistViewPlayMode view) newMode
pageViewUpdateInfoStatus view "PlayMode" (" PlayMode (" ++ show newMode ++ ")")
-- | Play current track.
playlistViewPlayCurrent :: PlaylistView -> IO ()
playlistViewPlayCurrent view = do
currentPath <- readTVarIO $ playlistViewCurrentPlayPath view
playlistViewPlayInternal view currentPath
-- | Draw playlist view.
playlistViewDraw :: PlaylistView -> IO ()
playlistViewDraw view = do
-- Get value.
let buffer = playlistViewBuffer view
treeView = playlistViewTreeView view
store = playlistViewListStore view
model = playlistViewSortModel view
infos <- readTVarIO $ playlistBufferInfos buffer
-- Append to list store.
listStoreClear store
forM_ infos (listStoreAppend store)
-- Set tree view model.
treeViewSetModel treeView model
-- Clean tree view.
treeViewRemoveColumns treeView
-- Add icon column.
playlistViewAddIconColumn treeView store
-- Add column file info to tree view.
forM_ (playlistBufferOptions buffer) (playlistViewAddColumn treeView store model)
-- Set default sort rule.
sortStatus <- readTVarIO $ playlistBufferSortStatus buffer
playlistViewSortInternal view sortStatus
return ()
-- | Internal sort function.
playlistViewSortInternal :: PlaylistView -> (MultimediaOption, SortType) -> IO ()
playlistViewSortInternal view (option, sortType) = do
let options = playlistBufferOptions $ playlistViewBuffer view
lookup option options ?>= \x ->
treeSortableSetSortColumnId (playlistViewSortModel view) x sortType
-- | Add icon column.
playlistViewAddIconColumn :: (TreeViewClass self
,TreeModelClass (model row)
,TypedTreeModelClass model)
=> self
-> model row
-> IO ()
playlistViewAddIconColumn treeView store = do
-- Any music format can work, we just need music icon pixbuf.
pixbuf <- getIconPixbuf $ contentTypeGetIcon "audio/mp3"
tvc <- treeViewColumnNew
set tvc [treeViewColumnTitle := ""]
treeViewAppendColumn treeView tvc
icon <- cellRendererPixbufNew
treeViewColumnPackStart tvc icon True
cellLayoutSetAttributes tvc icon store $ \_ -> [cellPixbuf := pixbuf]
-- | Add column.
playlistViewAddColumn :: (MultimediaInfoClass t
,TreeViewClass self1
,TreeModelClass self
,TreeModelSortClass self
,TypedTreeModelClass model
,TreeSortableClass self)
=> self1
-> model MultimediaInfo
-> self
-> (t, SortColumnId)
-> IO ()
playlistViewAddColumn treeView model sortModel option@(info,sortId) = do
playlistViewSetSortFunc model sortModel option
let name = getColumnTitle info
maxWidth = getColumnMaxWidth info
tvc <- treeViewAddColumnWithTitle treeView name sortId
maxWidth ?>= \width -> treeViewColumnSetMaxWidth tvc width
cell <- cellRendererTextNew
treeViewColumnPackStart tvc cell True
playlistViewSetCellText tvc cell model sortModel info
-- | Set sort function.
playlistViewSetSortFunc :: (TreeSortableClass self,
TypedTreeModelClass model,
MultimediaInfoClass a) =>
model MultimediaInfo
-> self
-> (a, SortColumnId)
-> IO ()
playlistViewSetSortFunc model sortModel (info, sortId) =
treeSortableSetSortFunc sortModel sortId $ \iter1 iter2 -> do
row1 <- treeModelGetRow model iter1
row2 <- treeModelGetRow model iter2
compareRow info row1 row2
-- | Set cell text.
playlistViewSetCellText :: (CellLayoutClass self,
CellRendererTextClass cell,
TreeModelClass model,
TreeModelSortClass model,
TypedTreeModelClass model1,
MultimediaInfoClass a)
=> self
-> cell
-> model1 MultimediaInfo
-> model
-> a
-> IO ()
playlistViewSetCellText tvc cell model sortModel info =
cellLayoutSetAttributeFunc tvc cell sortModel $ \iter -> do
row <- treeModelSortGetRow model sortModel iter
set cell [cellText := getCellText info row
,cellXAlign := getCellXAlign info]
-- | Next node.
playlistViewNextNode :: PlaylistView -> IO ()
playlistViewNextNode = treeViewFocusNextToplevelNode . playlistViewTreeView
-- | Previous node.
playlistViewPrevNode :: PlaylistView -> IO ()
playlistViewPrevNode = treeViewFocusPrevToplevelNode . playlistViewTreeView
-- | Keymap.
playlistViewKeymap :: Map Text (PlaylistView -> IO ())
playlistViewKeymap =
M.fromList
[("j", playlistViewNextNode)
,("k", playlistViewPrevNode)
,("Down", playlistViewNextNode)
,("Up", playlistViewPrevNode)
,("J", playlistViewScrollToBottom)
,("K", playlistViewScrollToTop)
,(" ", playlistViewScrollVerticalPage True)
,("b", playlistViewScrollVerticalPage False)
,("PageDown", playlistViewScrollVerticalPage True)
,("PageUp", playlistViewScrollVerticalPage False)
,("m", playlistViewPlay)
,("Return", playlistViewPlay)
,("M", playlistViewPause)
,("z", playlistViewStop)
,("h", playlistViewBackward)
,("l", playlistViewForward)
,("Right", playlistViewBackward)
,("Left", playlistViewForward)
,(",", playlistViewVolumeDec)
,(".", playlistViewVolumeInc)
,("-", playlistViewVolumeDec)
,("=", playlistViewVolumeInc)
,("n", playlistViewPlayNext)
,("p", playlistViewPlayPrev)
,("N", playlistViewPlayRandom)
,("P", playlistViewSwitchPlayMode)
,("1", playlistViewSortByTitle)
,("2", playlistViewSortByAlbum)
,("3", playlistViewSortByArtist)
,("4", playlistViewSortByYear)
,("5", playlistViewSortByTrack)
,("6", playlistViewSortByBitRate)
,("7", playlistViewSortByDuration)
]
-- | Sort by title.
playlistViewSortByTitle :: PlaylistView -> IO ()
playlistViewSortByTitle view = playlistViewSort view MOTitle
-- | Sort by album.
playlistViewSortByAlbum :: PlaylistView -> IO ()
playlistViewSortByAlbum view = playlistViewSort view MOAlbum
-- | Sort by artist.
playlistViewSortByArtist :: PlaylistView -> IO ()
playlistViewSortByArtist view = playlistViewSort view MOArtist
-- | Sort by year.
playlistViewSortByYear :: PlaylistView -> IO ()
playlistViewSortByYear view = playlistViewSort view MOYear
-- | Sort by track.
playlistViewSortByTrack :: PlaylistView -> IO ()
playlistViewSortByTrack view = playlistViewSort view MOTrack
-- | Sort by BitRate.
playlistViewSortByBitRate :: PlaylistView -> IO ()
playlistViewSortByBitRate view = playlistViewSort view MOBitRate
-- | Sort by duration.
playlistViewSortByDuration :: PlaylistView -> IO ()
playlistViewSortByDuration view = playlistViewSort view MODuration
-- | Sort column.
playlistViewSort :: PlaylistView -> MultimediaOption -> IO ()
playlistViewSort view option = do
-- Get model and options.
let model = playlistViewSortModel view
buffer = playlistViewBuffer view
options = playlistBufferOptions buffer
-- Get current sortType and columnId.
(curSortType, _, curSortColumnId) <- treeSortableGetSortColumnId model
lookup option options ?>= \id -> do
treeSortableSetSortColumnId model id $
if id == curSortColumnId
-- Just change sort order when sort column id is same.
then
-- Just change sort order.
case curSortType of
SortAscending -> SortDescending
SortDescending -> SortAscending
-- Otherwise sort ascending.
else SortAscending
-- Get new sort type.
(newSortType, _, _) <- treeSortableGetSortColumnId model
-- Update sort status of buffer.
writeTVarIO (playlistBufferSortStatus $ playlistViewBuffer view) (option, newSortType)
-- Focus to cell.
treeViewFocus (playlistViewTreeView view)
-- | Play current selected track.
playlistViewPlay :: PlaylistView -> IO ()
playlistViewPlay view = do
let treeView = playlistViewTreeView view
treeViewGetSelectedPath treeView
>?>= \ path -> do
-- Convert sorted path to unsorted model path.
currentPath <- treeModelSortConvertPathToChildPath (playlistViewSortModel view) path
playlistViewPlayInternal view currentPath
-- | Play next track randomly.
playlistViewPlayRandom :: PlaylistView -> IO ()
playlistViewPlayRandom view = do
size <- treeViewGetToplevelNodeCount (playlistViewTreeView view)
randomIndex <- randomRIO (0, size - 1)
playlistViewPlayInternal view [randomIndex]
-- | Play next track.
playlistViewPlayNext :: PlaylistView -> IO ()
playlistViewPlayNext view = do
currentPath <- readTVarIO $ playlistViewCurrentPlayPath view
nextSortPath <- treeViewNextSortPath (playlistViewTreeView view) (playlistViewSortModel view) currentPath
playlistViewPlayInternal view nextSortPath
-- | Play previous track.
playlistViewPlayPrev :: PlaylistView -> IO ()
playlistViewPlayPrev view = do
currentPath <- readTVarIO $ playlistViewCurrentPlayPath view
prevSortPath <- treeViewPrevSortPath (playlistViewTreeView view) (playlistViewSortModel view) currentPath
playlistViewPlayInternal view prevSortPath
-- | The internal function of `playlistViewPlay'.
playlistViewPlayInternal :: PlaylistView -> TreePath -> IO ()
playlistViewPlayInternal view path = do
-- Get file path and update play path status.
filePath <- liftM miFilePath $ listStoreGetValue (playlistViewListStore view) (head path)
let displayPath = filepathGetDisplayName filePath
writeTVarIO (playlistViewCurrentPlayPath view) path
-- Update play mode status.
mode <- readTVarIO (playlistViewPlayMode view)
pageViewUpdateInfoStatus view "PlayMode" (" PlayMode (" ++ show mode ++ ")")
-- Update playing status.
pageViewUpdateInfoStatus view "Playing" (" Playing : " ++ displayPath)
-- Send daemon signal to play.
processId <- getProcessID
mkMplayerDaemonSignal (pageViewClient view) Play (PlayArgs filePath processId)
-- | Stop play.
playlistViewStop :: PlaylistView -> IO ()
playlistViewStop view =
mkMplayerDaemonSignal (pageViewClient view) Stop StopArgs
-- | Pause or continue play.
playlistViewPause :: PlaylistView -> IO ()
playlistViewPause view =
mkMplayerDaemonSignal (pageViewClient view) Pause PauseArgs
-- | Forward current track.
playlistViewForward :: PlaylistView -> IO ()
playlistViewForward view =
mkMplayerDaemonSignal (pageViewClient view) Forward (ForwardArgs 10)
-- | Backward current track.
playlistViewBackward :: PlaylistView -> IO ()
playlistViewBackward view =
mkMplayerDaemonSignal (pageViewClient view) Backward (BackwardArgs 10)
-- | Increase volume.
playlistViewVolumeInc :: PlaylistView -> IO ()
playlistViewVolumeInc view =
mkMplayerDaemonSignal (pageViewClient view) VolumeInc (VolumeIncArgs 10)
-- | Decrease volume.
playlistViewVolumeDec :: PlaylistView -> IO ()
playlistViewVolumeDec view =
mkMplayerDaemonSignal (pageViewClient view) VolumeDec (VolumeDecArgs 10)
-- | Begin.
playlistViewScrollToTop :: PlaylistView -> IO ()
playlistViewScrollToTop =
treeViewFocusFirstToplevelNode . playlistViewTreeView
-- | End.
playlistViewScrollToBottom :: PlaylistView -> IO ()
playlistViewScrollToBottom =
treeViewFocusLastToplevelNode . playlistViewTreeView
-- | Scroll page vertically.
playlistViewScrollVerticalPage :: Bool -> PlaylistView -> IO ()
playlistViewScrollVerticalPage isDown a = do
let sw = playlistViewScrolledWindow a
tv = playlistViewTreeView a
pageInc <- (<=<) adjustmentGetPageIncrement scrolledWindowGetVAdjustment sw
treeViewScrollVertical tv sw (if isDown then pageInc else (- pageInc))
-- | Scroll step vertically.
playlistViewScrollVerticalStep :: Bool -> PlaylistView -> IO ()
playlistViewScrollVerticalStep isDown a = do
let sw = playlistViewScrolledWindow a
tv = playlistViewTreeView a
stepInc <- (<<<=) i2d treeViewGetSelectedCellHeight tv
treeViewScrollVertical tv sw (if isDown then stepInc else (- stepInc))
-- | Handle key action.
playlistViewHandleKeyAction :: PlaylistView -> Text -> SerializedEvent -> IO ()
playlistViewHandleKeyAction view keystoke sEvent =
case M.lookup keystoke playlistViewKeymap of
Just action -> action view
Nothing -> widgetPropagateEvent (playlistViewTreeView view) sEvent