packages feed

Barracuda-1.0.2: Barracuda/GUI/ChatView.hs

-- |
-- Maintainer: Henning Guenther, Oliver Mielentz
--
-- The central gui widget displaying the messages of the channels.
module Barracuda.GUI.ChatView
	(ChatView
	,ChatContent
	,chatViewNew
	,chatContentNew
	,chatViewSetChatContent
	,chatViewGetWidget
	,chatViewScroll
	,chatContentInsert
	,chatContentWantAuth
	,chatContentError
	,onDownload
	,onAuth
	) where

import Data.Map as Map
import Graphics.UI.Gtk

import Control.Monad (when)
import Data.IORef
import Data.Time
import System.Locale
import Network.AdHoc.UserID
import Network.AdHoc.Message

-- | A 'ChatView' is a Widget that displays the content of a 'ChatContent'
data ChatView = ChatView ScrolledWindow TextView

-- | A 'ChatContent' represents a buffer with messages, e.g. for a specific
--   channel
data ChatContent = ChatContent
	{buffer           :: TextBuffer
	,tagtable         :: TextTagTable
	,tagBold          :: TextTag
	,tagWarn          :: TextTag
	,tagDelayed       :: TextTag
	,callbackDownload :: IORef ([Attachment] -> IO ())
	,callbackAuth     :: IORef (UserID -> IO ())
	,imgSave          :: Pixbuf
	,imgApply         :: Pixbuf
	}

-- | Retrieves the main Widget of a 'ChatView'. Can be used to insert the
--   'ChatView' into another Widget.
chatViewGetWidget :: ChatView -> Widget
chatViewGetWidget (ChatView wid _) = toWidget wid

-- | Sets the content of a 'ChatView'.
chatViewSetChatContent :: ChatView -> ChatContent -> IO ()
chatViewSetChatContent (ChatView _ cv) cc = textViewSetBuffer cv (buffer cc)

-- | Creates a 'ChatView' Widget without any content.
chatViewNew :: IO ChatView
chatViewNew = do
	win <- scrolledWindowNew Nothing Nothing
	win `set` [scrolledWindowVscrollbarPolicy := PolicyAlways
		  ,scrolledWindowHscrollbarPolicy := PolicyNever]
	cv <- textViewNew
	cv `set` [textViewEditable := False
		 ,textViewWrapMode := WrapWordChar
		 ,textViewCursorVisible := False
		 ]
	containerAdd win cv
	return (ChatView win cv)

chatContentTimestamp :: UTCTime -> ChatContent -> IO ()
chatContentTimestamp time cc = do
	timezone <- getCurrentTimeZone
	let time_str = formatTime defaultTimeLocale "%R" (utcToLocalTime timezone time)
	iter <- textBufferGetEndIter (buffer cc)
	textBufferInsert (buffer cc) iter time_str
	time_iter <- textIterCopy iter
	textIterBackwardChars time_iter (length time_str)
	textBufferApplyTag (buffer cc) (tagBold cc) iter time_iter

-- | Inserts a message with timestamp, text and attachments into a
--   'ChatContent'.
chatContentInsert :: UTCTime -> Maybe UserID -> String -> [Attachment] -> Bool -> ChatContent -> IO ()
chatContentInsert time sender txt attachments delayed cv = do
	chatContentTimestamp time cv
	iter <- textBufferGetEndIter (buffer cv)
	let prefix = case sender of
		Nothing -> " <anonymous>"
		Just rsender -> " <"++show rsender++">"
	textBufferInsert (buffer cv) iter prefix
	when delayed (do
		textBufferInsert (buffer cv) iter " [delayed]"
		delIter <- textIterCopy iter
		textIterBackwardChars delIter 10
		textBufferApplyTag (buffer cv) (tagDelayed cv) delIter iter)
	textBufferInsert (buffer cv) iter (" "++txt)
	case attachments of
		[] -> return ()
		_  -> do
			textBufferInsert (buffer cv) iter " "
			textBufferInsertPixbuf (buffer cv) iter (imgSave cv)
			picIter <- textIterCopy iter
			textIterBackwardChar picIter
			save_tag <- textTagNew Nothing
			save_tag `onTextTagEvent` (\ev _ -> case ev of
				Button _ SingleClick _ _ _ [] LeftButton _ _ -> do
					cb <- readIORef (callbackDownload cv)
					cb attachments
				_ -> return ()
				)
			textTagTableAdd (tagtable cv) save_tag
			textBufferApplyTag (buffer cv) save_tag iter picIter
			return ()
	textBufferInsert (buffer cv) iter "\n"

