Compare commits
68 Commits
travis
...
v5.0.0-beta1
| Author | SHA1 | Date | |
|---|---|---|---|
| 6ca3821f81 | |||
| caf47940c7 | |||
| 6a78d5196d | |||
| fc0fdec25c | |||
| 2adcc7d5a3 | |||
| fcaea3e1bb | |||
| fc99f12568 | |||
| 1e5a6e1f87 | |||
| db263ff905 | |||
| e202f5cd19 | |||
| 2fbb9878c2 | |||
| ba4453213a | |||
| 5f133ffaf2 | |||
| cbac98894b | |||
| 3f368b2e02 | |||
| 1e73ab5be7 | |||
| f428069ac6 | |||
| adf3772b7a | |||
| 46cd191e49 | |||
| 9afe0cc181 | |||
| d3ed320636 | |||
| 6f4a114fdc | |||
| 5bec86c1f4 | |||
| 5b48691c3a | |||
| be11466b35 | |||
| 0445004e7d | |||
| f688d376f5 | |||
| 0a38a6352f | |||
| 2410435d8c | |||
| 6f1d6fa6b4 | |||
| b1db542cb9 | |||
| 3d7479969f | |||
| 8f4dffa1df | |||
| ffe9f31b0b | |||
| b1af1c5435 | |||
| 94b6fcb93f | |||
| 4b25dde6e2 | |||
| f83c077a7e | |||
| 323d775541 | |||
| 4448234281 | |||
| d31ced9ca5 | |||
| 9706af03b4 | |||
| b3ca17a90f | |||
| 4b13c95467 | |||
| 7864af3626 | |||
| 028f9582f5 | |||
| cd32559282 | |||
| 3980fca903 | |||
| 462b0ea20b | |||
| ba4b3eaef6 | |||
| bf826637ab | |||
| 316ff4454a | |||
| 3266625669 | |||
| 90378f19c5 | |||
| 78fb08aa12 | |||
| d081b75993 | |||
| 10e0e64ff4 | |||
| 3615e75d16 | |||
| 3c8cba0301 | |||
| dde9ea8a90 | |||
| b7c39e51c7 | |||
| a216441029 | |||
| 89592545f3 | |||
| 70fff6253d | |||
| 84e76de2b1 | |||
| fe289da983 | |||
| 371a8a8f89 | |||
| 1777cd4b29 |
@@ -1,3 +1,199 @@
|
||||
2018-02-17
|
||||
Version 5.0.0-beta1
|
||||
* pt_PT, hu, cs, it, fi, de, ru: update translation
|
||||
* update Readme for Gramps 5.0.0
|
||||
* new genealogy tree report
|
||||
* Replace backslash in image path for Windows
|
||||
* Escape special LaTeX characters
|
||||
* Copy output file from temporary directory
|
||||
* Fix character encoding for new tree category reports.
|
||||
* Add support for new genealogy tree report category.
|
||||
* Fix Place Tree view for proper display after filter cleared.
|
||||
* Implement place formats
|
||||
* Some dates are not translated in webcal
|
||||
* Some dates are not translated in webcal
|
||||
* Webcal link problem in the year glance page
|
||||
* Link problems depending on LANGUAGES.
|
||||
* Increment year to 2018 for About box.
|
||||
* data/holidays.xml.in: Fix holidays in Finnish
|
||||
* fix Sidebar to resize better
|
||||
* mac/gramps.modules: Update pyicu to v 2.0.3
|
||||
* mac/gramps.modules: Consolidate Python2 and Python3 meta-modules.
|
||||
GObject-Introspection now works with Python3 so the split isn't needed
|
||||
any more.
|
||||
* Add tooltip to links in styled text editor.
|
||||
* [MacOS] Replace GDK_CONTROL_MASK with GDK_META_MASK in accelerators.
|
||||
* Set the mnemonic modifier key to Alt-Ctrl for the Quartz Gdk backend.
|
||||
This avoids interference with the use of the Alt/Option key for extending
|
||||
the keyboard.
|
||||
* Fix Family Lines Graph when 'unknown' places are present
|
||||
* Fix Export View to CSV when Unicode characters are present
|
||||
* Fix several intl date displayers for missing parameter.
|
||||
* mac/gramps.accel: Update gramps.accel for gramps50
|
||||
* Some filter optimizations to avoid repeated parts of tree scan
|
||||
* Fix view buttons order keeps changing on different startups
|
||||
* Fix DescendentTree report; more space beneath Title
|
||||
* Fix DescendentTree report for crashes
|
||||
* Fix Book XML handler for unusual characters in report name
|
||||
* Fix 'Generate Book' dialog for bad transient parent
|
||||
* Fix Book XML handler to deal with unusual characters in Book name
|
||||
* Add color schemes to config
|
||||
* Surname Editor; fix so changes made update Person 'Preferred Name'
|
||||
* Surname Editor, fix fields for better fit in minimum width dialogs
|
||||
* Surname Editor; fix loss of data if using mouse to change fields
|
||||
* Add Garbage collection after tools/reports
|
||||
* Main Window menu 'Add' keyboard Acellerators
|
||||
* Fix Add menu Accelerators so they don't activate on Shift-'x'
|
||||
* Change Gramps 'Add' menu so 'New' is not used
|
||||
* Export Web Family Tree; error on file write is now a message, not exception.
|
||||
* Fix Export Web Family Tree for errors on file write
|
||||
* Pylint on Web Family Tree export
|
||||
* Fix Citation Editor to Tab out of Confidence ComboBox
|
||||
* Narrated Web Site:
|
||||
* Translation problem in narrative web.
|
||||
* crash in narrative web if no unused media
|
||||
* Apply places changes to the narrativeweb.
|
||||
Also change order between locations and locations alternate names.
|
||||
* Suppress extra line in Alternate Names section.
|
||||
* Added an option for the statistics page.
|
||||
* We have problems with notes: pages are too large
|
||||
* Difficult to see some page on a mobile phone when you have notes.
|
||||
* Resizing of images on a mobile
|
||||
* Maps are too bigs on a mobile
|
||||
* Split the event line to have a narrower page
|
||||
* The note is spanned on two columns (Place + Description)
|
||||
* In media pages, the progress meter doesn't work well.
|
||||
* Add a progress meter for the medialistpage
|
||||
* warnings reported to console
|
||||
* Mainz css file produces left aligned FamilyMap pages
|
||||
Solving Mainz problem creates new ones with Basic-Blue and Nebraska
|
||||
styles sheet.
|
||||
Some cleanup with unused css file (GeoView.css)
|
||||
* Many problems with css files and the ancestor tree
|
||||
* media file not created if we have unused media.
|
||||
* Fix Narrative Web works poorly on Android browser
|
||||
* Clear old custom name formats when database loaded
|
||||
* 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 Fan Chart for exception on right-click of person with no parent
|
||||
* Fix Fan Chart Print centering and scaling on Windows
|
||||
* Fix Media Preview Gramplet for closed db
|
||||
* Added a requirement for pango and pangocairo versions
|
||||
Eliminates a warning that was present when calling a report from the
|
||||
command line.
|
||||
* Fix Styled Text Editor for exception on non-editable text click
|
||||
Also prevent editing of links on non-editable text.
|
||||
* Suport FTM 2017 Gedcom tags on import
|
||||
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.
|
||||
* Fix Family tree Manager: rename a locked db, don't open, but title bar
|
||||
changes.
|
||||
* Fix Person, Family Sidebar Filters to add custom Event types
|
||||
Also fix Family Sidebar Filter RelType init with closed db
|
||||
* Fix ProxyCache to deal with memory leak
|
||||
* Fix duplicated method in ProxyCache
|
||||
Also changed size request to better utilize actual memory
|
||||
* Dbapi: Fix for broken 'backlinks' after a delete.
|
||||
* Dbapi: Use UPDATE instead of DELETE and INSERT
|
||||
* Remove write_version method from the database API
|
||||
This is specific to the BSDDB backend.
|
||||
* Fixes for in-memory databases
|
||||
In-memory databases always allow write access and don't need to use lock
|
||||
files.
|
||||
* Enable Sqlite backend for all users
|
||||
* Move PostgreSQL backend into third-party addons
|
||||
* Implement locking for dbapi backends
|
||||
* New convenience function to return database methods
|
||||
Examples: db.method('get_%s_from_handle', 'Person')
|
||||
db.method('get_%s_from_%s', 'Event', 'gramps_id')
|
||||
Returns None if the method doesn't exist.
|
||||
Formats 'iter_%s' and 'get_number_of_%s' use the plural forms and are
|
||||
not yet implemented. Replaces old get_table_metadata method.
|
||||
* postgresql: Add login dialog and username/password command line options
|
||||
* Write dbapi schema version to metadata table instead of a file
|
||||
* Remove hardcoded references to BSDDB database backend
|
||||
The default backend in the preferences is now used for:
|
||||
* Archive checkout
|
||||
* Import from the command line - python Gramps.py file
|
||||
* Import by drag and drop onto the family tree manager
|
||||
* db: Remove set_save_path method
|
||||
The path is already set in the database load method.
|
||||
* Add missing methods to database unit test
|
||||
* Add missing base database method.
|
||||
* db: Remove get_schema_version method.
|
||||
* db: Remove has_gramps_id method from the public API.
|
||||
* db: Remove get_gramps_ids method from the public API.
|
||||
* db: Make get_number_of, get_raw_data and has_handle methods protected.
|
||||
* db: Remove get_from_name_and_gramps_id method.
|
||||
* db: Remove get_from_name_and_handle_method.
|
||||
* postgresql: Ignore empty connection settings
|
||||
* Include new PostgreSQL error handling in CLI
|
||||
* Improve PostgreSQL error handling
|
||||
Re-raise a DbConnectionError if the connection raises an error.
|
||||
* Add connection preferences for PostgreSQL databases
|
||||
* bsddb: Build surname list with unique names.
|
||||
* fanchart2way: Use escape from html rather than cgi package
|
||||
* Fix QuestionDialog display for html like characters in title
|
||||
* Fix Find Database Loop tool for faster operation and better display
|
||||
* Fix FamilyRelationshpType _DATAMAP order to correspond with values
|
||||
* Fix HandleError on IsLessThanNthGenerationAncestorOf filters
|
||||
* Fix Relationship Graph to shows families if siblings but no parents
|
||||
* Fix Deep Relationship filter to avoid recursion crash
|
||||
This also changes filter to use shortest paths.
|
||||
* Fix to allow filter progress meters in flat views
|
||||
* Fix Gedcom import for illegal Gedcom Family Attributes
|
||||
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.
|
||||
* Fix Session Log for exception on closed db reference click
|
||||
* Fix 'Welcome' Gramplet and StyledTextEditor for Link handling
|
||||
* fix Quickreport start from Editor when object not yet committed
|
||||
* Fix Find Database Loop tool for wrong transient parent for progress
|
||||
* Fix Test Case Generator to not create illegal 'None' handles
|
||||
* Fix Gedcom export for bad Hebrew Months
|
||||
* Can't link to a place if wrong lat/lon in the db.
|
||||
* Update redirected wiki link from GRAMPS_XML > Gramps_XML
|
||||
* Missing arg when right clicking in geography view.
|
||||
* INSTALL: Change INSTALL to replace 'python' with 'python3 for script invokes
|
||||
* Check&Repair; add checks for 'backlinks' and repair if needed.
|
||||
* Fix CSV importer for place event name using gramps_id
|
||||
* Fix PlaceModel to avoid exception following merge
|
||||
* 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
|
||||
This is particularly useful on Mac OS X where Gramps is passed a PATH that
|
||||
does not include elements added by the terminal shell.
|
||||
* Fix relationship Graph so Unicode chars on Multiple pages works.
|
||||
* Use Gdk.Event.triggers_context_menu in osmgpsmaps.py.
|
||||
* Replace is_right_click implementation with Gdk.Event.triggers_context_menu().
|
||||
* Replace use of CONTROL_MASK with PRIMARY_INTENT. For better UX on Macs.
|
||||
* Fix opening recent family trees
|
||||
* Fix exception when changing a family with Add person editor open
|
||||
* Remove eval() and rename self.dbstate
|
||||
* reorderids: Another way to protect GOV IDs in Gramps ID position
|
||||
* updated German date handler: added missing Latin month names and
|
||||
some old German month names
|
||||
* remove vestage of (old) SVN source-control system
|
||||
* update authors file
|
||||
* Fix ManagedWindow/GrampsWindowManager for subsidiary window close
|
||||
* Fix Gedcom import for "1 MARR Y" issue
|
||||
* Correct ReorderID tool for several bugs and deal with GetGov ID
|
||||
* Bad call to unlink function.
|
||||
* Fix Gedcom export for erroneously removed WriterOptionBox import
|
||||
* Try to handle OSError when we use the --export argument in CLI mode.
|
||||
* Special char in place's name breaks xdot view
|
||||
* Organize Bookmarks Dialog doesn't close with x
|
||||
* tweak some DateParser comments
|
||||
* DateParserEN failures under the DateTest tool
|
||||
* Fix make_unknown to create valid Place
|
||||
* editplace: fix typos
|
||||
* StyledText.join method fails if the joint_text has StyledText Tags
|
||||
* 3.4 database conversion fails
|
||||
* fix invalid export (and import) of Gedcom EMAIL records
|
||||
Gedcom spec 5.5 requires '@' in general text to be doubled '@@'
|
||||
|
||||
2017-09-02
|
||||
Version 5.0.0-alpha3
|
||||
* Fix Undo -- crashes due to race in Gtk
|
||||
|
||||
@@ -16,14 +16,15 @@ The following packages **MUST** be installed in order for Gramps to work:
|
||||
|
||||
The following three packages with GObject Introspection bindings (the gi packages)
|
||||
|
||||
* **cairo** - a 2D graphics library with support for multiple output devices. http://cairographics.org/
|
||||
* **cairo** 1.13.1 or greater - a 2D graphics library with support for multiple output devices. http://cairographics.org/
|
||||
* **Pycairo** 1.13.3 or greater - GObject Introspection bindings for cairo. https://github.com/pygobject/pycairo
|
||||
* **pango** - a library for laying out and rendering of text, with an emphasis on internationalization. http://www.pango.org/
|
||||
* **pangocairo** - Allows you to use Pango with Cairo http://www.pango.org/
|
||||
|
||||
* **librsvg2** - (SVG icon view) a library to render SVG files using cairo. http://live.gnome.org/LibRsvg
|
||||
* **xdg-utils** - Desktop integration utilities from freedesktop.org
|
||||
* **bsddb3** - Python bindings for Oracle Berkeley DB https://pypi.python.org/pypi/bsddb3/
|
||||
|
||||
* **sqlite3** - Python bindings for SQLite Database library
|
||||
|
||||
The following package is needed for full translation of the interface
|
||||
to your language:
|
||||
@@ -44,7 +45,7 @@ The following packages are **STRONGLY RECOMMENDED** to be installed:
|
||||
It may be osmgpsmap, osm-gps-map, or python-osmgpsmap,
|
||||
but the Python bindings for this must also be present, so gir1.2-osmgpsmap-1.0.
|
||||
Without this the GeoView will not be active, see
|
||||
https://gramps-project.org/wiki/index.php?title=Gramps_4.2_Wiki_Manual_-_Categories#Geography_Category
|
||||
https://gramps-project.org/wiki/index.php?title=Gramps_5.0_Wiki_Manual_-_Categories#Geography_Category
|
||||
|
||||
* **Graphviz**
|
||||
|
||||
@@ -65,6 +66,10 @@ The following packages are **STRONGLY RECOMMENDED** to be installed:
|
||||
(These are Python bindings for the ICU package.
|
||||
https://pypi.python.org/pypi/PyICU/)
|
||||
|
||||
* **Ghostscript**
|
||||
|
||||
Used by Graphviz reports to help create PDF's
|
||||
|
||||
The following packages are optional:
|
||||
------------------------------------
|
||||
* **gtkspell**
|
||||
@@ -77,12 +82,12 @@ The following packages are optional:
|
||||
|
||||
The GNU Revision Control System (RCS) can be used to manage
|
||||
multiple revisions of your family trees. See info at
|
||||
https://gramps-project.org/wiki/index.php?title=Gramps_4.2_Wiki_Manual_-_Manage_Family_Trees#Archiving_a_Family_Tree
|
||||
https://gramps-project.org/wiki/index.php?title=Gramps_5.0_Wiki_Manual_-_Manage_Family_Trees#Archiving_a_Family_Tree
|
||||
Only rcs is needed, NO python bindings are required
|
||||
|
||||
* **PIL**
|
||||
|
||||
Python Image Library is needed to crop
|
||||
Python Image Library (PILLOW) is needed to crop
|
||||
images and also to convert non-JPG images to
|
||||
JPG so as to include them in LaTeX output.
|
||||
(For Python3 a different source may be needed,
|
||||
@@ -112,6 +117,9 @@ Prerequistes required for the following Addons to work:
|
||||
* **Graph View** - Requires: PyGoocanvas and Goocanvas (python-pygoocanvas, gir1.2-goocanvas-2.0).
|
||||
( https://gramps-project.org/wiki/index.php?title=Graph_View )
|
||||
|
||||
* **Network Chart** - Requires: networkx and pygraphviz
|
||||
( https://gramps-project.org/wiki/index.php?title=NetworkChart )
|
||||
|
||||
* **PedigreeChart** - Can optionally use - numpy if installed
|
||||
( https://gramps-project.org/wiki/index.php?title=PedigreeChart )
|
||||
|
||||
|
||||
@@ -288,10 +288,6 @@ table.infolist thead tr th {
|
||||
table.infolist tr td {
|
||||
border-bottom: dashed 1px #000;
|
||||
vertical-align: middle;
|
||||
padding: 6px 0 6px 10px;
|
||||
}
|
||||
table.infolist tr td a {
|
||||
display: block;
|
||||
}
|
||||
table.infolist tr.BeginLetter td, table.infolist tr.BeginSurname td {
|
||||
border-top: solid 1px #000;
|
||||
@@ -351,15 +347,15 @@ div#Individuals {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
div#Individuals table.individuallist {
|
||||
div#Individuals table.IndividualList {
|
||||
border-bottom: solid 1px #000;
|
||||
}
|
||||
div#Individuals table.individuallist tbody tr td.ColumnSurname a:hover,
|
||||
div#Individuals table.individuallist tbody tr td.ColumnSurname a:active {
|
||||
div#Individuals table.IndividualList tbody tr td.ColumnSurname a:hover,
|
||||
div#Individuals table.IndividualList tbody tr td.ColumnSurname a:active {
|
||||
cursor: default;
|
||||
background: none;
|
||||
}
|
||||
div#Individuals table.individuallist tbody tr td.ColumnName a {
|
||||
div#Individuals table.IndividualList tbody tr td.ColumnName a {
|
||||
vertical-align: middle;
|
||||
}
|
||||
div#Individuals div table.infolist tr td p {
|
||||
@@ -1129,9 +1125,10 @@ div.narrative {
|
||||
}
|
||||
.narrative p {
|
||||
font: normal .9em/1.4em sans-serif;
|
||||
margin-top: .5em;
|
||||
margin-bottom: 0;
|
||||
padding: 0 20px 1em 20px;
|
||||
margin: 0.1em 0 0.2em 0;
|
||||
}
|
||||
i + div.grampsstylednote p {
|
||||
margin: 0.1em 0 0.2em 0;
|
||||
}
|
||||
|
||||
/* Subsections : References
|
||||
|
||||
+12
-12
@@ -276,7 +276,6 @@ table.infolist tr th a:hover {
|
||||
table.infolist tr td {
|
||||
font:normal 1.1em/1.4em serif;
|
||||
vertical-align:middle;
|
||||
padding:.1em 10px;
|
||||
}
|
||||
table.infolist tr td a {
|
||||
display:block;
|
||||
@@ -396,30 +395,30 @@ table.surname thead tr th.ColumnParents, table.surname tbody tr td.ColumnParents
|
||||
/* Individuals
|
||||
----------------------------------------------------- */
|
||||
#Individuals { }
|
||||
#Individuals table.individuallist {
|
||||
#Individuals table.IndividualList {
|
||||
border-bottom:solid 1px #A97;
|
||||
}
|
||||
#Individuals table.individuallist tbody tr td {
|
||||
#Individuals table.IndividualList tbody tr td {
|
||||
border-bottom:dashed 1px #C1B398;
|
||||
}
|
||||
#Individuals table.individuallist tbody tr td a:hover {
|
||||
#Individuals table.IndividualList tbody tr td a:hover {
|
||||
text-decoration:none;
|
||||
}
|
||||
table.individuallist tbody tr td.ColumnSurname a:hover, table.individuallist tbody tr td.ColumnSurname a:active {
|
||||
table.IndividualList tbody tr td.ColumnSurname a:hover, table.IndividualList tbody tr td.ColumnSurname a:active {
|
||||
cursor:default;
|
||||
color:black;
|
||||
background:none;
|
||||
}
|
||||
table.individuallist tbody tr td.ColumnName {
|
||||
table.IndividualList tbody tr td.ColumnName {
|
||||
padding:0;
|
||||
background-color:#FFF;
|
||||
}
|
||||
table.individuallist tbody tr td.ColumnName a {
|
||||
table.IndividualList tbody tr td.ColumnName a {
|
||||
display:block;
|
||||
padding:.6em 10px;
|
||||
padding:.1em .1em;
|
||||
vertical-align:middle;
|
||||
}
|
||||
table.individuallist tbody tr td.ColumnName a:hover {
|
||||
table.IndividualList tbody tr td.ColumnName a:hover {
|
||||
background-color:#C1B398;
|
||||
}
|
||||
#Individuals div table.infolist tr td p {
|
||||
@@ -960,11 +959,12 @@ div#Addresses table.infolist tr td a, div#Addresses table.infolist tr td p a {
|
||||
div.narrative {
|
||||
padding-bottom:0;
|
||||
}
|
||||
i + div.grampsstylednote p {
|
||||
margin: 0.1em 0 0.2em 0;
|
||||
}
|
||||
.narrative p {
|
||||
margin: 0.1em 0 0.2em 0;
|
||||
font:normal .9em/1.4em sans-serif;
|
||||
margin-top:.5em;
|
||||
margin-bottom:0;
|
||||
padding:0 20px 1em 20px;
|
||||
}
|
||||
|
||||
/* Subsections : References
|
||||
|
||||
@@ -454,30 +454,30 @@ div#Individuals {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
div#Individuals table.individuallist {
|
||||
div#Individuals table.IndividualList {
|
||||
border-bottom: solid 1px #5D835F;
|
||||
}
|
||||
div#Individuals table.individuallist tbody tr td {
|
||||
div#Individuals table.IndividualList tbody tr td {
|
||||
border-bottom: dashed 1px #5D835F;
|
||||
background-color: #D8F3D6;
|
||||
}
|
||||
div#Individuals table.individuallist tbody tr td a {
|
||||
div#Individuals table.IndividualList tbody tr td a {
|
||||
display: block;
|
||||
padding: .6em 10px;
|
||||
}
|
||||
div#Individuals table.individuallist tbody tr td.ColumnSurname a:hover,
|
||||
div#Individuals table.individuallist tbody tr td.ColumnSurname a:active {
|
||||
div#Individuals table.IndividualList tbody tr td.ColumnSurname a:hover,
|
||||
div#Individuals table.IndividualList tbody tr td.ColumnSurname a:active {
|
||||
cursor:default;
|
||||
color: #000;
|
||||
background:none;
|
||||
}
|
||||
div#Individuals table.individuallist tbody tr td.ColumnName {
|
||||
div#Individuals table.IndividualList tbody tr td.ColumnName {
|
||||
background-color: #FFF;
|
||||
}
|
||||
div#Individuals table.individuallist tbody tr td.ColumnName a {
|
||||
div#Individuals table.IndividualList tbody tr td.ColumnName a {
|
||||
vertical-align:middle;
|
||||
}
|
||||
div#Individuals table.individuallist tbody tr td.ColumnPartner {
|
||||
div#Individuals table.IndividualList tbody tr td.ColumnPartner {
|
||||
background-color: #FFF;
|
||||
}
|
||||
div#Individuals div table.infolist tr td p {
|
||||
|
||||
@@ -33,7 +33,9 @@ body#FamilyMap {
|
||||
border: solid 4px #000;
|
||||
margin: 0px auto;
|
||||
width: 800px;
|
||||
height: 800px;
|
||||
height: 400px;
|
||||
max-width: 90%;
|
||||
max-height: 90%;
|
||||
}
|
||||
|
||||
/* Place Maps
|
||||
@@ -43,6 +45,8 @@ div#place_canvas {
|
||||
border: solid 4px #000;
|
||||
width: 500px;
|
||||
height: 400px;
|
||||
max-width: 90%;
|
||||
max-height: 90%;
|
||||
}
|
||||
button#drop {
|
||||
background-color: purple;
|
||||
|
||||
@@ -201,12 +201,12 @@
|
||||
<date name="Pääsiäispäivä" type="religious" value="> easter(y)" />
|
||||
<date name="2. Pääsiäispäivä" type="religious" value="> easter(y)" offset="1" />
|
||||
<date name="Helatorstai" type="religious" value="> easter(y)" offset="39" if="not(y>=1973)" />
|
||||
<date name="Kristuksen taivaaseenastumisen päivä." type="religious" value="> easter(y)" offset="34" if="(y>=1973) and not(y>=1992)" />
|
||||
<date name="Kristuksen taivaaseenastumisen päivä" type="religious" value="> easter(y)" offset="34" if="(y>=1973) and not(y>=1992)" />
|
||||
<date name="Helatorstai" type="religious" value="> easter(y)" offset="39" if="(y>=1992)" />
|
||||
<date name="Helluntaipäivä" value="easter(y)" offset="49" type="religious" />
|
||||
<date name="Pyhän Kolminaisuuden päivä" value="easter(y)" offset="56" type="religious" />
|
||||
<date name="Apostolienpäivä" value="easter(y)" offset="91" type="religious" />
|
||||
<date name="Kirkastussunnuntai." value="easter(y)" offset="105" type="religious" />
|
||||
<date name="Kirkastussunnuntai" value="easter(y)" offset="105" type="religious" />
|
||||
<date name="Juhannusaatto" value="*/jun/19" offset="fri" type="religious" if="(y>=1955)" />
|
||||
<date name="Juhannusaatto" value="*/jun/23" type="religious" if="not(y>=1955)" />
|
||||
<date name="Juhannuspäivä" value="*/jun/20" offset="sat" type="religious" if="(y>=1955)" />
|
||||
@@ -224,12 +224,12 @@
|
||||
<date name="Joulupäivä" value="*/12/25" type="religious" />
|
||||
<date name="Tapaninpäivä" value="*/12/26" type="religious" />
|
||||
<date name="Johannes Evankelistan päivä" value="*/dec/27" type="religious" if="not(y>=1774)" />
|
||||
<date name="Apostoli Johanneksen päivä." value="*/dec/27" type="religious" if="dow(y, m, d) == 7 and (y>=2000)" />
|
||||
<date name="Apostoli Johanneksen päivä" value="*/dec/27" type="religious" if="dow(y, m, d) == 7 and (y>=2000)" />
|
||||
<date name="Viattomien lasten päivä" value="*/12/28" type="religious" />
|
||||
<date name="Uudenvuodenpäivä" value="*/1/1" type="national" />
|
||||
<date name="Vainojen uhrien muistopäivä" value="*/1/27" type="national" if="(y>=2002)" />
|
||||
<date name="J.L. Runebergin päivä" value="*/2/5" type="national" if="(y>=1854)" />
|
||||
<date name="Saamelaisten kansallis päivä" value="*/2/6" type="religious" if="(y>=2004)" />
|
||||
<date name="Saamelaisten kansallispäivä" value="*/2/6" type="religious" if="(y>=2004)" />
|
||||
<date name="Ystävänpäivä" value="*/2/14" type="national" if="(y>=1987)" />
|
||||
<date name="Kalevalan päivä" value="*/2/28" type="national" if="(y>=1978)" />
|
||||
<date name="Naistenpäivä" value="*/3/8" type="national" if="(y>=1975)" />
|
||||
|
||||
@@ -318,6 +318,7 @@ class CLIManager:
|
||||
and self.dbstate.db.get_total() == 0):
|
||||
self.dbstate.db.set_researcher(owner)
|
||||
|
||||
name_displayer.clear_custom_formats()
|
||||
name_displayer.set_name_format(self.dbstate.db.name_formats)
|
||||
fmt_default = config.get('preferences.name-format')
|
||||
name_displayer.set_default_format(fmt_default)
|
||||
|
||||
@@ -47,7 +47,8 @@ LOG = logging.getLogger(".")
|
||||
#-------------------------------------------------------------------------
|
||||
from gramps.gen.plug import BasePluginManager
|
||||
from gramps.gen.plug.docgen import (StyleSheet, StyleSheetList, PaperStyle,
|
||||
PAPER_PORTRAIT, PAPER_LANDSCAPE, graphdoc)
|
||||
PAPER_PORTRAIT, PAPER_LANDSCAPE, graphdoc,
|
||||
treedoc)
|
||||
from gramps.gen.plug.menu import (FamilyOption, PersonOption, NoteOption,
|
||||
MediaOption, PersonListOption, NumberOption,
|
||||
BooleanOption, DestinationOption, Option,
|
||||
@@ -56,8 +57,8 @@ from gramps.gen.plug.menu import (FamilyOption, PersonOption, NoteOption,
|
||||
from gramps.gen.display.name import displayer as name_displayer
|
||||
from gramps.gen.errors import ReportError, FilterError
|
||||
from gramps.gen.plug.report import (CATEGORY_TEXT, CATEGORY_DRAW, CATEGORY_BOOK,
|
||||
CATEGORY_GRAPHVIZ, CATEGORY_CODE,
|
||||
ReportOptions, append_styles)
|
||||
CATEGORY_GRAPHVIZ, CATEGORY_TREE,
|
||||
CATEGORY_CODE, ReportOptions, append_styles)
|
||||
from gramps.gen.plug.report._paper import paper_sizes
|
||||
from gramps.gen.const import USER_HOME, DOCGEN_OPTIONS
|
||||
from gramps.gen.dbstate import DbState
|
||||
@@ -250,6 +251,15 @@ class CommandLineReport:
|
||||
if name not in self.option_class.options_dict:
|
||||
self.option_class.options_dict[
|
||||
name] = menu.get_option_by_name(name).get_value()
|
||||
if category == CATEGORY_TREE:
|
||||
# Need to include Genealogy Tree options
|
||||
self.__toptions = treedoc.TreeOptions()
|
||||
menu = self.option_class.menu
|
||||
self.__toptions.add_menu_options(menu)
|
||||
for name in menu.get_all_option_names():
|
||||
if name not in self.option_class.options_dict:
|
||||
self.option_class.options_dict[
|
||||
name] = menu.get_option_by_name(name).get_value()
|
||||
self.option_class.load_previous_values()
|
||||
_validate_options(self.option_class, database)
|
||||
self.show = options_str_dict.pop('show', None)
|
||||
@@ -320,6 +330,10 @@ class CommandLineReport:
|
||||
for graph_format in graphdoc.FORMATS:
|
||||
self.options_help['off'][2].append(
|
||||
graph_format["type"] + "\t" + graph_format["descr"])
|
||||
elif self.category == CATEGORY_TREE:
|
||||
for tree_format in treedoc.FORMATS:
|
||||
self.options_help['off'][2].append(
|
||||
tree_format["type"] + "\t" + tree_format["descr"])
|
||||
else:
|
||||
self.options_help['off'][2] = "NA"
|
||||
|
||||
@@ -498,6 +512,15 @@ class CommandLineReport:
|
||||
# Pick the first one as the default.
|
||||
self.format = graphdoc.FORMATS[0]["class"]
|
||||
_chosen_format = graphdoc.FORMATS[0]["type"]
|
||||
elif self.category == CATEGORY_TREE:
|
||||
for tree_format in treedoc.FORMATS:
|
||||
if tree_format['type'] == self.options_dict['off']:
|
||||
if not self.format: # choose the first one, not the last
|
||||
self.format = tree_format["class"]
|
||||
if self.format is None:
|
||||
# Pick the first one as the default.
|
||||
self.format = tree_format.FORMATS[0]["class"]
|
||||
_chosen_format = tree_format.FORMATS[0]["type"]
|
||||
else:
|
||||
self.format = None
|
||||
if _chosen_format and _format_str:
|
||||
@@ -665,7 +688,7 @@ def cl_report(database, name, category, report_class, options_class,
|
||||
clr.selected_style,
|
||||
PaperStyle(clr.paper, clr.orien, clr.marginl,
|
||||
clr.marginr, clr.margint, clr.marginb))
|
||||
elif category == CATEGORY_GRAPHVIZ:
|
||||
elif category in [CATEGORY_GRAPHVIZ, CATEGORY_TREE]:
|
||||
clr.option_class.handler.doc = clr.format(
|
||||
clr.option_class,
|
||||
PaperStyle(clr.paper, clr.orien, clr.marginl,
|
||||
|
||||
+20
-20
@@ -247,11 +247,8 @@ register('preferences.hide-ep-msg', False)
|
||||
register('preferences.invalid-date-format', "<b>%s</b>")
|
||||
register('preferences.iprefix', 'I%04d')
|
||||
register('preferences.name-format', 1)
|
||||
register('preferences.place-format', 0)
|
||||
register('preferences.place-auto', True)
|
||||
register('preferences.place-number', False)
|
||||
register('preferences.place-reverse', False)
|
||||
register('preferences.place-restrict', 0)
|
||||
register('preferences.place-lang', '')
|
||||
register('preferences.patronimic-surname', False)
|
||||
register('preferences.no-given-text', "[%s]" % _("Missing Given Name"))
|
||||
register('preferences.no-record-text', "[%s]" % _("Missing Record"))
|
||||
@@ -272,22 +269,25 @@ register('preferences.last-view', '')
|
||||
register('preferences.last-views', [])
|
||||
register('preferences.family-relation-type', 3) # UNKNOWN
|
||||
register('preferences.age-display-precision', 1)
|
||||
register('preferences.color-gender-male-alive', '#b8cee6')
|
||||
register('preferences.color-gender-male-death', '#b8cee6')
|
||||
register('preferences.color-gender-female-alive', '#feccf0')
|
||||
register('preferences.color-gender-female-death', '#feccf0')
|
||||
register('preferences.color-gender-unknown-alive', '#f3dbb6')
|
||||
register('preferences.color-gender-unknown-death', '#f3dbb6')
|
||||
#register('preferences.color-gender-other-alive', '#fcaf3e')
|
||||
#register('preferences.color-gender-other-death', '#fcaf3e')
|
||||
register('preferences.bordercolor-gender-male-alive', '#1f4986')
|
||||
register('preferences.bordercolor-gender-male-death', '#000000')
|
||||
register('preferences.bordercolor-gender-female-alive', '#861f69')
|
||||
register('preferences.bordercolor-gender-female-death', '#000000')
|
||||
register('preferences.bordercolor-gender-unknown-alive', '#8e5801')
|
||||
register('preferences.bordercolor-gender-unknown-death', '#000000')
|
||||
#register('preferences.bordercolor-gender-other-alive', '#f57900')
|
||||
#register('preferences.bordercolor-gender-other-death', '#000000')
|
||||
|
||||
register('colors.scheme', 0)
|
||||
register('colors.male-alive', ['#b8cee6', '#1f344a'])
|
||||
register('colors.male-dead', ['#b8cee6', '#2d3039'])
|
||||
register('colors.female-alive', ['#feccf0', '#62242D'])
|
||||
register('colors.female-dead', ['#feccf0', '#3a292b'])
|
||||
register('colors.unknown-alive', ['#f3dbb6', '#75507B'])
|
||||
register('colors.unknown-dead', ['#f3dbb6', '#35103b'])
|
||||
register('colors.family', ['#eeeeee', '#454545'])
|
||||
register('colors.family-divorced', ['#ffdede', '#5c3636'])
|
||||
register('colors.home-person', ['#bbe68a', '#304918'])
|
||||
register('colors.border-male-alive', ['#1f4986', '#171d26'])
|
||||
register('colors.border-male-dead', ['#000000', '#000000'])
|
||||
register('colors.border-female-alive', ['#861f69', '#261111'])
|
||||
register('colors.border-female-dead', ['#000000', '#000000'])
|
||||
register('colors.border-unknown-alive', ['#8e5801', '#8e5801'])
|
||||
register('colors.border-unknown-dead', ['#000000', '#000000'])
|
||||
register('colors.border-family', ['#cccccc', '#252525'])
|
||||
register('colors.border-family-divorced', ['#ff7373', '#720b0b'])
|
||||
|
||||
register('researcher.researcher-addr', '')
|
||||
register('researcher.researcher-locality', '')
|
||||
|
||||
+3
-2
@@ -112,6 +112,7 @@ VERSION_DIR = os.path.join(
|
||||
CUSTOM_FILTERS = os.path.join(VERSION_DIR, "custom_filters.xml")
|
||||
REPORT_OPTIONS = os.path.join(HOME_DIR, "report_options.xml")
|
||||
TOOL_OPTIONS = os.path.join(HOME_DIR, "tool_options.xml")
|
||||
PLACE_FORMATS = os.path.join(HOME_DIR, "place_formats.xml")
|
||||
|
||||
ENV_DIR = os.path.join(HOME_DIR, "env")
|
||||
TEMP_DIR = os.path.join(HOME_DIR, "temp")
|
||||
@@ -137,7 +138,7 @@ sys.path.insert(0, ROOT_DIR)
|
||||
git_revision = get_git_revision(ROOT_DIR).replace('\n', '')
|
||||
if sys.platform == 'win32' and git_revision == "":
|
||||
git_revision = get_git_revision(os.path.split(ROOT_DIR)[1])
|
||||
VERSION += git_revision
|
||||
#VERSION += git_revision
|
||||
|
||||
#
|
||||
# Glade files
|
||||
@@ -216,7 +217,7 @@ GTK_GETTEXT_DOMAIN = 'gtk30'
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
COPYRIGHT_MSG = "© 2001-2006 Donald N. Allingham\n" \
|
||||
"© 2007-2017 The Gramps Developers"
|
||||
"© 2007-2018 The Gramps Developers"
|
||||
COMMENTS = _("Gramps\n (Genealogical Research and Analysis "
|
||||
"Management Programming System)\n"
|
||||
"is a personal genealogy program.")
|
||||
|
||||
@@ -292,7 +292,7 @@ class DateDisplayDE(DateDisplay):
|
||||
)
|
||||
# this definition must agree with its "_display_gregorian" method
|
||||
|
||||
def _display_gregorian(self, date_val):
|
||||
def _display_gregorian(self, date_val, **kwargs):
|
||||
"""
|
||||
display gregorian calendar date in different format
|
||||
"""
|
||||
|
||||
@@ -155,7 +155,7 @@ class DateDisplayEL(DateDisplay):
|
||||
)
|
||||
# this definition must agree with its "_display_gregorian" method
|
||||
|
||||
def _display_gregorian(self, date_val):
|
||||
def _display_gregorian(self, date_val, **kwargs):
|
||||
"""
|
||||
display gregorian calendar date in different format
|
||||
"""
|
||||
|
||||
@@ -187,7 +187,7 @@ class DateDisplayLT(DateDisplay):
|
||||
"mmmm m. mėnesio diena d.", "Mėn diena, metai")
|
||||
# this definition must agree with its "_display_gregorian" method
|
||||
|
||||
def _display_gregorian(self, date_val):
|
||||
def _display_gregorian(self, date_val, **kwargs):
|
||||
"""
|
||||
display gregorian calendar date in different format
|
||||
"""
|
||||
|
||||
@@ -164,7 +164,7 @@ class DateDisplayNL(DateDisplay):
|
||||
)
|
||||
# this definition must agree with its "_display_gregorian" method
|
||||
|
||||
def _display_gregorian(self, date_val):
|
||||
def _display_gregorian(self, date_val, **kwargs):
|
||||
"""
|
||||
display gregorian calendar date in different format
|
||||
"""
|
||||
|
||||
@@ -215,7 +215,7 @@ class DateDisplayPL(DateDisplay):
|
||||
"XII"
|
||||
)
|
||||
|
||||
def _display_gregorian(self, date_val):
|
||||
def _display_gregorian(self, date_val, **kwargs):
|
||||
"""
|
||||
display gregorian calendar date in different format
|
||||
"""
|
||||
|
||||
@@ -240,7 +240,7 @@ class DateDisplaySR_Base(DateDisplay):
|
||||
"VII", "VIII", "IX", "X", "XI", "XII"
|
||||
)
|
||||
|
||||
def _display_gregorian(self, date_val):
|
||||
def _display_gregorian(self, date_val, **kwargs):
|
||||
"""
|
||||
display gregorian calendar date in different format
|
||||
"""
|
||||
|
||||
@@ -418,6 +418,11 @@ class NameDisplay:
|
||||
result = raw_data[_FIRSTNAME]
|
||||
return ' '.join(result.split())
|
||||
|
||||
def clear_custom_formats(self):
|
||||
self.name_formats = {num: value
|
||||
for num, value in self.name_formats.items()
|
||||
if num >= 0}
|
||||
|
||||
def set_name_format(self, formats):
|
||||
|
||||
raw_func_dict = {
|
||||
|
||||
+114
-16
@@ -1,7 +1,7 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2014-2015 Nick Hall
|
||||
# Copyright (C) 2014-2017 Nick Hall
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -22,15 +22,43 @@
|
||||
Class handling displaying of places.
|
||||
"""
|
||||
|
||||
#---------------------------------------------------------------
|
||||
#
|
||||
# Python imports
|
||||
#
|
||||
#---------------------------------------------------------------
|
||||
import os
|
||||
import xml.dom.minidom
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Gramps modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from ..const import PLACE_FORMATS
|
||||
from ..config import config
|
||||
from ..utils.location import get_location_list
|
||||
from ..lib import PlaceType
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# PlaceFormat class
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class PlaceFormat:
|
||||
def __init__(self, name, levels, language, street, reverse):
|
||||
self.name = name
|
||||
self.levels = levels
|
||||
self.language = language
|
||||
self.street = street
|
||||
self.reverse = reverse
|
||||
|
||||
def to_xml(self):
|
||||
return (' <format name="%s" levels="%s" language="%s" '
|
||||
'street="%s" reverse="%s"/>\n' %
|
||||
(self.name, self.levels, self.language,
|
||||
self.street, self.reverse))
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# PlaceDisplay class
|
||||
@@ -38,51 +66,121 @@ from ..lib import PlaceType
|
||||
#-------------------------------------------------------------------------
|
||||
class PlaceDisplay:
|
||||
|
||||
def display_event(self, db, event):
|
||||
def __init__(self):
|
||||
self.place_formats = []
|
||||
self.default_format = config.get('preferences.place-format')
|
||||
if os.path.exists(PLACE_FORMATS):
|
||||
self.load_formats()
|
||||
else:
|
||||
pf = PlaceFormat('Full', ':', '', 0, False)
|
||||
self.place_formats.append(pf)
|
||||
|
||||
def display_event(self, db, event, fmt=None):
|
||||
if not event:
|
||||
return ""
|
||||
place_handle = event.get_place_handle()
|
||||
if place_handle:
|
||||
place = db.get_place_from_handle(place_handle)
|
||||
return self.display(db, place, event.get_date_object())
|
||||
return self.display(db, place, event.get_date_object(), fmt)
|
||||
else:
|
||||
return ""
|
||||
|
||||
def display(self, db, place, date=None):
|
||||
def display(self, db, place, date=None, fmt=None):
|
||||
if not place:
|
||||
return ""
|
||||
if not config.get('preferences.place-auto'):
|
||||
return place.title
|
||||
else:
|
||||
lang = config.get('preferences.place-lang')
|
||||
places = get_location_list(db, place, date, lang)
|
||||
if fmt is None:
|
||||
fmt = config.get('preferences.place-format')
|
||||
pf = self.place_formats[fmt]
|
||||
lang = pf.language
|
||||
all_places = get_location_list(db, place, date, lang)
|
||||
|
||||
if config.get('preferences.place-restrict') > 0:
|
||||
index = _find_populated_place(places)
|
||||
if index is not None:
|
||||
if config.get('preferences.place-restrict') == 1:
|
||||
places = places[:index+1]
|
||||
# Apply format string to place list
|
||||
index = _find_populated_place(all_places)
|
||||
places = []
|
||||
for slice in pf.levels.split(','):
|
||||
parts = slice.split(':')
|
||||
if len(parts) == 1:
|
||||
offset = _get_offset(parts[0], index)
|
||||
if offset is not None:
|
||||
places.append(all_places[offset])
|
||||
elif len(parts) == 2:
|
||||
start = _get_offset(parts[0], index)
|
||||
end = _get_offset(parts[1], index)
|
||||
if start is None:
|
||||
places.extend(all_places[:end])
|
||||
elif end is None:
|
||||
places.extend(all_places[start:])
|
||||
else:
|
||||
places = places[index:]
|
||||
places.extend(all_places[start:end])
|
||||
|
||||
if config.get('preferences.place-number'):
|
||||
if pf.street:
|
||||
types = [item[1] for item in places]
|
||||
try:
|
||||
idx = types.index(PlaceType.NUMBER)
|
||||
except ValueError:
|
||||
idx = None
|
||||
if idx is not None and len(places) > idx+1:
|
||||
combined = (places[idx][0] + ' ' + places[idx+1][0],
|
||||
places[idx+1][1])
|
||||
if pf.street == 1:
|
||||
combined = (places[idx][0] + ' ' + places[idx+1][0],
|
||||
places[idx+1][1])
|
||||
else:
|
||||
combined = (places[idx+1][0] + ' ' + places[idx][0],
|
||||
places[idx+1][1])
|
||||
places = places[:idx] + [combined] + places[idx+2:]
|
||||
|
||||
names = [item[0] for item in places]
|
||||
if config.get('preferences.place-reverse'):
|
||||
if pf.reverse:
|
||||
names.reverse()
|
||||
|
||||
# TODO for Arabic, should the next line's comma be translated?
|
||||
return ", ".join(names)
|
||||
|
||||
def get_formats(self):
|
||||
return self.place_formats
|
||||
|
||||
def set_formats(self, formats):
|
||||
self.place_formats = formats
|
||||
|
||||
def load_formats(self):
|
||||
dom = xml.dom.minidom.parse(PLACE_FORMATS)
|
||||
top = dom.getElementsByTagName('place_formats')
|
||||
|
||||
for fmt in top[0].getElementsByTagName('format'):
|
||||
name = fmt.attributes['name'].value
|
||||
levels = fmt.attributes['levels'].value
|
||||
language = fmt.attributes['language'].value
|
||||
street = int(fmt.attributes['street'].value)
|
||||
reverse = fmt.attributes['reverse'].value == 'True'
|
||||
pf = PlaceFormat(name, levels, language, street, reverse)
|
||||
self.place_formats.append(pf)
|
||||
|
||||
dom.unlink()
|
||||
|
||||
def save_formats(self):
|
||||
with open(PLACE_FORMATS, 'w') as fd:
|
||||
fd.write('<?xml version="1.0" encoding="utf-8"?>\n')
|
||||
fd.write('<place_formats>\n')
|
||||
for fmt in self.place_formats:
|
||||
fd.write(fmt.to_xml())
|
||||
fd.write('</place_formats>\n')
|
||||
|
||||
def _get_offset(value, index):
|
||||
if index is not None and value.startswith('p'):
|
||||
try:
|
||||
offset = int(value[1:])
|
||||
except ValueError:
|
||||
offset = 0
|
||||
offset += index
|
||||
else:
|
||||
try:
|
||||
offset = int(value)
|
||||
except ValueError:
|
||||
offset = None
|
||||
return offset
|
||||
|
||||
def _find_populated_place(places):
|
||||
populated_place = None
|
||||
for index, item in enumerate(places):
|
||||
|
||||
@@ -82,7 +82,8 @@ class IsDescendantFamilyOf(Rule):
|
||||
|
||||
while expand:
|
||||
person = expand.pop(0)
|
||||
if person is None:
|
||||
if person is None or person.handle in self.matches:
|
||||
# if we have been here before, skip
|
||||
continue
|
||||
self.matches.add(person.handle)
|
||||
for family_handle in person.get_family_handle_list():
|
||||
|
||||
@@ -67,7 +67,8 @@ class IsDescendantOf(Rule):
|
||||
return person.handle in self.map
|
||||
|
||||
def init_list(self, person, first):
|
||||
if not person:
|
||||
if not person or person.handle in self.map:
|
||||
# if we have been here before, skip
|
||||
return
|
||||
if not first:
|
||||
self.map.add(person.handle)
|
||||
|
||||
@@ -61,6 +61,9 @@ class IsLessThanNthGenerationAncestorOf(Rule):
|
||||
queue = [(root_handle, 1)] # generation 1 is root
|
||||
while queue:
|
||||
handle, gen = queue.pop(0) # pop off front of queue
|
||||
if handle in self.map:
|
||||
# if we have been here before, skip
|
||||
continue
|
||||
self.map.add(handle)
|
||||
gen += 1
|
||||
if gen <= int(self.list[1]):
|
||||
|
||||
@@ -71,7 +71,8 @@ class IsLessThanNthGenerationAncestorOfBookmarked(Rule):
|
||||
def init_ancestor_list(self, handle, gen):
|
||||
# if p.get_handle() in self.map:
|
||||
# loop_error(self.orig,p)
|
||||
if not handle:
|
||||
if not handle or handle in self.map:
|
||||
# if been here already, skip
|
||||
return
|
||||
if gen:
|
||||
self.map.add(handle)
|
||||
|
||||
@@ -64,7 +64,8 @@ class IsLessThanNthGenerationAncestorOfDefaultPerson(Rule):
|
||||
def init_ancestor_list(self, handle, gen):
|
||||
# if p.get_handle() in self.map:
|
||||
# loop_error(self.orig,p)
|
||||
if not handle:
|
||||
if not handle or handle in self.map:
|
||||
# if we have been here before, skip
|
||||
return
|
||||
if gen:
|
||||
self.map.add(handle)
|
||||
|
||||
@@ -65,7 +65,8 @@ class IsLessThanNthGenerationDescendantOf(Rule):
|
||||
return person.handle in self.map
|
||||
|
||||
def init_list(self,person,gen):
|
||||
if not person:
|
||||
if not person or person.handle in self.map:
|
||||
# if we have been here before, skip
|
||||
return
|
||||
if gen:
|
||||
self.map.add(person.handle)
|
||||
|
||||
+12
-12
@@ -27,7 +27,7 @@ The "plug" package for handling plugins in Gramps.
|
||||
from ._plugin import Plugin
|
||||
from ._pluginreg import (PluginData, PluginRegister, REPORT, TOOL,
|
||||
CATEGORY_TEXT, CATEGORY_DRAW, CATEGORY_CODE,
|
||||
CATEGORY_WEB, CATEGORY_BOOK, CATEGORY_GRAPHVIZ,
|
||||
CATEGORY_WEB, CATEGORY_BOOK, CATEGORY_GRAPHVIZ, CATEGORY_TREE,
|
||||
TOOL_DEBUG, TOOL_ANAL, TOOL_DBPROC, TOOL_DBFIX, TOOL_REVCTL,
|
||||
TOOL_UTILS, CATEGORY_QR_MISC, CATEGORY_QR_PERSON,
|
||||
CATEGORY_QR_FAMILY, CATEGORY_QR_EVENT, CATEGORY_QR_SOURCE,
|
||||
@@ -46,14 +46,14 @@ from ._options import (Options, OptionListCollection, OptionList,
|
||||
OptionHandler, MenuOptions)
|
||||
|
||||
__all__ = [ "docbackend", "docgen", "menu", "Plugin", "PluginData",
|
||||
"PluginRegister", "BasePluginManager",
|
||||
"ImportPlugin", "ExportPlugin", "DocGenPlugin",
|
||||
"REPORT", "TOOL", "CATEGORY_TEXT", "CATEGORY_DRAW", "CATEGORY_CODE",
|
||||
"CATEGORY_WEB", "CATEGORY_BOOK", "CATEGORY_GRAPHVIZ",
|
||||
"TOOL_DEBUG", "TOOL_ANAL", "TOOL_DBPROC", "TOOL_DBFIX", "TOOL_REVCTL",
|
||||
"TOOL_UTILS", "CATEGORY_QR_MISC", "CATEGORY_QR_PERSON",
|
||||
"CATEGORY_QR_FAMILY", "CATEGORY_QR_EVENT", "CATEGORY_QR_SOURCE",
|
||||
"CATEGORY_QR_PLACE", "CATEGORY_QR_REPOSITORY", "CATEGORY_QR_NOTE",
|
||||
"CATEGORY_QR_DATE", "PTYPE_STR", "CATEGORY_QR_MEDIA",
|
||||
"CATEGORY_QR_CITATION", "CATEGORY_QR_SOURCE_OR_CITATION",
|
||||
"START", "END", "make_environment"]
|
||||
"PluginRegister", "BasePluginManager", "ImportPlugin",
|
||||
"ExportPlugin", "DocGenPlugin", "REPORT", "TOOL", "CATEGORY_TEXT",
|
||||
"CATEGORY_DRAW", "CATEGORY_CODE", "CATEGORY_WEB", "CATEGORY_BOOK",
|
||||
"CATEGORY_GRAPHVIZ", "CATEGORY_TREE", "TOOL_DEBUG", "TOOL_ANAL",
|
||||
"TOOL_DBPROC", "TOOL_DBFIX", "TOOL_REVCTL","TOOL_UTILS",
|
||||
"CATEGORY_QR_MISC", "CATEGORY_QR_PERSON", "CATEGORY_QR_FAMILY",
|
||||
"CATEGORY_QR_EVENT", "CATEGORY_QR_SOURCE", "CATEGORY_QR_PLACE",
|
||||
"CATEGORY_QR_REPOSITORY", "CATEGORY_QR_NOTE", "CATEGORY_QR_DATE",
|
||||
"PTYPE_STR", "CATEGORY_QR_MEDIA", "CATEGORY_QR_CITATION",
|
||||
"CATEGORY_QR_SOURCE_OR_CITATION", "START", "END",
|
||||
"make_environment"]
|
||||
|
||||
@@ -97,8 +97,10 @@ CATEGORY_CODE = 2
|
||||
CATEGORY_WEB = 3
|
||||
CATEGORY_BOOK = 4
|
||||
CATEGORY_GRAPHVIZ = 5
|
||||
CATEGORY_TREE = 6
|
||||
REPORT_CAT = [ CATEGORY_TEXT, CATEGORY_DRAW, CATEGORY_CODE,
|
||||
CATEGORY_WEB, CATEGORY_BOOK, CATEGORY_GRAPHVIZ]
|
||||
CATEGORY_WEB, CATEGORY_BOOK, CATEGORY_GRAPHVIZ,
|
||||
CATEGORY_TREE]
|
||||
#possible tool categories
|
||||
TOOL_DEBUG = -1
|
||||
TOOL_ANAL = 0
|
||||
@@ -1043,6 +1045,7 @@ def make_environment(**kwargs):
|
||||
'CATEGORY_WEB': CATEGORY_WEB,
|
||||
'CATEGORY_BOOK': CATEGORY_BOOK,
|
||||
'CATEGORY_GRAPHVIZ': CATEGORY_GRAPHVIZ,
|
||||
'CATEGORY_TREE': CATEGORY_TREE,
|
||||
'TOOL_DEBUG': TOOL_DEBUG,
|
||||
'TOOL_ANAL': TOOL_ANAL,
|
||||
'TOOL_DBPROC': TOOL_DBPROC,
|
||||
@@ -1242,8 +1245,7 @@ class PluginRegister:
|
||||
"""
|
||||
Return a list of :class:`PluginData` that are of type ptype
|
||||
"""
|
||||
return [self.get_plugin(id) for id in
|
||||
set([x.id for x in self.__plugindata if x.ptype == ptype])]
|
||||
return [x for x in self.__plugindata if x.ptype == ptype]
|
||||
|
||||
def report_plugins(self, gui=True):
|
||||
"""
|
||||
@@ -1352,6 +1354,4 @@ class PluginRegister:
|
||||
"""
|
||||
Return a list of :class:`PluginData` that have load_on_reg == True
|
||||
"""
|
||||
return [self.get_plugin(id) for id in
|
||||
set([x.id for x in self.__plugindata
|
||||
if x.load_on_reg == True])]
|
||||
return [x for x in self.__plugindata if x.load_on_reg == True]
|
||||
|
||||
@@ -37,3 +37,4 @@ from .textdoc import TextDoc, IndexMark,INDEX_TYPE_ALP, INDEX_TYPE_TOC,\
|
||||
URL_PATTERN, LOCAL_HYPERLINK, LOCAL_TARGET
|
||||
from .drawdoc import DrawDoc
|
||||
from .graphdoc import GVDoc
|
||||
from .treedoc import TreeDoc
|
||||
|
||||
@@ -0,0 +1,660 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2017-2018 Nick Hall
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
""" LaTeX Genealogy Tree adapter for Trees """
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Standard Python modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from abc import ABCMeta, abstractmethod
|
||||
import os
|
||||
import shutil
|
||||
import re
|
||||
from subprocess import Popen, PIPE
|
||||
from io import StringIO
|
||||
import tempfile
|
||||
import logging
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Gramps modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from ...utils.file import search_for
|
||||
from ...lib import Person, EventType, EventRoleType, Date
|
||||
from ...display.place import displayer as _pd
|
||||
from ...utils.file import media_path_full
|
||||
from . import BaseDoc, PAPER_PORTRAIT
|
||||
from ..menu import NumberOption, TextOption, EnumeratedListOption
|
||||
from ...constfunc import win
|
||||
from ...config import config
|
||||
from ...const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# set up logging
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
LOG = logging.getLogger(".treedoc")
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Private Constants
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
_DETAIL = [{'name': _("Full"), 'value': "full"},
|
||||
{'name': _("Medium"), 'value': "medium"},
|
||||
{'name': _("Short"), 'value': "short"}]
|
||||
|
||||
_MARRIAGE = [{'name': _("Default"), 'value': ""},
|
||||
{'name': _("Above"), 'value': "marriage above"},
|
||||
{'name': _("Below"), 'value': "marriage below"},
|
||||
{'name': _("Not shown"), 'value': "no marriage"}]
|
||||
|
||||
_COLOR = [{'name': _("None"), 'value': "none"},
|
||||
{'name': _("Default"), 'value': "default"},
|
||||
{'name': _("Preferences"), 'value': "preferences"}]
|
||||
|
||||
_TIMEFLOW = [{'name': _("Down (↓)"), 'value': ""},
|
||||
{'name': _("Up (↑)"), 'value': "up"},
|
||||
{'name': _("Right (→)"), 'value': "right"},
|
||||
{'name': _("Left (←)"), 'value': "left"}]
|
||||
|
||||
_EDGES = [{'name': _("Perpendicular"), 'value': ""},
|
||||
{'name': _("Rounded"), 'value': "rounded", },
|
||||
{'name': _("Swing"), 'value': "swing", },
|
||||
{'name': _("Mesh"), 'value': 'mesh'}]
|
||||
|
||||
_NOTELOC = [{'name': _("Top"), 'value': "t"},
|
||||
{'name': _("Bottom"), 'value': "b"}]
|
||||
|
||||
_NOTESIZE = [{'name': _("Tiny"), 'value': "tiny"},
|
||||
{'name': _("Script"), 'value': "scriptsize"},
|
||||
{'name': _("Footnote"), 'value': "footnotesize"},
|
||||
{'name': _("Small"), 'value': "small"},
|
||||
{'name': _("Normal"), 'value': "normalsize"},
|
||||
{'name': _("Large"), 'value': "large"},
|
||||
{'name': _("Very large"), 'value': "Large"},
|
||||
{'name': _("Extra large"), 'value': "LARGE"},
|
||||
{'name': _("Huge"), 'value': "huge"},
|
||||
{'name': _("Extra huge"), 'value': "Huge"}]
|
||||
|
||||
if win():
|
||||
_LATEX_FOUND = search_for("lualatex.exe")
|
||||
DETACHED_PROCESS = 8
|
||||
else:
|
||||
_LATEX_FOUND = search_for("lualatex")
|
||||
|
||||
def escape(text):
|
||||
lookup = {
|
||||
'&': '\\&',
|
||||
'%': '\\%',
|
||||
'$': '\\$',
|
||||
'#': '\\#',
|
||||
'_': '\\_',
|
||||
'{': '\\{',
|
||||
'}': '\\}',
|
||||
'~': '\\~{}',
|
||||
'^': '\\^{}',
|
||||
'\\': '\\textbackslash{}'
|
||||
}
|
||||
pattern = re.compile('|'.join([re.escape(key) for key in lookup.keys()]))
|
||||
return pattern.sub(lambda match: lookup[match.group(0)], text)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
#
|
||||
# TreeOptions
|
||||
#
|
||||
#------------------------------------------------------------------------------
|
||||
class TreeOptions:
|
||||
"""
|
||||
Defines all of the controls necessary
|
||||
to configure the genealogy tree reports.
|
||||
"""
|
||||
def add_menu_options(self, menu):
|
||||
"""
|
||||
Add all graph related options to the menu.
|
||||
|
||||
:param menu: The menu the options should be added to.
|
||||
:type menu: :class:`.Menu`
|
||||
:return: nothing
|
||||
"""
|
||||
################################
|
||||
category = _("Node Options")
|
||||
################################
|
||||
|
||||
detail = EnumeratedListOption(_("Node detail"), "full")
|
||||
for item in _DETAIL:
|
||||
detail.add_item(item["value"], item["name"])
|
||||
detail.set_help(_("Detail of information to be shown in a node."))
|
||||
menu.add_option(category, "detail", detail)
|
||||
|
||||
marriage = EnumeratedListOption(_("Marriage"), "")
|
||||
for item in _MARRIAGE:
|
||||
marriage.add_item(item["value"], item["name"])
|
||||
marriage.set_help(_("Position of marriage information."))
|
||||
menu.add_option(category, "marriage", marriage)
|
||||
|
||||
nodesize = NumberOption(_("Node size"), 25, 5, 100, 5)
|
||||
nodesize.set_help(_("One dimension of a node, in mm. If the timeflow "
|
||||
"is up or down then this is the width, otherwise "
|
||||
"it is the height."))
|
||||
menu.add_option(category, "nodesize", nodesize)
|
||||
|
||||
levelsize = NumberOption(_("Level size"), 35, 5, 100, 5)
|
||||
levelsize.set_help(_("One dimension of a node, in mm. If the timeflow "
|
||||
"is up or down then this is the height, otherwise "
|
||||
"it is the width."))
|
||||
menu.add_option(category, "levelsize", levelsize)
|
||||
|
||||
nodecolor = EnumeratedListOption(_("Color"), "none")
|
||||
for item in _COLOR:
|
||||
nodecolor.add_item(item["value"], item["name"])
|
||||
nodecolor.set_help(_("Node color."))
|
||||
menu.add_option(category, "nodecolor", nodecolor)
|
||||
|
||||
################################
|
||||
category = _("Tree Options")
|
||||
################################
|
||||
|
||||
timeflow = EnumeratedListOption(_("Timeflow"), "")
|
||||
for item in _TIMEFLOW:
|
||||
timeflow.add_item(item["value"], item["name"])
|
||||
timeflow.set_help(_("Direction that the graph will grow over time."))
|
||||
menu.add_option(category, "timeflow", timeflow)
|
||||
|
||||
edges = EnumeratedListOption(_("Edge style"), "")
|
||||
for item in _EDGES:
|
||||
edges.add_item(item["value"], item["name"])
|
||||
edges.set_help(_("Style of the edges between nodes."))
|
||||
menu.add_option(category, "edges", edges)
|
||||
|
||||
leveldist = NumberOption(_("Level distance"), 5, 1, 20, 1)
|
||||
leveldist.set_help(_("The minimum amount of free space, in mm, "
|
||||
"between levels. For vertical graphs, this "
|
||||
"corresponds to spacing between rows. For "
|
||||
"horizontal graphs, this corresponds to spacing "
|
||||
"between columns."))
|
||||
menu.add_option(category, "leveldist", leveldist)
|
||||
|
||||
################################
|
||||
category = _("Note")
|
||||
################################
|
||||
|
||||
note = TextOption(_("Note to add to the tree"), [""])
|
||||
note.set_help(_("This text will be added to the tree."))
|
||||
menu.add_option(category, "note", note)
|
||||
|
||||
noteloc = EnumeratedListOption(_("Note location"), 't')
|
||||
for item in _NOTELOC:
|
||||
noteloc.add_item(item["value"], item["name"])
|
||||
noteloc.set_help(_("Whether note will appear on top "
|
||||
"or bottom of the page."))
|
||||
menu.add_option(category, "noteloc", noteloc)
|
||||
|
||||
notesize = EnumeratedListOption(_("Note size"), 'normalsize')
|
||||
for item in _NOTESIZE:
|
||||
notesize.add_item(item["value"], item["name"])
|
||||
notesize.set_help(_("The size of note text."))
|
||||
menu.add_option(category, "notesize", notesize)
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
#
|
||||
# TreeDoc
|
||||
#
|
||||
#------------------------------------------------------------------------------
|
||||
class TreeDoc(metaclass=ABCMeta):
|
||||
"""
|
||||
Abstract Interface for genealogy tree document generators. Output formats
|
||||
for genealogy tree reports must implement this interface to be used by the
|
||||
report system.
|
||||
"""
|
||||
@abstractmethod
|
||||
def start_tree(self, option_list):
|
||||
"""
|
||||
Write the start of a tree.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def end_tree(self):
|
||||
"""
|
||||
Write the end of a tree.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def start_subgraph(self, level, subgraph_type, family, option_list=None):
|
||||
"""
|
||||
Write the start of a subgraph.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def end_subgraph(self, level):
|
||||
"""
|
||||
Write the end of a subgraph.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def write_node(self, db, level, node_type, person, marriage_flag,
|
||||
option_list=None):
|
||||
"""
|
||||
Write the contents of a node.
|
||||
"""
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
#
|
||||
# TreeDocBase
|
||||
#
|
||||
#------------------------------------------------------------------------------
|
||||
class TreeDocBase(BaseDoc, TreeDoc):
|
||||
"""
|
||||
Base document generator for all Graphviz document generators. Classes that
|
||||
inherit from this class will only need to implement the close function.
|
||||
The close function will generate the actual file of the appropriate type.
|
||||
"""
|
||||
def __init__(self, options, paper_style):
|
||||
BaseDoc.__init__(self, None, paper_style)
|
||||
|
||||
self._filename = None
|
||||
self._tex = StringIO()
|
||||
self._paper = paper_style
|
||||
|
||||
get_option = options.menu.get_option_by_name
|
||||
|
||||
self.detail = get_option('detail').get_value()
|
||||
self.marriage = get_option('marriage').get_value()
|
||||
self.nodesize = get_option('nodesize').get_value()
|
||||
self.levelsize = get_option('levelsize').get_value()
|
||||
self.nodecolor = get_option('nodecolor').get_value()
|
||||
|
||||
self.timeflow = get_option('timeflow').get_value()
|
||||
self.edges = get_option('edges').get_value()
|
||||
self.leveldist = get_option('leveldist').get_value()
|
||||
|
||||
self.note = get_option('note').get_value()
|
||||
self.noteloc = get_option('noteloc').get_value()
|
||||
self.notesize = get_option('notesize').get_value()
|
||||
|
||||
def write_start(self):
|
||||
"""
|
||||
Write the start of the document.
|
||||
"""
|
||||
paper_size = self._paper.get_size()
|
||||
name = paper_size.get_name().lower()
|
||||
if name == 'custom size':
|
||||
width = str(paper_size.get_width())
|
||||
height = str(paper_size.get_width())
|
||||
paper = 'papersize={%scm,%scm}' % (width, height)
|
||||
elif name in ('a', 'b', 'c', 'd', 'e'):
|
||||
paper = 'ansi' + name + 'paper'
|
||||
else:
|
||||
paper = name + 'paper'
|
||||
|
||||
if self._paper.get_orientation() == PAPER_PORTRAIT:
|
||||
orientation = 'portrait'
|
||||
else:
|
||||
orientation = 'landscape'
|
||||
|
||||
lmargin = self._paper.get_left_margin()
|
||||
rmargin = self._paper.get_right_margin()
|
||||
tmargin = self._paper.get_top_margin()
|
||||
bmargin = self._paper.get_bottom_margin()
|
||||
if lmargin == rmargin == tmargin == bmargin:
|
||||
margin = 'margin=%scm'% lmargin
|
||||
else:
|
||||
if lmargin == rmargin:
|
||||
margin = 'hmargin=%scm' % lmargin
|
||||
else:
|
||||
margin = 'hmargin={%scm,%scm}' % (lmargin, rmargin)
|
||||
if tmargin == bmargin:
|
||||
margin += ',vmargin=%scm' % tmargin
|
||||
else:
|
||||
margin += ',vmargin={%scm,%scm}' % (tmargin, bmargin)
|
||||
|
||||
self.write(0, '\\documentclass[%s]{article}\n' % orientation)
|
||||
|
||||
self.write(0, '\\IfFileExists{libertine.sty}{\n')
|
||||
self.write(0, ' \\usepackage{libertine}\n')
|
||||
self.write(0, '}{}\n')
|
||||
|
||||
self.write(0, '\\usepackage[%s,%s]{geometry}\n' % (paper, margin))
|
||||
self.write(0, '\\usepackage[all]{genealogytree}\n')
|
||||
self.write(0, '\\usepackage{color}\n')
|
||||
self.write(0, '\\begin{document}\n')
|
||||
|
||||
if self.nodecolor == 'preferences':
|
||||
scheme = config.get('colors.scheme')
|
||||
male_bg = config.get('colors.male-dead')[scheme][1:]
|
||||
female_bg = config.get('colors.female-dead')[scheme][1:]
|
||||
neuter_bg = config.get('colors.unknown-dead')[scheme][1:]
|
||||
self.write(0, '\\definecolor{male-bg}{HTML}{%s}\n' % male_bg)
|
||||
self.write(0, '\\definecolor{female-bg}{HTML}{%s}\n' % female_bg)
|
||||
self.write(0, '\\definecolor{neuter-bg}{HTML}{%s}\n' % neuter_bg)
|
||||
|
||||
if ''.join(self.note) != '' and self.noteloc == 't':
|
||||
for line in self.note:
|
||||
self.write(0, '{\\%s %s}\\par\n' % (self.notesize, line))
|
||||
self.write(0, '\\bigskip\n')
|
||||
|
||||
self.write(0, '\\begin{tikzpicture}\n')
|
||||
|
||||
def start_tree(self, option_list):
|
||||
self.write(0, '\\genealogytree[\n')
|
||||
self.write(0, 'processing=database,\n')
|
||||
if self.marriage:
|
||||
info = self.detail + ' ' + self.marriage
|
||||
else:
|
||||
info = self.detail
|
||||
self.write(0, 'database format=%s,\n' % info)
|
||||
if self.timeflow:
|
||||
self.write(0, 'timeflow=%s,\n' % self.timeflow)
|
||||
if self.edges:
|
||||
self.write(0, 'edges=%s,\n' % self.edges)
|
||||
if self.leveldist != 5:
|
||||
self.write(0, 'level distance=%smm,\n' % self.leveldist)
|
||||
if self.nodesize != 25:
|
||||
self.write(0, 'node size=%smm,\n' % self.nodesize)
|
||||
if self.levelsize != 35:
|
||||
self.write(0, 'level size=%smm,\n' % self.levelsize)
|
||||
if self.nodecolor == 'none':
|
||||
self.write(0, 'tcbset={male/.style={},\n')
|
||||
self.write(0, ' female/.style={},\n')
|
||||
self.write(0, ' neuter/.style={}},\n')
|
||||
if self.nodecolor == 'preferences':
|
||||
self.write(0, 'tcbset={male/.style={colback=male-bg},\n')
|
||||
self.write(0, ' female/.style={colback=female-bg},\n')
|
||||
self.write(0, ' neuter/.style={colback=neuter-bg}},\n')
|
||||
|
||||
for option in option_list:
|
||||
self.write(0, '%s,\n' % option)
|
||||
|
||||
self.write(0, ']{\n')
|
||||
|
||||
def end_tree(self):
|
||||
self.write(0, '}\n')
|
||||
|
||||
def write_end(self):
|
||||
"""
|
||||
Write the end of the document.
|
||||
"""
|
||||
self.write(0, '\\end{tikzpicture}\n')
|
||||
|
||||
if ''.join(self.note) != '' and self.noteloc == 'b':
|
||||
self.write(0, '\\bigskip\n')
|
||||
for line in self.note:
|
||||
self.write(0, '\\par{\\%s %s}\n' % (self.notesize, line))
|
||||
|
||||
self.write(0, '\\end{document}\n')
|
||||
|
||||
def start_subgraph(self, level, subgraph_type, family, option_list=None):
|
||||
options = ['id=%s' % family.gramps_id]
|
||||
if option_list:
|
||||
options.extend(option_list)
|
||||
if subgraph_type == 'sandclock':
|
||||
self.write(level, 'sandclock{\n')
|
||||
else:
|
||||
self.write(level, '%s[%s]{\n' % (subgraph_type, ','.join(options)))
|
||||
|
||||
def end_subgraph(self, level):
|
||||
self.write(level, '}\n')
|
||||
|
||||
def write_node(self, db, level, node_type, person, marriage_flag,
|
||||
option_list=None):
|
||||
options = ['id=%s' % person.gramps_id]
|
||||
if option_list:
|
||||
options.extend(option_list)
|
||||
self.write(level, '%s[%s]{\n' % (node_type, ','.join(options)))
|
||||
if person.gender == Person.MALE:
|
||||
self.write(level+1, 'male,\n')
|
||||
elif person.gender == Person.FEMALE:
|
||||
self.write(level+1, 'female,\n')
|
||||
elif person.gender == Person.UNKNOWN:
|
||||
self.write(level+1, 'neuter,\n')
|
||||
name = person.get_primary_name()
|
||||
nick = name.get_nick_name()
|
||||
surn = name.get_surname()
|
||||
name_parts = [self.format_given_names(name),
|
||||
'\\nick{{{}}}'.format(escape(nick)) if nick else '',
|
||||
'\\surn{{{}}}'.format(escape(surn)) if surn else '']
|
||||
self.write(level+1, 'name = {{{}}},\n'.format(
|
||||
' '.join([e for e in name_parts if e])))
|
||||
for eventref in person.get_event_ref_list():
|
||||
if eventref.role == EventRoleType.PRIMARY:
|
||||
event = db.get_event_from_handle(eventref.ref)
|
||||
self.write_event(db, level+1, event)
|
||||
if marriage_flag:
|
||||
for handle in person.get_family_handle_list():
|
||||
family = db.get_family_from_handle(handle)
|
||||
for eventref in family.get_event_ref_list():
|
||||
if eventref.role == EventRoleType.FAMILY:
|
||||
event = db.get_event_from_handle(eventref.ref)
|
||||
self.write_event(db, level+1, event)
|
||||
for attr in person.get_attribute_list():
|
||||
if str(attr.get_type()) == 'Occupation':
|
||||
self.write(level+1, 'profession = {%s},\n' %
|
||||
escape(attr.get_value()))
|
||||
if str(attr.get_type()) == 'Comment':
|
||||
self.write(level+1, 'comment = {%s},\n' %
|
||||
escape(attr.get_value()))
|
||||
for mediaref in person.get_media_list():
|
||||
media = db.get_media_from_handle(mediaref.ref)
|
||||
path = media_path_full(db, media.get_path())
|
||||
if os.path.isfile(path):
|
||||
if win():
|
||||
path = path.replace('\\', '/')
|
||||
self.write(level+1, 'image = {%s},\n' % path)
|
||||
break # first image only
|
||||
self.write(level, '}\n')
|
||||
|
||||
def write_event(self, db, level, event):
|
||||
"""
|
||||
Write an event.
|
||||
"""
|
||||
modifier = None
|
||||
if event.type == EventType.BIRTH:
|
||||
event_type = 'birth'
|
||||
if 'died' in event.description.lower():
|
||||
modifier = 'died'
|
||||
if 'stillborn' in event.description.lower():
|
||||
modifier = 'stillborn'
|
||||
# modifier = 'out of wedlock'
|
||||
elif event.type == EventType.BAPTISM:
|
||||
event_type = 'baptism'
|
||||
elif event.type == EventType.ENGAGEMENT:
|
||||
event_type = 'engagement'
|
||||
elif event.type == EventType.MARRIAGE:
|
||||
event_type = 'marriage'
|
||||
elif event.type == EventType.DIVORCE:
|
||||
event_type = 'divorce'
|
||||
elif event.type == EventType.DEATH:
|
||||
event_type = 'death'
|
||||
elif event.type == EventType.BURIAL:
|
||||
event_type = 'burial'
|
||||
if 'killed' in event.description.lower():
|
||||
modifier = 'killed'
|
||||
elif event.type == EventType.CREMATION:
|
||||
event_type = 'burial'
|
||||
modifier = 'cremated'
|
||||
else:
|
||||
return
|
||||
|
||||
date = event.get_date_object()
|
||||
|
||||
if date.get_calendar() == Date.CAL_GREGORIAN:
|
||||
calendar = 'AD' # GR
|
||||
elif date.get_calendar() == Date.CAL_JULIAN:
|
||||
calendar = 'JU'
|
||||
else:
|
||||
calendar = ''
|
||||
|
||||
if date.get_modifier() == Date.MOD_ABOUT:
|
||||
calendar = 'ca' + calendar
|
||||
|
||||
date_str = self.format_iso(date.get_ymd(), calendar)
|
||||
if date.get_modifier() == Date.MOD_BEFORE:
|
||||
date_str = '/' + date_str
|
||||
elif date.get_modifier() == Date.MOD_AFTER:
|
||||
date_str = date_str + '/'
|
||||
elif date.is_compound():
|
||||
stop_date = self.format_iso(date.get_stop_ymd(), calendar)
|
||||
date_str = date_str + '/' + stop_date
|
||||
|
||||
place = escape(_pd.display_event(db, event))
|
||||
|
||||
if modifier:
|
||||
event_type += '+'
|
||||
self.write(level, '%s = {%s}{%s}{%s},\n' %
|
||||
(event_type, date_str, place, modifier))
|
||||
elif place == '':
|
||||
event_type += '-'
|
||||
self.write(level, '%s = {%s},\n' % (event_type, date_str))
|
||||
else:
|
||||
self.write(level, '%s = {%s}{%s},\n' %
|
||||
(event_type, date_str, place))
|
||||
|
||||
def format_given_names(self, name):
|
||||
"""
|
||||
Format given names.
|
||||
"""
|
||||
first = name.get_first_name()
|
||||
call = name.get_call_name()
|
||||
if call:
|
||||
if call in first:
|
||||
where = first.index(call)
|
||||
return '{before}\\pref{{{call}}}{after}'.format(
|
||||
before=escape(first[:where]),
|
||||
call=escape(call),
|
||||
after=escape(first[where+len(call):]))
|
||||
else:
|
||||
# ignore erroneous call name
|
||||
return escape(first)
|
||||
else:
|
||||
return escape(first)
|
||||
|
||||
def format_iso(self, date_tuple, calendar):
|
||||
"""
|
||||
Format an iso date.
|
||||
"""
|
||||
year, month, day = date_tuple
|
||||
if year == 0:
|
||||
iso_date = ''
|
||||
elif month == 0:
|
||||
iso_date = str(year)
|
||||
elif day == 0:
|
||||
iso_date = '%s-%s' % (year, month)
|
||||
else:
|
||||
iso_date = '%s-%s-%s' % (year, month, day)
|
||||
if calendar and calendar != 'AD':
|
||||
iso_date = '(%s)%s' % (calendar, iso_date)
|
||||
return iso_date
|
||||
|
||||
def write(self, level, text):
|
||||
"""
|
||||
Write indented text.
|
||||
"""
|
||||
self._tex.write(' '*level + text)
|
||||
|
||||
def open(self, filename):
|
||||
""" Implement TreeDocBase.open() """
|
||||
self._filename = os.path.normpath(os.path.abspath(filename))
|
||||
self.write_start()
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
This isn't useful by itself. Other classes need to override this and
|
||||
actually generate a file.
|
||||
"""
|
||||
self.write_end()
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
#
|
||||
# TreeTexDoc
|
||||
#
|
||||
#------------------------------------------------------------------------------
|
||||
class TreeTexDoc(TreeDocBase):
|
||||
"""
|
||||
TreeTexDoc implementation that generates a .tex file.
|
||||
"""
|
||||
|
||||
def close(self):
|
||||
""" Implements TreeDocBase.close() """
|
||||
TreeDocBase.close(self)
|
||||
|
||||
# Make sure the extension is correct
|
||||
if self._filename[-4:] != ".tex":
|
||||
self._filename += ".tex"
|
||||
|
||||
with open(self._filename, 'w', encoding='utf-8') as texfile:
|
||||
texfile.write(self._tex.getvalue())
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
#
|
||||
# TreePdfDoc
|
||||
#
|
||||
#------------------------------------------------------------------------------
|
||||
class TreePdfDoc(TreeDocBase):
|
||||
"""
|
||||
TreePdfDoc implementation that generates a .pdf file.
|
||||
"""
|
||||
|
||||
def close(self):
|
||||
""" Implements TreeDocBase.close() """
|
||||
TreeDocBase.close(self)
|
||||
|
||||
# Make sure the extension is correct
|
||||
if self._filename[-4:] != ".pdf":
|
||||
self._filename += ".pdf"
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
basename = os.path.basename(self._filename)
|
||||
args = ['lualatex', '-output-directory', tmpdir,
|
||||
'-jobname', basename[:-4]]
|
||||
if win():
|
||||
proc = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE,
|
||||
creationflags=DETACHED_PROCESS)
|
||||
else:
|
||||
proc = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE)
|
||||
proc.communicate(input=self._tex.getvalue().encode('utf-8'))
|
||||
shutil.copy(os.path.join(tmpdir, basename), self._filename)
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
#
|
||||
# Various Genealogy Tree formats.
|
||||
#
|
||||
#------------------------------------------------------------------------------
|
||||
FORMATS = []
|
||||
|
||||
if _LATEX_FOUND:
|
||||
FORMATS += [{'type' : "pdf",
|
||||
'ext' : "pdf",
|
||||
'descr': _("PDF"),
|
||||
'mime' : "application/pdf",
|
||||
'class': TreePdfDoc}]
|
||||
|
||||
FORMATS += [{'type' : "tex",
|
||||
'ext' : "tex",
|
||||
'descr': _("LaTeX File"),
|
||||
'mime' : "application/x-latex",
|
||||
'class': TreeTexDoc}]
|
||||
@@ -495,14 +495,14 @@ class BookList:
|
||||
"""
|
||||
Saves the current BookList to the associated file.
|
||||
"""
|
||||
with open(self.file, "w") as b_f:
|
||||
with open(self.file, "w", encoding="utf-8") as b_f:
|
||||
b_f.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n")
|
||||
b_f.write('<booklist>\n')
|
||||
for name in sorted(self.bookmap): # enable a diff of archived copies
|
||||
book = self.get_book(name)
|
||||
dbname = book.get_dbname()
|
||||
dbname = escape(book.get_dbname())
|
||||
b_f.write(' <book name="%s" database="%s">'
|
||||
'\n' % (name, dbname))
|
||||
'\n' % (escape(name), dbname))
|
||||
for item in book.get_item_list():
|
||||
b_f.write(' <item name="%s" '
|
||||
'trans_name="%s">\n' % (
|
||||
@@ -566,7 +566,7 @@ class BookList:
|
||||
'\n' % book.get_format_name())
|
||||
if book.get_output():
|
||||
b_f.write(' <output name="%s"/>'
|
||||
'\n' % book.get_output())
|
||||
'\n' % escape(book.get_output()))
|
||||
b_f.write(' </book>\n')
|
||||
|
||||
b_f.write('</booklist>\n')
|
||||
@@ -578,8 +578,15 @@ class BookList:
|
||||
try:
|
||||
parser = make_parser()
|
||||
parser.setContentHandler(BookParser(self, self.dbase))
|
||||
with open(self.file) as the_file:
|
||||
parser.parse(the_file)
|
||||
# bug 10387; XML should be utf8, but was not previously saved
|
||||
# that way. So try to read utf8, if fails, try with system
|
||||
# encoding. Only an issue on non-utf8 systems.
|
||||
try:
|
||||
with open(self.file, encoding="utf-8") as the_file:
|
||||
parser.parse(the_file)
|
||||
except UnicodeDecodeError:
|
||||
with open(self.file) as the_file:
|
||||
parser.parse(the_file)
|
||||
except (IOError, OSError, ValueError, SAXParseException, KeyError,
|
||||
AttributeError):
|
||||
LOG.debug("Failed to parse book list", exc_info=True)
|
||||
|
||||
@@ -39,7 +39,7 @@ import os
|
||||
|
||||
# Report categories
|
||||
from .. import (CATEGORY_TEXT, CATEGORY_DRAW, CATEGORY_CODE, CATEGORY_WEB,
|
||||
CATEGORY_BOOK, CATEGORY_GRAPHVIZ)
|
||||
CATEGORY_BOOK, CATEGORY_GRAPHVIZ, CATEGORY_TREE)
|
||||
|
||||
standalone_categories = {
|
||||
CATEGORY_TEXT : ("RepText", _("Text Reports")),
|
||||
@@ -48,6 +48,7 @@ standalone_categories = {
|
||||
CATEGORY_WEB : ("RepWeb", _("Web Pages")),
|
||||
CATEGORY_BOOK : ("RepBook", _("Books")),
|
||||
CATEGORY_GRAPHVIZ : ("Graphs", _("Graphs")),
|
||||
CATEGORY_TREE : ("Trees", _("Trees")),
|
||||
}
|
||||
book_categories = {
|
||||
CATEGORY_TEXT : _("Text"),
|
||||
|
||||
@@ -32,6 +32,7 @@ from ...config import config
|
||||
from ...datehandler import get_date_formats, LANG_TO_DISPLAY, main_locale
|
||||
from ...display.name import displayer as global_name_display
|
||||
from ...lib.date import Today
|
||||
from ...display.place import displayer as _pd
|
||||
from ..menu import EnumeratedListOption, BooleanOption, NumberOption
|
||||
from ...proxy import PrivateProxyDb, LivingProxyDb
|
||||
from ...utils.grampslocale import GrampsLocale
|
||||
@@ -327,3 +328,16 @@ def add_gramps_id_option(menu, category, ownline=False):
|
||||
include_id.add_item(1, _('Include'))
|
||||
include_id.set_help(_("Whether to include Gramps IDs"))
|
||||
menu.add_option(category, 'inc_id', include_id)
|
||||
|
||||
def add_place_format_option(menu, category):
|
||||
"""
|
||||
Insert an option for changing the report's place format to a
|
||||
report-specific format instead of the user's Edit=>Preferences choice
|
||||
"""
|
||||
place_format = EnumeratedListOption(_("Place format"), None)
|
||||
place_format.add_item(None, _("Default"))
|
||||
for number, fmt in enumerate(_pd.get_formats()):
|
||||
place_format.add_item(number, fmt.name)
|
||||
place_format.set_help(_("Select the format to display places"))
|
||||
menu.add_option(category, "place_format", place_format)
|
||||
return place_format
|
||||
|
||||
@@ -31,6 +31,7 @@ import csv
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from .tabbeddoc import *
|
||||
from ...constfunc import win
|
||||
|
||||
class CSVTab(TabbedDoc):
|
||||
|
||||
@@ -48,7 +49,8 @@ class CSVTab(TabbedDoc):
|
||||
else:
|
||||
self.filename = filename
|
||||
|
||||
self.f = open(self.filename, "w")
|
||||
self.f = open(self.filename, "w",
|
||||
encoding='utf_8_sig' if win() else 'utf_8')
|
||||
self.writer = csv.writer(self.f)
|
||||
|
||||
def close(self):
|
||||
|
||||
+148
-99
@@ -53,6 +53,7 @@ from gramps.gen.const import HOME_DIR, URL_WIKISTRING
|
||||
from gramps.gen.datehandler import get_date_formats
|
||||
from gramps.gen.display.name import displayer as _nd
|
||||
from gramps.gen.display.name import NameDisplayError
|
||||
from gramps.gen.display.place import displayer as _pd
|
||||
from gramps.gen.utils.alive import update_constants
|
||||
from gramps.gen.utils.file import media_path
|
||||
from gramps.gen.utils.keyword import (get_keywords, get_translation_from_keyword,
|
||||
@@ -62,6 +63,7 @@ from gramps.gen.lib import Name, Surname, NameOriginType
|
||||
from .managedwindow import ManagedWindow
|
||||
from .widgets import MarkupLabel, BasicLabel
|
||||
from .dialog import ErrorDialog, QuestionDialog2, OkDialog
|
||||
from .editors.editplaceformat import EditPlaceFormat
|
||||
from .glade import Glade
|
||||
from gramps.gen.plug.utils import available_updates
|
||||
from .plug import PluginWindows
|
||||
@@ -249,13 +251,23 @@ class ConfigureDialog(ManagedWindow):
|
||||
"""
|
||||
self.__config.set(constant, obj.get_text())
|
||||
|
||||
def update_color(self, obj, constant, color_hex_label):
|
||||
def update_color(self, obj, pspec, constant, color_hex_label):
|
||||
"""
|
||||
Called on changing some color.
|
||||
Either on programmatically color change.
|
||||
"""
|
||||
rgba = obj.get_rgba()
|
||||
hexval = "#%02x%02x%02x" % (int(rgba.red * 255),
|
||||
int(rgba.green * 255),
|
||||
int(rgba.blue * 255))
|
||||
color_hex_label.set_text(hexval)
|
||||
self.__config.set(constant, hexval)
|
||||
colors = self.__config.get(constant)
|
||||
if isinstance(colors, list):
|
||||
scheme = self.__config.get('colors.scheme')
|
||||
colors[scheme] = hexval
|
||||
self.__config.set(constant, colors)
|
||||
else:
|
||||
self.__config.set(constant, hexval)
|
||||
|
||||
def update_checkbox(self, obj, constant, config=None):
|
||||
if not config:
|
||||
@@ -383,15 +395,24 @@ class ConfigureDialog(ManagedWindow):
|
||||
grid.attach(entry, col_attach+1, index, 1, 1)
|
||||
|
||||
def add_color(self, grid, label, index, constant, config=None, col=0):
|
||||
"""
|
||||
Add color chooser widget with label to the grid.
|
||||
"""
|
||||
if not config:
|
||||
config = self.__config
|
||||
lwidget = BasicLabel(_("%s: ") % label) # needed for French, else ignore
|
||||
hexval = config.get(constant)
|
||||
colors = config.get(constant)
|
||||
if isinstance(colors, list):
|
||||
scheme = config.get('colors.scheme')
|
||||
hexval = colors[scheme]
|
||||
else:
|
||||
hexval = colors
|
||||
color = Gdk.color_parse(hexval)
|
||||
entry = Gtk.ColorButton(color=color)
|
||||
color_hex_label = BasicLabel(hexval)
|
||||
color_hex_label.set_hexpand(True)
|
||||
entry.connect('color-set', self.update_color, constant, color_hex_label)
|
||||
entry.connect('notify::color', self.update_color, constant,
|
||||
color_hex_label)
|
||||
grid.attach(lwidget, col, index, 1, 1)
|
||||
grid.attach(entry, col+1, index, 1, 1)
|
||||
grid.attach(color_hex_label, col+2, index, 1, 1)
|
||||
@@ -492,7 +513,6 @@ class GrampsPreferences(ConfigureDialog):
|
||||
self.add_behavior_panel,
|
||||
self.add_famtree_panel,
|
||||
self.add_formats_panel,
|
||||
self.add_places_panel,
|
||||
self.add_text_panel,
|
||||
self.add_prefix_panel,
|
||||
self.add_date_panel,
|
||||
@@ -554,7 +574,7 @@ class GrampsPreferences(ConfigureDialog):
|
||||
|
||||
def add_color_panel(self, configdialog):
|
||||
"""
|
||||
Add the tab to set defaults colors for graph boxes
|
||||
Add the tab to set defaults colors for graph boxes.
|
||||
"""
|
||||
grid = Gtk.Grid()
|
||||
grid.set_border_width(12)
|
||||
@@ -562,40 +582,62 @@ class GrampsPreferences(ConfigureDialog):
|
||||
grid.set_row_spacing(6)
|
||||
self.add_text(grid, _('Set the colors used for boxes in the graphical views'),
|
||||
0, line_wrap=False)
|
||||
self.add_color(grid, _('Gender Male Alive'), 1,
|
||||
'preferences.color-gender-male-alive')
|
||||
self.add_color(grid, _('Border Male Alive'), 2,
|
||||
'preferences.bordercolor-gender-male-alive')
|
||||
self.add_color(grid, _('Gender Male Death'), 3,
|
||||
'preferences.color-gender-male-death')
|
||||
self.add_color(grid, _('Border Male Death'), 4,
|
||||
'preferences.bordercolor-gender-male-death')
|
||||
self.add_color(grid, _('Gender Female Alive'), 1,
|
||||
'preferences.color-gender-female-alive', col=4)
|
||||
self.add_color(grid, _('Border Female Alive'), 2,
|
||||
'preferences.bordercolor-gender-female-alive', col=4)
|
||||
self.add_color(grid, _('Gender Female Death'), 3,
|
||||
'preferences.color-gender-female-death', col=4)
|
||||
self.add_color(grid, _('Border Female Death'), 4,
|
||||
'preferences.bordercolor-gender-female-death', col=4)
|
||||
## self.add_color(grid, _('Gender Other Alive'), 5,
|
||||
## 'preferences.color-gender-other-alive')
|
||||
## self.add_color(grid, _('Border Other Alive'), 6,
|
||||
## 'preferences.bordercolor-gender-other-alive')
|
||||
## self.add_color(grid, _('Gender Other Death'), 7,
|
||||
## 'preferences.color-gender-other-death')
|
||||
## self.add_color(grid, _('Border Other Death'), 8,
|
||||
## 'preferences.bordercolor-gender-other-death')
|
||||
self.add_color(grid, _('Gender Unknown Alive'), 5,
|
||||
'preferences.color-gender-unknown-alive', col=4)
|
||||
self.add_color(grid, _('Border Unknown Alive'), 6,
|
||||
'preferences.bordercolor-gender-unknown-alive', col=4)
|
||||
self.add_color(grid, _('Gender Unknown Death'), 7,
|
||||
'preferences.color-gender-unknown-death', col=4)
|
||||
self.add_color(grid, _('Border Unknown Death'), 8,
|
||||
'preferences.bordercolor-gender-unknown-death', col=4)
|
||||
|
||||
hbox = Gtk.Box(spacing=12)
|
||||
self.color_scheme_box = Gtk.ComboBoxText()
|
||||
formats = [_("Light colors"),
|
||||
_("Dark colors"),]
|
||||
list(map(self.color_scheme_box.append_text, formats))
|
||||
scheme = config.get('colors.scheme')
|
||||
self.color_scheme_box.set_active(scheme)
|
||||
self.color_scheme_box.connect('changed', self.color_scheme_changed)
|
||||
lwidget = BasicLabel(_("%s: ") % _('Color scheme'))
|
||||
hbox.pack_start(lwidget, False, False, 0)
|
||||
hbox.pack_start(self.color_scheme_box, False, False, 0)
|
||||
|
||||
restore_btn = Gtk.Button(_('Restore to defaults'))
|
||||
restore_btn.connect('clicked', self.restore_colors)
|
||||
hbox.pack_start(restore_btn, False, False, 0)
|
||||
grid.attach(hbox, 1, 1, 6, 1)
|
||||
|
||||
color_list = [
|
||||
(_('Male Alive'), 'male-alive', 2, 0),
|
||||
(_('Male Dead'), 'male-dead', 4, 0),
|
||||
(_('Female Alive'), 'female-alive', 2, 4),
|
||||
(_('Female Dead'), 'female-dead', 4, 4),
|
||||
(_('Unknown Alive'), 'unknown-alive', 6, 4),
|
||||
(_('Unknown Dead'), 'unknown-dead', 8, 4),
|
||||
(_('Family Node'), 'family', 7, 0),
|
||||
(_('Family Divorced'), 'family-divorced', 9, 0),
|
||||
(_('Home Person'), 'home-person', 6, 0),
|
||||
(_('Border Male Alive'), 'border-male-alive', 3, 0),
|
||||
(_('Border Male Dead'), 'border-male-dead', 5, 0),
|
||||
(_('Border Female Alive'), 'border-female-alive', 3, 4),
|
||||
(_('Border Female Dead'), 'border-female-dead', 5, 4),
|
||||
(_('Border Unknown Alive'), 'border-unknown-alive', 7, 4),
|
||||
(_('Border Unknown Dead'), 'border-unknown-dead', 9, 4),
|
||||
(_('Border Family'), 'border-family', 8, 0),
|
||||
(_('Border Family Divorced'), 'border-family-divorced', 10, 0),
|
||||
]
|
||||
|
||||
self.colors = {}
|
||||
for color in color_list:
|
||||
pref_name = 'colors.' + color[1]
|
||||
self.colors[pref_name] = self.add_color(grid, color[0], color[2],
|
||||
pref_name, col=color[3])
|
||||
return _('Colors'), grid
|
||||
|
||||
def restore_colors(self, widget=None):
|
||||
"""
|
||||
Restore colors of selected scheme to default.
|
||||
"""
|
||||
scheme = config.get('colors.scheme')
|
||||
for key, widget in self.colors.items():
|
||||
color = Gdk.RGBA()
|
||||
hexval = config.get_default(key)[scheme]
|
||||
Gdk.RGBA.parse(color, hexval)
|
||||
widget.set_rgba(color)
|
||||
|
||||
def add_advanced_panel(self, configdialog):
|
||||
grid = Gtk.Grid()
|
||||
grid.set_border_width(12)
|
||||
@@ -890,6 +932,13 @@ class GrampsPreferences(ConfigureDialog):
|
||||
_nd.set_default_format(new_idx)
|
||||
self.uistate.emit('nameformat-changed')
|
||||
|
||||
def cb_place_fmt_changed(self, obj):
|
||||
"""
|
||||
Called when the place format is changed.
|
||||
"""
|
||||
config.set('preferences.place-format', obj.get_active())
|
||||
self.uistate.emit('placeformat-changed')
|
||||
|
||||
def cb_pa_sur_changed(self,*args):
|
||||
"""
|
||||
checkbox patronymic as surname changed, propagate to namedisplayer
|
||||
@@ -1011,6 +1060,34 @@ class GrampsPreferences(ConfigureDialog):
|
||||
grid.attach(obox, 1, row, 2, 1)
|
||||
row += 1
|
||||
|
||||
# Place format:
|
||||
self.pformat = Gtk.ComboBox()
|
||||
renderer = Gtk.CellRendererText()
|
||||
self.pformat.pack_start(renderer, True)
|
||||
self.pformat.add_attribute(renderer, "text", 0)
|
||||
self.cb_place_fmt_rebuild()
|
||||
active = config.get('preferences.place-format')
|
||||
self.pformat.set_active(active)
|
||||
self.pformat.connect('changed', self.cb_place_fmt_changed)
|
||||
lwidget = BasicLabel(_("%s: ") % _('Place format'))
|
||||
lwidget.set_use_underline(True)
|
||||
lwidget.set_mnemonic_widget(obox)
|
||||
hbox = Gtk.Box()
|
||||
self.fmt_btn = Gtk.Button(label=("%s..." % _('Edit')))
|
||||
self.fmt_btn.connect('clicked', self.cb_place_fmt_dialog)
|
||||
hbox.pack_start(self.pformat, True, True, 0)
|
||||
hbox.pack_start(self.fmt_btn, False, False, 0)
|
||||
grid.attach(lwidget, 0, row, 1, 1)
|
||||
grid.attach(hbox, 1, row, 2, 1)
|
||||
row += 1
|
||||
|
||||
auto = self.add_checkbox(grid,
|
||||
_("Enable automatic place title generation"),
|
||||
row, 'preferences.place-auto',
|
||||
extra_callback=self.auto_title_changed)
|
||||
self.auto_title_changed(auto)
|
||||
row += 1
|
||||
|
||||
# Age precision:
|
||||
# precision=1 for "year", 2: "year, month" or 3: "year, month, days"
|
||||
obox = Gtk.ComboBoxText()
|
||||
@@ -1110,66 +1187,13 @@ class GrampsPreferences(ConfigureDialog):
|
||||
row += 1
|
||||
return _('Display'), grid
|
||||
|
||||
def add_places_panel(self, configdialog):
|
||||
grid = Gtk.Grid()
|
||||
grid.set_border_width(12)
|
||||
grid.set_column_spacing(6)
|
||||
grid.set_row_spacing(6)
|
||||
|
||||
auto = self.add_checkbox(grid,
|
||||
_("Enable automatic place title generation"),
|
||||
0, 'preferences.place-auto',
|
||||
extra_callback=self.auto_title_changed)
|
||||
|
||||
row = 0
|
||||
grid2 = Gtk.Grid()
|
||||
grid2.set_border_width(12)
|
||||
grid2.set_column_spacing(6)
|
||||
grid2.set_row_spacing(6)
|
||||
grid.attach(grid2, 1, 1, 1, 1)
|
||||
|
||||
self.place_widgets = []
|
||||
cbox = self.add_checkbox(grid2, _("Suppress comma after house number"),
|
||||
row, 'preferences.place-number', start=0)
|
||||
self.place_widgets.append(cbox)
|
||||
row += 1
|
||||
|
||||
cbox = self.add_checkbox(grid2, _("Reverse display order"),
|
||||
row, 'preferences.place-reverse', start=0)
|
||||
self.place_widgets.append(cbox)
|
||||
row += 1
|
||||
|
||||
# Place restriction
|
||||
obox = Gtk.ComboBoxText()
|
||||
formats = [_("Full place name"),
|
||||
_("-> Hamlet/Village/Town/City"),
|
||||
_("Hamlet/Village/Town/City ->")]
|
||||
list(map(obox.append_text, formats))
|
||||
active = config.get('preferences.place-restrict')
|
||||
obox.set_active(active)
|
||||
obox.connect('changed', self.place_restrict_changed)
|
||||
lwidget = BasicLabel(_("%s: ") % _('Restrict'))
|
||||
grid2.attach(lwidget, 0, row, 1, 1)
|
||||
grid2.attach(obox, 1, row, 2, 1)
|
||||
self.place_widgets.append(obox)
|
||||
row += 1
|
||||
|
||||
entry = self.add_entry(grid2, _("Language"),
|
||||
row, 'preferences.place-lang')
|
||||
self.place_widgets.append(entry)
|
||||
row += 1
|
||||
|
||||
self.auto_title_changed(auto)
|
||||
|
||||
return _('Places'), grid
|
||||
|
||||
def auto_title_changed(self, obj):
|
||||
"""
|
||||
Update sensitivity of place configuration widgets.
|
||||
Update sensitivity of place format widget.
|
||||
"""
|
||||
active = obj.get_active()
|
||||
for widget in self.place_widgets:
|
||||
widget.set_sensitive(active)
|
||||
active = config.get('preferences.place-auto')
|
||||
self.pformat.set_sensitive(active)
|
||||
self.fmt_btn.set_sensitive(active)
|
||||
|
||||
def add_text_panel(self, configdialog):
|
||||
row = 0
|
||||
@@ -1205,6 +1229,35 @@ class GrampsPreferences(ConfigureDialog):
|
||||
self.old_format = the_list.get_value(the_iter, COL_FMT)
|
||||
win = DisplayNameEditor(self.uistate, self.dbstate, self.track, self)
|
||||
|
||||
def color_scheme_changed(self, obj):
|
||||
"""
|
||||
Called on swiching color scheme.
|
||||
"""
|
||||
scheme = obj.get_active()
|
||||
config.set('colors.scheme', scheme)
|
||||
for key, widget in self.colors.items():
|
||||
color = Gdk.RGBA()
|
||||
hexval = config.get(key)[scheme]
|
||||
Gdk.RGBA.parse(color, hexval)
|
||||
widget.set_rgba(color)
|
||||
|
||||
def cb_place_fmt_dialog(self, button):
|
||||
"""
|
||||
Called to invoke the place format editor.
|
||||
"""
|
||||
EditPlaceFormat(self.uistate, self.dbstate, self.track,
|
||||
self.cb_place_fmt_rebuild)
|
||||
|
||||
def cb_place_fmt_rebuild(self):
|
||||
"""
|
||||
Called to rebuild the place format list.
|
||||
"""
|
||||
model = Gtk.ListStore(str)
|
||||
for fmt in _pd.get_formats():
|
||||
model.append([fmt.name])
|
||||
self.pformat.set_model(model)
|
||||
self.pformat.set_active(0)
|
||||
|
||||
def check_for_type_changed(self, obj):
|
||||
active = obj.get_active()
|
||||
if active == 0: # update
|
||||
@@ -1228,10 +1281,6 @@ class GrampsPreferences(ConfigureDialog):
|
||||
active = obj.get_active()
|
||||
config.set('behavior.check-for-addon-updates', active)
|
||||
|
||||
def place_restrict_changed(self, obj):
|
||||
active = obj.get_active()
|
||||
config.set('preferences.place-restrict', active)
|
||||
|
||||
def date_format_changed(self, obj):
|
||||
config.set('preferences.date-format', obj.get_active())
|
||||
OkDialog(_('Change is not immediate'),
|
||||
|
||||
@@ -35,6 +35,7 @@ _LOG = logging.getLogger(".dialog")
|
||||
#-------------------------------------------------------------------------
|
||||
from gi.repository import GObject
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import Gdk
|
||||
from gi.repository import GdkPixbuf
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@@ -46,6 +47,7 @@ from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
from gramps.gen.const import ICON, URL_BUGHOME
|
||||
from gramps.gen.config import config
|
||||
from gramps.gen.constfunc import is_quartz
|
||||
from .glade import Glade
|
||||
from .display import display_url
|
||||
|
||||
@@ -506,6 +508,12 @@ def main(args):
|
||||
win = Gtk.Window()
|
||||
win.set_title('Dialog test window')
|
||||
win.set_position(Gtk.WindowPosition.CENTER)
|
||||
#Set the mnemonic modifier on Macs to alt-ctrl so that it
|
||||
#doesn't interfere with the extended keyboard, see
|
||||
#https://gramps-project.org/bugs/view.php?id=6943
|
||||
if is_quartz():
|
||||
win.set_mnemonic_modifier(
|
||||
Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.MOD1_MASK)
|
||||
def cb(window, event):
|
||||
Gtk.main_quit()
|
||||
win.connect('delete-event', cb)
|
||||
|
||||
@@ -372,6 +372,7 @@ class DisplayState(Callback):
|
||||
'filters-changed' : (str, ),
|
||||
'filter-name-changed' : (str, str, str),
|
||||
'nameformat-changed' : None,
|
||||
'placeformat-changed' : None,
|
||||
'grampletbar-close-changed' : None,
|
||||
'update-available' : (list, ),
|
||||
'autobackup' : None,
|
||||
|
||||
@@ -42,6 +42,7 @@ from gi.repository import Pango
|
||||
# Gramps classes
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from ...widgets.cellrenderertextedit import CellRendererTextEdit
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
from ...utils import is_right_click
|
||||
@@ -482,7 +483,10 @@ class EmbeddedList(ButtonTab):
|
||||
type_col = self._column_names[pair[1]][3]
|
||||
|
||||
if (type_col in [TEXT_COL, MARKUP_COL, TEXT_EDIT_COL]):
|
||||
renderer = Gtk.CellRendererText()
|
||||
if type_col == TEXT_EDIT_COL:
|
||||
renderer = CellRendererTextEdit()
|
||||
else:
|
||||
renderer = Gtk.CellRendererText()
|
||||
renderer.set_property('ellipsize', Pango.EllipsizeMode.END)
|
||||
if type_col == TEXT_COL or type_col == TEXT_EDIT_COL:
|
||||
column = Gtk.TreeViewColumn(name, renderer, text=pair[1])
|
||||
@@ -519,9 +523,12 @@ class EmbeddedList(ButtonTab):
|
||||
# insert the colum into the tree
|
||||
column.set_resizable(True)
|
||||
column.set_clickable(True)
|
||||
column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
|
||||
#column.set_min_width(self._column_names[pair[1]][2])
|
||||
column.set_fixed_width(self._column_names[pair[1]][2])
|
||||
if self._column_names[pair[1]][2] != -1:
|
||||
column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
|
||||
#column.set_min_width(self._column_names[pair[1]][2])
|
||||
column.set_fixed_width(self._column_names[pair[1]][2])
|
||||
else:
|
||||
column.set_expand(True)
|
||||
|
||||
column.set_sort_column_id(self._column_names[pair[1]][1])
|
||||
self.columns.append(column)
|
||||
|
||||
@@ -45,7 +45,7 @@ _ENTER = Gdk.keyval_from_name("Enter")
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from .surnamemodel import SurnameModel
|
||||
from .embeddedlist import EmbeddedList, TEXT_COL, MARKUP_COL, ICON_COL
|
||||
from .embeddedlist import EmbeddedList, TEXT_EDIT_COL
|
||||
from ...ddtargets import DdTargets
|
||||
from gramps.gen.lib import Surname, NameOriginType
|
||||
from ...utils import get_primary_mask
|
||||
@@ -71,9 +71,9 @@ class SurnameTab(EmbeddedList):
|
||||
#index = column in model. Value =
|
||||
# (name, sortcol in model, width, markup/text
|
||||
_column_names = [
|
||||
(_('Prefix'), -1, 150, TEXT_COL, -1, None),
|
||||
(_('Surname'), -1, 250, TEXT_COL, -1, None),
|
||||
(_('Connector'), -1, 100, TEXT_COL, -1, None),
|
||||
(_('Prefix'), 0, 150, TEXT_EDIT_COL, -1, None),
|
||||
(_('Surname'), 1, -1, TEXT_EDIT_COL, -1, None),
|
||||
(_('Connector'), 2, 100, TEXT_EDIT_COL, -1, None),
|
||||
]
|
||||
_column_combo = (_('Origin'), -1, 150, 3) # name, sort, width, modelcol
|
||||
_column_toggle = (_('Name|Primary'), -1, 80, 4)
|
||||
@@ -94,14 +94,6 @@ class SurnameTab(EmbeddedList):
|
||||
#first the standard text columns with normal method
|
||||
EmbeddedList.build_columns(self)
|
||||
|
||||
# Need to add attributes to renderers
|
||||
# and connect renderers to the 'edited' signal
|
||||
for colno in range(len(self.columns)):
|
||||
for renderer in self.columns[colno].get_cells():
|
||||
renderer.set_property('editable', not self.dbstate.db.readonly)
|
||||
renderer.connect('editing_started', self.on_edit_start, colno)
|
||||
renderer.connect('edited', self.on_edit_inline, self.column_order()[colno][1])
|
||||
|
||||
# now we add the two special columns
|
||||
# combobox for type
|
||||
colno = len(self.columns)
|
||||
@@ -133,7 +125,7 @@ class SurnameTab(EmbeddedList):
|
||||
column.set_resizable(True)
|
||||
column.set_sort_column_id(self._column_combo[1])
|
||||
column.set_min_width(self._column_combo[2])
|
||||
column.set_expand(True)
|
||||
column.set_expand(False)
|
||||
self.columns.append(column)
|
||||
self.tree.append_column(column)
|
||||
# toggle box for primary
|
||||
@@ -149,7 +141,7 @@ class SurnameTab(EmbeddedList):
|
||||
column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
|
||||
column.set_alignment(0.5)
|
||||
column.set_sort_column_id(self._column_toggle[1])
|
||||
column.set_min_width(self._column_toggle[2])
|
||||
column.set_max_width(self._column_toggle[2])
|
||||
self.columns.append(column)
|
||||
self.tree.append_column(column)
|
||||
|
||||
@@ -161,6 +153,24 @@ class SurnameTab(EmbeddedList):
|
||||
## svalue = self.cmborigmap[second]
|
||||
## return glocale.strcoll(fvalue, svalue)
|
||||
|
||||
def setup_editable_col(self):
|
||||
"""
|
||||
inherit this and set the variables needed for editable columns
|
||||
Variable edit_col_funcs needs to be a dictionary from model col_nr to
|
||||
function to call for
|
||||
Example:
|
||||
self.edit_col_funcs ={1: {'edit_start': self.on_edit_start,
|
||||
'edited': self.on_edited
|
||||
}}
|
||||
"""
|
||||
self.edit_col_funcs = {
|
||||
0: {'edit_start': self.on_edit_start,
|
||||
'edited': self.on_edit_inline},
|
||||
1: {'edit_start': self.on_edit_start,
|
||||
'edited': self.on_edit_inline},
|
||||
2: {'edit_start': self.on_edit_start,
|
||||
'edited': self.on_edit_inline}}
|
||||
|
||||
def get_data(self):
|
||||
return self.obj.get_surname_list()
|
||||
|
||||
@@ -194,6 +204,16 @@ class SurnameTab(EmbeddedList):
|
||||
if self.on_change:
|
||||
self.on_change()
|
||||
|
||||
def post_rebuild(self, prebuildpath):
|
||||
"""
|
||||
Called when data model has changed, in particular necessary when row
|
||||
order is updated.
|
||||
@param prebuildpath: path selected before rebuild, None if none
|
||||
@type prebuildpath: tree path
|
||||
"""
|
||||
if self.on_change:
|
||||
self.on_change()
|
||||
|
||||
def column_order(self):
|
||||
# order of columns for EmbeddedList. Only the text columns here
|
||||
return ((1, 0), (1, 1), (1, 2))
|
||||
@@ -239,11 +259,13 @@ class SurnameTab(EmbeddedList):
|
||||
"""
|
||||
self.on_edit_start(cellr, celle, path, colnr)
|
||||
#set up autocomplete
|
||||
entry = celle.get_child()
|
||||
entry.set_width_chars(10)
|
||||
completion = Gtk.EntryCompletion()
|
||||
completion.set_model(self.cmborig)
|
||||
completion.set_minimum_key_length(1)
|
||||
completion.set_text_column(1)
|
||||
celle.get_child().set_completion(completion)
|
||||
entry.set_completion(completion)
|
||||
#
|
||||
celle.connect('changed', self.on_origcmb_change, path, colnr)
|
||||
|
||||
|
||||
@@ -197,9 +197,6 @@ class EditPerson(EditPrimary):
|
||||
self.singsurnfr = SingSurn(self.top)
|
||||
self.multsurnfr = self.top.get_object("hboxmultsurnames")
|
||||
self.singlesurn_active = True
|
||||
self.surntab = SurnameTab(self.dbstate, self.uistate, self.track,
|
||||
self.obj.get_primary_name(),
|
||||
on_change=self._changed_name)
|
||||
|
||||
self.set_contexteventbox(self.top.get_object("eventboxtop"))
|
||||
|
||||
@@ -445,6 +442,9 @@ class EditPerson(EditPrimary):
|
||||
|
||||
self.preview_name = self.top.get_object("full_name")
|
||||
self.preview_name.override_font(Pango.FontDescription('sans bold 12'))
|
||||
self.surntab = SurnameTab(self.dbstate, self.uistate, self.track,
|
||||
self.obj.get_primary_name(),
|
||||
on_change=self._changed_name)
|
||||
|
||||
def get_start_date(self):
|
||||
"""
|
||||
@@ -936,7 +936,8 @@ class EditPerson(EditPrimary):
|
||||
msurhbox = self.top.get_object("hboxmultsurnames")
|
||||
msurhbox.remove(self.surntab)
|
||||
self.surntab = SurnameTab(self.dbstate, self.uistate, self.track,
|
||||
self.obj.get_primary_name())
|
||||
self.obj.get_primary_name(),
|
||||
on_change=self._changed_name)
|
||||
self.multsurnfr.set_size_request(-1,
|
||||
int(config.get('interface.surname-box-height')))
|
||||
msurhbox.pack_start(self.surntab, True, True, 0)
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2017 Nick Hall
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# GTK/Gnome modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from gi.repository import Gtk
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Gramps modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from ..managedwindow import ManagedWindow
|
||||
from ..glade import Glade
|
||||
from ..listmodel import ListModel
|
||||
from gramps.gen.errors import ValidationError
|
||||
from gramps.gen.display.place import displayer as _pd
|
||||
from gramps.gen.display.place import PlaceFormat
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# EditPlaceFormat
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class EditPlaceFormat(ManagedWindow):
|
||||
def __init__(self, uistate, dbstate, track, callback):
|
||||
self.title = _('Place Format Editor')
|
||||
ManagedWindow.__init__(self, uistate, track, EditPlaceFormat)
|
||||
self.callback = callback
|
||||
self.top = Glade()
|
||||
self.set_window(self.top.toplevel, None, self.title, None)
|
||||
self.setup_configs('interface.editplaceformat', 600, 400)
|
||||
self.top.get_object('add').connect('clicked', self.__add)
|
||||
self.top.get_object('remove').connect('clicked', self.__remove)
|
||||
self.top.get_object('name').connect('changed', self.__name_changed)
|
||||
self.top.get_object('levels').connect('validate', self._validate)
|
||||
self.window.connect('response', self.__close)
|
||||
self.model = None
|
||||
self.formats = _pd.get_formats()
|
||||
self.current_format = None
|
||||
self.__populate_format_list()
|
||||
self.show()
|
||||
|
||||
def build_menu_names(self, obj):
|
||||
return (self.title, None)
|
||||
|
||||
def __populate_format_list(self):
|
||||
flist = self.top.get_object('format_list')
|
||||
self.model = ListModel(flist,
|
||||
[(_('Format'), -1, 100)],
|
||||
select_func=self.__format_changed)
|
||||
for fmt in self.formats:
|
||||
self.model.add([fmt.name])
|
||||
self.model.select_row(0)
|
||||
|
||||
def __format_changed(self, selection):
|
||||
if self.current_format is not None:
|
||||
fmt = self.formats[self.current_format]
|
||||
self.__save_format(fmt)
|
||||
row = self.model.get_selected_row()
|
||||
if row != -1:
|
||||
fmt = self.formats[row]
|
||||
self.__load_format(fmt)
|
||||
self.current_format = row
|
||||
if row == 0:
|
||||
self.top.get_object('remove').set_sensitive(False)
|
||||
self.top.get_object('name').set_sensitive(False)
|
||||
self.top.get_object('levels').set_sensitive(False)
|
||||
self.top.get_object('street').set_sensitive(False)
|
||||
self.top.get_object('language').set_sensitive(False)
|
||||
self.top.get_object('reverse').set_sensitive(False)
|
||||
else:
|
||||
self.top.get_object('remove').set_sensitive(True)
|
||||
self.top.get_object('name').set_sensitive(True)
|
||||
self.top.get_object('levels').set_sensitive(True)
|
||||
self.top.get_object('street').set_sensitive(True)
|
||||
self.top.get_object('language').set_sensitive(True)
|
||||
self.top.get_object('reverse').set_sensitive(True)
|
||||
self.top.get_object('levels').validate(force=True)
|
||||
|
||||
def __name_changed(self, entry):
|
||||
store, iter_ = self.model.get_selected()
|
||||
self.model.set(iter_, [entry.get_text()])
|
||||
|
||||
def _validate(self, widget, text):
|
||||
for level in text.split(','):
|
||||
parts = level.split(':')
|
||||
if len(parts) < 1:
|
||||
return ValidationError('Empty level')
|
||||
if len(parts) > 2:
|
||||
return ValidationError('Invalid slice')
|
||||
for part in parts:
|
||||
integer_str = part.replace('p', '')
|
||||
if integer_str != '':
|
||||
try:
|
||||
integer = int(integer_str)
|
||||
except ValueError:
|
||||
return ValidationError('Invalid format string')
|
||||
|
||||
def __load_format(self, fmt):
|
||||
self.top.get_object('name').set_text(fmt.name)
|
||||
self.top.get_object('levels').set_text(fmt.levels)
|
||||
self.top.get_object('street').set_active(fmt.street)
|
||||
self.top.get_object('language').set_text(fmt.language)
|
||||
self.top.get_object('reverse').set_active(fmt.reverse)
|
||||
|
||||
def __save_format(self, fmt):
|
||||
fmt.name = self.top.get_object('name').get_text()
|
||||
fmt.levels = self.top.get_object('levels').get_text()
|
||||
fmt.street = self.top.get_object('street').get_active()
|
||||
fmt.language = self.top.get_object('language').get_text()
|
||||
fmt.reverse = self.top.get_object('reverse').get_active()
|
||||
|
||||
def __add(self, button):
|
||||
name = _('New')
|
||||
self.formats.append(PlaceFormat(name, ':', '', 0, False))
|
||||
self.model.add([name])
|
||||
self.model.select_row(len(self.formats)-1)
|
||||
|
||||
def __remove(self, button):
|
||||
store, iter_ = self.model.get_selected()
|
||||
if iter_:
|
||||
self.current_format = None
|
||||
del self.formats[self.model.get_selected_row()]
|
||||
self.model.remove(iter_)
|
||||
if self.model.get_selected_row() == -1:
|
||||
self.model.select_row(len(self.formats)-1)
|
||||
|
||||
def __close(self, *obj):
|
||||
row = self.model.get_selected_row()
|
||||
fmt = self.formats[self.current_format]
|
||||
self.__save_format(fmt)
|
||||
_pd.save_formats()
|
||||
self.callback()
|
||||
self.close()
|
||||
@@ -121,7 +121,7 @@ class PlaceSidebarFilter(SidebarFilter):
|
||||
self.filter_code.set_text('')
|
||||
self.filter_enclosed.set_text('')
|
||||
self.filter_note.set_text('')
|
||||
self.filter_within.set_value(0, 0)
|
||||
self.filter_within.set_value('', 0)
|
||||
self.ptype.get_child().set_text('')
|
||||
self.tag.set_active(0)
|
||||
self.generic.set_active(0)
|
||||
@@ -133,7 +133,7 @@ class PlaceSidebarFilter(SidebarFilter):
|
||||
code = str(self.filter_code.get_text()).strip()
|
||||
enclosed = str(self.filter_enclosed.get_text()).strip()
|
||||
note = str(self.filter_note.get_text()).strip()
|
||||
within = self.filter_within.get_value()
|
||||
within = self.filter_within.get_value()[0]
|
||||
regex = self.filter_regex.get_active()
|
||||
tag = self.tag.get_active() > 0
|
||||
gen = self.generic.get_active() > 0
|
||||
|
||||
+11
-2
@@ -46,6 +46,7 @@ from gi.repository import Gtk
|
||||
#
|
||||
#------------------------------------------------------------------------
|
||||
from gramps.gen.const import GLADE_DIR, GRAMPS_LOCALE as glocale
|
||||
from gramps.gen.constfunc import is_quartz
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
@@ -142,11 +143,19 @@ class Glade(Gtk.Builder):
|
||||
# toplevel is given
|
||||
if toplevel:
|
||||
loadlist = [toplevel] + also_load
|
||||
self.add_objects_from_file(path, loadlist)
|
||||
with open(path, 'r', encoding='utf-8') as builder_file:
|
||||
data = builder_file.read().replace('\n', '')
|
||||
if is_quartz():
|
||||
data = data.replace('GDK_CONTROL_MASK', 'GDK_META_MASK')
|
||||
self.add_objects_from_string(data, loadlist)
|
||||
self.__toplevel = self.get_object(toplevel)
|
||||
# toplevel not given
|
||||
else:
|
||||
self.add_from_file(path)
|
||||
with open(path, 'r', encoding='utf-8') as builder_file:
|
||||
data = builder_file.read().replace('\n', '')
|
||||
if is_quartz():
|
||||
data = data.replace('GDK_CONTROL_MASK', 'GDK_META_MASK')
|
||||
self.add_from_string(data)
|
||||
# first, use filename as possible toplevel widget name
|
||||
self.__toplevel = self.get_object(filename.rpartition('.')[0])
|
||||
|
||||
|
||||
@@ -197,7 +197,7 @@
|
||||
<child>
|
||||
<object class="GtkComboBox" id="confidence">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Conveys the submitter's quantitative evaluation of the credibility of a piece of information, based upon its supporting evidence. It is not intended to eliminate the receiver's need to evaluate the evidence for themselves.
|
||||
Very Low =Unreliable evidence or estimated data
|
||||
Low =Questionable reliability of evidence (interviews, census, oral genealogies, or potential for bias for example, an autobiography)
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.20.0 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.10"/>
|
||||
<requires lib="grampswidgets" version="0.0"/>
|
||||
<object class="GtkDialog" id="editplaceformat">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="button1">
|
||||
<property name="label" translatable="yes">_Close</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_underline">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkPaned" id="paned1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">6</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="format_list">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection"/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="add">
|
||||
<property name="label" translatable="yes">Add</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="remove">
|
||||
<property name="label" translatable="yes">Remove</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">False</property>
|
||||
<property name="shrink">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkGrid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">6</property>
|
||||
<property name="row_spacing">6</property>
|
||||
<property name="column_spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Levels:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Street format:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Language:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="language">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="reverse">
|
||||
<property name="label" translatable="yes">Reverse display order</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBoxText" id="street">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<items>
|
||||
<item translatable="yes">None</item>
|
||||
<item translatable="yes">Number Street</item>
|
||||
<item translatable="yes">Street Number</item>
|
||||
</items>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Name:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="name">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="ValidatableMaskedEntry" id="levels">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">True</property>
|
||||
<property name="shrink">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<action-widgets>
|
||||
<action-widget response="-7">button1</action-widget>
|
||||
</action-widgets>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
@@ -38,6 +38,7 @@ from io import StringIO
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import Gdk
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@@ -47,6 +48,7 @@ from gi.repository import Gtk
|
||||
from gramps.gen.const import GLADE_FILE, ICON
|
||||
from gramps.gen.errors import WindowActiveError
|
||||
from gramps.gen.config import config
|
||||
from gramps.gen.constfunc import is_quartz
|
||||
from .glade import Glade
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@@ -488,6 +490,13 @@ class ManagedWindow:
|
||||
#closing the Gtk.Window must also close ManagedWindow
|
||||
self.window = window
|
||||
self.window.connect('delete-event', self.close)
|
||||
#Set the mnemonic modifier on Macs to alt-ctrl so that it
|
||||
#doesn't interfere with the extended keyboard, see
|
||||
#https://gramps-project.org/bugs/view.php?id=6943
|
||||
if is_quartz():
|
||||
self.window.set_mnemonic_modifier(
|
||||
Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.MOD1_MASK)
|
||||
|
||||
if self.modal:
|
||||
self.window.set_modal(True)
|
||||
# The following makes sure that we only have one modal window open;
|
||||
|
||||
@@ -684,7 +684,7 @@ class BookSelector(ManagedWindow):
|
||||
old_margins = self.book.get_margins()
|
||||
old_format_name = self.book.get_format_name()
|
||||
old_output = self.book.get_output()
|
||||
BookDialog(self.dbstate, self.uistate, self.book, BookOptions)
|
||||
BookDialog(self.dbstate, self.uistate, self.book, BookOptions, track=self.track)
|
||||
new_paper_name = self.book.get_paper_name()
|
||||
new_orientation = self.book.get_orientation()
|
||||
new_paper_metric = self.book.get_paper_metric()
|
||||
@@ -918,7 +918,7 @@ class BookDialog(DocReportDialog):
|
||||
Create a dialog selecting target, format, and paper/HTML options.
|
||||
"""
|
||||
|
||||
def __init__(self, dbstate, uistate, book, options):
|
||||
def __init__(self, dbstate, uistate, book, options, track=[]):
|
||||
self.format_menu = None
|
||||
self.options = options
|
||||
self.page_html_added = False
|
||||
@@ -926,7 +926,7 @@ class BookDialog(DocReportDialog):
|
||||
self.title = _('Generate Book')
|
||||
self.database = dbstate.db
|
||||
DocReportDialog.__init__(self, dbstate, uistate, options,
|
||||
'book', self.title)
|
||||
'book', self.title, track=track)
|
||||
self.options.options_dict['bookname'] = self.book.get_name()
|
||||
|
||||
response = self.window.run()
|
||||
|
||||
@@ -63,7 +63,7 @@ class DocReportDialog(ReportDialog):
|
||||
dialogs for docgen derived reports.
|
||||
"""
|
||||
|
||||
def __init__(self, dbstate, uistate, option_class, name, trans_name):
|
||||
def __init__(self, dbstate, uistate, option_class, name, trans_name, track=[]):
|
||||
"""Initialize a dialog to request that the user select options
|
||||
for a basic *stand-alone* report."""
|
||||
|
||||
@@ -72,7 +72,7 @@ class DocReportDialog(ReportDialog):
|
||||
self.css = PLUGMAN.process_plugin_data('WEBSTUFF')
|
||||
self.dbname = dbstate.db.get_dbname()
|
||||
ReportDialog.__init__(self, dbstate, uistate, option_class,
|
||||
name, trans_name)
|
||||
name, trans_name, track=track)
|
||||
|
||||
self.basedocname = None # keep pylint happy
|
||||
self.css_filename = None
|
||||
|
||||
@@ -0,0 +1,289 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2007-2008 Brian G. Matherly
|
||||
# Copyright (C) 2007-2009 Stephane Charette
|
||||
# Copyright (C) 2009 Gary Burton
|
||||
# Contribution 2009 by Bob Ham <rah@bash.sh>
|
||||
# Copyright (C) 2010 Jakim Friant
|
||||
# Copyright (C) 2012-2013 Paul Franklin
|
||||
# Copyright (C) 2017 Nick Hall
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
"""base class for generating dialogs for graph-based reports """
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
# python modules
|
||||
#
|
||||
#------------------------------------------------------------------------
|
||||
from abc import ABCMeta, abstractmethod
|
||||
import os
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
#
|
||||
# GTK+ modules
|
||||
#
|
||||
#-------------------------------------------------------------------------------
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import GObject
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
#
|
||||
# Gramps modules
|
||||
#
|
||||
#-------------------------------------------------------------------------------
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
from gramps.gen.config import config
|
||||
from gramps.gen.plug.report import CATEGORY_GRAPHVIZ
|
||||
from ._reportdialog import ReportDialog
|
||||
from ._papermenu import PaperFrame
|
||||
import gramps.gen.plug.docgen.graphdoc as graphdoc
|
||||
from gramps.gen.plug.menu import Menu
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
#
|
||||
# GraphvizFormatComboBox
|
||||
#
|
||||
#-------------------------------------------------------------------------------
|
||||
class BaseFormatComboBox(Gtk.ComboBox):
|
||||
"""
|
||||
Combo box base class for graph-based report format choices.
|
||||
"""
|
||||
FORMATS = []
|
||||
|
||||
def set(self, active=None):
|
||||
""" initialize the Graphviz choices """
|
||||
store = Gtk.ListStore(GObject.TYPE_STRING)
|
||||
self.set_model(store)
|
||||
cell = Gtk.CellRendererText()
|
||||
self.pack_start(cell, True)
|
||||
self.add_attribute(cell, 'text', 0)
|
||||
|
||||
index = 0
|
||||
active_index = 0
|
||||
for item in self.FORMATS:
|
||||
name = item["descr"]
|
||||
store.append(row=[name])
|
||||
if item['type'] == active:
|
||||
active_index = index
|
||||
index += 1
|
||||
self.set_active(active_index)
|
||||
|
||||
def get_label(self):
|
||||
""" get the format description """
|
||||
return self.FORMATS[self.get_active()]["descr"]
|
||||
|
||||
def get_reference(self):
|
||||
""" get the format class """
|
||||
return self.FORMATS[self.get_active()]["class"]
|
||||
|
||||
def get_ext(self):
|
||||
""" get the format extension """
|
||||
return '.%s' % self.FORMATS[self.get_active()]['ext']
|
||||
|
||||
def get_clname(self):
|
||||
""" get the report's output format type"""
|
||||
return self.FORMATS[self.get_active()]["type"]
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
#
|
||||
# GraphReportDialog
|
||||
#
|
||||
#-----------------------------------------------------------------------
|
||||
class GraphReportDialog(ReportDialog, metaclass=ABCMeta):
|
||||
"""A base class of ReportDialog customized for graph-based reports."""
|
||||
|
||||
def __init__(self, dbstate, uistate, opt, name, translated_name):
|
||||
"""Initialize a dialog to request that the user select options
|
||||
for a Graphviz report. See the ReportDialog class for
|
||||
more information."""
|
||||
self.category = self.get_category()
|
||||
self._goptions = self.get_options()
|
||||
self.dbname = dbstate.db.get_dbname()
|
||||
ReportDialog.__init__(self, dbstate, uistate, opt,
|
||||
name, translated_name)
|
||||
|
||||
self.doc = None # keep pylint happy
|
||||
self.format = None
|
||||
self.paper_label = None
|
||||
|
||||
def init_options(self, option_class):
|
||||
try:
|
||||
if issubclass(option_class, object): # Old-style class
|
||||
self.options = option_class(self.raw_name,
|
||||
self.dbstate.get_database())
|
||||
except TypeError:
|
||||
self.options = option_class
|
||||
|
||||
menu = Menu()
|
||||
self._goptions.add_menu_options(menu)
|
||||
|
||||
for category in menu.get_categories():
|
||||
for name in menu.get_option_names(category):
|
||||
option = menu.get_option(category, name)
|
||||
self.options.add_menu_option(category, name, option)
|
||||
|
||||
self.options.load_previous_values()
|
||||
|
||||
def init_interface(self):
|
||||
ReportDialog.init_interface(self)
|
||||
self.doc_type_changed(self.format_menu)
|
||||
self.notebook.set_current_page(1) # don't start on "Paper Options"
|
||||
|
||||
def setup_format_frame(self):
|
||||
"""Set up the format frame of the dialog."""
|
||||
self.make_doc_menu()
|
||||
self.format_menu.set(self.options.handler.get_format_name())
|
||||
self.format_menu.connect('changed', self.doc_type_changed)
|
||||
label = Gtk.Label(label=_("%s:") % _("Output Format"))
|
||||
label.set_halign(Gtk.Align.START)
|
||||
self.grid.attach(label, 1, self.row, 1, 1)
|
||||
self.format_menu.set_hexpand(True)
|
||||
self.grid.attach(self.format_menu, 2, self.row, 2, 1)
|
||||
self.row += 1
|
||||
|
||||
self.open_with_app = Gtk.CheckButton(
|
||||
label=_("Open with default viewer"))
|
||||
self.open_with_app.set_active(
|
||||
config.get('interface.open-with-default-viewer'))
|
||||
self.grid.attach(self.open_with_app, 2, self.row, 2, 1)
|
||||
self.row += 1
|
||||
|
||||
ext = self.format_menu.get_ext()
|
||||
if ext is None:
|
||||
ext = ""
|
||||
else:
|
||||
spath = self.get_default_directory()
|
||||
if self.options.get_output():
|
||||
base = os.path.basename(self.options.get_output())
|
||||
else:
|
||||
if self.dbname is None:
|
||||
default_name = self.raw_name
|
||||
else:
|
||||
default_name = self.dbname + "_" + self.raw_name
|
||||
base = "%s%s" % (default_name, ext) # "ext" already has a dot
|
||||
spath = os.path.normpath(os.path.join(spath, base))
|
||||
self.target_fileentry.set_filename(spath)
|
||||
|
||||
def setup_report_options_frame(self):
|
||||
self.paper_label = Gtk.Label(label='<b>%s</b>' % _("Paper Options"))
|
||||
self.paper_label.set_use_markup(True)
|
||||
handler = self.options.handler
|
||||
self.paper_frame = PaperFrame(
|
||||
handler.get_paper_metric(),
|
||||
handler.get_paper_name(),
|
||||
handler.get_orientation(),
|
||||
handler.get_margins(),
|
||||
handler.get_custom_paper_size())
|
||||
self.notebook.insert_page(self.paper_frame, self.paper_label, 0)
|
||||
self.paper_frame.show_all()
|
||||
|
||||
ReportDialog.setup_report_options_frame(self)
|
||||
|
||||
def doc_type_changed(self, obj):
|
||||
"""
|
||||
This routine is called when the user selects a new file
|
||||
format for the report. It adjusts the various dialog sections
|
||||
to reflect the appropriate values for the currently selected
|
||||
file format. For example, a HTML document doesn't need any
|
||||
paper size/orientation options, but it does need a template
|
||||
file. Those changes are made here.
|
||||
"""
|
||||
self.open_with_app.set_sensitive(True)
|
||||
|
||||
fname = self.target_fileentry.get_full_path(0)
|
||||
(spath, ext) = os.path.splitext(fname)
|
||||
|
||||
ext_val = obj.get_ext()
|
||||
if ext_val:
|
||||
fname = spath + ext_val
|
||||
else:
|
||||
fname = spath
|
||||
self.target_fileentry.set_filename(fname)
|
||||
|
||||
def make_document(self):
|
||||
"""Create a document of the type requested by the user.
|
||||
"""
|
||||
pstyle = self.paper_frame.get_paper_style()
|
||||
|
||||
self.doc = self.format(self.options, pstyle)
|
||||
|
||||
self.options.set_document(self.doc)
|
||||
|
||||
def on_ok_clicked(self, obj):
|
||||
"""The user is satisfied with the dialog choices. Validate
|
||||
the output file name before doing anything else. If there is
|
||||
a file name, gather the options and create the report."""
|
||||
|
||||
# Is there a filename? This should also test file permissions, etc.
|
||||
if not self.parse_target_frame():
|
||||
self.window.run()
|
||||
|
||||
# Preparation
|
||||
self.parse_format_frame()
|
||||
self.parse_user_options()
|
||||
|
||||
self.options.handler.set_paper_metric(
|
||||
self.paper_frame.get_paper_metric())
|
||||
self.options.handler.set_paper_name(
|
||||
self.paper_frame.get_paper_name())
|
||||
self.options.handler.set_orientation(
|
||||
self.paper_frame.get_orientation())
|
||||
self.options.handler.set_margins(
|
||||
self.paper_frame.get_paper_margins())
|
||||
self.options.handler.set_custom_paper_size(
|
||||
self.paper_frame.get_custom_paper_size())
|
||||
|
||||
# Create the output document.
|
||||
self.make_document()
|
||||
|
||||
# Save options
|
||||
self.options.handler.save_options()
|
||||
config.set('interface.open-with-default-viewer',
|
||||
self.open_with_app.get_active())
|
||||
|
||||
def parse_format_frame(self):
|
||||
"""Parse the format frame of the dialog. Save the user
|
||||
selected output format for later use."""
|
||||
self.format = self.format_menu.get_reference()
|
||||
format_name = self.format_menu.get_clname()
|
||||
self.options.handler.set_format_name(format_name)
|
||||
|
||||
def setup_style_frame(self):
|
||||
"""Required by ReportDialog"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def make_doc_menu(self):
|
||||
"""
|
||||
Build a menu of document types that are appropriate for
|
||||
this graph report.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def get_category(self):
|
||||
"""
|
||||
Return the report category.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def get_options(self):
|
||||
"""
|
||||
Return the graph options.
|
||||
"""
|
||||
@@ -1,12 +1,7 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2007-2008 Brian G. Matherly
|
||||
# Copyright (C) 2007-2009 Stephane Charette
|
||||
# Copyright (C) 2009 Gary Burton
|
||||
# Contribution 2009 by Bob Ham <rah@bash.sh>
|
||||
# Copyright (C) 2010 Jakim Friant
|
||||
# Copyright (C) 2012-2013 Paul Franklin
|
||||
# Copyright (C) 2017 Nick Hall
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -23,174 +18,42 @@
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
""" a ReportDialog customized for Graphviz-based reports """
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
# python modules
|
||||
#
|
||||
#------------------------------------------------------------------------
|
||||
import os
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
#
|
||||
# GTK+ modules
|
||||
#
|
||||
#-------------------------------------------------------------------------------
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import GObject
|
||||
"""class for generating dialogs for graphviz-based reports """
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
#
|
||||
# Gramps modules
|
||||
#
|
||||
#-------------------------------------------------------------------------------
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
from gramps.gen.config import config
|
||||
from ._graphreportdialog import GraphReportDialog, BaseFormatComboBox
|
||||
from gramps.gen.plug.report import CATEGORY_GRAPHVIZ
|
||||
from ._reportdialog import ReportDialog
|
||||
from ._papermenu import PaperFrame
|
||||
import gramps.gen.plug.docgen.graphdoc as graphdoc
|
||||
from gramps.gen.plug.menu import Menu
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
#
|
||||
# GraphvizFormatComboBox
|
||||
#
|
||||
#-------------------------------------------------------------------------------
|
||||
class GraphvizFormatComboBox(Gtk.ComboBox):
|
||||
"""
|
||||
Combo box class for Graphviz report format choices.
|
||||
"""
|
||||
def set(self, active=None):
|
||||
""" initialize the Graphviz choices """
|
||||
store = Gtk.ListStore(GObject.TYPE_STRING)
|
||||
self.set_model(store)
|
||||
cell = Gtk.CellRendererText()
|
||||
self.pack_start(cell, True)
|
||||
self.add_attribute(cell, 'text', 0)
|
||||
|
||||
index = 0
|
||||
active_index = 0
|
||||
for item in graphdoc.FORMATS:
|
||||
name = item["descr"]
|
||||
store.append(row=[name])
|
||||
if item['type'] == active:
|
||||
active_index = index
|
||||
index += 1
|
||||
self.set_active(active_index)
|
||||
|
||||
def get_label(self):
|
||||
""" get the format description """
|
||||
return graphdoc.FORMATS[self.get_active()]["descr"]
|
||||
|
||||
def get_reference(self):
|
||||
""" get the format class """
|
||||
return graphdoc.FORMATS[self.get_active()]["class"]
|
||||
|
||||
def get_ext(self):
|
||||
""" get the format extension """
|
||||
return '.%s' % graphdoc.FORMATS[self.get_active()]['ext']
|
||||
|
||||
def get_clname(self):
|
||||
""" get the report's output format type"""
|
||||
return graphdoc.FORMATS[self.get_active()]["type"]
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
#
|
||||
# GraphvizReportDialog
|
||||
#
|
||||
#-----------------------------------------------------------------------
|
||||
class GraphvizReportDialog(ReportDialog):
|
||||
"""A class of ReportDialog customized for Graphviz-based reports."""
|
||||
class GraphvizReportDialog(GraphReportDialog):
|
||||
|
||||
def __init__(self, dbstate, uistate, opt, name, translated_name):
|
||||
"""Initialize a dialog to request that the user select options
|
||||
for a Graphviz report. See the ReportDialog class for
|
||||
more information."""
|
||||
self.category = CATEGORY_GRAPHVIZ
|
||||
self.__gvoptions = graphdoc.GVOptions()
|
||||
self.dbname = dbstate.db.get_dbname()
|
||||
ReportDialog.__init__(self, dbstate, uistate, opt,
|
||||
name, translated_name)
|
||||
|
||||
self.doc = None # keep pylint happy
|
||||
self.format = None
|
||||
self.paper_label = None
|
||||
|
||||
def init_options(self, option_class):
|
||||
try:
|
||||
if issubclass(option_class, object): # Old-style class
|
||||
self.options = option_class(self.raw_name,
|
||||
self.dbstate.get_database())
|
||||
except TypeError:
|
||||
self.options = option_class
|
||||
|
||||
menu = Menu()
|
||||
self.__gvoptions.add_menu_options(menu)
|
||||
|
||||
for category in menu.get_categories():
|
||||
for name in menu.get_option_names(category):
|
||||
option = menu.get_option(category, name)
|
||||
self.options.add_menu_option(category, name, option)
|
||||
|
||||
self.options.load_previous_values()
|
||||
|
||||
def init_interface(self):
|
||||
ReportDialog.init_interface(self)
|
||||
self.doc_type_changed(self.format_menu)
|
||||
self.notebook.set_current_page(1) # don't start on "Paper Options"
|
||||
|
||||
def setup_format_frame(self):
|
||||
"""Set up the format frame of the dialog."""
|
||||
def make_doc_menu(self):
|
||||
"""
|
||||
Build a menu of document types that are appropriate for
|
||||
this graph report.
|
||||
"""
|
||||
self.format_menu = GraphvizFormatComboBox()
|
||||
self.format_menu.set(self.options.handler.get_format_name())
|
||||
self.format_menu.connect('changed', self.doc_type_changed)
|
||||
label = Gtk.Label(label=_("%s:") % _("Output Format"))
|
||||
label.set_halign(Gtk.Align.START)
|
||||
self.grid.attach(label, 1, self.row, 1, 1)
|
||||
self.format_menu.set_hexpand(True)
|
||||
self.grid.attach(self.format_menu, 2, self.row, 2, 1)
|
||||
self.row += 1
|
||||
|
||||
self.open_with_app = Gtk.CheckButton(
|
||||
label=_("Open with default viewer"))
|
||||
self.open_with_app.set_active(
|
||||
config.get('interface.open-with-default-viewer'))
|
||||
self.grid.attach(self.open_with_app, 2, self.row, 2, 1)
|
||||
self.row += 1
|
||||
def get_category(self):
|
||||
"""
|
||||
Return the report category.
|
||||
"""
|
||||
return CATEGORY_GRAPHVIZ
|
||||
|
||||
ext = self.format_menu.get_ext()
|
||||
if ext is None:
|
||||
ext = ""
|
||||
else:
|
||||
spath = self.get_default_directory()
|
||||
if self.options.get_output():
|
||||
base = os.path.basename(self.options.get_output())
|
||||
else:
|
||||
if self.dbname is None:
|
||||
default_name = self.raw_name
|
||||
else:
|
||||
default_name = self.dbname + "_" + self.raw_name
|
||||
base = "%s%s" % (default_name, ext) # "ext" already has a dot
|
||||
spath = os.path.normpath(os.path.join(spath, base))
|
||||
self.target_fileentry.set_filename(spath)
|
||||
|
||||
def setup_report_options_frame(self):
|
||||
self.paper_label = Gtk.Label(label='<b>%s</b>' % _("Paper Options"))
|
||||
self.paper_label.set_use_markup(True)
|
||||
handler = self.options.handler
|
||||
self.paper_frame = PaperFrame(
|
||||
handler.get_paper_metric(),
|
||||
handler.get_paper_name(),
|
||||
handler.get_orientation(),
|
||||
handler.get_margins(),
|
||||
handler.get_custom_paper_size())
|
||||
self.notebook.insert_page(self.paper_frame, self.paper_label, 0)
|
||||
self.paper_frame.show_all()
|
||||
|
||||
ReportDialog.setup_report_options_frame(self)
|
||||
def get_options(self):
|
||||
"""
|
||||
Return the graph options.
|
||||
"""
|
||||
return graphdoc.GVOptions()
|
||||
|
||||
def doc_type_changed(self, obj):
|
||||
"""
|
||||
@@ -201,84 +64,31 @@ class GraphvizReportDialog(ReportDialog):
|
||||
paper size/orientation options, but it does need a template
|
||||
file. Those changes are made here.
|
||||
"""
|
||||
self.open_with_app.set_sensitive(True)
|
||||
|
||||
fname = self.target_fileentry.get_full_path(0)
|
||||
(spath, ext) = os.path.splitext(fname)
|
||||
|
||||
ext_val = obj.get_ext()
|
||||
if ext_val:
|
||||
fname = spath + ext_val
|
||||
else:
|
||||
fname = spath
|
||||
self.target_fileentry.set_filename(fname)
|
||||
GraphReportDialog.doc_type_changed(self, obj)
|
||||
|
||||
output_format_str = obj.get_clname()
|
||||
if output_format_str in ['gvpdf', 'gspdf', 'ps']:
|
||||
# Always use 72 DPI for PostScript and PDF files.
|
||||
self.__gvoptions.dpi.set_value(72)
|
||||
self.__gvoptions.dpi.set_available(False)
|
||||
self._goptions.dpi.set_value(72)
|
||||
self._goptions.dpi.set_available(False)
|
||||
else:
|
||||
self.__gvoptions.dpi.set_available(True)
|
||||
self._goptions.dpi.set_available(True)
|
||||
|
||||
if output_format_str in ['gspdf', 'dot']:
|
||||
# Multiple pages only valid for dot and PDF via GhostsScript.
|
||||
self.__gvoptions.h_pages.set_available(True)
|
||||
self.__gvoptions.v_pages.set_available(True)
|
||||
self._goptions.h_pages.set_available(True)
|
||||
self._goptions.v_pages.set_available(True)
|
||||
else:
|
||||
self.__gvoptions.h_pages.set_value(1)
|
||||
self.__gvoptions.v_pages.set_value(1)
|
||||
self.__gvoptions.h_pages.set_available(False)
|
||||
self.__gvoptions.v_pages.set_available(False)
|
||||
self._goptions.h_pages.set_value(1)
|
||||
self._goptions.v_pages.set_value(1)
|
||||
self._goptions.h_pages.set_available(False)
|
||||
self._goptions.v_pages.set_available(False)
|
||||
|
||||
def make_document(self):
|
||||
"""Create a document of the type requested by the user.
|
||||
"""
|
||||
pstyle = self.paper_frame.get_paper_style()
|
||||
|
||||
self.doc = self.format(self.options, pstyle)
|
||||
|
||||
self.options.set_document(self.doc)
|
||||
|
||||
def on_ok_clicked(self, obj):
|
||||
"""The user is satisfied with the dialog choices. Validate
|
||||
the output file name before doing anything else. If there is
|
||||
a file name, gather the options and create the report."""
|
||||
|
||||
# Is there a filename? This should also test file permissions, etc.
|
||||
if not self.parse_target_frame():
|
||||
self.window.run()
|
||||
|
||||
# Preparation
|
||||
self.parse_format_frame()
|
||||
self.parse_user_options()
|
||||
|
||||
self.options.handler.set_paper_metric(
|
||||
self.paper_frame.get_paper_metric())
|
||||
self.options.handler.set_paper_name(
|
||||
self.paper_frame.get_paper_name())
|
||||
self.options.handler.set_orientation(
|
||||
self.paper_frame.get_orientation())
|
||||
self.options.handler.set_margins(
|
||||
self.paper_frame.get_paper_margins())
|
||||
self.options.handler.set_custom_paper_size(
|
||||
self.paper_frame.get_custom_paper_size())
|
||||
|
||||
# Create the output document.
|
||||
self.make_document()
|
||||
|
||||
# Save options
|
||||
self.options.handler.save_options()
|
||||
config.set('interface.open-with-default-viewer',
|
||||
self.open_with_app.get_active())
|
||||
|
||||
def parse_format_frame(self):
|
||||
"""Parse the format frame of the dialog. Save the user
|
||||
selected output format for later use."""
|
||||
self.format = self.format_menu.get_reference()
|
||||
format_name = self.format_menu.get_clname()
|
||||
self.options.handler.set_format_name(format_name)
|
||||
|
||||
def setup_style_frame(self):
|
||||
"""Required by ReportDialog"""
|
||||
pass
|
||||
#-------------------------------------------------------------------------------
|
||||
#
|
||||
# GraphvizFormatComboBox
|
||||
#
|
||||
#-------------------------------------------------------------------------------
|
||||
class GraphvizFormatComboBox(BaseFormatComboBox):
|
||||
FORMATS = graphdoc.FORMATS
|
||||
|
||||
@@ -55,7 +55,8 @@ from ...user import User
|
||||
from ...dialog import ErrorDialog, OptionDialog
|
||||
from gramps.gen.plug.report import (CATEGORY_TEXT, CATEGORY_DRAW, CATEGORY_BOOK,
|
||||
CATEGORY_CODE, CATEGORY_WEB,
|
||||
CATEGORY_GRAPHVIZ, standalone_categories)
|
||||
CATEGORY_GRAPHVIZ, CATEGORY_TREE,
|
||||
standalone_categories)
|
||||
from gramps.gen.plug.docgen import StyleSheet, StyleSheetList
|
||||
from ...managedwindow import ManagedWindow
|
||||
from ._stylecombobox import StyleComboBox
|
||||
@@ -676,6 +677,9 @@ def report(dbstate, uistate, person, report_class, options_class,
|
||||
elif category == CATEGORY_GRAPHVIZ:
|
||||
from ._graphvizreportdialog import GraphvizReportDialog
|
||||
dialog_class = GraphvizReportDialog
|
||||
elif category == CATEGORY_TREE:
|
||||
from ._treereportdialog import TreeReportDialog
|
||||
dialog_class = TreeReportDialog
|
||||
elif category == CATEGORY_WEB:
|
||||
from ._webreportdialog import WebReportDialog
|
||||
dialog_class = WebReportDialog
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2017 Nick Hall
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
"""class for generating dialogs for graphviz-based reports """
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
#
|
||||
# Gramps modules
|
||||
#
|
||||
#-------------------------------------------------------------------------------
|
||||
from ._graphreportdialog import GraphReportDialog, BaseFormatComboBox
|
||||
from gramps.gen.plug.report import CATEGORY_TREE
|
||||
import gramps.gen.plug.docgen.treedoc as treedoc
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
#
|
||||
# TreeReportDialog
|
||||
#
|
||||
#-----------------------------------------------------------------------
|
||||
class TreeReportDialog(GraphReportDialog):
|
||||
|
||||
def make_doc_menu(self):
|
||||
"""
|
||||
Build a menu of document types that are appropriate for
|
||||
this graph report.
|
||||
"""
|
||||
self.format_menu = TreeFormatComboBox()
|
||||
|
||||
def get_category(self):
|
||||
"""
|
||||
Return the report category.
|
||||
"""
|
||||
return CATEGORY_TREE
|
||||
|
||||
def get_options(self):
|
||||
"""
|
||||
Return the graph options.
|
||||
"""
|
||||
return treedoc.TreeOptions()
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
#
|
||||
# TreeFormatComboBox
|
||||
#
|
||||
#-------------------------------------------------------------------------------
|
||||
class TreeFormatComboBox(BaseFormatComboBox):
|
||||
FORMATS = treedoc.FORMATS
|
||||
+46
-12
@@ -53,6 +53,7 @@ from gi.repository import Gdk
|
||||
#-------------------------------------------------------------------------
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
from gramps.gen.lib import EventType, EventRoleType
|
||||
from gramps.gen.lib.person import Person
|
||||
from gramps.gen.constfunc import has_display, is_quartz, mac, win
|
||||
from gramps.gen.config import config
|
||||
@@ -314,6 +315,7 @@ class ProgressMeter:
|
||||
"""
|
||||
Close the progress meter
|
||||
"""
|
||||
del self.__cancel_callback
|
||||
self.__dialog.destroy()
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@@ -473,33 +475,50 @@ def is_right_click(event):
|
||||
if Gdk.Event.triggers_context_menu(event):
|
||||
return True
|
||||
|
||||
def color_graph_family(family, dbstate):
|
||||
"""
|
||||
:return: based on the config the color for graph family node in hex
|
||||
:rtype: tuple (hex color fill, hex color border)
|
||||
"""
|
||||
scheme = config.get('colors.scheme')
|
||||
for event_ref in family.get_event_ref_list():
|
||||
event = dbstate.db.get_event_from_handle(event_ref.ref)
|
||||
if (event.type == EventType.DIVORCE and
|
||||
event_ref.get_role() in (EventRoleType.FAMILY,
|
||||
EventRoleType.PRIMARY)):
|
||||
return (config.get('colors.family-divorced')[scheme],
|
||||
config.get('colors.border-family-divorced')[scheme])
|
||||
return (config.get('colors.family')[scheme],
|
||||
config.get('colors.border-family')[scheme])
|
||||
|
||||
def color_graph_box(alive=False, gender=Person.MALE):
|
||||
"""
|
||||
:return: based on the config the color for graph boxes in hex
|
||||
If gender is None, an empty box is assumed
|
||||
:rtype: tuple (hex color fill, hex color border)
|
||||
"""
|
||||
scheme = config.get('colors.scheme')
|
||||
if gender == Person.MALE:
|
||||
if alive:
|
||||
return (config.get('preferences.color-gender-male-alive'),
|
||||
config.get('preferences.bordercolor-gender-male-alive'))
|
||||
return (config.get('colors.male-alive')[scheme],
|
||||
config.get('colors.border-male-alive')[scheme])
|
||||
else:
|
||||
return (config.get('preferences.color-gender-male-death'),
|
||||
config.get('preferences.bordercolor-gender-male-death'))
|
||||
return (config.get('colors.male-dead')[scheme],
|
||||
config.get('colors.border-male-dead')[scheme])
|
||||
elif gender == Person.FEMALE:
|
||||
if alive:
|
||||
return (config.get('preferences.color-gender-female-alive'),
|
||||
config.get('preferences.bordercolor-gender-female-alive'))
|
||||
return (config.get('colors.female-alive')[scheme],
|
||||
config.get('colors.border-female-alive')[scheme])
|
||||
else:
|
||||
return (config.get('preferences.color-gender-female-death'),
|
||||
config.get('preferences.bordercolor-gender-female-death'))
|
||||
return (config.get('colors.female-dead')[scheme],
|
||||
config.get('colors.border-female-dead')[scheme])
|
||||
elif gender == Person.UNKNOWN:
|
||||
if alive:
|
||||
return (config.get('preferences.color-gender-unknown-alive'),
|
||||
config.get('preferences.bordercolor-gender-unknown-alive'))
|
||||
return (config.get('colors.unknown-alive')[scheme],
|
||||
config.get('colors.border-unknown-alive')[scheme])
|
||||
else:
|
||||
return (config.get('preferences.color-gender-unknown-death'),
|
||||
config.get('preferences.bordercolor-gender-unknown-death'))
|
||||
return (config.get('colors.unknown-dead')[scheme],
|
||||
config.get('colors.border-unknown-dead')[scheme])
|
||||
#empty box, no gender
|
||||
return ('#d2d6ce', '#000000')
|
||||
## print 'male alive', rgb_to_hex((185/256.0, 207/256.0, 231/256.0))
|
||||
@@ -543,6 +562,21 @@ def rgb_to_hex(rgb):
|
||||
rgbint = (int(rgb[0] * 255), int(rgb[1] * 255), int(rgb[2] * 255))
|
||||
return '#%02x%02x%02x' % rgbint
|
||||
|
||||
def get_link_color(context):
|
||||
"""
|
||||
Find the link color for the current theme.
|
||||
"""
|
||||
from gi.repository import Gtk
|
||||
|
||||
if Gtk.get_minor_version() > 11:
|
||||
col = context.get_color(Gtk.StateFlags.LINK)
|
||||
else:
|
||||
found, col = context.lookup_color('link_color')
|
||||
if not found:
|
||||
col.parse('blue')
|
||||
|
||||
return rgb_to_hex((col.red, col.green, col.blue))
|
||||
|
||||
def edit_object(dbstate, uistate, reftype, ref):
|
||||
"""
|
||||
Invokes the appropriate editor for an object type and given handle.
|
||||
|
||||
+15
-10
@@ -39,6 +39,7 @@ import time
|
||||
import datetime
|
||||
from io import StringIO
|
||||
import posixpath
|
||||
import gc
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@@ -54,6 +55,7 @@ LOG = logging.getLogger(".")
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import Gdk
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@@ -136,8 +138,6 @@ UIDEFAULT = '''<ui>
|
||||
<menuitem action="Quit"/>
|
||||
</menu>
|
||||
<menu action="AddMenu">
|
||||
<menu action="AddNewMenu">
|
||||
<separator/>
|
||||
<menuitem action="PersonAdd"/>
|
||||
<separator/>
|
||||
<menuitem action="FamilyAdd"/>
|
||||
@@ -150,7 +150,6 @@ UIDEFAULT = '''<ui>
|
||||
<menuitem action="RepositoryAdd"/>
|
||||
<menuitem action="MediaAdd"/>
|
||||
<menuitem action="NoteAdd"/>
|
||||
</menu>
|
||||
</menu>
|
||||
<menu action="EditMenu">
|
||||
<menuitem action="Undo"/>
|
||||
@@ -396,7 +395,12 @@ class ViewManager(CLIManager):
|
||||
self.window.set_icon_from_file(ICON)
|
||||
self.window.set_default_size(width, height)
|
||||
self.window.move(horiz_position, vert_position)
|
||||
|
||||
#Set the mnemonic modifier on Macs to alt-ctrl so that it
|
||||
#doesn't interfere with the extended keyboard, see
|
||||
#https://gramps-project.org/bugs/view.php?id=6943
|
||||
if is_quartz():
|
||||
self.window.set_mnemonic_modifier(
|
||||
Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.MOD1_MASK)
|
||||
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||
self.window.add(vbox)
|
||||
hpane = Gtk.Paned()
|
||||
@@ -599,20 +603,20 @@ class ViewManager(CLIManager):
|
||||
('Clipboard', 'edit-paste', _('Clip_board'), "<PRIMARY>b",
|
||||
_("Open the Clipboard dialog"), self.clipboard),
|
||||
('AddMenu', None, _('_Add')),
|
||||
('AddNewMenu', None, _('New')),
|
||||
('PersonAdd', None, _('Person'), "<Alt>p", None,
|
||||
#('AddNewMenu', None, _('New')),
|
||||
('PersonAdd', None, _('Person'), "<shift><Alt>p", None,
|
||||
self.add_new_person),
|
||||
('FamilyAdd', None, _('Family'), "<Alt>y", None,
|
||||
('FamilyAdd', None, _('Family'), "<shift><Alt>f", None,
|
||||
self.add_new_family),
|
||||
('EventAdd', None, _('Event'), "<shift>e", None,
|
||||
('EventAdd', None, _('Event'), "<shift><Alt>e", None,
|
||||
self.add_new_event),
|
||||
('PlaceAdd', None, _('Place'), "<shift><Alt>p", None,
|
||||
('PlaceAdd', None, _('Place'), "<shift><Alt>l", None,
|
||||
self.add_new_place),
|
||||
('SourceAdd', None, _('Source'), "<shift><Alt>s", None,
|
||||
self.add_new_source),
|
||||
('CitationAdd', None, _('Citation'), "<shift><Alt>c", None,
|
||||
self.add_new_citation),
|
||||
('RepositoryAdd', None, _('Repository'), "<shift><Alt>y", None,
|
||||
('RepositoryAdd', None, _('Repository'), "<shift><Alt>r", None,
|
||||
self.add_new_repository),
|
||||
('MediaAdd', None, _('Media'), "<shift><Alt>m", None,
|
||||
self.add_new_media),
|
||||
@@ -1769,6 +1773,7 @@ def run_plugin(pdata, dbstate, uistate):
|
||||
name=pdata.id,
|
||||
category=pdata.category,
|
||||
callback=dbstate.db.request_rebuild)
|
||||
gc.collect(2)
|
||||
|
||||
def make_plugin_callback(pdata, dbstate, uistate):
|
||||
"""
|
||||
|
||||
@@ -278,14 +278,12 @@ class ListView(NavigationView):
|
||||
def foreground_color(self, column, renderer, model, iter_, data=None):
|
||||
'''
|
||||
Set the foreground color of the cell renderer. We use a cell data
|
||||
function because we don't want to set the color of untagged rows.
|
||||
function because there is a problem returning None from a model.
|
||||
'''
|
||||
fg_color = model.get_value(iter_, model.color_column())
|
||||
#for color errors, typically color column is badly set
|
||||
if fg_color:
|
||||
renderer.set_property('foreground', fg_color)
|
||||
else:
|
||||
LOG.debug('Bad color set: ' + str(fg_color))
|
||||
if fg_color == '':
|
||||
fg_color = None
|
||||
renderer.set_property('foreground', fg_color)
|
||||
|
||||
def set_active(self):
|
||||
"""
|
||||
|
||||
@@ -156,8 +156,7 @@ class PageView(DbGUIElement, metaclass=ABCMeta):
|
||||
hpane = Gtk.Paned()
|
||||
vpane = Gtk.Paned(orientation=Gtk.Orientation.VERTICAL)
|
||||
hpane.pack1(vpane, resize=True, shrink=False)
|
||||
hpane.pack2(self.sidebar, resize=False, shrink=True)
|
||||
self._setup_slider_config(hpane, 'hpane.slider-position')
|
||||
hpane.pack2(self.sidebar, resize=False, shrink=False)
|
||||
hpane.show()
|
||||
vpane.show()
|
||||
|
||||
@@ -168,14 +167,31 @@ class PageView(DbGUIElement, metaclass=ABCMeta):
|
||||
self._setup_slider_config(vpane, 'vpane.slider-position')
|
||||
|
||||
self.sidebar_toggled(self.sidebar.get_property('visible'))
|
||||
self.hpane_sig = hpane.connect("draw", self.set_page_slider)
|
||||
|
||||
return hpane
|
||||
|
||||
def _setup_slider_config(self, widget, setting):
|
||||
def set_page_slider(self, widget, dummy):
|
||||
""" Setup slider. We have the page realized at this point. """
|
||||
widget.disconnect(self.hpane_sig)
|
||||
# get current width of pane
|
||||
width = widget.get_allocated_width()
|
||||
# default will use natural size for sidebar until it gets to 400 pix
|
||||
side_ch = self.sidebar.get_children() # Gtk Notebook
|
||||
try:
|
||||
vp_ch = side_ch[0].get_children() # Gtk Viewport child
|
||||
ch_width = vp_ch[0].get_preferred_width()[0] + 3
|
||||
except AttributeError:
|
||||
ch_width = 300 # needed if no Gramplet installed
|
||||
pos = width - min(ch_width, 400)
|
||||
self._setup_slider_config(widget, 'hpane.slider-position',
|
||||
position=pos)
|
||||
|
||||
def _setup_slider_config(self, widget, setting, position=-1):
|
||||
"""
|
||||
Setup the slider configuration setting.
|
||||
"""
|
||||
self._config.register(setting, -1)
|
||||
self._config.register(setting, position)
|
||||
widget.set_position(self._config.get(setting))
|
||||
widget.connect('notify::position', self._position_changed, setting)
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ class CitationBaseModel:
|
||||
tag_handle = data[0]
|
||||
cached, tag_color = self.get_cached_value(tag_handle, "TAG_COLOR")
|
||||
if not cached:
|
||||
tag_color = "#000000000000"
|
||||
tag_color = ""
|
||||
tag_priority = None
|
||||
for handle in data[COLUMN_TAGS]:
|
||||
tag = self.db.get_tag_from_handle(handle)
|
||||
@@ -300,7 +300,7 @@ class CitationBaseModel:
|
||||
tag_handle = data[0]
|
||||
cached, tag_color = self.get_cached_value(tag_handle, "TAG_COLOR")
|
||||
if not cached:
|
||||
tag_color = "#000000000000"
|
||||
tag_color = ""
|
||||
tag_priority = None
|
||||
for handle in data[COLUMN2_TAGS]:
|
||||
tag = self.db.get_tag_from_handle(handle)
|
||||
|
||||
@@ -208,7 +208,7 @@ class EventModel(FlatBaseModel):
|
||||
tag_handle = data[0]
|
||||
cached, tag_color = self.get_cached_value(tag_handle, "TAG_COLOR")
|
||||
if not cached:
|
||||
tag_color = "#000000000000"
|
||||
tag_color = ""
|
||||
tag_priority = None
|
||||
for handle in data[COLUMN_TAGS]:
|
||||
tag = self.db.get_tag_from_handle(handle)
|
||||
|
||||
@@ -220,7 +220,7 @@ class FamilyModel(FlatBaseModel):
|
||||
tag_handle = data[0]
|
||||
cached, tag_color = self.get_cached_value(tag_handle, "TAG_COLOR")
|
||||
if not cached:
|
||||
tag_color = "#000000000000"
|
||||
tag_color = ""
|
||||
tag_priority = None
|
||||
for handle in data[13]:
|
||||
tag = self.db.get_tag_from_handle(handle)
|
||||
|
||||
@@ -171,7 +171,7 @@ class MediaModel(FlatBaseModel):
|
||||
tag_handle = data[0]
|
||||
cached, tag_color = self.get_cached_value(tag_handle, "TAG_COLOR")
|
||||
if not cached:
|
||||
tag_color = "#000000000000"
|
||||
tag_color = ""
|
||||
tag_priority = None
|
||||
for handle in data[11]:
|
||||
tag = self.db.get_tag_from_handle(handle)
|
||||
|
||||
@@ -148,7 +148,7 @@ class NoteModel(FlatBaseModel):
|
||||
tag_handle = data[0]
|
||||
cached, value = self.get_cached_value(tag_handle, "TAG_COLOR")
|
||||
if not cached:
|
||||
tag_color = "#000000000000"
|
||||
tag_color = ""
|
||||
tag_priority = None
|
||||
for handle in data[Note.POS_TAGS]:
|
||||
tag = self.db.get_tag_from_handle(handle)
|
||||
|
||||
@@ -538,7 +538,7 @@ class PeopleBaseModel(BaseModel):
|
||||
tag_handle = data[0]
|
||||
cached, value = self.get_cached_value(tag_handle, "TAG_COLOR")
|
||||
if not cached:
|
||||
tag_color = "#000000000000"
|
||||
tag_color = ""
|
||||
tag_priority = None
|
||||
for handle in data[COLUMN_TAGS]:
|
||||
tag = self.db.get_tag_from_handle(handle)
|
||||
|
||||
@@ -201,7 +201,7 @@ class PlaceBaseModel:
|
||||
tag_handle = data[0]
|
||||
cached, value = self.get_cached_value(tag_handle, "TAG_COLOR")
|
||||
if not cached:
|
||||
tag_color = "#000000000000"
|
||||
tag_color = ""
|
||||
tag_priority = None
|
||||
for handle in data[16]:
|
||||
tag = self.db.get_tag_from_handle(handle)
|
||||
|
||||
@@ -253,7 +253,7 @@ class RepositoryModel(FlatBaseModel):
|
||||
tag_handle = data[0]
|
||||
cached, tag_color = self.get_cached_value(tag_handle, "TAG_COLOR")
|
||||
if not cached:
|
||||
tag_color = "#000000000000"
|
||||
tag_color = ""
|
||||
tag_priority = None
|
||||
for handle in data[8]:
|
||||
tag = self.db.get_tag_from_handle(handle)
|
||||
|
||||
@@ -143,7 +143,7 @@ class SourceModel(FlatBaseModel):
|
||||
tag_handle = data[0]
|
||||
cached, value = self.get_cached_value(tag_handle, "TAG_COLOR")
|
||||
if not cached:
|
||||
tag_color = "#000000000000"
|
||||
tag_color = ""
|
||||
tag_priority = None
|
||||
for handle in data[11]:
|
||||
tag = self.db.get_tag_from_handle(handle)
|
||||
|
||||
@@ -898,7 +898,8 @@ class TreeBaseModel(GObject.GObject, Gtk.TreeModel, BaseModel):
|
||||
if node.handle is None:
|
||||
# Header rows dont get the foreground color set
|
||||
if col == self.color_column():
|
||||
return "#000000000000"
|
||||
#color must not be utf-8
|
||||
return ""
|
||||
|
||||
# Return the node name for the first column
|
||||
if col == 0:
|
||||
|
||||
@@ -32,6 +32,7 @@ from .photo import *
|
||||
from .placeentry import *
|
||||
from .monitoredwidgets import *
|
||||
from .selectionwidget import SelectionWidget, Region
|
||||
from .shadebox import *
|
||||
from .shortlistcomboentry import *
|
||||
from .springseparator import *
|
||||
from .statusbar import Statusbar
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2017 Paul Culley
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
# Python Modules
|
||||
#
|
||||
#------------------------------------------------------------------------
|
||||
from gi.repository import Gdk
|
||||
from gi.repository import Gtk
|
||||
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
# Gramps Modules
|
||||
#
|
||||
#------------------------------------------------------------------------
|
||||
class CellRendererTextEdit(Gtk.CellRendererText):
|
||||
""" To be used where you normally use Gtk.CellRendererText and you want to
|
||||
avoid losing the text if the user clicks outside the cell (Like an 'OK'
|
||||
button. """
|
||||
|
||||
__gtype_name__ = 'CellRendererTextEdit'
|
||||
|
||||
def __init__(self):
|
||||
Gtk.CellRendererText.__init__(self)
|
||||
|
||||
def do_start_editing(
|
||||
self, event, treeview, path, background_area, cell_area, flags):
|
||||
if not self.get_property('editable'):
|
||||
return
|
||||
entry = Gtk.Entry()
|
||||
entry.set_has_frame(False)
|
||||
xalign, yalign = self.get_alignment()
|
||||
entry.set_alignment(xalign)
|
||||
entry.set_width_chars(5)
|
||||
entry.set_text(self.get_property("text")) # get original cell text
|
||||
entry.add_events(Gdk.EventMask.FOCUS_CHANGE_MASK)
|
||||
entry.connect('focus-out-event', self.focus_out, path)
|
||||
entry.connect('key-press-event', self._key_press)
|
||||
entry.show()
|
||||
return entry
|
||||
|
||||
def focus_out(self, entry, event, path):
|
||||
self.emit('edited', path, entry.get_text())
|
||||
return False
|
||||
|
||||
def _key_press(self, entry, event):
|
||||
if event.type == Gdk.EventType.KEY_PRESS:
|
||||
if event.keyval == Gdk.KEY_Escape:
|
||||
# get original cell text
|
||||
entry.set_text(self.get_property("text"))
|
||||
return False
|
||||
@@ -1221,7 +1221,9 @@ class FanChartWidget(FanChartBaseWidget):
|
||||
cr.scale(scale, scale)
|
||||
if widget:
|
||||
self.center_xy = self.center_xy_from_delta()
|
||||
cr.translate(*self.center_xy)
|
||||
cr.translate(*self.center_xy)
|
||||
else:
|
||||
cr.translate(halfdist, halfdist)
|
||||
|
||||
cr.save()
|
||||
cr.rotate(math.radians(self.rotate_value))
|
||||
@@ -1596,6 +1598,8 @@ class FanChartGrampsGUI:
|
||||
siblings.append(sib_id)
|
||||
# Collect a list of per-step-family step-siblings
|
||||
for parent_h in [fam.get_father_handle(), fam.get_mother_handle()]:
|
||||
if not parent_h:
|
||||
continue
|
||||
parent = self.dbstate.db.get_person_from_handle(parent_h)
|
||||
other_families = [self.dbstate.db.get_family_from_handle(fam_id)
|
||||
for fam_id in parent.get_family_handle_list()
|
||||
|
||||
@@ -374,7 +374,9 @@ class FanChart2WayWidget(FanChartWidget, FanChartDescWidget):
|
||||
cr.scale(scale, scale)
|
||||
if widget:
|
||||
self.center_xy = self.center_xy_from_delta()
|
||||
cr.translate(*self.center_xy)
|
||||
cr.translate(*self.center_xy)
|
||||
else:
|
||||
cr.translate(halfdist, halfdist)
|
||||
|
||||
cr.save()
|
||||
# Draw background
|
||||
|
||||
@@ -370,7 +370,7 @@ class GrampletBar(Gtk.Notebook):
|
||||
"""
|
||||
Add a tab to the notebook for the given gramplet.
|
||||
"""
|
||||
width = min(int(self.uistate.screen_width() * 0.25), 400)
|
||||
width = -1 # Allow tab width to adjust (smaller) to sidebar
|
||||
height = min(int(self.uistate.screen_height() * 0.20), 400)
|
||||
gramplet.set_size_request(width, height)
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ from gramps.gen.errors import WindowActiveError
|
||||
from gramps.gen.const import URL_MANUAL_PAGE, VERSION_DIR, COLON
|
||||
from ..editors import EditPerson, EditFamily
|
||||
from ..managedwindow import ManagedWindow
|
||||
from ..utils import is_right_click, rgb_to_hex, get_primary_mask
|
||||
from ..utils import is_right_click, get_primary_mask, get_link_color
|
||||
from .menuitem import add_menuitem
|
||||
from ..plug import make_gui_option
|
||||
from ..plug.quick import run_quick_report_by_name
|
||||
@@ -196,12 +196,7 @@ class LinkTag(Gtk.TextTag):
|
||||
lid = 0
|
||||
#obtaining the theme link color once. Restart needed on theme change!
|
||||
linkcolor = Gtk.Label(label='test') #needed to avoid label destroyed to early
|
||||
linkcolor = linkcolor.get_style_context().lookup_color('link_color')
|
||||
if linkcolor[0]:
|
||||
linkcolor = rgb_to_hex((linkcolor[1].red, linkcolor[1].green,
|
||||
linkcolor[1].blue))
|
||||
else:
|
||||
linkcolor = 'blue'
|
||||
linkcolor = get_link_color(linkcolor.get_style_context())
|
||||
|
||||
def __init__(self, buffer):
|
||||
LinkTag.lid += 1
|
||||
|
||||
@@ -48,7 +48,7 @@ from gi.repository import Pango
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from gramps.gen.constfunc import has_display, win
|
||||
from ..utils import rgb_to_hex
|
||||
from ..utils import get_link_color
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@@ -81,11 +81,7 @@ class LinkLabel(Gtk.EventBox):
|
||||
Gtk.EventBox.__init__(self)
|
||||
|
||||
st_cont = self.get_style_context()
|
||||
col = st_cont.lookup_color('link_color')
|
||||
if col[0]:
|
||||
self.color = rgb_to_hex((col[1].red, col[1].green, col[1].blue))
|
||||
else:
|
||||
self.color = 'blue'
|
||||
self.color = get_link_color(st_cont)
|
||||
|
||||
if emph:
|
||||
#emphasize a link
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2018 Nick Hall
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
__all__ = ["ShadeBox"]
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Standard python modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
import logging
|
||||
_LOG = logging.getLogger(".widgets.shadebox")
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# GTK/Gnome modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from gi.repository import Gtk
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# ShadeBox class
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class ShadeBox(Gtk.EventBox):
|
||||
"""
|
||||
An EventBox with a shaded background.
|
||||
"""
|
||||
def __init__(self, use_shade):
|
||||
Gtk.EventBox.__init__(self)
|
||||
self.use_shade = use_shade
|
||||
|
||||
def do_draw(self, cr):
|
||||
if self.use_shade:
|
||||
tv = Gtk.TextView()
|
||||
tv_context = tv.get_style_context()
|
||||
width = self.get_allocated_width()
|
||||
height = self.get_allocated_height()
|
||||
Gtk.render_background(tv_context, cr, 0, 0, width, height)
|
||||
self.get_child().draw(cr)
|
||||
@@ -60,9 +60,9 @@ from .toolcomboentry import ToolComboEntry
|
||||
from .springseparator import SpringSeparatorAction
|
||||
from ..spell import Spell
|
||||
from ..display import display_url
|
||||
from ..utils import SystemFonts, rgb_to_hex, get_primary_mask
|
||||
from ..utils import SystemFonts, get_primary_mask, get_link_color
|
||||
from gramps.gen.config import config
|
||||
from gramps.gen.constfunc import has_display
|
||||
from gramps.gen.constfunc import has_display, mac
|
||||
from ..actiongroup import ActionGroup
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@@ -186,11 +186,7 @@ class StyledTextEditor(Gtk.TextView):
|
||||
self.set_buffer(self.textbuffer)
|
||||
|
||||
st_cont = self.get_style_context()
|
||||
col = st_cont.lookup_color('link_color')
|
||||
if col[0]:
|
||||
self.linkcolor = rgb_to_hex((col[1].red, col[1].green, col[1].blue))
|
||||
else:
|
||||
self.linkcolor = 'blue'
|
||||
self.linkcolor = get_link_color(st_cont)
|
||||
self.textbuffer.linkcolor = self.linkcolor
|
||||
|
||||
self.match = None
|
||||
@@ -319,7 +315,9 @@ class StyledTextEditor(Gtk.TextView):
|
||||
if url.startswith("gramps://"):
|
||||
obj_class, prop, value = url[9:].split("/")
|
||||
display = simple_access.display(obj_class, prop, value) or url
|
||||
return display
|
||||
return display + ((_("\nCommand-Click to follow link") if mac() else
|
||||
_("\nCtrl-Click to follow link"))
|
||||
if self.get_editable() else '')
|
||||
|
||||
def on_button_release_event(self, widget, event):
|
||||
"""
|
||||
|
||||
@@ -52,7 +52,7 @@ from gi.repository import Pango
|
||||
#-------------------------------------------------------------------------
|
||||
from gramps.gen.errors import MaskError, ValidationError, WindowActiveError
|
||||
from .undoableentry import UndoableEntry
|
||||
|
||||
from gramps.gen.constfunc import is_quartz
|
||||
#============================================================================
|
||||
#
|
||||
# MaskedEntry and ValidatableMaskedEntry copied and merged from the Kiwi
|
||||
@@ -1248,6 +1248,12 @@ def main(args):
|
||||
win = Gtk.Window()
|
||||
win.set_title('ValidatableMaskedEntry test window')
|
||||
win.set_position(Gtk.WindowPosition.CENTER)
|
||||
#Set the mnemonic modifier on Macs to alt-ctrl so that it
|
||||
#doesn't interfere with the extended keyboard, see
|
||||
#https://gramps-project.org/bugs/view.php?id=6943
|
||||
if is_quartz():
|
||||
win.set_mnemonic_modifier(
|
||||
Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.MOD1_MASK)
|
||||
def cb(window, event):
|
||||
Gtk.main_quit()
|
||||
win.connect('delete-event', cb)
|
||||
|
||||
@@ -468,8 +468,9 @@ class RecurseDown:
|
||||
#calculate the text.
|
||||
myself.calc_text(self.database, indi_handle, fams_handle)
|
||||
|
||||
myself.add_mark(self.database,
|
||||
self.database.get_person_from_handle(indi_handle))
|
||||
if indi_handle:
|
||||
myself.add_mark(self.database,
|
||||
self.database.get_person_from_handle(indi_handle))
|
||||
|
||||
self.add_to_col(myself)
|
||||
|
||||
@@ -692,7 +693,8 @@ class MakePersonTree(RecurseDown):
|
||||
family2 = family2_h = None
|
||||
if self.do_parents:
|
||||
family2_h = center1.get_main_parents_family_handle()
|
||||
family2 = self.database.get_family_from_handle(family2_h)
|
||||
if family2_h:
|
||||
family2 = self.database.get_family_from_handle(family2_h)
|
||||
|
||||
mother2_h = father2_h = None
|
||||
if family2:
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
# standard python modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
import os
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
@@ -43,9 +42,12 @@ log = logging.getLogger(".WriteFtree")
|
||||
# Gramps modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from gramps.gen.utils.alive import probably_alive
|
||||
# keep the following line even though not obviously used (works on import)
|
||||
from gramps.gui.plug.export import WriterOptionBox
|
||||
from gramps.gui.glade import Glade
|
||||
from gramps.gui.dialog import ErrorDialog
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@@ -53,22 +55,25 @@ from gramps.gui.glade import Glade
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
def writeData(database, filename, user, option_box=None):
|
||||
""" function to export Web Family Tree file """
|
||||
writer = FtreeWriter(database, filename, user, option_box)
|
||||
return writer.export_data()
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# FtreeWriter
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class FtreeWriter:
|
||||
|
||||
""" Export a Web Family Tree format file """
|
||||
def __init__(self, database, filename, user, option_box=None):
|
||||
self.db = database
|
||||
self.filename = filename
|
||||
self.user = user
|
||||
self.option_box = option_box
|
||||
if isinstance(self.user.callback, collections.Callable): # callback is really callable
|
||||
# is callback is really callable?
|
||||
if isinstance(self.user.callback, collections.Callable):
|
||||
self.update = self.update_real
|
||||
else:
|
||||
self.update = self.update_empty
|
||||
@@ -78,121 +83,132 @@ class FtreeWriter:
|
||||
self.db = option_box.get_filtered_database(self.db)
|
||||
|
||||
self.plist = [x for x in self.db.iter_person_handles()]
|
||||
# the following are used to update the progress meter
|
||||
self.total = 2 * len(self.plist)
|
||||
self.count = 0
|
||||
self.oldval = 0 # we only update when percentage changes
|
||||
|
||||
def update_empty(self):
|
||||
""" used when no callback is present """
|
||||
pass
|
||||
|
||||
def update_real(self):
|
||||
""" Progress update """
|
||||
self.count += 1
|
||||
newval = int(100*self.count/self.total)
|
||||
newval = int(100 * self.count / self.total)
|
||||
if newval != self.oldval:
|
||||
self.user.callback(newval)
|
||||
self.oldval = newval
|
||||
|
||||
def export_data(self):
|
||||
""" main export processing """
|
||||
name_map = {}
|
||||
id_map = {}
|
||||
id_name = {}
|
||||
self.count = 0
|
||||
self.oldval = 0
|
||||
self.total = 2*len(self.plist)
|
||||
|
||||
for key in self.plist:
|
||||
self.update()
|
||||
pn = self.db.get_person_from_handle(key).get_primary_name()
|
||||
sn = pn.get_surname()
|
||||
items = pn.get_first_name().split()
|
||||
n = ("%s %s" % (items[0], sn)) if items else sn
|
||||
pnam = self.db.get_person_from_handle(key).get_primary_name()
|
||||
snam = pnam.get_surname()
|
||||
items = pnam.get_first_name().split()
|
||||
nam = ("%s %s" % (items[0], snam)) if items else snam
|
||||
|
||||
count = -1
|
||||
if n in name_map:
|
||||
if nam in name_map:
|
||||
count = 0
|
||||
while 1:
|
||||
nn = "%s%d" % (n, count)
|
||||
if nn not in name_map:
|
||||
break;
|
||||
nam_num = "%s%d" % (nam, count)
|
||||
if nam_num not in name_map:
|
||||
break
|
||||
count += 1
|
||||
name_map[nn] = key
|
||||
id_map[key] = nn
|
||||
name_map[nam_num] = key
|
||||
id_map[key] = nam_num
|
||||
else:
|
||||
name_map[n] = key
|
||||
id_map[key] = n
|
||||
id_name[key] = get_name(pn, sn, count)
|
||||
name_map[nam] = key
|
||||
id_map[key] = nam
|
||||
id_name[key] = get_name(pnam, snam, count)
|
||||
|
||||
with open(self.filename, "w", encoding='utf_8') as f:
|
||||
try:
|
||||
with open(self.filename, "w", encoding='utf_8') as file:
|
||||
return self._export_data(file, id_name, id_map)
|
||||
except IOError as msg:
|
||||
msg2 = _("Could not create %s") % self.filename
|
||||
ErrorDialog(msg2, str(msg), parent=self.option_box.window)
|
||||
return False
|
||||
|
||||
for key in self.plist:
|
||||
self.update()
|
||||
p = self.db.get_person_from_handle(key)
|
||||
name = id_name[key]
|
||||
father = mother = email = web = ""
|
||||
def _export_data(self, file, id_name, id_map):
|
||||
""" file export processing """
|
||||
for key in self.plist:
|
||||
self.update()
|
||||
pers = self.db.get_person_from_handle(key)
|
||||
name = id_name[key]
|
||||
father = mother = email = web = ""
|
||||
|
||||
family_handle = p.get_main_parents_family_handle()
|
||||
if family_handle:
|
||||
family = self.db.get_family_from_handle(family_handle)
|
||||
if family.get_father_handle() and \
|
||||
family.get_father_handle() in id_map:
|
||||
father = id_map[family.get_father_handle()]
|
||||
if family.get_mother_handle() and \
|
||||
family.get_mother_handle() in id_map:
|
||||
mother = id_map[family.get_mother_handle()]
|
||||
family_handle = pers.get_main_parents_family_handle()
|
||||
if family_handle:
|
||||
family = self.db.get_family_from_handle(family_handle)
|
||||
if family.get_father_handle() and \
|
||||
family.get_father_handle() in id_map:
|
||||
father = id_map[family.get_father_handle()]
|
||||
if family.get_mother_handle() and \
|
||||
family.get_mother_handle() in id_map:
|
||||
mother = id_map[family.get_mother_handle()]
|
||||
|
||||
#
|
||||
# Calculate Date
|
||||
#
|
||||
birth_ref = p.get_birth_ref()
|
||||
death_ref = p.get_death_ref()
|
||||
if birth_ref:
|
||||
birth_event = self.db.get_event_from_handle(birth_ref.ref)
|
||||
birth = birth_event.get_date_object()
|
||||
#
|
||||
# Calculate Date
|
||||
#
|
||||
birth_ref = pers.get_birth_ref()
|
||||
death_ref = pers.get_death_ref()
|
||||
if birth_ref:
|
||||
birth_event = self.db.get_event_from_handle(birth_ref.ref)
|
||||
birth = birth_event.get_date_object()
|
||||
else:
|
||||
birth = None
|
||||
if death_ref:
|
||||
death_event = self.db.get_event_from_handle(death_ref.ref)
|
||||
death = death_event.get_date_object()
|
||||
else:
|
||||
death = None
|
||||
|
||||
#if self.restrict:
|
||||
# alive = probably_alive(pers, self.db)
|
||||
#else:
|
||||
# alive = 0
|
||||
|
||||
if birth:
|
||||
if death:
|
||||
dates = "%s-%s" % (fdate(birth), fdate(death))
|
||||
else:
|
||||
birth = None
|
||||
if death_ref:
|
||||
death_event = self.db.get_event_from_handle(death_ref.ref)
|
||||
death = death_event.get_date_object()
|
||||
dates = fdate(birth)
|
||||
else:
|
||||
if death:
|
||||
dates = fdate(death)
|
||||
else:
|
||||
death = None
|
||||
dates = ""
|
||||
|
||||
#if self.restrict:
|
||||
# alive = probably_alive(p, self.db)
|
||||
#else:
|
||||
# alive = 0
|
||||
file.write('%s;%s;%s;%s;%s;%s\n' %
|
||||
(name, father, mother, email, web, dates))
|
||||
|
||||
if birth:
|
||||
if death:
|
||||
dates = "%s-%s" % (fdate(birth), fdate(death))
|
||||
else:
|
||||
dates = fdate(birth)
|
||||
else:
|
||||
if death:
|
||||
dates = fdate(death)
|
||||
else:
|
||||
dates = ""
|
||||
return True
|
||||
|
||||
f.write('%s;%s;%s;%s;%s;%s\n' % (name, father, mother, email, web,
|
||||
dates))
|
||||
|
||||
return True
|
||||
|
||||
def fdate(val):
|
||||
""" return properly formatted date """
|
||||
if val.get_year_valid():
|
||||
if val.get_month_valid():
|
||||
if val.get_day_valid():
|
||||
return "%d/%d/%d" % (val.get_day(), val.get_month(),
|
||||
val.get_year())
|
||||
else:
|
||||
return "%d/%d" % (val.get_month(), val.get_year())
|
||||
else:
|
||||
return "%d" % val.get_year()
|
||||
else:
|
||||
return ""
|
||||
return "%d/%d" % (val.get_month(), val.get_year())
|
||||
return "%d" % val.get_year()
|
||||
return ""
|
||||
|
||||
|
||||
def get_name(name, surname, count):
|
||||
"""returns a name string built from the components of the Name
|
||||
instance, in the form of Firstname Surname"""
|
||||
|
||||
return (name.first_name + ' ' +
|
||||
surname +
|
||||
(str(count) if count != -1 else '') +
|
||||
(', ' +name.suffix if name.suffix else '')
|
||||
)
|
||||
surname +
|
||||
(str(count) if count != -1 else '') +
|
||||
(', ' + name.suffix if name.suffix else ''))
|
||||
|
||||
@@ -50,6 +50,7 @@ class PersonDetails(Gramplet):
|
||||
self.gui.get_container_widget().remove(self.gui.textview)
|
||||
self.gui.get_container_widget().add(self.gui.WIDGET)
|
||||
self.uistate.connect('nameformat-changed', self.update)
|
||||
self.uistate.connect('placeformat-changed', self.update)
|
||||
|
||||
def build_gui(self):
|
||||
"""
|
||||
|
||||
@@ -1072,7 +1072,7 @@ class FamilyLinesReport(Report):
|
||||
|
||||
def get_event_place(self, event):
|
||||
""" get the place of the event """
|
||||
place_text = None
|
||||
place_text = ''
|
||||
place_handle = event.get_place_handle()
|
||||
if place_handle:
|
||||
place = self._db.get_place_from_handle(place_handle)
|
||||
|
||||
@@ -157,6 +157,7 @@ class BasePersonView(ListView):
|
||||
})
|
||||
|
||||
uistate.connect('nameformat-changed', self.build_tree)
|
||||
uistate.connect('placeformat-changed', self.build_tree)
|
||||
|
||||
self.additional_uis.append(self.additional_ui())
|
||||
|
||||
|
||||
@@ -130,6 +130,9 @@ class PlaceBaseView(ListView):
|
||||
'<PRIMARY>BackSpace' : self.key_delete,
|
||||
})
|
||||
self.maptoolbtn = None
|
||||
|
||||
uistate.connect('placeformat-changed', self.build_tree)
|
||||
|
||||
self.additional_uis.append(self.additional_ui())
|
||||
|
||||
def navigation_type(self):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -226,7 +226,7 @@ class Printinfo:
|
||||
This class must first be initialized with set_class_vars
|
||||
"""
|
||||
def __init__(self, doc, database, numbering, showmarriage, showdivorce,
|
||||
name_display, rlocale, want_ids):
|
||||
name_display, rlocale, want_ids, pformat):
|
||||
#classes
|
||||
self._name_display = name_display
|
||||
self.doc = doc
|
||||
@@ -238,6 +238,7 @@ class Printinfo:
|
||||
self.want_ids = want_ids
|
||||
self._ = rlocale.translation.sgettext # needed for English
|
||||
self._get_date = rlocale.get_date
|
||||
self.pformat = pformat
|
||||
|
||||
def __date_place(self, event):
|
||||
""" return the date and/or place an event happened """
|
||||
@@ -245,7 +246,7 @@ class Printinfo:
|
||||
date = self._get_date(event.get_date_object())
|
||||
place_handle = event.get_place_handle()
|
||||
if place_handle:
|
||||
place = _pd.display_event(self.database, event)
|
||||
place = _pd.display_event(self.database, event, self.pformat)
|
||||
return("%(event_abbrev)s %(date)s - %(place)s" % {
|
||||
'event_abbrev': event.type.get_abbreviation(self._),
|
||||
'date' : date,
|
||||
@@ -474,9 +475,11 @@ class DescendantReport(Report):
|
||||
|
||||
stdoptions.run_name_format_option(self, menu)
|
||||
|
||||
pformat = menu.get_option_by_name("place_format").get_value()
|
||||
|
||||
self.obj_print = Printinfo(self.doc, self.database, obj, marrs, divs,
|
||||
self._name_display, self._locale,
|
||||
self.want_ids)
|
||||
self.want_ids, pformat)
|
||||
|
||||
def write_report(self):
|
||||
self.doc.start_paragraph("DR-Title")
|
||||
@@ -555,6 +558,8 @@ class DescendantOptions(MenuReportOptions):
|
||||
|
||||
stdoptions.add_name_format_option(menu, category_name)
|
||||
|
||||
stdoptions.add_place_format_option(menu, category_name)
|
||||
|
||||
stdoptions.add_private_data_option(menu, category_name)
|
||||
|
||||
stdoptions.add_living_people_option(menu, category_name)
|
||||
|
||||
@@ -168,6 +168,8 @@ class DetAncestorReport(Report):
|
||||
stdoptions.run_name_format_option(self, menu)
|
||||
self._nd = self._name_display
|
||||
|
||||
self.place_format = menu.get_option_by_name("place_format").get_value()
|
||||
|
||||
self.gen_handles = {}
|
||||
self.prev_gen_handles = {}
|
||||
|
||||
@@ -440,7 +442,7 @@ class DetAncestorReport(Report):
|
||||
else:
|
||||
date = event.get_date_object().get_year()
|
||||
|
||||
place = _pd.display_event(self._db, event)
|
||||
place = _pd.display_event(self._db, event, self.place_format)
|
||||
|
||||
self.doc.start_paragraph('DAR-MoreDetails')
|
||||
if date and place:
|
||||
@@ -848,6 +850,8 @@ class DetAncestorOptions(MenuReportOptions):
|
||||
|
||||
stdoptions.add_name_format_option(menu, category)
|
||||
|
||||
stdoptions.add_place_format_option(menu, category)
|
||||
|
||||
stdoptions.add_private_data_option(menu, category)
|
||||
|
||||
stdoptions.add_living_people_option(menu, category)
|
||||
|
||||
@@ -201,6 +201,8 @@ class DetDescendantReport(Report):
|
||||
|
||||
stdoptions.run_name_format_option(self, menu)
|
||||
|
||||
self.place_format = menu.get_option_by_name("place_format").get_value()
|
||||
|
||||
self.__narrator = Narrator(self._db, self.verbose,
|
||||
use_call, use_fulldate,
|
||||
empty_date, empty_place,
|
||||
@@ -474,7 +476,7 @@ class DetDescendantReport(Report):
|
||||
else:
|
||||
date = event.get_date_object().get_year()
|
||||
|
||||
place = _pd.display_event(self._db, event)
|
||||
place = _pd.display_event(self._db, event, self.place_format)
|
||||
|
||||
self.doc.start_paragraph('DDR-MoreDetails')
|
||||
event_name = self._get_type(event.get_type())
|
||||
@@ -1039,6 +1041,8 @@ class DetDescendantOptions(MenuReportOptions):
|
||||
|
||||
stdoptions.add_name_format_option(menu, category)
|
||||
|
||||
stdoptions.add_place_format_option(menu, category)
|
||||
|
||||
stdoptions.add_private_data_option(menu, category)
|
||||
|
||||
stdoptions.add_living_people_option(menu, category)
|
||||
|
||||
@@ -117,13 +117,15 @@ class FamilyGroup(Report):
|
||||
|
||||
stdoptions.run_name_format_option(self, menu)
|
||||
|
||||
self.place_format = menu.get_option_by_name("place_format").get_value()
|
||||
|
||||
def dump_parent_event(self, name, event):
|
||||
place = ""
|
||||
date = ""
|
||||
descr = ""
|
||||
if event:
|
||||
date = self._get_date(event.get_date_object())
|
||||
place = _pd.display_event(self.db, event)
|
||||
place = _pd.display_event(self.db, event, self.place_format)
|
||||
if place is None:
|
||||
place = ''
|
||||
descr = event.get_description()
|
||||
@@ -438,7 +440,7 @@ class FamilyGroup(Report):
|
||||
date = self._get_date(event.get_date_object())
|
||||
place_handle = event.get_place_handle()
|
||||
if place_handle:
|
||||
place = _pd.display_event(self.db, event)
|
||||
place = _pd.display_event(self.db, event, self.place_format)
|
||||
if place is None:
|
||||
place = ''
|
||||
|
||||
@@ -736,6 +738,8 @@ class FamilyGroupOptions(MenuReportOptions):
|
||||
|
||||
self.__update_filters()
|
||||
|
||||
stdoptions.add_place_format_option(menu, category_name)
|
||||
|
||||
stdoptions.add_private_data_option(menu, category_name)
|
||||
|
||||
stdoptions.add_living_people_option(menu, category_name)
|
||||
|
||||
@@ -157,6 +157,8 @@ class IndivCompleteReport(Report):
|
||||
|
||||
stdoptions.run_name_format_option(self, menu)
|
||||
|
||||
self.place_format = menu.get_option_by_name("place_format").get_value()
|
||||
|
||||
self.home_person = self._db.get_default_person() # might be None
|
||||
self.increlname = menu.get_option_by_name('incl_relname').get_value()
|
||||
if self.increlname and self.home_person:
|
||||
@@ -183,7 +185,7 @@ class IndivCompleteReport(Report):
|
||||
place_handle = event.get_place_handle()
|
||||
if place_handle:
|
||||
place = self._db.get_place_from_handle(place_handle)
|
||||
place_name = _pd.display_event(self._db, event)
|
||||
place_name = _pd.display_event(self._db, event, self.place_format)
|
||||
place_endnote = self._cite_endnote(place)
|
||||
# make sure it's translated, so it can be used below, in "combine"
|
||||
ignore = _('%(str1)s in %(str2)s. ') % {'str1' : '', 'str2' : ''}
|
||||
@@ -518,7 +520,8 @@ class IndivCompleteReport(Report):
|
||||
place_handle = lds_ord.get_place_handle()
|
||||
if place_handle:
|
||||
place = self._db.get_place_from_handle(place_handle)
|
||||
place_name = _pd.display_event(self._db, lds_ord)
|
||||
place_name = _pd.display_event(self._db, lds_ord,
|
||||
self.place_format)
|
||||
place_endnote = self._cite_endnote(place)
|
||||
endnotes = self._cite_endnote(lds_ord, prior=place_endnote)
|
||||
self.doc.start_row()
|
||||
@@ -716,7 +719,8 @@ class IndivCompleteReport(Report):
|
||||
place_handle = lds_ord.get_place_handle()
|
||||
if place_handle:
|
||||
place = self._db.get_place_from_handle(place_handle)
|
||||
place_name = _pd.display_event(self._db, lds_ord)
|
||||
place_name = _pd.display_event(self._db, lds_ord,
|
||||
self.place_format)
|
||||
place_endnote = self._cite_endnote(place)
|
||||
endnotes = self._cite_endnote(lds_ord, prior=place_endnote)
|
||||
self.doc.start_row()
|
||||
@@ -1083,6 +1087,8 @@ class IndivCompleteOptions(MenuReportOptions):
|
||||
|
||||
self.__update_filters()
|
||||
|
||||
stdoptions.add_place_format_option(menu, category_name)
|
||||
|
||||
stdoptions.add_private_data_option(menu, category_name)
|
||||
|
||||
stdoptions.add_living_people_option(menu, category_name)
|
||||
|
||||
@@ -105,6 +105,8 @@ class PlaceReport(Report):
|
||||
stdoptions.run_name_format_option(self, menu)
|
||||
self._nd = self._name_display
|
||||
|
||||
self.place_format = menu.get_option_by_name("place_format").get_value()
|
||||
|
||||
filter_option = menu.get_option_by_name('filter')
|
||||
self.filter = filter_option.get_filter()
|
||||
|
||||
@@ -196,7 +198,7 @@ class PlaceReport(Report):
|
||||
place_names += ' (%s)' % place_name.get_language()
|
||||
place_details += [self._("places|All Names: %s") % place_names,]
|
||||
self.doc.start_paragraph("PLC-PlaceTitle")
|
||||
place_title = _pd.display(self._db, place)
|
||||
place_title = _pd.display(self._db, place, self.place_format)
|
||||
self.doc.write_text(("%(nbr)s. %(place)s") % {'nbr' : place_nbr,
|
||||
'place' : place_title})
|
||||
self.doc.end_paragraph()
|
||||
@@ -425,7 +427,7 @@ class PlaceOptions(MenuReportOptions):
|
||||
if subject:
|
||||
subject += " + "
|
||||
place = self.__db.get_place_from_gramps_id(place_id)
|
||||
subject += _pd.display(self.__db, place)
|
||||
subject += _pd.display(self.__db, place, self.place_format)
|
||||
return subject
|
||||
|
||||
def add_menu_options(self, menu):
|
||||
|
||||
@@ -108,6 +108,8 @@ class TagReport(Report):
|
||||
|
||||
stdoptions.run_name_format_option(self, menu)
|
||||
|
||||
self.place_format = menu.get_option_by_name("place_format").get_value()
|
||||
|
||||
def write_report(self):
|
||||
self.doc.start_paragraph("TR-Title")
|
||||
# feature request 2356: avoid genitive form
|
||||
@@ -439,7 +441,7 @@ class TagReport(Report):
|
||||
|
||||
for place_handle in place_list:
|
||||
place = self.database.get_place_from_handle(place_handle)
|
||||
place_title = _pd.display(self.database, place)
|
||||
place_title = _pd.display(self.database, place, self.place_format)
|
||||
|
||||
self.doc.start_row()
|
||||
|
||||
@@ -916,6 +918,8 @@ class TagOptions(MenuReportOptions):
|
||||
|
||||
stdoptions.add_name_format_option(menu, category_name)
|
||||
|
||||
stdoptions.add_place_format_option(menu, category_name)
|
||||
|
||||
stdoptions.add_private_data_option(menu, category_name)
|
||||
|
||||
stdoptions.add_living_people_option(menu, category_name)
|
||||
|
||||
@@ -129,6 +129,7 @@ class EventView(ListView):
|
||||
})
|
||||
|
||||
uistate.connect('nameformat-changed', self.build_tree)
|
||||
uistate.connect('placeformat-changed', self.build_tree)
|
||||
|
||||
self.additional_uis.append(self.additional_ui())
|
||||
|
||||
|
||||
@@ -528,8 +528,6 @@ class CairoPrintSave():
|
||||
pxwidth = round(context.get_width())
|
||||
pxheight = round(context.get_height())
|
||||
scale = min(pxwidth/self.widthpx, pxheight/self.heightpx)
|
||||
if scale > 1:
|
||||
scale = 1
|
||||
self.drawfunc(None, cr, scale=scale)
|
||||
|
||||
def on_paginate(self, operation, context):
|
||||
|
||||
@@ -516,8 +516,6 @@ class CairoPrintSave:
|
||||
pxwidth = round(context.get_width())
|
||||
pxheight = round(context.get_height())
|
||||
scale = min(pxwidth/self.widthpx, pxheight/self.heightpx)
|
||||
if scale > 1:
|
||||
scale = 1
|
||||
self.drawfunc(None, cr, scale=scale)
|
||||
|
||||
def on_paginate(self, operation, context):
|
||||
|
||||
@@ -523,8 +523,6 @@ class CairoPrintSave:
|
||||
pxwidth = round(context.get_width())
|
||||
pxheight = round(context.get_height())
|
||||
scale = min(pxwidth/self.widthpx, pxheight/self.heightpx)
|
||||
if scale > 1:
|
||||
scale = 1
|
||||
self.drawfunc(None, cr, scale=scale)
|
||||
|
||||
def on_paginate(self, operation, context):
|
||||
|
||||
@@ -536,6 +536,7 @@ class PedigreeView(NavigationView):
|
||||
self.dbstate = dbstate
|
||||
self.dbstate.connect('database-changed', self.change_db)
|
||||
uistate.connect('nameformat-changed', self.person_rebuild)
|
||||
uistate.connect('placeformat-changed', self.person_rebuild)
|
||||
|
||||
self.format_helper = FormattingHelper(self.dbstate)
|
||||
|
||||
|
||||
@@ -144,6 +144,7 @@ class RelationshipView(NavigationView):
|
||||
|
||||
dbstate.connect('database-changed', self.change_db)
|
||||
uistate.connect('nameformat-changed', self.build_tree)
|
||||
uistate.connect('placeformat-changed', self.build_tree)
|
||||
self.redrawing = False
|
||||
|
||||
self.child = None
|
||||
@@ -316,15 +317,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()
|
||||
@@ -588,9 +580,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)
|
||||
@@ -887,9 +877,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)
|
||||
@@ -941,9 +929,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)
|
||||
@@ -971,9 +957,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)
|
||||
@@ -993,9 +977,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)
|
||||
@@ -1038,7 +1019,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)
|
||||
@@ -1052,8 +1033,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 Person (%s)') % name[0])
|
||||
@@ -1072,8 +1051,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,
|
||||
@@ -1173,9 +1150,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'):
|
||||
@@ -1390,9 +1364,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)
|
||||
@@ -1437,9 +1409,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)
|
||||
@@ -1467,9 +1437,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)
|
||||
|
||||
@@ -612,17 +612,25 @@ class BasePage: # pylint: disable=C1001
|
||||
"""
|
||||
creates the event header row for all events
|
||||
"""
|
||||
trow = Html("tr")
|
||||
trow = Html("tr", close=None)
|
||||
trow.extend(
|
||||
Html("th", trans, class_=colclass, inline=True)
|
||||
for trans, colclass in [
|
||||
(self._("Event"), "ColumnEvent"),
|
||||
(self._("Date"), "ColumnDate"),
|
||||
(self._("Place"), "ColumnPlace"),
|
||||
(self._("Description"), "ColumnDescription"),
|
||||
(self._("Notes"), "ColumnNotes"),
|
||||
(self._("Sources"), "ColumnSources")]
|
||||
(self._("Description"), "ColumnDescription")]
|
||||
)
|
||||
trow += Html("/tr", close=None)
|
||||
trow2 = Html("tr", indent=False)
|
||||
trow2.extend(
|
||||
Html("th", trans, class_=colclass, colspan=opt, inline=True)
|
||||
for trans, colclass, opt in [
|
||||
("", "ColumnEvent", 1),
|
||||
(self._("Sources"), "ColumnSources", 1),
|
||||
(self._("Notes"), "ColumnNotes", 2)]
|
||||
)
|
||||
trow.extend(trow2)
|
||||
return trow
|
||||
|
||||
def display_event_row(self, event, event_ref, place_lat_long,
|
||||
@@ -672,6 +680,12 @@ class BasePage: # pylint: disable=C1001
|
||||
for (label, colclass, data) in event_data
|
||||
)
|
||||
|
||||
trow2 = Html("tr")
|
||||
trow2 += Html("td", "", class_="ColumnSources")
|
||||
# get event source references
|
||||
srcrefs = self.get_citation_links(event.get_citation_list()) or " "
|
||||
trow2 += Html("td", srcrefs, class_="ColumnSources")
|
||||
|
||||
# get event notes
|
||||
notelist = event.get_note_list()
|
||||
notelist.extend(event_ref.get_note_list())
|
||||
@@ -693,12 +707,9 @@ class BasePage: # pylint: disable=C1001
|
||||
if notelist:
|
||||
htmllist.extend(self.dump_notes(notelist))
|
||||
|
||||
trow += Html("td", htmllist, class_="ColumnNotes")
|
||||
|
||||
# get event source references
|
||||
srcrefs = self.get_citation_links(event.get_citation_list()) or " "
|
||||
trow += Html("td", srcrefs, class_="ColumnSources")
|
||||
trow2 += Html("td", htmllist, class_="ColumnNotes", colspan=2)
|
||||
|
||||
trow += trow2
|
||||
# return events table row to its callers
|
||||
return trow
|
||||
|
||||
@@ -1285,9 +1296,8 @@ class BasePage: # pylint: disable=C1001
|
||||
msg += self._('Last change was the %(date)s') % {'date' :
|
||||
last_modif}
|
||||
else:
|
||||
dat_txt = ' on %(date)s' % {'date' :
|
||||
self.rlocale.get_date(Today())}
|
||||
msg += self._(dat_txt)
|
||||
dat_txt = self._(' on %(date)s')
|
||||
msg += dat_txt % {'date' : self.rlocale.get_date(Today())}
|
||||
|
||||
origin1 = self.report.filter.get_name(self.rlocale)
|
||||
filt_number = self.report.options['filter']
|
||||
@@ -1485,7 +1495,7 @@ class BasePage: # pylint: disable=C1001
|
||||
("addressbook", self._("Address Book"),
|
||||
self.report.inc_addressbook),
|
||||
('contact', self._("Contact"), self.report.use_contact),
|
||||
('statistics', self._("Statistics"), True),
|
||||
('statistics', self._("Statistics"), self.report.inc_stats),
|
||||
(self.target_cal_uri, self._("Web Calendar"), self.usecal)
|
||||
]
|
||||
|
||||
@@ -2550,6 +2560,44 @@ class BasePage: # pylint: disable=C1001
|
||||
)
|
||||
tbody += trow
|
||||
|
||||
# display all related locations
|
||||
for placeref in place.get_placeref_list():
|
||||
place_date = self.rlocale.get_date(placeref.get_date_object())
|
||||
if place_date != "":
|
||||
parent_place = self.r_db.get_place_from_handle(placeref.ref)
|
||||
parent_name = parent_place.get_name().get_value()
|
||||
trow = Html('tr') + (
|
||||
Html("td", self._("Locations"), class_="ColumnAttribute",
|
||||
inline=True),
|
||||
Html("td", parent_name, class_="ColumnValue", inline=True),
|
||||
Html("td", place_date, class_="ColumnValue", inline=True)
|
||||
)
|
||||
tbody += trow
|
||||
|
||||
altloc = place.get_alternative_names()
|
||||
if altloc:
|
||||
tbody += Html("tr") + Html("td", " ", colspan=2)
|
||||
trow = Html("tr") + (
|
||||
Html("th", self._("Alternate Names"), colspan=1,
|
||||
class_="ColumnAttribute", inline=True),
|
||||
Html("th", self._("Language"), colspan=1,
|
||||
class_="ColumnAttribute", inline=True),
|
||||
Html("th", self._("Date range in which the name is valid."), colspan=1,
|
||||
class_="ColumnAttribute", inline=True),
|
||||
)
|
||||
tbody += trow
|
||||
for loc in altloc:
|
||||
place_date = self.rlocale.get_date(loc.date)
|
||||
trow = Html("tr") + (
|
||||
Html("td", loc.get_value(), class_="ColumnValue",
|
||||
inline=True),
|
||||
Html("td", loc.get_language(), class_="ColumnValue",
|
||||
inline=True),
|
||||
Html("td", place_date, class_="ColumnValue",
|
||||
inline=True),
|
||||
)
|
||||
tbody += trow
|
||||
|
||||
altloc = place.get_alternate_locations()
|
||||
if altloc:
|
||||
tbody += Html("tr") + Html("td", " ", colspan=2)
|
||||
@@ -2577,20 +2625,6 @@ class BasePage: # pylint: disable=C1001
|
||||
tbody += trow
|
||||
tbody += Html("tr") + Html("td", " ", colspan=2)
|
||||
|
||||
# display all related locations
|
||||
for placeref in place.get_placeref_list():
|
||||
place_date = self.rlocale.get_date(placeref.get_date_object())
|
||||
if place_date != "":
|
||||
parent_place = self.r_db.get_place_from_handle(placeref.ref)
|
||||
parent_name = parent_place.get_name().get_value()
|
||||
trow = Html('tr') + (
|
||||
Html("td", self._("Locations"), class_="ColumnAttribute",
|
||||
inline=True),
|
||||
Html("td", parent_name, class_="ColumnValue", inline=True),
|
||||
Html("td", place_date, class_="ColumnValue", inline=True)
|
||||
)
|
||||
tbody += trow
|
||||
|
||||
# return place table to its callers
|
||||
return table
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ from unicodedata import normalize
|
||||
from collections import defaultdict
|
||||
from hashlib import md5
|
||||
import re
|
||||
import gc
|
||||
import logging
|
||||
from xml.sax.saxutils import escape
|
||||
|
||||
@@ -859,4 +860,3 @@ def html_escape(text):
|
||||
text = text.replace("'", ''')
|
||||
|
||||
return text
|
||||
|
||||
|
||||
@@ -110,17 +110,18 @@ class EventPages(BasePage):
|
||||
for event_handle in event_handle_list:
|
||||
event = self.r_db.get_event_from_handle(event_handle)
|
||||
event_types.append(self._(event.get_type().xml_str()))
|
||||
with self.r_user.progress(_("Narrated Web Site Report"),
|
||||
_("Creating event pages"),
|
||||
message = _("Creating event pages")
|
||||
with self.r_user.progress(_("Narrated Web Site Report"), message,
|
||||
len(event_handle_list) + 1
|
||||
) as step:
|
||||
self.eventlistpage(self.report, title, event_types,
|
||||
event_handle_list)
|
||||
|
||||
index = 1
|
||||
for event_handle in event_handle_list:
|
||||
step()
|
||||
index += 1
|
||||
self.eventpage(self.report, title, event_handle)
|
||||
|
||||
step()
|
||||
self.eventlistpage(self.report, title, event_types,
|
||||
event_handle_list)
|
||||
|
||||
def eventlistpage(self, report, title, event_types, event_handle_list):
|
||||
"""
|
||||
|
||||
@@ -103,16 +103,18 @@ class FamilyPages(BasePage):
|
||||
for item in self.report.obj_dict[Family].items():
|
||||
LOG.debug(" %s", str(item))
|
||||
|
||||
with self.r_user.progress(_("Narrated Web Site Report"),
|
||||
_("Creating family pages..."),
|
||||
message = _("Creating family pages...")
|
||||
index = 1
|
||||
with self.r_user.progress(_("Narrated Web Site Report"), message,
|
||||
len(self.report.obj_dict[Family]) + 1
|
||||
) as step:
|
||||
self.familylistpage(self.report, title,
|
||||
self.report.obj_dict[Family].keys())
|
||||
|
||||
for family_handle in self.report.obj_dict[Family]:
|
||||
step()
|
||||
index += 1
|
||||
self.familypage(self.report, title, family_handle)
|
||||
step()
|
||||
self.familylistpage(self.report, title,
|
||||
self.report.obj_dict[Family].keys())
|
||||
|
||||
def familylistpage(self, report, title, fam_list):
|
||||
"""
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user