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
]