Continued work on GEP008: Consolidate src/FilterEditor files into one file and move it to gui/filtereditor.py.

svn: r14074
This commit is contained in:
Brian Matherly
2010-01-14 04:58:30 +00:00
parent e2f9eec6e9
commit d9a0ab8a90
14 changed files with 511 additions and 740 deletions

View File

@@ -184,6 +184,7 @@ src/gui/__init__.py
src/gui/dbguielement.py src/gui/dbguielement.py
src/gui/dbloader.py src/gui/dbloader.py
src/gui/dbman.py src/gui/dbman.py
src/gui/filtereditor.py
src/gui/grampsgui.py src/gui/grampsgui.py
src/gui/pluginmanager.py src/gui/pluginmanager.py
src/gui/utils.py src/gui/utils.py
@@ -826,12 +827,6 @@ src/Filters/SideBar/_MediaSidebarFilter.py
src/Filters/SideBar/_RepoSidebarFilter.py src/Filters/SideBar/_RepoSidebarFilter.py
src/Filters/SideBar/_NoteSidebarFilter.py src/Filters/SideBar/_NoteSidebarFilter.py
# FilterEditor package
src/FilterEditor/_FilterEditor.py
src/FilterEditor/_EditFilter.py
src/FilterEditor/_EditRule.py
src/FilterEditor/_ShowResults.py
# #
# Glade files # Glade files
# #

View File

@@ -1,22 +0,0 @@
# This is the src/FilterEditor level Makefile for Gramps
pkgdatadir = $(datadir)/@PACKAGE@/FilterEditor
pkgdata_PYTHON = \
__init__.py \
_FilterEditor.py \
_EditFilter.py \
_EditRule.py \
_ShowResults.py
pkgpyexecdir = @pkgpyexecdir@/FilterEditor
pkgpythondir = @pkgpythondir@/FilterEditor
# Clean up all the byte-compiled files
MOSTLYCLEANFILES = *pyc *pyo
GRAMPS_PY_MODPATH = "../"
pycheck:
(export PYTHONPATH=$(GRAMPS_PY_MODPATH); \
pychecker $(pkgdata_PYTHON));

View File

@@ -1,218 +0,0 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2007 Donald N. Allingham
#
# 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:_EditFilter.py 9912 2008-01-22 09:17:46Z acraphae $
"""
Custom Filter Editor tool.
"""
#-------------------------------------------------------------------------
#
# Python modules
#
#-------------------------------------------------------------------------
#------------------------------------------------------------------------
#
# Set up logging
#
#------------------------------------------------------------------------
import logging
log = logging.getLogger(".FilterEdit")
#-------------------------------------------------------------------------
#
# GRAMPS modules
#
#-------------------------------------------------------------------------
import const
import ListModel
import ManagedWindow
import GrampsDisplay
import Errors
from TransUtils import sgettext as _
#-------------------------------------------------------------------------
#
# Constants
#
#-------------------------------------------------------------------------
WIKI_HELP_PAGE = WIKI_HELP_PAGE = '%s_-_Filters' % const.URL_MANUAL_PAGE
#-------------------------------------------------------------------------
#
#
#
#-------------------------------------------------------------------------
class EditFilter(ManagedWindow.ManagedWindow):
def __init__(self, namespace, dbstate, uistate, track, gfilter,
filterdb, update):
ManagedWindow.ManagedWindow.__init__(self, uistate, track, self)
self.namespace = namespace
self.update = update
self.dbstate = dbstate
self.db = dbstate.db
self.filter = gfilter
self.filterdb = filterdb
self.define_glade('define_filter', const.RULE_GLADE)
self.set_window(
self.get_widget('define_filter'),
self.get_widget('definition_title'),
_('Define filter'))
self.rlist = ListModel.ListModel(
self.get_widget('rule_list'),
[(_('Name'),-1,150),(_('Values'),-1,150)],
self.select_row,
self.on_edit_clicked)
self.fname = self.get_widget('filter_name')
self.logical = self.get_widget('rule_apply')
self.logical_not = self.get_widget('logical_not')
self.comment = self.get_widget('comment')
self.ok_btn = self.get_widget('definition_ok')
self.edit_btn = self.get_widget('definition_edit')
self.del_btn = self.get_widget('definition_delete')
self.add_btn = self.get_widget('definition_add')
self.ok_btn.connect('clicked', self.on_ok_clicked)
self.edit_btn.connect('clicked', self.on_edit_clicked)
self.del_btn.connect('clicked', self.on_delete_clicked)
self.add_btn.connect('clicked', self.on_add_clicked)
self.get_widget('definition_help').connect('clicked',
self.on_help_clicked)
self.get_widget('definition_cancel').connect('clicked',
self.close_window)
self.fname.connect('changed', self.filter_name_changed)
if self.filter.get_logical_op() == 'or':
self.logical.set_active(1)
elif self.filter.get_logical_op() == 'one':
self.logical.set_active(2)
else:
self.logical.set_active(0)
self.logical_not.set_active(self.filter.get_invert())
if self.filter.get_name():
self.fname.set_text(self.filter.get_name())
self.comment.set_text(self.filter.get_comment())
self.draw_rules()
self.show()
def on_help_clicked(self, obj):
"""Display the relevant portion of GRAMPS manual"""
GrampsDisplay.help(webpage=WIKI_HELP_PAGE)
def close_window(self, obj):
self.close()
def filter_name_changed(self, obj):
name = unicode(self.fname.get_text())
# Make sure that the name is not empty
# and not in the list of existing filters (excluding this one)
names = [filt.get_name()
for filt in self.filterdb.get_filters(self.namespace)
if filt != self.filter]
self.ok_btn.set_sensitive((len(name) != 0) and (name not in names))
def select_row(self, obj):
store, node = self.rlist.get_selected()
if node:
self.edit_btn.set_sensitive(True)
self.del_btn.set_sensitive(True)
else:
self.edit_btn.set_sensitive(False)
self.del_btn.set_sensitive(False)
def draw_rules(self):
self.rlist.clear()
for r in self.filter.get_rules():
self.rlist.add([r.name,r.display_values()],r)
def on_ok_clicked(self, obj):
n = unicode(self.fname.get_text()).strip()
if n == '':
return
if n != self.filter.get_name():
self.uistate.emit('filter-name-changed',
(self.namespace,unicode(self.filter.get_name()), n))
self.filter.set_name(n)
self.filter.set_comment(unicode(self.comment.get_text()).strip())
for f in self.filterdb.get_filters(self.namespace)[:]:
if n == f.get_name():
self.filterdb.get_filters(self.namespace).remove(f)
break
val = self.logical.get_active()
if val == 1:
op = 'or'
elif val == 2:
op = 'one'
else:
op = 'and'
self.filter.set_logical_op(op)
self.filter.set_invert(self.logical_not.get_active())
self.filterdb.add(self.namespace,self.filter)
self.update()
self.close()
def on_add_clicked(self, obj):
from _EditRule import EditRule
try:
EditRule(self.namespace, self.dbstate, self.uistate, self.track,
self.filterdb, None, _('Add Rule'), self.update_rule,
self.filter.get_name())
except Errors.WindowActiveError:
pass
def on_edit_clicked(self, obj):
store, node = self.rlist.get_selected()
if node:
from _EditRule import EditRule
d = self.rlist.get_object(node)
try:
EditRule(self.namespace, self.dbstate, self.uistate, self.track,
self.filterdb, d, _('Edit Rule'), self.update_rule,
self.filter.get_name())
except Errors.WindowActiveError:
pass
def update_rule(self, old_rule, new_rule):
if old_rule is not None:
self.filter.delete_rule(old_rule)
self.filter.add_rule(new_rule)
self.draw_rules()
def on_delete_clicked(self, obj):
store, node = self.rlist.get_selected()
if node:
gfilter = self.rlist.get_object(node)
self.filter.delete_rule(gfilter)
self.draw_rules()

