diff --git a/gramps/gen/filters/rules/citation/__init__.py b/gramps/gen/filters/rules/citation/__init__.py
index d29446045..dfbcb25b6 100644
--- a/gramps/gen/filters/rules/citation/__init__.py
+++ b/gramps/gen/filters/rules/citation/__init__.py
@@ -37,11 +37,13 @@ from ._hasnotematchingsubstringof import HasNoteMatchingSubstringOf
 from ._hasnoteregexp import HasNoteRegexp
 from ._hasreferencecountof import HasReferenceCountOf
 from ._hassource import HasSource
+from ._hassourceidof import HasSourceIdOf
 from ._matchesfilter import MatchesFilter
 from ._matchespagesubstringof import MatchesPageSubstringOf
 from ._matchesrepositoryfilter import MatchesRepositoryFilter
 from ._matchessourcefilter import MatchesSourceFilter
 from ._regexpidof import RegExpIdOf
+from ._regexpsourceidof import RegExpSourceIdOf
 from ._hastag import HasTag
 
 editor_rule_list = [
@@ -56,10 +58,12 @@ editor_rule_list = [
     HasNoteRegexp,
     HasReferenceCountOf,
     HasSource,
+    HasSourceIdOf,
     MatchesFilter,
     MatchesPageSubstringOf,
     MatchesRepositoryFilter,
     MatchesSourceFilter, 
     RegExpIdOf,
+    RegExpSourceIdOf,
     HasTag
 ]
diff --git a/gramps/gen/filters/rules/citation/_hassourceidof.py b/gramps/gen/filters/rules/citation/_hassourceidof.py
new file mode 100644
index 000000000..f6ff7236f
--- /dev/null
+++ b/gramps/gen/filters/rules/citation/_hassourceidof.py
@@ -0,0 +1,58 @@
+#
+# Gramps - a GTK+/GNOME based genealogy program
+#
+# Copyright (C) 2002-2006  Donald N. Allingham
+# Copyright (C) 2011-2013  Tim G L Lyons
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
+
+# $Id$
+
+#-------------------------------------------------------------------------
+#
+# Standard Python modules
+#
+#-------------------------------------------------------------------------
+from ....const import GRAMPS_LOCALE as glocale
+_ = glocale.translation.gettext
+
+#-------------------------------------------------------------------------
+#
+# GRAMPS modules
+#
+#-------------------------------------------------------------------------
+from .._hasgrampsid import HasGrampsId
+
+#-------------------------------------------------------------------------
+#
+# HasSourceIdOf
+#
+#-------------------------------------------------------------------------
+class HasSourceIdOf(HasGrampsId):
+    """Rule that checks for a citation with a source which has a specific
+    GRAMPS ID"""
+
+    name        = _('Citation with Source <Id>')
+    description = _("Matches a citation with a source with a specified Gramps "
+                    "ID")
+    category    = _('Source filters')
+
+    def apply(self, dbase, citation):
+        source = dbase.get_source_from_handle(
+                                    citation.get_reference_handle())
+        if HasGrampsId.apply(self, dbase, source):
+            return True
+        return False
diff --git a/gramps/gen/filters/rules/citation/_regexpsourceidof.py b/gramps/gen/filters/rules/citation/_regexpsourceidof.py
new file mode 100644
index 000000000..b4c972a37
--- /dev/null
+++ b/gramps/gen/filters/rules/citation/_regexpsourceidof.py
@@ -0,0 +1,60 @@
+#
+# Gramps - a GTK+/GNOME based genealogy program
+#
+# Copyright (C) 2002-2006  Donald N. Allingham
+# Copyright (C) 2011       Tim G L Lyons
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
+
+# $Id$
+
+#-------------------------------------------------------------------------
+#
+# Standard Python modules
+#
+#-------------------------------------------------------------------------
+from ....const import GRAMPS_LOCALE as glocale
+_ = glocale.translation.gettext
+
+#-------------------------------------------------------------------------
+#
+# GRAMPS modules
+#
+#-------------------------------------------------------------------------
+from .._regexpidbase import RegExpIdBase
+
+#-------------------------------------------------------------------------
+#
+# HasIdOf
+#
+#-------------------------------------------------------------------------
+class RegExpSourceIdOf(RegExpIdBase):
+    """
+    Rule that checks for a citation  whose GRAMPS ID
+    matches regular expression.
+    """
+
+    name        = _('Citations with Source Id containing <text>')
+    description = _("Matches citations whose source has a Gramps ID that "
+                    "matches the regular expression")
+    category    = _('Source filters')
+
+    def apply(self, dbase, citation):
+        source = dbase.get_source_from_handle(
+                                    citation.get_reference_handle())
+        if RegExpIdBase.apply(self, dbase, source):
+            return True
+        return False
diff --git a/gramps/gui/filters/sidebar/_citationsidebarfilter.py b/gramps/gui/filters/sidebar/_citationsidebarfilter.py
index 2ef0be1ff..1905ec7af 100644
--- a/gramps/gui/filters/sidebar/_citationsidebarfilter.py
+++ b/gramps/gui/filters/sidebar/_citationsidebarfilter.py
@@ -41,7 +41,7 @@ from gi.repository import Gtk
 # GRAMPS modules
 #
 #-------------------------------------------------------------------------
-from ...widgets import MonitoredMenu
+from ...widgets import MonitoredMenu, DateEntry, BasicEntry
 from gramps.gen.lib import Citation
 from .. import build_filter_model
 from . import SidebarFilter
@@ -49,7 +49,8 @@ from gramps.gen.constfunc import cuni
 from gramps.gen.filters import GenericFilterFactory, rules
 from gramps.gen.filters.rules.citation import (RegExpIdOf, HasIdOf, HasCitation, 
                                         HasNoteMatchingSubstringOf, 
-                                        HasNoteRegexp, MatchesFilter, HasTag)
+                                        HasNoteRegexp, MatchesFilter, HasTag,
+                                        HasSource, RegExpSourceIdOf)
 from gramps.gen.utils.string import confidence
 GenericCitationFilter = GenericFilterFactory('Citation')
 #-------------------------------------------------------------------------
