Initial version

svn: r20746
This commit is contained in:
Nick Hall 2012-12-02 14:23:29 +00:00
parent e2c48ddb54
commit 0758e411fd
40 changed files with 2312 additions and 1624 deletions

View File

@ -55,19 +55,15 @@ db.
#
#-------------------------------------------------------------------------
import os
import sys
if sys.version_info[0] < 3:
import cPickle as pickle
else:
import pickle
import cPickle as pickle
#------------------------------------------------------------------------
#
# Gramps libs
#
#------------------------------------------------------------------------
from .exceptions import DbException
from .write import FAMILY_TBL, PLACES_TBL, SOURCES_TBL, MEDIA_TBL, \
from exceptions import DbException
from write import FAMILY_TBL, PLACES_TBL, LOCATION_TBL, SOURCES_TBL, MEDIA_TBL,\
EVENTS_TBL, PERSON_TBL, REPO_TBL, NOTE_TBL, TAG_TBL, META, CITATIONS_TBL
#------------------------------------------------------------------------
@ -91,7 +87,7 @@ def backup(database):
"""
try:
__do_export(database)
except (OSError, IOError) as msg:
except (OSError, IOError), msg:
raise DbException(str(msg))
def __mk_backup_name(database, base):
@ -159,7 +155,7 @@ def restore(database):
"""
try:
__do_restore(database)
except (OSError, IOError) as msg:
except (OSError, IOError), msg:
raise DbException(str(msg))
def __do_restore(database):
@ -215,5 +211,6 @@ def __build_tbl_map(database):
( MEDIA_TBL, database.media_map.db),
( EVENTS_TBL, database.event_map.db),
( TAG_TBL, database.tag_map.db),
( LOCATION_TBL, database.location_map.db),
( META, database.metadata.db),
]

View File

@ -41,8 +41,8 @@ from ..ggettext import gettext as _
#-------------------------------------------------------------------------
from ..lib.childreftype import ChildRefType
from ..lib.childref import ChildRef
from .txn import DbTxn
from .exceptions import DbTransactionCancel
from txn import DbTxn
from exceptions import DbTransactionCancel
class DbReadBase(object):
"""
@ -321,6 +321,27 @@ class DbReadBase(object):
"""
raise NotImplementedError
def get_location_cursor(self):
"""
Return a reference to a cursor over Location objects
"""
raise NotImplementedError
def get_location_from_handle(self, handle):
"""
Find a Location in the database from the passed handle.
If no such Location exists, None is returned.
"""
raise NotImplementedError
def get_location_handles(self):
"""
Return a list of database handles, one handle for each Location in
the database.
"""
raise NotImplementedError
def get_media_attribute_types(self):
"""
Return a list of all Attribute types associated with Media and MediaRef
@ -436,6 +457,12 @@ class DbReadBase(object):
"""
raise NotImplementedError
def get_number_of_locations(self):
"""
Return the number of locations currently in the database.
"""
raise NotImplementedError
def get_number_of_media_objects(self):
"""
Return the number of media objects currently in the database.
@ -591,6 +618,12 @@ class DbReadBase(object):
"""
raise NotImplementedError
def get_raw_location_data(self, handle):
"""
Return raw (serialized and pickled) Location object from handle
"""
raise NotImplementedError
def get_raw_note_data(self, handle):
"""
Return raw (serialized and pickled) Note object from handle
@ -867,6 +900,12 @@ class DbReadBase(object):
"""
raise NotImplementedError
def has_location_handle(self, handle):
"""
Return True if the handle exists in the current Location database.
"""
raise NotImplementedError
def has_name_group_key(self, name):
"""
Return if a key exists in the name_group table.
@ -945,6 +984,18 @@ class DbReadBase(object):
"""
raise NotImplementedError
def iter_location_handles(self):
"""
Return an iterator over handles for Locations in the database
"""
raise NotImplementedError
def iter_locations(self):
"""
Return an iterator over objects for Locations in the database
"""
raise NotImplementedError
def iter_media_object_handles(self):
"""
Return an iterator over handles for Media in the database
@ -1235,6 +1286,13 @@ class DbWriteBase(DbReadBase):
"""
raise NotImplementedError
def add_location(self, location, transaction):
"""
Add a Location to the database, assigning a handle if it has not already
been defined.
"""
raise NotImplementedError
def add_note(self, obj, transaction, set_gid=True):
"""
Add a Note to the database, assigning internal IDs if they have
@ -1343,6 +1401,13 @@ class DbWriteBase(DbReadBase):
"""
raise NotImplementedError
def commit_location(self, location, transaction, change_time=None):
"""
Commit the specified Location to the database, storing the changes as
part of the transaction.
"""
raise NotImplementedError
def commit_media_object(self, obj, transaction, change_time=None):
"""
Commit the specified MediaObject to the database, storing the changes
@ -1460,6 +1525,15 @@ class DbWriteBase(DbReadBase):
"""
raise NotImplementedError
def remove_location(self, handle, transaction):
"""
Remove the Location specified by the database handle from the
database, preserving the change in the passed transaction.
This method must be overridden in the derived class.
"""
raise NotImplementedError
def remove_note(self, handle, transaction):
"""
Remove the Note specified by the database handle from the

View File

@ -30,7 +30,6 @@ Declare constants used by database modules
# standard python modules
#
#-------------------------------------------------------------------------
import sys
#-------------------------------------------------------------------------
#
@ -45,8 +44,8 @@ __all__ = (
) +
('PERSON_KEY', 'FAMILY_KEY', 'SOURCE_KEY', 'CITATION_KEY',
'EVENT_KEY', 'MEDIA_KEY', 'PLACE_KEY', 'REPOSITORY_KEY',
'NOTE_KEY', 'REFERENCE_KEY', 'TAG_KEY'
'EVENT_KEY', 'MEDIA_KEY', 'PLACE_KEY', 'LOCATION_KEY',
'REPOSITORY_KEY', 'NOTE_KEY', 'REFERENCE_KEY', 'TAG_KEY'
) +
('TXNADD', 'TXNUPD', 'TXNDEL')
@ -61,14 +60,14 @@ DBLOGNAME = ".Db" # Name of logger
DBMODE_R = "r" # Read-only access
DBMODE_W = "w" # Full Read/Write access
DBPAGE = 16384 # Size of the pages used to hold items in the database
DBMODE = 0o666 # Unix mode for database creation
DBMODE = 0666 # Unix mode for database creation
DBCACHE = 0x4000000 # Size of the shared memory buffer pool
DBLOCKS = 100000 # Maximum number of locks supported
DBOBJECTS = 100000 # Maximum number of simultaneously locked objects
DBUNDO = 1000 # Maximum size of undo buffer
from ..config import config
if config.get('preferences.use-bsddb3') or sys.version_info[0] >= 3:
if config.get('preferences.use-bsddb3'):
from bsddb3.db import DB_CREATE, DB_AUTO_COMMIT, DB_DUP, DB_DUPSORT, DB_RDONLY
else:
from bsddb.db import DB_CREATE, DB_AUTO_COMMIT, DB_DUP, DB_DUPSORT, DB_RDONLY
@ -87,5 +86,6 @@ REFERENCE_KEY = 7
NOTE_KEY = 8
TAG_KEY = 9
CITATION_KEY = 10
LOCATION_KEY = 11
TXNADD, TXNUPD, TXNDEL = 0, 1, 2

View File

@ -25,27 +25,21 @@
"""
Read classes for the GRAMPS databases.
"""
from __future__ import with_statement
#-------------------------------------------------------------------------
#
# libraries
#
#-------------------------------------------------------------------------
from __future__ import print_function, with_statement
import sys
if sys.version_info[0] < 3:
import cPickle as pickle
else:
import pickle
import cPickle
import time
import random
import locale
import os
from sys import maxsize
from sys import maxint
from ..config import config
if config.get('preferences.use-bsddb3') or sys.version_info[0] >= 3:
if config.get('preferences.use-bsddb3'):
from bsddb3 import db
else:
from bsddb import db
@ -66,6 +60,7 @@ from ..lib.src import Source
from ..lib.citation import Citation
from ..lib.event import Event
from ..lib.place import Place
from ..lib.location import Location
from ..lib.repo import Repository
from ..lib.note import Note
from ..lib.tag import Tag
@ -73,13 +68,12 @@ from ..lib.genderstats import GenderStats
from ..lib.researcher import Researcher
from ..lib.nameorigintype import NameOriginType
from .dbconst import *
from dbconst import *
from ..utils.callback import Callback
from ..utils.cast import conv_dbstr_to_unicode
from . import (BsddbBaseCursor, DbReadBase)
from ..utils.id import create_id
from ..errors import DbError
from ..constfunc import UNITYPE, STRTYPE, cuni
LOG = logging.getLogger(DBLOGNAME)
LOG = logging.getLogger(".citation")
@ -88,10 +82,10 @@ LOG = logging.getLogger(".citation")
# constants
#
#-------------------------------------------------------------------------
from .dbconst import *
from dbconst import *
_SIGBASE = ('person', 'family', 'source', 'citation',
'event', 'media', 'place', 'repository',
'event', 'media', 'place', 'location', 'repository',
'reference', 'note', 'tag')
DBERRS = (db.DBRunRecoveryError, db.DBAccessError,
@ -105,14 +99,12 @@ DBERRS = (db.DBRunRecoveryError, db.DBAccessError,
def find_surname(key, data):
"""
Creating a surname from raw data of a person, to use for sort and index
returns a byte string
"""
return __index_surname(data[3][5])
def find_surname_name(key, data):
"""
Creating a surname from raw name, to use for sort and index
returns a byte string
"""
return __index_surname(data[5])
@ -120,13 +112,12 @@ def __index_surname(surn_list):
"""
All non pa/matronymic surnames are used in indexing.
pa/matronymic not as they change for every generation!
returns a byte string
"""
if surn_list:
surn = " ".join([x[0] for x in surn_list if not (x[3][0] in [
surn = u" ".join([x[0] for x in surn_list if not (x[3][0] in [
NameOriginType.PATRONYMIC, NameOriginType.MATRONYMIC]) ])
else:
surn = ""
surn = u""
return surn.encode('utf-8')
@ -175,6 +166,32 @@ class DbReadCursor(BsddbBaseCursor):
self.cursor = source.db.cursor(txn)
self.source = source
#-------------------------------------------------------------------------
#
# DbBsddbTreeCursor
#
#-------------------------------------------------------------------------
class DbBsddbTreeCursor(BsddbBaseCursor):
def __init__(self, source, txn=None, **kwargs):
BsddbBaseCursor.__init__(self, txn=txn, **kwargs)
self.cursor = source.cursor(txn)
self.source = source
def __iter__(self):
"""
Iterator
"""
to_do = [None]
while to_do:
data = self.set(str(to_do.pop()))
_n = self.next_dup
while data:
payload = cPickle.loads(data[1])
yield (payload[0], payload)
to_do.append(payload[0])
data = _n()
class DbBsddbRead(DbReadBase, Callback):
"""
Read class for the GRAMPS databases. Implements methods necessary to read
@ -324,6 +341,13 @@ class DbBsddbRead(DbReadBase, Callback):
"cursor_func": self.get_tag_cursor,
"handles_func": self.get_tag_handles,
},
'Location':
{
"handle_func": self.get_location_from_handle,
"gramps_id_func": None,
"class_func": Location,
"cursor_func": self.get_location_cursor,
},
}
self.set_person_id_prefix('I%04d')
@ -378,6 +402,7 @@ class DbBsddbRead(DbReadBase, Callback):
self.nid_trans = {}
self.eid_trans = {}
self.tag_trans = {}
self.loc_trans = {}
self.env = None
self.person_map = {}
self.family_map = {}
@ -436,7 +461,7 @@ class DbBsddbRead(DbReadBase, Callback):
def get_table_names(self):
"""Return a list of valid table names."""
return list(self._tables.keys())
return self._tables.keys()
def get_table_metadata(self, table_name):
"""Return the metadata for a valid table name."""
@ -447,7 +472,7 @@ class DbBsddbRead(DbReadBase, Callback):
def get_cursor(self, table, *args, **kwargs):
try:
return DbReadCursor(table, self.txn)
except DBERRS as msg:
except DBERRS, msg:
self.__log_error()
raise DbError(msg)
@ -481,6 +506,9 @@ class DbBsddbRead(DbReadBase, Callback):
def get_tag_cursor(self, *args, **kwargs):
return self.get_cursor(self.tag_map, *args, **kwargs)
def get_location_cursor(self, *args, **kwargs):
return DbBsddbTreeCursor(self.parents, self.txn)
def close(self):
"""
Close the specified database.
@ -529,18 +557,16 @@ class DbBsddbRead(DbReadBase, Callback):
self.emit('repository-rebuild')
self.emit('note-rebuild')
self.emit('tag-rebuild')
self.emit('location-rebuild')
def __find_next_gramps_id(self, prefix, map_index, trans):
"""
Helper function for find_next_<object>_gramps_id methods
"""
index = prefix % map_index
#in bytes
bindex = index.encode('utf-8')
while trans.get(bindex, txn=self.txn) is not None:
while trans.get(str(index), txn=self.txn) is not None:
map_index += 1
index = prefix % map_index
bindex = index.encode('utf-8')
map_index += 1
return (map_index, index)
@ -626,9 +652,7 @@ class DbBsddbRead(DbReadBase, Callback):
return gid
def get_from_handle(self, handle, class_type, data_map):
if isinstance(handle, UNITYPE):
handle = handle.encode('utf-8')
data = data_map.get(handle)
data = data_map.get(str(handle))
if data:
newobj = class_type()
newobj.unserialize(data)
@ -744,11 +768,17 @@ class DbBsddbRead(DbReadBase, Callback):
"""
return self.get_from_handle(handle, Tag, self.tag_map)
def get_location_from_handle(self, handle):
"""
Find a Location in the database from the passed handle.
If no such Location exists, None is returned.
"""
return self.get_from_handle(handle, Location, self.location_map)
def __get_obj_from_gramps_id(self, val, tbl, class_, prim_tbl):
if isinstance(val, UNITYPE):
val = val.encode('utf-8')
try:
data = tbl.get(val, txn=self.txn)
data = tbl.get(str(val), txn=self.txn)
if data is not None:
obj = class_()
### FIXME: this is a dirty hack that works without no
@ -758,12 +788,12 @@ class DbBsddbRead(DbReadBase, Callback):
if self.readonly:
tuple_data = prim_tbl.get(data, txn=self.txn)
else:
tuple_data = pickle.loads(data)
tuple_data = cPickle.loads(data)
obj.unserialize(tuple_data)
return obj
else:
return None
except DBERRS as msg:
except DBERRS, msg:
self.__log_error()
raise DbError(msg)
@ -862,15 +892,17 @@ class DbBsddbRead(DbReadBase, Callback):
Return the default grouping name for a surname.
Return type is a unicode object
"""
if isinstance(surname, UNITYPE):
surname = surname.encode('utf-8')
return conv_dbstr_to_unicode(self.name_group.get(surname, surname))
if isinstance(surname, unicode):
ssurname = surname.encode('utf-8')
return conv_dbstr_to_unicode(self.name_group.get(ssurname, ssurname))
else:
return conv_dbstr_to_unicode(self.name_group.get(surname, surname))
def get_name_group_keys(self):
"""
Return the defined names that have been assigned to a default grouping.
"""
return list(map(conv_dbstr_to_unicode, list(self.name_group.keys())))
return map(conv_dbstr_to_unicode, self.name_group.keys())
def has_name_group_key(self, name):
"""
@ -878,9 +910,10 @@ class DbBsddbRead(DbReadBase, Callback):
"""
# The use of has_key seems allright because there is no write lock
# on the name_group table when this is called.
if isinstance(name, UNITYPE):
name = name.encode('utf-8')
return name in self.name_group
if isinstance(name, unicode):
return self.name_group.has_key(name.encode('utf-8'))
else:
return self.name_group.has_key(name)
def get_number_of_records(self, table):
if not self.db_is_open:
@ -950,6 +983,12 @@ class DbBsddbRead(DbReadBase, Callback):
"""
return self.get_number_of_records(self.tag_map)
def get_number_of_locations(self):
"""
Return the number of locations currently in the database.
"""
return self.get_number_of_records(self.location_map)
def all_handles(self, table):
return table.keys(txn=self.txn)
@ -1074,6 +1113,15 @@ class DbBsddbRead(DbReadBase, Callback):
return handle_list
return []
def get_location_handles(self):
"""
Return a list of database handles, one handle for each Location in the
database.
"""
if self.db_is_open:
return self.all_handles(self.location_map)
return []
def _f(curs_):
"""
Closure that returns an iterator over handles in the database.
@ -1096,6 +1144,7 @@ class DbBsddbRead(DbReadBase, Callback):
iter_repository_handles = _f(get_repository_cursor)
iter_note_handles = _f(get_note_cursor)
iter_tag_handles = _f(get_tag_cursor)
iter_location_handles = _f(get_location_cursor)
del _f
def _f(curs_, obj_):
@ -1122,6 +1171,7 @@ class DbBsddbRead(DbReadBase, Callback):
iter_repositories = _f(get_repository_cursor, Repository)
iter_notes = _f(get_note_cursor, Note)
iter_tags = _f(get_tag_cursor, Tag)
iter_locations = _f(get_location_cursor, Location)
del _f
def get_gramps_ids(self, obj_key):
@ -1138,7 +1188,7 @@ class DbBsddbRead(DbReadBase, Callback):
}
table = key2table[obj_key]
return list(table.keys())
return table.keys()
def has_gramps_id(self, obj_key, gramps_id):
key2table = {
@ -1154,9 +1204,8 @@ class DbBsddbRead(DbReadBase, Callback):
}
table = key2table[obj_key]
if isinstance(gramps_id, UNITYPE):
gramps_id = gramps_id.encode('utf-8')
return table.get(gramps_id, txn=self.txn) is not None
#return str(gramps_id) in table
return table.get(str(gramps_id), txn=self.txn) is not None
def find_initial_person(self):
person = self.get_default_person()
@ -1168,7 +1217,7 @@ class DbBsddbRead(DbReadBase, Callback):
@staticmethod
def _validated_id_prefix(val, default):
if isinstance(val, STRTYPE) and val:
if isinstance(val, basestring) and val:
try:
str_ = val % 1
except TypeError: # missing conversion specifier
@ -1190,24 +1239,23 @@ class DbBsddbRead(DbReadBase, Callback):
pattern_match = re.match(r"(.*)%[0 ](\d+)[diu]$", id_pattern)
if pattern_match:
str_prefix = pattern_match.group(1)
##nr_width = pattern_match.group(2)
nr_width = pattern_match.group(2)
def closure_func(gramps_id):
if gramps_id and gramps_id.startswith(str_prefix):
id_number = gramps_id[len(str_prefix):]
if id_number.isdigit():
id_value = int(id_number, 10)
## this code never ran, as an int compared to str with > is False!
## if len(cuni(id_value)) > nr_width:
## # The ID to be imported is too large to fit in the
## # users format. For now just create a new ID,
## # because that is also what happens with IDs that
## # are identical to IDs already in the database. If
## # the problem of colliding import and already
## # present IDs is solved the code here also needs
## # some solution.
## gramps_id = id_pattern % 1
## else:
gramps_id = id_pattern % id_value
if len(str(id_value)) > nr_width:
# The ID to be imported is too large to fit in the
# users format. For now just create a new ID,
# because that is also what happens with IDs that
# are identical to IDs already in the database. If
# the problem of colliding import and already
# present IDs is solved the code here also needs
# some solution.
gramps_id = id_pattern % 1
else:
gramps_id = id_pattern % id_value
return gramps_id
else:
def closure_func(gramps_id):
@ -1391,13 +1439,13 @@ class DbBsddbRead(DbReadBase, Callback):
if person:
return person
elif (self.metadata is not None) and (not self.readonly):
self.metadata[b'default'] = None
self.metadata['default'] = None
return None
def get_default_handle(self):
"""Return the default Person of the database."""
if self.metadata is not None:
return self.metadata.get(b'default')
return self.metadata.get('default')
return None
def get_save_path(self):
@ -1513,11 +1561,9 @@ class DbBsddbRead(DbReadBase, Callback):
"""
Helper method for get_raw_<object>_data methods
"""
if isinstance(handle, UNITYPE):
handle = handle.encode('utf-8')
try:
return table.get(handle, txn=self.txn)
except DBERRS as msg:
return table.get(str(handle), txn=self.txn)
except DBERRS, msg:
self.__log_error()
raise DbError(msg)
@ -1551,15 +1597,16 @@ class DbBsddbRead(DbReadBase, Callback):
def get_raw_tag_data(self, handle):
return self.__get_raw_data(self.tag_map, handle)
def get_raw_location_data(self, handle):
return self.__get_raw_data(self.location_map, handle)
def __has_handle(self, table, handle):
"""
Helper function for has_<object>_handle methods
"""
if isinstance(handle, UNITYPE):
handle = handle.encode('utf-8')
try:
return table.get(handle, txn=self.txn) is not None
except DBERRS as msg:
return table.get(str(handle), txn=self.txn) is not None
except DBERRS, msg:
self.__log_error()
raise DbError(msg)
@ -1623,94 +1670,68 @@ class DbBsddbRead(DbReadBase, Callback):
"""
return self.__has_handle(self.tag_map, handle)
def __sortbyperson_key(self, handle):
if isinstance(handle, UNITYPE):
handle = handle.encode('utf-8')
return locale.strxfrm(find_surname(handle,
self.person_map.get(handle)))
def has_location_handle(self, handle):
"""
Return True if the handle exists in the current Location database.
"""
return self.__has_handle(self.location_map, handle)
def __sortbyperson_key(self, person):
return locale.strxfrm(find_surname(str(person),
self.person_map.get(str(person))))
def __sortbyplace(self, first, second):
if isinstance(first, UNITYPE):
first = first.encode('utf-8')
if isinstance(second, UNITYPE):
second = second.encode('utf-8')
return locale.strcoll(self.place_map.get(first)[2],
self.place_map.get(second)[2])
return locale.strcoll(self.place_map.get(str(first))[2],
self.place_map.get(str(second))[2])
def __sortbyplace_key(self, place):
if isinstance(place, UNITYPE):
place = place.encode('utf-8')
return locale.strxfrm(self.place_map.get(place)[2])
return locale.strxfrm(self.place_map.get(str(place))[2])
def __sortbysource(self, first, second):
if isinstance(first, UNITYPE):
first = first.encode('utf-8')
if isinstance(second, UNITYPE):
second = second.encode('utf-8')
source1 = cuni(self.source_map[first][2])
source2 = cuni(self.source_map[second][2])
source1 = unicode(self.source_map[str(first)][2])
source2 = unicode(self.source_map[str(second)][2])
return locale.strcoll(source1, source2)
def __sortbysource_key(self, key):
if isinstance(key, UNITYPE):
key = key.encode('utf-8')
source = cuni(self.source_map[key][2])
source = unicode(self.source_map[str(key)][2])
return locale.strxfrm(source)
def __sortbycitation(self, first, second):
if isinstance(first, UNITYPE):
first = first.encode('utf-8')
if isinstance(second, UNITYPE):
second = second.encode('utf-8')
citation1 = cuni(self.citation_map[first][3])
citation2 = cuni(self.citation_map[second][3])
citation1 = unicode(self.citation_map[str(first)][3])
citation2 = unicode(self.citation_map[str(second)][3])
return locale.strcoll(citation1, citation2)
def __sortbycitation_key(self, key):
if isinstance(key, UNITYPE):
key = key.encode('utf-8')
citation = cuni(self.citation_map[key][3])
citation = unicode(self.citation_map[str(key)][3])
return locale.strxfrm(citation)
def __sortbymedia(self, first, second):
if isinstance(first, UNITYPE):
first = first.encode('utf-8')
if isinstance(second, UNITYPE):
second = second.encode('utf-8')
media1 = self.media_map[first][4]
media2 = self.media_map[second][4]
media1 = self.media_map[str(first)][4]
media2 = self.media_map[str(second)][4]
return locale.strcoll(media1, media2)
def __sortbymedia_key(self, key):
if isinstance(key, UNITYPE):
key = key.encode('utf-8')
media = self.media_map[key][4]
media = self.media_map[str(key)][4]
return locale.strxfrm(media)
def __sortbytag(self, first, second):
if isinstance(first, UNITYPE):
first = first.encode('utf-8')
if isinstance(second, UNITYPE):
second = second.encode('utf-8')
tag1 = self.tag_map[first][1]
tag2 = self.tag_map[second][1]
tag1 = self.tag_map[str(first)][1]
tag2 = self.tag_map[str(second)][1]
return locale.strcoll(tag1, tag2)
def __sortbytag_key(self, key):
if isinstance(key, UNITYPE):
key = key.encode('utf-8')
tag = self.tag_map[key][1]
tag = self.tag_map[str(key)][1]
return locale.strxfrm(tag)
def set_mediapath(self, path):
"""Set the default media path for database, path should be utf-8."""
if (self.metadata is not None) and (not self.readonly):
self.metadata[b'mediapath'] = path
self.metadata['mediapath'] = path
def get_mediapath(self):
"""Return the default media path of the database."""
if self.metadata is not None:
return self.metadata.get(b'mediapath', None)
return self.metadata.get('mediapath', None)
return None
def find_backlink_handles(self, handle, include_classes=None):
@ -1779,17 +1800,21 @@ class DbBsddbRead(DbReadBase, Callback):
'cursor_func': self.get_tag_cursor,
'class_func': Tag,
},
'Location': {
'cursor_func': self.get_location_cursor,
'class_func': Location,
},
}
# Find which tables to iterate over
if (include_classes is None):
the_tables = list(primary_tables.keys())
the_tables = primary_tables.keys()
else:
the_tables = include_classes
# Now we use the functions and classes defined above to loop through
# each of the existing primary object tables
for primary_table_name, funcs in the_tables.items():
for primary_table_name, funcs in the_tables.iteritems():
with funcs['cursor_func']() as cursor:
# Grab the real object class here so that the lookup does
@ -1834,7 +1859,7 @@ class DbBsddbRead(DbReadBase, Callback):
name_file = open(filepath, "r")
name = name_file.read()
name_file.close()
except (OSError, IOError) as msg:
except (OSError, IOError), msg:
self.__log_error()
name = None
return name

