GEPS 036: Add date and language to place names

This commit is contained in:
Nick Hall 2015-05-26 19:07:57 +01:00
parent e448272259
commit cc6820f80c
24 changed files with 757 additions and 147 deletions

View File

@ -47,6 +47,7 @@ from ..lib.nameorigintype import NameOriginType
from ..lib.place import Place
from ..lib.placeref import PlaceRef
from ..lib.placetype import PlaceType
from ..lib.placename import PlaceName
from ..lib.eventtype import EventType
from ..lib.tag import Tag
from ..utils.file import create_checksum
@ -80,6 +81,40 @@ def gramps_upgrade_pickle(self):
with BSDDBTxn(self.env, self.metadata) as txn:
txn.put(b'upgraded', 'Yes')
def gramps_upgrade_18(self):
"""
Upgrade database from version 17 to 18.
"""
length = len(self.place_map)
self.set_total(length)
# ---------------------------------
# Modify Place
# ---------------------------------
# Convert name fields to use PlaceName.
for handle in self.place_map.keys():
place = self.place_map[handle]
new_place = list(place)
name = PlaceName()
name.set_value(new_place[6])
new_place[6] = name.serialize()
alt_names = []
for name in new_place[7]:
alt_name = PlaceName()
alt_name.set_value(name)
alt_names.append(alt_name.serialize())
new_place[7] = alt_names
new_place = tuple(new_place)
with BSDDBTxn(self.env, self.place_map) as txn:
if isinstance(handle, str):
handle = handle.encode('utf-8')
txn.put(handle, new_place)
self.update()
# Bump up database version. Separate transaction to save metadata.
with BSDDBTxn(self.env, self.metadata) as txn:
txn.put(b'version', 18)
def gramps_upgrade_17(self):
"""
Upgrade database from version 16 to 17.

View File

@ -92,7 +92,7 @@ LOG = logging.getLogger(".citation")
#_hdlr.setFormatter(logging.Formatter(fmt="%(name)s.%(levelname)s: %(message)s"))
#_LOG.addHandler(_hdlr)
_MINVERSION = 9
_DBVERSION = 17
_DBVERSION = 18
IDTRANS = "person_id"
FIDTRANS = "family_id"
@ -2337,6 +2337,8 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
upgrade.gramps_upgrade_16(self)
if version < 17:
upgrade.gramps_upgrade_17(self)
if version < 18:
upgrade.gramps_upgrade_18(self)
self.reset()
self.set_total(6)

View File

@ -78,6 +78,6 @@ class HasData(Rule):
Match any name in a list of names.
"""
for name in place.get_all_names():
if self.match_substring(0, name):
if self.match_substring(0, name.get_value()):
return True
return False

View File

@ -34,6 +34,7 @@ from .eventref import EventRef
from .ldsord import LdsOrd
from .mediaref import MediaRef
from .name import Name
from .placename import PlaceName
from .placeref import PlaceRef
from .reporef import RepoRef
from .surname import Surname

View File

