diff --git a/Bustle/Loader/Pcap.hs b/Bustle/Loader/Pcap.hs
--- a/Bustle/Loader/Pcap.hs
+++ b/Bustle/Loader/Pcap.hs
@@ -28,16 +28,12 @@
 
 import Data.Maybe (fromMaybe)
 import Data.Either (partitionEithers)
-import Data.List (isSuffixOf)
 import qualified Data.Map as Map
 import Data.Map (Map)
-import Control.Exception (try, tryJust)
+import Control.Exception (try)
 import Control.Monad.State
 import System.IO.Error ( mkIOError
                        , userErrorType
-                       , isUserError
-                       , ioeGetErrorString
-                       , ioeSetErrorString
                        )
 
 import Network.Pcap
@@ -47,7 +43,6 @@
 import qualified Data.ByteString as BS
 
 import qualified Bustle.Types as B
-import Bustle.Translation (__)
 
 -- Conversions from dbus-core's types into Bustle's more stupid types. This
 -- whole section is pretty upsetting.
diff --git a/Bustle/Renderer.hs b/Bustle/Renderer.hs
--- a/Bustle/Renderer.hs
+++ b/Bustle/Renderer.hs
@@ -42,8 +42,8 @@
 import Data.List.NonEmpty (NonEmpty(..))
 import qualified Data.Set as Set
 import Data.Set (Set)
-import qualified Data.Map as Map
-import Data.Map (Map)
+import qualified Data.Map.Strict as Map
+import Data.Map.Strict (Map)
 
 import Control.Arrow (first)
 import Control.Monad
@@ -59,10 +59,6 @@
          | SystemBus
     deriving (Show, Eq, Ord)
 
-describeBus :: Bus -> String
-describeBus SessionBus = "session"
-describeBus SystemBus = "system"
-
 -- We keep the column in the map to allow the Monoid instance to preserve the
 -- ordering returned by sessionParticipants, which is the only view on this
 -- type exported.
@@ -142,8 +138,8 @@
 instance Monoid apps => Monoid (RendererResult apps) where
     mempty = RendererResult 0 0 [] [] mempty []
 
-processWithFilters :: (Log, Set UniqueName)
-                   -> (Log, Set UniqueName)
+processWithFilters :: (Log, NameFilter)
+                   -> (Log, NameFilter)
                    -> RendererResult ()
 processWithFilters (sessionBusLog, sessionFilter)
                    (systemBusLog,  systemFilter ) =
@@ -159,7 +155,7 @@
 
 -- Doesn't let you filter
 rendererStateNew :: RendererState
-rendererStateNew = initialState Set.empty Set.empty
+rendererStateNew = initialState emptyNameFilter emptyNameFilter
 
 buildResult :: RendererOutput
             -> RendererState
@@ -243,7 +239,8 @@
              , nextColumn :: Double
              , columnsInUse :: Set Double
              , pending :: Pending
-             , bsIgnoredNames :: Set UniqueName
+             , bsFilter :: NameFilter
+             , nextFakeName :: Integer
              }
 
 data RendererState =
@@ -254,7 +251,7 @@
                   , startTime :: Microseconds
                   }
 
-initialBusState :: Set UniqueName
+initialBusState :: NameFilter
                 -> Double
                 -> BusState
 initialBusState ignore x =
@@ -263,17 +260,18 @@
              , nextColumn = x
              , columnsInUse = Set.empty
              , pending = Map.empty
-             , bsIgnoredNames = ignore
+             , bsFilter = ignore
+             , nextFakeName = 0
              }
 
-initialSessionBusState, initialSystemBusState :: Set UniqueName -> BusState
+initialSessionBusState, initialSystemBusState :: NameFilter -> BusState
 initialSessionBusState f =
     initialBusState f $ timestampAndMemberWidth + firstColumnOffset
 initialSystemBusState f =
     initialBusState f $ negate firstColumnOffset
 
-initialState :: Set UniqueName
-             -> Set UniqueName
+initialState :: NameFilter
+             -> NameFilter
              -> RendererState
 initialState sessionFilter systemFilter = RendererState
     { sessionBusState = initialSessionBusState sessionFilter
@@ -359,18 +357,12 @@
     case filter (Set.member o . aiCurrentNames . snd) (Map.assocs as) of
         [details] -> return details
 
-        -- No matches indicates a corrupt log, which we try to recover from …
+        -- No known owner for the well-known name. This happens in many cases,
+        -- especially when a method call causes service activation.
         []        -> do
-            warn $ concat [ "'"
-                          , unOtherName o
-                          , "' appeared unheralded on the "
-                          , describeBus bus
-                          , " bus; making something up..."
-                          ]
-            let namesInUse = Map.keys as
-                candidates = map (fakeUniqueName . show)
-                                 ([1..] :: [Integer])
-                u = head $ filter (not . (`elem` namesInUse)) candidates
+            n <- getsBusState nextFakeName bus
+            modifyBusState bus $ \bs -> bs { nextFakeName = n + 1 }
+            let u = fakeUniqueName (show n)
             addUnique bus u
             addOther bus o u
             ai <- lookupUniqueName bus u
@@ -648,9 +640,12 @@
            -> Message
            -> Renderer Bool
 shouldShow bus m = do
-    ignored <- getsBusState bsIgnoredNames bus
+    nameFilter <- getsBusState bsFilter bus
     names <- mapM (fmap fst . lookupApp bus) (mentionedNames m)
-    return $ Set.null (ignored `Set.intersection` Set.fromList names)
+    return $
+        not (any (flip Set.member $ nfNever nameFilter) names)
+        && (Set.null (nfOnly nameFilter)
+            || any (flip Set.member $ nfOnly nameFilter) names)
 
 processOne :: Bus
            -> Detailed Event
diff --git a/Bustle/Types.hs b/Bustle/Types.hs
--- a/Bustle/Types.hs
+++ b/Bustle/Types.hs
@@ -38,6 +38,12 @@
   , unOtherName
   , unBusName
 
+  , NameFilter(..)
+  , emptyNameFilter
+  , nameFilterAddOnly
+  , nameFilterAddNever
+  , nameFilterRemove
+
   , dbusName
   , dbusInterface
 
@@ -68,6 +74,8 @@
             )
 import Data.Maybe (maybeToList)
 import Data.Either (partitionEithers)
+import Data.Set (Set)
+import qualified Data.Set as Set
 
 type Serial = Word32
 
@@ -94,6 +102,33 @@
 unBusName :: TaggedBusName -> String
 unBusName (U (UniqueName x)) = formatBusName x
 unBusName (O (OtherName  x)) = formatBusName x
+
+data NameFilter =
+    NameFilter { nfOnly :: Set UniqueName
+               , nfNever :: Set UniqueName
+               }
+  deriving
+    (Show, Eq, Ord)
+
+emptyNameFilter :: NameFilter
+emptyNameFilter = NameFilter Set.empty Set.empty
+
+nameFilterModify :: (Set UniqueName -> Set UniqueName)
+                 -> (Set UniqueName -> Set UniqueName)
+                 -> NameFilter
+                 -> NameFilter
+nameFilterModify updateOnly updateNever nf =
+    nf { nfOnly = updateOnly $ nfOnly nf
+       , nfNever = updateNever $ nfNever nf
+       }
+
+nameFilterAddOnly, nameFilterAddNever, nameFilterRemove
+    :: UniqueName
+    -> NameFilter
+  -> NameFilter
+nameFilterAddOnly u  = nameFilterModify (Set.insert u) (Set.delete u)
+nameFilterAddNever u = nameFilterModify (Set.delete u) (Set.insert u)
+nameFilterRemove u   = nameFilterModify (Set.delete u) (Set.delete u)
 
 -- These useful constants disappeared from dbus in the grand removing of the
 -- -core suffix.
