Compare commits
131 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1c241125d7 | |||
| e81c82fcc0 | |||
| 7d91f4fae8 | |||
| 78a74cae5c | |||
| 5039ad10f0 | |||
| 926bb60650 | |||
| 314484cf84 | |||
| ae0a039216 | |||
| 296e8ca562 | |||
| 4e6a06bfac | |||
| 5ff70a665e | |||
| c0a0c1d302 | |||
| e71115af1b | |||
| 5828dd3aa8 | |||
| 273a5f986f | |||
| 00032f0cad | |||
| 1ed6103a41 | |||
| ea2c71d238 | |||
| 6605d868cb | |||
| 7a57c8584e | |||
| 46a509ee1d | |||
| 1aad1d6be0 | |||
| 104808cf8f | |||
| 8a34d98fb5 | |||
| c42f714393 | |||
| d8ba31cc29 | |||
| f9bb9e5e73 | |||
| ed0b50e9b8 | |||
| 19acd368c0 | |||
| 9de644bf1d | |||
| bdd70dec1c | |||
| 2a26ca6c5c | |||
| f675fc1114 | |||
| 143df8d42c | |||
| d71a36b240 | |||
| 085d1ee095 | |||
| cc2f803b86 | |||
| 9c34e17bd9 | |||
| 136bdd015a | |||
| e0b56f65ef | |||
| 089d028e8f | |||
| 6e4ce84396 | |||
| 0930c0041f | |||
| ef0492e0ff | |||
| e1a71dcd34 | |||
| 6895594cdc | |||
| 45f82f1ec5 | |||
| c26fcf6d56 | |||
| 2701d51b62 | |||
| 9683a9e9eb | |||
| d03e9e6553 | |||
| 0f8b77b706 | |||
| a2244c6ab4 | |||
| 2eccc6c4ce | |||
| 815596a88d | |||
| 98fdeaeb6b | |||
| 4d57633006 | |||
| 9c3450531b | |||
| 2a35887da7 | |||
| b34e088fe7 | |||
| 189cc853b2 | |||
| 1d3d002490 | |||
| eb34416727 | |||
| 52842f8200 | |||
| 646afece89 | |||
| 45adb70330 | |||
| 4ac02a4ca0 | |||
| ffe417f43e | |||
| dc50b14ed3 | |||
| 35f5426a33 | |||
| 8ff85415e8 | |||
| be1980e39a | |||
| 93bd200ea0 | |||
| 906e46b0a5 | |||
| 51324b182f | |||
| 4b8d25083b | |||
| 7ef450515c | |||
| cb5fb2ed27 | |||
| b4eae781f8 | |||
| c5c5889a42 | |||
| fe2a36c580 | |||
| e3538e8a8b | |||
| 475e78669b | |||
| f022597545 | |||
| 672bffe620 | |||
| 70e453f031 | |||
| 8a4b9c7ee0 | |||
| e86f796cf7 | |||
| 436487072a | |||
| a2a68c64f7 | |||
| c9d9925965 | |||
| 4b8ca4a824 | |||
| 57a367e8c4 | |||
| 9ff34f2934 | |||
| 88f5fbeb9e | |||
| 42cf24205b | |||
| 36c03a0a53 | |||
| cf264e8d89 | |||
| ab056c8ac5 | |||
| cc1f5b734a | |||
| ac21380b60 | |||
| 706437f57d | |||
| ab0dfc8d68 | |||
| 4cb4d912fe | |||
| 315ddd6ffd | |||
| 60ff2584fb | |||
| d2363249b9 | |||
| b70adcb74a | |||
| f63385ac4b | |||
| d9cc3d3b0e | |||
| ccbf763ac4 | |||
| 111c245297 | |||
| 359d277423 | |||
| 1a0784b154 | |||
| b02e1fe467 | |||
| 8fe31528cf | |||
| 38b5b8ec54 | |||
| 8d57884b5b | |||
| 69e7501acc | |||
| 385c10a068 | |||
| d99adb8bd4 | |||
| 74995c0994 | |||
| 14ccc01acb | |||
| 542450bf6d | |||
| b381f2dd8b | |||
| 25f9ea64fc | |||
| c1b65d8cbe | |||
| 89a5d3fcda | |||
| 38addcedb3 | |||
| 6c0fdb27d6 | |||
| 3e5a7fe970 |
@@ -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%;
|
||||
}
|
||||
|
||||
+3
-3
@@ -82,9 +82,9 @@ gramps(1) @VERSION@ gramps(1)
|
||||
|
||||
|
||||
**-i** , **--import=** *FILE*
|
||||
Import data from *FILE* . If you haven't specified a database then
|
||||
a temporary database is used; this is deleted when you exit
|
||||
gramps.
|
||||
Import data from *FILE* . If you haven't specified a database, then
|
||||
an empty database is created for you called Family Tree x
|
||||
(where x is an incrementing number).
|
||||
|
||||
When more than one input file is given, each has to be preceded
|
||||
by **-i** flag. The files are imported in the specified order, i.e.
|
||||
|
||||
+2
-2
@@ -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.
|
||||
.
|
||||
|
||||
@@ -104,7 +104,7 @@ If no action, import or export options are given on the command line then an int
|
||||
|
||||
.TP
|
||||
.BI \-i,\-\^\-import= " FILE"
|
||||
Import data from \fIFILE\fR. If you haven't specified a database then a temporary database is used; this is deleted when you exit gramps.
|
||||
Import data from \fIFILE\fR. If you haven't specified a database, then an empty database is created for you called Family Tree x (where x is an incrementing number).
|
||||
.br
|
||||
|
||||
When more than one input file is given, each has to be preceded by \fB\-i\fR
|
||||
|
||||
+1
-1
@@ -89,7 +89,7 @@
|
||||
|
||||
<_tip number="48"><b>Web Family Tree Format</b><br/>Gramps can export data to the Web Family Tree (WFT) format. This format allows a family tree to be displayed online using a single file, instead of many html files.</_tip>
|
||||
|
||||
<_tip number="49"><b>Making a Genealogy Website</b><br/>You can easily export your family tree to a web page. Select the entire database, family lines or selected individuals to a collection of web pages ready for upload to the World Wide Web. The Gramps project provides free hosting of websites made with Gramps.</_tip>
|
||||
<_tip number="49"><b>Making a Genealogy Website</b><br/>You can easily export your family tree to a web page. Select the entire database, family lines or selected individuals to a collection of web pages ready for upload to the World Wide Web.</_tip>
|
||||
|
||||
<_tip number="50"><b>Reporting Bugs in Gramps</b><br/>The best way to report a bug in Gramps is to use the Gramps bug tracking system at http://bugs.gramps-project.org</_tip>
|
||||
|
||||
|
||||
+109
-21
@@ -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
|
||||
|
||||
+1
-1
@@ -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.
|
||||
|
||||
|
||||
+3
-6
@@ -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
|
||||
@@ -134,8 +134,8 @@ sys.path.insert(0, ROOT_DIR)
|
||||
git_revision = get_git_revision(ROOT_DIR)
|
||||
if sys.platform == 'win32' and git_revision == "":
|
||||
git_revision = get_git_revision(os.path.split(ROOT_DIR)[1])
|
||||
#VERSION += git_revision
|
||||
VERSION += "-1"
|
||||
VERSION += git_revision
|
||||
#VERSION += "-1"
|
||||
|
||||
#
|
||||
# Glade files
|
||||
@@ -215,9 +215,6 @@ DOCUMENTERS = [
|
||||
'Alexander Roitman',
|
||||
]
|
||||
|
||||
TRANSLATORS = _('TRANSLATORS: Translate this to your '
|
||||
'name in your native language')
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Constants
|
||||
|
||||
@@ -221,7 +221,9 @@ class DateDisplayCZ(DateDisplay):
|
||||
# this must agree with DateDisplayEn's "formats" definition
|
||||
# (since no locale-specific _display_gregorian exists, here)
|
||||
|
||||
def display(self, date):
|
||||
display = DateDisplay.display_formatted
|
||||
|
||||
def orig_display(self, date):
|
||||
"""
|
||||
Return a text string representing the date.
|
||||
"""
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -444,6 +444,7 @@ class DbBsddbRead(DbReadBase, Callback):
|
||||
self.citation_map = {}
|
||||
self.repository_map = {}
|
||||
self.note_map = {}
|
||||
self.tag_map = {}
|
||||
self.media_map = {}
|
||||
self.event_map = {}
|
||||
self.metadata = {}
|
||||
|
||||
+11
-9
@@ -2471,14 +2471,14 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
|
||||
with open(versionpath, "w") as version_file:
|
||||
version_file.write(version)
|
||||
|
||||
versionpath = os.path.join(name, cuni(PCKVERSFN))
|
||||
_LOG.debug("Write pickle version file to %s" % "Yes")
|
||||
with open(versionpath, "w") as version_file:
|
||||
version = "Yes"
|
||||
if sys.version_info[0] <3:
|
||||
if isinstance(version, UNITYPE):
|
||||
version = version.encode('utf-8')
|
||||
version_file.write(version)
|
||||
# The pickle upgrade file is not written for Python2; its contents is
|
||||
# never actually examined, all that matters is whether it is present
|
||||
if sys.version_info[0] >= 3:
|
||||
versionpath = os.path.join(name, cuni(PCKVERSFN))
|
||||
_LOG.debug("Write pickle version file to %s" % "Yes")
|
||||
with open(versionpath, "w") as version_file:
|
||||
version = "Yes"
|
||||
version_file.write(version)
|
||||
|
||||
versionpath = os.path.join(name, cuni(SCHVERSFN))
|
||||
_LOG.debug("Write schema version file to %s" % str(_DBVERSION))
|
||||
@@ -2529,7 +2529,9 @@ def write_lock_file(name):
|
||||
try:
|
||||
user = os.getlogin()
|
||||
except:
|
||||
user = os.environ['USER'] #not win, don't need get_env_var
|
||||
# not win, so don't need get_env_var.
|
||||
# under cron getlogin() throws and there is no USER.
|
||||
user = os.environ.get('USER', 'noUSER')
|
||||
if host:
|
||||
text = "%s@%s" % (user, host)
|
||||
else:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -78,22 +78,26 @@ class IsDescendantFamilyOf(Rule):
|
||||
return
|
||||
|
||||
# Add self
|
||||
self.matches.add(person.handle)
|
||||
expand = [person]
|
||||
|
||||
for family_handle in person.get_family_handle_list():
|
||||
family = self.db.get_family_from_handle(family_handle)
|
||||
if family:
|
||||
# Add every child recursively
|
||||
for child_ref in family.get_child_ref_list():
|
||||
if child_ref:
|
||||
self.add_matches(self.db.get_person_from_handle(child_ref.ref))
|
||||
|
||||
# Add spouse
|
||||
if person.handle == family.get_father_handle():
|
||||
spouse_handle = family.get_mother_handle()
|
||||
else:
|
||||
spouse_handle = family.get_father_handle()
|
||||
self.matches.add(spouse_handle)
|
||||
while expand:
|
||||
person = expand.pop(0)
|
||||
if person is None:
|
||||
continue
|
||||
self.matches.add(person.handle)
|
||||
for family_handle in person.get_family_handle_list():
|
||||
family = self.db.get_family_from_handle(family_handle)
|
||||
if family:
|
||||
# Add every child recursively
|
||||
for child_ref in family.get_child_ref_list():
|
||||
if child_ref:
|
||||
expand.append(self.db.get_person_from_handle(child_ref.ref))
|
||||
# Add spouse
|
||||
if person.handle == family.get_father_handle():
|
||||
spouse_handle = family.get_mother_handle()
|
||||
else:
|
||||
spouse_handle = family.get_father_handle()
|
||||
self.matches.add(spouse_handle)
|
||||
|
||||
def exclude(self):
|
||||
# This removes root person and his/her spouses from the matches set
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -67,6 +67,9 @@ class AttributeRoot(SecondaryObject, PrivacyBase):
|
||||
self.type = None
|
||||
self.value = None
|
||||
|
||||
def __str__(self):
|
||||
return str(self.value)
|
||||
|
||||
def serialize(self):
|
||||
"""
|
||||
Convert the object to a serialized tuple of data.
|
||||
|
||||
@@ -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
|
||||
|
||||
+10
-11
@@ -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:
|
||||
|
||||
@@ -195,4 +195,6 @@ def create_checksum(full_path):
|
||||
md5sum = hashlib.md5(media_file.read()).hexdigest()
|
||||
except IOError:
|
||||
md5sum = ''
|
||||
except UnicodeEncodeError:
|
||||
md5sum = ''
|
||||
return md5sum
|
||||
|
||||
@@ -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.
|
||||
|
||||
+16
-40
@@ -56,7 +56,7 @@ from gi.repository import GdkPixbuf
|
||||
#-------------------------------------------------------------------------
|
||||
from gramps.gen.const import (AUTHORS, AUTHORS_FILE, COMMENTS, COPYRIGHT_MSG,
|
||||
DOCUMENTERS, LICENSE_FILE, PROGRAM_NAME, SPLASH,
|
||||
TRANSLATORS, URL_HOMEPAGE, VERSION)
|
||||
URL_HOMEPAGE, VERSION)
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
from gramps.gen.constfunc import get_env_var
|
||||
@@ -68,14 +68,6 @@ if config.get('preferences.use-bsddb3') or sys.version_info[0] >= 3:
|
||||
else:
|
||||
import bsddb
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Constants
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
AUTHORS_HEADER = _('==== Authors ====\n')
|
||||
CONTRIB_HEADER = _('\n==== Contributors ====\n')
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# GrampsAboutDialog
|
||||
@@ -85,20 +77,19 @@ class GrampsAboutDialog(Gtk.AboutDialog):
|
||||
"""Create an About dialog with all fields set."""
|
||||
def __init__(self, parent):
|
||||
"""Setup all the fields shown in the About dialog."""
|
||||
GObject.GObject.__init__(self)
|
||||
Gtk.AboutDialog.__init__(self)
|
||||
self.set_transient_for(parent)
|
||||
self.set_modal(True)
|
||||
|
||||
self.set_name(PROGRAM_NAME)
|
||||
self.set_version(VERSION)
|
||||
self.set_copyright(COPYRIGHT_MSG)
|
||||
self.set_artists([
|
||||
_("Much of Gramps' artwork is either from\n"
|
||||
"the Tango Project or derived from the Tango\n"
|
||||
"Project. This artwork is released under the\n"
|
||||
"Creative Commons Attribution-ShareAlike 2.5\n"
|
||||
"license.")
|
||||
])
|
||||
artists = _("Much of Gramps' artwork is either from\n"
|
||||
"the Tango Project or derived from the Tango\n"
|
||||
"Project. This artwork is released under the\n"
|
||||
"Creative Commons Attribution-ShareAlike 2.5\n"
|
||||
"license.")
|
||||
self.set_artists(artists.split('\n'))
|
||||
|
||||
try:
|
||||
ifile = open(LICENSE_FILE, "r")
|
||||
@@ -111,12 +102,13 @@ class GrampsAboutDialog(Gtk.AboutDialog):
|
||||
self.set_website_label(_('Gramps Homepage'))
|
||||
self.set_website(URL_HOMEPAGE)
|
||||
|
||||
self.set_authors(_get_authors())
|
||||
authors, contributors = _get_authors()
|
||||
self.set_authors(authors)
|
||||
if len(contributors) > 0:
|
||||
self.add_credit_section(_('Contributions by'), contributors)
|
||||
|
||||
# Only set translation credits if they are translated
|
||||
trans_credits = _(TRANSLATORS)
|
||||
if trans_credits != TRANSLATORS:
|
||||
self.set_translator_credits(trans_credits)
|
||||
# TRANSLATORS: Translate this to your name in your native language
|
||||
self.set_translator_credits(_("translator-credits"))
|
||||
|
||||
self.set_documenters(DOCUMENTERS)
|
||||
self.set_logo(GdkPixbuf.Pixbuf.new_from_file(SPLASH))
|
||||
@@ -232,25 +224,9 @@ def _get_authors():
|
||||
parser.parse(authors_file)
|
||||
authors_file.close()
|
||||
|
||||
authors_text = ([AUTHORS_HEADER] + authors +
|
||||
[CONTRIB_HEADER] + contributors)
|
||||
authors_text = [authors, contributors]
|
||||
|
||||
except (IOError, OSError, SAXParseException):
|
||||
authors_text = AUTHORS
|
||||
authors_text = [AUTHORS, []]
|
||||
|
||||
return authors_text
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# _show_url
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
def _show_url(dialog, link, prefix):
|
||||
"""Show links in About dialog."""
|
||||
if prefix is not None:
|
||||
link = prefix + link
|
||||
display_url(link)
|
||||
|
||||
#TODO GTK3: is there an alternative for these:
|
||||
#Gtk.about_dialog_set_url_hook(_show_url, None)
|
||||
#Gtk.about_dialog_set_email_hook(_show_url, 'mailto:')
|
||||
|
||||
+10
-1
@@ -1564,7 +1564,8 @@ class MultiTreeView(Gtk.TreeView):
|
||||
|
||||
def edit_obj(self, objclass, handle):
|
||||
from .editors import (EditPerson, EditEvent, EditFamily, EditSource,
|
||||
EditPlace, EditRepository, EditNote, EditMedia)
|
||||
EditPlace, EditRepository, EditNote, EditMedia,
|
||||
EditCitation)
|
||||
if objclass == 'Person':
|
||||
person = self.dbstate.db.get_person_from_handle(handle)
|
||||
if person:
|
||||
@@ -1629,6 +1630,14 @@ class MultiTreeView(Gtk.TreeView):
|
||||
self.uistate, [], ref)
|
||||
except WindowActiveError:
|
||||
pass
|
||||
elif objclass == 'Citation':
|
||||
ref = self.dbstate.db.get_citation_from_handle(handle)
|
||||
if ref:
|
||||
try:
|
||||
EditCitation(self.dbstate,
|
||||
self.uistate, [], ref)
|
||||
except WindowActiveError:
|
||||
pass
|
||||
|
||||
def short(val,size=60):
|
||||
if len(val) > size:
|
||||
|
||||
+27
-21
@@ -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):
|
||||
|
||||
+22
-13
@@ -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
|
||||
|
||||
+1
-1
@@ -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])
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
<interface>
|
||||
<!-- interface-requires gtk+ 3.0 -->
|
||||
<object class="GtkDialog" id="clipboard">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title" translatable="yes">Clipboard</property>
|
||||
<property name="default_width">500</property>
|
||||
|
||||
@@ -391,7 +391,6 @@ You can use the mouse on the picture to select a region, or use these spinbutton
|
||||
<object class="GtkExpander" id="expander1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="expanded">True</property>
|
||||
<child>
|
||||
<object class="GtkNotebook" id="notebook_shared">
|
||||
<property name="visible">True</property>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+2
-2
@@ -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=""):
|
||||
|
||||
+15
-11
@@ -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')
|
||||
@@ -1080,7 +1095,7 @@ class GedcomWriter(UpdateCallback):
|
||||
|
||||
if event.get_place_handle():
|
||||
place = self.dbase.get_place_from_handle(event.get_place_handle())
|
||||
self._place(place, 2)
|
||||
self._place(place, dateobj, 2)
|
||||
|
||||
for attr in event.get_attribute_list():
|
||||
attr_type = attr.get_type()
|
||||
@@ -1151,8 +1166,8 @@ class GedcomWriter(UpdateCallback):
|
||||
if lds_ord.get_temple():
|
||||
self._writeln(index+1, 'TEMP', lds_ord.get_temple())
|
||||
if lds_ord.get_place_handle():
|
||||
self._place(
|
||||
self.dbase.get_place_from_handle(lds_ord.get_place_handle()), 2)
|
||||
place = self.dbase.get_place_from_handle(lds_ord.get_place_handle())
|
||||
self._place(place, lds_ord.get_date_object(), 2)
|
||||
if lds_ord.get_status() != LdsOrd.STATUS_NONE:
|
||||
self._writeln(2, 'STAT', LDS_STATUS[lds_ord.get_status()])
|
||||
|
||||
@@ -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)
|
||||
@@ -1350,7 +1371,7 @@ class GedcomWriter(UpdateCallback):
|
||||
|
||||
self._note_references(photo_obj.get_note_list(), level+1)
|
||||
|
||||
def _place(self, place, level):
|
||||
def _place(self, place, dateobj, level):
|
||||
"""
|
||||
PLACE_STRUCTURE:=
|
||||
n PLAC <PLACE_NAME> {1:1}
|
||||
@@ -1365,7 +1386,7 @@ class GedcomWriter(UpdateCallback):
|
||||
+1 <<NOTE_STRUCTURE>> {0:M}
|
||||
"""
|
||||
if place is None: return
|
||||
place_name = place_displayer.display(self.dbase, place)
|
||||
place_name = place_displayer.display(self.dbase, place, dateobj)
|
||||
self._writeln(level, "PLAC", place_name.replace('\r', ' '), limit=120)
|
||||
longitude = place.get_longitude()
|
||||
latitude = place.get_latitude()
|
||||
|
||||
@@ -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]))
|
||||
|
||||
@@ -693,7 +693,8 @@ class GrampsParser(UpdateCallback):
|
||||
"places": (None, self.stop_places),
|
||||
"placeobj": (self.start_placeobj, self.stop_placeobj),
|
||||
"placeref": (self.start_placeref, self.stop_placeref),
|
||||
"ptitle": (None, self.stop_ptitle),
|
||||
"ptitle": (None, self.stop_ptitle),
|
||||
"locality": (None, self.stop_locality),
|
||||
"location": (self.start_location, None),
|
||||
"lds_ord": (self.start_lds_ord, self.stop_lds_ord),
|
||||
"temple": (self.start_temple, None),
|
||||
|
||||
+401
-167
@@ -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"
|
||||
@@ -1900,6 +2004,7 @@ class GedcomParser(UpdateCallback):
|
||||
self.default_tag = None
|
||||
self.dir_path = os.path.dirname(filename)
|
||||
self.is_ftw = False
|
||||
self.addr_is_detail = False
|
||||
self.groups = None
|
||||
self.want_parse_warnings = True
|
||||
|
||||
@@ -2298,12 +2403,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 +2743,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 +2823,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,41 +2981,73 @@ 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.
|
||||
"""
|
||||
place = Place()
|
||||
|
||||
# check to see if we've encountered this name before
|
||||
# if we haven't we need to get a new GRAMPS ID
|
||||
Determines whether a location is empty.
|
||||
|
||||
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()
|
||||
@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, placeref_list):
|
||||
"""
|
||||
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)) and \
|
||||
place.get_placeref_list() == placeref_list:
|
||||
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) and \
|
||||
place.get_placeref_list() == placeref_list:
|
||||
return place
|
||||
return None
|
||||
|
||||
def __add_place(self, event, sub_state):
|
||||
"""
|
||||
Add a new place to an event if not already present, or update a
|
||||
place.
|
||||
|
||||
@param event: The event
|
||||
@type event: gen.lib.Event
|
||||
@param substate: The sub-state for PLAC or ADDR elements (i.e. parsed by
|
||||
event_parse_tbl)
|
||||
@type sub_state: CurrentState
|
||||
"""
|
||||
if sub_state.place:
|
||||
# see whether this place already exists
|
||||
place = self.__find_place(sub_state.place.get_title(),
|
||||
self.__get_first_loc(sub_state.place),
|
||||
sub_state.place.get_placeref_list())
|
||||
if place is None:
|
||||
place = sub_state.place
|
||||
self.dbase.add_place(place, self.trans)
|
||||
self.place_names[place.get_title()].append(place.get_handle())
|
||||
event.set_place_handle(place.get_handle())
|
||||
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
|
||||
return place
|
||||
place.merge(sub_state.place)
|
||||
self.dbase.commit_place(place, self.trans)
|
||||
event.set_place_handle(place.get_handle())
|
||||
place_title = place_displayer.display(self.dbase, place)
|
||||
sub_state.pf.load_place(self.place_import, place, place_title)
|
||||
|
||||
def __find_file(self, fullname, altpath):
|
||||
tries = []
|
||||
@@ -3321,7 +3465,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())
|
||||
@@ -3811,10 +3958,13 @@ class GedcomParser(UpdateCallback):
|
||||
sub_state.level = state.level+1
|
||||
sub_state.event = event
|
||||
sub_state.event_ref = event_ref
|
||||
sub_state.pf = self.place_parser
|
||||
|
||||
self.__parse_level(sub_state, self.event_parse_tbl, self.__undefined)
|
||||
state.msg += sub_state.msg
|
||||
|
||||
self.__add_place(event, sub_state)
|
||||
|
||||
self.dbase.commit_event(event, self.trans)
|
||||
event_ref.ref = event.handle
|
||||
state.person.add_event_ref(event_ref)
|
||||
@@ -4016,10 +4166,13 @@ class GedcomParser(UpdateCallback):
|
||||
sub_state.level = state.level+1
|
||||
sub_state.event = event
|
||||
sub_state.event_ref = event_ref
|
||||
sub_state.pf = self.place_parser
|
||||
|
||||
self.__parse_level(sub_state, self.event_parse_tbl, self.__undefined)
|
||||
state.msg += sub_state.msg
|
||||
|
||||
self.__add_place(event, sub_state)
|
||||
|
||||
self.dbase.add_event(event, self.trans)
|
||||
event_ref.ref = event.handle
|
||||
state.person.add_event_ref(event_ref)
|
||||
@@ -4466,9 +4619,16 @@ class GedcomParser(UpdateCallback):
|
||||
@type state: CurrentState
|
||||
"""
|
||||
try:
|
||||
state.place = self.__find_or_create_place(line.data)
|
||||
state.place.set_title(line.data)
|
||||
state.lds_ord.set_place_handle(state.place.handle)
|
||||
title = line.data
|
||||
place = self.__find_place(title, None, None)
|
||||
if place is None:
|
||||
place = Place()
|
||||
place.set_title(title)
|
||||
self.dbase.add_place(place, self.trans)
|
||||
self.place_names[place.get_title()].append(place.get_handle())
|
||||
else:
|
||||
pass
|
||||
state.lds_ord.set_place_handle(place.handle)
|
||||
except NameError:
|
||||
return
|
||||
|
||||
@@ -4832,10 +4992,13 @@ class GedcomParser(UpdateCallback):
|
||||
sub_state.level = state.level+1
|
||||
sub_state.event = event
|
||||
sub_state.event_ref = event_ref
|
||||
sub_state.pf = self.place_parser
|
||||
|
||||
self.__parse_level(sub_state, self.event_parse_tbl, self.__undefined)
|
||||
state.msg += sub_state.msg
|
||||
|
||||
self.__add_place(event, sub_state)
|
||||
|
||||
if event.type == EventType.MARRIAGE:
|
||||
descr = event.get_description()
|
||||
if descr == "Civil Union":
|
||||
@@ -4875,9 +5038,12 @@ class GedcomParser(UpdateCallback):
|
||||
sub_state.level = state.level+1
|
||||
sub_state.event = event
|
||||
sub_state.event_ref = event_ref
|
||||
sub_state.pf = self.place_parser
|
||||
|
||||
self.__parse_level(sub_state, self.event_parse_tbl, self.__undefined)
|
||||
state.msg += sub_state.msg
|
||||
|
||||
self.__add_place(event, sub_state)
|
||||
|
||||
self.dbase.commit_event(event, self.trans)
|
||||
event_ref.ref = event.handle
|
||||
@@ -5157,7 +5323,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,32 +5471,31 @@ 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
|
||||
place_handle = state.event.get_place_handle()
|
||||
if place_handle:
|
||||
place = self.dbase.get_place_from_handle(place_handle)
|
||||
title = line.data
|
||||
place = state.place
|
||||
if place:
|
||||
# We encounter a PLAC, having previously encountered an ADDR
|
||||
if place.get_title() and place.get_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
|
||||
else:
|
||||
# This is the first PLAC
|
||||
place.set_title(line.data)
|
||||
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
|
||||
state.place = Place()
|
||||
place = state.place
|
||||
place.set_title(line.data)
|
||||
|
||||
sub_state = CurrentState()
|
||||
sub_state.place = place
|
||||
sub_state.level = state.level+1
|
||||
sub_state.pf = PlaceParser()
|
||||
|
||||
self.__parse_level(sub_state, self.event_place_map,
|
||||
self.__undefined)
|
||||
state.msg += sub_state.msg
|
||||
|
||||
place_title = place_displayer.display(self.dbase, place)
|
||||
sub_state.pf.load_place(self.place_import, place, place_title)
|
||||
|
||||
self.dbase.commit_place(place, self.trans)
|
||||
|
||||
def __event_place_note(self, line, state):
|
||||
"""
|
||||
@param line: The current line in GedLine format
|
||||
@@ -5430,8 +5595,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,22 +5604,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)
|
||||
|
||||
if self.addr_is_detail and state.place:
|
||||
# Commit the enclosing place
|
||||
place = self.__find_place(state.place.get_title(), None,
|
||||
state.place.get_placeref_list())
|
||||
if place is None:
|
||||
place = state.place
|
||||
self.dbase.add_place(place, self.trans)
|
||||
self.place_names[place.get_title()].append(place.get_handle())
|
||||
else:
|
||||
place.merge(state.place)
|
||||
self.dbase.commit_place(place, self.trans)
|
||||
place_title = place_displayer.display(self.dbase, place)
|
||||
state.pf.load_place(self.place_import, place, place_title)
|
||||
|
||||
# Create the Place Details (it is committed with the event)
|
||||
place_detail = Place()
|
||||
place_detail.set_name(location.get_street())
|
||||
place_detail.set_title(location.get_street())
|
||||
# For RootsMagic etc. Place Details e.g. address, hospital, cemetary
|
||||
place_detail.set_type((PlaceType.CUSTOM, _("Detail")))
|
||||
placeref = PlaceRef()
|
||||
placeref.ref = place.get_handle()
|
||||
place_detail.set_placeref_list([placeref])
|
||||
state.place = place_detail
|
||||
else:
|
||||
place = self.__find_or_create_place(line.data)
|
||||
place.set_title(line.data)
|
||||
place_handle = place.handle
|
||||
place = state.place
|
||||
if place:
|
||||
# We encounter an ADDR having previously encountered a PLAC
|
||||
if len(place.get_alternate_locations()) != 0 and \
|
||||
not self.__get_first_loc(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
|
||||
else:
|
||||
# This is the first ADDR
|
||||
place.add_alternate_locations(location)
|
||||
else:
|
||||
# The first thing we encounter is ADDR
|
||||
state.place = Place()
|
||||
place = state.place
|
||||
place.add_alternate_locations(location)
|
||||
|
||||
self.__add_location(place, location)
|
||||
|
||||
list(map(place.add_note, note_list))
|
||||
|
||||
state.event.set_place_handle(place_handle)
|
||||
self.dbase.commit_place(place, self.trans)
|
||||
# merge notes etc into place
|
||||
place.merge(sub_state.place)
|
||||
|
||||
def __add_location(self, place, location):
|
||||
"""
|
||||
@@ -5468,6 +5664,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
|
||||
@@ -5475,12 +5683,10 @@ class GedcomParser(UpdateCallback):
|
||||
@param state: The current state
|
||||
@type state: CurrentState
|
||||
"""
|
||||
place_handle = state.event.get_place_handle()
|
||||
if place_handle:
|
||||
place = self.dbase.get_place_from_handle(place_handle)
|
||||
place = state.place
|
||||
if place:
|
||||
codes = [place.get_code(), line.data]
|
||||
place.set_code(' '.join(code for code in codes if code))
|
||||
self.dbase.commit_place(place, self.trans)
|
||||
|
||||
def __event_privacy(self, line, state):
|
||||
"""
|
||||
@@ -5626,7 +5832,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 +5853,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 +6841,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 +6909,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 +6918,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):
|
||||
@@ -6793,6 +6998,11 @@ class GedcomParser(UpdateCallback):
|
||||
self.gedsource = self.gedmap.get_from_source_tag(line.data)
|
||||
if line.data.strip() in ["FTW", "FTM"]:
|
||||
self.is_ftw = True
|
||||
# Some software (e.g. RootsMagic (http://files.rootsmagic.com/PAF-
|
||||
# Book/RootsMagic-for-PAF-Users-Printable.pdf) use the Addr fields for
|
||||
# 'Place Details (address, hospital, cemetary)'
|
||||
if line.data.strip().lower() in ['rootsmagic']:
|
||||
self.addr_is_detail = True
|
||||
# We will use the approved system ID as the name of the generating
|
||||
# software, in case we do not get the name in the proper place
|
||||
self.genby = line.data
|
||||
@@ -7063,8 +7273,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 +7499,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):
|
||||
"""
|
||||
@@ -7438,9 +7654,13 @@ class GedcomParser(UpdateCallback):
|
||||
sub_state.event_ref = event_ref
|
||||
sub_state.event = event
|
||||
sub_state.person = state.person
|
||||
sub_state.pf = self.place_parser
|
||||
|
||||
self.__parse_level(sub_state, event_map, self.__undefined)
|
||||
state.msg += sub_state.msg
|
||||
|
||||
self.__add_place(event, sub_state)
|
||||
|
||||
self.dbase.commit_event(event, self.trans)
|
||||
|
||||
event_ref.set_reference_handle(event.handle)
|
||||
@@ -7462,10 +7682,13 @@ class GedcomParser(UpdateCallback):
|
||||
sub_state.level = state.level+1
|
||||
sub_state.event = event
|
||||
sub_state.event_ref = event_ref
|
||||
sub_state.pf = self.place_parser
|
||||
|
||||
self.__parse_level(sub_state, event_map, self.__undefined)
|
||||
state.msg += sub_state.msg
|
||||
|
||||
self.__add_place(event, sub_state)
|
||||
|
||||
self.dbase.commit_event(event, self.trans)
|
||||
event_ref.set_reference_handle(event.handle)
|
||||
return event_ref
|
||||
@@ -7609,7 +7832,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 +7853,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 +7889,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)
|
||||
@@ -380,9 +392,7 @@ class GeoGraphyView(OsmGps, NavigationView):
|
||||
clearmap.show()
|
||||
menu.append(clearmap)
|
||||
menu.show()
|
||||
menu.popup(None, None,
|
||||
lambda menu, data: (event.get_root_coords()[0],
|
||||
event.get_root_coords()[1], True),
|
||||
menu.popup(None, None, None,
|
||||
None, event.button, event.time)
|
||||
return 1
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ from gramps.gen.lib import PlaceType
|
||||
|
||||
class OpensStreetMapService(MapService):
|
||||
"""Map service using http://openstreetmap.org
|
||||
Resource: http://wiki.openstreetmap.org/index.php/Name_finder
|
||||
Resource: http://wiki.openstreetmap.org/wiki/Nominatim
|
||||
"""
|
||||
def __init__(self):
|
||||
MapService.__init__(self)
|
||||
@@ -64,10 +64,10 @@ class OpensStreetMapService(MapService):
|
||||
city = location.get(PlaceType.CITY)
|
||||
country = location.get(PlaceType.COUNTRY)
|
||||
if city and country:
|
||||
self.url = "http://open.mapquestapi.com/nominatim/v1/"\
|
||||
self.url = "http://nominatim.openstreetmap.org/"\
|
||||
"search.php?q=%s%%2C%s" % (city, country)
|
||||
return
|
||||
|
||||
titledescr = place_displayer.display(self.database, place)
|
||||
self.url = "http://open.mapquestapi.com/nominatim/v1/"\
|
||||
self.url = "http://nominatim.openstreetmap.org/"\
|
||||
"search.php?q=%s" % '+'.join(titledescr.split())
|
||||
|
||||
@@ -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" '
|
||||
@@ -1939,6 +1949,7 @@ class CheckIntegrity(object):
|
||||
source = Source()
|
||||
source.unserialize(info)
|
||||
new_media_ref_list = []
|
||||
citation_changed = False
|
||||
for media_ref in source.get_media_list():
|
||||
citation_list = media_ref.get_citation_list()
|
||||
new_citation_list = []
|
||||
@@ -1961,6 +1972,7 @@ class CheckIntegrity(object):
|
||||
citation_handle = create_id()
|
||||
new_citation.set_handle(citation_handle)
|
||||
self.replaced_sourceref.append(handle)
|
||||
citation_changed = True
|
||||
logging.warning(' FAIL: the source "%s" has a media '
|
||||
'reference with a source citation '
|
||||
'which is invalid' % (source.gramps_id))
|
||||
@@ -1970,9 +1982,10 @@ class CheckIntegrity(object):
|
||||
|
||||
media_ref.set_citation_list(new_citation_list)
|
||||
new_media_ref_list.append(media_ref)
|
||||
|
||||
source.set_media_list(new_media_ref_list)
|
||||
self.db.commit_source(source, self.trans)
|
||||
|
||||
if citation_changed:
|
||||
source.set_media_list(new_media_ref_list)
|
||||
self.db.commit_source(source, self.trans)
|
||||
|
||||
if len(self.replaced_sourceref) > 0:
|
||||
logging.info(' OK: no broken source citations on mediarefs found')
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<interface>
|
||||
<!-- interface-requires gtk+ 3.0 -->
|
||||
<object class="GtkDialog" id="relcalc">
|
||||
<property name="visible">True</property>
|
||||
<property name="visible">False</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="default_width">600</property>
|
||||
<property name="default_height">400</property>
|
||||
@@ -91,6 +91,7 @@
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<property name="min_content_height">75</property>
|
||||
<child>
|
||||
<object class="GtkTextView" id="text1">
|
||||
<property name="height_request">75</property>
|
||||
|
||||
@@ -98,7 +98,7 @@
|
||||
<property name="receives_default">False</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="halign">3</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -116,7 +116,7 @@
|
||||
<property name="receives_default">False</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="halign">3</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -134,7 +134,7 @@
|
||||
<property name="receives_default">False</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="halign">3</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -152,7 +152,7 @@
|
||||
<property name="receives_default">False</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="halign">3</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -170,7 +170,7 @@
|
||||
<property name="receives_default">False</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="halign">3</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -188,7 +188,7 @@
|
||||
<property name="receives_default">False</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="halign">3</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
|
||||
@@ -129,11 +129,16 @@ class TestcaseGenerator(tool.BatchTool):
|
||||
|
||||
def __init__(self, dbstate, user, options_class, name, callback=None):
|
||||
uistate = user.uistate
|
||||
if uistate:
|
||||
self.parent_window = uistate.window
|
||||
else:
|
||||
self.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=self.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,8 @@ class TestcaseGenerator(tool.BatchTool):
|
||||
while Gtk.events_pending():
|
||||
Gtk.main_iteration()
|
||||
|
||||
self.progress = ProgressMeter(_('Generating testcases'),'')
|
||||
self.progress = ProgressMeter(_('Generating testcases'),'',
|
||||
parent=self.parent_window)
|
||||
self.transaction_count = 0;
|
||||
|
||||
if self.options.handler.options_dict['lowlevel']:
|
||||
|
||||
@@ -310,7 +310,7 @@ class Verify(tool.Tool, ManagedWindow, UpdateCallback):
|
||||
self.top.get_object(option).set_value(
|
||||
self.options.handler.options_dict[option]
|
||||
)
|
||||
self.window.show()
|
||||
self.show()
|
||||
|
||||
def build_menu_names(self, obj):
|
||||
return (_("Tool settings"),self.label)
|
||||
@@ -539,7 +539,7 @@ class VerifyResults(ManagedWindow):
|
||||
name_column.set_sort_column_id(VerifyResults.OBJ_NAME_COL)
|
||||
self.warn_tree.append_column(name_column)
|
||||
|
||||
self.window.show()
|
||||
self.show()
|
||||
self.window_shown = False
|
||||
|
||||
def __dummy(self, obj):
|
||||
|
||||
@@ -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 "
|
||||
@@ -535,9 +535,7 @@ class GeoClose(GeoGraphyView):
|
||||
event, lat, lon, prevmark)
|
||||
itemoption.append(center)
|
||||
menu.show()
|
||||
menu.popup(None, None,
|
||||
lambda menu, data: (event.get_root_coords()[0],
|
||||
event.get_root_coords()[1], True),
|
||||
menu.popup(None, None, None,
|
||||
None, event.button, event.time)
|
||||
return 0
|
||||
|
||||
|
||||
@@ -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,11 +367,9 @@ 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],
|
||||
event.get_root_coords()[1], True),
|
||||
menu.popup(None, None, None,
|
||||
None, event.button, event.time)
|
||||
return 1
|
||||
|
||||
|
||||
@@ -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 "
|
||||
@@ -678,9 +678,7 @@ class GeoFamClose(GeoGraphyView):
|
||||
event, lat, lon, prevmark)
|
||||
itemoption.append(center)
|
||||
menu.show()
|
||||
menu.popup(None, None,
|
||||
lambda menu, data: (event.get_root_coords()[0],
|
||||
event.get_root_coords()[1], True),
|
||||
menu.popup(None, None, None,
|
||||
None, event.button, event.time)
|
||||
return 0
|
||||
|
||||
|
||||
@@ -446,9 +446,7 @@ class GeoFamily(GeoGraphyView):
|
||||
add_item.show()
|
||||
menu.append(add_item)
|
||||
self.add_event_bubble_message(event, lat, lon, prevmark, add_item)
|
||||
menu.popup(None, None,
|
||||
lambda menu, data: (event.get_root_coords()[0],
|
||||
event.get_root_coords()[1], True),
|
||||
menu.popup(None, None, None,
|
||||
None, event.button, event.time)
|
||||
return 1
|
||||
|
||||
|
||||
@@ -607,12 +607,10 @@ 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,
|
||||
lambda menu, data: (event.get_root_coords()[0],
|
||||
event.get_root_coords()[1], True),
|
||||
menu.popup(None, None, None,
|
||||
None, event.button, event.time)
|
||||
return 1
|
||||
|
||||
|
||||
@@ -484,9 +484,7 @@ class GeoPerson(GeoGraphyView):
|
||||
center.connect("activate", self.center_here, event, lat, lon, prevmark)
|
||||
itemoption.append(center)
|
||||
menu.show()
|
||||
menu.popup(None, None,
|
||||
lambda menu, data: (event.get_root_coords()[0],
|
||||
event.get_root_coords()[1], True),
|
||||
menu.popup(None, None, None,
|
||||
None, event.button, event.time)
|
||||
return 1
|
||||
|
||||
|
||||
@@ -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,11 +357,9 @@ 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],
|
||||
event.get_root_coords()[1], True),
|
||||
menu.popup(None, None, None,
|
||||
None, event.button, event.time)
|
||||
return 1
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -987,7 +987,7 @@ class PedigreeView(NavigationView):
|
||||
rela = lst[2*i+1][1]
|
||||
line = LineWidget2(1, rela, self.tree_direction)
|
||||
|
||||
if lst[i] and lst[i][2]:
|
||||
if lst[((i+1) // 2) - 1] and lst[((i+1) // 2) - 1][2]:
|
||||
# Required for popup menu
|
||||
line.add_events(Gdk.EventMask.BUTTON_PRESS_MASK)
|
||||
line.connect("button-press-event",
|
||||
|
||||
@@ -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):
|
||||
|
||||
+1
-1
@@ -18,6 +18,6 @@
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
VERSION_TUPLE = (4, 1, 2)
|
||||
VERSION_TUPLE = (4, 1, 4)
|
||||
VERSION = '.'.join(map(str,VERSION_TUPLE))
|
||||
major_version = "%s.%s" % (VERSION_TUPLE[0], VERSION_TUPLE[1])
|
||||
|
||||
+4
-4
@@ -7,7 +7,7 @@
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>Gramps</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>4.1.1, (C) 1997-2014 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,13 +17,13 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>4.1.1-1</string>
|
||||
<string>4.1.3-1</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>4.1.1-1</string>
|
||||
<string>4.1.3-1</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright 1997 - 2014 The Gramps Team, GNU General Public License.</string>
|
||||
<string>Copyright 1997 - 2015 The Gramps Team, GNU General Public License.</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.5</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user