optimization and cleaning of the base model for flat treeviews
svn: r12723
This commit is contained in:
parent
db5458a507
commit
fb6967d000
@ -127,6 +127,7 @@ src/gen/plug/docgen/Makefile
|
||||
src/gen/plug/menu/Makefile
|
||||
src/gui/Makefile
|
||||
src/gui/views/Makefile
|
||||
src/gui/views/treemodels/Makefile
|
||||
src/Config/Makefile
|
||||
src/FilterEditor/Makefile
|
||||
src/Mime/Makefile
|
||||
|
@ -190,6 +190,10 @@ src/gui/viewmanager.py
|
||||
# gui/views - the GUI views package
|
||||
src/gui/views/__init__.py
|
||||
|
||||
# gui/views/treemodels - the GUI views package
|
||||
src/gui/views/treemodels/__init__.py
|
||||
src/gui/views/treemodels/flatbasemodel.py
|
||||
|
||||
# Simple API
|
||||
src/Simple/_SimpleTable.py
|
||||
|
||||
@ -250,7 +254,6 @@ src/docgen/SpreadSheetDoc.py
|
||||
src/docgen/TextBufDoc.py
|
||||
|
||||
# DisplayModels package
|
||||
src/DisplayModels/_BaseModel.py
|
||||
src/DisplayModels/_EventModel.py
|
||||
src/DisplayModels/_FamilyModel.py
|
||||
src/DisplayModels/_MediaModel.py
|
||||
|
@ -4,7 +4,6 @@ pkgdatadir = $(datadir)/@PACKAGE@/DisplayModels
|
||||
|
||||
pkgdata_PYTHON = \
|
||||
__init__.py \
|
||||
_BaseModel.py \
|
||||
_EventModel.py \
|
||||
_FamilyModel.py \
|
||||
_MediaModel.py \
|
||||
|
@ -1,357 +0,0 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2000-2006 Donald N. Allingham
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# $Id$
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# python modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
import locale
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# GNOME/GTK modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
import gtk
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# GRAMPS modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from Filters import SearchFilter
|
||||
import Config
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# NodeMap
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class NodeMap(object):
|
||||
"""
|
||||
Provide the Path to Iter mappings for a TreeView model. The implementation
|
||||
provides a list of nodes and a dictionary of handles. The datalist provides
|
||||
the path (index) to iter (handle) mapping, while the the indexmap provides
|
||||
the handle to index mappings
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Create a new instance, clearing the datalist and indexmap
|
||||
"""
|
||||
self.data_list = []
|
||||
self.index_map = {}
|
||||
|
||||
def set_path_map(self, dlist):
|
||||
"""
|
||||
Takes a list of handles and builds the index map from it.
|
||||
"""
|
||||
self.data_list = dlist
|
||||
i = 0
|
||||
self.index_map = {}
|
||||
for key in self.data_list:
|
||||
self.index_map[key] = i
|
||||
i +=1
|
||||
|
||||
def clear_map(self):
|
||||
"""
|
||||
Clears out the data_list and the index_map
|
||||
"""
|
||||
self.data_list = []
|
||||
self.index_map = {}
|
||||
|
||||
def get_path(self, handle):
|
||||
"""
|
||||
Return the path from the passed handle. This is accomplished by
|
||||
indexing into the index_map to get the index (path)
|
||||
"""
|
||||
return self.index_map.get(handle)
|
||||
|
||||
def get_handle(self, path):
|
||||
"""
|
||||
Return the handle from the path. The path is assumed to be an integer.
|
||||
This is accomplished by indexing into the data_list
|
||||
"""
|
||||
return self.data_list[path]
|
||||
|
||||
def delete_by_index(self, index):
|
||||
"""
|
||||
Deletes the item at the specified path, then rebuilds the index_map,
|
||||
subtracting one from each item greater than the deleted index.
|
||||
"""
|
||||
handle = self.data_list[index]
|
||||
del self.data_list[index]
|
||||
del self.index_map[handle]
|
||||
|
||||
for key in self.index_map:
|
||||
if self.index_map[key] > index:
|
||||
self.index_map[key] -= 1
|
||||
|
||||
def find_next_handle(self, handle):
|
||||
"""
|
||||
Finds the next handle based off the passed handle. This is accomplished
|
||||
by finding the index of associated with the handle, adding one to find
|
||||
the next index, then finding the handle associated with the next index.
|
||||
"""
|
||||
try:
|
||||
return self.data_list[self.index_map.get(handle)+1]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
def __len__(self):
|
||||
"""
|
||||
Return the number of entries in the map.
|
||||
"""
|
||||
return len(self.data_list)
|
||||
|
||||
def get_first_handle(self):
|
||||
"""
|
||||
Return the first handle in the map.
|
||||
"""
|
||||
return self.data_list[0]
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# BaseModel
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class BaseModel(gtk.GenericTreeModel):
|
||||
|
||||
def __init__(self, db, scol=0, order=gtk.SORT_ASCENDING,
|
||||
tooltip_column=None, search=None, skip=set(),
|
||||
sort_map=None):
|
||||
gtk.GenericTreeModel.__init__(self)
|
||||
self.prev_handle = None
|
||||
self.prev_data = None
|
||||
self.set_property("leak_references",False)
|
||||
self.db = db
|
||||
if sort_map:
|
||||
self.sort_map = [ f for f in sort_map if f[0]]
|
||||
col = self.sort_map[scol][1]
|
||||
self.sort_func = self.smap[col]
|
||||
else:
|
||||
self.sort_func = self.smap[scol]
|
||||
self.sort_col = scol
|
||||
self.skip = skip
|
||||
|
||||
self.total = 0
|
||||
self.displayed = 0
|
||||
|
||||
self.node_map = NodeMap()
|
||||
|
||||
if search:
|
||||
if search[0]:
|
||||
self.search = search[1]
|
||||
self.rebuild_data = self._rebuild_filter
|
||||
else:
|
||||
if search[1]:
|
||||
# we have search[1] = (index, text_unicode, inversion)
|
||||
col = search[1][0]
|
||||
text = search[1][1]
|
||||
inv = search[1][2]
|
||||
func = lambda x: self.on_get_value(x, col) or u""
|
||||
self.search = SearchFilter(func, text, inv)
|
||||
else:
|
||||
self.search = None
|
||||
self.rebuild_data = self._rebuild_search
|
||||
else:
|
||||
self.search = None
|
||||
self.rebuild_data = self._rebuild_search
|
||||
|
||||
self.reverse = (order == gtk.SORT_DESCENDING)
|
||||
self.tooltip_column = tooltip_column
|
||||
|
||||
Config.client.notify_add("/apps/gramps/preferences/todo-color",
|
||||
self.update_todo)
|
||||
Config.client.notify_add("/apps/gramps/preferences/custom-marker-color",
|
||||
self.update_custom)
|
||||
Config.client.notify_add("/apps/gramps/preferences/complete-color",
|
||||
self.update_complete)
|
||||
|
||||
self.complete_color = Config.get(Config.COMPLETE_COLOR)
|
||||
self.todo_color = Config.get(Config.TODO_COLOR)
|
||||
self.custom_color = Config.get(Config.CUSTOM_MARKER_COLOR)
|
||||
self.rebuild_data()
|
||||
|
||||
def update_todo(self,client,cnxn_id,entry,data):
|
||||
self.todo_color = Config.get(Config.TODO_COLOR)
|
||||
|
||||
def update_custom(self,client,cnxn_id,entry,data):
|
||||
self.custom_color = Config.get(Config.CUSTOM_MARKER_COLOR)
|
||||
|
||||
def update_complete(self,client,cnxn_id,entry,data):
|
||||
self.complete_color = Config.get(Config.COMPLETE_COLOR)
|
||||
|
||||
def set_sort_column(self,col):
|
||||
self.sort_func = self.smap[col]
|
||||
|
||||
def sort_keys(self):
|
||||
cursor = self.gen_cursor()
|
||||
self.sort_data = []
|
||||
data = cursor.next()
|
||||
|
||||
self.total = 0
|
||||
while data:
|
||||
self.sort_data.append((self.sort_func(data[1]),data[0]))
|
||||
self.total += 1
|
||||
data = cursor.next()
|
||||
cursor.close()
|
||||
|
||||
self.sort_data.sort(lambda x, y: locale.strcoll(x[0], y[0]),
|
||||
reverse=self.reverse)
|
||||
|
||||
return [ x[1] for x in self.sort_data ]
|
||||
|
||||
def _rebuild_search(self,ignore=None):
|
||||
""" function called when view must be build, given a search text
|
||||
in the top search bar
|
||||
"""
|
||||
self.total = 0
|
||||
if self.db.is_open():
|
||||
if self.search and self.search.text:
|
||||
dlist = [h for h in self.sort_keys()\
|
||||
if self.search.match(h,self.db) and \
|
||||
h not in self.skip and h != ignore]
|
||||
else:
|
||||
dlist = [h for h in self.sort_keys() \
|
||||
if h not in self.skip and h != ignore]
|
||||
self.displayed = len(dlist)
|
||||
self.node_map.set_path_map(dlist)
|
||||
else:
|
||||
self.displayed = 0
|
||||
self.node_map.clear_map()
|
||||
|
||||
def _rebuild_filter(self, ignore=None):
|
||||
""" function called when view must be build, given filter options
|
||||
in the filter sidebar
|
||||
"""
|
||||
self.total = 0
|
||||
if self.db.is_open():
|
||||
if self.search:
|
||||
dlist = self.search.apply(self.db,
|
||||
[ k for k in self.sort_keys()\
|
||||
if k != ignore])
|
||||
else:
|
||||
dlist = [ k for k in self.sort_keys() \
|
||||
if k != ignore ]
|
||||
|
||||
self.displayed = len(dlist)
|
||||
self.node_map.set_path_map(dlist)
|
||||
else:
|
||||
self.displayed = 0
|
||||
self.node_map.clear_map()
|
||||
|
||||
def add_row_by_handle(self, handle):
|
||||
if not self.search or \
|
||||
(self.search and self.search.match(handle, self.db)):
|
||||
|
||||
data = self.map(handle)
|
||||
self.sort_data.append((self.sort_func(data), handle))
|
||||
self.sort_data.sort(lambda x, y: locale.strcoll(x[0], y[0]),
|
||||
reverse=self.reverse)
|
||||
self.node_map.set_path_map([ x[1] for x in self.sort_data ])
|
||||
|
||||
index = self.node_map.get_path(handle)
|
||||
if index is not None:
|
||||
node = self.get_iter(index)
|
||||
self.row_inserted(index, node)
|
||||
|
||||
def delete_row_by_handle(self, handle):
|
||||
index = self.node_map.get_path(handle)
|
||||
|
||||
# remove from sort array
|
||||
i = 0
|
||||
for (key, node) in self.sort_data:
|
||||
if handle == node:
|
||||
del self.sort_data[i]
|
||||
break
|
||||
i += 1
|
||||
|
||||
self.node_map.delete_by_index(index)
|
||||
self.row_deleted(index)
|
||||
|
||||
def update_row_by_handle(self, handle):
|
||||
index = self.node_map.get_path(handle)
|
||||
node = self.get_iter(index)
|
||||
self.row_changed(index, node)
|
||||
|
||||
def on_get_flags(self):
|
||||
"""returns the GtkTreeModelFlags for this particular type of model"""
|
||||
return gtk.TREE_MODEL_LIST_ONLY | gtk.TREE_MODEL_ITERS_PERSIST
|
||||
|
||||
def on_get_n_columns(self):
|
||||
return 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.node_map.get_path(node)
|
||||
|
||||
def on_get_column_type(self,index):
|
||||
if index == self.tooltip_column:
|
||||
return object
|
||||
return str
|
||||
|
||||
def on_get_iter(self, path):
|
||||
try:
|
||||
return self.node_map.get_handle(path[0])
|
||||
except:
|
||||
return None
|
||||
|
||||
def on_get_value(self, node, col):
|
||||
try:
|
||||
if node != self.prev_handle:
|
||||
self.prev_data = self.map(str(node))
|
||||
self.prev_handle = node
|
||||
return self.fmap[col](self.prev_data)
|
||||
except:
|
||||
return u''
|
||||
|
||||
def on_iter_next(self, node):
|
||||
"""returns the next node at this level of the tree"""
|
||||
return self.node_map.find_next_handle(node)
|
||||
|
||||
def on_iter_children(self, node):
|
||||
"""Return the first child of the node"""
|
||||
if node is None and len(self.node_map):
|
||||
return self.node_map.get_first_handle()
|
||||
return None
|
||||
|
||||
def on_iter_has_child(self, node):
|
||||
"""returns true if this node has children"""
|
||||
if node is None:
|
||||
return len(self.node_map) > 0
|
||||
return False
|
||||
|
||||
def on_iter_n_children(self, node):
|
||||
if node is None:
|
||||
return len(self.node_map)
|
||||
return 0
|
||||
|
||||
def on_iter_nth_child(self, node, n):
|
||||
if node is None:
|
||||
return self.node_map.get_handle(n)
|
||||
return None
|
||||
|
||||
def on_iter_parent(self, node):
|
||||
"""returns the parent of this node"""
|
||||
return None
|
@ -44,7 +44,7 @@ import ToolTips
|
||||
import GrampsLocale
|
||||
import DateHandler
|
||||
import gen.lib
|
||||
from _BaseModel import BaseModel
|
||||
from gui.views.treemodels.flatbasemodel import FlatBaseModel
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@ -64,7 +64,7 @@ COLUMN_CHANGE = 10
|
||||
# EventModel
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class EventModel(BaseModel):
|
||||
class EventModel(FlatBaseModel):
|
||||
|
||||
def __init__(self, db, scol=0, order=gtk.SORT_ASCENDING, search=None,
|
||||
skip=set(), sort_map=None):
|
||||
@ -91,7 +91,7 @@ class EventModel(BaseModel):
|
||||
self.column_handle,
|
||||
self.column_tooltip,
|
||||
]
|
||||
BaseModel.__init__(self, db, scol, order, tooltip_column=8,
|
||||
FlatBaseModel.__init__(self, db, scol, order, tooltip_column=8,
|
||||
search=search, skip=skip, sort_map=sort_map)
|
||||
|
||||
def on_get_n_columns(self):
|
||||
|
@ -48,14 +48,14 @@ from BasicUtils import name_displayer
|
||||
import gen.lib
|
||||
import gen.utils
|
||||
|
||||
from _BaseModel import BaseModel
|
||||
from gui.views.treemodels.flatbasemodel import FlatBaseModel
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# FamilyModel
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class FamilyModel(BaseModel):
|
||||
class FamilyModel(FlatBaseModel):
|
||||
|
||||
_MARKER_COL = 13
|
||||
|
||||
@ -88,7 +88,7 @@ class FamilyModel(BaseModel):
|
||||
self.column_marker_color,
|
||||
]
|
||||
self.marker_color_column = 9
|
||||
BaseModel.__init__(self, db, scol, order, tooltip_column=6,
|
||||
FlatBaseModel.__init__(self, db, scol, order, tooltip_column=6,
|
||||
search=search, skip=skip, sort_map=sort_map)
|
||||
|
||||
def on_get_n_columns(self):
|
||||
|
@ -46,14 +46,14 @@ import ToolTips
|
||||
import GrampsLocale
|
||||
import DateHandler
|
||||
import gen.lib
|
||||
from _BaseModel import BaseModel
|
||||
from gui.views.treemodels.flatbasemodel import FlatBaseModel
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# MediaModel
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class MediaModel(BaseModel):
|
||||
class MediaModel(FlatBaseModel):
|
||||
|
||||
def __init__(self, db, scol=0, order=gtk.SORT_ASCENDING, search=None,
|
||||
skip=set(), sort_map=None):
|
||||
@ -80,7 +80,7 @@ class MediaModel(BaseModel):
|
||||
self.sort_date,
|
||||
self.column_handle,
|
||||
]
|
||||
BaseModel.__init__(self, db, scol, order, tooltip_column=7,
|
||||
FlatBaseModel.__init__(self, db, scol, order, tooltip_column=7,
|
||||
search=search, skip=skip, sort_map=sort_map)
|
||||
|
||||
def on_get_n_columns(self):
|
||||
|
@ -39,7 +39,7 @@ import gtk
|
||||
# GRAMPS modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from _BaseModel import BaseModel
|
||||
from gui.views.treemodels.flatbasemodel import FlatBaseModel
|
||||
from gen.lib import (Note, NoteType, MarkerType, StyledText)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@ -47,7 +47,7 @@ from gen.lib import (Note, NoteType, MarkerType, StyledText)
|
||||
# NoteModel
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class NoteModel(BaseModel):
|
||||
class NoteModel(FlatBaseModel):
|
||||
"""
|
||||
"""
|
||||
def __init__(self, db, scol=0, order=gtk.SORT_ASCENDING, search=None,
|
||||
@ -72,7 +72,7 @@ class NoteModel(BaseModel):
|
||||
self.column_marker_color
|
||||
]
|
||||
self.marker_color_column = 5
|
||||
BaseModel.__init__(self, db, scol, order, search=search,
|
||||
FlatBaseModel.__init__(self, db, scol, order, search=search,
|
||||
skip=skip, sort_map=sort_map)
|
||||
|
||||
def on_get_n_columns(self):
|
||||
|
@ -43,14 +43,14 @@ import gtk
|
||||
import const
|
||||
import ToolTips
|
||||
import GrampsLocale
|
||||
from _BaseModel import BaseModel
|
||||
from gui.views.treemodels.flatbasemodel import FlatBaseModel
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# PlaceModel
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class PlaceModel(BaseModel):
|
||||
class PlaceModel(FlatBaseModel):
|
||||
|
||||
HANDLE_COL = 12
|
||||
|
||||
@ -89,7 +89,7 @@ class PlaceModel(BaseModel):
|
||||
self.column_street,
|
||||
self.column_handle,
|
||||
]
|
||||
BaseModel.__init__(self, db, scol, order, tooltip_column=13,
|
||||
FlatBaseModel.__init__(self, db, scol, order, tooltip_column=13,
|
||||
search=search, skip=skip, sort_map=sort_map)
|
||||
|
||||
def on_get_n_columns(self):
|
||||
|
@ -42,14 +42,14 @@ import gtk
|
||||
#-------------------------------------------------------------------------
|
||||
import gen.lib
|
||||
import GrampsLocale
|
||||
from _BaseModel import BaseModel
|
||||
from gui.views.treemodels.flatbasemodel import FlatBaseModel
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# RepositoryModel
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class RepositoryModel(BaseModel):
|
||||
class RepositoryModel(FlatBaseModel):
|
||||
|
||||
def __init__(self, db, scol=0, order=gtk.SORT_ASCENDING, search=None,
|
||||
skip=set(), sort_map=None):
|
||||
@ -91,7 +91,7 @@ class RepositoryModel(BaseModel):
|
||||
self.column_handle,
|
||||
]
|
||||
|
||||
BaseModel.__init__(self, db, scol, order, tooltip_column=14,
|
||||
FlatBaseModel.__init__(self, db, scol, order, tooltip_column=14,
|
||||
search=search, skip=skip, sort_map=sort_map)
|
||||
|
||||
def on_get_n_columns(self):
|
||||
|
@ -43,14 +43,14 @@ import gtk
|
||||
import const
|
||||
import ToolTips
|
||||
import GrampsLocale
|
||||
from _BaseModel import BaseModel
|
||||
from gui.views.treemodels.flatbasemodel import FlatBaseModel
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# SourceModel
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class SourceModel(BaseModel):
|
||||
class SourceModel(FlatBaseModel):
|
||||
|
||||
def __init__(self,db,scol=0, order=gtk.SORT_ASCENDING,search=None,
|
||||
skip=set(), sort_map=None):
|
||||
@ -74,7 +74,7 @@ class SourceModel(BaseModel):
|
||||
self.column_pubinfo,
|
||||
self.sort_change,
|
||||
]
|
||||
BaseModel.__init__(self,db,scol, order,tooltip_column=7,search=search,
|
||||
FlatBaseModel.__init__(self,db,scol, order,tooltip_column=7,search=search,
|
||||
skip=skip, sort_map=sort_map)
|
||||
|
||||
def on_get_n_columns(self):
|
||||
|
@ -30,6 +30,10 @@ Provide the base classes for GRAMPS' DataView classes
|
||||
#
|
||||
#----------------------------------------------------------------
|
||||
import cPickle as pickle
|
||||
import time
|
||||
import logging
|
||||
|
||||
_LOG = logging.getLogger('.pageview')
|
||||
|
||||
#----------------------------------------------------------------
|
||||
#
|
||||
@ -835,9 +839,12 @@ class ListView(BookMarkView):
|
||||
self.inactive = False
|
||||
|
||||
def column_clicked(self, obj, data):
|
||||
cput = time.clock()
|
||||
same_col = False
|
||||
if self.sort_col != data:
|
||||
order = gtk.SORT_ASCENDING
|
||||
else:
|
||||
same_col = True
|
||||
if (self.columns[data].get_sort_order() == gtk.SORT_DESCENDING
|
||||
or not self.columns[data].get_sort_indicator()):
|
||||
order = gtk.SORT_ASCENDING
|
||||
@ -852,16 +859,17 @@ class ListView(BookMarkView):
|
||||
else:
|
||||
search = (False, self.search_bar.get_value())
|
||||
|
||||
self.model = self.make_model(self.dbstate.db, self.sort_col, order,
|
||||
if same_col:
|
||||
self.model.reverse_order()
|
||||
else:
|
||||
self.model = self.make_model(self.dbstate.db, self.sort_col, order,
|
||||
search=search,
|
||||
sort_map=self.column_order())
|
||||
|
||||
self.list.set_model(self.model)
|
||||
|
||||
if handle:
|
||||
path = self.model.on_get_path(handle)
|
||||
self.selection.select_path(path)
|
||||
self.list.scroll_to_cell(path, None, 1, 0.5, 0)
|
||||
self.goto_handle(handle)
|
||||
for i in xrange(len(self.columns)):
|
||||
enable_sort_flag = (i==self.sort_col)
|
||||
self.columns[i].set_sort_indicator(enable_sort_flag)
|
||||
@ -870,6 +878,8 @@ class ListView(BookMarkView):
|
||||
# set the search column to be the sorted column
|
||||
search_col = self.column_order()[data][1]
|
||||
self.list.set_search_column(search_col)
|
||||
_LOG.debug(' ' + self.__class__.__name__ + ' column_clicked ' +
|
||||
str(time.clock() - cput) + ' sec')
|
||||
|
||||
def build_columns(self):
|
||||
for column in self.columns:
|
||||
@ -899,6 +909,7 @@ class ListView(BookMarkView):
|
||||
|
||||
def build_tree(self):
|
||||
if self.active:
|
||||
cput = time.clock()
|
||||
if Config.get(Config.FILTER):
|
||||
filter_info = (True, self.generic_filter)
|
||||
else:
|
||||
@ -916,6 +927,9 @@ class ListView(BookMarkView):
|
||||
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')
|
||||
|
||||
else:
|
||||
self.dirty = True
|
||||
|
||||
@ -959,8 +973,11 @@ class ListView(BookMarkView):
|
||||
|
||||
def row_add(self, handle_list):
|
||||
if self.active:
|
||||
cput = time.clock()
|
||||
for handle in handle_list:
|
||||
self.model.add_row_by_handle(handle)
|
||||
_LOG.debug(' ' + self.__class__.__name__ + ' row_add ' +
|
||||
str(time.clock() - cput) + ' sec')
|
||||
else:
|
||||
self.dirty = True
|
||||
|
||||
@ -968,15 +985,21 @@ class ListView(BookMarkView):
|
||||
if self.model:
|
||||
self.model.prev_handle = None
|
||||
if self.active:
|
||||
cput = time.clock()
|
||||
for handle in handle_list:
|
||||
self.model.update_row_by_handle(handle)
|
||||
_LOG.debug(' ' + self.__class__.__name__ + ' row_update ' +
|
||||
str(time.clock() - cput) + ' sec')
|
||||
else:
|
||||
self.dirty = True
|
||||
|
||||
def row_delete(self, handle_list):
|
||||
if self.active:
|
||||
cput = time.clock()
|
||||
for handle in handle_list:
|
||||
self.model.delete_row_by_handle(handle)
|
||||
_LOG.debug(' ' + self.__class__.__name__ + ' row_delete ' +
|
||||
str(time.clock() - cput) + ' sec')
|
||||
else:
|
||||
self.dirty = True
|
||||
|
||||
|
@ -3,6 +3,9 @@
|
||||
# but that is not necessarily portable.
|
||||
# If not using GNU make, then list all .py files individually
|
||||
|
||||
SUBDIRS = \
|
||||
treemodels
|
||||
|
||||
pkgdatadir = $(datadir)/@PACKAGE@/views
|
||||
|
||||
pkgdata_PYTHON = \
|
||||
|
@ -22,3 +22,7 @@
|
||||
"""
|
||||
Package init for the views package.
|
||||
"""
|
||||
|
||||
# DO NOT IMPORT METHODS/CLASSES FROM src/gui/views HERE ! Only __all__
|
||||
|
||||
__all__ = [ "treemodels" ]
|
||||
|
24
src/gui/views/treemodels/__init__.py
Normal file
24
src/gui/views/treemodels/__init__.py
Normal file
@ -0,0 +1,24 @@
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# 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
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
|
||||
# $Id: __init__.py 11943 2009-02-09 23:37:40Z acraphae $
|
||||
|
||||
"""
|
||||
Package init for the treemodels package.
|
||||
"""
|
552
src/gui/views/treemodels/flatbasemodel.py
Normal file
552
src/gui/views/treemodels/flatbasemodel.py
Normal file
@ -0,0 +1,552 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2000-2006 Donald N. Allingham
|
||||
# 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
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# $Id: _BaseModel.py 12559 2009-05-21 17:19:50Z gbritton $
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# python modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from __future__ import with_statement
|
||||
|
||||
"""
|
||||
This module provides the flat treemodel that is used for all flat treeviews.
|
||||
|
||||
For performance, GRAMPS does not use gtk.TreeStore, as that would mean keeping
|
||||
the entire database table of an object in memory.
|
||||
Instead, it suffices to keep in memory the sortkey and the matching handle,
|
||||
as well as a map of sortkey,handle to treeview path, and vice versa.
|
||||
|
||||
For a flat view, the index of sortkey,handle will be the path, so it suffices
|
||||
to keep in memory a map that given a sortkey,handle returns the path.
|
||||
As we need to be able to insert/delete/update objects, and for that the handle
|
||||
is all we know initially, and as sortkey,handle is uniquely determined by
|
||||
handle, instead of keeping a map of sortkey,handle to path, we keep a map of
|
||||
handle to path
|
||||
|
||||
As a user selects another column to sort, the sortkey must be rebuild, and the
|
||||
map remade.
|
||||
|
||||
The class FlatNodeMap keeps a sortkeyhandle list with (sortkey, handle) entries,
|
||||
and a handle2path dictionary. As the Map is flat, the index in sortkeyhandle
|
||||
corresponds to the path.
|
||||
|
||||
The class FlatBaseModel, is the base class for all flat treeview models.
|
||||
It keeps a FlatNodeMap, and obtains data from database as needed
|
||||
"""
|
||||
import locale
|
||||
import logging
|
||||
import bisect
|
||||
|
||||
_LOG = logging.getLogger(".gui.basetreemodel")
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# GNOME/GTK modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
import gtk
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# GRAMPS modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from Filters import SearchFilter
|
||||
import Config
|
||||
import time
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# FlatNodeMap
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class FlatNodeMap(object):
|
||||
"""
|
||||
A NodeMap for a flat treeview. In such a TreeView, the paths possible are
|
||||
0, 1, 2, ..., n-1, where n is the number of items to show. For the model
|
||||
it is needed to keep the Path to Iter mappings of the TreeView in memory
|
||||
|
||||
The order of what is shown is based on the unique key: (sortkey, handle)
|
||||
Naming:
|
||||
* srtkey : key on which to sort
|
||||
* hndl : handle of the object, makes it possible to retrieve the
|
||||
object from the database. As handle is unique, it is used
|
||||
as the iter for the TreeView
|
||||
* index : the index in the internal lists. When a view is in reverse,
|
||||
this is not kept physically, but instead via an offset
|
||||
* path : integer path in the TreeView. This will be index if view is
|
||||
ascending, but will begin at back of list if view shows
|
||||
the entries in reverse.
|
||||
* index2hndl : list of (srtkey, hndl) tuples. The index gives the
|
||||
(srtkey, hndl) it belongs to
|
||||
* hndl2index : dictionary of *hndl: index* values
|
||||
|
||||
The implementation provides a list of (srtkey, hndl) of which the index is
|
||||
the path, and a dictionary mapping hndl to index.
|
||||
To obtain index given a path, method real_index() is available
|
||||
|
||||
..Note: If a string sortkey is used, apply locale.strxfrm on it , so as
|
||||
to have localized sort
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Create a new instance.
|
||||
"""
|
||||
self._index2hndl = []
|
||||
self._hndl2index = {}
|
||||
self._reverse = False
|
||||
self.__corr = (0, 1)
|
||||
|
||||
def set_path_map(self, index2hndllist, reverse=False):
|
||||
"""
|
||||
This is the core method to set up the FlatNodeMap
|
||||
Input is a list of (srtkey, handle), of which the index is the path
|
||||
Calling this method sets the index2hndllist, and creates the hndl2index
|
||||
map.
|
||||
|
||||
:param index2hndllist: the ascending sorted (sortkey, handle) values
|
||||
as they will appear in the flat treeview.
|
||||
:type index2hndllist: a list of (sortkey, handle) tuples
|
||||
"""
|
||||
self._index2hndl = index2hndllist
|
||||
self._hndl2index = {}
|
||||
self._reverse = reverse
|
||||
self.reverse_order()
|
||||
|
||||
def reverse_order(self):
|
||||
"""
|
||||
This method keeps the index2hndl map, but sets it up the index in
|
||||
reverse order. If the hndl2index map does not exist yet, it is created
|
||||
in the acending order as given in index2hndl
|
||||
The result is always a hndl2index map wich is correct, so or ascending
|
||||
order, or reverse order.
|
||||
"""
|
||||
if self._hndl2index:
|
||||
#if hndl2index is build already, invert order, otherwise keep
|
||||
# requested order
|
||||
self._reverse = not self._reverse
|
||||
if self._reverse:
|
||||
self.__corr = (len(self._index2hndl) - 1, -1)
|
||||
else:
|
||||
self.__corr = (0, 1)
|
||||
if not self._hndl2index:
|
||||
for index, key in enumerate(self._index2hndl):
|
||||
#the handle is key[1]
|
||||
self._hndl2index[key[1]] = index
|
||||
|
||||
def real_path(self, index):
|
||||
"""
|
||||
Given the index in the maps, return the real path.
|
||||
If reverse = False, then index is path, otherwise however, the
|
||||
path must be calculated so that the last index is the first path
|
||||
"""
|
||||
return self.__corr[0] + self.__corr[1] * index
|
||||
|
||||
def real_index(self, path):
|
||||
"""
|
||||
Given the path in the view, return the real index.
|
||||
If reverse = False, then path is index, otherwise however, the
|
||||
index must be calculated so that the last index is the first path
|
||||
"""
|
||||
return self.__corr[0] + self.__corr[1] * path
|
||||
|
||||
def clear_map(self):
|
||||
"""
|
||||
Clears out the index2hndl and the hndl2index
|
||||
"""
|
||||
self._index2hndl = []
|
||||
self._hndl2index = {}
|
||||
|
||||
def get_path(self, handle):
|
||||
"""
|
||||
Return the path from the passed handle.
|
||||
|
||||
:param handle: the key of the object for which the path in the treeview
|
||||
is needed
|
||||
:param type: an object handle
|
||||
"""
|
||||
return self.real_path(self._hndl2index.get(handle))
|
||||
|
||||
def get_handle(self, path):
|
||||
"""
|
||||
Return the handle from the path. The path is assumed to be an integer.
|
||||
This is accomplished by indexing into the index2hndl
|
||||
|
||||
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: path as it appears in the treeview
|
||||
:type path: integer
|
||||
"""
|
||||
return self._index2hndl[self.real_index(path)][1]
|
||||
|
||||
def find_next_handle(self, handle):
|
||||
"""
|
||||
Finds the next handle based off the passed handle. This is accomplished
|
||||
by finding the index associated with the handle, adding or substracting
|
||||
one to find the next index, then finding the handle associated with
|
||||
that.
|
||||
|
||||
:param handle: the key of the object for which the next handle shown
|
||||
in the treeview is needed
|
||||
:param type: an object handle
|
||||
"""
|
||||
index = self._hndl2index.get(handle)
|
||||
if self._reverse :
|
||||
index -= 1
|
||||
if index < 0:
|
||||
# -1 does not raise IndexError, as -1 is last element. Catch.
|
||||
return None
|
||||
else:
|
||||
index += 1
|
||||
|
||||
try:
|
||||
return self._index2hndl[index][1]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
def get_first_handle(self):
|
||||
"""
|
||||
Return the first handle that must be shown (corresponding to path 0)
|
||||
|
||||
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
|
||||
"""
|
||||
return self._index2hndl[self.real_index(0)][1]
|
||||
|
||||
def __len__(self):
|
||||
"""
|
||||
Return the number of entries in the map.
|
||||
"""
|
||||
return len(self._index2hndl)
|
||||
|
||||
def insert(self, srtkey_hndl):
|
||||
"""
|
||||
Insert a node. Given is a tuple (sortkey, handle), and this is added
|
||||
in the correct place, while the hndl2index map is updated.
|
||||
Returns the path of the inserted row
|
||||
|
||||
:param srtkey_hndl: the (sortkey, handle) tuple that must be inserted
|
||||
|
||||
:Returns: path of the row inserted in the treeview
|
||||
:Returns type: integer
|
||||
"""
|
||||
insert_pos = bisect.bisect_left(self._index2hndl, srtkey_hndl)
|
||||
self._index2hndl.insert(insert_pos, srtkey_hndl)
|
||||
#make sure the index map is updated
|
||||
for hndl, index in self._hndl2index.iteritems():
|
||||
if index >= insert_pos:
|
||||
self._hndl2index[hndl] += 1
|
||||
self._hndl2index[srtkey_hndl[1]] = insert_pos
|
||||
#update self.__corr so it remains correct
|
||||
if self._reverse:
|
||||
self.__corr = (len(self._index2hndl) - 1, -1)
|
||||
return self.real_path(insert_pos)
|
||||
|
||||
def delete(self, handle):
|
||||
"""
|
||||
Delete the row with handle.
|
||||
This then rebuilds the hndl2index, subtracting one from each item
|
||||
greater than the deleted index.
|
||||
|
||||
:param handle: the handle that must be removed
|
||||
:type handle: an object handle
|
||||
|
||||
:Returns: path of the row deleted from the treeview
|
||||
:Returns type: integer
|
||||
"""
|
||||
index = self._hndl2index[handle]
|
||||
del self._index2hndl[index]
|
||||
del self._hndl2index[handle]
|
||||
#update self.__corr so it remains correct
|
||||
if self._reverse:
|
||||
self.__corr = (len(self._index2hndl) - 1, -1)
|
||||
#update the handle2path map so it remains correct
|
||||
for key, val in self._hndl2index.iteritems():
|
||||
if val > index:
|
||||
self._hndl2index[key] -= 1
|
||||
return self.real_path(index)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# FlatBaseModel
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class FlatBaseModel(gtk.GenericTreeModel):
|
||||
"""
|
||||
The base class for all flat treeview models.
|
||||
It keeps a FlatNodeMap, and obtains data from database as needed
|
||||
"""
|
||||
|
||||
def __init__(self, db, scol=0, order=gtk.SORT_ASCENDING,
|
||||
tooltip_column=None, search=None, skip=set(),
|
||||
sort_map=None):
|
||||
cput = time.clock()
|
||||
gtk.GenericTreeModel.__init__(self)
|
||||
self.prev_handle = None
|
||||
self.prev_data = None
|
||||
self.set_property("leak_references",False)
|
||||
self.db = db
|
||||
if sort_map:
|
||||
self.sort_map = [ f for f in sort_map if f[0]]
|
||||
col = self.sort_map[scol][1]
|
||||
self.sort_func = self.smap[col]
|
||||
else:
|
||||
self.sort_func = self.smap[scol]
|
||||
self.sort_col = scol
|
||||
self.skip = skip
|
||||
|
||||
self.total = 0
|
||||
self.displayed = 0
|
||||
|
||||
self.node_map = FlatNodeMap()
|
||||
|
||||
if search:
|
||||
if search[0]:
|
||||
self.search = search[1]
|
||||
self.rebuild_data = self._rebuild_filter
|
||||
else:
|
||||
if search[1]:
|
||||
# we have search[1] = (index, text_unicode, inversion)
|
||||
col = search[1][0]
|
||||
text = search[1][1]
|
||||
inv = search[1][2]
|
||||
func = lambda x: self.on_get_value(x, col) or u""
|
||||
self.search = SearchFilter(func, text, inv)
|
||||
else:
|
||||
self.search = None
|
||||
self.rebuild_data = self._rebuild_search
|
||||
else:
|
||||
self.search = None
|
||||
self.rebuild_data = self._rebuild_search
|
||||
|
||||
self._reverse = (order == gtk.SORT_DESCENDING)
|
||||
self.tooltip_column = tooltip_column
|
||||
|
||||
Config.client.notify_add("/apps/gramps/preferences/todo-color",
|
||||
self.update_todo)
|
||||
Config.client.notify_add("/apps/gramps/preferences/custom-marker-color",
|
||||
self.update_custom)
|
||||
Config.client.notify_add("/apps/gramps/preferences/complete-color",
|
||||
self.update_complete)
|
||||
|
||||
self.complete_color = Config.get(Config.COMPLETE_COLOR)
|
||||
self.todo_color = Config.get(Config.TODO_COLOR)
|
||||
self.custom_color = Config.get(Config.CUSTOM_MARKER_COLOR)
|
||||
self.rebuild_data()
|
||||
_LOG.debug(self.__class__.__name__ + ' __init__ ' +
|
||||
str(time.clock() - cput) + ' sec')
|
||||
|
||||
def update_todo(self,client,cnxn_id,entry,data):
|
||||
self.todo_color = Config.get(Config.TODO_COLOR)
|
||||
|
||||
def update_custom(self,client,cnxn_id,entry,data):
|
||||
self.custom_color = Config.get(Config.CUSTOM_MARKER_COLOR)
|
||||
|
||||
def update_complete(self,client,cnxn_id,entry,data):
|
||||
self.complete_color = Config.get(Config.COMPLETE_COLOR)
|
||||
|
||||
def set_sort_column(self, col):
|
||||
self.sort_func = self.smap[col]
|
||||
|
||||
def reverse_order(self):
|
||||
self._reverse = not self._reverse
|
||||
self.node_map.reverse_order()
|
||||
|
||||
def sort_keys(self):
|
||||
sort_data = []
|
||||
self.total = 0
|
||||
|
||||
with self.gen_cursor() as cursor: # use cursor as a context manager
|
||||
#loop over database and store the sort field, and the handle
|
||||
for key, data in cursor:
|
||||
## as per locale doc, use strxfrm for frequent compare.
|
||||
## apparently broken in Win --> they should fix base lib !!
|
||||
#add to sort_data in such a way that bisect module can be
|
||||
# used on the result later on.
|
||||
sort_data.append((locale.strxfrm(self.sort_func(data)),
|
||||
key))
|
||||
#bisect.insort_left(sort_data,
|
||||
# (locale.strxfrm(self.sort_func(data)), key))
|
||||
sort_data.sort()
|
||||
self.total = len(sort_data)
|
||||
return sort_data
|
||||
|
||||
def _rebuild_search(self, ignore=None):
|
||||
""" function called when view must be build, given a search text
|
||||
in the top search bar
|
||||
"""
|
||||
self.total = 0
|
||||
if self.db.is_open():
|
||||
if self.search and self.search.text:
|
||||
dlist = [h for h in self.sort_keys()\
|
||||
if self.search.match(h[1],self.db) and \
|
||||
h[1] not in self.skip and h[1] != ignore]
|
||||
else:
|
||||
dlist = [h for h in self.sort_keys() \
|
||||
if h[1] not in self.skip and h[1] != ignore]
|
||||
self.displayed = len(dlist)
|
||||
self.node_map.set_path_map(dlist, reverse=self._reverse)
|
||||
else:
|
||||
self.displayed = 0
|
||||
self.node_map.clear_map()
|
||||
|
||||
def _rebuild_filter(self, ignore=None):
|
||||
""" function called when view must be build, given filter options
|
||||
in the filter sidebar
|
||||
"""
|
||||
self.total = 0
|
||||
if self.db.is_open():
|
||||
if self.search:
|
||||
dlist = self.search.apply(self.db,
|
||||
[ k for k in self.sort_keys()\
|
||||
if k[1] != ignore])
|
||||
else:
|
||||
dlist = [ k for k in self.sort_keys() \
|
||||
if k[1] != ignore ]
|
||||
self.displayed = len(dlist)
|
||||
self.node_map.set_path_map(dlist, reverse=self._reverse)
|
||||
else:
|
||||
self.displayed = 0
|
||||
self.node_map.clear_map()
|
||||
|
||||
def add_row_by_handle(self, handle):
|
||||
if not self.search or \
|
||||
(self.search and self.search.match(handle, self.db)):
|
||||
#row needs to be added to the model
|
||||
data = self.map(handle)
|
||||
insert_val = (locale.strxfrm(self.sort_func(data)), handle)
|
||||
insert_path = self.node_map.insert(insert_val)
|
||||
|
||||
if insert_path is not None:
|
||||
node = self.get_iter(insert_path)
|
||||
self.row_inserted(insert_path, node)
|
||||
|
||||
def delete_row_by_handle(self, handle):
|
||||
delete_path = self.node_map.delete(handle)
|
||||
self.row_deleted(delete_path)
|
||||
|
||||
def update_row_by_handle(self, handle):
|
||||
## TODO: if sort key changes, this is not updated correctly ....
|
||||
path = self.node_map.get_path(handle)
|
||||
node = self.get_iter(path)
|
||||
self.row_changed(path, node)
|
||||
|
||||
def on_get_flags(self):
|
||||
"""
|
||||
Returns the GtkTreeModelFlags for this particular type of model
|
||||
See gtk.TreeModel
|
||||
"""
|
||||
return gtk.TREE_MODEL_LIST_ONLY | gtk.TREE_MODEL_ITERS_PERSIST
|
||||
|
||||
def on_get_n_columns(self):
|
||||
"""
|
||||
Return the number of columns. Must be implemented in the child objects
|
||||
See gtk.TreeModel
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def on_get_path(self, handle):
|
||||
"""
|
||||
Return the tree path (a tuple of indices at the various
|
||||
levels) for a particular iter. We use handles for unique key iters
|
||||
See gtk.TreeModel
|
||||
"""
|
||||
return self.node_map.get_path(handle)
|
||||
|
||||
def on_get_column_type(self, index):
|
||||
"""
|
||||
See gtk.TreeModel
|
||||
"""
|
||||
if index == self.tooltip_column:
|
||||
return object
|
||||
return str
|
||||
|
||||
def on_get_iter(self, path):
|
||||
"""
|
||||
See gtk.TreeModel
|
||||
"""
|
||||
try:
|
||||
return self.node_map.get_handle(path[0])
|
||||
except:
|
||||
return None
|
||||
|
||||
def on_get_value(self, handle, col):
|
||||
"""
|
||||
See gtk.TreeModel
|
||||
"""
|
||||
try:
|
||||
if handle != self.prev_handle:
|
||||
self.prev_data = self.map(str(handle))
|
||||
self.prev_handle = handle
|
||||
return self.fmap[col](self.prev_data)
|
||||
except:
|
||||
return u''
|
||||
|
||||
def on_iter_next(self, handle):
|
||||
"""
|
||||
Returns the next node at this level of the tree
|
||||
See gtk.TreeModel
|
||||
"""
|
||||
return self.node_map.find_next_handle(handle)
|
||||
|
||||
def on_iter_children(self, handle):
|
||||
"""
|
||||
Return the first child of the node
|
||||
See gtk.TreeModel
|
||||
"""
|
||||
if handle is None and len(self.node_map):
|
||||
return self.node_map.get_first_handle()
|
||||
return None
|
||||
|
||||
def on_iter_has_child(self, handle):
|
||||
"""
|
||||
Returns true if this node has children
|
||||
See gtk.TreeModel
|
||||
"""
|
||||
if handle is None:
|
||||
return len(self.node_map) > 0
|
||||
return False
|
||||
|
||||
def on_iter_n_children(self, handle):
|
||||
"""
|
||||
See gtk.TreeModel
|
||||
"""
|
||||
if handle is None:
|
||||
return len(self.node_map)
|
||||
return 0
|
||||
|
||||
def on_iter_nth_child(self, handle, nth):
|
||||
"""
|
||||
See gtk.TreeModel
|
||||
"""
|
||||
if handle is None:
|
||||
return self.node_map.get_handle(nth)
|
||||
return None
|
||||
|
||||
def on_iter_parent(self, handle):
|
||||
"""
|
||||
Returns the parent of this node
|
||||
See gtk.TreeModel
|
||||
"""
|
||||
return None
|
Loading…
Reference in New Issue
Block a user