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:
parent
e44a7cc8e2
commit
1b556586a4
File diff suppressed because it is too large
Load Diff
@ -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),
|
||||
]
|
||||
|
@ -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()
|
||||
|
@ -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 :
|
||||
|
@ -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):
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user