packages feed

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 ()