Working person treeview, insert/add/delete still todo

read/write db, make sure unicode or utf8, nothing else!


svn: r19966
This commit is contained in:
Benny Malengier 2012-07-10 03:32:08 +00:00
parent 0b8d0a1a95
commit 9da71185a6
9 changed files with 215 additions and 116 deletions

View File

@ -58,7 +58,7 @@ from gen.lib import (MediaObject, Person, Family, Source, Citation, Event,
NameOriginType) NameOriginType)
from gen.db.dbconst import * from gen.db.dbconst import *
from gen.utils.callback import Callback from gen.utils.callback import Callback
from gen.utils.cast import conv_dbstr_to_unicode, conv_unicode_tosrtkey from gen.utils.cast import conv_dbstr_to_unicode
from gen.db import (BsddbBaseCursor, DbReadBase) from gen.db import (BsddbBaseCursor, DbReadBase)
from gen.utils.id import create_id from gen.utils.id import create_id
from gen.errors import DbError from gen.errors import DbError
@ -826,10 +826,10 @@ class DbBsddbRead(DbReadBase, Callback):
Return type is a unicode object Return type is a unicode object
""" """
if isinstance(surname, unicode): if isinstance(surname, unicode):
ssurname = conv_unicode_tosrtkey(surname) ssurname = surname.encode('utf-8')
return unicode(self.name_group.get(ssurname, ssurname), 'utf-8') return conv_dbstr_to_unicode(self.name_group.get(ssurname, ssurname))
else: else:
return unicode(self.name_group.get(surname, surname), 'utf-8') return conv_dbstr_to_unicode(self.name_group.get(surname, surname))
def get_name_group_keys(self): def get_name_group_keys(self):
""" """
@ -844,7 +844,7 @@ class DbBsddbRead(DbReadBase, Callback):
# The use of has_key seems allright because there is no write lock # The use of has_key seems allright because there is no write lock
# on the name_group table when this is called. # on the name_group table when this is called.
if isinstance(name, unicode): if isinstance(name, unicode):
return self.name_group.has_key(conv_unicode_tosrtkey(name)) return self.name_group.has_key(name.encode('utf-8'))
else: else:
return self.name_group.has_key(name) return self.name_group.has_key(name)

View File

@ -62,8 +62,7 @@ from gen.db import (DbBsddbRead, DbWriteBase, BSDDBTxn,
find_surname_name, DbUndoBSDDB as DbUndo) find_surname_name, DbUndoBSDDB as DbUndo)
from gen.db.dbconst import * from gen.db.dbconst import *
from gen.utils.callback import Callback from gen.utils.callback import Callback
from gen.utils.cast import (conv_unicode_tosrtkey_ongtk, conv_dbstr_to_unicode, from gen.utils.cast import (conv_unicode_tosrtkey_ongtk, conv_dbstr_to_unicode)
conv_unicode_tosrtkey)
from gen.updatecallback import UpdateCallback from gen.updatecallback import UpdateCallback
from gen.errors import DbError from gen.errors import DbError
from gen.constfunc import win from gen.constfunc import win
@ -1416,7 +1415,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
if not self.readonly: if not self.readonly:
# Start transaction # Start transaction
with BSDDBTxn(self.env, self.name_group) as txn: with BSDDBTxn(self.env, self.name_group) as txn:
sname = conv_unicode_tosrtkey(name) sname = name.encode('utf-8')
data = txn.get(sname) data = txn.get(sname)
if data is not None: if data is not None:
txn.delete(sname) txn.delete(sname)

View File