View File

@@ -1,276 +0,0 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2007 Donald N. Allingham
#
# 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:_FilterEditor.py 9912 2008-01-22 09:17:46Z acraphae $
"""
Custom Filter Editor tool.
"""
#-------------------------------------------------------------------------
#
# Python modules
#
#-------------------------------------------------------------------------
from gettext import gettext as _
#------------------------------------------------------------------------
#
# Set up logging
#
#------------------------------------------------------------------------
import logging
log = logging.getLogger(".FilterEdit")
#-------------------------------------------------------------------------
#
# GTK/GNOME
#
#-------------------------------------------------------------------------
import GrampsDisplay
#-------------------------------------------------------------------------
#
# GRAMPS modules
#
#-------------------------------------------------------------------------
import const
from Filters import (GenericFilterFactory, FilterList, reload_custom_filters)
from Filters.Rules._MatchesFilterBase import MatchesFilterBase
import ListModel
import ManagedWindow
from QuestionDialog import QuestionDialog
from FilterEditor._EditFilter import EditFilter
# dictionary mapping FILTER_TYPE of views to Filter window name
_TITLES = {
'Person' : _("Person Filters"),
'Family' : _('Family Filters'),
'Event' : _('Event Filters'),
'Place' : _('Place Filters'),
'Source' : _('Source Filters'),
'MediaObject' : _('Media Object Filters'),
'Repository' : _('Repository Filters'),
'Note' : _('Note Filters'),
}
class FilterEditor(ManagedWindow.ManagedWindow):
def __init__(self, namespace, filterdb, dbstate, uistate):
ManagedWindow.ManagedWindow.__init__(self, uistate, [], FilterEditor)
self.dbstate = dbstate
self.db = dbstate.db
self.filterdb = FilterList(filterdb)
self.filterdb.load()
self.namespace = namespace
self.define_glade('filter_list', const.RULE_GLADE)
self.filter_list = self.get_widget('filters')
self.edit = self.get_widget('filter_list_edit')
self.clone = self.get_widget('filter_list_clone')
self.delete = self.get_widget('filter_list_delete')
self.test = self.get_widget('filter_list_test')
self.edit.set_sensitive(False)
self.clone.set_sensitive(False)
self.delete.set_sensitive(False)
self.test.set_sensitive(False)
self.set_window(self.get_widget('filter_list'),
self.get_widget('filter_list_title'),
_TITLES[self.namespace])
self.edit.connect('clicked', self.edit_filter)
self.clone.connect('clicked', self.clone_filter)
self.test.connect('clicked', self.test_clicked)
self.delete.connect('clicked', self.delete_filter)
self.connect_button('filter_list_help', self.help_clicked)
self.connect_button('filter_list_close', self.close)
self.connect_button('filter_list_add', self.add_new_filter)
self.uistate.connect('filter-name-changed', self.clean_after_rename)
self.clist = ListModel.ListModel(
self.filter_list,
[(_('Filter'), 0, 150), (_('Comment'), 1, 150)],
self.filter_select_row,
self.edit_filter)
self.draw_filters()
self.show()
def build_menu_names(self, obj):
return (_("Custom Filter Editor"), _("Custom Filter Editor"))
def help_clicked(self, obj):
"""Display the relevant portion of GRAMPS manual"""
GrampsDisplay.help()
def filter_select_row(self, obj):
store, node = self.clist.get_selected()
if node:
self.edit.set_sensitive(True)
self.clone.set_sensitive(True)
self.delete.set_sensitive(True)
self.test.set_sensitive(True)
else:
self.edit.set_sensitive(False)
self.clone.set_sensitive(False)
self.delete.set_sensitive(False)
self.test.set_sensitive(False)
def close(self, *obj):
self.filterdb.save()
reload_custom_filters()
#reload_system_filters()
self.uistate.emit('filters-changed', (self.namespace,))
ManagedWindow.ManagedWindow.close(self, *obj)
def draw_filters(self):
self.clist.clear()
for f in self.filterdb.get_filters(self.namespace):
self.clist.add([f.get_name(), f.get_comment()], f)
def add_new_filter(self, obj):
the_filter = GenericFilterFactory(self.namespace)()
EditFilter(self.namespace, self.dbstate, self.uistate, self.track,
the_filter, self.filterdb, self.draw_filters)
def edit_filter(self, obj):
store, node = self.clist.get_selected()
if node:
gfilter = self.clist.get_object(node)
EditFilter(self.namespace, self.dbstate, self.uistate, self.track,
gfilter, self.filterdb, self.draw_filters)
def clone_filter(self, obj):
store, node = self.clist.get_selected()
if node:
old_filter = self.clist.get_object(node)
the_filter = GenericFilterFactory(self.namespace)(old_filter)
the_filter.set_name('')
EditFilter(self.namespace, self.dbstate, self.uistate, self.track,
the_filter, self.filterdb, self.draw_filters)
def test_clicked(self, obj):
store, node = self.clist.get_selected()
if node:
from FilterEditor._ShowResults import ShowResults
filt = self.clist.get_object(node)
handle_list = filt.apply(self.db, self.get_all_handles())
ShowResults(self.db, self.uistate, self.track, handle_list,
filt.get_name(),self.namespace)
def delete_filter(self, obj):
store, node = self.clist.get_selected()
if node:
gfilter = self.clist.get_object(node)
name = gfilter.get_name()
if self.check_recursive_filters(self.namespace, name):
QuestionDialog( _('Delete Filter?'),
_('This filter is currently being used '
'as the base for other filters. Deleting'
'this filter will result in removing all '
'other filters that depend on it.'),
_('Delete Filter'),
self._do_delete_selected_filter,
self.window)
else:
self._do_delete_selected_filter()
def _do_delete_selected_filter(self):
store, node = self.clist.get_selected()
if node:
gfilter = self.clist.get_object(node)
self._do_delete_filter(self.namespace, gfilter)
self.draw_filters()
def _do_delete_filter(self, space, gfilter):
# Find everything we need to remove
filter_set = set()
self._find_dependent_filters(space, gfilter, filter_set)
# Remove what we found
filters = self.filterdb.get_filters(space)
for the_filter in filter_set:
filters.remove(the_filter)
def _find_dependent_filters(self, space, gfilter, filter_set):
"""
This method recursively calls itself to find all filters that
depend on the given filter, either directly through one of the rules,
or through the chain of dependencies.
The filter_set is amended with the found filters.
"""
filters = self.filterdb.get_filters(space)
name = gfilter.get_name()
for the_filter in filters:
for rule in the_filter.get_rules():
values = rule.values()
if issubclass(rule.__class__, MatchesFilterBase) \
and (name in values):
self._find_dependent_filters(space, the_filter, filter_set)
break
# Add itself to the filter_set
filter_set.add(gfilter)
def get_all_handles(self):
if self.namespace == 'Person':
return self.db.iter_person_handles()
elif self.namespace == 'Family':
return self.db.iter_family_handles()
elif self.namespace == 'Event':
return self.db.get_event_handles()
elif self.namespace == 'Source':
return self.db.get_source_handles()
elif self.namespace == 'Place':
return self.db.iter_place_handles()
elif self.namespace == 'MediaObject':
return self.db.get_media_object_handles()
elif self.namespace == 'Repository':
return self.db.get_repository_handles()
elif self.namespace == 'Note':
return self.db.get_note_handles()
def clean_after_rename(self, space, old_name, new_name):
if old_name == "":
return
if old_name == new_name:
return
for the_filter in self.filterdb.get_filters(space):
for rule in the_filter.get_rules():
values = rule.values()
if issubclass(rule.__class__, MatchesFilterBase) \
and (old_name in values):
ind = values.index(old_name)
values[ind] = new_name
def check_recursive_filters(self, space, name):
for the_filter in self.filterdb.get_filters(space):
for rule in the_filter.get_rules():
values = rule.values()
if issubclass(rule.__class__, MatchesFilterBase) \
and (name in values):
return True
return False

