packages feed

bustle-0.5.1: Bustle/UI/FilterDialog.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleContexts #-}
{-
Bustle.UI.FilterDialog: allows the user to filter the displayed log
Copyright © 2011 Collabora Ltd.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-}
module Bustle.UI.FilterDialog
  ( runFilterDialog
  )
where

import Data.List (intercalate, groupBy, findIndices)
import qualified Data.Set as Set
import Data.Set (Set)
import qualified Data.Function as F

import Graphics.UI.Gtk

import Bustle.Translation (__)
import Bustle.Types

namespace :: String
          -> (String, String)
namespace name = case reverse (findIndices (== '.') name) of
    []    -> ("", name)
    (i:_) -> splitAt (i + 1) name

formatNames :: (UniqueName, Set OtherName)
            -> String
formatNames (u, os)
    | Set.null os = unUniqueName u
    | otherwise = intercalate "\n" . map (formatGroup . groupGroup) $ groups
  where
    groups = groupBy ((==) `F.on` fst) . map (namespace . unOtherName) $ Set.toAscList os

    groupGroup [] = error "unpossible empty group from groupBy"
    groupGroup xs@((ns, _):_) = (ns, map snd xs)

    formatGroup (ns, [y]) = ns ++ y
    formatGroup (ns, ys)  = ns ++ "{" ++ (intercalate "," ys) ++ "}"

type NameStore = ListStore (Bool, (UniqueName, Set OtherName))

makeStore :: [(UniqueName, Set OtherName)]
          -> Set UniqueName
          -> IO NameStore
makeStore names currentlyHidden = do
    listStoreNew $ map toPair names
  where
    toPair (name@(u, _)) = (not (Set.member u currentlyHidden), name)

makeView :: NameStore
         -> IO ScrolledWindow
makeView nameStore = do
    nameView <- treeViewNewWithModel nameStore
    -- We want rules because otherwise it's tough to see where each group
    -- starts and ends
    treeViewSetRulesHint nameView True
    treeViewSetHeadersVisible nameView False
    widgetSetSizeRequest nameView 600 371

    tickyCell <- cellRendererToggleNew
    tickyColumn <- treeViewColumnNew
    treeViewColumnPackStart tickyColumn tickyCell True
    treeViewAppendColumn nameView tickyColumn

    cellLayoutSetAttributes tickyColumn tickyCell nameStore $ \(ticked, _) ->
        [ cellToggleActive := ticked ]

    on tickyCell cellToggled $ \pathstr -> do
        let [i] = stringToTreePath pathstr
        (v, ns) <- listStoreGetValue nameStore i
        listStoreSetValue nameStore i (not v, ns)

    nameCell <- cellRendererTextNew
    nameColumn <- treeViewColumnNew
    treeViewColumnPackStart nameColumn nameCell True
    treeViewAppendColumn nameView nameColumn

    cellLayoutSetAttributes nameColumn nameCell nameStore $ \(_, ns) ->
        [ cellText := formatNames ns ]

    sw <- scrolledWindowNew Nothing Nothing
    scrolledWindowSetPolicy sw PolicyAutomatic PolicyAutomatic
    containerAdd sw nameView

    return sw

runFilterDialog :: WindowClass parent
                => parent -- ^ The window to which to attach the dialog
                -> [(UniqueName, Set OtherName)] -- ^ Names, in order of appearance
                -> Set UniqueName -- ^ Currently-hidden names
                -> IO (Set UniqueName) -- ^ The set of names to *hide*
runFilterDialog parent names currentlyHidden = do
    d <- dialogNew
    (windowWidth, windowHeight) <- windowGetSize parent
    windowSetDefaultSize d (windowWidth * 7 `div` 8) (windowHeight `div` 2)
    d `set` [ windowTransientFor := parent ]
    dialogAddButton d stockClose ResponseClose
    vbox <- fmap castToBox $ dialogGetContentArea d
    boxSetSpacing vbox 6

    nameStore <- makeStore names currentlyHidden
    sw <- makeView nameStore

    instructions <- labelNew (Nothing :: Maybe String)
    widgetSetSizeRequest instructions 600 (-1)
    labelSetMarkup instructions
        (__ "Unticking a service hides its column in the diagram, \
        \and all messages it is involved in. That is, all methods it calls \
        \or are called on it, the corresponding returns, and all signals it \
        \emits will be hidden.")
    labelSetLineWrap instructions True
    boxPackStart vbox instructions PackNatural 0

    boxPackStart vbox sw PackGrow 0
    widgetShowAll vbox

    _ <- dialogRun d

    widgetDestroy d

    results <- listStoreToList nameStore
    return $ Set.fromList [ u
                          | (ticked, (u, _)) <- results
                          , not ticked
                          ]