leksah-0.15.1.4: src/IDE/Pane/Workspace.hs
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE OverloadedStrings #-}
-----------------------------------------------------------------------------
--
-- Module : IDE.Pane.Workspace
-- Copyright : 2007-2011 Juergen Nicklisch-Franken, Hamish Mackenzie
-- License : GPL
--
-- Maintainer : maintainer@leksah.org
-- Stability : provisional
-- Portability :
--
-- | The pane of the IDE that shows the cabal packages in the workspace
-- and their build targets
--
-----------------------------------------------------------------------------
module IDE.Pane.Workspace (
WorkspaceState(..)
, IDEWorkspace
, updateWorkspace
, getWorkspace
, showWorkspace
) where
import Graphics.UI.Gtk hiding (get)
import Graphics.UI.Gtk.Gdk.EventM
import Data.Maybe
import Data.Typeable
import IDE.Core.State
import IDE.Workspaces
import qualified Data.Map as Map (empty)
import Data.List (sortBy)
import IDE.Pane.Files (refreshFiles)
import Control.Monad (when)
import Control.Monad.IO.Class (MonadIO(..))
import IDE.Utils.GUIUtils (treeViewContextMenu, __)
import System.Glib.Properties (newAttrFromMaybeStringProperty)
import Data.Tree (Tree(..))
import System.Log.Logger (debugM)
import qualified Data.Function as F (on)
import System.FilePath (takeDirectory, takeBaseName, takeFileName)
import Data.Text (Text)
import qualified Data.Text as T (pack)
-- | Workspace pane state
--
-- | The data in a row displays a package or an
-- executable of that package (in the first case
-- the path to the package is also stored)
type WorkspaceRecord = (IDEPackage, Maybe Text)
-- | The representation of the workspace pane
data IDEWorkspace = IDEWorkspace {
scrolledView :: ScrolledWindow
, treeViewC :: TreeView
, workspaceStore :: TreeStore (Bool, WorkspaceRecord)
, topBox :: VBox
} deriving Typeable
instance Pane IDEWorkspace IDEM
where
primPaneName _ = __ "Workspace"
getAddedIndex _ = 0
getTopWidget = castToWidget . topBox
paneId b = "*Workspace"
-- | The additional state used when recovering the pane
-- (none, the packages and their targets come from the IDE state)
data WorkspaceState = WorkspaceState
deriving(Eq,Ord,Read,Show,Typeable)
instance RecoverablePane IDEWorkspace WorkspaceState IDEM where
saveState p = return (Just WorkspaceState)
recoverState pp WorkspaceState = do
nb <- getNotebook pp
buildPane pp nb builder
buildPane pp nb builder = do
res <- buildThisPane pp nb builder
when (isJust res) $ updateWorkspace True False
return res
builder pp nb windows = reifyIDE $ \ideR -> do
treeStore <- treeStoreNew []
treeView <- treeViewNew
treeViewSetModel treeView treeStore
renderer0 <- cellRendererPixbufNew
set renderer0 [ newAttrFromMaybeStringProperty "stock-id" := (Nothing :: Maybe Text) ]
renderer1 <- cellRendererTextNew
col1 <- treeViewColumnNew
treeViewColumnSetTitle col1 (__ "Package")
treeViewColumnSetSizing col1 TreeViewColumnAutosize
treeViewColumnSetResizable col1 True
treeViewColumnSetReorderable col1 True
treeViewAppendColumn treeView col1
cellLayoutPackStart col1 renderer0 False
cellLayoutPackStart col1 renderer1 True
cellLayoutSetAttributes col1 renderer0 treeStore
$ \row -> [newAttrFromMaybeStringProperty "stock-id" :=
if fst row
then Just stockYes
else Nothing]
cellLayoutSetAttributes col1 renderer1 treeStore
$ \row -> [ cellText := name row ]
renderer2 <- cellRendererTextNew
col2 <- treeViewColumnNew
treeViewColumnSetTitle col2 (__ "File path")
treeViewColumnSetSizing col2 TreeViewColumnAutosize
treeViewColumnSetResizable col2 True
treeViewColumnSetReorderable col2 True
treeViewAppendColumn treeView col2
cellLayoutPackStart col2 renderer2 True
cellLayoutSetAttributes col2 renderer2 treeStore
$ \row -> [ cellText := T.pack $ file row ]
treeViewSetHeadersVisible treeView True
sel <- treeViewGetSelection treeView
treeSelectionSetMode sel SelectionSingle
sw <- scrolledWindowNew Nothing Nothing
scrolledWindowSetShadowType sw ShadowIn
containerAdd sw treeView
scrolledWindowSetPolicy sw PolicyAutomatic PolicyAutomatic
box <- vBoxNew False 2
boxPackEnd box sw PackGrow 0
let workspacePane = IDEWorkspace sw treeView treeStore box
widgetShowAll box
cid1 <- treeView `after` focusInEvent $ do
liftIO $ reflectIDE (makeActive workspacePane) ideR
return True
(cid2, cid3) <- treeViewContextMenu treeView $ workspaceContextMenu ideR workspacePane
cid4 <- treeView `on` rowActivated $ workspaceSelect ideR workspacePane
return (Just workspacePane, map ConnectC [cid1, cid2, cid3, cid4])
where
name (_, (_, Just exe)) = exe
name (_, (pack, Nothing)) = packageIdentifierToString $ ipdPackageId pack
file (_, (_, Just _)) = ""
file (_, (pack, _)) = ipdCabalFile pack
-- | Get the workspace pane
getWorkspace :: Maybe PanePath -> IDEM IDEWorkspace
getWorkspace Nothing = forceGetPane (Right "*Workspace")
getWorkspace (Just pp) = forceGetPane (Left pp)
-- | Show the workspace pane
showWorkspace :: IDEAction
showWorkspace = do
l <- getWorkspace Nothing
displayPane l False
-- | Try to get the current selected record and whether
-- it is selected
getSelectionTree :: TreeView
-> TreeStore (Bool, WorkspaceRecord)
-> IO (Maybe (Bool, IDEPackage, Maybe Text))
getSelectionTree treeView treeStore = do
liftIO $ debugM "leksah" "getSelectionTree"
treeSelection <- treeViewGetSelection treeView
rows <- treeSelectionGetSelectedRows treeSelection
case rows of
[path] -> do
val <- treeStoreGetValue treeStore path
case val of
(active, (p, exe)) -> return $ Just (active, p, exe)
_ -> return Nothing
-- | Construct the context menu for the pane
workspaceContextMenu :: IDERef
-> IDEWorkspace
-> Menu
-> IO ()
workspaceContextMenu ideR workspacePane theMenu = do
item1 <- menuItemNewWithLabel (__ "Activate Package")
item2 <- menuItemNewWithLabel (__ "Add Package")
item3 <- menuItemNewWithLabel (__ "Remove Package")
item1 `on` menuItemActivate $ do
sel <- getSelectionTree (treeViewC workspacePane)
(workspaceStore workspacePane)
case sel of
Just (_, ideP,mbExe) -> reflectIDE (workspaceTry $ workspaceActivatePackage ideP mbExe) ideR
otherwise -> return ()
item2 `on` menuItemActivate $ reflectIDE (workspaceTry workspaceAddPackage) ideR
item3 `on` menuItemActivate $ do
sel <- getSelectionTree (treeViewC workspacePane)
(workspaceStore workspacePane)
case sel of
Just (_,ideP,_) -> reflectIDE (workspaceTry $ workspaceRemovePackage ideP) ideR
otherwise -> return ()
menuShellAppend theMenu item1
menuShellAppend theMenu item2
menuShellAppend theMenu item3
-- | Try to activate the package pointed to by the given 'TreePath'
-- by modifying the given IDE state
workspaceSelect :: IDERef
-> IDEWorkspace
-> TreePath
-> TreeViewColumn
-> IO ()
workspaceSelect ideR workspacePane path _ = do
liftIO $ debugM "leksah" "workspaceSelect"
(_,(ideP,mbExe)) <- treeStoreGetValue (workspaceStore workspacePane) path
reflectIDE (workspaceTry $ workspaceActivatePackage ideP mbExe) ideR
-- | Repopulates the workspace pane with the current packages
-- (if the pane is open)
updateWorkspace :: Bool -- ^ Show the pane if it is open and a workspace is loaded
-> Bool -- ^ Empty the 'bufferProjCache'
-> IDEAction
updateWorkspace showPane updateFileCache = do
liftIO $ debugM "leksah" "updateWorkspace"
mbWs <- readIDE workspace
when updateFileCache $ modifyIDE_ (\ide -> ide{bufferProjCache = Map.empty})
mbMod <- getPane
case mbWs of
Nothing -> do
case mbMod of
Nothing -> return ()
Just (p :: IDEWorkspace) -> do
liftIO $ treeStoreClear (workspaceStore p)
when showPane $ displayPane p False
refreshFiles
Just ws -> do
case mbMod of
Nothing -> return ()
Just (p :: IDEWorkspace) -> do
liftIO $ do
treeStoreClear (workspaceStore p)
treeStoreInsertForest (workspaceStore p) [] 0 (buildForest ws)
when showPane $ displayPane p False
refreshFiles
where
-- | Given a workspace, constructs the forest of
-- packages and their targets
buildForest ws =
let sorted = sortBy (compare `F.on` ipdPackageId) $ wsPackages ws
in map (buildPackageTree ws) sorted
-- | Given an ide package, constructs the tree with the
-- package as root node, and its targets as children
buildPackageTree ws ideP =
let isActive = Just (ipdCabalFile ideP) == wsActivePackFile ws
sandboxChildren = map (\pack -> Node (False, (pack, Nothing)) [])
(ipdSandboxSources ideP)
targetsChildren = map (\test -> Node (
Just (ipdCabalFile ideP) == wsActivePackFile ws &&
Just test == wsActiveExe ws, (ideP, Just test)) [])
(ipdExes ideP ++ ipdTests ideP ++ ipdBenchmarks ideP)
in Node (isActive, (ideP, Nothing))
(sandboxChildren ++ targetsChildren)