packages feed

apelsin-1.0: src/Clanlist.hs

module Clanlist where

import Graphics.UI.Gtk

import Control.Concurrent
import Control.Concurrent.STM
import qualified Data.ByteString.Char8 as B
import Data.Ord
import Data.Tree
import Data.Maybe
import Data.List (sortBy)
import Data.IORef
import Network.Tremulous.NameInsensitive
import Network.Tremulous.ByteStringUtils
import qualified Network.Tremulous.Protocol as P
import Network.Tremulous.Util

import Types
import ClanFetcher
import Constants
import Config
import FilterBar
import InfoBox
import GtkUtils
import TremFormatting


newClanList :: Bundle -> IO (VBox, IO ())
newClanList Bundle{..} = do
	raw			<- listStoreNew []
	filtered		<- treeModelFilterNew raw []
	sorted			<- treeModelSortNewWithModel filtered
	view			<- treeViewNewWithModel sorted
	scrollview		<- scrollIt view PolicyAutomatic PolicyAlways

	(infobox, statNow, statTot) <- newInfobox "clans"

	(filterbar, current)	<- newFilterBar filtered statNow ""

	let updateF = do
		new <- atomically $ readTMVar mclans
		listStoreClear raw
		treeViewColumnsAutosize view
		mapM_ (listStoreAppend raw) new
		treeModelFilterRefilter filtered
		set statTot [ labelText := show (length new) ]
		n <- treeModelIterNChildren filtered Nothing
		set statNow [ labelText := show n ]

	addColumnsFilterSort raw filtered sorted view (Just (comparing name)) [
		  ("_Name"	, 0 , False	, True	, False	, unpackorig . name	, Just (comparing name))
		, ("_Tag"	, 0 , False	, True	, False	, unpackorig . tag	, Just (comparing tag))
		, ("Website"	, 0 , False	, True	, False	, B.unpack . showURL . website	, Nothing)
		, ("IRC"	, 0 , False	, True	, False	, B.unpack . irc	, Nothing)
		]

	treeModelFilterSetVisibleFunc filtered $ \iter -> do
		Clan{..}	<- treeModelGetRow raw iter
		s		<- readIORef current
		let cmplist	= [ cleanedCase name, cleanedCase tag ]
		return $ B.null s || smartFilter s cmplist

	box <- vBoxNew False 0

	boxPackStart box filterbar PackNatural spacing
	boxPackStart box scrollview PackGrow 0
	boxPackStart box infobox PackNatural 0

	updateF

	return (box, updateF)
	where showURL x = fromMaybe x (stripPrefix "http://" x)


newOnlineClans :: Bundle-> (Bool -> P.GameServer -> IO ()) -> IO (ScrolledWindow, IO ())
newOnlineClans Bundle{..} setServer = do
	Config {colors} <- atomically $ readTMVar mconfig

	let showName c =  case c of
		Left Clan{..}		-> "<b>" ++ htmlEscape (unpackorig name) ++ "</b>"
		Right (P.Player{..}, _)	-> pangoPretty colors name

	let showServer c =  case c of
		Left _ -> ""
		Right (_, P.GameServer{hostname}) -> pangoPretty colors hostname

	raw	<- treeStoreNew []
	view	<- treeViewNewWithModel raw


	treeViewExpandAll view
	addColumns raw view [
		  ("Name"	, 0	, True	, True	, True	, showName )
		, ("Server"	, 0	, True	, True	, True	, showServer )
		]

	let updateF = do
		P.PollResult{..}	<- atomically $ readTMVar mpolled
		clans			<- atomically $ readTMVar mclans
		let players = buildTree $ sortByPlayers $
			associatePlayerToClans (makePlayerList polled) clans
		treeStoreClear raw
		treeViewColumnsAutosize view
		mapM_ (treeStoreInsertTree raw [] 0) players
		treeViewExpandAll view

	onCursorChanged view $ do
		(x, _)		<- treeViewGetCursor view
		Just iter	<- treeModelGetIter raw x
		item		<- treeModelGetRow raw iter
		case item of
			Left _ -> return ()
			Right (_, a) -> setServer False a

	onRowActivated view $ \path _ -> do
		Just vIter	<- treeModelGetIter raw path
		gs	<- treeModelGetRow raw vIter
		case gs of
			Left _ -> return ()
			Right (_, gameserver) -> setServer True gameserver

	scroll <- scrollIt view PolicyAutomatic PolicyAutomatic


	return (scroll, updateF)





-- /// Utility functions ///////////////////////////////////////////////////////////////////////////

type PlayerList = [(P.Player, P.GameServer)]

type OnlineView = Forest (Either Clan (P.Player, P.GameServer))

associatePlayerToClans :: PlayerList -> [Clan] -> [(Clan, PlayerList)]
associatePlayerToClans players clans = map f clans
	where
	f c@Clan{tagexpr} = (c, filter (cmp tagexpr) players)
	cmp e = matchTagExpr e . P.name . fst

buildTree :: [(Clan, PlayerList)] -> OnlineView
buildTree = filter notEmpty . foldr f [] where
	f (clan, pls) xs = Node (Left clan) (map rightNode pls) : xs
	rightNode x = Node (Right x) []
	notEmpty (Node _ [])	= False
	notEmpty _		= True
--f tag x =

sortByPlayers :: [(a, [b])] -> [(a, [b])]
sortByPlayers = sortBy (comparing (length . snd))

newClanSync :: Bundle -> IO () -> IO (HBox, IO ())
newClanSync Bundle{..} updateF = do
	button	<- buttonNewWithMnemonic "_Sync clan list"
	img	<- imageNewFromStock stockSave IconSizeButton
	set button 	[ buttonImage := img
			, buttonRelief := ReliefNone
			, buttonFocusOnClick := False ]

	box	<- hBoxNew False 0
	boxPackStart box button PackRepel 0


	let doSync = do
		set button [ widgetSensitive := False ]
		Config {clanSyncURL} <- atomically $ readTMVar mconfig
		forkIO $ do
			new <- getClanList clanSyncURL
			case new of
				Nothing	-> postGUISync $ do
					gtkError "Unable to download clanlist"
					set button [ widgetSensitive := True ]
				Just a	-> do
					atomically $
						swapTMVar mclans a
					postGUISync $ do
						updateF
						set button [ widgetSensitive := True ]
		return ()

	on button buttonActivated doSync

	return (box, doSync)