{-| This module exports the functionality for graphically interacting with a user previous to interacting with a file. i.e. Query whether or not a user wants to delete a file. At the back of all the interaction methods are the functions exported by 'SystemOperations', which actually carry out the actions. This module is basically a graphical front ent to 'SystemOperations'. These actions are: * Move a file. * Move a directory. * Rename a file. * Rename a directory. * Copy a file. * Copy a directory. * Delete a file. * Delete a directory. * Create a file. * Create a directory. * Open a file. * Search a directory. -} module DirectoryOperations ( moveFile, moveFolder, renameFile, renameFolder, copyFile, copyFolder, deleteFile, deleteFolder, createFile, createDir, openFile, searchDir ) where import qualified GHC.IOBase as E import qualified SystemOperations as S import Graphics.UI.Gtk import List ( (\\), sort ) import Control.Monad( filterM, forM_ ) import System.FilePath( () ) import System.Directory ( doesFileExist, getCurrentDirectory, doesDirectoryExist ) import System.Cmd import TreeFunctions import Util import Types import Popup import FileSearch {-| This function takes a list of paths to files, and attempts to move them to a location which will be requested from the user. -} moveFile :: [FilePath] -- ^ The list of files to be moved. -> IO ( ) moveFile files = operateName files S.moveFile "Move File(s)" "Please enter the destination for the selected file(s): " {-| This function takes a list of directories, and attempts to move them to a location which will be provided by the user. -} moveFolder :: [FilePath] -- ^ The list of directories to be moved. -> IO ( ) moveFolder dirs = operateName dirs S.moveDirectory "Move Folder(s)" "Please enter the destination for the selected folder(s): " {-| This function takes a list of files, and attempts to copy them to a location which will be provided by the user. -} copyFile :: [FilePath] -- ^ The list of files to be copied. -> IO ( ) copyFile files = operateName files S.copyFile "Copy File(s)" "Please enter the destination for the selected file(s): " {-| This function takes a list of directories and attempts to copy them to a location which will be provided by the user. -} copyFolder :: [FilePath] -- ^ The list of directories to be copied. -> IO ( ) copyFolder dirs = operateName dirs S.copyDirectory "Copy Folders" "Please enter the destination for the selected folder(s): " {-| This function takes a list of files and attempts to delete them all. The user will be queried for confirmation. -} deleteFile :: [FilePath] -- ^ The list of files to be deleted. -> IO ( ) deleteFile files = delete files S.deleteFile "file" {-| This function takes a list of directories and attempts to delete them all. The user will be queried for confirmation. -} deleteFolder :: [FilePath] -- ^ The list of directories to be deleted. -> IO ( ) deleteFolder dirs = delete dirs S.deleteDirectory "folder" {-| This function takes a list of files and attempts to rename them. The user will be queried for the new names for each element of the list. -} renameFile :: [FilePath] -- ^ The list of files to be renamed. -> IO ( ) renameFile files = rename files S.renameFile {-| This function takes a list of directories and attempts to rename them. The user will be queried for the new names for each element of the list. -} renameFolder :: [FilePath] -- ^ The list of directories to be renamed. -> IO ( ) renameFolder dirs = rename dirs S.renameDirectory {-| This takes a list of items to be operated upon ( selected by the user ), and checks that lists length. If the length is 0, then it does nothing. For any other length i.e. >= 1, it it querys the user for a result based on a supplied query. It then carries out the requested operation on all the items in the supplied list. -} operateName :: [FilePath] -- ^ The list of items to be operated upon. -> ( FilePath -> FilePath -> IO E.ExitCode ) -- ^ The operation to be carried out on the list. -> String -- ^ The title of the query window. -> String -- ^ The query to be displayed. -> IO ( ) operateName paths func wtitle wmsg = case ( length paths ) of 0 -> return ( ) _ -> ( inputWindow wtitle wmsg ) >>= ( \r -> case r of "" -> return ( ) name -> forM_ paths $ \path -> func path name ) {-| This function takes a list of items ( selected by the user and attempts to delete them. It the list is empty, it does nothing. If there is one element in the list it asks the user if they would like to delete that item. For a list containing more than 1 element it asks the user if they would like to delete the amount of items. When the user confirms, it will either delete the single item or iterate through the list deleting each element. -} delete :: [FilePath] -- ^ The list of items to be deleted. -> ( FilePath -> IO E.ExitCode ) -- ^ The deleting function from 'SystemOperations'. -> String -- ^ The type of item to be deleted, either \"file\" or \"folder\". -> IO ( ) delete paths func ftype = case ( length paths ) of 0 -> return ( ) 1 -> ( confirmWindow ( "OK to delete" ++ ( head paths ) ++ "?" ) ) >>= ( \r -> case r of True -> func ( head paths ) >> return ( ) False -> return ( ) ) _ -> ( confirmWindow ( "OK to delete " ++ ( show ( length paths ) ) ++ " " ++ ftype ++ "s?" ) ) >>= ( \r -> case r of True -> forM_ paths $ \path -> func path False -> return ( ) ) {-| This function takes a list of items ( selected by the user ) and attempts to rename them. If the amount of items is 0, it does nothing, and for anything else it iterates through the list, querying the user for new names for the item. -} rename :: [FilePath] -- ^ The list of items to be renamed. -> ( FilePath -> FilePath -> IO E.ExitCode ) -- ^ The renaming function from 'SystemOperations'. -> IO ( ) rename paths func = case ( length paths ) of 0 -> return ( ) _ -> forM_ paths $ \p -> inputWindow "Rename" ( "Please enter " ++ p ++ "'s new name: " ) >>= ( \r -> case r of "" -> return ( ) r -> func p r >> return ( ) ) {-| This function takes a list of files ( selected by the user ) and attempts to open them. It firstly checks the length of the the list, if it is empty i.e. length = 0, it does nothing. For anything else it iterates throught the list using 'openFile' from 'SystemOperations' to open the files. -} openFile :: [FilePath] -- ^ The list of files to be opened. -> IO ( ) openFile files = case ( length files ) of 0 -> return ( ) _ -> forM_ files $ \file -> ( ( \d -> S.openFile (d++"/"++file) ) =<< getCurrentDirectory ) {-| Function to request the name of the item to be created. If nothing is entered it exits. If a name is entered it uses 'create' to create the item. -} createNew :: String -- ^ The title type to be created. Either \"File\" or \"Directory\". -> String -- ^ The message type to be created. Either \"file\" or \"directory\". -> String -- ^ The command to be used while creating the item. Either \"touch\" or \"mkdir\". -> ( FilePath -> IO Bool ) -- ^ The function to be used to check whether or not the item exists. -> ( FilePath -> IO E.ExitCode ) -- ^ The function to be used to remove the item if it already exists. -> IO ( ) createNew bt st cmd bool delf = inputWindow ( bt ++ " Name" ) ( "Please give the " ++ st ++ "'s name " ) >>= ( \n -> case n of "" -> return ( ) n -> create bt n cmd bool delf ) {-| Function to create a new file\/directory. It first checks to see whether the file\/directory exists. If so it will alert the user and ask them if they would like to overwrite it using 'confirmWindow' from 'Popup'. If the user agrees, it will remove the existing file\/directory and then create a new one. If the file\/directory doesn't exist then it will create the file\/directory. -} create :: String -- ^ The type of item to be created. Either \"File\" or \"Directory\". -> FilePath -- ^ The name of the file\/directory to be created. -> String -- ^ The command for creating the item. Either \"touch\" or \"mkdir\". -> ( FilePath -> IO Bool ) -- ^ Function for checking whether or not the item exists. -> ( FilePath -> IO E.ExitCode ) -- ^ Function for removing the item if it exists. -> IO ( ) create ftype path cmd func removeFunc = func path >>= ( \e -> case e of True -> confirmWindow ( ftype ++ path ++ " exists!\nOK to overwrite?" ) >>= ( \r -> case r of True -> do removeFunc path rawSystem cmd [path] >> return ( ) False -> return ( ) ) False -> rawSystem cmd [path] >> return ( ) ) {-| Creates a file in the current directory and updates the file 'ListStore' to reflect the addition. -} createFile :: ListStore StoreRow -- ^ The file 'ListStore'. -> IO ( ) createFile fileTree = do createNew "File" "file" "touch" doesFileExist S.deleteFile ( _, files ) <- viewDirectory =<< getCurrentDirectory refreshPane files fileTree >> return ( ) {-| Creates a directory in the current directory and updates the directory 'ListStore' to reflect the addition. -} createDir :: ListStore StoreRow -- ^ The directory 'ListStore'. -> IO ( ) createDir dirTree = do createNew "Directory" "directory" "mkdir" doesDirectoryExist S.deleteDirectory ( dirs, _ ) <- viewDirectory =<< getCurrentDirectory refreshPane dirs dirTree {-| This function uses 'FileSearch' to recursively search the contents of a directory and it's sub-directories. It gets the search term from an 'Entry' and updates the 'FileBrowser' with the generated list of results. It uses 'TreeFunctions' to update the list. -} searchDir :: Entry -- ^ The 'Entry' containing the search term. -> FileBrowser -- ^ The main 'FileBrowser'. -> IO ( ) searchDir query browse = do --Get search expression. expression <- entryGetText query cur <- getCurrentDirectory --Search directory contents, recursively for the expression. results <- searchDirectoryFiles cur ( "*" ++ expression ) --Split into files & folders. files <- filterM doesFileExist results dirs <- filterM doesDirectoryExist results --Refresh the two TreeViews. refreshPane ( sort ( map ( flip (\\) ( cur ++ "/" ) ) dirs ) ) ( dirStore browse ) refreshPane ( sort ( map ( flip (\\) ( cur ++ "/" ) ) files ) ) ( fileStore browse )