@ -32,6 +32,7 @@ Place object for Gramps.
#-------------------------------------------------------------------------
from .primaryobj import PrimaryObject
from .placeref import PlaceRef
from .placename import PlaceName
from .placetype import PlaceType
from .citationbase import CitationBase
from .notebase import NoteBase
@ -79,7 +80,7 @@ class Place(CitationBase, NoteBase, MediaBase, UrlBase, PrimaryObject):
self.long = ""
self.lat = ""
self.title = ""
self.name = ""
self.name = PlaceName()
self.alt_names = []
self.placeref_list = []
self.place_type = PlaceType()
@ -106,7 +107,8 @@ class Place(CitationBase, NoteBase, MediaBase, UrlBase, PrimaryObject):
"""
return (self.handle, self.gramps_id, self.title, self.long, self.lat,
[pr.serialize() for pr in self.placeref_list],
self.name, self.alt_names,
self.name.serialize(),
[an.serialize() for an in self.alt_names],
self.place_type.serialize(), self.code,
[al.serialize() for al in self.alt_loc],
UrlBase.serialize(self),
@ -142,8 +144,8 @@ class Place(CitationBase, NoteBase, MediaBase, UrlBase, PrimaryObject):
"long": self.long,
"lat": self.lat,
"placeref_list": [pr.to_struct() for pr in self.placeref_list],
"name": self.name,
"alt_names": self.alt_names,
"name": self.name.to_struct(),
"alt_names": [an.to_struct() for an in self.alt_names],
"place_type": self.place_type.to_struct(),
"code": self.code,
"alt_loc": [al.to_struct() for al in self.alt_loc],
@ -169,8 +171,8 @@ class Place(CitationBase, NoteBase, MediaBase, UrlBase, PrimaryObject):
struct.get("long", default.long),
struct.get("lat", default.lat),
[PlaceRef.from_struct(pr) for pr in struct.get("placeref_list", default.placeref_list)],
struct.get("name", default.name),
struct.get("alt_names", default.alt_names),
PlaceName.from_struct(struct.get("name", {})),
[PlaceName.from_struct(an) for an in struct.get("alt_names", default.alt_names)],
PlaceType.from_struct(struct.get("place_type", {})),
struct.get("code", default.code),
[Location.from_struct(al) for al in struct.get("alt_loc", default.alt_loc)],
@ -192,7 +194,7 @@ class Place(CitationBase, NoteBase, MediaBase, UrlBase, PrimaryObject):
:type data: tuple
"""
(self.handle, self.gramps_id, self.title, self.long, self.lat,
placeref_list, self.name, self.alt_names, the_type, self.code,
placeref_list, name, alt_names, the_type, self.code,
alt_loc, urls, media_list, citation_list, note_list,
self.change, tag_list, self.private) = data
@ -200,6 +202,8 @@ class Place(CitationBase, NoteBase, MediaBase, UrlBase, PrimaryObject):
self.place_type.unserialize(the_type)
self.alt_loc = [Location().unserialize(al) for al in alt_loc]
self.placeref_list = [PlaceRef().unserialize(pr) for pr in placeref_list]
self.name = PlaceName().unserialize(name)
self.alt_names = [PlaceName().unserialize(an) for an in alt_names]
UrlBase.unserialize(self, urls)
MediaBase.unserialize(self, media_list)
CitationBase.unserialize(self, citation_list)
@ -214,7 +218,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.name, self.gramps_id]
return [self.long, self.lat, self.title, self.gramps_id]
def get_text_data_child_list(self):
"""
@ -224,7 +228,8 @@ class Place(CitationBase, NoteBase, MediaBase, UrlBase, PrimaryObject):
:rtype: list
"""
ret = self.media_list + self.alt_loc + self.urls
ret = (self.media_list + self.alt_loc + self.urls +
self.name + self.alt_names)
return ret
def get_citation_child_list(self):
@ -306,8 +311,8 @@ class Place(CitationBase, NoteBase, MediaBase, UrlBase, PrimaryObject):
"""
Set the name of the Place object.
:param title: name to assign to the Place
:type title: str
:param name: name to assign to the Place
:type name: PlaceName
"""
self.name = name
@ -316,7 +321,7 @@ class Place(CitationBase, NoteBase, MediaBase, UrlBase, PrimaryObject):
Return the name of the Place object.
:returns: Returns the name of the Place
:rtype: str
:rtype: PlaceName
"""
return self.name
@ -325,7 +330,7 @@ class Place(CitationBase, NoteBase, MediaBase, UrlBase, PrimaryObject):
Return a list of all names of the Place object.
:returns: Returns a list of all names of the Place
:rtype: list
:rtype: list of PlaceName
"""
return [self.name] + self.alt_names
@ -485,7 +490,7 @@ class Place(CitationBase, NoteBase, MediaBase, UrlBase, PrimaryObject):
Return a list of alternative names for the current Place.
:returns: Returns the alternative names for the Place
:rtype: list of strings
:rtype: list of PlaceName
"""
return self.alt_names
@ -495,7 +500,7 @@ class Place(CitationBase, NoteBase, MediaBase, UrlBase, PrimaryObject):
:param name_list: The list of names to assign to the Place's internal
list.
:type name_list: list of strings
:type name_list: list of PlaceName
"""
self.alt_names = name_list

214
gramps/gen/lib/placename.py Normal file
View File

@ -0,0 +1,214 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2007 Donald N. Allingham
# Copyright (C) 2015 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
"""
Place name class for Gramps
"""
#-------------------------------------------------------------------------
#
# GRAMPS modules
#
#-------------------------------------------------------------------------
from .secondaryobj import SecondaryObject
from .datebase import DateBase
from .const import IDENTICAL, EQUAL, DIFFERENT
from .handle import Handle
#-------------------------------------------------------------------------
#
# Place Name
#
#-------------------------------------------------------------------------
class PlaceName(SecondaryObject, DateBase):
"""
Place name class.
This class is for keeping information about place names.
"""
def __init__(self, source=None):
"""
Create a new PlaceName instance, copying from the source if present.
"""
DateBase.__init__(self, source)
self.value = ''
self.lang = ''
def serialize(self):
"""
Convert the object to a serialized tuple of data.
"""
return (
self.value,
DateBase.serialize(self),
self.lang
)
def to_struct(self):
"""
Convert the data held in this object to a structure (eg,
struct) that represents all the data elements.
This method is used to recursively convert the object into a
self-documenting form that can easily be used for various
purposes, including diffs and queries.
These structures may be primitive Python types (string,
integer, boolean, etc.) or complex Python types (lists,
tuples, or dicts). If the return type is a dict, then the keys
of the dict match the fieldname of the object. If the return
struct (or value of a dict key) is a list, then it is a list
of structs. Otherwise, the struct is just the value of the
attribute.
:returns: Returns a struct containing the data of the object.
:rtype: dict
"""
return {
"_class": "PlaceName",
"value": self.value,
"date": DateBase.to_struct(self),
"lang": self.lang
}
@classmethod
def from_struct(cls, struct):
"""
Given a struct data representation, return a serialized object.
:returns: Returns a serialized object
"""
default = PlaceName()
return (
struct.get("value", default.value),
DateBase.from_struct(struct.get("date", {})),
struct.get("lang", default.lang)
)
def unserialize(self, data):
"""
Convert a serialized tuple of data to an object.
"""
(self.value, date, self.lang) = data
DateBase.unserialize(self, date)
return self
def get_text_data_list(self):
"""
Return the list of all textual attributes of the object.
:returns: Returns the list of all textual attributes of the object.
:rtype: list
"""
return [self.value]
def get_text_data_child_list(self):
"""
Return the list of child objects that may carry textual data.
:returns: Returns the list of child objects that may carry textual data.
:rtype: list
"""
return []
def get_citation_child_list(self):
"""
Return the list of child secondary objects that may refer citations.
:returns: Returns the list of child secondary child objects that may
refer citations.
:rtype: list
"""
return []
def get_note_child_list(self):
"""
Return the list of child secondary objects that may refer notes.
:returns: Returns the list of child secondary child objects that may
refer notes.
:rtype: list
"""
return []
def get_referenced_handles(self):
"""
Return the list of (classname, handle) tuples for all directly
referenced primary objects.
:returns: Returns the list of (classname, handle) tuples for referenced
objects.
:rtype: list
"""
return []
def get_handle_referents(self):
"""
Return the list of child objects which may, directly or through their
children, reference primary objects.
:returns: Returns the list of objects referencing primary objects.
:rtype: list
"""
return []
def is_equivalent(self, other):
"""
Return if this eventref is equivalent, that is agrees in handle and
role, to other.
:param other: The eventref to compare this one to.
:type other: PlaceName
:returns: Constant indicating degree of equivalence.
:rtype: int
"""
if (self.value != other.value or
self.date != other.date or
self.lang != other.lang):
return DIFFERENT
else:
if self.is_equal(other):
return IDENTICAL
else:
return EQUAL
def set_value(self, value):
"""
Set the name for the PlaceName instance.
"""
self.value = value
def get_value(self):
"""
Return the name for the PlaceName instance.
"""
return self.value
def set_language(self, lang):
"""
Set the language for the PlaceName instance.
"""
self.lang = lang
def get_language(self):
"""Return the language for the PlaceName instance."""
return self.lang

