phoityne-0.0.4.0: app/Phoityne/IO/GUI/GTK/FolderTree.hs
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE BinaryLiterals #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE DeriveDataTypeable #-}
module Phoityne.IO.GUI.GTK.FolderTree (
FolderTreeStore
, NodeData(..)
, FolderTreeDoubleClickedHandler
, FolderTreePopupHandler
, FolderTreeCreateFolderAction
, FolderTreeCreateFileAction
, FolderTreeRenameAction
, FolderTreeDeleteAction
, FolderTreeSearchAction
, FolderTreeReplaceAction
, FolderTreeStartupAction
, FolderTreeKeyPressEventHandler
, createTreeStore
, addNode2TreeStore
, setupFolderTree
, getPathFromNodeData
, getModNameFromNodeData
, findTreeNode
, getSelectedFolderTreeNodeData
, getSelectedFolderTreeAllNodeData
, folderTreeMenuPopup
, getNameFromNodeData
, expandCollapseFolderTree
, expandFolderTree
, collapseFolderTree
, updateTreeNode
, changeNameColorOfNodeData
) where
-- モジュール
import Phoityne.Constant
import Phoityne.IO.GUI.GTK.Constant
-- システム
import GHC.Float
import Graphics.UI.Gtk
import Control.Monad.IO.Class
import Data.String.Utils
import qualified Data.Tree as TR
import qualified Data.Text as T
-- |
--
--
type FolderTreeStore = TreeStore NodeData
type FolderTreeKeyPressEventHandler = String -> Bool -> Bool -> IO Bool
type FolderTreeDoubleClickedHandler = IO ()
type FolderTreePopupHandler = IO ()
type FolderTreeCreateFolderAction = IO ()
type FolderTreeCreateFileAction = IO ()
type FolderTreeRenameAction = IO ()
type FolderTreeDeleteAction = IO ()
type FolderTreeSearchAction = IO ()
type FolderTreeReplaceAction = IO ()
type FolderTreeStartupAction = IO ()
-- |
--
--
data NodeData =
FileNodeData {
moduleFileNodeData :: String
, nameFileNodeData :: FilePath
, pathFileNodeData :: FilePath
}
|
FolderNodeData {
moduleFolderNodeData :: String
, nameFolderNodeData :: FilePath
, pathFolderNodeData :: FilePath
} deriving (Show, Read, Eq)
instance Ord NodeData where
compare (FileNodeData x _ _) (FileNodeData x' _ _)
| x == x' = EQ
| x < x' = LT
| otherwise = GT
compare (FolderNodeData x _ _) (FolderNodeData x' _ _)
| x == x' = EQ
| x < x' = LT
| otherwise = GT
compare (FolderNodeData _ _ _) (FileNodeData _ _ _) = LT
compare (FileNodeData _ _ _) (FolderNodeData _ _ _) = GT
-- |
--
--
getNameFromNodeData :: NodeData -> String
getNameFromNodeData (FileNodeData _ name _) = name
getNameFromNodeData (FolderNodeData _ name _) = name
-- |
--
--
changeNameColorOfNodeData :: NodeData -> String -> String -> NodeData
changeNameColorOfNodeData nodeDat@(FolderNodeData _ _ _) _ _ = nodeDat
changeNameColorOfNodeData nodeDat@(FileNodeData _ name _) oldCol newCol = nodeDat { nameFileNodeData = changeColor name}
where
changeColor val = replace oldCol newCol val
-- |
--
--
getPathFromNodeData :: NodeData -> FilePath
getPathFromNodeData (FileNodeData _ _ path) = path
getPathFromNodeData (FolderNodeData _ _ path) = path
-- |
--
--
getModNameFromNodeData :: NodeData -> String
getModNameFromNodeData (FileNodeData name _ _) = name
getModNameFromNodeData (FolderNodeData name _ _) = name
-- |
--
--
createTreeStore :: TR.Tree NodeData -> IO FolderTreeStore
createTreeStore tree = treeStoreNew [tree]
-- |
-- Event Handler
--
folderTreeKeyPressEventHandler :: TreeView -> FolderTreeKeyPressEventHandler -> EventM EKey Bool
folderTreeKeyPressEventHandler _ evh = do
name <- eventKeyName
mods <- eventModifier
liftIO $ evh (T.unpack name) (elem Shift mods) (elem Control mods)
-- |
--
--
setupFolderTree :: Builder
-> FolderTreeStore
-> FolderTreeDoubleClickedHandler
-> FolderTreePopupHandler
-> FolderTreeCreateFolderAction
-> FolderTreeCreateFileAction
-> FolderTreeRenameAction
-> FolderTreeDeleteAction
-> FolderTreeSearchAction
-> FolderTreeReplaceAction
-> FolderTreeKeyPressEventHandler
-> FolderTreeStartupAction
-> IO ()
setupFolderTree builder store doublEH popupEH creteFolderAct createFileAct renameAct deleteAct searchAct replaceAct keyHandler startupAct = do
colLabel <- labelNew $ Just "Explorer"
col <- treeViewColumnNew
treeViewColumnSetWidget col $ Just colLabel
renderer <- cellRendererTextNew
cellLayoutPackStart col renderer True
cellLayoutSetAttributes col renderer store $ \cell -> [ cellTextMarkup := Just (getNameFromNodeData cell)]
_ <- builderGetObject builder castToMenu _NAME_TREE_VIEW_MENU
treeView <- builderGetObject builder castToTreeView _NAME_TREE_VIEW
_ <- treeViewSetModel treeView store
_ <- treeViewAppendColumn treeView col
_ <- on treeView buttonPressEvent $ folderTreeClickedHandler treeView doublEH popupEH
_ <- on treeView popupMenuSignal $ getSelectedFolderTreeNodeData builder store >>= folderTreeMenuPopup builder >> return True
_ <- on treeView keyPressEvent $ folderTreeKeyPressEventHandler treeView keyHandler
treeViewCreateFolderAction <- builderGetObject builder castToAction _NAME_TREE_VIEW_MENU_CREATE_FOLDER
_ <- on treeViewCreateFolderAction actionActivated creteFolderAct
treeViewCreateFileAction <- builderGetObject builder castToAction _NAME_TREE_VIEW_MENU_CREATE_FILE
_ <- on treeViewCreateFileAction actionActivated createFileAct
treeViewRenameAction <- builderGetObject builder castToAction _NAME_TREE_VIEW_MENU_RENAME
_ <- on treeViewRenameAction actionActivated renameAct
treeViewDeleteAction <- builderGetObject builder castToAction _NAME_TREE_VIEW_MENU_DELETE
_ <- on treeViewDeleteAction actionActivated deleteAct
treeViewSearchAction <- builderGetObject builder castToAction _NAME_TREE_VIEW_MENU_SEARCH
_ <- on treeViewSearchAction actionActivated searchAct
treeViewReplaceAction <- builderGetObject builder castToAction _NAME_TREE_VIEW_MENU_REPLACE
_ <- on treeViewReplaceAction actionActivated replaceAct
treeViewStartupAction <- builderGetObject builder castToAction _NAME_TREE_VIEW_MENU_STARTUP
_ <- on treeViewStartupAction actionActivated startupAct
widgetShowAll colLabel
widgetShowAll treeView
-- |
-- Event Handler
--
folderTreeClickedHandler :: TreeView
-> FolderTreeDoubleClickedHandler
-> FolderTreePopupHandler
-> EventM EButton Bool
folderTreeClickedHandler treeView doubleEH popupEH = do
bt <- eventButton
ck <- eventClick
(posXd, posYd) <- eventCoordinates
liftIO $ do
treeViewGetPathAtPos treeView (double2Int posXd, double2Int posYd) >>= \case
Nothing -> return False
Just (treePath, _, _) -> do
sel <- treeViewGetSelection treeView
treeSelectionSelectPath sel treePath
handle bt ck
where
handle LeftButton DoubleClick = doubleEH >> return True
handle RightButton SingleClick = popupEH >> return True
handle _ _ = return False
-- |
-- Event Handler
--
folderTreeMenuPopup :: Builder -> Maybe NodeData -> IO ()
folderTreeMenuPopup builder (Just (FolderNodeData _ fileName _))
| _PROJECT_ROOT_MODULE_NAME == fileName = return ()
| otherwise = do
treeViewCreateFolderAction <- builderGetObject builder castToAction _NAME_TREE_VIEW_MENU_CREATE_FOLDER
actionSetSensitive treeViewCreateFolderAction True
treeViewCreateFileAction <- builderGetObject builder castToAction _NAME_TREE_VIEW_MENU_CREATE_FILE
actionSetSensitive treeViewCreateFileAction True
treeViewStartupAction <- builderGetObject builder castToAction _NAME_TREE_VIEW_MENU_STARTUP
actionSetSensitive treeViewStartupAction False
treeViewMenu <- builderGetObject builder castToMenu _NAME_TREE_VIEW_MENU
menuPopup treeViewMenu Nothing
folderTreeMenuPopup builder (Just (FileNodeData _ _ path)) = do
treeViewCreateFolderAction <- builderGetObject builder castToAction _NAME_TREE_VIEW_MENU_CREATE_FOLDER
actionSetSensitive treeViewCreateFolderAction False
treeViewCreateFileAction <- builderGetObject builder castToAction _NAME_TREE_VIEW_MENU_CREATE_FILE
actionSetSensitive treeViewCreateFileAction False
treeViewStartupAction <- builderGetObject builder castToAction _NAME_TREE_VIEW_MENU_STARTUP
actionSetSensitive treeViewStartupAction (endswith _HS_FILE_EXT path)
treeViewMenu <- builderGetObject builder castToMenu _NAME_TREE_VIEW_MENU
menuPopup treeViewMenu Nothing
folderTreeMenuPopup _ _ = return ()
-- |
--
--
addNode2TreeStore :: FolderTreeStore -> NodeData -> NodeData -> IO ()
addNode2TreeStore store parent@(FolderNodeData _ _ _) child = do
findTreeNodeIter store (\n->n == parent) >>= \case
Nothing -> return ()
Just iter -> do
path <- treeIter2Path store iter
childIdx <- getInsertIndex iter
treeStoreInsert store path childIdx child
where
getInsertIndex parentIter = do
let model = castToTreeModel store
treeModelIterChildren model parentIter >>= \case
Nothing -> return 0
Just iter -> getIndexWithIter iter 0
getIndexWithIter iter idx = do
path <- treeIter2Path store iter
value <- treeStoreGetValue store path
if child < value then return idx
else searchNext iter (idx+1)
searchNext iter idx = do
let model = castToTreeModel store
treeModelIterNext model iter >>= \case
Nothing -> return idx
Just next -> getIndexWithIter next idx
addNode2TreeStore _ _ _ = return ()
-- |
--
--
treeIter2Path :: TreeStore a -> TreeIter -> IO TreePath
treeIter2Path store iter = treeModelGetPath (castToTreeModel store) iter
-- |
--
--
findTreeNode :: FolderTreeStore -> (NodeData -> Bool) -> IO (Maybe NodeData)
findTreeNode store finder = do
findTreeNodeIter store finder >>= \case
Nothing -> return Nothing
Just iter -> do
path <- treeIter2Path store iter
value <- treeStoreGetValue store path
return $ Just value
-- |
--
--
updateTreeNode :: FolderTreeStore -> NodeData -> NodeData -> IO ()
updateTreeNode store oldDat newDat = do
findTreeNodeIter store ((==) oldDat) >>= \case
Nothing -> return ()
Just iter -> do
path <- treeIter2Path store iter
treeStoreSetValue store path newDat
-- |
--
--
findTreeNodeIter :: FolderTreeStore -> (NodeData -> Bool) -> IO (Maybe TreeIter)
findTreeNodeIter store finder = do
let model = castToTreeModel store
treeModelGetIterFirst model >>= \case
Nothing -> return Nothing
Just iter -> findTreeNodeByIter model iter
where
findTreeNodeByIter model iter = do
path <- treeModelGetPath model iter
node <- treeStoreGetValue store path
if True == finder node then return (Just iter) else searchChild model iter
searchChild model iter =
treeModelIterChildren model iter >>= \case
Just child -> findTreeNodeByIter model child >>= \case
Just iter -> return (Just iter)
Nothing -> searchNext model iter
Nothing -> searchNext model iter
searchNext model iter =
treeModelIterNext model iter >>= \case
Just next -> findTreeNodeByIter model next
Nothing -> return Nothing
-- |
-- Event Handler
--
getSelectedFolderTreeNodeData :: Builder -> TreeStore NodeData -> IO (Maybe NodeData)
getSelectedFolderTreeNodeData builder store = do
treeView <- builderGetObject builder castToTreeView _NAME_TREE_VIEW
sel <- treeViewGetSelection treeView
treeSelectionGetSelected sel >>= \case
Nothing -> return Nothing
Just iter -> treeViewGetModel treeView >>= \case
Nothing -> return Nothing
Just model -> do
path <- treeModelGetPath model iter
val <- treeStoreGetValue store path
return $ Just val
-- |
-- Event Handler
--
getSelectedFolderTreeAllNodeData :: Builder -> TreeStore NodeData -> IO [NodeData]
getSelectedFolderTreeAllNodeData builder store = do
treeView <- builderGetObject builder castToTreeView _NAME_TREE_VIEW
sel <- treeViewGetSelection treeView
treeSelectionGetSelected sel >>= \case
Nothing -> return []
Just iter -> treeViewGetModel treeView >>= \case
Nothing -> return []
Just model -> do
path <- treeModelGetPath model iter
val <- treeStoreGetValue store path
treeModelIterChildren model iter >>= \case
Just childIter -> collectAll model childIter [val]
Nothing -> return [val]
where
collectAll model iter acc = do
path <- treeModelGetPath model iter
val <- treeStoreGetValue store path
let acc' = val : acc
acc'' <- treeModelIterChildren model iter >>= \case
Just childIter -> collectAll model childIter acc'
Nothing -> return acc'
treeModelIterNext model iter >>= \case
Just nextIter -> collectAll model nextIter acc''
Nothing -> return acc''
-- |
--
--
expandCollapseFolderTree :: Builder -> TreeStore NodeData -> IO ()
expandCollapseFolderTree builder store = do
treeView <- builderGetObject builder castToTreeView _NAME_TREE_VIEW
sel <- treeViewGetSelection treeView
treeSelectionGetSelected sel >>= \case
Nothing -> return ()
Just iter -> do
path <- treeIter2Path store iter
treeViewRowExpanded treeView path >>= \case
True -> treeViewCollapseRow treeView path >> return ()
False -> treeViewExpandToPath treeView path
-- |
--
--
expandFolderTree :: Builder -> TreeStore NodeData -> IO ()
expandFolderTree builder store = do
treeView <- builderGetObject builder castToTreeView _NAME_TREE_VIEW
sel <- treeViewGetSelection treeView
treeSelectionGetSelected sel >>= \case
Nothing -> return ()
Just iter -> do
path <- treeIter2Path store iter
treeViewRowExpanded treeView path >>= \case
True -> return ()
False -> treeViewExpandToPath treeView path
-- |
--
--
collapseFolderTree :: Builder -> TreeStore NodeData -> IO ()
collapseFolderTree builder store = do
treeView <- builderGetObject builder castToTreeView _NAME_TREE_VIEW
sel <- treeViewGetSelection treeView
treeSelectionGetSelected sel >>= \case
Nothing -> return ()
Just iter -> do
path <- treeIter2Path store iter
treeViewRowExpanded treeView path >>= \case
True -> treeViewCollapseRow treeView path >> return ()
False -> return ()