Compare commits

...
Author SHA1 Message Date
John Ralls 17c1dfe950 File copy doesn't work if the glob can match directories. 2018-08-05 13:18:35 -07:00
John Ralls 4531cbd049 Install the docs/gramps directory in the bundle.
Fixes bug #10705.
2018-08-04 21:40:34 -07:00
Paul CulleyandSam Manzi d24fc82031 Fix error when opening bsddb db in read-only mode (#628)
Fixes #10615
2018-06-22 10:44:13 +10:00
Paul CulleyandSam Manzi 16a9cd4c93 Fix FanChartDesc for typo (#610)
Fixes #10565

- Bug occurs when selected person has more than 4 parents.
2018-05-09 13:23:05 +10:00
Paul CulleyandSam Manzi 94018ed33a Fix multi-page Graph output to pdf with filenames containg spaces (#560)
Fixes #10470
2018-03-03 09:02:17 +11:00
arnaullvandNick Hall ffd77dc404 Updated catalan translation 2018-02-20 18:19:49 +00:00
Zdeněk Hataš 7c6e531c26 Czech translation fixes 2018-02-19 19:11:00 +01:00
Sam Manzi bc4b006dbe Increment year to 2018 for About box. 2018-02-13 13:29:09 +11:00
John Ralls 40da63a0fb Release gramps 4.2.8 on Mac. 2018-02-10 11:02:33 -08:00
John Ralls 6fafe8f6c3 Update pyicu to v 2.0.3 2018-02-10 11:01:25 -08:00
John Ralls 66a0c619b9 Consolidate Python2 and Python3 meta-modules.
GObject-Introspection now works with Python3 so the split isn't needed any more.
2018-02-10 11:01:24 -08:00
Nick Hall 6ffc095db0 Bump to 4.2.9 2018-02-09 21:12:16 +00:00
Nick Hall 5a5f4970f7 Release Gramps 4.2.8 2018-02-09 21:05:56 +00:00
Nick Hall ea3d49e50c Update Changelog and NEWS files 2018-02-09 21:02:23 +00:00
prculley e4b6d9beda Fix Geography views for bad 'dbstate.is_open()' test
Fixes #10417
2018-02-09 09:01:41 -06:00
Nick Hall b6f68c03bb Bump to 4.2.8 2018-02-08 20:36:04 +00:00
Nick Hall f5a66c3958 Release Gramps 4.2.7 2018-02-08 20:27:16 +00:00
Nick Hall c6ce61b4b9 Update ChangeLog and NEWS files 2018-02-08 19:58:51 +00:00
Nick Hall 9c050491ef Update translation template for new release 2018-02-08 17:51:13 +00:00
prculleyandNick Hall 54ab2820c3 Fix Export View to CSV when Unicode characters are present
Fixes #10404
2018-02-06 22:26:04 +00:00
prculley 1bf9fef1cb Fix several intl date displayers for missing parameter.
Fixes #10196
2018-02-05 09:40:18 -06:00
niememat dafde8df12 Update finnish translation 2018-01-29 20:52:22 +02:00
Paul CulleyandSam Manzi ac6eb9661c DescendantTree report; fix crashes and Title spacing for Gramps42 (#537)
* Fix DescendentTree report for crashes

Fixes #10377

* Fix DescendentTree report; more space beneath Title

Issue #10377
2018-01-29 14:35:03 +11:00
Paul CulleyandSam Manzi 09bc188a1c Fix Book XML handler to deal with unusual characters in Book name Gramps42 (#536)
* Fix Book XML handler to deal with unusual characters in Book name

Fixes #10387

* Fix 'Generate Book' dialog for bad transient parent

* Fix Book XML handler for unusual characters in report name

Issue #10387
2018-01-29 14:29:43 +11:00
Leonhaeuser 4d68f68742 update German translation 2018-01-27 15:30:53 +01:00
Nick Hall 70a634da1e Add support for new genealogy tree report category
Issue #10223.
2018-01-26 22:19:40 +00:00
prculleyandNick Hall ec44396680 Fix Media Preview Gramplet for closed db
Fixes #10332
2018-01-22 21:30:44 +00:00
prculleyandNick Hall 5490ff0b84 Suport FTM 2017 Gedcom tags on import
Bug #10285
FTM adds subordinate PLAC and OBJE data to INDI.ADDR which is
non-standard.  So treat it as a Residence Event, instead of an
Address.
FTM puts _DATE and _TEXT tags subordinate to OBJE, also non-standard.
2018-01-22 21:30:44 +00:00
prculleyandNick Hall a339e68877 Fix Person, Family Sidebar Filters to add custom Event types
Fixes #10112
Also fix Family Sidebar Filter RelType init with closed db
2018-01-22 21:30:44 +00:00
prculleyandNick Hall dd0956d6ce Fix QuestionDialog display for html like characters in title
Fixes #10298
2018-01-22 21:30:44 +00:00
prculleyandNick Hall 7911785ea4 Fix FamilyRelationshpType _DATAMAP order to correspond with values
Fixes #10275
2018-01-22 21:30:43 +00:00
prculleyandNick Hall 9eebeb05da Fix Gedcom import for illegal Gedcom Family Attributes
Issue #10262
TMG Gedcom exports an illegal NCHI with sub-data for FAM.  Gramps
could not handle this and attached the sub-data to the FAM creating
some corrupted Event records.
2018-01-22 21:30:43 +00:00
prculleyandNick Hall a492511533 Fix Gedcom export for bad Hebrew Months
Fixes #10245
2018-01-22 21:30:43 +00:00
prculleyandNick Hall ed907def1f Change INSTALL to replace 'python' with 'python3 for script invokes 2018-01-22 21:30:43 +00:00
prculleyandNick Hall 22a9cbdcc8 Fix CSV importer for place event name using gramps_id
Fixes #10239
2018-01-22 21:30:43 +00:00
prculleyandNick Hall 84e0d45ab6 Fix Geography view 'Find' when db is closed.
Fixes #10229
2018-01-22 21:30:43 +00:00
prculleyandNick Hall 85f42908f4 Fix interactive search for exception on click then down arrow
Fixes #10226
2018-01-22 21:30:43 +00:00
prculleyandNick Hall da1c942509 Create where_is utility to locate a binary in the standard places
This is particularly useful on Mac OS X where Gramps is passed a PATH
that does not include elements added by the terminal shell.
2018-01-22 21:30:43 +00:00
prculleyandNick Hall d98db27dcc Fix relationship Graph so Unicode chars on Multiple pages works.
Fixes: #9783, #9359, #8080, #6782, #6108
Bugs: #6431, #4164, #1485, #2964, #2790
2018-01-22 21:30:43 +00:00
prculleyandNick Hall 2b74e0734a update authors file 2018-01-22 21:30:43 +00:00
prculleyandNick Hall ebae9c3915 Fix Gedcom import for "1 MARR Y" issue
Fixes #10188
2018-01-22 21:30:43 +00:00
Paul CulleyandSam Manzi 286289aaf7 Fix Export Web Family Tree for errors on file write (#525)
Fixes #10364
2018-01-20 16:59:05 +11:00
Paul CulleyandSam Manzi e8489bf53c Fix Citation Editor to Tab out of Confidence ComboBox (#526)
Fixes #10351
2018-01-20 16:30:53 +11:00
John Ralls 9946d6f993 Reset the dependencies on the new meta-module. 2018-01-15 07:36:58 -08:00
John Ralls 00f87cc70e Use online modules.
Instead of the path that works on my machines only.
2018-01-15 07:33:21 -08:00
John Ralls 2fbc0b8aeb Consolidate Python2 and Python3 meta-modules.
GObject-Introspection now works with Python3 so the split isn't needed any more.
2018-01-15 07:27:43 -08:00
Nick Hall 56122b0a54 Use None as the foreground colour for untagged rows in list views
The call to get_style_context is not required.  It also caused
problems in Windows due to excessive calls to the foreground_color
method.

Fixes #10365.
2018-01-14 18:03:14 +00:00
Nick Hall 6648ecb5ff Fix shading colour in relationship view for dark themes
Fixes #7749.
2018-01-09 18:11:59 +00:00
Nick Hall 1b2d0a1956 Fix link colour for dark themes
Issue #7749.
2018-01-09 18:11:42 +00:00
Nick Hall 6bdac08358 Fix default foreground colour in list views for dark themes
Issue #7749.
2018-01-09 18:11:22 +00:00
prculley e74dc2fa8c fix Undo; crashes due to race in Gtk
Some Redraws occur during a model clear when the row changed signal
is emmitted.  Model is only partially cleared at that point.
Under some conditions, some more of the model gets
cleared before the redraw completes, and redraw fails.
fixes #9932
2017-10-14 10:05:24 -05:00
prculleyandNick Hall 314f7cce65 Gedcom import with OBJE/FORM URL on event
Fixes #7593.
2017-10-14 01:35:33 +01:00
JosipandNick Hall 11776db76d EOFError [Ran out of input] in Clipboard (#411)
[EOFError: Ran out of input] for Clipboard when you drag and
drop the bolded lines in selected tabs of Person Edit

Attempt to drag and drop any of the "bolded lines" in the Events or
Names tabs into the clipboard window. The unbolded lines drag and drop
ok.

Fixes #8788.
2017-10-14 01:26:49 +01:00
JosipandNick Hall eecf48e6e5 Cannot drag & drop textual value via clipboard
Allows dnd of textual values from clipboard and shows icon of
target-type during dragging instead of row image, except for text target
shows actual text as drag icon.

Fixes #7532.
2017-10-14 01:21:41 +01:00
prculley bedebc524b Fix 'DbBsddbRead' object has no attribute '_Callback__callback_map'
Fixes #10219
Same idea as 87a5412 for Gramps50
2017-10-06 14:32:25 -05:00
SNoiraud c01ee8ed7d Reports - Narrated Web Site Failure
Fixes #10206
2017-09-24 21:17:20 +02:00
John Ralls 578f4bdf62 Fix link path in gramps-launcher compile instructions. 2017-08-26 16:38:31 -07:00
John Ralls baadf6158a Release 4.2.6 on Mac. 2017-08-26 11:19:32 -07:00
romjerome 024204771b bump to 4.2.7 2017-08-01 11:08:29 +02:00
81 changed files with 15147 additions and 17561 deletions
+9 -249
View File
@@ -1,253 +1,13 @@
2017-08-01 romjerome <romjerome@users.noreply.github.com>
2018-02-09 prculley <paulr2787@gmail.com>
* po/fr.po: typos
* gramps/plugins/view/geoclose.py,
gramps/plugins/view/geoevents.py,
gramps/plugins/view/geofamclose.py,
gramps/plugins/view/geofamily.py, gramps/plugins/view/geoperson.py,
gramps/plugins/view/geoplaces.py: Fix Geography views for bad
'dbstate.is_open()' test Fixes #10417
2017-07-31 Zdeněk Hataš <zdenek.hatas@gmail.com>
2018-02-08 Nick Hall <nick-h@gramps-project.org>
* po/cs.po: update to recent pot
2017-07-29 romjerome <romjerome@users.noreply.github.com>
* NEWS: start '4.2.6' section
2017-07-29 romjerome <romjerome@users.noreply.github.com>
* po/fr.po, po/gramps.pot: Update template no modified or new message id, update references (header and
comments)
2017-07-26 vantu5z <vantu5z@mail.ru>
* po/ru.po: update Russian translation - merge last template - fix typo - change 'е' to 'ё' in some words
2017-06-21 Nick Hall <nick-h@gramps-project.org>
* gramps/gui/filters/sidebar/_citationsidebarfilter.py: Fix
HasCitation rule in citation filter sidebar Filter parameters should be of type str. Fixes #10076. (cherry picked from commit adb4883291159298781144706fa5bc6331870ef2)
2016-04-06 Nick Hall <nick-h@gramps-project.org>
* gramps/gen/filters/rules/_rule.py: Remove deprecated locale flag re.L is deprecated and re.U is now default. (cherry picked from commit da3db4bc2828210fa77d6d57bb28a7b0cc2c19e8) Fixes #10136
2017-07-23 Paul Franklin <pf.98052@gmail.com>
* gramps/gui/glade/editdate.glade: Date Editor has 'Type' and
'Quality' labels swapped Fixes #10135
2017-07-15 Leonhaeuser <mirko@leonhaeuser.de>
* po/de.po: update German translation Fix #0010131
2017-07-08 Lajos Nemeséri <nemeseril@gmail.com>
* po/hu.po: Update Hungarian translation
2017-06-30 John Ralls <jralls@ceridwen.us>
* gramps/gui/display.py: Open web links with /usr/bin/open on Mac
and Python older than 3.5 Works around https://bugs.python.org/issue24452 Fixes #10105
2017-06-27 John Ralls <jralls@ceridwen.us>
* gramps/gen/utils/grampslocale.py: FamilyGroup Report crash Python's sort routine gratuitously converts the bytearrays returned
by ICU::Collator::getByteArray to a string when storing it. This
resulted in a TypeError when attempting to compare a just-returned
bytearray with a stored string. We work around this by converting the bytearray into its hexadecimal
representation and then decoding that into a string. Fixes #10077.
2017-06-22 Paul Franklin <pf.98052@gmail.com>
* gramps/gen/plug/docgen/graphdoc.py: names not displayed in
relationship graph Issue #10093
2017-06-21 Paul Franklin <pf.98052@gmail.com>
* gramps/gen/const.py: Outdated Bugtracker link in Unexpected Error
reporting wizard Fixes #10100
2017-06-21 Paul Franklin <pf.98052@gmail.com>
* gramps/plugins/graph/gvfamilylines.py: Family Lines Report
generates empty PDFs when name [has double-quotes] Fixes #10096
2017-06-19 Paul Franklin <pf.98052@gmail.com>
* gramps/gui/dbman.py: non-local character in DB name causes crash
on Windows Fixes #10095
2017-05-28 Josip <bpisoj@gmail.com>
* gramps/gui/views/listview.py,
gramps/gui/widgets/interactivesearchbox.py: Quick search with Czech
characters -> Crash Crash happen when checking for "event.string" in "treeview_keypress"
function in "gramps/gui/widgets/interactivesearchbox.py" Note that
https://lazka.github.io/pgi-docs/Gdk-3.0/classes/EventKey.html#Gdk.EventKeysays it is deprecated and should never be used. Gdk.keyval_to_unicode return the corresponding unicode character, or
0 if there is no corresponding character. Fixes #9130. (cherry picked from commit bc1724533171fe69a6a775101d8d4318ba05b396)
2017-05-31 John Ralls <jralls@ceridwen.us>
* gramps/gen/datehandler/_dateparser.py: 8366: February 29th invalid
date in Julian dual-dated leap year
2017-05-19 Paul Franklin <pf.98052@gmail.com>
* gramps/plugins/textreport/indivcomplete.py: 10033: Note isn't
included on CIR when it is attached to a name
2017-05-15 Nick Hall <nick-h@gramps-project.org>
* gramps/gui/editors/displaytabs/citationembedlist.py,
gramps/gui/selectors/baseselector.py: Revert bugs #8785 and #9700 Expanding tree selectors by default caused performance issues. The
expanded person selector also made it more difficult to select a
specific person in some circumstances. GEPS 041 has been created to discuss a better selector design.
2017-05-05 Sam Manzi <manzi.sam@gmail.com>
* gramps/gen/soundex.py: Fix bug 9975 Incorrect SoundEx result
(#388)
2017-04-29 Paul Culley <paulr2787@gmail.com>
* gramps/gui/editors/filtereditor.py: bug 9564; fix exception on
Event filter editor (#378) for 'Events occurring on a particular day of the week' filter
2017-03-22 Paul Franklin <pf.98052@gmail.com>
* gramps/plugins/export/exportxml.py: 9995: Bug in the Name Editor /
Group As
2017-03-16 Paul Franklin <pf.98052@gmail.com>
* po/tr.po: add datestrings to gramps42 Turkish translation
2017-02-11 Nick Hall <nick-h@gramps-project.org>
* gramps/plugins/export/exportcsv.py: 9945: Write PlaceID links in
CSV export When places are included in the export use PlaceID links, otherwise
generate a formatted place for the event.
2017-02-08 Doug Reider <dreider@gramps.org>
* mac/gramps.accel: Fix bookmarks keybinding on Mac to match the
documentation.
2017-02-08 Bernard Banko <beernarrd@gmail.com>
* po/sl.po: updated sl translation
2017-02-08 Bernard Banko <beernarrd@gmail.com>
* po/sl.po: corrected slovenian name editor keywords
2017-02-07 Paul Franklin <pf.98052@gmail.com>
* gramps/plugins/drawreport/ancestortree.py,
gramps/plugins/lib/librecurse.py: 9004: Error printing graphical
report ancestor tree
2017-02-07 Jérôme Rapinat <romjerome@yahoo.fr>
* gramps/plugins/drawreport/ancestortree.py: 9004: Error printing
graphical report ancestor tree
2017-02-02 Peter Landgren <peter.talken@telia.com>
* po/sv.po: Update Swedish translation
2017-02-02 romjerome <romjerome@users.noreply.github.com>
* gramps/plugins/tool/changenames.glade: 9907: add a spacing value
for children widgets fit better for some Gtk themes
2017-02-02 romjerome <romjerome@users.noreply.github.com>
* docs/gen/gen_display.rst: 8855: update API doc for place displayer
2017-01-25 Matti Niemelä <matti.u.niemela@gmail.com>
* po/fi.po: update finnish translation
2017-01-25 romjerome <romjerome@users.noreply.github.com>
* gramps/plugins/drawreport/ancestortree.py: 9904: revert fd83162 TODO: need a solution for recursive iteration and private records
2017-01-24 romjerome <romjerome@users.noreply.github.com>
* gramps/plugins/drawreport/ancestortree.py: 9904: Errors printing
graphical report ancestor tree
2017-01-24 romjerome <romjerome@users.noreply.github.com>
* gramps/gui/filters/sidebar/_familysidebarfilter.py: 9908: Custom
Family Relations not shown in the filter siderbar
2017-01-14 romjerome <romjerome@users.noreply.github.com>
* gramps/plugins/textreport/tagreport.py: 9899: fix non-textual
value on place name
2017-01-11 Luigi Toscano <luigi.toscano@tiscali.it>
* po/it.po: Italian translation: updates and fixes
2017-01-08 Luigi Toscano <luigi.toscano@tiscali.it>
* po/it.po: Refresh it.po using the current pot template
2017-01-03 Nick Hall <nick-h@gramps-project.org>
* gramps/gui/editors/editnote.py: 9878: Update note text before
closing The object must be updated so that changes can be detected.
2017-01-02 SNoiraud <serge.noiraud@laposte.net>
* gramps/plugins/lib/maps/placeselection.py: 9865: Can not link the
place on geography view
2016-12-28 Paul Franklin <pf.98052@gmail.com>
* gramps/plugins/drawreport/ancestortree.py: 9862: ancestor tree
doesn't use replacements
2016-12-23 Josip <bpisoj@gmail.com>
* gramps/gui/glade/reorder.glade: 8128: GtkDialog mapped without a
transient parent Fix Reorder Relationships dialog (cherry picked from commit 3b4ceeb630fff4d1f9ed1d64a6db20bef7262ec5)
2016-12-17 prculley <paulr2787@gmail.com>
* gramps/gui/glade/dialog.glade: bug 9235: Shrink size of Break Lock
(and other QuestionDialogs)
2016-12-17 prculley <paulr2787@gmail.com>
* gramps/gui/editors/editperson.py: bug 9470; Don't allow selecton
of Active or Home person until actually commited
2016-12-18 prculley <paulr2787@gmail.com>
* gramps/gui/widgets/interactivesearchbox.py: bug 9478; fix quick
search exception when nothing in searched list
2016-12-17 Paul Franklin <pf.98052@gmail.com>
* gramps/gen/const.py, gramps/version.py: bump to 4.2.6
2016-12-17 Paul Franklin <pf.98052@gmail.com>
* gramps/gui/editors/editfamily.py: 9612: Problem adding parents
[with Latin names]
2016-12-16 John Ralls <jralls@ceridwen.us>
* mac/Info.plist, mac/gramps.bundle, mac/gramps.modules: Release
4.2.5 on Mac.
2016-12-16 John Ralls <jralls@ceridwen.us>
* gramps/plugins/gramplet/gramplet.gpr.py: Fix failure to load
default gramplets if GExiv2 is missing or too old.
2016-12-15 romjerome <romjerome@users.noreply.github.com>
* make official 4.2.5 release
* Bump to 4.2.8
+8 -8
View File
@@ -33,11 +33,11 @@ all required and optional dependencies. Missing dependencies will
result in runtime errors.
To build and install, whether from a tarball or git repo:
python setup.py build
sudo python setup.py install
python3 setup.py build
sudo python3 setup.py install
You can avoid using sudo for the install step by specifying a prefix to which you have write priviledge. The default is /usr/local, which is usually owned by root. You can learn of more options with
python setup.py --help
python3 setup.py --help
One can use gramps from the command line without installing it by
setting the following environment variables, but that won't provide
@@ -71,7 +71,7 @@ from the source directory.
b) You installed Gramps, and want to start it from the PYTHONPATH. In this
case use the command:
python -c 'from gramps.grampsapp import main; main()'
python3 -c 'from gramps.grampsapp import main; main()'
The executable 'gramps' in /usr/local/bin or /usr/bin from a) does
this for you.
@@ -79,7 +79,7 @@ from the source directory.
b) You downloaded the Gramps source code to a directory, and want to run it.
You can start Gramps from the source code directory with
python Gramps.py
python3 Gramps.py
See gramps/gen/const.py how Gramps finds its resource directories in case
you encounter problems.
@@ -90,17 +90,17 @@ If you would like to install Gramps without being root, or in an
alternative location on windows, supply the --root argument to setup.py
For example:
python setup.py install --root ~/test
python3 setup.py install --root ~/test
Packager's issues
------------------
There is a MANIFEST.in file to indicate the work needed.
To create a source distribution run:
python setup.py sdist
python3 setup.py sdist
If Gramps is built outside of the source tree in a temporary location (e.g. when
packaging for a distribution), the --resourcepath option can be used to specify
the path to the installed location of the Gramps resources (e.g. /usr/share):
python setup.py install --resourcepath=/usr/share
python3 setup.py install --resourcepath=/usr/share
+44
View File
@@ -1,3 +1,47 @@
2018-02-09
Version 4.2.8
* Fix Geography views for bad 'dbstate.is_open()' test
2018-02-08
Version 4.2.7
* Fix Export View to CSV when Unicode characters are present
* Fix several intl date displayers for missing parameter
* DescendantTree report; fix crashes and Title spacing
* Fix Book XML handler to deal with unusual characters in Book name
* Add support for new genealogy tree report category
* Fix Media Preview Gramplet for closed db
* Suport FTM 2017 Gedcom tags on import
* Fix Person, Family Sidebar Filters to add custom Event types
* Fix QuestionDialog display for html like characters in title
* Fix FamilyRelationshpType _DATAMAP order to correspond with values
* Fix Gedcom import for illegal Gedcom Family Attributes
* Fix Gedcom export for bad Hebrew Months
* Change INSTALL to replace 'python' with 'python3 for script invokes
* Fix CSV importer for place event name using gramps_id
* Fix Geography view 'Find' when db is closed
* Fix interactive search for exception on click then down arrow
* Create where_is utility to locate a binary in the standard places
* Fix relationship Graph so Unicode chars on Multiple pages works
* Update authors file
* Fix Gedcom import for "1 MARR Y" issue
* Fix Export Web Family Tree for errors on file write
* Fix Citation Editor to Tab out of Confidence ComboBox
* Reset the dependencies on the new meta-module
* Use online modules
* Consolidate Python2 and Python3 meta-modules
* Use None as the foreground colour for untagged rows in list views
* Fix shading colour in relationship view for dark themes
* Fix link colour for dark themes
* Fix default foreground colour in list views for dark themes
* Fix Undo; crashes due to race in Gtk
* Gedcom import with OBJE/FORM URL on event
* EOFError [Ran out of input] in Clipboard
* Cannot drag & drop textual value via clipboard
* Fix 'DbBsddbRead' object has no attribute '_Callback__callback_map'
* Reports - Narrated Web Site Failure
* Fix link path in gramps-launcher compile instructions
* Update translations: fi, de
2017-08-01
Version 4.2.6
* Fix HasCitation rule in citation filter sidebar
+3
View File
@@ -94,6 +94,9 @@
<author title="contributor">
Nick Hall &lt;<html:a href="mailto:nick__hall@hotmail.com">nick__hall@hotmail.com</html:a>&gt;
</author>
<author title="contributor">
Paul Culley &lt;<html:a href="mailto:paulr2787@gmail.com">paulr2787@gmail.com</html:a>&gt;
</author>
<author title="contributor">
Peter Landgren &lt;<html:a href="mailto:peter.talken@telia.com">peter.talken@telia.com</html:a>&gt;
</author>
+27 -4
View File
@@ -46,7 +46,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, StringOption,
@@ -54,8 +55,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
from gramps.gen.dbstate import DbState
@@ -233,6 +234,15 @@ class CommandLineReport(object):
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)
@@ -301,6 +311,10 @@ class CommandLineReport(object):
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"
@@ -478,6 +492,15 @@ class CommandLineReport(object):
# 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:
@@ -640,7 +663,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,
+2 -2
View File
@@ -132,7 +132,7 @@ sys.path.insert(0, ROOT_DIR)
git_revision = get_git_revision(ROOT_DIR)
if sys.platform == 'win32' and git_revision == "":
git_revision = get_git_revision(os.path.split(ROOT_DIR)[1])
#VERSION += git_revision
VERSION += git_revision
#VERSION += "-1"
#
@@ -190,7 +190,7 @@ GTK_GETTEXT_DOMAIN = 'gtk30'
#
#-------------------------------------------------------------------------
COPYRIGHT_MSG = "© 2001-2006 Donald N. Allingham\n" \
"© 2007-2016 The Gramps Developers"
"© 2007-2018 The Gramps Developers"
COMMENTS = _("Gramps\n (Genealogical Research and Analysis "
"Management Programming System)\n"
"is a personal genealogy program.")
+1 -1
View File
@@ -221,7 +221,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
"""
+1 -1
View File
@@ -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
"""
+1 -1
View File
@@ -185,7 +185,7 @@ class DateDisplayLT(DateDisplay):
"mmmm-MM-DD (ISO)", "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
"""
+1 -1
View File
@@ -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
"""
+1 -1
View File
@@ -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
"""
+1 -1
View File
@@ -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
"""
+9 -7
View File
@@ -763,11 +763,11 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
# The DB_PRIVATE flag must go if we ever move to multi-user setup
env_flags = db.DB_CREATE | db.DB_PRIVATE |\
db.DB_INIT_MPOOL |\
db.DB_INIT_LOG | db.DB_INIT_TXN
# As opposed to before, we always try recovery on databases
env_flags |= db.DB_RECOVER
db.DB_INIT_MPOOL
if not self.readonly:
env_flags |= db.DB_INIT_LOG | db.DB_INIT_TXN
# As opposed to before, we always try recovery on databases
env_flags |= db.DB_RECOVER
# Environment name is now based on the filename
env_name = name
@@ -782,7 +782,8 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
pass
raise DbEnvironmentError(msg)
self.env.txn_checkpoint()
if not self.readonly:
self.env.txn_checkpoint()
if callback:
callback(25)
@@ -1478,7 +1479,8 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
return
if self.txn:
self.transaction_abort(self.transaction)
self.env.txn_checkpoint()
if not self.readonly:
self.env.txn_checkpoint()
self.__close_metadata()
self.name_group.close()
+4 -4
View File
@@ -49,11 +49,11 @@ class FamilyRelType(GrampsType):
_DEFAULT = MARRIED
_DATAMAP = [
(UNKNOWN, _("Unknown"), "Unknown"),
(CUSTOM, _("Custom"), "Custom"),
(MARRIED, _("Married"), "Married"),
(UNMARRIED, _("Unmarried"), "Unmarried"),
(CIVIL_UNION, _("Civil Union"), "Civil Union"),
(UNMARRIED, _("Unmarried"), "Unmarried"),
(MARRIED, _("Married"), "Married"),
(UNKNOWN, _("Unknown"), "Unknown"),
(CUSTOM, _("Custom"), "Custom"),
]
def __init__(self, value=None):
+2 -2
View File
@@ -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,
@@ -49,7 +49,7 @@ __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,
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,
+4 -1
View File
@@ -94,8 +94,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
@@ -1009,6 +1011,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,
+1
View File
@@ -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
+68 -26
View File
@@ -8,6 +8,8 @@
# Copyright (C) 2007 Brian G. Matherly
# Copyright (C) 2009 Benny Malengier
# Copyright (C) 2009 Gary Burton
# Copyright (C) 2017 Mindaugas Baranauskas
# 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
@@ -42,7 +44,7 @@ import sys
#-------------------------------------------------------------------------------
from ...const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
from ...utils.file import search_for
from ...utils.file import search_for, where_is
from . import BaseDoc
from ..menu import NumberOption, TextOption, EnumeratedListOption, \
BooleanOption
@@ -101,13 +103,10 @@ if win():
DETACHED_PROCESS = DWORD(0x00000008).value
else:
_DOT_FOUND = search_for("dot")
if search_for("gs") == 1:
_GS_CMD = "gs"
else:
_GS_CMD = ""
#-------------------------------------------------------------------------------
_GS_CMD = where_is("gs")
#------------------------------------------------------------------------------
#
# GVOptions
#
@@ -659,6 +658,8 @@ class GVPsDoc(GVDocBase):
# disappeared. I used 1 inch margins always.
# See bug tracker issue 2815
# :cairo does not work with Graphviz 2.26.3 and later See issue 4164
# recent versions of Graphvis doesn't even try, just puts out a single
# large page.
command = 'dot -Tps:cairo -o"%s" "%s"' % (self._filename, tmp_dot)
if win():
@@ -670,8 +671,8 @@ class GVPsDoc(GVDocBase):
dotversion = str(Popen(['dot', '-V'],
stderr=PIPE).communicate(input=None)[1])
# Problem with dot 2.26.3 and later and multiple pages, which gives "cairo: out of
# memory" If the :cairo is skipped for these cases it gives acceptable
# result.
# memory" If the :cairo is skipped for these cases it gives bad
# result for non-Latin-1 characters (utf-8).
if (dotversion.find('2.26.3') or dotversion.find('2.28.0') != -1) and (self.vpages * self.hpages) > 1:
command = command.replace(':cairo','')
os.system(command)
@@ -921,9 +922,11 @@ class GVPdfGsDoc(GVDocBase):
# Generate PostScript using dot
# Reason for using -Tps:cairo. Needed for Non Latin-1 letters
# See bug tracker issue 2815
# :cairo does not work with Graphviz 2.26.3 and later See issue 4164
command = 'dot -Tps:cairo -o"%s" "%s"' % ( tmp_ps, tmp_dot )
# :cairo does not work with with multi-page See issue 4164
# recent versions of Graphvis doesn't even try, just puts out a single
# large page, so we use Ghostscript to split it up.
command = 'dot -Tps:cairo -o"%s" "%s"' % (tmp_ps, tmp_dot)
if win():
dotversion = str(Popen(['dot', '-V'],
creationflags=DETACHED_PROCESS,
@@ -932,26 +935,65 @@ class GVPdfGsDoc(GVDocBase):
else:
dotversion = str(Popen(['dot', '-V'],
stderr=PIPE).communicate(input=None)[1])
# Problem with dot 2.26.3 and later and multiple pages, which gives "cairo: out
# of memory". If the :cairo is skipped for these cases it gives
# acceptable result.
if (dotversion.find('2.26.3') or dotversion.find('2.28.0') != -1) and (self.vpages * self.hpages) > 1:
command = command.replace(':cairo','')
os.system(command)
# Add .5 to remove rounding errors.
paper_size = self._paper.get_size()
width_pt = int( (paper_size.get_width_inches() * 72) + 0.5 )
height_pt = int( (paper_size.get_height_inches() * 72) + 0.5 )
width_pt = int((paper_size.get_width_inches() * 72) + .5)
height_pt = int((paper_size.get_height_inches() * 72) + .5)
if (self.vpages * self.hpages) == 1:
# -dDEVICEWIDTHPOINTS=%d' -dDEVICEHEIGHTPOINTS=%d
command = '%s -q -sDEVICE=pdfwrite -dNOPAUSE '\
'-dDEVICEWIDTHPOINTS=%d -dDEVICEHEIGHTPOINTS=%d '\
'-sOutputFile="%s" "%s" -c quit' % (
_GS_CMD, width_pt, height_pt, self._filename, tmp_ps)
os.system(command)
os.remove(tmp_ps)
return
# Margins (in centimeters) to pixels 72/2.54=28.345
MarginT = int(28.345 * self._paper.get_top_margin())
MarginB = int(28.345 * self._paper.get_bottom_margin())
MarginR = int(28.345 * self._paper.get_right_margin())
MarginL = int(28.345 * self._paper.get_left_margin())
MarginX = MarginL + MarginR
MarginY = MarginT + MarginB
# Convert to PDF using ghostscript
command = '%s -q -sDEVICE=pdfwrite -dNOPAUSE -dDEVICEWIDTHPOINTS=%d' \
' -dDEVICEHEIGHTPOINTS=%d -sOutputFile="%s" "%s" -c quit' \
% ( _GS_CMD, width_pt, height_pt, self._filename, tmp_ps )
list_of_pieces = []
x_rng = range(1, self.hpages + 1) if 'L' in self.pagedir \
else range(self.hpages , 0, -1)
y_rng = range(1, self.vpages + 1) if 'B' in self.pagedir \
else range(self.vpages , 0, -1)
if self.pagedir[0] in 'TB':
the_list = ((x, y) for y in y_rng for x in x_rng)
else:
the_list = ((x, y) for x in x_rng for y in y_rng)
for x, y in the_list:
# Slit PS file to pieces of PDF
PageOffsetX = (x - 1) * (MarginX - width_pt)
PageOffsetY = (y - 1) * (MarginY - height_pt)
tmp_pdf_piece = "%s_%d_%d.pdf" % (tmp_ps, x, y)
list_of_pieces.append(tmp_pdf_piece)
# Generate Ghostscript code
command = '%s -q -dBATCH -dNOPAUSE -dSAFER -g%dx%d '\
'-sOutputFile="%s" -r72 -sDEVICE=pdfwrite '\
'-c "<</.HWMargins [%d %d %d %d] /PageOffset [%d %d]>> '\
'setpagedevice" -f "%s"' % (
_GS_CMD, width_pt + 10, height_pt + 10, tmp_pdf_piece,
MarginL, MarginB, MarginR, MarginT,
PageOffsetX + 5, PageOffsetY + 5, tmp_ps)
# Execute Ghostscript
os.system(command)
# Merge pieces to single multipage PDF ;
command = '%s -q -dBATCH -dNOPAUSE '\
'-sOUTPUTFILE="%s" -r72 -sDEVICE=pdfwrite %s '\
% (_GS_CMD, self._filename, ' '.join(list_of_pieces))
os.system(command)
# Clean temporary files
os.remove(tmp_ps)
for tmp_pdf_piece in list_of_pieces:
os.remove(tmp_pdf_piece)
os.remove(tmp_dot)
#-------------------------------------------------------------------------------
+632
View File
@@ -0,0 +1,632 @@
#
# 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
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")
else:
_LATEX_FOUND = search_for("lualatex")
#------------------------------------------------------------------------------
#
# 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':
male_bg = config.get('preferences.color-gender-male-death')[1:]
female_bg = config.get('preferences.color-gender-female-death')[1:]
neuter_bg = config.get('preferences.color-gender-unknown-death')[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(nick) if nick else '',
'\\surn{{{}}}'.format(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' % attr.get_value())
if str(attr.get_type()) == 'Comment':
self.write(level+1, 'comment = {%s},\n' % attr.get_value())
for mediaref in person.get_media_list():
media = db.get_object_from_handle(mediaref.ref)
path = media_path_full(db, media.get_path())
if os.path.isfile(path):
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 = _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=first[:where],
call=call,
after=first[where+len(call):])
else:
# ignore erroneous call name
return first
else:
return 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") 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:
with open(os.path.join(tmpdir, 'temp.tex'), "w") as texfile:
texfile.write(self._tex.getvalue())
os.system('lualatex -output-directory %s temp.tex >/dev/null'
% tmpdir)
shutil.copy(os.path.join(tmpdir, 'temp.pdf'), 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}]
+84 -65
View File
@@ -457,79 +457,98 @@ class BookList(object):
"""
Saves the current BookList to the associated file.
"""
f = open(self.file, "w")
f.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n")
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()
f.write('<book name="%s" database="%s">\n' % (name, dbname) )
for item in book.get_item_list():
f.write(' <item name="%s" trans_name="%s">\n' %
(item.get_name(),item.get_translated_name() ) )
options = item.option_class.handler.options_dict
for option_name in sorted(options.keys()): # enable a diff
option_value = options[option_name]
if isinstance(option_value, (list, tuple)):
f.write(' <option name="%s" value="" '
'length="%d">\n' % (
escape(option_name),
len(options[option_name]) ) )
for list_index in range(len(option_value)):
option_type = type_name(option_value[list_index])
value = escape(str(option_value[list_index]))
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 = escape(book.get_dbname())
b_f.write(' <book name="%s" database="%s">'
'\n' % (escape(name), dbname))
for item in book.get_item_list():
b_f.write(' <item name="%s" '
'trans_name="%s">\n' % (
item.get_name(),
item.get_translated_name()))
options = item.option_class.handler.options_dict
for option_name in sorted(options.keys()): # enable a diff
option_value = options[option_name]
if isinstance(option_value, (list, tuple)):
b_f.write(' <option name="%s" value="" '
'length="%d">\n' % (
escape(option_name),
len(options[option_name])))
for list_index in range(len(option_value)):
option_type = type_name(
option_value[list_index])
value = escape(str(option_value[list_index]))
value = value.replace('"', '&quot;')
b_f.write(' <listitem number="%d" '
'type="%s" value="%s"/>\n' % (
list_index,
option_type,
value))
b_f.write(' </option>\n')
else:
option_type = type_name(option_value)
value = escape(str(option_value))
value = value.replace('"', '&quot;')
f.write(' <listitem number="%d" type="%s" '
'value="%s"/>\n' % (
list_index,
option_type,
value ) )
f.write(' </option>\n')
else:
option_type = type_name(option_value)
value = escape(str(option_value))
value = value.replace('"', '&quot;')
f.write(' <option name="%s" type="%s" '
'value="%s"/>\n' % (
escape(option_name),
option_type,
value) )
b_f.write(' <option name="%s" type="%s" '
'value="%s"/>\n' % (
escape(option_name),
option_type,
value))
f.write(' <style name="%s"/>\n' % item.get_style_name() )
f.write(' </item>\n')
if book.get_paper_name():
f.write(' <paper name="%s"/>\n' % book.get_paper_name() )
if book.get_orientation() is not None: # 0 is legal
f.write(' <orientation value="%s"/>\n' %
book.get_orientation() )
if book.get_paper_metric() is not None: # 0 is legal
f.write(' <metric value="%s"/>\n' % book.get_paper_metric() )
if book.get_custom_paper_size():
size = book.get_custom_paper_size()
f.write(' <size value="%f %f"/>\n' % (size[0], size[1]) )
if book.get_margins():
for pos in range(len(book.get_margins())):
f.write(' <margin number="%s" value="%f"/>\n' %
(pos, book.get_margin(pos)) )
if book.get_format_name():
f.write(' <format name="%s"/>\n' % book.get_format_name() )
if book.get_output():
f.write(' <output name="%s"/>\n' % book.get_output() )
f.write('</book>\n')
b_f.write(' <style name="%s"/>'
'\n' % item.get_style_name())
b_f.write(' </item>\n')
if book.get_paper_name():
b_f.write(' <paper name="%s"/>'
'\n' % book.get_paper_name())
if book.get_orientation() is not None: # 0 is legal
b_f.write(' <orientation value="%s"/>'
'\n' % book.get_orientation())
if book.get_paper_metric() is not None: # 0 is legal
b_p_metric = book.get_paper_metric()
if isinstance(b_p_metric, bool):
b_p_metric = int(b_p_metric)
b_f.write(' <metric value="%s"/>'
'\n' % b_p_metric)
if book.get_custom_paper_size():
size = book.get_custom_paper_size()
b_f.write(' <size value="%f %f"/>'
'\n' % (size[0], size[1]))
if book.get_margins():
for pos in range(len(book.get_margins())):
b_f.write(' <margin number="%s" '
'value="%f"/>\n' % (
pos, book.get_margin(pos)))
if book.get_format_name():
b_f.write(' <format name="%s"/>'
'\n' % book.get_format_name())
if book.get_output():
b_f.write(' <output name="%s"/>'
'\n' % escape(book.get_output()))
b_f.write(' </book>\n')
b_f.write('</booklist>\n')
f.write('</booklist>\n')
f.close()
def parse(self):
"""
Loads the BookList from the associated file, if it exists.
"""
try:
p = make_parser()
p.setContentHandler(BookParser(self, self.dbase))
the_file = open(self.file)
p.parse(the_file)
the_file.close()
parser = make_parser()
parser.setContentHandler(BookParser(self, self.dbase))
# 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):
pass
+2 -1
View File
@@ -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"),
+1 -2
View File
@@ -310,8 +310,7 @@ class Callback(object):
for key in keymap:
self.__callback_map[signal_name].remove(key)
self.__callback_map[signal_name] = None
self.__callback_map = None
del self.__callback_map
self.__callback_map = {}
def emit(self, signal_name, args=tuple()):
"""
+3 -1
View File
@@ -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):
+19 -1
View File
@@ -62,7 +62,7 @@ def find_file( filename):
try:
if os.path.isfile(filename):
return(filename)
except UnicodeError:
except UnicodeError as err:
LOG.error("Filename %s raised a Unicode Error %s.", repr(filename), err)
LOG.debug("Filename %s not found.", repr(filename))
@@ -185,6 +185,24 @@ def search_for(name):
return 1
return 0
def where_is(name):
""" This command is similar to the Linux "whereis -b file" command.
It looks for an executable file (name) in the PATH python is using, as
well as several likely other paths. It returns the first file found,
or an empty string if not found.
"""
paths = set(os.environ['PATH'].split(os.pathsep))
if not win():
paths.update(("/bin", "/usr/bin", "/usr/local/bin", "/opt/local/bin",
"/opt/bin"))
for i in paths:
fname = os.path.join(i, name)
if os.access(fname, os.X_OK) and not os.path.isdir(fname):
return fname
return ""
def create_checksum(full_path):
"""
Create a md5 hash for the given file.
+83 -45
View File
@@ -38,6 +38,8 @@ from gi.repository import GObject
from gi.repository import Gdk
from gi.repository import Gtk
from gi.repository import GdkPixbuf
from gi.repository import Pango
import cairo
#-------------------------------------------------------------------------
#
@@ -117,6 +119,7 @@ def map2class(target):
'place-link': ClipPlace,
'placeref': ClipPlaceRef,
'note-link': ClipNote,
'TEXT': ClipText,
}
return d[target] if target in d else None
@@ -1018,6 +1021,7 @@ class ClipboardListView(object):
self._widget.connect('drag-data-get', self.object_drag_data_get)
self._widget.connect('drag-begin', self.object_drag_begin)
self._widget.connect_after('drag-begin', self.object_after_drag_begin)
self._widget.connect('drag-data-received',
self.object_drag_data_received)
self._widget.connect('drag-end', self.object_drag_end)
@@ -1202,7 +1206,35 @@ class ClipboardListView(object):
def object_drag_begin(self, widget, drag_context):
""" Handle the beginning of a drag operation. """
pass
def object_after_drag_begin(self, widget, drag_context):
tree_selection = widget.get_selection()
model, paths = tree_selection.get_selected_rows()
if len(paths) == 1:
path = paths[0]
node = model.get_iter(path)
target = model.get_value(node, 0)
if target == "TEXT":
layout = widget.create_pango_layout(model.get_value(node,4))
layout.set_alignment(Pango.Alignment.CENTER)
width, height = layout.get_pixel_size()
surface = cairo.ImageSurface(cairo.FORMAT_RGB24,
width, height)
ctx = cairo.Context(surface)
style_ctx = self._widget.get_style_context()
Gtk.render_background(style_ctx, ctx, 0, 0, width, height)
ctx.save()
Gtk.render_layout(style_ctx, ctx, 0, 0 , layout)
ctx.restore()
Gtk.drag_set_icon_surface(drag_context, surface)
else:
try:
if map2class(target):
Gtk.drag_set_icon_pixbuf(drag_context,
map2class(target).ICON, 0, 0)
except:
Gtk.drag_set_icon_default(drag_context)
def object_drag_end(self, widget, drag_context):
""" Handle the end of a drag operation. """
pass
@@ -1218,7 +1250,10 @@ class ClipboardListView(object):
path = paths[0]
node = model.get_iter(path)
o = model.get_value(node,1)
sel_data.set(tgs[0], 8, o.pack())
if model.get_value(node, 0) == 'TEXT':
sel_data.set_text(o._value, -1)
else:
sel_data.set(tgs[0], 8, o.pack())
elif len(paths) > 1:
raw_list = []
for path in paths:
@@ -1279,54 +1314,57 @@ class ClipboardListView(object):
# Just select the first match.
wrapper_class = self._target_type_to_wrapper_class_map[
str(possible_wrappers[0])]
o = wrapper_class(self.dbstate, sel_data)
if title:
o._title = title
if value:
o._value = value
if dbid:
o._dbid = dbid
if dbname:
o._dbname = dbname
try:
o = wrapper_class(self.dbstate, sel_data)
if title:
o._title = title
if value:
o._value = value
if dbid:
o._dbid = dbid
if dbname:
o._dbname = dbname
# If the wrapper object is a subclass of ClipDropList then
# the drag data was a list of objects and we need to decode
# all of them.
if isinstance(o,ClipDropList):
o_list = o.get_objects()
else:
o_list = [o]
for o in o_list:
if o.__class__.DRAG_TARGET is None:
continue
data = [o.__class__.DRAG_TARGET.drag_type, o, None,
o._type, o._value, o._dbid, o._dbname]
contains = model_contains(model, data)
if ((context.action if hasattr(context, "action") else context.get_actions())
!= Gdk.DragAction.MOVE) and contains:
continue
drop_info = widget.get_dest_row_at_pos(x, y)
if drop_info:
path, position = drop_info
node = model.get_iter(path)
if (position == Gtk.TreeViewDropPosition.BEFORE
or position == Gtk.TreeViewDropPosition.INTO_OR_BEFORE):
model.insert_before(node, data)
else:
model.insert_after(node, data)
# If the wrapper object is a subclass of ClipDropList then
# the drag data was a list of objects and we need to decode
# all of them.
if isinstance(o,ClipDropList):
o_list = o.get_objects()
else:
model.append(data)
o_list = [o]
for o in o_list:
if o.__class__.DRAG_TARGET is None:
continue
data = [o.__class__.DRAG_TARGET.drag_type, o, None,
o._type, o._value, o._dbid, o._dbname]
contains = model_contains(model, data)
if ((context.action if hasattr(context, "action") else context.get_actions())
!= Gdk.DragAction.MOVE) and contains:
continue
drop_info = widget.get_dest_row_at_pos(x, y)
if drop_info:
path, position = drop_info
node = model.get_iter(path)
if (position == Gtk.TreeViewDropPosition.BEFORE
or position == Gtk.TreeViewDropPosition.INTO_OR_BEFORE):
model.insert_before(node, data)
else:
model.insert_after(node, data)
else:
model.append(data)
# FIXME: there is one bug here: if you multi-select and drop
# on self, then it moves the first, and copies the rest.
# FIXME: there is one bug here: if you multi-select and drop
# on self, then it moves the first, and copies the rest.
if ((context.action if hasattr(context, "action") else context.get_actions()) ==
Gdk.DragAction.MOVE):
context.finish(True, True, time)
if ((context.action if hasattr(context, "action") else context.get_actions()) ==
Gdk.DragAction.MOVE):
context.finish(True, True, time)
# remember time for double drop workaround.
self._previous_drop_time = realTime
return o_list
# remember time for double drop workaround.
self._previous_drop_time = realTime
return o_list
except EOFError:
return None
# proxy methods to provide access to the real widget functions.
+5 -2
View File
@@ -24,6 +24,7 @@
#
#-------------------------------------------------------------------------
import sys
import html
import logging
_LOG = logging.getLogger(".dialog")
@@ -92,7 +93,8 @@ class QuestionDialog(object):
self.top.set_title("%s - Gramps" % msg1)
label1 = self.xml.get_object('qd_label1')
label1.set_text('<span weight="bold" size="larger">%s</span>' % msg1)
label1.set_text('<span weight="bold" size="larger">%s</span>' %
html.escape(msg1))
label1.set_use_markup(True)
label2 = self.xml.get_object('qd_label2')
@@ -124,7 +126,8 @@ class QuestionDialog2(object):
self.top.set_title("%s - Gramps" % msg1)
label1 = self.xml.get_object('qd_label1')
label1.set_text('<span weight="bold" size="larger">%s</span>' % msg1)
label1.set_text('<span weight="bold" size="larger">%s</span>' %
html.escape(msg1))
label1.set_use_markup(True)
label2 = self.xml.get_object('qd_label2')
@@ -67,21 +67,29 @@ class FamilySidebarFilter(SidebarFilter):
self.filter_event = Event()
self.filter_event.set_type((EventType.CUSTOM, ''))
self.etype = Gtk.ComboBox(has_entry=True)
try: # should use if dbstate.is_open(), but not in gramps42
self.custom_types = dbstate.db.get_event_types()
except:
self.custom_types = []
self.family_stub = Family()
self.family_stub.set_relationship((FamilyRelType.CUSTOM, ''))
self.rtype = Gtk.ComboBox(has_entry=True)
self.custom_types = dbstate.db.get_family_relation_types()
self.event_menu = widgets.MonitoredDataType(
self.etype,
self.filter_event.set_type,
self.filter_event.get_type)
self.filter_event.get_type,
custom_values=self.custom_types)
self.filter_family = Family()
self.filter_family.set_relationship((FamilyRelType.CUSTOM, ''))
self.rtype = Gtk.ComboBox(has_entry=True)
try: # should use if dbstate.is_open(), but not in gramps42
self.custom_types = dbstate.db.get_family_relation_types()
except:
self.custom_types = []
self.rel_menu = widgets.MonitoredDataType(
self.rtype,
self.family_stub.set_relationship,
self.family_stub.get_relationship,
self.filter_family.set_relationship,
self.filter_family.get_relationship,
custom_values=self.custom_types)
self.filter_note = widgets.BasicEntry()
@@ -139,7 +147,7 @@ class FamilySidebarFilter(SidebarFilter):
child = str(self.filter_child.get_text()).strip()
note = str(self.filter_note.get_text()).strip()
etype = self.filter_event.get_type().xml_str()
rtype = self.family_stub.get_relationship().xml_str()
rtype = self.filter_family.get_relationship().xml_str()
regex = self.filter_regex.get_active()
tag = self.tag.get_active() > 0
generic = self.generic.get_active() > 0
@@ -78,10 +78,15 @@ class PersonSidebarFilter(SidebarFilter):
self.filter_event = Event()
self.filter_event.set_type((EventType.CUSTOM, ''))
self.etype = Gtk.ComboBox(has_entry=True)
try: # Should use if dbstate.isopen() but not in gramps42
self.custom_types = dbstate.db.get_event_types()
except:
self.custom_types = []
self.event_menu = widgets.MonitoredDataType(
self.etype,
self.filter_event.set_type,
self.filter_event.get_type)
self.etype,
self.filter_event.set_type,
self.filter_event.get_type,
custom_values=self.custom_types)
self.filter_note = widgets.BasicEntry()
self.filter_gender = Gtk.ComboBoxText()
+1 -1
View File
@@ -198,7 +198,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)
+3 -3
View File
@@ -723,7 +723,7 @@ class BookSelector(ManagedWindow):
"""
if self.book.item_list:
BookDialog(self.dbstate, self.uistate,
self.book, BookOptions)
self.book, BookOptions, track=self.track)
else:
WarningDialog(_('No items'), _('This book has no items.'),
parent=self.window)
@@ -916,14 +916,14 @@ 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.is_from_saved_book = False
self.page_html_added = False
self.book = book
DocReportDialog.__init__(self, dbstate, uistate, options,
'book', _("Book"))
'book', _("Book"), track=track)
self.options.options_dict['bookname'] = self.book.name
self.database = dbstate.db
+4 -3
View File
@@ -58,16 +58,17 @@ 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."""
self.style_name = "default"
self.firstpage_added = False
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)
# Allow for post processing of the format frame, since the
# show_all task calls events that may reset values
@@ -0,0 +1,288 @@
#
# 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
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
#-------------------------------------------------------------------------------
#
# GTK+ modules
#
#-------------------------------------------------------------------------------
from gi.repository import Gtk
from gi.repository import GObject
#-------------------------------------------------------------------------------
#
# GRAMPS modules
#
#-------------------------------------------------------------------------------
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
#-------------------------------------------------------------------------------
#
# BaseFormatComboBox
#
#-------------------------------------------------------------------------------
class BaseFormatComboBox(Gtk.ComboBox):
"""
Combo box base class for graph-based report format choices.
"""
FORMATS=[]
def set(self, active=None):
self.store = Gtk.ListStore(GObject.TYPE_STRING)
self.set_model(self.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"]
self.store.append(row=[name])
if item['type'] == active:
active_index = index
index += 1
self.set_active(active_index)
def get_label(self):
return self.FORMATS[self.get_active()]["descr"]
def get_reference(self):
return self.FORMATS[self.get_active()]["class"]
def get_paper(self):
return 1
def get_styles(self):
return 0
def get_ext(self):
return '.%s' % self.FORMATS[self.get_active()]['ext']
def get_oformat_str(self): # the report's output-format type
return self.FORMATS[self.get_active()]["type"]
def is_file_output(self):
return True
def get_clname(self):
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)
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(_("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.
"""
+38 -225
View File
@@ -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.
#
#------------------------------------------------------------------------
#
# python modules
#
#------------------------------------------------------------------------
import os
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
"""class for generating dialogs for graphviz-based reports """
#-------------------------------------------------------------------------------
#
# GTK+ modules
# Gramps modules
#
#-------------------------------------------------------------------------------
from gi.repository import Gtk
from gi.repository import GObject
#-------------------------------------------------------------------------------
#
# GRAMPS modules
#
#-------------------------------------------------------------------------------
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):
"""
Format combo box class for Graphviz report.
"""
def set(self, active=None):
self.store = Gtk.ListStore(GObject.TYPE_STRING)
self.set_model(self.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"]
self.store.append(row=[name])
if item['type'] == active:
active_index = index
index += 1
self.set_active(active_index)
def get_label(self):
return graphdoc.FORMATS[self.get_active()]["descr"]
def get_reference(self):
return graphdoc.FORMATS[self.get_active()]["class"]
def get_paper(self):
return 1
def get_styles(self):
return 0
def get_ext(self):
return '.%s' % graphdoc.FORMATS[self.get_active()]['ext']
def get_oformat_str(self): # the report's output-format type
return graphdoc.FORMATS[self.get_active()]["type"]
def is_file_output(self):
return True
def get_clname(self):
return graphdoc.FORMATS[self.get_active()]["type"]
#-----------------------------------------------------------------------
#
# GraphvizReportDialog
#
#-----------------------------------------------------------------------
class GraphvizReportDialog(ReportDialog):
"""A class of ReportDialog customized for graphviz 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 = CATEGORY_GRAPHVIZ
self.__gvoptions = graphdoc.GVOptions()
self.dbname = dbstate.db.get_dbname()
ReportDialog.__init__(self, dbstate, uistate, opt,
name, translated_name)
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()
class GraphvizReportDialog(GraphReportDialog):
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(_("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,81 +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)
GraphReportDialog.doc_type_changed(self, obj)
ext_val = obj.get_ext()
if ext_val:
fname = spath + ext_val
else:
fname = spath
self.target_fileentry.set_filename(fname)
output_format_str = obj.get_oformat_str()
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
+6 -2
View File
@@ -54,8 +54,9 @@ from .. import add_gui_options, make_gui_option
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_CODE, CATEGORY_WEB,
CATEGORY_GRAPHVIZ, CATEGORY_TREE,
standalone_categories)
from gramps.gen.plug.docgen import StyleSheet, StyleSheetList
from ...managedwindow import ManagedWindow
from ._stylecombobox import StyleComboBox
@@ -673,6 +674,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
+4 -1
View File
@@ -121,10 +121,11 @@ class UndoHistory(ManagedWindow):
self.window.vbox.pack_start(scrolled_window, True, True, 0)
self.window.show_all()
self.sel_chng_hndlr = self.selection.connect('changed',
self._selection_changed)
self._build_model()
self._update_ui()
self.selection.connect('changed', self._selection_changed)
self.show()
def _selection_changed(self, obj):
@@ -227,6 +228,7 @@ class UndoHistory(ManagedWindow):
)
def _build_model(self):
self.selection.handler_block(self.sel_chng_hndlr)
self.model.clear()
fg = bg = None
@@ -244,6 +246,7 @@ class UndoHistory(ManagedWindow):
mod_text = txn.get_description()
self.model.append(row=[time_text, mod_text, fg, bg])
path = (self.undodb.undo_count,)
self.selection.handler_unblock(self.sel_chng_hndlr)
self.selection.select_path(path)
def update(self):
+15
View File
@@ -538,6 +538,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.
+4 -6
View File
@@ -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):
"""
@@ -138,7 +138,7 @@ class CitationBaseModel(object):
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)
@@ -297,7 +297,7 @@ class CitationBaseModel(object):
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)
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -187,7 +187,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)
+1 -1
View File
@@ -150,7 +150,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)
+1 -1
View File
@@ -545,7 +545,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)
+1 -1
View File
@@ -200,7 +200,7 @@ class PlaceBaseModel(object):
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)
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -897,7 +897,7 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel):
# Header rows dont get the foreground color set
if col == self.color_column():
#color must not be utf-8
return "#000000000000"
return ""
# Return the node name for the first column
if col == 0:
+1
View File
@@ -31,6 +31,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
+1 -1
View File
@@ -615,7 +615,7 @@ class FanChartDescWidget(FanChartBaseWidget):
elif nrparent <= 4:
angleinc = math.pi/2
else:
angleinc = 2 * math.pi / nrchild
angleinc = 2 * math.pi / nrparent
for data in self.parentsroot:
self.draw_innerring(cr, data[0], data[1], startangle, angleinc)
startangle += angleinc
+2 -7
View File
@@ -50,7 +50,7 @@ from gramps.gen.errors import WindowActiveError
from gramps.gen.const import URL_MANUAL_PAGE, VERSION_DIR
from ..editors import EditPerson, EditFamily
from ..managedwindow import ManagedWindow
from ..utils import is_right_click, rgb_to_hex
from ..utils import is_right_click, get_link_color
from .menuitem import add_menuitem
from ..plug.quick import run_quick_report_by_name
from ..display import display_help, display_url
@@ -197,12 +197,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
+1 -1
View File
@@ -399,7 +399,7 @@ class InteractiveSearchBox():
self._search_window.hide()
self._search_entry.set_text("")
self._treeview.emit('focus-in-event', event)
self.__selected_search_result = None
self.__selected_search_result = 0
def _position_func(self, userdata=None):
tree_window = self._treeview.get_window()
+2 -6
View File
@@ -49,7 +49,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
#-------------------------------------------------------------------------
#
@@ -85,11 +85,7 @@ class LinkLabel(Gtk.EventBox):
GObject.GObject.__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
+58
View File
@@ -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)
+2 -6
View File
@@ -60,7 +60,7 @@ from .toolcomboentry import ToolComboEntry
from .springseparator import SpringSeparatorAction
from ..spell import Spell
from ..display import display_url
from ..utils import SystemFonts, rgb_to_hex
from ..utils import SystemFonts, get_link_color
from gramps.gen.config import config
from gramps.gen.constfunc import has_display
from ..actiongroup import ActionGroup
@@ -184,11 +184,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
+6 -4
View File
@@ -464,8 +464,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)
@@ -682,8 +683,9 @@ 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:
father2_h = family2.get_father_handle()
+17 -7
View File
@@ -48,6 +48,9 @@ log = logging.getLogger(".WriteFtree")
from gramps.gen.utils.alive import probably_alive
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
#-------------------------------------------------------------------------
#
@@ -121,8 +124,16 @@ class FtreeWriter(object):
id_map[key] = n
id_name[key] = get_name(pn, sn, count)
f = open(self.filename,"w")
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
# cannot set parent below because gramps42 doesn't have it...
ErrorDialog(msg2, str(msg))
return False
def _export_data(self, file, id_name, id_map):
for key in self.plist:
self.update()
p = self.db.get_person_from_handle(key)
@@ -159,7 +170,7 @@ class FtreeWriter(object):
# alive = probably_alive(p, self.db)
#else:
# alive = 0
if birth:
if death:
dates = "%s-%s" % (fdate(birth), fdate(death))
@@ -170,11 +181,10 @@ class FtreeWriter(object):
dates = fdate(death)
else:
dates = ""
f.write('%s;%s;%s;%s;%s;%s\n' % (name, father, mother, email, web,
dates))
f.close()
file.write('%s;%s;%s;%s;%s;%s\n' %
(name, father, mother, email, web, dates))
return True
def fdate(val):
+1
View File
@@ -66,6 +66,7 @@ class MediaPreview(Gramplet):
self.set_has_data(False)
self.top.show()
else:
self.photo.set_image(None)
self.set_has_data(False)
def load_image(self, media):
+3
View File
@@ -919,6 +919,9 @@ class CSVParser:
def get_or_create_place(self, place_name):
"Return the requested place object tuple-packed with a new indicator."
if place_name.startswith("[") and place_name.endswith("]"):
place = self.lookup("place", place_name)
return (0, place)
LOG.debug("get_or_create_place: looking for: %s", place_name)
for place_handle in self.db.iter_place_handles():
place = self.db.get_place_from_handle(place_handle)
+58 -9
View File
@@ -134,6 +134,7 @@ from gramps.gui.dialog import WarningDialog
from gramps.gen.lib.const import IDENTICAL, DIFFERENT
from gramps.gen.lib import (StyledText, StyledTextTag, StyledTextTagType)
from gramps.gen.constfunc import conv_to_unicode, win
from gramps.gen.lib.urlbase import UrlBase
from gramps.plugins.lib.libplaceimport import PlaceImport
from gramps.gen.display.place import displayer as _pd
@@ -273,6 +274,8 @@ TOKEN__PHOTO = 132
TOKEN__LINK = 133
TOKEN__PRIM = 134
TOKEN__JUST = 135
TOKEN__TEXT = 136
TOKEN__DATE = 137
TOKENS = {
"HEAD" : TOKEN_HEAD, "MEDI" : TOKEN_MEDI,
@@ -364,7 +367,7 @@ TOKENS = {
"_WITN" : TOKEN__WITN, "_WTN" : TOKEN__WTN,
"_CHUR" : TOKEN_IGNORE,"RELA" : TOKEN_RELA,
"_DETAIL" : TOKEN_IGNORE,"_PREF" : TOKEN__PRIMARY,
"_LKD" : TOKEN__LKD, "_DATE" : TOKEN_IGNORE,
"_LKD" : TOKEN__LKD, "_DATE" : TOKEN__DATE,
"_SCBK" : TOKEN_IGNORE,"_TYPE" : TOKEN_TYPE,
"_PRIM" : TOKEN__PRIM, "_SSHOW" : TOKEN_IGNORE,
"_PAREN" : TOKEN_IGNORE,"BLOB" : TOKEN_BLOB,
@@ -382,7 +385,7 @@ TOKENS = {
"_ADPN" : TOKEN__ADPN, "_FSFTID" : TOKEN__FSFTID,
"_LINK" : TOKEN__LINK, "_PHOTO" : TOKEN__PHOTO,
"_JUST" : TOKEN__JUST, # FTM Citation Quality Justification
"FAX" : TOKEN_FAX,
"FAX" : TOKEN_FAX, "_TEXT" : TOKEN__TEXT,
}
ADOPT_NONE = 0
@@ -636,8 +639,8 @@ for __val, __key in PERSONALCONSTANTATTRIBUTES.items():
#
#-------------------------------------------------------------------------
HMONTH = [
"", "ELUL", "TSH", "CSH", "KSL", "TVT", "SHV", "ADR",
"ADS", "NSN", "IYR", "SVN", "TMZ", "AAV", "ELL" ]
"", "TSH", "CSH", "KSL", "TVT", "SHV", "ADR",
"ADS", "NSN", "IYR", "SVN", "TMZ", "AAV", "ELL"]
FMONTH = [
"", "VEND", "BRUM", "FRIM", "NIVO", "PLUV", "VENT",
@@ -1058,6 +1061,7 @@ class GedLine:
_MAP_DATA = {
TOKEN_UNKNOWN : GedLine.calc_unknown,
TOKEN_DATE : GedLine.calc_date,
TOKEN__DATE : GedLine.calc_date,
TOKEN_SEX : GedLine.calc_sex,
TOKEN_NOTE : GedLine.calc_note,
TOKEN_NCHI : GedLine.calc_nchi,
@@ -2549,7 +2553,9 @@ class GedcomParser(UpdateCallback):
TOKEN_TITL : self.__obje_title,
TOKEN_FILE : self.__obje_file, # de-facto extension
TOKEN_TEXT : self.__obje_text, # FTM extension
TOKEN__TEXT : self.__obje_text, # FTM 2017 extension
TOKEN_DATE : self.__obje_date, # FTM extension
TOKEN__DATE : self.__obje_date, # FTM 2017 extension
TOKEN_NOTE : self.__obje_note,
TOKEN_RNOTE : self.__obje_note,
TOKEN_SOUR : self.__obje_sour,
@@ -4121,6 +4127,9 @@ class GedcomParser(UpdateCallback):
@param state: The current state
@type state: CurrentState
"""
if self.is_ftw:
self.__person_resi(line, state)
return
free_form = line.data
sub_state = CurrentState(level=state.level + 1)
@@ -4132,6 +4141,26 @@ class GedcomParser(UpdateCallback):
self.__merge_address(free_form, sub_state.addr, line, state)
state.person.add_address(sub_state.addr)
def __person_resi(self, line, state):
"""
Parses GEDCOM ADDR tag, subordinate to the INDI tag, when sourced by
FTM. We treat this as a RESI event, because FTM puts standard event
details below the ADDR line.
n ADDR <ADDRESS_LINE> {0:1}
+1 <<EVENT_DETAIL>> {0:1} p.*
@param line: The current line in GedLine format
@type line: GedLine
@param state: The current state
@type state: CurrentState
"""
self.backoff = True # reprocess the current ADDR line
line.level += 1 # as if it was next level down
event_ref = self.__build_event_pair(state, EventType.RESIDENCE,
self.event_parse_tbl, '')
state.person.add_event_ref(event_ref)
def __person_phon(self, line, state):
"""
n PHON <PHONE_NUMBER> {0:3}
@@ -5055,6 +5084,8 @@ class GedcomParser(UpdateCallback):
event.set_description('')
else:
state.family.type.set(FamilyRelType.MARRIED)
if descr == "Y":
event.set_description('')
self.dbase.commit_event(event, self.trans)
event_ref.ref = event.handle
@@ -5259,12 +5290,20 @@ class GedcomParser(UpdateCallback):
def __family_attr(self, line, state):
"""
Parses an TOKEN that Gramps recognizes as an Attribute
@param line: The current line in GedLine format
@type line: GedLine
@param state: The current state
@type state: CurrentState
"""
sub_state = CurrentState()
sub_state.person = state.person
sub_state.attr = line.data
sub_state.level = state.level + 1
state.family.add_attribute(line.data)
self.__parse_level(sub_state, self.person_attr_parse_tbl,
self.__ignore)
state.msg += sub_state.msg
def __family_cust_attr(self, line, state):
"""
@@ -5343,11 +5382,21 @@ class GedcomParser(UpdateCallback):
# The following code that detects URL is an older v5.5 usage; the
# modern option is to use the EMAIL tag.
if isinstance(sub_state.form, str) and sub_state.form == "url":
url = Url()
url.set_path(sub_state.filename)
url.set_description(sub_state.title)
url.set_type(UrlType.WEB_HOME)
pri_obj.add_url(url)
if isinstance(pri_obj, UrlBase):
url = Url()
url.set_path(sub_state.filename)
url.set_description(sub_state.title)
url.set_type(UrlType.WEB_HOME)
pri_obj.add_url(url)
else: # some primary objects (Event) son't have spot for URL
new_note = Note(sub_state.filename)
new_note.set_gramps_id(self.nid_map[""])
new_note.set_handle(create_id())
new_note.set_type(OBJ_NOTETYPE.get(type(pri_obj).__name__,
NoteType.GENERAL))
self.dbase.commit_note(new_note, self.trans, new_note.change)
pri_obj.add_note(new_note.get_handle())
else:
# to allow import of references to URLs (especially for import from
# geni.com), do not try to find the file if it is blatently a URL
+1 -1
View File
@@ -669,7 +669,7 @@ class TitleBox(BoxBase):
return
#fix me. width should be the printable area
self.width = PT2CM(self.doc.string_width(self.font, self.text))
self.height = PT2CM(self.font.get_size() * 1.2)
self.height = PT2CM(self.font.get_size() * 2)
def _get_names(self, persons, name_displayer):
""" A helper function that receives a list of persons and
+10 -5
View File
@@ -313,13 +313,18 @@ class GeoClose(GeoGraphyView):
all handling of visibility is now in rebuild_trees, see that for more
information.
"""
active = self.get_active()
person = self.dbstate.db.get_person_from_handle(active)
self.lifeway_layer.clear_ways()
if person is None:
self.goto_handle(None)
if not self.dbstate.open:
return
active = self.get_active()
if active:
person = self.dbstate.db.get_person_from_handle(active)
if person is None:
self.goto_handle(None)
else:
self.goto_handle(handle=person)
else:
self.goto_handle(handle=person)
self.goto_handle(None)
def draw(self, menu, marks, color, reference):
"""
+2
View File
@@ -188,6 +188,8 @@ class GeoEvents(GeoGraphyView):
all handling of visibility is now in rebuild_trees, see that for more
information.
"""
if not self.dbstate.open:
return
active = self.uistate.get_active('Event')
if active:
self._createmap(active)
+3
View File
@@ -304,6 +304,9 @@ class GeoFamClose(GeoGraphyView):
all handling of visibility is now in rebuild_trees, see that for more
information.
"""
self.lifeway_layer.clear_ways()
if not self.dbstate.open:
return
active = self.get_active()
family = self.dbstate.db.get_family_from_handle(active)
self.goto_handle(handle=family)
+2
View File
@@ -178,6 +178,8 @@ class GeoFamily(GeoGraphyView):
all handling of visibility is now in rebuild_trees, see that for more
information.
"""
if not self.dbstate.open:
return
if self.uistate.get_active('Family'):
self._createmap(self.uistate.get_active('Family'))
else:
+2
View File
@@ -215,6 +215,8 @@ class GeoPerson(GeoGraphyView):
all handling of visibility is now in rebuild_trees, see that for more
information.
"""
if not self.dbstate.open:
return
active = self.get_active()
self._createmap(active)
+2
View File
@@ -189,6 +189,8 @@ class GeoPlaces(GeoGraphyView):
all handling of visibility is now in rebuild_trees, see that for more
information.
"""
if not self.dbstate.open:
return
active = self.uistate.get_active('Place')
if active:
self._createmap(active)
+8 -41
View File
@@ -306,15 +306,6 @@ class RelationshipView(NavigationView):
self.child = None
self.scroll = Gtk.ScrolledWindow()
st_cont = self.scroll.get_style_context()
col = st_cont.lookup_color('base_color')
if col[0]:
self.color = col[1]
else:
self.color = Gdk.RGBA()
self.color.parse("White")
self.scroll.set_policy(Gtk.PolicyType.AUTOMATIC,
Gtk.PolicyType.AUTOMATIC)
self.scroll.show()
@@ -575,9 +566,7 @@ class RelationshipView(NavigationView):
grid.attach(eventbox, 0, 0, 2, 1)
eventbox = Gtk.EventBox()
if self.use_shade:
eventbox.override_background_color(Gtk.StateType.NORMAL, self.color)
eventbox = widgets.ShadeBox(self.use_shade)
grid.attach(eventbox, 1, 1, 1, 1)
subgrid = Gtk.Grid()
subgrid.set_column_spacing(12)
@@ -874,9 +863,7 @@ class RelationshipView(NavigationView):
box = self.get_people_box(family.get_father_handle(),
family.get_mother_handle(),
post_msg=childmsg)
eventbox = Gtk.EventBox()
if self.use_shade:
eventbox.override_background_color(Gtk.StateType.NORMAL, self.color)
eventbox = widgets.ShadeBox(self.use_shade)
eventbox.add(box)
self.child.attach(eventbox, _PDATA_START, self.row,
_PDATA_STOP-_PDATA_START, 1)
@@ -928,9 +915,7 @@ class RelationshipView(NavigationView):
else :
childmsg = _(" (only child)")
box = self.get_people_box(post_msg=childmsg)
eventbox = Gtk.EventBox()
if self.use_shade:
eventbox.override_background_color(Gtk.StateType.NORMAL, self.color)
eventbox = widgets.ShadeBox(self.use_shade)
eventbox.add(box)
self.child.attach(eventbox, _PDATA_START, self.row,
_PDATA_STOP-_PDATA_START, 1)
@@ -958,9 +943,7 @@ class RelationshipView(NavigationView):
child_should_be_linked = (child_handle != active)
self.write_child(vbox, child_handle, i, child_should_be_linked)
i += 1
eventbox = Gtk.EventBox()
if self.use_shade:
eventbox.override_background_color(Gtk.StateType.NORMAL, self.color)
eventbox = widgets.ShadeBox(self.use_shade)
eventbox.add(vbox)
self.child.attach(eventbox, _CDATA_START-1, self.row,
_CDATA_STOP-_CDATA_START+1, 1)
@@ -980,9 +963,6 @@ class RelationshipView(NavigationView):
name = self.get_name(handle, True)
link_label = widgets.LinkLabel(name, self._button_press,
handle, theme=self.theme)
if self.use_shade:
link_label.override_background_color(Gtk.StateType.NORMAL,
self.color)
if self._config.get('preferences.releditbtn'):
button = widgets.IconButton(self.edit_button_press,
handle)
@@ -1025,7 +1005,7 @@ class RelationshipView(NavigationView):
_PLABEL_STOP-_PLABEL_START, 1)
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
eventbox = Gtk.EventBox()
eventbox = widgets.ShadeBox(self.use_shade)
if handle:
name = self.get_name(handle, True)
person = self.dbstate.db.get_person_from_handle(handle)
@@ -1039,8 +1019,6 @@ class RelationshipView(NavigationView):
emph = False
link_label = widgets.LinkLabel(name, self._button_press,
handle, emph, theme=self.theme)
if self.use_shade:
link_label.override_background_color(Gtk.StateType.NORMAL, self.color)
if self._config.get('preferences.releditbtn'):
button = widgets.IconButton(self.edit_button_press, handle)
button.set_tooltip_text(_('Edit %s') % name[0])
@@ -1059,8 +1037,6 @@ class RelationshipView(NavigationView):
if value:
vbox.pack_start(widgets.MarkupLabel(value), True, True, 0)
if self.use_shade:
eventbox.override_background_color(Gtk.StateType.NORMAL, self.color)
eventbox.add(vbox)
self.child.attach(eventbox, _PDATA_START, self.row,
@@ -1160,9 +1136,6 @@ class RelationshipView(NavigationView):
name = self.get_name(handle, True)
link_label = widgets.LinkLabel(name, link_func, handle, emph,
theme=self.theme)
if self.use_shade:
link_label.override_background_color(Gtk.StateType.NORMAL, self.color)
link_label.set_padding(3, 0)
if child_should_be_linked and self._config.get(
'preferences.releditbtn'):
@@ -1379,9 +1352,7 @@ class RelationshipView(NavigationView):
else :
childmsg = _(" (no children)")
box = self.get_people_box(handle, post_msg=childmsg)
eventbox = Gtk.EventBox()
if self.use_shade:
eventbox.override_background_color(Gtk.StateType.NORMAL, self.color)
eventbox = widgets.ShadeBox(self.use_shade)
eventbox.add(box)
self.child.attach(eventbox, _PDATA_START, self.row,
_PDATA_STOP-_PDATA_START, 1)
@@ -1426,9 +1397,7 @@ class RelationshipView(NavigationView):
else :
childmsg = _(" (no children)")
box = self.get_people_box(post_msg=childmsg)
eventbox = Gtk.EventBox()
if self.use_shade:
eventbox.override_background_color(Gtk.StateType.NORMAL, self.color)
eventbox = widgets.ShadeBox(self.use_shade)
eventbox.add(box)
self.child.attach(eventbox, _PDATA_START, self.row,
_PDATA_STOP-_PDATA_START, 1)
@@ -1456,9 +1425,7 @@ class RelationshipView(NavigationView):
i += 1
self.row += 1
eventbox = Gtk.EventBox()
if self.use_shade:
eventbox.override_background_color(Gtk.StateType.NORMAL, self.color)
eventbox = widgets.ShadeBox(self.use_shade)
eventbox.add(vbox)
self.child.attach(eventbox, _CDATA_START-1, self.row,
_CDATA_STOP-_CDATA_START+1, 1)
+1 -1
View File
@@ -1612,7 +1612,7 @@ class BasePage(object):
'<a href="' + URL_HOMEPAGE + '">' ,
'html_end' : '</a>' ,
'version' : VERSION }
if date is not None:
if date is not None and date > 0:
msg += "<br />"
last_modif = datetime.datetime.fromtimestamp(date).strftime('%Y-%m-%d %H:%M:%S')
msg += _('Last change was the %(date)s') % { 'date' : last_modif }
+1 -1
View File
@@ -18,6 +18,6 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
VERSION_TUPLE = (4, 2, 6)
VERSION_TUPLE = (4, 2, 9)
VERSION = '.'.join(map(str,VERSION_TUPLE))
major_version = "%s.%s" % (VERSION_TUPLE[0], VERSION_TUPLE[1])
+4 -4
View File
@@ -7,7 +7,7 @@
<key>CFBundleExecutable</key>
<string>Gramps</string>
<key>CFBundleGetInfoString</key>
<string>Gramps-4.2.5, (C) 1997-2016 The Gramps Team http://www.gramps-project.org</string>
<string>Gramps-4.2.8, (C) 1997-2018 The Gramps Team http://www.gramps-project.org</string>
<key>CFBundleIconFile</key>
<string>gramps.icns</string>
<key>CFBundleIdentifier</key>
@@ -17,13 +17,13 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>4.2.5</string>
<string>4.2.8</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>Gramps-4.2.5-1</string>
<string>Gramps-4.2.8-1</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright 1997 - 2016 The Gramps Team, GNU General Public License.</string>
<string>Copyright 1997 - 2017 The Gramps Team, GNU General Public License.</string>
<key>LSMinimumSystemVersion</key>
<string>10.5</string>
<key>GtkOSXLaunchScriptFile</key>
+12 -1
View File
@@ -13,7 +13,7 @@
<plist>${project}/Info.plist</plist>
<!-- Build gramps-launcher with:
gcc -L$PREFIX `python-config -\-cflags -\-ldflags` \
gcc -L$PREFIX/lib `python-config -\-cflags -\-ldflags` \
-o $PREFIX/bin/gramps-launcher \
path/to/gtk-mac-bundler/examples/python-launcher.c
with the obvious substitution.
@@ -231,4 +231,15 @@
${project}/settings.ini
</data>
<data>
${prefix}/share/doc/gramps/*
</data>
<data>
${prefix}/share/doc/gramps/example/gedcom/sample.ged
</data>
<data>
${prefix}/share/doc/gramps/example/gramps/*
</data>
</app-bundle>
+26 -30
View File
@@ -18,7 +18,7 @@
<repository type="tarball" name="oracle"
href="http://download.oracle.com/"/>
<repository type="tarball" name="pymodules"
href="http://pypi.python.org/packages/source/"/>
href="https://pypi.python.org/packages/"/>
<repository type="tarball" name="stowers"
href="http://www.johnstowers.co.nz/files/"/>
<repository type="git" name="github"
@@ -54,25 +54,23 @@ gtk-mac-bundler gtk-osx-build/projects/gramps/gramps.bundle
-->
<!--include href="http://git.gnome.org/browse/gtk-osx/plain/modulesets-stable/gtk-osx.modules"/-->
<include href="/Users/john/Development/GTK-OSX/gtk-osx-build/modulesets-stable/gtk-osx.modules"/>
<include href="http://git.gnome.org/browse/gtk-osx/plain/modulesets-stable/gtk-osx.modules"/>
<!--include href="/Users/john/Development/GTK-OSX/gtk-osx-build/modulesets-stable/gtk-osx.modules"/-->
<distutils id="gramps-git" supports-non-srcdir-builds="no">
<branch module="gramps-project/gramps.git" repo="github"
checkoutdir="gramps-git" tag="maintenance/gramps42"/>
<dependencies>
<dep package="meta-python2-modules"/>
<dep package="meta-python3-modules"/>
<dep package="meta-gramps-modules"/>
</dependencies>
</distutils>
<distutils id="gramps" supports-non-srcdir-builds="no">
<branch module="gramps-project/gramps/archive/v4.2.5.tar.gz"
repo="github-tarball" version="4.2.5"
checkoutdir="gramps-gramps-4.2.5"/>
<branch module="gramps-project/gramps/archive/v4.2.8.tar.gz"
repo="github-tarball" version="4.2.8"
checkoutdir="gramps-gramps-4.2.8"/>
<dependencies>
<dep package="meta-python2-modules"/>
<dep package="meta-python3-modules"/>
<dep package="meta-gramps-modules"/>
</dependencies>
</distutils>
@@ -114,9 +112,12 @@ gtk-mac-bundler gtk-osx-build/projects/gramps/gramps.bundle
supports-non-srcdir-builds="no">
<branch module="nzjrs/osm-gps-map" revision="1.0.2" repo="github"/>
<dependencies>
<dep package="meta-gtk-osx-gtk3"/>
<dep package="libsoup"/>
</dependencies>
<after>
<dep package="meta-gtk-osx-gtk3"/>
<dep package="meta-gtk-osx-gtk3.14"/>
</after>
</autotools>
<autotools id="graphviz" autogen-sh="configure"
@@ -162,18 +163,17 @@ gtk-mac-bundler gtk-osx-build/projects/gramps/gramps.bundle
</distutils>
<distutils id='pyicu'>
<branch version='1.8' repo='pymodules'
module='P/PyICU/PyICU-1.8.tar.gz'/>
<branch version='2.0.3' repo='pymodules'
module='bb/ef/3a7fcbba81bfd213e479131ae21445a2ddd14b46d70ef0109640b580bc5d/PyICU-2.0.3.tar.gz'/>
<dependencies>
<dep package='icu'/>
</dependencies>
</distutils>
<metamodule id="meta-python2-modules">
<metamodule id="meta-gramps-modules">
<dependencies>
<dep package="python"/>
<dep package="meta-gtk-osx-gtk3"/>
<dep package="gtk-mac-integration-python"/>
<dep package="python3"/>
<dep package="meta-gtk-osx-gtk3.14"/>
<dep package="meta-gtk-osx-gtk3-core-themes"/>
<dep package="goocanvas2"/>
<dep package="librsvg"/>
@@ -184,18 +184,14 @@ gtk-mac-bundler gtk-osx-build/projects/gramps/gramps.bundle
<dep package="graphviz"/>
<dep package="pil"/>
<dep package="gexiv2"/>
</dependencies>
</metamodule>
<metamodule id="meta-python3-modules">
<dependencies>
<dep package="python3"/>
<dep package="berkeleydb"/>
<dep package="pycairo"/>
<dep package="pygobject3"/>
<dep package='pyicu'/>
<dep package='pybsddb'/>
</dependencies>
</metamodule>
<dep package="berkeleydb"/>
<dep package="gtk-mac-integration-python"/>
<dep package="pycairo"/>
<dep package="pygobject3"/>
<dep package='pyicu'/>
<dep package='pybsddb'/>
<dep package="pil"/>
</dependencies>
</metamodule>
</moduleset>
-53
View File
@@ -1,53 +0,0 @@
2017-08-01 romjerome <romjerome@users.noreply.github.com>
* po/fr.po: typos
2017-07-31 Zdeněk Hataš <zdenek.hatas@gmail.com>
* po/cs.po: update to recent pot
2017-07-29 romjerome <romjerome@users.noreply.github.com>
* po/fr.po: Update template no modified or new message id, update references (header and
comments)
2017-07-26 vantu5z <vantu5z@mail.ru>
* po/ru.po: update Russian translation - merge last template - fix typo - change 'е' to 'ё' in some words
2017-07-15 Leonhaeuser <mirko@leonhaeuser.de>
* po/de.po: update German translation Fix #0010131
2017-07-08 Lajos Nemeséri <nemeseril@gmail.com>
* po/hu.po: Update Hungarian translation
2017-03-16 Paul Franklin <pf.98052@gmail.com>
* po/tr.po: add datestrings to gramps42 Turkish translation
2017-02-08 Bernard Banko <beernarrd@gmail.com>
* po/sl.po: updated sl translation
2017-02-08 Bernard Banko <beernarrd@gmail.com>
* po/sl.po: corrected slovenian name editor keywords
2017-02-02 Peter Landgren <peter.talken@telia.com>
* po/sv.po: Update Swedish translation
2017-01-25 Matti Niemelä <matti.u.niemela@gmail.com>
* po/fi.po: update finnish translation
2017-01-11 Luigi Toscano <luigi.toscano@tiscali.it>
* po/it.po: Italian translation: updates and fixes
2017-01-08 Luigi Toscano <luigi.toscano@tiscali.it>
* Refresh it.po using the current pot template
+1
View File
@@ -318,6 +318,7 @@ gramps/gen/plug/_pluginreg.py
gramps/gen/plug/docbackend/docbackend.py
gramps/gen/plug/docgen/graphdoc.py
gramps/gen/plug/docgen/paperstyle.py
gramps/gen/plug/docgen/treedoc.py
gramps/gen/plug/menu/_enumeratedlist.py
gramps/gen/plug/report/_book.py
gramps/gen/plug/report/_constants.py
+1
View File
@@ -366,6 +366,7 @@ gramps/gui/widgets/linkbox.py
gramps/gui/widgets/menuitem.py
gramps/gui/widgets/multitreeview.py
gramps/gui/widgets/placeentry.py
gramps/gui/widgets/shadebox.py
gramps/gui/widgets/shortlistcomboentry.py
gramps/gui/widgets/springseparator.py
gramps/gui/widgets/statusbar.py
+6527 -10513
View File
File diff suppressed because it is too large Load Diff
+863 -659
View File
File diff suppressed because it is too large Load Diff
+1319 -1118
View File
File diff suppressed because it is too large Load Diff
+3851 -3688
View File
File diff suppressed because it is too large Load Diff
+880 -715
View File
File diff suppressed because it is too large Load Diff