{- Copyright (c) 2007 John Goerzen Please see the COPYRIGHT file -} module RsyncGUI where import System.IO import Control.Concurrent.MVar import Control.Concurrent import Data.List import Text.Regex.Posix import Graphics.UI.Gtk import Graphics.UI.Gtk.Glade import Control.Monad import System.Exit import Data.Progress.Tracker import System.Time.Utils import RsyncParser import Paths_gtkrsync(getDataFileName) data GUIParts = GUIParts { lfile :: Label, ltotal :: Label, mainwin :: Window, pbfile :: ProgressBar, pbtotal :: ProgressBar, messages :: TextView, messageswin :: ScrolledWindow, btdone :: Button} initRsyncGUI :: IO () -> IO GUIParts initRsyncGUI exitfunc = do initGUI timeoutAddFull (yield >> return True) priorityDefaultIdle 50 gladefn <- getDataFileName "gtkrsync.glade" Just xml <- xmlNew gladefn -- Just xml <- xmlNew "gtkrsync.glade" window' <- xmlGetWidget xml castToWindow "mainwindow" onDestroy window' exitfunc pbfile' <- xmlGetWidget xml castToProgressBar "progressbarfile" pbtotal' <- xmlGetWidget xml castToProgressBar "progressbaroverall" lfile' <- xmlGetWidget xml castToLabel "labelfile" ltotal' <- xmlGetWidget xml castToLabel "labeloverall" messages' <- xmlGetWidget xml castToTextView "messages" messageswin' <- xmlGetWidget xml castToScrolledWindow "messageswindow" button' <- xmlGetWidget xml castToButton "donebutton" onClicked button' exitfunc let gui = GUIParts lfile' ltotal' window' pbfile' pbtotal' messages' messageswin' button' forkIO mainGUI return gui runGUI gui rsyncstream exitmv = do streamWithMsgActions <- procmessages gui rsyncstream procstream gui streamWithMsgActions modifyMVar_ exitmv mvdone labelSetText (lfile gui) "" labelSetText (ltotal gui) "Sync process has finished" progressBarSetFraction (pbfile gui) 1.0 progressBarSetText (pbfile gui) "" progressBarSetFraction (pbtotal gui) 1.0 progressBarSetText (pbtotal gui) "" buttonSetLabel (btdone gui) "gtk-close" where mvdone Nothing = return (Just ExitSuccess) mvdone (Just x) = return (Just x) exitApp exitmv = do mainQuit ec <- takeMVar exitmv case ec of Nothing -> exitWith (ExitFailure 20) Just x -> exitWith x procmessages gui stream = do buf <- textViewGetBuffer (messages gui) iter <- textBufferGetEndIter buf mark <- textBufferCreateMark buf Nothing iter True -- tag <- textTagNew Nothing -- set tag [textTagFamily := "Monospace"] -- textBufferApplyTag buf tag iter iter return $ map (\x -> (procmsg gui buf mark x, snd x)) stream procmsg gui buf mark (ltype, msg) = do end <- textBufferGetEndIter buf ipoint <- textBufferGetIterAtMark buf mark textBufferDelete buf ipoint end textBufferInsert buf ipoint ('\n' : msg) lines <- textBufferGetLineCount buf when (lines > 500) $ do start <- textBufferGetStartIter buf eol <- textBufferGetIterAtLine buf 1 textBufferDelete buf start eol -- scroll to the end of the buffer adj <- scrolledWindowGetVAdjustment (messageswin gui) upper <- adjustmentGetUpper adj adjustmentSetValue adj upper -- Update the iterator the new offset case ltype of HardLine -> do end <- textBufferGetEndIter buf textBufferMoveMark buf mark end SoftLine -> return () -- leave the mark where it is return () procstream gui stream = do (totalfiles, remainingstream) <- procscanning gui stream progress <- newProgress "total" totalfiles mapM_ (procprogress gui progress) remainingstream procscanning gui [] = return (0, []) procscanning gui ((action,x):xs) | isSuffixOf "files..." x = action >> labelSetText (ltotal gui) ("Scanned " ++ (head (words x)) ++ " files") >> progressBarPulse (pbtotal gui) >> procscanning gui xs | isSuffixOf "files to consider" x = action >> labelSetText (ltotal gui) "" >> progressBarSetFraction (pbtotal gui) 0.0 >> return (read . head . words $ x, xs) | otherwise = action >> procscanning gui xs procprogress gui progress (action, line) | progressl /= [] = do action case head progressl of [_, bytes, pct] -> progressBarSetFraction (pbfile gui) ((read pct) / 100) >> progressBarSetText (pbfile gui) (pct ++ "%") x -> fail $ "Couldn't handle " ++ show x case tocheck of [] -> return () [[_, thisfile, total]] -> progressBarSetFraction (pbtotal gui) (1.0 - (ithisfile / itotal)) >> progressBarSetText (pbtotal gui) ("File " ++ show (floor (itotal - ithisfile)) ++ " of " ++ total ++ " (" ++ show (intpct) ++ "%)") >> setP progress (floor (itotal - ithisfile)) >> setetr where itotal = read total ithisfile = read thisfile intpct = floor (100 * (1.0 - (ithisfile / itotal))) setetr = do etr <- getETR progress labelSetText (ltotal gui) ("ETA: " ++ renderSecs etr) x -> fail $ "Tocheck couldn't handle " ++ show x | otherwise = action >> labelSetText (lfile gui) line where progressl :: [[String]] progressl = line =~ "^ *([0-9]+) +([0-9]+)%" -- .+[0-9]+:[0-9]+:[0-9]+" =~ line tocheck = line =~ "xfer#[0-9]+, to-check=([0-9]+)/([0-9]+)" oobError gui msg = do dlg <- messageDialogNew (Just (mainwin gui)) [] MessageError ButtonsOk ("An error has been detected:\n\n" ++ msg ++ "\n\nExpand the Messages area in the main window for details.") dialogRun dlg widgetDestroy dlg