View File

@ -35,22 +35,29 @@ def get_location_list(db, place, date=None):
if date is None:
date = Today()
visited = [place.handle]
lines = [(place.name, place.get_type())]
lines = [(__get_name(place, date), place.get_type())]
while True:
handle = None
for placeref in place.get_placeref_list():
ref_date = placeref.get_date_object()
if ref_date.is_empty() or date.match(ref_date):
handle = placeref.ref
break
if handle is None or handle in visited:
break
place = db.get_place_from_handle(handle)
if place is None:
break
visited.append(handle)
lines.append((place.name, place.get_type()))
lines.append((__get_name(place, date), place.get_type()))
return lines
def __get_name(place, date):
for place_name in place.get_all_names():
name_date = place_name.get_date_object()
if name_date.is_empty() or date.match(name_date):
return place_name.get_value()
#-------------------------------------------------------------------------
#
# get_main_location
@ -77,7 +84,7 @@ def get_locations(db, place):
containing dictionaries of place types and names.
"""
locations = []
todo = [(place, [(int(place.get_type()), place.get_all_names())],
todo = [(place, [(int(place.get_type()), __get_all_names(place))],
[place.handle])]
while len(todo):
place, tree, visited = todo.pop()
@ -86,13 +93,16 @@ def get_locations(db, place):
parent_place = db.get_place_from_handle(parent.ref)
if parent_place is not None:
parent_tree = tree + [(int(parent_place.get_type()),
parent_place.get_all_names())]
__get_all_names(parent_place))]
parent_visited = visited + [parent.ref]
todo.append((parent_place, parent_tree, parent_visited))
if len(place.get_placeref_list()) == 0:
locations.append(dict(tree))
return locations
def __get_all_names(place):
return [name.get_value() for name in place.get_all_names()]
#-------------------------------------------------------------------------
#
# located_in

View File

@ -576,7 +576,7 @@ class ClipPlaceRef(ClipObjWrapper):
base = self._db.get_place_from_handle(self._obj.ref)
if base:
self._title = base.gramps_id
self._value = str(base.get_name())
self._value = place_displayer.display(self._db, base)
class ClipName(ClipObjWrapper):
@ -594,6 +594,22 @@ class ClipName(ClipObjWrapper):
self._title = str(self._obj.get_type())
self._value = self._obj.get_name()
class ClipPlaceName(ClipObjWrapper):
DROP_TARGETS = [DdTargets.PLACENAME]
DRAG_TARGET = DdTargets.PLACENAME
ICON = ICONS['name']
def __init__(self, dbstate, obj):
super(ClipPlaceName, self).__init__(dbstate, obj)
self._type = _("Place Name")
self.refresh()
def refresh(self):
if self._obj:
self._title = get_date(self._obj)
self._value = self._obj.get_value()
class ClipSurname(ClipObjWrapper):
DROP_TARGETS = [DdTargets.SURNAME]
@ -1099,6 +1115,7 @@ class ClipboardListView(object):
self.register_wrapper_class(ClipAttribute)
self.register_wrapper_class(ClipFamilyAttribute)
self.register_wrapper_class(ClipName)
self.register_wrapper_class(ClipPlaceName)
self.register_wrapper_class(ClipRepositoryLink)
self.register_wrapper_class(ClipMediaObj)
self.register_wrapper_class(ClipMediaRef)

View File

@ -138,6 +138,7 @@ class _DdTargets(object):
self.NAME = _DdType(self, 'name')
self.NOTE_LINK = _DdType(self, 'note-link')
self.PLACE_LINK = _DdType(self, 'place-link')
self.PLACENAME = _DdType(self, 'placename')
self.PLACEREF = _DdType(self, 'placeref')
self.REPO_LINK = _DdType(self, 'repo-link')
self.REPOREF = _DdType(self, 'reporef')
@ -149,7 +150,7 @@ class _DdTargets(object):
self.PERSONREF = _DdType(self, 'personref')
self.SOURCEREF = _DdType(self, 'srcref')
self.SOURCE_LINK = _DdType(self, 'source-link')
self.SRCATTRIBUTE = _DdType(self, 'sattr')
self.SRCATTRIBUTE = _DdType(self, 'sattr')
self.URL = _DdType(self, 'url')
self.SURNAME = _DdType(self, 'surname')
self.CITATION_LINK = _DdType(self, 'citation-link')
@ -170,6 +171,7 @@ class _DdTargets(object):
self.NOTE_LINK,
self.PLACE_LINK,
self.PLACEREF,
self.PLACENAME,
self.PERSON_LINK,
self.FAMILY_LINK,
self.LINK_LIST,

View File

@ -35,7 +35,6 @@ from .childmodel import ChildModel
from .grampstab import GrampsTab
from .embeddedlist import EmbeddedList, TEXT_COL, MARKUP_COL, ICON_COL
from .addrembedlist import AddrEmbedList
from .altnameembedlist import AltNameEmbedList
from .attrembedlist import AttrEmbedList
from .backreflist import BackRefList
from .eventattrembedlist import EventAttrEmbedList
@ -57,6 +56,7 @@ from .personeventembedlist import PersonEventEmbedList
from .personrefembedlist import PersonRefEmbedList
from .personbackreflist import PersonBackRefList
from .placebackreflist import PlaceBackRefList
from .placenameembedlist import PlaceNameEmbedList
from .placerefembedlist import PlaceRefEmbedList
from .repoembedlist import RepoEmbedList
from .surnametab import SurnameTab

View File

@ -2,6 +2,7 @@
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2006 Donald N. Allingham
# Copyright (C) 2015 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
@ -32,60 +33,86 @@ from gi.repository import GObject, GLib
# Gramps classes
#
#-------------------------------------------------------------------------
from gramps.gen.lib import PlaceName
from gramps.gen.errors import WindowActiveError
from .altnamemodel import AltNameModel
from ...ddtargets import DdTargets
from .placenamemodel import PlaceNameModel
from .embeddedlist import EmbeddedList, TEXT_COL, MARKUP_COL, ICON_COL
#-------------------------------------------------------------------------
#
# AltNameEmbedList
# PlaceNameEmbedList
#
#-------------------------------------------------------------------------
class AltNameEmbedList(EmbeddedList):
class PlaceNameEmbedList(EmbeddedList):
_HANDLE_COL = 0
_HANDLE_COL = 3
_DND_TYPE = DdTargets.PLACENAME
_MSG = {
'add' : _('Create and add a new place name'),
'del' : _('Remove the existing place name'),
'edit' : _('Edit the selected place name'),
'up' : _('Move the selected place name upwards'),
'down' : _('Move the selected place name downwards'),
}
#index = column in model. Value =
# (name, sortcol in model, width, markup/text, weigth_col
_column_names = [
(_('Place Name'), 0, 250, TEXT_COL, -1, None),
(_('Name'), 0, 250, TEXT_COL, -1, None),
(_('Date'), 1, 250, TEXT_COL, -1, None),
(_('Language'), 2, 100, TEXT_COL, -1, None),
]
def __init__(self, dbstate, uistate, track, data):
self.data = data
EmbeddedList.__init__(self, dbstate, uistate, track,
_('Alternative Names'), AltNameModel,
_('Alternative Names'), PlaceNameModel,
move_buttons=True)
def get_data(self):
return self.data
def column_order(self):
return ((1, 0),)
return ((1, 0), (1, 1), (1, 2))
def add_button_clicked(self, obj):
"""
Called when the Add button is clicked.
"""
pname = PlaceName()
try:
from .. import EditPlaceName
EditPlaceName(self.dbstate, self.uistate, self.track,
self.get_data(), -1, self.add_callback)
pname, self.add_callback)
except WindowActiveError:
pass
return
def add_callback(self, place_name):
def add_callback(self, pname):
"""
Called to update the screen when a new place name is added.
"""
data = self.get_data()
data.append(pname)
self.rebuild()
GLib.idle_add(self.tree.scroll_to_cell, len(data) - 1)
def edit_button_clicked(self, obj):
place_name = self.get_selected()
if place_name:
"""
Called with the Edit button is clicked.
"""
pname = self.get_selected()
if pname:
try:
from .. import EditPlaceName
data = self.get_data()
EditPlaceName(self.dbstate, self.uistate, self.track,
data, data.index(place_name), self.edit_callback)
pname, self.edit_callback)
except WindowActiveError:
pass
return
def edit_callback(self, place_name):
def edit_callback(self, name):
"""
Called to update the screen when the place name changes.
"""
self.rebuild()

View File

@ -1,7 +1,7 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2014 Nick Hall
# Copyright (C) 2014-2015 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
@ -30,17 +30,21 @@ from gi.repository import Gtk
# Gramps classes
#
#-------------------------------------------------------------------------
from gramps.gen.datehandler import get_date
#-------------------------------------------------------------------------
#
# AltNameModel
# PlaceNameModel
#
#-------------------------------------------------------------------------
class AltNameModel(Gtk.ListStore):
class PlaceNameModel(Gtk.ListStore):
def __init__(self, place_name_list, db):
Gtk.ListStore.__init__(self, str)
def __init__(self, obj_list, db):
Gtk.ListStore.__init__(self, str, str, str, object)
self.db = db
for place_name in place_name_list:
self.append(row=[place_name])
for obj in obj_list:
self.append(row=[obj.get_value(),
get_date(obj),
obj.get_language(),
obj,
])

View File

@ -2,6 +2,7 @@
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2006 Donald N. Allingham
# Copyright (C) 2015 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
@ -45,7 +46,7 @@ class PlaceRefModel(Gtk.ListStore):
for obj in obj_list:
place = self.db.get_place_from_handle(obj.ref)
self.append(row=[place.get_gramps_id(),
place.get_name(),
place.get_name().get_value(),
str(place.get_type()),
displayer.display(obj.date),
obj, ])

View File

@ -3,7 +3,7 @@
#
# Copyright (C) 2000-2006 Donald N. Allingham
# Copyright (C) 2009 Gary Burton
# Copyright (C) 2010 Nick Hall
# Copyright (C) 2010,2015 Nick Hall
# Copyright (C) 2011 Tim G L lyons
#
# This program is free software; you can redistribute it and/or modify
@ -47,7 +47,7 @@ from gramps.gen.lib import NoteType, Place
from gramps.gen.db import DbTxn
from gramps.gen.utils.location import get_location_list
from .editprimary import EditPrimary
from .displaytabs import (PlaceRefEmbedList, AltNameEmbedList,
from .displaytabs import (PlaceRefEmbedList, PlaceNameEmbedList,
LocationEmbedList, CitationEmbedList,
GalleryTab, NoteTab, WebEmbedList, PlaceBackRefList)
from ..widgets import (MonitoredEntry, PrivacyButton, MonitoredTagList,
@ -114,7 +114,8 @@ class EditPlace(EditPrimary):
self.db.readonly)
self.name = MonitoredEntry(self.top.get_object("name_entry"),
self.obj.set_name, self.obj.get_name,
self.obj.get_name().set_value,
self.obj.get_name().get_value,
self.db.readonly,
changed=self.name_changed)
@ -195,10 +196,10 @@ class EditPlace(EditPrimary):
self._add_tab(notebook, self.placeref_list)
self.track_ref_for_deletion("placeref_list")
self.alt_name_list = AltNameEmbedList(self.dbstate,
self.uistate,
self.track,
self.obj.alt_names)
self.alt_name_list = PlaceNameEmbedList(self.dbstate,
self.uistate,
self.track,
self.obj.alt_names)
self._add_tab(notebook, self.alt_name_list)
self.track_ref_for_deletion("alt_name_list")
@ -253,15 +254,9 @@ class EditPlace(EditPrimary):
def save(self, *obj):
self.ok_button.set_sensitive(False)
if self.object_is_empty():
ErrorDialog(_("Cannot save place"),
_("No data exists for this place. Please "
"enter data or cancel the edit."))
self.ok_button.set_sensitive(True)
return
if self.obj.get_name().strip() == '':
msg1 = _("Cannot save location. Name not entered.")
if self.obj.get_name().get_value().strip() == '':
msg1 = _("Cannot save place. Name not entered.")
msg2 = _("You must enter a name before saving.")
ErrorDialog(msg1, msg2)
self.ok_button.set_sensitive(True)

View File

@ -2,7 +2,7 @@
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2006 Donald N. Allingham
# Copyright (C) 2014 Nick Hall
# Copyright (C) 2014-2015 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
@ -31,9 +31,10 @@ from gi.repository import Gtk
# Gramps modules
#
#-------------------------------------------------------------------------
from ..managedwindow import ManagedWindow
from .editsecondary import EditSecondary
from ..glade import Glade
from ..widgets import MonitoredDate, MonitoredEntry
from ..dialog import ErrorDialog
from ..display import display_help
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
@ -42,62 +43,53 @@ _ = glocale.translation.gettext
# EditPlaceName class
#
#-------------------------------------------------------------------------
class EditPlaceName(ManagedWindow):
def __init__(self, dbstate, uistate, track, data, index, callback):
ManagedWindow.__init__(self, uistate, track, self.__class__)
self.data = data
self.index = index
self.callback = callback
class EditPlaceName(EditSecondary):
"""
Displays a dialog that allows the user to edit a place name.
"""
def __init__(self, dbstate, uistate, track, pname, callback):
EditSecondary.__init__(self, dbstate, uistate, track, pname, callback)
def _local_init(self):
self.width_key = 'interface.place-name-width'
self.height_key = 'interface.place-name-height'
window = Gtk.Dialog('', uistate.window,
Gtk.DialogFlags.DESTROY_WITH_PARENT, None)
self.top = Glade()
self.set_window(self.top.toplevel,
self.top.get_object("title"),
_('Place Name Editor'))
self.cancel_button = window.add_button(_('_Cancel'),
Gtk.ResponseType.CANCEL)
self.ok_button = window.add_button(_('_OK'),
Gtk.ResponseType.ACCEPT)
self.help_button = window.add_button(_('_Help'),
Gtk.ResponseType.HELP)
def _setup_fields(self):
self.value = MonitoredEntry(
self.top.get_object("value"), self.obj.set_value,
self.obj.get_value, self.db.readonly)
window.connect('response', self.response)
self.set_window(window, None, _('Place Name Editor'))
self.date = MonitoredDate(
self.top.get_object("date_entry"),
self.top.get_object("date_stat"),
self.obj.get_date_object(),
self.uistate,
self.track,
self.db.readonly)
hbox = Gtk.Box()
label = Gtk.Label(label=_('Place Name:'))
self.entry = Gtk.Entry()
if index >= 0:
self.entry.set_text(data[index])
hbox.pack_start(label, False, False, 4)
hbox.pack_start(self.entry, True, True, 4)
hbox.show_all()
window.vbox.pack_start(hbox, False, False, 4)
self.language = MonitoredEntry(
self.top.get_object("language"), self.obj.set_language,
self.obj.get_language, self.db.readonly)
self._set_size()
self.show()
def _connect_signals(self):
self.define_help_button(self.top.get_object('help'))
self.define_cancel_button(self.top.get_object('cancel'))
self.define_ok_button(self.top.get_object('ok'),self.save)
def response(self, obj, response_id):
if response_id == Gtk.ResponseType.CANCEL:
self.close()
if response_id == Gtk.ResponseType.ACCEPT:
self.save()
if response_id == Gtk.ResponseType.HELP:
display_help('', '')
def build_menu_names(self, obj):
return (_('Place Name'),_('Place Name Editor'))
def save(self, *obj):
place_name = self.entry.get_text()
if not place_name:
if not self.obj.get_value():
ErrorDialog(_("Cannot save place name"),
_("The place name cannot be empty"))
return
if self.index >= 0:
self.data[self.index] = place_name
else:
self.data.append(place_name)
if self.callback:
self.callback(place_name)
self.callback(self.obj)
self.close()

View File

@ -28,7 +28,7 @@ from .editreference import RefTab, EditReference
from ..glade import Glade
from ..widgets import (MonitoredDate, MonitoredEntry, MonitoredDataType,
PrivacyButton, MonitoredTagList)
from .displaytabs import (PlaceRefEmbedList, AltNameEmbedList,
from .displaytabs import (PlaceRefEmbedList, PlaceNameEmbedList,
LocationEmbedList, CitationEmbedList,
GalleryTab, NoteTab, WebEmbedList, PlaceBackRefList)
from gramps.gen.lib import NoteType
@ -37,6 +37,7 @@ from gramps.gen.errors import ValidationError
from gramps.gen.utils.place import conv_lat_lon
from gramps.gen.display.place import displayer as place_displayer
from gramps.gen.config import config
from ..dialog import ErrorDialog
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
@ -113,7 +114,8 @@ class EditPlaceRef(EditReference):
self.db.readonly)
self.name = MonitoredEntry(self.top.get_object("name_entry"),
self.source.set_name, self.source.get_name,
self.source.get_name().set_value,
self.source.get_name().get_value,
self.db.readonly,
changed=self.name_changed)
@ -197,10 +199,10 @@ class EditPlaceRef(EditReference):
self._add_tab(notebook, self.placeref_list)
self.track_ref_for_deletion("placeref_list")
self.alt_name_list = AltNameEmbedList(self.dbstate,
self.uistate,
self.track,
self.source.alt_names)
self.alt_name_list = PlaceNameEmbedList(self.dbstate,
self.uistate,
self.track,
self.source.alt_names)
self._add_tab(notebook, self.alt_name_list)
self.track_ref_for_deletion("alt_name_list")
@ -252,6 +254,14 @@ class EditPlaceRef(EditReference):
self._setup_notebook_tabs(notebook)
def save(self, *obj):
self.ok_button.set_sensitive(False)
if self.source.get_name().get_value().strip() == '':
msg1 = _("Cannot save place. Name not entered.")
msg2 = _("You must enter a name before saving.")
ErrorDialog(msg1, msg2)
self.ok_button.set_sensitive(True)
return
if self.source.handle:
with DbTxn(_("Modify Place"), self.db) as trans:

View File

@ -0,0 +1,239 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.18.3 -->
<interface>
<requires lib="gtk+" version="3.10"/>
<requires lib="grampswidgets" version="0.0"/>
<object class="GtkDialog" id="editplacename">
<property name="can_focus">False</property>
<property name="type_hint">dialog</property>
<child internal-child="vbox">
<object class="GtkBox" id="dialog-vbox13">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child internal-child="action_area">
<object class="GtkButtonBox" id="dialog-action_area13">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="layout_style">end</property>
<child>
<object class="GtkButton" id="cancel">
<property name="label" translatable="yes">_Cancel</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_underline">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="ok">
<property name="label" translatable="yes">_OK</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="has_tooltip">True</property>
<property name="tooltip_markup">Accept changes and close window</property>
<property name="tooltip_text" translatable="yes">Accept changes and close window</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="help">
<property name="label" translatable="yes">_Help</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_underline">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="vbox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkGrid" id="table26">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">12</property>
<property name="row_spacing">6</property>
<property name="column_spacing">12</property>
<child>
<object class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">_Date:</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<property name="mnemonic_widget">date_entry</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Language:</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<property name="mnemonic_widget">language</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="UndoableEntry" id="language">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">Mail address.
Note: Use Residence Event for genealogical address data.</property>
<property name="hexpand">True</property>
<property name="invisible_char">●</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkButton" id="date_stat">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="has_tooltip">True</property>
<property name="tooltip_text" translatable="yes">Invoke date editor</property>
<property name="relief">none</property>
<child>
<object class="GtkImage" id="date_stat_child">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">gramps-date</property>
<child internal-child="accessible">
<object class="AtkObject" id="date_stat_child-atkobject">
<property name="AtkObject::accessible-description" translatable="yes">Date</property>
</object>
</child>
</object>
</child>
<accessibility>
<relation type="labelled-by" target="label2"/>
</accessibility>
<child internal-child="accessible">
<object class="AtkObject" id="date_stat-atkobject">
<property name="AtkObject::accessible-name" translatable="yes">Date</property>
</object>
</child>
<accelerator key="d" signal="activate" modifiers="GDK_CONTROL_MASK"/>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="ValidatableMaskedEntry" id="date_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">Date at which the address is valid.</property>
<property name="hexpand">True</property>
<property name="invisible_char">●</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="UndoableEntry" id="value">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">Mail address.
Note: Use Residence Event for genealogical address data.</property>
<property name="hexpand">True</property>
<property name="invisible_char">●</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Name:</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<property name="mnemonic_widget">language</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<action-widgets>
<action-widget response="-6">cancel</action-widget>
<action-widget response="-5">ok</action-widget>
<action-widget response="-11">help</action-widget>
</action-widgets>
</object>
</interface>

View File

@ -118,7 +118,7 @@ class PlaceBaseModel(object):
return str(data[2])
def column_name(self, data):
return str(data[6])
return str(data[6][0])
def column_longitude(self, data):
if not data[3]:

View File

@ -731,6 +731,21 @@ class GrampsXmlWriter(UpdateCallback):
self.write_date(date, index+1)
self.g.write('%s</placeref>\n' % sp)
def dump_place_name(self, place_name, index=1):
sp = " " * index
value = place_name.get_value()
date = place_name.get_date_object()
lang = place_name.get_language()
self.g.write('%s<name value="%s"' % (sp, self.fix(value)))
if lang:
self.g.write(' lang="%s"' % self.fix(lang))
if date.is_empty():
self.g.write('/>\n')
else:
self.g.write('>\n')
self.write_date(date, index+1)
self.g.write('%s</name>\n' % sp)
def write_event(self,event,index=1):
if not event:
return
@ -1196,9 +1211,7 @@ class GrampsXmlWriter(UpdateCallback):
def write_place_obj(self, place, index=1):
self.write_primary_tag("placeobj", place, index, close=False)
pname = self.fix(place.get_name())
ptype = self.fix(place.get_type().xml_str())
self.g.write(' name="%s"' % pname)
self.g.write(' type="%s"' % ptype)
self.g.write('>\n')
@ -1206,8 +1219,10 @@ class GrampsXmlWriter(UpdateCallback):
code = self.fix(place.get_code())
self.write_line_nofix("ptitle", title, index+1)
self.write_line_nofix("code", code, index+1)
for name in place.get_alternative_names():
self.write_line("alt_name", name, index+1)
self.dump_place_name(place.get_name(), index+1)
for pname in place.get_alternative_names():
self.dump_place_name(pname, index+1)
longitude = self.fix(place.get_longitude())
lat = self.fix(place.get_latitude())

View File

@ -126,7 +126,7 @@ class Locations(Gramplet, DbGUIElement):
place_date = get_date(placeref)
place_sort = '%012d' % placeref.get_date_object().get_sort_value()
parent_place = self.dbstate.db.get_place_from_handle(placeref.ref)
parent_name = parent_place.get_name()
parent_name = parent_place.get_name().get_value()
parent_type = str(parent_place.get_type())
parent_node = self.model.add([placeref.ref,

View File

@ -1,6 +1,6 @@
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2011 Nick Hall
# Copyright (C) 2011,2015 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
@ -108,7 +108,7 @@ class PlaceDetails(Gramplet):
self.title.set_text(title)
self.clear_grid()
self.add_row(_('Name'), place.get_name())
self.add_row(_('Name'), place.get_name().get_value())
self.add_row(_('Type'), place.get_type())
self.display_separator()
self.display_alt_names(place)
@ -125,7 +125,7 @@ class PlaceDetails(Gramplet):
"""
Display alternative names for the place.
"""
alt_names = place .get_alternative_names()
alt_names = [name.get_value() for name in place.get_alternative_names()]
if len(alt_names) > 0:
self.add_row(_('Alternative Names'), '\n'.join(alt_names))

View File

@ -47,15 +47,15 @@ LOG = logging.getLogger(".ImportXML")
#
#-------------------------------------------------------------------------
from gramps.gen.mime import get_type
from gramps.gen.lib import (Address, Attribute, AttributeType, ChildRef,
ChildRefType, Citation, Date, DateError, Event, EventRef,
EventRoleType, EventType, Family, LdsOrd, Location,
MediaObject, MediaRef, Name, NameOriginType,
NameType, Note, NoteType, Person, PersonRef,
Place, RepoRef, Repository, Researcher, Source,
SrcAttribute, SrcAttributeType, PlaceType,
StyledText, StyledTextTag, StyledTextTagType,
Surname, Tag, Url, PlaceRef)
from gramps.gen.lib import (Address, Attribute, AttributeType, ChildRef,
ChildRefType, Citation, Date, DateError, Event,
EventRef, EventRoleType, EventType, Family, LdsOrd,
Location, MediaObject, MediaRef, Name,
NameOriginType, NameType, Note, NoteType, Person,
PersonRef, Place, PlaceName, PlaceRef, PlaceType,
RepoRef, Repository, Researcher, Source,
SrcAttribute, SrcAttributeType, StyledText,
StyledTextTag, StyledTextTagType, Surname, Tag, Url)
from gramps.gen.db import DbTxn
from gramps.gen.db.write import CLASS_TO_KEY_MAP
from gramps.gen.errors import GrampsImportError
@ -539,7 +539,10 @@ class GrampsParser(UpdateCallback):
self.attribute = None
self.srcattribute = None
self.placeobj = None
self.placeref = None
self.place_name = None
self.locations = 0
self.place_names = 0
self.place_map = {}
self.place_import = PlaceImport(self.db)
@ -1149,11 +1152,14 @@ class GrampsParser(UpdateCallback):
self.inaugurate_id(attrs.get('id'), PLACE_KEY, self.placeobj)
self.placeobj.private = bool(attrs.get("priv"))
self.placeobj.change = int(attrs.get('change', self.change))
if self.__xml_version >= (1, 6, 0):
self.placeobj.name = attrs.get('name', '')
if 'type' in attrs:
self.placeobj.place_type.set_from_xml_str(attrs.get('type'))
if self.__xml_version == (1, 6, 0):
place_name = PlaceName()
place_name.set_value(attrs.get('name', ''))
self.placeobj.name = place_name
if 'type' in attrs:
self.placeobj.place_type.set_from_xml_str(attrs.get('type'))
self.info.add('new-object', PLACE_KEY, self.placeobj)
self.place_names = 0
# GRAMPS LEGACY: title in the placeobj tag
self.placeobj.title = attrs.get('title', '')
@ -1192,8 +1198,9 @@ class GrampsParser(UpdateCallback):
for level, name in enumerate(location):
if name:
break
self.placeobj.set_name(name)
place_name = PlaceName()
place_name.set_value(name)
self.placeobj.set_name(place_name)
type_num = 7 - level if name else PlaceType.UNKNOWN
self.placeobj.set_type(PlaceType(type_num))
codes = [attrs.get('postal'), attrs.get('phone')]
@ -1692,6 +1699,23 @@ class GrampsParser(UpdateCallback):
self.person.add_family_handle(handle)
def start_name(self, attrs):
if self.person:
self.start_person_name(attrs)
else:
self.start_place_name(attrs)
def start_place_name(self, attrs):
self.place_name = PlaceName()
self.place_name.set_value(attrs["value"])
if "lang" in attrs:
self.place_name.set_language(attrs["lang"])
if self.place_names == 0:
self.placeobj.set_name(self.place_name)
else:
self.placeobj.add_alternative_name(self.place_name)
self.place_names += 1
def start_person_name(self, attrs):
if not self.in_witness:
self.name = Name()
name_type = attrs.get('type', "Birth Name")
@ -2321,8 +2345,10 @@ class GrampsParser(UpdateCallback):
date_value = self.name.get_date_object()
elif self.event:
date_value = self.event.get_date_object()
else:
elif self.placeref:
date_value = self.placeref.get_date_object()
elif self.place_name:
date_value = self.place_name.get_date_object()
start = attrs['start'].split('-')
stop = attrs['stop'].split('-')
@ -2411,8 +2437,10 @@ class GrampsParser(UpdateCallback):
date_value = self.name.get_date_object()
elif self.event:
date_value = self.event.get_date_object()
else:
elif self.placeref:
date_value = self.placeref.get_date_object()
elif self.place_name:
date_value = self.place_name.get_date_object()
bce = 1
val = attrs['val']
@ -2585,7 +2613,9 @@ class GrampsParser(UpdateCallback):
self.placeobj.code = tag
def stop_alt_name(self, tag):
self.placeobj.add_alternative_name(tag)
place_name = PlaceName()
place_name.set_value(tag)
self.placeobj.add_alternative_name(place_name)
def stop_placeobj(self, *tag):
self.db.commit_place(self.placeobj, self.trans,
@ -2658,7 +2688,16 @@ class GrampsParser(UpdateCallback):
self.event.get_change_time())
self.event = None
def stop_name(self, tag):
def stop_name(self, attrs):
if self.person:
self.stop_person_name(attrs)
else:
self.stop_place_name(attrs)
def stop_place_name(self, tag):
self.place_name = None
def stop_person_name(self, tag):
if self.in_witness:
# Parse witnesses created by older gramps
note = Note()

View File

@ -33,5 +33,5 @@
# Public Constants
#
#------------------------------------------------------------------------
GRAMPS_XML_VERSION_TUPLE = (1,6,0) # version for Gramps 4.1
GRAMPS_XML_VERSION_TUPLE = (1, 7, 0) # version for Gramps 4.2
GRAMPS_XML_VERSION = '.'.join(str(i) for i in GRAMPS_XML_VERSION_TUPLE)

View File

@ -27,7 +27,7 @@ Helper class for importing places.
# GRAMPS modules
#
#-------------------------------------------------------------------------
from gramps.gen.lib import Place, PlaceType, PlaceRef
from gramps.gen.lib import Place, PlaceName, PlaceType, PlaceRef
from gramps.gen.constfunc import handle2internal
#-------------------------------------------------------------------------
@ -106,7 +106,9 @@ class PlaceImport(object):
Add a missing place to the database.
"""
place = Place()
place.name = name
place_name = PlaceName()
place_name.set_value(name)
place.name = place_name
place.title = title
place.place_type = PlaceType(7-type_num)
if parent is not None: