Compare commits

...

68 Commits

Author SHA1 Message Date
prculley 6ca3821f81 Release Gramps 5.0.0-beta1 2018-02-18 08:18:09 -06:00
niememat caf47940c7 Update finnish translation 2018-02-18 09:11:12 +02:00
Sam Manzi 6a78d5196d Update README for Gramps50 (#550)
* Update wiki links to 5.0
* Mention undocumented prerequisites
2018-02-18 13:15:43 +11:00
prculley fc0fdec25c Update Translation template and POTFILES.in 2018-02-17 16:36:19 -06:00
vantu5z 2adcc7d5a3 update Russian translation 2018-02-17 08:30:01 -03:00
Nick Hall fcaea3e1bb Replace backslash in image path for Windows 2018-02-16 19:33:46 +00:00
Nick Hall fc99f12568 Escape special LaTeX characters 2018-02-16 19:33:46 +00:00
Serge Noiraud 1e5a6e1f87 crash in narrative web if no unused media (#549) 2018-02-16 19:20:02 +01:00
Serge Noiraud db263ff905 Apply places changes to the narrativeweb. (#547)
* Apply places changes to the narrativeweb.

Also change order between locations and locations alternate names.

Fixes #10427

* Put the alternate locations back.

* Suppress extra line in Alternate Names section.
2018-02-16 11:10:01 +01:00
Nick Hall e202f5cd19 Copy output file from temporary directory 2018-02-14 22:43:40 +00:00
Nick Hall 2fbb9878c2 Fix character encoding for new tree category reports
Fixes #10418.
2018-02-14 18:41:45 +00:00
Leonhaeuser ba4453213a update German translation 2018-02-13 17:53:33 +01:00
prculley 5f133ffaf2 Fix Place Tree view for proper display after filter cleared.
Fixes #10416
2018-02-13 14:07:35 +00:00
Nick Hall cbac98894b Implement place formats 2018-02-13 13:58:23 +00:00
Serge Noiraud 3f368b2e02 Some dates are not translated in webcal (#545)
* Some dates are not translated in webcal

Fixes #10425

* Webcal link problem in the year glance page

* Link problems depending on LANGUAGES.
2018-02-13 10:01:17 +01:00
Sam Manzi 1e73ab5be7 Increment year to 2018 for About box. 2018-02-13 13:21:28 +11:00
niememat f428069ac6 Fix and updated translation in Finnish 2018-02-11 13:04:08 +02:00
niememat adf3772b7a Fix holidays in Finnish 2018-02-11 12:58:49 +02:00
SNoiraud 46cd191e49 Translation problem in narrative web. 2018-02-10 08:37:46 +01:00
Leonhaeuser 9afe0cc181 update German translation 2018-02-09 20:24:12 +01:00
prculley d3ed320636 fix Sidebar to resize better
Fixes #10334
Issue #10161
2018-02-08 23:35:32 +00:00
John Ralls 6f4a114fdc Update pyicu to v 2.0.3 2018-02-08 14:53:05 -08:00
John Ralls 5bec86c1f4 Consolidate Python2 and Python3 meta-modules.
GObject-Introspection now works with Python3 so the split isn't needed any more.
2018-02-08 14:53:05 -08:00
niememat 5b48691c3a Fix and updated translation in Finnish 2018-02-08 15:36:00 +02:00
Nick Hall be11466b35 Add tooltip to links in styled text editor
Fixes #10179.
2018-02-07 16:34:41 +00:00
John Ralls 0445004e7d Merge pull request #541 from jralls/accels. 2018-02-06 14:48:49 -08:00
John Ralls f688d376f5 [MacOS] Replace GDK_CONTROL_MASK with GDK_META_MASK in accelerators.
Fixes #10231 and works on all versions of Gtk3 by avoiding
GdkModifierIntent.
2018-02-06 14:46:55 -08:00
John Ralls 0a38a6352f 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.

Fixes #6943.
2018-02-06 14:46:09 -08:00
prculley 2410435d8c Fix Family Lines Graph when 'unknown' places are present
Fixes #10402
2018-02-06 22:31:40 +00:00
prculley 6f1d6fa6b4 Fix Export View to CSV when Unicode characters are present
Fixes #10404
2018-02-06 22:23:27 +00:00
prculley b1db542cb9 Fix several intl date displayers for missing parameter.
Fixes #10196
2018-02-06 22:19:23 +00:00
John Ralls 3d7479969f Update gramps.accel for gramps50 2018-02-05 14:56:34 -08:00
Zdeněk Hataš 8f4dffa1df Czech translation fixes 2018-01-31 20:31:32 +01:00
prculley ffe9f31b0b Some filter optimizations to avoid repeated parts of tree scan 2018-01-28 23:36:23 +00:00
Leonhaeuser b1af1c5435 update German translation 2018-01-27 14:55:03 +01:00
niememat 94b6fcb93f New update for Finnish translation 2018-01-27 15:19:57 +02:00
vantu5z 4b25dde6e2 update Russian translation 2018-01-27 15:56:34 +09:00
prculley f83c077a7e Fix view buttons order keeps changing on different startups
Fixes #10391
2018-01-26 22:49:59 +00:00
prculley 323d775541 Fix DescendentTree report; more space beneath Title
Issue #10377
2018-01-26 22:47:29 +00:00
prculley 4448234281 Fix DescendentTree report for crashes
Fixes #10377
2018-01-26 22:47:29 +00:00
prculley d31ced9ca5 Fix Book XML handler for unusual characters in report name
Issue #10387
2018-01-26 22:44:02 +00:00
prculley 9706af03b4 Fix 'Generate Book' dialog for bad transient parent 2018-01-26 22:44:02 +00:00
prculley b3ca17a90f Fix Book XML handler to deal with unusual characters in Book name
Fixes #10387
2018-01-26 22:44:02 +00:00
Nick Hall 4b13c95467 Add support for new genealogy tree report category
Issue #10223.
2018-01-26 22:24:29 +00:00
niememat 7864af3626 Fix and updated translation in Finnish 2018-01-26 21:58:21 +02:00
vantu5z 028f9582f5 Add color schemes to config 2018-01-26 17:30:13 +00:00
prculley cd32559282 Surname Editor; fix so changes made update Person 'Preferred Name'
Fixes #10254
2018-01-24 11:40:04 -06:00
prculley 3980fca903 Surname Editor, fix fields for better fit in minimum width dialogs 2018-01-24 11:40:04 -06:00
prculley 462b0ea20b Surname Editor; fix loss of data if using mouse to change fields
Fixes #9868, #6828, #6257
2018-01-24 11:40:04 -06:00
prculley ba4b3eaef6 Add Garbage collection after tools/reports
Fixes #10287
2018-01-22 21:14:27 +00:00
Leonhaeuser bf826637ab update German translation 2018-01-22 20:11:12 +01:00
Sam Manzi 316ff4454a Fix duplicate Add menu keybinding after PR #530 2018-01-22 13:40:19 +11:00
Paul Culley 3266625669 Main Window menu 'Add' keyboard Acellerators (Gramps50) (#530)
* Fix Add menu Accelerators so they don't activate on Shift-'x'

Fixes #10379

* Change Gramps 'Add' menu so 'New' is not used
2018-01-22 13:21:46 +11:00
niememat 90378f19c5 Fixed and updated translation in Finnish 2018-01-20 14:17:17 +02:00
vantu5z 78fb08aa12 update Russian translation 2018-01-20 18:27:48 +09:00
Paul Culley d081b75993 Export Web Family Tree; error on file write is now a message, not exception. (#524)
* Fix Export Web Family Tree for errors on file write

Fixes #10364

* Pylint on Web Family Tree export
2018-01-20 16:58:13 +11:00
Paul Culley 10e0e64ff4 Fix Citation Editor to Tab out of Confidence ComboBox (#526)
Fixes #10351
2018-01-20 16:28:15 +11:00
Serge Noiraud 3615e75d16 Narrative web: multiple problems. (#517)
* Narrative web: multiple problems.

1 - Added an option for the statistics page.
2 - We have problems with notes : pages are too large
3 - Difficult to see some page on a mobile phone when you have notes.
4 - Resizing of images on a mobile
5 - Maps are too bigs on a mobile
6 - Split the event line to have a narrower page
7 - The note is spanned on two columns (Place + Description)
7 - In media pages, the progress meter doesn't work well.
8 - Add a progress meter for the medialistpage

Issues #10344

* Narrative Web: several modifications + css files

* Stylesheet problems and reverse set_header

* Removing memory cleanup.
2018-01-18 09:03:11 +01:00
niememat 3c8cba0301 New update for Finnish translation 2018-01-18 09:58:43 +02:00
Nick Hall dde9ea8a90 Use None as the foreground colour for untagged rows in list views
The call to get_style_context is not required.  It also caused
problems in Windows due to excessive calls to the foreground_color
method.

Fixes #10365.
2018-01-13 17:20:23 +00:00
Nick Hall b7c39e51c7 Remove trailing whitespace 2018-01-09 19:51:46 +00:00
Nick Hall a216441029 Clear old custom name formats when database loaded 2018-01-09 19:09:16 +00:00
Nick Hall 89592545f3 Fix shading colour in relationship view for dark themes
Fixes #7749.
2018-01-09 18:26:26 +00:00
Nick Hall 70fff6253d Fix link colour for dark themes
Issue #7749.
2018-01-09 18:24:52 +00:00
Nick Hall 84e76de2b1 Fix default foreground colour in list views for dark themes
Issue #7749.
2018-01-09 18:21:23 +00:00
prculley fe289da983 Fix memory leak in ProgressMeter 2018-01-08 19:24:27 +00:00
prculley 371a8a8f89 fix Fan Chart for exception on right-click of person with no parent
Fixes #10349
2018-01-08 19:09:14 +00:00
prculley 1777cd4b29 Fix Fan Chart Print centering and scaling on Windows
Fixes #8460
2018-01-04 23:16:01 +00:00
120 changed files with 14647 additions and 10439 deletions
+1019 -754
View File
File diff suppressed because it is too large Load Diff
+196
View File
@@ -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
+13 -5
View File
@@ -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 )
+8 -11
View File
@@ -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
View File
@@ -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
+8 -8
View File
@@ -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 {
+5 -1
View File
@@ -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;
+4 -4
View File
@@ -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)" />
+1
View File
@@ -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)
+27 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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.")
+1 -1
View File
@@ -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
"""
+1 -1
View File
@@ -155,7 +155,7 @@ class DateDisplayEL(DateDisplay):
)
# this definition must agree with its "_display_gregorian" method
def _display_gregorian(self, date_val):
def _display_gregorian(self, date_val, **kwargs):
"""
display gregorian calendar date in different format
"""
+1 -1
View File
@@ -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
"""
+1 -1
View File
@@ -164,7 +164,7 @@ class DateDisplayNL(DateDisplay):
)
# this definition must agree with its "_display_gregorian" method
def _display_gregorian(self, date_val):
def _display_gregorian(self, date_val, **kwargs):
"""
display gregorian calendar date in different format
"""
+1 -1
View File
@@ -215,7 +215,7 @@ class DateDisplayPL(DateDisplay):
"XII"
)
def _display_gregorian(self, date_val):
def _display_gregorian(self, date_val, **kwargs):
"""
display gregorian calendar date in different format
"""
+1 -1
View File
@@ -240,7 +240,7 @@ class DateDisplaySR_Base(DateDisplay):
"VII", "VIII", "IX", "X", "XI", "XII"
)
def _display_gregorian(self, date_val):
def _display_gregorian(self, date_val, **kwargs):
"""
display gregorian calendar date in different format
"""
+5
View File
@@ -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
View File
@@ -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
View File
@@ -27,7 +27,7 @@ The "plug" package for handling plugins in Gramps.
from ._plugin import Plugin
from ._pluginreg import (PluginData, PluginRegister, REPORT, TOOL,
CATEGORY_TEXT, CATEGORY_DRAW, CATEGORY_CODE,
CATEGORY_WEB, CATEGORY_BOOK, CATEGORY_GRAPHVIZ,
CATEGORY_WEB, CATEGORY_BOOK, CATEGORY_GRAPHVIZ, CATEGORY_TREE,
TOOL_DEBUG, TOOL_ANAL, TOOL_DBPROC, TOOL_DBFIX, TOOL_REVCTL,
TOOL_UTILS, CATEGORY_QR_MISC, CATEGORY_QR_PERSON,
CATEGORY_QR_FAMILY, CATEGORY_QR_EVENT, CATEGORY_QR_SOURCE,
@@ -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"]
+6 -6
View File
@@ -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]
+1
View File
@@ -37,3 +37,4 @@ from .textdoc import TextDoc, IndexMark,INDEX_TYPE_ALP, INDEX_TYPE_TOC,\
URL_PATTERN, LOCAL_HYPERLINK, LOCAL_TARGET
from .drawdoc import DrawDoc
from .graphdoc import GVDoc
from .treedoc import TreeDoc
+660
View File
@@ -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}]
+13 -6
View File
@@ -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)
+2 -1
View File
@@ -39,7 +39,7 @@ import os
# Report categories
from .. import (CATEGORY_TEXT, CATEGORY_DRAW, CATEGORY_CODE, CATEGORY_WEB,
CATEGORY_BOOK, CATEGORY_GRAPHVIZ)
CATEGORY_BOOK, CATEGORY_GRAPHVIZ, CATEGORY_TREE)
standalone_categories = {
CATEGORY_TEXT : ("RepText", _("Text Reports")),
@@ -48,6 +48,7 @@ standalone_categories = {
CATEGORY_WEB : ("RepWeb", _("Web Pages")),
CATEGORY_BOOK : ("RepBook", _("Books")),
CATEGORY_GRAPHVIZ : ("Graphs", _("Graphs")),
CATEGORY_TREE : ("Trees", _("Trees")),
}
book_categories = {
CATEGORY_TEXT : _("Text"),
+14
View File
@@ -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
+3 -1
View File
@@ -31,6 +31,7 @@ import csv
#
#-------------------------------------------------------------------------
from .tabbeddoc import *
from ...constfunc import win
class CSVTab(TabbedDoc):
@@ -48,7 +49,8 @@ class CSVTab(TabbedDoc):
else:
self.filename = filename
self.f = open(self.filename, "w")
self.f = open(self.filename, "w",
encoding='utf_8_sig' if win() else 'utf_8')
self.writer = csv.writer(self.f)
def close(self):
+148 -99
View File
@@ -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'),
+8
View File
@@ -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)
+1
View File
@@ -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,
+11 -4
View File
@@ -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)
+37 -15
View File
@@ -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)
+5 -4
View File
@@ -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)
+157
View File
@@ -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
View File
@@ -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])
+1 -1
View File
@@ -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)
+254
View File
@@ -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>
+9
View File
@@ -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;
+3 -3
View File
@@ -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()
+2 -2
View File
@@ -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.
"""
+36 -226
View File
@@ -1,12 +1,7 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2007-2008 Brian G. Matherly
# Copyright (C) 2007-2009 Stephane Charette
# Copyright (C) 2009 Gary Burton
# Contribution 2009 by Bob Ham <rah@bash.sh>
# Copyright (C) 2010 Jakim Friant
# Copyright (C) 2012-2013 Paul Franklin
# Copyright (C) 2017 Nick Hall
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -23,174 +18,42 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
""" 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
+5 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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):
"""
+4 -6
View File
@@ -278,14 +278,12 @@ class ListView(NavigationView):
def foreground_color(self, column, renderer, model, iter_, data=None):
'''
Set the foreground color of the cell renderer. We use a cell data
function because we don't want to set the color of untagged rows.
function because there is a problem returning None from a model.
'''
fg_color = model.get_value(iter_, model.color_column())
#for color errors, typically color column is badly set
if fg_color:
renderer.set_property('foreground', fg_color)
else:
LOG.debug('Bad color set: ' + str(fg_color))
if fg_color == '':
fg_color = None
renderer.set_property('foreground', fg_color)
def set_active(self):
"""
+20 -4
View File
@@ -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)
+1 -1
View File
@@ -208,7 +208,7 @@ class EventModel(FlatBaseModel):
tag_handle = data[0]
cached, tag_color = self.get_cached_value(tag_handle, "TAG_COLOR")
if not cached:
tag_color = "#000000000000"
tag_color = ""
tag_priority = None
for handle in data[COLUMN_TAGS]:
tag = self.db.get_tag_from_handle(handle)
+1 -1
View File
@@ -220,7 +220,7 @@ class FamilyModel(FlatBaseModel):
tag_handle = data[0]
cached, tag_color = self.get_cached_value(tag_handle, "TAG_COLOR")
if not cached:
tag_color = "#000000000000"
tag_color = ""
tag_priority = None
for handle in data[13]:
tag = self.db.get_tag_from_handle(handle)
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -253,7 +253,7 @@ class RepositoryModel(FlatBaseModel):
tag_handle = data[0]
cached, tag_color = self.get_cached_value(tag_handle, "TAG_COLOR")
if not cached:
tag_color = "#000000000000"
tag_color = ""
tag_priority = None
for handle in data[8]:
tag = self.db.get_tag_from_handle(handle)
+1 -1
View File
@@ -143,7 +143,7 @@ class SourceModel(FlatBaseModel):
tag_handle = data[0]
cached, value = self.get_cached_value(tag_handle, "TAG_COLOR")
if not cached:
tag_color = "#000000000000"
tag_color = ""
tag_priority = None
for handle in data[11]:
tag = self.db.get_tag_from_handle(handle)
+2 -1
View File
@@ -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:
+1
View File
@@ -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
+5 -1
View File
@@ -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()
+3 -1
View File
@@ -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
+1 -1
View File
@@ -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)
+2 -7
View File
@@ -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
+2 -6
View File
@@ -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
+58
View File
@@ -0,0 +1,58 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2018 Nick Hall
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
__all__ = ["ShadeBox"]
#-------------------------------------------------------------------------
#
# Standard python modules
#
#-------------------------------------------------------------------------
import logging
_LOG = logging.getLogger(".widgets.shadebox")
#-------------------------------------------------------------------------
#
# GTK/Gnome modules
#
#-------------------------------------------------------------------------
from gi.repository import Gtk
#-------------------------------------------------------------------------
#
# ShadeBox class
#
#-------------------------------------------------------------------------
class ShadeBox(Gtk.EventBox):
"""
An EventBox with a shaded background.
"""
def __init__(self, use_shade):
Gtk.EventBox.__init__(self)
self.use_shade = use_shade
def do_draw(self, cr):
if self.use_shade:
tv = Gtk.TextView()
tv_context = tv.get_style_context()
width = self.get_allocated_width()
height = self.get_allocated_height()
Gtk.render_background(tv_context, cr, 0, 0, width, height)
self.get_child().draw(cr)
+6 -8
View File
@@ -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):
"""
+7 -1
View File
@@ -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)
+5 -3
View File
@@ -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:
+94 -78
View File
@@ -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 ''))
+1
View File
@@ -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):
"""
+1 -1
View File
@@ -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)
+1
View File
@@ -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())
+3
View File
@@ -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):
+1 -1
View File
@@ -669,7 +669,7 @@ class TitleBox(BoxBase):
return
#fix me. width should be the printable area
self.width = PT2CM(self.doc.string_width(self.font, self.text))
self.height = PT2CM(self.font.get_size() * 1.2)
self.height = PT2CM(self.font.get_size() * 2)
def _get_names(self, persons, name_displayer):
""" A helper function that receives a list of persons and
+8 -3
View File
@@ -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)
+6 -2
View File
@@ -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)
+9 -3
View File
@@ -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)
+4 -2
View File
@@ -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):
+5 -1
View File
@@ -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)
+1
View File
@@ -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())
-2
View File
@@ -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):
-2
View File
@@ -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):
-2
View File
@@ -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):
+1
View File
@@ -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)
+9 -41
View File
@@ -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)
+61 -27
View File
@@ -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 "&nbsp;"
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 "&nbsp;"
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", "&nbsp;", 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", "&nbsp;", colspan=2)
@@ -2577,20 +2625,6 @@ class BasePage: # pylint: disable=C1001
tbody += trow
tbody += Html("tr") + Html("td", "&nbsp;", 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
+1 -1
View File
@@ -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("'", '&#39;')
return text
+7 -6
View File
@@ -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):
"""
+7 -5
View File
@@ -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