Compare commits
68 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6ca3821f81 | |||
| caf47940c7 | |||
| 6a78d5196d | |||
| fc0fdec25c | |||
| 2adcc7d5a3 | |||
| fcaea3e1bb | |||
| fc99f12568 | |||
| 1e5a6e1f87 | |||
| db263ff905 | |||
| e202f5cd19 | |||
| 2fbb9878c2 | |||
| ba4453213a | |||
| 5f133ffaf2 | |||
| cbac98894b | |||
| 3f368b2e02 | |||
| 1e73ab5be7 | |||
| f428069ac6 | |||
| adf3772b7a | |||
| 46cd191e49 | |||
| 9afe0cc181 | |||
| d3ed320636 | |||
| 6f4a114fdc | |||
| 5bec86c1f4 | |||
| 5b48691c3a | |||
| be11466b35 | |||
| 0445004e7d | |||
| f688d376f5 | |||
| 0a38a6352f | |||
| 2410435d8c | |||
| 6f1d6fa6b4 | |||
| b1db542cb9 | |||
| 3d7479969f | |||
| 8f4dffa1df | |||
| ffe9f31b0b | |||
| b1af1c5435 | |||
| 94b6fcb93f | |||
| 4b25dde6e2 | |||
| f83c077a7e | |||
| 323d775541 | |||
| 4448234281 | |||
| d31ced9ca5 | |||
| 9706af03b4 | |||
| b3ca17a90f | |||
| 4b13c95467 | |||
| 7864af3626 | |||
| 028f9582f5 | |||
| cd32559282 | |||
| 3980fca903 | |||
| 462b0ea20b | |||
| ba4b3eaef6 | |||
| bf826637ab | |||
| 316ff4454a | |||
| 3266625669 | |||
| 90378f19c5 | |||
| 78fb08aa12 | |||
| d081b75993 | |||
| 10e0e64ff4 | |||
| 3615e75d16 | |||
| 3c8cba0301 | |||
| dde9ea8a90 | |||
| b7c39e51c7 | |||
| a216441029 | |||
| 89592545f3 | |||
| 70fff6253d | |||
| 84e76de2b1 | |||
| fe289da983 | |||
| 371a8a8f89 | |||
| 1777cd4b29 |
@@ -23,8 +23,5 @@ test/data
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
|
||||
# Glade temp files
|
||||
*~
|
||||
|
||||
+6
-6
@@ -72,7 +72,7 @@ install:
|
||||
# by the automatic git checkout.
|
||||
|
||||
# Download Sean Ross-Ross's Pure Python module containing a framework to
|
||||
# manipulate and analyze python ast�s and bytecode. This is loaded to
|
||||
# manipulate and analyze python ast�s and bytecode. This is loaded to
|
||||
# /home/travis/build/gramps-project/gramps/meta
|
||||
# FIXME: This should be loaded from the release directory at
|
||||
# https://pypi.python.org/pypi/meta
|
||||
@@ -95,11 +95,11 @@ before_script:
|
||||
# set GRAMPS_RESOURCES for locale, data,image and documentation
|
||||
- export GRAMPS_RESOURCES=.
|
||||
# Install addons
|
||||
- mkdir -p ~/.gramps/gramps51/plugins/
|
||||
- wget https://github.com/gramps-project/addons/raw/master/gramps51/download/CliMerge.addon.tgz
|
||||
- tar -C ~/.gramps/gramps51/plugins -xzf CliMerge.addon.tgz
|
||||
- wget https://github.com/gramps-project/addons/raw/master/gramps51/download/ExportRaw.addon.tgz
|
||||
- tar -C ~/.gramps/gramps51/plugins -xzf ExportRaw.addon.tgz
|
||||
- mkdir -p ~/.gramps/gramps50/plugins/
|
||||
- wget https://github.com/gramps-project/addons/raw/master/gramps50/download/CliMerge.addon.tgz
|
||||
- tar -C ~/.gramps/gramps50/plugins -xzf CliMerge.addon.tgz
|
||||
- wget https://github.com/gramps-project/addons/raw/master/gramps50/download/ExportRaw.addon.tgz
|
||||
- tar -C ~/.gramps/gramps50/plugins -xzf ExportRaw.addon.tgz
|
||||
|
||||
script:
|
||||
# Ignore the virtualenv entirely. Use nosetests3, python3 (3.4.0) and coverage
|
||||
|
||||
+3
-1
@@ -1,5 +1,5 @@
|
||||
include AUTHORS
|
||||
include ChangeLog
|
||||
include AUTHORS
|
||||
include COPYING
|
||||
include FAQ
|
||||
include Gramps.py
|
||||
@@ -10,9 +10,11 @@ include RELEASE_NOTES
|
||||
include TODO
|
||||
include CONTRIBUTING
|
||||
include TestPlan.txt
|
||||
recursive-include bash *
|
||||
recursive-include data *
|
||||
recursive-include debian *
|
||||
recursive-include docs *
|
||||
recursive-include example *
|
||||
recursive-include gramps *
|
||||
recursive-include help *
|
||||
recursive-include images *
|
||||
|
||||
@@ -16,14 +16,15 @@ The following packages **MUST** be installed in order for Gramps to work:
|
||||
|
||||
The following three packages with GObject Introspection bindings (the gi packages)
|
||||
|
||||
* **cairo** - a 2D graphics library with support for multiple output devices. http://cairographics.org/
|
||||
* **cairo** 1.13.1 or greater - a 2D graphics library with support for multiple output devices. http://cairographics.org/
|
||||
* **Pycairo** 1.13.3 or greater - GObject Introspection bindings for cairo. https://github.com/pygobject/pycairo
|
||||
* **pango** - a library for laying out and rendering of text, with an emphasis on internationalization. http://www.pango.org/
|
||||
* **pangocairo** - Allows you to use Pango with Cairo http://www.pango.org/
|
||||
|
||||
* **librsvg2** - (SVG icon view) a library to render SVG files using cairo. http://live.gnome.org/LibRsvg
|
||||
* **xdg-utils** - Desktop integration utilities from freedesktop.org
|
||||
* **bsddb3** - Python bindings for Oracle Berkeley DB https://pypi.python.org/pypi/bsddb3/
|
||||
|
||||
* **sqlite3** - Python bindings for SQLite Database library
|
||||
|
||||
The following package is needed for full translation of the interface
|
||||
to your language:
|
||||
@@ -44,7 +45,7 @@ The following packages are **STRONGLY RECOMMENDED** to be installed:
|
||||
It may be osmgpsmap, osm-gps-map, or python-osmgpsmap,
|
||||
but the Python bindings for this must also be present, so gir1.2-osmgpsmap-1.0.
|
||||
Without this the GeoView will not be active, see
|
||||
https://gramps-project.org/wiki/index.php?title=Gramps_4.2_Wiki_Manual_-_Categories#Geography_Category
|
||||
https://gramps-project.org/wiki/index.php?title=Gramps_5.0_Wiki_Manual_-_Categories#Geography_Category
|
||||
|
||||
* **Graphviz**
|
||||
|
||||
@@ -65,6 +66,10 @@ The following packages are **STRONGLY RECOMMENDED** to be installed:
|
||||
(These are Python bindings for the ICU package.
|
||||
https://pypi.python.org/pypi/PyICU/)
|
||||
|
||||
* **Ghostscript**
|
||||
|
||||
Used by Graphviz reports to help create PDF's
|
||||
|
||||
The following packages are optional:
|
||||
------------------------------------
|
||||
* **gtkspell**
|
||||
@@ -77,12 +82,12 @@ The following packages are optional:
|
||||
|
||||
The GNU Revision Control System (RCS) can be used to manage
|
||||
multiple revisions of your family trees. See info at
|
||||
https://gramps-project.org/wiki/index.php?title=Gramps_4.2_Wiki_Manual_-_Manage_Family_Trees#Archiving_a_Family_Tree
|
||||
https://gramps-project.org/wiki/index.php?title=Gramps_5.0_Wiki_Manual_-_Manage_Family_Trees#Archiving_a_Family_Tree
|
||||
Only rcs is needed, NO python bindings are required
|
||||
|
||||
* **PIL**
|
||||
|
||||
Python Image Library is needed to crop
|
||||
Python Image Library (PILLOW) is needed to crop
|
||||
images and also to convert non-JPG images to
|
||||
JPG so as to include them in LaTeX output.
|
||||
(For Python3 a different source may be needed,
|
||||
@@ -98,18 +103,6 @@ The following packages are optional:
|
||||
|
||||
More font support in the reports
|
||||
|
||||
* **geocodeglib**
|
||||
|
||||
A library use to associate a geographical position (latitude, longitude)
|
||||
to a place name. This is used if you already have osmgpsmap installed.
|
||||
If installed, when you add or link a place from the map, you have a red line
|
||||
at the end of the table for selection.
|
||||
Debian, Ubuntu, ... : gir1.2-geocodeglib-1.0
|
||||
Fedora, Redhat, ... : geocode-glib
|
||||
openSUSE : geocode-glib
|
||||
ArchLinux : geocode-glib
|
||||
...
|
||||
|
||||
Optional packages required by Third-party Addons
|
||||
------------------------------------------------
|
||||
|
||||
@@ -124,6 +117,9 @@ Prerequistes required for the following Addons to work:
|
||||
* **Graph View** - Requires: PyGoocanvas and Goocanvas (python-pygoocanvas, gir1.2-goocanvas-2.0).
|
||||
( https://gramps-project.org/wiki/index.php?title=Graph_View )
|
||||
|
||||
* **Network Chart** - Requires: networkx and pygraphviz
|
||||
( https://gramps-project.org/wiki/index.php?title=NetworkChart )
|
||||
|
||||
* **PedigreeChart** - Can optionally use - numpy if installed
|
||||
( https://gramps-project.org/wiki/index.php?title=PedigreeChart )
|
||||
|
||||
|
||||
@@ -288,10 +288,6 @@ table.infolist thead tr th {
|
||||
table.infolist tr td {
|
||||
border-bottom: dashed 1px #000;
|
||||
vertical-align: middle;
|
||||
padding: 6px 0 6px 10px;
|
||||
}
|
||||
table.infolist tr td a {
|
||||
display: block;
|
||||
}
|
||||
table.infolist tr.BeginLetter td, table.infolist tr.BeginSurname td {
|
||||
border-top: solid 1px #000;
|
||||
@@ -351,15 +347,15 @@ div#Individuals {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
div#Individuals table.individuallist {
|
||||
div#Individuals table.IndividualList {
|
||||
border-bottom: solid 1px #000;
|
||||
}
|
||||
div#Individuals table.individuallist tbody tr td.ColumnSurname a:hover,
|
||||
div#Individuals table.individuallist tbody tr td.ColumnSurname a:active {
|
||||
div#Individuals table.IndividualList tbody tr td.ColumnSurname a:hover,
|
||||
div#Individuals table.IndividualList tbody tr td.ColumnSurname a:active {
|
||||
cursor: default;
|
||||
background: none;
|
||||
}
|
||||
div#Individuals table.individuallist tbody tr td.ColumnName a {
|
||||
div#Individuals table.IndividualList tbody tr td.ColumnName a {
|
||||
vertical-align: middle;
|
||||
}
|
||||
div#Individuals div table.infolist tr td p {
|
||||
@@ -1129,9 +1125,10 @@ div.narrative {
|
||||
}
|
||||
.narrative p {
|
||||
font: normal .9em/1.4em sans-serif;
|
||||
margin-top: .5em;
|
||||
margin-bottom: 0;
|
||||
padding: 0 20px 1em 20px;
|
||||
margin: 0.1em 0 0.2em 0;
|
||||
}
|
||||
i + div.grampsstylednote p {
|
||||
margin: 0.1em 0 0.2em 0;
|
||||
}
|
||||
|
||||
/* Subsections : References
|
||||
|
||||
+12
-12
@@ -276,7 +276,6 @@ table.infolist tr th a:hover {
|
||||
table.infolist tr td {
|
||||
font:normal 1.1em/1.4em serif;
|
||||
vertical-align:middle;
|
||||
padding:.1em 10px;
|
||||
}
|
||||
table.infolist tr td a {
|
||||
display:block;
|
||||
@@ -396,30 +395,30 @@ table.surname thead tr th.ColumnParents, table.surname tbody tr td.ColumnParents
|
||||
/* Individuals
|
||||
----------------------------------------------------- */
|
||||
#Individuals { }
|
||||
#Individuals table.individuallist {
|
||||
#Individuals table.IndividualList {
|
||||
border-bottom:solid 1px #A97;
|
||||
}
|
||||
#Individuals table.individuallist tbody tr td {
|
||||
#Individuals table.IndividualList tbody tr td {
|
||||
border-bottom:dashed 1px #C1B398;
|
||||
}
|
||||
#Individuals table.individuallist tbody tr td a:hover {
|
||||
#Individuals table.IndividualList tbody tr td a:hover {
|
||||
text-decoration:none;
|
||||
}
|
||||
table.individuallist tbody tr td.ColumnSurname a:hover, table.individuallist tbody tr td.ColumnSurname a:active {
|
||||
table.IndividualList tbody tr td.ColumnSurname a:hover, table.IndividualList tbody tr td.ColumnSurname a:active {
|
||||
cursor:default;
|
||||
color:black;
|
||||
background:none;
|
||||
}
|
||||
table.individuallist tbody tr td.ColumnName {
|
||||
table.IndividualList tbody tr td.ColumnName {
|
||||
padding:0;
|
||||
background-color:#FFF;
|
||||
}
|
||||
table.individuallist tbody tr td.ColumnName a {
|
||||
table.IndividualList tbody tr td.ColumnName a {
|
||||
display:block;
|
||||
padding:.6em 10px;
|
||||
padding:.1em .1em;
|
||||
vertical-align:middle;
|
||||
}
|
||||
table.individuallist tbody tr td.ColumnName a:hover {
|
||||
table.IndividualList tbody tr td.ColumnName a:hover {
|
||||
background-color:#C1B398;
|
||||
}
|
||||
#Individuals div table.infolist tr td p {
|
||||
@@ -960,11 +959,12 @@ div#Addresses table.infolist tr td a, div#Addresses table.infolist tr td p a {
|
||||
div.narrative {
|
||||
padding-bottom:0;
|
||||
}
|
||||
i + div.grampsstylednote p {
|
||||
margin: 0.1em 0 0.2em 0;
|
||||
}
|
||||
.narrative p {
|
||||
margin: 0.1em 0 0.2em 0;
|
||||
font:normal .9em/1.4em sans-serif;
|
||||
margin-top:.5em;
|
||||
margin-bottom:0;
|
||||
padding:0 20px 1em 20px;
|
||||
}
|
||||
|
||||
/* Subsections : References
|
||||
|
||||
@@ -454,30 +454,30 @@ div#Individuals {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
div#Individuals table.individuallist {
|
||||
div#Individuals table.IndividualList {
|
||||
border-bottom: solid 1px #5D835F;
|
||||
}
|
||||
div#Individuals table.individuallist tbody tr td {
|
||||
div#Individuals table.IndividualList tbody tr td {
|
||||
border-bottom: dashed 1px #5D835F;
|
||||
background-color: #D8F3D6;
|
||||
}
|
||||
div#Individuals table.individuallist tbody tr td a {
|
||||
div#Individuals table.IndividualList tbody tr td a {
|
||||
display: block;
|
||||
padding: .6em 10px;
|
||||
}
|
||||
div#Individuals table.individuallist tbody tr td.ColumnSurname a:hover,
|
||||
div#Individuals table.individuallist tbody tr td.ColumnSurname a:active {
|
||||
div#Individuals table.IndividualList tbody tr td.ColumnSurname a:hover,
|
||||
div#Individuals table.IndividualList tbody tr td.ColumnSurname a:active {
|
||||
cursor:default;
|
||||
color: #000;
|
||||
background:none;
|
||||
}
|
||||
div#Individuals table.individuallist tbody tr td.ColumnName {
|
||||
div#Individuals table.IndividualList tbody tr td.ColumnName {
|
||||
background-color: #FFF;
|
||||
}
|
||||
div#Individuals table.individuallist tbody tr td.ColumnName a {
|
||||
div#Individuals table.IndividualList tbody tr td.ColumnName a {
|
||||
vertical-align:middle;
|
||||
}
|
||||
div#Individuals table.individuallist tbody tr td.ColumnPartner {
|
||||
div#Individuals table.IndividualList tbody tr td.ColumnPartner {
|
||||
background-color: #FFF;
|
||||
}
|
||||
div#Individuals div table.infolist tr td p {
|
||||
|
||||
@@ -33,7 +33,9 @@ body#FamilyMap {
|
||||
border: solid 4px #000;
|
||||
margin: 0px auto;
|
||||
width: 800px;
|
||||
height: 800px;
|
||||
height: 400px;
|
||||
max-width: 90%;
|
||||
max-height: 90%;
|
||||
}
|
||||
|
||||
/* Place Maps
|
||||
@@ -43,6 +45,8 @@ div#place_canvas {
|
||||
border: solid 4px #000;
|
||||
width: 500px;
|
||||
height: 400px;
|
||||
max-width: 90%;
|
||||
max-height: 90%;
|
||||
}
|
||||
button#drop {
|
||||
background-color: purple;
|
||||
|
||||
@@ -201,12 +201,12 @@
|
||||
<date name="Pääsiäispäivä" type="religious" value="> easter(y)" />
|
||||
<date name="2. Pääsiäispäivä" type="religious" value="> easter(y)" offset="1" />
|
||||
<date name="Helatorstai" type="religious" value="> easter(y)" offset="39" if="not(y>=1973)" />
|
||||
<date name="Kristuksen taivaaseenastumisen päivä." type="religious" value="> easter(y)" offset="34" if="(y>=1973) and not(y>=1992)" />
|
||||
<date name="Kristuksen taivaaseenastumisen päivä" type="religious" value="> easter(y)" offset="34" if="(y>=1973) and not(y>=1992)" />
|
||||
<date name="Helatorstai" type="religious" value="> easter(y)" offset="39" if="(y>=1992)" />
|
||||
<date name="Helluntaipäivä" value="easter(y)" offset="49" type="religious" />
|
||||
<date name="Pyhän Kolminaisuuden päivä" value="easter(y)" offset="56" type="religious" />
|
||||
<date name="Apostolienpäivä" value="easter(y)" offset="91" type="religious" />
|
||||
<date name="Kirkastussunnuntai." value="easter(y)" offset="105" type="religious" />
|
||||
<date name="Kirkastussunnuntai" value="easter(y)" offset="105" type="religious" />
|
||||
<date name="Juhannusaatto" value="*/jun/19" offset="fri" type="religious" if="(y>=1955)" />
|
||||
<date name="Juhannusaatto" value="*/jun/23" type="religious" if="not(y>=1955)" />
|
||||
<date name="Juhannuspäivä" value="*/jun/20" offset="sat" type="religious" if="(y>=1955)" />
|
||||
@@ -224,12 +224,12 @@
|
||||
<date name="Joulupäivä" value="*/12/25" type="religious" />
|
||||
<date name="Tapaninpäivä" value="*/12/26" type="religious" />
|
||||
<date name="Johannes Evankelistan päivä" value="*/dec/27" type="religious" if="not(y>=1774)" />
|
||||
<date name="Apostoli Johanneksen päivä." value="*/dec/27" type="religious" if="dow(y, m, d) == 7 and (y>=2000)" />
|
||||
<date name="Apostoli Johanneksen päivä" value="*/dec/27" type="religious" if="dow(y, m, d) == 7 and (y>=2000)" />
|
||||
<date name="Viattomien lasten päivä" value="*/12/28" type="religious" />
|
||||
<date name="Uudenvuodenpäivä" value="*/1/1" type="national" />
|
||||
<date name="Vainojen uhrien muistopäivä" value="*/1/27" type="national" if="(y>=2002)" />
|
||||
<date name="J.L. Runebergin päivä" value="*/2/5" type="national" if="(y>=1854)" />
|
||||
<date name="Saamelaisten kansallis päivä" value="*/2/6" type="religious" if="(y>=2004)" />
|
||||
<date name="Saamelaisten kansallispäivä" value="*/2/6" type="religious" if="(y>=2004)" />
|
||||
<date name="Ystävänpäivä" value="*/2/14" type="national" if="(y>=1987)" />
|
||||
<date name="Kalevalan päivä" value="*/2/28" type="national" if="(y>=1978)" />
|
||||
<date name="Naistenpäivä" value="*/3/8" type="national" if="(y>=1975)" />
|
||||
|
||||
+504
-725
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@
|
||||
"http://gramps-project.org/xml/1.7.1/grampsxml.dtd">
|
||||
<database xmlns="http://gramps-project.org/xml/1.7.1/">
|
||||
<header>
|
||||
<created date="2017-08-08" version="5.1.0"/>
|
||||
<created date="2016-08-31" version="5.0.0"/>
|
||||
<researcher>
|
||||
<resname>Alex Roitman,,,</resname>
|
||||
</researcher>
|
||||
@@ -951,7 +951,7 @@
|
||||
<place hlink="_L3WJQCD3US67V2CNZT"/>
|
||||
<description>Birth of Garner, Anderson</description>
|
||||
</event>
|
||||
<event handle="_a5af0eb7dfb557da07e" change="1502187535" id="E0179">
|
||||
<event handle="_a5af0eb7dfb557da07e" change="1284030599" id="E0179">
|
||||
<type>Death</type>
|
||||
<dateval val="1887-04-07"/>
|
||||
<place hlink="_AKFKQC2N4SM243HCTN"/>
|
||||
@@ -6262,7 +6262,7 @@
|
||||
<dateval val="1842-06-28"/>
|
||||
<description>Death of Moreno, Joseph McDowell</description>
|
||||
</event>
|
||||
<event handle="_a5af0ec45b25c630e03" change="1502187451" id="E1179">
|
||||
<event handle="_a5af0ec45b25c630e03" change="1284030608" id="E1179">
|
||||
<type>Birth</type>
|
||||
<dateval val="1911-07-12"/>
|
||||
<description>Birth of Thornton, James Arthur</description>
|
||||
@@ -15003,7 +15003,7 @@
|
||||
<type>Marriage</type>
|
||||
<description>Marriage of Johnson, Henry and Sparks, Catherine</description>
|
||||
</event>
|
||||
<event handle="_a5af0ed5df832ee65c1" change="1502187371" id="E2815">
|
||||
<event handle="_a5af0ed5df832ee65c1" change="1328026859" id="E2815">
|
||||
<type>Marriage</type>
|
||||
<dateval val="1875-04-01"/>
|
||||
<place hlink="_RF5KQCNJRQY8OGTX2H"/>
|
||||
@@ -18649,7 +18649,7 @@
|
||||
<parentin hlink="_F4CKQCJZ24JRE9Z889"/>
|
||||
<citationref hlink="_c140d2472c91aad494f"/>
|
||||
</person>
|
||||
<person handle="_14LKQCYZJEAXTS3XX" change="1502187492" id="I1376">
|
||||
<person handle="_14LKQCYZJEAXTS3XX" change="1185438865" id="I1376">
|
||||
<gender>F</gender>
|
||||
<name type="Birth Name">
|
||||
<first>Elizabeth</first>
|
||||
@@ -18657,7 +18657,6 @@
|
||||
</name>
|
||||
<eventref hlink="_a5af0ebb003796f79a0" role="Primary"/>
|
||||
<eventref hlink="_a5af0ebb0143dab99ff" role="Primary"/>
|
||||
<eventref hlink="_a5af0eb7dfb557da07e" role="Witness"/>
|
||||
<childof hlink="_5IUJQCRJY47YQ8PU7N"/>
|
||||
<parentin hlink="_I4LKQCOAGPFH4R90A7"/>
|
||||
<citationref hlink="_c140d24731407d57b3c"/>
|
||||
@@ -19905,7 +19904,7 @@
|
||||
<childof hlink="_IO5KQC6H0Q6Y517LR9"/>
|
||||
<citationref hlink="_c140d24a1fd2319b703"/>
|
||||
</person>
|
||||
<person handle="_35WJQC1B7T7NPV8OLV" change="1502187260" id="I0106">
|
||||
<person handle="_35WJQC1B7T7NPV8OLV" change="1284030051" id="I0106">
|
||||
<gender>M</gender>
|
||||
<name type="Birth Name">
|
||||
<first>Robert W.</first>
|
||||
@@ -19914,11 +19913,6 @@
|
||||
<eventref hlink="_a5af0eb74ac73f86aa7" role="Primary"/>
|
||||
<eventref hlink="_a5af0eb74ba358391ae" role="Primary"/>
|
||||
<eventref hlink="_a5af0eb74c852f7c633" role="Primary"/>
|
||||
<eventref hlink="_a5af0ed5df832ee65c1" role="Witness">
|
||||
<attribute type="Age" value="23">
|
||||
<citationref hlink="_c140dafeb317af2fd79"/>
|
||||
</attribute>
|
||||
</eventref>
|
||||
<childof hlink="_X3WJQCSF48F6809142"/>
|
||||
<parentin hlink="_8OUJQCUVZ0XML7BQLF"/>
|
||||
<citationref hlink="_c140d24a27e19bb381a"/>
|
||||
@@ -21997,14 +21991,13 @@
|
||||
<childof hlink="_GCDKQCHI74ZPMI5GDJ"/>
|
||||
<citationref hlink="_c140d24f3f8704aa41b"/>
|
||||
</person>
|
||||
<person handle="_6G0KQC2UXYC66XDDC2" change="1502187537" id="I0363">
|
||||
<person handle="_6G0KQC2UXYC66XDDC2" change="1185438865" id="I0363">
|
||||
<gender>F</gender>
|
||||
<name type="Birth Name">
|
||||
<first>Miranda Keziah</first>
|
||||
<surname>Farmer</surname>
|
||||
</name>
|
||||
<eventref hlink="_a5af0ec7a76244d962d" role="Primary"/>
|
||||
<eventref hlink="_a5af0eb7dfb557da07e" role="Witness"/>
|
||||
<childof hlink="_8NVJQCGMJTCL7E6ZDV"/>
|
||||
<citationref hlink="_c140d24f439709d3118"/>
|
||||
</person>
|
||||
@@ -22996,7 +22989,7 @@
|
||||
<parentin hlink="_FBVJQCFBI50TW78O49"/>
|
||||
<citationref hlink="_c140d2522702664ec52"/>
|
||||
</person>
|
||||
<person handle="_8CLKQCT97PJOGREJ7W" change="1502187387" id="I1387">
|
||||
<person handle="_8CLKQCT97PJOGREJ7W" change="1185438865" id="I1387">
|
||||
<gender>M</gender>
|
||||
<name type="Birth Name">
|
||||
<first>Robert</first>
|
||||
@@ -23004,11 +22997,6 @@
|
||||
</name>
|
||||
<eventref hlink="_a5af0ebb3737a6ad088" role="Primary"/>
|
||||
<eventref hlink="_a5af0ebb39b441e9607" role="Primary"/>
|
||||
<eventref hlink="_a5af0ed5df832ee65c1" role="Clergy">
|
||||
<attribute type="Age" value="23">
|
||||
<citationref hlink="_c140dafeb317af2fd79"/>
|
||||
</attribute>
|
||||
</eventref>
|
||||
<childof hlink="_5IUJQCRJY47YQ8PU7N"/>
|
||||
<parentin hlink="_H6LKQCWVIFNRNGHFVH"/>
|
||||
<citationref hlink="_c140d2522d804491375"/>
|
||||
@@ -23815,7 +23803,7 @@
|
||||
<childof hlink="_5DBKQCVAB0XMBEW8R9"/>
|
||||
<citationref hlink="_c140d25425874b0b827"/>
|
||||
</person>
|
||||
<person handle="_9HUJQC6ONNW8SMSKGQ" change="1502187526" id="I0038">
|
||||
<person handle="_9HUJQC6ONNW8SMSKGQ" change="1185438865" id="I0038">
|
||||
<gender>M</gender>
|
||||
<name type="Birth Name">
|
||||
<first>David</first>
|
||||
@@ -23824,7 +23812,6 @@
|
||||
<eventref hlink="_a5af0ec7eb514c52fbf" role="Primary"/>
|
||||
<eventref hlink="_a5af0ec7ec844213b55" role="Primary"/>
|
||||
<eventref hlink="_a5af0ec7ed61c743fc8" role="Primary"/>
|
||||
<eventref hlink="_a5af0eb7dfb557da07e" role="Informant"/>
|
||||
<childof hlink="_5IUJQCRJY47YQ8PU7N"/>
|
||||
<parentin hlink="_3HUJQCK4DH582YUTZG"/>
|
||||
<citationref hlink="_c140d2542c764516f13"/>
|
||||
@@ -24519,7 +24506,7 @@
|
||||
<parentin hlink="_8NVJQCGMJTCL7E6ZDV"/>
|
||||
<citationref hlink="_c140d2560004fad8df6"/>
|
||||
</person>
|
||||
<person handle="_ANLKQCQSQNE5LDZMRC" change="1502187434" id="I1402">
|
||||
<person handle="_ANLKQCQSQNE5LDZMRC" change="1185438865" id="I1402">
|
||||
<gender>F</gender>
|
||||
<name type="Birth Name">
|
||||
<first>Margaret Jane "Maggie"</first>
|
||||
@@ -24527,7 +24514,6 @@
|
||||
</name>
|
||||
<eventref hlink="_a5af0ebb7d47a006ee2" role="Primary"/>
|
||||
<eventref hlink="_a5af0ebb7e54e14970f" role="Primary"/>
|
||||
<eventref hlink="_a5af0ec45b25c630e03" role="Witness"/>
|
||||
<childof hlink="_VDLKQCQQ1ADTJG1D1F"/>
|
||||
<parentin hlink="_SNLKQCD0VNJ627062Y"/>
|
||||
<citationref hlink="_c140d25607213be35da"/>
|
||||
@@ -24896,18 +24882,13 @@
|
||||
<parentin hlink="_JFYJQCG2KLRQN835JD"/>
|
||||
<citationref hlink="_c140d256e3403ba129d"/>
|
||||
</person>
|
||||
<person handle="_B3BKQCSV0G3NKSKWDX" change="1502187344" id="I0880">
|
||||
<person handle="_B3BKQCSV0G3NKSKWDX" change="1185438865" id="I0880">
|
||||
<gender>F</gender>
|
||||
<name type="Birth Name">
|
||||
<first>L. J.</first>
|
||||
<surname>Blanco</surname>
|
||||
</name>
|
||||
<eventref hlink="_a5af0ed2cf617169903" role="Primary"/>
|
||||
<eventref hlink="_a5af0ed5df832ee65c1" role="Clergy">
|
||||
<attribute type="Age" value="23">
|
||||
<citationref hlink="_c140dafeb317af2fd79"/>
|
||||
</attribute>
|
||||
</eventref>
|
||||
<childof hlink="_1BVJQCNTFAGS8273LJ"/>
|
||||
<citationref hlink="_c140d256ec4306a51ca"/>
|
||||
</person>
|
||||
@@ -31949,7 +31930,7 @@
|
||||
<childof hlink="_UDMKQC5D3A2PXPUGNC"/>
|
||||
<citationref hlink="_c140d266ec93334f40f"/>
|
||||
</person>
|
||||
<person handle="_MG5KQC6ZKSVO4A63G2" change="1502187451" id="I0624">
|
||||
<person handle="_MG5KQC6ZKSVO4A63G2" change="1328026883" id="I0624">
|
||||
<gender>M</gender>
|
||||
<name type="Birth Name">
|
||||
<first>Raymond E.</first>
|
||||
@@ -31957,7 +31938,6 @@
|
||||
</name>
|
||||
<eventref hlink="_a5af0ece8bd1125a1a9" role="Primary"/>
|
||||
<eventref hlink="_a5af0ece8d15511ddf9" role="Primary"/>
|
||||
<eventref hlink="_a5af0ec45b25c630e03" role="Informant"/>
|
||||
<childof hlink="_9OUJQCBOHW9UEK9CNV"/>
|
||||
<citationref hlink="_c140d266f0d5d178784"/>
|
||||
</person>
|
||||
@@ -32356,7 +32336,7 @@
|
||||
<parentin hlink="_F4CKQCJZ24JRE9Z889"/>
|
||||
<citationref hlink="_c140d267ccb7aef0cd0"/>
|
||||
</person>
|
||||
<person handle="_N4DKQCPEMZ7OO62O7J" change="1502187308" id="I0975">
|
||||
<person handle="_N4DKQCPEMZ7OO62O7J" change="1185438865" id="I0975">
|
||||
<gender>M</gender>
|
||||
<name type="Birth Name">
|
||||
<first>Henry</first>
|
||||
@@ -32365,11 +32345,6 @@
|
||||
<eventref hlink="_a5af0ed483738d4ed1e" role="Primary"/>
|
||||
<eventref hlink="_a5af0ed484a144d229b" role="Primary"/>
|
||||
<eventref hlink="_a5af0ed48605fb6b9eb" role="Primary"/>
|
||||
<eventref hlink="_a5af0ed5df832ee65c1" role="Witness">
|
||||
<attribute type="Age" value="23">
|
||||
<citationref hlink="_c140dafeb317af2fd79"/>
|
||||
</attribute>
|
||||
</eventref>
|
||||
<parentin hlink="_7PUJQC4PPS4EDIVMYE"/>
|
||||
<citationref hlink="_c140d267d2128356d8e"/>
|
||||
</person>
|
||||
|
||||
@@ -318,6 +318,7 @@ class CLIManager:
|
||||
and self.dbstate.db.get_total() == 0):
|
||||
self.dbstate.db.set_researcher(owner)
|
||||
|
||||
name_displayer.clear_custom_formats()
|
||||
name_displayer.set_name_format(self.dbstate.db.name_formats)
|
||||
fmt_default = config.get('preferences.name-format')
|
||||
name_displayer.set_default_format(fmt_default)
|
||||
|
||||
@@ -47,7 +47,8 @@ LOG = logging.getLogger(".")
|
||||
#-------------------------------------------------------------------------
|
||||
from gramps.gen.plug import BasePluginManager
|
||||
from gramps.gen.plug.docgen import (StyleSheet, StyleSheetList, PaperStyle,
|
||||
PAPER_PORTRAIT, PAPER_LANDSCAPE, graphdoc)
|
||||
PAPER_PORTRAIT, PAPER_LANDSCAPE, graphdoc,
|
||||
treedoc)
|
||||
from gramps.gen.plug.menu import (FamilyOption, PersonOption, NoteOption,
|
||||
MediaOption, PersonListOption, NumberOption,
|
||||
BooleanOption, DestinationOption, Option,
|
||||
@@ -56,8 +57,8 @@ from gramps.gen.plug.menu import (FamilyOption, PersonOption, NoteOption,
|
||||
from gramps.gen.display.name import displayer as name_displayer
|
||||
from gramps.gen.errors import ReportError, FilterError
|
||||
from gramps.gen.plug.report import (CATEGORY_TEXT, CATEGORY_DRAW, CATEGORY_BOOK,
|
||||
CATEGORY_GRAPHVIZ, CATEGORY_CODE,
|
||||
ReportOptions, append_styles)
|
||||
CATEGORY_GRAPHVIZ, CATEGORY_TREE,
|
||||
CATEGORY_CODE, ReportOptions, append_styles)
|
||||
from gramps.gen.plug.report._paper import paper_sizes
|
||||
from gramps.gen.const import USER_HOME, DOCGEN_OPTIONS
|
||||
from gramps.gen.dbstate import DbState
|
||||
@@ -250,6 +251,15 @@ class CommandLineReport:
|
||||
if name not in self.option_class.options_dict:
|
||||
self.option_class.options_dict[
|
||||
name] = menu.get_option_by_name(name).get_value()
|
||||
if category == CATEGORY_TREE:
|
||||
# Need to include Genealogy Tree options
|
||||
self.__toptions = treedoc.TreeOptions()
|
||||
menu = self.option_class.menu
|
||||
self.__toptions.add_menu_options(menu)
|
||||
for name in menu.get_all_option_names():
|
||||
if name not in self.option_class.options_dict:
|
||||
self.option_class.options_dict[
|
||||
name] = menu.get_option_by_name(name).get_value()
|
||||
self.option_class.load_previous_values()
|
||||
_validate_options(self.option_class, database)
|
||||
self.show = options_str_dict.pop('show', None)
|
||||
@@ -320,6 +330,10 @@ class CommandLineReport:
|
||||
for graph_format in graphdoc.FORMATS:
|
||||
self.options_help['off'][2].append(
|
||||
graph_format["type"] + "\t" + graph_format["descr"])
|
||||
elif self.category == CATEGORY_TREE:
|
||||
for tree_format in treedoc.FORMATS:
|
||||
self.options_help['off'][2].append(
|
||||
tree_format["type"] + "\t" + tree_format["descr"])
|
||||
else:
|
||||
self.options_help['off'][2] = "NA"
|
||||
|
||||
@@ -498,6 +512,15 @@ class CommandLineReport:
|
||||
# Pick the first one as the default.
|
||||
self.format = graphdoc.FORMATS[0]["class"]
|
||||
_chosen_format = graphdoc.FORMATS[0]["type"]
|
||||
elif self.category == CATEGORY_TREE:
|
||||
for tree_format in treedoc.FORMATS:
|
||||
if tree_format['type'] == self.options_dict['off']:
|
||||
if not self.format: # choose the first one, not the last
|
||||
self.format = tree_format["class"]
|
||||
if self.format is None:
|
||||
# Pick the first one as the default.
|
||||
self.format = tree_format.FORMATS[0]["class"]
|
||||
_chosen_format = tree_format.FORMATS[0]["type"]
|
||||
else:
|
||||
self.format = None
|
||||
if _chosen_format and _format_str:
|
||||
@@ -665,7 +688,7 @@ def cl_report(database, name, category, report_class, options_class,
|
||||
clr.selected_style,
|
||||
PaperStyle(clr.paper, clr.orien, clr.marginl,
|
||||
clr.marginr, clr.margint, clr.marginb))
|
||||
elif category == CATEGORY_GRAPHVIZ:
|
||||
elif category in [CATEGORY_GRAPHVIZ, CATEGORY_TREE]:
|
||||
clr.option_class.handler.doc = clr.format(
|
||||
clr.option_class,
|
||||
PaperStyle(clr.paper, clr.orien, clr.marginl,
|
||||
|
||||
+21
-23
@@ -155,7 +155,7 @@ register('behavior.translator-needed', True)
|
||||
register('behavior.use-tips', False)
|
||||
register('behavior.welcome', 100)
|
||||
register('behavior.web-search-url', 'http://google.com/#&q=%(text)s')
|
||||
register('behavior.addons-url', "https://raw.githubusercontent.com/gramps-project/addons/master/gramps51")
|
||||
register('behavior.addons-url', "https://raw.githubusercontent.com/gramps-project/addons/master/gramps50")
|
||||
|
||||
register('database.backend', 'bsddb')
|
||||
register('database.compress-backup', True)
|
||||
@@ -228,8 +228,6 @@ register('paths.report-directory', USER_HOME)
|
||||
register('paths.website-directory', USER_HOME)
|
||||
register('paths.website-cms-uri', '')
|
||||
register('paths.website-cal-uri', '')
|
||||
register('paths.website-extra-page-uri', '')
|
||||
register('paths.website-extra-page-name', '')
|
||||
register('paths.quick-backup-directory', USER_HOME)
|
||||
register('paths.quick-backup-filename',
|
||||
"%(filename)s_%(year)d-%(month)02d-%(day)02d.%(extension)s")
|
||||
@@ -249,11 +247,8 @@ register('preferences.hide-ep-msg', False)
|
||||
register('preferences.invalid-date-format', "<b>%s</b>")
|
||||
register('preferences.iprefix', 'I%04d')
|
||||
register('preferences.name-format', 1)
|
||||
register('preferences.place-format', 0)
|
||||
register('preferences.place-auto', True)
|
||||
register('preferences.place-number', False)
|
||||
register('preferences.place-reverse', False)
|
||||
register('preferences.place-restrict', 0)
|
||||
register('preferences.place-lang', '')
|
||||
register('preferences.patronimic-surname', False)
|
||||
register('preferences.no-given-text', "[%s]" % _("Missing Given Name"))
|
||||
register('preferences.no-record-text', "[%s]" % _("Missing Record"))
|
||||
@@ -274,22 +269,25 @@ register('preferences.last-view', '')
|
||||
register('preferences.last-views', [])
|
||||
register('preferences.family-relation-type', 3) # UNKNOWN
|
||||
register('preferences.age-display-precision', 1)
|
||||
register('preferences.color-gender-male-alive', '#b8cee6')
|
||||
register('preferences.color-gender-male-death', '#b8cee6')
|
||||
register('preferences.color-gender-female-alive', '#feccf0')
|
||||
register('preferences.color-gender-female-death', '#feccf0')
|
||||
register('preferences.color-gender-unknown-alive', '#f3dbb6')
|
||||
register('preferences.color-gender-unknown-death', '#f3dbb6')
|
||||
#register('preferences.color-gender-other-alive', '#fcaf3e')
|
||||
#register('preferences.color-gender-other-death', '#fcaf3e')
|
||||
register('preferences.bordercolor-gender-male-alive', '#1f4986')
|
||||
register('preferences.bordercolor-gender-male-death', '#000000')
|
||||
register('preferences.bordercolor-gender-female-alive', '#861f69')
|
||||
register('preferences.bordercolor-gender-female-death', '#000000')
|
||||
register('preferences.bordercolor-gender-unknown-alive', '#8e5801')
|
||||
register('preferences.bordercolor-gender-unknown-death', '#000000')
|
||||
#register('preferences.bordercolor-gender-other-alive', '#f57900')
|
||||
#register('preferences.bordercolor-gender-other-death', '#000000')
|
||||
|
||||
register('colors.scheme', 0)
|
||||
register('colors.male-alive', ['#b8cee6', '#1f344a'])
|
||||
register('colors.male-dead', ['#b8cee6', '#2d3039'])
|
||||
register('colors.female-alive', ['#feccf0', '#62242D'])
|
||||
register('colors.female-dead', ['#feccf0', '#3a292b'])
|
||||
register('colors.unknown-alive', ['#f3dbb6', '#75507B'])
|
||||
register('colors.unknown-dead', ['#f3dbb6', '#35103b'])
|
||||
register('colors.family', ['#eeeeee', '#454545'])
|
||||
register('colors.family-divorced', ['#ffdede', '#5c3636'])
|
||||
register('colors.home-person', ['#bbe68a', '#304918'])
|
||||
register('colors.border-male-alive', ['#1f4986', '#171d26'])
|
||||
register('colors.border-male-dead', ['#000000', '#000000'])
|
||||
register('colors.border-female-alive', ['#861f69', '#261111'])
|
||||
register('colors.border-female-dead', ['#000000', '#000000'])
|
||||
register('colors.border-unknown-alive', ['#8e5801', '#8e5801'])
|
||||
register('colors.border-unknown-dead', ['#000000', '#000000'])
|
||||
register('colors.border-family', ['#cccccc', '#252525'])
|
||||
register('colors.border-family-divorced', ['#ff7373', '#720b0b'])
|
||||
|
||||
register('researcher.researcher-addr', '')
|
||||
register('researcher.researcher-locality', '')
|
||||
|
||||
+3
-3
@@ -112,6 +112,7 @@ VERSION_DIR = os.path.join(
|
||||
CUSTOM_FILTERS = os.path.join(VERSION_DIR, "custom_filters.xml")
|
||||
REPORT_OPTIONS = os.path.join(HOME_DIR, "report_options.xml")
|
||||
TOOL_OPTIONS = os.path.join(HOME_DIR, "tool_options.xml")
|
||||
PLACE_FORMATS = os.path.join(HOME_DIR, "place_formats.xml")
|
||||
|
||||
ENV_DIR = os.path.join(HOME_DIR, "env")
|
||||
TEMP_DIR = os.path.join(HOME_DIR, "temp")
|
||||
@@ -137,8 +138,7 @@ sys.path.insert(0, ROOT_DIR)
|
||||
git_revision = get_git_revision(ROOT_DIR).replace('\n', '')
|
||||
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
|
||||
|
||||
#
|
||||
# Glade files
|
||||
@@ -217,7 +217,7 @@ GTK_GETTEXT_DOMAIN = 'gtk30'
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
COPYRIGHT_MSG = "© 2001-2006 Donald N. Allingham\n" \
|
||||
"© 2007-2017 The Gramps Developers"
|
||||
"© 2007-2018 The Gramps Developers"
|
||||
COMMENTS = _("Gramps\n (Genealogical Research and Analysis "
|
||||
"Management Programming System)\n"
|
||||
"is a personal genealogy program.")
|
||||
|
||||
@@ -292,7 +292,7 @@ class DateDisplayDE(DateDisplay):
|
||||
)
|
||||
# this definition must agree with its "_display_gregorian" method
|
||||
|
||||
def _display_gregorian(self, date_val):
|
||||
def _display_gregorian(self, date_val, **kwargs):
|
||||
"""
|
||||
display gregorian calendar date in different format
|
||||
"""
|
||||
|
||||
@@ -155,7 +155,7 @@ class DateDisplayEL(DateDisplay):
|
||||
)
|
||||
# this definition must agree with its "_display_gregorian" method
|
||||
|
||||
def _display_gregorian(self, date_val):
|
||||
def _display_gregorian(self, date_val, **kwargs):
|
||||
"""
|
||||
display gregorian calendar date in different format
|
||||
"""
|
||||
|
||||
@@ -187,7 +187,7 @@ class DateDisplayLT(DateDisplay):
|
||||
"mmmm m. mėnesio diena d.", "Mėn diena, metai")
|
||||
# this definition must agree with its "_display_gregorian" method
|
||||
|
||||
def _display_gregorian(self, date_val):
|
||||
def _display_gregorian(self, date_val, **kwargs):
|
||||
"""
|
||||
display gregorian calendar date in different format
|
||||
"""
|
||||
|
||||
@@ -164,7 +164,7 @@ class DateDisplayNL(DateDisplay):
|
||||
)
|
||||
# this definition must agree with its "_display_gregorian" method
|
||||
|
||||
def _display_gregorian(self, date_val):
|
||||
def _display_gregorian(self, date_val, **kwargs):
|
||||
"""
|
||||
display gregorian calendar date in different format
|
||||
"""
|
||||
|
||||
@@ -215,7 +215,7 @@ class DateDisplayPL(DateDisplay):
|
||||
"XII"
|
||||
)
|
||||
|
||||
def _display_gregorian(self, date_val):
|
||||
def _display_gregorian(self, date_val, **kwargs):
|
||||
"""
|
||||
display gregorian calendar date in different format
|
||||
"""
|
||||
|
||||
@@ -240,7 +240,7 @@ class DateDisplaySR_Base(DateDisplay):
|
||||
"VII", "VIII", "IX", "X", "XI", "XII"
|
||||
)
|
||||
|
||||
def _display_gregorian(self, date_val):
|
||||
def _display_gregorian(self, date_val, **kwargs):
|
||||
"""
|
||||
display gregorian calendar date in different format
|
||||
"""
|
||||
|
||||
@@ -216,9 +216,8 @@ class DummyDb(M_A_M_B("NewBaseClass", (DbReadBase, Callback, object,), {})):
|
||||
"""
|
||||
Create a new DummyDb instance.
|
||||
"""
|
||||
DbReadBase.__init__(self)
|
||||
Callback.__init__(self)
|
||||
self.basedb = None
|
||||
self.__feature = {} # {"feature": VALUE, ...}
|
||||
self.db_is_open = False
|
||||
self.readonly = True
|
||||
self.name_formats = []
|
||||
|
||||
@@ -418,6 +418,11 @@ class NameDisplay:
|
||||
result = raw_data[_FIRSTNAME]
|
||||
return ' '.join(result.split())
|
||||
|
||||
def clear_custom_formats(self):
|
||||
self.name_formats = {num: value
|
||||
for num, value in self.name_formats.items()
|
||||
if num >= 0}
|
||||
|
||||
def set_name_format(self, formats):
|
||||
|
||||
raw_func_dict = {
|
||||
|
||||
+114
-16
@@ -1,7 +1,7 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2014-2015 Nick Hall
|
||||
# Copyright (C) 2014-2017 Nick Hall
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -22,15 +22,43 @@
|
||||
Class handling displaying of places.
|
||||
"""
|
||||
|
||||
#---------------------------------------------------------------
|
||||
#
|
||||
# Python imports
|
||||
#
|
||||
#---------------------------------------------------------------
|
||||
import os
|
||||
import xml.dom.minidom
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Gramps modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from ..const import PLACE_FORMATS
|
||||
from ..config import config
|
||||
from ..utils.location import get_location_list
|
||||
from ..lib import PlaceType
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# PlaceFormat class
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class PlaceFormat:
|
||||
def __init__(self, name, levels, language, street, reverse):
|
||||
self.name = name
|
||||
self.levels = levels
|
||||
self.language = language
|
||||
self.street = street
|
||||
self.reverse = reverse
|
||||
|
||||
def to_xml(self):
|
||||
return (' <format name="%s" levels="%s" language="%s" '
|
||||
'street="%s" reverse="%s"/>\n' %
|
||||
(self.name, self.levels, self.language,
|
||||
self.street, self.reverse))
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# PlaceDisplay class
|
||||
@@ -38,51 +66,121 @@ from ..lib import PlaceType
|
||||
#-------------------------------------------------------------------------
|
||||
class PlaceDisplay:
|
||||
|
||||
def display_event(self, db, event):
|
||||
def __init__(self):
|
||||
self.place_formats = []
|
||||
self.default_format = config.get('preferences.place-format')
|
||||
if os.path.exists(PLACE_FORMATS):
|
||||
self.load_formats()
|
||||
else:
|
||||
pf = PlaceFormat('Full', ':', '', 0, False)
|
||||
self.place_formats.append(pf)
|
||||
|
||||
def display_event(self, db, event, fmt=None):
|
||||
if not event:
|
||||
return ""
|
||||
place_handle = event.get_place_handle()
|
||||
if place_handle:
|
||||
place = db.get_place_from_handle(place_handle)
|
||||
return self.display(db, place, event.get_date_object())
|
||||
return self.display(db, place, event.get_date_object(), fmt)
|
||||
else:
|
||||
return ""
|
||||
|
||||
def display(self, db, place, date=None):
|
||||
def display(self, db, place, date=None, fmt=None):
|
||||
if not place:
|
||||
return ""
|
||||
if not config.get('preferences.place-auto'):
|
||||
return place.title
|
||||
else:
|
||||
lang = config.get('preferences.place-lang')
|
||||
places = get_location_list(db, place, date, lang)
|
||||
if fmt is None:
|
||||
fmt = config.get('preferences.place-format')
|
||||
pf = self.place_formats[fmt]
|
||||
lang = pf.language
|
||||
all_places = get_location_list(db, place, date, lang)
|
||||
|
||||
if config.get('preferences.place-restrict') > 0:
|
||||
index = _find_populated_place(places)
|
||||
if index is not None:
|
||||
if config.get('preferences.place-restrict') == 1:
|
||||
places = places[:index+1]
|
||||
# Apply format string to place list
|
||||
index = _find_populated_place(all_places)
|
||||
places = []
|
||||
for slice in pf.levels.split(','):
|
||||
parts = slice.split(':')
|
||||
if len(parts) == 1:
|
||||
offset = _get_offset(parts[0], index)
|
||||
if offset is not None:
|
||||
places.append(all_places[offset])
|
||||
elif len(parts) == 2:
|
||||
start = _get_offset(parts[0], index)
|
||||
end = _get_offset(parts[1], index)
|
||||
if start is None:
|
||||
places.extend(all_places[:end])
|
||||
elif end is None:
|
||||
places.extend(all_places[start:])
|
||||
else:
|
||||
places = places[index:]
|
||||
places.extend(all_places[start:end])
|
||||
|
||||
if config.get('preferences.place-number'):
|
||||
if pf.street:
|
||||
types = [item[1] for item in places]
|
||||
try:
|
||||
idx = types.index(PlaceType.NUMBER)
|
||||
except ValueError:
|
||||
idx = None
|
||||
if idx is not None and len(places) > idx+1:
|
||||
combined = (places[idx][0] + ' ' + places[idx+1][0],
|
||||
places[idx+1][1])
|
||||
if pf.street == 1:
|
||||
combined = (places[idx][0] + ' ' + places[idx+1][0],
|
||||
places[idx+1][1])
|
||||
else:
|
||||
combined = (places[idx+1][0] + ' ' + places[idx][0],
|
||||
places[idx+1][1])
|
||||
places = places[:idx] + [combined] + places[idx+2:]
|
||||
|
||||
names = [item[0] for item in places]
|
||||
if config.get('preferences.place-reverse'):
|
||||
if pf.reverse:
|
||||
names.reverse()
|
||||
|
||||
# TODO for Arabic, should the next line's comma be translated?
|
||||
return ", ".join(names)
|
||||
|
||||
def get_formats(self):
|
||||
return self.place_formats
|
||||
|
||||
def set_formats(self, formats):
|
||||
self.place_formats = formats
|
||||
|
||||
def load_formats(self):
|
||||
dom = xml.dom.minidom.parse(PLACE_FORMATS)
|
||||
top = dom.getElementsByTagName('place_formats')
|
||||
|
||||
for fmt in top[0].getElementsByTagName('format'):
|
||||
name = fmt.attributes['name'].value
|
||||
levels = fmt.attributes['levels'].value
|
||||
language = fmt.attributes['language'].value
|
||||
street = int(fmt.attributes['street'].value)
|
||||
reverse = fmt.attributes['reverse'].value == 'True'
|
||||
pf = PlaceFormat(name, levels, language, street, reverse)
|
||||
self.place_formats.append(pf)
|
||||
|
||||
dom.unlink()
|
||||
|
||||
def save_formats(self):
|
||||
with open(PLACE_FORMATS, 'w') as fd:
|
||||
fd.write('<?xml version="1.0" encoding="utf-8"?>\n')
|
||||
fd.write('<place_formats>\n')
|
||||
for fmt in self.place_formats:
|
||||
fd.write(fmt.to_xml())
|
||||
fd.write('</place_formats>\n')
|
||||
|
||||
def _get_offset(value, index):
|
||||
if index is not None and value.startswith('p'):
|
||||
try:
|
||||
offset = int(value[1:])
|
||||
except ValueError:
|
||||
offset = 0
|
||||
offset += index
|
||||
else:
|
||||
try:
|
||||
offset = int(value)
|
||||
except ValueError:
|
||||
offset = None
|
||||
return offset
|
||||
|
||||
def _find_populated_place(places):
|
||||
populated_place = None
|
||||
for index, item in enumerate(places):
|
||||
|
||||
@@ -82,7 +82,8 @@ class IsDescendantFamilyOf(Rule):
|
||||
|
||||
while expand:
|
||||
person = expand.pop(0)
|
||||
if person is None:
|
||||
if person is None or person.handle in self.matches:
|
||||
# if we have been here before, skip
|
||||
continue
|
||||
self.matches.add(person.handle)
|
||||
for family_handle in person.get_family_handle_list():
|
||||
|
||||
@@ -67,7 +67,8 @@ class IsDescendantOf(Rule):
|
||||
return person.handle in self.map
|
||||
|
||||
def init_list(self, person, first):
|
||||
if not person:
|
||||
if not person or person.handle in self.map:
|
||||
# if we have been here before, skip
|
||||
return
|
||||
if not first:
|
||||
self.map.add(person.handle)
|
||||
|
||||
@@ -61,6 +61,9 @@ class IsLessThanNthGenerationAncestorOf(Rule):
|
||||
queue = [(root_handle, 1)] # generation 1 is root
|
||||
while queue:
|
||||
handle, gen = queue.pop(0) # pop off front of queue
|
||||
if handle in self.map:
|
||||
# if we have been here before, skip
|
||||
continue
|
||||
self.map.add(handle)
|
||||
gen += 1
|
||||
if gen <= int(self.list[1]):
|
||||
|
||||
@@ -71,7 +71,8 @@ class IsLessThanNthGenerationAncestorOfBookmarked(Rule):
|
||||
def init_ancestor_list(self, handle, gen):
|
||||
# if p.get_handle() in self.map:
|
||||
# loop_error(self.orig,p)
|
||||
if not handle:
|
||||
if not handle or handle in self.map:
|
||||
# if been here already, skip
|
||||
return
|
||||
if gen:
|
||||
self.map.add(handle)
|
||||
|
||||
@@ -64,7 +64,8 @@ class IsLessThanNthGenerationAncestorOfDefaultPerson(Rule):
|
||||
def init_ancestor_list(self, handle, gen):
|
||||
# if p.get_handle() in self.map:
|
||||
# loop_error(self.orig,p)
|
||||
if not handle:
|
||||
if not handle or handle in self.map:
|
||||
# if we have been here before, skip
|
||||
return
|
||||
if gen:
|
||||
self.map.add(handle)
|
||||
|
||||
@@ -65,7 +65,8 @@ class IsLessThanNthGenerationDescendantOf(Rule):
|
||||
return person.handle in self.map
|
||||
|
||||
def init_list(self,person,gen):
|
||||
if not person:
|
||||
if not person or person.handle in self.map:
|
||||
# if we have been here before, skip
|
||||
return
|
||||
if gen:
|
||||
self.map.add(person.handle)
|
||||
|
||||
+12
-12
@@ -27,7 +27,7 @@ The "plug" package for handling plugins in Gramps.
|
||||
from ._plugin import Plugin
|
||||
from ._pluginreg import (PluginData, PluginRegister, REPORT, TOOL,
|
||||
CATEGORY_TEXT, CATEGORY_DRAW, CATEGORY_CODE,
|
||||
CATEGORY_WEB, CATEGORY_BOOK, CATEGORY_GRAPHVIZ,
|
||||
CATEGORY_WEB, CATEGORY_BOOK, CATEGORY_GRAPHVIZ, CATEGORY_TREE,
|
||||
TOOL_DEBUG, TOOL_ANAL, TOOL_DBPROC, TOOL_DBFIX, TOOL_REVCTL,
|
||||
TOOL_UTILS, CATEGORY_QR_MISC, CATEGORY_QR_PERSON,
|
||||
CATEGORY_QR_FAMILY, CATEGORY_QR_EVENT, CATEGORY_QR_SOURCE,
|
||||
@@ -46,14 +46,14 @@ from ._options import (Options, OptionListCollection, OptionList,
|
||||
OptionHandler, MenuOptions)
|
||||
|
||||
__all__ = [ "docbackend", "docgen", "menu", "Plugin", "PluginData",
|
||||
"PluginRegister", "BasePluginManager",
|
||||
"ImportPlugin", "ExportPlugin", "DocGenPlugin",
|
||||
"REPORT", "TOOL", "CATEGORY_TEXT", "CATEGORY_DRAW", "CATEGORY_CODE",
|
||||
"CATEGORY_WEB", "CATEGORY_BOOK", "CATEGORY_GRAPHVIZ",
|
||||
"TOOL_DEBUG", "TOOL_ANAL", "TOOL_DBPROC", "TOOL_DBFIX", "TOOL_REVCTL",
|
||||
"TOOL_UTILS", "CATEGORY_QR_MISC", "CATEGORY_QR_PERSON",
|
||||
"CATEGORY_QR_FAMILY", "CATEGORY_QR_EVENT", "CATEGORY_QR_SOURCE",
|
||||
"CATEGORY_QR_PLACE", "CATEGORY_QR_REPOSITORY", "CATEGORY_QR_NOTE",
|
||||
"CATEGORY_QR_DATE", "PTYPE_STR", "CATEGORY_QR_MEDIA",
|
||||
"CATEGORY_QR_CITATION", "CATEGORY_QR_SOURCE_OR_CITATION",
|
||||
"START", "END", "make_environment"]
|
||||
"PluginRegister", "BasePluginManager", "ImportPlugin",
|
||||
"ExportPlugin", "DocGenPlugin", "REPORT", "TOOL", "CATEGORY_TEXT",
|
||||
"CATEGORY_DRAW", "CATEGORY_CODE", "CATEGORY_WEB", "CATEGORY_BOOK",
|
||||
"CATEGORY_GRAPHVIZ", "CATEGORY_TREE", "TOOL_DEBUG", "TOOL_ANAL",
|
||||
"TOOL_DBPROC", "TOOL_DBFIX", "TOOL_REVCTL","TOOL_UTILS",
|
||||
"CATEGORY_QR_MISC", "CATEGORY_QR_PERSON", "CATEGORY_QR_FAMILY",
|
||||
"CATEGORY_QR_EVENT", "CATEGORY_QR_SOURCE", "CATEGORY_QR_PLACE",
|
||||
"CATEGORY_QR_REPOSITORY", "CATEGORY_QR_NOTE", "CATEGORY_QR_DATE",
|
||||
"PTYPE_STR", "CATEGORY_QR_MEDIA", "CATEGORY_QR_CITATION",
|
||||
"CATEGORY_QR_SOURCE_OR_CITATION", "START", "END",
|
||||
"make_environment"]
|
||||
|
||||
@@ -97,8 +97,10 @@ CATEGORY_CODE = 2
|
||||
CATEGORY_WEB = 3
|
||||
CATEGORY_BOOK = 4
|
||||
CATEGORY_GRAPHVIZ = 5
|
||||
CATEGORY_TREE = 6
|
||||
REPORT_CAT = [ CATEGORY_TEXT, CATEGORY_DRAW, CATEGORY_CODE,
|
||||
CATEGORY_WEB, CATEGORY_BOOK, CATEGORY_GRAPHVIZ]
|
||||
CATEGORY_WEB, CATEGORY_BOOK, CATEGORY_GRAPHVIZ,
|
||||
CATEGORY_TREE]
|
||||
#possible tool categories
|
||||
TOOL_DEBUG = -1
|
||||
TOOL_ANAL = 0
|
||||
@@ -1043,6 +1045,7 @@ def make_environment(**kwargs):
|
||||
'CATEGORY_WEB': CATEGORY_WEB,
|
||||
'CATEGORY_BOOK': CATEGORY_BOOK,
|
||||
'CATEGORY_GRAPHVIZ': CATEGORY_GRAPHVIZ,
|
||||
'CATEGORY_TREE': CATEGORY_TREE,
|
||||
'TOOL_DEBUG': TOOL_DEBUG,
|
||||
'TOOL_ANAL': TOOL_ANAL,
|
||||
'TOOL_DBPROC': TOOL_DBPROC,
|
||||
@@ -1242,8 +1245,7 @@ class PluginRegister:
|
||||
"""
|
||||
Return a list of :class:`PluginData` that are of type ptype
|
||||
"""
|
||||
return [self.get_plugin(id) for id in
|
||||
set([x.id for x in self.__plugindata if x.ptype == ptype])]
|
||||
return [x for x in self.__plugindata if x.ptype == ptype]
|
||||
|
||||
def report_plugins(self, gui=True):
|
||||
"""
|
||||
@@ -1352,6 +1354,4 @@ class PluginRegister:
|
||||
"""
|
||||
Return a list of :class:`PluginData` that have load_on_reg == True
|
||||
"""
|
||||
return [self.get_plugin(id) for id in
|
||||
set([x.id for x in self.__plugindata
|
||||
if x.load_on_reg == True])]
|
||||
return [x for x in self.__plugindata if x.load_on_reg == True]
|
||||
|
||||
@@ -37,3 +37,4 @@ from .textdoc import TextDoc, IndexMark,INDEX_TYPE_ALP, INDEX_TYPE_TOC,\
|
||||
URL_PATTERN, LOCAL_HYPERLINK, LOCAL_TARGET
|
||||
from .drawdoc import DrawDoc
|
||||
from .graphdoc import GVDoc
|
||||
from .treedoc import TreeDoc
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
# Copyright (C) 2007 Brian G. Matherly
|
||||
# Copyright (C) 2009 Benny Malengier
|
||||
# Copyright (C) 2009 Gary Burton
|
||||
# Copyright (C) 2017 Jonathan Biegert <azrdev@qrdn.de>
|
||||
# Copyright (C) 2017 Mindaugas Baranauskas
|
||||
# Copyright (C) 2017 Paul Culley
|
||||
#
|
||||
@@ -73,11 +72,6 @@ _RANKDIR = [{'name' : _("Vertical (↓)"), 'value' : "TB"},
|
||||
{'name' : _("Horizontal (→)"), 'value' : "LR"},
|
||||
{'name' : _("Horizontal (←)"), 'value' : "RL"}]
|
||||
|
||||
_NODE_PORTS = {"TB" : ("n", "s"),
|
||||
"BT" : ("s", "n"),
|
||||
"LR" : ("w", "e"),
|
||||
"RL" : ("e", "w")}
|
||||
|
||||
_PAGEDIR = [{'name' : _("Bottom, left"), 'value' : "BL"},
|
||||
{'name' : _("Bottom, right"), 'value' : "BR"},
|
||||
{'name' : _("Top, left"), 'value' : "TL"},
|
||||
@@ -189,10 +183,6 @@ class GVOptions:
|
||||
spline.set_help(_("How the lines between objects will be drawn."))
|
||||
menu.add_option(category, "spline", spline)
|
||||
|
||||
node_ports = BooleanOption(_("Alternate line attachment"), False)
|
||||
node_ports.set_help(_("Whether lines attach to nodes differently"))
|
||||
menu.add_option(category, "node_ports", node_ports)
|
||||
|
||||
# the page direction option only makes sense when the
|
||||
# number of horizontal and/or vertical pages is > 1,
|
||||
# so we need to remember these 3 controls for later
|
||||
@@ -419,7 +409,6 @@ class GVDocBase(BaseDoc, GVDoc):
|
||||
self.vpages = get_option('v_pages').get_value()
|
||||
self.usesubgraphs = get_option('usesubgraphs').get_value()
|
||||
self.spline = get_option('spline').get_value()
|
||||
self.node_ports = get_option('node_ports').get_value()
|
||||
|
||||
paper_size = paper_style.get_size()
|
||||
|
||||
@@ -467,9 +456,6 @@ class GVDocBase(BaseDoc, GVDoc):
|
||||
' splines="%s";\n' % self.spline +
|
||||
'\n' +
|
||||
' edge [len=0.5 style=solid fontsize=%d];\n' % self.fontsize)
|
||||
if self.node_ports:
|
||||
self.write(' edge [headport=%s tailport=%s];\n'
|
||||
% _NODE_PORTS[self.rankdir])
|
||||
|
||||
if self.fontfamily:
|
||||
self.write(' node [style=filled fontname="%s" fontsize=%d];\n'
|
||||
|
||||
@@ -0,0 +1,660 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2017-2018 Nick Hall
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
""" LaTeX Genealogy Tree adapter for Trees """
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Standard Python modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from abc import ABCMeta, abstractmethod
|
||||
import os
|
||||
import shutil
|
||||
import re
|
||||
from subprocess import Popen, PIPE
|
||||
from io import StringIO
|
||||
import tempfile
|
||||
import logging
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Gramps modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from ...utils.file import search_for
|
||||
from ...lib import Person, EventType, EventRoleType, Date
|
||||
from ...display.place import displayer as _pd
|
||||
from ...utils.file import media_path_full
|
||||
from . import BaseDoc, PAPER_PORTRAIT
|
||||
from ..menu import NumberOption, TextOption, EnumeratedListOption
|
||||
from ...constfunc import win
|
||||
from ...config import config
|
||||
from ...const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# set up logging
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
LOG = logging.getLogger(".treedoc")
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Private Constants
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
_DETAIL = [{'name': _("Full"), 'value': "full"},
|
||||
{'name': _("Medium"), 'value': "medium"},
|
||||
{'name': _("Short"), 'value': "short"}]
|
||||
|
||||
_MARRIAGE = [{'name': _("Default"), 'value': ""},
|
||||
{'name': _("Above"), 'value': "marriage above"},
|
||||
{'name': _("Below"), 'value': "marriage below"},
|
||||
{'name': _("Not shown"), 'value': "no marriage"}]
|
||||
|
||||
_COLOR = [{'name': _("None"), 'value': "none"},
|
||||
{'name': _("Default"), 'value': "default"},
|
||||
{'name': _("Preferences"), 'value': "preferences"}]
|
||||
|
||||
_TIMEFLOW = [{'name': _("Down (↓)"), 'value': ""},
|
||||
{'name': _("Up (↑)"), 'value': "up"},
|
||||
{'name': _("Right (→)"), 'value': "right"},
|
||||
{'name': _("Left (←)"), 'value': "left"}]
|
||||
|
||||
_EDGES = [{'name': _("Perpendicular"), 'value': ""},
|
||||
{'name': _("Rounded"), 'value': "rounded", },
|
||||
{'name': _("Swing"), 'value': "swing", },
|
||||
{'name': _("Mesh"), 'value': 'mesh'}]
|
||||
|
||||
_NOTELOC = [{'name': _("Top"), 'value': "t"},
|
||||
{'name': _("Bottom"), 'value': "b"}]
|
||||
|
||||
_NOTESIZE = [{'name': _("Tiny"), 'value': "tiny"},
|
||||
{'name': _("Script"), 'value': "scriptsize"},
|
||||
{'name': _("Footnote"), 'value': "footnotesize"},
|
||||
{'name': _("Small"), 'value': "small"},
|
||||
{'name': _("Normal"), 'value': "normalsize"},
|
||||
{'name': _("Large"), 'value': "large"},
|
||||
{'name': _("Very large"), 'value': "Large"},
|
||||
{'name': _("Extra large"), 'value': "LARGE"},
|
||||
{'name': _("Huge"), 'value': "huge"},
|
||||
{'name': _("Extra huge"), 'value': "Huge"}]
|
||||
|
||||
if win():
|
||||
_LATEX_FOUND = search_for("lualatex.exe")
|
||||
DETACHED_PROCESS = 8
|
||||
else:
|
||||
_LATEX_FOUND = search_for("lualatex")
|
||||
|
||||
def escape(text):
|
||||
lookup = {
|
||||
'&': '\\&',
|
||||
'%': '\\%',
|
||||
'$': '\\$',
|
||||
'#': '\\#',
|
||||
'_': '\\_',
|
||||
'{': '\\{',
|
||||
'}': '\\}',
|
||||
'~': '\\~{}',
|
||||
'^': '\\^{}',
|
||||
'\\': '\\textbackslash{}'
|
||||
}
|
||||
pattern = re.compile('|'.join([re.escape(key) for key in lookup.keys()]))
|
||||
return pattern.sub(lambda match: lookup[match.group(0)], text)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
#
|
||||
# TreeOptions
|
||||
#
|
||||
#------------------------------------------------------------------------------
|
||||
class TreeOptions:
|
||||
"""
|
||||
Defines all of the controls necessary
|
||||
to configure the genealogy tree reports.
|
||||
"""
|
||||
def add_menu_options(self, menu):
|
||||
"""
|
||||
Add all graph related options to the menu.
|
||||
|
||||
:param menu: The menu the options should be added to.
|
||||
:type menu: :class:`.Menu`
|
||||
:return: nothing
|
||||
"""
|
||||
################################
|
||||
category = _("Node Options")
|
||||
################################
|
||||
|
||||
detail = EnumeratedListOption(_("Node detail"), "full")
|
||||
for item in _DETAIL:
|
||||
detail.add_item(item["value"], item["name"])
|
||||
detail.set_help(_("Detail of information to be shown in a node."))
|
||||
menu.add_option(category, "detail", detail)
|
||||
|
||||
marriage = EnumeratedListOption(_("Marriage"), "")
|
||||
for item in _MARRIAGE:
|
||||
marriage.add_item(item["value"], item["name"])
|
||||
marriage.set_help(_("Position of marriage information."))
|
||||
menu.add_option(category, "marriage", marriage)
|
||||
|
||||
nodesize = NumberOption(_("Node size"), 25, 5, 100, 5)
|
||||
nodesize.set_help(_("One dimension of a node, in mm. If the timeflow "
|
||||
"is up or down then this is the width, otherwise "
|
||||
"it is the height."))
|
||||
menu.add_option(category, "nodesize", nodesize)
|
||||
|
||||
levelsize = NumberOption(_("Level size"), 35, 5, 100, 5)
|
||||
levelsize.set_help(_("One dimension of a node, in mm. If the timeflow "
|
||||
"is up or down then this is the height, otherwise "
|
||||
"it is the width."))
|
||||
menu.add_option(category, "levelsize", levelsize)
|
||||
|
||||
nodecolor = EnumeratedListOption(_("Color"), "none")
|
||||
for item in _COLOR:
|
||||
nodecolor.add_item(item["value"], item["name"])
|
||||
nodecolor.set_help(_("Node color."))
|
||||
menu.add_option(category, "nodecolor", nodecolor)
|
||||
|
||||
################################
|
||||
category = _("Tree Options")
|
||||
################################
|
||||
|
||||
timeflow = EnumeratedListOption(_("Timeflow"), "")
|
||||
for item in _TIMEFLOW:
|
||||
timeflow.add_item(item["value"], item["name"])
|
||||
timeflow.set_help(_("Direction that the graph will grow over time."))
|
||||
menu.add_option(category, "timeflow", timeflow)
|
||||
|
||||
edges = EnumeratedListOption(_("Edge style"), "")
|
||||
for item in _EDGES:
|
||||
edges.add_item(item["value"], item["name"])
|
||||
edges.set_help(_("Style of the edges between nodes."))
|
||||
menu.add_option(category, "edges", edges)
|
||||
|
||||
leveldist = NumberOption(_("Level distance"), 5, 1, 20, 1)
|
||||
leveldist.set_help(_("The minimum amount of free space, in mm, "
|
||||
"between levels. For vertical graphs, this "
|
||||
"corresponds to spacing between rows. For "
|
||||
"horizontal graphs, this corresponds to spacing "
|
||||
"between columns."))
|
||||
menu.add_option(category, "leveldist", leveldist)
|
||||
|
||||
################################
|
||||
category = _("Note")
|
||||
################################
|
||||
|
||||
note = TextOption(_("Note to add to the tree"), [""])
|
||||
note.set_help(_("This text will be added to the tree."))
|
||||
menu.add_option(category, "note", note)
|
||||
|
||||
noteloc = EnumeratedListOption(_("Note location"), 't')
|
||||
for item in _NOTELOC:
|
||||
noteloc.add_item(item["value"], item["name"])
|
||||
noteloc.set_help(_("Whether note will appear on top "
|
||||
"or bottom of the page."))
|
||||
menu.add_option(category, "noteloc", noteloc)
|
||||
|
||||
notesize = EnumeratedListOption(_("Note size"), 'normalsize')
|
||||
for item in _NOTESIZE:
|
||||
notesize.add_item(item["value"], item["name"])
|
||||
notesize.set_help(_("The size of note text."))
|
||||
menu.add_option(category, "notesize", notesize)
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
#
|
||||
# TreeDoc
|
||||
#
|
||||
#------------------------------------------------------------------------------
|
||||
class TreeDoc(metaclass=ABCMeta):
|
||||
"""
|
||||
Abstract Interface for genealogy tree document generators. Output formats
|
||||
for genealogy tree reports must implement this interface to be used by the
|
||||
report system.
|
||||
"""
|
||||
@abstractmethod
|
||||
def start_tree(self, option_list):
|
||||
"""
|
||||
Write the start of a tree.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def end_tree(self):
|
||||
"""
|
||||
Write the end of a tree.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def start_subgraph(self, level, subgraph_type, family, option_list=None):
|
||||
"""
|
||||
Write the start of a subgraph.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def end_subgraph(self, level):
|
||||
"""
|
||||
Write the end of a subgraph.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def write_node(self, db, level, node_type, person, marriage_flag,
|
||||
option_list=None):
|
||||
"""
|
||||
Write the contents of a node.
|
||||
"""
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
#
|
||||
# TreeDocBase
|
||||
#
|
||||
#------------------------------------------------------------------------------
|
||||
class TreeDocBase(BaseDoc, TreeDoc):
|
||||
"""
|
||||
Base document generator for all Graphviz document generators. Classes that
|
||||
inherit from this class will only need to implement the close function.
|
||||
The close function will generate the actual file of the appropriate type.
|
||||
"""
|
||||
def __init__(self, options, paper_style):
|
||||
BaseDoc.__init__(self, None, paper_style)
|
||||
|
||||
self._filename = None
|
||||
self._tex = StringIO()
|
||||
self._paper = paper_style
|
||||
|
||||
get_option = options.menu.get_option_by_name
|
||||
|
||||
self.detail = get_option('detail').get_value()
|
||||
self.marriage = get_option('marriage').get_value()
|
||||
self.nodesize = get_option('nodesize').get_value()
|
||||
self.levelsize = get_option('levelsize').get_value()
|
||||
self.nodecolor = get_option('nodecolor').get_value()
|
||||
|
||||
self.timeflow = get_option('timeflow').get_value()
|
||||
self.edges = get_option('edges').get_value()
|
||||
self.leveldist = get_option('leveldist').get_value()
|
||||
|
||||
self.note = get_option('note').get_value()
|
||||
self.noteloc = get_option('noteloc').get_value()
|
||||
self.notesize = get_option('notesize').get_value()
|
||||
|
||||
def write_start(self):
|
||||
"""
|
||||
Write the start of the document.
|
||||
"""
|
||||
paper_size = self._paper.get_size()
|
||||
name = paper_size.get_name().lower()
|
||||
if name == 'custom size':
|
||||
width = str(paper_size.get_width())
|
||||
height = str(paper_size.get_width())
|
||||
paper = 'papersize={%scm,%scm}' % (width, height)
|
||||
elif name in ('a', 'b', 'c', 'd', 'e'):
|
||||
paper = 'ansi' + name + 'paper'
|
||||
else:
|
||||
paper = name + 'paper'
|
||||
|
||||
if self._paper.get_orientation() == PAPER_PORTRAIT:
|
||||
orientation = 'portrait'
|
||||
else:
|
||||
orientation = 'landscape'
|
||||
|
||||
lmargin = self._paper.get_left_margin()
|
||||
rmargin = self._paper.get_right_margin()
|
||||
tmargin = self._paper.get_top_margin()
|
||||
bmargin = self._paper.get_bottom_margin()
|
||||
if lmargin == rmargin == tmargin == bmargin:
|
||||
margin = 'margin=%scm'% lmargin
|
||||
else:
|
||||
if lmargin == rmargin:
|
||||
margin = 'hmargin=%scm' % lmargin
|
||||
else:
|
||||
margin = 'hmargin={%scm,%scm}' % (lmargin, rmargin)
|
||||
if tmargin == bmargin:
|
||||
margin += ',vmargin=%scm' % tmargin
|
||||
else:
|
||||
margin += ',vmargin={%scm,%scm}' % (tmargin, bmargin)
|
||||
|
||||
self.write(0, '\\documentclass[%s]{article}\n' % orientation)
|
||||
|
||||
self.write(0, '\\IfFileExists{libertine.sty}{\n')
|
||||
self.write(0, ' \\usepackage{libertine}\n')
|
||||
self.write(0, '}{}\n')
|
||||
|
||||
self.write(0, '\\usepackage[%s,%s]{geometry}\n' % (paper, margin))
|
||||
self.write(0, '\\usepackage[all]{genealogytree}\n')
|
||||
self.write(0, '\\usepackage{color}\n')
|
||||
self.write(0, '\\begin{document}\n')
|
||||
|
||||
if self.nodecolor == 'preferences':
|
||||
scheme = config.get('colors.scheme')
|
||||
male_bg = config.get('colors.male-dead')[scheme][1:]
|
||||
female_bg = config.get('colors.female-dead')[scheme][1:]
|
||||
neuter_bg = config.get('colors.unknown-dead')[scheme][1:]
|
||||
self.write(0, '\\definecolor{male-bg}{HTML}{%s}\n' % male_bg)
|
||||
self.write(0, '\\definecolor{female-bg}{HTML}{%s}\n' % female_bg)
|
||||
self.write(0, '\\definecolor{neuter-bg}{HTML}{%s}\n' % neuter_bg)
|
||||
|
||||
if ''.join(self.note) != '' and self.noteloc == 't':
|
||||
for line in self.note:
|
||||
self.write(0, '{\\%s %s}\\par\n' % (self.notesize, line))
|
||||
self.write(0, '\\bigskip\n')
|
||||
|
||||
self.write(0, '\\begin{tikzpicture}\n')
|
||||
|
||||
def start_tree(self, option_list):
|
||||
self.write(0, '\\genealogytree[\n')
|
||||
self.write(0, 'processing=database,\n')
|
||||
if self.marriage:
|
||||
info = self.detail + ' ' + self.marriage
|
||||
else:
|
||||
info = self.detail
|
||||
self.write(0, 'database format=%s,\n' % info)
|
||||
if self.timeflow:
|
||||
self.write(0, 'timeflow=%s,\n' % self.timeflow)
|
||||
if self.edges:
|
||||
self.write(0, 'edges=%s,\n' % self.edges)
|
||||
if self.leveldist != 5:
|
||||
self.write(0, 'level distance=%smm,\n' % self.leveldist)
|
||||
if self.nodesize != 25:
|
||||
self.write(0, 'node size=%smm,\n' % self.nodesize)
|
||||
if self.levelsize != 35:
|
||||
self.write(0, 'level size=%smm,\n' % self.levelsize)
|
||||
if self.nodecolor == 'none':
|
||||
self.write(0, 'tcbset={male/.style={},\n')
|
||||
self.write(0, ' female/.style={},\n')
|
||||
self.write(0, ' neuter/.style={}},\n')
|
||||
if self.nodecolor == 'preferences':
|
||||
self.write(0, 'tcbset={male/.style={colback=male-bg},\n')
|
||||
self.write(0, ' female/.style={colback=female-bg},\n')
|
||||
self.write(0, ' neuter/.style={colback=neuter-bg}},\n')
|
||||
|
||||
for option in option_list:
|
||||
self.write(0, '%s,\n' % option)
|
||||
|
||||
self.write(0, ']{\n')
|
||||
|
||||
def end_tree(self):
|
||||
self.write(0, '}\n')
|
||||
|
||||
def write_end(self):
|
||||
"""
|
||||
Write the end of the document.
|
||||
"""
|
||||
self.write(0, '\\end{tikzpicture}\n')
|
||||
|
||||
if ''.join(self.note) != '' and self.noteloc == 'b':
|
||||
self.write(0, '\\bigskip\n')
|
||||
for line in self.note:
|
||||
self.write(0, '\\par{\\%s %s}\n' % (self.notesize, line))
|
||||
|
||||
self.write(0, '\\end{document}\n')
|
||||
|
||||
def start_subgraph(self, level, subgraph_type, family, option_list=None):
|
||||
options = ['id=%s' % family.gramps_id]
|
||||
if option_list:
|
||||
options.extend(option_list)
|
||||
if subgraph_type == 'sandclock':
|
||||
self.write(level, 'sandclock{\n')
|
||||
else:
|
||||
self.write(level, '%s[%s]{\n' % (subgraph_type, ','.join(options)))
|
||||
|
||||
def end_subgraph(self, level):
|
||||
self.write(level, '}\n')
|
||||
|
||||
def write_node(self, db, level, node_type, person, marriage_flag,
|
||||
option_list=None):
|
||||
options = ['id=%s' % person.gramps_id]
|
||||
if option_list:
|
||||
options.extend(option_list)
|
||||
self.write(level, '%s[%s]{\n' % (node_type, ','.join(options)))
|
||||
if person.gender == Person.MALE:
|
||||
self.write(level+1, 'male,\n')
|
||||
elif person.gender == Person.FEMALE:
|
||||
self.write(level+1, 'female,\n')
|
||||
elif person.gender == Person.UNKNOWN:
|
||||
self.write(level+1, 'neuter,\n')
|
||||
name = person.get_primary_name()
|
||||
nick = name.get_nick_name()
|
||||
surn = name.get_surname()
|
||||
name_parts = [self.format_given_names(name),
|
||||
'\\nick{{{}}}'.format(escape(nick)) if nick else '',
|
||||
'\\surn{{{}}}'.format(escape(surn)) if surn else '']
|
||||
self.write(level+1, 'name = {{{}}},\n'.format(
|
||||
' '.join([e for e in name_parts if e])))
|
||||
for eventref in person.get_event_ref_list():
|
||||
if eventref.role == EventRoleType.PRIMARY:
|
||||
event = db.get_event_from_handle(eventref.ref)
|
||||
self.write_event(db, level+1, event)
|
||||
if marriage_flag:
|
||||
for handle in person.get_family_handle_list():
|
||||
family = db.get_family_from_handle(handle)
|
||||
for eventref in family.get_event_ref_list():
|
||||
if eventref.role == EventRoleType.FAMILY:
|
||||
event = db.get_event_from_handle(eventref.ref)
|
||||
self.write_event(db, level+1, event)
|
||||
for attr in person.get_attribute_list():
|
||||
if str(attr.get_type()) == 'Occupation':
|
||||
self.write(level+1, 'profession = {%s},\n' %
|
||||
escape(attr.get_value()))
|
||||
if str(attr.get_type()) == 'Comment':
|
||||
self.write(level+1, 'comment = {%s},\n' %
|
||||
escape(attr.get_value()))
|
||||
for mediaref in person.get_media_list():
|
||||
media = db.get_media_from_handle(mediaref.ref)
|
||||
path = media_path_full(db, media.get_path())
|
||||
if os.path.isfile(path):
|
||||
if win():
|
||||
path = path.replace('\\', '/')
|
||||
self.write(level+1, 'image = {%s},\n' % path)
|
||||
break # first image only
|
||||
self.write(level, '}\n')
|
||||
|
||||
def write_event(self, db, level, event):
|
||||
"""
|
||||
Write an event.
|
||||
"""
|
||||
modifier = None
|
||||
if event.type == EventType.BIRTH:
|
||||
event_type = 'birth'
|
||||
if 'died' in event.description.lower():
|
||||
modifier = 'died'
|
||||
if 'stillborn' in event.description.lower():
|
||||
modifier = 'stillborn'
|
||||
# modifier = 'out of wedlock'
|
||||
elif event.type == EventType.BAPTISM:
|
||||
event_type = 'baptism'
|
||||
elif event.type == EventType.ENGAGEMENT:
|
||||
event_type = 'engagement'
|
||||
elif event.type == EventType.MARRIAGE:
|
||||
event_type = 'marriage'
|
||||
elif event.type == EventType.DIVORCE:
|
||||
event_type = 'divorce'
|
||||
elif event.type == EventType.DEATH:
|
||||
event_type = 'death'
|
||||
elif event.type == EventType.BURIAL:
|
||||
event_type = 'burial'
|
||||
if 'killed' in event.description.lower():
|
||||
modifier = 'killed'
|
||||
elif event.type == EventType.CREMATION:
|
||||
event_type = 'burial'
|
||||
modifier = 'cremated'
|
||||
else:
|
||||
return
|
||||
|
||||
date = event.get_date_object()
|
||||
|
||||
if date.get_calendar() == Date.CAL_GREGORIAN:
|
||||
calendar = 'AD' # GR
|
||||
elif date.get_calendar() == Date.CAL_JULIAN:
|
||||
calendar = 'JU'
|
||||
else:
|
||||
calendar = ''
|
||||
|
||||
if date.get_modifier() == Date.MOD_ABOUT:
|
||||
calendar = 'ca' + calendar
|
||||
|
||||
date_str = self.format_iso(date.get_ymd(), calendar)
|
||||
if date.get_modifier() == Date.MOD_BEFORE:
|
||||
date_str = '/' + date_str
|
||||
elif date.get_modifier() == Date.MOD_AFTER:
|
||||
date_str = date_str + '/'
|
||||
elif date.is_compound():
|
||||
stop_date = self.format_iso(date.get_stop_ymd(), calendar)
|
||||
date_str = date_str + '/' + stop_date
|
||||
|
||||
place = escape(_pd.display_event(db, event))
|
||||
|
||||
if modifier:
|
||||
event_type += '+'
|
||||
self.write(level, '%s = {%s}{%s}{%s},\n' %
|
||||
(event_type, date_str, place, modifier))
|
||||
elif place == '':
|
||||
event_type += '-'
|
||||
self.write(level, '%s = {%s},\n' % (event_type, date_str))
|
||||
else:
|
||||
self.write(level, '%s = {%s}{%s},\n' %
|
||||
(event_type, date_str, place))
|
||||
|
||||
def format_given_names(self, name):
|
||||
"""
|
||||
Format given names.
|
||||
"""
|
||||
first = name.get_first_name()
|
||||
call = name.get_call_name()
|
||||
if call:
|
||||
if call in first:
|
||||
where = first.index(call)
|
||||
return '{before}\\pref{{{call}}}{after}'.format(
|
||||
before=escape(first[:where]),
|
||||
call=escape(call),
|
||||
after=escape(first[where+len(call):]))
|
||||
else:
|
||||
# ignore erroneous call name
|
||||
return escape(first)
|
||||
else:
|
||||
return escape(first)
|
||||
|
||||
def format_iso(self, date_tuple, calendar):
|
||||
"""
|
||||
Format an iso date.
|
||||
"""
|
||||
year, month, day = date_tuple
|
||||
if year == 0:
|
||||
iso_date = ''
|
||||
elif month == 0:
|
||||
iso_date = str(year)
|
||||
elif day == 0:
|
||||
iso_date = '%s-%s' % (year, month)
|
||||
else:
|
||||
iso_date = '%s-%s-%s' % (year, month, day)
|
||||
if calendar and calendar != 'AD':
|
||||
iso_date = '(%s)%s' % (calendar, iso_date)
|
||||
return iso_date
|
||||
|
||||
def write(self, level, text):
|
||||
"""
|
||||
Write indented text.
|
||||
"""
|
||||
self._tex.write(' '*level + text)
|
||||
|
||||
def open(self, filename):
|
||||
""" Implement TreeDocBase.open() """
|
||||
self._filename = os.path.normpath(os.path.abspath(filename))
|
||||
self.write_start()
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
This isn't useful by itself. Other classes need to override this and
|
||||
actually generate a file.
|
||||
"""
|
||||
self.write_end()
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
#
|
||||
# TreeTexDoc
|
||||
#
|
||||
#------------------------------------------------------------------------------
|
||||
class TreeTexDoc(TreeDocBase):
|
||||
"""
|
||||
TreeTexDoc implementation that generates a .tex file.
|
||||
"""
|
||||
|
||||
def close(self):
|
||||
""" Implements TreeDocBase.close() """
|
||||
TreeDocBase.close(self)
|
||||
|
||||
# Make sure the extension is correct
|
||||
if self._filename[-4:] != ".tex":
|
||||
self._filename += ".tex"
|
||||
|
||||
with open(self._filename, 'w', encoding='utf-8') as texfile:
|
||||
texfile.write(self._tex.getvalue())
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
#
|
||||
# TreePdfDoc
|
||||
#
|
||||
#------------------------------------------------------------------------------
|
||||
class TreePdfDoc(TreeDocBase):
|
||||
"""
|
||||
TreePdfDoc implementation that generates a .pdf file.
|
||||
"""
|
||||
|
||||
def close(self):
|
||||
""" Implements TreeDocBase.close() """
|
||||
TreeDocBase.close(self)
|
||||
|
||||
# Make sure the extension is correct
|
||||
if self._filename[-4:] != ".pdf":
|
||||
self._filename += ".pdf"
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
basename = os.path.basename(self._filename)
|
||||
args = ['lualatex', '-output-directory', tmpdir,
|
||||
'-jobname', basename[:-4]]
|
||||
if win():
|
||||
proc = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE,
|
||||
creationflags=DETACHED_PROCESS)
|
||||
else:
|
||||
proc = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE)
|
||||
proc.communicate(input=self._tex.getvalue().encode('utf-8'))
|
||||
shutil.copy(os.path.join(tmpdir, basename), self._filename)
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
#
|
||||
# Various Genealogy Tree formats.
|
||||
#
|
||||
#------------------------------------------------------------------------------
|
||||
FORMATS = []
|
||||
|
||||
if _LATEX_FOUND:
|
||||
FORMATS += [{'type' : "pdf",
|
||||
'ext' : "pdf",
|
||||
'descr': _("PDF"),
|
||||
'mime' : "application/pdf",
|
||||
'class': TreePdfDoc}]
|
||||
|
||||
FORMATS += [{'type' : "tex",
|
||||
'ext' : "tex",
|
||||
'descr': _("LaTeX File"),
|
||||
'mime' : "application/x-latex",
|
||||
'class': TreeTexDoc}]
|
||||
@@ -495,14 +495,14 @@ class BookList:
|
||||
"""
|
||||
Saves the current BookList to the associated file.
|
||||
"""
|
||||
with open(self.file, "w") as b_f:
|
||||
with open(self.file, "w", encoding="utf-8") as b_f:
|
||||
b_f.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n")
|
||||
b_f.write('<booklist>\n')
|
||||
for name in sorted(self.bookmap): # enable a diff of archived copies
|
||||
book = self.get_book(name)
|
||||
dbname = book.get_dbname()
|
||||
dbname = escape(book.get_dbname())
|
||||
b_f.write(' <book name="%s" database="%s">'
|
||||
'\n' % (name, dbname))
|
||||
'\n' % (escape(name), dbname))
|
||||
for item in book.get_item_list():
|
||||
b_f.write(' <item name="%s" '
|
||||
'trans_name="%s">\n' % (
|
||||
@@ -566,7 +566,7 @@ class BookList:
|
||||
'\n' % book.get_format_name())
|
||||
if book.get_output():
|
||||
b_f.write(' <output name="%s"/>'
|
||||
'\n' % book.get_output())
|
||||
'\n' % escape(book.get_output()))
|
||||
b_f.write(' </book>\n')
|
||||
|
||||
b_f.write('</booklist>\n')
|
||||
@@ -578,8 +578,15 @@ class BookList:
|
||||
try:
|
||||
parser = make_parser()
|
||||
parser.setContentHandler(BookParser(self, self.dbase))
|
||||
with open(self.file) as the_file:
|
||||
parser.parse(the_file)
|
||||
# bug 10387; XML should be utf8, but was not previously saved
|
||||
# that way. So try to read utf8, if fails, try with system
|
||||
# encoding. Only an issue on non-utf8 systems.
|
||||
try:
|
||||
with open(self.file, encoding="utf-8") as the_file:
|
||||
parser.parse(the_file)
|
||||
except UnicodeDecodeError:
|
||||
with open(self.file) as the_file:
|
||||
parser.parse(the_file)
|
||||
except (IOError, OSError, ValueError, SAXParseException, KeyError,
|
||||
AttributeError):
|
||||
LOG.debug("Failed to parse book list", exc_info=True)
|
||||
|
||||
@@ -39,7 +39,7 @@ import os
|
||||
|
||||
# Report categories
|
||||
from .. import (CATEGORY_TEXT, CATEGORY_DRAW, CATEGORY_CODE, CATEGORY_WEB,
|
||||
CATEGORY_BOOK, CATEGORY_GRAPHVIZ)
|
||||
CATEGORY_BOOK, CATEGORY_GRAPHVIZ, CATEGORY_TREE)
|
||||
|
||||
standalone_categories = {
|
||||
CATEGORY_TEXT : ("RepText", _("Text Reports")),
|
||||
@@ -48,6 +48,7 @@ standalone_categories = {
|
||||
CATEGORY_WEB : ("RepWeb", _("Web Pages")),
|
||||
CATEGORY_BOOK : ("RepBook", _("Books")),
|
||||
CATEGORY_GRAPHVIZ : ("Graphs", _("Graphs")),
|
||||
CATEGORY_TREE : ("Trees", _("Trees")),
|
||||
}
|
||||
book_categories = {
|
||||
CATEGORY_TEXT : _("Text"),
|
||||
|
||||
@@ -32,6 +32,7 @@ from ...config import config
|
||||
from ...datehandler import get_date_formats, LANG_TO_DISPLAY, main_locale
|
||||
from ...display.name import displayer as global_name_display
|
||||
from ...lib.date import Today
|
||||
from ...display.place import displayer as _pd
|
||||
from ..menu import EnumeratedListOption, BooleanOption, NumberOption
|
||||
from ...proxy import PrivateProxyDb, LivingProxyDb
|
||||
from ...utils.grampslocale import GrampsLocale
|
||||
@@ -327,3 +328,16 @@ def add_gramps_id_option(menu, category, ownline=False):
|
||||
include_id.add_item(1, _('Include'))
|
||||
include_id.set_help(_("Whether to include Gramps IDs"))
|
||||
menu.add_option(category, 'inc_id', include_id)
|
||||
|
||||
def add_place_format_option(menu, category):
|
||||
"""
|
||||
Insert an option for changing the report's place format to a
|
||||
report-specific format instead of the user's Edit=>Preferences choice
|
||||
"""
|
||||
place_format = EnumeratedListOption(_("Place format"), None)
|
||||
place_format.add_item(None, _("Default"))
|
||||
for number, fmt in enumerate(_pd.get_formats()):
|
||||
place_format.add_item(number, fmt.name)
|
||||
place_format.set_help(_("Select the format to display places"))
|
||||
menu.add_option(category, "place_format", place_format)
|
||||
return place_format
|
||||
|
||||
@@ -31,6 +31,7 @@ import csv
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from .tabbeddoc import *
|
||||
from ...constfunc import win
|
||||
|
||||
class CSVTab(TabbedDoc):
|
||||
|
||||
@@ -48,7 +49,8 @@ class CSVTab(TabbedDoc):
|
||||
else:
|
||||
self.filename = filename
|
||||
|
||||
self.f = open(self.filename, "w")
|
||||
self.f = open(self.filename, "w",
|
||||
encoding='utf_8_sig' if win() else 'utf_8')
|
||||
self.writer = csv.writer(self.f)
|
||||
|
||||
def close(self):
|
||||
|
||||
+148
-99
@@ -53,6 +53,7 @@ from gramps.gen.const import HOME_DIR, URL_WIKISTRING
|
||||
from gramps.gen.datehandler import get_date_formats
|
||||
from gramps.gen.display.name import displayer as _nd
|
||||
from gramps.gen.display.name import NameDisplayError
|
||||
from gramps.gen.display.place import displayer as _pd
|
||||
from gramps.gen.utils.alive import update_constants
|
||||
from gramps.gen.utils.file import media_path
|
||||
from gramps.gen.utils.keyword import (get_keywords, get_translation_from_keyword,
|
||||
@@ -62,6 +63,7 @@ from gramps.gen.lib import Name, Surname, NameOriginType
|
||||
from .managedwindow import ManagedWindow
|
||||
from .widgets import MarkupLabel, BasicLabel
|
||||
from .dialog import ErrorDialog, QuestionDialog2, OkDialog
|
||||
from .editors.editplaceformat import EditPlaceFormat
|
||||
from .glade import Glade
|
||||
from gramps.gen.plug.utils import available_updates
|
||||
from .plug import PluginWindows
|
||||
@@ -249,13 +251,23 @@ class ConfigureDialog(ManagedWindow):
|
||||
"""
|
||||
self.__config.set(constant, obj.get_text())
|
||||
|
||||
def update_color(self, obj, constant, color_hex_label):
|
||||
def update_color(self, obj, pspec, constant, color_hex_label):
|
||||
"""
|
||||
Called on changing some color.
|
||||
Either on programmatically color change.
|
||||
"""
|
||||
rgba = obj.get_rgba()
|
||||
hexval = "#%02x%02x%02x" % (int(rgba.red * 255),
|
||||
int(rgba.green * 255),
|
||||
int(rgba.blue * 255))
|
||||
color_hex_label.set_text(hexval)
|
||||
self.__config.set(constant, hexval)
|
||||
colors = self.__config.get(constant)
|
||||
if isinstance(colors, list):
|
||||
scheme = self.__config.get('colors.scheme')
|
||||
colors[scheme] = hexval
|
||||
self.__config.set(constant, colors)
|
||||
else:
|
||||
self.__config.set(constant, hexval)
|
||||
|
||||
def update_checkbox(self, obj, constant, config=None):
|
||||
if not config:
|
||||
@@ -383,15 +395,24 @@ class ConfigureDialog(ManagedWindow):
|
||||
grid.attach(entry, col_attach+1, index, 1, 1)
|
||||
|
||||
def add_color(self, grid, label, index, constant, config=None, col=0):
|
||||
"""
|
||||
Add color chooser widget with label to the grid.
|
||||
"""
|
||||
if not config:
|
||||
config = self.__config
|
||||
lwidget = BasicLabel(_("%s: ") % label) # needed for French, else ignore
|
||||
hexval = config.get(constant)
|
||||
colors = config.get(constant)
|
||||
if isinstance(colors, list):
|
||||
scheme = config.get('colors.scheme')
|
||||
hexval = colors[scheme]
|
||||
else:
|
||||
hexval = colors
|
||||
color = Gdk.color_parse(hexval)
|
||||
entry = Gtk.ColorButton(color=color)
|
||||
color_hex_label = BasicLabel(hexval)
|
||||
color_hex_label.set_hexpand(True)
|
||||
entry.connect('color-set', self.update_color, constant, color_hex_label)
|
||||
entry.connect('notify::color', self.update_color, constant,
|
||||
color_hex_label)
|
||||
grid.attach(lwidget, col, index, 1, 1)
|
||||
grid.attach(entry, col+1, index, 1, 1)
|
||||
grid.attach(color_hex_label, col+2, index, 1, 1)
|
||||
@@ -492,7 +513,6 @@ class GrampsPreferences(ConfigureDialog):
|
||||
self.add_behavior_panel,
|
||||
self.add_famtree_panel,
|
||||
self.add_formats_panel,
|
||||
self.add_places_panel,
|
||||
self.add_text_panel,
|
||||
self.add_prefix_panel,
|
||||
self.add_date_panel,
|
||||
@@ -554,7 +574,7 @@ class GrampsPreferences(ConfigureDialog):
|
||||
|
||||
def add_color_panel(self, configdialog):
|
||||
"""
|
||||
Add the tab to set defaults colors for graph boxes
|
||||
Add the tab to set defaults colors for graph boxes.
|
||||
"""
|
||||
grid = Gtk.Grid()
|
||||
grid.set_border_width(12)
|
||||
@@ -562,40 +582,62 @@ class GrampsPreferences(ConfigureDialog):
|
||||
grid.set_row_spacing(6)
|
||||
self.add_text(grid, _('Set the colors used for boxes in the graphical views'),
|
||||
0, line_wrap=False)
|
||||
self.add_color(grid, _('Gender Male Alive'), 1,
|
||||
'preferences.color-gender-male-alive')
|
||||
self.add_color(grid, _('Border Male Alive'), 2,
|
||||
'preferences.bordercolor-gender-male-alive')
|
||||
self.add_color(grid, _('Gender Male Death'), 3,
|
||||
'preferences.color-gender-male-death')
|
||||
self.add_color(grid, _('Border Male Death'), 4,
|
||||
'preferences.bordercolor-gender-male-death')
|
||||
self.add_color(grid, _('Gender Female Alive'), 1,
|
||||
'preferences.color-gender-female-alive', col=4)
|
||||
self.add_color(grid, _('Border Female Alive'), 2,
|
||||
'preferences.bordercolor-gender-female-alive', col=4)
|
||||
self.add_color(grid, _('Gender Female Death'), 3,
|
||||
'preferences.color-gender-female-death', col=4)
|
||||
self.add_color(grid, _('Border Female Death'), 4,
|
||||
'preferences.bordercolor-gender-female-death', col=4)
|
||||
## self.add_color(grid, _('Gender Other Alive'), 5,
|
||||
## 'preferences.color-gender-other-alive')
|
||||
## self.add_color(grid, _('Border Other Alive'), 6,
|
||||
## 'preferences.bordercolor-gender-other-alive')
|
||||
## self.add_color(grid, _('Gender Other Death'), 7,
|
||||
## 'preferences.color-gender-other-death')
|
||||
## self.add_color(grid, _('Border Other Death'), 8,
|
||||
## 'preferences.bordercolor-gender-other-death')
|
||||
self.add_color(grid, _('Gender Unknown Alive'), 5,
|
||||
'preferences.color-gender-unknown-alive', col=4)
|
||||
self.add_color(grid, _('Border Unknown Alive'), 6,
|
||||
'preferences.bordercolor-gender-unknown-alive', col=4)
|
||||
self.add_color(grid, _('Gender Unknown Death'), 7,
|
||||
'preferences.color-gender-unknown-death', col=4)
|
||||
self.add_color(grid, _('Border Unknown Death'), 8,
|
||||
'preferences.bordercolor-gender-unknown-death', col=4)
|
||||
|
||||
hbox = Gtk.Box(spacing=12)
|
||||
self.color_scheme_box = Gtk.ComboBoxText()
|
||||
formats = [_("Light colors"),
|
||||
_("Dark colors"),]
|
||||
list(map(self.color_scheme_box.append_text, formats))
|
||||
scheme = config.get('colors.scheme')
|
||||
self.color_scheme_box.set_active(scheme)
|
||||
self.color_scheme_box.connect('changed', self.color_scheme_changed)
|
||||
lwidget = BasicLabel(_("%s: ") % _('Color scheme'))
|
||||
hbox.pack_start(lwidget, False, False, 0)
|
||||
hbox.pack_start(self.color_scheme_box, False, False, 0)
|
||||
|
||||
restore_btn = Gtk.Button(_('Restore to defaults'))
|
||||
restore_btn.connect('clicked', self.restore_colors)
|
||||
hbox.pack_start(restore_btn, False, False, 0)
|
||||
grid.attach(hbox, 1, 1, 6, 1)
|
||||
|
||||
color_list = [
|
||||
(_('Male Alive'), 'male-alive', 2, 0),
|
||||
(_('Male Dead'), 'male-dead', 4, 0),
|
||||
(_('Female Alive'), 'female-alive', 2, 4),
|
||||
(_('Female Dead'), 'female-dead', 4, 4),
|
||||
(_('Unknown Alive'), 'unknown-alive', 6, 4),
|
||||
(_('Unknown Dead'), 'unknown-dead', 8, 4),
|
||||
(_('Family Node'), 'family', 7, 0),
|
||||
(_('Family Divorced'), 'family-divorced', 9, 0),
|
||||
(_('Home Person'), 'home-person', 6, 0),
|
||||
(_('Border Male Alive'), 'border-male-alive', 3, 0),
|
||||
(_('Border Male Dead'), 'border-male-dead', 5, 0),
|
||||
(_('Border Female Alive'), 'border-female-alive', 3, 4),
|
||||
(_('Border Female Dead'), 'border-female-dead', 5, 4),
|
||||
(_('Border Unknown Alive'), 'border-unknown-alive', 7, 4),
|
||||
(_('Border Unknown Dead'), 'border-unknown-dead', 9, 4),
|
||||
(_('Border Family'), 'border-family', 8, 0),
|
||||
(_('Border Family Divorced'), 'border-family-divorced', 10, 0),
|
||||
]
|
||||
|
||||
self.colors = {}
|
||||
for color in color_list:
|
||||
pref_name = 'colors.' + color[1]
|
||||
self.colors[pref_name] = self.add_color(grid, color[0], color[2],
|
||||
pref_name, col=color[3])
|
||||
return _('Colors'), grid
|
||||
|
||||
def restore_colors(self, widget=None):
|
||||
"""
|
||||
Restore colors of selected scheme to default.
|
||||
"""
|
||||
scheme = config.get('colors.scheme')
|
||||
for key, widget in self.colors.items():
|
||||
color = Gdk.RGBA()
|
||||
hexval = config.get_default(key)[scheme]
|
||||
Gdk.RGBA.parse(color, hexval)
|
||||
widget.set_rgba(color)
|
||||
|
||||
def add_advanced_panel(self, configdialog):
|
||||
grid = Gtk.Grid()
|
||||
grid.set_border_width(12)
|
||||
@@ -890,6 +932,13 @@ class GrampsPreferences(ConfigureDialog):
|
||||
_nd.set_default_format(new_idx)
|
||||
self.uistate.emit('nameformat-changed')
|
||||
|
||||
def cb_place_fmt_changed(self, obj):
|
||||
"""
|
||||
Called when the place format is changed.
|
||||
"""
|
||||
config.set('preferences.place-format', obj.get_active())
|
||||
self.uistate.emit('placeformat-changed')
|
||||
|
||||
def cb_pa_sur_changed(self,*args):
|
||||
"""
|
||||
checkbox patronymic as surname changed, propagate to namedisplayer
|
||||
@@ -1011,6 +1060,34 @@ class GrampsPreferences(ConfigureDialog):
|
||||
grid.attach(obox, 1, row, 2, 1)
|
||||
row += 1
|
||||
|
||||
# Place format:
|
||||
self.pformat = Gtk.ComboBox()
|
||||
renderer = Gtk.CellRendererText()
|
||||
self.pformat.pack_start(renderer, True)
|
||||
self.pformat.add_attribute(renderer, "text", 0)
|
||||
self.cb_place_fmt_rebuild()
|
||||
active = config.get('preferences.place-format')
|
||||
self.pformat.set_active(active)
|
||||
self.pformat.connect('changed', self.cb_place_fmt_changed)
|
||||
lwidget = BasicLabel(_("%s: ") % _('Place format'))
|
||||
lwidget.set_use_underline(True)
|
||||
lwidget.set_mnemonic_widget(obox)
|
||||
hbox = Gtk.Box()
|
||||
self.fmt_btn = Gtk.Button(label=("%s..." % _('Edit')))
|
||||
self.fmt_btn.connect('clicked', self.cb_place_fmt_dialog)
|
||||
hbox.pack_start(self.pformat, True, True, 0)
|
||||
hbox.pack_start(self.fmt_btn, False, False, 0)
|
||||
grid.attach(lwidget, 0, row, 1, 1)
|
||||
grid.attach(hbox, 1, row, 2, 1)
|
||||
row += 1
|
||||
|
||||
auto = self.add_checkbox(grid,
|
||||
_("Enable automatic place title generation"),
|
||||
row, 'preferences.place-auto',
|
||||
extra_callback=self.auto_title_changed)
|
||||
self.auto_title_changed(auto)
|
||||
row += 1
|
||||
|
||||
# Age precision:
|
||||
# precision=1 for "year", 2: "year, month" or 3: "year, month, days"
|
||||
obox = Gtk.ComboBoxText()
|
||||
@@ -1110,66 +1187,13 @@ class GrampsPreferences(ConfigureDialog):
|
||||
row += 1
|
||||
return _('Display'), grid
|
||||
|
||||
def add_places_panel(self, configdialog):
|
||||
grid = Gtk.Grid()
|
||||
grid.set_border_width(12)
|
||||
grid.set_column_spacing(6)
|
||||
grid.set_row_spacing(6)
|
||||
|
||||
auto = self.add_checkbox(grid,
|
||||
_("Enable automatic place title generation"),
|
||||
0, 'preferences.place-auto',
|
||||
extra_callback=self.auto_title_changed)
|
||||
|
||||
row = 0
|
||||
grid2 = Gtk.Grid()
|
||||
grid2.set_border_width(12)
|
||||
grid2.set_column_spacing(6)
|
||||
grid2.set_row_spacing(6)
|
||||
grid.attach(grid2, 1, 1, 1, 1)
|
||||
|
||||
self.place_widgets = []
|
||||
cbox = self.add_checkbox(grid2, _("Suppress comma after house number"),
|
||||
row, 'preferences.place-number', start=0)
|
||||
self.place_widgets.append(cbox)
|
||||
row += 1
|
||||
|
||||
cbox = self.add_checkbox(grid2, _("Reverse display order"),
|
||||
row, 'preferences.place-reverse', start=0)
|
||||
self.place_widgets.append(cbox)
|
||||
row += 1
|
||||
|
||||
# Place restriction
|
||||
obox = Gtk.ComboBoxText()
|
||||
formats = [_("Full place name"),
|
||||
_("-> Hamlet/Village/Town/City"),
|
||||
_("Hamlet/Village/Town/City ->")]
|
||||
list(map(obox.append_text, formats))
|
||||
active = config.get('preferences.place-restrict')
|
||||
obox.set_active(active)
|
||||
obox.connect('changed', self.place_restrict_changed)
|
||||
lwidget = BasicLabel(_("%s: ") % _('Restrict'))
|
||||
grid2.attach(lwidget, 0, row, 1, 1)
|
||||
grid2.attach(obox, 1, row, 2, 1)
|
||||
self.place_widgets.append(obox)
|
||||
row += 1
|
||||
|
||||
entry = self.add_entry(grid2, _("Language"),
|
||||
row, 'preferences.place-lang')
|
||||
self.place_widgets.append(entry)
|
||||
row += 1
|
||||
|
||||
self.auto_title_changed(auto)
|
||||
|
||||
return _('Places'), grid
|
||||
|
||||
def auto_title_changed(self, obj):
|
||||
"""
|
||||
Update sensitivity of place configuration widgets.
|
||||
Update sensitivity of place format widget.
|
||||
"""
|
||||
active = obj.get_active()
|
||||
for widget in self.place_widgets:
|
||||
widget.set_sensitive(active)
|
||||
active = config.get('preferences.place-auto')
|
||||
self.pformat.set_sensitive(active)
|
||||
self.fmt_btn.set_sensitive(active)
|
||||
|
||||
def add_text_panel(self, configdialog):
|
||||
row = 0
|
||||
@@ -1205,6 +1229,35 @@ class GrampsPreferences(ConfigureDialog):
|
||||
self.old_format = the_list.get_value(the_iter, COL_FMT)
|
||||
win = DisplayNameEditor(self.uistate, self.dbstate, self.track, self)
|
||||
|
||||
def color_scheme_changed(self, obj):
|
||||
"""
|
||||
Called on swiching color scheme.
|
||||
"""
|
||||
scheme = obj.get_active()
|
||||
config.set('colors.scheme', scheme)
|
||||
for key, widget in self.colors.items():
|
||||
color = Gdk.RGBA()
|
||||
hexval = config.get(key)[scheme]
|
||||
Gdk.RGBA.parse(color, hexval)
|
||||
widget.set_rgba(color)
|
||||
|
||||
def cb_place_fmt_dialog(self, button):
|
||||
"""
|
||||
Called to invoke the place format editor.
|
||||
"""
|
||||
EditPlaceFormat(self.uistate, self.dbstate, self.track,
|
||||
self.cb_place_fmt_rebuild)
|
||||
|
||||
def cb_place_fmt_rebuild(self):
|
||||
"""
|
||||
Called to rebuild the place format list.
|
||||
"""
|
||||
model = Gtk.ListStore(str)
|
||||
for fmt in _pd.get_formats():
|
||||
model.append([fmt.name])
|
||||
self.pformat.set_model(model)
|
||||
self.pformat.set_active(0)
|
||||
|
||||
def check_for_type_changed(self, obj):
|
||||
active = obj.get_active()
|
||||
if active == 0: # update
|
||||
@@ -1228,10 +1281,6 @@ class GrampsPreferences(ConfigureDialog):
|
||||
active = obj.get_active()
|
||||
config.set('behavior.check-for-addon-updates', active)
|
||||
|
||||
def place_restrict_changed(self, obj):
|
||||
active = obj.get_active()
|
||||
config.set('preferences.place-restrict', active)
|
||||
|
||||
def date_format_changed(self, obj):
|
||||
config.set('preferences.date-format', obj.get_active())
|
||||
OkDialog(_('Change is not immediate'),
|
||||
|
||||
@@ -35,6 +35,7 @@ _LOG = logging.getLogger(".dialog")
|
||||
#-------------------------------------------------------------------------
|
||||
from gi.repository import GObject
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import Gdk
|
||||
from gi.repository import GdkPixbuf
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@@ -46,6 +47,7 @@ from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
from gramps.gen.const import ICON, URL_BUGHOME
|
||||
from gramps.gen.config import config
|
||||
from gramps.gen.constfunc import is_quartz
|
||||
from .glade import Glade
|
||||
from .display import display_url
|
||||
|
||||
@@ -506,6 +508,12 @@ def main(args):
|
||||
win = Gtk.Window()
|
||||
win.set_title('Dialog test window')
|
||||
win.set_position(Gtk.WindowPosition.CENTER)
|
||||
#Set the mnemonic modifier on Macs to alt-ctrl so that it
|
||||
#doesn't interfere with the extended keyboard, see
|
||||
#https://gramps-project.org/bugs/view.php?id=6943
|
||||
if is_quartz():
|
||||
win.set_mnemonic_modifier(
|
||||
Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.MOD1_MASK)
|
||||
def cb(window, event):
|
||||
Gtk.main_quit()
|
||||
win.connect('delete-event', cb)
|
||||
|
||||
@@ -372,6 +372,7 @@ class DisplayState(Callback):
|
||||
'filters-changed' : (str, ),
|
||||
'filter-name-changed' : (str, str, str),
|
||||
'nameformat-changed' : None,
|
||||
'placeformat-changed' : None,
|
||||
'grampletbar-close-changed' : None,
|
||||
'update-available' : (list, ),
|
||||
'autobackup' : None,
|
||||
|
||||
@@ -42,6 +42,7 @@ from gi.repository import Pango
|
||||
# Gramps classes
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from ...widgets.cellrenderertextedit import CellRendererTextEdit
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
from ...utils import is_right_click
|
||||
@@ -482,7 +483,10 @@ class EmbeddedList(ButtonTab):
|
||||
type_col = self._column_names[pair[1]][3]
|
||||
|
||||
if (type_col in [TEXT_COL, MARKUP_COL, TEXT_EDIT_COL]):
|
||||
renderer = Gtk.CellRendererText()
|
||||
if type_col == TEXT_EDIT_COL:
|
||||
renderer = CellRendererTextEdit()
|
||||
else:
|
||||
renderer = Gtk.CellRendererText()
|
||||
renderer.set_property('ellipsize', Pango.EllipsizeMode.END)
|
||||
if type_col == TEXT_COL or type_col == TEXT_EDIT_COL:
|
||||
column = Gtk.TreeViewColumn(name, renderer, text=pair[1])
|
||||
@@ -519,9 +523,12 @@ class EmbeddedList(ButtonTab):
|
||||
# insert the colum into the tree
|
||||
column.set_resizable(True)
|
||||
column.set_clickable(True)
|
||||
column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
|
||||
#column.set_min_width(self._column_names[pair[1]][2])
|
||||
column.set_fixed_width(self._column_names[pair[1]][2])
|
||||
if self._column_names[pair[1]][2] != -1:
|
||||
column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
|
||||
#column.set_min_width(self._column_names[pair[1]][2])
|
||||
column.set_fixed_width(self._column_names[pair[1]][2])
|
||||
else:
|
||||
column.set_expand(True)
|
||||
|
||||
column.set_sort_column_id(self._column_names[pair[1]][1])
|
||||
self.columns.append(column)
|
||||
|
||||
@@ -45,7 +45,7 @@ _ENTER = Gdk.keyval_from_name("Enter")
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from .surnamemodel import SurnameModel
|
||||
from .embeddedlist import EmbeddedList, TEXT_COL, MARKUP_COL, ICON_COL
|
||||
from .embeddedlist import EmbeddedList, TEXT_EDIT_COL
|
||||
from ...ddtargets import DdTargets
|
||||
from gramps.gen.lib import Surname, NameOriginType
|
||||
from ...utils import get_primary_mask
|
||||
@@ -71,9 +71,9 @@ class SurnameTab(EmbeddedList):
|
||||
#index = column in model. Value =
|
||||
# (name, sortcol in model, width, markup/text
|
||||
_column_names = [
|
||||
(_('Prefix'), -1, 150, TEXT_COL, -1, None),
|
||||
(_('Surname'), -1, 250, TEXT_COL, -1, None),
|
||||
(_('Connector'), -1, 100, TEXT_COL, -1, None),
|
||||
(_('Prefix'), 0, 150, TEXT_EDIT_COL, -1, None),
|
||||
(_('Surname'), 1, -1, TEXT_EDIT_COL, -1, None),
|
||||
(_('Connector'), 2, 100, TEXT_EDIT_COL, -1, None),
|
||||
]
|
||||
_column_combo = (_('Origin'), -1, 150, 3) # name, sort, width, modelcol
|
||||
_column_toggle = (_('Name|Primary'), -1, 80, 4)
|
||||
@@ -94,14 +94,6 @@ class SurnameTab(EmbeddedList):
|
||||
#first the standard text columns with normal method
|
||||
EmbeddedList.build_columns(self)
|
||||
|
||||
# Need to add attributes to renderers
|
||||
# and connect renderers to the 'edited' signal
|
||||
for colno in range(len(self.columns)):
|
||||
for renderer in self.columns[colno].get_cells():
|
||||
renderer.set_property('editable', not self.dbstate.db.readonly)
|
||||
renderer.connect('editing_started', self.on_edit_start, colno)
|
||||
renderer.connect('edited', self.on_edit_inline, self.column_order()[colno][1])
|
||||
|
||||
# now we add the two special columns
|
||||
# combobox for type
|
||||
colno = len(self.columns)
|
||||
@@ -133,7 +125,7 @@ class SurnameTab(EmbeddedList):
|
||||
column.set_resizable(True)
|
||||
column.set_sort_column_id(self._column_combo[1])
|
||||
column.set_min_width(self._column_combo[2])
|
||||
column.set_expand(True)
|
||||
column.set_expand(False)
|
||||
self.columns.append(column)
|
||||
self.tree.append_column(column)
|
||||
# toggle box for primary
|
||||
@@ -149,7 +141,7 @@ class SurnameTab(EmbeddedList):
|
||||
column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
|
||||
column.set_alignment(0.5)
|
||||
column.set_sort_column_id(self._column_toggle[1])
|
||||
column.set_min_width(self._column_toggle[2])
|
||||
column.set_max_width(self._column_toggle[2])
|
||||
self.columns.append(column)
|
||||
self.tree.append_column(column)
|
||||
|
||||
@@ -161,6 +153,24 @@ class SurnameTab(EmbeddedList):
|
||||
## svalue = self.cmborigmap[second]
|
||||
## return glocale.strcoll(fvalue, svalue)
|
||||
|
||||
def setup_editable_col(self):
|
||||
"""
|
||||
inherit this and set the variables needed for editable columns
|
||||
Variable edit_col_funcs needs to be a dictionary from model col_nr to
|
||||
function to call for
|
||||
Example:
|
||||
self.edit_col_funcs ={1: {'edit_start': self.on_edit_start,
|
||||
'edited': self.on_edited
|
||||
}}
|
||||
"""
|
||||
self.edit_col_funcs = {
|
||||
0: {'edit_start': self.on_edit_start,
|
||||
'edited': self.on_edit_inline},
|
||||
1: {'edit_start': self.on_edit_start,
|
||||
'edited': self.on_edit_inline},
|
||||
2: {'edit_start': self.on_edit_start,
|
||||
'edited': self.on_edit_inline}}
|
||||
|
||||
def get_data(self):
|
||||
return self.obj.get_surname_list()
|
||||
|
||||
@@ -194,6 +204,16 @@ class SurnameTab(EmbeddedList):
|
||||
if self.on_change:
|
||||
self.on_change()
|
||||
|
||||
def post_rebuild(self, prebuildpath):
|
||||
"""
|
||||
Called when data model has changed, in particular necessary when row
|
||||
order is updated.
|
||||
@param prebuildpath: path selected before rebuild, None if none
|
||||
@type prebuildpath: tree path
|
||||
"""
|
||||
if self.on_change:
|
||||
self.on_change()
|
||||
|
||||
def column_order(self):
|
||||
# order of columns for EmbeddedList. Only the text columns here
|
||||
return ((1, 0), (1, 1), (1, 2))
|
||||
@@ -239,11 +259,13 @@ class SurnameTab(EmbeddedList):
|
||||
"""
|
||||
self.on_edit_start(cellr, celle, path, colnr)
|
||||
#set up autocomplete
|
||||
entry = celle.get_child()
|
||||
entry.set_width_chars(10)
|
||||
completion = Gtk.EntryCompletion()
|
||||
completion.set_model(self.cmborig)
|
||||
completion.set_minimum_key_length(1)
|
||||
completion.set_text_column(1)
|
||||
celle.get_child().set_completion(completion)
|
||||
entry.set_completion(completion)
|
||||
#
|
||||
celle.connect('changed', self.on_origcmb_change, path, colnr)
|
||||
|
||||
|
||||
@@ -197,9 +197,6 @@ class EditPerson(EditPrimary):
|
||||
self.singsurnfr = SingSurn(self.top)
|
||||
self.multsurnfr = self.top.get_object("hboxmultsurnames")
|
||||
self.singlesurn_active = True
|
||||
self.surntab = SurnameTab(self.dbstate, self.uistate, self.track,
|
||||
self.obj.get_primary_name(),
|
||||
on_change=self._changed_name)
|
||||
|
||||
self.set_contexteventbox(self.top.get_object("eventboxtop"))
|
||||
|
||||
@@ -445,6 +442,9 @@ class EditPerson(EditPrimary):
|
||||
|
||||
self.preview_name = self.top.get_object("full_name")
|
||||
self.preview_name.override_font(Pango.FontDescription('sans bold 12'))
|
||||
self.surntab = SurnameTab(self.dbstate, self.uistate, self.track,
|
||||
self.obj.get_primary_name(),
|
||||
on_change=self._changed_name)
|
||||
|
||||
def get_start_date(self):
|
||||
"""
|
||||
@@ -936,7 +936,8 @@ class EditPerson(EditPrimary):
|
||||
msurhbox = self.top.get_object("hboxmultsurnames")
|
||||
msurhbox.remove(self.surntab)
|
||||
self.surntab = SurnameTab(self.dbstate, self.uistate, self.track,
|
||||
self.obj.get_primary_name())
|
||||
self.obj.get_primary_name(),
|
||||
on_change=self._changed_name)
|
||||
self.multsurnfr.set_size_request(-1,
|
||||
int(config.get('interface.surname-box-height')))
|
||||
msurhbox.pack_start(self.surntab, True, True, 0)
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2017 Nick Hall
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# GTK/Gnome modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from gi.repository import Gtk
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Gramps modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from ..managedwindow import ManagedWindow
|
||||
from ..glade import Glade
|
||||
from ..listmodel import ListModel
|
||||
from gramps.gen.errors import ValidationError
|
||||
from gramps.gen.display.place import displayer as _pd
|
||||
from gramps.gen.display.place import PlaceFormat
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# EditPlaceFormat
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class EditPlaceFormat(ManagedWindow):
|
||||
def __init__(self, uistate, dbstate, track, callback):
|
||||
self.title = _('Place Format Editor')
|
||||
ManagedWindow.__init__(self, uistate, track, EditPlaceFormat)
|
||||
self.callback = callback
|
||||
self.top = Glade()
|
||||
self.set_window(self.top.toplevel, None, self.title, None)
|
||||
self.setup_configs('interface.editplaceformat', 600, 400)
|
||||
self.top.get_object('add').connect('clicked', self.__add)
|
||||
self.top.get_object('remove').connect('clicked', self.__remove)
|
||||
self.top.get_object('name').connect('changed', self.__name_changed)
|
||||
self.top.get_object('levels').connect('validate', self._validate)
|
||||
self.window.connect('response', self.__close)
|
||||
self.model = None
|
||||
self.formats = _pd.get_formats()
|
||||
self.current_format = None
|
||||
self.__populate_format_list()
|
||||
self.show()
|
||||
|
||||
def build_menu_names(self, obj):
|
||||
return (self.title, None)
|
||||
|
||||
def __populate_format_list(self):
|
||||
flist = self.top.get_object('format_list')
|
||||
self.model = ListModel(flist,
|
||||
[(_('Format'), -1, 100)],
|
||||
select_func=self.__format_changed)
|
||||
for fmt in self.formats:
|
||||
self.model.add([fmt.name])
|
||||
self.model.select_row(0)
|
||||
|
||||
def __format_changed(self, selection):
|
||||
if self.current_format is not None:
|
||||
fmt = self.formats[self.current_format]
|
||||
self.__save_format(fmt)
|
||||
row = self.model.get_selected_row()
|
||||
if row != -1:
|
||||
fmt = self.formats[row]
|
||||
self.__load_format(fmt)
|
||||
self.current_format = row
|
||||
if row == 0:
|
||||
self.top.get_object('remove').set_sensitive(False)
|
||||
self.top.get_object('name').set_sensitive(False)
|
||||
self.top.get_object('levels').set_sensitive(False)
|
||||
self.top.get_object('street').set_sensitive(False)
|
||||
self.top.get_object('language').set_sensitive(False)
|
||||
self.top.get_object('reverse').set_sensitive(False)
|
||||
else:
|
||||
self.top.get_object('remove').set_sensitive(True)
|
||||
self.top.get_object('name').set_sensitive(True)
|
||||
self.top.get_object('levels').set_sensitive(True)
|
||||
self.top.get_object('street').set_sensitive(True)
|
||||
self.top.get_object('language').set_sensitive(True)
|
||||
self.top.get_object('reverse').set_sensitive(True)
|
||||
self.top.get_object('levels').validate(force=True)
|
||||
|
||||
def __name_changed(self, entry):
|
||||
store, iter_ = self.model.get_selected()
|
||||
self.model.set(iter_, [entry.get_text()])
|
||||
|
||||
def _validate(self, widget, text):
|
||||
for level in text.split(','):
|
||||
parts = level.split(':')
|
||||
if len(parts) < 1:
|
||||
return ValidationError('Empty level')
|
||||
if len(parts) > 2:
|
||||
return ValidationError('Invalid slice')
|
||||
for part in parts:
|
||||
integer_str = part.replace('p', '')
|
||||
if integer_str != '':
|
||||
try:
|
||||
integer = int(integer_str)
|
||||
except ValueError:
|
||||
return ValidationError('Invalid format string')
|
||||
|
||||
def __load_format(self, fmt):
|
||||
self.top.get_object('name').set_text(fmt.name)
|
||||
self.top.get_object('levels').set_text(fmt.levels)
|
||||
self.top.get_object('street').set_active(fmt.street)
|
||||
self.top.get_object('language').set_text(fmt.language)
|
||||
self.top.get_object('reverse').set_active(fmt.reverse)
|
||||
|
||||
def __save_format(self, fmt):
|
||||
fmt.name = self.top.get_object('name').get_text()
|
||||
fmt.levels = self.top.get_object('levels').get_text()
|
||||
fmt.street = self.top.get_object('street').get_active()
|
||||
fmt.language = self.top.get_object('language').get_text()
|
||||
fmt.reverse = self.top.get_object('reverse').get_active()
|
||||
|
||||
def __add(self, button):
|
||||
name = _('New')
|
||||
self.formats.append(PlaceFormat(name, ':', '', 0, False))
|
||||
self.model.add([name])
|
||||
self.model.select_row(len(self.formats)-1)
|
||||
|
||||
def __remove(self, button):
|
||||
store, iter_ = self.model.get_selected()
|
||||
if iter_:
|
||||
self.current_format = None
|
||||
del self.formats[self.model.get_selected_row()]
|
||||
self.model.remove(iter_)
|
||||
if self.model.get_selected_row() == -1:
|
||||
self.model.select_row(len(self.formats)-1)
|
||||
|
||||
def __close(self, *obj):
|
||||
row = self.model.get_selected_row()
|
||||
fmt = self.formats[self.current_format]
|
||||
self.__save_format(fmt)
|
||||
_pd.save_formats()
|
||||
self.callback()
|
||||
self.close()
|
||||
@@ -121,7 +121,7 @@ class PlaceSidebarFilter(SidebarFilter):
|
||||
self.filter_code.set_text('')
|
||||
self.filter_enclosed.set_text('')
|
||||
self.filter_note.set_text('')
|
||||
self.filter_within.set_value(0, 0)
|
||||
self.filter_within.set_value('', 0)
|
||||
self.ptype.get_child().set_text('')
|
||||
self.tag.set_active(0)
|
||||
self.generic.set_active(0)
|
||||
@@ -133,7 +133,7 @@ class PlaceSidebarFilter(SidebarFilter):
|
||||
code = str(self.filter_code.get_text()).strip()
|
||||
enclosed = str(self.filter_enclosed.get_text()).strip()
|
||||
note = str(self.filter_note.get_text()).strip()
|
||||
within = self.filter_within.get_value()
|
||||
within = self.filter_within.get_value()[0]
|
||||
regex = self.filter_regex.get_active()
|
||||
tag = self.tag.get_active() > 0
|
||||
gen = self.generic.get_active() > 0
|
||||
|
||||
+11
-2
@@ -46,6 +46,7 @@ from gi.repository import Gtk
|
||||
#
|
||||
#------------------------------------------------------------------------
|
||||
from gramps.gen.const import GLADE_DIR, GRAMPS_LOCALE as glocale
|
||||
from gramps.gen.constfunc import is_quartz
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
@@ -142,11 +143,19 @@ class Glade(Gtk.Builder):
|
||||
# toplevel is given
|
||||
if toplevel:
|
||||
loadlist = [toplevel] + also_load
|
||||
self.add_objects_from_file(path, loadlist)
|
||||
with open(path, 'r', encoding='utf-8') as builder_file:
|
||||
data = builder_file.read().replace('\n', '')
|
||||
if is_quartz():
|
||||
data = data.replace('GDK_CONTROL_MASK', 'GDK_META_MASK')
|
||||
self.add_objects_from_string(data, loadlist)
|
||||
self.__toplevel = self.get_object(toplevel)
|
||||
# toplevel not given
|
||||
else:
|
||||
self.add_from_file(path)
|
||||
with open(path, 'r', encoding='utf-8') as builder_file:
|
||||
data = builder_file.read().replace('\n', '')
|
||||
if is_quartz():
|
||||
data = data.replace('GDK_CONTROL_MASK', 'GDK_META_MASK')
|
||||
self.add_from_string(data)
|
||||
# first, use filename as possible toplevel widget name
|
||||
self.__toplevel = self.get_object(filename.rpartition('.')[0])
|
||||
|
||||
|
||||
@@ -197,7 +197,7 @@
|
||||
<child>
|
||||
<object class="GtkComboBox" id="confidence">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Conveys the submitter's quantitative evaluation of the credibility of a piece of information, based upon its supporting evidence. It is not intended to eliminate the receiver's need to evaluate the evidence for themselves.
|
||||
Very Low =Unreliable evidence or estimated data
|
||||
Low =Questionable reliability of evidence (interviews, census, oral genealogies, or potential for bias for example, an autobiography)
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.20.0 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.10"/>
|
||||
<requires lib="grampswidgets" version="0.0"/>
|
||||
<object class="GtkDialog" id="editplaceformat">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="button1">
|
||||
<property name="label" translatable="yes">_Close</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_underline">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkPaned" id="paned1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">6</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="format_list">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection"/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="add">
|
||||
<property name="label" translatable="yes">Add</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="remove">
|
||||
<property name="label" translatable="yes">Remove</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">False</property>
|
||||
<property name="shrink">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkGrid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">6</property>
|
||||
<property name="row_spacing">6</property>
|
||||
<property name="column_spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Levels:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Street format:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Language:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="language">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="reverse">
|
||||
<property name="label" translatable="yes">Reverse display order</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBoxText" id="street">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<items>
|
||||
<item translatable="yes">None</item>
|
||||
<item translatable="yes">Number Street</item>
|
||||
<item translatable="yes">Street Number</item>
|
||||
</items>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Name:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="name">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="ValidatableMaskedEntry" id="levels">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">True</property>
|
||||
<property name="shrink">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<action-widgets>
|
||||
<action-widget response="-7">button1</action-widget>
|
||||
</action-widgets>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
@@ -256,7 +256,7 @@ class Gramps:
|
||||
and not gettext.find(GTK_GETTEXT_DOMAIN)):
|
||||
_display_gtk_gettext_message(parent=self._vm.window)
|
||||
|
||||
_display_welcome_message(parent=self._vm.window)
|
||||
#_display_welcome_message(parent=self._vm.window)
|
||||
|
||||
_display_translator_message(parent=self._vm.window)
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ from io import StringIO
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import Gdk
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@@ -47,6 +48,7 @@ from gi.repository import Gtk
|
||||
from gramps.gen.const import GLADE_FILE, ICON
|
||||
from gramps.gen.errors import WindowActiveError
|
||||
from gramps.gen.config import config
|
||||
from gramps.gen.constfunc import is_quartz
|
||||
from .glade import Glade
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@@ -488,6 +490,13 @@ class ManagedWindow:
|
||||
#closing the Gtk.Window must also close ManagedWindow
|
||||
self.window = window
|
||||
self.window.connect('delete-event', self.close)
|
||||
#Set the mnemonic modifier on Macs to alt-ctrl so that it
|
||||
#doesn't interfere with the extended keyboard, see
|
||||
#https://gramps-project.org/bugs/view.php?id=6943
|
||||
if is_quartz():
|
||||
self.window.set_mnemonic_modifier(
|
||||
Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.MOD1_MASK)
|
||||
|
||||
if self.modal:
|
||||
self.window.set_modal(True)
|
||||
# The following makes sure that we only have one modal window open;
|
||||
|
||||
@@ -684,7 +684,7 @@ class BookSelector(ManagedWindow):
|
||||
old_margins = self.book.get_margins()
|
||||
old_format_name = self.book.get_format_name()
|
||||
old_output = self.book.get_output()
|
||||
BookDialog(self.dbstate, self.uistate, self.book, BookOptions)
|
||||
BookDialog(self.dbstate, self.uistate, self.book, BookOptions, track=self.track)
|
||||
new_paper_name = self.book.get_paper_name()
|
||||
new_orientation = self.book.get_orientation()
|
||||
new_paper_metric = self.book.get_paper_metric()
|
||||
@@ -918,7 +918,7 @@ class BookDialog(DocReportDialog):
|
||||
Create a dialog selecting target, format, and paper/HTML options.
|
||||
"""
|
||||
|
||||
def __init__(self, dbstate, uistate, book, options):
|
||||
def __init__(self, dbstate, uistate, book, options, track=[]):
|
||||
self.format_menu = None
|
||||
self.options = options
|
||||
self.page_html_added = False
|
||||
@@ -926,7 +926,7 @@ class BookDialog(DocReportDialog):
|
||||
self.title = _('Generate Book')
|
||||
self.database = dbstate.db
|
||||
DocReportDialog.__init__(self, dbstate, uistate, options,
|
||||
'book', self.title)
|
||||
'book', self.title, track=track)
|
||||
self.options.options_dict['bookname'] = self.book.get_name()
|
||||
|
||||
response = self.window.run()
|
||||
|
||||
@@ -63,7 +63,7 @@ class DocReportDialog(ReportDialog):
|
||||
dialogs for docgen derived reports.
|
||||
"""
|
||||
|
||||
def __init__(self, dbstate, uistate, option_class, name, trans_name):
|
||||
def __init__(self, dbstate, uistate, option_class, name, trans_name, track=[]):
|
||||
"""Initialize a dialog to request that the user select options
|
||||
for a basic *stand-alone* report."""
|
||||
|
||||
@@ -72,7 +72,7 @@ class DocReportDialog(ReportDialog):
|
||||
self.css = PLUGMAN.process_plugin_data('WEBSTUFF')
|
||||
self.dbname = dbstate.db.get_dbname()
|
||||
ReportDialog.__init__(self, dbstate, uistate, option_class,
|
||||
name, trans_name)
|
||||
name, trans_name, track=track)
|
||||
|
||||
self.basedocname = None # keep pylint happy
|
||||
self.css_filename = None
|
||||
|
||||
@@ -0,0 +1,289 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2007-2008 Brian G. Matherly
|
||||
# Copyright (C) 2007-2009 Stephane Charette
|
||||
# Copyright (C) 2009 Gary Burton
|
||||
# Contribution 2009 by Bob Ham <rah@bash.sh>
|
||||
# Copyright (C) 2010 Jakim Friant
|
||||
# Copyright (C) 2012-2013 Paul Franklin
|
||||
# Copyright (C) 2017 Nick Hall
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
"""base class for generating dialogs for graph-based reports """
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
# python modules
|
||||
#
|
||||
#------------------------------------------------------------------------
|
||||
from abc import ABCMeta, abstractmethod
|
||||
import os
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
#
|
||||
# GTK+ modules
|
||||
#
|
||||
#-------------------------------------------------------------------------------
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import GObject
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
#
|
||||
# Gramps modules
|
||||
#
|
||||
#-------------------------------------------------------------------------------
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
from gramps.gen.config import config
|
||||
from gramps.gen.plug.report import CATEGORY_GRAPHVIZ
|
||||
from ._reportdialog import ReportDialog
|
||||
from ._papermenu import PaperFrame
|
||||
import gramps.gen.plug.docgen.graphdoc as graphdoc
|
||||
from gramps.gen.plug.menu import Menu
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
#
|
||||
# GraphvizFormatComboBox
|
||||
#
|
||||
#-------------------------------------------------------------------------------
|
||||
class BaseFormatComboBox(Gtk.ComboBox):
|
||||
"""
|
||||
Combo box base class for graph-based report format choices.
|
||||
"""
|
||||
FORMATS = []
|
||||
|
||||
def set(self, active=None):
|
||||
""" initialize the Graphviz choices """
|
||||
store = Gtk.ListStore(GObject.TYPE_STRING)
|
||||
self.set_model(store)
|
||||
cell = Gtk.CellRendererText()
|
||||
self.pack_start(cell, True)
|
||||
self.add_attribute(cell, 'text', 0)
|
||||
|
||||
index = 0
|
||||
active_index = 0
|
||||
for item in self.FORMATS:
|
||||
name = item["descr"]
|
||||
store.append(row=[name])
|
||||
if item['type'] == active:
|
||||
active_index = index
|
||||
index += 1
|
||||
self.set_active(active_index)
|
||||
|
||||
def get_label(self):
|
||||
""" get the format description """
|
||||
return self.FORMATS[self.get_active()]["descr"]
|
||||
|
||||
def get_reference(self):
|
||||
""" get the format class """
|
||||
return self.FORMATS[self.get_active()]["class"]
|
||||
|
||||
def get_ext(self):
|
||||
""" get the format extension """
|
||||
return '.%s' % self.FORMATS[self.get_active()]['ext']
|
||||
|
||||
def get_clname(self):
|
||||
""" get the report's output format type"""
|
||||
return self.FORMATS[self.get_active()]["type"]
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
#
|
||||
# GraphReportDialog
|
||||
#
|
||||
#-----------------------------------------------------------------------
|
||||
class GraphReportDialog(ReportDialog, metaclass=ABCMeta):
|
||||
"""A base class of ReportDialog customized for graph-based reports."""
|
||||
|
||||
def __init__(self, dbstate, uistate, opt, name, translated_name):
|
||||
"""Initialize a dialog to request that the user select options
|
||||
for a Graphviz report. See the ReportDialog class for
|
||||
more information."""
|
||||
self.category = self.get_category()
|
||||
self._goptions = self.get_options()
|
||||
self.dbname = dbstate.db.get_dbname()
|
||||
ReportDialog.__init__(self, dbstate, uistate, opt,
|
||||
name, translated_name)
|
||||
|
||||
self.doc = None # keep pylint happy
|
||||
self.format = None
|
||||
self.paper_label = None
|
||||
|
||||
def init_options(self, option_class):
|
||||
try:
|
||||
if issubclass(option_class, object): # Old-style class
|
||||
self.options = option_class(self.raw_name,
|
||||
self.dbstate.get_database())
|
||||
except TypeError:
|
||||
self.options = option_class
|
||||
|
||||
menu = Menu()
|
||||
self._goptions.add_menu_options(menu)
|
||||
|
||||
for category in menu.get_categories():
|
||||
for name in menu.get_option_names(category):
|
||||
option = menu.get_option(category, name)
|
||||
self.options.add_menu_option(category, name, option)
|
||||
|
||||
self.options.load_previous_values()
|
||||
|
||||
def init_interface(self):
|
||||
ReportDialog.init_interface(self)
|
||||
self.doc_type_changed(self.format_menu)
|
||||
self.notebook.set_current_page(1) # don't start on "Paper Options"
|
||||
|
||||
def setup_format_frame(self):
|
||||
"""Set up the format frame of the dialog."""
|
||||
self.make_doc_menu()
|
||||
self.format_menu.set(self.options.handler.get_format_name())
|
||||
self.format_menu.connect('changed', self.doc_type_changed)
|
||||
label = Gtk.Label(label=_("%s:") % _("Output Format"))
|
||||
label.set_halign(Gtk.Align.START)
|
||||
self.grid.attach(label, 1, self.row, 1, 1)
|
||||
self.format_menu.set_hexpand(True)
|
||||
self.grid.attach(self.format_menu, 2, self.row, 2, 1)
|
||||
self.row += 1
|
||||
|
||||
self.open_with_app = Gtk.CheckButton(
|
||||
label=_("Open with default viewer"))
|
||||
self.open_with_app.set_active(
|
||||
config.get('interface.open-with-default-viewer'))
|
||||
self.grid.attach(self.open_with_app, 2, self.row, 2, 1)
|
||||
self.row += 1
|
||||
|
||||
ext = self.format_menu.get_ext()
|
||||
if ext is None:
|
||||
ext = ""
|
||||
else:
|
||||
spath = self.get_default_directory()
|
||||
if self.options.get_output():
|
||||
base = os.path.basename(self.options.get_output())
|
||||
else:
|
||||
if self.dbname is None:
|
||||
default_name = self.raw_name
|
||||
else:
|
||||
default_name = self.dbname + "_" + self.raw_name
|
||||
base = "%s%s" % (default_name, ext) # "ext" already has a dot
|
||||
spath = os.path.normpath(os.path.join(spath, base))
|
||||
self.target_fileentry.set_filename(spath)
|
||||
|
||||
def setup_report_options_frame(self):
|
||||
self.paper_label = Gtk.Label(label='<b>%s</b>' % _("Paper Options"))
|
||||
self.paper_label.set_use_markup(True)
|
||||
handler = self.options.handler
|
||||
self.paper_frame = PaperFrame(
|
||||
handler.get_paper_metric(),
|
||||
handler.get_paper_name(),
|
||||
handler.get_orientation(),
|
||||
handler.get_margins(),
|
||||
handler.get_custom_paper_size())
|
||||
self.notebook.insert_page(self.paper_frame, self.paper_label, 0)
|
||||
self.paper_frame.show_all()
|
||||
|
||||
ReportDialog.setup_report_options_frame(self)
|
||||
|
||||
def doc_type_changed(self, obj):
|
||||
"""
|
||||
This routine is called when the user selects a new file
|
||||
format for the report. It adjusts the various dialog sections
|
||||
to reflect the appropriate values for the currently selected
|
||||
file format. For example, a HTML document doesn't need any
|
||||
paper size/orientation options, but it does need a template
|
||||
file. Those changes are made here.
|
||||
"""
|
||||
self.open_with_app.set_sensitive(True)
|
||||
|
||||
fname = self.target_fileentry.get_full_path(0)
|
||||
(spath, ext) = os.path.splitext(fname)
|
||||
|
||||
ext_val = obj.get_ext()
|
||||
if ext_val:
|
||||
fname = spath + ext_val
|
||||
else:
|
||||
fname = spath
|
||||
self.target_fileentry.set_filename(fname)
|
||||
|
||||
def make_document(self):
|
||||
"""Create a document of the type requested by the user.
|
||||
"""
|
||||
pstyle = self.paper_frame.get_paper_style()
|
||||
|
||||
self.doc = self.format(self.options, pstyle)
|
||||
|
||||
self.options.set_document(self.doc)
|
||||
|
||||
def on_ok_clicked(self, obj):
|
||||
"""The user is satisfied with the dialog choices. Validate
|
||||
the output file name before doing anything else. If there is
|
||||
a file name, gather the options and create the report."""
|
||||
|
||||
# Is there a filename? This should also test file permissions, etc.
|
||||
if not self.parse_target_frame():
|
||||
self.window.run()
|
||||
|
||||
# Preparation
|
||||
self.parse_format_frame()
|
||||
self.parse_user_options()
|
||||
|
||||
self.options.handler.set_paper_metric(
|
||||
self.paper_frame.get_paper_metric())
|
||||
self.options.handler.set_paper_name(
|
||||
self.paper_frame.get_paper_name())
|
||||
self.options.handler.set_orientation(
|
||||
self.paper_frame.get_orientation())
|
||||
self.options.handler.set_margins(
|
||||
self.paper_frame.get_paper_margins())
|
||||
self.options.handler.set_custom_paper_size(
|
||||
self.paper_frame.get_custom_paper_size())
|
||||
|
||||
# Create the output document.
|
||||
self.make_document()
|
||||
|
||||
# Save options
|
||||
self.options.handler.save_options()
|
||||
config.set('interface.open-with-default-viewer',
|
||||
self.open_with_app.get_active())
|
||||
|
||||
def parse_format_frame(self):
|
||||
"""Parse the format frame of the dialog. Save the user
|
||||
selected output format for later use."""
|
||||
self.format = self.format_menu.get_reference()
|
||||
format_name = self.format_menu.get_clname()
|
||||
self.options.handler.set_format_name(format_name)
|
||||
|
||||
def setup_style_frame(self):
|
||||
"""Required by ReportDialog"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def make_doc_menu(self):
|
||||
"""
|
||||
Build a menu of document types that are appropriate for
|
||||
this graph report.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def get_category(self):
|
||||
"""
|
||||
Return the report category.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def get_options(self):
|
||||
"""
|
||||
Return the graph options.
|
||||
"""
|
||||
@@ -1,12 +1,7 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2007-2008 Brian G. Matherly
|
||||
# Copyright (C) 2007-2009 Stephane Charette
|
||||
# Copyright (C) 2009 Gary Burton
|
||||
# Contribution 2009 by Bob Ham <rah@bash.sh>
|
||||
# Copyright (C) 2010 Jakim Friant
|
||||
# Copyright (C) 2012-2013 Paul Franklin
|
||||
# Copyright (C) 2017 Nick Hall
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -23,174 +18,42 @@
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
""" a ReportDialog customized for Graphviz-based reports """
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
# python modules
|
||||
#
|
||||
#------------------------------------------------------------------------
|
||||
import os
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
#
|
||||
# GTK+ modules
|
||||
#
|
||||
#-------------------------------------------------------------------------------
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import GObject
|
||||
"""class for generating dialogs for graphviz-based reports """
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
#
|
||||
# Gramps modules
|
||||
#
|
||||
#-------------------------------------------------------------------------------
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
from gramps.gen.config import config
|
||||
from ._graphreportdialog import GraphReportDialog, BaseFormatComboBox
|
||||
from gramps.gen.plug.report import CATEGORY_GRAPHVIZ
|
||||
from ._reportdialog import ReportDialog
|
||||
from ._papermenu import PaperFrame
|
||||
import gramps.gen.plug.docgen.graphdoc as graphdoc
|
||||
from gramps.gen.plug.menu import Menu
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
#
|
||||
# GraphvizFormatComboBox
|
||||
#
|
||||
#-------------------------------------------------------------------------------
|
||||
class GraphvizFormatComboBox(Gtk.ComboBox):
|
||||
"""
|
||||
Combo box class for Graphviz report format choices.
|
||||
"""
|
||||
def set(self, active=None):
|
||||
""" initialize the Graphviz choices """
|
||||
store = Gtk.ListStore(GObject.TYPE_STRING)
|
||||
self.set_model(store)
|
||||
cell = Gtk.CellRendererText()
|
||||
self.pack_start(cell, True)
|
||||
self.add_attribute(cell, 'text', 0)
|
||||
|
||||
index = 0
|
||||
active_index = 0
|
||||
for item in graphdoc.FORMATS:
|
||||
name = item["descr"]
|
||||
store.append(row=[name])
|
||||
if item['type'] == active:
|
||||
active_index = index
|
||||
index += 1
|
||||
self.set_active(active_index)
|
||||
|
||||
def get_label(self):
|
||||
""" get the format description """
|
||||
return graphdoc.FORMATS[self.get_active()]["descr"]
|
||||
|
||||
def get_reference(self):
|
||||
""" get the format class """
|
||||
return graphdoc.FORMATS[self.get_active()]["class"]
|
||||
|
||||
def get_ext(self):
|
||||
""" get the format extension """
|
||||
return '.%s' % graphdoc.FORMATS[self.get_active()]['ext']
|
||||
|
||||
def get_clname(self):
|
||||
""" get the report's output format type"""
|
||||
return graphdoc.FORMATS[self.get_active()]["type"]
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
#
|
||||
# GraphvizReportDialog
|
||||
#
|
||||
#-----------------------------------------------------------------------
|
||||
class GraphvizReportDialog(ReportDialog):
|
||||
"""A class of ReportDialog customized for Graphviz-based reports."""
|
||||
class GraphvizReportDialog(GraphReportDialog):
|
||||
|
||||
def __init__(self, dbstate, uistate, opt, name, translated_name):
|
||||
"""Initialize a dialog to request that the user select options
|
||||
for a Graphviz report. See the ReportDialog class for
|
||||
more information."""
|
||||
self.category = CATEGORY_GRAPHVIZ
|
||||
self.__gvoptions = graphdoc.GVOptions()
|
||||
self.dbname = dbstate.db.get_dbname()
|
||||
ReportDialog.__init__(self, dbstate, uistate, opt,
|
||||
name, translated_name)
|
||||
|
||||
self.doc = None # keep pylint happy
|
||||
self.format = None
|
||||
self.paper_label = None
|
||||
|
||||
def init_options(self, option_class):
|
||||
try:
|
||||
if issubclass(option_class, object): # Old-style class
|
||||
self.options = option_class(self.raw_name,
|
||||
self.dbstate.get_database())
|
||||
except TypeError:
|
||||
self.options = option_class
|
||||
|
||||
menu = Menu()
|
||||
self.__gvoptions.add_menu_options(menu)
|
||||
|
||||
for category in menu.get_categories():
|
||||
for name in menu.get_option_names(category):
|
||||
option = menu.get_option(category, name)
|
||||
self.options.add_menu_option(category, name, option)
|
||||
|
||||
self.options.load_previous_values()
|
||||
|
||||
def init_interface(self):
|
||||
ReportDialog.init_interface(self)
|
||||
self.doc_type_changed(self.format_menu)
|
||||
self.notebook.set_current_page(1) # don't start on "Paper Options"
|
||||
|
||||
def setup_format_frame(self):
|
||||
"""Set up the format frame of the dialog."""
|
||||
def make_doc_menu(self):
|
||||
"""
|
||||
Build a menu of document types that are appropriate for
|
||||
this graph report.
|
||||
"""
|
||||
self.format_menu = GraphvizFormatComboBox()
|
||||
self.format_menu.set(self.options.handler.get_format_name())
|
||||
self.format_menu.connect('changed', self.doc_type_changed)
|
||||
label = Gtk.Label(label=_("%s:") % _("Output Format"))
|
||||
label.set_halign(Gtk.Align.START)
|
||||
self.grid.attach(label, 1, self.row, 1, 1)
|
||||
self.format_menu.set_hexpand(True)
|
||||
self.grid.attach(self.format_menu, 2, self.row, 2, 1)
|
||||
self.row += 1
|
||||
|
||||
self.open_with_app = Gtk.CheckButton(
|
||||
label=_("Open with default viewer"))
|
||||
self.open_with_app.set_active(
|
||||
config.get('interface.open-with-default-viewer'))
|
||||
self.grid.attach(self.open_with_app, 2, self.row, 2, 1)
|
||||
self.row += 1
|
||||
def get_category(self):
|
||||
"""
|
||||
Return the report category.
|
||||
"""
|
||||
return CATEGORY_GRAPHVIZ
|
||||
|
||||
ext = self.format_menu.get_ext()
|
||||
if ext is None:
|
||||
ext = ""
|
||||
else:
|
||||
spath = self.get_default_directory()
|
||||
if self.options.get_output():
|
||||
base = os.path.basename(self.options.get_output())
|
||||
else:
|
||||
if self.dbname is None:
|
||||
default_name = self.raw_name
|
||||
else:
|
||||
default_name = self.dbname + "_" + self.raw_name
|
||||
base = "%s%s" % (default_name, ext) # "ext" already has a dot
|
||||
spath = os.path.normpath(os.path.join(spath, base))
|
||||
self.target_fileentry.set_filename(spath)
|
||||
|
||||
def setup_report_options_frame(self):
|
||||
self.paper_label = Gtk.Label(label='<b>%s</b>' % _("Paper Options"))
|
||||
self.paper_label.set_use_markup(True)
|
||||
handler = self.options.handler
|
||||
self.paper_frame = PaperFrame(
|
||||
handler.get_paper_metric(),
|
||||
handler.get_paper_name(),
|
||||
handler.get_orientation(),
|
||||
handler.get_margins(),
|
||||
handler.get_custom_paper_size())
|
||||
self.notebook.insert_page(self.paper_frame, self.paper_label, 0)
|
||||
self.paper_frame.show_all()
|
||||
|
||||
ReportDialog.setup_report_options_frame(self)
|
||||
def get_options(self):
|
||||
"""
|
||||
Return the graph options.
|
||||
"""
|
||||
return graphdoc.GVOptions()
|
||||
|
||||
def doc_type_changed(self, obj):
|
||||
"""
|
||||
@@ -201,84 +64,31 @@ class GraphvizReportDialog(ReportDialog):
|
||||
paper size/orientation options, but it does need a template
|
||||
file. Those changes are made here.
|
||||
"""
|
||||
self.open_with_app.set_sensitive(True)
|
||||
|
||||
fname = self.target_fileentry.get_full_path(0)
|
||||
(spath, ext) = os.path.splitext(fname)
|
||||
|
||||
ext_val = obj.get_ext()
|
||||
if ext_val:
|
||||
fname = spath + ext_val
|
||||
else:
|
||||
fname = spath
|
||||
self.target_fileentry.set_filename(fname)
|
||||
GraphReportDialog.doc_type_changed(self, obj)
|
||||
|
||||
output_format_str = obj.get_clname()
|
||||
if output_format_str in ['gvpdf', 'gspdf', 'ps']:
|
||||
# Always use 72 DPI for PostScript and PDF files.
|
||||
self.__gvoptions.dpi.set_value(72)
|
||||
self.__gvoptions.dpi.set_available(False)
|
||||
self._goptions.dpi.set_value(72)
|
||||
self._goptions.dpi.set_available(False)
|
||||
else:
|
||||
self.__gvoptions.dpi.set_available(True)
|
||||
self._goptions.dpi.set_available(True)
|
||||
|
||||
if output_format_str in ['gspdf', 'dot']:
|
||||
# Multiple pages only valid for dot and PDF via GhostsScript.
|
||||
self.__gvoptions.h_pages.set_available(True)
|
||||
self.__gvoptions.v_pages.set_available(True)
|
||||
self._goptions.h_pages.set_available(True)
|
||||
self._goptions.v_pages.set_available(True)
|
||||
else:
|
||||
self.__gvoptions.h_pages.set_value(1)
|
||||
self.__gvoptions.v_pages.set_value(1)
|
||||
self.__gvoptions.h_pages.set_available(False)
|
||||
self.__gvoptions.v_pages.set_available(False)
|
||||
self._goptions.h_pages.set_value(1)
|
||||
self._goptions.v_pages.set_value(1)
|
||||
self._goptions.h_pages.set_available(False)
|
||||
self._goptions.v_pages.set_available(False)
|
||||
|
||||
def make_document(self):
|
||||
"""Create a document of the type requested by the user.
|
||||
"""
|
||||
pstyle = self.paper_frame.get_paper_style()
|
||||
|
||||
self.doc = self.format(self.options, pstyle)
|
||||
|
||||
self.options.set_document(self.doc)
|
||||
|
||||
def on_ok_clicked(self, obj):
|
||||
"""The user is satisfied with the dialog choices. Validate
|
||||
the output file name before doing anything else. If there is
|
||||
a file name, gather the options and create the report."""
|
||||
|
||||
# Is there a filename? This should also test file permissions, etc.
|
||||
if not self.parse_target_frame():
|
||||
self.window.run()
|
||||
|
||||
# Preparation
|
||||
self.parse_format_frame()
|
||||
self.parse_user_options()
|
||||
|
||||
self.options.handler.set_paper_metric(
|
||||
self.paper_frame.get_paper_metric())
|
||||
self.options.handler.set_paper_name(
|
||||
self.paper_frame.get_paper_name())
|
||||
self.options.handler.set_orientation(
|
||||
self.paper_frame.get_orientation())
|
||||
self.options.handler.set_margins(
|
||||
self.paper_frame.get_paper_margins())
|
||||
self.options.handler.set_custom_paper_size(
|
||||
self.paper_frame.get_custom_paper_size())
|
||||
|
||||
# Create the output document.
|
||||
self.make_document()
|
||||
|
||||
# Save options
|
||||
self.options.handler.save_options()
|
||||
config.set('interface.open-with-default-viewer',
|
||||
self.open_with_app.get_active())
|
||||
|
||||
def parse_format_frame(self):
|
||||
"""Parse the format frame of the dialog. Save the user
|
||||
selected output format for later use."""
|
||||
self.format = self.format_menu.get_reference()
|
||||
format_name = self.format_menu.get_clname()
|
||||
self.options.handler.set_format_name(format_name)
|
||||
|
||||
def setup_style_frame(self):
|
||||
"""Required by ReportDialog"""
|
||||
pass
|
||||
#-------------------------------------------------------------------------------
|
||||
#
|
||||
# GraphvizFormatComboBox
|
||||
#
|
||||
#-------------------------------------------------------------------------------
|
||||
class GraphvizFormatComboBox(BaseFormatComboBox):
|
||||
FORMATS = graphdoc.FORMATS
|
||||
|
||||
@@ -55,7 +55,8 @@ from ...user import User
|
||||
from ...dialog import ErrorDialog, OptionDialog
|
||||
from gramps.gen.plug.report import (CATEGORY_TEXT, CATEGORY_DRAW, CATEGORY_BOOK,
|
||||
CATEGORY_CODE, CATEGORY_WEB,
|
||||
CATEGORY_GRAPHVIZ, standalone_categories)
|
||||
CATEGORY_GRAPHVIZ, CATEGORY_TREE,
|
||||
standalone_categories)
|
||||
from gramps.gen.plug.docgen import StyleSheet, StyleSheetList
|
||||
from ...managedwindow import ManagedWindow
|
||||
from ._stylecombobox import StyleComboBox
|
||||
@@ -676,6 +677,9 @@ def report(dbstate, uistate, person, report_class, options_class,
|
||||
elif category == CATEGORY_GRAPHVIZ:
|
||||
from ._graphvizreportdialog import GraphvizReportDialog
|
||||
dialog_class = GraphvizReportDialog
|
||||
elif category == CATEGORY_TREE:
|
||||
from ._treereportdialog import TreeReportDialog
|
||||
dialog_class = TreeReportDialog
|
||||
elif category == CATEGORY_WEB:
|
||||
from ._webreportdialog import WebReportDialog
|
||||
dialog_class = WebReportDialog
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2017 Nick Hall
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
"""class for generating dialogs for graphviz-based reports """
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
#
|
||||
# Gramps modules
|
||||
#
|
||||
#-------------------------------------------------------------------------------
|
||||
from ._graphreportdialog import GraphReportDialog, BaseFormatComboBox
|
||||
from gramps.gen.plug.report import CATEGORY_TREE
|
||||
import gramps.gen.plug.docgen.treedoc as treedoc
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
#
|
||||
# TreeReportDialog
|
||||
#
|
||||
#-----------------------------------------------------------------------
|
||||
class TreeReportDialog(GraphReportDialog):
|
||||
|
||||
def make_doc_menu(self):
|
||||
"""
|
||||
Build a menu of document types that are appropriate for
|
||||
this graph report.
|
||||
"""
|
||||
self.format_menu = TreeFormatComboBox()
|
||||
|
||||
def get_category(self):
|
||||
"""
|
||||
Return the report category.
|
||||
"""
|
||||
return CATEGORY_TREE
|
||||
|
||||
def get_options(self):
|
||||
"""
|
||||
Return the graph options.
|
||||
"""
|
||||
return treedoc.TreeOptions()
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
#
|
||||
# TreeFormatComboBox
|
||||
#
|
||||
#-------------------------------------------------------------------------------
|
||||
class TreeFormatComboBox(BaseFormatComboBox):
|
||||
FORMATS = treedoc.FORMATS
|
||||
+46
-12
@@ -53,6 +53,7 @@ from gi.repository import Gdk
|
||||
#-------------------------------------------------------------------------
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
from gramps.gen.lib import EventType, EventRoleType
|
||||
from gramps.gen.lib.person import Person
|
||||
from gramps.gen.constfunc import has_display, is_quartz, mac, win
|
||||
from gramps.gen.config import config
|
||||
@@ -314,6 +315,7 @@ class ProgressMeter:
|
||||
"""
|
||||
Close the progress meter
|
||||
"""
|
||||
del self.__cancel_callback
|
||||
self.__dialog.destroy()
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@@ -473,33 +475,50 @@ def is_right_click(event):
|
||||
if Gdk.Event.triggers_context_menu(event):
|
||||
return True
|
||||
|
||||
def color_graph_family(family, dbstate):
|
||||
"""
|
||||
:return: based on the config the color for graph family node in hex
|
||||
:rtype: tuple (hex color fill, hex color border)
|
||||
"""
|
||||
scheme = config.get('colors.scheme')
|
||||
for event_ref in family.get_event_ref_list():
|
||||
event = dbstate.db.get_event_from_handle(event_ref.ref)
|
||||
if (event.type == EventType.DIVORCE and
|
||||
event_ref.get_role() in (EventRoleType.FAMILY,
|
||||
EventRoleType.PRIMARY)):
|
||||
return (config.get('colors.family-divorced')[scheme],
|
||||
config.get('colors.border-family-divorced')[scheme])
|
||||
return (config.get('colors.family')[scheme],
|
||||
config.get('colors.border-family')[scheme])
|
||||
|
||||
def color_graph_box(alive=False, gender=Person.MALE):
|
||||
"""
|
||||
:return: based on the config the color for graph boxes in hex
|
||||
If gender is None, an empty box is assumed
|
||||
:rtype: tuple (hex color fill, hex color border)
|
||||
"""
|
||||
scheme = config.get('colors.scheme')
|
||||
if gender == Person.MALE:
|
||||
if alive:
|
||||
return (config.get('preferences.color-gender-male-alive'),
|
||||
config.get('preferences.bordercolor-gender-male-alive'))
|
||||
return (config.get('colors.male-alive')[scheme],
|
||||
config.get('colors.border-male-alive')[scheme])
|
||||
else:
|
||||
return (config.get('preferences.color-gender-male-death'),
|
||||
config.get('preferences.bordercolor-gender-male-death'))
|
||||
return (config.get('colors.male-dead')[scheme],
|
||||
config.get('colors.border-male-dead')[scheme])
|
||||
elif gender == Person.FEMALE:
|
||||
if alive:
|
||||
return (config.get('preferences.color-gender-female-alive'),
|
||||
config.get('preferences.bordercolor-gender-female-alive'))
|
||||
return (config.get('colors.female-alive')[scheme],
|
||||
config.get('colors.border-female-alive')[scheme])
|
||||
else:
|
||||
return (config.get('preferences.color-gender-female-death'),
|
||||
config.get('preferences.bordercolor-gender-female-death'))
|
||||
return (config.get('colors.female-dead')[scheme],
|
||||
config.get('colors.border-female-dead')[scheme])
|
||||
elif gender == Person.UNKNOWN:
|
||||
if alive:
|
||||
return (config.get('preferences.color-gender-unknown-alive'),
|
||||
config.get('preferences.bordercolor-gender-unknown-alive'))
|
||||
return (config.get('colors.unknown-alive')[scheme],
|
||||
config.get('colors.border-unknown-alive')[scheme])
|
||||
else:
|
||||
return (config.get('preferences.color-gender-unknown-death'),
|
||||
config.get('preferences.bordercolor-gender-unknown-death'))
|
||||
return (config.get('colors.unknown-dead')[scheme],
|
||||
config.get('colors.border-unknown-dead')[scheme])
|
||||
#empty box, no gender
|
||||
return ('#d2d6ce', '#000000')
|
||||
## print 'male alive', rgb_to_hex((185/256.0, 207/256.0, 231/256.0))
|
||||
@@ -543,6 +562,21 @@ def rgb_to_hex(rgb):
|
||||
rgbint = (int(rgb[0] * 255), int(rgb[1] * 255), int(rgb[2] * 255))
|
||||
return '#%02x%02x%02x' % rgbint
|
||||
|
||||
def get_link_color(context):
|
||||
"""
|
||||
Find the link color for the current theme.
|
||||
"""
|
||||
from gi.repository import Gtk
|
||||
|
||||
if Gtk.get_minor_version() > 11:
|
||||
col = context.get_color(Gtk.StateFlags.LINK)
|
||||
else:
|
||||
found, col = context.lookup_color('link_color')
|
||||
if not found:
|
||||
col.parse('blue')
|
||||
|
||||
return rgb_to_hex((col.red, col.green, col.blue))
|
||||
|
||||
def edit_object(dbstate, uistate, reftype, ref):
|
||||
"""
|
||||
Invokes the appropriate editor for an object type and given handle.
|
||||
|
||||
+15
-10
@@ -39,6 +39,7 @@ import time
|
||||
import datetime
|
||||
from io import StringIO
|
||||
import posixpath
|
||||
import gc
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@@ -54,6 +55,7 @@ LOG = logging.getLogger(".")
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import Gdk
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@@ -136,8 +138,6 @@ UIDEFAULT = '''<ui>
|
||||
<menuitem action="Quit"/>
|
||||
</menu>
|
||||
<menu action="AddMenu">
|
||||
<menu action="AddNewMenu">
|
||||
<separator/>
|
||||
<menuitem action="PersonAdd"/>
|
||||
<separator/>
|
||||
<menuitem action="FamilyAdd"/>
|
||||
@@ -150,7 +150,6 @@ UIDEFAULT = '''<ui>
|
||||
<menuitem action="RepositoryAdd"/>
|
||||
<menuitem action="MediaAdd"/>
|
||||
<menuitem action="NoteAdd"/>
|
||||
</menu>
|
||||
</menu>
|
||||
<menu action="EditMenu">
|
||||
<menuitem action="Undo"/>
|
||||
@@ -396,7 +395,12 @@ class ViewManager(CLIManager):
|
||||
self.window.set_icon_from_file(ICON)
|
||||
self.window.set_default_size(width, height)
|
||||
self.window.move(horiz_position, vert_position)
|
||||
|
||||
#Set the mnemonic modifier on Macs to alt-ctrl so that it
|
||||
#doesn't interfere with the extended keyboard, see
|
||||
#https://gramps-project.org/bugs/view.php?id=6943
|
||||
if is_quartz():
|
||||
self.window.set_mnemonic_modifier(
|
||||
Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.MOD1_MASK)
|
||||
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||
self.window.add(vbox)
|
||||
hpane = Gtk.Paned()
|
||||
@@ -599,20 +603,20 @@ class ViewManager(CLIManager):
|
||||
('Clipboard', 'edit-paste', _('Clip_board'), "<PRIMARY>b",
|
||||
_("Open the Clipboard dialog"), self.clipboard),
|
||||
('AddMenu', None, _('_Add')),
|
||||
('AddNewMenu', None, _('New')),
|
||||
('PersonAdd', None, _('Person'), "<Alt>p", None,
|
||||
#('AddNewMenu', None, _('New')),
|
||||
('PersonAdd', None, _('Person'), "<shift><Alt>p", None,
|
||||
self.add_new_person),
|
||||
('FamilyAdd', None, _('Family'), "<Alt>y", None,
|
||||
('FamilyAdd', None, _('Family'), "<shift><Alt>f", None,
|
||||
self.add_new_family),
|
||||
('EventAdd', None, _('Event'), "<shift>e", None,
|
||||
('EventAdd', None, _('Event'), "<shift><Alt>e", None,
|
||||
self.add_new_event),
|
||||
('PlaceAdd', None, _('Place'), "<shift><Alt>p", None,
|
||||
('PlaceAdd', None, _('Place'), "<shift><Alt>l", None,
|
||||
self.add_new_place),
|
||||
('SourceAdd', None, _('Source'), "<shift><Alt>s", None,
|
||||
self.add_new_source),
|
||||
('CitationAdd', None, _('Citation'), "<shift><Alt>c", None,
|
||||
self.add_new_citation),
|
||||
('RepositoryAdd', None, _('Repository'), "<shift><Alt>y", None,
|
||||
('RepositoryAdd', None, _('Repository'), "<shift><Alt>r", None,
|
||||
self.add_new_repository),
|
||||
('MediaAdd', None, _('Media'), "<shift><Alt>m", None,
|
||||
self.add_new_media),
|
||||
@@ -1769,6 +1773,7 @@ def run_plugin(pdata, dbstate, uistate):
|
||||
name=pdata.id,
|
||||
category=pdata.category,
|
||||
callback=dbstate.db.request_rebuild)
|
||||
gc.collect(2)
|
||||
|
||||
def make_plugin_callback(pdata, dbstate, uistate):
|
||||
"""
|
||||
|
||||
@@ -278,14 +278,12 @@ class ListView(NavigationView):
|
||||
def foreground_color(self, column, renderer, model, iter_, data=None):
|
||||
'''
|
||||
Set the foreground color of the cell renderer. We use a cell data
|
||||
function because we don't want to set the color of untagged rows.
|
||||
function because there is a problem returning None from a model.
|
||||
'''
|
||||
fg_color = model.get_value(iter_, model.color_column())
|
||||
#for color errors, typically color column is badly set
|
||||
if fg_color:
|
||||
renderer.set_property('foreground', fg_color)
|
||||
else:
|
||||
LOG.debug('Bad color set: ' + str(fg_color))
|
||||
if fg_color == '':
|
||||
fg_color = None
|
||||
renderer.set_property('foreground', fg_color)
|
||||
|
||||
def set_active(self):
|
||||
"""
|
||||
|
||||
@@ -156,8 +156,7 @@ class PageView(DbGUIElement, metaclass=ABCMeta):
|
||||
hpane = Gtk.Paned()
|
||||
vpane = Gtk.Paned(orientation=Gtk.Orientation.VERTICAL)
|
||||
hpane.pack1(vpane, resize=True, shrink=False)
|
||||
hpane.pack2(self.sidebar, resize=False, shrink=True)
|
||||
self._setup_slider_config(hpane, 'hpane.slider-position')
|
||||
hpane.pack2(self.sidebar, resize=False, shrink=False)
|
||||
hpane.show()
|
||||
vpane.show()
|
||||
|
||||
@@ -168,14 +167,31 @@ class PageView(DbGUIElement, metaclass=ABCMeta):
|
||||
self._setup_slider_config(vpane, 'vpane.slider-position')
|
||||
|
||||
self.sidebar_toggled(self.sidebar.get_property('visible'))
|
||||
self.hpane_sig = hpane.connect("draw", self.set_page_slider)
|
||||
|
||||
return hpane
|
||||
|
||||
def _setup_slider_config(self, widget, setting):
|
||||
def set_page_slider(self, widget, dummy):
|
||||
""" Setup slider. We have the page realized at this point. """
|
||||
widget.disconnect(self.hpane_sig)
|
||||
# get current width of pane
|
||||
width = widget.get_allocated_width()
|
||||
# default will use natural size for sidebar until it gets to 400 pix
|
||||
side_ch = self.sidebar.get_children() # Gtk Notebook
|
||||
try:
|
||||
vp_ch = side_ch[0].get_children() # Gtk Viewport child
|
||||
ch_width = vp_ch[0].get_preferred_width()[0] + 3
|
||||
except AttributeError:
|
||||
ch_width = 300 # needed if no Gramplet installed
|
||||
pos = width - min(ch_width, 400)
|
||||
self._setup_slider_config(widget, 'hpane.slider-position',
|
||||
position=pos)
|
||||
|
||||
def _setup_slider_config(self, widget, setting, position=-1):
|
||||
"""
|
||||
Setup the slider configuration setting.
|
||||
"""
|
||||
self._config.register(setting, -1)
|
||||
self._config.register(setting, position)
|
||||
widget.set_position(self._config.get(setting))
|
||||
widget.connect('notify::position', self._position_changed, setting)
|
||||
|
||||
@@ -316,9 +332,6 @@ class PageView(DbGUIElement, metaclass=ABCMeta):
|
||||
self.sidebar.set_active()
|
||||
self.bottombar.set_active()
|
||||
self.active = True
|
||||
new_title = "%s - %s - Gramps" % (self.dbstate.db.get_dbname(),
|
||||
self.get_title())
|
||||
self.uistate.window.set_title(new_title)
|
||||
if self.dirty:
|
||||
self.uistate.set_busy_cursor(True)
|
||||
self.build_tree()
|
||||
|
||||
@@ -139,7 +139,7 @@ class CitationBaseModel:
|
||||
tag_handle = data[0]
|
||||
cached, tag_color = self.get_cached_value(tag_handle, "TAG_COLOR")
|
||||
if not cached:
|
||||
tag_color = "#000000000000"
|
||||
tag_color = ""
|
||||
tag_priority = None
|
||||
for handle in data[COLUMN_TAGS]:
|
||||
tag = self.db.get_tag_from_handle(handle)
|
||||
@@ -300,7 +300,7 @@ class CitationBaseModel:
|
||||
tag_handle = data[0]
|
||||
cached, tag_color = self.get_cached_value(tag_handle, "TAG_COLOR")
|
||||
if not cached:
|
||||
tag_color = "#000000000000"
|
||||
tag_color = ""
|
||||
tag_priority = None
|
||||
for handle in data[COLUMN2_TAGS]:
|
||||
tag = self.db.get_tag_from_handle(handle)
|
||||
|
||||
@@ -208,7 +208,7 @@ class EventModel(FlatBaseModel):
|
||||
tag_handle = data[0]
|
||||
cached, tag_color = self.get_cached_value(tag_handle, "TAG_COLOR")
|
||||
if not cached:
|
||||
tag_color = "#000000000000"
|
||||
tag_color = ""
|
||||
tag_priority = None
|
||||
for handle in data[COLUMN_TAGS]:
|
||||
tag = self.db.get_tag_from_handle(handle)
|
||||
|
||||
@@ -220,7 +220,7 @@ class FamilyModel(FlatBaseModel):
|
||||
tag_handle = data[0]
|
||||
cached, tag_color = self.get_cached_value(tag_handle, "TAG_COLOR")
|
||||
if not cached:
|
||||
tag_color = "#000000000000"
|
||||
tag_color = ""
|
||||
tag_priority = None
|
||||
for handle in data[13]:
|
||||
tag = self.db.get_tag_from_handle(handle)
|
||||
|
||||
@@ -171,7 +171,7 @@ class MediaModel(FlatBaseModel):
|
||||
tag_handle = data[0]
|
||||
cached, tag_color = self.get_cached_value(tag_handle, "TAG_COLOR")
|
||||
if not cached:
|
||||
tag_color = "#000000000000"
|
||||
tag_color = ""
|
||||
tag_priority = None
|
||||
for handle in data[11]:
|
||||
tag = self.db.get_tag_from_handle(handle)
|
||||
|
||||
@@ -148,7 +148,7 @@ class NoteModel(FlatBaseModel):
|
||||
tag_handle = data[0]
|
||||
cached, value = self.get_cached_value(tag_handle, "TAG_COLOR")
|
||||
if not cached:
|
||||
tag_color = "#000000000000"
|
||||
tag_color = ""
|
||||
tag_priority = None
|
||||
for handle in data[Note.POS_TAGS]:
|
||||
tag = self.db.get_tag_from_handle(handle)
|
||||
|
||||
@@ -538,7 +538,7 @@ class PeopleBaseModel(BaseModel):
|
||||
tag_handle = data[0]
|
||||
cached, value = self.get_cached_value(tag_handle, "TAG_COLOR")
|
||||
if not cached:
|
||||
tag_color = "#000000000000"
|
||||
tag_color = ""
|
||||
tag_priority = None
|
||||
for handle in data[COLUMN_TAGS]:
|
||||
tag = self.db.get_tag_from_handle(handle)
|
||||
|
||||
@@ -201,7 +201,7 @@ class PlaceBaseModel:
|
||||
tag_handle = data[0]
|
||||
cached, value = self.get_cached_value(tag_handle, "TAG_COLOR")
|
||||
if not cached:
|
||||
tag_color = "#000000000000"
|
||||
tag_color = ""
|
||||
tag_priority = None
|
||||
for handle in data[16]:
|
||||
tag = self.db.get_tag_from_handle(handle)
|
||||
|
||||
@@ -253,7 +253,7 @@ class RepositoryModel(FlatBaseModel):
|
||||
tag_handle = data[0]
|
||||
cached, tag_color = self.get_cached_value(tag_handle, "TAG_COLOR")
|
||||
if not cached:
|
||||
tag_color = "#000000000000"
|
||||
tag_color = ""
|
||||
tag_priority = None
|
||||
for handle in data[8]:
|
||||
tag = self.db.get_tag_from_handle(handle)
|
||||
|
||||
@@ -143,7 +143,7 @@ class SourceModel(FlatBaseModel):
|
||||
tag_handle = data[0]
|
||||
cached, value = self.get_cached_value(tag_handle, "TAG_COLOR")
|
||||
if not cached:
|
||||
tag_color = "#000000000000"
|
||||
tag_color = ""
|
||||
tag_priority = None
|
||||
for handle in data[11]:
|
||||
tag = self.db.get_tag_from_handle(handle)
|
||||
|
||||
@@ -898,7 +898,8 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel):
|
||||
if node.handle is None:
|
||||
# Header rows dont get the foreground color set
|
||||
if col == self.color_column():
|
||||
return "#000000000000"
|
||||
#color must not be utf-8
|
||||
return ""
|
||||
|
||||
# Return the node name for the first column
|
||||
if col == 0:
|
||||
|
||||
@@ -32,6 +32,7 @@ from .photo import *
|
||||
from .placeentry import *
|
||||
from .monitoredwidgets import *
|
||||
from .selectionwidget import SelectionWidget, Region
|
||||
from .shadebox import *
|
||||
from .shortlistcomboentry import *
|
||||
from .springseparator import *
|
||||
from .statusbar import Statusbar
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2017 Paul Culley
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
# Python Modules
|
||||
#
|
||||
#------------------------------------------------------------------------
|
||||
from gi.repository import Gdk
|
||||
from gi.repository import Gtk
|
||||
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
# Gramps Modules
|
||||
#
|
||||
#------------------------------------------------------------------------
|
||||
class CellRendererTextEdit(Gtk.CellRendererText):
|
||||
""" To be used where you normally use Gtk.CellRendererText and you want to
|
||||
avoid losing the text if the user clicks outside the cell (Like an 'OK'
|
||||
button. """
|
||||
|
||||
__gtype_name__ = 'CellRendererTextEdit'
|
||||
|
||||
def __init__(self):
|
||||
Gtk.CellRendererText.__init__(self)
|
||||
|
||||
def do_start_editing(
|
||||
self, event, treeview, path, background_area, cell_area, flags):
|
||||
if not self.get_property('editable'):
|
||||
return
|
||||
entry = Gtk.Entry()
|
||||
entry.set_has_frame(False)
|
||||
xalign, yalign = self.get_alignment()
|
||||
entry.set_alignment(xalign)
|
||||
entry.set_width_chars(5)
|
||||
entry.set_text(self.get_property("text")) # get original cell text
|
||||
entry.add_events(Gdk.EventMask.FOCUS_CHANGE_MASK)
|
||||
entry.connect('focus-out-event', self.focus_out, path)
|
||||
entry.connect('key-press-event', self._key_press)
|
||||
entry.show()
|
||||
return entry
|
||||
|
||||
def focus_out(self, entry, event, path):
|
||||
self.emit('edited', path, entry.get_text())
|
||||
return False
|
||||
|
||||
def _key_press(self, entry, event):
|
||||
if event.type == Gdk.EventType.KEY_PRESS:
|
||||
if event.keyval == Gdk.KEY_Escape:
|
||||
# get original cell text
|
||||
entry.set_text(self.get_property("text"))
|
||||
return False
|
||||
@@ -1221,7 +1221,9 @@ class FanChartWidget(FanChartBaseWidget):
|
||||
cr.scale(scale, scale)
|
||||
if widget:
|
||||
self.center_xy = self.center_xy_from_delta()
|
||||
cr.translate(*self.center_xy)
|
||||
cr.translate(*self.center_xy)
|
||||
else:
|
||||
cr.translate(halfdist, halfdist)
|
||||
|
||||
cr.save()
|
||||
cr.rotate(math.radians(self.rotate_value))
|
||||
@@ -1596,6 +1598,8 @@ class FanChartGrampsGUI:
|
||||
siblings.append(sib_id)
|
||||
# Collect a list of per-step-family step-siblings
|
||||
for parent_h in [fam.get_father_handle(), fam.get_mother_handle()]:
|
||||
if not parent_h:
|
||||
continue
|
||||
parent = self.dbstate.db.get_person_from_handle(parent_h)
|
||||
other_families = [self.dbstate.db.get_family_from_handle(fam_id)
|
||||
for fam_id in parent.get_family_handle_list()
|
||||
|
||||
@@ -374,7 +374,9 @@ class FanChart2WayWidget(FanChartWidget, FanChartDescWidget):
|
||||
cr.scale(scale, scale)
|
||||
if widget:
|
||||
self.center_xy = self.center_xy_from_delta()
|
||||
cr.translate(*self.center_xy)
|
||||
cr.translate(*self.center_xy)
|
||||
else:
|
||||
cr.translate(halfdist, halfdist)
|
||||
|
||||
cr.save()
|
||||
# Draw background
|
||||
|
||||
@@ -370,7 +370,7 @@ class GrampletBar(Gtk.Notebook):
|
||||
"""
|
||||
Add a tab to the notebook for the given gramplet.
|
||||
"""
|
||||
width = min(int(self.uistate.screen_width() * 0.25), 400)
|
||||
width = -1 # Allow tab width to adjust (smaller) to sidebar
|
||||
height = min(int(self.uistate.screen_height() * 0.20), 400)
|
||||
gramplet.set_size_request(width, height)
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ from gramps.gen.errors import WindowActiveError
|
||||
from gramps.gen.const import URL_MANUAL_PAGE, VERSION_DIR, COLON
|
||||
from ..editors import EditPerson, EditFamily
|
||||
from ..managedwindow import ManagedWindow
|
||||
from ..utils import is_right_click, rgb_to_hex, get_primary_mask
|
||||
from ..utils import is_right_click, get_primary_mask, get_link_color
|
||||
from .menuitem import add_menuitem
|
||||
from ..plug import make_gui_option
|
||||
from ..plug.quick import run_quick_report_by_name
|
||||
@@ -196,12 +196,7 @@ class LinkTag(Gtk.TextTag):
|
||||
lid = 0
|
||||
#obtaining the theme link color once. Restart needed on theme change!
|
||||
linkcolor = Gtk.Label(label='test') #needed to avoid label destroyed to early
|
||||
linkcolor = linkcolor.get_style_context().lookup_color('link_color')
|
||||
if linkcolor[0]:
|
||||
linkcolor = rgb_to_hex((linkcolor[1].red, linkcolor[1].green,
|
||||
linkcolor[1].blue))
|
||||
else:
|
||||
linkcolor = 'blue'
|
||||
linkcolor = get_link_color(linkcolor.get_style_context())
|
||||
|
||||
def __init__(self, buffer):
|
||||
LinkTag.lid += 1
|
||||
|
||||
@@ -48,7 +48,7 @@ from gi.repository import Pango
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from gramps.gen.constfunc import has_display, win
|
||||
from ..utils import rgb_to_hex
|
||||
from ..utils import get_link_color
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@@ -81,11 +81,7 @@ class LinkLabel(Gtk.EventBox):
|
||||
Gtk.EventBox.__init__(self)
|
||||
|
||||
st_cont = self.get_style_context()
|
||||
col = st_cont.lookup_color('link_color')
|
||||
if col[0]:
|
||||
self.color = rgb_to_hex((col[1].red, col[1].green, col[1].blue))
|
||||
else:
|
||||
self.color = 'blue'
|
||||
self.color = get_link_color(st_cont)
|
||||
|
||||
if emph:
|
||||
#emphasize a link
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2018 Nick Hall
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
__all__ = ["ShadeBox"]
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Standard python modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
import logging
|
||||
_LOG = logging.getLogger(".widgets.shadebox")
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# GTK/Gnome modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from gi.repository import Gtk
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# ShadeBox class
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class ShadeBox(Gtk.EventBox):
|
||||
"""
|
||||
An EventBox with a shaded background.
|
||||
"""
|
||||
def __init__(self, use_shade):
|
||||
Gtk.EventBox.__init__(self)
|
||||
self.use_shade = use_shade
|
||||
|
||||
def do_draw(self, cr):
|
||||
if self.use_shade:
|
||||
tv = Gtk.TextView()
|
||||
tv_context = tv.get_style_context()
|
||||
width = self.get_allocated_width()
|
||||
height = self.get_allocated_height()
|
||||
Gtk.render_background(tv_context, cr, 0, 0, width, height)
|
||||
self.get_child().draw(cr)
|
||||
@@ -60,9 +60,9 @@ from .toolcomboentry import ToolComboEntry
|
||||
from .springseparator import SpringSeparatorAction
|
||||
from ..spell import Spell
|
||||
from ..display import display_url
|
||||
from ..utils import SystemFonts, rgb_to_hex, get_primary_mask
|
||||
from ..utils import SystemFonts, get_primary_mask, get_link_color
|
||||
from gramps.gen.config import config
|
||||
from gramps.gen.constfunc import has_display
|
||||
from gramps.gen.constfunc import has_display, mac
|
||||
from ..actiongroup import ActionGroup
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@@ -186,11 +186,7 @@ class StyledTextEditor(Gtk.TextView):
|
||||
self.set_buffer(self.textbuffer)
|
||||
|
||||
st_cont = self.get_style_context()
|
||||
col = st_cont.lookup_color('link_color')
|
||||
if col[0]:
|
||||
self.linkcolor = rgb_to_hex((col[1].red, col[1].green, col[1].blue))
|
||||
else:
|
||||
self.linkcolor = 'blue'
|
||||
self.linkcolor = get_link_color(st_cont)
|
||||
self.textbuffer.linkcolor = self.linkcolor
|
||||
|
||||
self.match = None
|
||||
@@ -319,7 +315,9 @@ class StyledTextEditor(Gtk.TextView):
|
||||
if url.startswith("gramps://"):
|
||||
obj_class, prop, value = url[9:].split("/")
|
||||
display = simple_access.display(obj_class, prop, value) or url
|
||||
return display
|
||||
return display + ((_("\nCommand-Click to follow link") if mac() else
|
||||
_("\nCtrl-Click to follow link"))
|
||||
if self.get_editable() else '')
|
||||
|
||||
def on_button_release_event(self, widget, event):
|
||||
"""
|
||||
|
||||
@@ -52,7 +52,7 @@ from gi.repository import Pango
|
||||
#-------------------------------------------------------------------------
|
||||
from gramps.gen.errors import MaskError, ValidationError, WindowActiveError
|
||||
from .undoableentry import UndoableEntry
|
||||
|
||||
from gramps.gen.constfunc import is_quartz
|
||||
#============================================================================
|
||||
#
|
||||
# MaskedEntry and ValidatableMaskedEntry copied and merged from the Kiwi
|
||||
@@ -1248,6 +1248,12 @@ def main(args):
|
||||
win = Gtk.Window()
|
||||
win.set_title('ValidatableMaskedEntry test window')
|
||||
win.set_position(Gtk.WindowPosition.CENTER)
|
||||
#Set the mnemonic modifier on Macs to alt-ctrl so that it
|
||||
#doesn't interfere with the extended keyboard, see
|
||||
#https://gramps-project.org/bugs/view.php?id=6943
|
||||
if is_quartz():
|
||||
win.set_mnemonic_modifier(
|
||||
Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.MOD1_MASK)
|
||||
def cb(window, event):
|
||||
Gtk.main_quit()
|
||||
win.connect('delete-event', cb)
|
||||
|
||||
@@ -27,7 +27,7 @@ plg.name = _("BSDDB")
|
||||
plg.name_accell = _("_BSDDB Database")
|
||||
plg.description = _("Berkeley Software Distribution Database Backend")
|
||||
plg.version = '1.0'
|
||||
plg.gramps_target_version = "5.1"
|
||||
plg.gramps_target_version = "5.0"
|
||||
plg.status = STABLE
|
||||
plg.fname = 'bsddb.py'
|
||||
plg.ptype = DATABASE
|
||||
|
||||
@@ -27,7 +27,7 @@ register(DATABASE,
|
||||
name_accell=_('_SQLite Database'),
|
||||
description=_('SQLite Database'),
|
||||
version='1.0.0',
|
||||
gramps_target_version='5.1',
|
||||
gramps_target_version='5.0',
|
||||
status=STABLE,
|
||||
fname='sqlite.py',
|
||||
databaseclass='SQLite',
|
||||
|
||||
@@ -21,7 +21,7 @@ from gramps.gen.plug._pluginreg import newplugin, STABLE
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
|
||||
MODULE_VERSION="5.1"
|
||||
MODULE_VERSION="5.0"
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
@@ -468,8 +468,9 @@ class RecurseDown:
|
||||
#calculate the text.
|
||||
myself.calc_text(self.database, indi_handle, fams_handle)
|
||||
|
||||
myself.add_mark(self.database,
|
||||
self.database.get_person_from_handle(indi_handle))
|
||||
if indi_handle:
|
||||
myself.add_mark(self.database,
|
||||
self.database.get_person_from_handle(indi_handle))
|
||||
|
||||
self.add_to_col(myself)
|
||||
|
||||
@@ -692,7 +693,8 @@ class MakePersonTree(RecurseDown):
|
||||
family2 = family2_h = None
|
||||
if self.do_parents:
|
||||
family2_h = center1.get_main_parents_family_handle()
|
||||
family2 = self.database.get_family_from_handle(family2_h)
|
||||
if family2_h:
|
||||
family2 = self.database.get_family_from_handle(family2_h)
|
||||
|
||||
mother2_h = father2_h = None
|
||||
if family2:
|
||||
|
||||
@@ -22,7 +22,7 @@ from gramps.gen.plug._pluginreg import *
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
|
||||
MODULE_VERSION="5.1"
|
||||
MODULE_VERSION="5.0"
|
||||
|
||||
# this is the default in gen/plug/_pluginreg.py: plg.require_active = True
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ from gramps.gen.plug._pluginreg import newplugin, STABLE, EXPORT
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
|
||||
MODULE_VERSION="5.1"
|
||||
MODULE_VERSION="5.0"
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
# standard python modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
import os
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
@@ -43,9 +42,12 @@ log = logging.getLogger(".WriteFtree")
|
||||
# Gramps modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from gramps.gen.utils.alive import probably_alive
|
||||
# keep the following line even though not obviously used (works on import)
|
||||
from gramps.gui.plug.export import WriterOptionBox
|
||||
from gramps.gui.glade import Glade
|
||||
from gramps.gui.dialog import ErrorDialog
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@@ -53,22 +55,25 @@ from gramps.gui.glade import Glade
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
def writeData(database, filename, user, option_box=None):
|
||||
""" function to export Web Family Tree file """
|
||||
writer = FtreeWriter(database, filename, user, option_box)
|
||||
return writer.export_data()
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# FtreeWriter
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class FtreeWriter:
|
||||
|
||||
""" Export a Web Family Tree format file """
|
||||
def __init__(self, database, filename, user, option_box=None):
|
||||
self.db = database
|
||||
self.filename = filename
|
||||
self.user = user
|
||||
self.option_box = option_box
|
||||
if isinstance(self.user.callback, collections.Callable): # callback is really callable
|
||||
# is callback is really callable?
|
||||
if isinstance(self.user.callback, collections.Callable):
|
||||
self.update = self.update_real
|
||||
else:
|
||||
self.update = self.update_empty
|
||||
@@ -78,121 +83,132 @@ class FtreeWriter:
|
||||
self.db = option_box.get_filtered_database(self.db)
|
||||
|
||||
self.plist = [x for x in self.db.iter_person_handles()]
|
||||
# the following are used to update the progress meter
|
||||
self.total = 2 * len(self.plist)
|
||||
self.count = 0
|
||||
self.oldval = 0 # we only update when percentage changes
|
||||
|
||||
def update_empty(self):
|
||||
""" used when no callback is present """
|
||||
pass
|
||||
|
||||
def update_real(self):
|
||||
""" Progress update """
|
||||
self.count += 1
|
||||
newval = int(100*self.count/self.total)
|
||||
newval = int(100 * self.count / self.total)
|
||||
if newval != self.oldval:
|
||||
self.user.callback(newval)
|
||||
self.oldval = newval
|
||||
|
||||
def export_data(self):
|
||||
""" main export processing """
|
||||
name_map = {}
|
||||
id_map = {}
|
||||
id_name = {}
|
||||
self.count = 0
|
||||
self.oldval = 0
|
||||
self.total = 2*len(self.plist)
|
||||
|
||||
for key in self.plist:
|
||||
self.update()
|
||||
pn = self.db.get_person_from_handle(key).get_primary_name()
|
||||
sn = pn.get_surname()
|
||||
items = pn.get_first_name().split()
|
||||
n = ("%s %s" % (items[0], sn)) if items else sn
|
||||
pnam = self.db.get_person_from_handle(key).get_primary_name()
|
||||
snam = pnam.get_surname()
|
||||
items = pnam.get_first_name().split()
|
||||
nam = ("%s %s" % (items[0], snam)) if items else snam
|
||||
|
||||
count = -1
|
||||
if n in name_map:
|
||||
if nam in name_map:
|
||||
count = 0
|
||||
while 1:
|
||||
nn = "%s%d" % (n, count)
|
||||
if nn not in name_map:
|
||||
break;
|
||||
nam_num = "%s%d" % (nam, count)
|
||||
if nam_num not in name_map:
|
||||
break
|
||||
count += 1
|
||||
name_map[nn] = key
|
||||
id_map[key] = nn
|
||||
name_map[nam_num] = key
|
||||
id_map[key] = nam_num
|
||||
else:
|
||||
name_map[n] = key
|
||||
id_map[key] = n
|
||||
id_name[key] = get_name(pn, sn, count)
|
||||
name_map[nam] = key
|
||||
id_map[key] = nam
|
||||
id_name[key] = get_name(pnam, snam, count)
|
||||
|
||||
with open(self.filename, "w", encoding='utf_8') as f:
|
||||
try:
|
||||
with open(self.filename, "w", encoding='utf_8') as file:
|
||||
return self._export_data(file, id_name, id_map)
|
||||
except IOError as msg:
|
||||
msg2 = _("Could not create %s") % self.filename
|
||||
ErrorDialog(msg2, str(msg), parent=self.option_box.window)
|
||||
return False
|
||||
|
||||
for key in self.plist:
|
||||
self.update()
|
||||
p = self.db.get_person_from_handle(key)
|
||||
name = id_name[key]
|
||||
father = mother = email = web = ""
|
||||
def _export_data(self, file, id_name, id_map):
|
||||
""" file export processing """
|
||||
for key in self.plist:
|
||||
self.update()
|
||||
pers = self.db.get_person_from_handle(key)
|
||||
name = id_name[key]
|
||||
father = mother = email = web = ""
|
||||
|
||||
family_handle = p.get_main_parents_family_handle()
|
||||
if family_handle:
|
||||
family = self.db.get_family_from_handle(family_handle)
|
||||
if family.get_father_handle() and \
|
||||
family.get_father_handle() in id_map:
|
||||
father = id_map[family.get_father_handle()]
|
||||
if family.get_mother_handle() and \
|
||||
family.get_mother_handle() in id_map:
|
||||
mother = id_map[family.get_mother_handle()]
|
||||
family_handle = pers.get_main_parents_family_handle()
|
||||
if family_handle:
|
||||
family = self.db.get_family_from_handle(family_handle)
|
||||
if family.get_father_handle() and \
|
||||
family.get_father_handle() in id_map:
|
||||
father = id_map[family.get_father_handle()]
|
||||
if family.get_mother_handle() and \
|
||||
family.get_mother_handle() in id_map:
|
||||
mother = id_map[family.get_mother_handle()]
|
||||
|
||||
#
|
||||
# Calculate Date
|
||||
#
|
||||
birth_ref = p.get_birth_ref()
|
||||
death_ref = p.get_death_ref()
|
||||
if birth_ref:
|
||||
birth_event = self.db.get_event_from_handle(birth_ref.ref)
|
||||
birth = birth_event.get_date_object()
|
||||
#
|
||||
# Calculate Date
|
||||
#
|
||||
birth_ref = pers.get_birth_ref()
|
||||
death_ref = pers.get_death_ref()
|
||||
if birth_ref:
|
||||
birth_event = self.db.get_event_from_handle(birth_ref.ref)
|
||||
birth = birth_event.get_date_object()
|
||||
else:
|
||||
birth = None
|
||||
if death_ref:
|
||||
death_event = self.db.get_event_from_handle(death_ref.ref)
|
||||
death = death_event.get_date_object()
|
||||
else:
|
||||
death = None
|
||||
|
||||
#if self.restrict:
|
||||
# alive = probably_alive(pers, self.db)
|
||||
#else:
|
||||
# alive = 0
|
||||
|
||||
if birth:
|
||||
if death:
|
||||
dates = "%s-%s" % (fdate(birth), fdate(death))
|
||||
else:
|
||||
birth = None
|
||||
if death_ref:
|
||||
death_event = self.db.get_event_from_handle(death_ref.ref)
|
||||
death = death_event.get_date_object()
|
||||
dates = fdate(birth)
|
||||
else:
|
||||
if death:
|
||||
dates = fdate(death)
|
||||
else:
|
||||
death = None
|
||||
dates = ""
|
||||
|
||||
#if self.restrict:
|
||||
# alive = probably_alive(p, self.db)
|
||||
#else:
|
||||
# alive = 0
|
||||
file.write('%s;%s;%s;%s;%s;%s\n' %
|
||||
(name, father, mother, email, web, dates))
|
||||
|
||||
if birth:
|
||||
if death:
|
||||
dates = "%s-%s" % (fdate(birth), fdate(death))
|
||||
else:
|
||||
dates = fdate(birth)
|
||||
else:
|
||||
if death:
|
||||
dates = fdate(death)
|
||||
else:
|
||||
dates = ""
|
||||
return True
|
||||
|
||||
f.write('%s;%s;%s;%s;%s;%s\n' % (name, father, mother, email, web,
|
||||
dates))
|
||||
|
||||
return True
|
||||
|
||||
def fdate(val):
|
||||
""" return properly formatted date """
|
||||
if val.get_year_valid():
|
||||
if val.get_month_valid():
|
||||
if val.get_day_valid():
|
||||
return "%d/%d/%d" % (val.get_day(), val.get_month(),
|
||||
val.get_year())
|
||||
else:
|
||||
return "%d/%d" % (val.get_month(), val.get_year())
|
||||
else:
|
||||
return "%d" % val.get_year()
|
||||
else:
|
||||
return ""
|
||||
return "%d/%d" % (val.get_month(), val.get_year())
|
||||
return "%d" % val.get_year()
|
||||
return ""
|
||||
|
||||
|
||||
def get_name(name, surname, count):
|
||||
"""returns a name string built from the components of the Name
|
||||
instance, in the form of Firstname Surname"""
|
||||
|
||||
return (name.first_name + ' ' +
|
||||
surname +
|
||||
(str(count) if count != -1 else '') +
|
||||
(', ' +name.suffix if name.suffix else '')
|
||||
)
|
||||
surname +
|
||||
(str(count) if count != -1 else '') +
|
||||
(', ' + name.suffix if name.suffix else ''))
|
||||
|
||||
@@ -24,7 +24,7 @@ from gramps.gen.plug._pluginreg import register, STABLE, UNSTABLE, GRAMPLET
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
|
||||
MODULE_VERSION="5.1"
|
||||
MODULE_VERSION="5.0"
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
@@ -1271,7 +1271,7 @@ register(GRAMPLET,
|
||||
gramps_target_version=MODULE_VERSION,
|
||||
status = UNSTABLE,
|
||||
fname="leak.py",
|
||||
height=400,
|
||||
height=200,
|
||||
gramplet = 'Leak',
|
||||
gramplet_title="Uncollected Objects",
|
||||
)
|
||||
|
||||
+37
-105
@@ -23,13 +23,7 @@
|
||||
"""
|
||||
Show uncollected objects in a window.
|
||||
"""
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
# Python modules
|
||||
#
|
||||
#------------------------------------------------------------------------
|
||||
import weakref
|
||||
import sys
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
# GNOME/GTK modules
|
||||
@@ -46,11 +40,10 @@ import gc
|
||||
#------------------------------------------------------------------------
|
||||
from gramps.gen.plug import Gramplet
|
||||
from gramps.gui.dialog import InfoDialog
|
||||
from gramps.gui.utils import is_right_click, ProgressMeter
|
||||
from gramps.gui.utils import is_right_click
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Leak
|
||||
@@ -65,7 +58,7 @@ class Leak(Gramplet):
|
||||
self.gui.get_container_widget().remove(self.gui.textview)
|
||||
self.gui.get_container_widget().add(self.gui.WIDGET)
|
||||
|
||||
flags = gc.DEBUG_UNCOLLECTABLE | gc.DEBUG_SAVEALL
|
||||
flags = gc.DEBUG_UNCOLLECTABLE|gc.DEBUG_SAVEALL
|
||||
if hasattr(gc, "DEBUG_OBJECTS"):
|
||||
flags = flags | gc.DEBUG_OBJECTS
|
||||
gc.set_debug(flags)
|
||||
@@ -81,27 +74,24 @@ class Leak(Gramplet):
|
||||
self.top.pack_start(self.label, False, False, 6)
|
||||
|
||||
self.scroll = Gtk.ScrolledWindow()
|
||||
# add a listview to the scrollable
|
||||
#add a listview to the scrollable
|
||||
self.list = Gtk.TreeView()
|
||||
self.list.set_headers_visible(True)
|
||||
self.list.connect('button-press-event', self._button_press)
|
||||
self.scroll.add(self.list)
|
||||
# make a model
|
||||
self.model = Gtk.ListStore(int, str, str)
|
||||
#make a model
|
||||
self.modeldata = []
|
||||
self.model = Gtk.ListStore(int, str)
|
||||
self.list.set_model(self.model)
|
||||
|
||||
# set the columns
|
||||
#set the columns
|
||||
self.renderer = Gtk.CellRendererText()
|
||||
column = Gtk.TreeViewColumn(_('Number'), self.renderer, text=0)
|
||||
column.set_resizable(True)
|
||||
column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
|
||||
self.list.append_column(column)
|
||||
column = Gtk.TreeViewColumn(_('Referrer'), self.renderer, text=1)
|
||||
column.set_resizable(True)
|
||||
column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
|
||||
self.list.append_column(column)
|
||||
column = Gtk.TreeViewColumn(_('Uncollected object'), self.renderer,
|
||||
text=2)
|
||||
text=1)
|
||||
column.set_resizable(True)
|
||||
column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
|
||||
self.list.append_column(column)
|
||||
@@ -119,9 +109,7 @@ class Leak(Gramplet):
|
||||
return self.top
|
||||
|
||||
def main(self):
|
||||
self.label.set_text(_('Press Refresh to see initial results'))
|
||||
self.model.clear()
|
||||
# self.display() # We should only run this on demand
|
||||
self.display()
|
||||
|
||||
def _button_press(self, obj, event):
|
||||
if event.type == Gdk.EventType._2BUTTON_PRESS and event.button == 1:
|
||||
@@ -132,52 +120,32 @@ class Leak(Gramplet):
|
||||
return True
|
||||
|
||||
def referenced_in(self):
|
||||
model, _iter = self.selection.get_selected()
|
||||
if _iter is not None:
|
||||
count = model.get_value(_iter, 0)
|
||||
gc.collect(2)
|
||||
referrers = gc.get_referrers(self.junk[count])
|
||||
model, iter = self.selection.get_selected()
|
||||
if iter is not None:
|
||||
count = model.get_value(iter, 0)
|
||||
referrers = gc.get_referrers(self.modeldata[count])
|
||||
text = ""
|
||||
for referrer in referrers:
|
||||
match = ""
|
||||
try:
|
||||
if referrer is not self.junk:
|
||||
match = "**** "
|
||||
for indx in range(len(self.junk)):
|
||||
if referrer is self.junk[indx]:
|
||||
match = str(indx) + ": "
|
||||
break
|
||||
match += str(referrer) + '\n'
|
||||
text += str(referrer) + '\n'
|
||||
except ReferenceError:
|
||||
match += 'weakly-referenced object no longer exists %s'\
|
||||
% type(referrer)
|
||||
except:
|
||||
print(sys.exc_info())
|
||||
text += match
|
||||
InfoDialog(_('Referrers of %d') % count, text, parent=self.parent)
|
||||
pass
|
||||
InfoDialog(_('Referrers of %d') % count, text,
|
||||
parent=self.uistate.window)
|
||||
|
||||
def refers_to(self):
|
||||
model, _iter = self.selection.get_selected()
|
||||
if _iter is not None:
|
||||
count = model.get_value(_iter, 0)
|
||||
referents = gc.get_referents(self.junk[count])
|
||||
model, iter = self.selection.get_selected()
|
||||
if iter is not None:
|
||||
count = model.get_value(iter, 0)
|
||||
referents = gc.get_referents(self.modeldata[count])
|
||||
text = ""
|
||||
for referent in referents:
|
||||
match = ""
|
||||
try:
|
||||
match = "****: "
|
||||
for indx in range(len(self.junk)):
|
||||
if referent is self.junk[indx]:
|
||||
match = str(indx) + ': '
|
||||
break
|
||||
match += str(referent) + '\n'
|
||||
text += str(referent) + '\n'
|
||||
except ReferenceError:
|
||||
match += '%s weakly-referenced object no longer'\
|
||||
' exists\n' % type(referent)
|
||||
except:
|
||||
print(sys.exc_info())
|
||||
text += match
|
||||
InfoDialog(_('%d refers to') % count, text, parent=self.parent)
|
||||
pass
|
||||
InfoDialog(_('%d refers to') % count, text,
|
||||
parent=self.uistate.window)
|
||||
|
||||
def display(self):
|
||||
try:
|
||||
@@ -187,57 +155,21 @@ class Leak(Gramplet):
|
||||
"""
|
||||
Dummy.
|
||||
"""
|
||||
self.parent = self.top.get_toplevel()
|
||||
progress = ProgressMeter(
|
||||
_('Updating display...'), '', parent=self.parent, can_cancel=True)
|
||||
self.model.clear()
|
||||
self.junk = []
|
||||
gc.collect(2)
|
||||
self.junk = gc.garbage
|
||||
self.label.set_text(_('Uncollected Objects: %s') %
|
||||
str(len(self.junk)))
|
||||
progress.set_pass(_('Updating display...'), len(self.junk))
|
||||
for count in range(0, len(self.junk)):
|
||||
if progress.step():
|
||||
break
|
||||
try:
|
||||
refs = []
|
||||
referrers = gc.get_referrers(self.junk[count])
|
||||
for referrer in referrers:
|
||||
try:
|
||||
if referrer is not self.junk:
|
||||
for indx in range(0, len(self.junk)):
|
||||
if referrer is self.junk[indx]:
|
||||
refs.append(str(indx) + ' ')
|
||||
break
|
||||
except:
|
||||
print(sys.exc_info())
|
||||
if len(refs) > 3:
|
||||
ref = ' '.join(refs[0:2]) + "..."
|
||||
else:
|
||||
ref = ' '.join(refs)
|
||||
self.model.clear()
|
||||
count = 0
|
||||
if len(gc.garbage):
|
||||
for each in gc.garbage:
|
||||
try:
|
||||
self.model.append((count, ref, str(self.junk[count])))
|
||||
self.modeldata.append(each)
|
||||
self.model.append((count, str(each)))
|
||||
except DBError:
|
||||
self.model.append((count, ref,
|
||||
'db.DB instance at %s' %
|
||||
id(self.junk[count])))
|
||||
self.modeldata.append(each)
|
||||
self.model.append((count, 'db.DB instance at %s' % id(each)))
|
||||
except ReferenceError:
|
||||
self.model.append((
|
||||
count, ref,
|
||||
'weakly-referenced object no longer exists %s'
|
||||
% type(self.junk[count])))
|
||||
except TypeError:
|
||||
self.model.append((
|
||||
count, ref,
|
||||
'Object cannot be displayed %s'
|
||||
% type(self.junk[count])))
|
||||
except:
|
||||
print(sys.exc_info())
|
||||
except ReferenceError:
|
||||
InfoDialog(_('Reference Error'), "Refresh to correct",
|
||||
parent=self.parent)
|
||||
progress.close()
|
||||
pass
|
||||
count += 1
|
||||
self.label.set_text(_('Uncollected Objects: %s') % str(len(gc.garbage)))
|
||||
|
||||
def apply_clicked(self, obj):
|
||||
self.display()
|
||||
|
||||
@@ -50,6 +50,7 @@ class PersonDetails(Gramplet):
|
||||
self.gui.get_container_widget().remove(self.gui.textview)
|
||||
self.gui.get_container_widget().add(self.gui.WIDGET)
|
||||
self.uistate.connect('nameformat-changed', self.update)
|
||||
self.uistate.connect('placeformat-changed', self.update)
|
||||
|
||||
def build_gui(self):
|
||||
"""
|
||||
|
||||
@@ -85,9 +85,7 @@ class RelativesGramplet(Gramplet):
|
||||
spouse = database.get_person_from_handle(spouse_handle)
|
||||
spousename = name_displayer.display(spouse)
|
||||
text = "%s" % spousename
|
||||
self.append_text(_("%(count)d. %(relation)s: ") %
|
||||
{"count": famc,
|
||||
"relation": family.get_relationship()})
|
||||
self.append_text(_("%d. Partner: ") % (famc))
|
||||
self.link(text, 'Person', spouse_handle)
|
||||
self.append_text("\n")
|
||||
else:
|
||||
|
||||
@@ -21,7 +21,7 @@ from gramps.gen.plug._pluginreg import *
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
|
||||
MODULE_VERSION="5.1"
|
||||
MODULE_VERSION="5.0"
|
||||
|
||||
# this is the default in gen/plug/_pluginreg.py: plg.require_active = True
|
||||
|
||||
|
||||
@@ -1072,7 +1072,7 @@ class FamilyLinesReport(Report):
|
||||
|
||||
def get_event_place(self, event):
|
||||
""" get the place of the event """
|
||||
place_text = None
|
||||
place_text = ''
|
||||
place_handle = event.get_place_handle()
|
||||
if place_handle:
|
||||
place = self._db.get_place_from_handle(place_handle)
|
||||
|
||||
@@ -21,7 +21,7 @@ from gramps.gen.plug._pluginreg import newplugin, STABLE, IMPORT
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
|
||||
MODULE_VERSION="5.1"
|
||||
MODULE_VERSION="5.0"
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+1708
-513
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user