3275: PageView reworking main work by Nick Hall

Moving personview to the new pageview classes, and using a generic 
treebasemodel


svn: r13515
This commit is contained in:
Benny Malengier 2009-11-07 13:04:45 +00:00
parent e44a7cc8e2
commit 1b556586a4
7 changed files with 865 additions and 1636 deletions

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,8 @@
#
# Copyright (C) 2000-2007 Donald N. Allingham
# Copyright (C) 2009 Gary Burton
# Copyright (C) 2009 Nick Hall
# Copyright (C) 2009 Benny Malengier
#
# 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
@ -34,15 +36,6 @@ from __future__ import with_statement
from gettext import gettext as _
import time
import cgi
import locale
#-------------------------------------------------------------------------
#
# set up logging
#
#-------------------------------------------------------------------------
import logging
log = logging.getLogger(".")
#-------------------------------------------------------------------------
#
@ -51,6 +44,14 @@ log = logging.getLogger(".")
#-------------------------------------------------------------------------
import gtk
#-------------------------------------------------------------------------
#
# set up logging
#
#-------------------------------------------------------------------------
import logging
_LOG = logging.getLogger(".")
#-------------------------------------------------------------------------
#
# GRAMPS modules
@ -62,412 +63,144 @@ from BasicUtils import name_displayer
import DateHandler
import ToolTips
import GrampsLocale
import config
from gen.utils.longop import LongOpStatus
from Filters import SearchFilter, ExactSearchFilter
from Lru import LRU
from gui.views.treemodels.treebasemodel import TreeBaseModel
_CACHE_SIZE = 250
invalid_date_format = config.get('preferences.invalid-date-format')
class NodeTreeMap(object):
def __init__(self):
self.sortnames = {}
self.temp_top_path2iter = []
self.iter2path = {}
self.path2iter = {}
self.sname_sub = {}
self.temp_iter2path = {}
self.temp_path2iter = {}
self.temp_sname_sub = {}
def clear_sort_names(self):
self.sortnames = {}
def clear_temp_data(self):
self.temp_iter2path = {}
self.temp_path2iter = {}
self.temp_sname_sub = {}
def build_toplevel(self):
self.temp_top_path2iter = sorted(self.temp_sname_sub, key=locale.strxfrm)
for name in self.temp_top_path2iter:
self.build_sub_entry(name)
def get_group_names(self):
return self.temp_top_path2iter
def assign_sort_name(self, handle, sorted_name, group_name):
self.sortnames[handle] = sorted_name
if group_name in self.temp_sname_sub:
self.temp_sname_sub[group_name] += [handle]
else:
self.temp_sname_sub[group_name] = [handle]
def assign_data(self):
self.top_path2iter = self.temp_top_path2iter
self.iter2path = self.temp_iter2path
self.path2iter = self.temp_path2iter
self.sname_sub = self.temp_sname_sub
self.top_iter2path = {}
for i, item in enumerate(self.top_path2iter):
self.top_iter2path[item] = i
def get_path(self, node):
if node in self.top_iter2path:
return (self.top_iter2path[node], )
else:
(surname, index) = self.iter2path[node]
return (self.top_iter2path[surname], index)
def has_entry(self, handle):
return handle in self.iter2path
def get_iter(self, path):
try:
if len(path)==1: # Top Level
return self.top_path2iter[path[0]]
else: # Sublevel
surname = self.top_path2iter[path[0]]
return self.path2iter[(surname, path[1])]
except:
return None
def has_top_node(self, node):
return node in self.sname_sub
def find_next_node(self, node):
if node in self.top_iter2path:
path = self.top_iter2path[node]
if path+1 < len(self.top_path2iter):
return self.top_path2iter[path+1]
else:
return None
else:
(surname, val) = self.iter2path[node]
return self.path2iter.get((surname, val+1))
def first_child(self, node):
if node is None:
return self.top_path2iter[0]
else:
return self.path2iter.get((node, 0))
def has_child(self, node):
if node is None:
return len(self.sname_sub)
if node in self.sname_sub and self.sname_sub[node]:
return True
return False
def number_of_children(self, node):
if node is None:
return len(self.sname_sub)
if node in self.sname_sub:
return len(self.sname_sub[node])
return 0
def get_nth_child(self, node, n):
if node is None:
if n < len(self.top_path2iter):
return self.top_path2iter[n]
else:
return None
else:
return self.path2iter.get((node, n))
def get_parent_of(self, node):
path = self.iter2path.get(node)
if path:
return path[0]
return None
def build_sub_entry(self, name):
slist = sorted(( (self.sortnames[x], x) \
for x in self.temp_sname_sub[name] ),
key=lambda x: locale.strxfrm(x[0]))
for val, (junk, person_handle) in enumerate(slist):
tpl = (name, val)
self.temp_iter2path[person_handle] = tpl
self.temp_path2iter[tpl] = person_handle
#-------------------------------------------------------------------------
#
# COLUMN constants
#
#-------------------------------------------------------------------------
COLUMN_ID = 1
COLUMN_GENDER = 2
COLUMN_NAME = 3
COLUMN_DEATH = 5
COLUMN_BIRTH = 6
COLUMN_EVENT = 7
COLUMN_FAMILY = 8
COLUMN_CHANGE = 17
COLUMN_MARKER = 18
#-------------------------------------------------------------------------
#
# PeopleModel
#
#-------------------------------------------------------------------------
class PeopleModel(gtk.GenericTreeModel):
class PeopleModel(TreeBaseModel):
"""
Basic GenericTreeModel interface to handle the Tree interface for
the PersonView
"""
# Model types
GENERIC = 0
SEARCH = 1
FAST = 2
# Column numbers
_ID_COL = 1
_GENDER_COL = 2
_NAME_COL = 3
_DEATH_COL = 5
_BIRTH_COL = 6
_EVENT_COL = 7
_FAMILY_COL = 8
_CHANGE_COL = 17
_MARKER_COL = 18
_GENDER = [ _(u'female'), _(u'male'), _(u'unknown') ]
# dynamic calculation of column indices, for use by various Views
COLUMN_INT_ID = 12
# The following is accessed from PersonView - CHECK
COLUMN_INT_ID = 12 # dynamic calculation of column indices
# indices into main column definition table
COLUMN_DEF_LIST = 0
COLUMN_DEF_HEADER = 1
COLUMN_DEF_TYPE = 2
def __init__(self, db, filter_info=None, skip=[]):
def __init__(self, db, scol=0, order=gtk.SORT_ASCENDING, search=None,
skip=set(), sort_map=None):
"""
Initialize the model building the initial data
"""
gtk.GenericTreeModel.__init__(self)
self.lru_name = LRU(TreeBaseModel._CACHE_SIZE)
self.lru_bdate = LRU(TreeBaseModel._CACHE_SIZE)
self.lru_ddate = LRU(TreeBaseModel._CACHE_SIZE)
self.db = db
self.in_build = False
self.lru_data = LRU(_CACHE_SIZE)
self.lru_name = LRU(_CACHE_SIZE)
self.lru_bdate = LRU(_CACHE_SIZE)
self.lru_ddate = LRU(_CACHE_SIZE)
self.gen_cursor = db.get_person_cursor
self.map = db.get_raw_person_data
self.scol = scol
config.connect("preferences.todo-color",
self.update_todo)
config.connect("preferences.custom-marker-color",
self.update_custom)
config.connect("preferences.complete-color",
self.update_complete)
self.complete_color = config.get('preferences.complete-color')
self.todo_color = config.get('preferences.todo-color')
self.custom_color = config.get('preferences.custom-marker-color')
self.marker_color_column = 10
self.tooltip_column = 11
self.mapper = NodeTreeMap()
self.total = 0
self.displayed = 0
if filter_info and filter_info != (1, (0, u'', False)):
if filter_info[0] == PeopleModel.GENERIC:
data_filter = filter_info[1]
self._build_data = self._build_filter_sub
elif filter_info[0] == PeopleModel.SEARCH:
col, text, inv = filter_info[1][:3]
func = lambda x: self.on_get_value(x, col) or u""
if col == self._GENDER_COL:
data_filter = ExactSearchFilter(func, text, inv)
else:
data_filter = SearchFilter(func, text, inv)
self._build_data = self._build_search_sub
else:
data_filter = filter_info[1]
self._build_data = self._build_search_sub
else:
self._build_data = self._build_search_sub
data_filter = None
self.current_filter = data_filter
self.rebuild_data(data_filter, skip)
def update_todo(self,client,cnxn_id,entry,data):
self.todo_color = config.get('preferences.todo-color')
def update_custom(self,client,cnxn_id,entry,data):
self.custom_color = config.get('preferences.custom-marker-color')
def update_complete(self,client,cnxn_id,entry,data):
self.complete_color = config.get('preferences.complete-color')
def rebuild_data(self, data_filter=None, skip=[]):
"""
Convience function that calculates the new data and assigns it.
"""
self.calculate_data(data_filter, skip)
self.assign_data()
self.current_filter = data_filter
def _build_search_sub(self,dfilter, skip):
ngn = name_displayer.name_grouping_data
nsn = name_displayer.raw_sorted_name
self.mapper.clear_sort_names()
self.total = 0
self.displayed = 0
with self.db.get_person_cursor() as cursor:
for handle, d in cursor:
self.total += 1
if not (handle in skip or (dfilter and not dfilter.match(handle,self.db))):
name_data = d[PeopleModel._NAME_COL]
group_name = ngn(self.db, name_data)
sorted_name = nsn(name_data)
self.displayed += 1
self.mapper.assign_sort_name(handle, sorted_name, group_name)
def _build_filter_sub(self,dfilter, skip):
ngn = name_displayer.name_grouping_data
nsn = name_displayer.raw_sorted_name
handle_list = self.db.iter_person_handles()
self.total = self.db.get_number_of_people()
if dfilter:
handle_list = dfilter.apply(self.db, handle_list)
self.displayed = len(handle_list)
else:
self.displayed = self.db.get_number_of_people()
self.mapper.clear_sort_names()
status = LongOpStatus(msg="Loading People",
total_steps=self.displayed,
interval=self.displayed//10)
self.db.emit('long-op-start', (status,))
for handle in handle_list:
status.heartbeat()
d = self.db.get_raw_person_data(handle)
if not handle in skip:
name_data = d[PeopleModel._NAME_COL]
group_name = ngn(self.db, name_data)
sorted_name = nsn(name_data)
self.mapper.assign_sort_name(handle, sorted_name, group_name)
status.end()
def calculate_data(self, dfilter=None, skip=[]):
"""
Calculate the new path to node values for the model.
"""
self.clear_cache()
self.in_build = True
self.total = 0
self.displayed = 0
if dfilter:
self.dfilter = dfilter
self.mapper.clear_temp_data()
if not self.db.is_open():
return
self._build_data(dfilter, skip)
self.mapper.build_toplevel()
self.in_build = False
#self.group_list = []
self.fmap = [
self.column_name,
self.column_id,
self.column_gender,
self.column_birth_day,
self.column_birth_place,
self.column_death_day,
self.column_death_place,
self.column_spouse,
self.column_change,
self.column_marker_text,
self.column_marker_color,
self.column_tooltip,
self.column_int_id,
]
self.smap = [
self.sort_name,
self.column_id,
self.column_gender,
self.sort_birth_day,
self.column_birth_place,
self.sort_death_day,
self.column_death_place,
self.column_spouse,
self.column_change,
self.column_marker_text,
self.column_marker_color,
self.column_tooltip,
self.column_int_id,
]
self.hmap = [
self.column_header,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
]
TreeBaseModel.__init__(self, db, search=search, skip=skip,
tooltip_column=11, marker_column=10,
scol=scol, order=order, sort_map=sort_map)
def clear_cache(self):
""" Clear the LRU cache """
TreeBaseModel.clear_cache(self)
self.lru_name.clear()
self.lru_data.clear()
self.lru_bdate.clear()
self.lru_ddate.clear()
def build_sub_entry(self, name):
self.mapper.build_sub_entry(name)
def assign_data(self):
self.mapper.assign_data()
def on_get_flags(self):
"""returns the GtkTreeModelFlags for this particular type of model"""
return gtk.TREE_MODEL_ITERS_PERSIST
def on_get_n_columns(self):
return len(PeopleModel.COLUMN_DEFS)
""" Return the number of columns in the model """
return len(self.fmap)+1
def on_get_path(self, node):
"""returns the tree path (a tuple of indices at the various
levels) for a particular node."""
return self.mapper.get_path(node)
def add_row(self, handle, data):
"""
Add nodes to the node map for a single person.
def is_visable(self, handle):
return self.mapper.has_entry(handle)
handle The handle of the gramps object.
data The object data.
"""
ngn = name_displayer.name_grouping_data
nsn = name_displayer.raw_sorted_name
name_data = data[COLUMN_NAME]
group_name = ngn(self.db, name_data)
sort_key = self.sort_func(data, handle)
def on_get_column_type(self, index):
return PeopleModel.COLUMN_DEFS[index][PeopleModel.COLUMN_DEF_TYPE]
def on_get_iter(self, path):
return self.mapper.get_iter(path)
def on_get_value(self, node, col):
# test for header or data row-type
if self.mapper.has_top_node(node):
# Header rows dont get the foreground color set
if col == self.marker_color_column:
return None
# test for 'header' column being empty (most are)
if not PeopleModel.COLUMN_DEFS[col][PeopleModel.COLUMN_DEF_HEADER]:
return u''
# return values for 'header' row, calling a function
# according to column_defs table
return (PeopleModel.COLUMN_DEFS[col]
[PeopleModel.COLUMN_DEF_HEADER](self, node)
)
else:
# return values for 'data' row, calling a function
# according to column_defs table
try:
if node in self.lru_data:
data = self.lru_data[node]
else:
data = self.db.get_raw_person_data(str(node))
if not self.in_build:
self.lru_data[node] = data
return (PeopleModel.COLUMN_DEFS[col]
[PeopleModel.COLUMN_DEF_LIST](self, data, node)
)
except:
return None
def on_iter_next(self, node):
"""returns the next node at this level of the tree"""
return self.mapper.find_next_node(node)
def on_iter_children(self, node):
"""Return the first child of the node"""
return self.mapper.first_child(node)
def on_iter_has_child(self, node):
"""returns true if this node has children"""
return self.mapper.has_child(node)
def on_iter_n_children(self, node):
return self.mapper.number_of_children(node)
def on_iter_nth_child(self, node, n):
return self.mapper.get_nth_child(node, n)
def on_iter_parent(self, node):
"""returns the parent of this node"""
return self.mapper.get_parent_of(node)
def column_sort_name(self, data, node):
#if group_name not in self.group_list:
#self.group_list.append(group_name)
#self.add_node(None, group_name, group_name, None)
# add as node: parent, child, sortkey, handle; parent and child are
# nodes in the treebasemodel, and will be used as iters
self.add_node(group_name, handle, sort_key, handle)
def sort_name(self, data, node):
n = Name()
n.unserialize(data[PeopleModel._NAME_COL])
n.unserialize(data[COLUMN_NAME])
return name_displayer.sort_string(n)
def column_spouse(self, data, node):
spouses_names = u""
handle = data[0]
for family_handle in data[PeopleModel._FAMILY_COL]:
for family_handle in data[COLUMN_FAMILY]:
family = self.db.get_family_from_handle(family_handle)
for spouse_id in [family.get_father_handle(),
family.get_mother_handle()]:
@ -485,43 +218,49 @@ class PeopleModel(gtk.GenericTreeModel):
if node in self.lru_name:
name = self.lru_name[node]
else:
name = name_displayer.raw_sorted_name(data[PeopleModel._NAME_COL])
if not self.in_build:
name = name_displayer.raw_sorted_name(data[COLUMN_NAME])
if not self._in_build:
self.lru_name[node] = name
return name
def column_id(self, data, node):
return data[PeopleModel._ID_COL]
return data[COLUMN_ID]
def column_change(self, data, node):
return unicode(
time.strftime('%x %X',
time.localtime(data[PeopleModel._CHANGE_COL])),
time.localtime(data[COLUMN_CHANGE])),
GrampsLocale.codeset)
def column_gender(self, data, node):
return PeopleModel._GENDER[data[PeopleModel._GENDER_COL]]
return PeopleModel._GENDER[data[COLUMN_GENDER]]
def column_birth_day(self, data, node):
if node in self.lru_bdate:
value = self.lru_bdate[node]
else:
value = self._get_birth_data(data, node)
if not self.in_build:
value = self._get_birth_data(data, node, False)
if not self._in_build:
self.lru_bdate[node] = value
return value
def sort_birth_day(self, data, node):
return self._get_birth_data(data, node, True)
def _get_birth_data(self, data, node):
index = data[PeopleModel._BIRTH_COL]
def _get_birth_data(self, data, node, sort_mode):
index = data[COLUMN_BIRTH]
if index != -1:
try:
local = data[PeopleModel._EVENT_COL][index]
local = data[COLUMN_EVENT][index]
b = EventRef()
b.unserialize(local)
birth = self.db.get_event_from_handle(b.ref)
date_str = DateHandler.get_date(birth)
if date_str != "":
retval = cgi.escape(date_str)
if sort_mode:
retval = "%09d" % birth.get_date_object().get_sort_value()
else:
date_str = DateHandler.get_date(birth)
if date_str != "":
retval = cgi.escape(date_str)
if not DateHandler.get_date_valid(birth):
return invalid_date_format % retval
else:
@ -529,7 +268,7 @@ class PeopleModel(gtk.GenericTreeModel):
except:
return u''
for event_ref in data[PeopleModel._EVENT_COL]:
for event_ref in data[COLUMN_EVENT]:
er = EventRef()
er.unserialize(event_ref)
event = self.db.get_event_from_handle(er.ref)
@ -538,7 +277,10 @@ class PeopleModel(gtk.GenericTreeModel):
if (etype in [EventType.BAPTISM, EventType.CHRISTEN]
and er.get_role() == EventRoleType.PRIMARY
and date_str != ""):
retval = u"<i>%s</i>" % cgi.escape(date_str)
if sort_mode:
retval = "%09d" % event.get_date_object().get_sort_value()
else:
retval = u"<i>%s</i>" % cgi.escape(date_str)
if not DateHandler.get_date_valid(event):
return invalid_date_format % retval
else:
@ -550,22 +292,28 @@ class PeopleModel(gtk.GenericTreeModel):
if node in self.lru_ddate:
value = self.lru_ddate[node]
else:
value = self._get_death_data(data, node)
if not self.in_build:
value = self._get_death_data(data, node, False)
if not self._in_build:
self.lru_ddate[node] = value
return value
def sort_death_day(self, data, node):
return self._get_death_data(data, node, True)
def _get_death_data(self, data, node):
index = data[PeopleModel._DEATH_COL]
def _get_death_data(self, data, node, sort_mode):
index = data[COLUMN_DEATH]
if index != -1:
try:
local = data[PeopleModel._EVENT_COL][index]
local = data[COLUMN_EVENT][index]
ref = EventRef()
ref.unserialize(local)
event = self.db.get_event_from_handle(ref.ref)
date_str = DateHandler.get_date(event)
if date_str != "":
retval = cgi.escape(date_str)
if sort_mode:
retval = "%09d" % event.get_date_object().get_sort_value()
else:
date_str = DateHandler.get_date(event)
if date_str != "":
retval = cgi.escape(date_str)
if not DateHandler.get_date_valid(event):
return invalid_date_format % retval
else:
@ -573,16 +321,21 @@ class PeopleModel(gtk.GenericTreeModel):
except:
return u''
for event_ref in data[PeopleModel._EVENT_COL]:
for event_ref in data[COLUMN_EVENT]:
er = EventRef()
er.unserialize(event_ref)
event = self.db.get_event_from_handle(er.ref)
etype = event.get_type()
date_str = DateHandler.get_date(event)
if (etype in [EventType.BURIAL, EventType.CREMATION, EventType.CAUSE_DEATH]
if (etype in [EventType.BURIAL,
EventType.CREMATION,
EventType.CAUSE_DEATH]
and er.get_role() == EventRoleType.PRIMARY
and date_str):
retval = "<i>%s</i>" % cgi.escape(date_str)
if sort_mode:
retval = "%09d" % event.get_date_object().get_sort_value()
else:
retval = "<i>%s</i>" % cgi.escape(date_str)
if not DateHandler.get_date_valid(event):
return invalid_date_format % retval
else:
@ -590,10 +343,10 @@ class PeopleModel(gtk.GenericTreeModel):
return u""
def column_birth_place(self, data, node):
index = data[PeopleModel._BIRTH_COL]
index = data[COLUMN_BIRTH]
if index != -1:
try:
local = data[PeopleModel._EVENT_COL][index]
local = data[COLUMN_EVENT][index]
br = EventRef()
br.unserialize(local)
event = self.db.get_event_from_handle(br.ref)
@ -607,7 +360,7 @@ class PeopleModel(gtk.GenericTreeModel):
except:
return u''
for event_ref in data[PeopleModel._EVENT_COL]:
for event_ref in data[COLUMN_EVENT]:
er = EventRef()
er.unserialize(event_ref)
event = self.db.get_event_from_handle(er.ref)
@ -625,10 +378,10 @@ class PeopleModel(gtk.GenericTreeModel):
return u""
def column_death_place(self, data, node):
index = data[PeopleModel._DEATH_COL]
index = data[COLUMN_DEATH]
if index != -1:
try:
local = data[PeopleModel._EVENT_COL][index]
local = data[COLUMN_EVENT][index]
dr = EventRef()
dr.unserialize(local)
event = self.db.get_event_from_handle(dr.ref)
@ -642,12 +395,13 @@ class PeopleModel(gtk.GenericTreeModel):
except:
return u''
for event_ref in data[PeopleModel._EVENT_COL]:
for event_ref in data[COLUMN_EVENT]:
er = EventRef()
er.unserialize(event_ref)
event = self.db.get_event_from_handle(er.ref)
etype = event.get_type()
if (etype in [EventType.BURIAL, EventType.CREMATION, EventType.CAUSE_DEATH]
if (etype in [EventType.BURIAL, EventType.CREMATION,
EventType.CAUSE_DEATH]
and er.get_role() == EventRoleType.PRIMARY):
place_handle = event.get_place_handle()
@ -659,18 +413,18 @@ class PeopleModel(gtk.GenericTreeModel):
return u""
def column_marker_text(self, data, node):
if PeopleModel._MARKER_COL < len(data):
return str(data[PeopleModel._MARKER_COL])
if COLUMN_MARKER < len(data):
return str(data[COLUMN_MARKER])
return ""
def column_marker_color(self, data, node):
try:
if data[PeopleModel._MARKER_COL]:
if data[PeopleModel._MARKER_COL][0] == MarkerType.COMPLETE:
if data[COLUMN_MARKER]:
if data[COLUMN_MARKER][0] == MarkerType.COMPLETE:
return self.complete_color
if data[PeopleModel._MARKER_COL][0] == MarkerType.TODO_TYPE:
if data[COLUMN_MARKER][0] == MarkerType.TODO_TYPE:
return self.todo_color
if data[PeopleModel._MARKER_COL][0] == MarkerType.CUSTOM:
if data[COLUMN_MARKER][0] == MarkerType.CUSTOM:
return self.custom_color
except IndexError:
pass
@ -694,24 +448,3 @@ class PeopleModel(gtk.GenericTreeModel):
def column_header_view(self, node):
return True
# table of column definitions
# (unless this is declared after the PeopleModel class, an error is thrown)
COLUMN_DEFS = [
(column_name, column_header, str),
(column_id, None, str),
(column_gender, None, str),
(column_birth_day, None, str),
(column_birth_place, None, str),
(column_death_day, None, str),
(column_death_place, None, str),
(column_spouse, None, str),
(column_change, None, str),
(column_marker_text, None, str),
(column_marker_color, None, str),
# the order of the above columns must match PeopleView.column_names
# these columns are hidden, and must always be last in the list
(column_tooltip, None, object),
(column_int_id, None, str),
]

View File

@ -37,7 +37,6 @@ import pango
import const
import ManagedWindow
from Filters import SearchBar
from DisplayModels import PeopleModel
from glade import Glade
#-------------------------------------------------------------------------
@ -62,7 +61,7 @@ class BaseSelector(ManagedWindow.ManagedWindow):
set of handles to skip in the view, and search_bar to show the
SearchBar at the top or not.
"""
self.filter = filter
self.filter = (2, filter, False)
# Set window title, some selectors may set self.title in their __init__
if not hasattr(self, 'title'):
@ -231,6 +230,12 @@ class BaseSelector(ManagedWindow.ManagedWindow):
"""
raise NotImplementedError
def exact_search(self):
"""
Returns a tuple indicating columns requiring an exact search
"""
return ()
def setup_filter(self):
"""
Builds the default filters and add them to the filter bar.
@ -244,23 +249,25 @@ class BaseSelector(ManagedWindow.ManagedWindow):
"""
Builds the selection people see in the Selector
"""
#search info for the
filter_info = (False, self.search_bar.get_value(), False)
if self.filter:
filter_info = self.filter
else:
#search info for the
if self.search_bar.get_value()[0] in self.exact_search():
filter_info = (0, self.search_bar.get_value(), True)
else:
filter_info = (0, self.search_bar.get_value(), False)
#set up cols the first time
if self.setupcols :
self.add_columns(self.tree)
#reset the model with correct sorting
if self.get_model_class() is PeopleModel:
self.model = PeopleModel(self.db,
(PeopleModel.FAST, self.filter),
skip=self.skip_list)
else:
self.model = self.get_model_class()(self.db, self.sort_col,
self.sortorder,
sort_map=self.column_order(),
skip=self.skip_list,
search=filter_info)
self.model = self.get_model_class()(self.db, self.sort_col,
self.sortorder,
sort_map=self.column_order(),
skip=self.skip_list,
search=filter_info)
self.tree.set_model(self.model)
@ -272,10 +279,7 @@ class BaseSelector(ManagedWindow.ManagedWindow):
self.columns[self.sort_col].set_sort_order(self.sortorder)
# set the search column to be the sorted column
if self.get_model_class() is PeopleModel:
search_col = 0
else:
search_col = self.column_order()[self.sort_col][1]
search_col = self.column_order()[self.sort_col][1]
self.tree.set_search_column(search_col)
self.setupcols = False
@ -283,18 +287,17 @@ class BaseSelector(ManagedWindow.ManagedWindow):
def column_clicked(self, obj, data):
if self.sort_col != data:
self.sortorder = gtk.SORT_ASCENDING
self.sort_col = data
self.build_tree()
else:
if (self.columns[data].get_sort_order() == gtk.SORT_DESCENDING
or not self.columns[data].get_sort_indicator()):
self.sortorder = gtk.SORT_ASCENDING
else:
self.sortorder = gtk.SORT_DESCENDING
self.model.reverse_order()
self.sort_col = data
handle = self.first_selected()
self.build_tree()
if handle:
path = self.model.on_get_path(handle)
self.selection.select_path(path)
@ -303,9 +306,12 @@ class BaseSelector(ManagedWindow.ManagedWindow):
return True
def show_toggle(self, obj):
filter = None if obj.get_active() else self.filter
filter_info = None if obj.get_active() else self.filter
self.model = PeopleModel(self.db, (PeopleModel.FAST, filter),
skip=self.skip_list)
self.model = self.get_model_class()(self.db, self.sort_col,
self.sortorder,
sort_map=self.column_order(),
skip=self.skip_list,
search=filter_info)
self.tree.set_model(self.model)
self.tree.grab_focus()

View File

@ -107,6 +107,12 @@ class SelectPerson(BaseSelector):
]
return column_names
def exact_search(self):
"""
Returns a tuple indicating columns requiring an exact search
"""
return (2,) # Gender ('female' contains the string 'male')
def _on_row_activated(self, treeview, path, view_col):
store, paths = self.selection.get_selected_rows()
if paths and len(paths[0]) == 2 :

View File

@ -1404,9 +1404,9 @@ class GrampsDbRead(GrampsDbBase, Callback):
Return the Person display common information stored in the database's
metadata.
"""
default = [(1, 1, 100), (1, 2, 100), (1, 3, 150), (0, 4, 150),
(1, 5, 150), (0, 6, 150), (0, 7, 100), (0, 8, 100),
]
default = [(1, 0, 250), (1, 1, 50), (1, 2, 75), (1, 3, 100),
(1, 4, 175), (1, 5, 100), (1, 6, 175), (1, 7, 100),
(0, 8, 100)]
return self.__get_column_order(PERSON_COL_KEY, default)
def __get_columns(self, key, default):

View File

@ -34,7 +34,7 @@ import cPickle as pickle
import time
import logging
_LOG = logging.getLogger('.listview')
_LOG = logging.getLogger('.gui.listview')
#----------------------------------------------------------------
#
@ -215,14 +215,15 @@ class ListView(NavigationView):
def build_tree(self):
if self.active:
cput = time.clock()
cput0 = time.clock()
if config.get('interface.filter'):
filter_info = (True, self.generic_filter)
filter_info = (True, self.generic_filter, False)
else:
if self.search_bar.get_value()[0] in self.exact_search():
filter_info = (False, self.search_bar.get_value(), True)
value = self.search_bar.get_value()
if value[0] in self.exact_search():
filter_info = (False, value, True)
else:
filter_info = (False, self.search_bar.get_value(), False)
filter_info = (False, value, False)
if self.dirty or not self.model:
self.model = self.make_model(self.dbstate.db, self.sort_col,
@ -235,8 +236,11 @@ class ListView(NavigationView):
self.model.set_search(filter_info)
self.model.rebuild_data()
cput1 = time.clock()
self.build_columns()
cput2 = time.clock()
self.list.set_model(self.model)
cput3 = time.clock()
self.__display_column_sort()
self.goto_active(None)
@ -244,11 +248,17 @@ class ListView(NavigationView):
self.tooltips = TreeTips.TreeTips(
self.list, self.model.tooltip_column, True)
self.dirty = False
cput4 = time.clock()
self.uistate.show_filter_results(self.dbstate,
self.model.displayed(),
self.model.total())
_LOG.debug(self.__class__.__name__ + ' build_tree ' +
str(time.clock() - cput) + ' sec')
str(time.clock() - cput0) + ' sec')
_LOG.debug('parts ' + str(cput1-cput0) + ' , '
+ str(cput2-cput1) + ' , '
+ str(cput3-cput2) + ' , '
+ str(cput4-cput3) + ' , '
+ str(time.clock() - cput4))
else:
self.dirty = True
@ -534,12 +544,13 @@ class ListView(NavigationView):
handle = self.first_selected()
if config.get('interface.filter'):
filter_info = (True, self.generic_filter)
filter_info = (True, self.generic_filter, False)
else:
if self.search_bar.get_value()[0] in self.exact_search():
filter_info = (False, self.search_bar.get_value(), True)
value = self.search_bar.get_value()
if value[0] in self.exact_search():
filter_info = (False, value, True)
else:
filter_info = (False, self.search_bar.get_value(), False)
filter_info = (False, value, False)
if same_col:
self.model.reverse_order()

View File

@ -4,6 +4,7 @@
# Copyright (C) 2000-2007 Donald N. Allingham
# Copyright (C) 2009 Gary Burton
# Copyright (C) 2009 Nick Hall
# Copyright (C) 2009 Benny Malengier
#
# 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
@ -49,20 +50,21 @@ import gtk
#
#-------------------------------------------------------------------------
import config
from Utils import conv_unicode_tosrtkey_ongtk
from gen.utils.longop import LongOpStatus
from Filters import SearchFilter
# from Filters import ExactSearchFilter
from Lru import LRU
from bisect import bisect_right
from Filters import SearchFilter, ExactSearchFilter
#-------------------------------------------------------------------------
#
# TreeNodeMap
# TreeBaseModel
#
#-------------------------------------------------------------------------
class TreeNodeMap(object):
class TreeBaseModel(gtk.GenericTreeModel):
"""
A NodeMap for a hierarchical treeview. The map defines the mapping
between a unique node and a path. Paths are defined by a tuple.
The base class for all hierarchical treeview models. The model defines the
mapping between a unique node and a path. Paths are defined by a tuple.
The first element is an integer specifying the position in the top
level of the hierarchy. The next element relates to the next level
in the hierarchy. The number of elements depends on the depth of the
@ -75,275 +77,56 @@ class TreeNodeMap(object):
is set to None if no gramps object is associated with the
node.
children A dictionary of parent nodes. Each entry is a list of
(child, sortkey) tuples. The list is sorted during the
(sortkey, child) tuples. The list is sorted during the
build. The top node of the hierarchy is None.
path2node A dictionary of paths. Each entry is a node.
node2path A dictionary of nodes. Each entry is a path.
handle2node A dictionary of gramps handles. Each entry is a node.
Nodes are added using the add_node method.
The path2node and node2path mapping is built using build_toplevel.
A simple recursive algorithm is used.
Branches of the tree can be re-built using build_sub_entry.
"""
def __init__(self):
"""
Initialise data structures
"""
self.tree = {}
self.children = {}
self.path2node = {}
self.node2path = {}
self.handle2node = {}
self.__reverse = False
def clear(self):
"""
Clear the entire map.
"""
self.tree = {}
self.children = {}
self.path2node = {}
self.node2path = {}
self.handle2node = {}
def clear_sub_entry(self, node):
"""
Clear a single branch of the map.
"""
if node is None:
self.path2node = {}
self.node2path = {}
else:
if node in self.children:
for child in self.children[node]:
self.clear_node(child[0])
def clear_node(self, node):
if node in self.node2path:
path = self.node2path[node]
del self.path2node[path]
del self.node2path[node]
if node in self.children:
for child in self.children[node]:
self.clear_node(child[0])
def add_node(self, parent, child, sortkey, handle):
"""
Add a node to the map.
parent The parent node for the child. None for top level.
child A unique ID for the node.
sortkey A key by which to sort child nodes of each parent.
handle The gramps handle of the object corresponding to the
node. None if the node does not have a handle.
"""
if child in self.tree:
if handle:
self.tree[child][1] = handle
node_added = False
else:
self.tree[child] = [parent, handle]
if parent in self.children:
self.children[parent] += [(child, sortkey)]
else:
self.children[parent] = [(child, sortkey)]
node_added = True
if handle:
self.handle2node[handle] = child
return node_added
def remove_node(self, node):
if node in self.children:
self.tree[node][1] = None
node_removed = False
else:
parent = self.tree[node][0]
del self.tree[node]
new_list = []
for child in self.children[parent]:
if child[0] != node:
new_list.append(child)
if len(new_list) == 0:
del self.children[parent]
else:
self.children[parent] = new_list
node_removed = True
return node_removed
def build_sub_entry(self, node, path, sort):
"""
Build the path2node and node2path maps for the children of a
given node and recursively builds the next level down.
node The parent node.
path The path of the parent node.
"""
if sort:
self.children[node].sort(key=lambda x: locale.strxfrm(x[1]))
for i, child in enumerate(self.children[node]):
if self.__reverse:
new_path = path + [len(self.children[node]) - i - 1]
else:
new_path = path + [i]
self.path2node[tuple(new_path)] = child[0]
self.node2path[child[0]] = tuple(new_path)
if child[0] in self.children:
self.build_sub_entry(child[0], new_path, sort)
def build_toplevel(self, sort=True):
"""
Build the complete map from the top level.
"""
if len(self.tree) == 0:
return
self.build_sub_entry(None, [], sort)
def reverse_order(self):
self.__reverse = not self.__reverse
self.path2node = {}
self.node2path = {}
self.build_toplevel(sort=False)
def get_handle(self, node):
"""
Get the gramps handle for a node. Return None if the node does
not correspond to a gramps object.
"""
if node in self.tree:
return self.tree[node][1]
else:
return None
def get_node(self, handle):
"""
Get the node for a handle.
"""
if handle in self.handle2node:
return self.handle2node[handle]
else:
return None
# The following methods support the public interface of the
# GenericTreeModel.
def get_path(self, node):
"""
Get the path for a node.
"""
# For trees without the active person a key error is thrown
return self.node2path[node]
def get_iter(self, path):
"""
Build the complete map from the top level.
"""
if path in self.path2node:
return self.path2node[path]
else:
# Empty tree
return None
def find_next_node(self, node):
"""
Get the next node with the same parent as the given node.
"""
path_list = list(self.node2path[node])
path_list[len(path_list)-1] += 1
path = tuple(path_list)
if path in self.path2node:
return self.path2node[path]
else:
return None
def first_child(self, node):
"""
Get the first child of the given node.
"""
if node in self.children:
if self.__reverse:
size = len(self.children[node])
return self.children[node][size - 1][0]
else:
return self.children[node][0][0]
else:
return None
def has_child(self, node):
"""
Find if the given node has any children.
"""
if node in self.children:
return True
else:
return False
def number_of_children(self, node):
"""
Get the number of children of the given node.
"""
if node in self.children:
return len(self.children[node])
else:
return 0
def get_nth_child(self, node, index):
"""
Get the nth child of the given node.
"""
if node in self.children:
if len(self.children[node]) > index:
if self.__reverse:
size = len(self.children[node])
return self.children[node][size - index - 1][0]
else:
return self.children[node][index][0]
else:
return None
else:
return None
def get_parent_of(self, node):
"""
Get the parent of the given node.
"""
if node in self.tree:
return self.tree[node][0]
else:
return None
#-------------------------------------------------------------------------
#
# TreeBaseModel
#
#-------------------------------------------------------------------------
class TreeBaseModel(gtk.GenericTreeModel):
"""
The base class for all hierarchical treeview models.
It keeps a TreeNodeMap, and obtains data from database as needed.
The model obtains data from database as needed and holds a cache of most
recently used data.
As iter for generictreemodel, node is used. This will be the handle for
database objects.
Creation:
db : the database
tooltip_column : column number of tooltip
marker_column : column number of marker
search : the search that must be shown
skip : values not to show
scol : column on which to sort
order : order of the sort
sort_map : mapping from columns seen on the GUI and the columns
as defined here
nrgroups : maximum number of grouping level, 0 = no group,
1= one group, .... Some optimizations can be for only
one group. nrgroups=0 should never be used, as then a
flatbasemodel should be used
group_can_have_handle :
can groups have a handle. If False, this means groups
are only used to group subnodes, not for holding data and
showing subnodes
"""
# LRU cache size
_CACHE_SIZE = 250
# Search/Filter modes
GENERIC = 0
SEARCH = 1
FAST = 2
def __init__(self, db, tooltip_column, marker_column=None,
search=None, skip=set(),
scol=0, order=gtk.SORT_ASCENDING, sort_map=None):
scol=0, order=gtk.SORT_ASCENDING, sort_map=None,
nrgroups = 1,
group_can_have_handle = False):
cput = time.clock()
gtk.GenericTreeModel.__init__(self)
# Initialise data structures
self.tree = {}
self.children = {}
self.children[None] = []
self.handle2node = {}
self.__reverse = (order == gtk.SORT_DESCENDING)
self.nrgroups = nrgroups
self.group_can_have_handle = group_can_have_handle
self.set_property("leak_references", False)
self.db = db
#normally sort on first column, so scol=0
if sort_map:
@ -353,11 +136,12 @@ class TreeBaseModel(gtk.GenericTreeModel):
#we need the model col, that corresponds with scol
col = self.sort_map[scol][1]
self.sort_func = self.smap[col]
self.sort_col = col
else:
self.sort_func = self.smap[scol]
self.sort_col = scol
self.sort_col = scol
self.in_build = False
self._in_build = False
self.lru_data = LRU(TreeBaseModel._CACHE_SIZE)
@ -372,15 +156,13 @@ class TreeBaseModel(gtk.GenericTreeModel):
self.todo_color = config.get('preferences.todo-color')
self.custom_color = config.get('preferences.custom-marker-color')
self.mapper = TreeNodeMap()
self.set_search(search)
self.tooltip_column = tooltip_column
self.marker_color_column = marker_column
self._tooltip_column = tooltip_column
self._marker_column = marker_column
self.__total = 0
self.__displayed = 0
self.set_search(search)
self.rebuild_data(self.current_filter, skip)
_LOG.debug(self.__class__.__name__ + ' __init__ ' +
@ -388,20 +170,63 @@ class TreeBaseModel(gtk.GenericTreeModel):
def __update_todo(self, *args):
"""
Update the todo color when the preferences change.
"""
self.todo_color = config.get('preferences.todo-color')
def __update_custom(self, *args):
"""
Update the custom color when the preferences change.
"""
self.custom_color = config.get('preferences.custom-marker-color')
def __update_complete(self, *args):
"""
Update the complete color when the preferences change.
"""
self.complete_color = config.get('preferences.complete-color')
def displayed(self):
"""
Return the number of rows displayed.
"""
return self.__displayed
def total(self):
"""
Return the total number of rows without a filter or search condition.
"""
return self.__total
def tooltip_column(self):
"""
Return the tooltip column.
"""
return self._tooltip_column
def marker_column(self):
"""
Return the marker color column.
"""
return self._marker_column
def clear_cache(self):
"""
Clear the LRU cache.
"""
self.lru_data.clear()
def clear(self):
"""
Clear the data map.
"""
self.tree = {}
self.children = {}
self.children[None] = []
self.handle2node = {}
self.__reverse = False
def set_search(self, search):
"""
Change the search function that filters the data in the model.
@ -412,24 +237,28 @@ class TreeBaseModel(gtk.GenericTreeModel):
with the new entries
"""
if search:
if search[0]:
if search[0] == 1: # Filter
#following is None if no data given in filter sidebar
self.search = search[1]
self._build_data = self._build_filter_sub
else:
self._build_data = self._rebuild_filter
elif search[0] == 0: # Search
if search[1]:
# we have search[1] = (index, text_unicode, inversion)
col = search[1][0]
text = search[1][1]
inv = search[1][2]
col, text, inv = search[1]
func = lambda x: self._get_value(x, col) or u""
self.search = SearchFilter(func, text, inv)
if search[2]:
self.search = ExactSearchFilter(func, text, inv)
else:
self.search = SearchFilter(func, text, inv)
else:
self.search = None
self._build_data = self._build_search_sub
self._build_data = self._rebuild_search
else: # Fast filter
self.search = search[1]
self._build_data = self._rebuild_search
else:
self.search = None
self._build_data = self._build_search_sub
self._build_data = self._rebuild_search
self.current_filter = self.search
@ -437,23 +266,28 @@ class TreeBaseModel(gtk.GenericTreeModel):
"""
Rebuild the data map.
"""
cput = time.clock()
self.clear_cache()
self.in_build = True
self.mapper.clear()
self._in_build = True
if not self.db.is_open():
return
#self._build_data(data_filter, skip)
self.clear()
self._build_data(self.current_filter, skip)
self.mapper.build_toplevel()
self.sort_data()
self.in_build = False
self._in_build = False
self.current_filter = data_filter
_LOG.debug(self.__class__.__name__ + ' rebuild_data ' +
str(time.clock() - cput) + ' sec')
def _build_search_sub(self, dfilter, skip):
def _rebuild_search(self, dfilter, skip):
"""
Rebuild the data map where a search condition is applied.
"""
self.__total = 0
self.__displayed = 0
with self.gen_cursor() as cursor:
@ -464,8 +298,10 @@ class TreeBaseModel(gtk.GenericTreeModel):
self.__displayed += 1
self.add_row(handle, data)
def _build_filter_sub(self, dfilter, skip):
def _rebuild_filter(self, dfilter, skip):
"""
Rebuild the data map where a filter is applied.
"""
with self.gen_cursor() as cursor:
handle_list = [key for key, data in cursor]
self.__total = len(handle_list)
@ -487,54 +323,160 @@ class TreeBaseModel(gtk.GenericTreeModel):
self.add_row(handle, data)
status.end()
def reverse_order(self):
self.mapper.reverse_order()
def add_node(self, parent, child, sortkey, handle, add_parent=True):
"""
Add a node to the map.
parent The parent node for the child. None for top level. If
this node does not exist, it will be added under the top
level if add_parent=True. For performance, if you have
added the parent, passing add_parent=False, will skip adding
missing parent
child A unique ID for the node.
sortkey A key by which to sort child nodes of each parent.
handle The gramps handle of the object corresponding to the
node. None if the node does not have a handle.
add_parent Bool, if True, check if parent is present, if not add the
parent as a top group with no handle
"""
sortkey = conv_unicode_tosrtkey_ongtk(sortkey)
if add_parent and not (parent in self.tree):
#add parent to self.tree as a node with no handle, as the first
#group level
self.add_node(None, parent, parent, None, add_parent=False)
if child in self.tree:
#a node is added that is already present,
self._add_dup_node(parent, child, sortkey, handle)
else:
self.tree[child] = (parent, handle)
if parent in self.children:
if self._in_build:
self.children[parent].append((sortkey, child))
else:
index = bisect_right(self.children[parent], (sortkey, child))
self.children[parent].insert(index, (sortkey, child))
else:
self.children[parent] = [(sortkey, child)]
def clear_cache(self):
self.lru_data.clear()
if not self._in_build:
# emit row_inserted signal
path = self.on_get_path(child)
node = self.get_iter(path)
self.row_inserted(path, node)
def build_sub_entry(self, node):
self.mapper.clear_sub_entry(node)
if node is None:
self.mapper.build_toplevel(sort=True)
if handle:
self.handle2node[handle] = child
def _add_dup_node(self, parent, child, sortkey, handle):
"""
How to handle adding a node a second time
Default: if group nodes can have handles, it is allowed to add it
again, and this time setting the handle
Otherwise, a node should never be added twice!
"""
if not self.group_can_have_handle:
raise ValueError, 'attempt to add twice a node to the model %s' % \
str(parent) + ' ' + str(child) + ' ' + sortkey
present_val = self.tree[child]
if handle and present_val[1] is None:
self.tree[child][1] = handle
elif handle is None:
pass
else:
#handle given, and present handle is not None
raise ValueError, 'attempt to add twice a node to the model'
def sort_data(self):
"""
Sort the data in the map according to the value of the sort key.
"""
for node in self.children:
self.children[node].sort()
def remove_node(self, node):
"""
Remove a node from the map.
"""
if node in self.children:
self.tree[node][1] = None
else:
path = self.on_get_path(node)
self.mapper.build_sub_entry(node, list(path), sort=True)
parent = self.tree[node][0]
del self.tree[node]
new_list = []
for child in self.children[parent]:
if child[1] != node:
new_list.append(child)
if len(new_list) == 0:
del self.children[parent]
else:
self.children[parent] = new_list
# emit row_deleted signal
self.row_deleted(path)
def reverse_order(self):
"""
Reverse the order of the map.
"""
cput = time.clock()
self.__reverse = not self.__reverse
self._reverse_level(None)
_LOG.debug(self.__class__.__name__ + ' reverse_order ' +
str(time.clock() - cput) + ' sec')
def _reverse_level(self, node):
"""
Reverse the order of a single level in the map.
"""
if node in self.children:
rows = range(len(self.children[node]))
rows.reverse()
if node is None:
path = iter = None
else:
path = self.on_get_path(node)
iter = self.get_iter(path)
self.rows_reordered(path, iter, rows)
for child in self.children[node]:
self._reverse_level(child[1])
def add_row(self, handle, data):
pass
"""
Add a row to the model. In general this will add more then one node.
"""
raise NotImplementedError
def add_row_by_handle(self, handle):
"""
Add a row to the model.
"""
cput = time.clock()
data = self.map(handle)
top_node = self.add_row(handle, data)
parent_node = self.on_iter_parent(top_node)
self.build_sub_entry(parent_node)
self.add_row(handle, data)
path = self.on_get_path(top_node)
node = self.get_iter(path)
# only one row_inserted and row_has_child_toggled is needed?
self.row_inserted(path, node)
self.row_has_child_toggled(path, node)
_LOG.debug(self.__class__.__name__ + ' add_row_by_handle ' +
str(time.clock() - cput) + ' sec')
def delete_row_by_handle(self, handle):
"""
Delete a row from the model.
"""
cput = time.clock()
self.clear_cache()
node = self.get_node(handle)
parent = self.on_iter_parent(node)
while node and self.mapper.remove_node(node):
path = self.on_get_path(node)
node = parent
parent = self.on_iter_parent(parent)
self.build_sub_entry(node)
self.remove_node(node)
self.row_deleted(path)
while parent is not None:
next_parent = self.on_iter_parent(parent)
if parent not in self.children:
self.remove_node(parent)
parent = next_parent
_LOG.debug(self.__class__.__name__ + ' delete_row_by_handle ' +
str(time.clock() - cput) + ' sec')
def update_row_by_handle(self, handle):
"""
@ -543,31 +485,26 @@ class TreeBaseModel(gtk.GenericTreeModel):
self.delete_row_by_handle(handle)
self.add_row_by_handle(handle)
# If the node hasn't moved all we need is to call row_changed.
# If the node hasn't moved, all we need is to call row_changed.
#self.row_changed(path, node)
def get_node(self, handle):
return self.mapper.get_node(handle)
def get_handle(self, node):
return self.mapper.get_handle(node)
def _get_value(self, handle, col):
"""
Returns the contents of a given column of a gramps object
Get the gramps handle for a node. Return None if the node does
not correspond to a gramps object.
"""
try:
if handle in self.lru_data:
data = self.lru_data[handle]
else:
data = self.map(handle)
if not self.in_build:
self.lru_data[handle] = data
return (self.fmap[col](data, handle))
except:
return None
ret = self.tree.get(node)
if ret:
return ret[1]
return ret
def get_node(self, handle):
"""
Get the node for a handle.
"""
return self.handle2node.get(handle)
# The following define the public interface of gtk.GenericTreeModel
# The following implement the public interface of gtk.GenericTreeModel
def on_get_flags(self):
"""
@ -582,34 +519,22 @@ class TreeBaseModel(gtk.GenericTreeModel):
"""
raise NotImplementedError
def on_get_path(self, node):
"""
See gtk.GenericTreeModel
"""
return self.mapper.get_path(node)
def on_get_column_type(self, index):
"""
See gtk.GenericTreeModel
"""
if index == self.tooltip_column:
if index == self._tooltip_column:
return object
return str
def on_get_iter(self, path):
"""
See gtk.GenericTreeModel
"""
return self.mapper.get_iter(path)
def on_get_value(self, node, col):
"""
See gtk.GenericTreeModel
"""
handle = self.mapper.get_handle(node)
handle = self.get_handle(node)
if handle is None:
# Header rows dont get the foreground color set
if col == self.marker_color_column:
if col == self._marker_column:
return None
# Look for header fuction for column and call it
@ -623,39 +548,131 @@ class TreeBaseModel(gtk.GenericTreeModel):
# return values for 'data' row, calling a function
# according to column_defs table
return self._get_value(handle, col)
def _get_value(self, handle, col):
"""
Returns the contents of a given column of a gramps object
"""
try:
if handle in self.lru_data:
data = self.lru_data[handle]
else:
data = self.map(handle)
if not self._in_build:
self.lru_data[handle] = data
return (self.fmap[col](data, handle))
except:
return None
def on_iter_next(self, node):
def on_get_iter(self, path):
"""
See gtk.GenericTreeModel
Returns a node from a given path.
"""
return self.mapper.find_next_node(node)
if not self.tree:
return None
node = None
pathlist = list(path)
for index in pathlist:
if self.__reverse:
size = len(self.children[node])
node = self.children[node][size - index - 1][1]
else:
node = self.children[node][index][1]
return node
def on_get_path(self, node):
"""
Returns a path from a given node.
"""
pathlist = []
while node is not None:
parent = self.tree[node][0]
for index, value in enumerate(self.children[parent]):
if value[1] == node:
break
if self.__reverse:
size = len(self.children[parent])
pathlist.append(size - index - 1)
else:
pathlist.append(index)
node = parent
if pathlist is not None:
pathlist.reverse()
return tuple(pathlist)
else:
return None
def on_iter_next(self, node):
"""
Get the next node with the same parent as the given node.
"""
parent = self.tree[node][0]
for index, child in enumerate(self.children[parent]):
if child[1] == node:
break
if self.__reverse:
index -= 1
else:
index += 1
if index >= 0 and index < len(self.children[parent]):
return self.children[parent][index][1]
else:
return None
def on_iter_children(self, node):
"""
See gtk.GenericTreeModel
Get the first child of the given node.
"""
return self.mapper.first_child(node)
if node in self.children:
if self.__reverse:
size = len(self.children[node])
return self.children[node][size - 1][1]
else:
return self.children[node][0][1]
else:
return None
def on_iter_has_child(self, node):
"""
See gtk.GenericTreeModel
Find if the given node has any children.
"""
return self.mapper.has_child(node)
if node in self.children:
return True
else:
return False
def on_iter_n_children(self, node):
"""
See gtk.GenericTreeModel
Get the number of children of the given node.
"""
return self.mapper.number_of_children(node)
if node in self.children:
return len(self.children[node])
else:
return 0
def on_iter_nth_child(self, node, index):
"""
See gtk.GenericTreeModel
Get the nth child of the given node.
"""
return self.mapper.get_nth_child(node, index)
if node in self.children:
if len(self.children[node]) > index:
if self.__reverse:
size = len(self.children[node])
return self.children[node][size - index - 1][1]
else:
return self.children[node][index][1]
else:
return None
else:
return None
def on_iter_parent(self, node):
"""
See gtk.GenericTreeModel
Get the parent of the given node.
"""
return self.mapper.get_parent_of(node)
if node in self.tree:
return self.tree[node][0]
else:
return None