packages feed

ghcprofview-0.1.0.0: src/Gui/Page.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE OverloadedLabels #-}

module Gui.Page where

import Control.Monad
import qualified Data.Text as T
import Data.IORef

import GI.Gtk hiding (main)

import Gui
import Gui.TreeWidget
import Gui.Utils

import Types
import Operations

data Page = Page {
    pageWidget :: Box
  , pageSearchState :: IORef (Int, [TreePath])
  }

type ShowTree = T.Text -> CostCentreData -> IO ()

mkContextMenu :: TreeView -> CostCentreData -> ShowTree -> IO Menu
mkContextMenu tree ccd showTree = do
  menu <- menuNew

--   mkMenuItem menu "Test" $ do
--     withSelected tree $ \store selected -> do
--       Just name <- getItem store selected nameColumn
--       print (name :: T.Text)
--       Just mod <- getItem store selected moduleColumn
--       Just src <- getItem store selected sourceColumn
--       let subtrees = ccdFind mod src name ccd
--       forM_ subtrees $ \child -> do
--         let parent = case ccdParent child of
--                        Nothing -> "no parent"
--                        Just parent -> T.pack (ccdRecordIds parent) <> ": " <> ccdLabel parent <> " = " <> T.pack (show $ ccdTimeInherited parent)
--         print $ T.pack (ccdRecordIds child) <> ": " <> ccdLabel child <> " = " <> T.pack (show $ ccdTimeInherited child) <> " => " <> parent

  mkMenuItem menu "Narrow view to this item" $ do
    withSelected tree $ \store selected -> do
      path <- getTruePath store selected
      Just idxs <- treePathGetIndices path
      case ccdByPath idxs ccd of
        Nothing -> return ()
        Just child -> do
          let label = ccdLabel child
          showTree ("Narrowed view: " <> label) child

  mkMenuItem menu "Group all outgoing calls" $
    withSelected tree $ \store selected -> do
      Just name <- getItem store selected nameColumn
      Just mod <- getItem store selected moduleColumn
      Just src <- getItem store selected sourceColumn
      let subtrees = ccdFind mod src name ccd
          result = ccdSum subtrees
      showTree ("Calls of " <> name) result

  mkMenuItem menu "Group all incoming calls" $
    withSelected tree $ \store selected -> do
      Just name <- getItem store selected nameColumn
      Just mod <- getItem store selected moduleColumn
      Just src <- getItem store selected sourceColumn
      let subtrees = ccdFindIncoming mod src name ccd
          result = ccdSum subtrees
      showTree ("Calls to " <> name) result
      
  return menu

mkPage :: Statusbar -> T.Text -> CostCentreData -> ShowTree -> IO Page
mkPage status label ccd showTree = do
  vbox <- boxNew OrientationVertical 0
  searchHbox <- boxNew OrientationHorizontal 0
  filterBox <- boxNew OrientationHorizontal 0

  entry <- searchEntryNew
  boxPackStart searchHbox entry True True 0
  searchButton <- buttonNewWithLabel "Search"
  searchNextButton <- buttonNewWithLabel "Next"
  searchMethodCombo <- mkComboBox [
                           (Contains, "Contains")
                         , (Exact, "Exact")
                         , (Regexp, "Reg.Exp")
                       ]

  boxPackStart searchHbox searchButton False False 0
  boxPackStart searchHbox searchNextButton False False 0
  boxPackStart searchHbox searchMethodCombo False False 0
  boxPackStart vbox searchHbox False False 0

  on entry #activate $ buttonClicked searchButton

  let addFilterPercent name = do
        lbl <- labelNew (Just name)
        spin <- spinButtonNewWithRange 0 100 1
        spinButtonSetDigits spin 2
        boxPackStart filterBox lbl False False 10
        boxPackStart filterBox spin True True 0
        return spin

  let addFilterNumber name = do
        lbl <- labelNew (Just name)
        spin <- spinButtonNewWithRange 0 (1e38) 1
        spinButtonSetDigits spin 0
        boxPackStart filterBox lbl False False 10
        boxPackStart filterBox spin True True 0
        return spin

  let addFilterText name = do
        lbl <- labelNew (Just name)
        entry <- entryNew
        boxPackStart filterBox lbl False False 10
        boxPackStart filterBox entry True True 0
        return entry

  fltrEntries <- addFilterNumber "Entries:"
  fltrTimeIndividual <- addFilterPercent "Time Individual:"
  fltrAllocIndividual <- addFilterPercent "Alloc Individual:"
  fltrTimeInherited <- addFilterPercent "Time Inherited:"
  fltrAllocInherited <- addFilterPercent "Alloc Inherited:"
  fltrModule <- addFilterText "Module:"
  fltrSource <- addFilterText "Source:"

  filterButton <- buttonNewWithLabel "Filter"

  boxPackStart filterBox filterButton False False 0
  boxPackStart vbox filterBox False False 0

  tree <- mkTreeView treeWidgetConfig ccd
  treeViewSetSearchColumn tree 1
  treeViewSetEnableSearch tree False
  let noAdjustment = Nothing :: Maybe Adjustment
  scroll <- scrolledWindowNew noAdjustment noAdjustment
  containerAdd scroll tree
  boxPackStart vbox scroll True True 10

  statusContext <- statusbarGetContextId status label

  searchResults <- newIORef (0, [])

  let message text =
        void $ statusbarPush status statusContext (T.pack text)

  on searchButton #clicked $ do
    text <- entryGetText entry
    unless (T.null text) $ do
      Just methodId <- comboBoxGetActiveId searchMethodCombo
      let method = read $ T.unpack methodId
      results <- treeSearch tree method text
      if null results
        then message "Not found."
        else do
          message $ "Found: " ++ show (length results)
          writeIORef searchResults (0, results)
          Just store <- treeViewGetModel tree
          let path = head results
          treeViewExpandToPath tree path
          treeViewSetCursor tree path (Nothing :: Maybe TreeViewColumn) False

  on searchNextButton #clicked $ do
    (prevIndex, results) <- readIORef searchResults
    if null results
      then message "Not found."
      else do
        let n = length results
            index = (prevIndex + 1) `mod` n
            path = results !! index
        message $ "Found: " ++ show index ++ "/" ++ show n
        writeIORef searchResults (index, results)
        treeViewExpandToPath tree path
        treeViewSetCursor tree path (Nothing :: Maybe TreeViewColumn) False

  on filterButton #clicked $ do
    entries <- spinButtonGetValueAsInt fltrEntries
    timeIndividual <- spinButtonGetValue fltrTimeIndividual
    allocIndividual <- spinButtonGetValue fltrAllocIndividual
    timeInherited <- spinButtonGetValue fltrTimeInherited
    allocInherited <- spinButtonGetValue fltrAllocInherited
    mod <- entryGetText fltrModule
    src <- entryGetText fltrSource

    let params = FilterParams {
          fpEntries = fromIntegral entries
        , fpTimeIndividual = timeIndividual
        , fpAllocIndividual = allocIndividual
        , fpTimeInherited = timeInherited
        , fpAllocInherited = allocInherited
        , fpModule = mod
        , fpSource = src
      }
    let ccd' = filterCcdRecursive (checkFilter params) ccd
    showTree "Filtered" ccd'

  on tree #buttonPressEvent $ \ev -> do
    button <- get ev #button
    when (button == 3) $ do
      menu <- mkContextMenu tree ccd showTree
      menuPopupAtPointer menu Nothing
    return False

  return $ Page vbox searchResults