@ -107,9 +107,9 @@ class Name(SecondaryObject, PrivacyBase, SurnameBase, CitationBase, NoteBase,
self.group_as = "" self.group_as = ""
self.sort_as = self.DEF self.sort_as = self.DEF
self.display_as = self.DEF self.display_as = self.DEF
self.call = u'' self.call = ""
self.nick = u'' self.nick = ""
self.famnick = u'' self.famnick = ""
def serialize(self): def serialize(self):
""" """
@ -130,9 +130,9 @@ class Name(SecondaryObject, PrivacyBase, SurnameBase, CitationBase, NoteBase,
""" """
Indicate if the name is empty. Indicate if the name is empty.
""" """
namefieldsempty = (self.first_name == u"" and namefieldsempty = (self.first_name == "" and
self.suffix == u"" and self.title == u"" and self.nick ==u"" self.suffix == "" and self.title == "" and self.nick == ""
and self.famnick == u"") and self.famnick == "")
surnamefieldsempty = not (False in surnamefieldsempty = not (False in
[surn.is_empty() for surn in self.surname_list]) [surn.is_empty() for surn in self.surname_list])
return namefieldsempty and surnamefieldsempty return namefieldsempty and surnamefieldsempty

View File

@ -48,6 +48,9 @@ conv_unicode_tosrtkey = lambda x: locale.strxfrm(x.encode('utf-8', 'replace'))
conv_unicode_tosrtkey_ongtk = lambda x: locale.strxfrm(x.encode( conv_unicode_tosrtkey_ongtk = lambda x: locale.strxfrm(x.encode(
codeset, 'replace')) codeset, 'replace'))
conv_str_tosrtkey_ongtk = lambda x: locale.strxfrm(unicode(x,'utf-8').encode(
codeset, 'replace'))
conv_dbstr_to_unicode = lambda x: unicode(x, 'utf-8') conv_dbstr_to_unicode = lambda x: unicode(x, 'utf-8')
def cast_to_bool(val): def cast_to_bool(val):

View File

@ -378,8 +378,9 @@ class ListView(NavigationView):
if self.type_list() == LISTFLAT: if self.type_list() == LISTFLAT:
# Flat # Flat
iter = self.model.nodemap.new_iter(handle)
try: try:
path = self.model.on_get_path(handle) path = self.model.do_get_path(iter)
except: except:
path = None path = None
else: else:
@ -387,15 +388,17 @@ class ListView(NavigationView):
path = None path = None
node = self.model.get_node(handle) node = self.model.get_node(handle)
if node: if node:
parent_node = self.model.on_iter_parent(node) iter = self.model.get_iter(node)
if parent_node: has_parent, parent_iter = self.model.do_iter_parent(iter)
parent_path = self.model.on_get_path(parent_node) if has_parent:
parent_path = self.model.do_get_path(parent_iter)
if parent_path: if parent_path:
for i in range(len(parent_path)): parent_path_list = parent_path.get_indices()
for i in range(len(parent_path_list)):
expand_path = Gtk.TreePath( expand_path = Gtk.TreePath(
tuple([x for x in parent_path[:i+1]])) tuple([x for x in parent_path_list[:i+1]]))
self.list.expand_row(expand_path, False) self.list.expand_row(expand_path, False)
path = self.model.on_get_path(node) path = self.model.do_get_path(iter)
if path is not None: if path is not None:
self.selection.unselect_all() self.selection.unselect_all()
@ -535,11 +538,14 @@ class ListView(NavigationView):
if not prompt: if not prompt:
self.uistate.set_busy_cursor(0) self.uistate.set_busy_cursor(0)
def blist(self, store, path, node, sel_list): def blist(self, store, path, iter, sel_list):
if store.get_flags() & Gtk.TreeModelFlags.LIST_ONLY: '''GtkTreeSelectionForeachFunc
handle = store.get_value(node, self.handle_col) construct a list sel_list with all selected handles
'''
if store.do_get_flags() & Gtk.TreeModelFlags.LIST_ONLY:
handle = store.node_map.get_handle(path)
else: else:
handle = store.get_handle(store.on_get_iter(path)) handle = store.get_handle(store.get_node_from_iter(iter))
if handle is not None: if handle is not None:
sel_list.append(handle) sel_list.append(handle)
@ -756,7 +762,8 @@ class ListView(NavigationView):
store, paths = self.selection.get_selected_rows() store, paths = self.selection.get_selected_rows()
if paths: if paths:
firstsel = paths[0] firstsel = paths[0]
firstnode = self.model.on_get_iter(firstsel) firstnode = self.model.get_node_from_iter(
self.model.do_get_iter(firstsel)[1])
if len(paths)==1 and firstnode.handle is None: if len(paths)==1 and firstnode.handle is None:
return self.expand_collapse_tree_branch() return self.expand_collapse_tree_branch()
else: else:
@ -832,7 +839,8 @@ class ListView(NavigationView):
store, paths = self.selection.get_selected_rows() store, paths = self.selection.get_selected_rows()
if paths: if paths:
firstsel = paths[0] firstsel = paths[0]
firstnode = self.model.on_get_iter(firstsel) firstnode = self.model.get_node_from_iter(
self.model.do_get_iter(firstsel)[1])
if len(paths) == 1 and firstnode.handle is None: if len(paths) == 1 and firstnode.handle is None:
return self.expand_collapse_tree_branch() return self.expand_collapse_tree_branch()
else: else:
@ -840,7 +848,8 @@ class ListView(NavigationView):
store, paths = self.selection.get_selected_rows() store, paths = self.selection.get_selected_rows()
if paths: if paths:
firstsel = paths[0] firstsel = paths[0]
firstnode = self.model.on_get_iter(firstsel) firstnode = self.model.get_node_from_iter(
self.model.do_get_iter(firstsel)[1])
if len(paths) == 1 and firstnode.handle is None: if len(paths) == 1 and firstnode.handle is None:
return self.expand_collapse_tree() return self.expand_collapse_tree()
else: else:
@ -857,7 +866,8 @@ class ListView(NavigationView):
store, paths = self.selection.get_selected_rows() store, paths = self.selection.get_selected_rows()
if paths: if paths:
firstsel = paths[0] firstsel = paths[0]
firstnode = self.model.on_get_iter(firstsel) firstnode = self.model.get_node_from_iter(
self.model.do_get_iter(firstsel)[1])
if firstnode.handle: if firstnode.handle:
return False return False
if self.list.row_expanded(firstsel): if self.list.row_expanded(firstsel):
@ -875,7 +885,8 @@ class ListView(NavigationView):
store, paths = self.selection.get_selected_rows() store, paths = self.selection.get_selected_rows()
if paths: if paths:
firstsel = paths[0] firstsel = paths[0]
firstnode = self.model.on_get_iter(firstsel) firstnode = self.model.get_node_from_iter(
self.model.do_get_iter(firstsel)[1])
if firstnode.handle: if firstnode.handle:
return False return False
if self.list.row_expanded(firstsel): if self.list.row_expanded(firstsel):
@ -996,7 +1007,7 @@ class ListView(NavigationView):
ofile.end_row() ofile.end_row()
else: else:
# Tree model # Tree model
node = self.model.on_get_iter((0,)) node = self.model.get_node_from_iter(self.model.do_get_iter((0,))[1])
self.write_node(node, len(levels), [], ofile, data_cols) self.write_node(node, len(levels), [], ofile, data_cols)
ofile.end_page() ofile.end_page()
@ -1006,18 +1017,20 @@ class ListView(NavigationView):
if node is None: if node is None:
return return
while node is not None: while node is not None:
new_level = level + [self.model.on_get_value(node, 0)] new_level = level + [self.model.do_get_value(node, 0)]
if self.model.get_handle(node): if self.model.get_handle(node):
ofile.start_row() ofile.start_row()
padded_level = new_level + [''] * (depth - len(new_level)) padded_level = new_level + [''] * (depth - len(new_level))
map(ofile.write_cell, padded_level) map(ofile.write_cell, padded_level)
for index in data_cols: for index in data_cols:
ofile.write_cell(self.model.on_get_value(node, index)) ofile.write_cell(self.model.do_get_value(node, index))
ofile.end_row() ofile.end_row()
first_child = self.model.on_iter_children(node) has_child, first_child = self.model.do_iter_children(node)
self.write_node(first_child, depth, new_level, ofile, data_cols) self.write_node(first_child, depth, new_level, ofile, data_cols)
node = self.model.on_iter_next(node) has_next = self.model.do_iter_next(node)
if not has_next:
node = None
#################################################################### ####################################################################
# Template functions # Template functions

View File

@ -151,15 +151,14 @@ class FlatNodeMap(object):
Reverse sets up how the path is determined from the index. If True the Reverse sets up how the path is determined from the index. If True the
first index is the last path first index is the last path
:param index2hndllist: the ascending sorted (sortkey, handle, iter) values :param index2hndllist: the ascending sorted (sortkey, handle) values
as they will appear in the flat treeview. This often is as they will appear in the flat treeview. This often is
a subset of all possible data. a subset of all possible data.
Set iter=None, it will be set internally
:type index2hndllist: a list of (sortkey, handle) tuples :type index2hndllist: a list of (sortkey, handle) tuples
:param fullhndllist: the list of all possilbe ascending sorted :param fullhndllist: the list of all possilbe ascending sorted
[sortkey, handle, iter] values as they will appear in the flat (sortkey, handle) values as they will appear in the flat
treeview if all data is shown. treeview if all data is shown.
:type fullhndllist: a list of (sortkey, handle, iter) tuples :type fullhndllist: a list of (sortkey, handl) tuples
:param identical: identify if index2hndllist and fullhndllist are the :param identical: identify if index2hndllist and fullhndllist are the
same list, so only one is kept in memory. same list, so only one is kept in memory.
:type identical: bool :type identical: bool
@ -446,7 +445,10 @@ class FlatBaseModel(GObject.Object, Gtk.TreeModel):
#inheriting classes must set self.map to obtain the data #inheriting classes must set self.map to obtain the data
self.prev_handle = None self.prev_handle = None
self.prev_data = None self.prev_data = None
#GTK3 We leak ref, yes??
#self.set_property("leak_references", False) #self.set_property("leak_references", False)
self.db = db self.db = db
#normally sort on first column, so scol=0 #normally sort on first column, so scol=0
if sort_map: if sort_map:
@ -752,7 +754,7 @@ class FlatBaseModel(GObject.Object, Gtk.TreeModel):
data = self.map(str(handle)) data = self.map(str(handle))
if data is None: if data is None:
#object is no longer present #object is no longer present
return u'' return ''
self.prev_data = data self.prev_data = data
self.prev_handle = handle self.prev_handle = handle
val = self.fmap[col](self.prev_data) val = self.fmap[col](self.prev_data)
@ -784,13 +786,14 @@ class FlatBaseModel(GObject.Object, Gtk.TreeModel):
else: else:
return False return False
def do_iter_children(self, iter): def do_iter_children(self, iterparent):
""" """
Return the first child of the node Return the first child of the node
See Gtk.TreeModel See Gtk.TreeModel
""" """
#print 'do_iter_children' #print 'do_iter_children'
print 'ERROR: iter children, should not be called in flat base!!' print 'ERROR: iter children, should not be called in flat base!!'
raise NotImplementedError
if handle is None and len(self.node_map): if handle is None and len(self.node_map):
return self.node_map.get_first_handle() return self.node_map.get_first_handle()
return None return None

View File

@ -90,7 +90,7 @@ class PeopleBaseModel(object):
""" """
Basic Model interface to handle the PersonViews Basic Model interface to handle the PersonViews
""" """
_GENDER = [ _(u'female'), _(u'male'), _(u'unknown') ] _GENDER = [ _('female'), _('male'), _('unknown') ]
# The following is accessed from the Person Selector # The following is accessed from the Person Selector
COLUMN_INT_ID = 10 # dynamic calculation of column indices COLUMN_INT_ID = 10 # dynamic calculation of column indices
@ -191,7 +191,11 @@ class PeopleBaseModel(object):
return len(self.fmap)+1 return len(self.fmap)+1
def sort_name(self, data): def sort_name(self, data):
return name_displayer.raw_sorted_name(data[COLUMN_NAME]) name = name_displayer.raw_sorted_name(data[COLUMN_NAME])
# internally we work with utf-8
if isinstance(name, unicode):
name = name.encode('utf-8')
return name
def column_name(self, data): def column_name(self, data):
handle = data[0] handle = data[0]
@ -199,6 +203,9 @@ class PeopleBaseModel(object):
name = self.lru_name[handle] name = self.lru_name[handle]
else: else:
name = name_displayer.raw_display_name(data[COLUMN_NAME]) name = name_displayer.raw_display_name(data[COLUMN_NAME])
# internally we work with utf-8
if isinstance(name, unicode):
name = name.encode('utf-8')
if not self._in_build: if not self._in_build:
self.lru_name[handle] = name self.lru_name[handle] = name
return name return name
@ -214,7 +221,7 @@ class PeopleBaseModel(object):
return value return value
def _get_spouse_data(self, data): def _get_spouse_data(self, data):
spouses_names = u"" spouses_names = ""
for family_handle in data[COLUMN_FAMILY]: for family_handle in data[COLUMN_FAMILY]:
family = self.db.get_family_from_handle(family_handle) family = self.db.get_family_from_handle(family_handle)
for spouse_id in [family.get_father_handle(), for spouse_id in [family.get_father_handle(),
@ -231,7 +238,7 @@ class PeopleBaseModel(object):
def column_id(self, data): def column_id(self, data):
return data[COLUMN_ID] return data[COLUMN_ID]
def sort_change(self,data): def sort_change(self,data):
return "%012x" % data[COLUMN_CHANGE] return "%012x" % data[COLUMN_CHANGE]
@ -274,7 +281,7 @@ class PeopleBaseModel(object):
else: else:
return retval return retval
except: except:
return u'' return ''
for event_ref in data[COLUMN_EVENT]: for event_ref in data[COLUMN_EVENT]:
er = EventRef() er = EventRef()
@ -288,13 +295,13 @@ class PeopleBaseModel(object):
if sort_mode: if sort_mode:
retval = "%09d" % event.get_date_object().get_sort_value() retval = "%09d" % event.get_date_object().get_sort_value()
else: else:
retval = u"<i>%s</i>" % cgi.escape(date_str) retval = "<i>%s</i>" % cgi.escape(date_str)
if not gen.datehandler.get_date_valid(event): if not gen.datehandler.get_date_valid(event):
return invalid_date_format % retval return invalid_date_format % retval
else: else:
return retval return retval
return u"" return ""
def column_death_day(self, data): def column_death_day(self, data):
handle = data[0] handle = data[0]
@ -329,7 +336,7 @@ class PeopleBaseModel(object):
else: else:
return retval return retval
except: except:
return u'' return ''
for event_ref in data[COLUMN_EVENT]: for event_ref in data[COLUMN_EVENT]:
er = EventRef() er = EventRef()
@ -350,7 +357,7 @@ class PeopleBaseModel(object):
return invalid_date_format % retval return invalid_date_format % retval
else: else:
return retval return retval
return u"" return ""
def column_birth_place(self, data): def column_birth_place(self, data):
index = data[COLUMN_BIRTH] index = data[COLUMN_BIRTH]
@ -368,7 +375,7 @@ class PeopleBaseModel(object):
if place_title: if place_title:
return cgi.escape(place_title) return cgi.escape(place_title)
except: except:
return u'' return ''
for event_ref in data[COLUMN_EVENT]: for event_ref in data[COLUMN_EVENT]:
er = EventRef() er = EventRef()
@ -385,7 +392,7 @@ class PeopleBaseModel(object):
if place_title: if place_title:
return "<i>%s</i>" % cgi.escape(place_title) return "<i>%s</i>" % cgi.escape(place_title)
return u"" return ""
def column_death_place(self, data): def column_death_place(self, data):
index = data[COLUMN_DEATH] index = data[COLUMN_DEATH]
@ -403,7 +410,7 @@ class PeopleBaseModel(object):
if place_title: if place_title:
return cgi.escape(place_title) return cgi.escape(place_title)
except: except:
return u'' return ''
for event_ref in data[COLUMN_EVENT]: for event_ref in data[COLUMN_EVENT]:
er = EventRef() er = EventRef()
@ -420,10 +427,10 @@ class PeopleBaseModel(object):
place_title = place.get_title() place_title = place.get_title()
if place_title != "": if place_title != "":
return "<i>" + cgi.escape(place_title) + "</i>" return "<i>" + cgi.escape(place_title) + "</i>"
return u"" return ""
def column_tooltip(self, data): def column_tooltip(self, data):
return u'Person tooltip' return 'Person tooltip'
def column_int_id(self, data): def column_int_id(self, data):
return data[0] return data[0]
@ -528,6 +535,8 @@ class PersonTreeModel(PeopleBaseModel, TreeBaseModel):
name_data = data[COLUMN_NAME] name_data = data[COLUMN_NAME]
group_name = ngn(self.db, name_data) group_name = ngn(self.db, name_data)
if isinstance(group_name, unicode):
group_name = group_name.encode('utf-8')
sort_key = self.sort_func(data) sort_key = self.sort_func(data)
#if group_name not in self.group_list: #if group_name not in self.group_list:

View File

@ -4,7 +4,7 @@
# Copyright (C) 2000-2007 Donald N. Allingham # Copyright (C) 2000-2007 Donald N. Allingham
# Copyright (C) 2009 Gary Burton # Copyright (C) 2009 Gary Burton
# Copyright (C) 2009-2011 Nick Hall # Copyright (C) 2009-2011 Nick Hall
# Copyright (C) 2009 Benny Malengier # Copyright (C) 2009-2012 Benny Malengier
# Copyright (C) 2011 Tim G L lyons # Copyright (C) 2011 Tim G L lyons
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
@ -34,7 +34,7 @@ This module provides the model that is used for all hierarchical treeviews.
# Standard python modules # Standard python modules
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
from __future__ import with_statement from __future__ import with_statement, print_function
import time import time
import locale import locale
from gen.ggettext import gettext as _ from gen.ggettext import gettext as _
@ -55,7 +55,7 @@ from gi.repository import Gtk
# GRAMPS modules # GRAMPS modules
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
from gen.utils.cast import conv_unicode_tosrtkey_ongtk from gen.utils.cast import conv_str_tosrtkey_ongtk, conv_unicode_tosrtkey_ongtk
import gui.widgets.progressdialog as progressdlg import gui.widgets.progressdialog as progressdlg
from lru import LRU from lru import LRU
from bisect import bisect_right from bisect import bisect_right
@ -87,10 +87,17 @@ class Node(object):
'prev', 'next', 'children')#, '__weakref__') 'prev', 'next', 'children')#, '__weakref__')
def __init__(self, ref, parent, sortkey, handle, secondary): def __init__(self, ref, parent, sortkey, handle, secondary):
self.name = sortkey
if sortkey: if sortkey:
self.sortkey = map(conv_unicode_tosrtkey_ongtk, sortkey) if isinstance(sortkey, unicode):
self.name = sortkey.encode('utf-8')
#sortkey must be localized sort, so
self.sortkey = conv_unicode_tosrtkey_ongtk(sortkey)
else:
self.name = sortkey
#sortkey must be localized sort, so
self.sortkey = conv_str_tosrtkey_ongtk(sortkey)
else: else:
self.name = ''
self.sortkey = None self.sortkey = None
self.ref = ref self.ref = ref
self.handle = handle self.handle = handle
@ -284,10 +291,15 @@ class TreeBaseModel(GObject.Object, Gtk.TreeModel):
nrgroups = 1, nrgroups = 1,
group_can_have_handle = False, group_can_have_handle = False,
has_secondary=False): has_secondary=False):
#TODO GTK3, first get flatbasemodel working !
raise NotImplementedError
cput = time.clock() cput = time.clock()
GObject.GObject.__init__(self) GObject.GObject.__init__(self)
#We create a stamp to recognize invalid iterators. From the docs:
#Set the stamp to be equal to your model's stamp, to mark the
#iterator as valid. When your model's structure changes, you should
#increment your model's stamp to mark all older iterators as invalid.
#They will be recognised as invalid because they will then have an
#incorrect stamp.
self.stamp = 0
#two unused attributes pesent to correspond to flatbasemodel #two unused attributes pesent to correspond to flatbasemodel
self.prev_handle = None self.prev_handle = None
self.prev_data = None self.prev_data = None
@ -306,7 +318,9 @@ class TreeBaseModel(GObject.Object, Gtk.TreeModel):
self.nodemap = NodeMap() self.nodemap = NodeMap()
self.handle2node = {} self.handle2node = {}
self.set_property("leak_references", False) #GTK3 We leak ref, yes??
#self.set_property("leak_references", False)
#normally sort on first column, so scol=0 #normally sort on first column, so scol=0
if sort_map: if sort_map:
#sort_map is the stored order of the columns and if they are #sort_map is the stored order of the columns and if they are
@ -349,6 +363,7 @@ class TreeBaseModel(GObject.Object, Gtk.TreeModel):
self.sort_func2 = None self.sort_func2 = None
if self.nodemap: if self.nodemap:
self.nodemap.destroy() self.nodemap.destroy()
self.nodemap = None self.nodemap = None
self.rebuild_data = None self.rebuild_data = None
self._build_data = None self._build_data = None
@ -422,10 +437,9 @@ class TreeBaseModel(GObject.Object, Gtk.TreeModel):
""" """
Clear the data map. Clear the data map.
""" """
#invalidate the iters within gtk
self.invalidate_iters()
self.tree.clear() self.tree.clear()
self.handle2node.clear() self.handle2node.clear()
self.stamp += 1
self.nodemap.clear() self.nodemap.clear()
#start with creating the new iters #start with creating the new iters
topnode = Node(None, None, None, None, False) topnode = Node(None, None, None, None, False)
@ -450,7 +464,7 @@ class TreeBaseModel(GObject.Object, Gtk.TreeModel):
if search[1]: if search[1]:
# we have search[1] = (index, text_unicode, inversion) # we have search[1] = (index, text_unicode, inversion)
col, text, inv = search[1] col, text, inv = search[1]
func = lambda x: self._get_value(x, col) or u"" func = lambda x: self._get_value(x, col) or ""
if search[2]: if search[2]:
self.search = ExactSearchFilter(func, text, inv) self.search = ExactSearchFilter(func, text, inv)
else: else:
@ -783,14 +797,39 @@ class TreeBaseModel(GObject.Object, Gtk.TreeModel):
# 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) #self.row_changed(path, node)
def new_iter(self, nodeid):
"""
Return a new iter containing the nodeid in the nodemap
"""
iter = Gtk.TreeIter()
iter.stamp = self.stamp
#user data should be an object, so we store the long as str
iter.user_data = str(nodeid)
return iter
def get_iter(self, node):
"""
Return an iter from the node.
iters are always created afresh
Will raise IndexError if the maps are not filled yet, or if it is empty.
Caller should take care of this if it allows calling with invalid path
:param path: node as it appears in the treeview
:type path: Node
"""
iter = self.new_iter(id(node))
return iter
def get_handle(self, node): def get_handle(self, node):
""" """
Get the gramps handle for a node. Return None if the node does Get the gramps handle for a node. Return None if the node does
not correspond to a gramps object. not correspond to a gramps object.
""" """
return node.handle return node.handle
def get_node(self, handle): def get_node(self, handle):
""" """
Get the node for a handle. Get the node for a handle.
@ -804,50 +843,57 @@ class TreeBaseModel(GObject.Object, Gtk.TreeModel):
""" """
return self.on_get_path(self.get_node(handle)) return self.on_get_path(self.get_node(handle))
# The following implement the public interface of Gtk.GenericTreeModel # The following implement the public interface of Gtk.TreeModel
def on_get_flags(self): def do_get_flags(self):
""" """
See Gtk.GenericTreeModel See Gtk.TreeModel
""" """
return Gtk.TreeModelFlags.ITERS_PERSIST return 0 #Gtk.TreeModelFlags.ITERS_PERSIST
def on_get_n_columns(self): def do_get_n_columns(self):
""" """
Return the number of columns. Must be implemented in the child objects Return the number of columns. Must be implemented in the child objects
See Gtk.GenericTreeModel See Gtk.TreeModel
""" """
raise NotImplementedError raise NotImplementedError
def on_get_column_type(self, index): def do_get_column_type(self, index):
""" """
See Gtk.GenericTreeModel See Gtk.TreeModel
""" """
if index == self._tooltip_column: if index == self._tooltip_column:
return object return object
return str return str
def on_get_value(self, nodeid, col): def do_get_value(self, iter, col):
""" """
See Gtk.GenericTreeModel See Gtk.TreeModel
""" """
#print 'get_value', nodeid, col nodeid = long(iter.user_data)
nodeid = id(nodeid)
node = self.nodemap.node(nodeid) node = self.nodemap.node(nodeid)
if node.handle is None: if node.handle is None:
# Header rows dont get the foreground color set # Header rows dont get the foreground color set
if col == self.color_column(): if col == self.color_column():
return None return "#000000000000"
# Return the node name for the first column # Return the node name for the first column
if col == 0: if col == 0:
return self.column_header(node) return self.column_header(node)
else:
#no value to show in other header column
return ''
else: else:
# return values for 'data' row, calling a function # return values for 'data' row, calling a function
# according to column_defs table # according to column_defs table
return self._get_value(node.handle, col, node.secondary) val = self._get_value(node.handle, col, node.secondary)
#GTK 3 should convert unicode objects automatically, but this
# gives wrong column values, so we convert
if isinstance(val, unicode):
return val.encode('utf-8')
else:
return val
def _get_value(self, handle, col, secondary=False): def _get_value(self, handle, col, secondary=False):
""" """
Returns the contents of a given column of a gramps object Returns the contents of a given column of a gramps object
@ -864,29 +910,38 @@ class TreeBaseModel(GObject.Object, Gtk.TreeModel):
try: try:
if not secondary: if not secondary:
return (self.fmap[col](data)) return self.fmap[col](data)
else: else:
return (self.fmap2[col](data)) return self.fmap2[col](data)
except: except:
return None return ''
def on_get_iter(self, path): def do_get_iter(self, path):
""" """
Returns a node from a given path. Returns a node from a given path.
""" """
if not self.tree or not self.tree[None].children: if not self.tree or not self.tree[None].children:
return None return False, Gtk.TreeIter()
node = self.tree[None] node = self.tree[None]
pathlist = list(path) pathlist = path.get_indices()
for index in pathlist: for index in pathlist:
_index = (-index - 1) if self.__reverse else index _index = (-index - 1) if self.__reverse else index
node = self.nodemap.node(node.children[_index][1]) node = self.nodemap.node(node.children[_index][1])
return node return True, self.get_iter(node)
def on_get_path(self, node): def get_node_from_iter(self, iter):
if iter and iter.user_data:
return self.nodemap.node(long(iter.user_data))
else:
print ('Problem', iter, iter.user_data)
raise NotImplementedError
return None
def do_get_path(self, iter):
""" """
Returns a path from a given node. Returns a path from a given node.
""" """
node = self.get_node_from_iter(iter)
pathlist = [] pathlist = []
while node.parent is not None: while node.parent is not None:
parent = self.nodemap.node(node.parent) parent = self.nodemap.node(node.parent)
@ -901,62 +956,79 @@ class TreeBaseModel(GObject.Object, Gtk.TreeModel):
if pathlist is not None: if pathlist is not None:
pathlist.reverse() pathlist.reverse()
return tuple(pathlist) return Gtk.TreePath(tuple(pathlist))
else: else:
return None return None
def on_iter_next(self, node): def do_iter_next(self, iter):
""" """
Sets iter to the next node at this level of the tree
See Gtk.TreeModel
Get the next node with the same parent as the given node. Get the next node with the same parent as the given node.
""" """
node = self.get_node_from_iter(iter)
val = node.prev if self.__reverse else node.next val = node.prev if self.__reverse else node.next
return val and self.nodemap.node(val) if val:
#user_data contains the nodeid
iter.user_data = str(val)
return True
else:
return False
def on_iter_children(self, node): def do_iter_children(self, iterparent):
""" """
Get the first child of the given node. Get the first child of the given node.
""" """
if node is None: if iterparent is None:
node = self.tree[None] nodeid = id(self.tree[None])
if node.children:
return self.nodemap.node(
node.children[-1 if self.__reverse else 0][1])
else: else:
return None nodeparent = self.get_node_from_iter(iterparent)
if nodeparent.children:
nodeid = nodeparent.children[-1 if self.__reverse else 0][1]
else:
return False, None
return True, self.new_iter(nodeid)
def on_iter_has_child(self, node): def do_iter_has_child(self, iter):
""" """
Find if the given node has any children. Find if the given node has any children.
""" """
if node is None: node = self.get_node_from_iter(iter)
node = self.tree[None]
return True if node.children else False return True if node.children else False
def on_iter_n_children(self, node): def do_iter_n_children(self, iter):
""" """
Get the number of children of the given node. Get the number of children of the given node.
""" """
if node is None: if iter is None:
node = self.tree[None] node = self.tree[None]
else:
node = self.get_node_from_iter(iter)
return len(node.children) return len(node.children)
def on_iter_nth_child(self, node, index): def do_iter_nth_child(self, iterparent, index):
""" """
Get the nth child of the given node. Get the nth child of the given node.
""" """
if node is None: if iterparent is None:
node = self.tree[None] node = self.tree[None]
else:
node = self.get_node_from_iter(iterparent)
if node.children: if node.children:
if len(node.children) > index: if len(node.children) > index:
_index = (-index - 1) if self.__reverse else index _index = (-index - 1) if self.__reverse else index
return self.nodemap.node(node.children[_index][1]) return True, self.new_iter(node.children[_index][1])
else: else:
return None return False, None
else: else:
return None return False, None
def on_iter_parent(self, node): def do_iter_parent(self, iterchild):
""" """
Get the parent of the given node. Get the parent of the given node.
""" """
return node.parent and self.nodemap.node(node.parent) node = self.get_node_from_iter(iterchild)
if node.parent:
return True, self.new_iter(node.parent)
else:
return False, None

View File

@ -53,7 +53,7 @@ from gen.ggettext import gettext as _
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
class PersonTreeView(BasePersonView): class PersonTreeView(BasePersonView):
""" """
A hierarchical view of the top three levels of places. A hierarchical view of the people based on family name.
""" """
def __init__(self, pdata, dbstate, uistate, nav_group=0): def __init__(self, pdata, dbstate, uistate, nav_group=0):
BasePersonView.__init__(self, pdata, dbstate, uistate, BasePersonView.__init__(self, pdata, dbstate, uistate,