View File

@ -31,18 +31,13 @@ undos and redos.
# Standard python modules
#
#-------------------------------------------------------------------------
from __future__ import print_function, with_statement
from __future__ import with_statement
import time, os
import sys
if sys.version_info[0] < 3:
import cPickle as pickle
else:
import pickle
import cPickle as pickle
from collections import deque
from ..config import config
if config.get('preferences.use-bsddb3') or sys.version_info[0] >= 3:
if config.get('preferences.use-bsddb3'):
from bsddb3 import db
else:
from bsddb import db
@ -53,7 +48,7 @@ from ..ggettext import gettext as _
# Gramps modules
#
#-------------------------------------------------------------------------
from .dbconst import *
from dbconst import *
from . import BSDDBTxn
from ..errors import DbError
@ -65,8 +60,8 @@ from ..errors import DbError
DBERRS = (db.DBRunRecoveryError, db.DBAccessError,
db.DBPageNotFoundError, db.DBInvalidArgError)
_SIGBASE = ('person', 'family', 'source', 'event', 'media',
'place', 'repository', 'reference', 'note', 'tag', 'citation')
_SIGBASE = ('person', 'family', 'source', 'event', 'media', 'place',
'location', 'repository', 'reference', 'note', 'tag', 'citation')
#-------------------------------------------------------------------------
#
# DbUndo class
@ -104,6 +99,7 @@ class DbUndo(object):
self.db.note_map,
self.db.tag_map,
self.db.citation_map,
self.db.location_map,
)
def clear(self):
@ -212,7 +208,7 @@ class DbUndo(object):
self.db.txn = None
return status
except DBERRS as msg:
except DBERRS, msg:
self.db._log_error()
raise DbError(msg)
@ -305,7 +301,7 @@ class DbUndo(object):
else:
db_map.put(handle, data, txn=self.txn)
except DBERRS as msg:
except DBERRS, msg:
self.db._log_error()
raise DbError(msg)
@ -326,7 +322,7 @@ class DbUndo(object):
db_map.put(handle, data, txn=self.txn)
emit(signal, ([handle],))
except DBERRS as msg:
except DBERRS, msg:
self.db._log_error()
raise DbError(msg)
@ -459,7 +455,7 @@ class DbUndoBSDDB(DbUndo):
data = cursor.first()
while data:
yield data
data = next(cursor)
data = cursor.next()
def testundo():
class T:
@ -479,35 +475,36 @@ def testundo():
self.place_map = {}
self.note_map = {}
self.tag_map = {}
self.location_map = {}
self.repository_map = {}
self.reference_map = {}
print("list tests")
print "list tests"
undo = DbUndoList(D())
print(undo.append('foo'))
print(undo.append('bar'))
print(undo[0])
print undo.append('foo')
print undo.append('bar')
print undo[0]
undo[0] = 'foobar'
print(undo[0])
print("len", len(undo))
print("iter")
print undo[0]
print "len", len(undo)
print "iter"
for data in undo:
print(data)
print()
print("bsddb tests")
print data
print
print "bsddb tests"
undo = DbUndoBSDDB(D(), '/tmp/testundo')
undo.open()
print(undo.append('foo'))
print(undo.append('fo2'))
print(undo.append('fo3'))
print(undo[1])
print undo.append('foo')
print undo.append('fo2')
print undo.append('fo3')
print undo[1]
undo[1] = 'bar'
print(undo[1])
print undo[1]
for data in undo:
print(data)
print("len", len(undo))
print data
print "len", len(undo)
print("test commit")
print "test commit"
undo.commit(T(), msg="test commit")
undo.close()

View File

