Merge branch 'maintenance/gramps41' of github.com:gramps-project/gramps into gramps41
This commit is contained in:
commit
d8ba31cc29
43
NEWS
43
NEWS
@ -1,3 +1,46 @@
|
||||
2015-05-01
|
||||
Version 4.1.3, "Thou shalt not count to five", a maintenance release.
|
||||
* Fix db upgrade failure
|
||||
* GtkDialog mapped without a transient parent
|
||||
* [Gedcom} SUBN and SUBM record handling
|
||||
* [Gedcom] Import/export round trip causes lost information
|
||||
* [Gedcom] Entering a witness to an event such as marriage might be ignored
|
||||
* [Gedcom] Gramps can't import estim. date period exported by itself
|
||||
* [Gedcom] 1/4 and 1/2 ANSEL characters not supported on importing ANSEL
|
||||
* [Gedcom] Importing file containing multibyte UTF-8 characters fails
|
||||
* [Gedcom] Import fails for ANSI file under python 3
|
||||
* [Gedcom] Failure importing ANSEL encoded gedcom file.
|
||||
* [Gedcom] Characters ignored on a Gedcom encoded ANSI (cp1252 West Europe, USA)
|
||||
* [Gedcom] NameError in importer
|
||||
* [Gedcom] Event address is lost on import, i.e. disconnected from event
|
||||
* Crash on geneweb export with python3
|
||||
* GuiColorOption missing avail-changed event handler
|
||||
* Bad generation of [timeline report] ODT files since 4.0.0
|
||||
* Fix bad handle in explanation note for unknown event
|
||||
* Fix spurious generation of empty 'Alternative Name' in place.merge()
|
||||
* Support creating directories in various scenarios
|
||||
* Attempting to add a bookmark causes an error
|
||||
* Long series of "unhandled exception" popup boxes while doing a check & repair
|
||||
* Crash when trying to link existing place as an enclosing place using P0001 number
|
||||
* HTML view fails to load
|
||||
* Relationship Graph crashes
|
||||
* Python3 needs new_subpixbuf not subpixbuf
|
||||
* Regression: running gramps from crontab fails
|
||||
* tag_map is not initialized
|
||||
* Some labels now fit better on citations sidebar filter
|
||||
* Event columns in web narrative are too narrow
|
||||
* Problem by start program (launcher)
|
||||
* Translation string missing in Not Related tool for help and close button
|
||||
* Date format month/year is not well reported at editing time [in Italian]
|
||||
* Fix unknown gender relationships handler for the french locale
|
||||
* Fix a handle type bug on sidebar filter
|
||||
* Tidy up About dialog
|
||||
* Cleanup on some man files
|
||||
* Convert some remaining unicode literals
|
||||
* Fix mac menubar setting
|
||||
* Enable python3 to run po/update_po.py
|
||||
* Updated translations: cs, de, fr, is, nl
|
||||
|
||||
2015-02-28
|
||||
Version 4.1.2, "That's no ordinary rabbit", a maintenance release.
|
||||
* Error converting python2 utf-8 strings to python3 str when loading data from database
|
||||
|
@ -248,6 +248,14 @@ table.primobjlist tr.BeginLetter td, table.primobjlist tr.BeginSurname td {
|
||||
td.ColumnLetter, td.ColumnRowLabel {
|
||||
font-weight: bold;
|
||||
}
|
||||
/* bug #8213 testing by Stephane, 2014-12-6 */
|
||||
td.ColumnEvent, td.ColumnDate {
|
||||
white-space: nowrap;
|
||||
}
|
||||
td.ColumnPlace, td.ColumnDescription {
|
||||
width: 20%
|
||||
}
|
||||
/* end of customizations by Stephane */
|
||||
td.ColumnBirth, td.ColumnDeath, td.ColumnPartner, td.ColumnParents {
|
||||
font-size: 90%;
|
||||
}
|
||||
|
@ -82,8 +82,8 @@ gramps(1) @VERSION@ gramps(1)
|
||||
|
||||
**-i** , **--import=** *FICHIER*
|
||||
Importer des données depuis un *FICHIER* . Si vous n'avez pas
|
||||
spécifié de base de données alors une base de données temporaire
|
||||
est utilisée; elle sera effacée quand vous quitterez gramps.
|
||||
spécifié de base de données, alors une base de données vide
|
||||
est utilisée.
|
||||
|
||||
Quand plus d'un fichier doit être importé, chacun doit être
|
||||
précédé par la commande **-i** . Ces fichiers sont importés dans le
|
||||
|
@ -1,4 +1,4 @@
|
||||
.TH "GRAMPS" "1" "28 December 2012" "4.0" "Gramps"
|
||||
.TH "GRAMPS" "1" "09 mars 2015" "4.1" "Gramps"
|
||||
.SH NAME
|
||||
gramps \- Gramps Documentation
|
||||
.
|
||||
@ -106,8 +106,8 @@ sources, vous devez utiliser l\(aqoption d\(aqimport.
|
||||
.TP
|
||||
.B \fB\-i\fP , \fB\-\-import=\fP \fIFICHIER\fP
|
||||
Importer des données depuis un \fIFICHIER\fP . Si vous n\(aqavez pas
|
||||
spécifié de base de données alors une base de données temporaire
|
||||
est utilisée; elle sera effacée quand vous quitterez gramps.
|
||||
spécifié de base de données, alors une base de données vide
|
||||
est utilisée.
|
||||
.sp
|
||||
Quand plus d\(aqun fichier doit être importé, chacun doit être
|
||||
précédé par la commande \fB\-i\fP . Ces fichiers sont importés dans le
|
||||
@ -337,6 +337,6 @@ gramps(1) @VERSION@ gramps(1)
|
||||
.SH AUTHOR
|
||||
Jerome Rapinat
|
||||
.SH COPYRIGHT
|
||||
2012, Gramps project
|
||||
2015, Gramps project
|
||||
.\" Generated by docutils manpage writer.
|
||||
.
|
||||
|
@ -3,11 +3,11 @@
|
||||
"http://gramps-project.org/xml/1.6.0/grampsxml.dtd">
|
||||
<database xmlns="http://gramps-project.org/xml/1.6.0/">
|
||||
<header>
|
||||
<created date="2014-11-13" version="4.1.0"/>
|
||||
<created date="2015-05-10" version="4.1.4"/>
|
||||
<researcher>
|
||||
<resname>Alex Roitman,,,</resname>
|
||||
</researcher>
|
||||
<mediapath>/home/cristina/gramps/master/example/gramps</mediapath>
|
||||
<mediapath>/home/pierre/Gramps/master/example/gramps</mediapath>
|
||||
</header>
|
||||
<name-formats>
|
||||
<format number="-1" name="SURNAME, Given (Common)" fmt_str="SURNAME, given (common)" active="1"/>
|
||||
@ -91,6 +91,11 @@
|
||||
<dateval val="1592" type="about"/>
|
||||
<description>Birth of Abbott, Frances</description>
|
||||
</event>
|
||||
<event handle="_a5af0eb6abd74c3d7fc" change="1284030605" id="E3415">
|
||||
<type>Death</type>
|
||||
<dateval val="1642-01" type="about"/>
|
||||
<description>Death of Abbott, Frances</description>
|
||||
</event>
|
||||
<event handle="_a5af0eb6add73de72aa" change="1284030598" id="E0014">
|
||||
<type>Birth</type>
|
||||
<dateval val="1520" type="about"/>
|
||||
@ -17897,11 +17902,6 @@
|
||||
<type>Death</type>
|
||||
<dateval val="1850" type="about" quality="estimated"/>
|
||||
</event>
|
||||
<event handle="_a5af0eb6abd74c3d7fc" change="1284030605" id="E3415">
|
||||
<type>Death</type>
|
||||
<dateval val="1642-01" type="about"/>
|
||||
<description>Death of Abbott, Frances</description>
|
||||
</event>
|
||||
</events>
|
||||
<people home="_GNUJQCL9MD64AM56OH">
|
||||
<person handle="_004KQCGYT27EEPQHK" change="1185438865" id="I0552">
|
||||
@ -20260,7 +20260,7 @@
|
||||
<parentin hlink="_HQ8KQCT2UX4S9I0E26"/>
|
||||
<citationref hlink="_c140d24b31f74169170"/>
|
||||
</person>
|
||||
<person handle="_3RFKQCNKMX9HVLNSLW" change="1185438865" id="I1116">
|
||||
<person handle="_3RFKQCNKMX9HVLNSLW" change="1431174900" id="I1116">
|
||||
<gender>F</gender>
|
||||
<name type="Birth Name">
|
||||
<surname>Garner</surname>
|
||||
@ -22104,7 +22104,7 @@
|
||||
<parentin hlink="_JT4KQC83ZKPOLC0UEJ"/>
|
||||
<citationref hlink="_c140d24fa2503a14583"/>
|
||||
</person>
|
||||
<person handle="_6TFKQCUTO94WB2NHN" change="1185438865" id="I1119">
|
||||
<person handle="_6TFKQCUTO94WB2NHN" change="1431174900" id="I1119">
|
||||
<gender>F</gender>
|
||||
<name type="Birth Name">
|
||||
<first>Zelpha Josephine</first>
|
||||
@ -23913,7 +23913,7 @@
|
||||
<parentin hlink="_1RUJQCCL9MVRYLMTBO"/>
|
||||
<citationref hlink="_c140d254dcc234394a3"/>
|
||||
</person>
|
||||
<person handle="_9QFKQC54ET79K2SD57" change="1185438865" id="I1115">
|
||||
<person handle="_9QFKQC54ET79K2SD57" change="1431174900" id="I1115">
|
||||
<gender>F</gender>
|
||||
<name type="Birth Name">
|
||||
<first>Mary M.</first>
|
||||
@ -24600,7 +24600,7 @@
|
||||
<parentin hlink="_4W1KQCYZD6N5M576RA"/>
|
||||
<citationref hlink="_c140d2566d57b164cf5"/>
|
||||
</person>
|
||||
<person handle="_AWFKQCJELLUWDY2PD3" change="1284030919" id="I1123">
|
||||
<person handle="_AWFKQCJELLUWDY2PD3" change="1431174900" id="I1123">
|
||||
<gender>M</gender>
|
||||
<name type="Birth Name">
|
||||
<first>Robert F.</first>
|
||||
@ -27182,7 +27182,7 @@
|
||||
<parentin hlink="_0Q3KQCBZ4421A3L5B4"/>
|
||||
<citationref hlink="_c140d25c5be3120050a"/>
|
||||
</person>
|
||||
<person handle="_EPFKQCETTDTEL3PYIR" change="1185438865" id="I1114">
|
||||
<person handle="_EPFKQCETTDTEL3PYIR" change="1431174900" id="I1114">
|
||||
<gender>F</gender>
|
||||
<name type="Birth Name">
|
||||
<first>Mary J.</first>
|
||||
@ -28226,7 +28226,7 @@
|
||||
<parentin hlink="_BWAKQCZLIWDX9ZEFED"/>
|
||||
<citationref hlink="_c140d25eec45aabbd80"/>
|
||||
</person>
|
||||
<person handle="_GNUJQCL9MD64AM56OH" change="1328027440" id="I0044">
|
||||
<person handle="_GNUJQCL9MD64AM56OH" change="1431174904" id="I0044">
|
||||
<gender>M</gender>
|
||||
<name type="Birth Name">
|
||||
<first>Lewis Anderson</first>
|
||||
@ -28454,7 +28454,7 @@
|
||||
<childof hlink="_05XJQC935HU62H3KL4"/>
|
||||
<citationref hlink="_c140d25f5c448b251ca"/>
|
||||
</person>
|
||||
<person handle="_GYFKQCPH8Q0JDN94GR" change="1185438865" id="I1126">
|
||||
<person handle="_GYFKQCPH8Q0JDN94GR" change="1431174900" id="I1126">
|
||||
<gender>F</gender>
|
||||
<name type="Birth Name">
|
||||
<first>Anetta</first>
|
||||
@ -32105,7 +32105,7 @@
|
||||
<parentin hlink="_ZA6KQC27P0I8E2JZUC"/>
|
||||
<citationref hlink="_c140d2677c105a1b132"/>
|
||||
</person>
|
||||
<person handle="_MUFKQCMXUJ07MCDUNI" change="1185438865" id="I1121">
|
||||
<person handle="_MUFKQCMXUJ07MCDUNI" change="1431174900" id="I1121">
|
||||
<gender>F</gender>
|
||||
<name type="Birth Name">
|
||||
<first>Iola Elizabeth Betty</first>
|
||||
@ -33279,7 +33279,7 @@
|
||||
<parentin hlink="_9SEKQCAAWRUCIO7A0M"/>
|
||||
<citationref hlink="_c140d269f4c7c13bf87"/>
|
||||
</person>
|
||||
<person handle="_ORFKQC4KLWEGTGR19L" change="1185438865" id="I1117">
|
||||
<person handle="_ORFKQC4KLWEGTGR19L" change="1431174900" id="I1117">
|
||||
<gender>F</gender>
|
||||
<name type="Birth Name">
|
||||
<first>Rebecca Catharine</first>
|
||||
@ -33963,7 +33963,7 @@
|
||||
<parentin hlink="_IXDKQCOYLEMDKWJZPC"/>
|
||||
<citationref hlink="_c140d26b98d33ec7f15"/>
|
||||
</person>
|
||||
<person handle="_PXFKQCXEHJX3W1Q1IV" change="1185438865" id="I1125">
|
||||
<person handle="_PXFKQCXEHJX3W1Q1IV" change="1431174900" id="I1125">
|
||||
<gender>F</gender>
|
||||
<name type="Birth Name">
|
||||
<first>Emma A.</first>
|
||||
@ -35695,7 +35695,7 @@
|
||||
<parentin hlink="_FP4KQCQQX8O84KK3IF"/>
|
||||
<citationref hlink="_c140d27142a05b2d019"/>
|
||||
</person>
|
||||
<person handle="_SOFKQCBYAO18OWC0CS" change="1185438865" id="I1113">
|
||||
<person handle="_SOFKQCBYAO18OWC0CS" change="1431174900" id="I1113">
|
||||
<gender>F</gender>
|
||||
<name type="Birth Name">
|
||||
<first>Phebe</first>
|
||||
@ -37068,7 +37068,7 @@
|
||||
<parentin hlink="_7ZWJQC8ZR4WJZE09RW"/>
|
||||
<citationref hlink="_c140d276c1802ec5ac3"/>
|
||||
</person>
|
||||
<person handle="_UZFKQCIHVT44DC9KGH" change="1185438865" id="I1128">
|
||||
<person handle="_UZFKQCIHVT44DC9KGH" change="1431174900" id="I1128">
|
||||
<gender>F</gender>
|
||||
<name type="Birth Name">
|
||||
<first>Antoinette</first>
|
||||
@ -42060,12 +42060,12 @@
|
||||
<childref hlink="_GH0KQCGPLF5J17PELU"/>
|
||||
<citationref hlink="_c140d286d0e2f46fb29"/>
|
||||
</family>
|
||||
<family handle="_8OUJQCUVZ0XML7BQLF" change="1185438865" id="F0018">
|
||||
<family handle="_8OUJQCUVZ0XML7BQLF" change="1431174900" id="F0018">
|
||||
<rel type="Married"/>
|
||||
<father hlink="_35WJQC1B7T7NPV8OLV"/>
|
||||
<mother hlink="_46WJQCIOLQ0KOX2XCC"/>
|
||||
<eventref hlink="_a5af0ed602318310d6d" role="Family"/>
|
||||
<childref hlink="_GNUJQCL9MD64AM56OH"/>
|
||||
<childref hlink="_GNUJQCL9MD64AM56OH" mrel="Custom relationship to mother" frel="Custom relationship to father"/>
|
||||
<childref hlink="_SOFKQCBYAO18OWC0CS"/>
|
||||
<childref hlink="_EPFKQCETTDTEL3PYIR"/>
|
||||
<childref hlink="_9QFKQC54ET79K2SD57"/>
|
||||
@ -63928,6 +63928,94 @@ page 26 Repository:Address</text>
|
||||
<range start="0" end="705"/>
|
||||
</style>
|
||||
</note>
|
||||
<note handle="_d0436bba4ec328d3b631259a4ee" change="1431184305" id="_header1" type="General">
|
||||
<text>Title for the example pages</text>
|
||||
<style name="fontcolor" value="#ef2929">
|
||||
<range start="0" end="27"/>
|
||||
</style>
|
||||
<style name="underline">
|
||||
<range start="0" end="27"/>
|
||||
</style>
|
||||
<style name="fontface" value="Serif">
|
||||
<range start="0" end="27"/>
|
||||
</style>
|
||||
<style name="bold">
|
||||
<range start="0" end="27"/>
|
||||
</style>
|
||||
<style name="fontsize" value="8">
|
||||
<range start="0" end="27"/>
|
||||
</style>
|
||||
</note>
|
||||
<note handle="_d0436bcc69d6bba278bff5bc7db" change="1431184300" id="_footer1" type="General">
|
||||
<text>Footer: exported by __GRAMPS_HOMEPAGE__ on __EXPORT_DATE__</text>
|
||||
</note>
|
||||
<note handle="_d0436be64ac277b615b79b34e72" change="1431211661" id="_custom1" type="General">
|
||||
<text>Export date: __EXPORT_DATE__
|
||||
GRAMPS homepage: __GRAMPS_HOMEPAGE__
|
||||
GRAMPS version: __GRAMPS_VERSION__
|
||||
|
||||
Number of families: __NB_FAMILIES__
|
||||
Number of persons: __NB_INDIVIDUALS__
|
||||
Number of media objects: __NB_MEDIA__
|
||||
Number of sources: __NB_SOURCES__
|
||||
Number of repositories: __NB_REPOSITORIES__
|
||||
Number of places: __NB_PLACES__
|
||||
|
||||
Search form:
|
||||
__SEARCH_FORM__
|
||||
|
||||
Test link person: Garner von Zieliński, Lewis Anderson Sr
|
||||
Test link family: Family of Warner, Allen Carl and Garner, Rita Marie
|
||||
Test link source: World of the Wierd
|
||||
Test link media: 1897_expeditionsmannschaft_rio_a
|
||||
Test link place: Warren-Farmington Hills-Troy, MI
|
||||
|
||||
Test internet link: blog.codinghorror.com
|
||||
Test relative path link: relative file path to "archive.zip"
|
||||
Test relative path link: relative file path to "archive.tgz"
|
||||
|
||||
Thumbnail for "1897_expeditionsmannschaft_rio_a":
|
||||
__THUMB_O0010__
|
||||
Image "AntoineClaudet":
|
||||
__MEDIA_O0011__
|
||||
Thumbnail for "1897_expeditionsmannschaft_rio_a" with link:
|
||||
__THUMB_O0010__
|
||||
Image "AntoineClaudet" with link:
|
||||
__MEDIA_O0011__
|
||||
|
||||
Wrong media ID:
|
||||
__MEDIA_wrong id__</text>
|
||||
<style name="link" value="relative://relative.archive.zip">
|
||||
<range start="663" end="686"/>
|
||||
</style>
|
||||
<style name="link" value="gramps://Media/handle/238CGQ939HG18SS5MG">
|
||||
<range start="952" end="967"/>
|
||||
</style>
|
||||
<style name="link" value="gramps://Media/handle/238CGQ939HG18SS5MG">
|
||||
<range start="520" end="535"/>
|
||||
</style>
|
||||
<style name="link" value="gramps://Family/handle/48TJQCGNNIR5SJRCAK">
|
||||
<range start="413" end="429"/>
|
||||
</style>
|
||||
<style name="link" value="gramps://Person/handle/GNUJQCL9MD64AM56OH">
|
||||
<range start="355" end="371"/>
|
||||
</style>
|
||||
<style name="link" value="http://blog.codinghorror.com/">
|
||||
<range start="621" end="639"/>
|
||||
</style>
|
||||
<style name="link" value="gramps://Source/handle/VUBKMQTA2XZG1V6QP8">
|
||||
<range start="483" end="499"/>
|
||||
</style>
|
||||
<style name="link" value="gramps://Place/handle/3WTJQCB9F2MX9W98VP">
|
||||
<range start="570" end="585"/>
|
||||
</style>
|
||||
<style name="link" value="gramps://Media/handle/Y3ARGQWE088EQRTTDH">
|
||||
<range start="1002" end="1017"/>
|
||||
</style>
|
||||
<style name="link" value="relative://relative.archive.tgz">
|
||||
<range start="724" end="747"/>
|
||||
</style>
|
||||
</note>
|
||||
</notes>
|
||||
<bookmarks>
|
||||
<bookmark target="person" hlink="_AWFKQCJELLUWDY2PD3"/>
|
||||
|
@ -497,6 +497,8 @@ def time_val(dirpath):
|
||||
if tval_mod > tval:
|
||||
tval = tval_mod
|
||||
last = time.strftime('%x %X', time.localtime(tval))
|
||||
if sys.version_info[0] < 3:
|
||||
last = last.decode(glocale.encoding)
|
||||
else:
|
||||
tval = 0
|
||||
last = _("Never")
|
||||
@ -517,6 +519,6 @@ def find_locker_name(dirpath):
|
||||
# feature request 2356: avoid genitive form
|
||||
last = _("Locked by %s") % username
|
||||
ifile.close()
|
||||
except (OSError, IOError):
|
||||
except (OSError, IOError, UnicodeDecodeError):
|
||||
last = _("Unknown")
|
||||
return last
|
||||
|
@ -117,7 +117,7 @@ class User(user.User):
|
||||
"""
|
||||
self._fileout.write("\r100%\n")
|
||||
|
||||
def prompt(self, title, message, accept_label, reject_label):
|
||||
def prompt(self, title, message, accept_label, reject_label, parent=None):
|
||||
"""
|
||||
Prompt the user with a message to select an alternative.
|
||||
|
||||
|
@ -49,7 +49,7 @@ from .constfunc import get_env_var, conv_to_unicode
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
PROGRAM_NAME = "Gramps"
|
||||
from ..version import VERSION, VERSION_TUPLE, major_version
|
||||
from gramps.version import VERSION, VERSION_TUPLE, major_version
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Standard GRAMPS Websites
|
||||
|
@ -631,4 +631,4 @@ class DateDisplayEn(DateDisplay):
|
||||
|
||||
display = DateDisplay.display_formatted
|
||||
|
||||
_locale = _grampslocale.glocale # normally set in register_datehandler
|
||||
_locale = DateDisplay._locale # normally set in register_datehandler
|
||||
|
@ -629,8 +629,12 @@ class DateParser(object):
|
||||
else:
|
||||
y = self._get_int(groups[4])
|
||||
if self.dmy:
|
||||
m = self._get_int(groups[3])
|
||||
d = self._get_int(groups[1])
|
||||
if groups[3] is None:
|
||||
m = self._get_int(groups[1])
|
||||
d = 0
|
||||
else:
|
||||
m = self._get_int(groups[3])
|
||||
d = self._get_int(groups[1])
|
||||
else:
|
||||
m = self._get_int(groups[1])
|
||||
d = self._get_int(groups[3])
|
||||
|
@ -27,6 +27,7 @@ Class handling language-specific selection for date parser and displayer.
|
||||
# Python modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
import sys
|
||||
import time
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@ -35,6 +36,7 @@ import time
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from ..lib.date import Date
|
||||
from ..const import GRAMPS_LOCALE as glocale
|
||||
from . import LANG_TO_DISPLAY, LANG, parser, displayer
|
||||
|
||||
#--------------------------------------------------------------
|
||||
@ -94,4 +96,7 @@ def format_time(secs):
|
||||
"""
|
||||
t = time.localtime(secs)
|
||||
d = Date(t.tm_year, t.tm_mon, t.tm_mday)
|
||||
return displayer.display(d) + time.strftime(' %X', t)
|
||||
if sys.version_info[0] < 3:
|
||||
return displayer.display(d) + time.strftime(' %X', t).decode(glocale.encoding)
|
||||
else:
|
||||
return displayer.display(d) + time.strftime(' %X', t)
|
||||
|
@ -55,6 +55,8 @@ class PlaceDisplay(object):
|
||||
if place_handle:
|
||||
place = db.get_place_from_handle(place_handle)
|
||||
return self.display(db, place, event.get_date_object())
|
||||
else:
|
||||
return ""
|
||||
|
||||
def display(self, db, place, date=None):
|
||||
if not place:
|
||||
|
@ -63,34 +63,42 @@ class IsRelatedWith(Rule):
|
||||
return person.handle in self.relatives
|
||||
|
||||
|
||||
def add_relative(self, person):
|
||||
"""Recursive function that scans relatives and add them to self.relatives"""
|
||||
if not(person) or person.handle in self.relatives:
|
||||
def add_relative(self, start):
|
||||
"""Non-recursive function that scans relatives and add them to self.relatives"""
|
||||
if not(start):
|
||||
return
|
||||
|
||||
# Add the relative to the list
|
||||
self.relatives.append(person.handle)
|
||||
expand = [start]
|
||||
relatives = {}
|
||||
|
||||
while expand:
|
||||
person = expand.pop()
|
||||
# Add the relative to the list
|
||||
if person is None or (person.handle in relatives):
|
||||
continue
|
||||
relatives[person.handle] = True
|
||||
|
||||
for family_handle in person.get_parent_family_handle_list():
|
||||
family = self.db.get_family_from_handle(family_handle)
|
||||
if family:
|
||||
# Check Parents
|
||||
for parent_handle in (family.get_father_handle(), family.get_mother_handle()):
|
||||
if parent_handle:
|
||||
self.add_relative(self.db.get_person_from_handle(parent_handle))
|
||||
# Check Sibilings
|
||||
for child_ref in family.get_child_ref_list():
|
||||
self.add_relative(self.db.get_person_from_handle(child_ref.ref))
|
||||
|
||||
for family_handle in person.get_family_handle_list():
|
||||
family = self.db.get_family_from_handle(family_handle)
|
||||
if family:
|
||||
# Check Spouse
|
||||
for parent_handle in (family.get_father_handle(), family.get_mother_handle()):
|
||||
if parent_handle:
|
||||
self.add_relative(self.db.get_person_from_handle(parent_handle))
|
||||
# Check Children
|
||||
for child_ref in family.get_child_ref_list():
|
||||
self.add_relative(self.db.get_person_from_handle(child_ref.ref))
|
||||
|
||||
return
|
||||
for family_handle in person.get_parent_family_handle_list():
|
||||
family = self.db.get_family_from_handle(family_handle)
|
||||
if family:
|
||||
# Check Parents
|
||||
for parent_handle in (family.get_father_handle(), family.get_mother_handle()):
|
||||
if parent_handle:
|
||||
expand.append(self.db.get_person_from_handle(parent_handle))
|
||||
# Check Sibilings
|
||||
for child_ref in family.get_child_ref_list():
|
||||
expand.append(self.db.get_person_from_handle(child_ref.ref))
|
||||
|
||||
for family_handle in person.get_family_handle_list():
|
||||
family = self.db.get_family_from_handle(family_handle)
|
||||
if family:
|
||||
# Check Spouse
|
||||
for parent_handle in (family.get_father_handle(), family.get_mother_handle()):
|
||||
if parent_handle:
|
||||
expand.append(self.db.get_person_from_handle(parent_handle))
|
||||
# Check Children
|
||||
for child_ref in family.get_child_ref_list():
|
||||
expand.append(self.db.get_person_from_handle(child_ref.ref))
|
||||
|
||||
self.relatives = list(relatives.keys())
|
||||
return
|
||||
|
@ -571,7 +571,7 @@ class Place(CitationBase, NoteBase, MediaBase, UrlBase, PrimaryObject):
|
||||
:param acquisition: instance to merge
|
||||
:type acquisition: :class:'~.place.Place
|
||||
"""
|
||||
if acquisition.name not in self.alt_names:
|
||||
if acquisition.name and (acquisition.name not in self.alt_names):
|
||||
self.alt_names.append(acquisition.name)
|
||||
|
||||
for addendum in acquisition.alt_names:
|
||||
|
@ -33,7 +33,7 @@ import libxslt
|
||||
|
||||
from gramps.plugins.lib.libgrampsxml import GRAMPS_XML_VERSION
|
||||
from ...const import ROOT_DIR, USER_PLUGINS
|
||||
from ....version import VERSION
|
||||
from gramps.version import VERSION
|
||||
from ...lib import Name, Surname
|
||||
from ...const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.sgettext
|
||||
|
@ -42,7 +42,7 @@ import io
|
||||
# GRAMPS modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from ...version import VERSION as GRAMPSVERSION, VERSION_TUPLE
|
||||
from gramps.version import VERSION as GRAMPSVERSION, VERSION_TUPLE
|
||||
from ..const import IMAGE_DIR
|
||||
from ..const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
@ -1100,10 +1100,16 @@ class PluginRegister(object):
|
||||
continue
|
||||
lenpd = len(self.__plugindata)
|
||||
full_filename = os.path.join(dir, filename)
|
||||
if sys.version_info[0] < 3:
|
||||
fd = open(full_filename, "r")
|
||||
else:
|
||||
fd = io.open(full_filename, "r", encoding='utf-8')
|
||||
try:
|
||||
if sys.version_info[0] < 3:
|
||||
fd = open(full_filename, "r")
|
||||
else:
|
||||
fd = io.open(full_filename, "r", encoding='utf-8')
|
||||
except Exception as msg:
|
||||
print(_('ERROR: Failed reading plugin registration %(filename)s') % \
|
||||
{'filename' : filename})
|
||||
print(msg)
|
||||
continue
|
||||
stream = fd.read()
|
||||
fd.close()
|
||||
if os.path.exists(os.path.join(os.path.dirname(full_filename),
|
||||
|
@ -50,7 +50,7 @@ LOG = logging.getLogger(".gen.plug")
|
||||
#-------------------------------------------------------------------------
|
||||
from ._pluginreg import make_environment
|
||||
from ..const import USER_PLUGINS
|
||||
from ...version import VERSION_TUPLE
|
||||
from gramps.version import VERSION_TUPLE
|
||||
from . import BasePluginManager
|
||||
from ..utils.configmanager import safe_eval
|
||||
from ..config import config
|
||||
|
@ -261,8 +261,15 @@ def get_participant_from_event(db, event_handle, all_=False):
|
||||
"""
|
||||
participant = ""
|
||||
ellipses = False
|
||||
result_list = list(db.find_backlink_handles(event_handle,
|
||||
include_classes=['Person', 'Family']))
|
||||
try:
|
||||
result_list = list(db.find_backlink_handles(event_handle,
|
||||
include_classes=['Person', 'Family']))
|
||||
except:
|
||||
# during a magic batch transaction find_backlink_handles tries to
|
||||
# access the reference_map_referenced_map which is closed
|
||||
# under those circumstances.
|
||||
return ''
|
||||
|
||||
#obtain handles without duplicates
|
||||
people = set([x[1] for x in result_list if x[0] == 'Person'])
|
||||
families = set([x[1] for x in result_list if x[0] == 'Family'])
|
||||
@ -328,15 +335,7 @@ def navigation_label(db, nav_type, handle):
|
||||
elif nav_type == 'Event':
|
||||
obj = db.get_event_from_handle(handle)
|
||||
if obj:
|
||||
try:
|
||||
who = get_participant_from_event(db, handle)
|
||||
except:
|
||||
# get_participants_from_event fails when called during a magic
|
||||
# batch transaction because find_backlink_handles tries to
|
||||
# access the reference_map_referenced_map which doesn't exist
|
||||
# under those circumstances. Since setting the navigation_label
|
||||
# is inessential, just accept this and go on.
|
||||
who = ''
|
||||
who = get_participant_from_event(db, handle)
|
||||
desc = obj.get_description()
|
||||
label = obj.get_type()
|
||||
if desc:
|
||||
|
@ -208,7 +208,7 @@ class GrampsLocale(object):
|
||||
self.lang = loc[0]
|
||||
self.encoding = loc[1]
|
||||
else:
|
||||
(lang, loc) = _check_mswin_locale(lang)
|
||||
(lang, loc) = _check_mswin_locale(locale.getdefaultlocale()[0])
|
||||
if lang:
|
||||
self.lang = lang
|
||||
self.encoding = loc[1]
|
||||
|
@ -90,10 +90,7 @@ def resize_to_jpeg(source, destination, width, height, crop=None):
|
||||
(start_x, start_y, end_x, end_y
|
||||
) = crop_percentage_to_pixel(
|
||||
img.get_width(), img.get_height(), crop)
|
||||
if sys.version_info[0] < 3:
|
||||
img = img.new_subpixbuf(start_x, start_y, end_x-start_x, end_y-start_y)
|
||||
else:
|
||||
img = img.subpixbuf(start_x, start_y, end_x-start_x, end_y-start_y)
|
||||
img = img.new_subpixbuf(start_x, start_y, end_x-start_x, end_y-start_y)
|
||||
|
||||
# Need to keep the ratio intact, otherwise scaled images look stretched
|
||||
# if the dimensions aren't close in size
|
||||
@ -231,10 +228,7 @@ def resize_to_buffer(source, size, crop=None):
|
||||
(start_x, start_y, end_x, end_y
|
||||
) = crop_percentage_to_pixel(
|
||||
img.get_width(), img.get_height(), crop)
|
||||
if sys.version_info[0] < 3:
|
||||
img = img.new_subpixbuf(start_x, start_y, end_x-start_x, end_y-start_y)
|
||||
else:
|
||||
img = img.subpixbuf(start_x, start_y, end_x-start_x, end_y-start_y)
|
||||
img = img.new_subpixbuf(start_x, start_y, end_x-start_x, end_y-start_y)
|
||||
|
||||
# Need to keep the ratio intact, otherwise scaled images look stretched
|
||||
# if the dimensions aren't close in size
|
||||
@ -272,10 +266,7 @@ def resize_to_jpeg_buffer(source, size, crop=None):
|
||||
) = crop_percentage_to_pixel(
|
||||
img.get_width(), img.get_height(), crop)
|
||||
|
||||
if sys.version_info[0] < 3:
|
||||
img = img.new_subpixbuf(start_x, start_y, end_x-start_x, end_y-start_y)
|
||||
else:
|
||||
img = img.subpixbuf(start_x, start_y, end_x-start_x, end_y-start_y)
|
||||
img = img.new_subpixbuf(start_x, start_y, end_x-start_x, end_y-start_y)
|
||||
|
||||
# Need to keep the ratio intact, otherwise scaled images look stretched
|
||||
# if the dimensions aren't close in size
|
||||
|
@ -29,6 +29,7 @@ Make an 'Unknown' primary object
|
||||
# Python modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
import sys
|
||||
import time
|
||||
import os
|
||||
|
||||
@ -146,8 +147,11 @@ def make_unknown(class_arg, explanation, class_func, commit_func, transaction,
|
||||
elif isinstance(obj, Tag):
|
||||
if not hasattr(make_unknown, 'count'):
|
||||
make_unknown.count = 1 #primitive static variable
|
||||
tval = time.strftime('%x %X', time.localtime())
|
||||
if sys.version_info[0] < 3:
|
||||
tval = tval.decode(glocale.encoding)
|
||||
obj.set_name(_("Unknown, was missing %(time)s (%(count)d)") % {
|
||||
'time': time.strftime('%x %X', time.localtime()),
|
||||
'time': tval,
|
||||
'count': make_unknown.count})
|
||||
make_unknown.count += 1
|
||||
else:
|
||||
@ -165,9 +169,11 @@ def create_explanation_note(dbase):
|
||||
those objects of type "Unknown" need a explanatory note. This funcion
|
||||
provides such a note for import methods.
|
||||
"""
|
||||
tval = time.strftime('%x %X', time.localtime())
|
||||
if sys.version_info[0] < 3:
|
||||
tval = tval.decode(glocale.encoding)
|
||||
note = Note( _('Objects referenced by this note '
|
||||
'were missing in a file imported on %s.') %
|
||||
time.strftime('%x %X', time.localtime()))
|
||||
'were missing in a file imported on %s.') % tval)
|
||||
note.set_handle(create_id())
|
||||
note.set_gramps_id(dbase.find_next_note_gramps_id())
|
||||
# Use defaults for privacy, format and type.
|
||||
|
@ -97,7 +97,7 @@ class DisplayNameEditor(ManagedWindow):
|
||||
def __init__(self, uistate, dbstate, track, dialog):
|
||||
# Assumes that there are two methods: dialog.name_changed_check(),
|
||||
# and dialog._build_custom_name_ui()
|
||||
ManagedWindow.__init__(self, uistate, [], DisplayNameEditor)
|
||||
ManagedWindow.__init__(self, uistate, track, DisplayNameEditor)
|
||||
self.dialog = dialog
|
||||
self.dbstate = dbstate
|
||||
self.set_window(
|
||||
@ -136,7 +136,8 @@ UPPERCASE keyword forces uppercase. Extra parentheses, commas are removed. Other
|
||||
ManagedWindow.close(self, *obj)
|
||||
|
||||
def build_menu_names(self, obj):
|
||||
return (_(" Name Editor"), _("Preferences"))
|
||||
# NameEditor is leaf of parent branch
|
||||
return (_(" Name Editor"), None)
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@ -188,7 +189,6 @@ class ConfigureDialog(ManagedWindow):
|
||||
|
||||
self.__setup_pages(configure_page_funcs)
|
||||
|
||||
self.window.show_all()
|
||||
self.show()
|
||||
|
||||
def __setup_pages(self, configure_page_funcs):
|
||||
@ -231,12 +231,12 @@ class ConfigureDialog(ManagedWindow):
|
||||
except TypeError:
|
||||
print("WARNING: ignoring invalid value for '%s'" % constant)
|
||||
ErrorDialog(_("Invalid or incomplete format definition."),
|
||||
obj.get_text())
|
||||
obj.get_text(), parent=self.window)
|
||||
obj.set_text('<b>%s</b>')
|
||||
except ValueError:
|
||||
print("WARNING: ignoring invalid value for '%s'" % constant)
|
||||
ErrorDialog(_("Invalid or incomplete format definition."),
|
||||
obj.get_text())
|
||||
obj.get_text(), parent=self.window)
|
||||
obj.set_text('<b>%s</b>')
|
||||
|
||||
self.__config.set(constant, unicode(obj.get_text()))
|
||||
@ -770,7 +770,7 @@ class GrampsPreferences(ConfigureDialog):
|
||||
# check to see if this pattern already exists
|
||||
if self.__check_for_name(translation, node):
|
||||
ErrorDialog(_("This format exists already."),
|
||||
translation)
|
||||
translation, parent=self.window)
|
||||
self.edit_button.emit('clicked')
|
||||
return
|
||||
# else, change the name
|
||||
@ -1170,13 +1170,15 @@ class GrampsPreferences(ConfigureDialog):
|
||||
config.set('preferences.date-format', obj.get_active())
|
||||
OkDialog(_('Change is not immediate'),
|
||||
_('Changing the date format will not take '
|
||||
'effect until the next time Gramps is started.'))
|
||||
'effect until the next time Gramps is started.'),
|
||||
parent=self.window)
|
||||
|
||||
def place_format_changed(self, obj):
|
||||
config.set('preferences.place-format', obj.get_active())
|
||||
OkDialog(_('Change is not immediate'),
|
||||
_('Changing the place format will not take '
|
||||
'effect until the next time Gramps is started.'))
|
||||
'effect until the next time Gramps is started.'),
|
||||
parent=self.window)
|
||||
|
||||
def date_calendar_changed(self, obj):
|
||||
config.set('preferences.calendar-format-report', obj.get_active())
|
||||
@ -1388,12 +1390,13 @@ class GrampsPreferences(ConfigureDialog):
|
||||
|
||||
def select_mediapath(self, *obj):
|
||||
f = Gtk.FileChooserDialog(
|
||||
_("Select media directory"),
|
||||
action=Gtk.FileChooserAction.SELECT_FOLDER,
|
||||
buttons=(Gtk.STOCK_CANCEL,
|
||||
Gtk.ResponseType.CANCEL,
|
||||
Gtk.STOCK_APPLY,
|
||||
Gtk.ResponseType.OK))
|
||||
title=_("Select media directory"),
|
||||
parent=self.window,
|
||||
action=Gtk.FileChooserAction.SELECT_FOLDER,
|
||||
buttons=(Gtk.STOCK_CANCEL,
|
||||
Gtk.ResponseType.CANCEL,
|
||||
Gtk.STOCK_APPLY,
|
||||
Gtk.ResponseType.OK))
|
||||
mpath = self.dbstate.db.get_mediapath()
|
||||
if not mpath:
|
||||
mpath = HOME_DIR
|
||||
@ -1412,12 +1415,13 @@ class GrampsPreferences(ConfigureDialog):
|
||||
|
||||
def select_dbpath(self, *obj):
|
||||
f = Gtk.FileChooserDialog(
|
||||
_("Select database directory"),
|
||||
action=Gtk.FileChooserAction.SELECT_FOLDER,
|
||||
buttons=(Gtk.STOCK_CANCEL,
|
||||
Gtk.ResponseType.CANCEL,
|
||||
Gtk.STOCK_APPLY,
|
||||
Gtk.ResponseType.OK))
|
||||
title=_("Select database directory"),
|
||||
parent=self.window,
|
||||
action=Gtk.FileChooserAction.SELECT_FOLDER,
|
||||
buttons=(Gtk.STOCK_CANCEL,
|
||||
Gtk.ResponseType.CANCEL,
|
||||
Gtk.STOCK_APPLY,
|
||||
Gtk.ResponseType.OK))
|
||||
dbpath = config.get('behavior.database-path')
|
||||
if not dbpath:
|
||||
dbpath = os.path.join(HOME_DIR,'grampsdb')
|
||||
@ -1470,7 +1474,9 @@ class GrampsPreferences(ConfigureDialog):
|
||||
obj.set_text(str(intval))
|
||||
|
||||
def build_menu_names(self, obj):
|
||||
return (_('Preferences'), None)
|
||||
# Preferences editor my open other dialog so let main dialog
|
||||
# be leaf in branch
|
||||
return (_('Preferences'), _('Preferences'))
|
||||
|
||||
# FIXME: is this needed?
|
||||
def _set_button(self, stock):
|
||||
|
@ -83,24 +83,24 @@ class DbLoader(CLIDbLoader):
|
||||
self.import_info = None
|
||||
|
||||
def _warn(self, title, warnmessage):
|
||||
WarningDialog(title, warnmessage)
|
||||
WarningDialog(title, warnmessage, parent=self.uistate.window)
|
||||
|
||||
def _errordialog(self, title, errormessage):
|
||||
"""
|
||||
Show the error.
|
||||
In the GUI, the error is shown, and a return happens
|
||||
"""
|
||||
ErrorDialog(title, errormessage)
|
||||
ErrorDialog(title, errormessage, parent=self.uistate.window)
|
||||
return 1
|
||||
|
||||
def _dberrordialog(self, msg):
|
||||
import traceback
|
||||
exc = traceback.format_exc()
|
||||
try:
|
||||
DBErrorDialog(str(msg.value))
|
||||
DBErrorDialog(str(msg.value), parent=self.uistate.window)
|
||||
_LOG.error(str(msg.value))
|
||||
except:
|
||||
DBErrorDialog(str(msg))
|
||||
DBErrorDialog(str(msg), parent=self.uistate.window)
|
||||
_LOG.error(str(msg) +"\n" + exc)
|
||||
|
||||
def _begin_progress(self):
|
||||
@ -198,7 +198,8 @@ class DbLoader(CLIDbLoader):
|
||||
_("Could not open file: %s") % filename,
|
||||
_('File type "%s" is unknown to Gramps.\n\n'
|
||||
'Valid types are: Gramps database, Gramps XML, '
|
||||
'Gramps package, GEDCOM, and others.') % extension)
|
||||
'Gramps package, GEDCOM, and others.') % extension,
|
||||
parent=self.uistate.window)
|
||||
|
||||
import_dialog.destroy()
|
||||
return False
|
||||
@ -220,13 +221,15 @@ class DbLoader(CLIDbLoader):
|
||||
elif os.path.isdir(filename):
|
||||
ErrorDialog(
|
||||
_('Cannot open file'),
|
||||
_('The selected file is a directory, not a file.\n'))
|
||||
_('The selected file is a directory, not a file.\n'),
|
||||
parent=self.uistate.window)
|
||||
return True
|
||||
elif os.path.exists(filename):
|
||||
if not os.access(filename, os.R_OK):
|
||||
ErrorDialog(
|
||||
_('Cannot open file'),
|
||||
_('You do not have read access to the selected file.'))
|
||||
_('You do not have read access to the selected file.'),
|
||||
parent=self.uistate.window)
|
||||
return True
|
||||
else:
|
||||
try:
|
||||
@ -236,7 +239,8 @@ class DbLoader(CLIDbLoader):
|
||||
except IOError:
|
||||
ErrorDialog(
|
||||
_('Cannot create file'),
|
||||
_('You do not have write access to the selected file.'))
|
||||
_('You do not have write access to the selected file.'),
|
||||
parent=self.uistate.window)
|
||||
return True
|
||||
|
||||
return False
|
||||
@ -259,7 +263,8 @@ class DbLoader(CLIDbLoader):
|
||||
_("Could not import file: %s") % filename,
|
||||
_("This file incorrectly identifies its character "
|
||||
"set, so it cannot be accurately imported. Please fix the "
|
||||
"encoding, and import again") + "\n\n %s" % msg)
|
||||
"encoding, and import again") + "\n\n %s" % msg,
|
||||
parent=self.uistate.window)
|
||||
except Exception:
|
||||
_LOG.error("Failed to import database.", exc_info=True)
|
||||
self._end_progress()
|
||||
@ -327,7 +332,8 @@ class DbLoader(CLIDbLoader):
|
||||
str(msg),
|
||||
_("I have made a backup,\n"
|
||||
"please upgrade my Family Tree"),
|
||||
_("Cancel"), self.uistate.window).run():
|
||||
_("Cancel"),
|
||||
parent=self.uistate.window).run():
|
||||
force_schema_upgrade = True
|
||||
force_bsddb_upgrade = False
|
||||
force_bsddb_downgrade = False
|
||||
@ -341,7 +347,8 @@ class DbLoader(CLIDbLoader):
|
||||
str(msg),
|
||||
_("I have made a backup,\n"
|
||||
"please upgrade my tree"),
|
||||
_("Cancel"), self.uistate.window).run():
|
||||
_("Cancel"),
|
||||
parent=self.uistate.window).run():
|
||||
force_schema_upgrade = False
|
||||
force_bsddb_upgrade = True
|
||||
force_bsddb_downgrade = False
|
||||
@ -355,7 +362,8 @@ class DbLoader(CLIDbLoader):
|
||||
str(msg),
|
||||
_("I have made a backup,\n"
|
||||
"please downgrade my Family Tree"),
|
||||
_("Cancel"), self.uistate.window).run():
|
||||
_("Cancel"),
|
||||
parent=self.uistate.window).run():
|
||||
force_schema_upgrade = False
|
||||
force_bsddb_upgrade = False
|
||||
force_bsddb_downgrade = True
|
||||
@ -369,7 +377,8 @@ class DbLoader(CLIDbLoader):
|
||||
str(msg),
|
||||
_("I have made a backup,\n"
|
||||
"please upgrade my Family Tree"),
|
||||
_("Cancel"), self.uistate.window).run():
|
||||
_("Cancel"),
|
||||
parent=self.uistate.window).run():
|
||||
force_schema_upgrade = False
|
||||
force_bsddb_upgrade = False
|
||||
force_bsddb_downgrade = False
|
||||
|
@ -248,7 +248,7 @@ class DbManager(CLIDbManager):
|
||||
|
||||
if store.get_value(node, STOCK_COL) == Gtk.STOCK_DIALOG_ERROR:
|
||||
path = conv_to_unicode(store.get_value(node, PATH_COL), 'utf8')
|
||||
backup = os.path.join(path, u"person.gbkp")
|
||||
backup = os.path.join(path, "person.gbkp")
|
||||
self.repair.set_sensitive(os.path.isfile(backup))
|
||||
else:
|
||||
self.repair.set_sensitive(False)
|
||||
|
@ -514,17 +514,10 @@ class GalleryTab(ButtonTab, DbGUIElement):
|
||||
elif self._DND_EXTRA and mytype == self._DND_EXTRA.drag_type:
|
||||
self.handle_extra_type(mytype, obj)
|
||||
except pickle.UnpicklingError:
|
||||
#modern file managers provide URI_LIST. For Windows split sel_data.data
|
||||
if win():
|
||||
files = sel_data.get_data().split('\n')
|
||||
else:
|
||||
files = sel_data.get_uris()
|
||||
|
||||
files = sel_data.get_uris()
|
||||
for file in files:
|
||||
if win():
|
||||
d = conv_to_unicode((file.replace('\0',' ').strip()), None)
|
||||
else:
|
||||
d = file
|
||||
protocol, site, mfile, j, k, l = urlparse(d)
|
||||
protocol, site, mfile, j, k, l = urlparse(file)
|
||||
if protocol == "file":
|
||||
name = url2pathname(mfile)
|
||||
mime = get_type(name)
|
||||
|
@ -271,6 +271,7 @@ class EditReference(ManagedWindow, DbGUIElement):
|
||||
if new_id:
|
||||
old_primary = self.db.get_from_name_and_gramps_id(type, new_id)
|
||||
if old_primary:
|
||||
description = None
|
||||
if type == 'Event':
|
||||
msg1 = _("Cannot save event. ID already exists.")
|
||||
description = old_primary.get_description()
|
||||
@ -280,6 +281,8 @@ class EditReference(ManagedWindow, DbGUIElement):
|
||||
elif type == 'Repository':
|
||||
msg1 = _("Cannot save repository. ID already exists.")
|
||||
description = old_primary.get_name()
|
||||
else:
|
||||
msg1 = _("Cannot save item. ID already exists.")
|
||||
if description:
|
||||
msg2 = _("You have attempted to use the existing Gramps "
|
||||
"ID with value %(id)s. This value is already "
|
||||
|
@ -62,11 +62,13 @@ class EventSidebarFilter(SidebarFilter):
|
||||
self.filter_event = Event()
|
||||
self.filter_event.set_type((EventType.CUSTOM, ''))
|
||||
self.etype = Gtk.ComboBox(has_entry=True)
|
||||
self.custom_types = dbstate.db.get_event_types()
|
||||
|
||||
self.event_menu = widgets.MonitoredDataType(
|
||||
self.etype,
|
||||
self.filter_event.set_type,
|
||||
self.filter_event.get_type)
|
||||
self.filter_event.get_type,
|
||||
custom_values=self.custom_types)
|
||||
|
||||
self.filter_mainparts = widgets.BasicEntry()
|
||||
self.filter_date = widgets.DateEntry(uistate, [])
|
||||
|
@ -29,6 +29,7 @@ from gi.repository import Pango
|
||||
from ... import widgets
|
||||
from ...dbguielement import DbGUIElement
|
||||
from gramps.gen.config import config
|
||||
from gramps.gen.constfunc import UNITYPE
|
||||
|
||||
_RETURN = Gdk.keyval_from_name("Return")
|
||||
_KP_ENTER = Gdk.keyval_from_name("KP_Enter")
|
||||
@ -212,6 +213,9 @@ class SidebarFilter(DbGUIElement):
|
||||
self.__tag_list = []
|
||||
for handle in self.dbstate.db.get_tag_handles(sort_handles=True):
|
||||
tag = self.dbstate.db.get_tag_from_handle(handle)
|
||||
# for python3 this returns a byte object, so conversion needed
|
||||
if not isinstance(handle, UNITYPE):
|
||||
handle = handle.decode('utf-8')
|
||||
self.__tag_list.append((tag.get_name(), handle))
|
||||
self.on_tags_changed([item[0] for item in self.__tag_list])
|
||||
|
||||
|
@ -350,11 +350,13 @@
|
||||
<child>
|
||||
<object class="GtkComboBox" id="place_type">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="tooltip_text" translatable="yes">What type of place this is. Eg 'Country', 'City', ... .</property>
|
||||
<property name="has_entry">True</property>
|
||||
<child internal-child="entry">
|
||||
<object class="GtkEntry" id="combobox-entry">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="overwrite_mode">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
@ -378,7 +378,7 @@ def __startgramps(errors, argparser):
|
||||
% e.code, exc_info=True)
|
||||
except OSError as e:
|
||||
quit_now = True
|
||||
exit_code = e[0] or 1
|
||||
exit_code = e.errno or 1
|
||||
try:
|
||||
fn = e.filename
|
||||
except AttributeError:
|
||||
|
@ -89,6 +89,15 @@ class ErrorReportAssistant(Gtk.Assistant):
|
||||
self.build_page4()
|
||||
self.build_page5()
|
||||
self.create_page_summary()
|
||||
|
||||
try:
|
||||
self.set_transient_for(self.list_toplevels()[-2])
|
||||
except IndexError:
|
||||
self.set_position(Gtk.WindowPosition.CENTER)
|
||||
self.set_urgency_hint(True)
|
||||
self.set_keep_above(True)
|
||||
self.set_default_size(800,-1)
|
||||
|
||||
self.show_all()
|
||||
|
||||
self.ownthread = ownthread
|
||||
|
@ -82,6 +82,13 @@ class ErrorView(object):
|
||||
def draw_window(self):
|
||||
title = "%s - Gramps" % _("Error Report")
|
||||
self.top = Gtk.Dialog(title)
|
||||
try:
|
||||
self.top.set_transient_for(self.top.list_toplevels()[-2])
|
||||
except IndexError:
|
||||
self.top.set_position(Gtk.WindowPosition.CENTER)
|
||||
self.top.set_urgency_hint(True)
|
||||
self.top.set_keep_above(True)
|
||||
self.top.set_default_size(800,-1)
|
||||
vbox = self.top.get_content_area()
|
||||
vbox.set_spacing(5)
|
||||
self.top.set_border_width(12)
|
||||
@ -128,7 +135,6 @@ class ErrorView(object):
|
||||
|
||||
vbox.pack_start(tb_expander, True, True, 5)
|
||||
|
||||
|
||||
self.top.add_button(Gtk.STOCK_CANCEL,Gtk.ResponseType.CANCEL)
|
||||
self.top.add_button(_("Report"),Gtk.ResponseType.YES)
|
||||
self.top.add_button(Gtk.STOCK_HELP,Gtk.ResponseType.HELP)
|
||||
|
@ -41,6 +41,7 @@ from gramps.gen.const import URL_MANUAL_PAGE
|
||||
from ..display import display_help
|
||||
from ..managedwindow import ManagedWindow
|
||||
from gramps.gen.merge import MergePlaceQuery
|
||||
from gramps.gen.display.place import displayer as place_displayer
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@ -140,6 +141,8 @@ class MergePlace(ManagedWindow):
|
||||
self.get_widget(widget_name).set_sensitive(False)
|
||||
|
||||
# Main window widgets that determine which handle survives
|
||||
title1 = place_displayer.display(database, self.pl1)
|
||||
title2 = place_displayer.display(database, self.pl2)
|
||||
rbutton1 = self.get_widget("handle_btn1")
|
||||
rbutton_label1 = self.get_widget("label_handle_btn1")
|
||||
rbutton_label2 = self.get_widget("label_handle_btn2")
|
||||
|
@ -292,10 +292,18 @@ class Navigator(object):
|
||||
# Functions
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
def cb_menu_position(menu, button):
|
||||
def cb_menu_position(*args):
|
||||
"""
|
||||
Determine the position of the popup menu.
|
||||
"""
|
||||
# takes two argument: menu, button
|
||||
if len(args) == 2:
|
||||
menu = args[0]
|
||||
button = args[1]
|
||||
# broken introspection can't handle MenuPositionFunc annotations corectly
|
||||
else:
|
||||
menu = args[0]
|
||||
button = args[3]
|
||||
ret_val, x_pos, y_pos = button.get_window().get_origin()
|
||||
x_pos += button.get_allocation().x
|
||||
y_pos += button.get_allocation().y + button.get_allocation().height
|
||||
|
@ -241,6 +241,9 @@ class GuiColorOption(Gtk.ColorButton):
|
||||
self.changekey = self.connect('color-set', self.__color_changed)
|
||||
self.valuekey = self.__option.connect('value-changed', self.__value_changed)
|
||||
|
||||
self.conkey = self.__option.connect('avail-changed', self.__update_avail)
|
||||
self.__update_avail()
|
||||
|
||||
self.set_tooltip_text(self.__option.get_help())
|
||||
|
||||
def __color_changed(self, obj): # IGNORE:W0613 - obj is unused
|
||||
@ -257,6 +260,13 @@ class GuiColorOption(Gtk.ColorButton):
|
||||
self.__option.set_value(value)
|
||||
self.__option.enable_signals()
|
||||
|
||||
def __update_avail(self):
|
||||
"""
|
||||
Update the availability (sensitivity) of this widget.
|
||||
"""
|
||||
avail = self.__option.get_available()
|
||||
self.set_sensitive(avail)
|
||||
|
||||
def __value_changed(self):
|
||||
"""
|
||||
Handle the change made programmatically
|
||||
@ -270,6 +280,7 @@ class GuiColorOption(Gtk.ColorButton):
|
||||
remove stuff that blocks garbage collection
|
||||
"""
|
||||
self.__option.disconnect(self.valuekey)
|
||||
self.__option.disconnect(self.conkey)
|
||||
self.__option = None
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
@ -50,9 +50,10 @@ class Progress(object):
|
||||
Mirros the same interface that the ExportAssistant uses in the
|
||||
selection, but this is for the preview selection.
|
||||
"""
|
||||
def __init__(self):
|
||||
def __init__(self, uistate):
|
||||
from gi.repository import Gtk
|
||||
self.pm = ProgressMeter(_("Selecting Preview Data"), _('Selecting...'))
|
||||
self.pm = ProgressMeter(_("Selecting Preview Data"), _('Selecting...'),
|
||||
parent=uistate.window)
|
||||
self.progress_cnt = 0
|
||||
self.title = _("Selecting...")
|
||||
while Gtk.events_pending():
|
||||
@ -239,7 +240,7 @@ class WriterOptionBox(object):
|
||||
Calculate previews to see the selected data.
|
||||
"""
|
||||
self.parse_options()
|
||||
pm = Progress()
|
||||
pm = Progress(self.uistate)
|
||||
self.preview_dbase = self.get_filtered_database(self.dbstate.db, pm, preview=True)
|
||||
pm.close()
|
||||
self.preview_button.set_sensitive(0)
|
||||
|
@ -527,13 +527,20 @@ class ReportDialog(ManagedWindow):
|
||||
# we will need to create the file/dir
|
||||
# need to make sure we can create in the parent dir
|
||||
parent_dir = os.path.dirname(os.path.normpath(self.target_path))
|
||||
if not os.access(parent_dir, os.W_OK):
|
||||
ErrorDialog(_('Permission problem'),
|
||||
_("You do not have permission to create "
|
||||
"%s\n\n"
|
||||
"Please select another path or correct "
|
||||
"the permissions.") % self.target_path
|
||||
)
|
||||
if os.path.isdir(parent_dir):
|
||||
if not os.access(parent_dir, os.W_OK):
|
||||
ErrorDialog(_('Permission problem'),
|
||||
_("You do not have permission to create "
|
||||
"%s\n\n"
|
||||
"Please select another path or correct "
|
||||
"the permissions.") % self.target_path
|
||||
)
|
||||
return None
|
||||
else:
|
||||
ErrorDialog(_('No directory'),
|
||||
_('There is no directory %s.\n\n'
|
||||
'Please select another directory '
|
||||
'or create it.') % parent_dir )
|
||||
return None
|
||||
|
||||
self.set_default_directory(os.path.dirname(self.target_path) + os.sep)
|
||||
|
@ -103,7 +103,9 @@ class BatchTool(Tool):
|
||||
Should be used for tools using batch transactions.
|
||||
"""
|
||||
|
||||
def __init__(self, dbstate, user, options_class, name):
|
||||
def __init__(self, dbstate, user, options_class, name, parent=None):
|
||||
if user.uistate:
|
||||
parent = user.uistate.window
|
||||
if not user.prompt(
|
||||
_('Undo history warning'),
|
||||
_('Proceeding with this tool will erase the undo history '
|
||||
@ -112,7 +114,7 @@ class BatchTool(Tool):
|
||||
'made prior to it.\n\n'
|
||||
'If you think you may want to revert running this tool, '
|
||||
'please stop here and backup your database.'),
|
||||
_('_Proceed with the tool'), _('_Stop')):
|
||||
_('_Proceed with the tool'), _('_Stop'), parent):
|
||||
self.fail = True
|
||||
return
|
||||
|
||||
|
@ -88,7 +88,7 @@ class User(user.User):
|
||||
self._progress.close()
|
||||
self._progress = None
|
||||
|
||||
def prompt(self, title, message, accept_label, reject_label):
|
||||
def prompt(self, title, message, accept_label, reject_label, parent=None):
|
||||
"""
|
||||
Prompt the user with a message to select an alternative.
|
||||
|
||||
@ -106,7 +106,7 @@ class User(user.User):
|
||||
:returns: the user's answer to the question
|
||||
:rtype: bool
|
||||
"""
|
||||
dialog = QuestionDialog2(title, message, accept_label, reject_label)
|
||||
dialog = QuestionDialog2(title, message, accept_label, reject_label, parent)
|
||||
return dialog.run()
|
||||
|
||||
def warn(self, title, warning=""):
|
||||
|
@ -386,7 +386,7 @@ class ViewManager(CLIManager):
|
||||
hpane.add2(self.notebook)
|
||||
self.menubar = self.uimanager.get_widget('/MenuBar')
|
||||
self.toolbar = self.uimanager.get_widget('/ToolBar')
|
||||
vbox.pack_start(self.menubar, False, True, 0)
|
||||
self.__attach_menubar(vbox)
|
||||
vbox.pack_start(self.toolbar, False, True, 0)
|
||||
vbox.add(hpane)
|
||||
self.statusbar = Statusbar()
|
||||
@ -834,13 +834,15 @@ class ViewManager(CLIManager):
|
||||
|
||||
self.uimanager.add_ui_from_string(UIDEFAULT)
|
||||
self.uimanager.ensure_update()
|
||||
|
||||
def __attach_menubar(self, vbox):
|
||||
vbox.pack_start(self.menubar, False, True, 0)
|
||||
if _GTKOSXAPPLICATION:
|
||||
menubar = self.uimanager.get_widget("/MenuBar")
|
||||
menubar.hide()
|
||||
self.menubar.hide()
|
||||
quit_item = self.uimanager.get_widget("/MenuBar/FileMenu/Quit")
|
||||
about_item = self.uimanager.get_widget("/MenuBar/HelpMenu/About")
|
||||
prefs_item = self.uimanager.get_widget("/MenuBar/EditMenu/Preferences")
|
||||
self.macapp.set_menu_bar(menubar)
|
||||
self.macapp.set_menu_bar(self.menubar)
|
||||
self.macapp.insert_app_menu_item(about_item, 0)
|
||||
self.macapp.insert_app_menu_item(prefs_item, 1)
|
||||
|
||||
@ -1306,7 +1308,8 @@ class ViewManager(CLIManager):
|
||||
_("Backup file already exists! Overwrite?"),
|
||||
_("The file '%s' exists.") % filename,
|
||||
_("Proceed and overwrite"),
|
||||
_("Cancel the backup"))
|
||||
_("Cancel the backup"),
|
||||
parent=self.window)
|
||||
yes_no = question.run()
|
||||
if not yes_no:
|
||||
return
|
||||
@ -1337,12 +1340,13 @@ class ViewManager(CLIManager):
|
||||
right pane, otherwise FileChooserDialog will hang.
|
||||
"""
|
||||
f = Gtk.FileChooserDialog(
|
||||
_("Select backup directory"),
|
||||
action=Gtk.FileChooserAction.SELECT_FOLDER,
|
||||
buttons=(Gtk.STOCK_CANCEL,
|
||||
Gtk.ResponseType.CANCEL,
|
||||
Gtk.STOCK_APPLY,
|
||||
Gtk.ResponseType.OK))
|
||||
title=_("Select backup directory"),
|
||||
parent=self.window,
|
||||
action=Gtk.FileChooserAction.SELECT_FOLDER,
|
||||
buttons=(Gtk.STOCK_CANCEL,
|
||||
Gtk.ResponseType.CANCEL,
|
||||
Gtk.STOCK_APPLY,
|
||||
Gtk.ResponseType.OK))
|
||||
mpath = path_entry.get_text()
|
||||
if not mpath:
|
||||
mpath = HOME_DIR
|
||||
|
@ -277,10 +277,18 @@ class Tags(DbGUIElement):
|
||||
view.add_tag(trans, object_handle, tag_handle)
|
||||
status.end()
|
||||
|
||||
def cb_menu_position(menu, button):
|
||||
def cb_menu_position(*args):
|
||||
"""
|
||||
Determine the position of the popup menu.
|
||||
"""
|
||||
# takes two argument: menu, button
|
||||
if len(args) == 2:
|
||||
menu = args[0]
|
||||
button = args[1]
|
||||
# broken introspection can't handle MenuPositionFunc annotations corectly
|
||||
else:
|
||||
menu = args[0]
|
||||
button = args[3]
|
||||
ret_val, x_pos, y_pos = button.get_window().get_origin()
|
||||
x_pos += button.get_allocation().x
|
||||
y_pos += button.get_allocation().y + button.get_allocation().height
|
||||
|
@ -44,9 +44,10 @@ from gi.repository import Gtk
|
||||
# GRAMPS modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from gramps.gen.lib.placetype import PlaceType
|
||||
from gramps.gen.lib import Place, PlaceType
|
||||
from gramps.gen.datehandler import format_time
|
||||
from gramps.gen.utils.place import conv_lat_lon
|
||||
from gramps.gen.display.place import displayer as place_displayer
|
||||
from gramps.gen.constfunc import cuni
|
||||
from .flatbasemodel import FlatBaseModel
|
||||
from .treebasemodel import TreeBaseModel
|
||||
@ -116,7 +117,9 @@ class PlaceBaseModel(object):
|
||||
return len(self.fmap)+1
|
||||
|
||||
def column_title(self, data):
|
||||
return cuni(data[2])
|
||||
place = Place()
|
||||
place.unserialize(data)
|
||||
return place_displayer.display(self.db, place)
|
||||
|
||||
def column_name(self, data):
|
||||
return cuni(data[6])
|
||||
|
@ -734,10 +734,18 @@ class TabLabel(Gtk.HBox):
|
||||
else:
|
||||
self.closebtn.hide()
|
||||
|
||||
def cb_menu_position(menu, button):
|
||||
def cb_menu_position(*args):
|
||||
"""
|
||||
Determine the position of the popup menu.
|
||||
"""
|
||||
# takes two argument: menu, button
|
||||
if len(args) == 2:
|
||||
menu = args[0]
|
||||
button = args[1]
|
||||
# broken introspection can't handle MenuPositionFunc annotations corectly
|
||||
else:
|
||||
menu = args[0]
|
||||
button = args[3]
|
||||
ret_val, x_pos, y_pos = button.get_window().get_origin()
|
||||
x_pos += button.get_allocation().x
|
||||
y_pos += button.get_allocation().y + button.get_allocation().height
|
||||
|
@ -284,7 +284,11 @@ class UndoableEntry(Gtk.Entry):
|
||||
self.set_position(undo_action.offset)
|
||||
|
||||
def _undo_delete(self, undo_action):
|
||||
self.insert_text(undo_action.text, undo_action.start)
|
||||
if not isinstance(undo_action.text, UNITYPE):
|
||||
undo_action.text = conv_to_unicode(undo_action.text, 'utf-8')
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter('ignore')
|
||||
self.insert_text(undo_action.text, undo_action.start)
|
||||
if undo_action.delete_key_used:
|
||||
self.set_position(undo_action.start)
|
||||
else:
|
||||
|
@ -731,7 +731,7 @@ class FanChartOptions(MenuReportOptions):
|
||||
p = ParagraphStyle()
|
||||
p.set_font(f)
|
||||
p.set_alignment(PARA_ALIGN_CENTER)
|
||||
p.set_description(_('The style used for the text display of generation ' + "%d" % i))
|
||||
p.set_description(_('The style used for the text display of generation "%d"') % i)
|
||||
default_style.add_paragraph_style("FC-Text" + "%02d" % i, p)
|
||||
|
||||
# GraphicsStyles
|
||||
|
@ -281,7 +281,7 @@ class TimeLine(Report):
|
||||
# subtitle if the report's output is in the main/UI language
|
||||
mark = None
|
||||
if toc:
|
||||
mark = IndexMark(title, INDEX_TYPE_TOC, 1)
|
||||
mark = IndexMark(title_one, INDEX_TYPE_TOC, 1)
|
||||
self.doc.center_text('TLG-title', title, width / 2.0, 0, mark)
|
||||
|
||||
def draw_year_headings(self, year_low, year_high, start_pos, stop_pos):
|
||||
|
@ -532,16 +532,27 @@ class GedcomWriter(UpdateCallback):
|
||||
extract the real event to discover the event type.
|
||||
|
||||
"""
|
||||
global adop_written
|
||||
# adop_written is only shared between this function and
|
||||
# _process_person_event. This is rather ugly code, but it is difficult
|
||||
# to support an Adoption event without an Adopted relationship from the
|
||||
# parent(s), an Adopted relationship from the parent(s) without an
|
||||
# event, and both an event and a relationship. All these need to be
|
||||
# supported without duplicating the output of the ADOP GEDCOM tag. See
|
||||
# bug report 2370.
|
||||
adop_written = False
|
||||
for event_ref in person.get_event_ref_list():
|
||||
event = self.dbase.get_event_from_handle(event_ref.ref)
|
||||
if not event: continue
|
||||
self._process_person_event(event, event_ref)
|
||||
self._adoption_records(person)
|
||||
self._process_person_event(person, event, event_ref)
|
||||
if not adop_written:
|
||||
self._adoption_records(person, adop_written)
|
||||
|
||||
def _process_person_event(self, event, event_ref):
|
||||
def _process_person_event(self, person, event, event_ref):
|
||||
"""
|
||||
Process a person event, which is not a BIRTH or DEATH event.
|
||||
"""
|
||||
global adop_written
|
||||
etype = int(event.get_type())
|
||||
# if the event is a birth or death, skip it.
|
||||
if etype in (EventType.BIRTH, EventType.DEATH):
|
||||
@ -578,8 +589,11 @@ class GedcomWriter(UpdateCallback):
|
||||
if descr:
|
||||
self._writeln(2, 'NOTE', "Description: " + descr)
|
||||
self._dump_event_stats(event, event_ref)
|
||||
if etype == EventType.ADOPT and not adop_written:
|
||||
adop_written = True
|
||||
self._adoption_records(person, adop_written)
|
||||
|
||||
def _adoption_records(self, person):
|
||||
def _adoption_records(self, person, adop_written):
|
||||
"""
|
||||
Write Adoption events for each child that has been adopted.
|
||||
|
||||
@ -603,7 +617,8 @@ class GedcomWriter(UpdateCallback):
|
||||
adoptions.append((family, child_ref.frel, child_ref.mrel))
|
||||
|
||||
for (fam, frel, mrel) in adoptions:
|
||||
self._writeln(1, 'ADOP', 'Y')
|
||||
if not adop_written:
|
||||
self._writeln(1, 'ADOP', 'Y')
|
||||
self._writeln(2, 'FAMC', '@%s@' % fam.get_gramps_id())
|
||||
if mrel == frel:
|
||||
self._writeln(3, 'ADOP', 'BOTH')
|
||||
@ -1170,16 +1185,22 @@ class GedcomWriter(UpdateCallback):
|
||||
cal = date.get_calendar()
|
||||
mod = date.get_modifier()
|
||||
quality = date.get_quality()
|
||||
if quality in libgedcom.DATE_QUALITY:
|
||||
qual_text = libgedcom.DATE_QUALITY[quality] + " "
|
||||
else:
|
||||
qual_text = ""
|
||||
if mod == Date.MOD_SPAN:
|
||||
val = "FROM %s TO %s" % (
|
||||
libgedcom.make_gedcom_date(start, cal, mod, quality),
|
||||
val = "%sFROM %s TO %s" % (
|
||||
qual_text,
|
||||
libgedcom.make_gedcom_date(start, cal, mod, None),
|
||||
libgedcom.make_gedcom_date(date.get_stop_date(),
|
||||
cal, mod, quality))
|
||||
cal, mod, None))
|
||||
elif mod == Date.MOD_RANGE:
|
||||
val = "BET %s AND %s" % (
|
||||
libgedcom.make_gedcom_date(start, cal, mod, quality),
|
||||
val = "%sBET %s AND %s" % (
|
||||
qual_text,
|
||||
libgedcom.make_gedcom_date(start, cal, mod, None),
|
||||
libgedcom.make_gedcom_date(date.get_stop_date(),
|
||||
cal, mod, quality))
|
||||
cal, mod, None))
|
||||
else:
|
||||
val = libgedcom.make_gedcom_date(start, cal, mod, quality)
|
||||
self._writeln(level, 'DATE', val)
|
||||
|
@ -90,7 +90,7 @@ class GeneWebWriter(object):
|
||||
|
||||
self.dirname = os.path.dirname (self.filename)
|
||||
try:
|
||||
self.g = open(self.filename, "w")
|
||||
self.g = open(self.filename, "wb")
|
||||
except IOError as msg:
|
||||
msg2 = _("Could not create %s") % self.filename
|
||||
self.user.notify_error(msg2, str(msg))
|
||||
|
@ -140,6 +140,12 @@ class GrampsXmlWriter(UpdateCallback):
|
||||
"Please make sure you have write access to the "
|
||||
"directory and try again."))
|
||||
return 0
|
||||
else:
|
||||
raise DbWriteFailure(_('No directory'),
|
||||
_('There is no directory %s.\n\n'
|
||||
'Please select another directory '
|
||||
'or create it.') % base )
|
||||
return 0
|
||||
|
||||
if os.path.exists(filename):
|
||||
if not os.access(filename, os.W_OK):
|
||||
|
@ -249,9 +249,9 @@ class AgeStatsGramplet(Gramplet):
|
||||
"""
|
||||
# first, binify:
|
||||
#print "create_bargraph", hash
|
||||
bin = [0] * (max_val/bin_size)
|
||||
bin = [0] * int(max_val/bin_size)
|
||||
for value, hash_value in hash.items():
|
||||
bin[value/bin_size] += hash_value
|
||||
bin[int(value/bin_size)] += hash_value
|
||||
text = ""
|
||||
max_bin = float(max(bin))
|
||||
if max_bin != 0:
|
||||
|
@ -18,6 +18,8 @@
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
#
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from collections import defaultdict
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
@ -66,9 +68,9 @@ class GivenNameCloudGramplet(Gramplet):
|
||||
allnames = [person.get_primary_name()] + person.get_alternate_names()
|
||||
allnames = set(name.get_first_name().strip() for name in allnames)
|
||||
for givenname in allnames:
|
||||
anyNBSP = givenname.split(u'\u00A0')
|
||||
anyNBSP = givenname.split('\u00A0')
|
||||
if len(anyNBSP) > 1: # there was an NBSP, a non-breaking space
|
||||
first_two = anyNBSP[0] + u'\u00A0' + anyNBSP[1].split()[0]
|
||||
first_two = anyNBSP[0] + '\u00A0' + anyNBSP[1].split()[0]
|
||||
givensubnames[first_two] += 1
|
||||
representative_handle[first_two] = person.handle
|
||||
givenname = ' '.join(anyNBSP[1].split()[1:])
|
||||
|
@ -76,15 +76,31 @@ def importData(database, filename, user):
|
||||
database.__class__.__bases__ = (DbMixin,) + \
|
||||
database.__class__.__bases__
|
||||
|
||||
try:
|
||||
ifile = open(filename, "r")
|
||||
except IOError:
|
||||
return
|
||||
if sys.version_info[0] < 3:
|
||||
try:
|
||||
ifile = open(filename, "rU")
|
||||
except IOError:
|
||||
return
|
||||
else:
|
||||
try:
|
||||
ifile = open(filename, "rb")
|
||||
except IOError:
|
||||
return
|
||||
|
||||
# print("file opened")
|
||||
ansel = False
|
||||
gramps = False
|
||||
for index in range(50):
|
||||
line = ifile.readline().split()
|
||||
# Treat the file as though it is UTF-8 since this is the more modern
|
||||
# option; and anyway it doesn't really matter as we are only trying to
|
||||
# detect a CHAR or SOUR line which is only 7-bit ASCII anyway, and we
|
||||
# ignore anything that can't be translated.
|
||||
line = ifile.readline()
|
||||
if sys.version_info[0] < 3:
|
||||
line = unicode(line, encoding='utf-8', errors='replace')
|
||||
else:
|
||||
line = line.decode(encoding='utf-8', errors='replace')
|
||||
line = line.split()
|
||||
if len(line) == 0:
|
||||
break
|
||||
if len(line) > 2 and line[1][0:4] == 'CHAR' and line[2] == "ANSEL":
|
||||
|
@ -644,9 +644,8 @@ class GeneWebParser(object):
|
||||
birth_source = self.get_or_create_source(self.decode(fields[idx]))
|
||||
idx += 1
|
||||
elif field[0] == '!':
|
||||
LOG.debug("Baptize at: %s" % fields[idx])
|
||||
bapt_date = self.parse_date(self.decode(fields[idx][1:]))
|
||||
idx += 1
|
||||
LOG.debug("Baptize at: %s" % field[1:])
|
||||
bapt_date = self.parse_date(self.decode(field[1:]))
|
||||
elif field == '#bp' and idx < len(fields):
|
||||
LOG.debug("Birth Place: %s" % fields[idx])
|
||||
birth_place = self.get_or_create_place(self.decode(fields[idx]))
|
||||
@ -668,9 +667,10 @@ class GeneWebParser(object):
|
||||
death_source = self.get_or_create_source(self.decode(fields[idx]))
|
||||
idx += 1
|
||||
elif field == '#buri' and idx < len(fields):
|
||||
LOG.debug("Burial Date: %s" % fields[idx])
|
||||
bur_date = self.parse_date(self.decode(fields[idx]))
|
||||
idx += 1
|
||||
if fields[idx][0]!='#': # bug in GeneWeb: empty #buri fields
|
||||
LOG.debug("Burial Date: %s" % fields[idx])
|
||||
bur_date = self.parse_date(self.decode(fields[idx]))
|
||||
idx += 1
|
||||
elif field == '#crem' and idx < len(fields):
|
||||
LOG.debug("Cremention Date: %s" % fields[idx])
|
||||
crem_date = self.parse_date(self.decode(fields[idx]))
|
||||
|
@ -578,6 +578,10 @@ LDS_STATUS = {
|
||||
# table for skipping illegal control chars in GEDCOM import
|
||||
# Only 09, 0A, 0D are allowed.
|
||||
STRIP_DICT = dict.fromkeys(list(range(9))+list(range(11, 13))+list(range(14, 32)))
|
||||
# The C1 Control characters are not treated in Latin-1 (ISO-8859-1) as
|
||||
# undefined, but if they have been used, the file is probably supposed to be
|
||||
# cp1252
|
||||
DEL_AND_C1 = dict.fromkeys(list(range(0x7F, 0x9F)))
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@ -689,7 +693,7 @@ class GedcomDateParser(DateParser):
|
||||
#-------------------------------------------------------------------------
|
||||
class Lexer(object):
|
||||
|
||||
def __init__(self, ifile):
|
||||
def __init__(self, ifile, __add_msg):
|
||||
self.ifile = ifile
|
||||
self.current_list = []
|
||||
self.eof = False
|
||||
@ -700,6 +704,7 @@ class Lexer(object):
|
||||
TOKEN_CONT : self.__fix_token_cont,
|
||||
TOKEN_CONC : self.__fix_token_conc,
|
||||
}
|
||||
self.__add_msg = __add_msg
|
||||
|
||||
def readline(self):
|
||||
if len(self.current_list) <= 1 and not self.eof:
|
||||
@ -738,6 +743,7 @@ class Lexer(object):
|
||||
self.eof = True
|
||||
return
|
||||
|
||||
original_line = line
|
||||
try:
|
||||
# According to the GEDCOM 5.5 standard,
|
||||
# Chapter 1 subsection Grammar
|
||||
@ -771,6 +777,13 @@ class Lexer(object):
|
||||
tag = line[0]
|
||||
line_value = line[2]
|
||||
except:
|
||||
problem = _("Line ignored ")
|
||||
text = original_line.rstrip('\n\r')
|
||||
prob_width = 66
|
||||
problem = problem.ljust(prob_width)[0:(prob_width-1)]
|
||||
text = text.replace("\n", "\n".ljust(prob_width + 22))
|
||||
message = "%s %s" % (problem, text)
|
||||
self.__add_msg(message)
|
||||
continue
|
||||
|
||||
token = TOKENS.get(tag, TOKEN_UNKNOWN)
|
||||
@ -780,6 +793,10 @@ class Lexer(object):
|
||||
if func:
|
||||
func(data)
|
||||
else:
|
||||
# There will normally only be one space between tag and
|
||||
# line_value, but in case there is more then one, remove extra
|
||||
# spaces after CONC/CONT processing
|
||||
data = data[:2] + (data[2].strip(),) + data[3:]
|
||||
self.current_list.insert(0, data)
|
||||
|
||||
def clean_up(self):
|
||||
@ -1234,27 +1251,29 @@ class GedInfoParser(object):
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class BaseReader(object):
|
||||
def __init__(self, ifile, encoding):
|
||||
def __init__(self, ifile, encoding, __add_msg):
|
||||
self.ifile = ifile
|
||||
self.enc = encoding
|
||||
self.__add_msg = __add_msg
|
||||
|
||||
def reset(self):
|
||||
self.ifile.seek(0)
|
||||
|
||||
def readline(self):
|
||||
if sys.version_info[0] < 3:
|
||||
line = unicode(self.ifile.readline(),
|
||||
encoding=self.enc,
|
||||
errors='replace')
|
||||
else:
|
||||
line = self.ifile.readline()
|
||||
line = line.decode(self.enc, errors='replace')
|
||||
return line.translate(STRIP_DICT)
|
||||
raise NotImplemented
|
||||
|
||||
def report_error(self, problem, line):
|
||||
line = line.rstrip('\n\r')
|
||||
prob_width = 66
|
||||
problem = problem.ljust(prob_width)[0:(prob_width-1)]
|
||||
text = line.replace("\n", "\n".ljust(prob_width + 22))
|
||||
message = "%s %s" % (problem, text)
|
||||
self.__add_msg(message)
|
||||
|
||||
class UTF8Reader(BaseReader):
|
||||
|
||||
def __init__(self, ifile):
|
||||
BaseReader.__init__(self, ifile, 'utf8')
|
||||
def __init__(self, ifile, __add_msg):
|
||||
BaseReader.__init__(self, ifile, 'utf8', __add_msg)
|
||||
self.reset()
|
||||
|
||||
def reset(self):
|
||||
@ -1275,23 +1294,61 @@ class UTF8Reader(BaseReader):
|
||||
|
||||
class UTF16Reader(BaseReader):
|
||||
|
||||
def __init__(self, ifile):
|
||||
def __init__(self, ifile, __add_msg):
|
||||
new_file = codecs.EncodedFile(ifile, 'utf8', 'utf16')
|
||||
BaseReader.__init__(self, new_file, 'utf16')
|
||||
BaseReader.__init__(self, new_file, '', __add_msg)
|
||||
self.reset()
|
||||
|
||||
def readline(self):
|
||||
l = self.ifile.readline()
|
||||
if l.strip():
|
||||
return l
|
||||
line = self.ifile.readline()
|
||||
if sys.version_info[0] < 3:
|
||||
line = unicode(line,
|
||||
encoding='utf8',
|
||||
errors='replace')
|
||||
if line.strip():
|
||||
return line.translate(STRIP_DICT)
|
||||
else:
|
||||
line = self.ifile.readline()
|
||||
line = unicode(line,
|
||||
encoding='utf8',
|
||||
errors='replace')
|
||||
return line.translate(STRIP_DICT)
|
||||
else:
|
||||
return self.ifile.readline()
|
||||
line = line.decode('utf8', errors='replace')
|
||||
return line.translate(STRIP_DICT)
|
||||
|
||||
class AnsiReader(BaseReader):
|
||||
|
||||
def __init__(self, ifile):
|
||||
BaseReader.__init__(self, ifile, 'latin1')
|
||||
|
||||
def __init__(self, ifile, __add_msg):
|
||||
BaseReader.__init__(self, ifile, 'latin1', __add_msg)
|
||||
|
||||
def readline(self):
|
||||
line = self.ifile.readline()
|
||||
if sys.version_info[0] < 3:
|
||||
line = unicode(line,
|
||||
encoding=self.enc,
|
||||
errors='replace')
|
||||
else:
|
||||
line = line.decode(self.enc, errors='replace')
|
||||
if line.translate(DEL_AND_C1) != line:
|
||||
self.report_error("DEL or C1 control chars in line did you mean CHAR cp1252??", line)
|
||||
return line.translate(STRIP_DICT)
|
||||
|
||||
class CP1252Reader(BaseReader):
|
||||
|
||||
def __init__(self, ifile, __add_msg):
|
||||
BaseReader.__init__(self, ifile, 'cp1252', __add_msg)
|
||||
|
||||
def readline(self):
|
||||
line = self.ifile.readline()
|
||||
if sys.version_info[0] < 3:
|
||||
line = unicode(line,
|
||||
encoding=self.enc,
|
||||
errors='replace')
|
||||
else:
|
||||
line = line.decode(self.enc, errors='replace')
|
||||
return line.translate(STRIP_DICT)
|
||||
|
||||
class AnselReader(BaseReader):
|
||||
"""
|
||||
ANSEL to Unicode Conversion
|
||||
@ -1311,7 +1368,8 @@ class AnselReader(BaseReader):
|
||||
TODO: should we allow TAB, as a Gramps extension?
|
||||
"""
|
||||
__printable_ascii = list(map(chr, list(range(32, 127)))) # note: up thru 126
|
||||
__use_ASCII = list(map(chr, [10, 27, 29 , 30, 31])) + __printable_ascii
|
||||
# LF CR Esc GS RS US
|
||||
__use_ASCII = list(map(chr, [10, 13, 27, 29 , 30, 31])) + __printable_ascii
|
||||
|
||||
# mappings of single byte ANSEL codes to unicode
|
||||
__onebyte = {
|
||||
@ -1324,9 +1382,11 @@ class AnselReader(BaseReader):
|
||||
b'\xB4' : '\u00fe', b'\xB5' : '\u00e6', b'\xB6' : '\u0153',
|
||||
b'\xB7' : '\u02ba', b'\xB8' : '\u0131', b'\xB9' : '\u00a3',
|
||||
b'\xBA' : '\u00f0', b'\xBC' : '\u01a1', b'\xBD' : '\u01b0',
|
||||
b'\xBE' : '\u25a1', b'\xBF' : '\u25a0',
|
||||
b'\xC0' : '\u00b0', b'\xC1' : '\u2113', b'\xC2' : '\u2117',
|
||||
b'\xC3' : '\u00a9', b'\xC4' : '\u266f', b'\xC5' : '\u00bf',
|
||||
b'\xC6' : '\u00a1', b'\xC7' : '\u00df', b'\xC8' : '\u20ac',
|
||||
b'\xCD' : '\u0065', b'\xCE' : '\u006f', b'\xCF' : '\u00df',
|
||||
}
|
||||
|
||||
# combining forms (in ANSEL, they precede the modified ASCII character
|
||||
@ -1347,6 +1407,7 @@ class AnselReader(BaseReader):
|
||||
b'\xF3' : '\u0324', b'\xF4' : '\u0325', b'\xF5' : '\u0333',
|
||||
b'\xF6' : '\u0332', b'\xF7' : '\u0326', b'\xF8' : '\u031c',
|
||||
b'\xF9' : '\u032e', b'\xFA' : '\ufe22', b'\xFB' : '\ufe23',
|
||||
b'\xFC' : '\u0338',
|
||||
b'\xFE' : '\u0313',
|
||||
}
|
||||
|
||||
@ -1504,52 +1565,94 @@ class AnselReader(BaseReader):
|
||||
b'\xF9\x48' : '\u1e2a', b'\xF9\x68' : '\u1e2b',
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def __ansel_to_unicode(s):
|
||||
def __ansel_to_unicode(self, s):
|
||||
""" Convert an ANSEL encoded string to unicode """
|
||||
|
||||
buff = StringIO()
|
||||
while s:
|
||||
if ord(s[0]) < 128:
|
||||
if s[0] in AnselReader.__use_ASCII:
|
||||
head = s[0]
|
||||
else:
|
||||
# substitute space for disallowed (control) chars
|
||||
head = ' '
|
||||
s = s[1:]
|
||||
else:
|
||||
if s[0:2] in AnselReader.__twobyte:
|
||||
head = AnselReader.__twobyte[s[0:2]]
|
||||
s = s[2:]
|
||||
elif s[0] in AnselReader.__onebyte:
|
||||
head = AnselReader.__onebyte[s[0]]
|
||||
s = s[1:]
|
||||
elif s[0] in AnselReader.__acombiners:
|
||||
c = AnselReader.__acombiners[s[0]]
|
||||
# always consume the combiner
|
||||
s = s[1:]
|
||||
next = s[0]
|
||||
if next in AnselReader.__printable_ascii:
|
||||
# consume next as well
|
||||
s = s[1:]
|
||||
# unicode: combiner follows base-char
|
||||
head = next + c
|
||||
else:
|
||||
# just drop the unexpected combiner
|
||||
continue
|
||||
else:
|
||||
head = '\ufffd' # "Replacement Char"
|
||||
s = s[1:]
|
||||
buff.write(head.encode("utf-8"))
|
||||
error = ""
|
||||
if sys.version_info[0] < 3:
|
||||
while s:
|
||||
if ord(s[0]) < 128:
|
||||
if s[0] in AnselReader.__use_ASCII:
|
||||
head = s[0]
|
||||
else:
|
||||
# substitute space for disallowed (control) chars
|
||||
error += " (%#X)" % ord(s[0])
|
||||
head = ' '
|
||||
s = s[1:]
|
||||
else:
|
||||
if s[0:2] in AnselReader.__twobyte:
|
||||
head = AnselReader.__twobyte[s[0:2]]
|
||||
s = s[2:]
|
||||
elif s[0] in AnselReader.__onebyte:
|
||||
head = AnselReader.__onebyte[s[0]]
|
||||
s = s[1:]
|
||||
elif s[0] in AnselReader.__acombiners:
|
||||
c = AnselReader.__acombiners[s[0]]
|
||||
# always consume the combiner
|
||||
s = s[1:]
|
||||
next = s[0]
|
||||
if next in AnselReader.__printable_ascii:
|
||||
# consume next as well
|
||||
s = s[1:]
|
||||
# unicode: combiner follows base-char
|
||||
head = next + c
|
||||
else:
|
||||
# just drop the unexpected combiner
|
||||
error += " (%#X)" % ord(s[0])
|
||||
continue
|
||||
else:
|
||||
error += " (%#X)" % ord(s[0])
|
||||
head = '\ufffd' # "Replacement Char"
|
||||
s = s[1:]
|
||||
buff.write(head.encode("utf-8"))
|
||||
ans = unicode(buff.getvalue(), "utf-8")
|
||||
else:
|
||||
ans = buff.getvalue().decode("utf-8")
|
||||
while s:
|
||||
if s[0] < 128:
|
||||
if chr(s[0]) in AnselReader.__use_ASCII:
|
||||
head = chr(s[0])
|
||||
else:
|
||||
# substitute space for disallowed (control) chars
|
||||
error += " (%#X)" % s[0]
|
||||
head = ' '
|
||||
s = s[1:]
|
||||
else:
|
||||
if s[0:2] in AnselReader.__twobyte:
|
||||
head = AnselReader.__twobyte[s[0:2]]
|
||||
s = s[2:]
|
||||
elif bytes([s[0]]) in AnselReader.__onebyte:
|
||||
head = AnselReader.__onebyte[bytes([s[0]])]
|
||||
s = s[1:]
|
||||
elif bytes([s[0]]) in AnselReader.__acombiners:
|
||||
c = AnselReader.__acombiners[bytes([s[0]])]
|
||||
# always consume the combiner
|
||||
s = s[1:]
|
||||
next_byte = s[0]
|
||||
if next_byte < 128 and chr(next_byte) in AnselReader.__printable_ascii:
|
||||
# consume next as well
|
||||
s = s[1:]
|
||||
# unicode: combiner follows base-char
|
||||
head = chr(next_byte) + c
|
||||
else:
|
||||
# just drop the unexpected combiner
|
||||
error += " (%#X)" % s[0]
|
||||
continue
|
||||
else:
|
||||
error += " (%#X)" % s[0]
|
||||
head = '\ufffd' # "Replacement Char"
|
||||
s = s[1:]
|
||||
buff.write(head)
|
||||
ans = buff.getvalue()
|
||||
|
||||
if error:
|
||||
# e.g. Illegal character (oxAB) (0xCB)... 1 NOTE xyz?pqr?lmn
|
||||
self.report_error(_("Illegal character%s") % error, ans)
|
||||
buff.close()
|
||||
return ans
|
||||
|
||||
def __init__(self, ifile):
|
||||
BaseReader.__init__(self, ifile, "")
|
||||
def __init__(self, ifile, __add_msg):
|
||||
BaseReader.__init__(self, ifile, "", __add_msg)
|
||||
|
||||
def readline(self):
|
||||
return self.__ansel_to_unicode(self.ifile.readline())
|
||||
@ -1807,6 +1910,7 @@ class GedcomParser(UpdateCallback):
|
||||
|
||||
__TRUNC_MSG = _("Your GEDCOM file is corrupted. "
|
||||
"It appears to have been truncated.")
|
||||
_EMPTY_LOC = Location().serialize()
|
||||
|
||||
SyntaxError = "Syntax Error"
|
||||
BadFile = "Not a GEDCOM file"
|
||||
@ -2298,12 +2402,12 @@ class GedcomParser(UpdateCallback):
|
||||
TOKEN_CTRY : self.__location_ctry,
|
||||
# Not legal GEDCOM - not clear why these are included at this level
|
||||
TOKEN_ADDR : self.__ignore,
|
||||
TOKEN_DATE : self.__location_date,
|
||||
TOKEN_DATE : self.__ignore, # there is nowhere to put a date
|
||||
TOKEN_NOTE : self.__location_note,
|
||||
TOKEN_RNOTE : self.__location_note,
|
||||
TOKEN__LOC : self.__ignore,
|
||||
TOKEN__NAME : self.__ignore,
|
||||
TOKEN_PHON : self.__ignore,
|
||||
TOKEN_PHON : self.__location_phone,
|
||||
TOKEN_IGNORE : self.__ignore,
|
||||
}
|
||||
self.func_list.append(self.parse_loc_tbl)
|
||||
@ -2638,27 +2742,29 @@ class GedcomParser(UpdateCallback):
|
||||
self.func_list.append(self.note_parse_tbl)
|
||||
|
||||
# look for existing place titles, build a map
|
||||
self.place_names = {}
|
||||
self.place_names = defaultdict(list)
|
||||
cursor = dbase.get_place_cursor()
|
||||
data = next(cursor)
|
||||
while data:
|
||||
(handle, val) = data
|
||||
self.place_names[val[2]] = handle
|
||||
self.place_names[val[2]].append(handle)
|
||||
data = next(cursor)
|
||||
cursor.close()
|
||||
|
||||
enc = stage_one.get_encoding()
|
||||
|
||||
if enc == "ANSEL":
|
||||
rdr = AnselReader(ifile)
|
||||
rdr = AnselReader(ifile, self.__add_msg)
|
||||
elif enc in ("UTF-8", "UTF8"):
|
||||
rdr = UTF8Reader(ifile)
|
||||
elif enc in ("UTF-16", "UTF16", "UNICODE"):
|
||||
rdr = UTF16Reader(ifile)
|
||||
rdr = UTF8Reader(ifile, self.__add_msg)
|
||||
elif enc in ("UTF-16LE", "UTF-16BE", "UTF16", "UNICODE"):
|
||||
rdr = UTF16Reader(ifile, self.__add_msg)
|
||||
elif enc in ("CP1252", "WINDOWS-1252"):
|
||||
rdr = CP1252Reader(ifile, self.__add_msg)
|
||||
else:
|
||||
rdr = AnsiReader(ifile)
|
||||
rdr = AnsiReader(ifile, self.__add_msg)
|
||||
|
||||
self.lexer = Lexer(rdr)
|
||||
self.lexer = Lexer(rdr, self.__add_msg)
|
||||
self.filename = filename
|
||||
self.backoff = False
|
||||
|
||||
@ -2716,7 +2822,12 @@ class GedcomParser(UpdateCallback):
|
||||
else:
|
||||
message = _("GEDCOM import report: %s errors detected") % \
|
||||
self.number_of_errors
|
||||
self.user.info(message, "".join(self.errors), monospaced=True)
|
||||
if hasattr(self.user.uistate, 'window'):
|
||||
parent_window = self.user.uistate.window
|
||||
else:
|
||||
parent_window = None
|
||||
self.user.info(message, "".join(self.errors),
|
||||
parent = parent_window, monospaced=True)
|
||||
|
||||
def __clean_up(self):
|
||||
"""
|
||||
@ -2869,40 +2980,60 @@ class GedcomParser(UpdateCallback):
|
||||
self.dbase.add_note(note, self.trans)
|
||||
return note
|
||||
|
||||
def __find_or_create_place(self, title):
|
||||
def __loc_is_empty(self, location):
|
||||
"""
|
||||
Finds or creates a place based on the GRAMPS ID. If the ID is
|
||||
already used (is in the db), we return the item in the db. Otherwise,
|
||||
we create a new place, assign the handle and GRAMPS ID.
|
||||
Determines whether a location is empty.
|
||||
|
||||
@param location: The current location
|
||||
@type location: gen.lib.Location
|
||||
@return True of False
|
||||
"""
|
||||
if location is None:
|
||||
return True
|
||||
elif location.serialize() == self._EMPTY_LOC:
|
||||
return True
|
||||
elif location.is_empty():
|
||||
return True
|
||||
return False
|
||||
|
||||
def __find_place(self, title, location):
|
||||
"""
|
||||
Finds an existing place based on the title and primary location.
|
||||
|
||||
@param title: The place title
|
||||
@type title: string
|
||||
@param location: The current location
|
||||
@type location: gen.lib.Location
|
||||
@return gen.lib.Place
|
||||
"""
|
||||
for place_handle in self.place_names[title]:
|
||||
place = self.dbase.get_place_from_handle(place_handle)
|
||||
if place.get_title() == title:
|
||||
if self.__loc_is_empty(location) and \
|
||||
self.__loc_is_empty(self.__get_first_loc(place)):
|
||||
return place
|
||||
elif (not self.__loc_is_empty(location) and \
|
||||
not self.__loc_is_empty(self.__get_first_loc(place)) and
|
||||
self.__get_first_loc(place).is_equivalent(location) == IDENTICAL):
|
||||
return place
|
||||
return None
|
||||
|
||||
def __create_place(self, title, location):
|
||||
"""
|
||||
Create a new place based on the title and primary location.
|
||||
|
||||
@param title: The place title
|
||||
@type title: string
|
||||
@param location: The current location
|
||||
@type location: gen.lib.Location
|
||||
@return gen.lib.Place
|
||||
"""
|
||||
place = Place()
|
||||
|
||||
# check to see if we've encountered this name before
|
||||
# if we haven't we need to get a new GRAMPS ID
|
||||
|
||||
intid = self.place_names.get(title)
|
||||
if intid is None:
|
||||
intid = self.lid2id.get(title)
|
||||
if intid is None:
|
||||
new_id = self.dbase.find_next_place_gramps_id()
|
||||
else:
|
||||
new_id = None
|
||||
else:
|
||||
new_id = None
|
||||
|
||||
# check to see if the name already existed in the database
|
||||
# if it does, create a new name by appending the GRAMPS ID.
|
||||
# generate a GRAMPS ID if needed
|
||||
|
||||
if self.dbase.has_place_handle(intid):
|
||||
place.unserialize(self.dbase.get_raw_place_data(intid))
|
||||
else:
|
||||
intid = create_id()
|
||||
place.set_handle(intid)
|
||||
place.set_title(title)
|
||||
place.set_gramps_id(new_id)
|
||||
self.dbase.add_place(place, self.trans)
|
||||
self.lid2id[title] = intid
|
||||
place.set_title(title)
|
||||
if location:
|
||||
place.add_alternate_locations(location)
|
||||
self.dbase.add_place(place, self.trans)
|
||||
self.place_names[title].append(place.get_handle())
|
||||
return place
|
||||
|
||||
def __find_file(self, fullname, altpath):
|
||||
@ -3321,7 +3452,10 @@ class GedcomParser(UpdateCallback):
|
||||
if line.token_text == self.subm and self.import_researcher:
|
||||
self.dbase.set_researcher(state.res)
|
||||
|
||||
submitter_name = _("SUBM (Submitter): @%s@") % line.token_text
|
||||
if state.res.get_name() == "":
|
||||
submitter_name = _("SUBM (Submitter): @%s@") % line.token_text
|
||||
else:
|
||||
submitter_name = _("SUBM (Submitter): (@%s@) %s") % (line.token_text, state.res.get_name())
|
||||
if self.use_def_src:
|
||||
repo.set_name(submitter_name)
|
||||
repo.set_handle(create_id())
|
||||
@ -4466,8 +4600,12 @@ class GedcomParser(UpdateCallback):
|
||||
@type state: CurrentState
|
||||
"""
|
||||
try:
|
||||
state.place = self.__find_or_create_place(line.data)
|
||||
state.place.set_title(line.data)
|
||||
title = line.data
|
||||
place = self.__find_place(title, None)
|
||||
if place:
|
||||
state.place = place
|
||||
else:
|
||||
state.place = self.__create_place(title, None)
|
||||
state.lds_ord.set_place_handle(state.place.handle)
|
||||
except NameError:
|
||||
return
|
||||
@ -5157,7 +5295,7 @@ class GedcomParser(UpdateCallback):
|
||||
# +1 SOUR @<XREF:SOUR>@ {0:M}
|
||||
if not line.data:
|
||||
self.__add_msg(_("Empty note ignored"), line, state)
|
||||
self.__skip_subordinate_levels(level+1, state)
|
||||
self.__skip_subordinate_levels(state.level+1, state)
|
||||
else:
|
||||
new_note = Note(line.data)
|
||||
new_note.set_gramps_id(self.nid_map[""])
|
||||
@ -5305,22 +5443,56 @@ class GedcomParser(UpdateCallback):
|
||||
if self.is_ftw and state.event.type in FTW_BAD_PLACE:
|
||||
state.event.set_description(line.data)
|
||||
else:
|
||||
# It is possible that we have already got an address structure
|
||||
# associated with this event. In that case, we will remember the
|
||||
# location to re-insert later, and set the place as the place name
|
||||
# and primary location
|
||||
title = line.data
|
||||
place_handle = state.event.get_place_handle()
|
||||
if place_handle:
|
||||
place = self.dbase.get_place_from_handle(place_handle)
|
||||
# We encounter a PLAC, having previously encountered an ADDR
|
||||
old_place = self.dbase.get_place_from_handle(place_handle)
|
||||
old_title = old_place.get_title()
|
||||
location = self.__get_first_loc(old_place)
|
||||
if old_title != "":
|
||||
# We have previously found a PLAC
|
||||
self.__add_msg(_("A second PLAC ignored"), line, state)
|
||||
# ignore this second PLAC, and use the old one
|
||||
title = old_title
|
||||
place = old_place
|
||||
else:
|
||||
# This is the first PLAC
|
||||
refs = list(self.dbase.find_backlink_handles(place_handle))
|
||||
# We haven't commited the event yet, so the place will not
|
||||
# be linked to it. If there are any refs they will be from
|
||||
# other events (etc)
|
||||
if len(refs) == 0:
|
||||
place = self.__find_place(title, location)
|
||||
if place is None:
|
||||
place = old_place
|
||||
place.set_title(title)
|
||||
self.place_names[old_title].remove(place_handle)
|
||||
self.place_names[title].append(place_handle)
|
||||
else:
|
||||
place.merge(old_place)
|
||||
self.place_import.remove_location(old_place.handle)
|
||||
self.dbase.remove_place(place_handle, self.trans)
|
||||
self.place_names[old_title].remove(place_handle)
|
||||
else:
|
||||
place = self.__find_place(title, location)
|
||||
if place is None:
|
||||
place = self.__create_place(title, location)
|
||||
else:
|
||||
pass
|
||||
else:
|
||||
place = self.__find_or_create_place(line.data)
|
||||
place.set_title(line.data)
|
||||
state.event.set_place_handle(place.handle)
|
||||
# The first thing we encounter is PLAC
|
||||
location = None
|
||||
place = self.__find_place(title, location)
|
||||
if place is None:
|
||||
place = self.__create_place(title, location)
|
||||
|
||||
state.event.set_place_handle(place.handle)
|
||||
|
||||
sub_state = CurrentState()
|
||||
sub_state.place = place
|
||||
sub_state.level = state.level+1
|
||||
sub_state.pf = PlaceParser()
|
||||
sub_state.pf = self.place_parser
|
||||
|
||||
self.__parse_level(sub_state, self.event_place_map,
|
||||
self.__undefined)
|
||||
@ -5430,8 +5602,8 @@ class GedcomParser(UpdateCallback):
|
||||
|
||||
sub_state = CurrentState(level=state.level+1)
|
||||
sub_state.location = Location()
|
||||
sub_state.note = []
|
||||
sub_state.event = state.event
|
||||
sub_state.place = Place() # temp stash for notes, citations etc
|
||||
|
||||
self.__parse_level(sub_state, self.parse_loc_tbl, self.__undefined)
|
||||
state.msg += sub_state.msg
|
||||
@ -5439,21 +5611,53 @@ class GedcomParser(UpdateCallback):
|
||||
self.__merge_address(free_form, sub_state.location, line, state)
|
||||
|
||||
location = sub_state.location
|
||||
note_list = sub_state.note
|
||||
|
||||
place_handle = state.event.get_place_handle()
|
||||
if place_handle:
|
||||
place = self.dbase.get_place_from_handle(place_handle)
|
||||
# We encounter an ADDR having previously encountered a PLAC
|
||||
old_place = self.dbase.get_place_from_handle(place_handle)
|
||||
title = old_place.get_title()
|
||||
if len(old_place.get_alternate_locations()) != 0 and \
|
||||
not self.__get_first_loc(old_place).is_empty():
|
||||
# We have perviously found an ADDR, or have populated location
|
||||
# from PLAC title
|
||||
self.__add_msg(_("Location already populated; ADDR ignored"),
|
||||
line, state)
|
||||
# ignore this second ADDR, and use the old one
|
||||
location = self.__get_first_loc(old_place)
|
||||
place = old_place
|
||||
else:
|
||||
# This is the first ADDR
|
||||
refs = list(self.dbase.find_backlink_handles(place_handle))
|
||||
# We haven't commited the event yet, so the place will not be
|
||||
# linked to it. If there are any refs they will be from other
|
||||
# events (etc)
|
||||
if len(refs) == 0:
|
||||
place = self.__find_place(title, location)
|
||||
if place is None:
|
||||
place = old_place
|
||||
self.__add_location(place, location)
|
||||
else:
|
||||
place.merge(old_place)
|
||||
self.place_import.remove_location(old_place.handle)
|
||||
self.dbase.remove_place(place_handle, self.trans)
|
||||
self.place_names[title].remove(place_handle)
|
||||
else:
|
||||
place = self.__find_place(title, location)
|
||||
if place is None:
|
||||
place = self.__create_place(title, location)
|
||||
else:
|
||||
pass
|
||||
else:
|
||||
place = self.__find_or_create_place(line.data)
|
||||
place.set_title(line.data)
|
||||
place_handle = place.handle
|
||||
# The first thing we encounter is ADDR
|
||||
title = ""
|
||||
place = self.__find_place(title, location)
|
||||
if place is None:
|
||||
place = self.__create_place(title, location)
|
||||
|
||||
self.__add_location(place, location)
|
||||
# merge notes etc into place
|
||||
place.merge(sub_state.place)
|
||||
|
||||
list(map(place.add_note, note_list))
|
||||
|
||||
state.event.set_place_handle(place_handle)
|
||||
state.event.set_place_handle(place.get_handle())
|
||||
self.dbase.commit_place(place, self.trans)
|
||||
|
||||
def __add_location(self, place, location):
|
||||
@ -5468,6 +5672,18 @@ class GedcomParser(UpdateCallback):
|
||||
return
|
||||
place.add_alternate_locations(location)
|
||||
|
||||
def __get_first_loc(self, place):
|
||||
"""
|
||||
@param place: A place object
|
||||
@type place: Place
|
||||
@return location: the first alternate location if any else None
|
||||
@type location: gen.lib.location
|
||||
"""
|
||||
if len(place.get_alternate_locations()) == 0:
|
||||
return None
|
||||
else:
|
||||
return place.get_alternate_locations()[0]
|
||||
|
||||
def __event_phon(self, line, state):
|
||||
"""
|
||||
@param line: The current line in GedLine format
|
||||
@ -5626,7 +5842,7 @@ class GedcomParser(UpdateCallback):
|
||||
"""
|
||||
while True:
|
||||
line = self.__get_next_line()
|
||||
if self.__level_is_finished(line, state.level):
|
||||
if self.__level_is_finished(line, state.level+1):
|
||||
break
|
||||
elif line.token == TOKEN_AGE:
|
||||
attr = Attribute()
|
||||
@ -5647,7 +5863,7 @@ class GedcomParser(UpdateCallback):
|
||||
"""
|
||||
while True:
|
||||
line = self.__get_next_line()
|
||||
if self.__level_is_finished(line, state.level):
|
||||
if self.__level_is_finished(line, state.level+1):
|
||||
break
|
||||
elif line.token == TOKEN_AGE:
|
||||
attr = Attribute()
|
||||
@ -6635,17 +6851,6 @@ class GedcomParser(UpdateCallback):
|
||||
url.set_type(UrlType(UrlType.EMAIL))
|
||||
state.repo.add_url(url)
|
||||
|
||||
def __location_date(self, line, state):
|
||||
"""
|
||||
@param line: The current line in GedLine format
|
||||
@type line: GedLine
|
||||
@param state: The current state
|
||||
@type state: CurrentState
|
||||
"""
|
||||
if not state.location:
|
||||
state.location = Location()
|
||||
state.location.set_date_object(line.data)
|
||||
|
||||
def __location_adr1(self, line, state):
|
||||
"""
|
||||
@param line: The current line in GedLine format
|
||||
@ -6714,7 +6919,7 @@ class GedcomParser(UpdateCallback):
|
||||
state.location = Location()
|
||||
state.location.set_country(line.data)
|
||||
|
||||
def __location_note(self, line, state):
|
||||
def __location_phone(self, line, state):
|
||||
"""
|
||||
@param line: The current line in GedLine format
|
||||
@type line: GedLine
|
||||
@ -6723,9 +6928,19 @@ class GedcomParser(UpdateCallback):
|
||||
"""
|
||||
if not state.location:
|
||||
state.location = Location()
|
||||
state.location.set_phone(line.data)
|
||||
|
||||
def __location_note(self, line, state):
|
||||
"""
|
||||
@param line: The current line in GedLine format
|
||||
@type line: GedLine
|
||||
@param state: The current state
|
||||
@type state: CurrentState
|
||||
"""
|
||||
if state.event:
|
||||
self.__parse_note(line, state.event, state.level+1, state)
|
||||
self.__parse_note(line, state.place, state.level, state)
|
||||
else:
|
||||
# This causes notes below SUBMitter to be ignored
|
||||
self.__not_recognized(line, state.level, state)
|
||||
|
||||
def __optional_note(self, line, state):
|
||||
@ -7063,8 +7278,13 @@ class GedcomParser(UpdateCallback):
|
||||
sattr.set_value(line.data)
|
||||
self.def_src.add_attribute(sattr)
|
||||
elif line.token == TOKEN_FORM:
|
||||
if line.data != "LINEAGE-LINKED":
|
||||
self.__add_msg(_("GEDCOM form not supported"), line, state)
|
||||
if line.data == "LINEAGE-LINKED":
|
||||
pass
|
||||
elif line.data.upper() == "LINEAGE-LINKED":
|
||||
# Allow Lineage-Linked etc. though it should be in uppercase
|
||||
self.__add_msg(_("GEDCOM FORM should be in uppercase"), line, state)
|
||||
else:
|
||||
self.__add_msg(_("GEDCOM FORM not supported"), line, state)
|
||||
if self.use_def_src:
|
||||
sattr = SrcAttribute()
|
||||
sattr.set_type(_('GEDCOM form'))
|
||||
@ -7284,6 +7504,7 @@ class GedcomParser(UpdateCallback):
|
||||
sattr.set_type(msg)
|
||||
sattr.set_value(line.data)
|
||||
self.def_src.add_attribute(sattr)
|
||||
self.dbase.commit_source(self.def_src, self.trans)
|
||||
|
||||
def handle_source(self, line, level, state):
|
||||
"""
|
||||
@ -7609,7 +7830,7 @@ class GedcomStageOne(object):
|
||||
input_file.read(1)
|
||||
self.enc = "UTF8"
|
||||
return input_file
|
||||
elif line == b"\xff\xfe":
|
||||
elif line == b"\xff\xfe" or line == b"\xfe\xff":
|
||||
self.enc = "UTF16"
|
||||
input_file.seek(0)
|
||||
return codecs.EncodedFile(input_file, 'utf8', 'utf16')
|
||||
@ -7630,25 +7851,33 @@ class GedcomStageOne(object):
|
||||
reader = self.__detect_file_decoder(self.ifile)
|
||||
|
||||
for line in reader:
|
||||
# Treat the file as though it is UTF-8 since this will be right if a
|
||||
# BOM was detected; it is the more modern option; and anyway it
|
||||
# doesn't really matter as we are only trying to detect a CHAR line
|
||||
# which is only 7-bit ASCII anyway, and we ignore anything that
|
||||
# can't be translated.
|
||||
if sys.version_info[0] < 3:
|
||||
line = unicode(line, encoding='utf-8', errors='replace')
|
||||
else:
|
||||
line = line.decode(encoding='utf-8', errors='replace')
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
self.lcnt += 1
|
||||
|
||||
data = line.split(None, 2) + ['']
|
||||
try:
|
||||
data = line.split(None, 2) + ['']
|
||||
(level, key, value) = data[:3]
|
||||
level = int(level)
|
||||
key = conv_to_unicode(key.strip())
|
||||
value = conv_to_unicode(value.strip())
|
||||
key = key.strip()
|
||||
value = value.strip()
|
||||
except:
|
||||
LOG.warn(_("Invalid line %d in GEDCOM file.") % self.lcnt)
|
||||
continue
|
||||
|
||||
if level == 0 and key[0] == '@':
|
||||
if value == ("FAM", "FAMILY") :
|
||||
if value in ("FAM", "FAMILY") :
|
||||
current_family_id = key.strip()[1:-1]
|
||||
elif value == ("INDI", "INDIVIDUAL"):
|
||||
elif value in ("INDI", "INDIVIDUAL"):
|
||||
self.pcnt += 1
|
||||
elif key in ("HUSB", "HUSBAND", "WIFE") and \
|
||||
self.__is_xref_value(value):
|
||||
@ -7658,6 +7887,9 @@ class GedcomStageOne(object):
|
||||
elif key == 'CHAR' and not self.enc:
|
||||
assert(isinstance(value, STRTYPE))
|
||||
self.enc = value
|
||||
LOG.debug("parse pcnt %d" % self.pcnt)
|
||||
LOG.debug("parse famc %s" % dict(self.famc))
|
||||
LOG.debug("parse fams %s" % dict(self.fams))
|
||||
|
||||
def get_famc_map(self):
|
||||
"""
|
||||
|
@ -51,6 +51,15 @@ class PlaceImport(object):
|
||||
self.loc2handle[location] = handle
|
||||
self.handle2loc[handle] = location
|
||||
|
||||
def remove_location(self, handle):
|
||||
"""
|
||||
Remove the location of a place already in the database.
|
||||
"""
|
||||
if handle in self.handle2loc:
|
||||
loc = self.handle2loc[handle]
|
||||
del(self.loc2handle[loc])
|
||||
del(self.handle2loc[handle])
|
||||
|
||||
def generate_hierarchy(self, trans):
|
||||
"""
|
||||
Generate missing places in the place hierarchy.
|
||||
|
@ -181,7 +181,19 @@ class GeoGraphyView(OsmGps, NavigationView):
|
||||
self.geo_othermap[ident] = cairo.ImageSurface.create_from_png(fh)
|
||||
#self.geo_othermap[ident] = cairo.ImageSurface.create_from_png(path)
|
||||
|
||||
def add_bookmark(self, menu, handle):
|
||||
def add_bookmark(self, menu):
|
||||
mlist = self.selected_handles()
|
||||
if mlist:
|
||||
self.bookmarks.add(mlist[0])
|
||||
else:
|
||||
from gramps.gui.dialog import WarningDialog
|
||||
WarningDialog(
|
||||
_("Could Not Set a Bookmark"),
|
||||
_("A bookmark could not be set because "
|
||||
"no one was selected."))
|
||||
|
||||
|
||||
def add_bookmark_from_popup(self, menu, handle):
|
||||
if handle:
|
||||
self.uistate.set_active(handle, self.navigation_type())
|
||||
self.bookmarks.add(handle)
|
||||
|
@ -25,6 +25,8 @@
|
||||
Display a people who have a person's same surname or given name.
|
||||
"""
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
ngettext = glocale.translation.ngettext # else "nearby" comments are ignored
|
||||
@ -68,9 +70,9 @@ class SameGiven(Rule):
|
||||
src = self.list[0].upper()
|
||||
for name in [person.get_primary_name()] + person.get_alternate_names():
|
||||
if name.first_name:
|
||||
anyNBSP = name.first_name.split(u'\u00A0')
|
||||
anyNBSP = name.first_name.split('\u00A0')
|
||||
if len(anyNBSP) > 1: # there was an NBSP, a non-breaking space
|
||||
first_two = anyNBSP[0] + u'\u00A0' + anyNBSP[1].split()[0]
|
||||
first_two = anyNBSP[0] + '\u00A0' + anyNBSP[1].split()[0]
|
||||
if first_two.upper() == src:
|
||||
return True
|
||||
else:
|
||||
|
@ -828,9 +828,9 @@ class RelationshipCalculator(gramps.gen.relationship.RelationshipCalculator):
|
||||
"""
|
||||
|
||||
rel_str = "parents llunyans"
|
||||
#atgen = u" de la %sena generació"
|
||||
#bygen = u" per la %sena generació"
|
||||
#cmt = u" (germans o germanes d'un avantpassat" + atgen % Ga + ")"
|
||||
#atgen = " de la %sena generació"
|
||||
#bygen = " per la %sena generació"
|
||||
#cmt = " (germans o germanes d'un avantpassat" + atgen % Ga + ")"
|
||||
|
||||
if in_law_a or in_law_b:
|
||||
inlaw = self.INLAW
|
||||
@ -920,7 +920,7 @@ class RelationshipCalculator(gramps.gen.relationship.RelationshipCalculator):
|
||||
inlaw = ""
|
||||
|
||||
rel_str = "un parent llunyà%s" % inlaw
|
||||
#bygen = u" per la %sena generació"
|
||||
#bygen = " per la %sena generació"
|
||||
if Ga == 0:
|
||||
|
||||
# b is descendant of a
|
||||
|
@ -333,7 +333,7 @@ def get_child_unknown(level, inlaw=""):
|
||||
else:
|
||||
return "un descendant lointain%s" % inlaw
|
||||
|
||||
def get_sibling_unknown(inlaw=""):
|
||||
def get_sibling_unknown(Ga, inlaw=""):
|
||||
"""
|
||||
sibling of an ancestor, gender = unknown
|
||||
"""
|
||||
@ -653,7 +653,7 @@ class RelationshipCalculator(gramps.gen.relationship.RelationshipCalculator):
|
||||
elif gender_b == Person.FEMALE:
|
||||
rel_str = "la tante lointaine" + bygen % (Ga + 1)
|
||||
elif gender_b == Person.UNKNOWN:
|
||||
rel_str = get_sibling_unknown(inlaw)
|
||||
rel_str = get_sibling_unknown(Ga, inlaw)
|
||||
else:
|
||||
return rel_str
|
||||
elif Ga == 1:
|
||||
@ -672,7 +672,7 @@ class RelationshipCalculator(gramps.gen.relationship.RelationshipCalculator):
|
||||
rel_str = "la nièce lointaine%s (%dème génération)" % \
|
||||
(inlaw, Gb)
|
||||
elif gender_b == Person.UNKNOWN:
|
||||
rel_str = get_sibling_unknown(inlaw)
|
||||
rel_str = get_sibling_unknown(Ga, inlaw)
|
||||
else:
|
||||
return rel_str
|
||||
elif Ga == Gb:
|
||||
@ -684,7 +684,7 @@ class RelationshipCalculator(gramps.gen.relationship.RelationshipCalculator):
|
||||
elif gender_b == Person.FEMALE:
|
||||
rel_str = get_cousine(Ga - 1, 0, inlaw=inlaw)
|
||||
elif gender_b == Person.UNKNOWN:
|
||||
rel_str = get_sibling_unknown(inlaw)
|
||||
rel_str = get_sibling_unknown(Ga, inlaw)
|
||||
else:
|
||||
return rel_str
|
||||
elif Ga > 1 and Ga > Gb:
|
||||
|
@ -205,10 +205,18 @@ class DropdownSidebar(BaseSidebar):
|
||||
self.viewmanager.notebook.set_current_page(page_no)
|
||||
self.__handlers_unblock()
|
||||
|
||||
def cb_menu_position(menu, button):
|
||||
def cb_menu_position(*args):
|
||||
"""
|
||||
Determine the position of the popup menu.
|
||||
"""
|
||||
# takes two argument: menu, button
|
||||
if len(args) == 2:
|
||||
menu = args[0]
|
||||
button = args[1]
|
||||
# broken introspection can't handle MenuPositionFunc annotations corectly
|
||||
else:
|
||||
menu = args[0]
|
||||
button = args[3]
|
||||
ret_val, x_pos, y_pos = button.get_window().get_origin()
|
||||
x_pos += button.get_allocation().x
|
||||
y_pos += button.get_allocation().y + button.get_allocation().height
|
||||
|
@ -218,10 +218,18 @@ class ExpanderSidebar(BaseSidebar):
|
||||
self.viewmanager.notebook.set_current_page(page_no)
|
||||
self.__handlers_unblock()
|
||||
|
||||
def cb_menu_position(menu, button):
|
||||
def cb_menu_position(*args):
|
||||
"""
|
||||
Determine the position of the popup menu.
|
||||
"""
|
||||
# takes two argument: menu, button
|
||||
if len(args) == 2:
|
||||
menu = args[0]
|
||||
button = args[1]
|
||||
# broken introspection can't handle MenuPositionFunc annotations corectly
|
||||
else:
|
||||
menu = args[0]
|
||||
button = args[3]
|
||||
ret_val, x_pos, y_pos = button.get_window().get_origin()
|
||||
x_pos += button.get_allocation().x
|
||||
y_pos += button.get_allocation().y + button.get_allocation().height
|
||||
|
@ -3,7 +3,9 @@
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.0"/>
|
||||
<object class="GtkDialog" id="check">
|
||||
<!-- This dialog is ManagedWindow class
|
||||
<property name="visible">True</property>
|
||||
-->
|
||||
<property name="can_focus">False</property>
|
||||
<property name="default_width">450</property>
|
||||
<property name="default_height">400</property>
|
||||
|
@ -83,7 +83,7 @@ from gramps.gen.constfunc import UNITYPE, cuni, handle2internal, conv_to_unicode
|
||||
strip_dict = dict.fromkeys(list(range(9))+list(range(11,13))+list(range(14, 32)), " ")
|
||||
|
||||
class ProgressMeter(object):
|
||||
def __init__(self, *args): pass
|
||||
def __init__(self, *args, **kwargs): pass
|
||||
def set_pass(self, *args): pass
|
||||
def step(self): pass
|
||||
def close(self): pass
|
||||
@ -93,7 +93,7 @@ class ProgressMeter(object):
|
||||
# Low Level repair
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
def cross_table_duplicates(db):
|
||||
def cross_table_duplicates(db, uistate):
|
||||
"""
|
||||
Function to find the presence of identical handles that occur in different
|
||||
database tables.
|
||||
@ -105,7 +105,11 @@ def cross_table_duplicates(db):
|
||||
:returns: the presence of cross table duplicate handles
|
||||
:rtype: bool
|
||||
"""
|
||||
progress = ProgressMeter(_('Checking Database'),'')
|
||||
if uistate:
|
||||
parent = uistate.window
|
||||
else:
|
||||
parent = None
|
||||
progress = ProgressMeter(_('Checking Database'),'', parent)
|
||||
progress.set_pass(_('Looking for cross table duplicates'), 9)
|
||||
logging.info('Looking for cross table duplicates')
|
||||
total_nr_handles = 0
|
||||
@ -154,7 +158,7 @@ class Check(tool.BatchTool):
|
||||
# As such, we run it before starting the transaction.
|
||||
# We only do this for the dbdir backend.
|
||||
if self.db.__class__.__name__ == 'DbBsddb':
|
||||
if cross_table_duplicates(self.db):
|
||||
if cross_table_duplicates(self.db, uistate):
|
||||
Report(uistate, _(
|
||||
"Your Family Tree contains cross table duplicate handles.\n "
|
||||
"This is bad and can be fixed by making a backup of your\n"
|
||||
@ -216,6 +220,11 @@ class Check(tool.BatchTool):
|
||||
class CheckIntegrity(object):
|
||||
|
||||
def __init__(self, dbstate, uistate, trans):
|
||||
self.uistate = uistate
|
||||
if self.uistate:
|
||||
self.parent_window = self.uistate.window
|
||||
else:
|
||||
self.parent_window = None
|
||||
self.db = dbstate.db
|
||||
self.trans = trans
|
||||
self.bad_photo = []
|
||||
@ -243,7 +252,8 @@ class CheckIntegrity(object):
|
||||
self.empty_objects = defaultdict(list)
|
||||
self.replaced_sourceref = []
|
||||
self.last_img_dir = config.get('behavior.addmedia-image-dir')
|
||||
self.progress = ProgressMeter(_('Checking Database'),'')
|
||||
self.progress = ProgressMeter(_('Checking Database'),'',
|
||||
parent=self.parent_window)
|
||||
self.explanation = Note(_('Objects referenced by this note '
|
||||
'were referenced but missing so that is why they have been created '
|
||||
'when you ran Check and Repair on %s.') %
|
||||
@ -723,7 +733,7 @@ class CheckIntegrity(object):
|
||||
"You may choose to either remove the reference from the database, "
|
||||
"keep the reference to the missing file, or select a new file."
|
||||
) % {'file_name' : '<b>%s</b>' % photo_name},
|
||||
remove_clicked, leave_clicked, select_clicked)
|
||||
remove_clicked, leave_clicked, select_clicked, parent=self.uistate.window)
|
||||
missmedia_action = mmd.default_action
|
||||
elif missmedia_action == 1:
|
||||
logging.warning(' FAIL: media object "%(desc)s" '
|
||||
|
@ -63,13 +63,22 @@ class DateParserDisplayTest(tool.Tool):
|
||||
tool.Tool.__init__(self, dbstate, options_class, name)
|
||||
if uistate:
|
||||
# Running with gui -> Show message
|
||||
QuestionDialog(_("Start date test?"),_("This test will create many persons and events in the current database. Do you really want to run this test?"),_("Run test"),self.run_tool)
|
||||
self.parent_window = uistate.window
|
||||
QuestionDialog(_("Start date test?"),
|
||||
_("This test will create many persons and events " \
|
||||
"in the current database. Do you really want to " \
|
||||
"run this test?"),
|
||||
_("Run test"),
|
||||
self.run_tool,
|
||||
parent=self.parent_window)
|
||||
else:
|
||||
self.parent_window = None
|
||||
self.run_tool()
|
||||
|
||||
|
||||
def run_tool(self):
|
||||
self.progress = ProgressMeter(_('Running Date Test'),'')
|
||||
self.progress = ProgressMeter(_('Running Date Test'),'',
|
||||
parent=self.parent_window)
|
||||
self.progress.set_pass(_('Generating dates'),
|
||||
4)
|
||||
dates = []
|
||||
|
@ -170,12 +170,13 @@
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="button26">
|
||||
<property name="label">_Apply</property>
|
||||
<property name="label" translatable="yes">_Apply</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_underline">True</property>
|
||||
<signal name="clicked" handler="on_apply_clicked" object="filters" swapped="yes"/>
|
||||
</object>
|
||||
<packing>
|
||||
|
@ -134,7 +134,7 @@ class EventComparison(tool.Tool,ManagedWindow):
|
||||
})
|
||||
|
||||
window = self.filterDialog.toplevel
|
||||
window.show()
|
||||
#window.show()
|
||||
self.filters = self.filterDialog.get_object("filter_list")
|
||||
self.label = _('Event comparison filter selection')
|
||||
self.set_window(window,self.filterDialog.get_object('title'),
|
||||
@ -177,7 +177,8 @@ class EventComparison(tool.Tool,ManagedWindow):
|
||||
def on_apply_clicked(self, obj):
|
||||
cfilter = self.filter_model[self.filters.get_active()][1]
|
||||
|
||||
progress_bar = ProgressMeter(_('Comparing events'),'')
|
||||
progress_bar = ProgressMeter(_('Comparing events'),'',
|
||||
parent=self.window)
|
||||
progress_bar.set_pass(_('Selecting people'),1)
|
||||
|
||||
plist = cfilter.apply(self.db,
|
||||
@ -190,7 +191,7 @@ class EventComparison(tool.Tool,ManagedWindow):
|
||||
self.options.handler.save_options()
|
||||
|
||||
if len(plist) == 0:
|
||||
WarningDialog(_("No matches were found"))
|
||||
WarningDialog(_("No matches were found"), parent=self.window)
|
||||
else:
|
||||
DisplayChart(self.dbstate,self.uistate,plist,self.track)
|
||||
|
||||
@ -238,7 +239,6 @@ class DisplayChart(ManagedWindow):
|
||||
})
|
||||
|
||||
window = self.topDialog.toplevel
|
||||
window.show()
|
||||
self.set_window(window, self.topDialog.get_object('title'),
|
||||
_('Event Comparison Results'))
|
||||
|
||||
@ -306,7 +306,8 @@ class DisplayChart(ManagedWindow):
|
||||
self.progress_bar.close()
|
||||
|
||||
def build_row_data(self):
|
||||
self.progress_bar = ProgressMeter(_('Comparing Events'),'')
|
||||
self.progress_bar = ProgressMeter(_('Comparing Events'),'',
|
||||
parent=self.window)
|
||||
self.progress_bar.set_pass(_('Building data'),len(self.my_list))
|
||||
for individual_id in self.my_list:
|
||||
individual = self.db.get_person_from_handle(individual_id)
|
||||
|
@ -122,7 +122,6 @@ class Merge(tool.Tool,ManagedWindow):
|
||||
self.menu.set_active(0)
|
||||
|
||||
window = top.toplevel
|
||||
window.show()
|
||||
self.set_window(window, top.get_object('title'),
|
||||
_('Find Possible Duplicate People'))
|
||||
|
||||
@ -164,7 +163,7 @@ class Merge(tool.Tool,ManagedWindow):
|
||||
try:
|
||||
self.find_potentials(threshold)
|
||||
except AttributeError as msg:
|
||||
RunDatabaseRepair(str(msg))
|
||||
RunDatabaseRepair(str(msg), parent=self.window)
|
||||
return
|
||||
|
||||
self.options.handler.options_dict['threshold'] = threshold
|
||||
@ -186,8 +185,8 @@ class Merge(tool.Tool,ManagedWindow):
|
||||
|
||||
def find_potentials(self, thresh):
|
||||
self.progress = ProgressMeter(_('Find Duplicates'),
|
||||
_('Looking for duplicate people')
|
||||
)
|
||||
_('Looking for duplicate people'),
|
||||
parent=self.window)
|
||||
|
||||
index = 0
|
||||
males = {}
|
||||
@ -550,7 +549,6 @@ class ShowMatches(ManagedWindow):
|
||||
|
||||
top = Glade(toplevel="mergelist")
|
||||
window = top.toplevel
|
||||
window.show()
|
||||
self.set_window(window, top.get_object('title'),
|
||||
_('Potential Merges'))
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="close">
|
||||
<property name="label">_Close</property>
|
||||
<property name="label" translatable="yes">_Close</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
@ -37,7 +37,7 @@
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="help">
|
||||
<property name="label">_Help</property>
|
||||
<property name="label" translatable="yes">_Help</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
@ -146,7 +146,7 @@
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="tagapply">
|
||||
<property name="label">_Apply</property>
|
||||
<property name="label" translatable="yes">_Apply</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
|
@ -129,11 +129,16 @@ class TestcaseGenerator(tool.BatchTool):
|
||||
|
||||
def __init__(self, dbstate, user, options_class, name, callback=None):
|
||||
uistate = user.uistate
|
||||
if uistate:
|
||||
parent_window = uistate.window
|
||||
else:
|
||||
parent_window = None
|
||||
self.person = None
|
||||
if dbstate.db.readonly:
|
||||
return
|
||||
|
||||
tool.BatchTool.__init__(self, dbstate, user, options_class, name)
|
||||
tool.BatchTool.__init__(self, dbstate, user, options_class, name,
|
||||
parent=parent_window)
|
||||
|
||||
if self.fail:
|
||||
return
|
||||
@ -192,7 +197,7 @@ class TestcaseGenerator(tool.BatchTool):
|
||||
|
||||
def init_gui(self,uistate):
|
||||
title = "%s - Gramps" % _("Generate testcases")
|
||||
self.top = Gtk.Dialog(title)
|
||||
self.top = Gtk.Dialog(title, parent=uistate.window)
|
||||
self.top.set_default_size(400,150)
|
||||
self.top.vbox.set_spacing(5)
|
||||
label = Gtk.Label(label='<span size="larger" weight="bold">%s</span>' % _("Generate testcases"))
|
||||
@ -279,7 +284,7 @@ class TestcaseGenerator(tool.BatchTool):
|
||||
while Gtk.events_pending():
|
||||
Gtk.main_iteration()
|
||||
|
||||
self.progress = ProgressMeter(_('Generating testcases'),'')
|
||||
self.progress = ProgressMeter(_('Generating testcases'),'', parent=self.window)
|
||||
self.transaction_count = 0;
|
||||
|
||||
if self.options.handler.options_dict['lowlevel']:
|
||||
|
@ -232,7 +232,7 @@ class GeoClose(GeoGraphyView):
|
||||
self._createmap(self.refperson, color, self.place_list_ref, True)
|
||||
if self.refperson_bookmark is None:
|
||||
self.refperson_bookmark = self.refperson.get_handle()
|
||||
self.add_bookmark(None, self.refperson_bookmark)
|
||||
self.add_bookmark_from_popup(None, self.refperson_bookmark)
|
||||
else:
|
||||
self.message_layer.add_message(_("You must choose one reference person."))
|
||||
self.message_layer.add_message(_("Go to the person view and select "
|
||||
|
@ -335,7 +335,7 @@ class GeoEvents(GeoGraphyView):
|
||||
hdle = evt.get_handle()
|
||||
bookm = Gtk.MenuItem(label=_("Bookmark this event"))
|
||||
bookm.show()
|
||||
bookm.connect("activate", self.add_bookmark, hdle)
|
||||
bookm.connect("activate", self.add_bookmark_from_popup, hdle)
|
||||
itemoption.append(bookm)
|
||||
if mark[0] != oldplace:
|
||||
message = "%s :" % mark[0]
|
||||
@ -367,7 +367,7 @@ class GeoEvents(GeoGraphyView):
|
||||
hdle = evt.get_handle()
|
||||
bookm = Gtk.MenuItem(label=_("Bookmark this event"))
|
||||
bookm.show()
|
||||
bookm.connect("activate", self.add_bookmark, hdle)
|
||||
bookm.connect("activate", self.add_bookmark_from_popup, hdle)
|
||||
itemoption.append(bookm)
|
||||
menu.popup(None, None,
|
||||
lambda menu, data: (event.get_root_coords()[0],
|
||||
|
@ -255,7 +255,7 @@ class GeoFamClose(GeoGraphyView):
|
||||
self.message_layer.add_message(_("The other family : %s" % _("Unknown")))
|
||||
if self.reffamily_bookmark is None:
|
||||
self.reffamily_bookmark = self.reffamily.get_handle()
|
||||
self.add_bookmark(None, self.reffamily_bookmark)
|
||||
self.add_bookmark_from_popup(None, self.reffamily_bookmark)
|
||||
else:
|
||||
self.message_layer.add_message(_("You must choose one reference family."))
|
||||
self.message_layer.add_message(_("Go to the family view and select "
|
||||
|
@ -607,7 +607,7 @@ class GeoMoves(GeoGraphyView):
|
||||
hdle = person.get_handle()
|
||||
bookm = Gtk.MenuItem(label=_("Bookmark this person"))
|
||||
bookm.show()
|
||||
bookm.connect("activate", self.add_bookmark, hdle)
|
||||
bookm.connect("activate", self.add_bookmark_from_popup, hdle)
|
||||
itemoption.append(bookm)
|
||||
menu.show()
|
||||
menu.popup(None, None,
|
||||
|
@ -333,7 +333,7 @@ class GeoPlaces(GeoGraphyView):
|
||||
hdle = place.get_handle()
|
||||
bookm = Gtk.MenuItem(label=_("Bookmark this place"))
|
||||
bookm.show()
|
||||
bookm.connect("activate", self.add_bookmark, hdle)
|
||||
bookm.connect("activate", self.add_bookmark_from_popup, hdle)
|
||||
itemoption.append(bookm)
|
||||
message = "%s" % mark[0]
|
||||
prevmark = mark
|
||||
@ -357,7 +357,7 @@ class GeoPlaces(GeoGraphyView):
|
||||
hdle = place.get_handle()
|
||||
bookm = Gtk.MenuItem(label=_("Bookmark this place"))
|
||||
bookm.show()
|
||||
bookm.connect("activate", self.add_bookmark, hdle)
|
||||
bookm.connect("activate", self.add_bookmark_from_popup, hdle)
|
||||
itemoption.append(bookm)
|
||||
menu.popup(None, None,
|
||||
lambda menu, data: (event.get_root_coords()[0],
|
||||
|
@ -30,7 +30,7 @@ Can use the Webkit or Gecko ( Mozilla ) library
|
||||
# Python modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
import os
|
||||
import os, io
|
||||
import sys
|
||||
if sys.version_info[0] < 3:
|
||||
from urlparse import urlunsplit
|
||||
@ -557,7 +557,7 @@ class HtmlView(NavigationView):
|
||||
# Now we have two views : Web and Geography, we need to create the
|
||||
# startpage only once.
|
||||
if not os.path.exists(filename):
|
||||
ufd = file(filename, "w+")
|
||||
ufd = io.open(filename, "w+", encoding="utf8")
|
||||
ufd.write(data)
|
||||
ufd.close()
|
||||
return urlunsplit(('file', '',
|
||||
|
@ -181,16 +181,9 @@ class MediaView(ListView):
|
||||
"""
|
||||
if not sel_data:
|
||||
return
|
||||
#modern file managers provide URI_LIST. For Windows split sel_data.data
|
||||
files = sel_data.get_uris()
|
||||
for file in files:
|
||||
if win():
|
||||
clean_string = conv_to_unicode(
|
||||
file.replace('\0',' ').replace("\r", " ").strip(),
|
||||
None)
|
||||
else:
|
||||
clean_string = file
|
||||
protocol, site, mfile, j, k, l = urlparse(clean_string)
|
||||
protocol, site, mfile, j, k, l = urlparse(file)
|
||||
if protocol == "file":
|
||||
name = url2pathname(mfile)
|
||||
mime = get_type(name)
|
||||
|
@ -142,7 +142,7 @@ from gramps.plugins.lib.libhtml import Html, xml_lang
|
||||
# import styled notes from src/plugins/lib/libhtmlbackend.py
|
||||
from gramps.plugins.lib.libhtmlbackend import HtmlBackend, process_spaces
|
||||
|
||||
from gramps.plugins.lib.libgedcom import make_gedcom_date
|
||||
from gramps.plugins.lib.libgedcom import make_gedcom_date, DATE_QUALITY
|
||||
from gramps.gen.utils.place import conv_lat_lon
|
||||
from gramps.gui.pluginmanager import GuiPluginManager
|
||||
|
||||
@ -517,14 +517,20 @@ def format_date(date):
|
||||
cal = date.get_calendar()
|
||||
mod = date.get_modifier()
|
||||
quality = date.get_quality()
|
||||
if quality in DATE_QUALITY:
|
||||
qual_text = DATE_QUALITY[quality] + " "
|
||||
else:
|
||||
qual_text = ""
|
||||
if mod == Date.MOD_SPAN:
|
||||
val = "FROM %s TO %s" % (
|
||||
make_gedcom_date(start, cal, mod, quality),
|
||||
make_gedcom_date(date.get_stop_date(), cal, mod, quality))
|
||||
val = "%sFROM %s TO %s" % (
|
||||
qual_text,
|
||||
make_gedcom_date(start, cal, mod, None),
|
||||
make_gedcom_date(date.get_stop_date(), cal, mod, None))
|
||||
elif mod == Date.MOD_RANGE:
|
||||
val = "BET %s AND %s" % (
|
||||
make_gedcom_date(start, cal, mod, quality),
|
||||
make_gedcom_date(date.get_stop_date(), cal, mod, quality))
|
||||
val = "%sBET %s AND %s" % (
|
||||
qual_text,
|
||||
make_gedcom_date(start, cal, mod, None),
|
||||
make_gedcom_date(date.get_stop_date(), cal, mod, None))
|
||||
else:
|
||||
val = make_gedcom_date(start, cal, mod, quality)
|
||||
return val
|
||||
@ -5046,14 +5052,14 @@ class DownloadPage(BasePage):
|
||||
else:
|
||||
tcell += " "
|
||||
|
||||
# clear line for proper styling
|
||||
# create footer section
|
||||
footer = self.write_footer()
|
||||
body += (fullclear, footer)
|
||||
# clear line for proper styling
|
||||
# create footer section
|
||||
footer = self.write_footer()
|
||||
body += (fullclear, footer)
|
||||
|
||||
# send page out for processing
|
||||
# and close the file
|
||||
self.XHTMLWriter(downloadpage, of, sio)
|
||||
# send page out for processing
|
||||
# and close the file
|
||||
self.XHTMLWriter(downloadpage, of, sio)
|
||||
|
||||
class ContactPage(BasePage):
|
||||
def __init__(self, report, title):
|
||||
|
@ -18,6 +18,6 @@
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
VERSION_TUPLE = (4, 1, 3)
|
||||
VERSION_TUPLE = (4, 1, 4)
|
||||
VERSION = '.'.join(map(str,VERSION_TUPLE))
|
||||
major_version = "%s.%s" % (VERSION_TUPLE[0], VERSION_TUPLE[1])
|
||||
|
@ -7,7 +7,7 @@
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>Gramps</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>4.1.2, (C) 1997-2015 The Gramps Team http://www.gramps-project.org</string>
|
||||
<string>4.1.3, (C) 1997-2015 The Gramps Team http://www.gramps-project.org</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>gramps.icns</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
@ -17,11 +17,11 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>4.1.2-1</string>
|
||||
<string>4.1.3-1</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>4.1.2-1</string>
|
||||
<string>4.1.3-1</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright 1997 - 2015 The Gramps Team, GNU General Public License.</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
|
@ -39,7 +39,8 @@ export PYTHONHOME="$bundle_res"
|
||||
export GRAMPSDIR="$bundle_lib/python$PYVER/site-packages/gramps"
|
||||
export GRAMPSI18N="$bundle_data"/locale
|
||||
export GRAMPS_RESOURCES="$bundle_data"
|
||||
export GRAMPSHOME="$HOME/Library/Application Support"
|
||||
export USERPROFILE="$HOME"
|
||||
export APPDATA="$HOME/Library/Application Support"
|
||||
|
||||
# Strip out the argument added by the OS.
|
||||
if /bin/expr "x$1" : '^x-psn_' > /dev/null; then
|
||||
|
@ -58,7 +58,7 @@ gtk-mac-bundler gtk-osx-build/projects/gramps/gramps.bundle
|
||||
<!--include href="/Users/john/Development/GTK-OSX/gtk-osx-build/modulesets-stable/gtk-osx.modules"/-->
|
||||
|
||||
<distutils id="gramps" autogen-sh="configure">
|
||||
<branch module="gramps/gramps-4.1.2.tar.gz" version="4.1.2"
|
||||
<branch module="gramps/gramps-4.1.3.tar.gz" version="4.1.3"
|
||||
repo="sourceforge">
|
||||
</branch>
|
||||
<dependencies>
|
||||
|
33
po/check_po
33
po/check_po
@ -116,6 +116,38 @@ class Check_named_fmt( Check ):
|
||||
msgstr = msg.msgstr[1]
|
||||
self.__process( msg, msgid, msgstr )
|
||||
|
||||
class Check_mapping_fmt( Check ):
|
||||
# A pattern to find all {}
|
||||
find_map_pat = re.compile('\{ \w+ \}', re.VERBOSE)
|
||||
|
||||
def __init__( self ):
|
||||
Check.__init__( self )
|
||||
self.diag_header = "-------- {} name mismatches --------------"
|
||||
self.summary_text = "{} name mismatches:"
|
||||
|
||||
def __process( self, msg, msgid, msgstr ):
|
||||
# Same number of named formats?
|
||||
fmts1 = self.find_map_pat.findall( msgid )
|
||||
fmts2 = self.find_map_pat.findall( msgstr )
|
||||
if len( fmts1 ) != len( fmts2 ):
|
||||
self.msgs.append( msg )
|
||||
else:
|
||||
# Do we have the same named formats?
|
||||
fmts1.sort()
|
||||
fmts2.sort()
|
||||
if fmts1 != fmts2:
|
||||
self.msgs.append( msg )
|
||||
|
||||
def process( self, msg ):
|
||||
msgid = msg.msgid
|
||||
msgstr = msg.msgstr[0]
|
||||
self.__process( msg, msgid, msgstr )
|
||||
|
||||
if msg.msgidp and len(msg.msgstr) >= 2:
|
||||
msgid = msg.msgidp
|
||||
msgstr = msg.msgstr[1]
|
||||
self.__process( msg, msgid, msgstr )
|
||||
|
||||
class Check_missing_sd( Check ):
|
||||
# A pattern to find %() without s or d
|
||||
# Here is a command to use for testing
|
||||
@ -513,6 +545,7 @@ def analyze_msgs( args, fname, msgs, nr_templates = None, nth = 0 ):
|
||||
checks.append( Check_fmt( '%s' ) )
|
||||
checks.append( Check_fmt( '%d' ) )
|
||||
checks.append( Check_named_fmt() )
|
||||
checks.append( Check_mapping_fmt() )
|
||||
checks.append( Check_missing_sd() )
|
||||
checks.append( Check_runaway() )
|
||||
checks.append( Check_xml_chars() )
|
||||
|
1760
po/gramps.pot
1760
po/gramps.pot
File diff suppressed because it is too large
Load Diff
20
po/nl.po
20
po/nl.po
@ -6399,23 +6399,20 @@ msgid "Street"
|
||||
msgstr "Straat"
|
||||
|
||||
#: ../gramps/gen/lib/placetype.py:76
|
||||
#, fuzzy
|
||||
msgid "Province"
|
||||
msgstr "Deelstaat/Provincie"
|
||||
|
||||
#: ../gramps/gen/lib/placetype.py:77
|
||||
#, fuzzy
|
||||
msgid "Region"
|
||||
msgstr "Religie"
|
||||
msgstr "Regio"
|
||||
|
||||
#: ../gramps/gen/lib/placetype.py:78
|
||||
#, fuzzy
|
||||
msgid "Department"
|
||||
msgstr "Pensioen"
|
||||
msgstr "Departement"
|
||||
|
||||
#: ../gramps/gen/lib/placetype.py:79
|
||||
msgid "Neighborhood"
|
||||
msgstr ""
|
||||
msgstr "Buurt"
|
||||
|
||||
#: ../gramps/gen/lib/placetype.py:80
|
||||
msgid "District"
|
||||
@ -6426,9 +6423,8 @@ msgid "Borough"
|
||||
msgstr ""
|
||||
|
||||
#: ../gramps/gen/lib/placetype.py:82
|
||||
#, fuzzy
|
||||
msgid "Municipality"
|
||||
msgstr "Plaats"
|
||||
msgstr "Gemeente"
|
||||
|
||||
#: ../gramps/gen/lib/placetype.py:83
|
||||
msgid "Town"
|
||||
@ -6436,7 +6432,7 @@ msgstr ""
|
||||
|
||||
#: ../gramps/gen/lib/placetype.py:84
|
||||
msgid "Village"
|
||||
msgstr ""
|
||||
msgstr "Dorp"
|
||||
|
||||
#: ../gramps/gen/lib/placetype.py:85
|
||||
msgid "Hamlet"
|
||||
@ -6444,7 +6440,7 @@ msgstr ""
|
||||
|
||||
#: ../gramps/gen/lib/placetype.py:86
|
||||
msgid "Farm"
|
||||
msgstr ""
|
||||
msgstr "Boerderij"
|
||||
|
||||
#: ../gramps/gen/lib/placetype.py:87
|
||||
#, fuzzy
|
||||
@ -8110,7 +8106,7 @@ msgstr "Onbekend, aangemaakt om een ontbrekend opmerkingsobject te vervangen."
|
||||
#: ../gramps/gen/utils/unknown.py:149
|
||||
#, python-format
|
||||
msgid "Unknown, was missing %(time)s (%(count)d)"
|
||||
msgstr "Obekend, %(time)s (%(count)d) ontbrak"
|
||||
msgstr "Onbekend, %(time)s (%(count)d) ontbrak"
|
||||
|
||||
#: ../gramps/gen/utils/unknown.py:168
|
||||
#, python-format
|
||||
@ -17681,7 +17677,7 @@ msgstr "doopdatum"
|
||||
|
||||
#: ../gramps/plugins/export/exportcsv.py:368 ../gramps/plugins/importer/importcsv.py:201
|
||||
msgid "Baptism place"
|
||||
msgstr "doopplaats"
|
||||
msgstr "Doopplaats"
|
||||
|
||||
#: ../gramps/plugins/export/exportcsv.py:368 ../gramps/plugins/importer/importcsv.py:206
|
||||
msgid "Baptism source"
|
||||
|
@ -169,9 +169,13 @@ def TipsParse(filename, mark):
|
||||
tips = open('../data/tips.xml.in.h', 'w')
|
||||
marklist = root.iter(mark)
|
||||
for key in marklist:
|
||||
tip = ElementTree.tostring(key, encoding="UTF-8")
|
||||
tip = tip.replace("<?xml version='1.0' encoding='UTF-8'?>", "")
|
||||
tip = tip.replace('\n<_tip number="%(number)s">' % key.attrib, "")
|
||||
tip = ElementTree.tostring(key, encoding="UTF-8", method="xml")
|
||||
if sys.version_info[0] < 3:
|
||||
tip = tip.replace("<?xml version='1.0' encoding='UTF-8'?>", "")
|
||||
tip = tip.replace('\n<_tip number="%(number)s">' % key.attrib, "")
|
||||
else: # python3 support
|
||||
tip = tip.decode("utf-8")
|
||||
tip = tip.replace('<_tip number="%(number)s">' % key.attrib, "")
|
||||
tip = tip.replace("<br />", "<br/>")
|
||||
#tip = tip.replace("\n</_tip>\n", "</_tip>\n") # special case tip 7
|
||||
#tip = tip.replace("\n<b>", "<b>") # special case tip 18
|
||||
|
Loading…
Reference in New Issue
Block a user