View File

@@ -1,164 +0,0 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2008 Donald N. Allingham
#
# 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$
"""
Custom Filter Editor tool.
"""
#-------------------------------------------------------------------------
#
# Python modules
#
#-------------------------------------------------------------------------
import locale
from gettext import gettext as _
#------------------------------------------------------------------------
#
# Set up logging
#
#------------------------------------------------------------------------
import logging
log = logging.getLogger(".FilterEdit")
#-------------------------------------------------------------------------
#
# GTK/GNOME
#
#-------------------------------------------------------------------------
import gtk
import gobject
#-------------------------------------------------------------------------
#
# GRAMPS modules
#
#-------------------------------------------------------------------------
import const
import ManagedWindow
from gen.display.name import displayer as _nd
import Utils
#-------------------------------------------------------------------------
#
#
#
#-------------------------------------------------------------------------
class ShowResults(ManagedWindow.ManagedWindow):
def __init__(self, db, uistate, track, handle_list, filtname, namespace):
ManagedWindow.ManagedWindow.__init__(self, uistate, track, self)
self.db = db
self.filtname = filtname
self.namespace = namespace
self.define_glade('test', const.RULE_GLADE,)
self.set_window(
self.get_widget('test'),
self.get_widget('test_title'),
_('Filter Test'))
render = gtk.CellRendererText()
tree = self.get_widget('list')
model = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
tree.set_model(model)
column_n = gtk.TreeViewColumn(_('Name'), render, text=0)
tree.append_column(column_n)
column_n = gtk.TreeViewColumn(_('ID'), render, text=1)
tree.append_column(column_n)
self.get_widget('test_close').connect('clicked', self.close)
new_list = sorted(
(self.sort_val_from_handle(h) for h in handle_list),
key=lambda x: locale.strxfrm(x[0])
)
for s_, handle in new_list:
name, gid = self.get_name_id(handle)
model.append(row=[name, gid])
self.show()
def get_name_id(self, handle):
if self.namespace == 'Person':
person = self.db.get_person_from_handle(handle)
name = _nd.sorted(person)
gid = person.get_gramps_id()
elif self.namespace == 'Family':
family = self.db.get_family_from_handle(handle)
name = Utils.family_name(family, self.db)
gid = family.get_gramps_id()
elif self.namespace == 'Event':
event = self.db.get_event_from_handle(handle)
name = event.get_description()
gid = event.get_gramps_id()
elif self.namespace == 'Source':
source = self.db.get_source_from_handle(handle)
name = source.get_title()
gid = source.get_gramps_id()
elif self.namespace == 'Place':
place = self.db.get_place_from_handle(handle)
name = place.get_title()
gid = place.get_gramps_id()
elif self.namespace == 'MediaObject':
obj = self.db.get_object_from_handle(handle)
name = obj.get_description()
gid = obj.get_gramps_id()
elif self.namespace == 'Repository':
repo = self.db.get_repository_from_handle(handle)
name = repo.get_name()
gid = repo.get_gramps_id()
elif self.namespace == 'Note':
note = self.db.get_note_from_handle(handle)
name = note.get().replace('\n', ' ')
#String must be unicode for truncation to work for non ascii characters
name = unicode(name)
if len(name) > 80:
name = name[:80]+"..."
gid = note.get_gramps_id()
return (name, gid)
def sort_val_from_handle(self, handle):
if self.namespace == 'Person':
name = self.db.get_person_from_handle(handle).get_primary_name()
sortname = _nd.sort_string(name)
elif self.namespace == 'Family':
sortname = Utils.family_name(
self.db.get_family_from_handle(handle),self.db)
elif self.namespace == 'Event':
sortname = self.db.get_event_from_handle(handle).get_description()
elif self.namespace == 'Source':
sortname = self.db.get_source_from_handle(handle).get_title()
elif self.namespace == 'Place':
sortname = self.db.get_place_from_handle(handle).get_title()
elif self.namespace == 'MediaObject':
sortname = self.db.get_object_from_handle(handle).get_description()
elif self.namespace == 'Repository':
sortname = self.db.get_repository_from_handle(handle).get_name()
elif self.namespace == 'Note':
gid = self.db.get_note_from_handle(handle).get_gramps_id()
sortname = gid
return (sortname, handle)