diff --git a/Bustle/UI.hs b/Bustle/UI.hs
--- a/Bustle/UI.hs
+++ b/Bustle/UI.hs
@@ -30,7 +30,6 @@
 
 import Data.IORef
 import qualified Data.Map as Map
-import qualified Data.Set as Set
 import Data.List (intercalate)
 import Data.Time
 import Data.Tuple (swap)
@@ -672,7 +671,7 @@
            rr = io $ void $ do
     wiSetLogDetails wi logDetails
 
-    hiddenRef <- newIORef Set.empty
+    nameFilterRef <- newIORef emptyNameFilter
 
     updateDisplayedLog wi rr
 
@@ -697,13 +696,12 @@
                     widgetHide sidebarHeader
 
     widgetSetSensitivity filterNames True
-    onMenuItemActivate filterNames $ do
-        hidden <- readIORef hiddenRef
-        hidden' <- runFilterDialog window (sessionParticipants $ rrApplications rr) hidden
-        writeIORef hiddenRef hidden'
-        let rr' = processWithFilters (sessionMessages, hidden') (systemMessages, Set.empty)
-
-        updateDisplayedLog wi rr'
+    onMenuItemActivate filterNames $
+        -- FIXME: also allow filtering system bus in two-bus case
+        runFilterDialog window (sessionParticipants $ rrApplications rr) nameFilterRef $ do
+            nameFilter <- readIORef nameFilterRef
+            let rr' = processWithFilters (sessionMessages, nameFilter) (systemMessages, emptyNameFilter)
+            updateDisplayedLog wi rr'
 
 showOpenDialog :: Window
                -> B ()
diff --git a/Bustle/UI/DetailsView.hs b/Bustle/UI/DetailsView.hs
--- a/Bustle/UI/DetailsView.hs
+++ b/Bustle/UI/DetailsView.hs
@@ -93,23 +93,19 @@
 
 getErrorName :: Detailed a -> Maybe String
 getErrorName (Detailed _ _ _ rm) = case rm of
-    (D.ReceivedMethodError _ (MethodError { methodErrorName = ErrorName en})) -> Just en
-    _                                                                         -> Nothing
-
-getErrorMessage :: Detailed a
-                -> Maybe String
-getErrorMessage (Detailed _ _ _ (D.ReceivedMethodError _ (MethodError _ _ _ _ body))) =
-    case D.fromVariant <$> body of
-        [message] -> message
-        _         -> Nothing
-getErrorMessage _ = Nothing
+    D.ReceivedMethodError _ MethodError{ methodErrorName = ErrorName en} -> Just en
+    _                                                                    -> Nothing
 
 formatMessage :: Detailed Message -> String
 formatMessage (Detailed _ _ _ rm) =
-    formatArgs $ D.receivedMessageBody rm
+    case (rm, D.fromVariant <$> body) of
+        -- Special-case errors, which (are supposed to) have a single
+        -- human-readable string argument
+        (D.ReceivedMethodError _ _, [Just message]) -> message
+        _                                           -> formatted
   where
-    formatArgs = intercalate "\n" . map (format_Variant VariantStyleSignature)
--- TODO: suppress escaping and type sig for errors, which are always (s)
+    body = D.receivedMessageBody rm
+    formatted = intercalate "\n" $ map (format_Variant VariantStyleSignature) body
 
 detailsViewGetTop :: DetailsView -> Widget
 detailsViewGetTop = toWidget . detailsGrid
@@ -117,15 +113,13 @@
 setOptionalRow :: OptionalRow
                -> Maybe String
                -> IO ()
-setOptionalRow (caption, label) s_ = do
-    case s_ of
-        Just s -> do
-            labelSetText label s
-            widgetShow label
-            widgetShow caption
-        Nothing -> do
-            widgetHide label
-            widgetHide caption
+setOptionalRow (caption, label) (Just s) = do
+    labelSetText label s
+    widgetShow label
+    widgetShow caption
+setOptionalRow (caption, label) Nothing = do
+    widgetHide label
+    widgetHide caption
 
 detailsViewUpdate :: DetailsView
                   -> Detailed Message
@@ -143,8 +137,6 @@
 
     labelSetText (detailsPath d) (maybe unknown (D.formatObjectPath . path) member_)
     labelSetMarkup (detailsMember d) (maybe unknown getMemberMarkup member_)
-    textBufferSetText buf $ case getErrorMessage m of
-        Just message -> message
-        Nothing      -> formatMessage m
+    textBufferSetText buf (formatMessage m)
   where
     unknown = ""