@ -21,9 +21,8 @@
# $Id$
from __future__ import with_statement, unicode_literals
from __future__ import with_statement
import sys
from ..lib.markertype import MarkerType
from ..lib.tag import Tag
import time
@ -31,23 +30,87 @@ import logging
LOG = logging.getLogger(".citation")
from ..ggettext import gettext as _
from ..constfunc import cuni
"""
methods to upgrade a database from version 13 to current version
"""
from ..config import config
if config.get('preferences.use-bsddb3') or sys.version_info[0] >= 3:
if config.get('preferences.use-bsddb3'):
from bsddb3 import db
else:
from bsddb import db
from . import BSDDBTxn
from ..lib.nameorigintype import NameOriginType
from .write import _mkname, SURNAMES
from .dbconst import (PERSON_KEY, FAMILY_KEY, EVENT_KEY,
MEDIA_KEY, PLACE_KEY, REPOSITORY_KEY)
from write import _mkname, SURNAMES
from dbconst import (PERSON_KEY, FAMILY_KEY, EVENT_KEY,
MEDIA_KEY, PLACE_KEY, LOCATION_KEY, REPOSITORY_KEY)
from gramps.gui.dialog import (InfoDialog)
def gramps_upgrade_17(self):
self.set_total(len(self.place_map))
self.children = {None: []}
for handle in self.place_map.keys():
place = self.place_map[handle]
new_place = list(place)
lat_long = (new_place[4], new_place[3])
if new_place[5] is not None:
new_place[5] = process_location(self, new_place[5], lat_long)
else:
new_place[5] = process_location(self, None, lat_long)
add_reference(self, handle, new_place[5])
alt_locs = []
for alt_loc in new_place[6]:
ref_handle = process_location(self, alt_loc, lat_long)
add_reference(self, handle, ref_handle)
alt_locs.append(ref_handle)
new_place[6] = alt_locs
new_place = tuple(new_place[:3] + new_place[5:])
with BSDDBTxn(self.env, self.place_map) as txn:
txn.put(str(handle), new_place)
self.update()
with BSDDBTxn(self.env, self.metadata) as txn:
txn.put('version', 17)
def add_reference(self, pri_handle, ref_handle):
key = (PLACE_KEY, pri_handle)
data = ((PLACE_KEY, pri_handle), (LOCATION_KEY, ref_handle))
with BSDDBTxn(self.env, self.reference_map) as txn:
txn.put(str(key), data)
def process_location(self, loc, lat_long):
if loc is None:
location = ['Unknown']
else:
# (street, locality, parish, city, county, state, country)
# We need to think about where to put ZIP code and Phone number
location = loc[0][:2] + (loc[1],) + loc[0][2:6]
location = list(location)
location.reverse()
items = [x for x in enumerate(location) if x[1]]
parent = None
for item in items:
parent = match_location(self, parent, item, lat_long)
return parent
def match_location(self, parent, item, lat_long):
for handle in self.children[parent]:
if self.location_map[handle][2] == item[1]:
return handle
handle = self.create_id()
self.children[handle] = []
self.children[parent].append(handle)
new_location = (handle,
parent,
item[1], # Name
item[0]+1, # Type
lat_long[0],
lat_long[1],
int(time.time()))
with BSDDBTxn(self.env, self.location_map) as txn:
txn.put(str(handle), new_location)
return handle
def gramps_upgrade_16(self):
"""Upgrade database from version 15 to 16. This upgrade converts all
SourceRef child objects to Citation Primary objects.
@ -148,10 +211,10 @@ def gramps_upgrade_16(self):
self.update()
LOG.debug("%d persons upgraded with %d citations in %d seconds. " %
(len(list(self.person_map.keys())),
(len(self.person_map.keys()),
self.cmap_index - start_num_citations,
time.time() - start_time))
data_upgradeobject[key2data[PERSON_KEY]] = (len(list(self.person_map.keys())),
data_upgradeobject[key2data[PERSON_KEY]] = (len(self.person_map.keys()),
self.cmap_index - start_num_citations,
time.time() - start_time)
@ -184,7 +247,7 @@ def gramps_upgrade_16(self):
LOG.debug("Media upgrade %d citations upgraded in %d seconds" %
(self.cmap_index - start_num_citations,
int(time.time() - start_time)))
data_upgradeobject[key2data[MEDIA_KEY]] = (len(list(self.media_map.keys())),
data_upgradeobject[key2data[MEDIA_KEY]] = (len(self.media_map.keys()),
self.cmap_index - start_num_citations,
time.time() - start_time)
@ -195,7 +258,7 @@ def gramps_upgrade_16(self):
start_time = time.time()
for place_handle in self.place_map.keys():
place = self.place_map[place_handle]
(handle, gramps_id, title, int, lat,
(handle, gramps_id, title, long, lat,
main_loc, alt_loc, urls, media_list, source_list, note_list,
change, private) = place
if source_list:
@ -208,7 +271,7 @@ def gramps_upgrade_16(self):
self, media_list)
if source_list or media_list:
new_place = (handle, gramps_id, title,
int, lat, main_loc, alt_loc, urls,
long, lat, main_loc, alt_loc, urls,
media_list, new_citation_list, note_list,
change, private)
with BSDDBTxn(self.env, self.place_map) as txn:
@ -216,10 +279,10 @@ def gramps_upgrade_16(self):
self.update()
LOG.debug("%d places upgraded with %d citations in %d seconds. " %
(len(list(self.place_map.keys())),
(len(self.place_map.keys()),
self.cmap_index - start_num_citations,
time.time() - start_time))
data_upgradeobject[key2data[PLACE_KEY]] = (len(list(self.place_map.keys())),
data_upgradeobject[key2data[PLACE_KEY]] = (len(self.place_map.keys()),
self.cmap_index - start_num_citations,
time.time() - start_time)
@ -264,10 +327,10 @@ def gramps_upgrade_16(self):
self.update()
LOG.debug("%d familys upgraded with %d citations in %d seconds. " %
(len(list(self.family_map.keys())),
(len(self.family_map.keys()),
self.cmap_index - start_num_citations,
time.time() - start_time))
data_upgradeobject[key2data[FAMILY_KEY]] = (len(list(self.family_map.keys())),
data_upgradeobject[key2data[FAMILY_KEY]] = (len(self.family_map.keys()),
self.cmap_index - start_num_citations,
time.time() - start_time)
# ---------------------------------
@ -309,10 +372,10 @@ def gramps_upgrade_16(self):
LOG.debug("%d events upgraded with %d citations in %d seconds. "
"Backlinks took %d seconds" %
(len(list(self.event_map.keys())),
(len(self.event_map.keys()),
self.cmap_index - start_num_citations,
int(upgrade_time), int(backlink_time)))
data_upgradeobject[key2data[EVENT_KEY]] = (len(list(self.event_map.keys())),
data_upgradeobject[key2data[EVENT_KEY]] = (len(self.event_map.keys()),
self.cmap_index - start_num_citations,
time.time() - start_time)
@ -336,10 +399,10 @@ def gramps_upgrade_16(self):
self.update()
LOG.debug("%d repositorys upgraded with %d citations in %d seconds. " %
(len(list(self.repository_map.keys())),
(len(self.repository_map.keys()),
self.cmap_index - start_num_citations,
time.time() - start_time))
data_upgradeobject[key2data[REPOSITORY_KEY]] = (len(list(self.repository_map.keys())),
data_upgradeobject[key2data[REPOSITORY_KEY]] = (len(self.repository_map.keys()),
self.cmap_index - start_num_citations,
time.time() - start_time)
# ---------------------------------
@ -594,9 +657,9 @@ def gramps_upgrade_15(self):
tags = [tag_handle]
else:
tags = []
address_list = list(map(convert_address, address_list))
address_list = map(convert_address, address_list)
new_primary_name = convert_name_15(primary_name)
new_alternate_names = list(map(convert_name_15, alternate_names))
new_alternate_names = map(convert_name_15, alternate_names)
new_person = (junk_handle, # 0
gramps_id, # 1
gender, # 2
@ -700,7 +763,7 @@ def gramps_upgrade_15(self):
new_place = list(place)
if new_place[5] is not None:
new_place[5] = convert_location(new_place[5])
new_place[6] = list(map(convert_location, new_place[6]))
new_place[6] = map(convert_location, new_place[6])
new_place = new_place[:12] + new_place[13:]
new_place = tuple(new_place)
with BSDDBTxn(self.env, self.place_map) as txn:
@ -728,7 +791,7 @@ def gramps_upgrade_15(self):
repository = self.repository_map[handle]
new_repository = list(repository)
new_repository = new_repository[:8] + new_repository[9:]
new_repository[5] = list(map(convert_address, new_repository[5]))
new_repository[5] = map(convert_address, new_repository[5])
new_repository = tuple(new_repository)
with BSDDBTxn(self.env, self.repository_map) as txn:
txn.put(str(handle), new_repository)
@ -742,7 +805,7 @@ def convert_marker(self, marker_field):
"""Convert a marker into a tag."""
marker = MarkerType()
marker.unserialize(marker_field)
tag_name = cuni(marker)
tag_name = unicode(marker)
if tag_name != '':
if tag_name not in self.tags:
@ -761,7 +824,7 @@ def convert_marker(self, marker_field):
def convert_locbase(loc):
"""Convert location base to include an empty locality field."""
return tuple([loc[0], ''] + list(loc[1:]))
return tuple([loc[0], u''] + list(loc[1:]))
def convert_location(loc):
"""Convert a location into the new format."""
@ -777,26 +840,26 @@ def convert_name_15(name):
name_type, prefix, patronymic,
group_as, sort_as, display_as, call) = name
connector = ""
origintype = (NameOriginType.NONE, "")
patorigintype = (NameOriginType.PATRONYMIC, "")
connector = u""
origintype = (NameOriginType.NONE, u"")
patorigintype = (NameOriginType.PATRONYMIC, u"")
if patronymic.strip() == "":
if patronymic.strip() == u"":
#no patronymic, create a single surname
surname_list = [(surname, prefix, True, origintype, connector)]
else:
#a patronymic, if no surname or equal as patronymic, a single surname
if (surname.strip() == "") or (surname == patronymic and prefix == ""):
if (surname.strip() == u"") or (surname == patronymic and prefix == u""):
surname_list = [(patronymic, prefix, True, patorigintype, connector)]
else:
#two surnames, first patronymic, then surname which is primary
surname_list = [(patronymic, "", False, patorigintype, ""),
surname_list = [(patronymic, u"", False, patorigintype, u""),
(surname, prefix, True, origintype, connector)]
#return new value, add two empty strings for nick and family nick
return (privacy, source_list, note_list, date,
first_name, surname_list, suffix, title, name_type,
group_as, sort_as, display_as, call, "", "")
group_as, sort_as, display_as, call, u"", u"")
def gramps_upgrade_14(self):
"""Upgrade database from version 13 to 14."""
@ -1004,12 +1067,12 @@ def gramps_upgrade_14(self):
# ---------------------------------
for place_handle in self.place_map.keys():
place = self.place_map[place_handle]
(handle, gramps_id, title, int, lat,
(handle, gramps_id, title, long, lat,
main_loc, alt_loc, urls, media_list, source_list, note_list,
change, marker, private) = place
new_media_list = new_media_list_14(media_list)
new_source_list = new_source_list_14(source_list)
new_place = (handle, gramps_id, title, int, lat,
new_place = (handle, gramps_id, title, long, lat,
main_loc, alt_loc, urls, new_media_list,
new_source_list, note_list, change, marker, private)

View File

@ -32,23 +32,19 @@ This is used since GRAMPS version 3.0
# Standard python modules
#
#-------------------------------------------------------------------------
from __future__ import print_function, with_statement
import sys
if sys.version_info[0] < 3:
import cPickle as pickle
else:
import pickle
from __future__ import with_statement
import cPickle as pickle
import os
import time
import locale
import bisect
from functools import wraps
import logging
from sys import maxsize
from sys import maxint
from ..ggettext import gettext as _
from ..config import config
if config.get('preferences.use-bsddb3') or sys.version_info[0] >= 3:
if config.get('preferences.use-bsddb3'):
from bsddb3 import dbshelve, db
else:
from bsddb import dbshelve, db
@ -64,6 +60,7 @@ from ..lib.src import Source
from ..lib.citation import Citation
from ..lib.event import Event
from ..lib.place import Place
from ..lib.location import Location
from ..lib.repo import Repository
from ..lib.mediaobj import MediaObject
from ..lib.note import Note
@ -75,17 +72,17 @@ from . import (DbBsddbRead, DbWriteBase, BSDDBTxn,
DbTxn, BsddbBaseCursor, BsddbDowngradeError, DbVersionError,
DbEnvironmentError, DbUpgradeRequiredError, find_surname,
find_surname_name, DbUndoBSDDB as DbUndo)
from .dbconst import *
from dbconst import *
from ..utils.callback import Callback
from ..utils.cast import (conv_unicode_tosrtkey, conv_dbstr_to_unicode)
from ..updatecallback import UpdateCallback
from ..errors import DbError
from ..constfunc import win, conv_to_unicode, cuni, UNITYPE
from ..constfunc import win
_LOG = logging.getLogger(DBLOGNAME)
LOG = logging.getLogger(".citation")
_MINVERSION = 9
_DBVERSION = 16
_DBVERSION = 17
IDTRANS = "person_id"
FIDTRANS = "family_id"
@ -97,6 +94,8 @@ NIDTRANS = "note_id"
SIDTRANS = "source_id"
CIDTRANS = "citation_id"
TAGTRANS = "tag_name"
LPARENTS = "location_parent"
LNAMES = "location_name"
SURNAMES = "surnames"
NAME_GROUP = "name_group"
META = "meta_data"
@ -111,6 +110,7 @@ PERSON_TBL = "person"
REPO_TBL = "repo"
NOTE_TBL = "note"
TAG_TBL = "tag"
LOCATION_TBL = "location"
REF_MAP = "reference_map"
REF_PRI = "primary_map"
@ -134,7 +134,8 @@ CLASS_TO_KEY_MAP = {Person.__name__: PERSON_KEY,
Place.__name__: PLACE_KEY,
Repository.__name__:REPOSITORY_KEY,
Note.__name__: NOTE_KEY,
Tag.__name__: TAG_KEY}
Tag.__name__: TAG_KEY,
Location.__name__: LOCATION_KEY}
KEY_TO_CLASS_MAP = {PERSON_KEY: Person.__name__,
FAMILY_KEY: Family.__name__,
@ -145,7 +146,8 @@ KEY_TO_CLASS_MAP = {PERSON_KEY: Person.__name__,
PLACE_KEY: Place.__name__,
REPOSITORY_KEY: Repository.__name__,
NOTE_KEY: Note.__name__,
TAG_KEY: Tag.__name__}
TAG_KEY: Tag.__name__,
LOCATION_KEY: Location.__name__}
KEY_TO_NAME_MAP = {PERSON_KEY: 'person',
FAMILY_KEY: 'family',
@ -157,7 +159,8 @@ KEY_TO_NAME_MAP = {PERSON_KEY: 'person',
REPOSITORY_KEY: 'repository',
#REFERENCE_KEY: 'reference',
NOTE_KEY: 'note',
TAG_KEY: 'tag'}
TAG_KEY: 'tag',
LOCATION_KEY: 'location'}
#-------------------------------------------------------------------------
#
# Helper functions
@ -165,13 +168,13 @@ KEY_TO_NAME_MAP = {PERSON_KEY: 'person',
#-------------------------------------------------------------------------
def find_idmap(key, data):
""" return id for association of secondary index.
returns a byte string
"""
val = data[1]
if isinstance(val, UNITYPE):
val = val.encode('utf-8')
return val
return str(data[1])
def find_parent(key, data):
return str(data[1])
def find_name(key, data):
return str(data[2]).upper()
# Secondary database key lookups for reference_map table
# reference_map data values are of the form:
@ -179,22 +182,10 @@ def find_idmap(key, data):
# (referenced_object_class_name, referenced_object_handle))
def find_primary_handle(key, data):
""" return handle for association of indexes
returns byte string
"""
val = (data)[0][1]
if isinstance(val, UNITYPE):
val = val.encode('utf-8')
return val
return str((data)[0][1])
def find_referenced_handle(key, data):
""" return handle for association of indexes
returns byte string
"""
val = (data)[1][1]
if isinstance(val, UNITYPE):
val = val.encode('utf-8')
return val
return str((data)[1][1])
#-------------------------------------------------------------------------
#
@ -219,7 +210,7 @@ class DbBsddbAssocCursor(BsddbBaseCursor):
BsddbBaseCursor.__init__(self, txn=txn, **kwargs)
self.cursor = source.cursor(txn)
self.source = source
#-------------------------------------------------------------------------
#
# DbBsddb
@ -235,7 +226,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
# 1. Signals for primary objects
__signals__ = dict((obj+'-'+op, signal)
for obj in
['person', 'family', 'event', 'place',
['person', 'family', 'event', 'place', 'location',
'source', 'citation', 'media', 'note', 'repository', 'tag']
for op, signal in zip(
['add', 'update', 'delete', 'rebuild'],
@ -252,11 +243,8 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
# 3. Special signal for change in home person
__signals__['home-person-changed'] = None
# 4. Signal for change in person group name, parameters are
if sys.version_info[0] < 3:
__signals__['person-groupname-rebuild'] = (unicode, unicode)
else:
__signals__['person-groupname-rebuild'] = (str, str)
# 4. Signal for change in person group name, parameters are
__signals__['person-groupname-rebuild'] = (unicode, unicode)
def __init__(self):
"""Create a new GrampsDB."""
@ -280,7 +268,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
def try_(self, *args, **kwargs):
try:
return func(self, *args, **kwargs)
except DBERRS as msg:
except DBERRS, msg:
self.__log_error()
raise DbError(msg)
return try_
@ -363,19 +351,30 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
return DbBsddbAssocCursor(self.reference_map_referenced_map,
self.txn)
@catch_db_error
def get_location_parent_cursor(self):
"""
Returns a reference to a cursor over the location parents
"""
return DbBsddbAssocCursor(self.parents, self.txn)
@catch_db_error
def get_location_name_cursor(self):
"""
Returns a reference to a cursor over the location names
"""
return DbBsddbAssocCursor(self.names, self.txn)
# These are overriding the DbBsddbRead's methods of saving metadata
# because we now have txn-capable metadata table
@catch_db_error
def set_default_person_handle(self, handle):
"""Set the default Person to the passed instance."""
#we store a byte string!
if isinstance(handle, UNITYPE):
handle = handle.encode('utf-8')
if not self.readonly:
# Start transaction
with BSDDBTxn(self.env, self.metadata) as txn:
txn.put(b'default', handle)
txn.put('default', str(handle))
self.emit('home-person-changed')
@catch_db_error
@ -387,7 +386,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
elif (self.metadata) and (not self.readonly):
# Start transaction
with BSDDBTxn(self.env, self.metadata) as txn:
txn.put(b'default', None)
txn.put('default', None)
return None
def set_mediapath(self, path):
@ -395,7 +394,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
if self.metadata and not self.readonly:
# Start transaction
with BSDDBTxn(self.env, self.metadata) as txn:
txn.put(b'mediapath', path)
txn.put('mediapath', path)
def __check_bdb_version(self, name):
"""Older version of Berkeley DB can't read data created by a newer
@ -410,9 +409,6 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
except:
# Just assume that the Berkeley DB version is OK.
pass
if not env_version:
#empty file, assume it is ok to open
env_version = (0, 0, 0)
if (env_version[0] > bdb_version[0]) or \
(env_version[0] == bdb_version[0] and
env_version[1] > bdb_version[1]):
@ -423,12 +419,12 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
@catch_db_error
def version_supported(self):
dbversion = self.metadata.get(b'version', default=0)
dbversion = self.metadata.get('version', default=0)
return ((dbversion <= _DBVERSION) and (dbversion >= _MINVERSION))
@catch_db_error
def need_upgrade(self):
dbversion = self.metadata.get(b'version', default=0)
dbversion = self.metadata.get('version', default=0)
return not self.readonly and dbversion < _DBVERSION
def __check_readonly(self, name):
@ -444,7 +440,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
# See if we lack write access to any files in the directory
for base in [FAMILY_TBL, PLACES_TBL, SOURCES_TBL, CITATIONS_TBL,
MEDIA_TBL, EVENTS_TBL, PERSON_TBL, REPO_TBL,
NOTE_TBL, REF_MAP, META]:
NOTE_TBL, TAG_TBL, LOCATION_TBL, REF_MAP, META]:
path = os.path.join(name, base + DBEXT)
if os.path.isfile(path) and not os.access(path, os.W_OK):
return True
@ -506,7 +502,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
try:
self.env.open(env_name, env_flags)
except Exception as msg:
except Exception, msg:
_LOG.warning("Error opening db environment: " + str(msg))
try:
self.__close_early()
@ -529,7 +525,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
raise DbVersionError()
self.__load_metadata()
gstats = self.metadata.get(b'gender_stats', default=None)
gstats = self.metadata.get('gender_stats', default=None)
# Ensure version info in metadata
if not self.readonly:
@ -537,12 +533,12 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
with BSDDBTxn(self.env, self.metadata) as txn:
if gstats is None:
# New database. Set up the current version.
#self.metadata.put(b'version', _DBVERSION, txn=the_txn)
txn.put(b'version', _DBVERSION)
elif b'version' not in self.metadata:
#self.metadata.put('version', _DBVERSION, txn=the_txn)
txn.put('version', _DBVERSION)
elif 'version' not in self.metadata:
# Not new database, but the version is missing.
# Use 0, but it is likely to fail anyway.
txn.put(b'version', 0)
txn.put('version', 0)
self.genderStats = GenderStats(gstats)
@ -558,6 +554,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
("repository_map", REPO_TBL, db.DB_HASH),
("note_map", NOTE_TBL, db.DB_HASH),
("tag_map", TAG_TBL, db.DB_HASH),
("location_map", LOCATION_TBL, db.DB_HASH),
("reference_map", REF_MAP, db.DB_BTREE),
]
@ -629,7 +626,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
def __load_metadata(self):
# name display formats
self.name_formats = self.metadata.get(b'name_formats', default=[])
self.name_formats = self.metadata.get('name_formats', default=[])
# upgrade formats if they were saved in the old way
for format_ix in range(len(self.name_formats)):
format = self.name_formats[format_ix]
@ -639,7 +636,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
# database owner
try:
owner_data = self.metadata.get(b'researcher')
owner_data = self.metadata.get('researcher')
if owner_data:
if len(owner_data[0]) == 7: # Pre-3.3 format
owner_data = upgrade_researcher(owner_data)
@ -650,35 +647,35 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
# bookmarks
meta = lambda meta: self.metadata.get(meta, default=[])
self.bookmarks.set(meta(b'bookmarks'))
self.family_bookmarks.set(meta(b'family_bookmarks'))
self.event_bookmarks.set(meta(b'event_bookmarks'))
self.source_bookmarks.set(meta(b'source_bookmarks'))
self.citation_bookmarks.set(meta(b'citation_bookmarks'))
self.repo_bookmarks.set(meta(b'repo_bookmarks'))
self.media_bookmarks.set(meta(b'media_bookmarks'))
self.place_bookmarks.set(meta(b'place_bookmarks'))
self.note_bookmarks.set(meta(b'note_bookmarks'))
self.bookmarks.set(meta('bookmarks'))
self.family_bookmarks.set(meta('family_bookmarks'))
self.event_bookmarks.set(meta('event_bookmarks'))
self.source_bookmarks.set(meta('source_bookmarks'))
self.citation_bookmarks.set(meta('citation_bookmarks'))
self.repo_bookmarks.set(meta('repo_bookmarks'))
self.media_bookmarks.set(meta('media_bookmarks'))
self.place_bookmarks.set(meta('place_bookmarks'))
self.note_bookmarks.set(meta('note_bookmarks'))
# Custom type values
self.family_event_names = set(meta(b'fevent_names'))
self.individual_event_names = set(meta(b'pevent_names'))
self.family_attributes = set(meta(b'fattr_names'))
self.individual_attributes = set(meta(b'pattr_names'))
self.marker_names = set(meta(b'marker_names'))
self.child_ref_types = set(meta(b'child_refs'))
self.family_rel_types = set(meta(b'family_rels'))
self.event_role_names = set(meta(b'event_roles'))
self.name_types = set(meta(b'name_types'))
self.origin_types = set(meta(b'origin_types'))
self.repository_types = set(meta(b'repo_types'))
self.note_types = set(meta(b'note_types'))
self.source_media_types = set(meta(b'sm_types'))
self.url_types = set(meta(b'url_types'))
self.media_attributes = set(meta(b'mattr_names'))
self.family_event_names = set(meta('fevent_names'))
self.individual_event_names = set(meta('pevent_names'))
self.family_attributes = set(meta('fattr_names'))
self.individual_attributes = set(meta('pattr_names'))
self.marker_names = set(meta('marker_names'))
self.child_ref_types = set(meta('child_refs'))
self.family_rel_types = set(meta('family_rels'))
self.event_role_names = set(meta('event_roles'))
self.name_types = set(meta('name_types'))
self.origin_types = set(meta('origin_types'))
self.repository_types = set(meta('repo_types'))
self.note_types = set(meta('note_types'))
self.source_media_types = set(meta('sm_types'))
self.url_types = set(meta('url_types'))
self.media_attributes = set(meta('mattr_names'))
# surname list
self.surname_list = meta(b'surname_list')
self.surname_list = meta('surname_list')
def __connect_secondary(self):
"""
@ -706,6 +703,8 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
("rid_trans", RIDTRANS, db.DB_HASH, 0),
("nid_trans", NIDTRANS, db.DB_HASH, 0),
("tag_trans", TAGTRANS, db.DB_HASH, 0),
("parents", LPARENTS, db.DB_HASH, 0),
("names", LNAMES, db.DB_BTREE, db.DB_DUPSORT),
("reference_map_primary_map", REF_PRI, db.DB_BTREE, 0),
("reference_map_referenced_map", REF_REF, db.DB_BTREE, db.DB_DUPSORT),
]
@ -729,6 +728,8 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
(self.repository_map, self.rid_trans, find_idmap),
(self.note_map, self.nid_trans, find_idmap),
(self.tag_map, self.tag_trans, find_idmap),
(self.location_map, self.parents, find_parent),
(self.location_map, self.names, find_name),
(self.reference_map, self.reference_map_primary_map,
find_primary_handle),
(self.reference_map, self.reference_map_referenced_map,
@ -770,6 +771,8 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
( self.nid_trans, NIDTRANS ),
( self.cid_trans, CIDTRANS ),
( self.tag_trans, TAGTRANS ),
( self.parents, LPARENTS ),
( self.names, LNAMES ),
( self.reference_map_primary_map, REF_PRI),
( self.reference_map_referenced_map, REF_REF),
]
@ -796,6 +799,60 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
if callback:
callback(12)
@catch_db_error
def find_location_child_handles(self, handle):
"""
"""
handle = str(handle)
parent_cur = self.get_location_parent_cursor()
try:
ret = parent_cur.set(handle)
except:
ret = None
while (ret is not None):
(key, data) = ret
### FIXME: this is a dirty hack that works without no
### sensible explanation. For some reason, for a readonly
### database, secondary index returns a primary table key
### corresponding to the data, not the data.
if self.readonly:
data = self.location_map.get(data)
else:
data = pickle.loads(data)
yield data[0]
ret = parent_cur.next_dup()
parent_cur.close()
@catch_db_error
def find_location_from_name(self, name):
"""
"""
name = str(name).upper()
size = len(name)
name_cur = self.get_location_name_cursor()
try:
ret = name_cur.set_range(name)
ret = name_cur.current()
except:
ret = None
while (ret is not None):
(key, data) = ret
if key[:size] != name:
break
yield data[0]
ret = name_cur.next()
name_cur.close()
@catch_db_error
def find_backlink_handles(self, handle, include_classes=None):
"""
@ -814,8 +871,8 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
result_list = list(find_backlink_handles(handle))
"""
if isinstance(handle, UNITYPE):
handle = handle.encode('utf-8')
handle = str(handle)
# Use the secondary index to locate all the reference_map entries
# that include a reference to the object we are looking for.
referenced_cur = self.get_reference_map_referenced_cursor()
@ -949,34 +1006,25 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
Remove the reference specified by the key, preserving the change in
the passed transaction.
"""
if isinstance(key, tuple):
#create a string key
key = str(key)
if isinstance(key, UNITYPE):
key = key.encode('utf-8')
if not self.readonly:
if not transaction.batch:
old_data = self.reference_map.get(key, txn=txn)
transaction.add(REFERENCE_KEY, TXNDEL, key, old_data, None)
old_data = self.reference_map.get(str(key), txn=txn)
transaction.add(REFERENCE_KEY, TXNDEL, str(key), old_data, None)
#transaction.reference_del.append(str(key))
self.reference_map.delete(key, txn=txn)
self.reference_map.delete(str(key), txn=txn)
def __add_reference(self, key, data, transaction, txn):
"""
Add the reference specified by the key and the data, preserving the
change in the passed transaction.
"""
if isinstance(key, tuple):
#create a string key
key = str(key)
if isinstance(key, UNITYPE):
key = key.encode('utf-8')
if self.readonly or not key:
return
self.reference_map.put(key, data, txn=txn)
self.reference_map.put(str(key), data, txn=txn)
if not transaction.batch:
transaction.add(REFERENCE_KEY, TXNADD, key, None, data)
transaction.add(REFERENCE_KEY, TXNADD, str(key), None, data)
#transaction.reference_add.append((str(key), data))
@catch_db_error
@ -1063,45 +1111,45 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
with BSDDBTxn(self.env, self.metadata) as txn:
# name display formats
txn.put(b'name_formats', self.name_formats)
txn.put('name_formats', self.name_formats)
# database owner
owner_data = self.owner.serialize()
txn.put(b'researcher', owner_data)
txn.put('researcher', owner_data)
# bookmarks
txn.put(b'bookmarks', self.bookmarks.get())
txn.put(b'family_bookmarks', self.family_bookmarks.get())
txn.put(b'event_bookmarks', self.event_bookmarks.get())
txn.put(b'source_bookmarks', self.source_bookmarks.get())
txn.put(b'citation_bookmarks', self.citation_bookmarks.get())
txn.put(b'place_bookmarks', self.place_bookmarks.get())
txn.put(b'repo_bookmarks', self.repo_bookmarks.get())
txn.put(b'media_bookmarks', self.media_bookmarks.get())
txn.put(b'note_bookmarks', self.note_bookmarks.get())
txn.put('bookmarks', self.bookmarks.get())
txn.put('family_bookmarks', self.family_bookmarks.get())
txn.put('event_bookmarks', self.event_bookmarks.get())
txn.put('source_bookmarks', self.source_bookmarks.get())
txn.put('citation_bookmarks', self.citation_bookmarks.get())
txn.put('place_bookmarks', self.place_bookmarks.get())
txn.put('repo_bookmarks', self.repo_bookmarks.get())
txn.put('media_bookmarks', self.media_bookmarks.get())
txn.put('note_bookmarks', self.note_bookmarks.get())
# gender stats
txn.put(b'gender_stats', self.genderStats.save_stats())
txn.put('gender_stats', self.genderStats.save_stats())
# Custom type values
txn.put(b'fevent_names', list(self.family_event_names))
txn.put(b'pevent_names', list(self.individual_event_names))
txn.put(b'fattr_names', list(self.family_attributes))
txn.put(b'pattr_names', list(self.individual_attributes))
txn.put(b'marker_names', list(self.marker_names))
txn.put(b'child_refs', list(self.child_ref_types))
txn.put(b'family_rels', list(self.family_rel_types))
txn.put(b'event_roles', list(self.event_role_names))
txn.put(b'name_types', list(self.name_types))
txn.put(b'origin_types', list(self.origin_types))
txn.put(b'repo_types', list(self.repository_types))
txn.put(b'note_types', list(self.note_types))
txn.put(b'sm_types', list(self.source_media_types))
txn.put(b'url_types', list(self.url_types))
txn.put(b'mattr_names', list(self.media_attributes))
txn.put('fevent_names', list(self.family_event_names))
txn.put('pevent_names', list(self.individual_event_names))
txn.put('fattr_names', list(self.family_attributes))
txn.put('pattr_names', list(self.individual_attributes))
txn.put('marker_names', list(self.marker_names))
txn.put('child_refs', list(self.child_ref_types))
txn.put('family_rels', list(self.family_rel_types))
txn.put('event_roles', list(self.event_role_names))
txn.put('name_types', list(self.name_types))
txn.put('origin_types', list(self.origin_types))
txn.put('repo_types', list(self.repository_types))
txn.put('note_types', list(self.note_types))
txn.put('sm_types', list(self.source_media_types))
txn.put('url_types', list(self.url_types))
txn.put('mattr_names', list(self.media_attributes))
# name display formats
txn.put(b'surname_list', self.surname_list)
txn.put('surname_list', self.surname_list)
self.metadata.close()
@ -1143,6 +1191,8 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
self.cid_trans.close()
self.pid_trans.close()
self.tag_trans.close()
self.parents.close()
self.names.close()
self.reference_map_primary_map.close()
self.reference_map_referenced_map.close()
self.reference_map.close()
@ -1160,6 +1210,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
self.media_map.close()
self.event_map.close()
self.tag_map.close()
self.location_map.close()
self.env.close()
self.__close_undodb()
@ -1173,6 +1224,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
self.media_map = None
self.event_map = None
self.tag_map = None
self.location_map = None
self.surnames = None
self.env = None
self.metadata = None
@ -1191,6 +1243,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
self.media_map = None
self.event_map = None
self.tag_map = None
self.location_map = None
self.reference_map_primary_map = None
self.reference_map_referenced_map = None
self.reference_map = None
@ -1203,14 +1256,9 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
versionpath = os.path.join(self.path, BDBVERSFN)
try:
with open(versionpath, "w") as version_file:
version = str(db.version())
if sys.version_info[0] < 3:
if isinstance(version, UNITYPE):
version = version.encode('utf-8')
version_file.write(version)
version_file.write(str(db.version()))
except:
# Storing the version of Berkeley Db is not really vital.
print ("Error storing berkeley db version")
pass
try:
@ -1220,7 +1268,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
def create_id(self):
return "%08x%08x" % ( int(time.time()*10000),
self.rand.randint(0, maxsize))
self.rand.randint(0, maxint))
def __add_object(self, obj, transaction, find_next_func, commit_func):
if find_next_func and not obj.gramps_id:
@ -1356,12 +1404,18 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
"""
return self.__add_object(obj, transaction, None, self.commit_tag)
def add_location(self, obj, transaction):
"""
Add a Location to the database, assigning a handle if it has not already
been defined.
"""
return self.__add_object(obj, transaction, None, self.commit_location)
def __do_remove(self, handle, transaction, data_map, key):
if self.readonly or not handle:
return
if isinstance(handle, UNITYPE):
handle = handle.encode('utf-8')
handle = str(handle)
if transaction.batch:
with BSDDBTxn(self.env, data_map) as txn:
self.delete_primary_from_reference_map(handle, transaction,
@ -1385,8 +1439,6 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
person = self.get_person_from_handle(handle)
self.genderStats.uncount_person (person)
self.remove_from_surname_list(person)
if isinstance(handle, UNITYPE):
handle = handle.encode('utf-8')
if transaction.batch:
with BSDDBTxn(self.env, self.person_map) as txn:
self.delete_primary_from_reference_map(handle, transaction,
@ -1395,7 +1447,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
else:
self.delete_primary_from_reference_map(handle, transaction,
txn=self.txn)
self.person_map.delete(handle, txn=self.txn)
self.person_map.delete(str(handle), txn=self.txn)
transaction.add(PERSON_KEY, TXNDEL, handle, person.serialize(), None)
def remove_source(self, handle, transaction):
@ -1470,6 +1522,14 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
self.__do_remove(handle, transaction, self.tag_map,
TAG_KEY)
def remove_location(self, handle, transaction):
"""
Remove the Location specified by the database handle from the
database, preserving the change in the passed transaction.
"""
self.__do_remove(handle, transaction, self.location_map,
LOCATION_KEY)
@catch_db_error
def set_name_group_mapping(self, name, group):
if not self.readonly:
@ -1482,7 +1542,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
if group is not None:
txn.put(sname, group)
if group == None:
grouppar = ''
grouppar = u''
else:
grouppar = group
self.emit('person-groupname-rebuild', (name, grouppar))
@ -1508,7 +1568,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
"""
if batch_transaction:
return
name = conv_to_unicode(find_surname_name(person.handle,
name = unicode(find_surname_name(person.handle,
person.get_primary_name().serialize()), 'utf-8')
i = bisect.bisect(self.surname_list, name)
if 0 < i <= len(self.surname_list):
@ -1528,18 +1588,11 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
"""
name = find_surname_name(person.handle,
person.get_primary_name().serialize())
if sys.version_info[0] < 3:
if isinstance(name, unicode):
uname = name
name = str(name)
else:
uname = unicode(name, 'utf-8')
if isinstance(name, unicode):
uname = name
name = str(name)
else:
if isinstance(name, str):
uname = name
name = name.encode('utf-8')
else:
uname = str(name)
uname = unicode(name, 'utf-8')
try:
cursor = self.surnames.cursor(txn=self.txn)
cursor_position = cursor.set(name)
@ -1548,7 +1601,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
i = bisect.bisect(self.surname_list, uname)
if 0 <= i-1 < len(self.surname_list):
del self.surname_list[i-1]
except db.DBError as err:
except db.DBError, err:
if str(err) == "(0, 'DB object has been closed')":
pass # A batch transaction closes the surnames db table.
else:
@ -1566,9 +1619,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
return
obj.change = int(change_time or time.time())
handle = obj.handle
if isinstance(handle, UNITYPE):
handle = handle.encode('utf-8')
handle = str(obj.handle)
self.update_reference_map(obj, transaction, self.txn)
@ -1790,11 +1841,17 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
self.commit_base(tag, self.tag_map, TAG_KEY,
transaction, change_time)
def commit_location(self, location, transaction, change_time=None):
"""
Commit the specified Location to the database, storing the changes as
part of the transaction.
"""
self.commit_base(location, self.location_map, LOCATION_KEY,
transaction, change_time)
def get_from_handle(self, handle, class_type, data_map):
if isinstance(handle, UNITYPE):
handle = handle.encode('utf-8')
try:
data = data_map.get(handle, txn=self.txn)
data = data_map.get(str(handle), txn=self.txn)
except:
data = None
# under certain circumstances during a database reload,
@ -1879,7 +1936,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
self.env.log_flush()
if not transaction.batch:
emit = self.__emit
for obj_type, obj_name in KEY_TO_NAME_MAP.items():
for obj_type, obj_name in KEY_TO_NAME_MAP.iteritems():
emit(transaction, obj_type, TXNADD, obj_name, '-add')
emit(transaction, obj_type, TXNUPD, obj_name, '-update')
emit(transaction, obj_type, TXNDEL, obj_name, '-delete')
@ -1926,7 +1983,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
# "while Gtk.events_pending(): Gtk.main_iteration() loop"
# (typically used in a progress bar), so emit rebuild signals
# to correct that.
object_types = set([x[0] for x in list(transaction.keys())])
object_types = set([x[0] for x in transaction.keys()])
for object_type in object_types:
if object_type == REFERENCE_KEY:
continue
@ -1983,11 +2040,11 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
def gramps_upgrade(self, callback=None):
UpdateCallback.__init__(self, callback)
version = self.metadata.get(b'version', default=_MINVERSION)
version = self.metadata.get('version', default=_MINVERSION)
t = time.time()
from . import upgrade
import upgrade
if version < 14:
upgrade.gramps_upgrade_14(self)
if version < 15:
@ -2007,6 +2064,9 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
self.__close_undodb()
self.db_is_open = False
if version < 17:
self.__connect_secondary()
upgrade.gramps_upgrade_17(self)
_LOG.debug("Upgrade time: %d seconds" % int(time.time()-t))
@ -2060,7 +2120,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
self.metadata = self.__open_shelf(full_name, META)
with BSDDBTxn(self.env, self.metadata) as txn:
txn.put(b'version', _DBVERSION)
txn.put('version', _DBVERSION)
self.metadata.close()
self.env.close()
@ -2133,14 +2193,13 @@ if __name__ == "__main__":
db_name = f.read()
if db_name == 'Small Example':
break
print("loading", db_path)
print "loading", db_path
d.load(db_path, lambda x: x)
print(d.get_default_person())
out = ''
print d.get_default_person()
with d.get_person_cursor() as c:
for key, data in c:
person = Person(data)
out += key + person.get_primary_name().get_name()
print key, person.get_primary_name().get_name(),
print(out, list(d.surnames.keys()))
print d.surnames.keys()

View File

@ -25,25 +25,25 @@
Package providing filter rules for GRAMPS.
"""
from ._allplaces import AllPlaces
from ._hascitation import HasCitation
from ._hasgallery import HasGallery
from ._hasidof import HasIdOf
from ._regexpidof import RegExpIdOf
from ._hasnote import HasNote
from ._hasnoteregexp import HasNoteRegexp
from ._hasnotematchingsubstringof import HasNoteMatchingSubstringOf
from ._hasreferencecountof import HasReferenceCountOf
from ._hassourcecount import HasSourceCount
from ._hassourceof import HasSourceOf
from ._placeprivate import PlacePrivate
from ._matchesfilter import MatchesFilter
from ._hasplace import HasPlace
from ._hasnolatorlon import HasNoLatOrLon
from ._inlatlonneighborhood import InLatLonNeighborhood
from ._matcheseventfilter import MatchesEventFilter
from ._matchessourceconfidence import MatchesSourceConfidence
from ._changedsince import ChangedSince
from _allplaces import AllPlaces
from _hascitation import HasCitation
from _hasgallery import HasGallery
from _hasidof import HasIdOf
from _regexpidof import RegExpIdOf
from _hasnote import HasNote
from _hasnoteregexp import HasNoteRegexp
from _hasnotematchingsubstringof import HasNoteMatchingSubstringOf
from _hasreferencecountof import HasReferenceCountOf
from _hassourcecount import HasSourceCount
from _hassourceof import HasSourceOf
from _placeprivate import PlacePrivate
from _matchesfilter import MatchesFilter
from _haslocation import HasLocation
from _hasnolatorlon import HasNoLatOrLon
from _inlatlonneighborhood import InLatLonNeighborhood
from _matcheseventfilter import MatchesEventFilter
from _matchessourceconfidence import MatchesSourceConfidence
from _changedsince import ChangedSince
editor_rule_list = [
AllPlaces,
@ -60,7 +60,7 @@ editor_rule_list = [
PlacePrivate,
MatchesFilter,
MatchesSourceConfidence,
HasPlace,
HasLocation,
HasNoLatOrLon,
InLatLonNeighborhood,
MatchesEventFilter,

View File

@ -0,0 +1,63 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2012 Nick Hall
#
# 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$
#-------------------------------------------------------------------------
#
# Standard Python modules
#
#-------------------------------------------------------------------------
from ....ggettext import gettext as _
#-------------------------------------------------------------------------
#
# GRAMPS modules
#
#-------------------------------------------------------------------------
from .. import Rule
#-------------------------------------------------------------------------
#
# HasLocation
#
#-------------------------------------------------------------------------
class HasLocation(Rule):
"""Rule that checks if a Place is at a specified Location"""
labels = [ _('Location:') ]
name = _('Place at <Location>')
description = _("Matches places at a specified Location")
category = _('General filters')
def prepare(self, db):
self.children = []
to_do = [self.list[0]]
while to_do:
for child in db.find_location_child_handles(to_do.pop()):
to_do.append(child)
self.children.append(child)
def apply(self, db, obj):
"""
apply the rule on the obj.
return true if the rule passes, false otherwise.
"""
return obj.get_main_location() in self.children

View File

@ -50,7 +50,8 @@ class HasNoLatOrLon(Rule):
description = _("Matches places with empty latitude or longitude")
category = _('Position filters')
def apply(self,db,place):
if place.get_latitude().strip and place.get_longitude().strip() :
def apply(self, db, place):
location = db.get_location_from_handle(place.get_main_location())
if location.get_latitude().strip and location.get_longitude().strip():
return False
return True

View File

@ -121,6 +121,10 @@ class InLatLonNeighborhood(Rule):
def apply(self,db,place):
location = db.get_location_from_handle(place.get_main_location())
latitude = location.get_latitude().strip()
longitude = location.get_longitude().strip()
if self.halfheight == -1 and self.halfwidth ==-1 :
return False

View File

@ -30,16 +30,14 @@ Location class for GRAMPS.
# GRAMPS modules
#
#-------------------------------------------------------------------------
from .secondaryobj import SecondaryObject
from .locationbase import LocationBase
from .const import IDENTICAL, DIFFERENT
from .tableobj import TableObject
#-------------------------------------------------------------------------
#
# Location class for Places
#
#-------------------------------------------------------------------------
class Location(SecondaryObject, LocationBase):
class Location(TableObject):
"""
Provide information about a place.
@ -52,17 +50,31 @@ class Location(SecondaryObject, LocationBase):
"""
Create a Location object, copying from the source object if it exists.
"""
LocationBase.__init__(self, source)
TableObject.__init__(self, source)
if source:
self.parish = source.parish
self.parent = source.parent
self.name = source.name
self.location_type = source.location_type
self.lat = source.lat
self.long = source.long
else:
self.parish = ""
self.parent = None
self.name = ''
self.location_type = 1 # Country
self.lat = ''
self.long = ''
def serialize(self):
"""
Convert the object to a serialized tuple of data.
"""
return (LocationBase.serialize(self), self.parish)
return (self.handle,
self.parent,
self.name,
self.location_type,
self.lat,
self.long,
self.change)
def to_struct(self):
"""
@ -98,9 +110,13 @@ class Location(SecondaryObject, LocationBase):
"""
Convert a serialized tuple of data to an object.
"""
(lbase, self.parish) = data
LocationBase.unserialize(self, lbase)
return self
(self.handle,
self.parent,
self.name,
self.location_type,
self.lat,
self.long,
self.change) = data
def get_text_data_list(self):
"""
@ -109,42 +125,102 @@ class Location(SecondaryObject, LocationBase):
:returns: Returns the list of all textual attributes of the object.
:rtype: list
"""
return [self.parish] + LocationBase.get_text_data_list(self)
def is_equivalent(self, other):
"""
Return if this location is equivalent to other.
:param other: The location to compare this one to.
:rtype other: Location
:returns: Constant inidicating degree of equivalence.
:rtype: int
"""
if self.is_equal(other):
return IDENTICAL
else:
return DIFFERENT
def merge(self, acquisition):
"""
Merge the content of acquisition into this location.
Lost: everything of acquisition.
:param acquisition: The location to merge with the present location.
:rtype acquisition: Location
"""
pass
return [self.name, self.lat, self.long]
def is_empty(self):
return not self.street and not self.locality and not self.city and \
not self.county and not self.state and not self.country and \
not self.postal and not self.phone
def set_parish(self, data):
"""Set the religious parish name."""
self.parish = data
"""
Return True if the Location is an empty object (no values set).
def get_parish(self):
"""Get the religious parish name."""
return self.parish
:returns: True if the Location is empty
:rtype: bool
"""
return self.name == ''
def are_equal(self, other):
"""
Return True if the passed Tag is equivalent to the current Location.
:param other: Location to compare against
:type other: Location
:returns: True if the Locations are equal
:rtype: bool
"""
if other is None:
other = Location()
if self.name != other.name or \
self.parent != other.parent:
return False
return True
def set_parent(self, parent):
"""
Set the parent of the Location to the passed string.
"""
self.parent = parent
def get_parent(self):
"""
Return the parent of the Location.
"""
return self.parent
def set_name(self, name):
"""
Set the name of the Location to the passed string.
"""
self.name = name
def get_name(self):
"""
Return the name of the Location.
"""
return self.name
def set_type(self, location_type):
"""
Set the type of the Location to the passed integer.
"""
self.location_type = location_type
def get_type(self):
"""
Return the type of the Location.
"""
return self.location_type
def set_latitude(self, latitude):
"""
Set the latitude of the Location object.
:param latitude: latitude to assign to the Location
:type latitude: str
"""
self.lat = latitude
def get_latitude(self):
"""
Return the latitude of the Location object.
:returns: Returns the latitude of the Location
:rtype: str
"""
return self.lat
def set_longitude(self, longitude):
"""
Set the longitude of the Location object.
:param longitude: longitude to assign to the Location
:type longitude: str
"""
self.long = longitude
def get_longitude(self):
"""
Return the longitude of the Location object.
:returns: Returns the longitude of the Location
:rtype: str
"""
return self.long

View File

@ -25,21 +25,18 @@
"""
Place object for GRAMPS.
"""
from __future__ import unicode_literals
#-------------------------------------------------------------------------
#
# GRAMPS modules
#
#-------------------------------------------------------------------------
from .primaryobj import PrimaryObject
from .citationbase import CitationBase
from .notebase import NoteBase
from .mediabase import MediaBase
from .urlbase import UrlBase
from .location import Location
_EMPTY_LOC = Location().serialize()
from primaryobj import PrimaryObject
from citationbase import CitationBase
from notebase import NoteBase
from mediabase import MediaBase
from urlbase import UrlBase
from location import Location
#-------------------------------------------------------------------------
#
@ -66,14 +63,10 @@ class Place(CitationBase, NoteBase, MediaBase, UrlBase, PrimaryObject):
MediaBase.__init__(self, source)
UrlBase.__init__(self, source)
if source:
self.long = source.long
self.lat = source.lat
self.title = source.title
self.main_loc = Location(source.main_loc)
self.alt_loc = list(map(Location, source.alt_loc))
self.main_loc = source.main_loc
self.alt_loc = source.alt_loc
else:
self.long = ""
self.lat = ""
self.title = ""
self.main_loc = None
self.alt_loc = []
@ -96,14 +89,8 @@ class Place(CitationBase, NoteBase, MediaBase, UrlBase, PrimaryObject):
be considered persistent.
:rtype: tuple
"""
if self.main_loc is None or self.main_loc.serialize() == _EMPTY_LOC:
main_loc = None
else:
main_loc = self.main_loc.serialize()
return (self.handle, self.gramps_id, self.title, self.long, self.lat,
main_loc, [al.serialize() for al in self.alt_loc],
return (self.handle, self.gramps_id, self.title,
self.main_loc, self.alt_loc,
UrlBase.serialize(self),
MediaBase.serialize(self),
CitationBase.serialize(self),
@ -158,15 +145,11 @@ class Place(CitationBase, NoteBase, MediaBase, UrlBase, PrimaryObject):
Person object
:type data: tuple
"""
(self.handle, self.gramps_id, self.title, self.long, self.lat,
main_loc, alt_loc, urls, media_list, citation_list, note_list,
(self.handle, self.gramps_id, self.title,
self.main_loc, self.alt_loc,
urls, media_list, citation_list, note_list,
self.change, self.private) = data
if main_loc is None:
self.main_loc = None
else:
self.main_loc = Location().unserialize(main_loc)
self.alt_loc = [Location().unserialize(al) for al in alt_loc]
UrlBase.unserialize(self, urls)
MediaBase.unserialize(self, media_list)
CitationBase.unserialize(self, citation_list)
@ -180,7 +163,7 @@ class Place(CitationBase, NoteBase, MediaBase, UrlBase, PrimaryObject):
:returns: Returns the list of all textual attributes of the object.
:rtype: list
"""
return [self.long, self.lat, self.title, self.gramps_id]
return [self.title, self.gramps_id]
def get_text_data_child_list(self):
"""
@ -224,6 +207,20 @@ class Place(CitationBase, NoteBase, MediaBase, UrlBase, PrimaryObject):
"""
return self.get_citation_child_list()
def get_referenced_location_handles(self):
"""
Return the list of (classname, handle) tuples for all referenced notes.
This method should be used to get the :class:`~gen.lib.note.Note` portion of the list
by objects that store note lists.
:returns: List of (classname, handle) tuples for referenced objects.
:rtype: list
"""
refs = [('Location', self.main_loc)]
refs.extend([('Location', handle) for handle in self.alt_loc])
return refs
def get_referenced_handles(self):
"""
Return the list of (classname, handle) tuples for all directly
@ -232,7 +229,8 @@ class Place(CitationBase, NoteBase, MediaBase, UrlBase, PrimaryObject):
:returns: List of (classname, handle) tuples for referenced objects.
:rtype: list
"""
return self.get_referenced_note_handles() + \
return self.get_referenced_location_handles() + \
self.get_referenced_note_handles() + \
self.get_referenced_citation_handles()
def merge(self, acquisition):
@ -266,42 +264,6 @@ class Place(CitationBase, NoteBase, MediaBase, UrlBase, PrimaryObject):
"""
return self.title
def set_longitude(self, longitude):
"""
Set the longitude of the Place object.
:param longitude: longitude to assign to the Place
:type longitude: str
"""
self.long = longitude
def get_longitude(self):
"""
Return the longitude of the Place object.
:returns: Returns the longitude of the Place
:rtype: str
"""
return self.long
def set_latitude(self, latitude):
"""
Set the latitude of the Place object.
:param latitude: latitude to assign to the Place
:type latitude: str
"""
self.lat = latitude
def get_latitude(self):
"""
Return the latitude of the Place object.
:returns: Returns the latitude of the Place
:rtype: str
"""
return self.lat
def get_main_location(self):
"""
Return the :class:`~gen.lib.location.Location` object representing the primary information for
@ -313,8 +275,6 @@ class Place(CitationBase, NoteBase, MediaBase, UrlBase, PrimaryObject):
location information about the Place.
:rtype: :class:`~gen.lib.location.Location`
"""
if not self.main_loc:
self.main_loc = Location()
return self.main_loc
def set_main_location(self, location):
@ -382,28 +342,3 @@ class Place(CitationBase, NoteBase, MediaBase, UrlBase, PrimaryObject):
break
else:
self.alt_loc.append(addendum)
def get_display_info(self):
"""
Get the display information associated with the object.
This includes the information that is used for display and for sorting.
Returns a list consisting of 13 strings. These are:
Place Title, Place ID, Main Location Parish, Main Location County,
Main Location City, Main Location State/Province,
Main Location Country, upper case Place Title, upper case Parish,
upper case city, upper case county, upper case state,
upper case country.
"""
if self.main_loc:
return [self.title, self.gramps_id, self.main_loc.parish,
self.main_loc.city, self.main_loc.county,
self.main_loc.state, self.main_loc.country,
self.title.upper(), self.main_loc.parish.upper(),
self.main_loc.city.upper(), self.main_loc.county.upper(),
self.main_loc.state.upper(), self.main_loc.country.upper()]
else:
return [self.title, self.gramps_id, '', '', '', '', '',
self.title.upper(), '', '', '', '', '']

View File

@ -61,7 +61,6 @@ from gramps.gen.db.exceptions import (DbUpgradeRequiredError,
BsddbDowngradeError,
DbVersionError,
DbEnvironmentError)
from gramps.gen.constfunc import STRTYPE
from gramps.gen.utils.file import get_unicode_path_from_file_chooser
from .pluginmanager import GuiPluginManager
from .dialog import (DBErrorDialog, ErrorDialog, QuestionDialog2,
@ -133,13 +132,11 @@ class DbLoader(CLIDbLoader):
pmgr = GuiPluginManager.get_instance()
import_dialog = Gtk.FileChooserDialog(_('Gramps: Import Family Tree'),
import_dialog = Gtk.FileChooserDialog(_('Gramps: Import database'),
self.uistate.window,
Gtk.FileChooserAction.OPEN,
(Gtk.STOCK_CANCEL,
Gtk.ResponseType.CANCEL,
_('Import'),
Gtk.ResponseType.OK))
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
'gramps-import', Gtk.ResponseType.OK))
import_dialog.set_local_only(False)
# Always add automatic (match all files) filter
@ -208,7 +205,8 @@ class DbLoader(CLIDbLoader):
In this process, a warning dialog can pop up.
"""
if not isinstance(filename, STRTYPE):
if not isinstance(filename, basestring):
return True
filename = os.path.normpath(os.path.abspath(filename))
@ -251,7 +249,7 @@ class DbLoader(CLIDbLoader):
User(callback=self._pulse_progress))
dirname = os.path.dirname(filename) + os.path.sep
config.set('paths.recent-import-dir', dirname)
except UnicodeError as msg:
except UnicodeError, msg:
ErrorDialog(
_("Could not import file: %s") % filename,
_("This file incorrectly identifies its character "
@ -268,7 +266,7 @@ class DbLoader(CLIDbLoader):
is returned
"""
if self.import_info is None:
return ""
return u""
return self.import_info.info_text()
def read_file(self, filename):
@ -308,12 +306,12 @@ class DbLoader(CLIDbLoader):
db.load(filename, self._pulse_progress,
mode, upgrade=False)
self.dbstate.change_database(db)
except DbUpgradeRequiredError as msg:
except DbUpgradeRequiredError, msg:
if QuestionDialog2(_("Need to upgrade database!"),
str(msg),
_("Upgrade now"),
_("Cancel")).run():
db = gen.db.DbBsddb()
db = DbBsddb()
db.disable_signals()
db.load(filename, self._pulse_progress,
mode, upgrade=True)
@ -321,20 +319,20 @@ class DbLoader(CLIDbLoader):
self.dbstate.change_database(db)
else:
self.dbstate.no_database()
except BsddbDowngradeError as msg:
except BsddbDowngradeError, msg:
self.dbstate.no_database()
self._errordialog( _("Cannot open database"), str(msg))
except DbVersionError as msg:
except DbVersionError, msg:
self.dbstate.no_database()
self._errordialog( _("Cannot open database"), str(msg))
except DbEnvironmentError as msg:
except DbEnvironmentError, msg:
self.dbstate.no_database()
self._errordialog( _("Cannot open database"), str(msg))
except OSError as msg:
except OSError, msg:
self.dbstate.no_database()
self._errordialog(
_("Could not open file: %s") % filename, str(msg))
except DbError as msg:
except DbError, msg:
self.dbstate.no_database()
self._dberrordialog(msg)
except Exception as newerror:

View File

@ -36,8 +36,8 @@ from gi.repository import GObject
from gramps.gen.lib import Location
from gramps.gen.errors import WindowActiveError
from ...ddtargets import DdTargets
from .locationmodel import LocationModel
from .embeddedlist import EmbeddedList
from locationmodel import LocationModel
from embeddedlist import EmbeddedList
#-------------------------------------------------------------------------
#
@ -46,18 +46,13 @@ from .embeddedlist import EmbeddedList
#-------------------------------------------------------------------------
class LocationEmbedList(EmbeddedList):
_HANDLE_COL = 6
_HANDLE_COL = 1
_DND_TYPE = DdTargets.LOCATION
#index = column in model. Value =
# (name, sortcol in model, width, markup/text, weigth_col
_column_names = [
(_('Street'), 0, 150, 0, -1),
(_('Locality'), 1, 100, 0, -1),
(_('City'), 2, 100, 0, -1),
(_('County'), 3, 100, 0, -1),
(_('State'), 4, 100, 0, -1),
(_('Country'), 5, 75, 0, -1),
(_('Location'), 0, 300, 0, -1),
]
def __init__(self, dbstate, uistate, track, data):
@ -70,7 +65,7 @@ class LocationEmbedList(EmbeddedList):
return self.data
def column_order(self):
return ((1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (1, 5))
return ((1, 0),)
def add_button_clicked(self, obj):
loc = Location()
@ -81,15 +76,16 @@ class LocationEmbedList(EmbeddedList):
except WindowActiveError:
pass
def add_callback(self, name):
def add_callback(self, location):
data = self.get_data()
data.append(name)
data.append(location.handle)
self.rebuild()
GObject.idle_add(self.tree.scroll_to_cell, len(data) - 1)
def edit_button_clicked(self, obj):
loc = self.get_selected()
if loc:
handle = self.get_selected()
if handle:
loc = self.dbstate.db.get_location_from_handle(handle)
try:
from .. import EditLocation
EditLocation(self.dbstate, self.uistate, self.track,

View File

@ -41,9 +41,19 @@ from gi.repository import Gtk
#-------------------------------------------------------------------------
class LocationModel(Gtk.ListStore):
def __init__(self, obj_list, db):
Gtk.ListStore.__init__(self, str, str, str, str, str, str, object)
def __init__(self, location_list, db):
Gtk.ListStore.__init__(self, str, object)
self.db = db
for obj in obj_list:
self.append(row=[obj.street, obj.locality, obj.city, obj.county,
obj.state, obj.country, obj, ])
for handle in location_list:
self.__add_location(handle)
def __add_location(self, handle):
"""
Append a location to the model.
"""
loc = self.db.get_location_from_handle(handle)
lines = [loc.name]
while loc.parent is not None:
loc = self.db.get_location_from_handle(loc.parent)
lines.append(loc.name)
self.append(row=[', '.join(lines), handle])

View File

@ -27,10 +27,22 @@
# gramps modules
#
#-------------------------------------------------------------------------
from .editsecondary import EditSecondary
from gramps.gen.db import DbTxn
from editsecondary import EditSecondary
from ..glade import Glade
from ..widgets import MonitoredEntry
from gramps.gen.errors import ValidationError
from gramps.gen.utils.place import conv_lat_lon
from ..widgets import MonitoredEntry, LocationEntry
from gramps.gen.ggettext import gettext as _
from ..selectors import SelectorFactory
from ..dialog import ErrorDialog
SelectLocation = SelectorFactory('Location')
from gi.repository import Gtk
LOCATIONTYPES = [_('Country'), _('State'), _('County'), _('City'),
_('Parish'), _('Locality'), _('Street')]
#-------------------------------------------------------------------------
#
@ -47,72 +59,148 @@ class EditLocation(EditSecondary):
self.width_key = 'interface.location-width'
self.height_key = 'interface.location-height'
self.top = Glade()
self.set_window(self.top.toplevel, None,
_('Location Editor'))
self.set_window(self.top.toplevel, None, _('Location Editor'))
def _setup_fields(self):
self.street = MonitoredEntry(
self.top.get_object("street"),
self.obj.set_street,
self.obj.get_street,
self.name = MonitoredEntry(self.top.get_object("entry1"),
self.obj.set_name,
self.obj.get_name,
self.db.readonly)
self.longitude = MonitoredEntry(
self.top.get_object("lon_entry"),
self.obj.set_longitude, self.obj.get_longitude,
self.db.readonly)
self.longitude.connect("validate", self._validate_coordinate, "lon")
#force validation now with initial entry
self.top.get_object("lon_entry").validate(force=True)
self.locality = MonitoredEntry(
self.top.get_object("locality"),
self.obj.set_locality,
self.obj.get_locality,
self.latitude = MonitoredEntry(
self.top.get_object("lat_entry"),
self.obj.set_latitude, self.obj.get_latitude,
self.db.readonly)
self.latitude.connect("validate", self._validate_coordinate, "lat")
#force validation now with initial entry
self.top.get_object("lat_entry").validate(force=True)
self.loc_type = self.top.get_object('combobox1')
active_iter = None
model = Gtk.ListStore(int, str)
for key, value in enumerate(LOCATIONTYPES):
_iter = model.append((key+1, value))
if key+1 == self.obj.get_type():
active_iter = _iter
self.loc_type.set_model(model)
cell = Gtk.CellRendererText()
self.loc_type.pack_start(cell, True)
self.loc_type.add_attribute(cell, 'text', 1)
if active_iter is not None:
self.loc_type.set_active_iter(active_iter)
self.city = MonitoredEntry(
self.top.get_object("city"),
self.obj.set_city,
self.obj.get_city,
self.db.readonly)
self.state = MonitoredEntry(
self.top.get_object("state"),
self.obj.set_state,
self.obj.get_state,
self.db.readonly)
self.postal = MonitoredEntry(
self.top.get_object("postal"),
self.obj.set_postal_code,
self.obj.get_postal_code,
self.db.readonly)
self.phone = MonitoredEntry(
self.top.get_object("phone"),
self.obj.set_phone,
self.obj.get_phone,
self.db.readonly)
self.parish = MonitoredEntry(
self.top.get_object("parish"),
self.obj.set_parish,
self.obj.get_parish,
self.db.readonly)
self.county = MonitoredEntry(
self.top.get_object("county"),
self.obj.set_county,
self.obj.get_county,
self.db.readonly)
self.country = MonitoredEntry(
self.top.get_object("country"),
self.obj.set_country,
self.obj.get_country,
self.db.readonly)
self.parent = self.top.get_object('label4')
parent_loc = self.db.get_location_from_handle(self.obj.parent)
if parent_loc:
self.parent.set_text(parent_loc.get_name())
else:
self.parent.set_text(_('None'))
button = self.top.get_object('button1')
button.connect('clicked', self.select_parent)
self.sibling_names = []
for handle in self.db.find_location_child_handles(self.obj.parent):
location = self.db.get_location_from_handle(handle)
name = location.get_name()
if name != self.obj.get_name():
self.sibling_names.append(name)
def _validate_coordinate(self, widget, text, typedeg):
if (typedeg == 'lat') and not conv_lat_lon(text, "0", "ISO-D"):
return ValidationError(_(u"Invalid latitude (syntax: 18\u00b09'") +
_('48.21"S, -18.2412 or -18:9:48.21)'))
elif (typedeg == 'lon') and not conv_lat_lon("0", text, "ISO-D"):
return ValidationError(_(u"Invalid longitude (syntax: 18\u00b09'") +
_('48.21"E, -18.2412 or -18:9:48.21)'))
def select_parent(self, button):
skip_list = []
sel = SelectLocation(self.dbstate, self.uistate, self.track)
parent = sel.run()
if parent:
self.parent.set_text(parent.get_name())
self.obj.parent = parent.get_handle()
def _connect_signals(self):
self.ok_button = self.top.get_object('button118')
self.define_cancel_button(self.top.get_object('button119'))
self.define_ok_button(self.top.get_object('button118'),self.save)
self.define_ok_button(self.ok_button, self.save)
self.define_help_button(self.top.get_object('button128'))
def save(self,*obj):
self.ok_button.set_sensitive(False)
model = self.loc_type.get_model()
loc_type = model.get_value(self.loc_type.get_active_iter(), 0)
self.obj.set_type(loc_type)
if self.obj.get_name().strip() == '':
msg1 = _("Cannot save location. Name not entered.")
msg2 = _("You must enter a name before saving.'")
ErrorDialog(msg1, msg2)
self.ok_button.set_sensitive(True)
return
if self.obj.get_name() in self.sibling_names:
msg1 = _("Cannot save location. Name already exists.")
msg2 = _("You have attempted to use a name that is already "
"used at this level in the location hierarchy.'")
ErrorDialog(msg1, msg2)
self.ok_button.set_sensitive(True)
return
if not self.obj.handle:
with DbTxn(_("Add Location (%s)") % self.obj.get_name(),
self.db) as trans:
self.db.add_location(self.obj, trans)
else:
orig = self.db.get_location_from_handle(self.obj.handle)
if self.obj.parent != orig.parent:
# Location has moved in the tree
for handle in self.db.find_location_child_handles(self.obj.parent):
location = self.db.get_location_from_handle(handle)
name = location.get_name()
if name == self.obj.get_name():
with DbTxn(_("Merge Location (%s)") % self.obj.get_name(),
self.db) as trans:
self.merge(location, self.obj, trans)
if cmp(self.obj.serialize(), orig.serialize()):
with DbTxn(_("Edit Location (%s)") % self.obj.get_name(),
self.db) as trans:
self.db.commit_location(self.obj, trans)
if self.callback:
self.callback(self.obj)
self.close()
def merge(self, location1, location2, trans):
"""
Merge location2 into location1.
"""
children = {}
for handle in self.db.find_location_child_handles(location1.handle):
child = self.db.get_location_from_handle(handle)
children[child.get_name()] = child.handle
for handle in self.db.find_location_child_handles(location2.handle):
child2 = self.db.get_location_from_handle(handle)
if child2.get_name() in children:
handle = children[child2.get_name()]
child1 = self.db.get_location_from_handle(handle)
self.merge(child1, child2, trans)
else:
child2.parent = location1.handle
self.db.commit_location(child2, trans)
self.db.remove_location(location2.handle, trans)

View File

@ -28,8 +28,6 @@
# python modules
#
#-------------------------------------------------------------------------
from __future__ import unicode_literals
from gramps.gen.ggettext import gettext as _
import logging
log = logging.getLogger(".")
@ -48,12 +46,10 @@ from gi.repository import Gtk
#-------------------------------------------------------------------------
from gramps.gen.lib import NoteType, Place
from gramps.gen.db import DbTxn
from .editprimary import EditPrimary
from .displaytabs import (GrampsTab, LocationEmbedList, CitationEmbedList,
from editprimary import EditPrimary
from displaytabs import (GrampsTab, LocationEmbedList, CitationEmbedList,
GalleryTab, NoteTab, WebEmbedList, PlaceBackRefList)
from ..widgets import MonitoredEntry, PrivacyButton
from gramps.gen.errors import ValidationError
from gramps.gen.utils.place import conv_lat_lon
from ..widgets import MonitoredEntry, PrivacyButton, LocationEntry
from ..dialog import ErrorDialog
from ..glade import Glade
@ -123,12 +119,12 @@ class EditPlace(EditPrimary):
self.set_window(self.top.toplevel, None,
self.get_menu_title())
tblmloc = self.top.get_object('table19')
self.tblmloc = self.top.get_object('loc_table')
notebook = self.top.get_object('notebook3')
#recreate start page as GrampsTab
notebook.remove_page(0)
self.mloc = MainLocTab(self.dbstate, self.uistate, self.track,
_('_Location'), tblmloc)
_('_Location'), self.tblmloc)
self.track_ref_for_deletion("mloc")
@ -154,24 +150,11 @@ class EditPlace(EditPrimary):
self._add_db_signal('place-delete', self.check_for_close)
def _setup_fields(self):
mloc = self.obj.get_main_location()
self.title = MonitoredEntry(self.top.get_object("place_title"),
self.obj.set_title, self.obj.get_title,
self.db.readonly)
self.street = MonitoredEntry(self.top.get_object("street"),
mloc.set_street, mloc.get_street,
self.db.readonly)
self.locality = MonitoredEntry(self.top.get_object("locality"),
mloc.set_locality, mloc.get_locality,
self.db.readonly)
self.city = MonitoredEntry(self.top.get_object("city"),
mloc.set_city, mloc.get_city,
self.db.readonly)
self.gid = MonitoredEntry(self.top.get_object("gid"),
self.obj.set_gramps_id,
self.obj.get_gramps_id, self.db.readonly)
@ -179,54 +162,6 @@ class EditPlace(EditPrimary):
self.privacy = PrivacyButton(self.top.get_object("private"), self.obj,
self.db.readonly)
self.parish = MonitoredEntry(self.top.get_object("parish"),
mloc.set_parish, mloc.get_parish,
self.db.readonly)
self.county = MonitoredEntry(self.top.get_object("county"),
mloc.set_county, mloc.get_county,
self.db.readonly)
self.state = MonitoredEntry(self.top.get_object("state"),
mloc.set_state, mloc.get_state,
self.db.readonly)
self.phone = MonitoredEntry(self.top.get_object("phone"),
mloc.set_phone, mloc.get_phone,
self.db.readonly)
self.postal = MonitoredEntry(self.top.get_object("postal"),
mloc.set_postal_code,
mloc.get_postal_code, self.db.readonly)
self.country = MonitoredEntry(self.top.get_object("country"),
mloc.set_country, mloc.get_country,
self.db.readonly)
self.longitude = MonitoredEntry(
self.top.get_object("lon_entry"),
self.obj.set_longitude, self.obj.get_longitude,
self.db.readonly)
self.longitude.connect("validate", self._validate_coordinate, "lon")
#force validation now with initial entry
self.top.get_object("lon_entry").validate(force=True)
self.latitude = MonitoredEntry(
self.top.get_object("lat_entry"),
self.obj.set_latitude, self.obj.get_latitude,
self.db.readonly)
self.latitude.connect("validate", self._validate_coordinate, "lat")
#force validation now with initial entry
self.top.get_object("lat_entry").validate(force=True)
def _validate_coordinate(self, widget, text, typedeg):
if (typedeg == 'lat') and not conv_lat_lon(text, "0", "ISO-D"):
return ValidationError(_("Invalid latitude (syntax: 18\u00b09'") +
_('48.21"S, -18.2412 or -18:9:48.21)'))
elif (typedeg == 'lon') and not conv_lat_lon("0", text, "ISO-D"):
return ValidationError(_("Invalid longitude (syntax: 18\u00b09'") +
_('48.21"E, -18.2412 or -18:9:48.21)'))
def build_menu_names(self, place):
return (_('Edit Place'), self.get_menu_title())
@ -240,6 +175,9 @@ class EditPlace(EditPrimary):
self._add_tab(notebook, self.mloc)
handle = self.obj.get_main_location()
self.lentry = LocationEntry(self.tblmloc, self.dbstate.db, handle)
self.loc_list = LocationEmbedList(self.dbstate,
self.uistate,
self.track,
@ -311,6 +249,16 @@ class EditPlace(EditPrimary):
self.ok_button.set_sensitive(True)
return
handle, new_locations = self.lentry.get_result()
with DbTxn(_('Add location'), self.dbstate.db) as trans:
for loc_type, name in new_locations:
new_location = gen.lib.Location()
new_location.parent = handle
new_location.name = name
new_location.set_type(loc_type)
handle = self.dbstate.db.add_location(new_location, trans)
self.obj.set_main_location(handle)
with DbTxn('', self.db) as trans:
if not self.obj.get_handle():
self.db.add_place(self.obj, trans)

View File

@ -71,7 +71,6 @@ from ..selectors import SelectorFactory
from gramps.gen.display.name import displayer as _nd
from gramps.gen.utils.db import family_name
from gramps.gen.utils.string import confidence
from gramps.gen.constfunc import cuni
#-------------------------------------------------------------------------
#
@ -107,6 +106,14 @@ _name2typeclass = {
_('Surname origin type:'): NameOriginType,
}
#-------------------------------------------------------------------------
#
# Sorting function for the filter rules
#
#-------------------------------------------------------------------------
def by_rule_name(f, s):
return cmp(f.name, s.name)
#-------------------------------------------------------------------------
#
# MyBoolean - check button with standard interface
@ -330,7 +337,7 @@ class MyID(Gtk.Box):
self.set_text(val.get_gramps_id())
def get_text(self):
return cuni(self.entry.get_text())
return unicode(self.entry.get_text())
def name_from_gramps_id(self, gramps_id):
if self.namespace == 'Person':
@ -422,6 +429,25 @@ class MyEntry(Gtk.Entry):
GObject.GObject.__init__(self)
self.show()
#-------------------------------------------------------------------------
#
# MyLocation
#
#-------------------------------------------------------------------------
class MyLocation(Gtk.HBox):
def __init__(self, dbstate):
Gtk.HBox.__init__(self)
self.location = LocationEntry2(dbstate)
self.pack_start(self.location)
self.show_all()
def get_text(self):
return self.location.get_handle()
def set_text(self, handle):
self.location.set_handle(handle)
#-------------------------------------------------------------------------
#
# EditRule
@ -550,8 +576,10 @@ class EditRule(ManagedWindow):
taglist = taglist + [tag.get_name() for tag in dbstate.db.iter_tags()]
t = MyList(taglist, taglist)
elif v == _('Confidence level:'):
t = MyList(list(map(str, list(range(5)))),
t = MyList(map(str, range(5)),
[confidence[i] for i in range(5)])
elif v == _('Location:'):
t = MyLocation(dbstate)
else:
t = MyEntry()
tlist.append(t)
@ -589,7 +617,7 @@ class EditRule(ManagedWindow):
else:
self.sel_class = None
keys = sorted(the_map, key=lambda x: x.name, reverse=True)
keys = sorted(the_map, by_rule_name, reverse=True)
catlist = sorted(set(class_obj.category for class_obj in keys))
for category in catlist:
@ -614,7 +642,7 @@ class EditRule(ManagedWindow):
self.notebook.set_current_page(page)
self.display_values(self.active_rule.__class__)
(class_obj, vallist, tlist) = self.page[page]
r = list(self.active_rule.values())
r = self.active_rule.values()
for i in range(0, min(len(tlist), len(r))):
tlist[i].set_text(r[i])
@ -701,7 +729,7 @@ class EditRule(ManagedWindow):
try:
page = self.notebook.get_current_page()
(class_obj, vallist, tlist) = self.page[page]
value_list = [cuni(sclass.get_text()) for sclass in tlist]
value_list = [unicode(sclass.get_text()) for sclass in tlist]
new_rule = class_obj(value_list)
self.update_rule(self.active_rule, new_rule)
@ -783,7 +811,7 @@ class EditFilter(ManagedWindow):
self.close()
def filter_name_changed(self, obj):
name = cuni(self.fname.get_text())
name = unicode(self.fname.get_text())
# Make sure that the name is not empty
# and not in the list of existing filters (excluding this one)
names = [filt.get_name()
@ -806,14 +834,14 @@ class EditFilter(ManagedWindow):
self.rlist.add([r.name,r.display_values()],r)
def on_ok_clicked(self, obj):
n = cuni(self.fname.get_text()).strip()
n = unicode(self.fname.get_text()).strip()
if n == '':
return
if n != self.filter.get_name():
self.uistate.emit('filter-name-changed',
(self.namespace, cuni(self.filter.get_name()), n))
(self.namespace,unicode(self.filter.get_name()), n))
self.filter.set_name(n)
self.filter.set_comment(cuni(self.comment.get_text()).strip())
self.filter.set_comment(unicode(self.comment.get_text()).strip())
for f in self.filterdb.get_filters(self.namespace)[:]:
if n == f.get_name():
self.filterdb.get_filters(self.namespace).remove(f)
@ -946,7 +974,7 @@ class ShowResults(ManagedWindow):
gid = repo.get_gramps_id()
elif self.namespace == 'Note':
note = self.db.get_note_from_handle(handle)
name = note.get().replace('\n', ' ')
name = note.get().replace(u'\n', u' ')
if len(name) > 80:
name = name[:80]+"..."
gid = note.get_gramps_id()
@ -1121,7 +1149,7 @@ class FilterEditor(ManagedWindow):
# Remove what we found
filters = self.filterdb.get_filters(space)
list(map(filters.remove, filter_set))
map(filters.remove, filter_set)
def _find_dependent_filters(self, space, gfilter, filter_set):
"""
@ -1137,7 +1165,7 @@ class FilterEditor(ManagedWindow):
if the_filter.get_name() == name:
continue
for rule in the_filter.get_rules():
values = list(rule.values())
values = rule.values()
if issubclass(rule.__class__, MatchesFilterBase) \
and (name in values):
self._find_dependent_filters(space, the_filter, filter_set)
@ -1175,7 +1203,7 @@ class FilterEditor(ManagedWindow):
for the_filter in self.filterdb.get_filters(space):
for rule in the_filter.get_rules():
values = list(rule.values())
values = rule.values()
if issubclass(rule.__class__, MatchesFilterBase) \
and (old_name in values):
ind = values.index(old_name)
@ -1184,7 +1212,7 @@ class FilterEditor(ManagedWindow):
def check_recursive_filters(self, space, name):
for the_filter in self.filterdb.get_filters(space):
for rule in the_filter.get_rules():
values = list(rule.values())
values = rule.values()
if issubclass(rule.__class__, MatchesFilterBase) \
and (name in values):
return True

View File

@ -44,9 +44,8 @@ from gi.repository import Gtk
from ... import widgets
from .. import build_filter_model
from . import SidebarFilter
from gramps.gen.constfunc import cuni
from gramps.gen.filters import GenericFilterFactory, rules
from gramps.gen.filters.rules.place import (RegExpIdOf, HasIdOf, HasPlace,
from gramps.gen.filters.rules.place import (RegExpIdOf, HasIdOf, HasLocation,
HasNoteRegexp, HasNoteMatchingSubstringOf,
MatchesFilter)
@ -63,14 +62,7 @@ class PlaceSidebarFilter(SidebarFilter):
self.filter_id = widgets.BasicEntry()
self.filter_title = widgets.BasicEntry()
self.filter_street = widgets.BasicEntry()
self.filter_locality = widgets.BasicEntry()
self.filter_city = widgets.BasicEntry()
self.filter_county = widgets.BasicEntry()
self.filter_state = widgets.BasicEntry()
self.filter_country = widgets.BasicEntry()
self.filter_zip = widgets.BasicEntry()
self.filter_parish = widgets.BasicEntry()
self.filter_location = widgets.LocationEntry2(dbstate)
self.filter_note = widgets.BasicEntry()
self.filter_regex = Gtk.CheckButton(_('Use regular expressions'))
@ -88,49 +80,28 @@ class PlaceSidebarFilter(SidebarFilter):
self.add_text_entry(_('ID'), self.filter_id)
self.add_text_entry(_('Place Name'), self.filter_title)
self.add_text_entry(_('Street'), self.filter_street)
self.add_text_entry(_('Locality'), self.filter_locality)
self.add_text_entry(_('City'), self.filter_city)
self.add_text_entry(_('County'), self.filter_county)
self.add_text_entry(_('State'), self.filter_state)
self.add_text_entry(_('Country'), self.filter_country)
self.add_text_entry(_('ZIP/Postal code'), self.filter_zip)
self.add_text_entry(_('Church parish'), self.filter_parish)
self.add_text_entry(_('Location'), self.filter_location)
self.add_text_entry(_('Note'), self.filter_note)
self.add_filter_entry(_('Custom filter'), self.generic)
self.add_regex_entry(self.filter_regex)
def clear(self, obj):
self.filter_id.set_text('')
self.filter_title.set_text('')
self.filter_street.set_text('')
self.filter_locality.set_text('')
self.filter_city.set_text('')
self.filter_county.set_text('')
self.filter_state.set_text('')
self.filter_country.set_text('')
self.filter_zip.set_text('')
self.filter_parish.set_text('')
self.filter_location.set_text('')
self.filter_note.set_text('')
self.generic.set_active(0)
def get_filter(self):
gid = cuni(self.filter_id.get_text()).strip()
title = cuni(self.filter_title.get_text()).strip()
street = cuni(self.filter_street.get_text()).strip()
locality = cuni(self.filter_locality.get_text()).strip()
city = cuni(self.filter_city.get_text()).strip()
county = cuni(self.filter_county.get_text()).strip()
state = cuni(self.filter_state.get_text()).strip()
country = cuni(self.filter_country.get_text()).strip()
zipc = cuni(self.filter_zip.get_text()).strip()
parish = cuni(self.filter_parish.get_text()).strip()
note = cuni(self.filter_note.get_text()).strip()
gid = unicode(self.filter_id.get_text()).strip()
title = unicode(self.filter_title.get_text()).strip()
location = self.filter_location.get_handle()
note = unicode(self.filter_note.get_text()).strip()
regex = self.filter_regex.get_active()
gen = self.generic.get_active() > 0
empty = not (gid or title or street or locality or city or county or
state or country or zipc or parish or note or regex or gen)
empty = not (gid or title or location or note or regex or gen)
if empty:
generic_filter = None
else:
@ -141,10 +112,10 @@ class PlaceSidebarFilter(SidebarFilter):
else:
rule = HasIdOf([gid])
generic_filter.add_rule(rule)
rule = HasPlace([title, street, locality, city, county, state,
country, zipc, parish], use_regex=regex)
generic_filter.add_rule(rule)
if location:
rule = HasLocation([location])
generic_filter.add_rule(rule)
if note:
if regex:
@ -156,7 +127,7 @@ class PlaceSidebarFilter(SidebarFilter):
if self.generic.get_active() != 0:
model = self.generic.get_model()
node = self.generic.get_active_iter()
obj = cuni(model.get_value(node, 0))
obj = unicode(model.get_value(node, 0))
rule = MatchesFilter([obj])
generic_filter.add_rule(rule)

View File

@ -17,12 +17,10 @@
<child>
<object class="GtkButton" id="button119">
<property name="label">gtk-cancel</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<property name="use_stock">True</property>
</object>
<packing>
@ -34,13 +32,11 @@
<child>
<object class="GtkButton" id="button118">
<property name="label">gtk-ok</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="has_default">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<property name="use_stock">True</property>
</object>
<packing>
@ -52,12 +48,10 @@
<child>
<object class="GtkButton" id="button128">
<property name="label">gtk-help</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<property name="use_stock">True</property>
</object>
<packing>
@ -84,7 +78,7 @@
<property name="can_focus">False</property>
<property name="border_width">12</property>
<property name="n_rows">5</property>
<property name="n_columns">4</property>
<property name="n_columns">3</property>
<property name="column_spacing">12</property>
<property name="row_spacing">6</property>
<child>
@ -92,10 +86,9 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">C_ity:</property>
<property name="label" translatable="yes">Parent:</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<property name="mnemonic_widget">city</property>
</object>
<packing>
<property name="top_attach">2</property>
@ -104,79 +97,28 @@
<property name="y_options"/>
</packing>
</child>
<child>
<object class="UndoableEntry" id="city">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">The town or city where the place is.</property>
<property name="invisible_char">●</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="label663">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">S_treet:</property>
<property name="label" translatable="yes">Name:</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<property name="mnemonic_widget">city</property>
</object>
<packing>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="label184">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Ch_urch parish:</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<property name="mnemonic_widget">parish</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="UndoableEntry" id="parish">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">Lowest clergical division of this place. Typically used for church sources that only mention the parish.</property>
<property name="invisible_char">●</property>
</object>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="label181">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Co_unty:</property>
<property name="label" translatable="yes">Latitude:</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<property name="mnemonic_widget">county</property>
</object>
<packing>
<property name="top_attach">3</property>
@ -185,64 +127,14 @@
<property name="y_options"/>
</packing>
</child>
<child>
<object class="UndoableEntry" id="county">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">Third level of place division. Eg., in the USA a county.</property>
<property name="invisible_char">●</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="label183">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_State:</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<property name="mnemonic_widget">state</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="UndoableEntry" id="state">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">Second level of place division, eg., in the USA a state, in Germany a Bundesland.</property>
<property name="invisible_char">●</property>
</object>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="label182">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Cou_ntry:</property>
<property name="label" translatable="yes">Longitude:</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<property name="mnemonic_widget">country</property>
</object>
<packing>
<property name="top_attach">4</property>
@ -251,106 +143,13 @@
<property name="y_options"/>
</packing>
</child>
<child>
<object class="UndoableEntry" id="country">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">The country where the place is.</property>
<property name="invisible_char">●</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="label297">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_ZIP/Postal code:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">postal</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="UndoableEntry" id="postal">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">●</property>
</object>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="label296">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Phon_e:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">phone</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="UndoableEntry" id="phone">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">●</property>
</object>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="UndoableEntry" id="street">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">Lowest level of a place division: eg the street name.</property>
<property name="invisible_char">●</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">4</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_Locality:</property>
<property name="label" translatable="yes">Type:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">locality</property>
</object>
<packing>
<property name="top_attach">1</property>
@ -360,17 +159,85 @@
</packing>
</child>
<child>
<object class="GtkEntry" id="locality">
<object class="GtkEntry" id="entry1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">A district within, or a settlement near to, a town or city.</property>
<property name="invisible_char">●</property>
<property name="invisible_char">•</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">3</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="combobox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">3</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="ValidatableMaskedEntry" id="lat_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">•</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">3</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="ValidatableMaskedEntry" id="lon_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">•</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">3</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button1">
<property name="label" translatable="yes">...</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options"/>
<property name="y_options"/>
</packing>
</child>

View File

@ -16,12 +16,10 @@
<child>
<object class="GtkButton" id="cancel">
<property name="label">gtk-cancel</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<property name="use_stock">True</property>
</object>
<packing>
@ -33,13 +31,11 @@
<child>
<object class="GtkButton" id="ok">
<property name="label">gtk-ok</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="has_default">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<property name="use_stock">True</property>
</object>
<packing>
@ -51,12 +47,10 @@
<child>
<object class="GtkButton" id="help">
<property name="label">gtk-help</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<property name="use_stock">True</property>
</object>
<packing>
@ -82,13 +76,10 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">12</property>
<property name="n_rows">3</property>
<property name="n_rows">2</property>
<property name="n_columns">4</property>
<property name="column_spacing">6</property>
<property name="row_spacing">4</property>
<child>
<placeholder/>
</child>
<child>
<object class="GtkLabel" id="label244">
<property name="visible">True</property>
@ -104,42 +95,6 @@
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="label249">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">L_atitude:</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<property name="mnemonic_widget">lat_entry</property>
</object>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="label250">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_Longitude:</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<property name="mnemonic_widget">lon_entry</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="UndoableEntry" id="place_title">
<property name="visible">True</property>
@ -163,8 +118,8 @@
<property name="mnemonic_widget">gid</property>
</object>
<packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
</packing>
@ -179,18 +134,16 @@
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="private">
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<property name="relief">none</property>
<accelerator key="p" signal="activate" modifiers="GDK_CONTROL_MASK"/>
<child internal-child="accessible">
@ -214,45 +167,14 @@
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options"/>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="ValidatableMaskedEntry" id="lat_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">Latitude (position above the Equator) of the place in decimal or degree notation.
Eg, valid values are 12.0154, 50°5221.92″N, N50°5221.92″ or 50:52:21.92
You can set these values via the Geography View by searching the place, or via a map service in the place view.</property>
<property name="invisible_char">●</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="ValidatableMaskedEntry" id="lon_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">Longitude (position relative to the Prime, or Greenwich, Meridian) of the place in decimal or degree notation.
Eg, valid values are -124.3647, 124°5221.92″E, E124°5221.92″ or 124:52:21.92
You can set these values via the Geography View by searching the place, or via a map service in the place view.</property>
<property name="invisible_char">●</property>
</object>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options"/>
</packing>
<placeholder/>
</child>
</object>
<packing>
@ -266,305 +188,17 @@ You can set these values via the Geography View by searching the place, or via a
<property name="visible">True</property>
<property name="can_focus">True</property>
<child>
<object class="GtkTable" id="table19">
<object class="GtkTable" id="loc_table">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">12</property>
<property name="n_rows">5</property>
<property name="n_columns">4</property>
<property name="column_spacing">12</property>
<property name="row_spacing">6</property>
<child>
<object class="GtkLabel" id="label245">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">C_ity:</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<property name="mnemonic_widget">city</property>
</object>
<packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
</packing>
<placeholder/>
</child>
<child>
<object class="GtkLabel" id="label664">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">S_treet:</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<property name="mnemonic_widget">street</property>
</object>
<packing>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="UndoableEntry" id="street">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">Lowest level of a place division: eg the street name.
Use Alternate Locations tab to store the current name.</property>
<property name="invisible_char">●</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">4</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="UndoableEntry" id="city">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">The town or city where the place is.
Use Alternate Locations tab to store the current name.</property>
<property name="invisible_char">●</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="UndoableEntry" id="parish">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">Lowest clergical division of this place. Typically used for church sources that only mention the parish.</property>
<property name="invisible_char">●</property>
</object>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="label279">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Ch_urch parish:</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<property name="mnemonic_widget">parish</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="label247">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Co_unty:</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<property name="mnemonic_widget">county</property>
</object>
<packing>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="UndoableEntry" id="county">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">Third level of place division. Eg., in the USA a county.</property>
<property name="invisible_char">●</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="label246">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_State:</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<property name="mnemonic_widget">state</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="UndoableEntry" id="state">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">Second level of place division, eg., in the USA a state, in Germany a Bundesland.</property>
<property name="invisible_char">●</property>
</object>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="label248">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Count_ry:</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<property name="mnemonic_widget">country</property>
</object>
<packing>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="UndoableEntry" id="country">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">The country where the place is.
</property>
<property name="invisible_char">●</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="label290">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_ZIP/Postal code:</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<property name="mnemonic_widget">postal</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="UndoableEntry" id="postal">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">●</property>
</object>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="label291">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Phon_e:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">phone</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="UndoableEntry" id="phone">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">●</property>
</object>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_Locality:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">locality</property>
</object>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkEntry" id="locality">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">A district within, or a settlement near to, a town or city.
Use Alternate Locations tab to store the current name.</property>
<property name="invisible_char">●</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options"/>
</packing>
<placeholder/>
</child>
</object>
</child>

View File

@ -0,0 +1,70 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2003-2006 Donald N. Allingham
# Copyright (C) 2009-2010 Gary Burton
# Copyright (C) 2010 Nick Hall
#
# 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$
#-------------------------------------------------------------------------
#
# internationalization
#
#-------------------------------------------------------------------------
from gramps.gen.ggettext import gettext as _
#-------------------------------------------------------------------------
#
# gramps modules
#
#-------------------------------------------------------------------------
from ..views.treemodels.locationmodel import LocationTreeModel
from baseselector import BaseSelector
#-------------------------------------------------------------------------
#
# SelectLocation
#
#-------------------------------------------------------------------------
class SelectLocation(BaseSelector):
def _local_init(self):
"""
Perform local initialisation for this class
"""
self.width_key = 'interface.place-sel-width'
self.height_key = 'interface.place-sel-height'
def get_window_title(self):
return _("Select Location")
def get_model_class(self):
return LocationTreeModel
def get_column_titles(self):
return [
(_('Name'), 350, BaseSelector.TEXT, 0),
(_('Type'), 75, BaseSelector.TEXT, 1),
]
def get_from_handle_func(self):
return self.db.get_location_from_handle
def get_handle_column(self):
return LocationTreeModel.HANDLE_COL

View File

@ -21,35 +21,38 @@
# $Id$
from .selectorexceptions import SelectorException
from selectorexceptions import SelectorException
def SelectorFactory(classname):
if classname == 'Person':
from .selectperson import SelectPerson
from selectperson import SelectPerson
cls = SelectPerson
elif classname == 'Family':
from .selectfamily import SelectFamily
from selectfamily import SelectFamily
cls = SelectFamily
elif classname == 'Event':
from .selectevent import SelectEvent
from selectevent import SelectEvent
cls = SelectEvent
elif classname == 'Place':
from .selectplace import SelectPlace
from selectplace import SelectPlace
cls = SelectPlace
elif classname == 'Location':
from selectlocation import SelectLocation
cls = SelectLocation
elif classname == 'Source':
from .selectsource import SelectSource
from selectsource import SelectSource
cls = SelectSource
elif classname == 'Citation':
from .selectcitation import SelectCitation
from selectcitation import SelectCitation
cls = SelectCitation
elif classname in ['MediaObject', 'Media']:
from .selectobject import SelectObject
from selectobject import SelectObject
cls = SelectObject
elif classname == 'Repository':
from .selectrepository import SelectRepository
from selectrepository import SelectRepository
cls = SelectRepository
elif classname == 'Note':
from .selectnote import SelectNote
from selectnote import SelectNote
cls = SelectNote
else:
raise SelectorException("Attempt to create unknown "

View File

@ -35,7 +35,7 @@ from gramps.gen.ggettext import gettext as _
#
#-------------------------------------------------------------------------
from ..views.treemodels.placemodel import PlaceListModel
from .baseselector import BaseSelector
from baseselector import BaseSelector
#-------------------------------------------------------------------------
#
@ -59,15 +59,9 @@ class SelectPlace(BaseSelector):
def get_column_titles(self):
return [
(_('Title'), 350, BaseSelector.TEXT, 0),
(_('ID'), 75, BaseSelector.TEXT, 1),
(_('Street'), 75, BaseSelector.TEXT, 2),
(_('Locality'), 75, BaseSelector.TEXT, 3),
(_('City'), 75, BaseSelector.TEXT, 4),
(_('County'), 75, BaseSelector.TEXT, 5),
(_('State'), 75, BaseSelector.TEXT, 6),
(_('Country'), 75, BaseSelector.TEXT, 7),
(_('Parish'), 75, BaseSelector.TEXT, 9),
(_('Title'), 350, BaseSelector.TEXT, 0),
(_('ID'), 75, BaseSelector.TEXT, 1),
(_('Location'), 350, BaseSelector.TEXT, 2),
]
def get_from_handle_func(self):

View File

@ -24,14 +24,15 @@
Package init for the treemodels package.
"""
from .peoplemodel import PeopleBaseModel, PersonListModel, PersonTreeModel
from .familymodel import FamilyModel
from .eventmodel import EventModel
from .sourcemodel import SourceModel
from .placemodel import PlaceBaseModel, PlaceListModel, PlaceTreeModel
from .mediamodel import MediaModel
from .repomodel import RepositoryModel
from .notemodel import NoteModel
from .citationbasemodel import CitationBaseModel
from .citationlistmodel import CitationListModel
from .citationtreemodel import CitationTreeModel
from peoplemodel import PeopleBaseModel, PersonListModel, PersonTreeModel
from familymodel import FamilyModel
from eventmodel import EventModel
from sourcemodel import SourceModel
from placemodel import PlaceBaseModel, PlaceListModel, PlaceTreeModel
from locationmodel import LocationTreeModel
from mediamodel import MediaModel
from repomodel import RepositoryModel
from notemodel import NoteModel
from citationbasemodel import CitationBaseModel
from citationlistmodel import CitationListModel
from citationtreemodel import CitationTreeModel

View File

@ -0,0 +1,252 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2006 Donald N. Allingham
# Copyright (C) 2009-2010 Nick Hall
# Copyright (C) 2009 Benny Malengier
# Copyright (C) 2010 Gary Burton
#
# 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$
"""
Location Model.
"""
#-------------------------------------------------------------------------
#
# python modules
#
#-------------------------------------------------------------------------
import cgi
import logging
_LOG = logging.getLogger(".gui.views.treemodels.locationmodel")
#-------------------------------------------------------------------------
#
# GNOME/GTK modules
#
#-------------------------------------------------------------------------
from gi.repository import Gtk
#-------------------------------------------------------------------------
#
# GRAMPS modules
#
#-------------------------------------------------------------------------
from gramps.gen.datehandler import format_time
from gramps.gen.utils.place import conv_lat_lon
from .flatbasemodel import FlatBaseModel
from .treebasemodel import TreeBaseModel
#-------------------------------------------------------------------------
#
# internationalization
#
#-------------------------------------------------------------------------
from gramps.gen.ggettext import gettext as _
#-------------------------------------------------------------------------
#
# Constants
#
#-------------------------------------------------------------------------
LOCATIONTYPES = [_('Country'), _('State'), _('County'), _('City'),
_('Parish'), _('Locality'), _('Street')]
#-------------------------------------------------------------------------
#
# LocationBaseModel
#
#-------------------------------------------------------------------------
class LocationBaseModel(object):
HANDLE_COL = 5
def __init__(self, db):
self.gen_cursor = db.get_location_cursor
self.map = db.get_raw_location_data
self.fmap = [
self.column_name,
self.column_type,
self.column_latitude,
self.column_longitude,
self.column_change,
self.column_handle,
]
self.smap = [
self.column_name,
self.column_type,
self.sort_latitude,
self.sort_longitude,
self.sort_change,
self.column_handle,
]
def destroy(self):
"""
Unset all elements that can prevent garbage collection
"""
self.db = None
self.gen_cursor = None
self.map = None
self.fmap = None
self.smap = None
def on_get_n_columns(self):
return len(self.fmap)+1
def column_handle(self, data):
return unicode(data[0])
def column_name(self, data):
return unicode(data[2])
def column_type(self, data):
return LOCATIONTYPES[data[3]-1]
def column_latitude(self, data):
if not data[4]:
return u' '
value = conv_lat_lon(data[4], '0', format='DEG')[0]
if not value:
return _("Error in format")
return value
def column_longitude(self, data):
if not data[5]:
return u' '
value = conv_lat_lon('0', data[5], format='DEG')[1]
if not value:
return _("Error in format")
return value
def sort_latitude(self, data):
if not data[4]:
return u' '
value = conv_lat_lon(data[4], '0', format='ISO-DMS') if data[4] else u''
if not value:
return _("Error in format")
return value
def sort_longitude(self, data):
if not data[5]:
return u' '
value = conv_lat_lon('0', data[5], format='ISO-DMS') if data[5] else u''
if not value:
return _("Error in format")
return value
def sort_change(self, data):
return "%012x" % data[6]
def column_change(self, data):
return Utils.format_time(data[6])
def column_place_name(self, data):
return unicode(data[2])
def sort_place_change(self, data):
return "%012x" % data[9]
def column_place_change(self, data):
return Utils.format_time(data[9])
#-------------------------------------------------------------------------
#
# LocationTreeModel
#
#-------------------------------------------------------------------------
class LocationTreeModel(LocationBaseModel, TreeBaseModel):
"""
Hierarchical place model.
"""
def __init__(self, db, scol=0, order=Gtk.SortType.ASCENDING, search=None,
skip=set(), sort_map=None):
LocationBaseModel.__init__(self, db)
TreeBaseModel.__init__(self, db, scol=scol, order=order,
tooltip_column=15,
search=search, skip=skip, sort_map=sort_map,
nrgroups = 3,
group_can_have_handle = True,
has_secondary=False)
def destroy(self):
"""
Unset all elements that can prevent garbage collection
"""
LocationBaseModel.destroy(self)
self.number_items = None
TreeBaseModel.destroy(self)
def _set_base_data(self):
"""See TreeBaseModel, for place, most have been set in init of
PlaceBaseModel
"""
self.number_items = self.db.get_number_of_locations
self.gen_cursor2 = self.db.get_place_cursor
self.map2 = self.db.get_raw_place_data
self.fmap2 = [
self.column_place_name,
lambda handle, data: u'',
self.column_place_change,
self.column_handle,
]
self.smap2 = [
self.column_place_name,
lambda handle, data: u'',
self.sort_place_change,
self.column_handle,
]
self.number_items2 = self.db.get_number_of_places
def get_tree_levels(self):
"""
Return the headings of the levels in the hierarchy.
"""
return [_('Country'), _('State'), _('County'), _('Place')]
def add_row(self, handle, data):
"""
Add nodes to the node map for a single location.
handle The handle of the gramps object.
data The object data.
"""
sort_key = self.sort_func(data)
parent = data[1]
# Add the node as a root node if the parent is not in the tree. This
# will happen when the view is filtered.
if not self.get_node(parent):
parent = None
self.add_node(parent, handle, sort_key, handle, add_parent=False)
def add_row2(self, handle, data):
"""
Add nodes to the node map for a single place.
handle The handle of the gramps object.
data The object data.
"""
sort_key = self.sort_func2(data)
parent = data[5]
if self.get_node(parent):
self.add_node(parent, handle, sort_key, handle, add_parent=False,
secondary=True)

View File

@ -48,7 +48,6 @@ from gi.repository import Gtk
#-------------------------------------------------------------------------
from gramps.gen.datehandler import format_time
from gramps.gen.utils.place import conv_lat_lon
from gramps.gen.constfunc import cuni
from .flatbasemodel import FlatBaseModel
from .treebasemodel import TreeBaseModel
@ -76,7 +75,7 @@ COUNTRYLEVELS = {
#-------------------------------------------------------------------------
class PlaceBaseModel(object):
HANDLE_COL = 14
HANDLE_COL = 5
def __init__(self, db):
self.gen_cursor = db.get_place_cursor
@ -84,16 +83,7 @@ class PlaceBaseModel(object):
self.fmap = [
self.column_name,
self.column_id,
self.column_street,
self.column_locality,
self.column_city,
self.column_county,
self.column_state,
self.column_country,
self.column_postal_code,
self.column_parish,
self.column_latitude,
self.column_longitude,
self.column_location,
self.column_change,
self.column_place_name,
self.column_handle,
@ -102,16 +92,7 @@ class PlaceBaseModel(object):
self.smap = [
self.column_name,
self.column_id,
self.column_street,
self.column_locality,
self.column_city,
self.column_county,
self.column_state,
self.column_country,
self.column_postal_code,
self.column_parish,
self.sort_latitude,
self.sort_longitude,
self.column_location,
self.sort_change,
self.column_place_name,
self.column_handle,
@ -131,102 +112,33 @@ class PlaceBaseModel(object):
return len(self.fmap)+1
def column_handle(self, data):
return cuni(data[0])
return unicode(data[0])
def column_place_name(self, data):
return cuni(data[2])
def column_longitude(self, data):
if not data[3]:
return ''
value = conv_lat_lon('0', data[3], format='DEG')[1]
if not value:
return _("Error in format")
return value
def column_latitude(self, data):
if not data[4]:
return ''
value = conv_lat_lon(data[4], '0', format='DEG')[0]
if not value:
return _("Error in format")
return value
def sort_longitude(self, data):
if not data[3]:
return ''
value = conv_lat_lon('0', data[3], format='ISO-DMS') if data[3] else ''
if not value:
return _("Error in format")
return value
def sort_latitude(self, data):
if not data[4]:
return ''
value = conv_lat_lon(data[4], '0', format='ISO-DMS') if data[4] else ''
if not value:
return _("Error in format")
return value
return unicode(data[2])
def column_id(self, data):
return cuni(data[1])
return unicode(data[1])
def column_parish(self, data):
def column_location(self, data):
try:
return data[5][1]
loc = self.db.get_location_from_handle(data[3])
lines = [loc.name]
while loc.parent is not None:
loc = self.db.get_location_from_handle(loc.parent)
lines.append(loc.name)
return ', '.join(lines)
except:
return ''
def column_street(self, data):
try:
return data[5][0][0]
except:
return ''
def column_locality(self, data):
try:
return data[5][0][1]
except:
return ''
def column_city(self, data):
try:
return data[5][0][2]
except:
return ''
def column_county(self, data):
try:
return data[5][0][3]
except:
return ''
def column_state(self, data):
try:
return data[5][0][4]
except:
return ''
def column_country(self, data):
try:
return data[5][0][5]
except:
return ''
def column_postal_code(self, data):
try:
return data[5][0][6]
except:
return ''
return u''
def sort_change(self, data):
return "%012x" % data[11]
return "%012x" % data[9]
def column_change(self, data):
return format_time(data[11])
return format_time(data[9])
def column_tooltip(self, data):
return cuni('Place tooltip')
return u'Place tooltip'
#-------------------------------------------------------------------------
#
@ -252,7 +164,7 @@ class PlaceListModel(PlaceBaseModel, FlatBaseModel):
FlatBaseModel.destroy(self)
def column_name(self, data):
return cuni(data[2])
return unicode(data[2])
#-------------------------------------------------------------------------
#
@ -342,16 +254,16 @@ class PlaceTreeModel(PlaceBaseModel, TreeBaseModel):
if data[5] is not None:
level = [data[5][0][i] for i in range(5,-1,-1)]
if not (level[3] or level[4] or level[5]):
name = cuni(level[2] or level[1] or level[0])
name = unicode(level[2] or level[1] or level[0])
else:
name = ', '.join([item for item in level[3:] if item])
if not name:
name = cuni(data[2])
name = unicode(data[2])
if name:
return cgi.escape(name)
else:
return "<i>%s<i>" % cgi.escape(_("<no name>"))
return u"<i>%s<i>" % cgi.escape(_("<no name>"))
def column_header(self, node):
"""

View File

@ -23,23 +23,25 @@
"""Custom widgets."""
from .basicentry import *
from .buttons import *
from .expandcollapsearrow import *
from .labels import *
from .linkbox import *
from .photo import *
from .monitoredwidgets import *
from .shortlistcomboentry import *
from .springseparator import *
from .statusbar import Statusbar
from .styledtextbuffer import *
from .styledtexteditor import *
from .toolcomboentry import *
from .undoablebuffer import *
from .undoableentry import *
from .undoablestyledbuffer import *
from .validatedcomboentry import *
from .validatedmaskedentry import *
from .valueaction import *
from .valuetoolitem import *
from basicentry import *
from buttons import *
from expandcollapsearrow import *
from labels import *
from locationentry import *
from locationentry2 import *
from linkbox import *
from photo import *
from monitoredwidgets import *
from shortlistcomboentry import *
from springseparator import *
from statusbar import Statusbar
from styledtextbuffer import *
from styledtexteditor import *
from toolcomboentry import *
from undoablebuffer import *
from undoableentry import *
from undoablestyledbuffer import *
from validatedcomboentry import *
from validatedmaskedentry import *
from valueaction import *
from valuetoolitem import *

View File

@ -0,0 +1,196 @@
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2012 Nick Hall
#
# 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$
#
#-------------------------------------------------------------------------
#
# GTK/Gnome modules
#
#-------------------------------------------------------------------------
from gi.repository import Gtk
#-------------------------------------------------------------------------
#
# Gramps modules
#
#-------------------------------------------------------------------------
from gramps.gen.ggettext import gettext as _
#-------------------------------------------------------------------------
#
# Constants
#
#-------------------------------------------------------------------------
LOCATIONTYPES = [_('Country'), _('State'), _('County'), _('City'),
_('Parish'), _('Locality'), _('Street')]
MAX_LEVELS = 7
#-------------------------------------------------------------------------
#
# LocationEntry class
#
#-------------------------------------------------------------------------
class LocationEntry(object):
"""
Allows the hierarchical entry of a location.
"""
def __init__(self, table, db, handle):
self.db = db
self.widgets = []
self.labels = []
self.types = [-1] * MAX_LEVELS
self.signals = []
table.set_col_spacings(10)
for row in range(MAX_LEVELS):
widget = self.create_widget(table, row)
sig_id = widget.connect('changed', self.changed_cb, row)
self.signals.append(sig_id)
if handle:
locs = []
loc = db.get_location_from_handle(handle)
while loc.parent is not None:
locs.append(loc)
loc = db.get_location_from_handle(loc.parent)
locs.append(loc)
locs.reverse()
for row, loc in enumerate(locs):
self.populate_widget(row, loc.parent, loc.handle)
else:
self.populate_widget(0, None, None)
def create_widget(self, table, row):
model = Gtk.ListStore(str, str, int)
widget = Gtk.ComboBox.new_with_model_and_entry(model)
widget.set_entry_text_column(1)
widget.set_sensitive(False)
label = Gtk.Label()
label.set_alignment(1, 0.5)
label.show()
table.attach(label, 0, 1, row, row+1, xoptions=Gtk.AttachOptions.FILL, yoptions=0)
self.labels.append(label)
table.attach(widget, 1, 2, row, row+1, yoptions=0)
self.widgets.append(widget)
return widget
def populate_widget(self, row, parent_handle, default):
widget = self.widgets[row]
model = widget.get_model()
widget.set_model(None)
model.clear()
active_iter = None
children = self.db.find_location_child_handles(str(parent_handle))
loc_type = None
has_children = False
for handle in children:
child = self.db.get_location_from_handle(handle)
iter_ = model.append((handle, child.name, child.get_type()))
if handle == default:
active_iter = iter_
loc_type = child.get_type()
has_children = True
model.set_sort_column_id(1, Gtk.SortType.ASCENDING)
widget.set_model(model)
widget.get_child().set_text('')
if active_iter is not None:
widget.set_active_iter(active_iter)
widget.set_sensitive(True)
if has_children:
if loc_type is None:
loc_type = child.get_type()
self.set_label(row, loc_type)
else:
if parent_handle:
parent = self.db.get_location_from_handle(parent_handle)
if parent.get_type() < len(LOCATIONTYPES):
self.set_label(row, parent.get_type() + 1)
else:
self.set_label(row, 1)
def set_label(self, row, loc_type):
self.types[row] = loc_type
label_text = '%s:' % LOCATIONTYPES[loc_type - 1]
self.labels[row].set_label(label_text)
def clear_widget(self, row):
widget = self.widgets[row]
widget.get_child().set_text('')
model = widget.get_model()
model.clear()
def changed_cb(self, widget, row):
self.disable_signals()
if widget.get_active() == -1:
# Text entry
if row+1 < MAX_LEVELS:
self.clear_widget(row+1)
if self.types[row] < len(LOCATIONTYPES):
self.widgets[row+1].set_sensitive(True)
self.set_label(row + 1, self.types[row] + 1)
else:
# New selection
model = widget.get_model()
parent = model.get_value(widget.get_active_iter(), 0)
loc_type = model.get_value(widget.get_active_iter(), 2)
self.set_label(row, loc_type)
if row+1 < MAX_LEVELS:
if self.types[row] < len(LOCATIONTYPES):
self.populate_widget(row+1, parent, None)
else:
self.clear_widget(row+1)
self.widgets[row+1].set_sensitive(False)
self.labels[row+1].set_label('')
# Clear rows below the active row
for row in range(row+2, MAX_LEVELS):
widget = self.widgets[row]
self.clear_widget(row)
widget.set_sensitive(False)
self.labels[row].set_label('')
self.enable_signals()
def enable_signals(self):
for row in range(MAX_LEVELS):
self.widgets[row].handler_unblock(self.signals[row])
def disable_signals(self):
for row in range(MAX_LEVELS):
self.widgets[row].handler_block(self.signals[row])
def get_result(self):
handle = None
new_locations = []
for row in range(MAX_LEVELS):
widget = self.widgets[row]
if widget.get_active() == -1:
# New name
new_name = widget.get_child().get_text().strip()
if new_name:
new_locations.append((self.types[row], new_name))
else:
# Existing location
model = widget.get_model()
handle = model.get_value(widget.get_active_iter(), 0)
return (handle, new_locations)

View File

@ -0,0 +1,138 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2012 Nick Hall
#
# 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$
__all__ = ["LocationEntry2"]
#-------------------------------------------------------------------------
#
# Standard python modules
#
#-------------------------------------------------------------------------
import logging
_LOG = logging.getLogger(".widgets.locationentry2")
#-------------------------------------------------------------------------
#
# GTK/Gnome modules
#
#-------------------------------------------------------------------------
from gi.repository import Gtk
#-------------------------------------------------------------------------
#
# Constants
#
#-------------------------------------------------------------------------
MIN_CHARACTERS = 3
MAX_ENTRIES = 20
#-------------------------------------------------------------------------
#
# LocationEntry2 class
#
#-------------------------------------------------------------------------
class LocationEntry2(Gtk.Entry):
def __init__(self, dbstate):
Gtk.Entry.__init__(self)
self.dbstate = dbstate
self.set_width_chars(5)
self.connect('changed', self.changed)
self.connect('focus-out-event', self.lost_focus)
self.show()
self.handle = None
self.pwin = Gtk.Window(Gtk.WindowType.POPUP)
self.vbox = Gtk.VBox()
self.pwin.add(self.vbox)
def get_handle(self):
return self.handle
def set_handle(handle):
self.set_text(self.get_location_text(handle))
def changed(self, widget):
txt = self.get_text()
if len(txt) >= MIN_CHARACTERS:
loc_list = self.get_location_list(txt)
if loc_list:
self.build_menu(loc_list)
else:
self.pwin.hide()
self.handle = None
def lost_focus(self, widget, event):
self.pwin.hide()
def build_menu(self, loc_list):
self.pwin.hide() # required to get correct allocation
self.pwin.resize(1, 1)
map(self.vbox.remove, self.vbox.get_children())
count = 0
for loc in loc_list:
item = Gtk.Button(loc[1])
item.set_alignment(0, 0.5)
item.set_relief(Gtk.ReliefStyle.NONE)
item.connect("clicked", self.item_selected, loc[0])
item.show()
self.vbox.pack_start(item, False, False, 0)
count += 1
if count >= MAX_ENTRIES:
break
self.pwin.show_all()
unused, x_pos, y_pos = self.get_window().get_origin()
x_pos += self.get_allocation().x
y_pos += self.get_allocation().y
y_pos += self.get_allocation().height
screen = self.pwin.get_screen()
width = self.pwin.get_allocation().width
height = self.pwin.get_allocation().height
if x_pos + width > screen.get_width():
x_pos = screen.get_width() - width
if y_pos + height > screen.get_height():
y_pos -= height + self.get_allocation().height
self.pwin.move(x_pos, y_pos)
def item_selected(self, menu_item, handle):
self.set_text(menu_item.get_label())
self.handle = handle
self.pwin.hide()
def get_location_list(self, txt):
loc_list = []
for handle in self.dbstate.db.find_location_from_name(txt):
loc_list.append((handle, self.get_location_text(handle)))
return loc_list
def get_location_text(self, handle):
loc = self.dbstate.db.get_location_from_handle(handle)
lines = [loc.name]
while loc.parent is not None:
loc = self.dbstate.db.get_location_from_handle(loc.parent)
lines.append(loc.name)
return ', '.join(lines)

View File

@ -75,7 +75,7 @@ class PlaceDetails(Gramplet):
"""
Remove all the rows from the table.
"""
list(map(self.table.remove, self.table.get_children()))
map(self.table.remove, self.table.get_children())
self.table.resize(1, 2)
def db_changed(self):
@ -107,21 +107,27 @@ class PlaceDetails(Gramplet):
self.title.set_text(place.get_title())
self.clear_table()
self.display_location(place.get_main_location())
mloc = place.get_main_location()
self.display_location(mloc)
self.display_separator()
lat, lon = conv_lat_lon(place.get_latitude(),
place.get_longitude(),
location = self.dbstate.db.get_location_from_handle(mloc)
lat, lon = conv_lat_lon(location.get_latitude(),
location.get_longitude(),
format='DEG')
if lat:
self.add_row(_('Latitude'), lat)
if lon:
self.add_row(_('Longitude'), lon)
def display_location(self, location):
def display_location(self, handle):
"""
Display a location.
"""
lines = [line for line in location.get_text_data_list()[:-1] if line]
loc = self.dbstate.db.get_location_from_handle(handle)
lines = [loc.name]
while loc.parent is not None:
loc = self.dbstate.db.get_location_from_handle(loc.parent)
lines.append(loc.name)
self.add_row(_('Location'), '\n'.join(lines))
def display_empty(self):

View File

@ -83,8 +83,10 @@ class MapService():
"""return the lat, lon value of place in the requested format
None, None if invalid
"""
return conv_lat_lon(place.get_latitude(),
place.get_longitude(), format)
mloc = place.get_main_location()
location = self.database.get_location_from_handle(mloc)
return conv_lat_lon(location.get_latitude(),
location.get_longitude(), format)
def calc_url(self):
"""Base class needs to overwrite this, calculation of the self.path"""

View File

@ -31,7 +31,7 @@ Base view for Place Views
# Global modules
#
#-------------------------------------------------------------------------
from __future__ import print_function
#-------------------------------------------------------------------------
#
@ -78,44 +78,22 @@ class PlaceBaseView(ListView):
"""
COL_NAME = 0
COL_ID = 1
COL_STREET = 2
COL_LOCALITY = 3
COL_CITY = 4
COL_COUNTY = 5
COL_STATE = 6
COL_COUNTRY = 7
COL_ZIP = 8
COL_PARISH = 9
COL_LAT = 10
COL_LON = 11
COL_CHAN = 12
COL_LOCATION = 2
COL_CHAN = 3
# name of the columns
COLUMN_NAMES = [
_('Place Name'),
_('ID'),
_('Street'),
_('Locality'),
_('City'),
_('County'),
_('State'),
_('Country'),
_('ZIP/Postal Code'),
_('Church Parish'),
_('Latitude'),
_('Longitude'),
_('Location'),
_('Last Changed'),
]
# columns that contain markup
MARKUP_COLS = [COL_NAME]
# default setting with visible columns, order of the col, and their size
CONFIGSETTINGS = (
('columns.visible', [COL_NAME, COL_ID, COL_STREET, COL_LOCALITY,
COL_CITY, COL_COUNTY, COL_STATE]),
('columns.rank', [COL_NAME, COL_ID, COL_STREET, COL_LOCALITY, COL_CITY,
COL_COUNTY, COL_STATE, COL_COUNTRY, COL_ZIP,
COL_PARISH, COL_LAT, COL_LON, COL_CHAN]),
('columns.size', [250, 75, 150, 150, 150, 150, 100, 100, 100,
100, 150, 150, 100])
('columns.visible', [COL_NAME, COL_ID, COL_LOCATION]),
('columns.rank', [COL_NAME, COL_ID, COL_LOCATION, COL_CHAN]),
('columns.size', [250, 75, 350, 100])
)
ADD_MSG = _("Add a new place")
EDIT_MSG = _("Edit the selected place")
@ -139,7 +117,7 @@ class PlaceBaseView(ListView):
ListView.__init__(
self, title, pdata, dbstate, uistate,
self.COLUMN_NAMES, 14,
self.COLUMN_NAMES, 5,
model, signal_map,
dbstate.db.get_place_bookmarks(),
PlaceBookmarks, nav_group,
@ -150,7 +128,7 @@ class PlaceBaseView(ListView):
'<PRIMARY>J' : self.jump,
'<PRIMARY>BackSpace' : self.key_delete,
})
self.maptoolbtn = None
self.additional_uis.append(self.additional_ui())
def navigation_type(self):
@ -161,6 +139,11 @@ class PlaceBaseView(ListView):
def define_actions(self):
ListView.define_actions(self)
self._add_toolmenu_action('MapsList', _('Loading...'),
_("Attempt to see selected locations with a Map "
"Service (OpenstreetMap, Google Maps, ...)"),
self.gotomap,
_('Select a Map Service'))
self._add_action('GotoMap', Gtk.STOCK_JUMP_TO,
_('_Look up with Map Service'),
callback=self.gotomap,
@ -170,13 +153,6 @@ class PlaceBaseView(ListView):
callback=self.filter_editor)
self._add_action('QuickReport', None, _("Quick View"), None, None, None)
def set_inactive(self):
"""called by viewmanager when moving away from the page
Here we need to remove the menutoolbutton from the menu
"""
tb = self.uistate.viewmanager.uimanager.get_widget('/ToolBar')
tb.remove(self.maptoolbtn)
def change_page(self):
"""
Called by viewmanager at end of realization when arriving on the page
@ -188,18 +164,11 @@ class PlaceBaseView(ListView):
5. store label so it can be changed when selection changes
"""
ListView.change_page(self)
#menutoolbutton has to be made and added in correct place on toolbar
if not self.maptoolbtn:
self.maptoolbtn = Gtk.MenuToolButton.new_from_stock(Gtk.STOCK_JUMP_TO)
self.maptoolbtn.connect('clicked', self.gotomap)
self.mmenu = self.__create_maps_menu_actions()
self.maptoolbtn.set_menu(self.mmenu)
self.maptoolbtn.show()
tb = self.uistate.viewmanager.uimanager.get_widget('/ToolBar')
ind = tb.get_item_index(self.uistate.viewmanager.uimanager.get_widget(
'/ToolBar/CommonEdit/Merge'))
tb.insert(self.maptoolbtn, ind+1)
widget = self.maptoolbtn
#menutoolbutton actions are stored in PageView class,
# obtain the widgets where we need to add to menu
actionservices = self.action_toolmenu['MapsList']
widgets = actionservices.get_proxies()
mmenu = self.__create_maps_menu_actions()
if not self.mapservicedata:
return
@ -207,19 +176,18 @@ class PlaceBaseView(ListView):
self.mapslistlabel = []
if not self.mapservice in self.mapservicedata:
#stored val no longer exists, use the first key instead
self.set_mapservice(list(self.mapservicedata.keys())[0])
self.set_mapservice(self.mapservicedata.keys()[0])
#store all gtk labels to be able to update label on selection change_('Loading...'),
widget.set_menu(self.mmenu)
widget.set_arrow_tooltip_text(_('Select a Map Service'))
widget.set_tooltip_text(
_("Attempt to see selected locations with a Map "
"Service (OpenstreetMap, Google Maps, ...)"))
lbl = Gtk.Label(label=self.mapservice_label())
lbl.show()
self.mapslistlabel.append(lbl)
widget.set_label_widget(self.mapslistlabel[-1])
widget.set_stock_id(Gtk.STOCK_JUMP_TO)
#store all gtk labels to be able to update label on selection change
for widget in widgets :
if isinstance(widget, Gtk.MenuToolButton):
widget.set_menu(mmenu)
widget.set_arrow_tooltip_text(actionservices.arrowtooltip)
lbl = Gtk.Label(label=self.mapservice_label())
lbl.show()
self.mapslistlabel.append(lbl)
widget.set_label_widget(self.mapslistlabel[-1])
widget.set_stock_id(Gtk.STOCK_JUMP_TO)
if self.drag_info():
self.list.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK,
[],
@ -228,7 +196,7 @@ class PlaceBaseView(ListView):
tglist.add(self.drag_info().atom_drag_type,
self.drag_info().target_flags,
self.drag_info().app_id)
tglist.add_text_targets (0)
tglist.add_text_targets (0L)
self.list.drag_source_set_target_list(tglist)
def __create_maps_menu_actions(self):
@ -301,7 +269,7 @@ class PlaceBaseView(ListView):
servfunc = eval('mod.' + serv.mapservice)
servfunc()(self.dbstate.db, places)
else:
print('Failed to load map plugin, see Plugin Manager')
print 'Failed to load map plugin, see Plugin Manager'
def drag_info(self):
return DdTargets.PLACE_LINK
@ -351,6 +319,7 @@ class PlaceBaseView(ListView):
<toolitem action="Remove"/>
<toolitem action="Merge"/>
<separator/>
<toolitem action="MapsList"/>
</placeholder>
</toolbar>
<popup name="Popup">

View File

@ -33,6 +33,7 @@ Geography for events
from gramps.gen.ggettext import gettext as _
import os
import sys
import urlparse
import operator
import locale
from gi.repository import Gdk
@ -207,8 +208,10 @@ class GeoEvents(GeoGraphyView):
place = dbstate.db.get_place_from_handle(place_handle)
if place:
descr1 = place.get_title()
longitude = place.get_longitude()
latitude = place.get_latitude()
mloc = place.get_main_location()
location = self.dbstate.db.get_location_from_handle(mloc)
longitude = location.get_longitude()
latitude = location.get_latitude()
latitude, longitude = conv_lat_lon(latitude, longitude, "D.D8")
# place.get_longitude and place.get_latitude return
# one string. We have coordinates when the two values

View File

@ -33,6 +33,7 @@ Geography for one family
from gramps.gen.ggettext import gettext as _
import os
import sys
import urlparse
import operator
import locale
from gi.repository import Gdk
@ -202,8 +203,10 @@ class GeoFamily(GeoGraphyView):
if place_handle:
place = dbstate.db.get_place_from_handle(place_handle)
if place:
longitude = place.get_longitude()
latitude = place.get_latitude()
mloc = place.get_main_location()
location = self.dbstate.db.get_location_from_handle(mloc)
longitude = location.get_longitude()
latitude = location.get_latitude()
latitude, longitude = conv_lat_lon(latitude,
longitude, "D.D8")
descr = place.get_title()

View File

@ -33,6 +33,7 @@ Geography for one person
from gramps.gen.ggettext import gettext as _
import os
import sys
import urlparse
import operator
import locale
from gi.repository import Gdk
@ -318,8 +319,10 @@ class GeoPerson(GeoGraphyView):
if place_handle:
place = dbstate.db.get_place_from_handle(place_handle)
if place:
longitude = place.get_longitude()
latitude = place.get_latitude()
mloc = place.get_main_location()
location = self.dbstate.db.get_location_from_handle(mloc)
longitude = location.get_longitude()
latitude = location.get_latitude()
latitude, longitude = conv_lat_lon(latitude,
longitude, "D.D8")
descr = place.get_title()

View File

@ -34,6 +34,7 @@ from gramps.gen.ggettext import gettext as _
import os
import sys
import time
import urlparse
import operator
import locale
from gi.repository import Gdk
@ -201,8 +202,10 @@ class GeoPlaces(GeoGraphyView):
if self.nbplaces >= self._config.get("geography.max_places"):
return
descr = place.get_title()
longitude = place.get_longitude()
latitude = place.get_latitude()
mloc = place.get_main_location()
location = self.dbstate.db.get_location_from_handle(mloc)
longitude = location.get_longitude()
latitude = location.get_latitude()
latitude, longitude = conv_lat_lon(latitude, longitude, "D.D8")
# place.get_longitude and place.get_latitude return
# one string. We have coordinates when the two values

View File

@ -0,0 +1,281 @@
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2001-2007 Donald N. Allingham
# Copyright (C) 2008 Gary Burton
# Copyright (C) 2011 Tim G L Lyons
#
# 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$
"""
Provide the location view.
"""
#-------------------------------------------------------------------------
#
# Standard python modules
#
#-------------------------------------------------------------------------
from gramps.gen.ggettext import gettext as _
import logging
_LOG = logging.getLogger(".plugins.locationview")
#-------------------------------------------------------------------------
#
# gramps modules
#
#-------------------------------------------------------------------------
from gramps.gen.lib import Location
from gramps.gen.db import DbTxn
from gramps.gui.views.listview import ListView
from gramps.gui.views.treemodels import LocationTreeModel
from gramps.gen.errors import WindowActiveError
from gramps.gui.views.bookmarks import PlaceBookmarks
from gramps.gui.ddtargets import DdTargets
from gramps.gui.dialog import ErrorDialog
from gramps.gui.editors import EditLocation
from gramps.gen.plug import CATEGORY_QR_PLACE
#-------------------------------------------------------------------------
#
# LocationView
#
#-------------------------------------------------------------------------
class LocationView(ListView):
"""
LocationView class, derived from the ListView
"""
# columns in the model used in view
COL_NAME = 0
COL_TYPE = 1
COL_LAT = 2
COL_LON = 3
COL_CHAN = 4
# name of the columns
COLUMN_NAMES = [
_('Name'),
_('Type'),
_('Latitude'),
_('Longitude'),
_('Last Changed'),
]
# columns that contain markup
#MARKUP_COLS = [COL_DATE]
# default setting with visible columns, order of the col, and their size
CONFIGSETTINGS = (
('columns.visible', [COL_NAME, COL_TYPE]),
('columns.rank', [COL_NAME, COL_TYPE, COL_LAT, COL_LON, COL_CHAN]),
('columns.size', [400, 100, 150, 150, 100])
)
ADD_MSG = _("Add a new location")
EDIT_MSG = _("Edit the selected location")
DEL_MSG = _("Delete the selected location")
MERGE_MSG = _("Merge the selected locations")
FILTER_TYPE = "Place"
QR_CATEGORY = CATEGORY_QR_PLACE
def __init__(self, pdata, dbstate, uistate, nav_group=0):
"""
Create the Location View
"""
signal_map = {
'location-add' : self.row_add,
'location-update' : self.row_update,
'location-delete' : self.row_delete,
'location-rebuild' : self.object_build,
}
ListView.__init__(
self, _('Locations'), pdata, dbstate, uistate,
LocationView.COLUMN_NAMES, len(LocationView.COLUMN_NAMES),
LocationTreeModel,
signal_map, dbstate.db.get_place_bookmarks(),
PlaceBookmarks, nav_group,
multiple=True)
self.func_list.update({
'<CONTROL>J' : self.jump,
'<CONTROL>BackSpace' : self.key_delete,
})
self.additional_uis.append(self.additional_ui())
def navigation_type(self):
return 'Place'
def get_bookmarks(self):
"""
Return the bookmark object
"""
return self.dbstate.db.get_place_bookmarks()
def drag_info(self):
"""
Indicate that the drag type is a PLACE_LINK
"""
return DdTargets.PLACE_LINK
def get_stock(self):
"""
Use the gramps-place stock icon
"""
return 'gramps-place'
def additional_ui(self):
"""
Defines the UI string for UIManager
"""
return '''<ui>
<menubar name="MenuBar">
<menu action="BookMenu">
<placeholder name="AddEditBook">
<menuitem action="AddBook"/>
<menuitem action="EditBook"/>
</placeholder>
</menu>
<menu action="GoMenu">
<placeholder name="CommonGo">
<menuitem action="Back"/>
<menuitem action="Forward"/>
<separator/>
</placeholder>
</menu>
<menu action="FileMenu">
<placeholder name="LocalExport">
<menuitem action="ExportTab"/>
</placeholder>
</menu>
<menu action="EditMenu">
<placeholder name="CommonEdit">
<menuitem action="Add"/>
<menuitem action="Edit"/>
<menuitem action="Remove"/>
<menuitem action="Merge"/>
</placeholder>
</menu>
</menubar>
<toolbar name="ToolBar">
<placeholder name="CommonNavigation">
<toolitem action="Back"/>
<toolitem action="Forward"/>
</placeholder>
<placeholder name="CommonEdit">
<toolitem action="Add"/>
<toolitem action="Edit"/>
<toolitem action="Remove"/>
</placeholder>
</toolbar>
<popup name="Popup">
<menuitem action="OpenBranch"/>
<menuitem action="CloseBranch"/>
<menuitem action="OpenAllNodes"/>
<menuitem action="CloseAllNodes"/>
<separator/>
<menuitem action="Add"/>
<menuitem action="Edit"/>
<menuitem action="Remove"/>
<menuitem action="Merge"/>
<separator/>
<menu name="QuickReport" action="QuickReport">
<menuitem action="Dummy"/>
</menu>
</popup>
</ui>'''
def define_actions(self):
ListView.define_actions(self)
self._add_action('QuickReport', None,
_("Quick View"), None, None, None)
self._add_action('Dummy', None,
' ', None, None, self.dummy_report)
self._add_action('OpenBranch', None, _("Expand this Entire Group"),
callback=self.open_branch)
self._add_action('CloseBranch', None, _("Collapse this Entire Group"),
callback=self.close_branch)
self._add_action('OpenAllNodes', None, _("Expand all Nodes"),
callback=self.open_all_nodes)
self._add_action('CloseAllNodes', None, _("Collapse all Nodes"),
callback=self.close_all_nodes)
def get_handle_from_gramps_id(self, gid):
obj = self.dbstate.db.get_place_from_gramps_id(gid)
if obj:
return obj.get_handle()
else:
return None
def add(self, obj):
loc = Location()
selected = self.selected_handles()
if len(selected) == 1:
loc.parent = selected[0]
parent_loc = self.dbstate.db.get_location_from_handle(selected[0])
parent_type = parent_loc.get_type()
if parent_type < 7:
loc.set_type(parent_type + 1)
else:
loc.set_type(7)
try:
EditLocation(self.dbstate, self.uistate, [], loc, None)
except WindowActiveError:
pass
def remove(self, obj):
for handle in self.selected_handles():
place_list = [
item[1] for item in
self.dbstate.db.find_backlink_handles(handle, ['Place'])]
children = [handle for handle in
self.dbstate.db.find_location_child_handles(handle)]
if place_list or children:
msg = _("Cannot remove location object.")
msg2 = _("The location is in use.")
ErrorDialog(msg, msg2)
else:
location = self.dbstate.db.get_location_from_handle(handle)
with DbTxn(_("Delete Location (%s)") % location.get_name(),
self.dbstate.db) as trans:
self.dbstate.db.remove_location(handle, trans)
def edit(self, obj):
for handle in self.selected_handles():
loc = self.dbstate.db.get_location_from_handle(handle)
try:
EditLocation(self.dbstate, self.uistate, [], loc, None)
except WindowActiveError:
pass
def merge(self, obj):
"""
Merge the selected locations.
"""
msg = _("Not yet implemented.")
ErrorDialog(msg, msg)
def dummy_report(self, obj):
""" For the xml UI definition of popup to work, the submenu
Quick Report must have an entry in the xml
As this submenu will be dynamically built, we offer a dummy action
"""
pass
def get_default_gramplets(self):
"""
Define the default gramplets for the sidebar and bottombar.
"""
return ((), ())

View File

@ -38,7 +38,7 @@ version = '1.0',
gramps_target_version = '4.0',
status = STABLE,
fname = 'eventview.py',
authors = ["The Gramps project"],
authors = [u"The Gramps project"],
authors_email = ["http://gramps-project.org"],
category = ("Events", _("Events")),
viewclass = 'EventView',
@ -53,7 +53,7 @@ version = '1.0',
gramps_target_version = '4.0',
status = STABLE,
fname = 'familyview.py',
authors = ["The Gramps project"],
authors = [u"The Gramps project"],
authors_email = ["http://gramps-project.org"],
category = ("Families", _("Families")),
viewclass = 'FamilyView',
@ -68,7 +68,7 @@ version = '1.0',
gramps_target_version = '4.0',
status = STABLE,
fname = 'grampletview.py',
authors = ["The Gramps project"],
authors = [u"The Gramps project"],
authors_email = ["http://gramps-project.org"],
category = ("Gramplets", _("Gramplets")),
viewclass = 'GrampletView',
@ -83,7 +83,7 @@ version = '1.0',
gramps_target_version = '4.0',
status = STABLE,
fname = 'mediaview.py',
authors = ["The Gramps project"],
authors = [u"The Gramps project"],
authors_email = ["http://gramps-project.org"],
category = ("Media", _("Media")),
viewclass = 'MediaView',
@ -98,7 +98,7 @@ version = '1.0',
gramps_target_version = '4.0',
status = STABLE,
fname = 'noteview.py',
authors = ["The Gramps project"],
authors = [u"The Gramps project"],
authors_email = ["http://gramps-project.org"],
category = ("Notes", _("Notes")),
viewclass = 'NoteView',
@ -113,7 +113,7 @@ version = '1.0',
gramps_target_version = '4.0',
status = STABLE,
fname = 'relview.py',
authors = ["The Gramps project"],
authors = [u"The Gramps project"],
authors_email = ["http://gramps-project.org"],
category = ("Relationships", _("Relationships")),
viewclass = 'RelationshipView',
@ -128,7 +128,7 @@ version = '1.0',
gramps_target_version = '4.0',
status = STABLE,
fname = 'pedigreeview.py',
authors = ["The Gramps project"],
authors = [u"The Gramps project"],
authors_email = ["http://gramps-project.org"],
category = ("Ancestry", _("Charts")),
viewclass = 'PedigreeView',
@ -145,7 +145,7 @@ version = '1.0',
gramps_target_version = '4.0',
status = STABLE,
fname = 'fanchartview.py',
authors = ["Douglas S. Blank", "B. Malengier"],
authors = [u"Douglas S. Blank", u"B. Malengier"],
authors_email = ["doug.blank@gmail.com", "benny.malengier@gmail.com"],
viewclass = 'FanChartView',
stock_icon = 'gramps-fanchart',
@ -160,7 +160,7 @@ version = '1.0',
gramps_target_version = '4.0',
status = STABLE,
fname = 'fanchartdescview.py',
authors = ["B. Malengier"],
authors = [u"B. Malengier"],
authors_email = ["benny.malengier@gmail.com"],
viewclass = 'FanChartDescView',
stock_icon = 'gramps-fanchartdesc',
@ -175,7 +175,7 @@ version = '1.0',
gramps_target_version = '4.0',
status = STABLE,
fname = 'persontreeview.py',
authors = ["The Gramps project"],
authors = [u"The Gramps project"],
authors_email = ["http://gramps-project.org"],
category = ("People", _("People")),
viewclass = 'PersonTreeView',
@ -192,7 +192,7 @@ version = '1.0',
gramps_target_version = '4.0',
status = STABLE,
fname = 'personlistview.py',
authors = ["The Gramps project"],
authors = [u"The Gramps project"],
authors_email = ["http://gramps-project.org"],
category = ("People", _("People")),
viewclass = 'PersonListView',
@ -208,7 +208,7 @@ version = '1.0',
gramps_target_version = '4.0',
status = STABLE,
fname = 'placelistview.py',
authors = ["The Gramps project"],
authors = [u"The Gramps project"],
authors_email = ["http://gramps-project.org"],
category = ("Places", _("Places")),
viewclass = 'PlaceListView',
@ -216,20 +216,20 @@ order = START,
stock_icon = 'gramps-tree-list',
)
register(VIEW,
id = 'placetreeview',
name = _("Place Tree"),
description = _("A view displaying places in a tree format."),
version = '1.0',
gramps_target_version = '4.0',
status = STABLE,
fname = 'placetreeview.py',
authors = ["Donald N. Allingham", "Gary Burton", "Nick Hall"],
authors_email = [""],
category = ("Places", _("Places")),
viewclass = 'PlaceTreeView',
stock_icon = 'gramps-tree-group',
)
#register(VIEW,
#id = 'placetreeview',
#name = _("Place Tree"),
#description = _("A view displaying places in a tree format."),
#version = '1.0',
#gramps_target_version = '4.0',
#status = STABLE,
#fname = 'placetreeview.py',
#authors = [u"Donald N. Allingham", u"Gary Burton", u"Nick Hall"],
#authors_email = [""],
#category = ("Places", _("Places")),
#viewclass = 'PlaceTreeView',
#stock_icon = 'gramps-tree-group',
#)
register(VIEW,
id = 'repoview',
@ -239,7 +239,7 @@ version = '1.0',
gramps_target_version = '4.0',
status = STABLE,
fname = 'repoview.py',
authors = ["The Gramps project"],
authors = [u"The Gramps project"],
authors_email = ["http://gramps-project.org"],
category = ("Repositories", _("Repositories")),
viewclass = 'RepositoryView',
@ -254,7 +254,7 @@ version = '1.0',
gramps_target_version = '4.0',
status = STABLE,
fname = 'sourceview.py',
authors = ["The Gramps project"],
authors = [u"The Gramps project"],
authors_email = ["http://gramps-project.org"],
category = ("Sources", _("Sources")),
viewclass = 'SourceView',
@ -270,7 +270,7 @@ version = '1.0',
gramps_target_version = '4.0',
status = STABLE,
fname = 'citationlistview.py',
authors = ["The Gramps project"],
authors = [u"The Gramps project"],
authors_email = ["http://gramps-project.org"],
category = ("Citations", _("Citations")),
viewclass = 'CitationListView',
@ -285,9 +285,25 @@ version = '1.0',
gramps_target_version = '4.0',
status = STABLE,
fname = 'citationtreeview.py',
authors = ["Tim G L Lyons", "Nick Hall"],
authors = [u"Tim G L Lyons", u"Nick Hall"],
authors_email = [""],
category = ("Sources", _("Sources")),
viewclass = 'CitationTreeView',
stock_icon = 'gramps-tree-select',
)
register(VIEW,
id = 'locationview',
name = _("Location View"),
description = _("The view showing all locations"),
version = '1.0',
gramps_target_version = '4.0',
status = STABLE,
fname = 'locationview.py',
authors = [u"The Gramps project"],
authors_email = ["http://gramps-project.org"],
category = ("Places", _("Places")),
viewclass = 'LocationView',
order = START,
stock_icon = 'gramps-tree-group',
)