View File

@@ -1,23 +0,0 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2006 Donald N. Allingham
#
# 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$
from _FilterEditor import FilterEditor

View File

@@ -2,13 +2,11 @@
# $Id$ # $Id$
SUBDIRS = \ SUBDIRS = \
BasicUtils \
cli \ cli \
data \ data \
DateHandler \ DateHandler \
docgen \ docgen \
Filters \ Filters \
FilterEditor \
gen \ gen \
glade \ glade \
GrampsLocale \ GrampsLocale \

View File

@@ -16,6 +16,7 @@ pkgdata_PYTHON = \
dbguielement.py \ dbguielement.py \
dbloader.py \ dbloader.py \
dbman.py \ dbman.py \
filtereditor.py \
grampsgui.py \ grampsgui.py \
pluginmanager.py \ pluginmanager.py \
utils.py \ utils.py \

View File

@@ -2,8 +2,6 @@
# Gramps - a GTK+/GNOME based genealogy program # Gramps - a GTK+/GNOME based genealogy program
# #
# Copyright (C) 2000-2007 Donald N. Allingham # Copyright (C) 2000-2007 Donald N. Allingham
# Copyright (C) 2007-2008 Brian G. Matherly
# Copyright (C) 2008 Benny Malengier
# #
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@@ -20,7 +18,7 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
# $Id$ # $Id:_FilterEditor.py 9912 2008-01-22 09:17:46Z acraphae $
""" """
Custom Filter Editor tool. Custom Filter Editor tool.
@@ -31,7 +29,7 @@ Custom Filter Editor tool.
# Python modules # Python modules
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
from gettext import gettext as _ import locale
#------------------------------------------------------------------------ #------------------------------------------------------------------------
# #
@@ -39,7 +37,7 @@ from gettext import gettext as _
# #
#------------------------------------------------------------------------ #------------------------------------------------------------------------
import logging import logging
log = logging.getLogger(".FilterEdit") log = logging.getLogger(".filtereditor")
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
@@ -54,29 +52,41 @@ import gobject
# GRAMPS modules # GRAMPS modules
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
import GrampsDisplay from Filters import (GenericFilterFactory, FilterList, reload_custom_filters)
from Filters.Rules._MatchesFilterBase import MatchesFilterBase
import ListModel
import ManagedWindow
from QuestionDialog import QuestionDialog
import const import const
import GrampsDisplay
import Errors
from TransUtils import sgettext as _
import gen.lib import gen.lib
from Filters import Rules from Filters import Rules
import AutoComp import AutoComp
from gui.selectors import SelectorFactory from gui.selectors import SelectorFactory
from gen.display.name import displayer as _nd from gen.display.name import displayer as _nd
import Utils import Utils
import ManagedWindow
#-------------------------------------------------------------------------
#
# Sorting function for the filter rules
#
#-------------------------------------------------------------------------
def by_rule_name(f, s):
return cmp(f.name, s.name)
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
# Constants # Constants
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
WIKI_HELP_PAGE = WIKI_HELP_PAGE = '%s_-_Filters' % const.URL_MANUAL_PAGE
# dictionary mapping FILTER_TYPE of views to Filter window name
_TITLES = {
'Person' : _("Person Filters"),
'Family' : _('Family Filters'),
'Event' : _('Event Filters'),
'Place' : _('Place Filters'),
'Source' : _('Source Filters'),
'MediaObject' : _('Media Object Filters'),
'Repository' : _('Repository Filters'),
'Note' : _('Note Filters'),
}
_name2typeclass = { _name2typeclass = {
_('Personal event:') : gen.lib.EventType, _('Personal event:') : gen.lib.EventType,
_('Family event:') : gen.lib.EventType, _('Family event:') : gen.lib.EventType,
@@ -90,6 +100,14 @@ _name2typeclass = {
_('Note type:') : gen.lib.NoteType, _('Note type:') : gen.lib.NoteType,
} }
#-------------------------------------------------------------------------
#
# Sorting function for the filter rules
#
#-------------------------------------------------------------------------
def by_rule_name(f, s):
return cmp(f.name, s.name)
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
# MyBoolean - check button with standard interface # MyBoolean - check button with standard interface
@@ -336,7 +354,7 @@ class MySource(MyID):
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
# # MySelect
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
class MySelect(gtk.ComboBoxEntry): class MySelect(gtk.ComboBoxEntry):
@@ -359,7 +377,7 @@ class MySelect(gtk.ComboBoxEntry):
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
# # MyEntry
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
class MyEntry(gtk.Entry): class MyEntry(gtk.Entry):
@@ -370,7 +388,7 @@ class MyEntry(gtk.Entry):
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
# # EditRule
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
class EditRule(ManagedWindow.ManagedWindow): class EditRule(ManagedWindow.ManagedWindow):
@@ -635,3 +653,470 @@ class EditRule(ManagedWindow.ManagedWindow):
self.close() self.close()
except KeyError: except KeyError:
pass pass
#-------------------------------------------------------------------------
#
# EditFilter
#
#-------------------------------------------------------------------------
class EditFilter(ManagedWindow.ManagedWindow):
def __init__(self, namespace, dbstate, uistate, track, gfilter,
filterdb, update):
ManagedWindow.ManagedWindow.__init__(self, uistate, track, self)
self.namespace = namespace
self.update = update
self.dbstate = dbstate
self.db = dbstate.db
self.filter = gfilter
self.filterdb = filterdb
self.define_glade('define_filter', const.RULE_GLADE)
self.set_window(
self.get_widget('define_filter'),
self.get_widget('definition_title'),
_('Define filter'))
self.rlist = ListModel.ListModel(
self.get_widget('rule_list'),
[(_('Name'),-1,150),(_('Values'),-1,150)],
self.select_row,
self.on_edit_clicked)
self.fname = self.get_widget('filter_name')
self.logical = self.get_widget('rule_apply')
self.logical_not = self.get_widget('logical_not')
self.comment = self.get_widget('comment')
self.ok_btn = self.get_widget('definition_ok')
self.edit_btn = self.get_widget('definition_edit')
self.del_btn = self.get_widget('definition_delete')
self.add_btn = self.get_widget('definition_add')
self.ok_btn.connect('clicked', self.on_ok_clicked)
self.edit_btn.connect('clicked', self.on_edit_clicked)
self.del_btn.connect('clicked', self.on_delete_clicked)
self.add_btn.connect('clicked', self.on_add_clicked)
self.get_widget('definition_help').connect('clicked',
self.on_help_clicked)
self.get_widget('definition_cancel').connect('clicked',
self.close_window)
self.fname.connect('changed', self.filter_name_changed)
if self.filter.get_logical_op() == 'or':
self.logical.set_active(1)
elif self.filter.get_logical_op() == 'one':
self.logical.set_active(2)
else:
self.logical.set_active(0)
self.logical_not.set_active(self.filter.get_invert())
if self.filter.get_name():
self.fname.set_text(self.filter.get_name())
self.comment.set_text(self.filter.get_comment())
self.draw_rules()
self.show()
def on_help_clicked(self, obj):
"""Display the relevant portion of GRAMPS manual"""
GrampsDisplay.help(webpage=WIKI_HELP_PAGE)
def close_window(self, obj):
self.close()
def filter_name_changed(self, obj):
name = unicode(self.fname.get_text())
# Make sure that the name is not empty
# and not in the list of existing filters (excluding this one)
names = [filt.get_name()
for filt in self.filterdb.get_filters(self.namespace)
if filt != self.filter]
self.ok_btn.set_sensitive((len(name) != 0) and (name not in names))
def select_row(self, obj):
store, node = self.rlist.get_selected()
if node:
self.edit_btn.set_sensitive(True)
self.del_btn.set_sensitive(True)
else:
self.edit_btn.set_sensitive(False)
self.del_btn.set_sensitive(False)
def draw_rules(self):
self.rlist.clear()
for r in self.filter.get_rules():
self.rlist.add([r.name,r.display_values()],r)
def on_ok_clicked(self, obj):
n = unicode(self.fname.get_text()).strip()
if n == '':
return
if n != self.filter.get_name():
self.uistate.emit('filter-name-changed',
(self.namespace,unicode(self.filter.get_name()), n))
self.filter.set_name(n)
self.filter.set_comment(unicode(self.comment.get_text()).strip())
for f in self.filterdb.get_filters(self.namespace)[:]:
if n == f.get_name():
self.filterdb.get_filters(self.namespace).remove(f)
break
val = self.logical.get_active()
if val == 1:
op = 'or'
elif val == 2:
op = 'one'
else:
op = 'and'
self.filter.set_logical_op(op)
self.filter.set_invert(self.logical_not.get_active())
self.filterdb.add(self.namespace,self.filter)
self.update()
self.close()
def on_add_clicked(self, obj):
try:
EditRule(self.namespace, self.dbstate, self.uistate, self.track,
self.filterdb, None, _('Add Rule'), self.update_rule,
self.filter.get_name())
except Errors.WindowActiveError:
pass
def on_edit_clicked(self, obj):
store, node = self.rlist.get_selected()
if node:
d = self.rlist.get_object(node)
try:
EditRule(self.namespace, self.dbstate, self.uistate, self.track,
self.filterdb, d, _('Edit Rule'), self.update_rule,
self.filter.get_name())
except Errors.WindowActiveError:
pass
def update_rule(self, old_rule, new_rule):
if old_rule is not None:
self.filter.delete_rule(old_rule)
self.filter.add_rule(new_rule)
self.draw_rules()
def on_delete_clicked(self, obj):
store, node = self.rlist.get_selected()
if node:
gfilter = self.rlist.get_object(node)
self.filter.delete_rule(gfilter)
self.draw_rules()
#-------------------------------------------------------------------------
#
# ShowResults
#
#-------------------------------------------------------------------------
class ShowResults(ManagedWindow.ManagedWindow):
def __init__(self, db, uistate, track, handle_list, filtname, namespace):
ManagedWindow.ManagedWindow.__init__(self, uistate, track, self)
self.db = db
self.filtname = filtname
self.namespace = namespace
self.define_glade('test', const.RULE_GLADE,)
self.set_window(
self.get_widget('test'),
self.get_widget('test_title'),
_('Filter Test'))
render = gtk.CellRendererText()
tree = self.get_widget('list')
model = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
tree.set_model(model)
column_n = gtk.TreeViewColumn(_('Name'), render, text=0)
tree.append_column(column_n)
column_n = gtk.TreeViewColumn(_('ID'), render, text=1)
tree.append_column(column_n)
self.get_widget('test_close').connect('clicked', self.close)
new_list = sorted(
(self.sort_val_from_handle(h) for h in handle_list),
key=lambda x: locale.strxfrm(x[0])
)
for s_, handle in new_list:
name, gid = self.get_name_id(handle)
model.append(row=[name, gid])
self.show()
def get_name_id(self, handle):
if self.namespace == 'Person':
person = self.db.get_person_from_handle(handle)
name = _nd.sorted(person)
gid = person.get_gramps_id()
elif self.namespace == 'Family':
family = self.db.get_family_from_handle(handle)
name = Utils.family_name(family, self.db)
gid = family.get_gramps_id()
elif self.namespace == 'Event':
event = self.db.get_event_from_handle(handle)
name = event.get_description()
gid = event.get_gramps_id()
elif self.namespace == 'Source':
source = self.db.get_source_from_handle(handle)
name = source.get_title()
gid = source.get_gramps_id()
elif self.namespace == 'Place':
place = self.db.get_place_from_handle(handle)
name = place.get_title()
gid = place.get_gramps_id()
elif self.namespace == 'MediaObject':
obj = self.db.get_object_from_handle(handle)
name = obj.get_description()
gid = obj.get_gramps_id()
elif self.namespace == 'Repository':
repo = self.db.get_repository_from_handle(handle)
name = repo.get_name()
gid = repo.get_gramps_id()
elif self.namespace == 'Note':
note = self.db.get_note_from_handle(handle)
name = note.get().replace('\n', ' ')
#String must be unicode for truncation to work for non ascii characters
name = unicode(name)
if len(name) > 80:
name = name[:80]+"..."
gid = note.get_gramps_id()
return (name, gid)
def sort_val_from_handle(self, handle):
if self.namespace == 'Person':
name = self.db.get_person_from_handle(handle).get_primary_name()
sortname = _nd.sort_string(name)
elif self.namespace == 'Family':
sortname = Utils.family_name(
self.db.get_family_from_handle(handle),self.db)
elif self.namespace == 'Event':
sortname = self.db.get_event_from_handle(handle).get_description()
elif self.namespace == 'Source':
sortname = self.db.get_source_from_handle(handle).get_title()
elif self.namespace == 'Place':
sortname = self.db.get_place_from_handle(handle).get_title()
elif self.namespace == 'MediaObject':
sortname = self.db.get_object_from_handle(handle).get_description()
elif self.namespace == 'Repository':
sortname = self.db.get_repository_from_handle(handle).get_name()
elif self.namespace == 'Note':
gid = self.db.get_note_from_handle(handle).get_gramps_id()
sortname = gid
return (sortname, handle)
#-------------------------------------------------------------------------
#
# FilterEditor
#
#-------------------------------------------------------------------------
class FilterEditor(ManagedWindow.ManagedWindow):
def __init__(self, namespace, filterdb, dbstate, uistate):
ManagedWindow.ManagedWindow.__init__(self, uistate, [], FilterEditor)
self.dbstate = dbstate
self.db = dbstate.db
self.filterdb = FilterList(filterdb)
self.filterdb.load()
self.namespace = namespace
self.define_glade('filter_list', const.RULE_GLADE)
self.filter_list = self.get_widget('filters')
self.edit = self.get_widget('filter_list_edit')
self.clone = self.get_widget('filter_list_clone')
self.delete = self.get_widget('filter_list_delete')
self.test = self.get_widget('filter_list_test')
self.edit.set_sensitive(False)
self.clone.set_sensitive(False)
self.delete.set_sensitive(False)
self.test.set_sensitive(False)
self.set_window(self.get_widget('filter_list'),
self.get_widget('filter_list_title'),
_TITLES[self.namespace])
self.edit.connect('clicked', self.edit_filter)
self.clone.connect('clicked', self.clone_filter)
self.test.connect('clicked', self.test_clicked)
self.delete.connect('clicked', self.delete_filter)
self.connect_button('filter_list_help', self.help_clicked)
self.connect_button('filter_list_close', self.close)
self.connect_button('filter_list_add', self.add_new_filter)
self.uistate.connect('filter-name-changed', self.clean_after_rename)
self.clist = ListModel.ListModel(
self.filter_list,
[(_('Filter'), 0, 150), (_('Comment'), 1, 150)],
self.filter_select_row,
self.edit_filter)
self.draw_filters()
self.show()
def build_menu_names(self, obj):
return (_("Custom Filter Editor"), _("Custom Filter Editor"))
def help_clicked(self, obj):
"""Display the relevant portion of GRAMPS manual"""
GrampsDisplay.help()
def filter_select_row(self, obj):
store, node = self.clist.get_selected()
if node:
self.edit.set_sensitive(True)
self.clone.set_sensitive(True)
self.delete.set_sensitive(True)
self.test.set_sensitive(True)
else:
self.edit.set_sensitive(False)
self.clone.set_sensitive(False)
self.delete.set_sensitive(False)
self.test.set_sensitive(False)
def close(self, *obj):
self.filterdb.save()
reload_custom_filters()
#reload_system_filters()
self.uistate.emit('filters-changed', (self.namespace,))
ManagedWindow.ManagedWindow.close(self, *obj)
def draw_filters(self):
self.clist.clear()
for f in self.filterdb.get_filters(self.namespace):
self.clist.add([f.get_name(), f.get_comment()], f)
def add_new_filter(self, obj):
the_filter = GenericFilterFactory(self.namespace)()
EditFilter(self.namespace, self.dbstate, self.uistate, self.track,
the_filter, self.filterdb, self.draw_filters)
def edit_filter(self, obj):
store, node = self.clist.get_selected()
if node:
gfilter = self.clist.get_object(node)
EditFilter(self.namespace, self.dbstate, self.uistate, self.track,
gfilter, self.filterdb, self.draw_filters)
def clone_filter(self, obj):
store, node = self.clist.get_selected()
if node:
old_filter = self.clist.get_object(node)
the_filter = GenericFilterFactory(self.namespace)(old_filter)
the_filter.set_name('')
EditFilter(self.namespace, self.dbstate, self.uistate, self.track,
the_filter, self.filterdb, self.draw_filters)
def test_clicked(self, obj):
store, node = self.clist.get_selected()
if node:
filt = self.clist.get_object(node)
handle_list = filt.apply(self.db, self.get_all_handles())
ShowResults(self.db, self.uistate, self.track, handle_list,
filt.get_name(),self.namespace)
def delete_filter(self, obj):
store, node = self.clist.get_selected()
if node:
gfilter = self.clist.get_object(node)
name = gfilter.get_name()
if self.check_recursive_filters(self.namespace, name):
QuestionDialog( _('Delete Filter?'),
_('This filter is currently being used '
'as the base for other filters. Deleting'
'this filter will result in removing all '
'other filters that depend on it.'),
_('Delete Filter'),
self._do_delete_selected_filter,
self.window)
else:
self._do_delete_selected_filter()
def _do_delete_selected_filter(self):
store, node = self.clist.get_selected()
if node:
gfilter = self.clist.get_object(node)
self._do_delete_filter(self.namespace, gfilter)
self.draw_filters()
def _do_delete_filter(self, space, gfilter):
# Find everything we need to remove
filter_set = set()
self._find_dependent_filters(space, gfilter, filter_set)
# Remove what we found
filters = self.filterdb.get_filters(space)
for the_filter in filter_set:
filters.remove(the_filter)
def _find_dependent_filters(self, space, gfilter, filter_set):
"""
This method recursively calls itself to find all filters that
depend on the given filter, either directly through one of the rules,
or through the chain of dependencies.
The filter_set is amended with the found filters.
"""
filters = self.filterdb.get_filters(space)
name = gfilter.get_name()
for the_filter in filters:
for rule in the_filter.get_rules():
values = rule.values()
if issubclass(rule.__class__, MatchesFilterBase) \
and (name in values):
self._find_dependent_filters(space, the_filter, filter_set)
break
# Add itself to the filter_set
filter_set.add(gfilter)
def get_all_handles(self):
if self.namespace == 'Person':
return self.db.iter_person_handles()
elif self.namespace == 'Family':
return self.db.iter_family_handles()
elif self.namespace == 'Event':
return self.db.get_event_handles()
elif self.namespace == 'Source':
return self.db.get_source_handles()
elif self.namespace == 'Place':
return self.db.iter_place_handles()
elif self.namespace == 'MediaObject':
return self.db.get_media_object_handles()
elif self.namespace == 'Repository':
return self.db.get_repository_handles()
elif self.namespace == 'Note':
return self.db.get_note_handles()
def clean_after_rename(self, space, old_name, new_name):
if old_name == "":
return
if old_name == new_name:
return
for the_filter in self.filterdb.get_filters(space):
for rule in the_filter.get_rules():
values = rule.values()
if issubclass(rule.__class__, MatchesFilterBase) \
and (old_name in values):
ind = values.index(old_name)
values[ind] = new_name
def check_recursive_filters(self, space, name):
for the_filter in self.filterdb.get_filters(space):
for rule in the_filter.get_rules():
values = rule.values()
if issubclass(rule.__class__, MatchesFilterBase) \
and (name in values):
return True
return False

View File

@@ -59,6 +59,7 @@ from gui.utils import add_menuitem
import const import const
import Utils import Utils
from QuestionDialog import QuestionDialog, QuestionDialog2 from QuestionDialog import QuestionDialog, QuestionDialog2
from gui.filtereditor import FilterEditor
from TransUtils import sgettext as _ from TransUtils import sgettext as _
#---------------------------------------------------------------- #----------------------------------------------------------------
@@ -354,8 +355,6 @@ class ListView(NavigationView):
self.build_tree() self.build_tree()
def filter_editor(self, obj): def filter_editor(self, obj):
from FilterEditor import FilterEditor
try: try:
FilterEditor(self.FILTER_TYPE , const.CUSTOM_FILTERS, FilterEditor(self.FILTER_TYPE , const.CUSTOM_FILTERS,
self.dbstate, self.uistate) self.dbstate, self.uistate)

View File

@@ -58,7 +58,7 @@ import GrampsDisplay
import ManagedWindow import ManagedWindow
from TransUtils import sgettext as _ from TransUtils import sgettext as _
from glade import Glade from glade import Glade
from gui.filtereditor import FilterEditor
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
@@ -163,9 +163,8 @@ class EventComparison(Tool.Tool,ManagedWindow.ManagedWindow):
return (_("Filter selection"),_("Event Comparison tool")) return (_("Filter selection"),_("Event Comparison tool"))
def filter_editor_clicked(self, obj): def filter_editor_clicked(self, obj):
import FilterEditor
try: try:
FilterEditor.FilterEditor('Person',const.CUSTOM_FILTERS, FilterEditor('Person',const.CUSTOM_FILTERS,
self.dbstate,self.uistate) self.dbstate,self.uistate)
except Errors.WindowActiveError: except Errors.WindowActiveError:
pass pass

View File

@@ -65,6 +65,7 @@ import config
from QuestionDialog import RunDatabaseRepair, ErrorDialog from QuestionDialog import RunDatabaseRepair, ErrorDialog
import Bookmarks import Bookmarks
import const import const
from gui.filtereditor import FilterEditor
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
@@ -505,8 +506,6 @@ class PedigreeView(NavigationView):
callback=self.filter_editor) callback=self.filter_editor)
def filter_editor(self, obj): def filter_editor(self, obj):
from FilterEditor import FilterEditor
try: try:
FilterEditor('Person', const.CUSTOM_FILTERS, FilterEditor('Person', const.CUSTOM_FILTERS,
self.dbstate, self.uistate) self.dbstate, self.uistate)