diff --git a/Bustle/UI/FilterDialog.hs b/Bustle/UI/FilterDialog.hs
--- a/Bustle/UI/FilterDialog.hs
+++ b/Bustle/UI/FilterDialog.hs
@@ -1,8 +1,8 @@
-{-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE FlexibleContexts #-}
 {-
 Bustle.UI.FilterDialog: allows the user to filter the displayed log
 Copyright © 2011 Collabora Ltd.
+Copyright © 2019 Will Thompson
 
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
@@ -23,28 +23,52 @@
   )
 where
 
-import Data.List (intercalate, groupBy, elemIndices)
+import Control.Monad (forM_)
+import Data.List (intercalate, groupBy, elemIndices, elemIndex)
 import qualified Data.Set as Set
 import Data.Set (Set)
 import qualified Data.Function as F
+import Data.IORef
 
 import Graphics.UI.Gtk
+import Graphics.UI.Gtk.ModelView.CellRendererCombo (cellComboTextModel)
 
 import Bustle.Translation (__)
 import Bustle.Types
 
+import Paths_bustle
+
+data NameVisibility = NameVisibilityDefault
+                    | NameVisibilityOnly
+                    | NameVisibilityNever
+  deriving (Show, Eq, Ord, Enum, Bounded)
+
+nameVisibilityName :: NameVisibility
+                   -> String
+nameVisibilityName v = case v of
+    NameVisibilityDefault -> __ "Default"
+    NameVisibilityOnly    -> __ "Only this"
+    NameVisibilityNever   -> __ "Hidden"
+
+data NameEntry = NameEntry { neUniqueName :: UniqueName
+                           , neOtherNames :: Set OtherName
+                           , neVisibility :: NameVisibility
+                           }
+
 namespace :: String
           -> (String, String)
 namespace name = case reverse (elemIndices '.' name) of
     []    -> ("", name)
     (i:_) -> splitAt (i + 1) name
 
-formatNames :: (UniqueName, Set OtherName)
+formatNames :: NameEntry
             -> String
-formatNames (u, os)
-    | Set.null os = unUniqueName u
+formatNames ne
+    | Set.null os = unUniqueName (neUniqueName ne)
     | otherwise = intercalate "\n" . map (formatGroup . groupGroup) $ groups
   where
+    os = neOtherNames ne
+
     groups = groupBy ((==) `F.on` fst) . map (namespace . unOtherName) $ Set.toAscList os
 
     groupGroup [] = error "unpossible empty group from groupBy"
@@ -53,89 +77,128 @@
     formatGroup (ns, [y]) = ns ++ y
     formatGroup (ns, ys)  = ns ++ "{" ++ intercalate "," ys ++ "}"
 
-type NameStore = ListStore (Bool, (UniqueName, Set OtherName))
+type NameStore = ListStore NameEntry
 
 makeStore :: [(UniqueName, Set OtherName)]
-          -> Set UniqueName
+          -> NameFilter
           -> IO NameStore
-makeStore names currentlyHidden =
-    listStoreNew $ map toPair names
+makeStore names nameFilter =
+    listStoreNew $ map toNameEntry 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
+    toNameEntry (u, os) = NameEntry { neUniqueName = u
+                                    , neOtherNames = os
+                                    , neVisibility = toVisibility u
+                                    }
+    toVisibility u | Set.member u (nfOnly nameFilter)  = NameVisibilityOnly
+                   | Set.member u (nfNever nameFilter) = NameVisibilityNever
+                   | otherwise                         = NameVisibilityDefault
 
-    cellLayoutSetAttributes tickyColumn tickyCell nameStore $ \(ticked, _) ->
-        [ cellToggleActive := ticked ]
+nameStoreUpdate :: NameStore
+                -> Int
+                -> (NameEntry -> NameEntry)
+                -> IO ()
+nameStoreUpdate nameStore i f = do
+    ne <- listStoreGetValue nameStore i
+    listStoreSetValue nameStore i $ f ne
 
-    on tickyCell cellToggled $ \pathstr -> do
-        let [i] = stringToTreePath pathstr
-        (v, ns) <- listStoreGetValue nameStore i
-        listStoreSetValue nameStore i (not v, ns)
+makeView :: NameStore
+         -> TreeView
+         -> IO ()
+makeView nameStore nameView = do
+    treeViewSetModel nameView (Just nameStore)
 
+    -- Bus name column
     nameCell <- cellRendererTextNew
     nameColumn <- treeViewColumnNew
+    nameColumn `set` [ treeViewColumnTitle := __ "Bus Name"
+                     , treeViewColumnExpand := True
+                     ]
     treeViewColumnPackStart nameColumn nameCell True
     treeViewAppendColumn nameView nameColumn
 
-    cellLayoutSetAttributes nameColumn nameCell nameStore $ \(_, ns) ->
-        [ cellText := formatNames ns ]
+    cellLayoutSetAttributes nameColumn nameCell nameStore $ \ne ->
+        [ cellText := formatNames ne ]
 
-    sw <- scrolledWindowNew Nothing Nothing
-    scrolledWindowSetPolicy sw PolicyAutomatic PolicyAutomatic
-    containerAdd sw nameView
+    -- TreeStore of possible visibility states
+    let nameVisibilities = [minBound..]
+    let nameVisibilityNames = map nameVisibilityName nameVisibilities
+    visibilityModel <- listStoreNew nameVisibilityNames
+    let visibilityNameCol = makeColumnIdString 1
+    treeModelSetColumn visibilityModel visibilityNameCol id
 
-    return sw
+    -- Visibility column
+    comboCell <- cellRendererComboNew
+    comboCell `set` [ cellTextEditable := True
+                    , cellComboHasEntry := False
+                    ]
 
+    comboColumn <- treeViewColumnNew
+    comboColumn `set` [ treeViewColumnTitle := __ "Visibility"
+                      , treeViewColumnExpand := False
+                      ]
+    treeViewColumnPackStart comboColumn comboCell True
+    treeViewAppendColumn nameView comboColumn
+
+    cellLayoutSetAttributes comboColumn comboCell nameStore $ \ne ->
+        [ cellComboTextModel := (visibilityModel, visibilityNameCol)
+        , cellText :=> do
+            let Just j = elemIndex (neVisibility ne) nameVisibilities
+            listStoreGetValue visibilityModel j
+        ]
+    comboCell `on` edited $ \[i] str -> do
+        let (Just j) = elemIndex str nameVisibilityNames
+        nameStoreUpdate nameStore i $ \ne ->
+            ne { neVisibility = nameVisibilities !! j }
+
+    return ()
+
 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)
+                -> IORef NameFilter -- ^ Current filter
+                -> IO () -- ^ Callback when filter changes
+                -> IO ()
+runFilterDialog parent names filterRef callback = do
+    builder <- builderNew
+    builderAddFromFile builder =<< getDataFileName "data/FilterDialog.ui"
+
+    d <- builderGetObject builder castToDialog ("filterDialog" :: String)
+    (_, windowHeight) <- windowGetSize parent
+    windowSetDefaultSize d (-1) (windowHeight * 3 `div` 4)
     d `set` [ windowTransientFor := parent ]
-    dialogAddButton d stockClose ResponseClose
-    vbox <- castToBox <$> dialogGetContentArea d
-    boxSetSpacing vbox 6
 
-    nameStore <- makeStore names currentlyHidden
-    sw <- makeView nameStore
+    nameStore <- makeStore names =<< readIORef filterRef
+    makeView nameStore =<< builderGetObject builder castToTreeView ("filterTreeView" :: String)
 
-    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
+    resetButton <- builderGetObject builder castToButton ("resetButton" :: String)
+    resetButton `on` buttonActivated $ do
+        n <- listStoreGetSize nameStore
+        forM_ [0..n-1] $ \i -> do
+            ne <- listStoreGetValue nameStore i
+            case neVisibility ne of
+                NameVisibilityDefault -> return ()
+                _                     -> listStoreSetValue nameStore i $
+                    ne { neVisibility = NameVisibilityDefault }
 
-    boxPackStart vbox sw PackGrow 0
-    widgetShowAll vbox
+    let updateResetSensitivity = do
+            nf <- readIORef filterRef
+            let isEmpty = Set.null (nfOnly nf) && Set.null (nfNever nf)
+            widgetSetSensitive resetButton $ not isEmpty
 
+    updateResetSensitivity
+    nameStore `on` rowChanged $ \[i] _iter -> do
+        ne <- listStoreGetValue nameStore i
+        let u = neUniqueName ne
+        -- Should we smush this into nameFilterModify, move the enum into
+        -- Bustle.Types?
+        let f = case neVisibility ne of
+                NameVisibilityDefault -> nameFilterRemove
+                NameVisibilityOnly    -> nameFilterAddOnly
+                NameVisibilityNever   -> nameFilterAddNever
+        modifyIORef' filterRef $ f u
+        updateResetSensitivity
+        callback
+
     _ <- dialogRun d
 
     widgetDestroy d
-
-    results <- listStoreToList nameStore
-    return $ Set.fromList [ u
-                          | (ticked, (u, _)) <- results
-                          , not ticked
-                          ]
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -35,7 +35,6 @@
 
 * Ideally, automate the steps below
 * Write news in `NEWS.md` and `data/org.freedesktop.Bustle.appdata.xml.in`
