{-- HSFM, a filemanager written in Haskell. Copyright (C) 2015 Julian Ospald This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --} {-# OPTIONS_HADDOCK ignore-exports #-} module GUI.Gtk.Callbacks where import Control.Applicative ( (<$>) , (<*>) ) import Control.Concurrent.STM ( readTVarIO ) import Control.Exception ( throw ) import Control.Monad ( void , forM_ ) import Control.Monad.IO.Class ( liftIO ) import Data.DirTree import Data.Foldable ( for_ ) import Graphics.UI.Gtk import GUI.Gtk.Data import GUI.Gtk.Dialogs import GUI.Gtk.MyView import GUI.Gtk.Utils import IO.Error import IO.File import IO.Utils import System.FilePath ( isAbsolute , () ) import System.Glib.UTFString ( glibToString ) ----------------- --[ Callbacks ]-- ----------------- -- |Set callbacks, on hotkeys, events and stuff. setCallbacks :: MyGUI -> MyView -> IO () setCallbacks mygui myview = do view' <- readTVarIO $ view myview case view' of FMTreeView treeView -> do _ <- treeView `on` rowActivated $ (\_ _ -> withItems mygui myview open) commonGuiEvents treeView return () FMIconView iconView -> do _ <- iconView `on` itemActivated $ (\_ -> withItems mygui myview open) commonGuiEvents iconView return () menubarCallbacks where menubarCallbacks = do -- menubar-file _ <- menubarFileQuit mygui `on` menuItemActivated $ mainQuit _ <- menubarFileOpen mygui `on` menuItemActivated $ liftIO $ withItems mygui myview open _ <- menubarFileExecute mygui `on` menuItemActivated $ liftIO $ withItems mygui myview execute _ <- menubarFileNew mygui `on` menuItemActivated $ liftIO $ newFile mygui myview -- menubar-edit _ <- menubarEditCut mygui `on` menuItemActivated $ liftIO $ withItems mygui myview moveInit _ <- menubarEditCopy mygui `on` menuItemActivated $ liftIO $ withItems mygui myview copyInit _ <- menubarEditRename mygui `on` menuItemActivated $ liftIO $ withItems mygui myview renameF _ <- menubarEditPaste mygui `on` menuItemActivated $ liftIO $ operationFinal mygui myview _ <- menubarEditDelete mygui `on` menuItemActivated $ liftIO $ withItems mygui myview del -- mewnubar-view _ <- menubarViewIcon mygui `on` menuItemActivated $ liftIO $ switchView mygui myview createIconView _ <- menubarViewTree mygui `on` menuItemActivated $ liftIO $ switchView mygui myview createTreeView -- menubar-help _ <- menubarHelpAbout mygui `on` menuItemActivated $ liftIO showAboutDialog return () commonGuiEvents view = do -- GUI events _ <- urlBar mygui `on` entryActivated $ urlGoTo mygui myview _ <- refreshViewB mygui `on` buttonActivated $ do cdir <- liftIO $ getCurrentDir myview refreshView' mygui myview cdir _ <- clearStatusBar mygui `on` buttonActivated $ do popStatusbar mygui writeTVarIO (operationBuffer myview) None -- key events _ <- rootWin mygui `on` keyPressEvent $ tryEvent $ do [Control] <- eventModifier "q" <- fmap glibToString eventKeyName liftIO mainQuit _ <- view `on` keyPressEvent $ tryEvent $ do [Control] <- eventModifier "h" <- fmap glibToString eventKeyName cdir <- liftIO $ getCurrentDir myview liftIO $ modifyTVarIO (settings mygui) (\x -> x { showHidden = not . showHidden $ x}) >> refreshView' mygui myview cdir _ <- view `on` keyPressEvent $ tryEvent $ do [Alt] <- eventModifier "Up" <- fmap glibToString eventKeyName liftIO $ upDir mygui myview _ <- view `on` keyPressEvent $ tryEvent $ do "Delete" <- fmap glibToString eventKeyName liftIO $ withItems mygui myview del _ <- view `on` keyPressEvent $ tryEvent $ do [] <- eventModifier "Return" <- fmap glibToString eventKeyName liftIO $ withItems mygui myview open _ <- view `on` keyPressEvent $ tryEvent $ do [Control] <- eventModifier "c" <- fmap glibToString eventKeyName liftIO $ withItems mygui myview copyInit _ <- view `on` keyPressEvent $ tryEvent $ do [Control] <- eventModifier "x" <- fmap glibToString eventKeyName liftIO $ withItems mygui myview moveInit _ <- view `on` keyPressEvent $ tryEvent $ do [Control] <- eventModifier "v" <- fmap glibToString eventKeyName liftIO $ operationFinal mygui myview -- righ-click _ <- view `on` buttonPressEvent $ do eb <- eventButton t <- eventTime case eb of RightButton -> liftIO $ menuPopup (rcMenu mygui) $ Just (RightButton, t) _ -> return () return False _ <- rcFileOpen mygui `on` menuItemActivated $ liftIO $ withItems mygui myview open _ <- rcFileExecute mygui `on` menuItemActivated $ liftIO $ withItems mygui myview execute _ <- rcFileNew mygui `on` menuItemActivated $ liftIO $ newFile mygui myview _ <- rcFileCopy mygui `on` menuItemActivated $ liftIO $ withItems mygui myview copyInit _ <- rcFileRename mygui `on` menuItemActivated $ liftIO $ withItems mygui myview renameF _ <- rcFilePaste mygui `on` menuItemActivated $ liftIO $ operationFinal mygui myview _ <- rcFileDelete mygui `on` menuItemActivated $ liftIO $ withItems mygui myview del _ <- rcFileCut mygui `on` menuItemActivated $ liftIO $ withItems mygui myview moveInit return () -- |Go to the url given at the 'urlBar' and visualize it in the given -- treeView. urlGoTo :: MyGUI -> MyView -> IO () urlGoTo mygui myview = withErrorDialog $ do fp <- entryGetText (urlBar mygui) let abs = isAbsolute fp exists <- (||) <$> doesDirectoryExist fp <*> doesFileExist fp -- TODO: more explicit error handling? refreshView mygui myview (Just fp) -- |Supposed to be used with 'withRows'. Opens a file or directory. open :: [Item] -> MyGUI -> MyView -> IO () open [item] mygui myview = withErrorDialog $ case item of ADirOrSym r -> do nv <- Data.DirTree.readFile $ fullPath r refreshView' mygui myview nv r -> void $ openFile r -- this throws on the first error that occurs open (FileLikeList fs) mygui myview = withErrorDialog $ forM_ fs $ \f -> void $ openFile f open _ _ _ = withErrorDialog . throw $ InvalidOperation "Operation not supported on multiple files" -- |Execute a given file. execute :: [Item] -> MyGUI -> MyView -> IO () execute [item] mygui myview = withErrorDialog $ void $ executeFile item [] execute _ _ _ = withErrorDialog . throw $ InvalidOperation "Operation not supported on multiple files" -- |Supposed to be used with 'withRows'. Deletes a file or directory. del :: [Item] -> MyGUI -> MyView -> IO () del [item] mygui myview = withErrorDialog $ do let cmsg = "Really delete \"" ++ fullPath item ++ "\"?" withConfirmationDialog cmsg $ easyDelete item -- this throws on the first error that occurs del items@(_:_) mygui myview = withErrorDialog $ do let cmsg = "Really delete " ++ show (length items) ++ " files?" withConfirmationDialog cmsg $ forM_ items $ \item -> easyDelete item del _ _ _ = withErrorDialog . throw $ InvalidOperation "Operation not supported on multiple files" -- |Initializes a file move operation. moveInit :: [Item] -> MyGUI -> MyView -> IO () moveInit [item] mygui myview = do writeTVarIO (operationBuffer myview) (FMove . MP1 $ item) let sbmsg = "Move buffer: " ++ fullPath item popStatusbar mygui void $ pushStatusBar mygui sbmsg moveInit _ _ _ = withErrorDialog . throw $ InvalidOperation "Operation not supported on multiple files" -- |Supposed to be used with 'withRows'. Initializes a file copy operation. copyInit :: [Item] -> MyGUI -> MyView -> IO () copyInit [item] mygui myview = do writeTVarIO (operationBuffer myview) (FCopy . CP1 $ item) let sbmsg = "Copy buffer: " ++ fullPath item popStatusbar mygui void $ pushStatusBar mygui sbmsg copyInit _ _ _ = withErrorDialog . throw $ InvalidOperation "Operation not supported on multiple files" -- |Finalizes a file operation, such as copy or move. operationFinal :: MyGUI -> MyView -> IO () operationFinal mygui myview = withErrorDialog $ do op <- readTVarIO (operationBuffer myview) cdir <- getCurrentDir myview case op of FMove (MP1 s) -> do let cmsg = "Really move \"" ++ fullPath s ++ "\"" ++ " to \"" ++ fullPath cdir ++ "\"?" withConfirmationDialog cmsg . withCopyModeDialog $ \cm -> void $ runFileOp (FMove . MC s cdir $ cm) return () FCopy (CP1 s) -> do let cmsg = "Really copy \"" ++ fullPath s ++ "\"" ++ " to \"" ++ fullPath cdir ++ "\"?" withConfirmationDialog cmsg . withCopyModeDialog $ \cm -> void $ runFileOp (FCopy . CC s cdir $ cm) return () _ -> return () -- |Go up one directory and visualize it in the treeView. upDir :: MyGUI -> MyView -> IO () upDir mygui myview = withErrorDialog $ do cdir <- getCurrentDir myview rawModel' <- readTVarIO $ rawModel myview sortedModel' <- readTVarIO $ sortedModel myview nv <- goUp cdir refreshView' mygui myview nv -- |Go up one directory and visualize it in the treeView. newFile :: MyGUI -> MyView -> IO () newFile mygui myview = withErrorDialog $ do mfn <- textInputDialog "Enter file name" for_ mfn $ \fn -> do cdir <- getCurrentDir myview createFile cdir fn renameF :: [Item] -> MyGUI -> MyView -> IO () renameF [item] mygui myview = withErrorDialog $ do mfn <- textInputDialog "Enter new file name" for_ mfn $ \fn -> do let cmsg = "Really rename \"" ++ fullPath item ++ "\"" ++ " to \"" ++ anchor item fn ++ "\"?" withConfirmationDialog cmsg $ IO.File.renameFile item fn renameF _ _ _ = withErrorDialog . throw $ InvalidOperation "Operation not supported on multiple files"