chatContentWantAuth :: UTCTime -> UserID -> ChatContent -> IO ()
chatContentWantAuth time from cc = do
	chatContentTimestamp time cc
	iter <- textBufferGetEndIter (buffer cc)
	textBufferInsert (buffer cc) iter (" "++show from++" wants to join. Allow? ")
	textBufferInsertPixbuf (buffer cc) iter (imgApply cc)
	picIter <- textIterCopy iter
	textIterBackwardChar picIter
	apply_tag <- textTagNew Nothing
	apply_tag `onTextTagEvent` (\ev _ -> case ev of
		Button _ SingleClick _ _ _ [] LeftButton _ _ -> do
			cb <- readIORef (callbackAuth cc)
			cb from
		_ -> return ())
	textTagTableAdd (tagtable cc) apply_tag
	textBufferApplyTag (buffer cc) apply_tag iter picIter
	textBufferInsert (buffer cc) iter ("\n")

-- | Creates an empty 'ChatContent'(no messages in it).
chatContentNew :: WidgetClass w => w -> IO ChatContent
chatContentNew wid = do
	Just pbApply <- widgetRenderIcon wid stockApply iconSizeMenu ""
	Just pbSave <- widgetRenderIcon wid stockSave iconSizeMenu ""
	tt <- textTagTableNew
	buf <- textBufferNew (Just tt)
	func <- newIORef (const $ return ())
	func2 <- newIORef (const $ return ())
	tb <- textTagNew Nothing
	tb `set` [textTagWeight := fromEnum WeightBold]
	tw <- textTagNew Nothing
	tw `set` [textTagForeground := "red"]
	td <- textTagNew Nothing
	td `set` [textTagForeground := "orange"]
	textTagTableAdd tt tb
	textTagTableAdd tt tw
	textTagTableAdd tt td
	return $ ChatContent
		{buffer = buf
		,tagtable = tt
		,tagBold = tb
		,tagWarn = tw
		,tagDelayed = td
		,callbackDownload = func
		,callbackAuth = func2
		,imgApply = pbApply
		,imgSave = pbSave
		}

chatContentError :: UTCTime -> String -> ChatContent -> IO ()
chatContentError time txt cc = do
	chatContentTimestamp time cc
	iter <- textBufferGetEndIter (buffer cc)
	textBufferInsert (buffer cc) iter (" "++txt)
	iter2 <- textIterCopy iter
	textIterBackwardChars iter2 (length txt)
	textBufferApplyTag (buffer cc) (tagWarn cc) iter iter2
	textBufferInsert (buffer cc) iter "\n"

-- | Sets a callback function to be called whenever the user wants to save
--   specific attachments.
onDownload :: ChatContent -> ([Attachment] -> IO ()) -> IO ()
onDownload cc f = modifyIORef (callbackDownload cc) (const f)

-- | Sets a callback function to be called whenever the user wants to
--   allow another user into the channel.
onAuth :: ChatContent -> (UserID -> IO ()) -> IO ()
onAuth cc f = modifyIORef (callbackAuth cc) (const f)

chatViewScroll :: ChatView -> IO ()
chatViewScroll (ChatView win cv) = do
	buf <- textViewGetBuffer cv
	iter <- textBufferGetEndIter buf
	textViewScrollToIter cv iter 0 Nothing
--	adj <- win `get` scrolledWindowVAdjustment
--	max <- adjustmentGetUpper adj
--	adjustmentSetValue adj max
	return ()