@@ -61,6 +62,12 @@ class CitationSidebarFilter(SidebarFilter):
 
     def __init__(self, dbstate, uistate, clicked):
         self.clicked_func = clicked
+        self.filter_src_id = BasicEntry()
+        self.filter_src_title = BasicEntry()
+        self.filter_src_author = BasicEntry()
+        self.filter_src_abbr = BasicEntry()
+        self.filter_src_pub = BasicEntry()
+        self.filter_src_note = BasicEntry()
         self.filter_id = Gtk.Entry()
         self.filter_page = Gtk.Entry()       
         self.filter_date = Gtk.Entry()
@@ -101,16 +108,28 @@ class CitationSidebarFilter(SidebarFilter):
         self.tag.pack_start(cell, True)
         self.tag.add_attribute(cell, 'text', 0)
 
-        self.add_text_entry(_('ID'), self.filter_id)
-        self.add_text_entry(_('Volume/Page'), self.filter_page)
-        self.add_text_entry(_('Date'), self.filter_date)
-        self.add_entry(_('Minimum Confidence|Min. Conf.'), self.filter_conf)
-        self.add_text_entry(_('Note'), self.filter_note)
+        self.add_text_entry(_('Source: ID'), self.filter_src_id)
+        self.add_text_entry(_('Source: Title'), self.filter_src_title)
+        self.add_text_entry(_('Source: Author'), self.filter_src_author)
+        self.add_text_entry(_('Source: Abbreviation'), self.filter_src_abbr)
+        self.add_text_entry(_('Source: Publication'), self.filter_src_pub)
+        self.add_text_entry(_('Source: Note'), self.filter_src_note)
+        self.add_text_entry(_('Citation: ID'), self.filter_id)
+        self.add_text_entry(_('Citation: Volume/Page'), self.filter_page)
+        self.add_text_entry(_('Citation: Date'), self.filter_date)
+        self.add_entry(_('Citation: Minimum Confidence|Min. Conf.'), self.filter_conf)
+        self.add_text_entry(_('Citation: Note'), self.filter_note)
         self.add_entry(_('Tag'), self.tag)
         self.add_filter_entry(_('Custom filter'), self.generic)
         self.add_entry(None, self.filter_regex)
 
     def clear(self, obj):
+        self.filter_src_id.set_text('')
+        self.filter_src_title.set_text('')
+        self.filter_src_author.set_text('')
+        self.filter_src_abbr.set_text('')
+        self.filter_src_pub.set_text('')
+        self.filter_src_note.set_text('')
         self.filter_id.set_text('')
         self.filter_page.set_text('')
         self.filter_date.set_text('')
@@ -120,6 +139,15 @@ class CitationSidebarFilter(SidebarFilter):
         self.generic.set_active(0)
 
     def get_filter(self):
+        src_id = unicode(self.filter_src_id.get_text()).strip()
+        src_title = unicode(self.filter_src_title.get_text()).strip()
+        src_author = unicode(self.filter_src_author.get_text()).strip()
+        src_abbr = unicode(self.filter_src_abbr.get_text()).strip()
+        src_pub = unicode(self.filter_src_pub.get_text()).strip()
+        src_note = unicode(self.filter_src_note.get_text()).strip()
+        gid = unicode(self.filter_id.get_text()).strip()
+        page = unicode(self.filter_page.get_text()).strip()
+        date = unicode(self.filter_date.get_text()).strip()
         gid = cuni(self.filter_id.get_text()).strip()
         page = cuni(self.filter_page.get_text()).strip()
         date = cuni(self.filter_date.get_text()).strip()
@@ -137,7 +165,9 @@ class CitationSidebarFilter(SidebarFilter):
         tag = self.tag.get_active() > 0
         gen = self.generic.get_active() > 0
 
-        empty = not (gid or page or date or conf or note or regex or tag or gen)
+        empty = not (src_id or src_title or src_author or src_abbr or src_pub or
+                     src_note or
+                     gid or page or date or conf or note or regex or gen)
         if empty:
             generic_filter = None
         else:
@@ -152,6 +182,14 @@ class CitationSidebarFilter(SidebarFilter):
             rule = HasCitation([page, date, conf], use_regex=regex)
             generic_filter.add_rule(rule)
                 
+            if src_id:
+                rule = RegExpSourceIdOf([src_id], use_regex=regex)
+                generic_filter.add_rule(rule)
+            
+            rule = HasSource([src_title, src_author, src_abbr, src_pub],
+                             use_regex=regex)
+            generic_filter.add_rule(rule)
+                
             if note:
                 if regex:
                     rule = HasNoteRegexp([note])
diff --git a/gramps/gui/selectors/selectcitation.py b/gramps/gui/selectors/selectcitation.py
index b80a98dfc..5f78cfa7d 100644
--- a/gramps/gui/selectors/selectcitation.py
+++ b/gramps/gui/selectors/selectcitation.py
@@ -64,7 +64,7 @@ class SelectCitation(BaseSelector):
 
     def get_column_titles(self):
         return [
-            (_('Page'), 350, BaseSelector.TEXT, 0),
+            (_('Name'), 350, BaseSelector.TEXT, 0),
             (_('ID'),     75, BaseSelector.TEXT, 1)
             ]
 
diff --git a/gramps/gui/views/treemodels/citationtreemodel.py b/gramps/gui/views/treemodels/citationtreemodel.py
index 819fa7bc8..2a9aa04a0 100644
--- a/gramps/gui/views/treemodels/citationtreemodel.py
+++ b/gramps/gui/views/treemodels/citationtreemodel.py
@@ -192,7 +192,20 @@ class CitationTreeModel(CitationBaseModel, TreeBaseModel):
         data        The object data.
         """
         sort_key = self.sort_func2(data)
+        # If the source for this citation already exists (in the tree model) we
+        # add the citation as a child of the source. Otherwise we add the source
+        # first (because citations don't have any meaning without the associated
+        # source)
         if self._get_node(data[5]):
+            #             parent   child   sortkey   handle
+            self.add_node(data[5], handle, sort_key, handle, secondary=True)
+        else:
+            # add the source node first
+            source_sort_key = self.sort_func(self.map(data[5]))
+            #            parent child    sortkey          handle
+            self.add_node(None, data[5], source_sort_key, data[5])
+            
+            #            parent    child   sortkey   handle
             self.add_node(data[5], handle, sort_key, handle, secondary=True)
 
     def on_get_n_columns(self):
diff --git a/gramps/gui/views/treemodels/treebasemodel.py b/gramps/gui/views/treemodels/treebasemodel.py
index 343ef9807..6f1591abd 100644
--- a/gramps/gui/views/treemodels/treebasemodel.py
+++ b/gramps/gui/views/treemodels/treebasemodel.py
@@ -342,7 +342,10 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel):
         self.__displayed = 0
 
         self.set_search(search)
-        self.rebuild_data(self.current_filter, skip)
+        if self.has_secondary:
+            self.rebuild_data(self.current_filter, self.current_filter2, skip)
+        else:
+            self.rebuild_data(self.current_filter, skip=skip)
 
         _LOG.debug(self.__class__.__name__ + ' __init__ ' +
                     str(time.clock() - cput) + ' sec')
@@ -362,7 +365,9 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel):
         self.rebuild_data = None
         self._build_data = None
         self.search = None
+        self.search2 = None
         self.current_filter = None
+        self.current_filter2 = None
         self.clear_cache()
         self.lru_data = None
 
@@ -447,31 +452,56 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel):
             if search[0] == 1: # Filter
                 #following is None if no data given in filter sidebar
                 self.search = search[1]
+                if self.has_secondary:
+                    self.search2 = search[1]
+                    _LOG.debug("search2 filter %s %s" % (search[0], search[1]))
                 self._build_data = self._rebuild_filter
             elif search[0] == 0: # Search
                 if search[1]:
                     # we have search[1] = (index, text_unicode, inversion)
                     col, text, inv = search[1]
-                    func = lambda x: self._get_value(x, col, store_cache=False) or ""
+                    func = lambda x: self._get_value(x, col, secondary=False) or u""
+                    if self.has_secondary:
+                        func2 = lambda x: self._get_value(x, col, secondary=True) or u""
                     if search[2]:
                         self.search = ExactSearchFilter(func, text, inv)
+                        if self.has_secondary:
+                            self.search2 = ExactSearchFilter(func2, text, inv)
                     else:
                         self.search = SearchFilter(func, text, inv)
+                        if self.has_secondary:
+                            self.search2 = SearchFilter(func2, text, inv)
                 else:
                     self.search = None
+                    if self.has_secondary:
+                        self.search2 = None
+                        _LOG.debug("search2 search with no data")
                 self._build_data = self._rebuild_search
             else: # Fast filter
                 self.search = search[1]
+                if self.has_secondary:
+                    self.search2 = search[2]
+                    _LOG.debug("search2 fast filter")
                 self._build_data = self._rebuild_search
         else:
             self.search = None
+            if self.has_secondary:
+                self.search2 = search[2]
+                _LOG.debug("search2 no search parameter")
             self._build_data = self._rebuild_search
             
         self.current_filter = self.search
+        if self.has_secondary:
+            self.current_filter2 = self.search2
 
-    def rebuild_data(self, data_filter=None, skip=[]):
+    def rebuild_data(self, data_filter=None, data_filter2=None, skip=[]):
         """
         Rebuild the data map.