-* Update `po/messages.pot`
 * Update version number in `bustle.cabal`
 
 ```sh
diff --git a/INSTALL.md b/INSTALL.md
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -1,47 +1,37 @@
-Building from source
-====================
-
-First, make sure the Haskell Platform is installed, preferably along with the
-Gtk+ bindings for Haskell, and some other dependencies. On Debian-flavoured
-systems, well, actually just `apt-get build-dep bustle`, but:
-
-    sudo apt-get install \
-        pkg-config \
-        libdbus-1-dev \
-        libglib2.0-dev \
-        libpcap0.8-dev \
-        haskell-platform \
-        libghc-mtl-dev \
-        libghc-cairo-dev \
-        libghc-gtk-dev \
-        libghc-glade-dev \
-        libghc-dbus-dev \
-        libghc-pcap-dev \
-        help2man
+Installing from Flathub
+=======================
 
-(If you can't get the Haskell Platform via your package manager, see
-<http://hackage.haskell.org/platform/>. If you can't get the Gtk+ binding for
-Haskell via your package manager, you'll need to run:
+If you don't want to modify Bustle, by far the easiest way to install it is to
+[get it from Flathub](https://gitlab.freedesktop.org/bustle/bustle).
 
-    cabal install gtk2hs-buildtools
+<a href='https://flathub.org/apps/details/org.freedesktop.Bustle'><img width='240' alt='Download on Flathub' src='https://flathub.org/assets/badges/flathub-badge-en.png'/></a>
 
-and ensure that ~/.cabal/bin is in your PATH before continuing.)
+Building from source
+====================
 
-Got that? Great!
+I recommend using Stack; see the instructions in
+[CONTRIBUTING.md](./CONTRIBUTING.md). You can also build a Git checkout using
+Flatpak:
 
-    export PREFIX=/opt/bustle
+```
+flatpak-builder --install --user --force-clean app flatpak/org.freedesktop.Bustle.yaml
+```
 
-    # Build and install Bustle itself.
-    cabal install --prefix=$PREFIX
+On exotic platforms with no Haskell toolchain
+=============================================
 
-    # Build and install the stand-alone logger binary, plus the icons, desktop
-    # file, etc. etc.
-    make install PREFIX=$PREFIX
+If you're working on an embedded platform, you may have D-Bus but no Haskell
+toolchain, and you may not want to bootstrap everything just to run Bustle on
+the target device. That's fine: Bustle was originally written for exactly this
+situation!
 
-If the Haskell Platform is not available on the platform you want to do
-some D-Bus profiling on, that's fine: the logger is written in C, and
-you can view logs generated on your fancy embedded hardware on your more
-pedestrian Linux laptop. The logger only depends on a few widely-available
-libraries:
+First, install Bustle on your (x86_64) development system as above. You then
+have two options to monitor D-Bus traffic on the target device:
 
-    sudo apt-get install libglib2.0-dev libpcap-dev
+1. On the target device, run `dbus-monitor --pcap --session >session.pcap`, and
+   hit `Ctrl+C` when you're done. Then copy `session.pcap` to your development
+   system, and open it in Bustle.
+2. On the target device, arrange for D-Bus to be accessible via TCP or via some
+   kind of socket forwarding. On your development system, run Bustle, choose
+   _Record → Record Address_, and enter the remote address in the same form
+   accepted by `dbus-monitor`.
diff --git a/NEWS.md b/NEWS.md
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,3 +1,17 @@
+Bustle 0.7.5 (2019-03-08)
+-------------------------
+
+User-facing changes:
+
+* As well as being able to filter out messages involving certain
+  services, you can now also filter messages to only show certain
+  services.
+
+Internal changes:
+
+* SVGs are now 256×256px to placate flatpak-validate-icon
+* Add Nix compatibility (Daniel Firth)
+
 Bustle 0.7.4 (2018-12-07)
 -------------------------
 
diff --git a/bustle.cabal b/bustle.cabal
--- a/bustle.cabal
+++ b/bustle.cabal
@@ -1,6 +1,6 @@
 Name:           bustle
 Category:       Network, Desktop
-Version:        0.7.4
+Version:        0.7.5
 Cabal-Version:  2.0
 Tested-With:    GHC == 8.4.3
 Synopsis:       Draw sequence diagrams of D-Bus traffic
@@ -11,6 +11,7 @@
 Maintainer:     Will Thompson <will@willthompson.co.uk>
 Homepage:       https://gitlab.freedesktop.org/bustle/bustle#readme
 Data-files:     data/bustle.ui,
+                data/FilterDialog.ui,
                 data/OpenTwoDialog.ui,
                 data/RecordAddressDialog.ui,
                 LICENSE
@@ -36,7 +37,6 @@
 
                   -- wow many translate
                   , po/*.po
-                  , po/*.pot
 
                   -- intl bits
                   , data/org.freedesktop.Bustle.appdata.xml.in
diff --git a/data/FilterDialog.ui b/data/FilterDialog.ui
new file mode 100644
--- /dev/null
+++ b/data/FilterDialog.ui
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface>
+  <requires lib="gtk+" version="3.20"/>
+  <object class="GtkDialog" id="filterDialog">
+    <property name="can_focus">False</property>
+    <property name="type_hint">dialog</property>
+    <child type="titlebar">
+      <object class="GtkHeaderBar">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="title" translatable="yes">Filter Visible Services</property>
+        <property name="has_subtitle">False</property>
+        <property name="show_close_button">True</property>
+        <child>
+          <object class="GtkButton" id="resetButton">
+            <property name="label" translatable="yes">_Reset</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="use_underline">True</property>
+          </object>
+        </child>
+      </object>
+    </child>
+    <child internal-child="vbox">
+      <object class="GtkBox">
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <!-- FIXME: round-tripping through Glade removes this property. -->
+        <property name="border_width">0</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox">
+            <property name="can_focus">False</property>
+            <property name="layout_style">end</property>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkScrolledWindow">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="border_width">0</property>
+            <property name="hscrollbar_policy">never</property>
+            <property name="shadow_type">in</property>
+            <child>
+              <object class="GtkTreeView" id="filterTreeView">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="headers_clickable">False</property>
+                <property name="rules_hint">True</property>
+                <child internal-child="selection">
+                  <object class="GtkTreeSelection"/>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/data/icons/hicolor/scalable/apps/org.freedesktop.Bustle-symbolic.svg b/data/icons/hicolor/scalable/apps/org.freedesktop.Bustle-symbolic.svg
--- a/data/icons/hicolor/scalable/apps/org.freedesktop.Bustle-symbolic.svg
+++ b/data/icons/hicolor/scalable/apps/org.freedesktop.Bustle-symbolic.svg
@@ -9,12 +9,12 @@
    xmlns="http://www.w3.org/2000/svg"
    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="600"
-   height="600"
+   width="256"
+   height="256"
    id="svg2"
    version="1.1"
-   inkscape:version="0.91 r13725"
-   sodipodi:docname="bustle-symbolic.svg">
+   inkscape:version="0.92.4 5da689c313, 2019-01-14"
+   sodipodi:docname="org.freedesktop.Bustle-symbolic.svg">
   <defs
      id="defs4" />
   <sodipodi:namedview
@@ -24,16 +24,16 @@
      borderopacity="1.0"
      inkscape:pageopacity="0.0"
      inkscape:pageshadow="2"
-     inkscape:zoom="1.18"
-     inkscape:cx="296.02826"
-     inkscape:cy="407.83651"
+     inkscape:zoom="9.6796875"
+     inkscape:cx="128"
+     inkscape:cy="93.172193"
      inkscape:document-units="px"
      inkscape:current-layer="layer1"
      showgrid="false"
-     inkscape:window-width="1440"
-     inkscape:window-height="824"
+     inkscape:window-width="3200"
+     inkscape:window-height="1671"
      inkscape:window-x="0"
-     inkscape:window-y="27"
+     inkscape:window-y="55"
      inkscape:window-maximized="1"
      fit-margin-top="0"
      fit-margin-left="0"
@@ -48,8 +48,10 @@
        visible="true"
        enabled="true"
        snapvisiblegridlinesonly="true"
-       originx="-56.941089px"
-       originy="-465.18127px" />
+       originx="-228.94108"
+       originy="-779.84559"
+       spacingx="1"
+       spacingy="1" />
   </sodipodi:namedview>
   <metadata
      id="metadata7">
@@ -67,38 +69,41 @@
      inkscape:label="Layer 1"
      inkscape:groupmode="layer"
      id="layer1"
-     transform="translate(-56.941089,12.819102)">
-    <path
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#dedede;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:57.94400024;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:5.19999981;stroke-dasharray:115.88800049, 57.94400024;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-       d="m 349.58008,-12.728516 c -19.78034,0.499891 -39.51808,2.890348 -58.82031,7.2539096 4.10872,18.8723954 8.21745,37.7447914 12.32617,56.6171874 33.74925,-7.665785 69.12626,-7.971229 103.02539,-1.0293 3.71633,-18.821141 8.65912,-38.435398 11.61328,-56.7636714 -22.39128,-4.5980686 -45.29405,-6.6135646 -68.14453,-6.0781256 z m 105.80078,78.716797 c 31.47813,14.008295 59.87299,34.867319 82.70703,60.664059 14.44727,-12.81901 28.89453,-25.63802 43.3418,-38.457028 C 553.05494,56.141376 517.74279,30.253699 478.60547,12.900391 470.86393,30.596354 463.1224,48.292318 455.38086,65.988281 Z M 227.18164,16.626953 C 189.51961,34.723972 155.71119,60.77538 128.625,92.591797 c 14.7487,12.470703 29.49739,24.941413 44.24609,37.412113 22.37137,-26.20404 50.37565,-47.593699 81.60157,-62.169926 -8.17123,-17.501302 -16.34245,-35.002604 -24.51368,-52.503906 -0.92578,0.432292 -1.85156,0.864583 -2.77734,1.296875 z M 568.0293,168.66016 c 16.99699,30.09909 27.33124,63.91871 30.12695,98.36914 19.23893,-1.70833 38.47787,-3.41667 57.7168,-5.125 -3.5014,-42.55686 -16.29754,-84.32012 -37.26568,-121.51953 -16.85936,9.42513 -33.71871,18.85026 -50.57807,28.27539 z M 89.939453,150.27539 c -18.701268,36.38678 -29.811885,76.6502 -32.357428,117.48438 19.276042,1.22135 38.552083,2.44271 57.828125,3.66406 2.15345,-34.4883 11.88032,-68.47463 28.29492,-98.88281 -17.08268,-9.01302 -34.16536,-18.02605 -51.248045,-27.03907 -0.839191,1.59115 -1.678381,3.18229 -2.517572,4.77344 z"
-       id="path4626"
-       inkscape:connector-curvature="0" />
-    <circle
-       style="fill:none;stroke:#bebebe;stroke-width:75.08624021;stroke-miterlimit:5.19999981;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
-       id="path2985-7"
-       cx="381.42856"
-       cy="433.79074"
-       transform="matrix(0.50880116,0,0,0.55776489,164.13818,45.848517)"
-       ry="118.57143"
-       rx="130" />
-    <path
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#bebebe;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:60;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:5.19999981;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-       d="m 430,220 30,55 -85,0 0,50 85,0 -30,55 170,-80 z"
-       transform="translate(56.941089,-12.819102)"
-       id="path3757-7"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cccccccc" />
-    <path
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#bebebe;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:60;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:5.19999981;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-       d="m 170,220 -170,80 170,80 -30,-55 85,0 0,-50 -85,0 z"
-       transform="translate(56.941089,-12.819102)"
-       id="path3761-7"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cccccccc" />
-    <path
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#bebebe;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:34.40008163;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:5.19999981;stroke-dasharray:none;stroke-dashoffset:94.40000153;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-       d="m 356.94141,187.18164 c -55.02475,0 -100.00001,44.97526 -100,100 -1e-5,55.02475 44.97525,100 100,100 55.02475,0 100,-44.97525 100,-100 0,-55.02475 -44.97525,-100 -100,-100 z m 0,34.39844 c 36.43357,0 65.59961,29.168 65.59961,65.60156 0,36.43356 -29.16604,65.59961 -65.59961,65.59961 -36.43357,0 -65.59961,-29.16605 -65.59961,-65.59961 0,-36.43356 29.16604,-65.60156 65.59961,-65.60156 z"
-       id="path4619"
-       inkscape:connector-curvature="0" />
+     transform="translate(-228.94108,-16.516576)">
+    <g
+       id="g1404"
+       transform="matrix(0.42666667,0,0,0.42666667,204.64622,21.983358)">
+      <path
+         inkscape:connector-curvature="0"
+         id="path4626"
+         d="m 349.58008,-12.728516 c -19.78034,0.499891 -39.51808,2.890348 -58.82031,7.2539096 4.10872,18.8723954 8.21745,37.7447914 12.32617,56.6171874 33.74925,-7.665785 69.12626,-7.971229 103.02539,-1.0293 3.71633,-18.821141 8.65912,-38.435398 11.61328,-56.7636714 -22.39128,-4.5980686 -45.29405,-6.6135646 -68.14453,-6.0781256 z m 105.80078,78.716797 c 31.47813,14.008295 59.87299,34.867319 82.70703,60.664059 14.44727,-12.81901 28.89453,-25.63802 43.3418,-38.457028 C 553.05494,56.141376 517.74279,30.253699 478.60547,12.900391 470.86393,30.596354 463.1224,48.292318 455.38086,65.988281 Z M 227.18164,16.626953 C 189.51961,34.723972 155.71119,60.77538 128.625,92.591797 c 14.7487,12.470703 29.49739,24.941413 44.24609,37.412113 22.37137,-26.20404 50.37565,-47.593699 81.60157,-62.169926 -8.17123,-17.501302 -16.34245,-35.002604 -24.51368,-52.503906 -0.92578,0.432292 -1.85156,0.864583 -2.77734,1.296875 z M 568.0293,168.66016 c 16.99699,30.09909 27.33124,63.91871 30.12695,98.36914 19.23893,-1.70833 38.47787,-3.41667 57.7168,-5.125 -3.5014,-42.55686 -16.29754,-84.32012 -37.26568,-121.51953 -16.85936,9.42513 -33.71871,18.85026 -50.57807,28.27539 z M 89.939453,150.27539 c -18.701268,36.38678 -29.811885,76.6502 -32.357428,117.48438 19.276042,1.22135 38.552083,2.44271 57.828125,3.66406 2.15345,-34.4883 11.88032,-68.47463 28.29492,-98.88281 -17.08268,-9.01302 -34.16536,-18.02605 -51.248045,-27.03907 -0.839191,1.59115 -1.678381,3.18229 -2.517572,4.77344 z"
+         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#dedede;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:57.94400024;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:5.19999981;stroke-dasharray:115.88800049, 57.94400024;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+      <circle
+         transform="matrix(0.50880116,0,0,0.55776489,164.13818,45.848517)"
+         cy="433.79074"
+         cx="381.42856"
+         id="path2985-7"
+         style="fill:none;stroke:#bebebe;stroke-width:75.08624268;stroke-miterlimit:5.19999981;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         r="0" />
+      <path
+         sodipodi:nodetypes="cccccccc"
+         inkscape:connector-curvature="0"
+         id="path3757-7"
+         transform="translate(56.941089,-12.819102)"
+         d="m 430,220 30,55 h -85 v 50 h 85 l -30,55 170,-80 z"
+         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#bebebe;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:60;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:5.19999981;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+      <path
+         sodipodi:nodetypes="cccccccc"
+         inkscape:connector-curvature="0"
+         id="path3761-7"
+         transform="translate(56.941089,-12.819102)"
+         d="M 170,220 0,300 170,380 140,325 h 85 v -50 h -85 z"
+         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#bebebe;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:60;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:5.19999981;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+      <path
+         inkscape:connector-curvature="0"
+         id="path4619"
+         d="m 356.94141,187.18164 c -55.02475,0 -100.00001,44.97526 -100,100 -1e-5,55.02475 44.97525,100 100,100 55.02475,0 100,-44.97525 100,-100 0,-55.02475 -44.97525,-100 -100,-100 z m 0,34.39844 c 36.43357,0 65.59961,29.168 65.59961,65.60156 0,36.43356 -29.16604,65.59961 -65.59961,65.59961 -36.43357,0 -65.59961,-29.16605 -65.59961,-65.59961 0,-36.43356 29.16604,-65.60156 65.59961,-65.60156 z"
+         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#bebebe;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:34.40008163;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:5.19999981;stroke-dasharray:none;stroke-dashoffset:94.40000153;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+    </g>
   </g>
 </svg>
diff --git a/data/icons/hicolor/scalable/apps/org.freedesktop.Bustle.svg b/data/icons/hicolor/scalable/apps/org.freedesktop.Bustle.svg
--- a/data/icons/hicolor/scalable/apps/org.freedesktop.Bustle.svg
+++ b/data/icons/hicolor/scalable/apps/org.freedesktop.Bustle.svg
@@ -9,12 +9,12 @@
    xmlns="http://www.w3.org/2000/svg"
    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="605.06"
-   height="605.06"
+   width="256"
+   height="256"
    id="svg2"
    version="1.1"
-   inkscape:version="0.48.4 r9939"
-   sodipodi:docname="bustle.svg">
+   inkscape:version="0.92.4 5da689c313, 2019-01-14"
+   sodipodi:docname="org.freedesktop.Bustle.svg">
   <defs
      id="defs4">
     <inkscape:path-effect
@@ -94,7 +94,7 @@
        height="2"
        y="-0.5"
        x="-0.30000001"
-       color-interpolation-filters="sRGB">
+       style="color-interpolation-filters:sRGB">
       <feTurbulence
          id="feTurbulence4106"
          baseFrequency="0.4"
@@ -124,7 +124,7 @@
        height="2"
        y="-0.5"
        x="-0.30000001"
-       color-interpolation-filters="sRGB">
+       style="color-interpolation-filters:sRGB">
       <feTurbulence
          id="feTurbulence4185"
          baseFrequency="0.4"
@@ -157,18 +157,18 @@
      borderopacity="1.0"
      inkscape:pageopacity="0.0"
      inkscape:pageshadow="2"
-     inkscape:zoom="0.59"
-     inkscape:cx="351.05408"
-     inkscape:cy="216.30891"
+     inkscape:zoom="9.2695312"
+     inkscape:cx="128"
+     inkscape:cy="71.987112"
      inkscape:document-units="px"
      inkscape:current-layer="layer1"
      showgrid="false"
-     inkscape:window-width="1388"
-     inkscape:window-height="833"
-     inkscape:window-x="50"
-     inkscape:window-y="223"
-     inkscape:window-maximized="0"
-     fit-margin-top="0"
+     inkscape:window-width="3200"
+     inkscape:window-height="1671"
+     inkscape:window-x="0"
+     inkscape:window-y="55"
+     inkscape:window-maximized="1"
+     fit-margin-top="1"
      fit-margin-left="0"
      fit-margin-right="0"
      fit-margin-bottom="0">
@@ -179,8 +179,10 @@
        visible="true"
        enabled="true"
        snapvisiblegridlinesonly="true"
-       originx="-56.941089px"
-       originy="-465.18127px" />
+       originx="-232.64974"
+       originy="-799.15823"
+       spacingx="1"
+       spacingy="1" />
   </sodipodi:namedview>
   <metadata
      id="metadata7">
@@ -190,7 +192,7 @@
         <dc:format>image/svg+xml</dc:format>
         <dc:type
            rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title />
+        <dc:title></dc:title>
       </cc:Work>
     </rdf:RDF>
   </metadata>
@@ -198,56 +200,53 @@
      inkscape:label="Layer 1"
      inkscape:groupmode="layer"
      id="layer1"
-     transform="translate(-56.941089,17.879098)">
-    <path
-       style="fill:none;stroke:#74b674;stroke-width:27.59247017;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:82.77741051, 27.59247017;stroke-dashoffset:27.59247017"
-       d="m 70.761814,288.36996 c -0.01207,-1.2669 -0.02449,-2.53589 -0.02449,-3.80671 0,-159.41492 129.271656,-288.6461129 288.736206,-288.6461129 159.46457,0 288.73621,129.2311929 288.73621,288.6461129 l 0,0 0,0 c 0,1.27082 -0.009,2.53969 -0.0245,3.80671"
-       id="path3802-8"
-       inkscape:connector-curvature="0" />
-    <path
-       sodipodi:type="arc"
-       style="fill:none;stroke:#000000;stroke-width:34.42315674;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
-       id="path2985-7"
-       sodipodi:cx="381.42856"
-       sodipodi:cy="433.79074"
-       sodipodi:rx="130"
-       sodipodi:ry="118.57143"
-       d="m 511.42856,433.79074 a 130,118.57143 0 1 1 -260,0 130,118.57143 0 1 1 260,0 z"
-       transform="matrix(0.50880116,0,0,0.55776489,164.13818,45.848517)"
-       />
-    <path
-       style="fill:none;stroke:#000000;stroke-width:18.45480156;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-       d="m 417.41556,287.80173 211.44171,0"
-       id="path3757-7"
-       inkscape:path-effect="#path-effect3759-7"
-       inkscape:original-d="m 417.41556,287.80173 211.44171,0"
-       inkscape:connector-curvature="0"
-       />
-    <path
-       style="fill:none;stroke:#000000;stroke-width:18.17925262;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-       d="m 294.08479,287.66116 -211.718251,0.2812"
-       id="path3761-7"
-       inkscape:path-effect="#path-effect3763-2"
-       inkscape:original-d="m 294.08479,287.66116 -211.718251,0.2812"
-       inkscape:connector-curvature="0"
-       />
-    <path
-       style="fill:none;stroke:#000000;stroke-width:18.33793068;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-       d="M 200.97285,226.67532 77.897224,289.6408 200.97285,348.92819"
-       id="path3768-2"
-       inkscape:path-effect="#path-effect3770-6"
-       inkscape:original-d="M 200.97285,226.67532 77.897224,289.6408 200.97285,348.92819"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccc"
-       />
-    <path
-       style="fill:none;stroke:#000000;stroke-width:18.33793068;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-       d="M 520.66838,225.39683 643.74401,288.36232 520.66838,347.6497"
-       id="path3768-9-7"
-       inkscape:path-effect="#path-effect3770-3-1"
-       inkscape:original-d="M 520.66838,225.39683 643.74401,288.36232 520.66838,347.6497"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ccc"
-       />
+     transform="translate(-232.64974,2.7960645)">
+    <g
+       id="g1412"
+       transform="matrix(0.41816393,0,0,0.41816393,209.83903,7.3082603)">
+      <path
+         inkscape:connector-curvature="0"
+         id="path3802-8"
+         d="m 70.761814,288.36996 c -0.01207,-1.2669 -0.02449,-2.53589 -0.02449,-3.80671 0,-159.41492 129.271656,-288.6461129 288.736206,-288.6461129 159.46457,0 288.73621,129.2311929 288.73621,288.6461129 v 0 0 c 0,1.27082 -0.009,2.53969 -0.0245,3.80671"
+         style="fill:none;stroke:#74b674;stroke-width:27.59247017;stroke-miterlimit:4;stroke-dasharray:82.77741051, 27.59247017;stroke-dashoffset:27.59247017;stroke-opacity:1" />
+      <ellipse
+         transform="matrix(0.50880116,0,0,0.55776489,164.13818,45.848517)"
+         id="path2985-7"
+         style="fill:none;stroke:#000000;stroke-width:34.42315674;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         cx="381.42856"
+         cy="433.79074"
+         rx="130"
+         ry="118.57143" />
+      <path
+         inkscape:connector-curvature="0"
+         inkscape:original-d="M 417.41556,287.80173 H 628.85727"
+         inkscape:path-effect="#path-effect3759-7"
+         id="path3757-7"
+         d="M 417.41556,287.80173 H 628.85727"
+         style="fill:none;stroke:#000000;stroke-width:18.45480156;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         inkscape:connector-curvature="0"
+         inkscape:original-d="m 294.08479,287.66116 -211.718251,0.2812"
+         inkscape:path-effect="#path-effect3763-2"
+         id="path3761-7"
+         d="m 294.08479,287.66116 -211.718251,0.2812"
+         style="fill:none;stroke:#000000;stroke-width:18.17925262;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="ccc"
+         inkscape:connector-curvature="0"
+         inkscape:original-d="M 200.97285,226.67532 77.897224,289.6408 200.97285,348.92819"
+         inkscape:path-effect="#path-effect3770-6"
+         id="path3768-2"
+         d="M 200.97285,226.67532 77.897224,289.6408 200.97285,348.92819"
+         style="fill:none;stroke:#000000;stroke-width:18.33793068;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="ccc"
+         inkscape:connector-curvature="0"
+         inkscape:original-d="M 520.66838,225.39683 643.74401,288.36232 520.66838,347.6497"
+         inkscape:path-effect="#path-effect3770-3-1"
+         id="path3768-9-7"
+         d="M 520.66838,225.39683 643.74401,288.36232 520.66838,347.6497"
+         style="fill:none;stroke:#000000;stroke-width:18.33793068;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+    </g>
   </g>
 </svg>
diff --git a/data/org.freedesktop.Bustle.appdata.xml.in b/data/org.freedesktop.Bustle.appdata.xml.in
--- a/data/org.freedesktop.Bustle.appdata.xml.in
+++ b/data/org.freedesktop.Bustle.appdata.xml.in
@@ -41,6 +41,11 @@
 		<id>bustle.desktop</id>
 	</provides>
 	<releases>
+		<release date="2019-03-08" version="0.7.5">
+			<description>
+				<p>As well as being able to filter out messages involving certain services, you can now also filter messages to only show certain services.</p>
+			</description>
+		</release>
 		<release date="2018-12-07" version="0.7.4">
 			<description>
 				<p>In the details for an error reply, the error name is now shown, and the error message is formatted more legibly.</p>
@@ -54,7 +59,7 @@
 		</release>
 		<release date="2018-07-24" version="0.7.2">
 			<description>
-				<p>You can now explore messages while they're being recorded. (Filtering, statistics and exporting are still only available once you stop recording.)</p>
+				<p>You can now explore messages while they're being recorded. Filtering, statistics and exporting are still only available once you stop recording.</p>
 				<p>The raw sender and destination for each message is now shown in the details pane.</p>
 				<p>Bytestrings with embedded NULs which are otherwise ASCII are now shown as ASCII strings.</p>
 			</description>
diff --git a/po/messages.pot b/po/messages.pot
deleted file mode 100644
--- a/po/messages.pot
+++ /dev/null
@@ -1,326 +0,0 @@
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
-#
-#, fuzzy
-msgid ""
-msgstr ""
-"Project-Id-Version: PACKAGE VERSION\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-12-07 09:43+0000\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: LANGUAGE <LL@li.org>\n"
-"Language: \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-
-#: Bustle/StatisticsPane.hs:189 Bustle/StatisticsPane.hs:192
-msgid "%.1f ms"
-msgstr ""
-
-#: Bustle/Noninteractive.hs:55
-msgid "(no interface)"
-msgstr ""
-
-#: Bustle/UI/AboutDialog.hs:46 data/bustle.ui:101
-#: data/org.freedesktop.Bustle.desktop.in:3
-#: data/org.freedesktop.Bustle.appdata.xml.in:9
-msgid "Bustle"
-msgstr ""
-
-#: Bustle/StatisticsPane.hs:190
-msgid "Calls"
-msgstr ""
-
-#: Bustle/UI.hs:456
-msgid "Close _Without Saving"
-msgstr ""
-
-#: Bustle/UI.hs:226
-msgid "Could not read '%s'"
-msgstr ""
-
-#: Bustle/UI.hs:763
-msgid "Couldn't export log as PDF: "
-msgstr ""
-
-#: Bustle/Noninteractive.hs:48
-msgid "Couldn't parse '%s': %s"
-msgstr ""
-
-#: Bustle/UI.hs:431
-msgid "Couldn't save log: "
-msgstr ""
-
-#: Bustle/StatisticsPane.hs:222 data/bustle.ui:787 data/bustle.ui:896
-msgid "Error"
-msgstr ""
-
-#: Bustle/StatisticsPane.hs:150
-msgid "Frequency"
-msgstr ""
-
-#: Bustle/UI.hs:455
-msgid "If you don't save, this log will be lost forever."
-msgstr ""
-
-#: Bustle/StatisticsPane.hs:229
-msgid "Largest"
-msgstr ""
-
-#: Bustle/UI.hs:239
-msgid "Logged <b>%u</b> messages"
-msgstr ""
-
-#: Bustle/StatisticsPane.hs:191 Bustle/StatisticsPane.hs:228
-msgid "Mean"
-msgstr ""
-
-#: Bustle/StatisticsPane.hs:135 Bustle/StatisticsPane.hs:213 data/bustle.ui:688
-msgid "Member"
-msgstr ""
-
-#: Bustle/StatisticsPane.hs:142 Bustle/StatisticsPane.hs:179
-msgid "Method"
-msgstr ""
-
-#: Bustle/StatisticsPane.hs:220 data/bustle.ui:762
-msgid "Method call"
-msgstr ""
-
-#: Bustle/StatisticsPane.hs:221 data/bustle.ui:774
-msgid "Method return"
-msgstr ""
-
-#: Bustle/UI.hs:348
-msgid "Recording %s&#8230;"
-msgstr ""
-
-#: Bustle/UI.hs:448
-msgid "Save log '%s' before closing?"
-msgstr ""
-
-#: Bustle/StatisticsPane.hs:143 Bustle/StatisticsPane.hs:223 data/bustle.ui:800
-msgid "Signal"
-msgstr ""
-
-#: Bustle/StatisticsPane.hs:227
-msgid "Smallest"
-msgstr ""
-
-#: Bustle/UI/AboutDialog.hs:48
-msgid "Someone's favourite D-Bus profiler"
-msgstr ""
-
-#: Bustle/StatisticsPane.hs:188
-msgid "Total"
-msgstr ""
-
-#: Bustle/UI/FilterDialog.hs:123
-msgid ""
-"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."
-msgstr ""
-
-#: Bustle/Util.hs:48
-msgid "Warning: "
-msgstr ""
-
-#: Bustle/UI.hs:433
-msgid ""
-"You might want to manually recover the log from the temporary file at \"%s\"."
-msgstr ""
-
-#: data/bustle.ui:14
-msgid "_Filter Visible Services…"
-msgstr ""
-
-#: data/bustle.ui:24
-msgid "_Statistics"
-msgstr ""
-
-#: data/bustle.ui:62
-msgid ""
-"Display two logs—one for the session bus, one for the system bus—side by "
-"side."
-msgstr ""
-
-#: data/bustle.ui:63
-msgid "O_pen a Pair of Logs…"
-msgstr ""
-
-#: data/bustle.ui:75
-msgid "Record S_ession Bus"
-msgstr ""
-
-#: data/bustle.ui:84
-msgid "Record S_ystem Bus"
-msgstr ""
-
-#: data/bustle.ui:93
-msgid "Record _Address…"
-msgstr ""
-
-#: data/bustle.ui:138
-msgid "Record a new log"
-msgstr ""
-
-#: data/bustle.ui:149
-msgid "_Record"
-msgstr ""
-
-#: data/bustle.ui:182
-msgid "_Stop"
-msgstr ""
-
-#: data/bustle.ui:200
-msgid "Open an existing log"
-msgstr ""
-
-#: data/bustle.ui:310
-msgid "Export as PDF"
-msgstr ""
-
-#: data/bustle.ui:334
-msgid "Save"
-msgstr ""
-
-#: data/bustle.ui:493
-msgid ""
-"Start recording D-Bus activity with the <b>Record</b> button above\n"
-" You can also run <i>dbus-monitor --pcap</i> from the command line"
-msgstr ""
-
-#: data/bustle.ui:512
-msgid "Welcome to Bustle"
-msgstr ""
-
-#: data/bustle.ui:537
-msgid "<big><b>Waiting for D-Bus traffic; please hold…</b></big>"
-msgstr ""
-
-#: data/bustle.ui:564
-msgid "Frequencies"
-msgstr ""
-
-#: data/bustle.ui:578
-msgid "Durations"
-msgstr ""
-
-#: data/bustle.ui:593
-msgid "Sizes"
-msgstr ""
-
-#: data/bustle.ui:640
-msgid "Type"
-msgstr ""
-
-#: data/bustle.ui:656
-msgid "Path"
-msgstr ""
-
-#: data/bustle.ui:720
-msgid "Arguments"
-msgstr ""
-
-#: data/bustle.ui:813
-msgid "Directed signal"
-msgstr ""
-
-#: data/bustle.ui:832
-msgid "Sender"
-msgstr ""
-
-#: data/bustle.ui:848
-msgid "Destination"
-msgstr ""
-
-#: data/org.freedesktop.Bustle.desktop.in:4
-#: data/org.freedesktop.Bustle.appdata.xml.in:10
-msgid "Draw sequence diagrams of D-Bus activity"
-msgstr ""
-
-#: data/org.freedesktop.Bustle.desktop.in:6
-msgid "org.freedesktop.Bustle"
-msgstr ""
-
-#: data/org.freedesktop.Bustle.desktop.in:11
-msgid "debug;profile;d-bus;dbus;sequence;monitor;"
-msgstr ""
-
-#. Translators: These are the application description paragraphs in the AppData file.
-#: data/org.freedesktop.Bustle.appdata.xml.in:13
-msgid ""
-"Bustle draws sequence diagrams of D-Bus activity. It shows signal emissions, "
-"method calls and their corresponding returns, with time stamps for each "
-"individual event and the duration of each method call. This can help you "
-"check for unwanted D-Bus traffic, and pinpoint why your D-Bus-based "
-"application is not performing as well as you like. It also provides "
-"statistics like signal frequencies and average method call times."
-msgstr ""
-
-#: data/org.freedesktop.Bustle.appdata.xml.in:26
-msgid "Explore sequence diagrams of D-Bus activity"
-msgstr ""
-
-#: data/org.freedesktop.Bustle.appdata.xml.in:30
-msgid "See statistics summarizing the log"
-msgstr ""
-
-#: data/org.freedesktop.Bustle.appdata.xml.in:34
-msgid "Relax with this soothing greyscale welcome page"
-msgstr ""
-
-#: data/org.freedesktop.Bustle.appdata.xml.in:46
-msgid ""
-"In the details for an error reply, the error name is now shown, and the "
-"error message is formatted more legibly."
-msgstr ""
-
-#: data/org.freedesktop.Bustle.appdata.xml.in:47
-msgid ""
-"The default file extension for log files is now ‘.pcap’, reflecting what "
-"they actually are."
-msgstr ""
-
-#: data/org.freedesktop.Bustle.appdata.xml.in:52
-msgid ""
-"Bustle now handles the application/vnd.tcpdump.pcap MIME type, which in "
-"practice means that your file manager will offer to open pcap files with "
-"Bustle."
-msgstr ""
-
-#: data/org.freedesktop.Bustle.appdata.xml.in:57
-msgid ""
-"You can now explore messages while they're being recorded. (Filtering, "
-"statistics and exporting are still only available once you stop recording.)"
-msgstr ""
-
-#: data/org.freedesktop.Bustle.appdata.xml.in:58
-msgid ""
-"The raw sender and destination for each message is now shown in the details "
-"pane."
-msgstr ""
-
-#: data/org.freedesktop.Bustle.appdata.xml.in:59
-msgid ""
-"Bytestrings with embedded NULs which are otherwise ASCII are now shown as "
-"ASCII strings."
-msgstr ""
-
-#: data/org.freedesktop.Bustle.appdata.xml.in:64
-msgid ""
-"It's now possible to monitor the system bus (from the user interface and "
-"with the bustle-pcap command-line tool), with no need to reconfigure the "
-"system bus. It's also possible to monitor an arbitrary bus by address."
-msgstr ""
-
-#: data/org.freedesktop.Bustle.appdata.xml.in:65
-msgid ""
-"Bustle now requires that dbus-monitor (≥ 1.9.10) and pkexec are installed on "
-"your system."
-msgstr ""
