Tree metadata support
This commit is contained in:
parent
41720c5a7e
commit
ebb72b6803
@ -22,7 +22,7 @@
|
||||
-->
|
||||
<xsl:stylesheet version="1.0"
|
||||
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
||||
xmlns:g="http://gramps-project.org/xml/1.4.0/">
|
||||
xmlns:g="http://gramps-project.org/xml/1.7.2/">
|
||||
|
||||
<!--
|
||||
Transform a Gramps XML file into "canonical form", that is strip the
|
||||
@ -33,8 +33,7 @@
|
||||
|
||||
<xsl:output method="xml"/>
|
||||
|
||||
<xsl:param name="replace_handles"/>
|
||||
<xsl:key name="primary_obj" match="g:person|g:family|g:event|g:placeobj|g:source|g:repository|g:object|g:note|g:tag" use="@handle"/>
|
||||
<xsl:key name="primary_obj" match="g:person|g:family|g:event|g:placeobj|g:source|g:repository|g:object|g:note|g:tag|g:researcher" use="@handle"/>
|
||||
|
||||
<xsl:template match="*|@*|text()">
|
||||
<xsl:copy>
|
||||
@ -45,7 +44,7 @@
|
||||
<xsl:template match="@change">
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="g:researcher">
|
||||
<xsl:template match="*[local-name()='provenance' or local-name()='tree' or local-name()='researcher']">
|
||||
<xsl:copy/>
|
||||
</xsl:template>
|
||||
|
||||
@ -57,6 +56,7 @@
|
||||
</xsl:copy>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:param name="replace_handles"/>
|
||||
<xsl:template match="@handle">
|
||||
<xsl:choose>
|
||||
<xsl:when test="$replace_handles='ID'">
|
||||
|
@ -67,12 +67,11 @@ DATABASE
|
||||
<!-- ************************************************************
|
||||
HEADER
|
||||
|
||||
A <header> consists of <created> (information about this
|
||||
genealogical database) and <researcher> (information about the
|
||||
person who created this genealogical database)
|
||||
A <header> consists of <created> and several additional sections
|
||||
with high level information about this genealogical database.
|
||||
-->
|
||||
|
||||
<!ELEMENT header (created, researcher?, mediapath?)>
|
||||
<!ELEMENT header (created, origin?, comment?, tree?, researcher?, mediapath?)>
|
||||
|
||||
<!ELEMENT created EMPTY>
|
||||
<!ATTLIST created
|
||||
@ -80,8 +79,40 @@ HEADER
|
||||
version CDATA #REQUIRED
|
||||
>
|
||||
|
||||
<!--
|
||||
PROVENANCE identifies the database used to generate this export.
|
||||
-->
|
||||
|
||||
<!ELEMENT provenance (database-id, last-transaction-timestamp, comment>
|
||||
<!ELEMENT database-id (#PCDATA)>
|
||||
<!ELEMENT last-transaction-timestamp (#PCDATA)>
|
||||
<!ELEMENT export-note (#PCDATA)>
|
||||
|
||||
<!--
|
||||
TREE contains information about the tree.
|
||||
-->
|
||||
|
||||
<!ELEMENT tree (name, copyright?, license?, description?,
|
||||
contributors?)>
|
||||
<!ATTLIST tree
|
||||
change CDATA #REQUIRED
|
||||
>
|
||||
<!ELEMENT name (#PCDATA)>
|
||||
<!ELEMENT copyright (#PCDATA)>
|
||||
<!ELEMENT license (#PCDATA)>
|
||||
<!ELEMENT description (#PCDATA)>
|
||||
<!ELEMENT contributors (#PCDATA)>
|
||||
|
||||
<!--
|
||||
RESEARCHER
|
||||
-->
|
||||
|
||||
<!ELEMENT researcher (resname?, resaddr?, reslocality?, rescity?, resstate?,
|
||||
rescountry?, respostal?, resphone?, resemail?)>
|
||||
<!ATTLIST researcher
|
||||
change CDATA #REQUIRED
|
||||
handle IDREF #IMPLIED
|
||||
>
|
||||
<!ELEMENT resname (#PCDATA)>
|
||||
<!ELEMENT resaddr (#PCDATA)>
|
||||
<!ELEMENT reslocality (#PCDATA)>
|
||||
|
@ -41,14 +41,25 @@
|
||||
<attribute name="date"><data type="date"/></attribute>
|
||||
<attribute name="version"><text/></attribute>
|
||||
</element>
|
||||
<element name="researcher">
|
||||
<optional>
|
||||
<ref name="researcher-content"/>
|
||||
</optional>
|
||||
</element>
|
||||
<optional>
|
||||
<element name="mediapath"><text/>
|
||||
</element>
|
||||
<element name="provenance">
|
||||
<ref name="provenance-content"/>
|
||||
</element>
|
||||
<element name="tree">
|
||||
<attribute name="change"><text/></attribute>
|
||||
<ref name="tree-content"/>
|
||||
</element>
|
||||
<element name="researcher">
|
||||
<attribute name="change"><text/></attribute>
|
||||
<optional><attribute name="handle">
|
||||
<data type="IDREF"/>
|
||||
</attribute></optional>
|
||||
<optional>
|
||||
<ref name="researcher-content"/>
|
||||
</optional>
|
||||
</element>
|
||||
<element name="mediapath"><text/>
|
||||
</element>
|
||||
</optional>
|
||||
</element>
|
||||
|
||||
@ -135,6 +146,22 @@
|
||||
</element></optional>
|
||||
</element></start>
|
||||
|
||||
<define name="provenance-content">
|
||||
<element name="database-id"><text/></element>
|
||||
<element name="last-transaction-timestamp"><text/></element>
|
||||
<element name="export-note"><text/></element>
|
||||
</define>
|
||||
|
||||
<define name="tree-content">
|
||||
<element name="name"><text/></element>
|
||||
<optional>
|
||||
<element name="copyright"><text/></element>
|
||||
<element name="license"><text/></element>
|
||||
<element name="description"><text/></element>
|
||||
<element name="contributors"><text/></element>
|
||||
</optional>
|
||||
</define>
|
||||
|
||||
<define name="researcher-content">
|
||||
<element name="resname"><text/></element>
|
||||
<optional><element name="resaddr"><text/></element></optional>
|
||||
|
@ -1,10 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE database PUBLIC "-//Gramps//DTD Gramps XML 1.7.1//EN"
|
||||
"http://gramps-project.org/xml/1.7.1/grampsxml.dtd">
|
||||
<database xmlns="http://gramps-project.org/xml/1.7.1/">
|
||||
<!DOCTYPE database PUBLIC "-//Gramps//DTD Gramps XML 1.7.2//EN"
|
||||
"http://gramps-project.org/xml/1.7.2/grampsxml.dtd">
|
||||
<database xmlns="http://gramps-project.org/xml/1.7.2/">
|
||||
<header>
|
||||
<created date="2017-08-08" version="5.1.0"/>
|
||||
<researcher>
|
||||
<created date="2023-07-31" version="5.2.0"/>
|
||||
<provenance>
|
||||
<database-id>64c83c93</database-id>
|
||||
<last-transaction-timestamp>1690844342.4027495</last-transaction-timestamp>
|
||||
</provenance>
|
||||
<tree change="1690843977">
|
||||
<name>example_gramps</name>
|
||||
<copyright>2001-2006 Donald Allingham, 2007-2023 The Gramps Developers</copyright>
|
||||
<license>Creative Commons Attribution-ShareAlike 2.5</license>
|
||||
<description>An example tree for use in producing documentation and with a lot of additional test data to help facilitate unit tests.</description>
|
||||
<contributors>Donald Allingham, The Gramps Developers</contributors>
|
||||
</tree>
|
||||
<researcher change="0">
|
||||
<resname>Alex Roitman,,,</resname>
|
||||
</researcher>
|
||||
<mediapath>{GRAMPS_RESOURCES}/doc/gramps/example/gramps</mediapath>
|
||||
|
@ -1424,6 +1424,30 @@ class DbReadBase:
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_last_transaction_time(self):
|
||||
"""
|
||||
Return timestamp of last database transaction.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_tree(self):
|
||||
"""
|
||||
Return the tree.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_researcher_handle(self):
|
||||
"""
|
||||
Return the researcher handle.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_researcher_person(self):
|
||||
"""
|
||||
Return the researcher person object.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_summary(self):
|
||||
"""
|
||||
Returns dictionary of summary item.
|
||||
@ -2044,3 +2068,33 @@ class DbWriteBase(DbReadBase):
|
||||
|
||||
person.birth_ref_index = birth_ref_index
|
||||
person.death_ref_index = death_ref_index
|
||||
|
||||
def set_tree(self, tree):
|
||||
"""
|
||||
Set the tree information.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def set_tree_change(self, change):
|
||||
"""
|
||||
Set the tree last change value.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def set_researcher_changed(self, change):
|
||||
"""
|
||||
Set the researcher last change value.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def set_researcher_handle(self, handle):
|
||||
"""
|
||||
Set the researcher handle.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def refresh_cached_tree_name(self):
|
||||
"""
|
||||
Check and update tree name if changed.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
@ -1614,3 +1614,31 @@ class DummyDb(
|
||||
A name for this database on this computer.
|
||||
"""
|
||||
return ""
|
||||
|
||||
def get_last_transaction_time(self):
|
||||
"""
|
||||
Timestamp of last database transaction.
|
||||
"""
|
||||
if not self.db_is_open:
|
||||
LOG.debug("database is closed")
|
||||
|
||||
def get_tree(self):
|
||||
"""
|
||||
Return the tree information.
|
||||
"""
|
||||
if not self.db_is_open:
|
||||
LOG.debug("database is closed")
|
||||
|
||||
def get_researcher_handle(self):
|
||||
"""
|
||||
Return the researcher handle.
|
||||
"""
|
||||
if not self.db_is_open:
|
||||
LOG.debug("database is closed")
|
||||
|
||||
def get_researcher_person(self):
|
||||
"""
|
||||
Return the researcher person object.
|
||||
"""
|
||||
if not self.db_is_open:
|
||||
LOG.debug("database is closed")
|
||||
|
@ -74,21 +74,22 @@ from ..updatecallback import UpdateCallback
|
||||
from .bookmarks import DbBookmarks
|
||||
|
||||
from ..utils.id import create_id
|
||||
from ..lib.researcher import Researcher
|
||||
from ..lib import (
|
||||
Tag,
|
||||
Media,
|
||||
Person,
|
||||
Family,
|
||||
Source,
|
||||
Citation,
|
||||
Event,
|
||||
Family,
|
||||
GenderStats,
|
||||
Media,
|
||||
NameOriginType,
|
||||
Note,
|
||||
Person,
|
||||
Place,
|
||||
Repository,
|
||||
Note,
|
||||
NameOriginType,
|
||||
Researcher,
|
||||
Source,
|
||||
Tag,
|
||||
Tree
|
||||
)
|
||||
from ..lib.genderstats import GenderStats
|
||||
from ..config import config
|
||||
from ..const import GRAMPS_LOCALE as glocale
|
||||
|
||||
@ -352,7 +353,8 @@ class DbGeneric(DbWriteBase, DbReadBase, UpdateCallback, Callback):
|
||||
"tag",
|
||||
]
|
||||
for op, signal in zip(
|
||||
["add", "update", "delete", "rebuild"], [(list,), (list,), (list,), None]
|
||||
["add", "update", "delete", "rebuild"],
|
||||
[(list,), (list,), (list,), None],
|
||||
)
|
||||
)
|
||||
|
||||
@ -368,6 +370,12 @@ class DbGeneric(DbWriteBase, DbReadBase, UpdateCallback, Callback):
|
||||
# 4. Signal for change in person group name, parameters are
|
||||
__signals__["person-groupname-rebuild"] = (str, str)
|
||||
|
||||
# 5. Signal for change in tree metadata
|
||||
__signals__["tree-data-changed"] = None
|
||||
|
||||
# 6. Signal for change in either researcher metadata or handle
|
||||
__signals__["researcher-changed"] = None
|
||||
|
||||
__callback_map = {}
|
||||
|
||||
VERSION = (20, 0, 0)
|
||||
@ -589,6 +597,10 @@ class DbGeneric(DbWriteBase, DbReadBase, UpdateCallback, Callback):
|
||||
self.surname_list = []
|
||||
self.genderStats = GenderStats() # can pass in loaded stats as dict
|
||||
self.owner = Researcher()
|
||||
self.owner_change = 0
|
||||
self.tree = Tree()
|
||||
self.tree_change = 0
|
||||
self.db_last_transaction = 0.0
|
||||
if directory:
|
||||
self.load(directory)
|
||||
|
||||
@ -653,6 +665,12 @@ class DbGeneric(DbWriteBase, DbReadBase, UpdateCallback, Callback):
|
||||
# Load metadata
|
||||
self.name_formats = self._get_metadata("name_formats")
|
||||
self.owner = self._get_metadata("researcher", default=Researcher())
|
||||
self.owner_change = self._get_metadata("researcher_change", default=0)
|
||||
self.tree = self._get_metadata("tree", default=Tree())
|
||||
self.tree_change = self._get_metadata("tree_change", default=0)
|
||||
self.db_last_transaction = self._get_metadata(
|
||||
"db_last_transaction", default=0.0
|
||||
)
|
||||
|
||||
# Load bookmarks
|
||||
self.bookmarks.load(self._get_metadata("bookmarks"))
|
||||
@ -712,6 +730,7 @@ class DbGeneric(DbWriteBase, DbReadBase, UpdateCallback, Callback):
|
||||
self.nmap_index = self._get_metadata("nmap_index", 0)
|
||||
|
||||
self.db_is_open = True
|
||||
self.refresh_cached_tree_name()
|
||||
|
||||
# Check on db version to see if we need upgrade or too new
|
||||
dbversion = int(self._get_metadata("version", default="0"))
|
||||
@ -721,7 +740,9 @@ class DbGeneric(DbWriteBase, DbReadBase, UpdateCallback, Callback):
|
||||
|
||||
if not self.readonly and dbversion < self.VERSION[0]:
|
||||
LOG.debug(
|
||||
"Schema upgrade required from %s to %s", dbversion, self.VERSION[0]
|
||||
"Schema upgrade required from %s to %s",
|
||||
dbversion,
|
||||
self.VERSION[0],
|
||||
)
|
||||
if force_schema_upgrade:
|
||||
self._gramps_upgrade(dbversion, directory, callback)
|
||||
@ -735,6 +756,12 @@ class DbGeneric(DbWriteBase, DbReadBase, UpdateCallback, Callback):
|
||||
"""
|
||||
return DbGenericUndo(self, self.undolog)
|
||||
|
||||
def refresh_cached_tree_name(self):
|
||||
name = self.get_dbname()
|
||||
if self.tree.get_name() != name:
|
||||
self.tree.set_name(name)
|
||||
self._save_tree()
|
||||
|
||||
def _close(self):
|
||||
"""
|
||||
Close database backend.
|
||||
@ -754,8 +781,8 @@ class DbGeneric(DbWriteBase, DbReadBase, UpdateCallback, Callback):
|
||||
Path(filename).touch()
|
||||
|
||||
# Save metadata
|
||||
self.refresh_cached_tree_name()
|
||||
self._set_metadata("name_formats", self.name_formats)
|
||||
self._set_metadata("researcher", self.owner)
|
||||
|
||||
# Bookmarks
|
||||
self._set_metadata("bookmarks", self.bookmarks.get())
|
||||
@ -827,7 +854,7 @@ class DbGeneric(DbWriteBase, DbReadBase, UpdateCallback, Callback):
|
||||
In DbGeneric, the database is in a text file at the path
|
||||
"""
|
||||
name = None
|
||||
if self._directory:
|
||||
if self._directory and self._directory != ":memory:":
|
||||
filepath = os.path.join(self._directory, "name.txt")
|
||||
try:
|
||||
with open(filepath, "r", encoding="utf8") as name_file:
|
||||
@ -1056,7 +1083,16 @@ class DbGeneric(DbWriteBase, DbReadBase, UpdateCallback, Callback):
|
||||
self.nid2user_format = self.__id2user_format(self.note_prefix)
|
||||
|
||||
def set_prefixes(
|
||||
self, person, media, family, source, citation, place, event, repository, note
|
||||
self,
|
||||
person,
|
||||
media,
|
||||
family,
|
||||
source,
|
||||
citation,
|
||||
place,
|
||||
event,
|
||||
repository,
|
||||
note,
|
||||
):
|
||||
self.set_person_id_prefix(person)
|
||||
self.set_media_id_prefix(media)
|
||||
@ -1815,22 +1851,38 @@ class DbGeneric(DbWriteBase, DbReadBase, UpdateCallback, Callback):
|
||||
|
||||
def add_person(self, person, trans, set_gid=True):
|
||||
return self._add_base(
|
||||
person, trans, set_gid, self.find_next_person_gramps_id, self.commit_person
|
||||
person,
|
||||
trans,
|
||||
set_gid,
|
||||
self.find_next_person_gramps_id,
|
||||
self.commit_person,
|
||||
)
|
||||
|
||||
def add_family(self, family, trans, set_gid=True):
|
||||
return self._add_base(
|
||||
family, trans, set_gid, self.find_next_family_gramps_id, self.commit_family
|
||||
family,
|
||||
trans,
|
||||
set_gid,
|
||||
self.find_next_family_gramps_id,
|
||||
self.commit_family,
|
||||
)
|
||||
|
||||
def add_event(self, event, trans, set_gid=True):
|
||||
return self._add_base(
|
||||
event, trans, set_gid, self.find_next_event_gramps_id, self.commit_event
|
||||
event,
|
||||
trans,
|
||||
set_gid,
|
||||
self.find_next_event_gramps_id,
|
||||
self.commit_event,
|
||||
)
|
||||
|
||||
def add_place(self, place, trans, set_gid=True):
|
||||
return self._add_base(
|
||||
place, trans, set_gid, self.find_next_place_gramps_id, self.commit_place
|
||||
place,
|
||||
trans,
|
||||
set_gid,
|
||||
self.find_next_place_gramps_id,
|
||||
self.commit_place,
|
||||
)
|
||||
|
||||
def add_repository(self, repository, trans, set_gid=True):
|
||||
@ -1844,7 +1896,11 @@ class DbGeneric(DbWriteBase, DbReadBase, UpdateCallback, Callback):
|
||||
|
||||
def add_source(self, source, trans, set_gid=True):
|
||||
return self._add_base(
|
||||
source, trans, set_gid, self.find_next_source_gramps_id, self.commit_source
|
||||
source,
|
||||
trans,
|
||||
set_gid,
|
||||
self.find_next_source_gramps_id,
|
||||
self.commit_source,
|
||||
)
|
||||
|
||||
def add_citation(self, citation, trans, set_gid=True):
|
||||
@ -1858,12 +1914,20 @@ class DbGeneric(DbWriteBase, DbReadBase, UpdateCallback, Callback):
|
||||
|
||||
def add_media(self, media, trans, set_gid=True):
|
||||
return self._add_base(
|
||||
media, trans, set_gid, self.find_next_media_gramps_id, self.commit_media
|
||||
media,
|
||||
trans,
|
||||
set_gid,
|
||||
self.find_next_media_gramps_id,
|
||||
self.commit_media,
|
||||
)
|
||||
|
||||
def add_note(self, note, trans, set_gid=True):
|
||||
return self._add_base(
|
||||
note, trans, set_gid, self.find_next_note_gramps_id, self.commit_note
|
||||
note,
|
||||
trans,
|
||||
set_gid,
|
||||
self.find_next_note_gramps_id,
|
||||
self.commit_note,
|
||||
)
|
||||
|
||||
def add_tag(self, tag, trans):
|
||||
@ -2150,6 +2214,9 @@ class DbGeneric(DbWriteBase, DbReadBase, UpdateCallback, Callback):
|
||||
"""
|
||||
Post-transaction commit processing
|
||||
"""
|
||||
self.db_last_transaction = time.time()
|
||||
self._set_metadata("db_last_transaction", self.db_last_transaction)
|
||||
|
||||
# Reset callbacks if necessary
|
||||
if transaction.batch or not len(transaction):
|
||||
return
|
||||
@ -2483,11 +2550,54 @@ class DbGeneric(DbWriteBase, DbReadBase, UpdateCallback, Callback):
|
||||
def save_gender_stats(self, gstats):
|
||||
raise NotImplementedError
|
||||
|
||||
def get_last_transaction_time(self):
|
||||
return self.db_last_transaction
|
||||
|
||||
def get_tree(self):
|
||||
return self.tree
|
||||
|
||||
def set_tree(self, tree, quiet=False):
|
||||
self.tree = tree
|
||||
self._save_tree(quiet)
|
||||
|
||||
def _save_tree(self, quiet=False):
|
||||
self._set_metadata("tree", self.tree)
|
||||
self.set_tree_change()
|
||||
if not quiet:
|
||||
self.emit("tree-data-changed")
|
||||
|
||||
def set_tree_change(self, change=int(time.time())):
|
||||
self.tree_change = change
|
||||
self._set_metadata("tree_change", change)
|
||||
|
||||
def get_researcher(self):
|
||||
return self.owner
|
||||
|
||||
def set_researcher(self, owner):
|
||||
def set_researcher(self, owner, quiet=False):
|
||||
self.owner.set_from(owner)
|
||||
self._set_metadata("researcher", self.owner)
|
||||
self.set_researcher_change()
|
||||
if not quiet:
|
||||
self.emit("researcher-changed")
|
||||
|
||||
def set_researcher_change(self, change=int(time.time())):
|
||||
self.researcher_change = change
|
||||
self._set_metadata("researcher_change", change)
|
||||
|
||||
def get_researcher_handle(self):
|
||||
return self._get_metadata("researcher_handle", default=None)
|
||||
|
||||
def get_researcher_person(self):
|
||||
handle = self.get_researcher_handle()
|
||||
try:
|
||||
return self.get_person_from_handle(handle)
|
||||
except HandleError:
|
||||
return None
|
||||
|
||||
def set_researcher_handle(self, handle, quiet=False):
|
||||
self._set_metadata("researcher_handle", handle)
|
||||
if not quiet:
|
||||
self.emit("researcher-changed")
|
||||
|
||||
def request_rebuild(self):
|
||||
self.emit("person-rebuild")
|
||||
@ -2572,7 +2682,10 @@ class DbGeneric(DbWriteBase, DbReadBase, UpdateCallback, Callback):
|
||||
for surname in person.primary_name.surname_list
|
||||
if (
|
||||
int(surname.origintype)
|
||||
not in [NameOriginType.PATRONYMIC, NameOriginType.MATRONYMIC]
|
||||
not in [
|
||||
NameOriginType.PATRONYMIC,
|
||||
NameOriginType.MATRONYMIC,
|
||||
]
|
||||
)
|
||||
]
|
||||
order_by = " ".join(order_by_list)
|
||||
|
@ -60,6 +60,7 @@ from .tag import Tag
|
||||
# These are actually metadata
|
||||
from .genderstats import GenderStats
|
||||
from .researcher import Researcher
|
||||
from .tree import Tree
|
||||
|
||||
# Type classes
|
||||
from .grampstype import GrampsType
|
||||
|
@ -29,21 +29,25 @@ Researcher information for Gramps.
|
||||
#
|
||||
# -------------------------------------------------------------------------
|
||||
from .locationbase import LocationBase
|
||||
from ..const import GRAMPS_LOCALE as glocale
|
||||
|
||||
_ = glocale.translation.gettext
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
#
|
||||
#
|
||||
# Researcher class
|
||||
#
|
||||
# -------------------------------------------------------------------------
|
||||
class Researcher(LocationBase):
|
||||
"""Contains the information about the owner of the database."""
|
||||
"""
|
||||
Contains the information about the owner of the database.
|
||||
"""
|
||||
|
||||
def __init__(self, source=None):
|
||||
"""
|
||||
Initialize the Researcher object, copying from the source if provided.
|
||||
"""
|
||||
|
||||
LocationBase.__init__(self, source)
|
||||
if source:
|
||||
self.name = source.name
|
||||
@ -60,41 +64,81 @@ class Researcher(LocationBase):
|
||||
"""
|
||||
return (LocationBase.serialize(self), self.name, self.addr, self.email)
|
||||
|
||||
@classmethod
|
||||
def get_schema(cls):
|
||||
"""
|
||||
Returns the JSON Schema for this class.
|
||||
|
||||
:returns: Returns a dict containing the schema.
|
||||
:rtype: dict
|
||||
"""
|
||||
return {
|
||||
"type": "object",
|
||||
"title": _("Researcher"),
|
||||
"properties": {
|
||||
"_class": {"enum": [cls.__name__]},
|
||||
"name": {"type": "string", "title": _("Name")},
|
||||
"address": {"type": "string", "title": _("Address")},
|
||||
"email": {"type": "string", "title": _("Email")},
|
||||
"street": {"type": "string", "title": _("Street")},
|
||||
"locality": {"type": "string", "title": _("Locality")},
|
||||
"city": {"type": "string", "title": _("City")},
|
||||
"county": {"type": "string", "title": _("County")},
|
||||
"state": {"type": "string", "title": _("State")},
|
||||
"country": {"type": "string", "title": _("Country")},
|
||||
"postal": {"type": "string", "title": _("Postal Code")},
|
||||
"phone": {"type": "string", "title": _("Phone")},
|
||||
},
|
||||
}
|
||||
|
||||
def unserialize(self, data):
|
||||
"""
|
||||
Convert a serialized tuple of data to an object.
|
||||
"""
|
||||
(location, self.name, self.addr, self.email) = data
|
||||
LocationBase.unserialize(self, location)
|
||||
|
||||
return self
|
||||
|
||||
def set_name(self, data):
|
||||
"""Set the database owner's name."""
|
||||
"""
|
||||
Set the database owner's name.
|
||||
"""
|
||||
self.name = data
|
||||
|
||||
def get_name(self):
|
||||
"""Return the database owner's name."""
|
||||
"""
|
||||
Return the database owner's name.
|
||||
"""
|
||||
return self.name
|
||||
|
||||
def set_address(self, data):
|
||||
"""Set the database owner's address."""
|
||||
"""
|
||||
Set the database owner's address.
|
||||
"""
|
||||
self.addr = data
|
||||
|
||||
def get_address(self):
|
||||
"""Return the database owner's address."""
|
||||
"""
|
||||
Return the database owner's address.
|
||||
"""
|
||||
return self.addr
|
||||
|
||||
def set_email(self, data):
|
||||
"""Set the database owner's email."""
|
||||
"""
|
||||
Set the database owner's email.
|
||||
"""
|
||||
self.email = data
|
||||
|
||||
def get_email(self):
|
||||
"""Return the database owner's email."""
|
||||
"""
|
||||
Return the database owner's email.
|
||||
"""
|
||||
return self.email
|
||||
|
||||
def set_from(self, other_researcher):
|
||||
"""Set all attributes from another instance."""
|
||||
"""
|
||||
Set all attributes from another instance.
|
||||
"""
|
||||
self.street = other_researcher.street
|
||||
self.locality = other_researcher.locality
|
||||
self.city = other_researcher.city
|
||||
@ -109,6 +153,9 @@ class Researcher(LocationBase):
|
||||
self.email = other_researcher.email
|
||||
|
||||
def get(self):
|
||||
"""
|
||||
Return all the attributes as a list.
|
||||
"""
|
||||
return [
|
||||
getattr(self, value)
|
||||
for value in [
|
||||
@ -125,6 +172,9 @@ class Researcher(LocationBase):
|
||||
]
|
||||
|
||||
def is_empty(self):
|
||||
"""
|
||||
Check if nothing has been set in the object.
|
||||
"""
|
||||
for attr in [
|
||||
"name",
|
||||
"addr",
|
||||
|
178
gramps/gen/lib/tree.py
Normal file
178
gramps/gen/lib/tree.py
Normal file
@ -0,0 +1,178 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2000-2007 Donald N. Allingham
|
||||
# Copyright (C) 2013 Doug Blank <doug.blank@gmail.com>
|
||||
# Copyright (C) 2022 Christopher Horn
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
"""
|
||||
Tree information for Gramps.
|
||||
"""
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
#
|
||||
# Gramps modules
|
||||
#
|
||||
# -------------------------------------------------------------------------
|
||||
from ..const import GRAMPS_LOCALE as glocale
|
||||
|
||||
_ = glocale.translation.gettext
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
#
|
||||
# Tree class
|
||||
#
|
||||
# -------------------------------------------------------------------------
|
||||
class Tree:
|
||||
"""
|
||||
Contains the tree related metadata.
|
||||
"""
|
||||
|
||||
def __init__(self, source=None):
|
||||
"""
|
||||
Initialize the Tree object, copying from the source if provided.
|
||||
"""
|
||||
self.name = ""
|
||||
self.description = ""
|
||||
self.copyright_used = ""
|
||||
self.license_used = ""
|
||||
self.contributors = ""
|
||||
if source:
|
||||
self.unserialize(source)
|
||||
|
||||
def serialize(self):
|
||||
"""
|
||||
Convert the object to a serialized tuple of data.
|
||||
"""
|
||||
return (
|
||||
self.name,
|
||||
self.description,
|
||||
self.copyright_used,
|
||||
self.license_used,
|
||||
self.contributors,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_schema(cls):
|
||||
"""
|
||||
Returns the JSON Schema for this class.
|
||||
|
||||
:returns: Returns a dict containing the schema.
|
||||
:rtype: dict
|
||||
"""
|
||||
return {
|
||||
"type": "object",
|
||||
"title": _("Tree"),
|
||||
"properties": {
|
||||
"_class": {"enum": [cls.__name__]},
|
||||
"name": {"type": "string", "title": _("Tree name")},
|
||||
"description": {"type": "string", "title": _("Description")},
|
||||
"copyright": {"type": "string", "title": _("Copyright")},
|
||||
"license": {"type": "string", "title": _("License")},
|
||||
"contributors": {"type": "string", "title": _("Contributors")},
|
||||
},
|
||||
}
|
||||
|
||||
def unserialize(self, data):
|
||||
"""
|
||||
Convert a serialized tuple of data to an object.
|
||||
"""
|
||||
(
|
||||
self.name,
|
||||
self.description,
|
||||
self.copyright_used,
|
||||
self.license_used,
|
||||
self.contributors,
|
||||
) = data
|
||||
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.name,
|
||||
self.description,
|
||||
self.copyright_used,
|
||||
self.license_used,
|
||||
self.contributors,
|
||||
]
|
||||
|
||||
def get_name(self):
|
||||
"""
|
||||
Return the tree name.
|
||||
"""
|
||||
return self.name
|
||||
|
||||
def set_name(self, data):
|
||||
"""
|
||||
Set the tree name.
|
||||
"""
|
||||
self.name = data
|
||||
|
||||
def get_description(self):
|
||||
"""
|
||||
Return the description.
|
||||
"""
|
||||
return self.description
|
||||
|
||||
def set_description(self, data):
|
||||
"""
|
||||
Set the description.
|
||||
"""
|
||||
self.description = data
|
||||
|
||||
def get_copyright(self):
|
||||
"""
|
||||
Return the copyright.
|
||||
"""
|
||||
return self.copyright_used
|
||||
|
||||
def set_copyright(self, data):
|
||||
"""
|
||||
Set the copyright.
|
||||
"""
|
||||
self.copyright_used = data
|
||||
|
||||
def get_license(self):
|
||||
"""
|
||||
Return the license.
|
||||
"""
|
||||
return self.license_used
|
||||
|
||||
def set_license(self, data):
|
||||
"""
|
||||
Set the license.
|
||||
"""
|
||||
self.license_used = data
|
||||
|
||||
def get_contributors(self):
|
||||
"""
|
||||
Return the contributors.
|
||||
"""
|
||||
return self.contributors
|
||||
|
||||
def set_contributors(self, data):
|
||||
"""
|
||||
Set the contributors.
|
||||
"""
|
||||
self.contributors = data
|
@ -70,6 +70,8 @@ class BaseMergeCheck(unittest.TestCase):
|
||||
<database xmlns="http://gramps-project.org/xml/%s/">
|
||||
<header>
|
||||
<created date="%04d-%02d-%02d" version="%s"/>
|
||||
<provenance>\n </provenance>
|
||||
<tree>\n </tree>
|
||||
<researcher>\n </researcher>
|
||||
</header>
|
||||
""" % (
|
||||
|
@ -145,6 +145,36 @@ class ProxyDbBase(DbReadBase):
|
||||
"""
|
||||
return self.db.is_open()
|
||||
|
||||
def get_last_transaction_time(self):
|
||||
"""
|
||||
Return the last transaction timestamp.
|
||||
"""
|
||||
return self.db.get_last_transaction_time()
|
||||
|
||||
def get_tree(self):
|
||||
"""
|
||||
Return the tree information.
|
||||
"""
|
||||
return self.db.get_tree()
|
||||
|
||||
def get_researcher_handle(self):
|
||||
"""
|
||||
Return the researcher handle.
|
||||
"""
|
||||
return self.db.get_researcher_handle()
|
||||
|
||||
def get_researcher_person(self):
|
||||
"""
|
||||
Return the researcher person.
|
||||
"""
|
||||
return self.db.get_researcher_person()
|
||||
|
||||
def get_tree_metadata(self):
|
||||
"""
|
||||
Return the tree metadata.
|
||||
"""
|
||||
return self.db.get_tree_metadata()
|
||||
|
||||
def get_researcher(self):
|
||||
"""returns the Researcher instance, providing information about
|
||||
the owner of the database"""
|
||||
|
@ -41,3 +41,7 @@ class UndoableBuffer(Gtk.TextBuffer):
|
||||
|
||||
class PersistentTreeView(Gtk.TreeView):
|
||||
__gtype_name__ = "PersistentTreeView"
|
||||
|
||||
|
||||
class MultiLineEntry(Gtk.Box):
|
||||
__gtype_name__ = "MultiLineEntry"
|
||||
|
@ -19,11 +19,16 @@
|
||||
name="PersistentTreeView"
|
||||
title="Persistent TreeView"
|
||||
generic-name="resizable_treeview"/>
|
||||
<glade-widget-class
|
||||
name="MultiLineEntry"
|
||||
title="Multi-Line Text Entry"
|
||||
generic-name="multi_line_entry"/>
|
||||
</glade-widget-classes>
|
||||
<glade-widget-group name="GrampsWidgets" title="Gramps Widgets">
|
||||
<glade-widget-class-ref name="ValidatableMaskedEntry"/>
|
||||
<glade-widget-class-ref name="UndoableEntry"/>
|
||||
<glade-widget-class-ref name="StyledTextEditor"/>
|
||||
<glade-widget-class-ref name="PersistentTreeView"/>
|
||||
<glade-widget-class-ref name="MultiLineEntry"/>
|
||||
</glade-widget-group>
|
||||
</glade-catalog>
|
||||
|
@ -22,6 +22,7 @@
|
||||
"""Custom widgets."""
|
||||
|
||||
from .basicentry import *
|
||||
from .multientry import *
|
||||
from .buttons import *
|
||||
from .dateentry import *
|
||||
from .expandcollapsearrow import *
|
||||
|
134
gramps/gui/widgets/multientry.py
Normal file
134
gramps/gui/widgets/multientry.py
Normal file
@ -0,0 +1,134 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2022 Christopher Horn
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
"""
|
||||
Provide a basic multi-line text entry widget.
|
||||
"""
|
||||
|
||||
__all__ = ["MultiLineEntry"]
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
#
|
||||
# GTK/Gnome modules
|
||||
#
|
||||
# -------------------------------------------------------------------------
|
||||
from gi.repository import GObject, Gtk
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
#
|
||||
# MultiLineEntry class
|
||||
#
|
||||
# -------------------------------------------------------------------------
|
||||
class MultiLineEntry(Gtk.Box):
|
||||
"""
|
||||
Provide a simple multi-line text entry widget with a frame that
|
||||
should look like a normal single line text entry widget.
|
||||
"""
|
||||
|
||||
__gtype_name__ = "MultiLineEntry"
|
||||
|
||||
__gsignals__ = {
|
||||
"changed": (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE, ())
|
||||
}
|
||||
|
||||
def __init__(self, hexpand=True, vexpand=True, text=""):
|
||||
super().__init__(self)
|
||||
self.frame = Gtk.Frame(hexpand=hexpand, vexpand=vexpand)
|
||||
css = ".frame { border-style: solid; border-radius: 5px; }"
|
||||
self.provider = Gtk.CssProvider()
|
||||
self.provider.load_from_data(css.encode("utf-8"))
|
||||
context = self.frame.get_style_context()
|
||||
context.add_provider(self.provider, Gtk.STYLE_PROVIDER_PRIORITY_USER)
|
||||
context.add_class("frame")
|
||||
|
||||
self.buffer = Gtk.TextBuffer()
|
||||
self.buffer.set_text(text)
|
||||
self.buffer.connect("changed", self.changed)
|
||||
self.view = Gtk.TextView(
|
||||
buffer=self.buffer, hexpand=True, vexpand=True
|
||||
)
|
||||
self.view.set_accepts_tab(False)
|
||||
self.view.set_left_margin(6)
|
||||
self.view.set_right_margin(6)
|
||||
self.view.set_top_margin(6)
|
||||
self.view.set_bottom_margin(6)
|
||||
self.view.set_wrap_mode(Gtk.WrapMode.WORD)
|
||||
self.connect("focus-in-event", self.focus_outer_set)
|
||||
self.view.connect("focus-in-event", self.focus_inner_set)
|
||||
self.view.connect("focus-out-event", self.focus_unset)
|
||||
self.frame.add(self.view)
|
||||
self.add(self.frame)
|
||||
self.show()
|
||||
|
||||
def changed(self, _cb_obj):
|
||||
"""
|
||||
Emit change signal in case being monitored.
|
||||
"""
|
||||
self.emit("changed")
|
||||
|
||||
def set_editable(self, *args):
|
||||
"""
|
||||
Set text view edit state.
|
||||
"""
|
||||
self.view.set_editable(*args)
|
||||
|
||||
def set_text(self, text):
|
||||
"""
|
||||
Set the text.
|
||||
"""
|
||||
self.buffer.set_text(text)
|
||||
|
||||
def get_text(self):
|
||||
"""
|
||||
Get the text.
|
||||
"""
|
||||
start, end = self.buffer.get_bounds()
|
||||
return self.buffer.get_text(start, end, False)
|
||||
|
||||
def focus_outer_set(self, *_dummy_args):
|
||||
"""
|
||||
Pivot focus to the view.
|
||||
"""
|
||||
self.view.grab_focus()
|
||||
|
||||
def focus_inner_set(self, *_dummy_args):
|
||||
"""
|
||||
Set border when in focus.
|
||||
"""
|
||||
css = (
|
||||
".frame { border-style: solid; border-width: 2px; "
|
||||
"border-radius: 5px; border-color: rgb(53,132,228); }"
|
||||
)
|
||||
self.provider.load_from_data(css.encode("utf-8"))
|
||||
self.frame.queue_draw()
|
||||
self.buffer.place_cursor(self.buffer.get_start_iter())
|
||||
self.set_can_focus(False)
|
||||
|
||||
def focus_unset(self, *_dummy_args):
|
||||
"""
|
||||
Reset border when releasing focus.
|
||||
"""
|
||||
css = ".frame { border-style: solid; border-radius: 5px; }"
|
||||
self.provider.load_from_data(css.encode("utf-8"))
|
||||
self.frame.queue_draw()
|
||||
self.set_can_focus(True)
|
||||
return False
|
@ -380,7 +380,11 @@ class GedcomWriter(UpdateCallback):
|
||||
self._writeln(2, "TIME", time_str)
|
||||
self._writeln(1, "SUBM", "@SUBM@")
|
||||
self._writeln(1, "FILE", filename, limit=255)
|
||||
self._writeln(1, "COPR", "Copyright (c) %d %s." % (year, rname))
|
||||
tree = self.dbase.get_tree()
|
||||
if tree.get_copyright():
|
||||
self._writeln(1, "COPR", tree.get_copyright())
|
||||
else:
|
||||
self._writeln(1, "COPR", "Copyright (c) %d %s." % (year, rname))
|
||||
self._writeln(1, "GEDC")
|
||||
self._writeln(2, "VERS", "5.5.1")
|
||||
self._writeln(2, "FORM", "LINEAGE-LINKED")
|
||||
|
@ -8,7 +8,7 @@
|
||||
# Copyright (C) 2009 Douglas S. Blank
|
||||
# Copyright (C) 2010 Jakim Friant
|
||||
# Copyright (C) 2010-2011 Nick Hall
|
||||
# Copyright (C) 2013 Benny Malengier
|
||||
# Copyright (C) 2013 Benny Malengier
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@ -56,18 +56,18 @@ LOG = logging.getLogger(".WriteXML")
|
||||
#
|
||||
# -------------------------------------------------------------------------
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
|
||||
_ = glocale.translation.gettext
|
||||
from gramps.gen.const import URL_HOMEPAGE
|
||||
from gramps.gen.constfunc import win
|
||||
from gramps.gen.db.exceptions import DbWriteFailure
|
||||
from gramps.gen.lib import Date, Person
|
||||
from gramps.gen.updatecallback import UpdateCallback
|
||||
from gramps.gen.db.exceptions import DbWriteFailure
|
||||
from gramps.version import VERSION
|
||||
from gramps.gen.constfunc import win
|
||||
from gramps.gui.plug.export import WriterOptionBox, WriterOptionBoxWithCompression
|
||||
from gramps.version import VERSION
|
||||
import gramps.plugins.lib.libgrampsxml as libgrampsxml
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
_ = glocale.translation.gettext
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Attempt to load the GZIP library. Some version of python do not seem
|
||||
# to be compiled with this available.
|
||||
@ -109,7 +109,8 @@ class GrampsXmlWriter(UpdateCallback):
|
||||
Writes a database to the XML file.
|
||||
"""
|
||||
|
||||
def __init__(self, db, strip_photos=0, compress=1, version="unknown", user=None):
|
||||
def __init__(self, db, strip_photos=0, compress=1, version="unknown",
|
||||
user=None, export_note=""):
|
||||
"""
|
||||
Initialize, but does not write, an XML file.
|
||||
|
||||
@ -128,6 +129,7 @@ class GrampsXmlWriter(UpdateCallback):
|
||||
self.db = db
|
||||
self.strip_photos = strip_photos
|
||||
self.version = version
|
||||
self.export_note = export_note
|
||||
|
||||
self.status = None
|
||||
|
||||
@ -225,6 +227,9 @@ class GrampsXmlWriter(UpdateCallback):
|
||||
def write_xml_data(self):
|
||||
date = time.localtime(time.time())
|
||||
owner = self.db.get_researcher()
|
||||
owner_handle = self.db.get_researcher_handle()
|
||||
self.db.refresh_cached_tree_name()
|
||||
tree = self.db.get_tree()
|
||||
|
||||
person_len = self.db.get_number_of_people()
|
||||
family_len = self.db.get_number_of_families()
|
||||
@ -271,16 +276,37 @@ class GrampsXmlWriter(UpdateCallback):
|
||||
self.g.write(' <created date="%04d-%02d-%02d"' % date[:3])
|
||||
self.g.write(' version="' + self.version + '"')
|
||||
self.g.write("/>\n")
|
||||
self.g.write(" <researcher>\n")
|
||||
self.write_line("resname", owner.get_name(), 3)
|
||||
self.write_line("resaddr", owner.get_address(), 3)
|
||||
self.write_line("reslocality", owner.get_locality(), 3)
|
||||
self.write_line("rescity", owner.get_city(), 3)
|
||||
self.write_line("resstate", owner.get_state(), 3)
|
||||
self.write_line("rescountry", owner.get_country(), 3)
|
||||
self.write_line("respostal", owner.get_postal_code(), 3)
|
||||
self.write_line("resphone", owner.get_phone(), 3)
|
||||
self.write_line("resemail", owner.get_email(), 3)
|
||||
self.g.write(" <provenance>\n")
|
||||
self.write_line("database-id", self.db.get_dbid(), 3)
|
||||
self.write_line(
|
||||
"last-transaction-timestamp",
|
||||
self.db.get_last_transaction_time(),
|
||||
3
|
||||
)
|
||||
self.write_line("export-note", self.export_note, 3)
|
||||
self.g.write(" </provenance>\n")
|
||||
|
||||
self.g.write(' <tree change="%s">\n' % self.db.tree_change)
|
||||
self.write_line("name", tree.get_name(), 3)
|
||||
self.write_line("copyright", tree.get_copyright(), 3)
|
||||
self.write_line("license", tree.get_license(), 3)
|
||||
self.write_line("description", tree.get_description(), 3)
|
||||
self.write_line("contributors", tree.get_contributors(), 3)
|
||||
self.g.write(" </tree>\n")
|
||||
|
||||
self.g.write(' <researcher change="%s"' % self.db.owner_change)
|
||||
if owner_handle:
|
||||
self.g.write(' handle="_%s"' % owner_handle)
|
||||
self.g.write(">\n")
|
||||
self.write_line("resname", owner.get_name(),3)
|
||||
self.write_line("resaddr", owner.get_address(),3)
|
||||
self.write_line("reslocality", owner.get_locality(),3)
|
||||
self.write_line("rescity", owner.get_city(),3)
|
||||
self.write_line("resstate", owner.get_state(),3)
|
||||
self.write_line("rescountry", owner.get_country(),3)
|
||||
self.write_line("respostal", owner.get_postal_code(),3)
|
||||
self.write_line("resphone", owner.get_phone(),3)
|
||||
self.write_line("resemail", owner.get_email(),3)
|
||||
self.g.write(" </researcher>\n")
|
||||
self.write_metadata()
|
||||
self.g.write(" </header>\n")
|
||||
|
130
gramps/plugins/gramplet/navhistorygramplet.py
Normal file
130
gramps/plugins/gramplet/navhistorygramplet.py
Normal file
@ -0,0 +1,130 @@
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2007-2009 Douglas S. Blank <doug.blank@gmail.com>
|
||||
#
|
||||
# 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.
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
# Python modules
|
||||
#
|
||||
#------------------------------------------------------------------------
|
||||
import time
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
# Gramps modules
|
||||
#
|
||||
#------------------------------------------------------------------------
|
||||
|
||||
from gramps.gen.lib import Person, Family
|
||||
from gramps.gen.db import PERSON_KEY, FAMILY_KEY, TXNDEL
|
||||
from gramps.gen.plug import Gramplet
|
||||
from gramps.gen.display.name import displayer as name_displayer
|
||||
from gramps.gen.utils.db import family_name
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.sgettext
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
# NavHistoryGramplet class
|
||||
#
|
||||
#------------------------------------------------------------------------
|
||||
class NavHistoryGramplet(Gramplet):
|
||||
def init(self):
|
||||
self.set_tooltip(_("Click name to change active\nDouble-click name to edit"))
|
||||
self.set_text(_("Log for this Session") + "\n")
|
||||
self.gui.force_update = True # will always update, even if minimized
|
||||
self.last_log = None
|
||||
|
||||
def timestamp(self):
|
||||
self.append_text(time.strftime("%Y-%m-%d %H:%M:%S "))
|
||||
|
||||
def db_changed(self):
|
||||
self.timestamp()
|
||||
self.append_text(_("Opened data base -----------\n"))
|
||||
# List of translated strings used here (translated in self.log ).
|
||||
_('Added'), _('Deleted'), _('Edited'), _('Selected') # Dead code for l10n
|
||||
self.connect(self.dbstate.db, 'person-add',
|
||||
lambda handles: self.log('Person', 'Added', handles))
|
||||
self.connect(self.dbstate.db, 'person-delete',
|
||||
lambda handles: self.log('Person', 'Deleted', handles))
|
||||
self.connect(self.dbstate.db, 'person-update',
|
||||
lambda handles: self.log('Person', 'Edited', handles))
|
||||
self.connect(self.dbstate.db, 'family-add',
|
||||
lambda handles: self.log('Family', 'Added', handles))
|
||||
self.connect(self.dbstate.db, 'family-delete',
|
||||
lambda handles: self.log('Family', 'Deleted', handles))
|
||||
self.connect(self.dbstate.db, 'family-update',
|
||||
lambda handles: self.log('Family', 'Edited', handles))
|
||||
self.connect_signal('Person', self.active_changed)
|
||||
self.connect_signal('Family', self.active_changed_family)
|
||||
|
||||
def active_changed(self, handle):
|
||||
if handle:
|
||||
self.log('Person', 'Selected', [handle])
|
||||
|
||||
def active_changed_family(self, handle):
|
||||
if handle:
|
||||
self.log('Family', 'Selected', [handle])
|
||||
|
||||
def log(self, ltype, action, handles):
|
||||
for handle in set(handles):
|
||||
if self.last_log == (ltype, action, handle):
|
||||
continue
|
||||
self.last_log = (ltype, action, handle)
|
||||
self.timestamp()
|
||||
# Translators: needed for French, ignore otherwise
|
||||
self.append_text(_("%s: ") % _(action))
|
||||
if action == 'Deleted':
|
||||
transaction = self.dbstate.db.transaction
|
||||
if ltype == 'Person':
|
||||
name = 'a person'
|
||||
if transaction is not None:
|
||||
for i in transaction.get_recnos(reverse=True):
|
||||
(obj_type, trans_type, hndl, old_data, dummy) = \
|
||||
transaction.get_record(i)
|
||||
if isinstance(hndl, bytes):
|
||||
hndl = str(hndl, "utf-8")
|
||||
if (obj_type == PERSON_KEY and trans_type == TXNDEL
|
||||
and hndl == handle):
|
||||
person = Person()
|
||||
person.unserialize(old_data)
|
||||
name = name_displayer.display(person)
|
||||
break
|
||||
elif ltype == 'Family':
|
||||
name = 'a family'
|
||||
if transaction is not None:
|
||||
for i in transaction.get_recnos(reverse=True):
|
||||
(obj_type, trans_type, hndl, old_data, dummy) = \
|
||||
transaction.get_record(i)
|
||||
if isinstance(hndl, bytes):
|
||||
hndl = str(hndl, "utf-8")
|
||||
if (obj_type == FAMILY_KEY and trans_type == TXNDEL
|
||||
and hndl == handle):
|
||||
family = Family()
|
||||
family.unserialize(old_data)
|
||||
name = family_name(family, self.dbstate.db, name)
|
||||
break
|
||||
self.append_text(name)
|
||||
else:
|
||||
if ltype == 'Person':
|
||||
person = self.dbstate.db.get_person_from_handle(handle)
|
||||
name = name_displayer.display(person)
|
||||
elif ltype == 'Family':
|
||||
family = self.dbstate.db.get_family_from_handle(handle)
|
||||
name = family_name(family, self.dbstate.db, 'a family')
|
||||
self.link(name, ltype, handle)
|
||||
self.append_text("\n")
|
@ -89,11 +89,10 @@ from gramps.gen.lib import (
|
||||
StyledTextTagType,
|
||||
Surname,
|
||||
Tag,
|
||||
Tree,
|
||||
Url,
|
||||
)
|
||||
from gramps.gen.db import DbTxn
|
||||
|
||||
# from gramps.gen.db.write import CLASS_TO_KEY_MAP
|
||||
from gramps.gen.errors import GrampsImportError
|
||||
from gramps.gen.utils.id import create_id
|
||||
from gramps.gen.utils.db import family_name
|
||||
@ -117,8 +116,6 @@ from gramps.gen.db.dbconst import (
|
||||
from gramps.gen.updatecallback import UpdateCallback
|
||||
from gramps.version import VERSION
|
||||
from gramps.gen.config import config
|
||||
|
||||
# import gramps.plugins.lib.libgrampsxml
|
||||
from gramps.plugins.lib import libgrampsxml
|
||||
from gramps.gen.plug.utils import version_str_to_tup
|
||||
from gramps.plugins.lib.libplaceimport import PlaceImport
|
||||
@ -601,16 +598,12 @@ class GrampsParser(UpdateCallback):
|
||||
self.place_map = {}
|
||||
self.place_import = PlaceImport(self.db)
|
||||
|
||||
self.resname = ""
|
||||
self.resaddr = ""
|
||||
self.reslocality = ""
|
||||
self.rescity = ""
|
||||
self.resstate = ""
|
||||
self.rescon = ""
|
||||
self.respos = ""
|
||||
self.resphone = ""
|
||||
self.resemail = ""
|
||||
|
||||
self.tree = Tree()
|
||||
self.in_tree = False
|
||||
self.tree_change = 0
|
||||
self.owner = Researcher()
|
||||
self.owner_change = 0
|
||||
self.owner_handle = ""
|
||||
self.mediapath = ""
|
||||
|
||||
self.pmap = {}
|
||||
@ -632,7 +625,6 @@ class GrampsParser(UpdateCallback):
|
||||
self.surname = None
|
||||
self.surnamepat = None
|
||||
self.home = None
|
||||
self.owner = Researcher()
|
||||
self.func_list = [None] * 50
|
||||
self.func_index = 0
|
||||
self.func = None
|
||||
@ -759,7 +751,7 @@ class GrampsParser(UpdateCallback):
|
||||
"pos": (self.start_pos, None),
|
||||
"postal": (None, self.stop_postal),
|
||||
"range": (self.start_range, None),
|
||||
"researcher": (None, self.stop_research),
|
||||
"researcher": (self.start_research, None),
|
||||
"resname": (None, self.stop_resname),
|
||||
"resaddr": (None, self.stop_resaddr),
|
||||
"reslocality": (None, self.stop_reslocality),
|
||||
@ -790,6 +782,10 @@ class GrampsParser(UpdateCallback):
|
||||
"repository": (self.start_repo, self.stop_repo),
|
||||
"reporef": (self.start_reporef, self.stop_reporef),
|
||||
"rname": (None, self.stop_rname),
|
||||
"tree": (self.start_tree, self.stop_tree),
|
||||
"copyright": (None, self.stop_tree_copyright),
|
||||
"license": (None, self.stop_tree_license),
|
||||
"contributors": (None, self.stop_tree_contributors),
|
||||
}
|
||||
self.grampsuri = re.compile(
|
||||
r"^gramps://(?P<object_class>[A-Z][a-z]+)/" r"handle/(?P<handle>\w+)$"
|
||||
@ -1070,7 +1066,12 @@ class GrampsParser(UpdateCallback):
|
||||
# If the database was originally empty we update the researcher from
|
||||
# the XML (or initialised to no researcher)
|
||||
if self.import_researcher:
|
||||
self.db.set_researcher(self.owner)
|
||||
self.db.set_researcher(self.owner, quiet=True)
|
||||
self.db.set_researcher_change(self.owner_change)
|
||||
self.db.set_researcher_handle(self.owner_handle, quiet=True)
|
||||
self.db.set_tree(self.tree, quiet=True)
|
||||
self.db.set_tree_change(self.tree_change)
|
||||
|
||||
if self.home is not None:
|
||||
person = self.db.get_person_from_handle(self.home)
|
||||
self.db.set_default_person_handle(person.handle)
|
||||
@ -3055,7 +3056,10 @@ class GrampsParser(UpdateCallback):
|
||||
self.person = None
|
||||
|
||||
def stop_description(self, tag):
|
||||
self.event.set_description(tag)
|
||||
if self.in_tree:
|
||||
self.tree.set_description(tag)
|
||||
else:
|
||||
self.event.set_description(tag)
|
||||
|
||||
def stop_cause(self, tag):
|
||||
# The old event's cause is now an attribute
|
||||
@ -3291,43 +3295,54 @@ class GrampsParser(UpdateCallback):
|
||||
self.db.commit_note(self.note, self.trans, self.note.get_change_time())
|
||||
self.note = None
|
||||
|
||||
def stop_research(self, tag):
|
||||
self.owner.set_name(self.resname)
|
||||
self.owner.set_address(self.resaddr)
|
||||
self.owner.set_locality(self.reslocality)
|
||||
self.owner.set_city(self.rescity)
|
||||
self.owner.set_state(self.resstate)
|
||||
self.owner.set_country(self.rescon)
|
||||
self.owner.set_postal_code(self.respos)
|
||||
self.owner.set_phone(self.resphone)
|
||||
self.owner.set_email(self.resemail)
|
||||
def start_tree(self, attrs):
|
||||
self.tree.set_name(self.db.get_dbname())
|
||||
self.tree_change = int(attrs.get('change', self.change))
|
||||
self.in_tree = True
|
||||
|
||||
def stop_tree_copyright(self, tag):
|
||||
self.tree.set_copyright(tag)
|
||||
|
||||
def stop_tree_license(self, tag):
|
||||
self.tree.set_license(tag)
|
||||
|
||||
def stop_tree_contributors(self, tag):
|
||||
self.tree.set_contributors(tag)
|
||||
|
||||
def stop_tree(self, tag):
|
||||
self.in_tree = False
|
||||
|
||||
def start_research(self, attrs):
|
||||
self.owner_change = int(attrs.get('change', self.change))
|
||||
if 'handle' in attrs:
|
||||
self.owner_handle = attrs['handle'].replace('_', '')
|
||||
|
||||
def stop_resname(self, tag):
|
||||
self.resname = tag
|
||||
self.owner.set_name(tag)
|
||||
|
||||
def stop_resaddr(self, tag):
|
||||
self.resaddr = tag
|
||||
self.owner.set_address(tag)
|
||||
|
||||
def stop_reslocality(self, tag):
|
||||
self.reslocality = tag
|
||||
self.owner.set_locality(tag)
|
||||
|
||||
def stop_rescity(self, tag):
|
||||
self.rescity = tag
|
||||
self.owner.set_city(tag)
|
||||
|
||||
def stop_resstate(self, tag):
|
||||
self.resstate = tag
|
||||
self.owner.set_state(tag)
|
||||
|
||||
def stop_rescountry(self, tag):
|
||||
self.rescon = tag
|
||||
self.owner.set_country(tag)
|
||||
|
||||
def stop_respostal(self, tag):
|
||||
self.respos = tag
|
||||
self.owner.set_postal_code(tag)
|
||||
|
||||
def stop_resphone(self, tag):
|
||||
self.resphone = tag
|
||||
self.owner.set_phone(tag)
|
||||
|
||||
def stop_resemail(self, tag):
|
||||
self.resemail = tag
|
||||
self.owner.set_email(tag)
|
||||
|
||||
def stop_mediapath(self, tag):
|
||||
self.mediapath = tag
|
||||
|
@ -42,7 +42,12 @@ class VCardCheck(unittest.TestCase):
|
||||
self.header = """<database xmlns="http://gramps-project.org/xml/%s/">
|
||||
<header>
|
||||
<created date="%04d-%02d-%02d" version="%s"/>
|
||||
<researcher/>
|
||||
<provenance>
|
||||
<database-id />
|
||||
<last-transaction-timestamp />
|
||||
</provenance>
|
||||
<tree />
|
||||
<researcher />
|
||||
</header>""" % (
|
||||
GRAMPS_XML_VERSION,
|
||||
date[0],
|
||||
@ -84,6 +89,11 @@ class VCardCheck(unittest.TestCase):
|
||||
element.text = ""
|
||||
if element.tail is not None and not element.tail.strip():
|
||||
element.tail = ""
|
||||
if element.tag.split("}")[-1] in [
|
||||
"database-id",
|
||||
"last-transaction-timestamp",
|
||||
]:
|
||||
element.text = ""
|
||||
|
||||
return ET.tostring(doc, encoding="utf-8")
|
||||
|
||||
|
574
gramps/plugins/importer/test/importvcard_test.py.lxml
Normal file
574
gramps/plugins/importer/test/importvcard_test.py.lxml
Normal file
@ -0,0 +1,574 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2011 Michiel D. Nauta
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
"""
|
||||
Unittest of import of VCard
|
||||
"""
|
||||
|
||||
# in case of a failing test, add True as last parameter to do_case to see the output.
|
||||
|
||||
import unittest
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import subprocess
|
||||
#import xml.etree.ElementTree as ET
|
||||
import lxml.etree as ET
|
||||
|
||||
from gramps.gen.const import DATA_DIR
|
||||
from gramps.plugins.lib.libgrampsxml import GRAMPS_XML_VERSION
|
||||
from gramps.version import VERSION
|
||||
from gramps.plugins.importer.importvcard import VCardParser, fitin, splitof_nameprefix
|
||||
|
||||
class VCardCheck(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.parser = ET.XMLParser(remove_blank_text=True)
|
||||
styledoc = ET.parse(os.path.join(DATA_DIR, "gramps_canonicalize.xsl"),
|
||||
parser=self.parser)
|
||||
self.transform = ET.XSLT(styledoc)
|
||||
|
||||
date = time.localtime(time.time())
|
||||
self.header = """<database xmlns="http://gramps-project.org/xml/%s/">
|
||||
<header>
|
||||
<created date="%04d-%02d-%02d" version="%s"/>
|
||||
<provenance/>
|
||||
<tree/>
|
||||
<researcher/>
|
||||
</header>""" % (GRAMPS_XML_VERSION, date[0], date[1], date[2], VERSION)
|
||||
|
||||
expect_str = self.header + """
|
||||
<people>
|
||||
<person handle="I0000" id="I0000">
|
||||
<gender>U</gender>
|
||||
<name type="Birth Name">
|
||||
<surname>Lastname</surname>
|
||||
</name>
|
||||
</person>
|
||||
</people>
|
||||
</database>"""
|
||||
|
||||
# namespace = "http://gramps-project.org/xml/%s/" % GRAMPS_XML_VERSION
|
||||
# ET.register_namespace("", namespace)
|
||||
|
||||
self.gramps = ET.XML(expect_str)
|
||||
self.person = self.gramps[1][0]
|
||||
self.name = self.person[1]
|
||||
self.vcard = ["BEGIN:VCARD", "VERSION:3.0", "FN:Lastname",
|
||||
"N:Lastname;;;;", "END:VCARD"]
|
||||
|
||||
def canonicalize(self, doctxt):
|
||||
"""
|
||||
Return a canonicalized string representation
|
||||
|
||||
:param doctxt: the text to bring in canonical form.
|
||||
:type doctxt: either a string or an Xml document.
|
||||
:returns: The text but in canonical form.
|
||||
:rtype: string
|
||||
"""
|
||||
# if isinstance(doctxt, bytes):
|
||||
# doc = ET.fromstring(doctxt, parser=self.parser)
|
||||
# elif isinstance(doctxt, ET._Element):
|
||||
# doc = doctxt
|
||||
# else:
|
||||
# raise TypeError
|
||||
doc = doctxt
|
||||
canonical_doc = self.transform(doc, replace_handles="ID")
|
||||
# result = ET.tostring(canonical_doc, pretty_print=True)
|
||||
result = ET.tostring(canonical_doc)
|
||||
#print(str(result, 'utf-8'))
|
||||
return result
|
||||
|
||||
# def canonicalize(self, doc):
|
||||
# handles = {}
|
||||
# for element in doc.iter("*"):
|
||||
# gramps_id = element.get('id')
|
||||
# if gramps_id is not None:
|
||||
# handles[element.get('handle')] = gramps_id
|
||||
# element.set('handle', gramps_id)
|
||||
# hlink = element.get('hlink')
|
||||
# if hlink is not None:
|
||||
# element.set('hlink', handles.get(hlink))
|
||||
# if element.get('change') is not None:
|
||||
# del element.attrib['change']
|
||||
# if element.text is not None and not element.text.strip():
|
||||
# element.text = ''
|
||||
# if element.tail is not None and not element.tail.strip():
|
||||
# element.tail = ''
|
||||
|
||||
# return ET.tostring(doc, encoding='utf-8')
|
||||
|
||||
def do_case(self, input_str, expect_doc, debug=True):
|
||||
if debug:
|
||||
print(input_str)
|
||||
|
||||
gcmd = [sys.executable, 'Gramps.py',
|
||||
'-d', '.Date', '-d', '.ImportVCard',
|
||||
'--config=preferences.eprefix:DEFAULT',
|
||||
'-i', '-', '-f', 'vcf',
|
||||
'-e', '-', '-f', 'gramps']
|
||||
process = subprocess.Popen(gcmd,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
env=os.environ)
|
||||
result_str, err_str = process.communicate(input_str.encode("utf-8"))
|
||||
if debug:
|
||||
print(err_str)
|
||||
result_doc = ET.XML(result_str)
|
||||
|
||||
# if debug:
|
||||
if True:
|
||||
print(self.canonicalize(result_doc))
|
||||
print(self.canonicalize(expect_doc))
|
||||
self.assertEqual(self.canonicalize(result_doc),
|
||||
self.canonicalize(expect_doc))
|
||||
|
||||
def test_base(self):
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_splitof_nameprefix_noprefix(self):
|
||||
self.assertEqual(splitof_nameprefix("Noprefix"), ('',"Noprefix"))
|
||||
|
||||
def test_splitof_nameprefix_prefix(self):
|
||||
self.assertEqual(splitof_nameprefix("van Prefix"), ('van',"Prefix"))
|
||||
|
||||
def test_splitof_nameprefix_doublespace(self):
|
||||
self.assertEqual(splitof_nameprefix("van Prefix"), ('van',"Prefix"))
|
||||
|
||||
def test_fitin_regular(self):
|
||||
self.assertEqual(fitin("Mr. Gaius Julius Caesar",
|
||||
"Gaius Caesar", "Julius"), 6)
|
||||
|
||||
def test_fitin_wrong_receiver(self):
|
||||
self.assertEqual(fitin("A B C", "A D", "B"), -1)
|
||||
|
||||
def test_fitin_wrong_element(self):
|
||||
self.assertRaises(ValueError, fitin, "A B C", "A C", "D")
|
||||
|
||||
def test_fitin_last_element(self):
|
||||
self.assertRaises(IndexError, fitin, "A B C", "A B", "C")
|
||||
|
||||
def test_name_value_split_begin_colon(self):
|
||||
self.vcard.insert(4, ":email@example.com")
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_name_value_split_quoted_colon(self):
|
||||
self.vcard.insert(4, 'TEL;TYPE="FANCY:TYPE":01234-56789')
|
||||
address = ET.SubElement(self.person, "address")
|
||||
ET.SubElement(address, 'phone').text = '01234-56789'
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_name_value_split_grouped(self):
|
||||
self.vcard[1] = "group." + self.vcard[1]
|
||||
self.vcard[3] = "group." + self.vcard[3]
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_unesc_string(self):
|
||||
self.assertEqual(VCardParser.unesc("TEL:012\\\\345\\,67\\;89"),
|
||||
"TEL:012\\345,67;89")
|
||||
|
||||
def test_unesc_list(self):
|
||||
self.assertEqual(VCardParser.unesc([r"Last\,name", r"First\;name"]),
|
||||
["Last,name", "First;name"])
|
||||
|
||||
def test_unesc_tuple(self):
|
||||
self.assertRaises(TypeError, VCardParser.unesc,
|
||||
(r"Last\,name", r"First\;name"))
|
||||
|
||||
def test_count_escapes_null(self):
|
||||
self.assertEqual(VCardParser.count_escapes("Lastname"), 0)
|
||||
|
||||
def test_count_escapes_one(self):
|
||||
self.assertEqual(VCardParser.count_escapes("Lastname\\"), 1)
|
||||
|
||||
def test_count_escapes_two(self):
|
||||
self.assertEqual(VCardParser.count_escapes(r"Lastname\\"), 2)
|
||||
|
||||
def test_split_unescaped_regular(self):
|
||||
self.assertEqual(VCardParser.split_unescaped("Lastname;Firstname", ';'),
|
||||
["Lastname", "Firstname"])
|
||||
|
||||
def test_split_unescaped_one(self):
|
||||
self.assertEqual(VCardParser.split_unescaped("Lastname\\;Firstname", ';'),
|
||||
["Lastname\\;Firstname",])
|
||||
|
||||
def test_split_unescaped_two(self):
|
||||
self.assertEqual(VCardParser.split_unescaped("Lastname\\\\;Firstname", ';'),
|
||||
["Lastname\\\\", "Firstname",])
|
||||
|
||||
def test_split_unescaped_three(self):
|
||||
self.assertEqual(VCardParser.split_unescaped(r"Lastname\\\;Firstname", ';'),
|
||||
[r"Lastname\\\;Firstname",])
|
||||
|
||||
def test_get_next_line_continuation(self):
|
||||
self.vcard.insert(4, "TEL:01234-\r\n 56789")
|
||||
address = ET.SubElement(self.person, "address")
|
||||
ET.SubElement(address, 'phone').text = '01234-56789'
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_get_next_line_cr(self):
|
||||
self.vcard.insert(4, "TEL:01234-56789\r")
|
||||
address = ET.SubElement(self.person, "address")
|
||||
ET.SubElement(address, 'phone').text = '01234-56789'
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_get_next_line_strip(self):
|
||||
self.vcard.insert(4, "TEL:01234-56789 ")
|
||||
address = ET.SubElement(self.person, "address")
|
||||
ET.SubElement(address, 'phone').text = '01234-56789'
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_get_next_line_none(self):
|
||||
self.vcard.insert(4, "")
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_parse_vCard_file_no_colon(self):
|
||||
self.vcard.insert(4, "TEL;01234-56789")
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_parse_vCard_file_lowercase(self):
|
||||
self.vcard.insert(4, "tel:01234-56789")
|
||||
address = ET.SubElement(self.person, "address")
|
||||
ET.SubElement(address, 'phone').text = '01234-56789'
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_parse_vCard_unknown_property(self):
|
||||
self.vcard.insert(4, "TELEPHONE:01234-56789")
|
||||
self.gramps = self.gramps
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_next_person_no_end(self):
|
||||
self.vcard[4] = "BEGIN:VCARD"
|
||||
self.vcard.extend(["VERSION:3.0", "FN:Another", "N:Another;;;;", "END:VCARD"])
|
||||
attribs = {'handle': 'I0001', 'id': 'I0001'}
|
||||
person = ET.SubElement(self.gramps[1], "person", attribs)
|
||||
ET.SubElement(person, 'gender').text = 'U'
|
||||
name = ET.SubElement(person, 'name', {'type': 'Birth Name'})
|
||||
ET.SubElement(name, 'surname').text = 'Another'
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_check_version(self):
|
||||
self.vcard = ["BEGIN:VCARD", "VERSION:3.7", "FN:Another",
|
||||
"N:Another;;;;", "END:VCARD"]
|
||||
expected = ET.XML(self.header + "</database>")
|
||||
self.do_case("\r\n".join(self.vcard), expected)
|
||||
|
||||
def test_add_formatted_name_twice(self):
|
||||
self.vcard[2] = "FN:Lastname B A"
|
||||
self.vcard[3] = "N:Lastname;A;B;;"
|
||||
self.vcard.insert(4, "FN:Lastname A B")
|
||||
name = self.person[1]
|
||||
first = ET.Element("first")
|
||||
first.text = "B A"
|
||||
name.insert(0, first)
|
||||
call = ET.Element("call")
|
||||
call.text = "A"
|
||||
name.insert(1, call)
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_name_parts_twice(self):
|
||||
self.vcard.insert(4, "N:Another;;;;")
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_name_regular(self):
|
||||
self.vcard[2] = "FN:Mr. Firstname Middlename Lastname Jr."
|
||||
self.vcard[3] = "N:Lastname;Firstname;Middlename;Mr.;Jr."
|
||||
first = ET.Element('first')
|
||||
first.text = 'Firstname Middlename'
|
||||
self.name.insert(0, first)
|
||||
ET.SubElement(self.name, 'suffix').text = 'Jr.'
|
||||
ET.SubElement(self.name, 'title').text = 'Mr.'
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_name_multisurname(self):
|
||||
self.vcard[2] = "FN:Lastname Lastname2"
|
||||
self.vcard[3] = "N:Lastname,Lastname2;;;;"
|
||||
surname = ET.SubElement(self.name, 'surname')
|
||||
surname.text = 'Lastname2'
|
||||
surname.set('prim', '0')
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_name_prefixsurname(self):
|
||||
self.vcard[2] = "FN:van d'Alembert"
|
||||
self.vcard[3] = "N:van d'Alembert;;;;"
|
||||
surname = self.name[0]
|
||||
surname.text = 'Alembert'
|
||||
surname.set('prefix', "van d'")
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_name_only_surname(self):
|
||||
self.vcard[3] = "N:Lastname"
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_name_upto_firstname(self):
|
||||
self.vcard[2] = "FN:Firstname Lastname"
|
||||
self.vcard[3] = "N:Lastname; Firstname;"
|
||||
first = ET.Element('first')
|
||||
first.text = 'Firstname'
|
||||
self.name.insert(0, first)
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_name_empty(self):
|
||||
self.vcard[2] = "FN:Lastname"
|
||||
self.vcard[3] = "N: "
|
||||
self.gramps.remove(self.gramps[1])
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_firstname_regular(self):
|
||||
self.vcard[2] = "FN:A B Lastname"
|
||||
self.vcard[3] = "N:Lastname;A;B;;"
|
||||
first = ET.Element('first')
|
||||
first.text = 'A B'
|
||||
self.name.insert(0, first)
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_firstname_callname(self):
|
||||
self.vcard[2] = "FN:A B Lastname"
|
||||
self.vcard[3] = "N:Lastname;B;A;;"
|
||||
first = ET.Element('first')
|
||||
first.text = 'A B'
|
||||
self.name.insert(0, first)
|
||||
call = ET.Element('call')
|
||||
call.text = 'B'
|
||||
self.name.insert(1, call)
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_firstname_incomplete_fn(self):
|
||||
self.vcard[2] = "FN:A Lastname"
|
||||
self.vcard[3] = "N:Lastname;A;B;;"
|
||||
first = ET.Element('first')
|
||||
first.text = 'A B'
|
||||
self.name.insert(0, first)
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_firstname_middle(self):
|
||||
self.vcard[2] = "FN:A B C Lastname"
|
||||
self.vcard[3] = "N:Lastname;B;A C;;"
|
||||
first = ET.Element('first')
|
||||
first.text = 'A B C'
|
||||
self.name.insert(0, first)
|
||||
call = ET.Element('call')
|
||||
call.text = 'B'
|
||||
self.name.insert(1, call)
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_firstname_fn_not_given(self):
|
||||
self.vcard[2] = "FN:B Lastname"
|
||||
self.vcard[3] = "N:Lastname;A;B;;"
|
||||
first = ET.Element('first')
|
||||
first.text = 'A B'
|
||||
self.name.insert(0, first)
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_firstname_no_addnames(self):
|
||||
self.vcard[2] = "FN:A Lastname"
|
||||
self.vcard[3] = "N:Lastname;A;;;"
|
||||
first = ET.Element('first')
|
||||
first.text = 'A'
|
||||
self.name.insert(0, first)
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_firstname_no_givenname(self):
|
||||
self.vcard[2] = "FN:A Lastname"
|
||||
self.vcard[3] = "N:Lastname;;A;;"
|
||||
first = ET.Element('first')
|
||||
first.text = 'A'
|
||||
self.name.insert(0, first)
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_firstname_no_fn(self):
|
||||
self.vcard[2] = "FN:"
|
||||
self.vcard[3] = "N:Lastname;;A;;"
|
||||
first = ET.Element('first')
|
||||
first.text = 'A'
|
||||
self.name.insert(0, first)
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_nicknames_single(self):
|
||||
self.vcard.insert(4, "NICKNAME:Ton")
|
||||
attribs = {"alt": "1", "type": "Birth Name"}
|
||||
name = ET.SubElement(self.person, 'name', attribs)
|
||||
ET.SubElement(name, 'nick').text = "Ton"
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_nicknames_empty(self):
|
||||
self.vcard.insert(4, "NICKNAME:")
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_nicknames_multiple(self):
|
||||
self.vcard.insert(4, r"NICKNAME:A,B\,C")
|
||||
attribs = {"alt": "1", "type": "Birth Name"}
|
||||
name = ET.SubElement(self.person, 'name', attribs)
|
||||
ET.SubElement(name, 'nick').text = "A"
|
||||
attribs = {"alt": "1", "type": "Birth Name"}
|
||||
name = ET.SubElement(self.person, 'name', attribs)
|
||||
ET.SubElement(name, 'nick').text = "B,C"
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_address_all(self):
|
||||
self.vcard.insert(4, "ADR:box 1;bis;Broadway 11; New York; New York; NY1234; USA")
|
||||
address = ET.SubElement(self.person, 'address')
|
||||
ET.SubElement(address, 'street').text = 'box 1 bis Broadway 11'
|
||||
ET.SubElement(address, 'city').text = 'New York'
|
||||
ET.SubElement(address, 'state').text = 'New York'
|
||||
ET.SubElement(address, 'country').text = 'USA'
|
||||
ET.SubElement(address, 'postal').text = 'NY1234'
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_address_too_many(self):
|
||||
self.vcard.insert(4, r"ADR:;;Broadway 11; New\,York; ;; USA; Earth")
|
||||
address = ET.SubElement(self.person, 'address')
|
||||
ET.SubElement(address, 'street').text = 'Broadway 11'
|
||||
ET.SubElement(address, 'city').text = 'New,York'
|
||||
ET.SubElement(address, 'country').text = 'USA'
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_address_too_few(self):
|
||||
self.vcard.insert(4, "ADR:;;Broadway 11; New York")
|
||||
address = ET.SubElement(self.person, 'address')
|
||||
ET.SubElement(address, 'street').text = 'Broadway 11'
|
||||
ET.SubElement(address, 'city').text = 'New York'
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_address_empty(self):
|
||||
self.vcard.insert(4, "ADR: ")
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_phone_regular(self):
|
||||
self.vcard.insert(4, "TEL:01234-56789")
|
||||
address = ET.SubElement(self.person, 'address')
|
||||
ET.SubElement(address, 'phone').text = '01234-56789'
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_phone_empty(self):
|
||||
self.vcard.insert(4, "TEL: ")
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_birthday_regular(self):
|
||||
self.vcard.insert(4, 'BDAY:2001-09-28')
|
||||
attribs = {'hlink': 'E0000', 'role': 'Primary'}
|
||||
eventref = ET.SubElement(self.person, 'eventref', attribs)
|
||||
events = ET.Element('events')
|
||||
self.gramps.insert(1, events)
|
||||
attribs = {'handle': 'E0000', 'id': 'E0000'}
|
||||
event = ET.SubElement(events, 'event', attribs)
|
||||
ET.SubElement(event, 'type').text = 'Birth'
|
||||
ET.SubElement(event, 'dateval', {'val': '2001-09-28'})
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_birthday_datetime(self):
|
||||
self.vcard.insert(4, 'BDAY:2001-09-28T09:23:47Z')
|
||||
attribs = {'hlink': 'E0000', 'role': 'Primary'}
|
||||
eventref = ET.SubElement(self.person, 'eventref', attribs)
|
||||
events = ET.Element('events')
|
||||
self.gramps.insert(1, events)
|
||||
attribs = {'handle': 'E0000', 'id': 'E0000'}
|
||||
event = ET.SubElement(events, 'event', attribs)
|
||||
ET.SubElement(event, 'type').text = 'Birth'
|
||||
ET.SubElement(event, 'dateval', {'val': '2001-09-28'})
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_birthday_no_dash(self):
|
||||
self.vcard.insert(4, 'BDAY:20010928')
|
||||
attribs = {'hlink': 'E0000', 'role': 'Primary'}
|
||||
eventref = ET.SubElement(self.person, 'eventref', attribs)
|
||||
events = ET.Element('events')
|
||||
self.gramps.insert(1, events)
|
||||
attribs = {'handle': 'E0000', 'id': 'E0000'}
|
||||
event = ET.SubElement(events, 'event', attribs)
|
||||
ET.SubElement(event, 'type').text = 'Birth'
|
||||
ET.SubElement(event, 'dateval', {'val': '2001-09-28'})
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_birthday_long_Feb_converted_to_datestr(self):
|
||||
self.vcard.insert(4, 'BDAY:20010229')
|
||||
attribs = {'hlink': 'E0000', 'role': 'Primary'}
|
||||
eventref = ET.SubElement(self.person, 'eventref', attribs)
|
||||
events = ET.Element('events')
|
||||
self.gramps.insert(1, events)
|
||||
attribs = {'handle': 'E0000', 'id': 'E0000'}
|
||||
event = ET.SubElement(events, 'event', attribs)
|
||||
ET.SubElement(event, 'type').text = 'Birth'
|
||||
ET.SubElement(event, 'datestr', {'val': '20010229'})
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_birthday_invalid_format_converted_to_datestr(self):
|
||||
self.vcard.insert(4, 'BDAY:unforgettable')
|
||||
attribs = {'hlink': 'E0000', 'role': 'Primary'}
|
||||
eventref = ET.SubElement(self.person, 'eventref', attribs)
|
||||
events = ET.Element('events')
|
||||
self.gramps.insert(1, events)
|
||||
attribs = {'handle': 'E0000', 'id': 'E0000'}
|
||||
event = ET.SubElement(events, 'event', attribs)
|
||||
ET.SubElement(event, 'type').text = 'Birth'
|
||||
ET.SubElement(event, 'datestr', {'val': 'unforgettable'})
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_birthday_one_dash(self):
|
||||
self.vcard.insert(4, 'BDAY:2001-0928')
|
||||
attribs = {'hlink': 'E0000', 'role': 'Primary'}
|
||||
eventref = ET.SubElement(self.person, 'eventref', attribs)
|
||||
events = ET.Element('events')
|
||||
self.gramps.insert(1, events)
|
||||
attribs = {'handle': 'E0000', 'id': 'E0000'}
|
||||
event = ET.SubElement(events, 'event', attribs)
|
||||
ET.SubElement(event, 'type').text = 'Birth'
|
||||
ET.SubElement(event, 'dateval', {'val': '2001-09-28'})
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_birthday_empty(self):
|
||||
self.vcard.insert(4, "BDAY: ")
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_occupation_regular(self):
|
||||
self.vcard.insert(4, "ROLE:scarecrow")
|
||||
attribs = {'hlink': 'E0000', 'role': 'Primary'}
|
||||
eventref = ET.SubElement(self.person, 'eventref', attribs)
|
||||
events = ET.Element('events')
|
||||
self.gramps.insert(1, events)
|
||||
attribs = {'handle': 'E0000', 'id': 'E0000'}
|
||||
event = ET.SubElement(events, 'event', attribs)
|
||||
ET.SubElement(event, 'type').text = 'Occupation'
|
||||
ET.SubElement(event, 'description').text = 'scarecrow'
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_occupation_empty(self):
|
||||
self.vcard.insert(4, "ROLE: ")
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_url_regular(self):
|
||||
self.vcard.insert(4, "URL:http://www.example.com")
|
||||
attribs = {'href': 'http://www.example.com', 'type': 'Unknown'}
|
||||
ET.SubElement(self.person, 'url', attribs)
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_url_empty(self):
|
||||
self.vcard.insert(4, "URL: ")
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
def test_add_email(self):
|
||||
self.vcard.insert(4, "EMAIL:me@example.org")
|
||||
attribs = {'href': 'me@example.org', 'type': 'E-mail'}
|
||||
ET.SubElement(self.person, 'url', attribs)
|
||||
self.do_case("\r\n".join(self.vcard), self.gramps)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
@ -33,5 +33,5 @@
|
||||
# Public Constants
|
||||
#
|
||||
# ------------------------------------------------------------------------
|
||||
GRAMPS_XML_VERSION_TUPLE = (1, 7, 1) # version for Gramps 4.2
|
||||
GRAMPS_XML_VERSION_TUPLE = (1, 7, 2) # version for Gramps 5.2
|
||||
GRAMPS_XML_VERSION = ".".join(str(i) for i in GRAMPS_XML_VERSION_TUPLE)
|
||||
|
272
gramps/plugins/tool/edittree.glade
Normal file
272
gramps/plugins/tool/edittree.glade
Normal file
@ -0,0 +1,272 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.38.2 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.10"/>
|
||||
<requires lib="grampswidgets" version="0.0"/>
|
||||
<object class="GtkAccelGroup" id="accelgroup1"/>
|
||||
<object class="GtkWindow" id="edittree">
|
||||
<property name="can-focus">False</property>
|
||||
<property name="window-position">center</property>
|
||||
<child>
|
||||
<object class="GtkEventBox" id="eventbox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="vbox2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="border-width">6</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="title">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="padding">6</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<!-- n-columns=3 n-rows=5 -->
|
||||
<object class="GtkGrid" id="table1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="border-width">6</property>
|
||||
<property name="row-spacing">6</property>
|
||||
<property name="column-spacing">6</property>
|
||||
<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>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<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">_Copyright:</property>
|
||||
<property name="use-underline">True</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">_License:</property>
|
||||
<property name="use-underline">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label4">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">_Description:</property>
|
||||
<property name="use-underline">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label5">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">_Contributors:</property>
|
||||
<property name="use-underline">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="name">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</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="GtkEntry" id="copyright">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</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="GtkEntry" id="license">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</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="MultiLineEntry" id="description">
|
||||
<property name="name">description</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="vexpand">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="MultiLineEntry" id="contributors">
|
||||
<property name="name">contributors</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="vexpand">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButtonBox" id="action_area">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="layout-style">end</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="cancel_button">
|
||||
<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">False</property>
|
||||
<property name="use-stock">True</property>
|
||||
<signal name="clicked" handler="on_cancel_button_clicked" swapped="no"/>
|
||||
</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_button">
|
||||
<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="receives-default">False</property>
|
||||
<property name="use-stock">True</property>
|
||||
<signal name="clicked" handler="on_ok_button_clicked" swapped="no"/>
|
||||
</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_button">
|
||||
<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">False</property>
|
||||
<property name="use-stock">True</property>
|
||||
<signal name="clicked" handler="on_help_button_clicked" swapped="no"/>
|
||||
</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">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparator" id="hseparator1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="padding">3</property>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
200
gramps/plugins/tool/edittree.py
Normal file
200
gramps/plugins/tool/edittree.py
Normal file
@ -0,0 +1,200 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2000-2007 Donald N. Allingham
|
||||
# Copyright (C) 2008 Brian G. Matherly
|
||||
# Copyright (C) 2010 Jakim Friant
|
||||
# Copyright (C) 2010 Nick Hall
|
||||
# Copyright (C) 2022 Christopher Horn
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
"""Tools/Database Processing/Edit Tree Information"""
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
#
|
||||
# Python modules
|
||||
#
|
||||
# -------------------------------------------------------------------------
|
||||
import os
|
||||
from copy import copy
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
#
|
||||
# Gramps modules
|
||||
#
|
||||
# -------------------------------------------------------------------------
|
||||
from gramps.cli.clidbman import CLIDbManager
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
from gramps.gen.const import URL_MANUAL_PAGE
|
||||
from gramps.gen.recentfiles import rename_filename
|
||||
from gramps.gui.dialog import ErrorDialog
|
||||
from gramps.gui.display import display_help
|
||||
from gramps.gui.glade import Glade
|
||||
from gramps.gui.managedwindow import ManagedWindow
|
||||
from gramps.gui.plug import tool
|
||||
from gramps.gui.widgets import MonitoredEntry
|
||||
|
||||
_ = glocale.translation.sgettext
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
#
|
||||
# Constants
|
||||
#
|
||||
# -------------------------------------------------------------------------
|
||||
WIKI_HELP_PAGE = "%s_-_Tools" % URL_MANUAL_PAGE
|
||||
WIKI_HELP_SEC = _("Edit_Tree_Information", "manual")
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
#
|
||||
# TreeEditor
|
||||
#
|
||||
# -------------------------------------------------------------------------
|
||||
class TreeEditor(tool.Tool, ManagedWindow):
|
||||
"""
|
||||
Enables editing of tree metadata like copyright and description.
|
||||
"""
|
||||
|
||||
def __init__(self, dbstate, user, options_class, name, callback=None):
|
||||
uistate = user.uistate
|
||||
ManagedWindow.__init__(self, uistate, [], self.__class__)
|
||||
tool.Tool.__init__(self, dbstate, options_class, name)
|
||||
self.dbstate = dbstate
|
||||
self.db.refresh_cached_tree_name()
|
||||
self.tree = self.db.get_tree()
|
||||
self.edit = copy(self.tree)
|
||||
self.display()
|
||||
|
||||
def display(self):
|
||||
"""
|
||||
Display the edit dialog.
|
||||
"""
|
||||
top_dialog = Glade()
|
||||
|
||||
# Setup window
|
||||
window = top_dialog.toplevel
|
||||
self.set_window(
|
||||
window,
|
||||
top_dialog.get_object("title"),
|
||||
_("Tree Information Editor"),
|
||||
)
|
||||
self.setup_configs("interface.edit_tree", 500, 400)
|
||||
|
||||
# Move help button to the left side
|
||||
action_area = top_dialog.get_object("action_area")
|
||||
help_button = top_dialog.get_object("help_button")
|
||||
action_area.set_child_secondary(help_button, True)
|
||||
|
||||
# Connect signals
|
||||
top_dialog.connect_signals(
|
||||
{
|
||||
"on_ok_button_clicked": self.on_ok_button_clicked,
|
||||
"on_cancel_button_clicked": self.close,
|
||||
"on_help_button_clicked": on_help_button_clicked,
|
||||
}
|
||||
)
|
||||
|
||||
# Setup inputs
|
||||
self.entries = []
|
||||
entry = [
|
||||
("name", self.edit.set_name, self.edit.get_name),
|
||||
(
|
||||
"description",
|
||||
self.edit.set_description,
|
||||
self.edit.get_description,
|
||||
),
|
||||
("copyright", self.edit.set_copyright, self.edit.get_copyright),
|
||||
("license", self.edit.set_license, self.edit.get_license),
|
||||
(
|
||||
"contributors",
|
||||
self.edit.set_contributors,
|
||||
self.edit.get_contributors,
|
||||
),
|
||||
]
|
||||
for (name, set_fn, get_fn) in entry:
|
||||
self.entries.append(
|
||||
MonitoredEntry(
|
||||
top_dialog.get_object(name),
|
||||
set_fn,
|
||||
get_fn,
|
||||
self.db.readonly,
|
||||
)
|
||||
)
|
||||
self.show()
|
||||
|
||||
def build_menu_names(self, obj):
|
||||
"""
|
||||
Return the menu names.
|
||||
"""
|
||||
return (_("Main window"), _("Edit tree information"))
|
||||
|
||||
def on_ok_button_clicked(self, _cb_obj):
|
||||
"""
|
||||
Save the changes.
|
||||
"""
|
||||
name = self.edit.get_name().strip()
|
||||
if name != self.tree.get_name():
|
||||
if self.rename_database():
|
||||
self.db.refresh_cached_tree_name()
|
||||
self.uistate.window.set_title(name)
|
||||
else:
|
||||
if self.edit != self.tree:
|
||||
self.db.set_tree(self.edit)
|
||||
self.close()
|
||||
|
||||
def rename_database(self):
|
||||
"""
|
||||
Renames the database by writing the new value to the name.txt file
|
||||
"""
|
||||
name = self.edit.get_name().strip()
|
||||
dbman = CLIDbManager(self.dbstate)
|
||||
for (tree_name, dummy_dir_name) in dbman.family_tree_list():
|
||||
if name == tree_name:
|
||||
ErrorDialog(
|
||||
_("Could not rename the Family Tree."),
|
||||
_("Family Tree already exists, choose a unique name."),
|
||||
parent=self,
|
||||
)
|
||||
return False
|
||||
|
||||
filename = os.path.join(self.db.get_save_path(), "name.txt")
|
||||
old_text, new_text = dbman.rename_database(filename, name)
|
||||
if old_text is not None:
|
||||
rename_filename(old_text, new_text)
|
||||
return True
|
||||
|
||||
|
||||
def on_help_button_clicked(_cb_obj):
|
||||
"""
|
||||
Display the relevant portion of Gramps manual
|
||||
"""
|
||||
display_help(webpage=WIKI_HELP_PAGE, section=WIKI_HELP_SEC)
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
#
|
||||
# TreeEditorOptions (None at the moment)
|
||||
#
|
||||
# -------------------------------------------------------------------------
|
||||
class TreeEditorOptions(tool.ToolOptions):
|
||||
"""
|
||||
Defines options and provides handling interface.
|
||||
"""
|
||||
|
||||
def __init__(self, name, person_id=None):
|
||||
tool.ToolOptions.__init__(self, name, person_id)
|
@ -1,65 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.18.3 -->
|
||||
<!-- Generated with glade 3.38.2 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.10"/>
|
||||
<object class="GtkAccelGroup" id="accelgroup1"/>
|
||||
<object class="GtkMenu" id="popup_menu">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="copy_from_db_to_preferences">
|
||||
<property name="label" translatable="yes">Copy from DB to Preferences</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="label">Copy from DB to Preferences</property>
|
||||
<property name="use-action-appearance">False</property>
|
||||
<property name="name">copy_from_db_to_preferences</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="accel_group">accelgroup1</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="use-underline">True</property>
|
||||
<property name="use-stock">True</property>
|
||||
<property name="accel-group">accelgroup1</property>
|
||||
<signal name="activate" handler="on_menu_activate" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="copy_from_preferences_to_db">
|
||||
<property name="label" translatable="yes">Copy from Preferences to DB</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="label">Copy from Preferences to DB</property>
|
||||
<property name="use-action-appearance">False</property>
|
||||
<property name="name">copy_from_preferences_to_db</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="accel_group">accelgroup1</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="use-underline">True</property>
|
||||
<property name="use-stock">True</property>
|
||||
<property name="accel-group">accelgroup1</property>
|
||||
<signal name="activate" handler="on_menu_activate" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkWindow" id="ownereditor">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="window_position">center</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="window-position">center</property>
|
||||
<child>
|
||||
<object class="GtkEventBox" id="eventbox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<signal name="button-press-event" handler="on_eventbox_button_press_event" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkBox" id="vbox2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">6</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="border-width">6</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkButtonBox" id="action_area">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="layout_style">end</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="layout-style">end</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="cancel_button">
|
||||
<property name="label">gtk-cancel</property>
|
||||
<property name="use_action_appearance">False</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">False</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="can-default">True</property>
|
||||
<property name="receives-default">False</property>
|
||||
<property name="use-stock">True</property>
|
||||
<signal name="clicked" handler="on_cancel_button_clicked" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
@ -71,12 +71,12 @@
|
||||
<child>
|
||||
<object class="GtkButton" id="ok_button">
|
||||
<property name="label">gtk-ok</property>
|
||||
<property name="use_action_appearance">False</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">False</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="can-default">True</property>
|
||||
<property name="receives-default">False</property>
|
||||
<property name="use-stock">True</property>
|
||||
<signal name="clicked" handler="on_ok_button_clicked" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
@ -88,12 +88,12 @@
|
||||
<child>
|
||||
<object class="GtkButton" id="help_button">
|
||||
<property name="label">gtk-help</property>
|
||||
<property name="use_action_appearance">False</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">False</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="can-default">True</property>
|
||||
<property name="receives-default">False</property>
|
||||
<property name="use-stock">True</property>
|
||||
<signal name="clicked" handler="on_help_button_clicked" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
@ -106,27 +106,27 @@
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparator" id="hseparator1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="padding">3</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="title">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@ -136,258 +136,428 @@
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<!-- n-columns=3 n-rows=11 -->
|
||||
<object class="GtkGrid" id="table1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">6</property>
|
||||
<property name="row_spacing">6</property>
|
||||
<property name="column_spacing">6</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="border-width">6</property>
|
||||
<property name="row-spacing">6</property>
|
||||
<property name="column-spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</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="mnemonic_widget">name</property>
|
||||
<property name="use-underline">True</property>
|
||||
<property name="mnemonic-widget">name</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">_Street:</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="mnemonic_widget">address</property>
|
||||
<property name="use-underline">True</property>
|
||||
<property name="mnemonic-widget">address</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
<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="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">_City:</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="mnemonic_widget">city</property>
|
||||
<property name="use-underline">True</property>
|
||||
<property name="mnemonic-widget">city</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label4">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">_State/County:</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="mnemonic_widget">state</property>
|
||||
<property name="use-underline">True</property>
|
||||
<property name="mnemonic-widget">state</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">4</property>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label5">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">_Country:</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="mnemonic_widget">country</property>
|
||||
<property name="use-underline">True</property>
|
||||
<property name="mnemonic-widget">country</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">5</property>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label6">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">_ZIP/Postal Code:</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="mnemonic_widget">zip</property>
|
||||
<property name="use-underline">True</property>
|
||||
<property name="mnemonic-widget">zip</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">6</property>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">6</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label7">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">_Phone:</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="mnemonic_widget">phone</property>
|
||||
<property name="use-underline">True</property>
|
||||
<property name="mnemonic-widget">phone</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">7</property>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">7</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label8">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">_Email:</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="mnemonic_widget">email</property>
|
||||
<property name="use-underline">True</property>
|
||||
<property name="mnemonic-widget">email</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">8</property>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">8</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="name">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="invisible_char">•</property>
|
||||
<property name="invisible-char">•</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="address">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="invisible_char">•</property>
|
||||
<property name="invisible-char">•</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="city">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="invisible_char">•</property>
|
||||
<property name="invisible-char">•</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="state">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="invisible_char">•</property>
|
||||
<property name="invisible-char">•</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">4</property>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="country">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="invisible_char">•</property>
|
||||
<property name="invisible-char">•</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">5</property>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="zip">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="invisible_char">•</property>
|
||||
<property name="invisible-char">•</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">6</property>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">6</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="phone">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="invisible_char">•</property>
|
||||
<property name="invisible-char">•</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">7</property>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">7</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="email">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="invisible_char">•</property>
|
||||
<property name="invisible-char">•</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">8</property>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">8</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label9">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">_Locality:</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="mnemonic_widget">locality</property>
|
||||
<property name="use-underline">True</property>
|
||||
<property name="mnemonic-widget">locality</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="locality">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="invisible_char">●</property>
|
||||
<property name="invisible-char">●</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label11">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">Right-click to copy from/to Researcher Preferences</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">10</property>
|
||||
<property name="width">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label10">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Right-click to copy from/to Researcher Preferences</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">_Person In Tree:</property>
|
||||
<property name="use-underline">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">9</property>
|
||||
<property name="width">2</property>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">9</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="remove_button">
|
||||
<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="relief">none</property>
|
||||
<signal name="clicked" handler="on_remove_button_clicked" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="image2699">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">list-remove</property>
|
||||
<child internal-child="accessible">
|
||||
<object class="AtkObject" id="image2699-atkobject">
|
||||
<property name="AtkObject::accessible-description" translatable="yes">Remove</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child internal-child="accessible">
|
||||
<object class="AtkObject" id="remove_button-atkobject">
|
||||
<property name="AtkObject::accessible-name" translatable="yes">Person</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="select_button">
|
||||
<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="relief">none</property>
|
||||
<signal name="clicked" handler="on_select_button_clicked" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="image2671">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">gtk-index</property>
|
||||
<child internal-child="accessible">
|
||||
<object class="AtkObject" id="image2671-atkobject">
|
||||
<property name="AtkObject::accessible-description" translatable="yes">Selector</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child internal-child="accessible">
|
||||
<object class="AtkObject" id="select_button-atkobject">
|
||||
<property name="AtkObject::accessible-name" translatable="yes">Person</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="padding">2</property>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="add_button">
|
||||
<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="relief">none</property>
|
||||
<signal name="clicked" handler="on_add_button_clicked" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="image2697">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">list-add</property>
|
||||
<child internal-child="accessible">
|
||||
<object class="AtkObject" id="image2697-atkobject">
|
||||
<property name="AtkObject::accessible-description" translatable="yes">Add</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child internal-child="accessible">
|
||||
<object class="AtkObject" id="add_button-atkobject">
|
||||
<property name="AtkObject::accessible-name" translatable="yes">Person</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="person_name">
|
||||
<property name="name">person_name</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">9</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
|
@ -5,6 +5,7 @@
|
||||
# Copyright (C) 2008 Brian G. Matherly
|
||||
# Copyright (C) 2010 Jakim Friant
|
||||
# Copyright (C) 2010 Nick Hall
|
||||
# Copyright (C) 2022 Christopher Horn
|
||||
#
|
||||
# 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
|
||||
@ -25,28 +26,27 @@
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
#
|
||||
# gnome/gtk
|
||||
# Gramps modules
|
||||
#
|
||||
# -------------------------------------------------------------------------
|
||||
from gi.repository import Gtk
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
#
|
||||
# gramps modules
|
||||
#
|
||||
# -------------------------------------------------------------------------
|
||||
from gramps.gen.const import URL_MANUAL_PAGE
|
||||
from gramps.gen.config import config
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
from gramps.gen.const import URL_MANUAL_PAGE
|
||||
from gramps.gen.display.name import displayer as name_displayer
|
||||
from gramps.gen.errors import WindowActiveError
|
||||
from gramps.gen.lib import Person
|
||||
from gramps.gen.utils.config import get_researcher
|
||||
from gramps.gui.dialog import QuestionDialog
|
||||
from gramps.gui.display import display_help
|
||||
from gramps.gui.widgets import MonitoredEntry
|
||||
from gramps.gui.editors import EditPerson
|
||||
from gramps.gui.glade import Glade
|
||||
from gramps.gui.managedwindow import ManagedWindow
|
||||
from gramps.gui.plug import tool
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
from gramps.gui.selectors import SelectorFactory
|
||||
from gramps.gui.utils import is_right_click
|
||||
from gramps.gui.widgets import MonitoredEntry
|
||||
|
||||
_ = glocale.translation.sgettext
|
||||
from gramps.gui.glade import Glade
|
||||
from gramps.gui.utils import is_right_click
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
#
|
||||
@ -58,7 +58,7 @@ WIKI_HELP_SEC = _("Edit_Database_Owner_Information", "manual")
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
#
|
||||
# constants
|
||||
# Constants
|
||||
#
|
||||
# -------------------------------------------------------------------------
|
||||
config_keys = (
|
||||
@ -91,45 +91,49 @@ class OwnerEditor(tool.Tool, ManagedWindow):
|
||||
uistate = user.uistate
|
||||
ManagedWindow.__init__(self, uistate, [], self.__class__)
|
||||
tool.Tool.__init__(self, dbstate, options_class, name)
|
||||
|
||||
self.dbstate = dbstate
|
||||
self.person = self.db.get_researcher_person()
|
||||
self.display()
|
||||
|
||||
def display(self):
|
||||
"""
|
||||
Display the edit dialog.
|
||||
"""
|
||||
# get the main window from glade
|
||||
topDialog = Glade()
|
||||
top_dialog = Glade()
|
||||
|
||||
# set gramps style title for the window
|
||||
window = topDialog.toplevel
|
||||
window = top_dialog.toplevel
|
||||
self.set_window(
|
||||
window, topDialog.get_object("title"), _("Database Owner Editor")
|
||||
window, top_dialog.get_object("title"), _("Database Owner Editor")
|
||||
)
|
||||
self.setup_configs("interface.ownereditor", 500, 400)
|
||||
|
||||
# move help button to the left side
|
||||
action_area = topDialog.get_object("action_area")
|
||||
help_button = topDialog.get_object("help_button")
|
||||
action_area = top_dialog.get_object("action_area")
|
||||
help_button = top_dialog.get_object("help_button")
|
||||
action_area.set_child_secondary(help_button, True)
|
||||
|
||||
# connect signals
|
||||
topDialog.connect_signals(
|
||||
top_dialog.connect_signals(
|
||||
{
|
||||
"on_select_button_clicked": self.on_select_button_clicked,
|
||||
"on_add_button_clicked": self.on_add_button_clicked,
|
||||
"on_remove_button_clicked": self.on_remove_button_clicked,
|
||||
"on_ok_button_clicked": self.on_ok_button_clicked,
|
||||
"on_cancel_button_clicked": self.close,
|
||||
"on_help_button_clicked": self.on_help_button_clicked,
|
||||
"on_help_button_clicked": on_help_button_clicked,
|
||||
"on_eventbox_button_press_event": self.on_button_press_event,
|
||||
"on_menu_activate": self.on_menu_activate,
|
||||
}
|
||||
)
|
||||
|
||||
# fetch the popup menu
|
||||
self.menu = topDialog.get_object("popup_menu")
|
||||
self.menu = top_dialog.get_object("popup_menu")
|
||||
self.track_ref_for_deletion("menu")
|
||||
|
||||
# topDialog.connect_signals({"on_menu_activate": self.on_menu_activate})
|
||||
|
||||
# get current db owner and attach it to the entries of the window
|
||||
self.owner = self.db.get_researcher()
|
||||
|
||||
self.entries = []
|
||||
entry = [
|
||||
("name", self.owner.set_name, self.owner.get_name),
|
||||
@ -146,52 +150,153 @@ class OwnerEditor(tool.Tool, ManagedWindow):
|
||||
for name, set_fn, get_fn in entry:
|
||||
self.entries.append(
|
||||
MonitoredEntry(
|
||||
topDialog.get_object(name), set_fn, get_fn, self.db.readonly
|
||||
top_dialog.get_object(name),
|
||||
set_fn,
|
||||
get_fn,
|
||||
self.db.readonly,
|
||||
)
|
||||
)
|
||||
|
||||
self.person_label = top_dialog.get_object("person_name")
|
||||
self.render_person()
|
||||
|
||||
# ok, let's see what we've done
|
||||
self.show()
|
||||
|
||||
def on_ok_button_clicked(self, obj):
|
||||
"""Update the current db's owner information from editor"""
|
||||
def render_person(self):
|
||||
"""
|
||||
Render person name and gramps id.
|
||||
"""
|
||||
if self.person:
|
||||
name = "%s [%s]" % (
|
||||
name_displayer.display(self.person),
|
||||
self.person.gramps_id,
|
||||
)
|
||||
self.person_label.set_label(name.strip())
|
||||
else:
|
||||
self.person_label.set_label("")
|
||||
|
||||
def on_select_button_clicked(self, _cb_obj):
|
||||
"""
|
||||
Select a person.
|
||||
"""
|
||||
person_selector = SelectorFactory("Person")
|
||||
selector = person_selector(
|
||||
self.dbstate, self.uistate, self.track, _("Select Researcher")
|
||||
)
|
||||
person = selector.run()
|
||||
if person:
|
||||
self.person = person
|
||||
self.render_person()
|
||||
|
||||
def on_add_button_clicked(self, _cb_obj):
|
||||
"""
|
||||
Launch new person editor.
|
||||
"""
|
||||
try:
|
||||
EditPerson(
|
||||
self.dbstate,
|
||||
self.uistate,
|
||||
self.track,
|
||||
Person(),
|
||||
self.add_person,
|
||||
)
|
||||
except WindowActiveError:
|
||||
pass
|
||||
|
||||
def on_remove_button_clicked(self, _cb_obj):
|
||||
"""
|
||||
Verify the remove.
|
||||
"""
|
||||
if self.person:
|
||||
QuestionDialog(
|
||||
_("Remove researcher association to %s?")
|
||||
% name_displayer.display(self.person),
|
||||
_(
|
||||
"Removing the association only removes the reference "
|
||||
"between the researcher and a person in the database, "
|
||||
"it does not delete either."
|
||||
),
|
||||
_("Remove Association"),
|
||||
self.remove_association,
|
||||
parent=self.window,
|
||||
)
|
||||
|
||||
def remove_association(self):
|
||||
"""
|
||||
Remove the association.
|
||||
"""
|
||||
self.person = ""
|
||||
self.render_person()
|
||||
|
||||
def add_person(self, person):
|
||||
"""
|
||||
Save person for update.
|
||||
"""
|
||||
self.person = person
|
||||
self.render_person()
|
||||
|
||||
def on_ok_button_clicked(self, _cb_obj):
|
||||
"""
|
||||
Update the current db's owner information from editor.
|
||||
"""
|
||||
self.db.set_researcher(self.owner)
|
||||
if self.person:
|
||||
self.db.set_researcher_handle(self.person.handle)
|
||||
else:
|
||||
self.db.set_researcher_handle("")
|
||||
self.menu.destroy()
|
||||
self.close()
|
||||
|
||||
def on_help_button_clicked(self, obj):
|
||||
"""Display the relevant portion of Gramps manual"""
|
||||
display_help(webpage=WIKI_HELP_PAGE, section=WIKI_HELP_SEC)
|
||||
|
||||
def on_button_press_event(self, obj, event):
|
||||
"""Shows popup-menu for db <-> preferences copying"""
|
||||
def on_button_press_event(self, _cb_obj, event):
|
||||
"""
|
||||
Shows popup-menu for db <-> preferences copying.
|
||||
"""
|
||||
if is_right_click(event):
|
||||
self.menu.popup(None, None, None, None, 0, 0)
|
||||
|
||||
def build_menu_names(self, obj):
|
||||
"""
|
||||
Return the menu names.
|
||||
"""
|
||||
return (_("Main window"), _("Edit database owner information"))
|
||||
|
||||
def on_menu_activate(self, menuitem):
|
||||
"""Copies the owner information from/to the preferences"""
|
||||
"""
|
||||
Copies the owner information from/to the preferences.
|
||||
"""
|
||||
if menuitem.props.name == "copy_from_preferences_to_db":
|
||||
self.owner.set_from(get_researcher())
|
||||
for entry in self.entries:
|
||||
entry.update()
|
||||
|
||||
elif menuitem.props.name == "copy_from_db_to_preferences":
|
||||
for i in range(len(config_keys)):
|
||||
config.set(config_keys[i], self.owner.get()[i])
|
||||
for index, config_key in enumerate(config_keys):
|
||||
config.set(config_key, self.owner.get()[index])
|
||||
|
||||
def clean_up(self):
|
||||
"""
|
||||
Cleanup.
|
||||
"""
|
||||
self.menu.destroy()
|
||||
|
||||
|
||||
def on_help_button_clicked(_cb_obj):
|
||||
"""
|
||||
Display the relevant portion of Gramps manual.
|
||||
"""
|
||||
display_help(webpage=WIKI_HELP_PAGE, section=WIKI_HELP_SEC)
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
#
|
||||
# OwnerEditorOptions (None at the moment)
|
||||
#
|
||||
# -------------------------------------------------------------------------
|
||||
class OwnerEditorOptions(tool.ToolOptions):
|
||||
"""Defines options and provides handling interface."""
|
||||
"""
|
||||
Defines options and provides handling interface.
|
||||
"""
|
||||
|
||||
def __init__(self, name, person_id=None):
|
||||
tool.ToolOptions.__init__(self, name, person_id)
|
||||
|
@ -230,6 +230,28 @@ register(
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
#
|
||||
# Edit Tree Information
|
||||
#
|
||||
#------------------------------------------------------------------------
|
||||
|
||||
register(TOOL,
|
||||
id = 'edittree',
|
||||
name = _("Edit Tree Information"),
|
||||
description = _("Enabled editing of tree information."),
|
||||
version = '1.0',
|
||||
gramps_target_version = MODULE_VERSION,
|
||||
status = STABLE,
|
||||
fname = 'edittree.py',
|
||||
authors = ["Zsolt Foldvari", "Christopher Horn"],
|
||||
authors_email = ["zfoldvar@users.sourceforge.net"],
|
||||
category = TOOL_DBPROC,
|
||||
toolclass = 'TreeEditor',
|
||||
optionclass = 'TreeEditorOptions',
|
||||
tool_modes = [TOOL_MODE_GUI]
|
||||
)
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
# Edit Database Owner Information
|
||||
#
|
||||
# ------------------------------------------------------------------------
|
||||
|
Loading…
Reference in New Issue
Block a user