View File

@@ -53,6 +53,7 @@ except:
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
import gen.lib import gen.lib
from gui.views.navigationview import NavigationView from gui.views.navigationview import NavigationView
from gui.filtereditor import FilterEditor
from gen.display.name import displayer as name_displayer from gen.display.name import displayer as name_displayer
from Utils import (media_path_full, probably_alive, find_children, from Utils import (media_path_full, probably_alive, find_children,
find_parents, find_witnessed_people) find_parents, find_witnessed_people)
@@ -632,8 +633,6 @@ class PedigreeViewExt(NavigationView):
callback=self.filter_editor) callback=self.filter_editor)
def filter_editor(self, obj): def filter_editor(self, obj):
from FilterEditor import FilterEditor
try: try:
FilterEditor('Person', const.CUSTOM_FILTERS, FilterEditor('Person', const.CUSTOM_FILTERS,
self.dbstate, self.uistate) self.dbstate, self.uistate)

View File

@@ -49,6 +49,7 @@ import pango
import gen.lib import gen.lib
from gui.views.navigationview import NavigationView from gui.views.navigationview import NavigationView
from gui.editors import EditPerson, EditFamily from gui.editors import EditPerson, EditFamily
from gui.filtereditor import FilterEditor
from gen.display.name import displayer as name_displayer from gen.display.name import displayer as name_displayer
from Utils import media_path_full, probably_alive from Utils import media_path_full, probably_alive
import DateHandler import DateHandler
@@ -384,8 +385,6 @@ class RelationshipView(NavigationView):
self.family_action.set_sensitive(False) self.family_action.set_sensitive(False)
def filter_editor(self, obj): def filter_editor(self, obj):
from FilterEditor import FilterEditor
try: try:
FilterEditor('Person', const.CUSTOM_FILTERS, FilterEditor('Person', const.CUSTOM_FILTERS,
self.dbstate, self.uistate) self.dbstate, self.uistate)