Compare commits
168 Commits
v5.0.0
...
FR10850_V1
| Author | SHA1 | Date | |
|---|---|---|---|
| d48dcc9c5f | |||
| 263a082afe | |||
| f2bc982c3d | |||
| dd9ddab849 | |||
| 3046b0586c | |||
| cfa278886b | |||
| 1c430a6b91 | |||
| b0eb712933 | |||
| 51a7ad9483 | |||
| d6e9d62fad | |||
| 3bac4a1036 | |||
| 1995a7068c | |||
| 5dc06d023d | |||
| d98a1857dc | |||
| 7da8811327 | |||
| 375773d657 | |||
| 01060f0b0d | |||
| 36779c1229 | |||
| 5b7f5164db | |||
| 15db2dd603 | |||
| 97b15322d4 | |||
| 824bf7e40f | |||
| 56fa261ef2 | |||
| 1e9a3841cd | |||
| e6ddedf6c6 | |||
| 325c3f22cc | |||
| 54be8d62f3 | |||
| a40eca36d9 | |||
| 4b17ca4724 | |||
| 69aaec11a5 | |||
| 896b77a165 | |||
| 1b60193f77 | |||
| 1efab19f1e | |||
| e667431dd5 | |||
| cc7e4fda85 | |||
| 2490e2d07d | |||
| e5c5a210b4 | |||
| 04a40c7a50 | |||
| 44c8e3c0f0 | |||
| 6f7242a773 | |||
| 358c3fc967 | |||
| f1ca280441 | |||
| 482cecaa7e | |||
| b6c57ab3c3 | |||
| f3b5f75e37 | |||
| 7d9f4dcc80 | |||
| 224748eb96 | |||
| 2aec83f057 | |||
| fd399323a6 | |||
| bb6b3edee2 | |||
| 3f7ea4418c | |||
| efcc115aa7 | |||
| 893c715a61 | |||
| 6bab78df21 | |||
| 29019eed98 | |||
| d8c006e0f7 | |||
| b8438dddeb | |||
| 4ba28c637e | |||
| ae84040bc7 | |||
| 51e294f748 | |||
| cb65fd93ca | |||
| 5b9600d835 | |||
| cf80d17040 | |||
| d4f906f22d | |||
| d189e334d8 | |||
| a48a94d872 | |||
| 8e5dc220dc | |||
| dca2f610dc | |||
| 3d1833e307 | |||
| aa221cc3c7 | |||
| 51b7d1c420 | |||
| 674d286f87 | |||
| 0161c4b917 | |||
| de31a42fc8 | |||
| 9c508de5fd | |||
| 1b00d95ce4 | |||
| 78870decb8 | |||
| 1a5696eb2c | |||
| 269d38da2c | |||
| 99f77b0a88 | |||
| 264fdda4d0 | |||
| 8092b88ad4 | |||
| 3251e2ae88 | |||
| f718c5f8b4 | |||
| a96a446b47 | |||
| 5658411acf | |||
| a4403d719e | |||
| 8597a10c40 | |||
| d0c0045dc1 | |||
| 868abdc0d9 | |||
| ebf88bf5c5 | |||
| c3814a4e42 | |||
| 2a0b3afa4b | |||
| 52fe365919 | |||
| 414aac4c7e | |||
| 037e26a5a3 | |||
| 158d3a993b | |||
| 2ef35174d1 | |||
| 3249938b88 | |||
| 9b18083e48 | |||
| 04bdeb8321 | |||
| 67061d58e2 | |||
| bc39dda9e7 | |||
| 257275f169 | |||
| 9e61809b17 | |||
| 9e2b935db1 | |||
| 9a386574d8 | |||
| 90bdb0a840 | |||
| ae322dbdc8 | |||
| 208feceb03 | |||
| 32dd3a2e14 | |||
| 0ae51c9522 | |||
| 59bd9f040d | |||
| df00dd1f32 | |||
| ca49b56774 | |||
| 19f8c3561c | |||
| 79ff9bc216 | |||
| b54672d28d | |||
| 6b42d71158 | |||
| ac593d814d | |||
| 617e2212c8 | |||
| ad15ef7961 | |||
| 406faa5603 | |||
| 4e0d562208 | |||
| 43ef686622 | |||
| d59fe6b2af | |||
| d677a1a785 | |||
| be776d6a31 | |||
| 1482fedb9c | |||
| 00f8df60ce | |||
| f31caf1ea4 | |||
| 8ebb4d35fa | |||
| c5f2717623 | |||
| e5d5cfbd3a | |||
| bd5f6e4711 | |||
| e15deff4ed | |||
| 9d906c29a2 | |||
| 6577d43e4e | |||
| 1a065485e3 | |||
| 009783a59a | |||
| 1b4f70c808 | |||
| dd4a727521 | |||
| ebb7111f25 | |||
| 86fd14613e | |||
| 5276461239 | |||
| 40f47bc22d | |||
| 8150403ccb | |||
| b43d94ab16 | |||
| 849290a3b7 | |||
| 0b617e9fd6 | |||
| 6eb435142e | |||
| e14ea777d5 | |||
| 46d73a16a0 | |||
| 4d2b918d39 | |||
| 4acfdcce52 | |||
| f3b3eec211 | |||
| a840566e02 | |||
| f6a2199f68 | |||
| dfc9dde32d | |||
| 50f93f54ec | |||
| 9cb1d96725 | |||
| 40013dccc3 | |||
| 24af1b8420 | |||
| 213d656df4 | |||
| c09b4e96d2 | |||
| a80875e00c | |||
| bbdedb5097 | |||
| dd0671b111 |
@@ -23,5 +23,8 @@ test/data
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
|
||||
# Glade temp files
|
||||
*~
|
||||
|
||||
+6
-6
@@ -72,7 +72,7 @@ install:
|
||||
# by the automatic git checkout.
|
||||
|
||||
# Download Sean Ross-Ross's Pure Python module containing a framework to
|
||||
# manipulate and analyze python ast�s and bytecode. This is loaded to
|
||||
# manipulate and analyze python ast�s and bytecode. This is loaded to
|
||||
# /home/travis/build/gramps-project/gramps/meta
|
||||
# FIXME: This should be loaded from the release directory at
|
||||
# https://pypi.python.org/pypi/meta
|
||||
@@ -95,11 +95,11 @@ before_script:
|
||||
# set GRAMPS_RESOURCES for locale, data,image and documentation
|
||||
- export GRAMPS_RESOURCES=.
|
||||
# Install addons
|
||||
- mkdir -p ~/.gramps/gramps50/plugins/
|
||||
- wget https://github.com/gramps-project/addons/raw/master/gramps50/download/CliMerge.addon.tgz
|
||||
- tar -C ~/.gramps/gramps50/plugins -xzf CliMerge.addon.tgz
|
||||
- wget https://github.com/gramps-project/addons/raw/master/gramps50/download/ExportRaw.addon.tgz
|
||||
- tar -C ~/.gramps/gramps50/plugins -xzf ExportRaw.addon.tgz
|
||||
- mkdir -p ~/.gramps/gramps51/plugins/
|
||||
- wget https://github.com/gramps-project/addons/raw/master/gramps51/download/CliMerge.addon.tgz
|
||||
- tar -C ~/.gramps/gramps51/plugins -xzf CliMerge.addon.tgz
|
||||
- wget https://github.com/gramps-project/addons/raw/master/gramps51/download/ExportRaw.addon.tgz
|
||||
- tar -C ~/.gramps/gramps51/plugins -xzf ExportRaw.addon.tgz
|
||||
|
||||
script:
|
||||
# Ignore the virtualenv entirely. Use nosetests3, python3 (3.4.0) and coverage
|
||||
|
||||
+1
-3
@@ -1,5 +1,5 @@
|
||||
include ChangeLog
|
||||
include AUTHORS
|
||||
include ChangeLog
|
||||
include COPYING
|
||||
include FAQ
|
||||
include Gramps.py
|
||||
@@ -10,11 +10,9 @@ include RELEASE_NOTES
|
||||
include TODO
|
||||
include CONTRIBUTING
|
||||
include TestPlan.txt
|
||||
recursive-include bash *
|
||||
recursive-include data *
|
||||
recursive-include debian *
|
||||
recursive-include docs *
|
||||
recursive-include example *
|
||||
recursive-include gramps *
|
||||
recursive-include help *
|
||||
recursive-include images *
|
||||
|
||||
@@ -11,7 +11,7 @@ Requirements
|
||||
The following packages **MUST** be installed in order for Gramps to work:
|
||||
|
||||
* **Python** 3.2 or greater - The programming language used by Gramps. https://www.python.org/
|
||||
* **GTK** 3.10 or greater - A cross-platform widget toolkit for creating graphical user interfaces. http://www.gtk.org/
|
||||
* **GTK** 3.12 or greater - A cross-platform widget toolkit for creating graphical user interfaces. http://www.gtk.org/
|
||||
* **pygobject** 3.12 or greater - Python Bindings for GLib/GObject/GIO/GTK+ https://wiki.gnome.org/Projects/PyGObject
|
||||
|
||||
The following three packages with GObject Introspection bindings (the gi packages)
|
||||
@@ -103,6 +103,18 @@ The following packages are optional:
|
||||
|
||||
More font support in the reports
|
||||
|
||||
* **geocodeglib**
|
||||
|
||||
A library use to associate a geographical position (latitude, longitude)
|
||||
to a place name. This is used if you already have osmgpsmap installed.
|
||||
If installed, when you add or link a place from the map, you have a red line
|
||||
at the end of the table for selection.
|
||||
Debian, Ubuntu, ... : gir1.2-geocodeglib-1.0
|
||||
Fedora, Redhat, ... : geocode-glib
|
||||
openSUSE : geocode-glib
|
||||
ArchLinux : geocode-glib
|
||||
...
|
||||
|
||||
Optional packages required by Third-party Addons
|
||||
------------------------------------------------
|
||||
|
||||
|
||||
@@ -330,6 +330,10 @@ table.IndividualList td.ColumnSurname {
|
||||
content: "";
|
||||
}
|
||||
|
||||
table.eventlist tbody tr td.ColumnSources {
|
||||
width: 5%;
|
||||
}
|
||||
|
||||
/* Gallery
|
||||
----------------------------------------------------- */
|
||||
#GalleryNav {
|
||||
|
||||
@@ -489,7 +489,7 @@ table.eventlist tbody tr td.ColumnNotes {
|
||||
width: 20%;
|
||||
}
|
||||
table.eventlist tbody tr td.ColumnSources {
|
||||
width: 17%;
|
||||
width: 5%;
|
||||
}
|
||||
table.eventlist tbody tr td.ColumnPerson {
|
||||
width: 35%;
|
||||
|
||||
@@ -373,6 +373,10 @@ table.IndividualList td.ColumnSurname {
|
||||
content: "";
|
||||
}
|
||||
|
||||
table.eventlist tbody tr td.ColumnSources {
|
||||
width: 5%;
|
||||
}
|
||||
|
||||
/* Gallery
|
||||
----------------------------------------------------- */
|
||||
#GalleryNav {
|
||||
|
||||
@@ -366,6 +366,10 @@ table.IndividualList td.ColumnSurname {
|
||||
content: "";
|
||||
}
|
||||
|
||||
table.eventlist tbody tr td.ColumnSources {
|
||||
width: 5%;
|
||||
}
|
||||
|
||||
/* Gallery
|
||||
----------------------------------------------------- */
|
||||
#GalleryNav {
|
||||
|
||||
@@ -366,6 +366,10 @@ table.IndividualList td.ColumnSurname {
|
||||
content: "";
|
||||
}
|
||||
|
||||
table.eventlist tbody tr td.ColumnSources {
|
||||
width: 5%;
|
||||
}
|
||||
|
||||
/* Gallery
|
||||
----------------------------------------------------- */
|
||||
#GalleryNav {
|
||||
|
||||
@@ -366,6 +366,10 @@ table.IndividualList td.ColumnSurname {
|
||||
content: "";
|
||||
}
|
||||
|
||||
table.eventlist tbody tr td.ColumnSources {
|
||||
width: 5%;
|
||||
}
|
||||
|
||||
/* Gallery
|
||||
----------------------------------------------------- */
|
||||
#GalleryNav {
|
||||
|
||||
@@ -32,7 +32,6 @@ body {
|
||||
background-color: #00029D;
|
||||
color: #00029D;
|
||||
width: 100%;
|
||||
padding: 0px 14px;
|
||||
}
|
||||
|
||||
/* Navigation Menus
|
||||
|
||||
@@ -312,6 +312,10 @@ table#SortByCount thead th.ColumnQuantity a:after {
|
||||
content: "";
|
||||
}
|
||||
|
||||
table.eventlist tbody tr td.ColumnSources {
|
||||
width: 5%;
|
||||
}
|
||||
|
||||
/* Gallery
|
||||
-----------------------------------------------------------------*/
|
||||
#GalleryNav {
|
||||
|
||||
@@ -576,6 +576,10 @@ table.relationships tbody tr td.ColumnPartner a:hover {
|
||||
content: "";
|
||||
}
|
||||
|
||||
table.eventlist tbody tr td.ColumnSources {
|
||||
width: 5%;
|
||||
}
|
||||
|
||||
/* Gallery
|
||||
----------------------------------------------------- */
|
||||
#Gallery { }
|
||||
|
||||
+722
-501
File diff suppressed because it is too large
Load Diff
Vendored
+6
@@ -1,3 +1,9 @@
|
||||
gramps (5.0.0-1) unstable; urgency=medium
|
||||
|
||||
* New Gramps release
|
||||
|
||||
-- Ross Gammon <rossgammon@debian.org> Wed, 25 Jul 2018 21:19:00 +0200
|
||||
|
||||
gramps (5.0.0~rc1-1) unstable; urgency=medium
|
||||
|
||||
* First release candidate for Gramps 5.0
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"http://gramps-project.org/xml/1.7.1/grampsxml.dtd">
|
||||
<database xmlns="http://gramps-project.org/xml/1.7.1/">
|
||||
<header>
|
||||
<created date="2018-03-08" version="5.0.0"/>
|
||||
<created date="2017-08-08" version="5.1.0"/>
|
||||
<researcher>
|
||||
<resname>Alex Roitman,,,</resname>
|
||||
</researcher>
|
||||
@@ -951,7 +951,7 @@
|
||||
<place hlink="_L3WJQCD3US67V2CNZT"/>
|
||||
<description>Birth of Garner, Anderson</description>
|
||||
</event>
|
||||
<event handle="_a5af0eb7dfb557da07e" change="1284030599" id="E0179">
|
||||
<event handle="_a5af0eb7dfb557da07e" change="1502187535" id="E0179">
|
||||
<type>Death</type>
|
||||
<dateval val="1887-04-07"/>
|
||||
<place hlink="_AKFKQC2N4SM243HCTN"/>
|
||||
@@ -6262,7 +6262,7 @@
|
||||
<dateval val="1842-06-28"/>
|
||||
<description>Death of Moreno, Joseph McDowell</description>
|
||||
</event>
|
||||
<event handle="_a5af0ec45b25c630e03" change="1284030608" id="E1179">
|
||||
<event handle="_a5af0ec45b25c630e03" change="1502187451" id="E1179">
|
||||
<type>Birth</type>
|
||||
<dateval val="1911-07-12"/>
|
||||
<description>Birth of Thornton, James Arthur</description>
|
||||
@@ -15003,7 +15003,7 @@
|
||||
<type>Marriage</type>
|
||||
<description>Marriage of Johnson, Henry and Sparks, Catherine</description>
|
||||
</event>
|
||||
<event handle="_a5af0ed5df832ee65c1" change="1328026859" id="E2815">
|
||||
<event handle="_a5af0ed5df832ee65c1" change="1502187371" id="E2815">
|
||||
<type>Marriage</type>
|
||||
<dateval val="1875-04-01"/>
|
||||
<place hlink="_RF5KQCNJRQY8OGTX2H"/>
|
||||
@@ -18649,7 +18649,7 @@
|
||||
<parentin hlink="_F4CKQCJZ24JRE9Z889"/>
|
||||
<citationref hlink="_c140d2472c91aad494f"/>
|
||||
</person>
|
||||
<person handle="_14LKQCYZJEAXTS3XX" change="1185438865" id="I1376">
|
||||
<person handle="_14LKQCYZJEAXTS3XX" change="1502187492" id="I1376">
|
||||
<gender>F</gender>
|
||||
<name type="Birth Name">
|
||||
<first>Elizabeth</first>
|
||||
@@ -18657,6 +18657,7 @@
|
||||
</name>
|
||||
<eventref hlink="_a5af0ebb003796f79a0" role="Primary"/>
|
||||
<eventref hlink="_a5af0ebb0143dab99ff" role="Primary"/>
|
||||
<eventref hlink="_a5af0eb7dfb557da07e" role="Witness"/>
|
||||
<childof hlink="_5IUJQCRJY47YQ8PU7N"/>
|
||||
<parentin hlink="_I4LKQCOAGPFH4R90A7"/>
|
||||
<citationref hlink="_c140d24731407d57b3c"/>
|
||||
@@ -19904,7 +19905,7 @@
|
||||
<childof hlink="_IO5KQC6H0Q6Y517LR9"/>
|
||||
<citationref hlink="_c140d24a1fd2319b703"/>
|
||||
</person>
|
||||
<person handle="_35WJQC1B7T7NPV8OLV" change="1284030051" id="I0106">
|
||||
<person handle="_35WJQC1B7T7NPV8OLV" change="1502187260" id="I0106">
|
||||
<gender>M</gender>
|
||||
<name type="Birth Name">
|
||||
<first>Robert W.</first>
|
||||
@@ -19913,6 +19914,11 @@
|
||||
<eventref hlink="_a5af0eb74ac73f86aa7" role="Primary"/>
|
||||
<eventref hlink="_a5af0eb74ba358391ae" role="Primary"/>
|
||||
<eventref hlink="_a5af0eb74c852f7c633" role="Primary"/>
|
||||
<eventref hlink="_a5af0ed5df832ee65c1" role="Witness">
|
||||
<attribute type="Age" value="23">
|
||||
<citationref hlink="_c140dafeb317af2fd79"/>
|
||||
</attribute>
|
||||
</eventref>
|
||||
<childof hlink="_X3WJQCSF48F6809142"/>
|
||||
<parentin hlink="_8OUJQCUVZ0XML7BQLF"/>
|
||||
<citationref hlink="_c140d24a27e19bb381a"/>
|
||||
@@ -21991,13 +21997,14 @@
|
||||
<childof hlink="_GCDKQCHI74ZPMI5GDJ"/>
|
||||
<citationref hlink="_c140d24f3f8704aa41b"/>
|
||||
</person>
|
||||
<person handle="_6G0KQC2UXYC66XDDC2" change="1185438865" id="I0363">
|
||||
<person handle="_6G0KQC2UXYC66XDDC2" change="1502187537" id="I0363">
|
||||
<gender>F</gender>
|
||||
<name type="Birth Name">
|
||||
<first>Miranda Keziah</first>
|
||||
<surname>Farmer</surname>
|
||||
</name>
|
||||
<eventref hlink="_a5af0ec7a76244d962d" role="Primary"/>
|
||||
<eventref hlink="_a5af0eb7dfb557da07e" role="Witness"/>
|
||||
<childof hlink="_8NVJQCGMJTCL7E6ZDV"/>
|
||||
<citationref hlink="_c140d24f439709d3118"/>
|
||||
</person>
|
||||
@@ -22989,7 +22996,7 @@
|
||||
<parentin hlink="_FBVJQCFBI50TW78O49"/>
|
||||
<citationref hlink="_c140d2522702664ec52"/>
|
||||
</person>
|
||||
<person handle="_8CLKQCT97PJOGREJ7W" change="1185438865" id="I1387">
|
||||
<person handle="_8CLKQCT97PJOGREJ7W" change="1502187387" id="I1387">
|
||||
<gender>M</gender>
|
||||
<name type="Birth Name">
|
||||
<first>Robert</first>
|
||||
@@ -22997,6 +23004,11 @@
|
||||
</name>
|
||||
<eventref hlink="_a5af0ebb3737a6ad088" role="Primary"/>
|
||||
<eventref hlink="_a5af0ebb39b441e9607" role="Primary"/>
|
||||
<eventref hlink="_a5af0ed5df832ee65c1" role="Clergy">
|
||||
<attribute type="Age" value="23">
|
||||
<citationref hlink="_c140dafeb317af2fd79"/>
|
||||
</attribute>
|
||||
</eventref>
|
||||
<childof hlink="_5IUJQCRJY47YQ8PU7N"/>
|
||||
<parentin hlink="_H6LKQCWVIFNRNGHFVH"/>
|
||||
<citationref hlink="_c140d2522d804491375"/>
|
||||
@@ -23803,7 +23815,7 @@
|
||||
<childof hlink="_5DBKQCVAB0XMBEW8R9"/>
|
||||
<citationref hlink="_c140d25425874b0b827"/>
|
||||
</person>
|
||||
<person handle="_9HUJQC6ONNW8SMSKGQ" change="1185438865" id="I0038">
|
||||
<person handle="_9HUJQC6ONNW8SMSKGQ" change="1502187526" id="I0038">
|
||||
<gender>M</gender>
|
||||
<name type="Birth Name">
|
||||
<first>David</first>
|
||||
@@ -23812,6 +23824,7 @@
|
||||
<eventref hlink="_a5af0ec7eb514c52fbf" role="Primary"/>
|
||||
<eventref hlink="_a5af0ec7ec844213b55" role="Primary"/>
|
||||
<eventref hlink="_a5af0ec7ed61c743fc8" role="Primary"/>
|
||||
<eventref hlink="_a5af0eb7dfb557da07e" role="Informant"/>
|
||||
<childof hlink="_5IUJQCRJY47YQ8PU7N"/>
|
||||
<parentin hlink="_3HUJQCK4DH582YUTZG"/>
|
||||
<citationref hlink="_c140d2542c764516f13"/>
|
||||
@@ -24506,7 +24519,7 @@
|
||||
<parentin hlink="_8NVJQCGMJTCL7E6ZDV"/>
|
||||
<citationref hlink="_c140d2560004fad8df6"/>
|
||||
</person>
|
||||
<person handle="_ANLKQCQSQNE5LDZMRC" change="1185438865" id="I1402">
|
||||
<person handle="_ANLKQCQSQNE5LDZMRC" change="1502187434" id="I1402">
|
||||
<gender>F</gender>
|
||||
<name type="Birth Name">
|
||||
<first>Margaret Jane "Maggie"</first>
|
||||
@@ -24514,6 +24527,7 @@
|
||||
</name>
|
||||
<eventref hlink="_a5af0ebb7d47a006ee2" role="Primary"/>
|
||||
<eventref hlink="_a5af0ebb7e54e14970f" role="Primary"/>
|
||||
<eventref hlink="_a5af0ec45b25c630e03" role="Witness"/>
|
||||
<childof hlink="_VDLKQCQQ1ADTJG1D1F"/>
|
||||
<parentin hlink="_SNLKQCD0VNJ627062Y"/>
|
||||
<citationref hlink="_c140d25607213be35da"/>
|
||||
@@ -24882,13 +24896,18 @@
|
||||
<parentin hlink="_JFYJQCG2KLRQN835JD"/>
|
||||
<citationref hlink="_c140d256e3403ba129d"/>
|
||||
</person>
|
||||
<person handle="_B3BKQCSV0G3NKSKWDX" change="1185438865" id="I0880">
|
||||
<person handle="_B3BKQCSV0G3NKSKWDX" change="1502187344" id="I0880">
|
||||
<gender>F</gender>
|
||||
<name type="Birth Name">
|
||||
<first>L. J.</first>
|
||||
<surname>Blanco</surname>
|
||||
</name>
|
||||
<eventref hlink="_a5af0ed2cf617169903" role="Primary"/>
|
||||
<eventref hlink="_a5af0ed5df832ee65c1" role="Clergy">
|
||||
<attribute type="Age" value="23">
|
||||
<citationref hlink="_c140dafeb317af2fd79"/>
|
||||
</attribute>
|
||||
</eventref>
|
||||
<childof hlink="_1BVJQCNTFAGS8273LJ"/>
|
||||
<citationref hlink="_c140d256ec4306a51ca"/>
|
||||
</person>
|
||||
@@ -31930,7 +31949,7 @@
|
||||
<childof hlink="_UDMKQC5D3A2PXPUGNC"/>
|
||||
<citationref hlink="_c140d266ec93334f40f"/>
|
||||
</person>
|
||||
<person handle="_MG5KQC6ZKSVO4A63G2" change="1328026883" id="I0624">
|
||||
<person handle="_MG5KQC6ZKSVO4A63G2" change="1502187451" id="I0624">
|
||||
<gender>M</gender>
|
||||
<name type="Birth Name">
|
||||
<first>Raymond E.</first>
|
||||
@@ -31938,6 +31957,7 @@
|
||||
</name>
|
||||
<eventref hlink="_a5af0ece8bd1125a1a9" role="Primary"/>
|
||||
<eventref hlink="_a5af0ece8d15511ddf9" role="Primary"/>
|
||||
<eventref hlink="_a5af0ec45b25c630e03" role="Informant"/>
|
||||
<childof hlink="_9OUJQCBOHW9UEK9CNV"/>
|
||||
<citationref hlink="_c140d266f0d5d178784"/>
|
||||
</person>
|
||||
@@ -32336,7 +32356,7 @@
|
||||
<parentin hlink="_F4CKQCJZ24JRE9Z889"/>
|
||||
<citationref hlink="_c140d267ccb7aef0cd0"/>
|
||||
</person>
|
||||
<person handle="_N4DKQCPEMZ7OO62O7J" change="1185438865" id="I0975">
|
||||
<person handle="_N4DKQCPEMZ7OO62O7J" change="1502187308" id="I0975">
|
||||
<gender>M</gender>
|
||||
<name type="Birth Name">
|
||||
<first>Henry</first>
|
||||
@@ -32345,6 +32365,11 @@
|
||||
<eventref hlink="_a5af0ed483738d4ed1e" role="Primary"/>
|
||||
<eventref hlink="_a5af0ed484a144d229b" role="Primary"/>
|
||||
<eventref hlink="_a5af0ed48605fb6b9eb" role="Primary"/>
|
||||
<eventref hlink="_a5af0ed5df832ee65c1" role="Witness">
|
||||
<attribute type="Age" value="23">
|
||||
<citationref hlink="_c140dafeb317af2fd79"/>
|
||||
</attribute>
|
||||
</eventref>
|
||||
<parentin hlink="_7PUJQC4PPS4EDIVMYE"/>
|
||||
<citationref hlink="_c140d267d2128356d8e"/>
|
||||
</person>
|
||||
|
||||
@@ -155,7 +155,7 @@ register('behavior.translator-needed', True)
|
||||
register('behavior.use-tips', False)
|
||||
register('behavior.welcome', 100)
|
||||
register('behavior.web-search-url', 'http://google.com/#&q=%(text)s')
|
||||
register('behavior.addons-url', "https://raw.githubusercontent.com/gramps-project/addons/master/gramps50")
|
||||
register('behavior.addons-url', "https://raw.githubusercontent.com/gramps-project/addons/master/gramps51")
|
||||
|
||||
register('database.backend', 'bsddb')
|
||||
register('database.compress-backup', True)
|
||||
@@ -196,6 +196,7 @@ register('interface.view-categories',
|
||||
register('interface.filter', False)
|
||||
register('interface.fullscreen', False)
|
||||
register('interface.grampletbar-close', False)
|
||||
register('interface.grampletbar-refresh', False)
|
||||
register('interface.ignore-gexiv2', False)
|
||||
register('interface.ignore-pil', False)
|
||||
register('interface.ignore-osmgpsmap', False)
|
||||
@@ -217,6 +218,7 @@ register('interface.sidebar-text', True)
|
||||
register('interface.size-checked', False)
|
||||
register('interface.statusbar', 1)
|
||||
register('interface.toolbar-on', True)
|
||||
register('interface.toolbar-text', False)
|
||||
register('interface.view', True)
|
||||
register('interface.surname-box-height', 150)
|
||||
register('interface.treemodel-cache-size', 1000)
|
||||
@@ -228,6 +230,8 @@ register('paths.report-directory', USER_HOME)
|
||||
register('paths.website-directory', USER_HOME)
|
||||
register('paths.website-cms-uri', '')
|
||||
register('paths.website-cal-uri', '')
|
||||
register('paths.website-extra-page-uri', '')
|
||||
register('paths.website-extra-page-name', '')
|
||||
register('paths.quick-backup-directory', USER_HOME)
|
||||
register('paths.quick-backup-filename',
|
||||
"%(filename)s_%(year)d-%(month)02d-%(day)02d.%(extension)s")
|
||||
@@ -325,7 +329,7 @@ if not os.path.exists(CONFIGMAN.filename):
|
||||
# check previous version of gramps:
|
||||
fullpath, filename = os.path.split(CONFIGMAN.filename)
|
||||
fullpath, previous = os.path.split(fullpath)
|
||||
match = re.match('gramps(\d*)', previous)
|
||||
match = re.match(r'gramps(\d*)', previous)
|
||||
if match:
|
||||
# cycle back looking for previous versions of gramps
|
||||
for i in range(1, 20): # check back 2 gramps versions
|
||||
|
||||
+2
-1
@@ -138,7 +138,8 @@ 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
|
||||
#VERSION += "-1"
|
||||
|
||||
#
|
||||
# Glade files
|
||||
|
||||
@@ -124,12 +124,12 @@ class DateParserAR(DateParser):
|
||||
_span_2 = ['إلى']
|
||||
_range_1 = ['بين']
|
||||
_range_2 = ['و']
|
||||
self._span = re.compile("(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_span_1), '|'.join(_span_2)),
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile("(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_range_1), '|'.join(_range_2)),
|
||||
re.IGNORECASE)
|
||||
self._span = re.compile(
|
||||
r"(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_span_1), '|'.join(_span_2)), re.IGNORECASE)
|
||||
self._range = re.compile(
|
||||
r"(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_range_1), '|'.join(_range_2)), re.IGNORECASE)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
@@ -166,12 +166,12 @@ class DateParserBG(DateParser):
|
||||
_span_2 = ['до']
|
||||
_range_1 = ['между']
|
||||
_range_2 = ['и']
|
||||
self._span = re.compile("(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_span_1), '|'.join(_span_2)),
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile("(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_range_1), '|'.join(_range_2)),
|
||||
re.IGNORECASE)
|
||||
self._span = re.compile(
|
||||
r"(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_span_1), '|'.join(_span_2)), re.IGNORECASE)
|
||||
self._range = re.compile(
|
||||
r"(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_range_1), '|'.join(_range_2)), re.IGNORECASE)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
@@ -98,14 +98,14 @@ class DateParserCA(DateParser):
|
||||
DateParser.init_strings(self)
|
||||
_span_1 = ['des de']
|
||||
_span_2 = ['fins a']
|
||||
_range_1 = ['entre', 'ent\.', 'ent']
|
||||
_range_1 = ['entre', r'ent\.', 'ent']
|
||||
_range_2 = ['i']
|
||||
self._span = re.compile("(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_span_1), '|'.join(_span_2)),
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile("(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_range_1), '|'.join(_range_2)),
|
||||
re.IGNORECASE)
|
||||
self._span = re.compile(
|
||||
r"(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_span_1), '|'.join(_span_2)), re.IGNORECASE)
|
||||
self._range = re.compile(
|
||||
r"(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_range_1), '|'.join(_range_2)), re.IGNORECASE)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
@@ -89,10 +89,11 @@ class DateParserDa(DateParser):
|
||||
|
||||
def init_strings(self):
|
||||
DateParser.init_strings(self)
|
||||
self._span = re.compile("(fra)?\s*(?P<start>.+)\s*(til|--|–)\s*(?P<stop>.+)",
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile("(mellem)\s+(?P<start>.+)\s+og\s+(?P<stop>.+)",
|
||||
re.IGNORECASE)
|
||||
self._span = re.compile(
|
||||
r"(fra)?\s*(?P<start>.+)\s*(til|--|–)\s*(?P<stop>.+)",
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile(
|
||||
r"(mellem)\s+(?P<start>.+)\s+og\s+(?P<stop>.+)", re.IGNORECASE)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
@@ -248,14 +248,16 @@ class DateParserDE(DateParser):
|
||||
|
||||
def init_strings(self):
|
||||
DateParser.init_strings(self)
|
||||
self._span = re.compile("(von|vom)\s+(?P<start>.+)\s+(bis)\s+(?P<stop>.+)",
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile("zwischen\s+(?P<start>.+)\s+und\s+(?P<stop>.+)",
|
||||
re.IGNORECASE)
|
||||
self._text2 = re.compile('(\d+)?.?\s+?%s\s*((\d+)(/\d+)?)?' % self._mon_str,
|
||||
re.IGNORECASE)
|
||||
self._jtext2 = re.compile('(\d+)?.?\s+?%s\s*((\d+)(/\d+)?)?' % self._jmon_str,
|
||||
re.IGNORECASE)
|
||||
self._span = re.compile(
|
||||
r"(von|vom)\s+(?P<start>.+)\s+(bis)\s+(?P<stop>.+)", re.IGNORECASE)
|
||||
self._range = re.compile(
|
||||
r"zwischen\s+(?P<start>.+)\s+und\s+(?P<stop>.+)", re.IGNORECASE)
|
||||
self._text2 = re.compile(
|
||||
r'(\d+)?.?\s+?%s\s*((\d+)(/\d+)?)?' % self._mon_str,
|
||||
re.IGNORECASE)
|
||||
self._jtext2 = re.compile(
|
||||
r'(\d+)?.?\s+?%s\s*((\d+)(/\d+)?)?' % self._jmon_str,
|
||||
re.IGNORECASE)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
@@ -116,14 +116,14 @@ class DateParserEL(DateParser):
|
||||
DateParser.init_strings(self)
|
||||
_span_1 = ['από']
|
||||
_span_2 = ['έως']
|
||||
_range_1 = ['μετ', 'μετ\.', 'μεταξύ']
|
||||
_range_1 = ['μετ', r'μετ\.', 'μεταξύ']
|
||||
_range_2 = ['και']
|
||||
self._span = re.compile("(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_span_1), '|'.join(_span_2)),
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile("(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_range_1), '|'.join(_range_2)),
|
||||
re.IGNORECASE)
|
||||
self._span = re.compile(
|
||||
r"(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_span_1), '|'.join(_span_2)), re.IGNORECASE)
|
||||
self._range = re.compile(
|
||||
r"(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_range_1), '|'.join(_range_2)), re.IGNORECASE)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
@@ -97,14 +97,14 @@ class DateParserES(DateParser):
|
||||
DateParser.init_strings(self)
|
||||
_span_1 = ['de']
|
||||
_span_2 = ['a']
|
||||
_range_1 = ['entre', 'ent\.', 'ent']
|
||||
_range_1 = ['entre', r'ent\.', 'ent']
|
||||
_range_2 = ['y']
|
||||
self._span = re.compile("(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_span_1), '|'.join(_span_2)),
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile("(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_range_1), '|'.join(_range_2)),
|
||||
re.IGNORECASE)
|
||||
self._span = re.compile(
|
||||
r"(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_span_1), '|'.join(_span_2)), re.IGNORECASE)
|
||||
self._range = re.compile(
|
||||
r"(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_range_1), '|'.join(_range_2)), re.IGNORECASE)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
@@ -98,12 +98,12 @@ class DateParserFI(DateParser):
|
||||
|
||||
def init_strings(self):
|
||||
DateParser.init_strings(self)
|
||||
self._text2 = re.compile('(\d+)?\.?\s+?%s\.?\s*((\d+)(/\d+)?)?\s*$'
|
||||
% self._mon_str, re.IGNORECASE)
|
||||
self._span = re.compile("(?P<start>.+)\s+(-)\s+(?P<stop>.+)",
|
||||
re.IGNORECASE)
|
||||
self._text2 = re.compile(r'(\d+)?\.?\s+?%s\.?\s*((\d+)(/\d+)?)?\s*$'
|
||||
% self._mon_str, re.IGNORECASE)
|
||||
self._span = re.compile(r"(?P<start>.+)\s+(-)\s+(?P<stop>.+)",
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile(
|
||||
"(vuosien\s*)?(?P<start>.+)\s+ja\s+(?P<stop>.+)\s+välillä",
|
||||
r"(vuosien\s*)?(?P<start>.+)\s+ja\s+(?P<stop>.+)\s+välillä",
|
||||
re.IGNORECASE)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
@@ -79,15 +79,27 @@ class DateParserHR(DateParser):
|
||||
#~ 'персидский' : Date.CAL_PERSIAN,
|
||||
#~ 'п' : Date.CAL_PERSIAN,
|
||||
#~ })
|
||||
|
||||
# match 'Day. MONTH year.' format with or without dots
|
||||
self._text2 = re.compile(r'(\d+)?\.?\s*?%s\.?\s*((\d+)(/\d+)?)?\s*\.?$'
|
||||
% self._mon_str, re.IGNORECASE)
|
||||
|
||||
# match Day.Month.Year.
|
||||
self._numeric = re.compile(
|
||||
r"((\d+)[/\. ])?\s*((\d+)[/\.])?\s*(\d+)\.?$")
|
||||
|
||||
self._jtext2 = re.compile(r'(\d+)?.?\s+?%s\s*((\d+)(/\d+)?)?'
|
||||
% self._jmon_str, re.IGNORECASE)
|
||||
|
||||
_span_1 = ['od']
|
||||
_span_2 = ['do']
|
||||
_range_1 = ['između']
|
||||
_range_2 = ['i']
|
||||
self._span = re.compile("(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_span_1), '|'.join(_span_2)),
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile("(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_range_1), '|'.join(_range_2)),
|
||||
self._span = re.compile(r"(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)"
|
||||
% ('|'.join(_span_1), '|'.join(_span_2)),
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile(r"(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)"
|
||||
% ('|'.join(_range_1), '|'.join(_range_2)),
|
||||
re.IGNORECASE)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@@ -105,11 +117,35 @@ class DateDisplayHR(DateDisplay):
|
||||
|
||||
display = DateDisplay.display_formatted
|
||||
|
||||
def format_short_month_year(self, month, year, inflect, short_months):
|
||||
""" Allow a subclass to modify the year, e.g. add a period """
|
||||
if not hasattr(short_months[1], 'f'): # not a Lexeme: no inflection
|
||||
return "{short_month} {year}.".format(
|
||||
short_month = short_months[month], year = year)
|
||||
return self.FORMATS_short_month_year[inflect].format(
|
||||
short_month = short_months[month], year = year)
|
||||
|
||||
def _get_localized_year(self, year):
|
||||
""" Allow a subclass to modify the year, e.g. add a period """
|
||||
return year + '.'
|
||||
|
||||
# FIXME probably there should be a Croatian-specific "formats" (and this
|
||||
# ("American comma") format (and dd_dformat03 too) should be eliminated)
|
||||
def dd_dformat02(self, date_val, inflect, long_months):
|
||||
""" month_name day, year """
|
||||
return DateDisplay.dd_dformat02(
|
||||
self, date_val, inflect, long_months).replace(' .', '')
|
||||
|
||||
def dd_dformat04(self, date_val, inflect, long_months):
|
||||
""" day month_name year """
|
||||
return DateDisplay.dd_dformat04(
|
||||
self, date_val, inflect, long_months).replace(' .', '')
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Register classes
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
register_datehandler(
|
||||
('hr_HR', 'hr', 'HR', 'croatian', 'Croatian', 'hrvatski', ('%d.%m.%Y',)),
|
||||
('hr_HR', 'hr', 'HR', 'croatian', 'Croatian', 'hrvatski', ('%d.%m.%Y.',)),
|
||||
DateParserHR, DateDisplayHR)
|
||||
|
||||
@@ -228,19 +228,19 @@ class DateParserHU(DateParser):
|
||||
DateParser.init_strings(self)
|
||||
|
||||
self._numeric = re.compile(
|
||||
"((\d+)[/\.])?\s*((\d+)[/\.])?\s*(\d+)[/\. ]?$")
|
||||
r"((\d+)[/\.])?\s*((\d+)[/\.])?\s*(\d+)[/\. ]?$")
|
||||
# this next RE has the (possibly-slashed) year at the string's start
|
||||
self._text2 = re.compile('((\d+)(/\d+)?\.)?\s+?%s\.?\s*(\d+\.)?\s*$'
|
||||
% self._mon_str, re.IGNORECASE)
|
||||
_span_1 = ['-tó\(ő\)l', '-tól', '-től']
|
||||
self._text2 = re.compile(r'((\d+)(/\d+)?\.)?\s+?%s\.?\s*(\d+\.)?\s*$'
|
||||
% self._mon_str, re.IGNORECASE)
|
||||
_span_1 = [r'-tó\(ő\)l', '-tól', '-től']
|
||||
_span_2 = ['-ig']
|
||||
_range_1 = ['és']
|
||||
_range_2 = ['között']
|
||||
self._span = re.compile("(?P<start>.+)(%s)\s+(?P<stop>.+)(%s)" %
|
||||
('|'.join(_span_1), '|'.join(_span_2)),
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile("(?P<start>.+)\s+(%s)\s+(?P<stop>.+)\s+(%s)" %
|
||||
('|'.join(_range_1), '|'.join(_range_2)),
|
||||
self._span = re.compile(r"(?P<start>.+)(%s)\s+(?P<stop>.+)(%s)" %
|
||||
('|'.join(_span_1), '|'.join(_span_2)),
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile(r"(?P<start>.+)\s+(%s)\s+(?P<stop>.+)\s+(%s)"
|
||||
% ('|'.join(_range_1), '|'.join(_range_2)),
|
||||
re.IGNORECASE)
|
||||
|
||||
|
||||
|
||||
@@ -90,22 +90,23 @@ class DateParserIs(DateParser):
|
||||
}
|
||||
|
||||
def dhformat_changed(self):
|
||||
self._dhformat_parse = re.compile(".*%(\S).*%(\S).*%(\S).*%(\S).*")
|
||||
self._dhformat_parse = re.compile(r".*%(\S).*%(\S).*%(\S).*%(\S).*")
|
||||
|
||||
def init_strings(self):
|
||||
DateParser.init_strings(self)
|
||||
|
||||
# match 'day. month year' format
|
||||
self._text2 = re.compile('(\d+)?\.?\s*?%s\.?\s*((\d+)(/\d+)?)?\s*$'
|
||||
self._text2 = re.compile(r'(\d+)?\.?\s*?%s\.?\s*((\d+)(/\d+)?)?\s*$'
|
||||
% self._mon_str, re.IGNORECASE)
|
||||
# match 'short-day day.month year' format
|
||||
short_day_str = '(' + '|'.join(self._ds.short_days[1:]) + ')'
|
||||
self._numeric = re.compile("%s\s*((\d+)[\.]\s*)?((\d+)\s*)?(\d+)\s*$"
|
||||
self._numeric = re.compile(r"%s\s*((\d+)[\.]\s*)?((\d+)\s*)?(\d+)\s*$"
|
||||
% short_day_str, re.IGNORECASE)
|
||||
self._span = re.compile("(frá)?\s*(?P<start>.+)\s*(til|--|–)\s*(?P<stop>.+)",
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile("(milli)\s+(?P<start>.+)\s+og\s+(?P<stop>.+)",
|
||||
re.IGNORECASE)
|
||||
self._span = re.compile(
|
||||
r"(frá)?\s*(?P<start>.+)\s*(til|--|–)\s*(?P<stop>.+)",
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile(
|
||||
r"(milli)\s+(?P<start>.+)\s+og\s+(?P<stop>.+)", re.IGNORECASE)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@@ -185,7 +186,8 @@ class DateDisplayIs(DateDisplay):
|
||||
text, scal)
|
||||
|
||||
def _get_weekday(self, date_val):
|
||||
if date_val[0] == 0 or date_val[1] == 0: # no day or no month or both
|
||||
if (date_val[0] == 0 or date_val[1] == 0 # no day or no month or both
|
||||
or date_val[2] > datetime.MAXYEAR): # bug 10815
|
||||
return ''
|
||||
w_day = datetime.date(date_val[2], date_val[1], date_val[0]) # y, m, d
|
||||
return self.short_days[((w_day.weekday() + 1) % 7) + 1]
|
||||
|
||||
@@ -96,12 +96,12 @@ class DateParserIT(DateParser):
|
||||
_span_2 = ['al', 'a']
|
||||
_range_1 = ['tra', 'fra']
|
||||
_range_2 = ['e']
|
||||
self._span = re.compile("(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_span_1), '|'.join(_span_2)),
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile("(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_range_1), '|'.join(_range_2)),
|
||||
self._span = re.compile(r"(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_span_1), '|'.join(_span_2)),
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile(
|
||||
r"(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_range_1), '|'.join(_range_2)), re.IGNORECASE)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
@@ -166,13 +166,13 @@ class DateParserJA(DateParser):
|
||||
_span_2 = ['まで', '']
|
||||
_range_1 = ['から', 'と', '~', '〜']
|
||||
_range_2 = ['までの間', 'の間']
|
||||
self._span = re.compile("(?P<start>.+)(%s)(?P<stop>\d+)(%s)" %
|
||||
('|'.join(_span_1), '|'.join(_span_2)),
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile("(?P<start>.+)(%s)(?P<stop>.+)(%s)" %
|
||||
self._span = re.compile(r"(?P<start>.+)(%s)(?P<stop>\d+)(%s)" %
|
||||
('|'.join(_span_1), '|'.join(_span_2)),
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile(r"(?P<start>.+)(%s)(?P<stop>.+)(%s)" %
|
||||
('|'.join(_range_1), '|'.join(_range_2)),
|
||||
re.IGNORECASE)
|
||||
self._numeric = re.compile("((\d+)年\s*)?((\d+)月\s*)?(\d+)?日?\s*$")
|
||||
self._numeric = re.compile(r"((\d+)年\s*)?((\d+)月\s*)?(\d+)?日?\s*$")
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
@@ -131,18 +131,19 @@ class DateParserLT(DateParser):
|
||||
def init_strings(self):
|
||||
DateParser.init_strings(self)
|
||||
# this next RE has the (possibly-slashed) year at the string's start
|
||||
self._text2 = re.compile('((\d+)(/\d+)?)?\s+?m\.\s+%s\s*(\d+)?\s*d?\.?$'
|
||||
% self._mon_str, re.IGNORECASE)
|
||||
self._text2 = re.compile(
|
||||
r'((\d+)(/\d+)?)?\s+?m\.\s+%s\s*(\d+)?\s*d?\.?$'
|
||||
% self._mon_str, re.IGNORECASE)
|
||||
_span_1 = ['nuo']
|
||||
_span_2 = ['iki']
|
||||
_range_1 = ['tarp']
|
||||
_range_2 = ['ir']
|
||||
self._span = re.compile("(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_span_1), '|'.join(_span_2)),
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile("(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_range_1), '|'.join(_range_2)),
|
||||
re.IGNORECASE)
|
||||
self._span = re.compile(r"(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_span_1), '|'.join(_span_2)),
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile(
|
||||
r"(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_range_1), '|'.join(_range_2)), re.IGNORECASE)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
@@ -58,6 +58,7 @@ class DateParserNb(DateParser):
|
||||
'innen' : Date.MOD_BEFORE,
|
||||
'etter' : Date.MOD_AFTER,
|
||||
'omkring' : Date.MOD_ABOUT,
|
||||
'omtrent' : Date.MOD_ABOUT,
|
||||
'ca' : Date.MOD_ABOUT
|
||||
}
|
||||
|
||||
@@ -89,12 +90,13 @@ class DateParserNb(DateParser):
|
||||
|
||||
def init_strings(self):
|
||||
DateParser.init_strings(self)
|
||||
# match day. month year
|
||||
self._numeric = re.compile("((\d+)[\.])?\s*((\d+))?\s*(\d+)$")
|
||||
self._span = re.compile("(fra)?\s*(?P<start>.+)\s*(til|--|–)\s*(?P<stop>.+)",
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile("(mellom)\s+(?P<start>.+)\s+og\s+(?P<stop>.+)",
|
||||
re.IGNORECASE)
|
||||
self._numeric = re.compile(
|
||||
r"((\d+)[/\.\s]\s*)?((\d+)[/\.\-\s]\s*)?(\d+)\s*$")
|
||||
self._span = re.compile(
|
||||
r"(fra)?\s*(?P<start>.+)\s*(til|--|–)\s*(?P<stop>.+)",
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile(
|
||||
r"(mellom)\s+(?P<start>.+)\s+og\s+(?P<stop>.+)", re.IGNORECASE)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@@ -173,6 +175,10 @@ class DateDisplayNb(DateDisplay):
|
||||
return "%s%s%s%s" % (qual_str, self._mod_str[mod],
|
||||
text, scal)
|
||||
|
||||
def dd_dformat01(self, date_val):
|
||||
""" numerical -- for Norwegian dates """
|
||||
return DateDisplay.dd_dformat01(self, date_val).lstrip()
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Register classes
|
||||
|
||||
@@ -119,16 +119,14 @@ class DateParserNL(DateParser):
|
||||
|
||||
def init_strings(self):
|
||||
DateParser.init_strings(self)
|
||||
self._span = re.compile("(van)\s+(?P<start>.+)\s+(tot)\s+(?P<stop>.+)",
|
||||
self._span = re.compile(
|
||||
r"(van)\s+(?P<start>.+)\s+(tot)\s+(?P<stop>.+)", re.IGNORECASE)
|
||||
self._range = re.compile(r"tussen\s+(?P<start>.+)\s+en\s+(?P<stop>.+)",
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile("tussen\s+(?P<start>.+)\s+en\s+(?P<stop>.+)",
|
||||
re.IGNORECASE)
|
||||
self._text2 = re.compile('(\d+)?.?\s+?%s\s*((\d+)(/\d+)?)?'
|
||||
% self._mon_str,
|
||||
re.IGNORECASE)
|
||||
self._jtext2 = re.compile('(\d+)?.?\s+?%s\s*((\d+)(/\d+)?)?'
|
||||
% self._jmon_str,
|
||||
re.IGNORECASE)
|
||||
self._text2 = re.compile(r'(\d+)?.?\s+?%s\s*((\d+)(/\d+)?)?'
|
||||
% self._mon_str, re.IGNORECASE)
|
||||
self._jtext2 = re.compile(r'(\d+)?.?\s+?%s\s*((\d+)(/\d+)?)?'
|
||||
% self._jmon_str, re.IGNORECASE)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
@@ -156,13 +156,16 @@ class DateParserPL(DateParser):
|
||||
|
||||
def init_strings(self):
|
||||
DateParser.init_strings(self)
|
||||
self._span = re.compile("(od)\s+(?P<start>.+)\s+(do)\s+(?P<stop>.+)", re.IGNORECASE)
|
||||
self._span = re.compile(
|
||||
r"(od)\s+(?P<start>.+)\s+(do)\s+(?P<stop>.+)", re.IGNORECASE)
|
||||
# Also handle a common mistakes
|
||||
self._range = re.compile("((?:po)?mi(?:ę|e)dzy)\s+(?P<start>.+)\s+(a)\s+(?P<stop>.+)", re.IGNORECASE)
|
||||
self._text2 = re.compile('(\d+)?.?\s+?%s\s*((\d+)(/\d+)?)?' % self._mon_str,
|
||||
re.IGNORECASE)
|
||||
self._jtext2 = re.compile('(\d+)?.?\s+?%s\s*((\d+)(/\d+)?)?' % self._jmon_str,
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile(
|
||||
r"((?:po)?mi(?:ę|e)dzy)\s+(?P<start>.+)\s+(a)\s+(?P<stop>.+)",
|
||||
re.IGNORECASE)
|
||||
self._text2 = re.compile(r'(\d+)?.?\s+?%s\s*((\d+)(/\d+)?)?'
|
||||
% self._mon_str, re.IGNORECASE)
|
||||
self._jtext2 = re.compile(r'(\d+)?.?\s+?%s\s*((\d+)(/\d+)?)?'
|
||||
% self._jmon_str, re.IGNORECASE)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
@@ -102,13 +102,13 @@ class DateParserPT(DateParser):
|
||||
DateParser.init_strings(self)
|
||||
_span_1 = ['de']
|
||||
_span_2 = ['a']
|
||||
_range_1 = ['entre','ent\.','ent']
|
||||
_range_1 = ['entre', r'ent\.', 'ent']
|
||||
_range_2 = ['e']
|
||||
self._span = re.compile("(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_span_1), '|'.join(_span_2)),
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile("(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_range_1), '|'.join(_range_2)),
|
||||
self._span = re.compile(r"(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)"
|
||||
% ('|'.join(_span_1), '|'.join(_span_2)),
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile(r"(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)"
|
||||
% ('|'.join(_range_1), '|'.join(_range_2)),
|
||||
re.IGNORECASE)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
@@ -94,13 +94,13 @@ class DateParserRU(DateParser):
|
||||
_span_1 = ['с', 'от']
|
||||
#_span_2 = ['по', 'до'] # <-- clashes with bce parsing :-(
|
||||
_span_2 = ['по']
|
||||
_range_1 = ['между', 'меж\.', 'меж']
|
||||
_range_1 = ['между', r'меж\.', 'меж']
|
||||
_range_2 = ['и']
|
||||
self._span = re.compile("(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_span_1), '|'.join(_span_2)),
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile("(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_range_1), '|'.join(_range_2)),
|
||||
self._span = re.compile(r"(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)"
|
||||
% ('|'.join(_span_1), '|'.join(_span_2)),
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile(r"(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)"
|
||||
% ('|'.join(_range_1), '|'.join(_range_2)),
|
||||
re.IGNORECASE)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
@@ -86,11 +86,11 @@ class DateParserSK(DateParser):
|
||||
_span_2 = ['do']
|
||||
_range_1 = ['medzi']
|
||||
_range_2 = ['a']
|
||||
self._span = re.compile("(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_span_1), '|'.join(_span_2)),
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile("(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_range_1), '|'.join(_range_2)),
|
||||
self._span = re.compile(r"(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)"
|
||||
% ('|'.join(_span_1), '|'.join(_span_2)),
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile(r"(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)"
|
||||
% ('|'.join(_range_1), '|'.join(_range_2)),
|
||||
re.IGNORECASE)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
@@ -88,18 +88,18 @@ class DateParserSL(DateParser):
|
||||
|
||||
DateParser.init_strings(self)
|
||||
# match 'Day. MONTH year.' format with or without dots
|
||||
self._text2 = re.compile('(\d+)?\.?\s*?%s\.?\s*((\d+)(/\d+)?)?\s*\.?$'
|
||||
% self._mon_str, re.IGNORECASE)
|
||||
self._text2 = re.compile(r'(\d+)?\.?\s*?%s\.?\s*((\d+)(/\d+)?)?\s*\.?$'
|
||||
% self._mon_str, re.IGNORECASE)
|
||||
# match Day.Month.Year.
|
||||
self._numeric = re.compile("((\d+)[/\.-])?\s*((\d+)[/\.-])?\s*(\d+)\.?$")
|
||||
self._numeric = re.compile(
|
||||
r"((\d+)[/\.-])?\s*((\d+)[/\.-])?\s*(\d+)\.?$")
|
||||
|
||||
self._span = re.compile("od\s+(?P<start>.+)\s+do\s+(?P<stop>.+)",
|
||||
self._span = re.compile(r"od\s+(?P<start>.+)\s+do\s+(?P<stop>.+)",
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile(
|
||||
"med\s+(?P<start>.+)\s+in\s+(?P<stop>.+)",
|
||||
re.IGNORECASE)
|
||||
self._jtext2 = re.compile('(\d+)?.?\s+?%s\s*((\d+)(/\d+)?)?'\
|
||||
% self._jmon_str, re.IGNORECASE)
|
||||
r"med\s+(?P<start>.+)\s+in\s+(?P<stop>.+)", re.IGNORECASE)
|
||||
self._jtext2 = re.compile(r'(\d+)?.?\s+?%s\s*((\d+)(/\d+)?)?'
|
||||
% self._jmon_str, re.IGNORECASE)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
@@ -208,21 +208,22 @@ class DateParserSR(DateParser):
|
||||
"""
|
||||
DateParser.init_strings(self)
|
||||
# match 'Day. MONTH year.' format with or without dots
|
||||
self._text2 = re.compile('(\d+)?\.?\s*?%s\s*((\d+)(/\d+)?)?\.?\s*$'
|
||||
% self._mon_str, re.IGNORECASE)
|
||||
self._text2 = re.compile(r'(\d+)?\.?\s*?%s\s*((\d+)(/\d+)?)?\.?\s*$'
|
||||
% self._mon_str, re.IGNORECASE)
|
||||
|
||||
# match Day.Month.Year.
|
||||
self._numeric = re.compile("((\d+)[/\. ])?\s*((\d+)[/\.])?\s*(\d+)\.?$")
|
||||
self._numeric = re.compile(
|
||||
r"((\d+)[/\. ])?\s*((\d+)[/\.])?\s*(\d+)\.?$")
|
||||
|
||||
_span_1 = ['od', 'од']
|
||||
_span_2 = ['do', 'до']
|
||||
_range_1 = ['između', 'између']
|
||||
_range_2 = ['i', 'и']
|
||||
self._span = re.compile("(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_span_1), '|'.join(_span_2)),
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile("(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_range_1), '|'.join(_range_2)),
|
||||
self._span = re.compile(r"(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)"
|
||||
% ('|'.join(_span_1), '|'.join(_span_2)),
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile(r"(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)"
|
||||
% ('|'.join(_range_1), '|'.join(_range_2)),
|
||||
re.IGNORECASE)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
@@ -95,14 +95,16 @@ class DateParserSv(DateParser):
|
||||
def init_strings(self):
|
||||
""" Define, in Swedish, span and range regular expressions"""
|
||||
DateParser.init_strings(self)
|
||||
self._numeric = re.compile("((\d+)/)?\s*((\d+)/)?\s*(\d+)[/ ]?$")
|
||||
self._numeric = re.compile(r"((\d+)/)?\s*((\d+)/)?\s*(\d+)[/ ]?$")
|
||||
# this next RE has the (possibly-slashed) year at the string's start
|
||||
self._text2 = re.compile('((\d+)(/\d+)?)?\s+?%s\s*(\d+)?\s*$'
|
||||
% self._mon_str, re.IGNORECASE)
|
||||
self._span = re.compile("(från)?\s*(?P<start>.+)\s*(till|--|–)\s*(?P<stop>.+)",
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile("(mellan)\s+(?P<start>.+)\s+och\s+(?P<stop>.+)",
|
||||
re.IGNORECASE)
|
||||
self._text2 = re.compile(r'((\d+)(/\d+)?)?\s+?%s\s*(\d+)?\s*$'
|
||||
% self._mon_str, re.IGNORECASE)
|
||||
self._span = re.compile(
|
||||
r"(från)?\s*(?P<start>.+)\s*(till|--|–)\s*(?P<stop>.+)",
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile(
|
||||
r"(mellan)\s+(?P<start>.+)\s+och\s+(?P<stop>.+)",
|
||||
re.IGNORECASE)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
@@ -108,14 +108,14 @@ class DateParserUK(DateParser):
|
||||
|
||||
_span_1 = ['з', 'від']
|
||||
# b.c.e. pattern also have "до" so skip "до н."
|
||||
_span_2 = ['по', 'до(?!\s+н)']
|
||||
_span_2 = ['по', r'до(?!\s+н)']
|
||||
_range_1 = ['між']
|
||||
_range_2 = ['і', 'та']
|
||||
self._span = re.compile("(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_span_1), '|'.join(_span_2)),
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile("(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_range_1), '|'.join(_range_2)),
|
||||
self._span = re.compile(r"(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)" %
|
||||
('|'.join(_span_1), '|'.join(_span_2)),
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile(r"(%s)\s+(?P<start>.+)\s+(%s)\s+(?P<stop>.+)"
|
||||
% ('|'.join(_range_1), '|'.join(_range_2)),
|
||||
re.IGNORECASE)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
@@ -124,13 +124,13 @@ class DateParserZH_CN(DateParser):
|
||||
_span_2 = ['至']
|
||||
_range_1 = ['介于']
|
||||
_range_2 = ['与']
|
||||
self._span = re.compile("(%s)(?P<start>.+)(%s)(?P<stop>\d+)" %
|
||||
('|'.join(_span_1), '|'.join(_span_2)),
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile("(%s)(?P<start>.+)(%s)(?P<stop>\d+)" %
|
||||
self._span = re.compile(r"(%s)(?P<start>.+)(%s)(?P<stop>\d+)" %
|
||||
('|'.join(_span_1), '|'.join(_span_2)),
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile(r"(%s)(?P<start>.+)(%s)(?P<stop>\d+)" %
|
||||
('|'.join(_range_1), '|'.join(_range_2)),
|
||||
re.IGNORECASE)
|
||||
self._numeric = re.compile("((\d+)年\s*)?((\d+)月\s*)?(\d+)?日?\s*$")
|
||||
self._numeric = re.compile(r"((\d+)年\s*)?((\d+)月\s*)?(\d+)?日?\s*$")
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
@@ -124,13 +124,13 @@ class DateParserZH_TW(DateParser):
|
||||
_span_2 = ['至']
|
||||
_range_1 = ['介於']
|
||||
_range_2 = ['與']
|
||||
self._span = re.compile("(%s)(?P<start>.+)(%s)(?P<stop>\d+)" %
|
||||
('|'.join(_span_1), '|'.join(_span_2)),
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile("(%s)(?P<start>.+)(%s)(?P<stop>\d+)" %
|
||||
self._span = re.compile(r"(%s)(?P<start>.+)(%s)(?P<stop>\d+)" %
|
||||
('|'.join(_span_1), '|'.join(_span_2)),
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile(r"(%s)(?P<start>.+)(%s)(?P<stop>\d+)" %
|
||||
('|'.join(_range_1), '|'.join(_range_2)),
|
||||
re.IGNORECASE)
|
||||
self._numeric = re.compile("((\d+)年\s*)?((\d+)月\s*)?(\d+)?日?\s*$")
|
||||
self._numeric = re.compile(r"((\d+)年\s*)?((\d+)月\s*)?(\d+)?日?\s*$")
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#
|
||||
# Copyright (C) 2004-2006 Donald N. Allingham
|
||||
# Copyright (C) 2013 Vassilii Khachaturov
|
||||
# Copyright (C) 2014-2017 Paul Franklin
|
||||
# Copyright (C) 2014-2018 Paul Franklin
|
||||
#
|
||||
# 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
|
||||
@@ -570,6 +570,7 @@ class DateDisplay:
|
||||
def _get_short_weekday(self, date_val):
|
||||
if (date_val[0] == 0 or date_val[1] == 0 # no day or no month or both
|
||||
or date_val[1] == 13 # Hebrew has 13 months
|
||||
or date_val[2] > datetime.MAXYEAR # bug 10815
|
||||
or date_val[2] < 0): # B.C.E. date
|
||||
return ''
|
||||
w_day = datetime.date(date_val[2], date_val[1], date_val[0]) # y, m, d
|
||||
@@ -578,11 +579,16 @@ class DateDisplay:
|
||||
def _get_long_weekday(self, date_val):
|
||||
if (date_val[0] == 0 or date_val[1] == 0 # no day or no month or both
|
||||
or date_val[1] == 13 # Hebrew has 13 months
|
||||
or date_val[2] > datetime.MAXYEAR # bug 10815
|
||||
or date_val[2] < 0): # B.C.E. date
|
||||
return ''
|
||||
w_day = datetime.date(date_val[2], date_val[1], date_val[0]) # y, m, d
|
||||
return self.long_days[((w_day.weekday() + 1) % 7) + 1]
|
||||
|
||||
def _get_localized_year(self, year):
|
||||
""" Allow a subclass to modify the year, e.g. add a period """
|
||||
return year
|
||||
|
||||
def dd_dformat01(self, date_val):
|
||||
"""
|
||||
numerical
|
||||
@@ -594,19 +600,23 @@ class DateDisplay:
|
||||
return self.display_iso(date_val)
|
||||
else:
|
||||
if date_val[0] == date_val[1] == 0:
|
||||
return str(date_val[2])
|
||||
return self._get_localized_year(str(date_val[2]))
|
||||
else:
|
||||
value = self.dhformat.replace('%m', str(date_val[1]))
|
||||
# some locales have %b for the month, e.g. ar_EG, is_IS, nb_NO
|
||||
# so it would be "Jan" but as it's "numeric" I'll make it "1"
|
||||
value = value.replace('%b', str(date_val[1]))
|
||||
# some locales have %B for the month, e.g. ta_IN
|
||||
# so it would be "January" but as it's "numeric" I'll make it 1
|
||||
value = value.replace('%B', str(date_val[1]))
|
||||
# some locales have %a for the abbreviated day, e.g. is_IS
|
||||
value = value.replace('%a', self._get_short_weekday(date_val))
|
||||
# some locales have %A for the long/full day, e.g. ta_IN
|
||||
value = value.replace('%A', self._get_long_weekday(date_val))
|
||||
if '%b' in value or '%B' in value:
|
||||
# some locales have %b for the month (ar_EG, is_IS, nb_NO)
|
||||
# so it would be "Jan" but as it's "numeric" make it "1"
|
||||
value = value.replace('%b', str(date_val[1]))
|
||||
# some locales have %B for the month, e.g. ta_IN
|
||||
# so it would be "January" but as it's "numeric" make it 1
|
||||
value = value.replace('%B', str(date_val[1]))
|
||||
if '%a' in value or '%A' in value:
|
||||
# some locales have %a for the abbreviated day, e.g. is_IS
|
||||
value = value.replace('%a',
|
||||
self._get_short_weekday(date_val))
|
||||
# some locales have %A for the long/full day, e.g. ta_IN
|
||||
value = value.replace('%A',
|
||||
self._get_long_weekday(date_val))
|
||||
if date_val[0] == 0: # ignore the zero day and its delimiter
|
||||
i_day = value.find('%d')
|
||||
if len(value) == i_day + 2: # delimiter is left of the day
|
||||
@@ -628,7 +638,7 @@ class DateDisplay:
|
||||
year = self._slash_year(date_val[2], date_val[3])
|
||||
if date_val[0] == 0:
|
||||
if date_val[1] == 0:
|
||||
return year
|
||||
return self._get_localized_year(year)
|
||||
else:
|
||||
return self.format_long_month_year(date_val[1], year,
|
||||
inflect, long_months)
|
||||
@@ -654,7 +664,7 @@ class DateDisplay:
|
||||
year = self._slash_year(date_val[2], date_val[3])
|
||||
if date_val[0] == 0:
|
||||
if date_val[1] == 0:
|
||||
return year
|
||||
return self._get_localized_year(year)
|
||||
else:
|
||||
return self.format_short_month_year(date_val[1], year,
|
||||
inflect, short_months)
|
||||
@@ -680,7 +690,7 @@ class DateDisplay:
|
||||
year = self._slash_year(date_val[2], date_val[3])
|
||||
if date_val[0] == 0:
|
||||
if date_val[1] == 0:
|
||||
return year
|
||||
return self._get_localized_year(year)
|
||||
else:
|
||||
return self.format_long_month_year(date_val[1], year,
|
||||
inflect, long_months)
|
||||
@@ -706,7 +716,7 @@ class DateDisplay:
|
||||
year = self._slash_year(date_val[2], date_val[3])
|
||||
if date_val[0] == 0:
|
||||
if date_val[1] == 0:
|
||||
return year
|
||||
return self._get_localized_year(year)
|
||||
else:
|
||||
return self.format_short_month_year(date_val[1], year,
|
||||
inflect, short_months)
|
||||
|
||||
@@ -193,7 +193,7 @@ class DateParser:
|
||||
converted, the text string is assigned.
|
||||
"""
|
||||
|
||||
_dhformat_parse = re.compile(".*%(\S).*%(\S).*%(\S).*")
|
||||
_dhformat_parse = re.compile(r".*%(\S).*%(\S).*%(\S).*")
|
||||
|
||||
# RFC-2822 only uses capitalized English abbreviated names, no locales.
|
||||
_rfc_days = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat')
|
||||
@@ -423,63 +423,69 @@ class DateParser:
|
||||
# bce, calendar type and quality may be either at the end or at
|
||||
# the beginning of the given date string, therefore they will
|
||||
# be parsed from the middle and will be in match.group(2).
|
||||
self._bce_re = re.compile("(.*)\s+%s( ?.*)" % self._bce_str)
|
||||
self._bce_re = re.compile(r"(.*)\s+%s( ?.*)" % self._bce_str)
|
||||
|
||||
self._cal = re.compile("(.*)\s+\(%s\)( ?.*)" % self._cal_str,
|
||||
re.IGNORECASE)
|
||||
self._calny = re.compile("(.*)\s+\(%s,\s*%s\)( ?.*)" % (self._cal_str,
|
||||
self._ny_str),
|
||||
re.IGNORECASE)
|
||||
self._calny_iso = re.compile("(.*)\s+\(%s,\s*(\d{1,2}-\d{1,2})\)( ?.*)" % self._cal_str,
|
||||
re.IGNORECASE)
|
||||
self._cal = re.compile(r"(.*)\s+\(%s\)( ?.*)" % self._cal_str,
|
||||
re.IGNORECASE)
|
||||
self._calny = re.compile(r"(.*)\s+\(%s,\s*%s\)( ?.*)" %
|
||||
(self._cal_str, self._ny_str), re.IGNORECASE)
|
||||
self._calny_iso = re.compile(
|
||||
r"(.*)\s+\(%s,\s*(\d{1,2}-\d{1,2})\)( ?.*)" % self._cal_str,
|
||||
re.IGNORECASE)
|
||||
|
||||
self._ny = re.compile("(.*)\s+\(%s\)( ?.*)" % self._ny_str,
|
||||
re.IGNORECASE)
|
||||
self._ny_iso = re.compile("(.*)\s+\((\d{1,2}-\d{1,2})\)( ?.*)")
|
||||
self._ny = re.compile(r"(.*)\s+\(%s\)( ?.*)" % self._ny_str,
|
||||
re.IGNORECASE)
|
||||
self._ny_iso = re.compile(r"(.*)\s+\((\d{1,2}-\d{1,2})\)( ?.*)")
|
||||
|
||||
self._qual = re.compile("(.* ?)%s\s+(.+)" % self._qual_str,
|
||||
re.IGNORECASE)
|
||||
self._qual = re.compile(r"(.* ?)%s\s+(.+)" % self._qual_str,
|
||||
re.IGNORECASE)
|
||||
|
||||
self._span = re.compile("(from)\s+(?P<start>.+)\s+to\s+(?P<stop>.+)",
|
||||
self._span = re.compile(r"(from)\s+(?P<start>.+)\s+to\s+(?P<stop>.+)",
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile(
|
||||
r"(bet|bet.|between)\s+(?P<start>.+)\s+and\s+(?P<stop>.+)",
|
||||
re.IGNORECASE)
|
||||
self._modifier = re.compile(r'%s\s+(.*)' % self._mod_str,
|
||||
re.IGNORECASE)
|
||||
self._range = re.compile("(bet|bet.|between)\s+(?P<start>.+)\s+and\s+(?P<stop>.+)",
|
||||
re.IGNORECASE)
|
||||
self._modifier = re.compile('%s\s+(.*)' % self._mod_str,
|
||||
re.IGNORECASE)
|
||||
self._modifier_after = re.compile('(.*)\s+%s' % self._mod_after_str,
|
||||
self._modifier_after = re.compile(r'(.*)\s+%s' % self._mod_after_str,
|
||||
re.IGNORECASE)
|
||||
self._abt2 = re.compile('<(.*)>', re.IGNORECASE)
|
||||
self._text = re.compile('%s\.?(\s+\d+)?\s*,?\s+((\d+)(/\d+)?)?\s*$'
|
||||
% self._mon_str, re.IGNORECASE)
|
||||
self._abt2 = re.compile('<(.*)>', re.IGNORECASE)
|
||||
self._text = re.compile(r'%s\.?(\s+\d+)?\s*,?\s+((\d+)(/\d+)?)?\s*$'
|
||||
% self._mon_str, re.IGNORECASE)
|
||||
# this next RE has the (possibly-slashed) year at the string's end
|
||||
self._text2 = re.compile('(\d+)?\s+?%s\.?\s*((\d+)(/\d+)?)?\s*$' % self._mon_str,
|
||||
re.IGNORECASE)
|
||||
self._jtext = re.compile('%s\s+(\d+)?\s*,?\s*((\d+)(/\d+)?)?\s*$' % self._jmon_str,
|
||||
re.IGNORECASE)
|
||||
self._jtext2 = re.compile('(\d+)?\s+?%s\s*((\d+)(/\d+)?)?\s*$' % self._jmon_str,
|
||||
re.IGNORECASE)
|
||||
self._ftext = re.compile('%s\s+(\d+)?\s*,?\s*((\d+)(/\d+)?)?\s*$' % self._fmon_str,
|
||||
re.IGNORECASE)
|
||||
self._ftext2 = re.compile('(\d+)?\s+?%s\s*((\d+)(/\d+)?)?\s*$' % self._fmon_str,
|
||||
re.IGNORECASE)
|
||||
self._ptext = re.compile('%s\s+(\d+)?\s*,?\s*((\d+)(/\d+)?)?\s*$' % self._pmon_str,
|
||||
re.IGNORECASE)
|
||||
self._ptext2 = re.compile('(\d+)?\s+?%s\s*((\d+)(/\d+)?)?\s*$' % self._pmon_str,
|
||||
re.IGNORECASE)
|
||||
self._itext = re.compile('%s\s+(\d+)?\s*,?\s*((\d+)(/\d+)?)?\s*$' % self._imon_str,
|
||||
re.IGNORECASE)
|
||||
self._itext2 = re.compile('(\d+)?\s+?%s\s*((\d+)(/\d+)?)?\s*$' % self._imon_str,
|
||||
re.IGNORECASE)
|
||||
self._stext = re.compile('%s\.?\s+(\d+)?\s*,?\s*((\d+)(/\d+)?)?\s*$' % self._smon_str,
|
||||
re.IGNORECASE)
|
||||
self._stext2 = re.compile('(\d+)?\s+?%s\.?\s*((\d+)(/\d+)?)?\s*$' % self._smon_str,
|
||||
re.IGNORECASE)
|
||||
self._numeric = re.compile("((\d+)[/\.]\s*)?((\d+)[/\.]\s*)?(\d+)\s*$")
|
||||
self._iso = re.compile("(\d+)(/(\d+))?-(\d+)-(\d+)\s*$")
|
||||
self._isotimestamp = re.compile("^\s*?(\d{4})([01]\d)([0123]\d)(?:(?:[012]\d[0-5]\d[0-5]\d)|(?:\s+[012]\d:[0-5]\d(?::[0-5]\d)?))?\s*?$")
|
||||
self._rfc = re.compile("(%s,)?\s+(\d|\d\d)\s+%s\s+(\d+)\s+\d\d:\d\d(:\d\d)?\s+(\+|-)\d\d\d\d"
|
||||
% (self._rfc_day_str, self._rfc_mon_str))
|
||||
self._today = re.compile("^\s*%s\s*$" % self._today_str, re.IGNORECASE)
|
||||
self._text2 = re.compile(r'(\d+)?\s+?%s\.?\s*((\d+)(/\d+)?)?\s*$'
|
||||
% self._mon_str, re.IGNORECASE)
|
||||
self._jtext = re.compile(r'%s\s+(\d+)?\s*,?\s*((\d+)(/\d+)?)?\s*$'
|
||||
% self._jmon_str, re.IGNORECASE)
|
||||
self._jtext2 = re.compile(r'(\d+)?\s+?%s\s*((\d+)(/\d+)?)?\s*$'
|
||||
% self._jmon_str, re.IGNORECASE)
|
||||
self._ftext = re.compile(r'%s\s+(\d+)?\s*,?\s*((\d+)(/\d+)?)?\s*$'
|
||||
% self._fmon_str, re.IGNORECASE)
|
||||
self._ftext2 = re.compile(r'(\d+)?\s+?%s\s*((\d+)(/\d+)?)?\s*$'
|
||||
% self._fmon_str, re.IGNORECASE)
|
||||
self._ptext = re.compile(r'%s\s+(\d+)?\s*,?\s*((\d+)(/\d+)?)?\s*$'
|
||||
% self._pmon_str, re.IGNORECASE)
|
||||
self._ptext2 = re.compile(r'(\d+)?\s+?%s\s*((\d+)(/\d+)?)?\s*$'
|
||||
% self._pmon_str, re.IGNORECASE)
|
||||
self._itext = re.compile(r'%s\s+(\d+)?\s*,?\s*((\d+)(/\d+)?)?\s*$'
|
||||
% self._imon_str, re.IGNORECASE)
|
||||
self._itext2 = re.compile(r'(\d+)?\s+?%s\s*((\d+)(/\d+)?)?\s*$'
|
||||
% self._imon_str, re.IGNORECASE)
|
||||
self._stext = re.compile(r'%s\.?\s+(\d+)?\s*,?\s*((\d+)(/\d+)?)?\s*$'
|
||||
% self._smon_str, re.IGNORECASE)
|
||||
self._stext2 = re.compile(r'(\d+)?\s+?%s\.?\s*((\d+)(/\d+)?)?\s*$'
|
||||
% self._smon_str, re.IGNORECASE)
|
||||
self._numeric = re.compile(
|
||||
r"((\d+)[/\.]\s*)?((\d+)[/\.]\s*)?(\d+)\s*$")
|
||||
self._iso = re.compile(r"(\d+)(/(\d+))?-(\d+)-(\d+)\s*$")
|
||||
self._isotimestamp = re.compile(
|
||||
r"^\s*?(\d{4})([01]\d)([0123]\d)(?:(?:[012]\d[0-5]\d[0-5]\d)|"
|
||||
r"(?:\s+[012]\d:[0-5]\d(?::[0-5]\d)?))?\s*?$")
|
||||
self._rfc = re.compile(
|
||||
r"(%s,)?\s+(\d|\d\d)\s+%s\s+(\d+)\s+\d\d:\d\d(:\d\d)?\s+"
|
||||
r"(\+|-)\d\d\d\d" % (self._rfc_day_str, self._rfc_mon_str))
|
||||
self._today = re.compile(r"^\s*%s\s*$" % self._today_str,
|
||||
re.IGNORECASE)
|
||||
|
||||
def _get_int(self, val):
|
||||
"""
|
||||
|
||||
@@ -216,8 +216,9 @@ class DummyDb(M_A_M_B("NewBaseClass", (DbReadBase, Callback, object,), {})):
|
||||
"""
|
||||
Create a new DummyDb instance.
|
||||
"""
|
||||
DbReadBase.__init__(self)
|
||||
Callback.__init__(self)
|
||||
self.basedb = None
|
||||
self.__feature = {} # {"feature": VALUE, ...}
|
||||
self.db_is_open = False
|
||||
self.readonly = True
|
||||
self.name_formats = []
|
||||
|
||||
@@ -1059,13 +1059,13 @@ class NameDisplay:
|
||||
format_str = format_str[1:]
|
||||
else:
|
||||
patterns = [
|
||||
",\W*\"%(" + ("|".join(codes)) + ")\"", # ,\W*"%s"
|
||||
",\W*\(%(" + ("|".join(codes)) + ")\)", # ,\W*(%s)
|
||||
",\W*%(" + ("|".join(codes)) + ")", # ,\W*%s
|
||||
"\"%(" + ("|".join(codes)) + ")\"", # "%s"
|
||||
"_%(" + ("|".join(codes)) + ")_", # _%s_
|
||||
"\(%(" + ("|".join(codes)) + ")\)", # (%s)
|
||||
"%(" + ("|".join(codes)) + ")", # %s
|
||||
",\\W*\"%(" + ("|".join(codes)) + ")\"", # ,\W*"%s"
|
||||
",\\W*\\(%(" + ("|".join(codes)) + ")\\)", # ,\W*(%s)
|
||||
",\\W*%(" + ("|".join(codes)) + ")", # ,\W*%s
|
||||
"\"%(" + ("|".join(codes)) + ")\"", # "%s"
|
||||
"_%(" + ("|".join(codes)) + ")_", # _%s_
|
||||
"\\(%(" + ("|".join(codes)) + ")\\)", # (%s)
|
||||
"%(" + ("|".join(codes)) + ")", # %s
|
||||
]
|
||||
new_fmt = format_str
|
||||
|
||||
|
||||
@@ -35,7 +35,8 @@ import xml.dom.minidom
|
||||
# Gramps modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from ..const import PLACE_FORMATS
|
||||
from ..const import PLACE_FORMATS, GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
from ..config import config
|
||||
from ..utils.location import get_location_list
|
||||
from ..lib import PlaceType
|
||||
@@ -72,7 +73,7 @@ class PlaceDisplay:
|
||||
if os.path.exists(PLACE_FORMATS):
|
||||
self.load_formats()
|
||||
else:
|
||||
pf = PlaceFormat('Full', ':', '', 0, False)
|
||||
pf = PlaceFormat(_('Full'), ':', '', 0, False)
|
||||
self.place_formats.append(pf)
|
||||
|
||||
def display_event(self, db, event, fmt=-1):
|
||||
|
||||
@@ -56,17 +56,17 @@ class ChangedSinceBase(Rule):
|
||||
category = _('General filters')
|
||||
|
||||
def add_time(self, date):
|
||||
if re.search("\d.*\s+\d{1,2}:\d{2}:\d{2}", date):
|
||||
if re.search(r"\d.*\s+\d{1,2}:\d{2}:\d{2}", date):
|
||||
return date
|
||||
elif re.search("\d.*\s+\d{1,2}:\d{2}", date):
|
||||
elif re.search(r"\d.*\s+\d{1,2}:\d{2}", date):
|
||||
return date + ":00"
|
||||
elif re.search("\d.*\s+\d{1,2}", date):
|
||||
elif re.search(r"\d.*\s+\d{1,2}", date):
|
||||
return date + ":00:00"
|
||||
elif re.search("\d{4}-\d{1,2}-\d{1,2}", date):
|
||||
elif re.search(r"\d{4}-\d{1,2}-\d{1,2}", date):
|
||||
return date + " 00:00:00"
|
||||
elif re.search("\d{4}-\d{1,2}", date):
|
||||
elif re.search(r"\d{4}-\d{1,2}", date):
|
||||
return date + "-01 00:00:00"
|
||||
elif re.search("\d{4}", date):
|
||||
elif re.search(r"\d{4}", date):
|
||||
return date + "-01-01 00:00:00"
|
||||
else:
|
||||
return date
|
||||
|
||||
@@ -520,11 +520,11 @@ class Place(CitationBase, NoteBase, MediaBase, UrlBase, PrimaryObject):
|
||||
Return a list of alternate :class:`~.location.Location` objects the
|
||||
present alternate information about the current Place.
|
||||
|
||||
A Place can have more than one :class:`~.location.Location`, since names
|
||||
and jurisdictions can change over time for the same place.
|
||||
A Place can have more than one :class:`~.location.Location`, since
|
||||
names and jurisdictions can change over time for the same place.
|
||||
|
||||
:returns: Returns the alternate :class:`~.location.Location`\ s for the
|
||||
Place
|
||||
:returns: Returns the alternate :class:`~.location.Location` objects
|
||||
for the Place
|
||||
:rtype: list of :class:`~.location.Location` objects
|
||||
"""
|
||||
return self.alt_loc
|
||||
|
||||
@@ -164,8 +164,16 @@ class MergeFamilyQuery:
|
||||
self.phoenix = self.database.get_family_from_handle(new_handle)
|
||||
self.titanic = self.database.get_family_from_handle(old_handle)
|
||||
|
||||
phoenix_father = self.database.get_person_from_handle(self.phoenix_fh)
|
||||
phoenix_mother = self.database.get_person_from_handle(self.phoenix_mh)
|
||||
if self.phoenix_fh:
|
||||
phoenix_father = self.database.get_person_from_handle(
|
||||
self.phoenix_fh)
|
||||
else:
|
||||
phoenix_father = None
|
||||
if self.phoenix_mh:
|
||||
phoenix_mother = self.database.get_person_from_handle(
|
||||
self.phoenix_mh)
|
||||
else:
|
||||
phoenix_mother = None
|
||||
self.phoenix = self.database.get_family_from_handle(new_handle)
|
||||
self.titanic = self.database.get_family_from_handle(old_handle)
|
||||
self.phoenix.merge(self.titanic)
|
||||
|
||||
@@ -85,7 +85,7 @@ def _get_extension(mime_type):
|
||||
extension = None
|
||||
try:
|
||||
hcr = ConnectRegistry(None, HKEY_CLASSES_ROOT)
|
||||
subkey = OpenKey(hcr, "MIME\DataBase\Content Type")
|
||||
subkey = OpenKey(hcr, r"MIME\DataBase\Content Type")
|
||||
mimekey = OpenKey(subkey, mime_type)
|
||||
extension, value_type = QueryValueEx(mimekey, "Extension")
|
||||
CloseKey(mimekey)
|
||||
|
||||
@@ -34,6 +34,7 @@ LOG = logging.getLogger(".Gramplets")
|
||||
#
|
||||
#------------------------------------------------------------------------
|
||||
from ..const import GRAMPS_LOCALE as glocale
|
||||
from gramps.gen.config import config
|
||||
_ = glocale.translation.gettext
|
||||
|
||||
class Gramplet:
|
||||
@@ -296,12 +297,15 @@ class Gramplet:
|
||||
not self.gui.force_update):
|
||||
self.dirty = True
|
||||
if self.dbstate.is_open():
|
||||
#print " %s is not active" % self.gui.gname
|
||||
#print(" %s is not active" % self.gui.gname)
|
||||
self.update_has_data()
|
||||
else:
|
||||
self.set_has_data(False)
|
||||
return
|
||||
#print " %s is UPDATING" % self.gui.gname
|
||||
#print(" %s is UPDATING" % self.gui.gname)
|
||||
if (config.get('interface.grampletbar-refresh') and
|
||||
self.gui.view.navigation_type() != None): # dashboard has no navtype
|
||||
return # Don't update the gramplet if we are in manual refresh
|
||||
self.dirty = False
|
||||
LOG.debug("gramplet updater: %s: running" % self.gui.title)
|
||||
if self._idle_id != 0:
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
# Copyright (C) 2007 Brian G. Matherly
|
||||
# Copyright (C) 2009 Benny Malengier
|
||||
# Copyright (C) 2009 Gary Burton
|
||||
# Copyright (C) 2017 Jonathan Biegert <azrdev@qrdn.de>
|
||||
# Copyright (C) 2017 Mindaugas Baranauskas
|
||||
# Copyright (C) 2017 Paul Culley
|
||||
#
|
||||
@@ -72,6 +73,11 @@ _RANKDIR = [{'name' : _("Vertical (↓)"), 'value' : "TB"},
|
||||
{'name' : _("Horizontal (→)"), 'value' : "LR"},
|
||||
{'name' : _("Horizontal (←)"), 'value' : "RL"}]
|
||||
|
||||
_NODE_PORTS = {"TB" : ("n", "s"),
|
||||
"BT" : ("s", "n"),
|
||||
"LR" : ("w", "e"),
|
||||
"RL" : ("e", "w")}
|
||||
|
||||
_PAGEDIR = [{'name' : _("Bottom, left"), 'value' : "BL"},
|
||||
{'name' : _("Bottom, right"), 'value' : "BR"},
|
||||
{'name' : _("Top, left"), 'value' : "TL"},
|
||||
@@ -183,6 +189,10 @@ class GVOptions:
|
||||
spline.set_help(_("How the lines between objects will be drawn."))
|
||||
menu.add_option(category, "spline", spline)
|
||||
|
||||
node_ports = BooleanOption(_("Alternate line attachment"), False)
|
||||
node_ports.set_help(_("Whether lines attach to nodes differently"))
|
||||
menu.add_option(category, "node_ports", node_ports)
|
||||
|
||||
# the page direction option only makes sense when the
|
||||
# number of horizontal and/or vertical pages is > 1,
|
||||
# so we need to remember these 3 controls for later
|
||||
@@ -409,6 +419,7 @@ class GVDocBase(BaseDoc, GVDoc):
|
||||
self.vpages = get_option('v_pages').get_value()
|
||||
self.usesubgraphs = get_option('usesubgraphs').get_value()
|
||||
self.spline = get_option('spline').get_value()
|
||||
self.node_ports = get_option('node_ports').get_value()
|
||||
|
||||
paper_size = paper_style.get_size()
|
||||
|
||||
@@ -456,6 +467,9 @@ class GVDocBase(BaseDoc, GVDoc):
|
||||
' splines="%s";\n' % self.spline +
|
||||
'\n' +
|
||||
' edge [len=0.5 style=solid fontsize=%d];\n' % self.fontsize)
|
||||
if self.node_ports:
|
||||
self.write(' edge [headport=%s tailport=%s];\n'
|
||||
% _NODE_PORTS[self.rankdir])
|
||||
|
||||
if self.fontfamily:
|
||||
self.write(' node [style=filled fontname="%s" fontsize=%d];\n'
|
||||
|
||||
@@ -340,3 +340,4 @@ def add_place_format_option(menu, category):
|
||||
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
|
||||
|
||||
@@ -34,7 +34,7 @@ import unicodedata
|
||||
# constants
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
IGNORE = "HW~!@#$%^&*()_+=-`[]\|;:'/?.,<>\" \t\f\v"
|
||||
IGNORE = "HW~!@#$%^&*()_+=-`[]\\|;:'/?.,<>\" \t\f\v"
|
||||
TABLE = bytes.maketrans(b'ABCDEFGIJKLMNOPQRSTUVXYZ',
|
||||
b'012301202245501262301202')
|
||||
|
||||
|
||||
@@ -256,7 +256,7 @@ def get_participant_from_event(db, event_handle, all_=False):
|
||||
Obtain the first primary or family participant to an event we find in the
|
||||
database. Note that an event can have more than one primary or
|
||||
family participant, only one is returned, adding ellipses if there are
|
||||
more. If the all\_ parameter is true a comma-space separated string with
|
||||
more. If the all_ parameter is true a comma-space separated string with
|
||||
the names of all primary participants is returned and no ellipses is used.
|
||||
"""
|
||||
participant = ""
|
||||
|
||||
@@ -251,11 +251,17 @@ def create_checksum(full_path):
|
||||
Create a md5 hash for the given file.
|
||||
"""
|
||||
full_path = os.path.normpath(full_path)
|
||||
md5 = hashlib.md5()
|
||||
try:
|
||||
with open(full_path, 'rb') as media_file:
|
||||
md5sum = hashlib.md5(media_file.read()).hexdigest()
|
||||
while True:
|
||||
buf = media_file.read(65536)
|
||||
if not buf:
|
||||
break
|
||||
md5.update(buf)
|
||||
md5sum = md5.hexdigest()
|
||||
except IOError:
|
||||
md5sum = ''
|
||||
md5sum = ''
|
||||
except UnicodeEncodeError:
|
||||
md5sum = ''
|
||||
md5sum = ''
|
||||
return md5sum
|
||||
|
||||
+2
-2
@@ -459,12 +459,12 @@ def run():
|
||||
if argpars.need_gui():
|
||||
LOG.debug("A GUI is needed, set it up")
|
||||
try:
|
||||
from .gui.grampsgui import startgtkloop
|
||||
from .gui.grampsgui import startgramps
|
||||
# no DISPLAY is a RuntimeError in an older pygtk (e.g. F14's 2.17)
|
||||
except RuntimeError as msg:
|
||||
error += [(_("Configuration error:"), str(msg))]
|
||||
return error
|
||||
startgtkloop(error, argpars)
|
||||
startgramps(error, argpars)
|
||||
else:
|
||||
# CLI use of Gramps
|
||||
argpars.print_help()
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2015 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.
|
||||
#
|
||||
|
||||
"""
|
||||
A replacement ActionGroup that correctly loads named icons from an icon theme.
|
||||
"""
|
||||
|
||||
from gi.repository import Gtk
|
||||
|
||||
class ActionGroup(Gtk.ActionGroup):
|
||||
|
||||
def add_actions(self, action_list, **kwargs):
|
||||
Gtk.ActionGroup.add_actions(self, action_list, **kwargs)
|
||||
self.fix_icon_name(action_list)
|
||||
|
||||
def add_toggle_actions(self, action_list, **kwargs):
|
||||
Gtk.ActionGroup.add_toggle_actions(self, action_list, **kwargs)
|
||||
self.fix_icon_name(action_list)
|
||||
|
||||
def add_radio_actions(self, action_list, **kwargs):
|
||||
Gtk.ActionGroup.add_radio_actions(self, action_list, **kwargs)
|
||||
self.fix_icon_name(action_list)
|
||||
|
||||
def fix_icon_name(self, action_list):
|
||||
for action_tuple in action_list:
|
||||
if action_tuple[1]:
|
||||
action = self.get_action(action_tuple[0])
|
||||
action.set_icon_name(action_tuple[1])
|
||||
@@ -143,8 +143,10 @@ class ColumnOrder(Gtk.Box):
|
||||
index = 0
|
||||
for val, size in zip(self.oldorder, self.oldsize):
|
||||
if val in self.oldvis:
|
||||
size = widths[index]
|
||||
index += 1
|
||||
if val != self.oldvis[-1]:
|
||||
# don't use last col width, its wrong
|
||||
size = widths[index]
|
||||
index += 1
|
||||
colord.append((1, val, size))
|
||||
else:
|
||||
colord.append((0, val, size))
|
||||
|
||||
@@ -984,6 +984,12 @@ class GrampsPreferences(ConfigureDialog):
|
||||
"""
|
||||
self.uistate.emit('grampletbar-close-changed')
|
||||
|
||||
def cb_grampletbar_refresh(self, obj):
|
||||
"""
|
||||
Gramplet bar refresh button preference callback
|
||||
"""
|
||||
self.uistate.emit('grampletbar-refresh-changed')
|
||||
|
||||
def add_formats_panel(self, configdialog):
|
||||
row = 0
|
||||
grid = Gtk.Grid()
|
||||
@@ -1185,6 +1191,13 @@ class GrampsPreferences(ConfigureDialog):
|
||||
row, 'interface.grampletbar-close', stop=3,
|
||||
extra_callback=self.cb_grampletbar_close)
|
||||
row += 1
|
||||
|
||||
# Gramplet bar refresh button:
|
||||
self.add_checkbox(grid,
|
||||
_("Enable manual refresh for the active gramplet"),
|
||||
row, 'interface.grampletbar-refresh', stop=3,
|
||||
extra_callback=self.cb_grampletbar_refresh)
|
||||
row += 1
|
||||
return _('Display'), grid
|
||||
|
||||
def auto_title_changed(self, obj):
|
||||
|
||||
+2
-2
@@ -1048,8 +1048,8 @@ def find_revisions(name):
|
||||
"""
|
||||
import re
|
||||
|
||||
rev = re.compile("\s*revision\s+([\d\.]+)")
|
||||
date = re.compile("date:\s+(\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d)[-+]\d\d;")
|
||||
rev = re.compile(r"\s*revision\s+([\d\.]+)")
|
||||
date = re.compile(r"date:\s+(\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d)[-+]\d\d;")
|
||||
|
||||
if not os.path.isfile(name) or not _RCS_FOUND:
|
||||
return []
|
||||
|
||||
+49
-29
@@ -28,6 +28,7 @@
|
||||
#-------------------------------------------------------------------------
|
||||
import os
|
||||
from io import StringIO
|
||||
import html
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@@ -65,6 +66,7 @@ from .glade import Glade
|
||||
from gramps.gen.utils.db import navigation_label
|
||||
from .widgets.progressdialog import ProgressMonitor, GtkProgressDialog
|
||||
from .dialog import ErrorDialog
|
||||
from .uimanager import ActionGroup
|
||||
|
||||
DISABLED = -1
|
||||
|
||||
@@ -246,14 +248,34 @@ class History(Callback):
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
_RCT_TOP = '<ui><menubar name="MenuBar"><menu action="FileMenu"><menu action="OpenRecent">'
|
||||
_RCT_BTM = '</menu></menu></menubar></ui>'
|
||||
_RCT_TOP = '<placeholder id="OpenRecentMenu">'
|
||||
_RCT_MENU = '''
|
||||
<item>
|
||||
<attribute name="action">win.%s</attribute>
|
||||
<attribute name="label" translatable="no">%s</attribute>
|
||||
</item>'''
|
||||
_RCT_BTM = '\n </placeholder>\n'
|
||||
_RCT_BAR_TOP = ('<object class="GtkMenu" id="OpenBtnMenu">\n'
|
||||
'<property name="visible">True</property>\n'
|
||||
'<property name="can_focus">False</property>')
|
||||
_RCT_BAR = '''
|
||||
<child>
|
||||
<object class="GtkMenuItem">
|
||||
<property name="action-name">win.%s</property>
|
||||
<property name="label" translatable="yes">%s</property>
|
||||
<property name="use_underline">False</property>
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
</child>'''
|
||||
_RCT_BAR_BTM = '\n</object>\n'
|
||||
|
||||
|
||||
from gramps.gen.recentfiles import RecentFiles
|
||||
|
||||
class RecentDocsMenu:
|
||||
def __init__(self, uistate, state, fileopen):
|
||||
self.action_group = Gtk.ActionGroup(name='RecentFiles')
|
||||
self.ui_xml = []
|
||||
self.action_group = ActionGroup('RecentFiles')
|
||||
self.active = DISABLED
|
||||
self.uistate = uistate
|
||||
self.uimanager = uistate.uimanager
|
||||
@@ -268,55 +290,52 @@ class RecentDocsMenu:
|
||||
ErrorDialog(_('Cannot load database'), str(err),
|
||||
parent=self.uistate.window)
|
||||
|
||||
def build(self):
|
||||
buf = StringIO()
|
||||
buf.write(_RCT_TOP)
|
||||
def build(self, update_menu=True):
|
||||
gramps_rf = RecentFiles()
|
||||
|
||||
count = 0
|
||||
|
||||
if self.active != DISABLED:
|
||||
self.uimanager.remove_ui(self.active)
|
||||
self.uimanager.remove_ui(self.ui_xml)
|
||||
self.uimanager.remove_action_group(self.action_group)
|
||||
self.action_group = Gtk.ActionGroup(name='RecentFiles')
|
||||
self.active = DISABLED
|
||||
|
||||
actions = []
|
||||
actionlist = []
|
||||
menu = _RCT_TOP
|
||||
bar = _RCT_BAR_TOP
|
||||
rfiles = gramps_rf.gramps_recent_files
|
||||
rfiles.sort(key=lambda x: x.get_time(), reverse=True)
|
||||
|
||||
new_menu = Gtk.Menu()
|
||||
#new_menu = Gtk.Menu()
|
||||
#new_menu.set_tooltip_text(_("Connect to a recent database"))
|
||||
|
||||
for item in rfiles:
|
||||
try:
|
||||
title = item.get_name()
|
||||
title = html.escape(item.get_name())
|
||||
filename = os.path.basename(item.get_path())
|
||||
action_id = "RecentMenu%d" % count
|
||||
buf.write('<menuitem action="%s"/>' % action_id)
|
||||
actions.append((action_id, None, title, None, None,
|
||||
make_callback(item, self.load)))
|
||||
mitem = Gtk.MenuItem(label=title, use_underline=False)
|
||||
mitem.connect('activate', make_callback(item, self.load))
|
||||
mitem.show()
|
||||
new_menu.append(mitem)
|
||||
# add the menuitem for this file
|
||||
menu += _RCT_MENU % (action_id, title)
|
||||
# add the action for this file
|
||||
actionlist.append((action_id, make_callback(item, self.load)))
|
||||
# add the toolbar menuitem
|
||||
bar += _RCT_BAR % (action_id, title)
|
||||
except RuntimeError:
|
||||
# ignore no longer existing files
|
||||
_LOG.info("Ignoring the RecentItem %s (%s)" % (title, filename))
|
||||
|
||||
count += 1
|
||||
buf.write(_RCT_BTM)
|
||||
self.action_group.add_actions(actions)
|
||||
self.uimanager.insert_action_group(self.action_group, 1)
|
||||
self.active = self.uimanager.add_ui_from_string(buf.getvalue())
|
||||
self.uimanager.ensure_update()
|
||||
buf.close()
|
||||
|
||||
if len(rfiles) > 0:
|
||||
new_menu.show()
|
||||
self.uistate.set_open_recent_menu(new_menu)
|
||||
menu += _RCT_BTM
|
||||
bar += _RCT_BAR_BTM
|
||||
self.ui_xml = [menu, bar]
|
||||
self.action_group.add_actions(actionlist)
|
||||
self.uimanager.insert_action_group(self.action_group)
|
||||
self.active = self.uimanager.add_ui_from_string(self.ui_xml)
|
||||
if update_menu:
|
||||
self.uimanager.update_menu()
|
||||
|
||||
def make_callback(val, func):
|
||||
return lambda x: func(val)
|
||||
return lambda x, y: func(val)
|
||||
|
||||
from .logger import RotateHandler
|
||||
|
||||
@@ -379,6 +398,7 @@ class DisplayState(Callback):
|
||||
'nameformat-changed' : None,
|
||||
'placeformat-changed' : None,
|
||||
'grampletbar-close-changed' : None,
|
||||
'grampletbar-refresh-changed' : None,
|
||||
'update-available' : (list, ),
|
||||
'autobackup' : None,
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ class EditLink(ManagedWindow):
|
||||
def update_ui(self, widget):
|
||||
url = self.url_link.get_text()
|
||||
# text needs to have 3 or more chars://and at least one char
|
||||
match = re.match("\w{3,}://\w+", url)
|
||||
match = re.match(r"\w{3,}://\w+", url)
|
||||
if match:
|
||||
self.ok_button.set_sensitive(True)
|
||||
else:
|
||||
|
||||
@@ -180,7 +180,6 @@ class EditNote(EditPrimary):
|
||||
self.set_window(win, None, self.get_menu_title())
|
||||
self.setup_configs('interface.note', 700, 500)
|
||||
|
||||
|
||||
vboxnote = self.top.get_object('vbox131')
|
||||
notebook = self.top.get_object('note_notebook')
|
||||
#recreate start page as GrampsTab
|
||||
@@ -271,7 +270,9 @@ class EditNote(EditPrimary):
|
||||
# create a formatting toolbar
|
||||
if not self.dbstate.db.readonly:
|
||||
vbox = self.top.get_object('container')
|
||||
vbox.pack_start(self.texteditor.get_toolbar(), False, False, 0)
|
||||
toolbar, self.action_group = self.texteditor.create_toolbar(
|
||||
self.uistate.uimanager, self.window)
|
||||
vbox.pack_start(toolbar, False, False, 0)
|
||||
self.texteditor.set_transient_parent(self.window)
|
||||
|
||||
# setup initial values for textview and textbuffer
|
||||
|
||||
@@ -676,33 +676,33 @@ class EditPerson(EditPrimary):
|
||||
EditMediaRef(self.dbstate, self.uistate, self.track,
|
||||
media_obj, media_ref, self.load_photo)
|
||||
|
||||
def _top_contextmenu(self):
|
||||
def _top_contextmenu(self, prefix):
|
||||
"""
|
||||
Override from base class, the menuitems and actiongroups for the top
|
||||
of context menu.
|
||||
"""
|
||||
self.all_action = Gtk.ActionGroup(name="/PersonAll")
|
||||
self.home_action = Gtk.ActionGroup(name="/PersonHome")
|
||||
self.track_ref_for_deletion("all_action")
|
||||
self.track_ref_for_deletion("home_action")
|
||||
if self.added:
|
||||
# Don't add items if not a real person yet
|
||||
return '', []
|
||||
|
||||
self.all_action.add_actions([
|
||||
('ActivePerson', None, _("Make Active Person"),
|
||||
None, None, self._make_active),
|
||||
])
|
||||
self.home_action.add_actions([
|
||||
('HomePerson', 'go-home', _("Make Home Person"),
|
||||
None, None, self._make_home_person),
|
||||
])
|
||||
_actions = [('ActivePerson', self._make_active),
|
||||
('HomePerson', self._make_home_person)]
|
||||
|
||||
self.all_action.set_visible(not self.added)
|
||||
self.home_action.set_visible(not self.added)
|
||||
ui_top_cm = (
|
||||
'''
|
||||
<item>
|
||||
<attribute name="action">{prefix}.ActivePerson</attribute>
|
||||
<attribute name="label" translatable="yes">Make Active Person'''
|
||||
'''</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="action">{prefix}.HomePerson</attribute>
|
||||
<attribute name="label" translatable="yes">Make Home Person'''
|
||||
'''</attribute>
|
||||
</item>
|
||||
'''.format(prefix=prefix))
|
||||
|
||||
ui_top_cm = '''
|
||||
<menuitem action="ActivePerson"/>
|
||||
<menuitem action="HomePerson"/>'''
|
||||
|
||||
return ui_top_cm, [self.all_action, self.home_action]
|
||||
return ui_top_cm, _actions
|
||||
|
||||
def _top_drag_data_get(self, widget, context, sel_data, info, time):
|
||||
if info == DdTargets.PERSON_LINK.app_id:
|
||||
@@ -713,17 +713,21 @@ class EditPerson(EditPrimary):
|
||||
"""
|
||||
Override base class, make inactive home action if not needed.
|
||||
"""
|
||||
if self.added:
|
||||
return
|
||||
home_action = self.uistate.uimanager.get_action(self.action_group,
|
||||
'HomePerson')
|
||||
if (self.dbstate.db.get_default_person() and
|
||||
self.obj.get_handle() ==
|
||||
self.dbstate.db.get_default_person().get_handle()):
|
||||
self.home_action.set_sensitive(False)
|
||||
home_action.set_enabled(False)
|
||||
else:
|
||||
self.home_action.set_sensitive(True)
|
||||
home_action.set_enabled(True)
|
||||
|
||||
def _make_active(self, obj):
|
||||
def _make_active(self, obj, value):
|
||||
self.uistate.set_active(self.obj.get_handle(), 'Person')
|
||||
|
||||
def _make_home_person(self, obj):
|
||||
def _make_home_person(self, obj, value):
|
||||
handle = self.obj.get_handle()
|
||||
if handle:
|
||||
self.dbstate.db.set_default_person_handle(handle)
|
||||
|
||||
@@ -179,8 +179,8 @@ class EditPlaceRef(EditReference):
|
||||
self.latitude.set_text(value[:coma])
|
||||
self.top.get_object("lat_entry").validate(force=True)
|
||||
self.top.get_object("lon_entry").validate(force=True)
|
||||
self.obj.set_latitude(self.latitude.get_value())
|
||||
self.obj.set_longitude(self.longitude.get_value())
|
||||
self.source.set_latitude(self.latitude.get_value())
|
||||
self.source.set_longitude(self.longitude.get_value())
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import abc
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from gi.repository import Gtk
|
||||
from gi.repository.Gio import SimpleActionGroup
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@@ -49,6 +50,8 @@ from ..display import display_help
|
||||
from ..dialog import SaveDialog
|
||||
from gramps.gen.lib import PrimaryObject
|
||||
from ..dbguielement import DbGUIElement
|
||||
from ..uimanager import ActionGroup
|
||||
|
||||
|
||||
class EditPrimary(ManagedWindow, DbGUIElement, metaclass=abc.ABCMeta):
|
||||
|
||||
@@ -75,6 +78,7 @@ class EditPrimary(ManagedWindow, DbGUIElement, metaclass=abc.ABCMeta):
|
||||
self.get_from_gramps_id = get_from_gramps_id
|
||||
self.contexteventbox = None
|
||||
self.__tabs = []
|
||||
self.action_group = None
|
||||
|
||||
ManagedWindow.__init__(self, uistate, track, obj)
|
||||
DbGUIElement.__init__(self, self.db)
|
||||
@@ -184,6 +188,8 @@ class EditPrimary(ManagedWindow, DbGUIElement, metaclass=abc.ABCMeta):
|
||||
self.dbstate.disconnect(self.dbstate_connect_key)
|
||||
self._cleanup_connects()
|
||||
self._cleanup_on_exit()
|
||||
if self.action_group:
|
||||
self.uistate.uimanager.remove_action_group(self.action_group)
|
||||
self.get_from_handle = None
|
||||
self.get_from_gramps_id = None
|
||||
ManagedWindow.close(self)
|
||||
@@ -283,48 +289,62 @@ class EditPrimary(ManagedWindow, DbGUIElement, metaclass=abc.ABCMeta):
|
||||
return False
|
||||
|
||||
#build the possible popup menu
|
||||
self._build_popup_ui()
|
||||
menu_model = self._build_popup_ui()
|
||||
if not menu_model:
|
||||
return False
|
||||
#set or unset sensitivity in popup
|
||||
self._post_build_popup_ui()
|
||||
|
||||
menu = self.popupmanager.get_widget('/Popup')
|
||||
if menu:
|
||||
menu.popup(None, None, None, None, event.button, event.time)
|
||||
return True
|
||||
menu = Gtk.Menu.new_from_model(menu_model)
|
||||
menu.attach_to_widget(obj, None)
|
||||
menu.show_all()
|
||||
if Gtk.MINOR_VERSION < 22:
|
||||
# ToDo The following is reported to work poorly with Wayland
|
||||
menu.popup(None, None, None, None,
|
||||
event.button, event.time)
|
||||
else:
|
||||
menu.popup_at_pointer(event)
|
||||
return True
|
||||
return False
|
||||
|
||||
def _build_popup_ui(self):
|
||||
"""
|
||||
Create actions and ui of context menu
|
||||
If you don't need a popup, override this and return None
|
||||
"""
|
||||
from ..plug.quick import create_quickreport_menu
|
||||
|
||||
self.popupmanager = Gtk.UIManager()
|
||||
#add custom actions
|
||||
(ui_top, action_groups) = self._top_contextmenu()
|
||||
for action in action_groups :
|
||||
self.popupmanager.insert_action_group(action, -1)
|
||||
prefix = str(id(self))
|
||||
#get custom ui and actions
|
||||
(ui_top, actions) = self._top_contextmenu(prefix)
|
||||
#see which quick reports are available now:
|
||||
ui_qr = ''
|
||||
if self.QR_CATEGORY > -1 :
|
||||
(ui_qr, reportactions) = create_quickreport_menu(self.QR_CATEGORY,
|
||||
self.dbstate, self.uistate,
|
||||
self.obj, track=self.track)
|
||||
self.report_action = Gtk.ActionGroup(name="/PersonReport")
|
||||
self.report_action.add_actions(reportactions)
|
||||
self.report_action.set_visible(True)
|
||||
self.popupmanager.insert_action_group(self.report_action, -1)
|
||||
(ui_qr, reportactions) = create_quickreport_menu(
|
||||
self.QR_CATEGORY, self.dbstate, self.uistate,
|
||||
self.obj, prefix, track=self.track)
|
||||
actions.extend(reportactions)
|
||||
|
||||
popupui = '''
|
||||
<ui>
|
||||
<popup name="Popup">''' + ui_top + '''
|
||||
<separator/>''' + ui_qr + '''
|
||||
</popup>
|
||||
</ui>'''
|
||||
popupui = '''<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<menu id="Popup">''' + ui_top + '''
|
||||
<section>
|
||||
''' + ui_qr + '''
|
||||
</section>
|
||||
</menu>
|
||||
</interface>'''
|
||||
|
||||
self.popupmanager.add_ui_from_string(popupui)
|
||||
builder = Gtk.Builder.new_from_string(popupui, -1)
|
||||
|
||||
def _top_contextmenu(self):
|
||||
self.action_group = ActionGroup('EditPopup' + prefix, actions,
|
||||
prefix)
|
||||
act_grp = SimpleActionGroup()
|
||||
self.window.insert_action_group(prefix, act_grp)
|
||||
self.window.set_application(self.uistate.uimanager.app)
|
||||
self.uistate.uimanager.insert_action_group(self.action_group, act_grp)
|
||||
return builder.get_object('Popup')
|
||||
|
||||
def _top_contextmenu(self, prefix):
|
||||
"""
|
||||
Derived class can create a ui with menuitems and corresponding list of
|
||||
actiongroups
|
||||
|
||||
@@ -186,8 +186,10 @@ class PersonSidebarFilter(SidebarFilter):
|
||||
# if the name is not empty, choose either the regular expression
|
||||
# version or the normal text match
|
||||
if name:
|
||||
rule = RegExpName([name], use_regex=regex)
|
||||
generic_filter.add_rule(rule)
|
||||
name_parts = name.split(sep=" ")
|
||||
for name_part in name_parts:
|
||||
rule = RegExpName([name_part], use_regex=regex)
|
||||
generic_filter.add_rule(rule)
|
||||
|
||||
# if the id is not empty, choose either the regular expression
|
||||
# version or the normal text match
|
||||
|
||||
+452
-70
@@ -3,6 +3,7 @@
|
||||
#
|
||||
# Copyright (C) 2000-2006 Donald N. Allingham
|
||||
# Copyright (C) 2009 Benny Malengier
|
||||
# Copyright (C) 2018 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
|
||||
@@ -50,7 +51,369 @@ _ = glocale.translation.gettext
|
||||
|
||||
MIN_PYGOBJECT_VERSION = (3, 12, 0)
|
||||
PYGOBJ_ERR = False
|
||||
MIN_GTK_VERSION = (3, 10)
|
||||
MIN_GTK_VERSION = (3, 12)
|
||||
UIDEFAULT = (
|
||||
'''<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<menu id="menubar"><section id="menubar-update">
|
||||
<submenu id='m1'>
|
||||
<attribute name="label" translatable="yes">_Family Trees</attribute>
|
||||
<section id="ftree">
|
||||
<item>
|
||||
<attribute name="action">win.Open</attribute>
|
||||
<attribute name="label" translatable="yes">'''
|
||||
'''_Manage Family Trees</attribute>
|
||||
</item>
|
||||
<submenu>
|
||||
<attribute name="action">win.OpenRecent</attribute>
|
||||
<attribute name="label" translatable="yes">Open _Recent</attribute>
|
||||
<placeholder id="OpenRecentMenu">
|
||||
</placeholder>
|
||||
</submenu>
|
||||
<item groups='RO'>
|
||||
<attribute name="action">win.Close</attribute>
|
||||
<attribute name="label" translatable="yes">_Close</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section groups='RO RW'>
|
||||
<item groups='RW'>
|
||||
<attribute name="action">win.Import</attribute>
|
||||
<attribute name="label" translatable="yes">_Import...</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="action">win.Export</attribute>
|
||||
<attribute name="label" translatable="yes">_Export...</attribute>
|
||||
</item>
|
||||
<placeholder id="LocalExport">
|
||||
</placeholder>
|
||||
<item>
|
||||
<attribute name="action">win.Backup</attribute>
|
||||
<attribute name="label" translatable="yes">Make Backup...</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section>
|
||||
<item groups='RO'>
|
||||
<attribute name="action">win.Abandon</attribute>
|
||||
<attribute name="label" translatable="yes">'''
|
||||
'''_Abandon Changes and Quit</attribute>
|
||||
</item>
|
||||
<placeholder groups='OSX' id='osxquit'>
|
||||
<item>
|
||||
<attribute name="action">app.quit</attribute>
|
||||
<attribute name="label" translatable="yes">_Quit</attribute>
|
||||
</item>
|
||||
</placeholder>
|
||||
</section>
|
||||
</submenu>
|
||||
<submenu id='m2' groups='RW'>
|
||||
<attribute name="label" translatable="yes">_Add</attribute>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="action">win.PersonAdd</attribute>
|
||||
<attribute name="label" translatable="yes">Person</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="action">win.FamilyAdd</attribute>
|
||||
<attribute name="label" translatable="yes">Family</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="action">win.EventAdd</attribute>
|
||||
<attribute name="label" translatable="yes">Event</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="action">win.PlaceAdd</attribute>
|
||||
<attribute name="label" translatable="yes">Place</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="action">win.SourceAdd</attribute>
|
||||
<attribute name="label" translatable="yes">Source</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="action">win.CitationAdd</attribute>
|
||||
<attribute name="label" translatable="yes">Citation</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="action">win.RepositoryAdd</attribute>
|
||||
<attribute name="label" translatable="yes">Repository</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="action">win.MediaAdd</attribute>
|
||||
<attribute name="label" translatable="yes">Media</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="action">win.NoteAdd</attribute>
|
||||
<attribute name="label" translatable="yes">Note</attribute>
|
||||
</item>
|
||||
</section>
|
||||
</submenu>
|
||||
<submenu id='m3'>
|
||||
<attribute name="label" translatable="yes">_Edit</attribute>
|
||||
<section groups='RW'>
|
||||
<placeholder id="undo">
|
||||
</placeholder>
|
||||
<placeholder id="redo">
|
||||
</placeholder>
|
||||
<item>
|
||||
<attribute name="action">win.UndoHistory</attribute>
|
||||
<attribute name="label" translatable="yes">Undo History</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section id='CommonEdit' groups='RW'>
|
||||
</section>
|
||||
<section id='TagMenu' groups='RW'>
|
||||
</section>
|
||||
<section groups='RW'>
|
||||
<item>
|
||||
<attribute name="action">win.Clipboard</attribute>
|
||||
<attribute name="label" translatable="yes">Clip_board</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section>
|
||||
<placeholder groups='OSX' id='osxpref'>
|
||||
<item>
|
||||
<attribute name="action">app.preferences</attribute>
|
||||
<attribute name="label" translatable="yes">'''
|
||||
'''_Preferences...</attribute>
|
||||
</item>
|
||||
</placeholder>
|
||||
<placeholder id='otheredit'>
|
||||
</placeholder>
|
||||
</section>
|
||||
</submenu>
|
||||
<submenu id='m4'>
|
||||
<attribute name="label" translatable="yes">_View</attribute>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="action">win.ConfigView</attribute>
|
||||
<attribute name="label" translatable="yes">_Configure...</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="action">win.Navigator</attribute>
|
||||
<attribute name="label" translatable="yes">_Navigator</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="action">win.Toolbar</attribute>
|
||||
<attribute name="label" translatable="yes">_Toolbar</attribute>
|
||||
</item>
|
||||
<placeholder id='Bars'>
|
||||
</placeholder>
|
||||
<item>
|
||||
<attribute name="action">win.Fullscreen</attribute>
|
||||
<attribute name="label" translatable="yes">F_ull Screen</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section id="ViewsInCatagory">
|
||||
</section>
|
||||
</submenu>
|
||||
<submenu id="m5" groups='RO'>
|
||||
<attribute name="label" translatable="yes">_Go</attribute>
|
||||
<placeholder id="CommonGo">
|
||||
</placeholder>
|
||||
<section id="CommonHistory">
|
||||
</section>
|
||||
</submenu>
|
||||
<submenu id='m6' groups='RW'>
|
||||
<attribute name="label" translatable="yes">_Bookmarks</attribute>
|
||||
<section id="AddEditBook">
|
||||
</section>
|
||||
<section id="GoToBook">
|
||||
</section>
|
||||
</submenu>
|
||||
<submenu id='m7' groups='RO'>
|
||||
<attribute name="label" translatable="yes">_Reports</attribute>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="action">win.Books</attribute>
|
||||
<attribute name="label" translatable="yes">Books...</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section id="P_ReportsMenu">
|
||||
</section>
|
||||
</submenu>
|
||||
<submenu id='m8' groups='RW'>
|
||||
<attribute name="label" translatable="yes">_Tools</attribute>
|
||||
<section id="P_ToolsMenu">
|
||||
</section>
|
||||
</submenu>
|
||||
<submenu id='m9' groups='RO'>
|
||||
<attribute name="label" translatable="yes">_Windows</attribute>
|
||||
<section id="WinMenu">
|
||||
</section>
|
||||
</submenu>
|
||||
<submenu id='m10'>
|
||||
<attribute name="label" translatable="yes">_Help</attribute>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="action">win.UserManual</attribute>
|
||||
<attribute name="label" translatable="yes">_User Manual</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="action">win.FAQ</attribute>
|
||||
<attribute name="label" translatable="yes">_FAQ</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="action">win.KeyBindings</attribute>
|
||||
<attribute name="label" translatable="yes">_Key Bindings</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="action">win.TipOfDay</attribute>
|
||||
<attribute name="label" translatable="yes">Tip of the Day</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="action">win.PluginStatus</attribute>
|
||||
<attribute name="label" translatable="yes">'''
|
||||
'''_Plugin Manager</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="action">win.HomePage</attribute>
|
||||
<attribute name="label" translatable="yes">'''
|
||||
'''Gramps _Home Page</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="action">win.MailingLists</attribute>
|
||||
<attribute name="label" translatable="yes">'''
|
||||
'''Gramps _Mailing Lists</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="action">win.ReportBug</attribute>
|
||||
<attribute name="label" translatable="yes">_Report a Bug</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="action">win.ExtraPlugins</attribute>
|
||||
<attribute name="label" translatable="yes">'''
|
||||
'''_Extra Reports/Tools</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section groups='OSX'>
|
||||
<item>
|
||||
<attribute name="action">app.about</attribute>
|
||||
<attribute name="label" translatable="yes">_About</attribute>
|
||||
</item>
|
||||
</section>
|
||||
</submenu>
|
||||
</section></menu>
|
||||
|
||||
<object class="GtkMenu" id="OpenBtnMenu">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
|
||||
<object class="GtkToolbar" id="ToolBar">
|
||||
<property name="hexpand">1</property>
|
||||
<property name="toolbar-style">GTK_TOOLBAR_ICONS</property>
|
||||
<style>
|
||||
<class name="primary-toolbar"/>
|
||||
</style>
|
||||
<child>
|
||||
<object class="GtkToolButton">
|
||||
<property name="icon-name">gramps</property>
|
||||
<property name="action-name">win.Open</property>
|
||||
<property name="tooltip_text" translatable="yes">'''
|
||||
'''Manage databases</property>
|
||||
<property name="label" translatable="yes">_Family Trees</property>
|
||||
<property name="use-underline">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="homogeneous">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToolItem">
|
||||
<child>
|
||||
<object class="GtkMenuButton">
|
||||
<property name="tooltip_text" translatable="yes">'''
|
||||
'''Connect to a recent database</property>
|
||||
<property name="popup">OpenBtnMenu</property>
|
||||
<property name="relief">GTK_RELIEF_NONE</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<placeholder id='CommonNavigation'>
|
||||
</placeholder>
|
||||
<placeholder id='BarCommonEdit'>
|
||||
</placeholder>
|
||||
<placeholder id='TagTool'>
|
||||
</placeholder>
|
||||
<child groups='RW'>
|
||||
<object class="GtkToolButton" id="Clipboard">
|
||||
<property name="icon-name">edit-paste</property>
|
||||
<property name="action-name">win.Clipboard</property>
|
||||
<property name="tooltip_text" translatable="yes">'''
|
||||
'''Open the Clipboard dialog</property>
|
||||
<property name="label" translatable="yes">Clip_board</property>
|
||||
<property name="use-underline">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="homogeneous">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparatorToolItem"/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToolButton" id="ConfigView">
|
||||
<property name="icon-name">gramps-config</property>
|
||||
<property name="action-name">win.ConfigView</property>
|
||||
<property name="tooltip_text" translatable="yes">'''
|
||||
'''Configure the active view</property>
|
||||
<property name="label" translatable="yes">_Configure...</property>
|
||||
<property name="use-underline">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="homogeneous">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<placeholder id='ViewsInCategoryBar'>
|
||||
</placeholder>
|
||||
<child>
|
||||
<object class="GtkSeparatorToolItem" id="sep"/>
|
||||
</child>
|
||||
<placeholder id="MoreButtons">
|
||||
</placeholder>
|
||||
<child groups='RO'>
|
||||
<object class="GtkToolButton" id="Reports">
|
||||
<property name="icon-name">gramps-reports</property>
|
||||
<property name="action-name">win.Reports</property>
|
||||
<property name="tooltip_text" translatable="yes">'''
|
||||
'''Open the reports dialog</property>
|
||||
<property name="label" translatable="yes">_Reports</property>
|
||||
<property name="use-underline">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="homogeneous">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child groups='RW'>
|
||||
<object class="GtkToolButton" id="Tools">
|
||||
<property name="icon-name">gramps-tools</property>
|
||||
<property name="action-name">win.Tools</property>
|
||||
<property name="tooltip_text" translatable="yes">'''
|
||||
'''Open the tools dialog</property>
|
||||
<property name="label" translatable="yes">_Tools</property>
|
||||
<property name="use-underline">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="homogeneous">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<placeholder id='AfterTools'>
|
||||
</placeholder>
|
||||
</object>
|
||||
|
||||
<menu id="Popup">
|
||||
</menu>
|
||||
|
||||
</interface>
|
||||
''')
|
||||
|
||||
try:
|
||||
#import gnome introspection, part of pygobject
|
||||
@@ -235,9 +598,8 @@ class Gramps:
|
||||
process. It may spawn several windows and control several databases.
|
||||
"""
|
||||
|
||||
def __init__(self, argparser):
|
||||
def __init__(self, argparser, app):
|
||||
from gramps.gen.dbstate import DbState
|
||||
from . import viewmanager
|
||||
from .viewmanager import ViewManager
|
||||
from gramps.cli.arghandler import ArgHandler
|
||||
from .tipofday import TipOfDay
|
||||
@@ -248,7 +610,7 @@ class Gramps:
|
||||
theme.append_search_path(IMAGE_DIR)
|
||||
|
||||
dbstate = DbState()
|
||||
self._vm = ViewManager(dbstate,
|
||||
self._vm = ViewManager(app, dbstate,
|
||||
config.get("interface.view-categories"))
|
||||
|
||||
if (lin()
|
||||
@@ -256,7 +618,7 @@ class Gramps:
|
||||
and not gettext.find(GTK_GETTEXT_DOMAIN)):
|
||||
_display_gtk_gettext_message(parent=self._vm.window)
|
||||
|
||||
#_display_welcome_message(parent=self._vm.window)
|
||||
_display_welcome_message(parent=self._vm.window)
|
||||
|
||||
_display_translator_message(parent=self._vm.window)
|
||||
|
||||
@@ -303,68 +665,18 @@ class Gramps:
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
def __startgramps(errors, argparser):
|
||||
def startgramps(errors, argparser):
|
||||
"""
|
||||
Main startup function started via GObject.timeout_add
|
||||
First action inside the gtk loop
|
||||
"""
|
||||
try:
|
||||
from .dialog import ErrorDialog
|
||||
#handle first existing errors in GUI fashion
|
||||
if errors:
|
||||
for error in errors:
|
||||
ErrorDialog(error[0], error[1]) # TODO no-parent
|
||||
Gtk.main_quit()
|
||||
sys.exit(1)
|
||||
|
||||
if argparser.errors:
|
||||
for error in argparser.errors:
|
||||
ErrorDialog(error[0], error[1]) # TODO no-parent
|
||||
Gtk.main_quit()
|
||||
sys.exit(1)
|
||||
|
||||
# add gui logger
|
||||
from .logger import RotateHandler, GtkHandler
|
||||
form = logging.Formatter(
|
||||
fmt="%(relativeCreated)d: %(levelname)s: "
|
||||
"%(filename)s: line %(lineno)d: %(message)s")
|
||||
# Create the log handlers
|
||||
rot_h = RotateHandler(capacity=20)
|
||||
rot_h.setFormatter(form)
|
||||
# Only error and critical log records should
|
||||
# trigger the GUI handler.
|
||||
gtkh = GtkHandler(rotate_handler=rot_h)
|
||||
gtkh.setFormatter(form)
|
||||
gtkh.setLevel(logging.ERROR)
|
||||
logger = logging.getLogger()
|
||||
logger.addHandler(rot_h)
|
||||
logger.addHandler(gtkh)
|
||||
|
||||
except:
|
||||
#make sure there is a clean exit if there is an error in above steps
|
||||
quit_now = True
|
||||
exit_code = 1
|
||||
LOG.error(_("\nGramps failed to start. "
|
||||
"Please report a bug about this.\n"
|
||||
"This could be because of an error "
|
||||
"in a (third party) View on startup.\n"
|
||||
"To use another view, don't load a Family Tree, "
|
||||
"change view, and then load your Family Tree.\n"
|
||||
"You can also change manually "
|
||||
"the startup view in the gramps.ini file \n"
|
||||
"by changing the last-view parameter.\n"
|
||||
), exc_info=True)
|
||||
|
||||
# start Gramps, errors stop the gtk loop
|
||||
app = GrampsApplication(errors, argparser)
|
||||
try:
|
||||
quit_now = False
|
||||
exit_code = 0
|
||||
if has_display():
|
||||
Gramps(argparser)
|
||||
else:
|
||||
if app.run():
|
||||
print(_("Gramps terminated because of no DISPLAY"))
|
||||
quit_now = True
|
||||
exit_code = 1
|
||||
|
||||
except SystemExit as err:
|
||||
quit_now = True
|
||||
if err.code:
|
||||
@@ -395,18 +707,88 @@ def __startgramps(errors, argparser):
|
||||
), exc_info=True)
|
||||
|
||||
if quit_now:
|
||||
#stop gtk loop and quit
|
||||
Gtk.main_quit()
|
||||
app.quit()
|
||||
sys.exit(exit_code)
|
||||
|
||||
#function finished, return False to stop the timeout_add function calls
|
||||
return False
|
||||
|
||||
def startgtkloop(errors, argparser):
|
||||
"""
|
||||
We start the gtk loop and run the function to start up Gramps
|
||||
"""
|
||||
GLib.timeout_add(100, __startgramps, errors, argparser, priority=100)
|
||||
if os.path.exists(os.path.join(DATA_DIR, "gramps.accel")):
|
||||
Gtk.AccelMap.load(os.path.join(DATA_DIR, "gramps.accel"))
|
||||
Gtk.main()
|
||||
# we do the following import here to avoid the Gtk require version warning
|
||||
from .uimanager import UIManager
|
||||
from gramps.gen.constfunc import is_quartz
|
||||
|
||||
|
||||
class GrampsApplication(Gtk.Application):
|
||||
|
||||
def __init__(self, errors, argparser):
|
||||
super().__init__(application_id="org.gramps-project.Gramps")
|
||||
self.window = None
|
||||
self.errors = errors
|
||||
self.argparser = argparser
|
||||
|
||||
def do_startup(self):
|
||||
Gtk.Application.do_startup(self)
|
||||
self.uimanager = UIManager(self, UIDEFAULT)
|
||||
if not is_quartz():
|
||||
self.uimanager.show_groups = ['OSX']
|
||||
self.uimanager.update_menu(init=True)
|
||||
|
||||
if os.path.exists(os.path.join(DATA_DIR, "gramps.accel")):
|
||||
self.uimanager.load_accels(os.path.join(DATA_DIR, "gramps.accel"))
|
||||
try:
|
||||
from .dialog import ErrorDialog
|
||||
#handle first existing errors in GUI fashion
|
||||
if self.errors:
|
||||
for error in self.errors:
|
||||
ErrorDialog(error[0], error[1]) # TODO no-parent
|
||||
Gtk.main_quit()
|
||||
sys.exit(1)
|
||||
|
||||
if self.argparser.errors:
|
||||
for error in self.argparser.errors:
|
||||
ErrorDialog(error[0], error[1]) # TODO no-parent
|
||||
Gtk.main_quit()
|
||||
sys.exit(1)
|
||||
|
||||
# add gui logger
|
||||
from .logger import RotateHandler, GtkHandler
|
||||
form = logging.Formatter(
|
||||
fmt="%(relativeCreated)d: %(levelname)s: "
|
||||
"%(filename)s: line %(lineno)d: %(message)s")
|
||||
# Create the log handlers
|
||||
rot_h = RotateHandler(capacity=20)
|
||||
rot_h.setFormatter(form)
|
||||
# Only error and critical log records should
|
||||
# trigger the GUI handler.
|
||||
gtkh = GtkHandler(rotate_handler=rot_h)
|
||||
gtkh.setFormatter(form)
|
||||
gtkh.setLevel(logging.ERROR)
|
||||
logger = logging.getLogger()
|
||||
logger.addHandler(rot_h)
|
||||
logger.addHandler(gtkh)
|
||||
|
||||
except:
|
||||
#make sure there is a clean exit if error in above steps
|
||||
exit_code = 1
|
||||
LOG.error(_("\nGramps failed to start. "
|
||||
"Please report a bug about this.\n"
|
||||
"This could be because of an error "
|
||||
"in a (third party) View on startup.\n"
|
||||
"To use another view, don't load a Family Tree, "
|
||||
"change view, and then load your Family Tree.\n"
|
||||
"You can also change manually "
|
||||
"the startup view in the gramps.ini file \n"
|
||||
"by changing the last-view parameter.\n"
|
||||
), exc_info=True)
|
||||
#stop gtk loop and quit
|
||||
self.quit()
|
||||
sys.exit(exit_code)
|
||||
|
||||
def do_activate(self):
|
||||
# We only allow a single window and raise any existing ones
|
||||
if not self.window:
|
||||
# Windows are associated with the application
|
||||
# when the last one is closed the application shuts down
|
||||
Gramps(self.argparser, self)
|
||||
else:
|
||||
print('Gramps is already running.')
|
||||
self.window.present()
|
||||
|
||||
+25
-23
@@ -31,7 +31,7 @@ the create/deletion of dialog windows.
|
||||
#-------------------------------------------------------------------------
|
||||
import os
|
||||
from io import StringIO
|
||||
|
||||
import html
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# GNOME/GTK
|
||||
@@ -49,6 +49,7 @@ 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 .uimanager import ActionGroup
|
||||
from .glade import Glade
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@@ -57,8 +58,8 @@ from .glade import Glade
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
_win_top = '<ui><menubar name="MenuBar"><menu action="WindowsMenu">'
|
||||
_win_btm = '</menu></menubar></ui>'
|
||||
_win_top = '<section id="WinMenu">\n'
|
||||
_win_btm = '</section>\n'
|
||||
DISABLED = -1
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
@@ -108,7 +109,7 @@ class GrampsWindowManager:
|
||||
self.uimanager = uimanager
|
||||
self.window_tree = []
|
||||
self.id2item = {}
|
||||
self.action_group = Gtk.ActionGroup(name='WindowManger')
|
||||
self.action_group = ActionGroup(name='WindowManger')
|
||||
self.active = DISABLED
|
||||
self.ui = _win_top + _win_btm
|
||||
|
||||
@@ -125,9 +126,9 @@ class GrampsWindowManager:
|
||||
"""
|
||||
Enables the UI and action groups
|
||||
"""
|
||||
self.uimanager.insert_action_group(self.action_group, 1)
|
||||
self.active = self.uimanager.add_ui_from_string(self.ui)
|
||||
self.uimanager.ensure_update()
|
||||
self.uimanager.insert_action_group(self.action_group)
|
||||
self.active = self.uimanager.add_ui_from_string([self.ui])
|
||||
self.uimanager.update_menu()
|
||||
|
||||
def get_item_from_track(self, track):
|
||||
# Recursively find an item given track sequence
|
||||
@@ -270,31 +271,35 @@ class GrampsWindowManager:
|
||||
|
||||
def call_back_factory(self, item):
|
||||
if not isinstance(item, list):
|
||||
def func(obj):
|
||||
def func(*obj):
|
||||
if item.window_id and self.id2item.get(item.window_id):
|
||||
self.id2item[item.window_id]._present()
|
||||
else:
|
||||
def func(obj):
|
||||
def func(*obj):
|
||||
pass
|
||||
return func
|
||||
|
||||
def generate_id(self, item):
|
||||
return str(item.window_id)
|
||||
return 'wm/' + str(item.window_id)
|
||||
|
||||
def display_menu_list(self, data, action_data, mlist):
|
||||
menuitem = ('<item>\n'
|
||||
'<attribute name="action">win.%s</attribute>\n'
|
||||
'<attribute name="label" translatable="yes">'
|
||||
'%s...</attribute>\n'
|
||||
'</item>\n')
|
||||
if isinstance(mlist, (list, tuple)):
|
||||
i = mlist[0]
|
||||
idval = self.generate_id(i)
|
||||
data.write('<menu action="M:%s">' % idval)
|
||||
action_data.append(("M:"+idval, None, i.submenu_label,
|
||||
None, None, None))
|
||||
data.write('<submenu>\n<attribute name="label"'
|
||||
' translatable="yes">%s</attribute>\n' %
|
||||
html.escape(i.submenu_label))
|
||||
else:
|
||||
i = mlist
|
||||
idval = self.generate_id(i)
|
||||
|
||||
data.write('<menuitem action="%s"/>' % idval)
|
||||
action_data.append((idval, None, i.menu_label, None, None,
|
||||
self.call_back_factory(i)))
|
||||
data.write(menuitem % (idval, html.escape(i.menu_label)))
|
||||
action_data.append((idval, self.call_back_factory(i)))
|
||||
|
||||
if isinstance(mlist, (list, tuple)) and (len(mlist) > 1):
|
||||
for i in mlist[1:]:
|
||||
@@ -302,20 +307,17 @@ class GrampsWindowManager:
|
||||
self.display_menu_list(data, action_data, i)
|
||||
else:
|
||||
idval = self.generate_id(i)
|
||||
data.write('<menuitem action="%s"/>'
|
||||
% self.generate_id(i))
|
||||
action_data.append((idval, None, i.menu_label,
|
||||
None, None,
|
||||
self.call_back_factory(i)))
|
||||
data.write(menuitem % (idval, html.escape(i.menu_label)))
|
||||
action_data.append((idval, self.call_back_factory(i)))
|
||||
if isinstance(mlist, (list, tuple)):
|
||||
data.write('</menu>')
|
||||
data.write('</submenu>\n')
|
||||
|
||||
def build_windows_menu(self):
|
||||
if self.active != DISABLED:
|
||||
self.uimanager.remove_ui(self.active)
|
||||
self.uimanager.remove_action_group(self.action_group)
|
||||
|
||||
self.action_group = Gtk.ActionGroup(name='WindowManger')
|
||||
self.action_group = ActionGroup(name='WindowManger')
|
||||
action_data = []
|
||||
|
||||
data = StringIO()
|
||||
|
||||
+49
-31
@@ -38,22 +38,21 @@ from gi.repository import Gdk
|
||||
#-------------------------------------------------------------------------
|
||||
from gramps.gen.plug import (START, END)
|
||||
from .pluginmanager import GuiPluginManager
|
||||
from .actiongroup import ActionGroup
|
||||
from .uimanager import ActionGroup
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Constants
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
UICATEGORY = '''<ui>
|
||||
<menubar name="MenuBar">
|
||||
<menu action="ViewMenu">
|
||||
<placeholder name="ViewsInCategory">%s
|
||||
</placeholder>
|
||||
</menu>
|
||||
</menubar>
|
||||
</ui>
|
||||
'''
|
||||
UICATEGORY = ''' <section id="ViewsInCatagory">
|
||||
%s
|
||||
</section>
|
||||
'''
|
||||
UICATAGORYBAR = ''' <placeholder id='ViewsInCategoryBar'>
|
||||
%s
|
||||
</placeholder>
|
||||
'''
|
||||
|
||||
CATEGORY_ICON = {
|
||||
'Dashboard': 'gramps-gramplet',
|
||||
@@ -88,7 +87,6 @@ class Navigator:
|
||||
self.active_view = None
|
||||
|
||||
self.ui_category = {}
|
||||
self.view_toggle_actions = {}
|
||||
self.cat_view_group = None
|
||||
self.merge_ids = []
|
||||
|
||||
@@ -139,13 +137,35 @@ class Navigator:
|
||||
"""
|
||||
Load the sidebar plugins.
|
||||
"""
|
||||
menuitem = '''
|
||||
<item>
|
||||
<attribute name="action">win.ViewInCatagory</attribute>
|
||||
<attribute name="label" translatable="yes">%s</attribute>
|
||||
<attribute name="target">%d %d</attribute>
|
||||
</item>
|
||||
'''
|
||||
baritem = '''
|
||||
<child>
|
||||
<object class="GtkToggleToolButton" id="bar%d">
|
||||
<property name="action-name">win.ViewInCatagory</property>
|
||||
<property name="action-target">'%d %d'</property>
|
||||
<property name="icon-name">%s</property>
|
||||
<property name="tooltip_text" translatable="yes">%s</property>
|
||||
<property name="label" translatable="yes">%s</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="homogeneous">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
'''
|
||||
|
||||
plugman = GuiPluginManager.get_instance()
|
||||
|
||||
categories = []
|
||||
views = {}
|
||||
for cat_num, cat_views in enumerate(self.viewmanager.get_views()):
|
||||
uimenuitems = ''
|
||||
self.view_toggle_actions[cat_num] = []
|
||||
uibaritems = ''
|
||||
for view_num, page in enumerate(cat_views):
|
||||
|
||||
if view_num == 0:
|
||||
@@ -156,26 +176,26 @@ class Navigator:
|
||||
cat_icon = 'gramps-view'
|
||||
categories.append([cat_num, cat_name, cat_icon])
|
||||
|
||||
pageid = 'page_%i_%i' % (cat_num, view_num)
|
||||
uimenuitems += '\n<menuitem action="%s"/>' % pageid
|
||||
# id, stock, button text, UI, tooltip, page
|
||||
if view_num < 9:
|
||||
modifier = "<PRIMARY><ALT>%d" % ((view_num % 9) + 1)
|
||||
else:
|
||||
modifier = ""
|
||||
accel = "<PRIMARY><ALT>%d" % ((view_num % 9) + 1)
|
||||
self.viewmanager.uimanager.app.set_accels_for_action(
|
||||
"win.ViewInCatagory('%d %d')" % (cat_num, view_num),
|
||||
[accel])
|
||||
uimenuitems += menuitem % (page[0].name, cat_num, view_num)
|
||||
|
||||
stock_icon = page[0].stock_icon
|
||||
if stock_icon is None:
|
||||
stock_icon = cat_icon
|
||||
self.view_toggle_actions[cat_num].append((pageid,
|
||||
stock_icon,
|
||||
page[0].name, modifier, page[0].name, view_num))
|
||||
uibaritems += baritem % (view_num, cat_num, view_num,
|
||||
stock_icon, page[0].name,
|
||||
page[0].name)
|
||||
|
||||
views[cat_num].append((view_num, page[0].name, stock_icon))
|
||||
|
||||
if len(cat_views) > 1:
|
||||
#allow for switching views in a category
|
||||
self.ui_category[cat_num] = UICATEGORY % uimenuitems
|
||||
self.ui_category[cat_num] = [UICATEGORY % uimenuitems,
|
||||
UICATAGORYBAR % uibaritems]
|
||||
|
||||
for pdata in plugman.get_reg_sidebars():
|
||||
module = plugman.load_plugin(pdata)
|
||||
@@ -229,12 +249,10 @@ class Navigator:
|
||||
list(map(uimanager.remove_ui, self.merge_ids))
|
||||
|
||||
if cat_num in self.ui_category:
|
||||
self.cat_view_group = ActionGroup(name='viewmenu')
|
||||
self.cat_view_group.add_radio_actions(
|
||||
self.view_toggle_actions[cat_num], value=view_num,
|
||||
on_change=self.cb_view_clicked, user_data=cat_num)
|
||||
self.cat_view_group.set_sensitive(True)
|
||||
uimanager.insert_action_group(self.cat_view_group, 1)
|
||||
action = ('ViewInCatagory', self.cb_view_clicked, '',
|
||||
str(cat_num) + ' ' + str(view_num))
|
||||
self.cat_view_group = ActionGroup('viewmenu', [action])
|
||||
uimanager.insert_action_group(self.cat_view_group)
|
||||
mergeid = uimanager.add_ui_from_string(self.ui_category[cat_num])
|
||||
self.merge_ids.append(mergeid)
|
||||
|
||||
@@ -245,12 +263,12 @@ class Navigator:
|
||||
return
|
||||
sidebar.view_changed(cat_num, view_num)
|
||||
|
||||
def cb_view_clicked(self, radioaction, current, cat_num):
|
||||
def cb_view_clicked(self, radioaction, value):
|
||||
"""
|
||||
Called when a view is selected from the menu.
|
||||
"""
|
||||
view_num = radioaction.get_current_value()
|
||||
self.viewmanager.goto_page(cat_num, view_num)
|
||||
cat_num, view_num = value.get_string().split()
|
||||
self.viewmanager.goto_page(int(cat_num), int(view_num))
|
||||
|
||||
def __menu_button_pressed(self, button, event):
|
||||
"""
|
||||
|
||||
@@ -63,6 +63,12 @@ from gramps.gen.plug import (CATEGORY_QR_PERSON, CATEGORY_QR_FAMILY, CATEGORY_QR
|
||||
from ._textbufdoc import TextBufDoc
|
||||
from gramps.gen.simple import make_basic_stylesheet
|
||||
|
||||
MENUITEM = ('<item>\n'
|
||||
'<attribute name="action">{prefix}.{action}</attribute>\n'
|
||||
'<attribute name="label" translatable="yes">'
|
||||
'{label}</attribute>\n'
|
||||
'</item>\n')
|
||||
|
||||
def flatten(L):
|
||||
"""
|
||||
Flattens a possibly nested list. Removes None results, too.
|
||||
@@ -77,7 +83,7 @@ def flatten(L):
|
||||
retval.append(L)
|
||||
return retval
|
||||
|
||||
def create_web_connect_menu(dbstate, uistate, nav_group, handle):
|
||||
def create_web_connect_menu(dbstate, uistate, nav_group, handle, prefix):
|
||||
"""
|
||||
This functions querries the registered web connects. It collects
|
||||
the connects of the requested category, which must be one of
|
||||
@@ -88,12 +94,12 @@ def create_web_connect_menu(dbstate, uistate, nav_group, handle):
|
||||
handle as input method. A tuple is returned, containing the ui
|
||||
string of the menu, and its associated actions.
|
||||
"""
|
||||
top = ("<placeholder id='WebConnect'><submenu>\n"
|
||||
'<attribute name="label" translatable="yes">'
|
||||
'Web Connection</attribute>\n')
|
||||
actions = []
|
||||
ofile = StringIO()
|
||||
ofile.write('<menu action="WebConnect">')
|
||||
actions.append(('WebConnect', None, _("Web Connect"), None, None, None))
|
||||
menu = Gtk.Menu()
|
||||
menu.show()
|
||||
ofile.write(top)
|
||||
#select the web connects to show
|
||||
showlst = []
|
||||
pmgr = GuiPluginManager.get_instance()
|
||||
@@ -109,16 +115,17 @@ def create_web_connect_menu(dbstate, uistate, nav_group, handle):
|
||||
connections.sort(key=lambda plug: plug.name)
|
||||
actions = []
|
||||
for connect in connections:
|
||||
ofile.write('<menuitem action="%s"/>' % connect.key)
|
||||
actions.append((connect.key, None, connect.name, None, None,
|
||||
connect(dbstate, uistate, nav_group, handle)))
|
||||
ofile.write('</menu>')
|
||||
retval = [ofile.getvalue()]
|
||||
retval.extend(actions)
|
||||
return retval
|
||||
action = connect.key.replace(' ', '-')
|
||||
ofile.write(MENUITEM.format(prefix=prefix, action=action,
|
||||
label=connect.name))
|
||||
callback = connect(dbstate, uistate, nav_group, handle)
|
||||
actions.append((action,
|
||||
lambda x, y: callback(x)))
|
||||
ofile.write('</submenu></placeholder>\n')
|
||||
return (ofile.getvalue(), actions)
|
||||
|
||||
|
||||
def create_quickreport_menu(category, dbstate, uistate, handle, track=[]):
|
||||
def create_quickreport_menu(category, dbstate, uistate, handle, prefix, track=[]):
|
||||
""" This functions querries the registered quick reports with
|
||||
quick_report_list of _PluginMgr.py
|
||||
It collects the reports of the requested category, which must be one of
|
||||
@@ -132,39 +139,35 @@ def create_quickreport_menu(category, dbstate, uistate, handle, track=[]):
|
||||
A tuple is returned, containing the ui string of the quick report menu,
|
||||
and its associated actions
|
||||
"""
|
||||
|
||||
top = ("<submenu>\n"
|
||||
'<attribute name="label" translatable="yes">'
|
||||
'Quick View</attribute>\n')
|
||||
actions = []
|
||||
ofile = StringIO()
|
||||
ofile.write('<menu action="QuickReport">')
|
||||
|
||||
actions.append(('QuickReport', None, _("Quick View"), None, None, None))
|
||||
|
||||
menu = Gtk.Menu()
|
||||
menu.show()
|
||||
ofile.write(top)
|
||||
|
||||
#select the reports to show
|
||||
showlst = []
|
||||
pmgr = GuiPluginManager.get_instance()
|
||||
for pdata in pmgr.get_reg_quick_reports():
|
||||
if pdata.supported and pdata.category == category :
|
||||
#add tuple function, translated name, name, status
|
||||
showlst.append(pdata)
|
||||
|
||||
showlst.sort(key=lambda x: x.name)
|
||||
for pdata in showlst:
|
||||
new_key = pdata.id.replace(' ', '-')
|
||||
ofile.write('<menuitem action="%s"/>' % new_key)
|
||||
actions.append((new_key, None, pdata.name, None, None,
|
||||
make_quick_report_callback(pdata, category, dbstate,
|
||||
uistate, handle, track=track)))
|
||||
ofile.write('</menu>')
|
||||
ofile.write(MENUITEM.format(prefix=prefix, action=new_key,
|
||||
label=pdata.name))
|
||||
actions.append((new_key, make_quick_report_callback(
|
||||
pdata, category, dbstate, uistate, handle, track=track)))
|
||||
ofile.write('</submenu>\n')
|
||||
|
||||
return (ofile.getvalue(), actions)
|
||||
|
||||
def make_quick_report_callback(pdata, category, dbstate, uistate, handle,
|
||||
track=[]):
|
||||
return lambda x: run_report(dbstate, uistate, category, handle, pdata,
|
||||
track=track)
|
||||
return lambda x, y: run_report(dbstate, uistate, category, handle, pdata,
|
||||
track=track)
|
||||
|
||||
def get_quick_report_list(qv_category=None):
|
||||
"""
|
||||
|
||||
@@ -0,0 +1,517 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2018 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.
|
||||
#
|
||||
|
||||
"""
|
||||
A replacement UIManager and ActionGroup.
|
||||
"""
|
||||
|
||||
import copy
|
||||
import sys
|
||||
from ..gen.config import config
|
||||
import logging
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from gi.repository import GLib, Gio, Gtk
|
||||
|
||||
from ..gen.config import config
|
||||
|
||||
|
||||
LOG = logging.getLogger('gui.uimanager')
|
||||
|
||||
|
||||
ACTION_NAME = 0 # tuple index for action name
|
||||
ACTION_CB = 1 # tuple index for action callback
|
||||
ACTION_ACC = 2 # tuple index for action accelerator
|
||||
ACTION_ST = 3 # tuple index for action state
|
||||
|
||||
|
||||
class ActionGroup():
|
||||
""" This class represents a group of actions that con be manipulated
|
||||
together.
|
||||
"""
|
||||
def __init__(self, name, actionlist=None, prefix='win'):
|
||||
"""
|
||||
@param name: the action group name, used to match to the 'groups'
|
||||
attribute in the ui xml.
|
||||
@type name: string
|
||||
@type actionlist: list
|
||||
@param actionlist: the list of actions to add
|
||||
The list contains tuples with the following contents:
|
||||
string: Action Name
|
||||
method: signal callback function.
|
||||
None if just adding an accelerator
|
||||
string: accelerator ex: '<Primary>Enter' or '' for no accelerator.
|
||||
optional for non-stateful actions.
|
||||
state: initial state for stateful actions.
|
||||
'True' or 'False': the action is interpreted as a checkbox.
|
||||
'None': non stateful action (optional)
|
||||
'string': the action is interpreted as a Radio button
|
||||
@type prefix: str
|
||||
@param prefix: the prefix used by this group. If not provided, 'win'
|
||||
is assumed.
|
||||
"""
|
||||
self.name = name
|
||||
self.actionlist = actionlist if actionlist else []
|
||||
self.prefix = prefix + '.'
|
||||
self.act_group = None
|
||||
self.sensitive = True
|
||||
|
||||
def add_actions(self, actionlist):
|
||||
""" Add a list of actions to the current list
|
||||
@type actionlist: list
|
||||
@param actionlist: the list of actions to add
|
||||
"""
|
||||
self.actionlist.extend(actionlist)
|
||||
|
||||
|
||||
class UIManager():
|
||||
"""
|
||||
This is Gramps UIManager, it is designed to replace the deprecated Gtk
|
||||
UIManager. The replacement is not exact, but performs similar
|
||||
functions, in some case with the same method names and parameters.
|
||||
It is designed to be a singleton. The menu portion of this is responsible
|
||||
only for Gramps main window menus and toolbar.
|
||||
This was implemented to extend Gtk.Builder functions to allow editing
|
||||
(merging) of the original builder XML with additional XML fragments during
|
||||
operations. This allows changing of the menus and toolbar when the tree is
|
||||
loaded, views are changed etc.
|
||||
|
||||
The ActionGroup portions can also be used by other windows. Other windows
|
||||
needing menus or toolbars can create them via Gtk.Builder.
|
||||
"""
|
||||
|
||||
def __init__(self, app, initial_xml):
|
||||
"""
|
||||
@param app: Gramps Gtk.Application reference
|
||||
@type app: Gtk.Application
|
||||
@param initial_xml: Initial (primary) XML string for Gramps menus and
|
||||
toolbar
|
||||
@type changexml: string
|
||||
|
||||
The xml is basically Gtk Builder xml, in particular the various menu
|
||||
and toolbar elements. It is possible to add other elements as well.
|
||||
The xml this supports has been extended in two ways;
|
||||
1) there is an added "groups=" attribute to elements. This
|
||||
attribute associates the element with one or more named ActionGroups
|
||||
for making the element visible or not. If 'groups' is missing, the
|
||||
element will be shown as long as enclosing elements are shown. The
|
||||
element will be shown if the group is present and considered visible
|
||||
by the uimanager. If more than one group is needed, they should be
|
||||
separated by a space.
|
||||
2) there is an added <placeholder> tag supported; this is used to mark
|
||||
a place where merged UI XML can be inserted. During the update_menu
|
||||
processing, elements enclosed in this tag pair are promoted to the
|
||||
level of the placeholder tag, and the placeholder tag is removed.
|
||||
|
||||
Note that any elements can be merged (replaced) by the
|
||||
add_ui_from_string method, not just placeholders. This works by
|
||||
matching the "id=" attribute on the element, and replacing the
|
||||
original element with the one from the add method.
|
||||
|
||||
Note that when added elements are removed by the remove_ui method, they
|
||||
are replaced by the containing xml (with the 'id=') only, so don't put
|
||||
anything inside of the containing xml to start with as it will be lost
|
||||
during editing.
|
||||
"""
|
||||
self.app = app # the Gtk.Application of Gramps
|
||||
self.et_xml = ET.fromstring(initial_xml)
|
||||
self.builder = None
|
||||
self.toolbar = None
|
||||
self.action_groups = [] # current list of action groups
|
||||
self.show_groups = [] # groups to show at the moment
|
||||
self.accel_dict = {} # used to store accel overrides from file
|
||||
|
||||
def update_menu(self, init=False):
|
||||
""" This updates the menus and toolbar when there is a change in the
|
||||
ui; any addition or removal or set_visible operation needs to call
|
||||
this. It is best to make the call only once, at the end, if multiple
|
||||
changes are to be made.
|
||||
It starts with the ET xml stored in self, cleans it up to meet the
|
||||
Gtk.Builder specifications, and then updates the ui.
|
||||
|
||||
@param init: When True, this is first call and we set the builder
|
||||
toolbar and menu to the application.
|
||||
When False, we update the menus and toolbar
|
||||
@type init: bool
|
||||
"""
|
||||
|
||||
def iterator(parents):
|
||||
""" This recursively goes through the ET xml and deals with the
|
||||
'groups' attribute and <placeholder> tags, which are not valid for
|
||||
builder. Empty submenus are also removed.
|
||||
Groups processing removes elements that are not shown, as well as
|
||||
the 'groups' attribute itself.
|
||||
<placeholder> tags are removed and their enclosed elements are
|
||||
promoted to the level of the placeholder.
|
||||
|
||||
@param parents: the current element to recursively process
|
||||
@type parents: ET element
|
||||
"""
|
||||
indx = 0
|
||||
while indx < len(parents):
|
||||
child = parents[indx]
|
||||
if len(child) >= 1:
|
||||
# Recurse until we have a stand-alone child
|
||||
iterator(child)
|
||||
if((len(child) == 1 and child.tag == "submenu") or
|
||||
(len(child) == 0 and child.tag == "section")):
|
||||
# remove empty submenus and sections
|
||||
# print('del', child.tag, child.attrib)
|
||||
del parents[indx]
|
||||
continue
|
||||
# print(child.attrib)
|
||||
groups = child.get('groups')
|
||||
if not groups:
|
||||
indx += 1
|
||||
continue
|
||||
del child.attrib['groups']
|
||||
for group in groups.split(' '):
|
||||
if group in self.show_groups:
|
||||
indx += 1
|
||||
break
|
||||
else:
|
||||
#print('del', child.tag, child.attrib, parents.tag,
|
||||
# parents.attrib)
|
||||
del parents[indx]
|
||||
break
|
||||
# The following looks for 'placeholder' elements and if found,
|
||||
# promotes any children to the same level as the placeholder.
|
||||
# this allows the user to insert elements without using a section.
|
||||
indx = 0
|
||||
while indx < len(parents):
|
||||
if parents[indx].tag == "placeholder":
|
||||
subtree = parents[indx]
|
||||
#print('placholder del', parents[indx].tag,
|
||||
# parents[indx].attrib, parents.tag, parents.attrib)
|
||||
del parents[indx]
|
||||
for child in subtree:
|
||||
parents.insert(indx, child)
|
||||
indx += 1
|
||||
else:
|
||||
indx += 1
|
||||
|
||||
if self.builder:
|
||||
toolbar = self.builder.get_object("ToolBar") # previous toolbar
|
||||
|
||||
# need to copy the tree so we can preserve original for later edits.
|
||||
editable = copy.deepcopy(self.et_xml)
|
||||
iterator(editable) # clean up tree to builder specifications
|
||||
xml_str = ET.tostring(editable, encoding="unicode")
|
||||
#print(xml_str)
|
||||
self.builder = Gtk.Builder.new_from_string(xml_str, -1)
|
||||
if init:
|
||||
self.app.menubar = self.builder.get_object("menubar")
|
||||
self.app.set_menubar(self.app.menubar)
|
||||
return
|
||||
# The following is the only way I have found to update the menus.
|
||||
# app.set_menubar can apparently only be used once, before
|
||||
# ApplicationWindow creation, further uses do NOT cause the menus to
|
||||
# update.
|
||||
self.app.menubar.remove_all()
|
||||
section = self.builder.get_object('menubar-update')
|
||||
self.app.menubar.append_section(None, section)
|
||||
|
||||
# the following updates the toolbar from the new builder
|
||||
toolbar_parent = toolbar.get_parent()
|
||||
tb_show = toolbar.get_visible()
|
||||
toolbar_parent.remove(toolbar)
|
||||
toolbar = self.builder.get_object("ToolBar") # new toolbar
|
||||
if config.get('interface.toolbar-text'):
|
||||
toolbar.set_style(Gtk.ToolbarStyle.BOTH)
|
||||
toolbar_parent.pack_start(toolbar, False, True, 0)
|
||||
if tb_show:
|
||||
toolbar.show_all()
|
||||
else:
|
||||
toolbar.hide()
|
||||
#print('*** Update ui')
|
||||
|
||||
def add_ui_from_string(self, changexml):
|
||||
""" This performs a merge operation on the xml elements that have
|
||||
matching 'id's between the current ui xml and change xml strings.
|
||||
The 'changexml' is a list of xml fragment strings used to replace
|
||||
matching elements in the current xml.
|
||||
|
||||
There MUST one and only one matching id in the orig xml.
|
||||
@param changexml: list of xml fragments to merge into main
|
||||
@type changexml: list
|
||||
@return: changexml
|
||||
"""
|
||||
try:
|
||||
for xml in changexml:
|
||||
if not xml:
|
||||
# allow an xml fragment to be an empty string
|
||||
continue
|
||||
update = ET.fromstring(xml)
|
||||
el_id = update.attrib['id']
|
||||
# find the parent of the id'd element in original xml
|
||||
parent = self.et_xml.find(".//*[@id='%s'].." % el_id)
|
||||
if parent:
|
||||
# we found it, now delete original, inset updated
|
||||
for indx in range(len(parent)):
|
||||
if parent[indx].get('id') == el_id:
|
||||
del parent[indx]
|
||||
parent.insert(indx, update)
|
||||
else:
|
||||
# updated item not present in original, just add it
|
||||
# This allow addition of popups etc.
|
||||
self.et_xml.append(update)
|
||||
#results = ET.tostring(self.et_xml, encoding="unicode")
|
||||
#print(results)
|
||||
#print ('*** Add ui')
|
||||
return changexml
|
||||
except:
|
||||
# the following is only here to assist debug
|
||||
print('*****', sys.exc_info())
|
||||
print(xml)
|
||||
print(changexml)
|
||||
assert False
|
||||
|
||||
def remove_ui(self, change_xml):
|
||||
""" This removes the 'change_xml' from the current ui xml. It works on
|
||||
any element with matching 'id', the actual element remains but any
|
||||
children are removed.
|
||||
The 'change_xml' is a list of xml strings originally used to replace
|
||||
matching elements in the current ui xml.
|
||||
@param change_xml: list of xml fragments to remove from main
|
||||
@type change_xml: list
|
||||
"""
|
||||
# if not change_xml:
|
||||
# import pydevd
|
||||
# pydevd.settrace()
|
||||
for xml in change_xml:
|
||||
if not xml:
|
||||
continue
|
||||
update = ET.fromstring(xml)
|
||||
el_id = update.attrib['id']
|
||||
# find parent of id'd element
|
||||
element = self.et_xml.find(".//*[@id='%s']" % el_id)
|
||||
if element: # element may have already been deleted
|
||||
for dummy in range(len(element)):
|
||||
del element[0]
|
||||
#results = ET.tostring(self.et_xml, encoding="unicode")
|
||||
#print(results)
|
||||
#print ('*** Remove ui')
|
||||
return
|
||||
|
||||
def get_widget(self, obj):
|
||||
""" Get the object from the builder.
|
||||
@param obj: the widget to get
|
||||
@type obj: string
|
||||
@return: the object
|
||||
"""
|
||||
return self.builder.get_object(obj)
|
||||
|
||||
def insert_action_group(self, group, gio_group=None):
|
||||
"""
|
||||
This inserts (actually overwrites any matching actions) the action
|
||||
group's actions to the app.
|
||||
By default (with no gio_group), the action group is added to the main
|
||||
Gramps window and the group assumes a 'win' prefix.
|
||||
If not using the main window, the window MUST have the 'application'
|
||||
property set for the accels to work. In this case the actiongroups
|
||||
must be created like the following:
|
||||
|
||||
# create Gramps ActionGroup
|
||||
self.action_group = ActionGroup('name', actions, 'prefix')
|
||||
# create Gio action group
|
||||
act_grp = SimpleActionGroup()
|
||||
# associate window with Gio group and its prefix
|
||||
window.insert_action_group('prefix', act_grp)
|
||||
# make the window 'application' aware
|
||||
window.set_application(uimanager.app)
|
||||
# tell the uimanager about the groups.
|
||||
uimanager.insert_action_group(self.action_group, act_grp)
|
||||
|
||||
@param group: the action group
|
||||
@type group: ActionGroup
|
||||
@param gio_group: the Gio action group associated with a window.
|
||||
@type gio_group: Gio.SimpleActionGroup
|
||||
"""
|
||||
try:
|
||||
assert isinstance(group.actionlist, list)
|
||||
if gio_group:
|
||||
window_group = group.act_group = gio_group
|
||||
elif group.act_group:
|
||||
window_group = group.act_group
|
||||
else:
|
||||
window_group = group.act_group = self.app.window
|
||||
for item in group.actionlist:
|
||||
# deal with accelerator overrides from a file
|
||||
accel = self.accel_dict.get(group.prefix + item[ACTION_NAME])
|
||||
if accel:
|
||||
self.app.set_accels_for_action(
|
||||
group.prefix + item[ACTION_NAME], [accel])
|
||||
elif len(item) > 2 and item[ACTION_ACC]:
|
||||
# deal with accelerators defined in the group
|
||||
accels = self.app.get_actions_for_accel(item[ACTION_ACC])
|
||||
if accels:
|
||||
# diagnostic printout; a duplicate accelerator may be
|
||||
# a problem if both are valid for the same window at
|
||||
# the same time. If the actions are for a different
|
||||
# window, this is not an error. Here we assume a
|
||||
# different prefix is used for different windows.
|
||||
for accel in accels:
|
||||
if group.prefix in accel:
|
||||
LOG.warning('**Duplicate Accelerator %s',
|
||||
item[ACTION_ACC])
|
||||
self.app.set_accels_for_action(
|
||||
group.prefix + item[ACTION_NAME], [item[ACTION_ACC]])
|
||||
if len(item) <= 3:
|
||||
# Normal stateless actions
|
||||
action = Gio.SimpleAction.new(item[ACTION_NAME], None)
|
||||
if item[ACTION_CB]: # in case we have only accelerator
|
||||
action.connect("activate", item[ACTION_CB])
|
||||
elif isinstance(item[ACTION_ST], str):
|
||||
# Radio Actions
|
||||
action = Gio.SimpleAction.new_stateful(
|
||||
item[ACTION_NAME], GLib.VariantType.new("s"),
|
||||
GLib.Variant("s", item[ACTION_ST]))
|
||||
action.connect("change-state", item[ACTION_CB])
|
||||
elif isinstance(item[ACTION_ST], bool):
|
||||
# Checkbox actions
|
||||
action = Gio.SimpleAction.new_stateful(
|
||||
item[ACTION_NAME], None,
|
||||
GLib.Variant.new_boolean(item[ACTION_ST]))
|
||||
action.connect("change-state", item[ACTION_CB])
|
||||
window_group.add_action(action)
|
||||
self.action_groups.append(group)
|
||||
# if action sensitivity was set prior to actually inserting into
|
||||
# UIManager, we need to do it now that we have the action
|
||||
if not group.sensitive:
|
||||
self.set_actions_sensitive(group, False)
|
||||
except:
|
||||
# the following is only to assist in debug
|
||||
print(group.name, item)
|
||||
assert False
|
||||
|
||||
def remove_action_group(self, group):
|
||||
""" This removes the ActionGroup from the UIManager
|
||||
|
||||
@param group: the action group
|
||||
@type group: ActionGroup
|
||||
"""
|
||||
if group.act_group:
|
||||
window_group = group.act_group
|
||||
else:
|
||||
window_group = self.app.window
|
||||
for item in group.actionlist:
|
||||
window_group.remove_action(item[ACTION_NAME])
|
||||
self.app.set_accels_for_action(group.prefix + item[ACTION_NAME],
|
||||
[])
|
||||
self.action_groups.remove(group)
|
||||
|
||||
def get_action_groups(self):
|
||||
""" This returns a list of action Groups installed into the UIManager.
|
||||
@return: list of groups
|
||||
"""
|
||||
return self.action_groups
|
||||
|
||||
def set_actions_sensitive(self, group, value):
|
||||
""" This sets an ActionGroup enabled or disabled. A disabled action
|
||||
will be greyed out in the UI.
|
||||
|
||||
@param group: the action group
|
||||
@type group: ActionGroup
|
||||
@param value: the state of the group
|
||||
@type value: bool
|
||||
"""
|
||||
if group.act_group:
|
||||
for item in group.actionlist:
|
||||
action = group.act_group.lookup_action(item[ACTION_NAME])
|
||||
if action:
|
||||
# We check in case the group has not been inserted into
|
||||
# UIManager yet
|
||||
action.set_enabled(value)
|
||||
group.sensitive = value
|
||||
|
||||
def get_actions_sensitive(self, group):
|
||||
""" This gets an ActionGroup sensitive setting. A disabled action
|
||||
will be greyed out in the UI.
|
||||
We assume that the first action represents the group.
|
||||
|
||||
@param group: the action group
|
||||
@type group: ActionGroup
|
||||
@return: the state of the group
|
||||
"""
|
||||
item = group.actionlist[0]
|
||||
action = group.act_group.lookup_action(item[ACTION_NAME])
|
||||
return action.get_enabled()
|
||||
|
||||
def set_actions_visible(self, group, value):
|
||||
""" This sets an ActionGroup visible and enabled or invisible and
|
||||
disabled. Make sure that the menuitems or sections and toolbar items
|
||||
have the 'groups=' xml attribute matching the group name for this to
|
||||
work correctly.
|
||||
|
||||
@param group: the action group
|
||||
@type group: ActionGroup
|
||||
@param value: the state of the group
|
||||
@type value: bool
|
||||
"""
|
||||
self.set_actions_sensitive(group, value)
|
||||
if value:
|
||||
if group.name not in self.show_groups:
|
||||
self.show_groups.append(group.name)
|
||||
else:
|
||||
if group.name in self.show_groups:
|
||||
self.show_groups.remove(group.name)
|
||||
|
||||
def get_action(self, group, actionname):
|
||||
""" Return a single action from the group.
|
||||
@param group: the action group
|
||||
@type group: ActionGroup
|
||||
@param actionname: the action name
|
||||
@type actionname: string
|
||||
@return: Gio.Action
|
||||
"""
|
||||
return group.act_group.lookup_action(actionname)
|
||||
|
||||
def dump_all_accels(self):
|
||||
''' A function used diagnostically to see what accels are present.
|
||||
This will only dump the current accel set, if other non-open windows
|
||||
or views have accels, you will need to open them and run this again
|
||||
and manually merge the result files. The results are in a
|
||||
'gramps.accel' file located in the current working directory.'''
|
||||
out_dict = {}
|
||||
for group in self.action_groups:
|
||||
for item in group.actionlist:
|
||||
act = group.prefix + item[ACTION_NAME]
|
||||
accels = self.app.get_accels_for_action(
|
||||
group.prefix + item[ACTION_NAME])
|
||||
out_dict[act] = accels[0] if accels else ''
|
||||
import json
|
||||
with open('gramps.accel', 'w', ) as hndl:
|
||||
accels = json.dumps(out_dict, indent=0).replace('\n"', '\n# "')
|
||||
hndl.write(accels)
|
||||
|
||||
def load_accels(self, filename):
|
||||
""" This function loads accels from a file such as created by
|
||||
dump_all_accels. The file contents is basically a Python dict
|
||||
definition. As such it contains a line for each dict element.
|
||||
These elements can be commented out with '#' at the beginning of the
|
||||
line.
|
||||
|
||||
If used, this file overrides the accels defined in other Gramps code.
|
||||
As such it must be loaded before any insert_action_group calls.
|
||||
"""
|
||||
import ast
|
||||
with open(filename, 'r') as hndl:
|
||||
accels = hndl.read()
|
||||
self.accel_dict = ast.literal_eval(accels)
|
||||
+281
-550
File diff suppressed because it is too large
Load Diff
@@ -28,6 +28,7 @@
|
||||
#-------------------------------------------------------------------------
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from io import StringIO
|
||||
import html
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@@ -51,9 +52,11 @@ from gi.repository import Gtk
|
||||
from ..display import display_help
|
||||
from ..listmodel import ListModel
|
||||
from ..managedwindow import ManagedWindow
|
||||
from ..uimanager import ActionGroup
|
||||
from gramps.gen.utils.db import navigation_label
|
||||
from gramps.gen.const import URL_MANUAL_PAGE
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
from gramps.gen.errors import HandleError
|
||||
_ = glocale.translation.sgettext
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@@ -71,9 +74,6 @@ WIKI_HELP_SEC = _('manual|Bookmarks')
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
TOP = '''<ui><menubar name="MenuBar"><menu action="BookMenu">'''
|
||||
BTM = '''</menu></menubar></ui>'''
|
||||
|
||||
DISABLED = -1
|
||||
|
||||
class Bookmarks(metaclass=ABCMeta):
|
||||
@@ -93,7 +93,7 @@ class Bookmarks(metaclass=ABCMeta):
|
||||
if self.dbstate.is_open():
|
||||
self.update_bookmarks()
|
||||
self.active = DISABLED
|
||||
self.action_group = Gtk.ActionGroup(name='Bookmarks')
|
||||
self.action_group = ActionGroup(name='Bookmarks')
|
||||
if self.dbstate.is_open():
|
||||
self.connect_signals()
|
||||
self.dbstate.connect('database-changed', self.db_changed)
|
||||
@@ -129,7 +129,8 @@ class Bookmarks(metaclass=ABCMeta):
|
||||
"""
|
||||
Redraw the display.
|
||||
"""
|
||||
self.redraw()
|
||||
# used by navigationview; other updates follow
|
||||
self.redraw(update_menu=False)
|
||||
|
||||
def undisplay(self):
|
||||
"""
|
||||
@@ -138,8 +139,7 @@ class Bookmarks(metaclass=ABCMeta):
|
||||
if self.active != DISABLED:
|
||||
self.uistate.uimanager.remove_ui(self.active)
|
||||
self.uistate.uimanager.remove_action_group(self.action_group)
|
||||
self.action_group = Gtk.ActionGroup(name='Bookmarks')
|
||||
self.uistate.uimanager.ensure_update()
|
||||
self.action_group = ActionGroup(name='Bookmarks')
|
||||
self.active = DISABLED
|
||||
|
||||
def redraw_and_report_change(self):
|
||||
@@ -147,36 +147,49 @@ class Bookmarks(metaclass=ABCMeta):
|
||||
self.dbstate.db.report_bm_change()
|
||||
self.redraw()
|
||||
|
||||
def redraw(self):
|
||||
def redraw(self, update_menu=True):
|
||||
"""Create the pulldown menu."""
|
||||
menuitem = ('<item>\n'
|
||||
'<attribute name="action">win.%s</attribute>\n'
|
||||
'<attribute name="label" translatable="yes">'
|
||||
'%s</attribute>\n'
|
||||
'</item>\n')
|
||||
text = StringIO()
|
||||
text.write(TOP)
|
||||
|
||||
self.undisplay()
|
||||
|
||||
actions = []
|
||||
count = 0
|
||||
bad_bookmarks = [] # list of bad bookmarks
|
||||
|
||||
if self.dbstate.is_open() and len(self.bookmarks.get()) > 0:
|
||||
text.write('<placeholder name="GoToBook">')
|
||||
text.write('<section id="GoToBook">\n')
|
||||
for item in self.bookmarks.get():
|
||||
try:
|
||||
label, dummy_obj = self.make_label(item)
|
||||
func = self.callback(item)
|
||||
action_id = "BM:%s" % item
|
||||
actions.append((action_id, None, label, None, None, func))
|
||||
text.write('<menuitem action="%s"/>' % action_id)
|
||||
action_id = "BM.%s" % item
|
||||
actions.append((action_id, func))
|
||||
text.write(menuitem % (action_id, html.escape(label)))
|
||||
count += 1
|
||||
except AttributeError:
|
||||
pass
|
||||
text.write('</placeholder>')
|
||||
except HandleError:
|
||||
# if bookmark contains handle to something missing now
|
||||
bad_bookmarks.append(item)
|
||||
text.write('</section>\n')
|
||||
|
||||
text.write(BTM)
|
||||
self.action_group.add_actions(actions)
|
||||
self.uistate.uimanager.insert_action_group(self.action_group, 1)
|
||||
self.active = self.uistate.uimanager.add_ui_from_string(text.getvalue())
|
||||
self.uistate.uimanager.ensure_update()
|
||||
self.uistate.uimanager.insert_action_group(self.action_group)
|
||||
self.active = self.uistate.uimanager.add_ui_from_string(
|
||||
[text.getvalue()])
|
||||
if update_menu:
|
||||
self.uistate.uimanager.update_menu()
|
||||
text.close()
|
||||
# Clean up any bad bookmarks (can happen if Gramps crashes;
|
||||
# modified bookmarks set is saved only on normal Gramps close)
|
||||
for handle in bad_bookmarks:
|
||||
self.bookmarks.remove(handle)
|
||||
|
||||
@abstractmethod
|
||||
def make_label(self, handle):
|
||||
@@ -538,4 +551,4 @@ def make_callback(handle, function):
|
||||
"""
|
||||
Build a unique call to the function with the associated handle.
|
||||
"""
|
||||
return lambda x: function(handle)
|
||||
return lambda x, y: function(handle)
|
||||
|
||||
@@ -55,7 +55,7 @@ from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.sgettext
|
||||
from .pageview import PageView
|
||||
from .navigationview import NavigationView
|
||||
from ..actiongroup import ActionGroup
|
||||
from ..uimanager import ActionGroup
|
||||
from ..columnorder import ColumnOrder
|
||||
from gramps.gen.config import config
|
||||
from gramps.gen.errors import WindowActiveError, FilterError, HandleError
|
||||
@@ -64,6 +64,7 @@ from ..widgets.menuitem import add_menuitem
|
||||
from gramps.gen.const import CUSTOM_FILTERS
|
||||
from gramps.gen.utils.debug import profile
|
||||
from gramps.gen.utils.string import data_recover_msg
|
||||
from gramps.gen.plug import CATEGORY_QR_PERSON
|
||||
from ..dialog import QuestionDialog, QuestionDialog2, ErrorDialog
|
||||
from ..editors import FilterEditor
|
||||
from ..ddtargets import DdTargets
|
||||
@@ -122,6 +123,8 @@ class ListView(NavigationView):
|
||||
self.generic_filter = None
|
||||
dbstate.connect('database-changed', self.change_db)
|
||||
self.connect_signals()
|
||||
self.at_popup_action = None
|
||||
self.at_popup_menu = None
|
||||
|
||||
def no_database(self):
|
||||
## TODO GTK3: This is never called!! Dbguielement disconnects
|
||||
@@ -206,26 +209,27 @@ class ListView(NavigationView):
|
||||
|
||||
NavigationView.define_actions(self)
|
||||
|
||||
self.edit_action = ActionGroup(name=self.title + '/ChangeOrder')
|
||||
self.edit_action = ActionGroup(name=self.title + '/Edits')
|
||||
self.edit_action.add_actions([
|
||||
('Add', 'list-add', _("_Add..."), "<PRIMARY>Insert",
|
||||
self.ADD_MSG, self.add),
|
||||
('Remove', 'list-remove', _("_Delete"), "<PRIMARY>Delete",
|
||||
self.DEL_MSG, self.remove),
|
||||
('Merge', 'gramps-merge', _('_Merge...'), None,
|
||||
self.MERGE_MSG, self.merge),
|
||||
('ExportTab', None, _('Export View...'), None, None,
|
||||
self.export),
|
||||
])
|
||||
('Add', self.add, '<Primary>Insert'),
|
||||
('Remove', self.remove, '<Primary>Delete'),
|
||||
('PRIMARY-BackSpace', self.remove, '<PRIMARY>BackSpace'),
|
||||
('Merge', self.merge), ])
|
||||
|
||||
self._add_action_group(self.edit_action)
|
||||
self.action_list.extend([
|
||||
('ExportTab', self.export),
|
||||
('Edit', self.edit, '<Primary>Return'),
|
||||
('PRIMARY-J', self.jump, '<PRIMARY>J'),
|
||||
('FilterEdit', self.filter_editor)])
|
||||
|
||||
self._add_action('Edit', 'gtk-edit', _("action|_Edit..."),
|
||||
accel="<PRIMARY>Return",
|
||||
tip=self.EDIT_MSG,
|
||||
callback=self.edit)
|
||||
|
||||
def build_columns(self):
|
||||
def build_columns(self, preserve_col=True):
|
||||
"""
|
||||
build the columns
|
||||
"""
|
||||
# Preserve the column widths if rebuilding the view.
|
||||
if self.columns and preserve_col:
|
||||
self.save_column_info()
|
||||
list(map(self.list.remove_column, self.columns))
|
||||
|
||||
self.columns = []
|
||||
@@ -291,7 +295,7 @@ class ListView(NavigationView):
|
||||
Called when the page is displayed.
|
||||
"""
|
||||
NavigationView.set_active(self)
|
||||
self.uistate.viewmanager.tags.tag_enable()
|
||||
self.uistate.viewmanager.tags.tag_enable(update_menu=False)
|
||||
self.uistate.show_filter_results(self.dbstate,
|
||||
self.model.displayed(),
|
||||
self.model.total())
|
||||
@@ -303,10 +307,7 @@ class ListView(NavigationView):
|
||||
NavigationView.set_inactive(self)
|
||||
self.uistate.viewmanager.tags.tag_disable()
|
||||
|
||||
def __build_tree(self):
|
||||
profile(self._build_tree)
|
||||
|
||||
def build_tree(self, force_sidebar=False):
|
||||
def build_tree(self, force_sidebar=False, preserve_col=True):
|
||||
if self.active:
|
||||
cput0 = time.clock()
|
||||
if not self.search_bar.is_visible():
|
||||
@@ -335,7 +336,7 @@ class ListView(NavigationView):
|
||||
parent=self.uistate.window)
|
||||
|
||||
cput1 = time.clock()
|
||||
self.build_columns()
|
||||
self.build_columns(preserve_col)
|
||||
cput2 = time.clock()
|
||||
self.list.set_model(self.model)
|
||||
cput3 = time.clock()
|
||||
@@ -372,7 +373,7 @@ class ListView(NavigationView):
|
||||
"""
|
||||
return 'gramps-tree-list'
|
||||
|
||||
def filter_editor(self, obj):
|
||||
def filter_editor(self, *obj):
|
||||
try:
|
||||
FilterEditor(self.FILTER_TYPE , CUSTOM_FILTERS,
|
||||
self.dbstate, self.uistate)
|
||||
@@ -441,7 +442,7 @@ class ListView(NavigationView):
|
||||
self.uistate.push_message(self.dbstate,
|
||||
_("Active object not visible"))
|
||||
|
||||
def add_bookmark(self, obj):
|
||||
def add_bookmark(self, *obj):
|
||||
mlist = []
|
||||
self.selection.selected_foreach(self.blist, mlist)
|
||||
|
||||
@@ -512,7 +513,7 @@ class ListView(NavigationView):
|
||||
self.sort_col = 0
|
||||
self.sort_order = Gtk.SortType.ASCENDING
|
||||
self.setup_filter()
|
||||
self.build_tree()
|
||||
self.build_tree(preserve_col=False)
|
||||
|
||||
def column_order(self):
|
||||
"""
|
||||
@@ -851,6 +852,7 @@ class ListView(NavigationView):
|
||||
"""
|
||||
if not self.dbstate.is_open():
|
||||
return False
|
||||
menu = self.uimanager.get_widget('Popup')
|
||||
if event.type == Gdk.EventType._2BUTTON_PRESS and event.button == 1:
|
||||
if self.model.get_flags() & Gtk.TreeModelFlags.LIST_ONLY:
|
||||
self.edit(obj)
|
||||
@@ -866,49 +868,54 @@ class ListView(NavigationView):
|
||||
else:
|
||||
self.edit(obj)
|
||||
return True
|
||||
elif is_right_click(event):
|
||||
menu = self.uistate.uimanager.get_widget('/Popup')
|
||||
if menu:
|
||||
# Quick Reports
|
||||
qr_menu = self.uistate.uimanager.\
|
||||
get_widget('/Popup/QuickReport')
|
||||
if qr_menu and self.QR_CATEGORY > -1 :
|
||||
(ui, qr_actions) = create_quickreport_menu(
|
||||
self.QR_CATEGORY,
|
||||
self.dbstate,
|
||||
self.uistate,
|
||||
self.first_selected())
|
||||
self.__build_menu(qr_menu, qr_actions)
|
||||
elif is_right_click(event) and menu:
|
||||
prefix = 'win'
|
||||
self.at_popup_menu = []
|
||||
actions = []
|
||||
# Quick Reports
|
||||
if self.QR_CATEGORY > -1:
|
||||
(qr_ui, qr_actions) = create_quickreport_menu(
|
||||
self.QR_CATEGORY, self.dbstate, self.uistate,
|
||||
self.first_selected(), prefix)
|
||||
if self.get_active() and qr_actions:
|
||||
actions.extend(qr_actions)
|
||||
qr_ui = ("<placeholder id='QuickReport'>%s</placeholder>" %
|
||||
qr_ui)
|
||||
self.at_popup_menu.append(qr_ui)
|
||||
|
||||
# Web Connects
|
||||
web_menu = self.uistate.uimanager.\
|
||||
get_widget('/Popup/WebConnect')
|
||||
if web_menu:
|
||||
web_actions = create_web_connect_menu(
|
||||
self.dbstate,
|
||||
self.uistate,
|
||||
self.navigation_type(),
|
||||
self.first_selected())
|
||||
self.__build_menu(web_menu, web_actions)
|
||||
# Web Connects
|
||||
if self.QR_CATEGORY == CATEGORY_QR_PERSON:
|
||||
(web_ui, web_actions) = create_web_connect_menu(
|
||||
self.dbstate, self.uistate, self.navigation_type(),
|
||||
self.first_selected(), prefix)
|
||||
if self.get_active() and web_actions:
|
||||
actions.extend(web_actions)
|
||||
self.at_popup_menu.append(web_ui)
|
||||
|
||||
menu.popup(None, None, None, None, event.button, event.time)
|
||||
return True
|
||||
if self.at_popup_action:
|
||||
self.uimanager.remove_ui(self.at_popup_menu)
|
||||
self.uimanager.remove_action_group(self.at_popup_action)
|
||||
self.at_popup_action = ActionGroup('AtPopupActions',
|
||||
actions)
|
||||
self.uimanager.insert_action_group(self.at_popup_action)
|
||||
self.at_popup_menu = self.uimanager.add_ui_from_string(
|
||||
self.at_popup_menu)
|
||||
self.uimanager.update_menu()
|
||||
|
||||
menu = self.uimanager.get_widget('Popup')
|
||||
popup_menu = Gtk.Menu.new_from_model(menu)
|
||||
popup_menu.attach_to_widget(obj, None)
|
||||
popup_menu.show_all()
|
||||
if Gtk.MINOR_VERSION < 22:
|
||||
# ToDo The following is reported to work poorly with Wayland
|
||||
popup_menu.popup(None, None, None, None,
|
||||
event.button, event.time)
|
||||
else:
|
||||
popup_menu.popup_at_pointer(event)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def __build_menu(self, menu, actions):
|
||||
"""
|
||||
Build a submenu for quick reports and web connects
|
||||
"""
|
||||
if self.get_active() and len(actions) > 1:
|
||||
sub_menu = Gtk.Menu()
|
||||
for action in actions[1:]:
|
||||
add_menuitem(sub_menu, action[2], None, action[5])
|
||||
menu.set_submenu(sub_menu)
|
||||
menu.show()
|
||||
else:
|
||||
menu.hide()
|
||||
|
||||
def _key_press(self, obj, event):
|
||||
"""
|
||||
Called when a key is pressed on a listview
|
||||
@@ -1002,9 +1009,6 @@ class ListView(NavigationView):
|
||||
return True
|
||||
return False
|
||||
|
||||
def key_delete(self):
|
||||
self.remove(None)
|
||||
|
||||
def change_page(self):
|
||||
"""
|
||||
Called when a page is changed.
|
||||
@@ -1014,31 +1018,39 @@ class ListView(NavigationView):
|
||||
self.uistate.show_filter_results(self.dbstate,
|
||||
self.model.displayed(),
|
||||
self.model.total())
|
||||
self.edit_action.set_visible(True)
|
||||
self.edit_action.set_sensitive(not self.dbstate.db.readonly)
|
||||
self.uimanager.set_actions_visible(self.edit_action, True)
|
||||
self.uimanager.set_actions_sensitive(self.edit_action,
|
||||
not self.dbstate.db.readonly)
|
||||
|
||||
def on_delete(self):
|
||||
"""
|
||||
Save the column widths when the view is shutdown.
|
||||
"""
|
||||
self.save_column_info()
|
||||
PageView.on_delete(self)
|
||||
|
||||
def save_column_info(self):
|
||||
"""
|
||||
Save the column widths, order, and view settings
|
||||
"""
|
||||
widths = self.get_column_widths()
|
||||
order = self._config.get('columns.rank')
|
||||
size = self._config.get('columns.size')
|
||||
vis = self._config.get('columns.visible')
|
||||
vis = self._config.get('columns.visible')
|
||||
newsize = []
|
||||
index = 0
|
||||
for val, size in zip(order, size):
|
||||
if val in vis:
|
||||
size = widths[index]
|
||||
if val in vis[:-1]: # don't use last column size, it's wrong
|
||||
if widths[index]:
|
||||
size = widths[index]
|
||||
index += 1
|
||||
newsize.append(size)
|
||||
self._config.set('columns.size', newsize)
|
||||
PageView.on_delete(self)
|
||||
|
||||
####################################################################
|
||||
# Export data
|
||||
####################################################################
|
||||
def export(self, obj):
|
||||
def export(self, *obj):
|
||||
chooser = Gtk.FileChooserDialog(
|
||||
_("Export View as Spreadsheet"),
|
||||
self.uistate.window,
|
||||
@@ -1140,25 +1152,25 @@ class ListView(NavigationView):
|
||||
# Template functions
|
||||
####################################################################
|
||||
@abstractmethod
|
||||
def edit(self, obj, data=None):
|
||||
def edit(self, *obj):
|
||||
"""
|
||||
Template function to allow the editing of the selected object
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def remove(self, handle, data=None):
|
||||
def remove(self, *obj):
|
||||
"""
|
||||
Template function to allow the removal of an object by its handle
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def add(self, obj, data=None):
|
||||
def add(self, *obj):
|
||||
"""
|
||||
Template function to allow the adding of a new object
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def merge(self, obj, data=None):
|
||||
def merge(self, *obj):
|
||||
"""
|
||||
Template function to allow the merger of two objects.
|
||||
"""
|
||||
@@ -1169,7 +1181,7 @@ class ListView(NavigationView):
|
||||
Template function to allow the removal of an object by its handle
|
||||
"""
|
||||
|
||||
def open_all_nodes(self, obj):
|
||||
def open_all_nodes(self, *obj):
|
||||
"""
|
||||
Method for Treeviews to open all groups
|
||||
obj: for use of method in event callback
|
||||
@@ -1182,14 +1194,14 @@ class ListView(NavigationView):
|
||||
self.uistate.set_busy_cursor(False)
|
||||
self.uistate.modify_statusbar(self.dbstate)
|
||||
|
||||
def close_all_nodes(self, obj):
|
||||
def close_all_nodes(self, *obj):
|
||||
"""
|
||||
Method for Treeviews to close all groups
|
||||
obj: for use of method in event callback
|
||||
"""
|
||||
self.list.collapse_all()
|
||||
|
||||
def open_branch(self, obj):
|
||||
def open_branch(self, *obj):
|
||||
"""
|
||||
Expand the selected branches and all children.
|
||||
obj: for use of method in event callback
|
||||
@@ -1204,7 +1216,7 @@ class ListView(NavigationView):
|
||||
self.uistate.set_busy_cursor(False)
|
||||
self.uistate.modify_statusbar(self.dbstate)
|
||||
|
||||
def close_branch(self, obj):
|
||||
def close_branch(self, *obj):
|
||||
"""
|
||||
Collapse the selected branches.
|
||||
:param obj: not used, present only to allow the use of the method in
|
||||
|
||||
@@ -29,6 +29,7 @@ Provide the base classes for GRAMPS' DataView classes
|
||||
#
|
||||
#----------------------------------------------------------------
|
||||
from abc import abstractmethod
|
||||
import html
|
||||
import logging
|
||||
|
||||
_LOG = logging.getLogger('.navigationview')
|
||||
@@ -49,7 +50,7 @@ from gi.repository import Gtk
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.sgettext
|
||||
from .pageview import PageView
|
||||
from ..actiongroup import ActionGroup
|
||||
from ..uimanager import ActionGroup
|
||||
from gramps.gen.utils.db import navigation_label
|
||||
from gramps.gen.constfunc import mod_key
|
||||
from ..utils import match_primary_mask
|
||||
@@ -57,19 +58,9 @@ from ..utils import match_primary_mask
|
||||
DISABLED = -1
|
||||
MRU_SIZE = 10
|
||||
|
||||
MRU_TOP = [
|
||||
'<ui>'
|
||||
'<menubar name="MenuBar">'
|
||||
'<menu action="GoMenu">'
|
||||
'<placeholder name="CommonHistory">'
|
||||
]
|
||||
MRU_TOP = '<section id="CommonHistory">'
|
||||
MRU_BTM = '</section>'
|
||||
|
||||
MRU_BTM = [
|
||||
'</placeholder>'
|
||||
'</menu>'
|
||||
'</menubar>'
|
||||
'</ui>'
|
||||
]
|
||||
#------------------------------------------------------------------------------
|
||||
#
|
||||
# NavigationView
|
||||
@@ -94,6 +85,7 @@ class NavigationView(PageView):
|
||||
self.mru_signal = None
|
||||
self.nav_group = nav_group
|
||||
self.mru_active = DISABLED
|
||||
self.uimanager = uistate.uimanager
|
||||
|
||||
self.uistate.register(state, self.navigation_type(), self.nav_group)
|
||||
|
||||
@@ -122,8 +114,8 @@ class NavigationView(PageView):
|
||||
"""
|
||||
PageView.disable_action_group(self)
|
||||
|
||||
self.fwd_action.set_visible(False)
|
||||
self.back_action.set_visible(False)
|
||||
self.uimanager.set_actions_visible(self.fwd_action, False)
|
||||
self.uimanager.set_actions_visible(self.back_action, False)
|
||||
|
||||
def enable_action_group(self, obj):
|
||||
"""
|
||||
@@ -133,20 +125,25 @@ class NavigationView(PageView):
|
||||
"""
|
||||
PageView.enable_action_group(self, obj)
|
||||
|
||||
self.fwd_action.set_visible(True)
|
||||
self.back_action.set_visible(True)
|
||||
self.uimanager.set_actions_visible(self.fwd_action, True)
|
||||
self.uimanager.set_actions_visible(self.back_action, True)
|
||||
hobj = self.get_history()
|
||||
self.fwd_action.set_sensitive(not hobj.at_end())
|
||||
self.back_action.set_sensitive(not hobj.at_front())
|
||||
self.uimanager.set_actions_sensitive(self.fwd_action,
|
||||
not hobj.at_end())
|
||||
self.uimanager.set_actions_sensitive(self.back_action,
|
||||
not hobj.at_front())
|
||||
|
||||
def change_page(self):
|
||||
"""
|
||||
Called when the page changes.
|
||||
"""
|
||||
hobj = self.get_history()
|
||||
self.fwd_action.set_sensitive(not hobj.at_end())
|
||||
self.back_action.set_sensitive(not hobj.at_front())
|
||||
self.other_action.set_sensitive(not self.dbstate.db.readonly)
|
||||
self.uimanager.set_actions_sensitive(self.fwd_action,
|
||||
not hobj.at_end())
|
||||
self.uimanager.set_actions_sensitive(self.back_action,
|
||||
not hobj.at_front())
|
||||
self.uimanager.set_actions_sensitive(self.other_action,
|
||||
not self.dbstate.db.readonly)
|
||||
self.uistate.modify_statusbar(self.dbstate)
|
||||
|
||||
def set_active(self):
|
||||
@@ -159,7 +156,7 @@ class NavigationView(PageView):
|
||||
hobj = self.get_history()
|
||||
self.active_signal = hobj.connect('active-changed', self.goto_active)
|
||||
self.mru_signal = hobj.connect('mru-changed', self.update_mru_menu)
|
||||
self.update_mru_menu(hobj.mru)
|
||||
self.update_mru_menu(hobj.mru, update_menu=False)
|
||||
|
||||
self.goto_active(None)
|
||||
|
||||
@@ -199,8 +196,10 @@ class NavigationView(PageView):
|
||||
self.goto_handle(active_handle)
|
||||
|
||||
hobj = self.get_history()
|
||||
self.fwd_action.set_sensitive(not hobj.at_end())
|
||||
self.back_action.set_sensitive(not hobj.at_front())
|
||||
self.uimanager.set_actions_sensitive(self.fwd_action,
|
||||
not hobj.at_end())
|
||||
self.uimanager.set_actions_sensitive(self.back_action,
|
||||
not hobj.at_front())
|
||||
|
||||
def get_active(self):
|
||||
"""
|
||||
@@ -238,7 +237,7 @@ class NavigationView(PageView):
|
||||
####################################################################
|
||||
# BOOKMARKS
|
||||
####################################################################
|
||||
def add_bookmark(self, obj):
|
||||
def add_bookmark(self, *obj):
|
||||
"""
|
||||
Add a bookmark to the list.
|
||||
"""
|
||||
@@ -259,7 +258,7 @@ class NavigationView(PageView):
|
||||
"no one was selected."),
|
||||
parent=self.uistate.window)
|
||||
|
||||
def edit_bookmarks(self, obj):
|
||||
def edit_bookmarks(self, *obj):
|
||||
"""
|
||||
Call the bookmark editor.
|
||||
"""
|
||||
@@ -271,12 +270,8 @@ class NavigationView(PageView):
|
||||
"""
|
||||
self.book_action = ActionGroup(name=self.title + '/Bookmark')
|
||||
self.book_action.add_actions([
|
||||
('AddBook', 'gramps-bookmark-new', _('_Add Bookmark'),
|
||||
'<PRIMARY>d', None, self.add_bookmark),
|
||||
('EditBook', 'gramps-bookmark-edit',
|
||||
_("%(title)s...") % {'title': _("Organize Bookmarks")},
|
||||
'<shift><PRIMARY>D', None,
|
||||
self.edit_bookmarks),
|
||||
('AddBook', self.add_bookmark, '<PRIMARY>d'),
|
||||
('EditBook', self.edit_bookmarks, '<shift><PRIMARY>D'),
|
||||
])
|
||||
|
||||
self._add_action_group(self.book_action)
|
||||
@@ -290,35 +285,25 @@ class NavigationView(PageView):
|
||||
"""
|
||||
# add the Forward action group to handle the Forward button
|
||||
self.fwd_action = ActionGroup(name=self.title + '/Forward')
|
||||
self.fwd_action.add_actions([
|
||||
('Forward', 'go-next', _("_Forward"),
|
||||
"%sRight" % mod_key(), _("Go to the next object in the history"),
|
||||
self.fwd_clicked)
|
||||
])
|
||||
self.fwd_action.add_actions([('Forward', self.fwd_clicked,
|
||||
"%sRight" % mod_key())])
|
||||
|
||||
# add the Backward action group to handle the Forward button
|
||||
self.back_action = ActionGroup(name=self.title + '/Backward')
|
||||
self.back_action.add_actions([
|
||||
('Back', 'go-previous', _("_Back"),
|
||||
"%sLeft" % mod_key(), _("Go to the previous object in the history"),
|
||||
self.back_clicked)
|
||||
])
|
||||
self.back_action.add_actions([('Back', self.back_clicked,
|
||||
"%sLeft" % mod_key())])
|
||||
|
||||
self._add_action('HomePerson', 'go-home', _("_Home"),
|
||||
accel="%sHome" % mod_key(),
|
||||
tip=_("Go to the default person"), callback=self.home)
|
||||
self._add_action('HomePerson', self.home, "%sHome" % mod_key())
|
||||
|
||||
self.other_action = ActionGroup(name=self.title + '/PersonOther')
|
||||
self.other_action.add_actions([
|
||||
('SetActive', 'go-home', _("Set _Home Person"), None,
|
||||
None, self.set_default_person),
|
||||
])
|
||||
('SetActive', self.set_default_person)])
|
||||
|
||||
self._add_action_group(self.back_action)
|
||||
self._add_action_group(self.fwd_action)
|
||||
self._add_action_group(self.other_action)
|
||||
|
||||
def set_default_person(self, obj):
|
||||
def set_default_person(self, *obj):
|
||||
"""
|
||||
Set the default person.
|
||||
"""
|
||||
@@ -326,7 +311,7 @@ class NavigationView(PageView):
|
||||
if active:
|
||||
self.dbstate.db.set_default_person_handle(active)
|
||||
|
||||
def home(self, obj):
|
||||
def home(self, *obj):
|
||||
"""
|
||||
Move to the default person.
|
||||
"""
|
||||
@@ -342,7 +327,7 @@ class NavigationView(PageView):
|
||||
"via the menu Edit ->Set Home Person."),
|
||||
parent=self.uistate.window)
|
||||
|
||||
def jump(self):
|
||||
def jump(self, *obj):
|
||||
"""
|
||||
A dialog to move to a Gramps ID entered by the user.
|
||||
"""
|
||||
@@ -383,7 +368,7 @@ class NavigationView(PageView):
|
||||
"""
|
||||
pass
|
||||
|
||||
def fwd_clicked(self, obj):
|
||||
def fwd_clicked(self, *obj):
|
||||
"""
|
||||
Move forward one object in the history.
|
||||
"""
|
||||
@@ -392,11 +377,12 @@ class NavigationView(PageView):
|
||||
if not hobj.at_end():
|
||||
hobj.forward()
|
||||
self.uistate.modify_statusbar(self.dbstate)
|
||||
self.fwd_action.set_sensitive(not hobj.at_end())
|
||||
self.back_action.set_sensitive(True)
|
||||
self.uimanager.set_actions_sensitive(self.fwd_action,
|
||||
not hobj.at_end())
|
||||
self.uimanager.set_actions_sensitive(self.back_action, True)
|
||||
hobj.lock = False
|
||||
|
||||
def back_clicked(self, obj):
|
||||
def back_clicked(self, *obj):
|
||||
"""
|
||||
Move backward one object in the history.
|
||||
"""
|
||||
@@ -405,8 +391,9 @@ class NavigationView(PageView):
|
||||
if not hobj.at_front():
|
||||
hobj.back()
|
||||
self.uistate.modify_statusbar(self.dbstate)
|
||||
self.back_action.set_sensitive(not hobj.at_front())
|
||||
self.fwd_action.set_sensitive(True)
|
||||
self.uimanager.set_actions_sensitive(self.back_action,
|
||||
not hobj.at_front())
|
||||
self.uimanager.set_actions_sensitive(self.fwd_action, True)
|
||||
hobj.lock = False
|
||||
|
||||
####################################################################
|
||||
@@ -418,44 +405,52 @@ class NavigationView(PageView):
|
||||
Remove the UI and action groups for the MRU list.
|
||||
"""
|
||||
if self.mru_active != DISABLED:
|
||||
self.uistate.uimanager.remove_ui(self.mru_active)
|
||||
self.uistate.uimanager.remove_action_group(self.mru_action)
|
||||
self.uimanager.remove_ui(self.mru_active)
|
||||
self.uimanager.remove_action_group(self.mru_action)
|
||||
self.mru_active = DISABLED
|
||||
|
||||
def mru_enable(self):
|
||||
def mru_enable(self, update_menu=False):
|
||||
"""
|
||||
Enables the UI and action groups for the MRU list.
|
||||
"""
|
||||
if self.mru_active == DISABLED:
|
||||
self.uistate.uimanager.insert_action_group(self.mru_action, 1)
|
||||
self.mru_active = self.uistate.uimanager.add_ui_from_string(self.mru_ui)
|
||||
self.uistate.uimanager.ensure_update()
|
||||
self.uimanager.insert_action_group(self.mru_action)
|
||||
self.mru_active = self.uimanager.add_ui_from_string(self.mru_ui)
|
||||
if update_menu:
|
||||
self.uimanager.update_menu()
|
||||
|
||||
def update_mru_menu(self, items):
|
||||
def update_mru_menu(self, items, update_menu=True):
|
||||
"""
|
||||
Builds the UI and action group for the MRU list.
|
||||
"""
|
||||
menuitem = ''' <item>
|
||||
<attribute name="action">win.%s%02d</attribute>
|
||||
<attribute name="label" translatable="yes">%s</attribute>
|
||||
</item>
|
||||
'''
|
||||
menus = ''
|
||||
self.mru_disable()
|
||||
nav_type = self.navigation_type()
|
||||
hobj = self.get_history()
|
||||
menu_len = min(len(items) - 1, MRU_SIZE)
|
||||
|
||||
entry = '<menuitem action="%s%02d"/>'
|
||||
data = [entry % (nav_type, index) for index in range(0, menu_len)]
|
||||
self.mru_ui = "".join(MRU_TOP) + "".join(data) + "".join(MRU_BTM)
|
||||
for index in range(0, menu_len):
|
||||
name, obj = navigation_label(self.dbstate.db, nav_type,
|
||||
items[index])
|
||||
menus += menuitem % (nav_type, index, html.escape(name))
|
||||
self.mru_ui = [MRU_TOP + menus + MRU_BTM]
|
||||
|
||||
mitems = items[-MRU_SIZE - 1:-1] # Ignore current handle
|
||||
mitems.reverse()
|
||||
data = []
|
||||
for index, handle in enumerate(mitems):
|
||||
name, obj = navigation_label(self.dbstate.db, nav_type, handle)
|
||||
data.append(('%s%02d'%(nav_type, index), None, name,
|
||||
"%s%d" % (mod_key(), index), None,
|
||||
make_callback(hobj.push, handle)))
|
||||
data.append(('%s%02d'%(nav_type, index),
|
||||
make_callback(hobj.push, handle),
|
||||
"%s%d" % (mod_key(), index)))
|
||||
|
||||
self.mru_action = ActionGroup(name=self.title + '/MRU')
|
||||
self.mru_action.add_actions(data)
|
||||
self.mru_enable()
|
||||
self.mru_enable(update_menu)
|
||||
|
||||
####################################################################
|
||||
# Template functions
|
||||
@@ -503,4 +498,4 @@ def make_callback(func, handle):
|
||||
"""
|
||||
Generates a callback function based off the passed arguments
|
||||
"""
|
||||
return lambda x: func(handle)
|
||||
return lambda x, y: func(handle)
|
||||
|
||||
@@ -51,7 +51,7 @@ from ..dbguielement import DbGUIElement
|
||||
from ..widgets.grampletbar import GrampletBar
|
||||
from ..configure import ConfigureDialog
|
||||
from gramps.gen.config import config
|
||||
from ..actiongroup import ActionGroup
|
||||
from ..uimanager import ActionGroup
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
#
|
||||
@@ -97,25 +97,24 @@ class PageView(DbGUIElement, metaclass=ABCMeta):
|
||||
self.uistate = uistate
|
||||
self.action_list = []
|
||||
self.action_toggle_list = []
|
||||
self.action_toolmenu_list = []
|
||||
self.action_toolmenu = {} #easy access to toolmenuaction and proxies
|
||||
self.action_group = None
|
||||
self.additional_action_groups = []
|
||||
self.additional_uis = []
|
||||
self.ui_def = '''<ui>
|
||||
<menubar name="MenuBar">
|
||||
<menu action="ViewMenu">
|
||||
<placeholder name="Bars">
|
||||
<menuitem action="Sidebar"/>
|
||||
<menuitem action="Bottombar"/>
|
||||
</placeholder>
|
||||
</menu>
|
||||
</menubar>
|
||||
</ui>'''
|
||||
self.ui_def = ['''
|
||||
<placeholder id="Bars">
|
||||
<item>
|
||||
<attribute name="action">win.Sidebar</attribute>
|
||||
<attribute name="label" translatable="yes">_Sidebar</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="action">win.Bottombar</attribute>
|
||||
<attribute name="label" translatable="yes">_Bottombar</attribute>
|
||||
</item>
|
||||
</placeholder>
|
||||
''']
|
||||
self.dirty = True
|
||||
self.active = False
|
||||
self._dirty_on_change_inactive = True
|
||||
self.func_list = {}
|
||||
|
||||
if isinstance(self.pdata.category, tuple):
|
||||
self.category, self.translated_category = self.pdata.category
|
||||
@@ -201,24 +200,24 @@ class PageView(DbGUIElement, metaclass=ABCMeta):
|
||||
"""
|
||||
self._config.set(setting, widget.get_position())
|
||||
|
||||
def __sidebar_toggled(self, action):
|
||||
def __sidebar_toggled(self, action, value):
|
||||
"""
|
||||
Called when the sidebar is toggled.
|
||||
"""
|
||||
active = action.get_active()
|
||||
if active:
|
||||
action.set_state(value) # change GUI
|
||||
if value.get_boolean():
|
||||
self.sidebar.show()
|
||||
self.sidebar_toggled(True)
|
||||
else:
|
||||
self.sidebar.hide()
|
||||
self.sidebar_toggled(False)
|
||||
|
||||
def __bottombar_toggled(self, action):
|
||||
def __bottombar_toggled(self, action, value):
|
||||
"""
|
||||
Called when the bottombar is toggled.
|
||||
"""
|
||||
active = action.get_active()
|
||||
if active:
|
||||
action.set_state(value) # change GUI
|
||||
if value.get_boolean():
|
||||
self.bottombar.show()
|
||||
else:
|
||||
self.bottombar.hide()
|
||||
@@ -277,7 +276,7 @@ class PageView(DbGUIElement, metaclass=ABCMeta):
|
||||
def get_data(self):
|
||||
return self.data
|
||||
class Context:
|
||||
targets = [drag_type]
|
||||
targets = [drag_type.name()]
|
||||
action = 1
|
||||
def list_targets(self):
|
||||
return Context.targets
|
||||
@@ -312,12 +311,6 @@ class PageView(DbGUIElement, metaclass=ABCMeta):
|
||||
return True
|
||||
return False
|
||||
|
||||
def call_function(self, key):
|
||||
"""
|
||||
Calls the function associated with the key value
|
||||
"""
|
||||
self.func_list.get(key)()
|
||||
|
||||
def post(self):
|
||||
"""
|
||||
Called after a page is created.
|
||||
@@ -332,6 +325,9 @@ class PageView(DbGUIElement, metaclass=ABCMeta):
|
||||
self.sidebar.set_active()
|
||||
self.bottombar.set_active()
|
||||
self.active = True
|
||||
new_title = "%s - %s - Gramps" % (self.dbstate.db.get_dbname(),
|
||||
self.get_title())
|
||||
self.uistate.window.set_title(new_title)
|
||||
if self.dirty:
|
||||
self.uistate.set_busy_cursor(True)
|
||||
self.build_tree()
|
||||
@@ -370,14 +366,15 @@ class PageView(DbGUIElement, metaclass=ABCMeta):
|
||||
Turns off the visibility of the View's action group, if defined
|
||||
"""
|
||||
if self.action_group:
|
||||
self.action_group.set_visible(False)
|
||||
self.uistate.uimanager.set_actions_visible(self.action_group,
|
||||
False)
|
||||
|
||||
def enable_action_group(self, obj):
|
||||
"""
|
||||
Turns on the visibility of the View's action group, if defined
|
||||
"""
|
||||
if self.action_group:
|
||||
self.action_group.set_visible(True)
|
||||
self.uistate.uimanager.set_actions_visible(self.action_group, True)
|
||||
|
||||
def get_stock(self):
|
||||
"""
|
||||
@@ -435,11 +432,9 @@ class PageView(DbGUIElement, metaclass=ABCMeta):
|
||||
View. The user typically defines self.action_list and
|
||||
self.action_toggle_list in this function.
|
||||
"""
|
||||
self._add_toggle_action('Sidebar', None, _('_Sidebar'),
|
||||
"<shift><PRIMARY>R", None, self.__sidebar_toggled,
|
||||
self._add_toggle_action('Sidebar', self.__sidebar_toggled, '',
|
||||
self.sidebar.get_property('visible'))
|
||||
self._add_toggle_action('Bottombar', None, _('_Bottombar'),
|
||||
"<shift><PRIMARY>B", None, self.__bottombar_toggled,
|
||||
self._add_toggle_action('Bottombar', self.__bottombar_toggled, '',
|
||||
self.bottombar.get_property('visible'))
|
||||
|
||||
def __build_action_group(self):
|
||||
@@ -452,31 +447,19 @@ class PageView(DbGUIElement, metaclass=ABCMeta):
|
||||
if len(self.action_list) > 0:
|
||||
self.action_group.add_actions(self.action_list)
|
||||
if len(self.action_toggle_list) > 0:
|
||||
self.action_group.add_toggle_actions(self.action_toggle_list)
|
||||
self.action_group.add_actions(self.action_toggle_list)
|
||||
|
||||
def _add_action(self, name, icon_name, label, accel=None, tip=None,
|
||||
callback=None):
|
||||
def _add_action(self, name, callback=None, accel=None):
|
||||
"""
|
||||
Add an action to the action list for the current view.
|
||||
"""
|
||||
self.action_list.append((name, icon_name, label, accel, tip,
|
||||
callback))
|
||||
self.action_list.append((name, callback, accel))
|
||||
|
||||
def _add_toggle_action(self, name, icon_name, label, accel=None,
|
||||
tip=None, callback=None, value=False):
|
||||
def _add_toggle_action(self, name, callback=None, accel= None, value=False):
|
||||
"""
|
||||
Add a toggle action to the action list for the current view.
|
||||
"""
|
||||
self.action_toggle_list.append((name, icon_name, label, accel,
|
||||
tip, callback, value))
|
||||
|
||||
def _add_toolmenu_action(self, name, label, tooltip, callback,
|
||||
arrowtooltip):
|
||||
"""
|
||||
Add a menu action to the action list for the current view.
|
||||
"""
|
||||
self.action_toolmenu_list.append((name, label, tooltip, callback,
|
||||
arrowtooltip))
|
||||
self.action_toggle_list.append((name, callback, accel, value))
|
||||
|
||||
def get_actions(self):
|
||||
"""
|
||||
|
||||
+80
-46
@@ -50,7 +50,7 @@ from gramps.gen.const import URL_MANUAL_PAGE
|
||||
from ..display import display_help
|
||||
from ..dialog import ErrorDialog, QuestionDialog2
|
||||
import gramps.gui.widgets.progressdialog as progressdlg
|
||||
from ..actiongroup import ActionGroup
|
||||
from ..uimanager import ActionGroup
|
||||
from ..managedwindow import ManagedWindow
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@@ -58,29 +58,54 @@ from ..managedwindow import ManagedWindow
|
||||
# Constants
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
TAG_1 = '''<ui>
|
||||
<menubar name="MenuBar">
|
||||
<menu action="EditMenu">
|
||||
<placeholder name="TagMenu">
|
||||
<menu action="Tag">
|
||||
'''
|
||||
TAG_1 = '''
|
||||
<section id='TagMenu' groups='RW'>
|
||||
<submenu>
|
||||
<attribute name="label" translatable="yes">Tag</attribute>
|
||||
%s
|
||||
</submenu>
|
||||
</section>
|
||||
'''
|
||||
|
||||
TAG_2 = '''
|
||||
</menu>
|
||||
</placeholder>
|
||||
</menu>
|
||||
</menubar>
|
||||
<toolbar name="ToolBar">
|
||||
<placeholder name="TagTool">
|
||||
<toolitem action="TagButton"/>
|
||||
TAG_2 = (
|
||||
''' <placeholder id='TagTool' groups='RW'>
|
||||
<child groups='RO'>
|
||||
<object class="GtkToolButton" id="TagButton">
|
||||
<property name="icon-name">gramps-tag</property>
|
||||
<property name="action-name">win.TagButton</property>
|
||||
<property name="tooltip_text" translatable="yes">'''
|
||||
'''Tag selected rows</property>
|
||||
<property name="label" translatable="yes">Tag</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="homogeneous">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</placeholder>
|
||||
</toolbar>
|
||||
<popup name="TagPopup">
|
||||
'''
|
||||
''')
|
||||
|
||||
TAG_3 = '''
|
||||
</popup>
|
||||
</ui>'''
|
||||
<menu id='TagPopup' groups='RW'>
|
||||
%s
|
||||
</menu>'''
|
||||
|
||||
TAG_MENU = (
|
||||
'''<section>
|
||||
<item>
|
||||
<attribute name="action">win.NewTag</attribute>
|
||||
<attribute name="label" translatable="yes">'''
|
||||
'''New Tag...</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="action">win.OrganizeTags</attribute>
|
||||
<attribute name="label" translatable="yes">'''
|
||||
'''Organize Tags...</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section>
|
||||
%s
|
||||
</section>
|
||||
''')
|
||||
|
||||
WIKI_HELP_PAGE = '%s_-_Filters' % \
|
||||
URL_MANUAL_PAGE
|
||||
@@ -119,13 +144,14 @@ class Tags(DbGUIElement):
|
||||
|
||||
self._build_tag_menu()
|
||||
|
||||
def tag_enable(self):
|
||||
def tag_enable(self, update_menu=True):
|
||||
"""
|
||||
Enables the UI and action groups for the tag menu.
|
||||
"""
|
||||
self.uistate.uimanager.insert_action_group(self.tag_action, 1)
|
||||
self.uistate.uimanager.insert_action_group(self.tag_action)
|
||||
self.tag_id = self.uistate.uimanager.add_ui_from_string(self.tag_ui)
|
||||
self.uistate.uimanager.ensure_update()
|
||||
if update_menu:
|
||||
self.uistate.uimanager.update_menu()
|
||||
|
||||
def tag_disable(self):
|
||||
"""
|
||||
@@ -134,7 +160,6 @@ class Tags(DbGUIElement):
|
||||
if self.tag_id is not None:
|
||||
self.uistate.uimanager.remove_ui(self.tag_id)
|
||||
self.uistate.uimanager.remove_action_group(self.tag_action)
|
||||
self.uistate.uimanager.ensure_update()
|
||||
self.tag_id = None
|
||||
|
||||
def _db_changed(self, db):
|
||||
@@ -209,46 +234,55 @@ class Tags(DbGUIElement):
|
||||
actions = []
|
||||
|
||||
if not self.dbstate.is_open():
|
||||
self.tag_ui = ''
|
||||
self.tag_ui = ['']
|
||||
self.tag_action = ActionGroup(name='Tag')
|
||||
return
|
||||
|
||||
tag_menu = '<menuitem action="NewTag"/>'
|
||||
tag_menu += '<menuitem action="OrganizeTags"/>'
|
||||
tag_menu += '<separator/>'
|
||||
tag_menu = ''
|
||||
menuitem = '''
|
||||
<item>
|
||||
<attribute name="action">win.TAG_%s</attribute>
|
||||
<attribute name="label" translatable="yes">%s</attribute>
|
||||
</item>'''
|
||||
|
||||
for tag_name, handle in self.__tag_list:
|
||||
tag_menu += '<menuitem action="TAG_%s"/>' % handle
|
||||
actions.append(('TAG_%s' % handle, None, tag_name, None, None,
|
||||
make_callback(self.tag_selected_rows, handle)))
|
||||
tag_menu += menuitem % (handle, tag_name)
|
||||
actions.append(('TAG_%s' % handle,
|
||||
make_callback(self.tag_selected_rows, handle)))
|
||||
tag_menu = TAG_MENU % tag_menu
|
||||
|
||||
self.tag_ui = TAG_1 + tag_menu + TAG_2 + tag_menu + TAG_3
|
||||
self.tag_ui = [TAG_1 % tag_menu, TAG_2, TAG_3 % tag_menu]
|
||||
|
||||
actions.append(('Tag', 'gramps-tag', _('Tag'), None, None, None))
|
||||
actions.append(('NewTag', 'gramps-tag-new', _('New Tag...'), None, None,
|
||||
self.cb_new_tag))
|
||||
actions.append(('OrganizeTags', None, _('Organize Tags...'), None, None,
|
||||
self.cb_organize_tags))
|
||||
actions.append(('TagButton', 'gramps-tag', _('Tag'), None,
|
||||
_('Tag selected rows'), self.cb_tag_button))
|
||||
actions.append(('NewTag', self.cb_new_tag))
|
||||
actions.append(('OrganizeTags', self.cb_organize_tags))
|
||||
actions.append(('TagButton', self.cb_tag_button))
|
||||
|
||||
self.tag_action = ActionGroup(name='Tag')
|
||||
self.tag_action.add_actions(actions)
|
||||
|
||||
def cb_tag_button(self, action):
|
||||
def cb_tag_button(self, *args):
|
||||
"""
|
||||
Display the popup menu when the toolbar button is clicked.
|
||||
"""
|
||||
menu = self.uistate.uimanager.get_widget('/TagPopup')
|
||||
button = self.uistate.uimanager.get_widget('/ToolBar/TagTool/TagButton')
|
||||
menu.popup(None, None, cb_menu_position, button, 0, 0)
|
||||
menu = self.uistate.uimanager.get_widget('TagPopup')
|
||||
button = self.uistate.uimanager.get_widget('TagButton')
|
||||
popup_menu = Gtk.Menu.new_from_model(menu)
|
||||
popup_menu.attach_to_widget(button, None)
|
||||
popup_menu.show_all()
|
||||
if Gtk.MINOR_VERSION < 22:
|
||||
# ToDo The following is reported to work poorly with Wayland
|
||||
popup_menu.popup(None, None, cb_menu_position, button, 0, 0)
|
||||
else:
|
||||
popup_menu.popup_at_widget(button, Gdk.Gravity.SOUTH,
|
||||
Gdk.Gravity.NORTH_WEST, None)
|
||||
|
||||
def cb_organize_tags(self, action):
|
||||
def cb_organize_tags(self, *action):
|
||||
"""
|
||||
Display the Organize Tags dialog.
|
||||
"""
|
||||
OrganizeTagsDialog(self.db, self.uistate, [])
|
||||
|
||||
def cb_new_tag(self, action):
|
||||
def cb_new_tag(self, *action):
|
||||
"""
|
||||
Create a new tag and tag the selected objects.
|
||||
"""
|
||||
@@ -304,7 +338,7 @@ def make_callback(func, tag_handle):
|
||||
"""
|
||||
Generates a callback function based off the passed arguments
|
||||
"""
|
||||
return lambda x: func(tag_handle)
|
||||
return lambda x, y: func(tag_handle)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
@@ -34,16 +34,12 @@ from .monitoredwidgets import *
|
||||
from .selectionwidget import SelectionWidget, Region
|
||||
from .shadebox import *
|
||||
from .shortlistcomboentry import *
|
||||
from .springseparator import *
|
||||
from .statusbar import Statusbar
|
||||
from .styledtextbuffer import *
|
||||
from .styledtexteditor import *
|
||||
from .toolcomboentry import *
|
||||
from .undoablebuffer import *
|
||||
from .undoableentry import *
|
||||
from .undoablestyledbuffer import *
|
||||
from .validatedcomboentry import *
|
||||
from .validatedmaskedentry import *
|
||||
from .valueaction import *
|
||||
from .valuetoolitem import *
|
||||
from .placewithin import *
|
||||
|
||||
@@ -104,6 +104,16 @@ class GrampletBar(Gtk.Notebook):
|
||||
self.set_show_border(False)
|
||||
self.set_scrollable(True)
|
||||
|
||||
image = Gtk.Image(stock=Gtk.STOCK_REFRESH)
|
||||
refresh_button = Gtk.Button(image=image)
|
||||
refresh_button.set_relief(Gtk.ReliefStyle.NONE)
|
||||
refresh_button.connect('clicked', self.__refresh_clicked)
|
||||
self.set_action_widget(refresh_button, Gtk.PackType.START)
|
||||
if config.get('interface.grampletbar-refresh'):
|
||||
refresh_button.show()
|
||||
else:
|
||||
refresh_button.hide()
|
||||
|
||||
book_button = Gtk.Button()
|
||||
# Arrow is too small unless in a box
|
||||
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||
@@ -142,6 +152,7 @@ class GrampletBar(Gtk.Notebook):
|
||||
self.set_current_page(config_settings[1])
|
||||
|
||||
uistate.connect('grampletbar-close-changed', self.cb_close_changed)
|
||||
uistate.connect('grampletbar-refresh-changed', self.cb_refresh_changed)
|
||||
|
||||
# Connect after gramplets added to prevent making them active
|
||||
self.connect('switch-page', self.__switch_page)
|
||||
@@ -404,6 +415,16 @@ class GrampletBar(Gtk.Notebook):
|
||||
tablabel = self.get_tab_label(gramplet)
|
||||
tablabel.use_close(config.get('interface.grampletbar-close'))
|
||||
|
||||
def cb_refresh_changed(self):
|
||||
"""
|
||||
Refresh button preference changed.
|
||||
"""
|
||||
button = self.get_action_widget(Gtk.PackType.START)
|
||||
if config.get('interface.grampletbar-refresh'):
|
||||
button.show()
|
||||
else:
|
||||
button.hide()
|
||||
|
||||
def __delete_clicked(self, button, gramplet):
|
||||
"""
|
||||
Called when the delete button is clicked.
|
||||
@@ -476,6 +497,15 @@ class GrampletBar(Gtk.Notebook):
|
||||
gramplet.detached_window.close()
|
||||
gramplet.detached_window = None
|
||||
|
||||
def __refresh_clicked(self, button):
|
||||
"""
|
||||
Called when the drop-down button is clicked.
|
||||
"""
|
||||
for gramplet in self.get_children():
|
||||
if gramplet and gramplet.pui:
|
||||
if gramplet.pui.active:
|
||||
gramplet.pui.main()
|
||||
|
||||
def __button_clicked(self, button):
|
||||
"""
|
||||
Called when the drop-down button is clicked.
|
||||
@@ -743,7 +773,7 @@ class TabLabel(Gtk.Box):
|
||||
|
||||
def use_close(self, use_close):
|
||||
"""
|
||||
Display the cose button according to user preference.
|
||||
Display the close button according to user preference.
|
||||
"""
|
||||
if use_close:
|
||||
self.closebtn.show()
|
||||
|
||||
@@ -34,6 +34,7 @@ from gi.repository import Pango
|
||||
import time
|
||||
import os
|
||||
import configparser
|
||||
from gramps.gen.config import config
|
||||
|
||||
import logging
|
||||
|
||||
@@ -49,7 +50,7 @@ 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, match_primary_mask, get_link_color
|
||||
from .menuitem import add_menuitem
|
||||
from ..uimanager import ActionGroup
|
||||
from ..plug import make_gui_option
|
||||
from ..plug.quick import run_quick_report_by_name
|
||||
from ..display import display_help, display_url
|
||||
@@ -189,6 +190,13 @@ def logical_true(value):
|
||||
"""
|
||||
return value in ["True", True, 1, "1"]
|
||||
|
||||
def make_callback(func, arg):
|
||||
"""
|
||||
Generates a callback function based off the passed arguments
|
||||
"""
|
||||
return lambda x, y: func(arg)
|
||||
|
||||
|
||||
class LinkTag(Gtk.TextTag):
|
||||
"""
|
||||
Class for keeping track of link data.
|
||||
@@ -235,6 +243,8 @@ class GrampletWindow(ManagedWindow):
|
||||
self.setup_configs('interface.' + cfg_name,
|
||||
gramplet.detached_width, gramplet.detached_height)
|
||||
self.window.add_button(_('_Help'), Gtk.ResponseType.HELP)
|
||||
if config.get('interface.grampletbar-refresh'):
|
||||
self.window.add_button(_('Refresh'), Gtk.ResponseType.APPLY)
|
||||
# add gramplet:
|
||||
if self.gramplet.pui:
|
||||
self.gramplet.pui.active = True
|
||||
@@ -267,6 +277,8 @@ class GrampletWindow(ManagedWindow):
|
||||
else:
|
||||
display_help(WIKI_HELP_PAGE,
|
||||
self.gramplet.tname.replace(" ", "_"))
|
||||
elif response == Gtk.ResponseType.APPLY:
|
||||
self.refresh()
|
||||
|
||||
def build_menu_names(self, obj):
|
||||
"""
|
||||
@@ -274,6 +286,12 @@ class GrampletWindow(ManagedWindow):
|
||||
"""
|
||||
return (self.title, 'Gramplet')
|
||||
|
||||
def refresh(self):
|
||||
"""
|
||||
Refresh the detached gramplet
|
||||
"""
|
||||
self.gramplet.pui.main() # refresh
|
||||
|
||||
def get_title(self):
|
||||
"""
|
||||
Returns the window title.
|
||||
@@ -999,6 +1017,8 @@ class GrampletPane(Gtk.ScrolledWindow):
|
||||
self.pageview = pageview
|
||||
self.pane = self
|
||||
self._popup_xy = None
|
||||
self.at_popup_action = None
|
||||
self.at_popup_menu = None
|
||||
user_gramplets = self.load_gramplets()
|
||||
# build the GUI:
|
||||
msg = _("Right click to add gramplets")
|
||||
@@ -1349,8 +1369,7 @@ class GrampletPane(Gtk.ScrolledWindow):
|
||||
self.place_gramplets(recolumn=True)
|
||||
self.show()
|
||||
|
||||
def restore_gramplet(self, obj):
|
||||
name = obj.get_child().get_label()
|
||||
def restore_gramplet(self, name):
|
||||
############### First kind: from current session
|
||||
for gramplet in self.closed_gramplets:
|
||||
if gramplet.title == name:
|
||||
@@ -1392,8 +1411,7 @@ class GrampletPane(Gtk.ScrolledWindow):
|
||||
else:
|
||||
self.drop_widget(self, gramplet, 0, 0, 0)
|
||||
|
||||
def add_gramplet(self, obj):
|
||||
tname = obj.get_child().get_label()
|
||||
def add_gramplet(self, tname):
|
||||
all_opts = get_gramplet_options_by_tname(tname)
|
||||
name = all_opts["name"]
|
||||
if all_opts is None:
|
||||
@@ -1437,39 +1455,73 @@ class GrampletPane(Gtk.ScrolledWindow):
|
||||
LOG.warning("Can't make gramplet of type '%s'.", name)
|
||||
|
||||
def _button_press(self, obj, event):
|
||||
ui_def = (
|
||||
''' <menu id="Popup">
|
||||
<submenu>
|
||||
<attribute name="action">win.AddGramplet</attribute>
|
||||
<attribute name="label" translatable="yes">Add a gramplet</attribute>
|
||||
%s
|
||||
</submenu>
|
||||
<submenu>
|
||||
<attribute name="action">win.RestoreGramplet</attribute>
|
||||
<attribute name="label" translatable="yes">'''
|
||||
'''Restore a gramplet</attribute>
|
||||
%s
|
||||
</submenu>
|
||||
</menu>
|
||||
''')
|
||||
menuitem = ('<item>\n'
|
||||
'<attribute name="action">win.%s</attribute>\n'
|
||||
'<attribute name="label" translatable="yes">'
|
||||
'%s</attribute>\n'
|
||||
'</item>\n')
|
||||
|
||||
if is_right_click(event):
|
||||
self._popup_xy = (event.x, event.y)
|
||||
uiman = self.uistate.uimanager
|
||||
ag_menu = uiman.get_widget('/GrampletPopup/AddGramplet')
|
||||
if ag_menu:
|
||||
qr_menu = ag_menu.get_submenu()
|
||||
qr_menu = Gtk.Menu()
|
||||
names = [gplug.name for gplug in PLUGMAN.get_reg_gramplets()
|
||||
if gplug.navtypes == []
|
||||
or 'Dashboard' in gplug.navtypes]
|
||||
names.sort()
|
||||
actions = []
|
||||
r_menuitems = ''
|
||||
a_menuitems = ''
|
||||
names = [gplug.name for gplug in PLUGMAN.get_reg_gramplets()
|
||||
if gplug.navtypes == []
|
||||
or 'Dashboard' in gplug.navtypes]
|
||||
names.sort()
|
||||
for name in names:
|
||||
action_name = name.replace(' ', '-')
|
||||
a_menuitems += menuitem % (action_name, name)
|
||||
actions.append((action_name,
|
||||
make_callback(self.add_gramplet, name)))
|
||||
names = [gramplet.title for gramplet in self.closed_gramplets]
|
||||
names.extend(opts["title"] for opts in self.closed_opts)
|
||||
names.sort()
|
||||
if len(names) > 0:
|
||||
for name in names:
|
||||
add_menuitem(qr_menu, name, None,
|
||||
self.add_gramplet)
|
||||
ag_menu.set_submenu(qr_menu)
|
||||
rg_menu = uiman.get_widget('/GrampletPopup/RestoreGramplet')
|
||||
if rg_menu:
|
||||
qr_menu = rg_menu.get_submenu()
|
||||
if qr_menu is not None:
|
||||
rg_menu.set_submenu(None)
|
||||
names = [gramplet.title for gramplet in self.closed_gramplets]
|
||||
names.extend(opts["title"] for opts in self.closed_opts)
|
||||
names.sort()
|
||||
if len(names) > 0:
|
||||
qr_menu = Gtk.Menu()
|
||||
for name in names:
|
||||
add_menuitem(qr_menu, name, None,
|
||||
self.restore_gramplet)
|
||||
rg_menu.set_submenu(qr_menu)
|
||||
self.menu = uiman.get_widget('/GrampletPopup')
|
||||
if self.menu:
|
||||
#GTK3 does not show the popup, workaround: menu as attribute
|
||||
self.menu.popup(None, None, None, None, event.button, event.time)
|
||||
action_name = name.replace(' ', '-')
|
||||
r_menuitems += menuitem % (action_name, name)
|
||||
actions.append((action_name,
|
||||
make_callback(self.restore_gramplet,
|
||||
name)))
|
||||
|
||||
if self.at_popup_action:
|
||||
uiman.remove_ui(self.at_popup_menu)
|
||||
uiman.remove_action_group(self.at_popup_action)
|
||||
self.at_popup_action = ActionGroup('AtPopupActions',
|
||||
actions)
|
||||
uiman.insert_action_group(self.at_popup_action)
|
||||
self.at_popup_menu = uiman.add_ui_from_string([
|
||||
ui_def % (a_menuitems, r_menuitems)])
|
||||
uiman.update_menu()
|
||||
|
||||
menu = uiman.get_widget('Popup')
|
||||
popup_menu = Gtk.Menu.new_from_model(menu)
|
||||
popup_menu.attach_to_widget(obj, None)
|
||||
popup_menu.show_all()
|
||||
if Gtk.MINOR_VERSION < 22:
|
||||
# ToDo The following is reported to work poorly with Wayland
|
||||
popup_menu.popup(None, None, None, None,
|
||||
event.button, event.time)
|
||||
else:
|
||||
popup_menu.popup_at_pointer(event)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
@@ -70,7 +70,10 @@ class ShortlistComboEntry(ValidatedComboEntry):
|
||||
"""
|
||||
__gtype_name__ = "ShortlistComboEntry"
|
||||
|
||||
def __init__(self, items, shortlist=True, validator=None):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def init(self, items, shortlist=True, validator=None):
|
||||
if not items:
|
||||
raise ValueError
|
||||
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2008 Zsolt Foldvari
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
"Separator classes used for Toolbar."
|
||||
|
||||
__all__ = ["SpringSeparatorAction", "SpringSeparatorToolItem"]
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Python modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
import logging
|
||||
_LOG = logging.getLogger(".widgets.springseparator")
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# GTK modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from gi.repository import Gtk
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# SpringSeparatorToolItem class
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class SpringSeparatorToolItem(Gtk.SeparatorToolItem):
|
||||
"""Custom separator toolitem.
|
||||
|
||||
Its only purpose is to push following tool items to the right end
|
||||
of the toolbar.
|
||||
|
||||
"""
|
||||
__gtype_name__ = "SpringSeparatorToolItem"
|
||||
|
||||
def __init__(self):
|
||||
Gtk.SeparatorToolItem.__init__(self)
|
||||
|
||||
self.set_draw(False)
|
||||
self.set_expand(True)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# SpringSeparatorAction class
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class SpringSeparatorAction(Gtk.Action):
|
||||
"""Custom Action to hold a SpringSeparatorToolItem."""
|
||||
|
||||
__gtype_name__ = "SpringSeparatorAction"
|
||||
|
||||
def __init__(self, name, label, tooltip, stock_id):
|
||||
Gtk.Action.__init__(self, name=name, label=label,
|
||||
tooltip=tooltip, stock_id=stock_id)
|
||||
|
||||
## TODO GTK3, How to connect these? Used in styledtexteditor
|
||||
##SpringSeparatorToolItem.set_related_action(SpringSeparatorAction)
|
||||
##deprecated: SpringSeparatorAction.set_tool_item_type(SpringSeparatorToolItem)
|
||||
@@ -43,6 +43,8 @@ from gi.repository import GObject
|
||||
from gi.repository import Gdk
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import Pango
|
||||
from gi.repository.Gio import SimpleActionGroup
|
||||
from gi.repository.GLib import Variant
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@@ -55,15 +57,12 @@ from .styledtextbuffer import (ALLOWED_STYLES,
|
||||
MATCH_FLAVOR, MATCH_STRING,
|
||||
LinkTag)
|
||||
from .undoablestyledbuffer import UndoableStyledBuffer
|
||||
from .valueaction import ValueAction
|
||||
from .toolcomboentry import ToolComboEntry
|
||||
from .springseparator import SpringSeparatorAction
|
||||
from ..spell import Spell
|
||||
from ..display import display_url
|
||||
from ..utils import SystemFonts, match_primary_mask, get_link_color
|
||||
from gramps.gen.config import config
|
||||
from gramps.gen.constfunc import has_display, mac
|
||||
from ..actiongroup import ActionGroup
|
||||
from ..uimanager import ActionGroup
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@@ -75,33 +74,141 @@ if has_display():
|
||||
HAND_CURSOR = Gdk.Cursor.new_for_display(display, Gdk.CursorType.HAND2)
|
||||
REGULAR_CURSOR = Gdk.Cursor.new_for_display(display, Gdk.CursorType.XTERM)
|
||||
|
||||
FORMAT_TOOLBAR = '''
|
||||
<ui>
|
||||
<toolbar name="ToolBar">
|
||||
<toolitem action="%d"/>
|
||||
<toolitem action="%d"/>
|
||||
<toolitem action="%d"/>
|
||||
<toolitem action="%d"/>
|
||||
<toolitem action="%d"/>
|
||||
<toolitem action="Undo"/>
|
||||
<toolitem action="Redo"/>
|
||||
<toolitem action="%d"/>
|
||||
<toolitem action="%d"/>
|
||||
<toolitem action="%d"/>
|
||||
<toolitem action="spring"/>
|
||||
<toolitem action="clear"/>
|
||||
</toolbar>
|
||||
</ui>
|
||||
''' % (StyledTextTagType.ITALIC,
|
||||
StyledTextTagType.BOLD,
|
||||
StyledTextTagType.UNDERLINE,
|
||||
StyledTextTagType.FONTFACE,
|
||||
StyledTextTagType.FONTSIZE,
|
||||
StyledTextTagType.FONTCOLOR,
|
||||
StyledTextTagType.HIGHLIGHT,
|
||||
StyledTextTagType.LINK,
|
||||
)
|
||||
|
||||
FORMAT_TOOLBAR = (
|
||||
'''<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<object class="GtkToolbar" id="ToolBar">
|
||||
<property name="hexpand">True</property>
|
||||
<property name="toolbar-style">GTK_TOOLBAR_ICONS</property>
|
||||
<style>
|
||||
<class name="primary-toolbar"/>
|
||||
</style>
|
||||
<child>
|
||||
<object class="GtkToggleToolButton">
|
||||
<property name="icon-name">format-text-italic</property>
|
||||
<property name="action-name">ste.ITALIC</property>
|
||||
<property name="tooltip_text" translatable="yes">Italic</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="homogeneous">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleToolButton">
|
||||
<property name="icon-name">format-text-bold</property>
|
||||
<property name="action-name">ste.BOLD</property>
|
||||
<property name="tooltip_text" translatable="yes">Bold</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="homogeneous">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleToolButton">
|
||||
<property name="icon-name">format-text-underline</property>
|
||||
<property name="action-name">ste.UNDERLINE</property>
|
||||
<property name="tooltip_text" translatable="yes">Underline</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="homogeneous">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToolItem">
|
||||
<property name="tooltip_text" translatable="yes">Font family</property>
|
||||
<child>
|
||||
<object class="ShortlistComboEntry" id="Fontface"></object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToolItem">
|
||||
<property name="tooltip_text" translatable="yes">Font size</property>
|
||||
<child>
|
||||
<object class="ShortlistComboEntry" id="Fontsize"></object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToolButton">
|
||||
<property name="icon-name">edit-undo</property>
|
||||
<property name="action-name">ste.STUndo</property>
|
||||
<property name="tooltip_text" translatable="yes">Undo</property>
|
||||
<property name="label" translatable="yes">Undo</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="homogeneous">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToolButton">
|
||||
<property name="icon-name">edit-redo</property>
|
||||
<property name="action-name">ste.STRedo</property>
|
||||
<property name="tooltip_text" translatable="yes">Redo</property>
|
||||
<property name="label" translatable="yes">Redo</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="homogeneous">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToolButton">
|
||||
<property name="icon-name">gramps-font-color</property>
|
||||
<property name="action-name">ste.FONTCOLOR</property>
|
||||
<property name="tooltip_text" translatable="yes">Font Color</property>
|
||||
<property name="label" translatable="yes">Font Color</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="homogeneous">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToolButton">
|
||||
<property name="icon-name">gramps-font-bgcolor</property>
|
||||
<property name="action-name">ste.HIGHLIGHT</property>
|
||||
<property name="tooltip_text" translatable="yes">'''
|
||||
'''Background Color</property>
|
||||
<property name="label" translatable="yes">Background Color</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="homogeneous">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToolButton">
|
||||
<property name="icon-name">go-jump</property>
|
||||
<property name="action-name">ste.LINK</property>
|
||||
<property name="tooltip_text" translatable="yes">Link</property>
|
||||
<property name="label" translatable="yes">Link</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="homogeneous">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparatorToolItem">
|
||||
<property name="draw">False</property>
|
||||
<property name="hexpand">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToolButton">
|
||||
<property name="icon-name">edit-clear</property>
|
||||
<property name="action-name">ste.CLEAR</property>
|
||||
<property name="tooltip_text" translatable="yes">'''
|
||||
'''Clear Markup</property>
|
||||
<property name="label" translatable="yes">Clear Markup</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="homogeneous">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
''')
|
||||
FONT_SIZES = [8, 9, 10, 11, 12, 13, 14, 16, 18, 20, 22,
|
||||
24, 26, 28, 32, 36, 40, 48, 56, 64, 72]
|
||||
|
||||
@@ -182,6 +289,7 @@ class StyledTextEditor(Gtk.TextView):
|
||||
self.undo_disabled = self.textbuffer.undo_disabled # see bug 7097
|
||||
self.textbuffer.connect('style-changed', self._on_buffer_style_changed)
|
||||
self.textbuffer.connect('changed', self._on_buffer_changed)
|
||||
self.undo_action = self.redo_action = None
|
||||
Gtk.TextView.__init__(self)
|
||||
self.set_buffer(self.textbuffer)
|
||||
|
||||
@@ -194,9 +302,9 @@ class StyledTextEditor(Gtk.TextView):
|
||||
self._init_url_match()
|
||||
self.url_match = None
|
||||
|
||||
self.toolbar = self._create_toolbar()
|
||||
self.spellcheck = Spell(self)
|
||||
self._internal_style_change = False
|
||||
self.uimanager = None
|
||||
|
||||
self._connect_signals()
|
||||
|
||||
@@ -236,30 +344,6 @@ class StyledTextEditor(Gtk.TextView):
|
||||
window.set_cursor(REGULAR_CURSOR)
|
||||
self.url_match = None
|
||||
|
||||
def on_key_press_event(self, widget, event):
|
||||
"""Signal handler.
|
||||
|
||||
Handle formatting shortcuts.
|
||||
|
||||
"""
|
||||
if ((Gdk.keyval_name(event.keyval) == 'Z') and
|
||||
match_primary_mask(event.get_state(), Gdk.ModifierType.SHIFT_MASK)):
|
||||
self.redo()
|
||||
return True
|
||||
elif ((Gdk.keyval_name(event.keyval) == 'z') and
|
||||
match_primary_mask(event.get_state())):
|
||||
self.undo()
|
||||
return True
|
||||
else:
|
||||
for accel, accel_name in self.action_accels.items():
|
||||
key, mod = Gtk.accelerator_parse(accel)
|
||||
if ((event.keyval == key) and (event.get_state() & mod)):
|
||||
action_name = accel_name
|
||||
action = self.action_group.get_action(action_name)
|
||||
action.activate()
|
||||
return True
|
||||
return False
|
||||
|
||||
def on_insert_at_cursor(self, widget, string):
|
||||
"""Signal handler. for debugging only."""
|
||||
_LOG.debug("Textview insert '%s'" % string)
|
||||
@@ -423,14 +507,13 @@ class StyledTextEditor(Gtk.TextView):
|
||||
Reset the undoable buffer
|
||||
"""
|
||||
self.textbuffer.reset()
|
||||
self.undo_action.set_sensitive(False)
|
||||
self.redo_action.set_sensitive(False)
|
||||
self.undo_action.set_enabled(False)
|
||||
self.redo_action.set_enabled(False)
|
||||
|
||||
# private methods
|
||||
|
||||
def _connect_signals(self):
|
||||
"""Connect to several signals of the super class Gtk.TextView."""
|
||||
self.connect('key-press-event', self.on_key_press_event)
|
||||
self.connect('insert-at-cursor', self.on_insert_at_cursor)
|
||||
self.connect('delete-from-cursor', self.on_delete_from_cursor)
|
||||
self.connect('paste-clipboard', self.on_paste_clipboard)
|
||||
@@ -439,104 +522,74 @@ class StyledTextEditor(Gtk.TextView):
|
||||
self.connect('button-release-event', self.on_button_release_event)
|
||||
self.connect('populate-popup', self.on_populate_popup)
|
||||
|
||||
def _create_toolbar(self):
|
||||
def create_toolbar(self, uimanager, window):
|
||||
"""
|
||||
Create a formatting toolbar.
|
||||
|
||||
:returns: toolbar containing text formatting toolitems.
|
||||
:rtype: Gtk.Toolbar
|
||||
"""
|
||||
self.uimanager = uimanager
|
||||
# build the toolbar
|
||||
builder = Gtk.Builder.new_from_string(FORMAT_TOOLBAR, -1)
|
||||
# define the actions...
|
||||
# ...first the toggle actions, which have a ToggleToolButton as proxy
|
||||
format_toggle_actions = [
|
||||
(str(StyledTextTagType.ITALIC), 'format-text-italic', None, None,
|
||||
_('Italic'), self._on_toggle_action_activate),
|
||||
(str(StyledTextTagType.BOLD), 'format-text-bold', None, None,
|
||||
_('Bold'), self._on_toggle_action_activate),
|
||||
(str(StyledTextTagType.UNDERLINE), 'format-text-underline', None,
|
||||
None, _('Underline'), self._on_toggle_action_activate),
|
||||
_actions = [
|
||||
('ITALIC', self._on_toggle_action_activate, '<PRIMARY>i', False),
|
||||
('BOLD', self._on_toggle_action_activate, '<PRIMARY>b', False),
|
||||
('UNDERLINE', self._on_toggle_action_activate, '<PRIMARY>u',
|
||||
False),
|
||||
('FONTCOLOR', self._on_action_activate),
|
||||
('HIGHLIGHT', self._on_action_activate),
|
||||
('LINK', self._on_link_activate),
|
||||
('CLEAR', self._format_clear_cb),
|
||||
('STUndo', self.undo, '<primary>z'),
|
||||
('STRedo', self.redo, '<primary><shift>z'),
|
||||
]
|
||||
|
||||
self.toggle_actions = [action[0] for action in format_toggle_actions]
|
||||
|
||||
# ...then the normal actions, which have a ToolButton as proxy
|
||||
format_actions = [
|
||||
(str(StyledTextTagType.FONTCOLOR), 'gramps-font-color',
|
||||
_('Font Color'), None, _('Font Color'), self._on_action_activate),
|
||||
(str(StyledTextTagType.HIGHLIGHT), 'gramps-font-bgcolor',
|
||||
_('Background Color'), None, _('Background Color'),
|
||||
self._on_action_activate),
|
||||
(str(StyledTextTagType.LINK), 'go-jump', _('Link'), None,
|
||||
_('Link'), self._on_link_activate),
|
||||
('clear', 'edit-clear', _('Clear Markup'), None,
|
||||
_('Clear Markup'), self._format_clear_cb),
|
||||
]
|
||||
|
||||
# ...last the custom actions, which have custom proxies
|
||||
default = StyledTextTagType.STYLE_DEFAULT[StyledTextTagType.FONTFACE]
|
||||
# the following are done manually rather than using actions
|
||||
fonts = SystemFonts()
|
||||
fontface_action = ValueAction(str(StyledTextTagType.FONTFACE),
|
||||
_("Font family"),
|
||||
default,
|
||||
ToolComboEntry,
|
||||
fonts.get_system_fonts(),
|
||||
False, #editable
|
||||
True, #shortlist
|
||||
None) # validator
|
||||
fontface_action.connect('changed', self._on_valueaction_changed)
|
||||
fontface = builder.get_object('Fontface')
|
||||
fontface.init(fonts.get_system_fonts(), shortlist=True, validator=None)
|
||||
fontface.set_entry_editable(False)
|
||||
fontface.connect('changed', make_cb(
|
||||
self._on_valueaction_changed, StyledTextTagType.FONTFACE))
|
||||
# set initial value
|
||||
default = StyledTextTagType.STYLE_DEFAULT[StyledTextTagType.FONTFACE]
|
||||
self.fontface = fontface.get_child()
|
||||
self.fontface.set_text(str(default))
|
||||
fontface.show()
|
||||
|
||||
items = FONT_SIZES
|
||||
fontsize = builder.get_object('Fontsize')
|
||||
fontsize.init(items, shortlist=False, validator=is_valid_fontsize)
|
||||
fontsize.set_entry_editable(True)
|
||||
fontsize.connect('changed', make_cb(
|
||||
self._on_valueaction_changed, StyledTextTagType.FONTSIZE))
|
||||
# set initial value
|
||||
default = StyledTextTagType.STYLE_DEFAULT[StyledTextTagType.FONTSIZE]
|
||||
fontsize_action = ValueAction(str(StyledTextTagType.FONTSIZE),
|
||||
_("Font size"),
|
||||
default,
|
||||
ToolComboEntry,
|
||||
items,
|
||||
True, #editable
|
||||
False, #shortlist
|
||||
is_valid_fontsize) #validator
|
||||
fontsize_action.connect('changed', self._on_valueaction_changed)
|
||||
|
||||
spring = SpringSeparatorAction("spring", "", "", None)
|
||||
|
||||
# action accelerators
|
||||
self.action_accels = {
|
||||
'<PRIMARY>i': str(StyledTextTagType.ITALIC),
|
||||
'<PRIMARY>b': str(StyledTextTagType.BOLD),
|
||||
'<PRIMARY>u': str(StyledTextTagType.UNDERLINE),
|
||||
}
|
||||
self.fontsize = fontsize.get_child()
|
||||
self.fontsize.set_text(str(default))
|
||||
fontsize.show()
|
||||
|
||||
# create the action group and insert all the actions
|
||||
self.action_group = ActionGroup(name='Format')
|
||||
self.action_group.add_toggle_actions(format_toggle_actions)
|
||||
self.undo_action = Gtk.Action(name="Undo", label=_('Undo'),
|
||||
tooltip=_('Undo'))
|
||||
self.undo_action.set_icon_name('edit-undo')
|
||||
self.undo_action.connect('activate', self.undo)
|
||||
self.redo_action = Gtk.Action.new(name="Redo", label=_('Redo'),
|
||||
tooltip=_('Redo'))
|
||||
self.redo_action.set_icon_name('edit-redo')
|
||||
self.redo_action.connect('activate', self.redo)
|
||||
self.action_group.add_action(self.undo_action)
|
||||
self.action_group.add_action(self.redo_action)
|
||||
self.action_group.add_actions(format_actions)
|
||||
self.action_group.add_action(fontface_action)
|
||||
self.action_group.add_action(fontsize_action)
|
||||
self.action_group.add_action(spring)
|
||||
self.action_group = ActionGroup('Format', _actions, 'ste')
|
||||
act_grp = SimpleActionGroup()
|
||||
window.insert_action_group('ste', act_grp)
|
||||
window.set_application(uimanager.app)
|
||||
uimanager.insert_action_group(self.action_group, act_grp)
|
||||
|
||||
# define the toolbar and create the proxies via ensure_update()
|
||||
uimanager = Gtk.UIManager()
|
||||
uimanager.insert_action_group(self.action_group, 0)
|
||||
uimanager.add_ui_from_string(FORMAT_TOOLBAR)
|
||||
uimanager.ensure_update()
|
||||
self.undo_action = uimanager.get_action(self.action_group, "STUndo")
|
||||
self.redo_action = uimanager.get_action(self.action_group, "STRedo")
|
||||
# allow undo/redo to see actions if editable.
|
||||
self.textbuffer.connect('changed', self._on_buffer_changed)
|
||||
# undo/redo are initially greyed out, until something is changed
|
||||
self.undo_action.set_enabled(False)
|
||||
self.redo_action.set_enabled(False)
|
||||
|
||||
# get the toolbar and set it's style
|
||||
toolbar = uimanager.get_widget('/ToolBar')
|
||||
toolbar.set_style(Gtk.ToolbarStyle.ICONS)
|
||||
self.undo_action.set_sensitive(False)
|
||||
self.redo_action.set_sensitive(False)
|
||||
toolbar = builder.get_object('ToolBar')
|
||||
|
||||
return toolbar
|
||||
return toolbar, self.action_group
|
||||
|
||||
def set_transient_parent(self, parent=None):
|
||||
self.transient_parent = parent
|
||||
@@ -582,21 +635,22 @@ class StyledTextEditor(Gtk.TextView):
|
||||
|
||||
# Callback functions
|
||||
|
||||
def _on_toggle_action_activate(self, action):
|
||||
def _on_toggle_action_activate(self, action, value):
|
||||
"""
|
||||
Toggle a style.
|
||||
|
||||
Toggle styles are e.g. 'bold', 'italic', 'underline'.
|
||||
"""
|
||||
action.set_state(value)
|
||||
if self._internal_style_change:
|
||||
return
|
||||
|
||||
style = int(action.get_name())
|
||||
value = action.get_active()
|
||||
_LOG.debug("applying style '%d' with value '%s'" % (style, str(value)))
|
||||
self.textbuffer.apply_style(style, value)
|
||||
style = action.get_name()
|
||||
value = value.get_boolean()
|
||||
_LOG.debug("applying style '%s' with value '%s'" % (style, str(value)))
|
||||
self.textbuffer.apply_style(getattr(StyledTextTagType, style), value)
|
||||
|
||||
def _on_link_activate(self, action):
|
||||
def _on_link_activate(self, action, value):
|
||||
"""
|
||||
Create a link of a selected region of text.
|
||||
"""
|
||||
@@ -633,10 +687,9 @@ class StyledTextEditor(Gtk.TextView):
|
||||
else:
|
||||
tag.data = uri
|
||||
|
||||
|
||||
def _on_action_activate(self, action):
|
||||
def _on_action_activate(self, action, value):
|
||||
"""Apply a format set from a Gtk.Action type of action."""
|
||||
style = int(action.get_name())
|
||||
style = getattr(StyledTextTagType, action.get_name())
|
||||
current_value = self.textbuffer.get_style_at_cursor(style)
|
||||
|
||||
if style == StyledTextTagType.FONTCOLOR:
|
||||
@@ -668,14 +721,12 @@ class StyledTextEditor(Gtk.TextView):
|
||||
(style, str(value)))
|
||||
self.textbuffer.apply_style(style, value)
|
||||
|
||||
def _on_valueaction_changed(self, action):
|
||||
"""Apply a format set by a ValueAction type of action."""
|
||||
def _on_valueaction_changed(self, obj, style):
|
||||
"""Apply a format set by a ShortListComboEntry."""
|
||||
if self._internal_style_change:
|
||||
return
|
||||
|
||||
style = int(action.get_name())
|
||||
|
||||
value = action.get_value()
|
||||
value = obj.get_active_data()
|
||||
try:
|
||||
value = StyledTextTagType.STYLE_TYPE[style](value)
|
||||
_LOG.debug("applying style '%d' with value '%s'" %
|
||||
@@ -685,7 +736,7 @@ class StyledTextEditor(Gtk.TextView):
|
||||
_LOG.debug("unable to convert '%s' to '%s'" %
|
||||
(value, StyledTextTagType.STYLE_TYPE[style]))
|
||||
|
||||
def _format_clear_cb(self, action):
|
||||
def _format_clear_cb(self, action, value):
|
||||
"""
|
||||
Remove all formats from the selection or from all.
|
||||
|
||||
@@ -709,24 +760,27 @@ class StyledTextEditor(Gtk.TextView):
|
||||
|
||||
def _on_buffer_changed(self, buffer):
|
||||
"""synchronize the undo/redo buttons with what is possible"""
|
||||
self.undo_action.set_sensitive(self.textbuffer.can_undo)
|
||||
self.redo_action.set_sensitive(self.textbuffer.can_redo)
|
||||
if self.undo_action:
|
||||
self.undo_action.set_enabled(self.textbuffer.can_undo)
|
||||
self.redo_action.set_enabled(self.textbuffer.can_redo)
|
||||
|
||||
def _on_buffer_style_changed(self, buffer, changed_styles):
|
||||
"""Synchronize actions as the format changes at the buffer's cursor."""
|
||||
if not self.uimanager:
|
||||
return # never initialized a toolbar, not editable
|
||||
types = [StyledTextTagType.ITALIC, StyledTextTagType.BOLD,
|
||||
StyledTextTagType.UNDERLINE]
|
||||
self._internal_style_change = True
|
||||
for style, style_value in changed_styles.items():
|
||||
if str(style) in self.toggle_actions:
|
||||
action = self.action_group.get_action(str(style))
|
||||
self._internal_style_change = True
|
||||
action.set_active(style_value)
|
||||
self._internal_style_change = False
|
||||
|
||||
if ((style == StyledTextTagType.FONTFACE) or
|
||||
(style == StyledTextTagType.FONTSIZE)):
|
||||
action = self.action_group.get_action(str(style))
|
||||
self._internal_style_change = True
|
||||
action.set_value(style_value)
|
||||
self._internal_style_change = False
|
||||
if style in types:
|
||||
action = self.uimanager.get_action(
|
||||
self.action_group, str(StyledTextTagType(style)).upper())
|
||||
action.change_state(Variant.new_boolean(style_value))
|
||||
elif (style == StyledTextTagType.FONTFACE):
|
||||
self.fontface.set_text(style_value)
|
||||
elif style == StyledTextTagType.FONTSIZE:
|
||||
self.fontsize.set_text(str(style_value))
|
||||
self._internal_style_change = False
|
||||
|
||||
def _spell_change_cb(self, menuitem, spellcheck):
|
||||
"""Set spell checker spellcheck according to user selection."""
|
||||
@@ -818,20 +872,17 @@ class StyledTextEditor(Gtk.TextView):
|
||||
start, end = self.textbuffer.get_bounds()
|
||||
return self.textbuffer.get_text(start, end, True)
|
||||
|
||||
def get_toolbar(self):
|
||||
"""
|
||||
Get the formatting toolbar of the editor.
|
||||
|
||||
:returns: toolbar widget to use as formatting GUI.
|
||||
:rtype: Gtk.Toolbar
|
||||
"""
|
||||
return self.toolbar
|
||||
|
||||
def undo(self, obj=None):
|
||||
def undo(self, *obj):
|
||||
self.textbuffer.undo()
|
||||
|
||||
def redo(self, obj=None):
|
||||
def redo(self, *obj):
|
||||
self.textbuffer.redo()
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Module functions
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
|
||||
def uri_dialog(self, uri, callback):
|
||||
"""
|
||||
@@ -851,12 +902,14 @@ def uri_dialog(self, uri, callback):
|
||||
uri = "gramps://%s/handle/%s" % (object_class, handle)
|
||||
EditLink(obj.dbstate, obj.uistate, obj.track, uri, callback)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Module functions
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
def is_valid_fontsize(size):
|
||||
"""Validator function for font size selector widget."""
|
||||
return (size > 0) and (size < 73)
|
||||
|
||||
|
||||
def make_cb(func, value):
|
||||
"""
|
||||
Generates a callback function based off the passed arguments
|
||||
"""
|
||||
return lambda x: func(x, value)
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2008 Zsolt Foldvari
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
"ToolComboEntry class."
|
||||
|
||||
__all__ = ["ToolComboEntry"]
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Python modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
import logging
|
||||
_LOG = logging.getLogger(".widgets.toolcomboentry")
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# GTK modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
#from gi.repository import GObject
|
||||
from gi.repository import Gtk
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Gramps modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from .valuetoolitem import ValueToolItem
|
||||
from .shortlistcomboentry import ShortlistComboEntry
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# ToolComboEntry class
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class ToolComboEntry(ValueToolItem):
|
||||
"""Tool bar item containing a ShortlistComboEntry widget."""
|
||||
__gtype_name__ = "ToolComboEntry"
|
||||
|
||||
def _create_widget(self, items, editable, shortlist=True, validator=None):
|
||||
self.set_border_width(2)
|
||||
self.set_homogeneous(False)
|
||||
self.set_expand(False)
|
||||
|
||||
combo = ShortlistComboEntry(items, shortlist, validator)
|
||||
if (Gtk.MAJOR_VERSION, Gtk.MINOR_VERSION) < (3, 20):
|
||||
combo.set_focus_on_click(False)
|
||||
else:
|
||||
Gtk.Widget.set_focus_on_click(combo, False)
|
||||
combo.set_entry_editable(editable)
|
||||
combo.show()
|
||||
self.add(combo)
|
||||
|
||||
combo.connect('changed', self._on_widget_changed)
|
||||
|
||||
def set_value(self, value):
|
||||
self.get_child().set_active_data(value)
|
||||
|
||||
def get_value(self):
|
||||
return self.get_child().get_active_data()
|
||||
@@ -65,7 +65,8 @@ class ValidatedComboEntry(Gtk.ComboBox):
|
||||
__gtype_name__ = "ValidatedComboEntry"
|
||||
|
||||
def __init__(self, datatype, model=None, column=-1, validator=None, width=-1):
|
||||
Gtk.ComboBox.__init__(self, model=model)
|
||||
Gtk.ComboBox.__init__(self)
|
||||
self.set_model(model)
|
||||
|
||||
self._entry = Gtk.Entry()
|
||||
self._entry.set_width_chars(width)
|
||||
@@ -201,6 +202,9 @@ class ValidatedComboEntry(Gtk.ComboBox):
|
||||
self._internal_change = True
|
||||
new_iter = self._is_in_model(new_data)
|
||||
if new_iter is None:
|
||||
if self.get_active_iter() is None:
|
||||
# allows response when changing between two non-model values
|
||||
self.set_active(0)
|
||||
self.set_active(-1)
|
||||
else:
|
||||
self.set_active_iter(new_iter)
|
||||
|
||||
@@ -1,169 +0,0 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2008 Zsolt Foldvari
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
"ValueAction class."
|
||||
|
||||
__all__ = ["ValueAction"]
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Python modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
import logging
|
||||
_LOG = logging.getLogger(".widgets.valueaction")
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# GTK modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from gi.repository import GObject
|
||||
from gi.repository import Gtk
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Gramps modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from .valuetoolitem import ValueToolItem
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# ValueAction class
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class ValueAction(Gtk.Action):
|
||||
"""
|
||||
Value action class.
|
||||
|
||||
(A ValueAction with menu item doesn't make any sense.)
|
||||
"""
|
||||
__gtype_name__ = "ValueAction"
|
||||
|
||||
__gsignals__ = {
|
||||
'changed': (GObject.SignalFlags.RUN_FIRST,
|
||||
None, #return value
|
||||
()), # arguments
|
||||
}
|
||||
|
||||
def __init__(self, name, tooltip, default, itemtype, *args):
|
||||
"""
|
||||
Create a new ValueAction instance.
|
||||
|
||||
:param name: the name of the action
|
||||
:type name: str
|
||||
:param tooltip: tooltip string
|
||||
:type tooltip: str
|
||||
:param default: default value for the action, it will set the type of
|
||||
the action and thus the type of all the connected
|
||||
proxies.
|
||||
:type default: set by itemtype
|
||||
:param itemtype: default tool item class
|
||||
:type itemtype: :class:`.ValueToolItem` subclass
|
||||
:param args: arguments to be passed to the default toolitem class
|
||||
at creation. see: :meth:`do_create_tool_item`
|
||||
:type args: list
|
||||
"""
|
||||
Gtk.Action.__init__(self, name=name, label='', tooltip=tooltip,
|
||||
stock_id=None)
|
||||
|
||||
self._value = default
|
||||
self._data_type = type(default)
|
||||
|
||||
# have to be remembered, because we can't access
|
||||
# GtkAction->toolbar_item_type later.
|
||||
self._default_toolitem_type = itemtype
|
||||
##TODO GTK3: following is deprecated, must be replaced by
|
||||
## itemtype.set_related_action(ValueAction) in calling class?
|
||||
## self.set_tool_item_type(itemtype)
|
||||
self._args_for_toolitem = args
|
||||
|
||||
self._handlers = {}
|
||||
|
||||
def do_changed(self):
|
||||
"""
|
||||
Default signal handler for 'changed' signal.
|
||||
|
||||
Synchronize all the proxies with the active value.
|
||||
"""
|
||||
for proxy in self.get_proxies():
|
||||
proxy.handler_block(self._handlers[proxy])
|
||||
proxy.set_value(self._value)
|
||||
proxy.handler_unblock(self._handlers[proxy])
|
||||
|
||||
def do_create_tool_item(self):
|
||||
"""
|
||||
Create a 'default' toolbar item widget.
|
||||
|
||||
Override the default method, to be able to pass the required
|
||||
parameters to the proxy's constructor.
|
||||
|
||||
This method is called from Gtk.UIManager.ensure_update(), when a
|
||||
'toolitem' is found in the UI definition with a name refering to a
|
||||
ValueAction. Thus, to use the action via the UIManager a 'default'
|
||||
toolitem type has to be set with the Gtk.Action.set_tool_item_type()
|
||||
method, before invoking the Gtk.UIManager.ensure_update() method.
|
||||
|
||||
Widgets other than the default type has to be created and added
|
||||
manually with the Gtk.Action.connect_proxy() method.
|
||||
|
||||
:returns: a toolbar item connected to the action.
|
||||
:rtype: :class:`.ValueToolItem` subclass
|
||||
"""
|
||||
proxy = self._default_toolitem_type(self._data_type,
|
||||
self._args_for_toolitem)
|
||||
self.connect_proxy(proxy)
|
||||
return proxy
|
||||
|
||||
def _on_proxy_changed(self, proxy):
|
||||
"""Signal handler for the proxies' 'changed' signal."""
|
||||
value = proxy.get_value()
|
||||
if value is not None:
|
||||
self.set_value(value)
|
||||
|
||||
def connect_proxy(self, proxy):
|
||||
"""
|
||||
Connect a widget to an action object as a proxy.
|
||||
|
||||
:param proxy: widget to be connected
|
||||
:type proxy: :class:`.ValueToolItem` subclass
|
||||
"""
|
||||
if not isinstance(proxy, ValueToolItem):
|
||||
raise TypeError
|
||||
|
||||
# do this before connecting, so that we don't call the handler
|
||||
proxy.set_value(self._value)
|
||||
self._handlers[proxy] = proxy.connect('changed', self._on_proxy_changed)
|
||||
|
||||
# if this is called the proxy will appear on the proxy list twice. why?
|
||||
#Gtk.Action.connect_proxy(self, proxy)
|
||||
|
||||
def set_value(self, value):
|
||||
"""Set value to action."""
|
||||
if not isinstance(value, self._data_type):
|
||||
raise TypeError
|
||||
|
||||
self._value = value
|
||||
self.emit('changed')
|
||||
|
||||
def get_value(self):
|
||||
"""Get the value from the action."""
|
||||
return self._value
|
||||
@@ -1,92 +0,0 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2008 Zsolt Foldvari
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
"ValueToolItem class."
|
||||
|
||||
__all__ = ["ValueToolItem"]
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Python modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
import logging
|
||||
_LOG = logging.getLogger(".widgets.valuetoolitem")
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# GTK modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from gi.repository import GObject
|
||||
from gi.repository import Gtk
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# ValueToolItem class
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class ValueToolItem(Gtk.ToolItem):
|
||||
"""ValueToolItem is an abstract toolbar proxy for ValueAction.
|
||||
|
||||
For each kind of widget a separete tool item proxy has to be
|
||||
subclassed from this ValueToolItem.
|
||||
|
||||
"""
|
||||
__gtype_name__ = "ValueToolItem"
|
||||
|
||||
__gsignals__ = {
|
||||
'changed': (GObject.SignalFlags.RUN_FIRST,
|
||||
None, #return value
|
||||
()), # arguments
|
||||
}
|
||||
|
||||
def __init__(self, data_type, args):
|
||||
Gtk.ToolItem.__init__(self)
|
||||
|
||||
self._data_type = data_type
|
||||
|
||||
self._create_widget(*args)
|
||||
|
||||
def _on_widget_changed(self, widget):
|
||||
self.emit('changed')
|
||||
|
||||
def _create_widget(self, args):
|
||||
"""Create the apropriate widget for the actual proxy."""
|
||||
raise NotImplementedError
|
||||
|
||||
def set_value(self, value):
|
||||
"""Set new value for the proxied widget.
|
||||
|
||||
The method is responsible converting the data type between action and
|
||||
widget.
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_value(self):
|
||||
"""Get value from the proxied widget.
|
||||
|
||||
The method is responsible converting the data type between action and
|
||||
widget.
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
@@ -27,7 +27,7 @@ plg.name = _("BSDDB")
|
||||
plg.name_accell = _("_BSDDB Database")
|
||||
plg.description = _("Berkeley Software Distribution Database Backend")
|
||||
plg.version = '1.0'
|
||||
plg.gramps_target_version = "5.0"
|
||||
plg.gramps_target_version = "5.1"
|
||||
plg.status = STABLE
|
||||
plg.fname = 'bsddb.py'
|
||||
plg.ptype = DATABASE
|
||||
|
||||
@@ -129,9 +129,12 @@ def find_fullname(key, data):
|
||||
# surname primary,
|
||||
# surname origin type,
|
||||
# surname connector)]
|
||||
fullname_data = [(data[3][5][0][0] + ' ' + data[3][4], # surname givenname
|
||||
data[3][5][0][1], data[3][5][0][2],
|
||||
data[3][5][0][3], data[3][5][0][4])]
|
||||
if data[3][5]: # if Surname available
|
||||
fullname_data = [(data[3][5][0][0] + ' ' + data[3][4], # combined
|
||||
data[3][5][0][1], data[3][5][0][2],
|
||||
data[3][5][0][3], data[3][5][0][4])]
|
||||
else: # Some importers don't add any Surname at all
|
||||
fullname_data = [(' ' + data[3][4], '', True, (1, ''), '')]
|
||||
# ignore if origin type is PATRONYMIC or MATRONYMIC
|
||||
return __index_surname(fullname_data)
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ register(DATABASE,
|
||||
name_accell=_('_SQLite Database'),
|
||||
description=_('SQLite Database'),
|
||||
version='1.0.0',
|
||||
gramps_target_version='5.0',
|
||||
gramps_target_version='5.1',
|
||||
status=STABLE,
|
||||
fname='sqlite.py',
|
||||
databaseclass='SQLite',
|
||||
|
||||
@@ -21,7 +21,7 @@ from gramps.gen.plug._pluginreg import newplugin, STABLE
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
|
||||
MODULE_VERSION="5.0"
|
||||
MODULE_VERSION="5.1"
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
@@ -579,7 +579,7 @@ class LaTeXBackend(DocBackend):
|
||||
LaTeXBackend.ESCAPE_FUNC = lambda x: latexescapeverbatim
|
||||
|
||||
def _create_xmltag(self, type, value):
|
||||
"""
|
||||
r"""
|
||||
overwrites the method in DocBackend.
|
||||
creates the latex tags needed for non bool style types we support:
|
||||
FONTSIZE : use different \large denomination based
|
||||
@@ -1267,7 +1267,8 @@ class LaTeXDoc(BaseDoc, TextDoc):
|
||||
text = re.sub(URL_PATTERN, _CLICKABLE, text)
|
||||
|
||||
#hard coded replace of the underline used for missing names/data
|
||||
text = text.replace('\\_'*13, '\\underline{\hspace{3\\grbaseindent}}')
|
||||
text = text.replace('\\_' * 13,
|
||||
'\\underline{\\hspace{3\\grbaseindent}}')
|
||||
self.emit(text + ' ')
|
||||
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ class RTFDoc(BaseDoc, TextDoc):
|
||||
'{\\fonttbl\n'
|
||||
'{\\f0\\froman\\fcharset0\\fprq0 Times New Roman;}\n'
|
||||
'{\\f1\\fswiss\\fcharset0\\fprq0 Arial;}}\n'
|
||||
'{\colortbl\n'
|
||||
'{\\colortbl\n'
|
||||
)
|
||||
|
||||
self.color_map = {}
|
||||
@@ -442,7 +442,7 @@ class RTFDoc(BaseDoc, TextDoc):
|
||||
act_height = size[1]
|
||||
|
||||
if self.in_table:
|
||||
self.text += '{\*\shppict{\\pict\\jpegblip'
|
||||
self.text += '{\\*\\shppict{\\pict\\jpegblip'
|
||||
self.text += '\\picwgoal%d\\pichgoal%d\n' % (act_width, act_height)
|
||||
index = 1
|
||||
for i in buf:
|
||||
@@ -452,7 +452,7 @@ class RTFDoc(BaseDoc, TextDoc):
|
||||
index = index+1
|
||||
self.text += '}}\\par\n'
|
||||
else:
|
||||
self.file.write('{\*\shppict{\\pict\\jpegblip')
|
||||
self.file.write('{\\*\\shppict{\\pict\\jpegblip')
|
||||
self.file.write('\\picwgoal%d\\pichgoal%d\n' % (act_width, act_height))
|
||||
index = 1
|
||||
for i in buf:
|
||||
|
||||
@@ -227,13 +227,9 @@ class Calendar(Report):
|
||||
self.doc.draw_box("CAL-Title", "", 0, 0, width, header, mark)
|
||||
self.doc.draw_line("CAL-Border", 0, header, width, header)
|
||||
year = self.year
|
||||
# TRANSLATORS: see
|
||||
# http://gramps-project.org/wiki/index.php?title=Translating_Gramps#Translating_dates
|
||||
# to learn how to select proper inflection for your language.
|
||||
title = self._("{long_month} {year}").format(
|
||||
long_month = self._ldd.long_months[month],
|
||||
year = year
|
||||
).capitalize()
|
||||
# assume every calendar header in the world is "<month-name> <year>"
|
||||
title = "%s %s" % (self._ldd.long_months[month].capitalize(),
|
||||
self._get_date(Date(self.year))) # localized year
|
||||
mark = IndexMark(title, INDEX_TYPE_TOC, 2)
|
||||
font_height = pt2cm(ptitle.get_font().get_size())
|
||||
self.doc.center_text("CAL-Title", title,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user