+        
+        When called externally (from listview), data_filter and data_filter2
+        should be None; set_search will already have been called to establish
+        the filter functions. When called internally (from __init__) both
+        data_filter and data_filter2 will have been set from set_search
         """
         cput = time.clock()
         self.clear_cache()
@@ -481,16 +511,21 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel):
             return
 
         self.clear()
-        self._build_data(self.current_filter, skip)
+        if self.has_secondary:
+            self._build_data(self.current_filter, self.current_filter2, skip)
+        else:
+            self._build_data(self.current_filter, None, skip)
 
         self._in_build = False
 
         self.current_filter = data_filter
+        if self.has_secondary:
+            self.current_filter2 = data_filter2
 
         _LOG.debug(self.__class__.__name__ + ' rebuild_data ' +
                     str(time.clock() - cput) + ' sec')
 
-    def _rebuild_search(self, dfilter, skip):
+    def _rebuild_search(self, dfilter, dfilter2, skip):
         """
         Rebuild the data map where a search condition is applied.
         """
@@ -498,12 +533,14 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel):
         self.__displayed = 0
 
         items = self.number_items()
+        _LOG.debug("rebuild search primary")
         self.__rebuild_search(dfilter, skip, items, 
                               self.gen_cursor, self.add_row)
 
         if self.has_secondary:
+            _LOG.debug("rebuild search secondary")
             items = self.number_items2()
-            self.__rebuild_search(dfilter, skip, items, 
+            self.__rebuild_search(dfilter2, skip, items, 
                                   self.gen_cursor2, self.add_row2)
 
     def __rebuild_search(self, dfilter, skip, items, gen_cursor, add_func):
@@ -533,20 +570,27 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel):
         if not status.was_cancelled():
             status.end()
 
-    def _rebuild_filter(self, dfilter, skip):
+    def _rebuild_filter(self, dfilter, dfilter2, skip):
         """
         Rebuild the data map where a filter is applied.
         """
         self.__total = 0
         self.__displayed = 0
 
-        items = self.number_items()
-        self.__rebuild_filter(dfilter, skip, items, 
-                              self.gen_cursor, self.map, self.add_row)
-        if self.has_secondary:
-            items = self.number_items2()
+        if not self.has_secondary:
+            # The tree only has primary data
+            items = self.number_items()
+            _LOG.debug("rebuild filter primary")
             self.__rebuild_filter(dfilter, skip, items, 
-                                  self.gen_cursor2, self.map2, self.add_row2)
+                                  self.gen_cursor, self.map, self.add_row)
+        else:
+            # The tree has both primary and secondary data. The navigation type
+            # (navtype) which governs the filters that are offered, is for the
+            # secondary data.
+                items = self.number_items2()
+                _LOG.debug("rebuild filter secondary")
+                self.__rebuild_filter(dfilter2, skip, items, 
+                                      self.gen_cursor2, self.map2, self.add_row2)
             
     def __rebuild_filter(self, dfilter, skip, items, gen_cursor, data_map, 
                          add_func):
@@ -578,6 +622,7 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel):
         status.heartbeat()
 
         if dfilter:
+            _LOG.debug("rebuild filter %s" % dfilter)
             status_filter = progressdlg.LongOpStatus(msg=_("Applying filter"),
                         total_steps=items, interval=items//10)
             pmon.add_op(status_filter)
@@ -743,13 +788,14 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel):
         if self._get_node(handle) is not None:
             return # row already exists
         cput = time.clock()
-        if not self.search or \
-                (self.search and self.search.match(handle, self.db)):
-            #row needs to be added to the model
-            data = self.map(handle)
-            if data:
+        data = self.map(handle)
+        if data:
+            if not self.search or \
+                    (self.search and self.search.match(handle, self.db)):
                 self.add_row(handle, data)
-            else:
+        else:
+            if not self.search2 or \
+                    (self.search2 and self.search2.match(handle, self.db)):
                 self.add_row2(handle, self.map2(handle))
 
         _LOG.debug(self.__class__.__name__ + ' add_row_by_handle ' +
@@ -916,6 +962,9 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel):
         """
         Returns the contents of a given column of a gramps object
         """
+        if secondary is None:
+            raise NotImplementedError
+
         if handle in self.lru_data:
             data = self.lru_data[handle]
         else:
@@ -923,16 +972,20 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel):
                 data = self.map(handle)
             else:
                 data = self.map2(handle)
-        if not self._in_build and store_cache:
-            self.lru_data[handle] = data
-
-        try:
-            if not secondary:
-                return self.fmap[col](data)
-            else:
-                return self.fmap2[col](data)
-        except:
-            return ''
+            if not self._in_build:
+                self.lru_data[handle] = data
+                
+        if not secondary:
+            # None is used to indicate this column has no data
+            if self.fmap[col] is None:
+                return ''
+            value = self.fmap[col](data)
+        else:
+            if self.fmap2[col] is None:
+                return ''
+            value = self.fmap2[col](data)
+            
+        return value
 
     def do_get_iter(self, path):
         """
diff --git a/gramps/plugins/view/citationtreeview.py b/gramps/plugins/view/citationtreeview.py
index 5c6afa207..484392573 100644
--- a/gramps/plugins/view/citationtreeview.py
+++ b/gramps/plugins/view/citationtreeview.py
@@ -175,17 +175,10 @@ class CitationTreeView(ListView):
     def setup_filter(self):
         """
         Override the setup of the default Search Bar in listview, so that only
-        the searchable source fields are shown. This includes renaming the
-        'Title or Page' search to 'Title'
+        the searchable source fields are shown.
         """
-        def name(i):
-            if i == 0:
-                return _('Title')
-            else:
-                return self.COLUMNS[i][0]
-             
         self.search_bar.setup_filter(
-            [(name(pair[1]), pair[1], pair[1] in self.exact_search())
+            [(self.COLUMNS[(pair[1])][0], pair[1], pair[1] in self.exact_search())
                 for pair in self.column_order() if pair[0] and 
                                 pair[1] in self.COLUMN_FILTERABLE])
 
@@ -593,7 +586,7 @@ class CitationTreeView(ListView):
         """
         Define the default gramplets for the sidebar and bottombar.
         """
-        return (("Source Filter",),
+        return (("Citation Filter",),
                 ("Citation Gallery",
                  "Citation Notes",
                  "Citation Backlinks"))