From 0ca0ae439ea101e0d790c3a644d199f702b367d8 Mon Sep 17 00:00:00 2001 From: Don Allingham Date: Sun, 20 Oct 2002 14:25:16 +0000 Subject: [PATCH] Initial revision svn: r1140 --- gramps2/.cvsignore | 5 + gramps2/AUTHORS | 5 + gramps2/COPYING | 339 + gramps2/ChangeLog | 1 + gramps2/INSTALL | 79 + gramps2/Makefile.am | 30 + gramps2/Makefile.comm | 18 + gramps2/Makefile.in | 490 ++ gramps2/NEWS | 362 + gramps2/README | 65 + gramps2/TODO | 31 + gramps2/aclocal.m4 | 939 +++ gramps2/autogen.sh | 96 + gramps2/configure | 4941 ++++++++++++ gramps2/configure.in | 293 + gramps2/doc/Makefile.am | 11 + gramps2/doc/Makefile.in | 409 + gramps2/doc/extending-gramps/C/Makefile.am | 10 + gramps2/doc/extending-gramps/C/Makefile.in | 240 + .../extending-gramps/C/extending-gramps-C.omf | 14 + .../extending-gramps/C/extending-gramps.sgml | 774 ++ .../C/extending-gramps/index.html | 345 + .../C/extending-gramps/ln7.html | 133 + .../stylesheet-images/caution.gif | Bin 0 -> 1039 bytes .../stylesheet-images/home.gif | Bin 0 -> 995 bytes .../stylesheet-images/important.gif | Bin 0 -> 1081 bytes .../stylesheet-images/next.gif | Bin 0 -> 964 bytes .../stylesheet-images/note.gif | Bin 0 -> 1070 bytes .../stylesheet-images/prev.gif | Bin 0 -> 944 bytes .../stylesheet-images/tip.gif | Bin 0 -> 1029 bytes .../stylesheet-images/toc-blank.gif | Bin 0 -> 95 bytes .../stylesheet-images/toc-minus.gif | Bin 0 -> 843 bytes .../stylesheet-images/toc-plus.gif | Bin 0 -> 846 bytes .../extending-gramps/stylesheet-images/up.gif | Bin 0 -> 922 bytes .../stylesheet-images/warning.gif | Bin 0 -> 1052 bytes .../C/extending-gramps/t1.html | 345 + .../C/extending-gramps/x131.html | 282 + .../C/extending-gramps/x162.html | 219 + .../C/extending-gramps/x177.html | 180 + .../C/extending-gramps/x57.html | 276 + .../C/extending-gramps/x83.html | 369 + gramps2/doc/extending-gramps/C/index.html | 345 + gramps2/doc/extending-gramps/C/omf_timestamp | 0 gramps2/doc/extending-gramps/Makefile.am | 4 + gramps2/doc/extending-gramps/Makefile.in | 352 + gramps2/doc/gramps-manual/C/Makefile.am | 53 + gramps2/doc/gramps-manual/C/Makefile.in | 461 ++ .../doc/gramps-manual/C/figures/addmedia.png | Bin 0 -> 11061 bytes .../doc/gramps-manual/C/figures/druidpg1.png | Bin 0 -> 57083 bytes .../gramps-manual/C/figures/editbookmarks.png | Bin 0 -> 4722 bytes .../gramps-manual/C/figures/ep-address.png | Bin 0 -> 8539 bytes .../gramps-manual/C/figures/ep-altname.png | Bin 0 -> 7992 bytes .../gramps-manual/C/figures/ep-attributes.png | Bin 0 -> 7333 bytes .../doc/gramps-manual/C/figures/ep-event.png | Bin 0 -> 8248 bytes .../gramps-manual/C/figures/ep-gallery.png | Bin 0 -> 13410 bytes .../gramps-manual/C/figures/ep-general.png | Bin 0 -> 45280 bytes .../gramps-manual/C/figures/ep-internet.png | Bin 0 -> 8090 bytes .../doc/gramps-manual/C/figures/ep-lds.png | Bin 0 -> 9528 bytes .../doc/gramps-manual/C/figures/ep-notes.png | Bin 0 -> 7044 bytes .../gramps-manual/C/figures/familyview.png | Bin 0 -> 11981 bytes .../doc/gramps-manual/C/figures/filter.png | Bin 0 -> 11015 bytes .../gramps-manual/C/figures/globalmedia.png | Bin 0 -> 23393 bytes .../gramps-manual/C/figures/gotobookmark.png | Bin 0 -> 16647 bytes .../gramps-manual/C/figures/localmedia.png | Bin 0 -> 12342 bytes .../doc/gramps-manual/C/figures/mainwin.png | Bin 0 -> 12195 bytes .../doc/gramps-manual/C/figures/mediaview.png | Bin 0 -> 18852 bytes .../doc/gramps-manual/C/figures/opendb.png | Bin 0 -> 5472 bytes .../gramps-manual/C/figures/pedegreesel.png | Bin 0 -> 9796 bytes .../gramps-manual/C/figures/pedegreeview.png | Bin 0 -> 9883 bytes .../gramps-manual/C/figures/peoplelist.png | Bin 0 -> 16110 bytes .../doc/gramps-manual/C/figures/placelist.png | Bin 0 -> 13337 bytes .../gramps-manual/C/figures/prefs-bars.png | Bin 0 -> 8385 bytes .../gramps-manual/C/figures/prefs-colors.png | Bin 0 -> 8333 bytes .../gramps-manual/C/figures/prefs-dates.png | Bin 0 -> 8818 bytes .../gramps-manual/C/figures/prefs-disp.png | Bin 0 -> 9187 bytes .../gramps-manual/C/figures/prefs-find.png | Bin 0 -> 7375 bytes .../doc/gramps-manual/C/figures/prefs-gen.png | Bin 0 -> 8568 bytes .../gramps-manual/C/figures/prefs-guess.png | Bin 0 -> 7460 bytes .../doc/gramps-manual/C/figures/prefs-ids.png | Bin 0 -> 8384 bytes .../gramps-manual/C/figures/prefs-main.png | Bin 0 -> 46184 bytes .../gramps-manual/C/figures/prefs-media.png | Bin 0 -> 9062 bytes .../gramps-manual/C/figures/prefs-report.png | Bin 0 -> 9068 bytes .../C/figures/prefs-research.png | Bin 0 -> 8110 bytes .../C/figures/prefs-revision.png | Bin 0 -> 7700 bytes .../doc/gramps-manual/C/figures/reportsel.png | Bin 0 -> 8195 bytes .../gramps-manual/C/figures/revcontrol.png | Bin 0 -> 10447 bytes .../gramps-manual/C/figures/savecomment.png | Bin 0 -> 4456 bytes .../gramps-manual/C/figures/sourcelist.png | Bin 0 -> 9028 bytes .../gramps-manual/C/figures/sourcerefsel.png | Bin 0 -> 6009 bytes .../doc/gramps-manual/C/figures/toolsel.png | Bin 0 -> 7349 bytes .../doc/gramps-manual/C/gramps-manual-C.omf | 14 + .../doc/gramps-manual/C/gramps-manual.sgml | 1863 +++++ .../gramps-manual/C/gramps-manual/index.html | 222 + .../gramps-manual/C/gramps-manual/ln7.html | 133 + .../stylesheet-images/caution.gif | Bin 0 -> 1039 bytes .../gramps-manual/stylesheet-images/home.gif | Bin 0 -> 995 bytes .../stylesheet-images/important.gif | Bin 0 -> 1081 bytes .../gramps-manual/stylesheet-images/next.gif | Bin 0 -> 964 bytes .../gramps-manual/stylesheet-images/note.gif | Bin 0 -> 1070 bytes .../gramps-manual/stylesheet-images/prev.gif | Bin 0 -> 944 bytes .../C/gramps-manual/stylesheet-images/tip.gif | Bin 0 -> 1029 bytes .../stylesheet-images/toc-blank.gif | Bin 0 -> 95 bytes .../stylesheet-images/toc-minus.gif | Bin 0 -> 843 bytes .../stylesheet-images/toc-plus.gif | Bin 0 -> 846 bytes .../C/gramps-manual/stylesheet-images/up.gif | Bin 0 -> 922 bytes .../stylesheet-images/warning.gif | Bin 0 -> 1052 bytes .../doc/gramps-manual/C/gramps-manual/t1.html | 222 + .../gramps-manual/C/gramps-manual/x129.html | 355 + .../gramps-manual/C/gramps-manual/x28.html | 181 + .../gramps-manual/C/gramps-manual/x41.html | 306 + .../gramps-manual/C/gramps-manual/x84.html | 334 + gramps2/doc/gramps-manual/C/index.html | 222 + gramps2/doc/gramps-manual/C/omf_timestamp | 0 gramps2/doc/gramps-manual/Makefile.am | 4 + gramps2/doc/gramps-manual/Makefile.in | 352 + gramps2/doc/gramps.1 | 49 + gramps2/doc/gramps.1.in | 49 + gramps2/doc/gramps.dtd | 320 + gramps2/doc/gramps.sgml | 793 ++ gramps2/doc/sgmldocs.make | 155 + gramps2/gramps.sh.in | 48 + gramps2/gramps.spec | 95 + gramps2/gramps.spec.in | 95 + gramps2/install-sh | 250 + gramps2/py-compile | 84 + gramps2/src/.cvsignore | 5 + gramps2/src/.thumbnails/logo.png | Bin 0 -> 5230 bytes gramps2/src/.xvpics/family48.png | Bin 0 -> 2370 bytes gramps2/src/.xvpics/icon-people02d-24x24.png | Bin 0 -> 2370 bytes gramps2/src/.xvpics/icon-people02d-48x48.png | Bin 0 -> 2370 bytes gramps2/src/.xvpics/people48.png | Bin 0 -> 2370 bytes gramps2/src/.xvpics/people48.xpm | Bin 0 -> 2370 bytes gramps2/src/.xvpics/place.png | Bin 0 -> 2374 bytes gramps2/src/AUTHORS | 2 + gramps2/src/AddMedia.py | 151 + gramps2/src/AddSpouse.py | 382 + gramps2/src/AddrEdit.py | 157 + gramps2/src/AttrEdit.py | 151 + gramps2/src/AutoComp.py | 264 + gramps2/src/Bookmarks.py | 177 + gramps2/src/Calendar.py | 513 ++ gramps2/src/ChangeLog | 0 gramps2/src/ChooseParents.py | 440 + gramps2/src/Date.py | 1037 +++ gramps2/src/DateEdit.py | 83 + gramps2/src/DbPrompter.py | 139 + gramps2/src/DisplayTrace.py | 73 + gramps2/src/DrawDoc.py | 153 + gramps2/src/EditPerson.py | 1516 ++++ gramps2/src/EditPlace.py | 419 + gramps2/src/EditSource.py | 263 + gramps2/src/EventEdit.py | 226 + gramps2/src/FamilyView.py | 438 + gramps2/src/Filter.py | 142 + gramps2/src/Find.py | 255 + gramps2/src/FontScale.py | 260 + gramps2/src/GedcomInfo.py | 222 + gramps2/src/GenericFilter.py | 894 +++ gramps2/src/GrampsCfg.py | 1127 +++ gramps2/src/GrampsParser.py | 913 +++ gramps2/src/GrampsXML.py | 58 + gramps2/src/GrampsZODB.py | 259 + gramps2/src/GraphLayout.py | 69 + gramps2/src/ImageSelect.py | 838 ++ gramps2/src/ImgManip.py | 168 + gramps2/src/LocEdit.py | 116 + gramps2/src/Makefile.am | 59 + gramps2/src/Makefile.in | 478 ++ gramps2/src/Marriage.py | 567 ++ gramps2/src/MediaView.py | 356 + gramps2/src/MergeData.py | 881 ++ gramps2/src/NEWS | 0 gramps2/src/NameEdit.py | 160 + gramps2/src/NoteEdit.py | 96 + gramps2/src/PaperMenu.py | 70 + gramps2/src/PedView.py | 486 ++ gramps2/src/PlaceView.py | 225 + gramps2/src/Plugins.py | 648 ++ gramps2/src/QuestionDialog.py | 61 + gramps2/src/QuickAdd.py | 60 + gramps2/src/README | 0 gramps2/src/ReadXML.py | 199 + gramps2/src/RelImage.py | 179 + gramps2/src/RelLib.py | 2404 ++++++ gramps2/src/Report.py | 1092 +++ gramps2/src/SelectChild.py | 395 + gramps2/src/Sorter.py | 200 + gramps2/src/SourceView.py | 197 + gramps2/src/Sources.py | 325 + gramps2/src/SpreadSheetDoc.py | 116 + gramps2/src/StartupDialog.py | 92 + gramps2/src/StyleEditor.py | 262 + gramps2/src/SubstKeywords.py | 120 + gramps2/src/TarFile.py | 157 + gramps2/src/TextDoc.py | 1077 +++ gramps2/src/UrlEdit.py | 90 + gramps2/src/Utils.py | 554 ++ gramps2/src/VersionControl.py | 278 + gramps2/src/WriteXML.py | 679 ++ gramps2/src/acconfig.h | 7 + gramps2/src/autogen.sh | 19 + gramps2/src/bad.xpm | 35 + gramps2/src/build_po | 8 + gramps2/src/caution.xpm | 35 + gramps2/src/config.glade | 843 ++ gramps2/src/const.py | 878 ++ gramps2/src/const.py.in | 878 ++ gramps2/src/data/.cvsignore | 1 + gramps2/src/data/Makefile.am | 7 + gramps2/src/data/Makefile.in | 376 + gramps2/src/data/gedcom.xml | 118 + gramps2/src/data/templates/Makefile.am | 5 + gramps2/src/data/templates/Makefile.in | 254 + gramps2/src/data/templates/blue_edge.tpkg | Bin 0 -> 690 bytes gramps2/src/data/templates/marble.tpkg | Bin 0 -> 17009 bytes gramps2/src/data/templates/pink_marble.tpkg | Bin 0 -> 15665 bytes gramps2/src/data/templates/sepia.tpkg | Bin 0 -> 2656 bytes gramps2/src/data/templates/sky_border.tpkg | Bin 0 -> 5825 bytes gramps2/src/data/templates/templates.xml | 7 + gramps2/src/dialog.glade | 3123 ++++++++ gramps2/src/dialog.gladep | 7 + gramps2/src/docgen/.cvsignore | 3 + gramps2/src/docgen/AbiWordDoc.py | 322 + gramps2/src/docgen/HtmlDoc.py | 395 + gramps2/src/docgen/KwordDoc.py | 478 ++ gramps2/src/docgen/LaTeXDoc.py | 392 + gramps2/src/docgen/Makefile.am | 6 + gramps2/src/docgen/Makefile.in | 258 + gramps2/src/docgen/OpenDrawDoc.py | 449 ++ gramps2/src/docgen/OpenOfficeDoc.py | 576 ++ gramps2/src/docgen/OpenSpreadSheet.py | 457 ++ gramps2/src/docgen/PSDrawDoc.py | 167 + gramps2/src/docgen/PdfDoc.py | 307 + gramps2/src/docgen/PdfDrawDoc.py | 160 + gramps2/src/docgen/RTFDoc.py | 516 ++ gramps2/src/docgen/SvgDrawDoc.py | 128 + gramps2/src/edit_person.glade | 5738 +++++++++++++ gramps2/src/edit_person.gladep | 7 + gramps2/src/fam.xpm | 48 + gramps2/src/family48.png | Bin 0 -> 1447 bytes gramps2/src/filelist | 124 + gramps2/src/filters/.cvsignore | 3 + gramps2/src/filters/After.py | 58 + gramps2/src/filters/AltFam.py | 38 + gramps2/src/filters/Before.py | 58 + gramps2/src/filters/Disconnected.py | 41 + gramps2/src/filters/EventPlace.py | 71 + gramps2/src/filters/EventType.py | 43 + gramps2/src/filters/Females.py | 41 + gramps2/src/filters/HavePhotos.py | 41 + gramps2/src/filters/IncompleteNames.py | 42 + gramps2/src/filters/Makefile.am | 6 + gramps2/src/filters/Makefile.in | 258 + gramps2/src/filters/Males.py | 42 + gramps2/src/filters/MatchSndEx.py | 46 + gramps2/src/filters/MatchSndEx2.py | 42 + gramps2/src/filters/MutlipleMarriages.py | 40 + gramps2/src/filters/NeverMarried.py | 41 + gramps2/src/filters/NoBirthdate.py | 41 + gramps2/src/filters/NoChildren.py | 46 + gramps2/src/filters/RegExMatch.py | 61 + gramps2/src/filters/SubString.py | 46 + gramps2/src/get_strings | 557 ++ gramps2/src/good.xpm | 38 + gramps2/src/gramps.desktop | 9 + gramps2/src/gramps.glade | 7099 +++++++++++++++++ gramps2/src/gramps.gladep | 7 + gramps2/src/gramps.png | Bin 0 -> 1145 bytes gramps2/src/gramps.py | 60 + gramps2/src/gramps.xpm | 101 + gramps2/src/gramps.zodb | Bin 0 -> 13401 bytes gramps2/src/gramps_main.py | 1695 ++++ gramps2/src/imagesel.glade | 2332 ++++++ gramps2/src/intl.c | 159 + gramps2/src/intl.py | 60 + gramps2/src/latin_ansel.py | 189 + gramps2/src/latin_utf8.py | 62 + gramps2/src/logo.png | Bin 0 -> 21320 bytes gramps2/src/marriage.glade | 1893 +++++ gramps2/src/marriage.gladep | 7 + gramps2/src/media.png | Bin 0 -> 3822 bytes gramps2/src/mergedata.glade | 1945 +++++ gramps2/src/paper.xml | 13 + gramps2/src/pedigree.xpm | 33 + gramps2/src/people.xpm | 52 + gramps2/src/people48.png | Bin 0 -> 1287 bytes gramps2/src/place.png | Bin 0 -> 3327 bytes gramps2/src/places.glade | 1658 ++++ gramps2/src/places.gladep | 7 + gramps2/src/plugins.glade | 367 + gramps2/src/plugins.gladep | 7 + gramps2/src/plugins/.cvsignore | 3 + gramps2/src/plugins/AncestorChart.py | 448 ++ gramps2/src/plugins/AncestorReport.py | 386 + gramps2/src/plugins/ChangeTypes.py | 97 + gramps2/src/plugins/Check.py | 229 + gramps2/src/plugins/DesGraph.py | 455 ++ gramps2/src/plugins/Desbrowser.py | 116 + gramps2/src/plugins/DescendReport.py | 288 + gramps2/src/plugins/DetAncestralReport.py | 931 +++ gramps2/src/plugins/DetDescendantReport.py | 1000 +++ gramps2/src/plugins/EventCmp.py | 335 + gramps2/src/plugins/FamilyGroup.py | 555 ++ gramps2/src/plugins/FilterEditor.py | 398 + gramps2/src/plugins/GraphViz.py | 396 + gramps2/src/plugins/IndivComplete.py | 668 ++ gramps2/src/plugins/IndivSummary.py | 532 ++ gramps2/src/plugins/Makefile.am | 14 + gramps2/src/plugins/Makefile.in | 307 + gramps2/src/plugins/Merge.py | 531 ++ gramps2/src/plugins/PatchNames.py | 134 + gramps2/src/plugins/ReadGedcom.py | 1689 ++++ gramps2/src/plugins/ReadNative.py | 96 + gramps2/src/plugins/RelCalc.py | 439 + gramps2/src/plugins/ReorderIds.py | 127 + gramps2/src/plugins/Summary.py | 151 + gramps2/src/plugins/Verify.py | 309 + gramps2/src/plugins/WebPage.py | 1252 +++ gramps2/src/plugins/WriteGedcom.py | 966 +++ gramps2/src/plugins/WritePafPalm.py | 593 ++ gramps2/src/plugins/WritePkg.py | 154 + gramps2/src/plugins/changetype.glade | 216 + gramps2/src/plugins/count_anc.py | 101 + gramps2/src/plugins/desbrowse.glade | 147 + gramps2/src/plugins/eventcmp.glade | 547 ++ gramps2/src/plugins/gedcomexport.glade | 584 ++ gramps2/src/plugins/gedcomimport.glade | 473 ++ gramps2/src/plugins/merge.glade | 481 ++ gramps2/src/plugins/pafexport.glade | 327 + gramps2/src/plugins/patchnames.glade | 148 + gramps2/src/plugins/pkgexport.glade | 386 + gramps2/src/plugins/readgedcom.glade | 85 + gramps2/src/plugins/relcalc.glade | 241 + gramps2/src/plugins/soundex.glade | 251 + gramps2/src/plugins/soundgen.py | 95 + gramps2/src/plugins/summary.glade | 111 + gramps2/src/plugins/verify.glade | 906 +++ gramps2/src/po/ChangeLog | 0 gramps2/src/po/Makefile.am | 23 + gramps2/src/po/Makefile.in | 253 + gramps2/src/po/POTFILES.in | 6 + gramps2/src/po/cs.mo | Bin 0 -> 79440 bytes gramps2/src/po/cs.po | 5167 ++++++++++++ gramps2/src/po/da_DK.mo | Bin 0 -> 90954 bytes gramps2/src/po/da_DK.po | 5580 +++++++++++++ gramps2/src/po/de.mo | Bin 0 -> 94624 bytes gramps2/src/po/de.po | 6322 +++++++++++++++ gramps2/src/po/es.mo | Bin 0 -> 80822 bytes gramps2/src/po/es.po | 5177 ++++++++++++ gramps2/src/po/fr.mo | Bin 0 -> 93742 bytes gramps2/src/po/fr.po | 5406 +++++++++++++ gramps2/src/po/it.mo | Bin 0 -> 96055 bytes gramps2/src/po/it.po | 5472 +++++++++++++ gramps2/src/po/pt_BR.mo | Bin 0 -> 92657 bytes gramps2/src/po/pt_BR.po | 5453 +++++++++++++ gramps2/src/po/ru.mo | Bin 0 -> 93743 bytes gramps2/src/po/ru.po | 5526 +++++++++++++ gramps2/src/po/sv.mo | Bin 0 -> 79654 bytes gramps2/src/po/sv.po | 4833 +++++++++++ gramps2/src/po/template.po | 5283 ++++++++++++ gramps2/src/preferences.glade | 3135 ++++++++ gramps2/src/preferences.gladep | 7 + gramps2/src/revision.glade | 433 + gramps2/src/revision.gladep | 7 + gramps2/src/rule.glade | 960 +++ gramps2/src/sort.py | 95 + gramps2/src/soundex.py | 68 + gramps2/src/sources.png | Bin 0 -> 3289 bytes gramps2/src/splash.jpg | Bin 0 -> 7493 bytes gramps2/src/srcsel.glade | 825 ++ gramps2/src/srcsel.gladep | 7 + gramps2/src/styles.glade | 1376 ++++ gramps2/src/system_filters.xml | 13 + gramps2/src/trace.glade | 371 + 374 files changed, 160218 insertions(+) create mode 100644 gramps2/.cvsignore create mode 100644 gramps2/AUTHORS create mode 100644 gramps2/COPYING create mode 100644 gramps2/ChangeLog create mode 100644 gramps2/INSTALL create mode 100644 gramps2/Makefile.am create mode 100644 gramps2/Makefile.comm create mode 100644 gramps2/Makefile.in create mode 100644 gramps2/NEWS create mode 100644 gramps2/README create mode 100644 gramps2/TODO create mode 100644 gramps2/aclocal.m4 create mode 100755 gramps2/autogen.sh create mode 100755 gramps2/configure create mode 100644 gramps2/configure.in create mode 100644 gramps2/doc/Makefile.am create mode 100644 gramps2/doc/Makefile.in create mode 100644 gramps2/doc/extending-gramps/C/Makefile.am create mode 100644 gramps2/doc/extending-gramps/C/Makefile.in create mode 100644 gramps2/doc/extending-gramps/C/extending-gramps-C.omf create mode 100644 gramps2/doc/extending-gramps/C/extending-gramps.sgml create mode 100644 gramps2/doc/extending-gramps/C/extending-gramps/index.html create mode 100644 gramps2/doc/extending-gramps/C/extending-gramps/ln7.html create mode 100644 gramps2/doc/extending-gramps/C/extending-gramps/stylesheet-images/caution.gif create mode 100644 gramps2/doc/extending-gramps/C/extending-gramps/stylesheet-images/home.gif create mode 100644 gramps2/doc/extending-gramps/C/extending-gramps/stylesheet-images/important.gif create mode 100644 gramps2/doc/extending-gramps/C/extending-gramps/stylesheet-images/next.gif create mode 100644 gramps2/doc/extending-gramps/C/extending-gramps/stylesheet-images/note.gif create mode 100644 gramps2/doc/extending-gramps/C/extending-gramps/stylesheet-images/prev.gif create mode 100644 gramps2/doc/extending-gramps/C/extending-gramps/stylesheet-images/tip.gif create mode 100644 gramps2/doc/extending-gramps/C/extending-gramps/stylesheet-images/toc-blank.gif create mode 100644 gramps2/doc/extending-gramps/C/extending-gramps/stylesheet-images/toc-minus.gif create mode 100644 gramps2/doc/extending-gramps/C/extending-gramps/stylesheet-images/toc-plus.gif create mode 100644 gramps2/doc/extending-gramps/C/extending-gramps/stylesheet-images/up.gif create mode 100644 gramps2/doc/extending-gramps/C/extending-gramps/stylesheet-images/warning.gif create mode 100644 gramps2/doc/extending-gramps/C/extending-gramps/t1.html create mode 100644 gramps2/doc/extending-gramps/C/extending-gramps/x131.html create mode 100644 gramps2/doc/extending-gramps/C/extending-gramps/x162.html create mode 100644 gramps2/doc/extending-gramps/C/extending-gramps/x177.html create mode 100644 gramps2/doc/extending-gramps/C/extending-gramps/x57.html create mode 100644 gramps2/doc/extending-gramps/C/extending-gramps/x83.html create mode 100644 gramps2/doc/extending-gramps/C/index.html create mode 100644 gramps2/doc/extending-gramps/C/omf_timestamp create mode 100644 gramps2/doc/extending-gramps/Makefile.am create mode 100644 gramps2/doc/extending-gramps/Makefile.in create mode 100644 gramps2/doc/gramps-manual/C/Makefile.am create mode 100644 gramps2/doc/gramps-manual/C/Makefile.in create mode 100644 gramps2/doc/gramps-manual/C/figures/addmedia.png create mode 100644 gramps2/doc/gramps-manual/C/figures/druidpg1.png create mode 100644 gramps2/doc/gramps-manual/C/figures/editbookmarks.png create mode 100644 gramps2/doc/gramps-manual/C/figures/ep-address.png create mode 100644 gramps2/doc/gramps-manual/C/figures/ep-altname.png create mode 100644 gramps2/doc/gramps-manual/C/figures/ep-attributes.png create mode 100644 gramps2/doc/gramps-manual/C/figures/ep-event.png create mode 100644 gramps2/doc/gramps-manual/C/figures/ep-gallery.png create mode 100644 gramps2/doc/gramps-manual/C/figures/ep-general.png create mode 100644 gramps2/doc/gramps-manual/C/figures/ep-internet.png create mode 100644 gramps2/doc/gramps-manual/C/figures/ep-lds.png create mode 100644 gramps2/doc/gramps-manual/C/figures/ep-notes.png create mode 100644 gramps2/doc/gramps-manual/C/figures/familyview.png create mode 100644 gramps2/doc/gramps-manual/C/figures/filter.png create mode 100644 gramps2/doc/gramps-manual/C/figures/globalmedia.png create mode 100644 gramps2/doc/gramps-manual/C/figures/gotobookmark.png create mode 100644 gramps2/doc/gramps-manual/C/figures/localmedia.png create mode 100644 gramps2/doc/gramps-manual/C/figures/mainwin.png create mode 100644 gramps2/doc/gramps-manual/C/figures/mediaview.png create mode 100644 gramps2/doc/gramps-manual/C/figures/opendb.png create mode 100644 gramps2/doc/gramps-manual/C/figures/pedegreesel.png create mode 100644 gramps2/doc/gramps-manual/C/figures/pedegreeview.png create mode 100644 gramps2/doc/gramps-manual/C/figures/peoplelist.png create mode 100644 gramps2/doc/gramps-manual/C/figures/placelist.png create mode 100644 gramps2/doc/gramps-manual/C/figures/prefs-bars.png create mode 100644 gramps2/doc/gramps-manual/C/figures/prefs-colors.png create mode 100644 gramps2/doc/gramps-manual/C/figures/prefs-dates.png create mode 100644 gramps2/doc/gramps-manual/C/figures/prefs-disp.png create mode 100644 gramps2/doc/gramps-manual/C/figures/prefs-find.png create mode 100644 gramps2/doc/gramps-manual/C/figures/prefs-gen.png create mode 100644 gramps2/doc/gramps-manual/C/figures/prefs-guess.png create mode 100644 gramps2/doc/gramps-manual/C/figures/prefs-ids.png create mode 100644 gramps2/doc/gramps-manual/C/figures/prefs-main.png create mode 100644 gramps2/doc/gramps-manual/C/figures/prefs-media.png create mode 100644 gramps2/doc/gramps-manual/C/figures/prefs-report.png create mode 100644 gramps2/doc/gramps-manual/C/figures/prefs-research.png create mode 100644 gramps2/doc/gramps-manual/C/figures/prefs-revision.png create mode 100644 gramps2/doc/gramps-manual/C/figures/reportsel.png create mode 100644 gramps2/doc/gramps-manual/C/figures/revcontrol.png create mode 100644 gramps2/doc/gramps-manual/C/figures/savecomment.png create mode 100644 gramps2/doc/gramps-manual/C/figures/sourcelist.png create mode 100644 gramps2/doc/gramps-manual/C/figures/sourcerefsel.png create mode 100644 gramps2/doc/gramps-manual/C/figures/toolsel.png create mode 100644 gramps2/doc/gramps-manual/C/gramps-manual-C.omf create mode 100644 gramps2/doc/gramps-manual/C/gramps-manual.sgml create mode 100644 gramps2/doc/gramps-manual/C/gramps-manual/index.html create mode 100644 gramps2/doc/gramps-manual/C/gramps-manual/ln7.html create mode 100644 gramps2/doc/gramps-manual/C/gramps-manual/stylesheet-images/caution.gif create mode 100644 gramps2/doc/gramps-manual/C/gramps-manual/stylesheet-images/home.gif create mode 100644 gramps2/doc/gramps-manual/C/gramps-manual/stylesheet-images/important.gif create mode 100644 gramps2/doc/gramps-manual/C/gramps-manual/stylesheet-images/next.gif create mode 100644 gramps2/doc/gramps-manual/C/gramps-manual/stylesheet-images/note.gif create mode 100644 gramps2/doc/gramps-manual/C/gramps-manual/stylesheet-images/prev.gif create mode 100644 gramps2/doc/gramps-manual/C/gramps-manual/stylesheet-images/tip.gif create mode 100644 gramps2/doc/gramps-manual/C/gramps-manual/stylesheet-images/toc-blank.gif create mode 100644 gramps2/doc/gramps-manual/C/gramps-manual/stylesheet-images/toc-minus.gif create mode 100644 gramps2/doc/gramps-manual/C/gramps-manual/stylesheet-images/toc-plus.gif create mode 100644 gramps2/doc/gramps-manual/C/gramps-manual/stylesheet-images/up.gif create mode 100644 gramps2/doc/gramps-manual/C/gramps-manual/stylesheet-images/warning.gif create mode 100644 gramps2/doc/gramps-manual/C/gramps-manual/t1.html create mode 100644 gramps2/doc/gramps-manual/C/gramps-manual/x129.html create mode 100644 gramps2/doc/gramps-manual/C/gramps-manual/x28.html create mode 100644 gramps2/doc/gramps-manual/C/gramps-manual/x41.html create mode 100644 gramps2/doc/gramps-manual/C/gramps-manual/x84.html create mode 100644 gramps2/doc/gramps-manual/C/index.html create mode 100644 gramps2/doc/gramps-manual/C/omf_timestamp create mode 100644 gramps2/doc/gramps-manual/Makefile.am create mode 100644 gramps2/doc/gramps-manual/Makefile.in create mode 100644 gramps2/doc/gramps.1 create mode 100644 gramps2/doc/gramps.1.in create mode 100644 gramps2/doc/gramps.dtd create mode 100644 gramps2/doc/gramps.sgml create mode 100644 gramps2/doc/sgmldocs.make create mode 100644 gramps2/gramps.sh.in create mode 100644 gramps2/gramps.spec create mode 100644 gramps2/gramps.spec.in create mode 100644 gramps2/install-sh create mode 100755 gramps2/py-compile create mode 100644 gramps2/src/.cvsignore create mode 100644 gramps2/src/.thumbnails/logo.png create mode 100644 gramps2/src/.xvpics/family48.png create mode 100644 gramps2/src/.xvpics/icon-people02d-24x24.png create mode 100644 gramps2/src/.xvpics/icon-people02d-48x48.png create mode 100644 gramps2/src/.xvpics/people48.png create mode 100644 gramps2/src/.xvpics/people48.xpm create mode 100644 gramps2/src/.xvpics/place.png create mode 100644 gramps2/src/AUTHORS create mode 100644 gramps2/src/AddMedia.py create mode 100644 gramps2/src/AddSpouse.py create mode 100644 gramps2/src/AddrEdit.py create mode 100644 gramps2/src/AttrEdit.py create mode 100644 gramps2/src/AutoComp.py create mode 100644 gramps2/src/Bookmarks.py create mode 100644 gramps2/src/Calendar.py create mode 100644 gramps2/src/ChangeLog create mode 100644 gramps2/src/ChooseParents.py create mode 100644 gramps2/src/Date.py create mode 100644 gramps2/src/DateEdit.py create mode 100644 gramps2/src/DbPrompter.py create mode 100644 gramps2/src/DisplayTrace.py create mode 100644 gramps2/src/DrawDoc.py create mode 100644 gramps2/src/EditPerson.py create mode 100644 gramps2/src/EditPlace.py create mode 100644 gramps2/src/EditSource.py create mode 100644 gramps2/src/EventEdit.py create mode 100644 gramps2/src/FamilyView.py create mode 100644 gramps2/src/Filter.py create mode 100644 gramps2/src/Find.py create mode 100644 gramps2/src/FontScale.py create mode 100644 gramps2/src/GedcomInfo.py create mode 100644 gramps2/src/GenericFilter.py create mode 100644 gramps2/src/GrampsCfg.py create mode 100644 gramps2/src/GrampsParser.py create mode 100644 gramps2/src/GrampsXML.py create mode 100644 gramps2/src/GrampsZODB.py create mode 100644 gramps2/src/GraphLayout.py create mode 100644 gramps2/src/ImageSelect.py create mode 100644 gramps2/src/ImgManip.py create mode 100644 gramps2/src/LocEdit.py create mode 100644 gramps2/src/Makefile.am create mode 100644 gramps2/src/Makefile.in create mode 100644 gramps2/src/Marriage.py create mode 100644 gramps2/src/MediaView.py create mode 100644 gramps2/src/MergeData.py create mode 100644 gramps2/src/NEWS create mode 100644 gramps2/src/NameEdit.py create mode 100644 gramps2/src/NoteEdit.py create mode 100644 gramps2/src/PaperMenu.py create mode 100644 gramps2/src/PedView.py create mode 100644 gramps2/src/PlaceView.py create mode 100644 gramps2/src/Plugins.py create mode 100644 gramps2/src/QuestionDialog.py create mode 100644 gramps2/src/QuickAdd.py create mode 100644 gramps2/src/README create mode 100644 gramps2/src/ReadXML.py create mode 100644 gramps2/src/RelImage.py create mode 100644 gramps2/src/RelLib.py create mode 100644 gramps2/src/Report.py create mode 100644 gramps2/src/SelectChild.py create mode 100644 gramps2/src/Sorter.py create mode 100644 gramps2/src/SourceView.py create mode 100644 gramps2/src/Sources.py create mode 100644 gramps2/src/SpreadSheetDoc.py create mode 100644 gramps2/src/StartupDialog.py create mode 100644 gramps2/src/StyleEditor.py create mode 100644 gramps2/src/SubstKeywords.py create mode 100644 gramps2/src/TarFile.py create mode 100644 gramps2/src/TextDoc.py create mode 100644 gramps2/src/UrlEdit.py create mode 100644 gramps2/src/Utils.py create mode 100644 gramps2/src/VersionControl.py create mode 100644 gramps2/src/WriteXML.py create mode 100644 gramps2/src/acconfig.h create mode 100755 gramps2/src/autogen.sh create mode 100644 gramps2/src/bad.xpm create mode 100755 gramps2/src/build_po create mode 100644 gramps2/src/caution.xpm create mode 100644 gramps2/src/config.glade create mode 100644 gramps2/src/const.py create mode 100644 gramps2/src/const.py.in create mode 100644 gramps2/src/data/.cvsignore create mode 100644 gramps2/src/data/Makefile.am create mode 100644 gramps2/src/data/Makefile.in create mode 100644 gramps2/src/data/gedcom.xml create mode 100644 gramps2/src/data/templates/Makefile.am create mode 100644 gramps2/src/data/templates/Makefile.in create mode 100644 gramps2/src/data/templates/blue_edge.tpkg create mode 100644 gramps2/src/data/templates/marble.tpkg create mode 100644 gramps2/src/data/templates/pink_marble.tpkg create mode 100644 gramps2/src/data/templates/sepia.tpkg create mode 100644 gramps2/src/data/templates/sky_border.tpkg create mode 100644 gramps2/src/data/templates/templates.xml create mode 100644 gramps2/src/dialog.glade create mode 100644 gramps2/src/dialog.gladep create mode 100644 gramps2/src/docgen/.cvsignore create mode 100644 gramps2/src/docgen/AbiWordDoc.py create mode 100644 gramps2/src/docgen/HtmlDoc.py create mode 100644 gramps2/src/docgen/KwordDoc.py create mode 100644 gramps2/src/docgen/LaTeXDoc.py create mode 100644 gramps2/src/docgen/Makefile.am create mode 100644 gramps2/src/docgen/Makefile.in create mode 100644 gramps2/src/docgen/OpenDrawDoc.py create mode 100644 gramps2/src/docgen/OpenOfficeDoc.py create mode 100644 gramps2/src/docgen/OpenSpreadSheet.py create mode 100644 gramps2/src/docgen/PSDrawDoc.py create mode 100644 gramps2/src/docgen/PdfDoc.py create mode 100644 gramps2/src/docgen/PdfDrawDoc.py create mode 100644 gramps2/src/docgen/RTFDoc.py create mode 100644 gramps2/src/docgen/SvgDrawDoc.py create mode 100644 gramps2/src/edit_person.glade create mode 100644 gramps2/src/edit_person.gladep create mode 100644 gramps2/src/fam.xpm create mode 100644 gramps2/src/family48.png create mode 100644 gramps2/src/filelist create mode 100644 gramps2/src/filters/.cvsignore create mode 100644 gramps2/src/filters/After.py create mode 100644 gramps2/src/filters/AltFam.py create mode 100644 gramps2/src/filters/Before.py create mode 100644 gramps2/src/filters/Disconnected.py create mode 100644 gramps2/src/filters/EventPlace.py create mode 100644 gramps2/src/filters/EventType.py create mode 100644 gramps2/src/filters/Females.py create mode 100644 gramps2/src/filters/HavePhotos.py create mode 100644 gramps2/src/filters/IncompleteNames.py create mode 100644 gramps2/src/filters/Makefile.am create mode 100644 gramps2/src/filters/Makefile.in create mode 100644 gramps2/src/filters/Males.py create mode 100644 gramps2/src/filters/MatchSndEx.py create mode 100644 gramps2/src/filters/MatchSndEx2.py create mode 100644 gramps2/src/filters/MutlipleMarriages.py create mode 100644 gramps2/src/filters/NeverMarried.py create mode 100644 gramps2/src/filters/NoBirthdate.py create mode 100644 gramps2/src/filters/NoChildren.py create mode 100644 gramps2/src/filters/RegExMatch.py create mode 100644 gramps2/src/filters/SubString.py create mode 100755 gramps2/src/get_strings create mode 100644 gramps2/src/good.xpm create mode 100644 gramps2/src/gramps.desktop create mode 100644 gramps2/src/gramps.glade create mode 100644 gramps2/src/gramps.gladep create mode 100644 gramps2/src/gramps.png create mode 100755 gramps2/src/gramps.py create mode 100644 gramps2/src/gramps.xpm create mode 100644 gramps2/src/gramps.zodb create mode 100755 gramps2/src/gramps_main.py create mode 100644 gramps2/src/imagesel.glade create mode 100644 gramps2/src/intl.c create mode 100644 gramps2/src/intl.py create mode 100644 gramps2/src/latin_ansel.py create mode 100644 gramps2/src/latin_utf8.py create mode 100644 gramps2/src/logo.png create mode 100644 gramps2/src/marriage.glade create mode 100644 gramps2/src/marriage.gladep create mode 100644 gramps2/src/media.png create mode 100644 gramps2/src/mergedata.glade create mode 100644 gramps2/src/paper.xml create mode 100644 gramps2/src/pedigree.xpm create mode 100644 gramps2/src/people.xpm create mode 100644 gramps2/src/people48.png create mode 100644 gramps2/src/place.png create mode 100644 gramps2/src/places.glade create mode 100644 gramps2/src/places.gladep create mode 100644 gramps2/src/plugins.glade create mode 100644 gramps2/src/plugins.gladep create mode 100644 gramps2/src/plugins/.cvsignore create mode 100644 gramps2/src/plugins/AncestorChart.py create mode 100644 gramps2/src/plugins/AncestorReport.py create mode 100644 gramps2/src/plugins/ChangeTypes.py create mode 100644 gramps2/src/plugins/Check.py create mode 100644 gramps2/src/plugins/DesGraph.py create mode 100644 gramps2/src/plugins/Desbrowser.py create mode 100644 gramps2/src/plugins/DescendReport.py create mode 100644 gramps2/src/plugins/DetAncestralReport.py create mode 100644 gramps2/src/plugins/DetDescendantReport.py create mode 100644 gramps2/src/plugins/EventCmp.py create mode 100644 gramps2/src/plugins/FamilyGroup.py create mode 100644 gramps2/src/plugins/FilterEditor.py create mode 100644 gramps2/src/plugins/GraphViz.py create mode 100644 gramps2/src/plugins/IndivComplete.py create mode 100644 gramps2/src/plugins/IndivSummary.py create mode 100644 gramps2/src/plugins/Makefile.am create mode 100644 gramps2/src/plugins/Makefile.in create mode 100644 gramps2/src/plugins/Merge.py create mode 100644 gramps2/src/plugins/PatchNames.py create mode 100644 gramps2/src/plugins/ReadGedcom.py create mode 100644 gramps2/src/plugins/ReadNative.py create mode 100644 gramps2/src/plugins/RelCalc.py create mode 100644 gramps2/src/plugins/ReorderIds.py create mode 100644 gramps2/src/plugins/Summary.py create mode 100644 gramps2/src/plugins/Verify.py create mode 100644 gramps2/src/plugins/WebPage.py create mode 100644 gramps2/src/plugins/WriteGedcom.py create mode 100644 gramps2/src/plugins/WritePafPalm.py create mode 100644 gramps2/src/plugins/WritePkg.py create mode 100644 gramps2/src/plugins/changetype.glade create mode 100644 gramps2/src/plugins/count_anc.py create mode 100644 gramps2/src/plugins/desbrowse.glade create mode 100644 gramps2/src/plugins/eventcmp.glade create mode 100644 gramps2/src/plugins/gedcomexport.glade create mode 100644 gramps2/src/plugins/gedcomimport.glade create mode 100644 gramps2/src/plugins/merge.glade create mode 100644 gramps2/src/plugins/pafexport.glade create mode 100644 gramps2/src/plugins/patchnames.glade create mode 100644 gramps2/src/plugins/pkgexport.glade create mode 100644 gramps2/src/plugins/readgedcom.glade create mode 100644 gramps2/src/plugins/relcalc.glade create mode 100644 gramps2/src/plugins/soundex.glade create mode 100644 gramps2/src/plugins/soundgen.py create mode 100644 gramps2/src/plugins/summary.glade create mode 100644 gramps2/src/plugins/verify.glade create mode 100644 gramps2/src/po/ChangeLog create mode 100644 gramps2/src/po/Makefile.am create mode 100644 gramps2/src/po/Makefile.in create mode 100644 gramps2/src/po/POTFILES.in create mode 100644 gramps2/src/po/cs.mo create mode 100644 gramps2/src/po/cs.po create mode 100644 gramps2/src/po/da_DK.mo create mode 100644 gramps2/src/po/da_DK.po create mode 100644 gramps2/src/po/de.mo create mode 100644 gramps2/src/po/de.po create mode 100644 gramps2/src/po/es.mo create mode 100644 gramps2/src/po/es.po create mode 100644 gramps2/src/po/fr.mo create mode 100644 gramps2/src/po/fr.po create mode 100644 gramps2/src/po/it.mo create mode 100644 gramps2/src/po/it.po create mode 100644 gramps2/src/po/pt_BR.mo create mode 100644 gramps2/src/po/pt_BR.po create mode 100644 gramps2/src/po/ru.mo create mode 100644 gramps2/src/po/ru.po create mode 100644 gramps2/src/po/sv.mo create mode 100644 gramps2/src/po/sv.po create mode 100644 gramps2/src/po/template.po create mode 100644 gramps2/src/preferences.glade create mode 100644 gramps2/src/preferences.gladep create mode 100644 gramps2/src/revision.glade create mode 100644 gramps2/src/revision.gladep create mode 100644 gramps2/src/rule.glade create mode 100644 gramps2/src/sort.py create mode 100644 gramps2/src/soundex.py create mode 100644 gramps2/src/sources.png create mode 100644 gramps2/src/splash.jpg create mode 100644 gramps2/src/srcsel.glade create mode 100644 gramps2/src/srcsel.gladep create mode 100644 gramps2/src/styles.glade create mode 100644 gramps2/src/system_filters.xml create mode 100644 gramps2/src/trace.glade diff --git a/gramps2/.cvsignore b/gramps2/.cvsignore new file mode 100644 index 000000000..c365ab55d --- /dev/null +++ b/gramps2/.cvsignore @@ -0,0 +1,5 @@ +Makefile +config.cache +config.log +config.status +gramps.sh diff --git a/gramps2/AUTHORS b/gramps2/AUTHORS new file mode 100644 index 000000000..461241508 --- /dev/null +++ b/gramps2/AUTHORS @@ -0,0 +1,5 @@ +Donald N. Allingham ** Concept and main design + +David Hampton ** Autocomp.py and Report.py + +Donald A. Peterson ** Makefiles, LaTeXDoc.py diff --git a/gramps2/COPYING b/gramps2/COPYING new file mode 100644 index 000000000..a43ea2126 --- /dev/null +++ b/gramps2/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/gramps2/ChangeLog b/gramps2/ChangeLog new file mode 100644 index 000000000..8d1c8b69c --- /dev/null +++ b/gramps2/ChangeLog @@ -0,0 +1 @@ + diff --git a/gramps2/INSTALL b/gramps2/INSTALL new file mode 100644 index 000000000..5b2bdffdd --- /dev/null +++ b/gramps2/INSTALL @@ -0,0 +1,79 @@ +For rebuilding from source, here are some guidelines to follow. For +developers who may be adding functionality to the program, be sure to read +the last section of this documen: "FINAL WORDS". + +SUPER-SHORT VERSION: +You should be able to just run "./configure && make". But if you make any +significant changes or experience any problems, you may wish to run +"./autogen.sh && make" to regenerate everything. + +This will call aclocal if necessary, then automake, which creates +Makefile.in from Makefile.am rules. Then it calls autoconf, which will +generate configure from configure.in and the Makefile.{am,in} sources. +Finally, autogen calls configure to generate the final files necessary for +building gramps. + +SHORT VERSION: +Execute: +aclocal +automake --add-missing --gnu && autoconf && make + +HTML documentation is built from SGML sources via jw. It is known to work +with docbook-utils-0.6.9. There is a problem with 0.6.10, which seems to +have some messed up dsl files. If you have difficulty building the HTML +documentation with your version of jw, then remove "doc" from the SUBDIRS +list in the top-level Makefile and send a report of where the failure was to +the gramps-devel list. This distribution is tested with the style sheets +and PNG support from the gnome-doc-tools-2-1 package. + +MORE INFO: Version and package info is now set in configure.in through a +call to the AM_INIT_AUTOMAKE macro. The results are stored in variables +PACKAGE and VERSION, which then get substituted wherever necessary. +(gramps.sh, gramps.spec, src/const.py, asst. Makefiles, etc.) We also +manually set the RELEASE variable for setting things like "pre" or minor +bugfix issues. * Note: Using @VERSION@ in the manuals has the advantage that +the current manual always states that it describes current version of +gramps. The disadvantage is that this becomes misleading if the manual +isn't regularly updated. Keep in mind this is GRAMPS version and not +*manual* version. Another problem is that the standard GNOME SGML +documentation make rules (sgmldocs.make) have their own rule, but automake +generates its own rule and this introduces a conflict. + +"make (un)install" now runs scrollkeeper-update to ensure +documentation database is up to date. Scrollkeeper v. > 0.1.4 should in +principle work, v. > 0.2 should see no problems. + +VERBOSE, UGLY DETAILS FOR DEVELOPERS: +Using automake/autoconf adds many, MANY build targets to the makefiles. +Basically, we only care about the main and "install" targets. However, +there are some others that bear further notice: + +* make dist -- will create a lovely gramps-{VERSION}.tar.gz archive with +everything needed to distribute, including the HTML documentation just in +case Joe User doen't know about or have a compatable jw/db2html. After +running "make dist" you can create the rpms using +"rpm -ta gramps-{VERSION}.tar.gz". How nice is that? + +* make clean -- only gets rid of byte-compiled stuff like .so files. + +* make distclean -- improves on clean by eliminating configuration (*.in, +config.*, Makefiles, and converted documentation.) stuff. This is generally +what you (as a developer) will want for testing "fresh" compiles. + +* make trans -- We add this one on our own for building the template.po file. + +Another caveat of the automake mantra is that new/overriding make +targets/rules/defines should generally go in the Makefile.am files rather +than Makefile.in. + +* Note: Another beauty of the automake mechanism (and having automake macros +in the configure script) is that once the scripts have been made, a change +to any .am file will trigger "make" to regenerate the Makefile.in/configure +scripts as appropriate. It is _very_ convenient. + +FINAL WORDS: automake "thinks" of a distribution in terms of "SOURCES", such +as raw C code, "COMPILED OBJECTS" like executables and libraries, and +"DATA", such as images, scripts, and documentation. Thus, for gramps we +concentrate on DATA-type objects. We must tell automake what objects are +important. We do this by adding to the EXTRA_DIST variable in the various +Makefile.am files before running automake. diff --git a/gramps2/Makefile.am b/gramps2/Makefile.am new file mode 100644 index 000000000..d11ae66d1 --- /dev/null +++ b/gramps2/Makefile.am @@ -0,0 +1,30 @@ +# copyright (C) 2000 Sun Microsystems, Inc. + +SUBDIRS = src doc omf-install + +EXTRA_DIST = autogen.sh gramps.spec.in + +bin_SCRIPTS = gramps + +grampsdocdir = $(datadir)/doc/gramps-$(VERSION) +scrollkeeper_localstate_dir = $(localstatedir)/lib/scrollkeeper + +gramps: gramps.sh + cp gramps.sh gramps + +dist-hook: gramps.spec + cp gramps.spec $(distdir) + +# Build/rebuild the catalog +install-data-hook: + rm -rf $(DESTDIR)$(scrollkeeper_localstate_dir) + $(mkinstalldirs) $(DESTDIR)$(scrollkeeper_localstate_dir) + $(mkinstalldirs) $(DESTDIR)$(localstatedir)/log + scrollkeeper-rebuilddb -p $(DESTDIR)$(scrollkeeper_localstate_dir) + +# Remove generated files +uninstall-local: + -rm -rf $(DESTDIR)$(gramps_localstate_dir) + -rm -f $(DESTDIR)$(localstatedir)/log/gramps.log + -rm -f $(DESTDIR)$(localstatedir)/log/gramps.log.1 + diff --git a/gramps2/Makefile.comm b/gramps2/Makefile.comm new file mode 100644 index 000000000..b750e4c17 --- /dev/null +++ b/gramps2/Makefile.comm @@ -0,0 +1,18 @@ +# Generated automatically from Makefile.comm.in by configure. +# Hold variable definitions needed by slave Makefiles +prefix = /usr/local +exec_prefix = ${prefix} +bindir = ${exec_prefix}/bin +datadir = ${prefix}/share/gramps +INSTALL = /usr/bin/install -c +sharedir = ${prefix}/share/gramps +GNOMEHELP = ${prefix}/share/gnome/help +GM = gramps-manual +EG = extending-gramps +DB2HTML = + + + +# Ensure the correct "/bin/sh" for interpreting commands +# in case the user has some other shell environment +SHELL = /bin/sh diff --git a/gramps2/Makefile.in b/gramps2/Makefile.in new file mode 100644 index 000000000..17c084b13 --- /dev/null +++ b/gramps2/Makefile.in @@ -0,0 +1,490 @@ +# Makefile.in generated by automake 1.6.3 from Makefile.am. +# @configure_input@ + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# copyright (C) 2000 Sun Microsystems, Inc. +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = . + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : + +EXEEXT = @EXEEXT@ +OBJEXT = @OBJEXT@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +AMTAR = @AMTAR@ +AWK = @AWK@ +BINSH = @BINSH@ +CC = @CC@ +DEPDIR = @DEPDIR@ +DISABLE_SCROLLKEEPER = @DISABLE_SCROLLKEEPER@ +GNOMEHELP = @GNOMEHELP@ +HAVE_GNOME_CONFIG = @HAVE_GNOME_CONFIG@ +HAVE_JW = @HAVE_JW@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +JW = @JW@ +LANGUAGES = @LANGUAGES@ +LIBS = @LIBS@ +MOFILES = @MOFILES@ +MSGFMT = @MSGFMT@ +P15_INCLUDES = @P15_INCLUDES@ +P20_INCLUDES = @P20_INCLUDES@ +P21_INCLUDES = @P21_INCLUDES@ +P22_INCLUDES = @P22_INCLUDES@ +PACKAGE = @PACKAGE@ +POFILES = @POFILES@ +PYTHON = @PYTHON@ +PYTHON22 = @PYTHON22@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RELEASE = @RELEASE@ +SCROLLKEEPER_CONFIG = @SCROLLKEEPER_CONFIG@ +SCROLLKEEPER_REQUIRED = @SCROLLKEEPER_REQUIRED@ +STRIP = @STRIP@ +VERSION = @VERSION@ +VERSIONSTRING = @VERSIONSTRING@ +ZIP = @ZIP@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ + +SUBDIRS = src doc omf-install + +EXTRA_DIST = autogen.sh gramps.spec.in + +bin_SCRIPTS = gramps + +grampsdocdir = $(datadir)/doc/gramps-$(VERSION) +scrollkeeper_localstate_dir = $(localstatedir)/lib/scrollkeeper +subdir = . +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_CLEAN_FILES = gramps.spec gramps.sh +SCRIPTS = $(bin_SCRIPTS) + +DIST_SOURCES = + +RECURSIVE_TARGETS = info-recursive dvi-recursive install-info-recursive \ + uninstall-info-recursive all-recursive install-data-recursive \ + install-exec-recursive installdirs-recursive install-recursive \ + uninstall-recursive check-recursive installcheck-recursive +DIST_COMMON = README AUTHORS COPYING ChangeLog INSTALL Makefile.am \ + Makefile.in NEWS TODO aclocal.m4 configure configure.in \ + gramps.sh.in gramps.spec.in install-sh missing mkinstalldirs \ + py-compile +DIST_SUBDIRS = $(SUBDIRS) +all: all-recursive + +.SUFFIXES: + +am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ + configure.lineno +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe) + +$(top_builddir)/config.status: $(srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + $(SHELL) ./config.status --recheck +$(srcdir)/configure: $(srcdir)/configure.in $(ACLOCAL_M4) $(CONFIGURE_DEPENDENCIES) + cd $(srcdir) && $(AUTOCONF) + +$(ACLOCAL_M4): configure.in + cd $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) +gramps.spec: $(top_builddir)/config.status gramps.spec.in + cd $(top_builddir) && $(SHELL) ./config.status $@ +gramps.sh: $(top_builddir)/config.status gramps.sh.in + cd $(top_builddir) && $(SHELL) ./config.status $@ +binSCRIPT_INSTALL = $(INSTALL_SCRIPT) +install-binSCRIPTS: $(bin_SCRIPTS) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(bindir) + @list='$(bin_SCRIPTS)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + if test -f $$d$$p; then \ + f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \ + echo " $(binSCRIPT_INSTALL) $$d$$p $(DESTDIR)$(bindir)/$$f"; \ + $(binSCRIPT_INSTALL) $$d$$p $(DESTDIR)$(bindir)/$$f; \ + else :; fi; \ + done + +uninstall-binSCRIPTS: + @$(NORMAL_UNINSTALL) + @list='$(bin_SCRIPTS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \ + echo " rm -f $(DESTDIR)$(bindir)/$$f"; \ + rm -f $(DESTDIR)$(bindir)/$$f; \ + done +uninstall-info-am: + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @set fnord $$MAKEFLAGS; amf=$$2; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @set fnord $$MAKEFLAGS; amf=$$2; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done + +ETAGS = etags +ETAGSFLAGS = + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -f $$subdir/TAGS && tags="$$tags -i $$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$tags$$unique" \ + || $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = . +distdir = $(PACKAGE)-$(VERSION) + +am__remove_distdir = \ + { test ! -d $(distdir) \ + || { find $(distdir) -type d ! -perm -200 -exec chmod u+w {} ';' \ + && rm -fr $(distdir); }; } + +GZIP_ENV = --best +distcleancheck_listfiles = find . -type f -print + +distdir: $(DISTFILES) + $(am__remove_distdir) + mkdir $(distdir) + $(mkinstalldirs) $(distdir)/. $(distdir)/src + @list='$(DISTFILES)'; for file in $$list; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkinstalldirs) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d $(distdir)/$$subdir \ + || mkdir $(distdir)/$$subdir \ + || exit 1; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" \ + distdir=../$(distdir)/$$subdir \ + distdir) \ + || exit 1; \ + fi; \ + done + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="${top_distdir}" distdir="$(distdir)" \ + dist-hook + -find $(distdir) -type d ! -perm -777 -exec chmod a+rwx {} \; -o \ + ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -444 -exec $(SHELL) $(install_sh) -c -m a+r {} {} \; \ + || chmod -R a+r $(distdir) +dist-gzip: distdir + $(AMTAR) chof - $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__remove_distdir) + +dist dist-all: distdir + $(AMTAR) chof - $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__remove_distdir) + +# This target untars the dist file and tries a VPATH configuration. Then +# it guarantees that the distribution is self-contained by making another +# tarfile. +distcheck: dist + $(am__remove_distdir) + GZIP=$(GZIP_ENV) gunzip -c $(distdir).tar.gz | $(AMTAR) xf - + chmod -R a-w $(distdir); chmod a+w $(distdir) + mkdir $(distdir)/=build + mkdir $(distdir)/=inst + chmod a-w $(distdir) + dc_install_base=`$(am__cd) $(distdir)/=inst && pwd` \ + && cd $(distdir)/=build \ + && ../configure --srcdir=.. --prefix=$$dc_install_base \ + $(DISTCHECK_CONFIGURE_FLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) dvi \ + && $(MAKE) $(AM_MAKEFLAGS) check \ + && $(MAKE) $(AM_MAKEFLAGS) install \ + && $(MAKE) $(AM_MAKEFLAGS) installcheck \ + && $(MAKE) $(AM_MAKEFLAGS) uninstall \ + && (test `find $$dc_install_base -type f -print | wc -l` -le 1 \ + || { echo "ERROR: files left after uninstall:" ; \ + find $$dc_install_base -type f -print ; \ + exit 1; } >&2 ) \ + && $(MAKE) $(AM_MAKEFLAGS) dist-gzip \ + && rm -f $(distdir).tar.gz \ + && $(MAKE) $(AM_MAKEFLAGS) distcleancheck + $(am__remove_distdir) + @echo "$(distdir).tar.gz is ready for distribution" | \ + sed 'h;s/./=/g;p;x;p;x' +distcleancheck: distclean + if test '$(srcdir)' = . ; then \ + echo "ERROR: distcleancheck can only run from a VPATH build" ; \ + exit 1 ; \ + fi + test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left after distclean:" ; \ + $(distcleancheck_listfiles) ; \ + exit 1; } >&2 +check-am: all-am +check: check-recursive +all-am: Makefile $(SCRIPTS) +installdirs: installdirs-recursive +installdirs-am: + $(mkinstalldirs) $(DESTDIR)$(bindir) + +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-recursive + +dvi-am: + +info: info-recursive + +info-am: + +install-data-am: + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) install-data-hook + +install-exec-am: install-binSCRIPTS + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf autom4te.cache +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic + +uninstall-am: uninstall-binSCRIPTS uninstall-info-am uninstall-local + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) GTAGS all all-am check check-am clean \ + clean-generic clean-recursive dist dist-all dist-gzip distcheck \ + distclean distclean-generic distclean-recursive distclean-tags \ + distcleancheck distdir dvi dvi-am dvi-recursive info info-am \ + info-recursive install install-am install-binSCRIPTS \ + install-data install-data-am install-data-recursive \ + install-exec install-exec-am install-exec-recursive \ + install-info install-info-am install-info-recursive install-man \ + install-recursive install-strip installcheck installcheck-am \ + installdirs installdirs-am installdirs-recursive \ + maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-generic \ + mostlyclean-recursive tags tags-recursive uninstall \ + uninstall-am uninstall-binSCRIPTS uninstall-info-am \ + uninstall-info-recursive uninstall-local uninstall-recursive + + +gramps: gramps.sh + cp gramps.sh gramps + +dist-hook: gramps.spec + cp gramps.spec $(distdir) + +# Build/rebuild the catalog +install-data-hook: + rm -rf $(DESTDIR)$(scrollkeeper_localstate_dir) + $(mkinstalldirs) $(DESTDIR)$(scrollkeeper_localstate_dir) + $(mkinstalldirs) $(DESTDIR)$(localstatedir)/log + scrollkeeper-rebuilddb -p $(DESTDIR)$(scrollkeeper_localstate_dir) + +# Remove generated files +uninstall-local: + -rm -rf $(DESTDIR)$(gramps_localstate_dir) + -rm -f $(DESTDIR)$(localstatedir)/log/gramps.log + -rm -f $(DESTDIR)$(localstatedir)/log/gramps.log.1 +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/gramps2/NEWS b/gramps2/NEWS new file mode 100644 index 000000000..b4976c73f --- /dev/null +++ b/gramps2/NEWS @@ -0,0 +1,362 @@ +Version 0.7.3 +* New Russian (Alex Roitman) and Danish (Lars Lundin) translations. + GRAMPS now supports English, Italian, German, French, Spanish, + Brazilian-Portuguese, Swedish, Danish, and Russian. +* Custom filter editor allows you to create your personal complex + filters that can be applied to reports to select the people that + you want. Tools->Utilites->Custom Filter Editor to access the + filter builder. (Thanks to Jeff Ollie for some major contributions + here). +* New report - Complete Individual Report. Allows you to produce a + more complete form of the Individual Summary. You can use filters + to select all ancestors, all decendants, or any custom filter you + want to create more of a 'book' form of a report. +* On-the-fly document generation instead of pre-built HTML files + (Thanks to Don Peterson) +* Web page generation now generates a two column list, along with + labels. +* GEDCOM impovements, including the ability to use GRAMPS as a GEDCOM + viewer (accepts a GEDCOM file as an argument). + + +Version 0.7.2 +* New Brazilian-Portuguese translation (Marcos Bedinelli) +* Python Imaging Library no longer required for several report + formats. +* Better sizing of images in reports (Bruce DeGrasse) +* An image subdirectory name can now be specified in the WebPage + generation. This allows you to specify the subdirectory (or + absence of a subdirectory) where images are placed when generating + a web site. +* Merging allows you to choose the GRAMPS ID of the merged person. +* Better handling of auto completion of names, places, and other + items. +* Ability to rerun Startup dialog when necessary +* More information in the Startup dialog +* Fewer intermediate files when handling images for reports +* Reports are now plugins, stored in the DocGen library +* Filter out control characters on GEDCOM import +* New document format generators for SVG and PostScript. +* Improved document format generators for LaTeX (Donald Peterson), + KWord (Bruce DeGrasse), and AbiWord. +* Names now have a type (e.g. "Married Name", "Birth Name") + associated with them. +* Eliminated the intermediate dialog box on importing - GRAMPS + now always appends to the current database. +* If an imported GEDCOM contains REFN tags, they are correlated + to the GRAMPS ID if possible. + + +Version 0.7.1 +* A database must now be open at all times. Prevents the problem of + not having a place to put files. +* Several drop down menus for names have been replaced with + autocompletion text boxes. +* Autosave - allows the database to be saved to a backup file + periodically. +* New Descendant Graph dialog. +* Reorganization of reports. +* New preferences dialog. +* Configurable guessing of last names for new children. +* Slightly improved incomplete date handling. +* More sort columns and improved sorting in the lists. +* New tooltips (Don Peterson) +* Improved navigation in the Pedigree View (Shane Hathaway) +* Italian translation (Marco Molteni) + +Version 0.7.0 +* LD_PRELOAD set to handle Mandrake 8.1 shared library problems. +* Drag and drop copying of events, address, attributes, and URLs. +* Improved GEDCOM. GRAMPS now understands many more of the "quirks" + of the GEDCOM produced by other programs. In addition, GRAMPS can + now tailor its GEDCOM output several other programs. An XML data + file is used to describe to GRAMPS the types of quirks each + format uses. +* Support for French Republican, Hebrew, and Julian calendars. +* LDS ordinance support. +* Cleaned up report dialog code. +* Bug fixes in GEDCOM reading and relationship calculator. +* Toolbar may display Icons and Text, Text only, or Icons only. This + helps in some of the translations, where the long text forced the + toolbar to be unusually large. +* Dragging and dropping media objects can optionally display property + dialogs. +* All toolbar icons have tooltips. +* Keyboard shortcuts for bookmark saving. +* Find dialog supports autocompletion, and has the more standard shortcut + of C-F. +* Change in XML database format to provides better support for dates. +* Added entries to the Help menu to jump to the GRAMPS home page and the + GRAMPS mailing list page. + +Version 0.6.2 +* Fixed SuSE 7.3/lib-imlib problem. +* Improved GEDCOM import/export. Gramps now passes the GEDCHK program + and can now properly handle the quirks in names, notes, and parent/child + relationships from PAF, Legacy, FTW, Brother's Keeper, and Family Origins. +* After an Add Spouse, make the new spouse display in the Family View + interface. + +Version 0.6.1 +* Fixed Family View/Pedigree View button swap +* Updated Spanish translation + +Version 0.6.0 +* Support for QUAY, NCHI, and CAUS on GEDCOM import. GEDCOM + import is significantly faster. +* Support for multiple sources per item. +* Support for revision control. RCS based revision control is + available. +* Confidence level moved to SourceReference instead of object. +* All fields of birth and death events are editable. Clicking the + Edit button brings up the event editor. Birth and death fields + on the Edit Person dialog are now read only. +* Support for unknown gender. +* Central image/media repository added. All images in a gallery + are references to these media objects. Media objects can have + global properties (in the repository) or local properties (in + each gallery). +* Media types are beyond images are now supported (PDF, MP3, + word processor documents, etc.) +* Drag and drop of images from one gallery to another. Drag and + drop from file managers into the gallery. Drag and drop from a + web page to a gallery. +* Drag and drop reordering of children in children lists. +* Drag and drop reordering of images in a gallery. +* Autocompletion support on most entry boxes with drop down menus. +* Plugins can be reloaded without re-starting gramps. +* Two people can be selected and directly merged from the main + person list. +* Two places can be selected and directly merged from the main + place list. +* The "Find and Merge" functionality has been improved to make it + easier to find matches. +* Web page generation is significantly faster. +* Many plugins enhanced. Descendant browser can jump to an Edit Person + display, Event Comparison can now save and load complex filters, + etc. +* User had significant more control over the format of the internal + GRAMPS' ids. You can now set a prefix for each type (such as "I-" + for individuals) or specify more powerful formatting using C-like + format statements (such as "I-%04d"). +* Adoption relationships are visible on the pedigree view (seen as + a dotted line). +* Reordering GRAMPS' ids attempts to preserve the integer originally + assigned to the object. +* The person view can be sorted by GRAMPS id. +* KWord format generated is now compatible with KWord 1.1. Images may + now be included in KWord documents (requires Python Imaging Library). + Tables are not working yet. I can figure out how to format a KWord + table. + +Version 0.5.1 +* Bug fixes +* Allow gramps' ids to be edited +* Added Jesper Zedlitz's PAF for Palm export +* Progress report on GEDCOM import +* Add a new person from the parent selection dialog +* Improved configure script for FreeBSD +* Documenation improvements +* Slight performance improvements reading large databases + +Version 0.5.0 +* New pedegree form which can display 3, 4, or 5 generations + depending on the screen size. Supports better navigation. +* Added a Find function to quickly find people. +* Implemented Place objects, allowing additional information + to be stored about a place. +* New icons for Person View and Family View. +* Online documentation. +* Pressing enter on qualifier field for a filter will apply + the filter. +* New verification tool added to look for odd things in the + database. +* Improvements in GEDCOM import, including handling some + Family Tree Maker pecularities. +* Spanish translation is now available. This brings the languages + to English, Spanish, Swedish, German, and French. + +Version 0.4.1 +* Tool/Report menus added to top level menu bar +* Extract Titles and Nicknames plugin improved to provide more + information +* Several bug fixes, including the problem of swapping father/ + mother relationships in families +* Significant improvements in speed for updating displays after + adding, editing, or deleting people, and for applying filters. +* Added Bruce DeGrasse's Detailed Ancestral Report. This will + eventually replace the current Ancestral Report. + +Version 0.4.0 +* Redesigned Family page. More complex family relationships can be + handled. +* ISO date format supported +* Places are stored in a pulldown menu +* gramps ID can be displayed in many lists +* Multiple selection in Add Children box +* Internal gramps ID now a string instead of an integer +* Double clicking on a name in the Pedegree view brings up the edit + box for that person +* Support for same sex parent families has been added +* Complete rework of Edit Person dialog +* Added privacy flag and confidence level to events, attributes, etc. +* Automatically detect gzip'ed XML vs. XML +* Added option to write either gzip'ed XML or straight XML +* Warn on closing the edit person and marriage/relationshp window + when modifications have been made. +* French translation +* New plugin from Jesper Zedlitz that implements a graphical + representation of the people in the database, allowing you to + reposition people according to your own desires. +* XML is indented properly +* XML DTD available + +Version 0.3.2 +* Fixed Style Editor on WebPage.py, to allow styles to be edited. +* WebPage generator now lists more information, including sources. +* May have ironed out the PyXML/Python/SAX issues once and for all, + cleaning up the python 1.5/2.X problems. +* Added the ability to set default directories for reports and + databases. +* Fixed major bug that added a marriage and divorce to every record + that was viewed in the family viewer. +* Fixed bug that was dropping images +* Many, many other bug fixes. +* Improved many of the translations. + +Version 0.3.1 +* Improved Web Site generation (changed from Individual Web Pages) +* Faster load times for XML database +* Fixed unicode problems with Python 2.0 +* Improved GEDCOM exporter +* Use the GRAMPSDIR environment variable in the shell script to determine + the root path instead of dynamically doing it by attempting to look + at __file__ in const.py +* Added better GEDCOM importing for files generated by Brother's + Keeper and Reunion. + +Version 0.3.0 +* Support for RTF (export to MSWord) and limited support for KWord + and LaTeX added. +* User defined styles for reports. Allows the selection of fonts, + sizes, colors, alignment, and many other features in output + reports. Styles can be saved for future use. +* Improved report formats +* Initial German translation. Please note that the German translation + is new, and it will take a while before it is fully in sync with the + program. It will probably take a release or two before it is as + stable as the Swedish translation (it can take a while to provide the + translations, and the code is changing, so it takes a while to flush + things out). +* Output format preference and page size preference can be set in the + preferences menu. +* Names can now have sources and notes. +* Lots of bug fixes +* Fixes to GEDCOM importer + +Version 0.2.0 +* New GEDCOM importer. This new importer understands the GEDCOM + file structure, and intelligently parses the file. More + information is extracted from the GEDCOM file. Guessing the + context of information is no longer done, so the data should go + into the right place. The display dialog now displays useful and + interesting information as the file is loaded. The importer + has been checked with file generated Personal Ancestral File, + Brother's Keeper, DISGEN, the GEDCOM Torture test, and even the + sometimes strange output of Family Tree Maker. +* Better support for sources. Source button on the main page, and + sources are entered from the main screen, instead of being about + five layers deep. +* Events, sources, attributes, and addresses can now have both + sources and notes. +* Limited configurability in status bar display. Instead of just a + name, you can have a name, the internal ID and a name, or a user + selectable attribute and a name. +* A user selectable attribute can be displayed on the Edit Person + display along with the gramps internal ID. The attribute is + specified in the preferences settings, and the value is taken + from the person's attribute list. +* Images now load faster. Thumbnail images are created and maintained + by gramps, eliminating the need to rescale images everytime a + gallery is displayed. +* Paper size preference can now be specified in the preferences + dialog. Once this is set, gramps will make this the default for + report generators. Eventually, you will be able to do the same + for output format preference. This button is current disabled. +* Generating slightly better XML. Maintaining compatibility with + the older files. + + +Version 0.1.5 + +* Plenty of bug fixes in the report generators and merging due to the + previous addition of date ranges. +* Added PDF file type generation for reports. Depends on the reportlab + package (available at www.reportlab.com). If the package is not + installed, gramps will run, but without PDF generation ability. +* Will use the Python Imaging Library if present to handle images. If + not, it will revert to the old method of using Imagick (convert). +* The user can select an attribute (from the attribute list) to display + on the Edit Person window. +* The internal gramps ID is now displayed on the Edit Person window. +* Marriage types can now be recorded. +* Addresses now use a single date instead of multiple dates, since + dates can now deal with ranges. +* Due to a bug in Python 2.0/GTK interaction, list colors are disabled + for Python 2.0 and above. +* configure script now properly deals with Python include paths, + eliminating the need to hand edit the src/Makefile to get gramps to + run under Python 2.X. +* Photos are now displayed properly again in the Edit Person gallery. +* Family notes are now implemented. + +Version 0.1.4 + +* Implemented date ranges. Valid forms are "from to " and + "between and ". +* Better support for partial dates. A question mark can optionally be + used for a placeholder for a year if the year is unknown. Illegal + date formats issue a warning upon an attempt to save the date. +* Start of internationalization (i18n) support. Had to include a single + C file to allow libglade to understand translations. This means that + binary releases are no longer going to be platform independant. +* Start of a generic output formatter for report generation. Currently + supports HTML, OpenOffice (625+), and AbiWord (0.7.13+). Allows + control of paragraph and font styles. +* Added into CVS on sourceforge +* Fixed quite a few bugs + +Version 0.1.3 + +* Allow the user to specifiy their preferred numerical date input format, + either DD/MM/YY or MM/DD/YY on the preferences dialog box. +* Handles file problems a bit more gracefully. +* Use ISO-8859-1 character set instead of ASCII to support languages other + than English. Eventually this should go to unicode, but python 1.5.2 + does not have good unicode support. Python 2.X/Gnome 2.X will be the + migration for this feature. +* Handle ANSEL encoding for import and exporting GEDCOM files. +* Fixed a bug in the selection of parents, which did not accept new parents + if previous parents did not exist. + +Version 0.1.2 + +* Allow the user the option to display alternate names in the person + list. Alternate names in the list have an '*' appended to the end + of the name. +* Merge function now gives the user the option of saving the name of + the merged individual as an alternate name if the names are not + the same. +* Added the "Alternate Birth" and "Alternate Death" to the event list, + to allow other birthdates to be recorded. Gramps makes a distinction + between the birthdate/deathdate and the alternates. +* GEDCOM import and export adapted to load/save alternate birth and + death dates. The first "BIRT" and "DEAT" tags are the ones used + for the primary dates, the others are loaded as alternates. +* Added the ability to store web page addresses for each person. From + the edit person page, the "Go" button brings up a web browser to + display the page. +* Fixed a bug in the relationship calculator that caused a traceback + when the person selected was a direct ancestor of the active person. +* Added the suffix field to the alternate name entry on the Edit Person + form. \ No newline at end of file diff --git a/gramps2/README b/gramps2/README new file mode 100644 index 000000000..183651e75 --- /dev/null +++ b/gramps2/README @@ -0,0 +1,65 @@ +Please read the COPYING file first. +If building from source, also read the INSTALL file (at least through the +"SUPER-SHORT VERSION") before going further. + +Requirements +-------------------------------- +Python 1.5.2 or greater +Gnome 1.2 or greater +PyGnome 1.0.53 or greater + +If you are using python 1.5.2, you may also need PyXML 0.6.2 or +greater. Many distributions already provide this, but if your +installation does not have it, you can get it from +http://sourceforge.net/project/showfiles.php?group_id=6473 + +Documentation +--------------------------------- +Gramps documentation is supplied in the form of SGML files, which will be +installed in the GNOME help path(*). Recent versions of Nautilus and Galeon +can generate HTML on-the-fly documents from these. For more information on +building HTML files (including info about packages that do and do not work +with the documentation) see the INSTALL file. To generate HTML +documentation the following packages *MUST* installed: +* db2html >= 0.6.9 (jw >= 1.1) to convert the SGML -> HTML +* gnome-doc-tools-2-1 for the GNOME documentation style sheets +The former is part of the docbook-utils package, the latter can be found +from the developer section at gnome.org. (note that docbook-utils 0.6.10 is +buggy) + +One also needs png support for sgml, which should be a part of the +gnome-doc-tools package. The /etc/sgml/catalog file should +contain an entry pointing to PNG support. If configured properly, your +db2html should automatically look up and use the /etc/sgml/catalog file. + +Of course, current HTML documentation can also be found on the gramps website, +http://gramps.sourceforge.net/help.html + +(*) More precisely, they are installed in ${prefix}/share/gnome/help, where +${prefix} is given by the --prefix= option to configure. If this is +different from where your standard GNOME installation looks for help files +and documentation, then set your GNOMEDIR environment variable to the +${prefix} path before starting gramps. For example, if you are installing +gramps in /usr/local/, then type the following: +in tcsh: setenv GNOMEDIR /usr/local/ +in bash: GNOMEDIR=/usr/local/ ; export GNOMEDIR + + +Building on non-Linux systems: i18n support and GNU make +-------------------------------------------------------- + +Linux has libintl (GNU gettext) built-in the C library. Other systems +are likely to have libintl as a separate or optional library. Also, +other systems may have a different make utility. + +On those systems, like FreeBSD, you must tell configure where to find +the libintl library and the libintl.h include file: + +CPPFLAGS="-I/usr/local/include -L/usr/local/lib" ./configure --prefix=/usr/local + +Once you have done this, if make fails, use gmake (the name FreeBSD +gives to GNU make) instead. + +-------------------------------- +Donald Allingham +dallingham@users.sourceforge.net diff --git a/gramps2/TODO b/gramps2/TODO new file mode 100644 index 000000000..79168315c --- /dev/null +++ b/gramps2/TODO @@ -0,0 +1,31 @@ +* Allow for multiple notes. A tabbed interface would be really useful, + since there are no titles for notes. Not all objects would necessarily + need multiple notes. Determine which ones should and shouldn't. +* Drag and drop should display the icon we are dragging instead of just + the default icon. Nautilus does this very effectively, and GTK has + support for this. +* Provide an "import" of a gramps package. Not too difficult to do this, + since there is already a ReadTarFile class which will unpackage the + file. Needs have an interface built around it. +* Catch uncaught exceptions at the top level, notifiy the user, and + store the results in a file that can be emailed. Have the start of + this with the gramps.err file, but most users don't realize that + this file has been created. Some type of notification is needed. +* Speed up the reading of the database. The python XML routines are not + as fast as I would like, and it can take a minute or so to read a + large database. This is way too slow. +* Finish the generic load of revision control interfaces to allow a + revision control plugin system. Most of the work is already done. +* Extend the gramps package exporting to export to a ISO-9660 CD-ROM + image. Thumbnails would need to be exported for this as well, since + the CD-ROM would be read-only after burning. +* Disable the save buttons if gramps database is marked read-only. Disable + the adding of media objects as well, since this will cause gramps to + try to create a thumbnail in a readonly database. +* OpenOffice zip file is not handled very gracefully. Uses the "system" + call to generate the zip file using the hard coded path of /usr/bin/zip. + Python 2.0 provides a zip interface, so this may need to hold off until + the move is made to Python 2.0. +* Sort all lists +* Startup tips. +* And a whole lot more.... diff --git a/gramps2/aclocal.m4 b/gramps2/aclocal.m4 new file mode 100644 index 000000000..bcf9c02c5 --- /dev/null +++ b/gramps2/aclocal.m4 @@ -0,0 +1,939 @@ +# aclocal.m4 generated automatically by aclocal 1.6.3 -*- Autoconf -*- + +# Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002 +# Free Software Foundation, Inc. +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +# Do all the work for Automake. -*- Autoconf -*- + +# This macro actually does too much some checks are only needed if +# your package does certain things. But this isn't really a big deal. + +# Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002 +# Free Software Foundation, Inc. + +# 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, 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 8 + +# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be +# written in clear, in which case automake, when reading aclocal.m4, +# will think it sees a *use*, and therefore will trigger all it's +# C support machinery. Also note that it means that autoscan, seeing +# CC etc. in the Makefile, will ask for an AC_PROG_CC use... + + +AC_PREREQ([2.52]) + +# Autoconf 2.50 wants to disallow AM_ names. We explicitly allow +# the ones we care about. +m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl + +# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) +# AM_INIT_AUTOMAKE([OPTIONS]) +# ----------------------------------------------- +# The call with PACKAGE and VERSION arguments is the old style +# call (pre autoconf-2.50), which is being phased out. PACKAGE +# and VERSION should now be passed to AC_INIT and removed from +# the call to AM_INIT_AUTOMAKE. +# We support both call styles for the transition. After +# the next Automake release, Autoconf can make the AC_INIT +# arguments mandatory, and then we can depend on a new Autoconf +# release and drop the old call support. +AC_DEFUN([AM_INIT_AUTOMAKE], +[AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl + AC_REQUIRE([AC_PROG_INSTALL])dnl +# test to see if srcdir already configured +if test "`cd $srcdir && pwd`" != "`pwd`" && + test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) +fi + +# Define the identity of the package. +dnl Distinguish between old-style and new-style calls. +m4_ifval([$2], +[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl + AC_SUBST([PACKAGE], [$1])dnl + AC_SUBST([VERSION], [$2])], +[_AM_SET_OPTIONS([$1])dnl + AC_SUBST([PACKAGE], [AC_PACKAGE_TARNAME])dnl + AC_SUBST([VERSION], [AC_PACKAGE_VERSION])])dnl + +_AM_IF_OPTION([no-define],, +[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package]) + AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl + +# Some tools Automake needs. +AC_REQUIRE([AM_SANITY_CHECK])dnl +AC_REQUIRE([AC_ARG_PROGRAM])dnl +AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version}) +AM_MISSING_PROG(AUTOCONF, autoconf) +AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version}) +AM_MISSING_PROG(AUTOHEADER, autoheader) +AM_MISSING_PROG(MAKEINFO, makeinfo) +AM_MISSING_PROG(AMTAR, tar) +AM_PROG_INSTALL_SH +AM_PROG_INSTALL_STRIP +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([AC_PROG_MAKE_SET])dnl + +_AM_IF_OPTION([no-dependencies],, +[AC_PROVIDE_IFELSE([AC_PROG_][CC], + [_AM_DEPENDENCIES(CC)], + [define([AC_PROG_][CC], + defn([AC_PROG_][CC])[_AM_DEPENDENCIES(CC)])])dnl +AC_PROVIDE_IFELSE([AC_PROG_][CXX], + [_AM_DEPENDENCIES(CXX)], + [define([AC_PROG_][CXX], + defn([AC_PROG_][CXX])[_AM_DEPENDENCIES(CXX)])])dnl +]) +]) + +# Copyright 2002 Free Software Foundation, Inc. + +# 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, 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., 59 Temple Place - Suite 330, Boston, MA + +# AM_AUTOMAKE_VERSION(VERSION) +# ---------------------------- +# Automake X.Y traces this macro to ensure aclocal.m4 has been +# generated from the m4 files accompanying Automake X.Y. +AC_DEFUN([AM_AUTOMAKE_VERSION],[am__api_version="1.6"]) + +# AM_SET_CURRENT_AUTOMAKE_VERSION +# ------------------------------- +# Call AM_AUTOMAKE_VERSION so it can be traced. +# This function is AC_REQUIREd by AC_INIT_AUTOMAKE. +AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], + [AM_AUTOMAKE_VERSION([1.6.3])]) + +# Helper functions for option handling. -*- Autoconf -*- + +# Copyright 2001, 2002 Free Software Foundation, Inc. + +# 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, 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 2 + +# _AM_MANGLE_OPTION(NAME) +# ----------------------- +AC_DEFUN([_AM_MANGLE_OPTION], +[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) + +# _AM_SET_OPTION(NAME) +# ------------------------------ +# Set option NAME. Presently that only means defining a flag for this option. +AC_DEFUN([_AM_SET_OPTION], +[m4_define(_AM_MANGLE_OPTION([$1]), 1)]) + +# _AM_SET_OPTIONS(OPTIONS) +# ---------------------------------- +# OPTIONS is a space-separated list of Automake options. +AC_DEFUN([_AM_SET_OPTIONS], +[AC_FOREACH([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) + +# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) +# ------------------------------------------- +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +AC_DEFUN([_AM_IF_OPTION], +[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) + +# +# Check to make sure that the build environment is sane. +# + +# Copyright 1996, 1997, 2000, 2001 Free Software Foundation, Inc. + +# 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, 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 3 + +# AM_SANITY_CHECK +# --------------- +AC_DEFUN([AM_SANITY_CHECK], +[AC_MSG_CHECKING([whether build environment is sane]) +# Just in case +sleep 1 +echo timestamp > conftest.file +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null` + if test "$[*]" = "X"; then + # -L didn't work. + set X `ls -t $srcdir/configure conftest.file` + fi + rm -f conftest.file + if test "$[*]" != "X $srcdir/configure conftest.file" \ + && test "$[*]" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken +alias in your environment]) + fi + + test "$[2]" = conftest.file + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +AC_MSG_RESULT(yes)]) + +# -*- Autoconf -*- + + +# Copyright 1997, 1999, 2000, 2001 Free Software Foundation, Inc. + +# 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, 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 3 + +# AM_MISSING_PROG(NAME, PROGRAM) +# ------------------------------ +AC_DEFUN([AM_MISSING_PROG], +[AC_REQUIRE([AM_MISSING_HAS_RUN]) +$1=${$1-"${am_missing_run}$2"} +AC_SUBST($1)]) + + +# AM_MISSING_HAS_RUN +# ------------------ +# Define MISSING if not defined so far and test if it supports --run. +# If it does, set am_missing_run to use it, otherwise, to nothing. +AC_DEFUN([AM_MISSING_HAS_RUN], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing" +# Use eval to expand $SHELL +if eval "$MISSING --run true"; then + am_missing_run="$MISSING --run " +else + am_missing_run= + AC_MSG_WARN([`missing' script is too old or missing]) +fi +]) + +# AM_AUX_DIR_EXPAND + +# Copyright 2001 Free Software Foundation, Inc. + +# 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, 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets +# $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to +# `$srcdir', `$srcdir/..', or `$srcdir/../..'. +# +# Of course, Automake must honor this variable whenever it calls a +# tool from the auxiliary directory. The problem is that $srcdir (and +# therefore $ac_aux_dir as well) can be either absolute or relative, +# depending on how configure is run. This is pretty annoying, since +# it makes $ac_aux_dir quite unusable in subdirectories: in the top +# source directory, any form will work fine, but in subdirectories a +# relative path needs to be adjusted first. +# +# $ac_aux_dir/missing +# fails when called from a subdirectory if $ac_aux_dir is relative +# $top_srcdir/$ac_aux_dir/missing +# fails if $ac_aux_dir is absolute, +# fails when called from a subdirectory in a VPATH build with +# a relative $ac_aux_dir +# +# The reason of the latter failure is that $top_srcdir and $ac_aux_dir +# are both prefixed by $srcdir. In an in-source build this is usually +# harmless because $srcdir is `.', but things will broke when you +# start a VPATH build or use an absolute $srcdir. +# +# So we could use something similar to $top_srcdir/$ac_aux_dir/missing, +# iff we strip the leading $srcdir from $ac_aux_dir. That would be: +# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` +# and then we would define $MISSING as +# MISSING="\${SHELL} $am_aux_dir/missing" +# This will work as long as MISSING is not called from configure, because +# unfortunately $(top_srcdir) has no meaning in configure. +# However there are other variables, like CC, which are often used in +# configure, and could therefore not use this "fixed" $ac_aux_dir. +# +# Another solution, used here, is to always expand $ac_aux_dir to an +# absolute PATH. The drawback is that using absolute paths prevent a +# configured tree to be moved without reconfiguration. + +# Rely on autoconf to set up CDPATH properly. +AC_PREREQ([2.50]) + +AC_DEFUN([AM_AUX_DIR_EXPAND], [ +# expand $ac_aux_dir to an absolute path +am_aux_dir=`cd $ac_aux_dir && pwd` +]) + +# AM_PROG_INSTALL_SH +# ------------------ +# Define $install_sh. + +# Copyright 2001 Free Software Foundation, Inc. + +# 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, 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +AC_DEFUN([AM_PROG_INSTALL_SH], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +install_sh=${install_sh-"$am_aux_dir/install-sh"} +AC_SUBST(install_sh)]) + +# AM_PROG_INSTALL_STRIP + +# Copyright 2001 Free Software Foundation, Inc. + +# 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, 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# One issue with vendor `install' (even GNU) is that you can't +# specify the program used to strip binaries. This is especially +# annoying in cross-compiling environments, where the build's strip +# is unlikely to handle the host's binaries. +# Fortunately install-sh will honor a STRIPPROG variable, so we +# always use install-sh in `make install-strip', and initialize +# STRIPPROG with the value of the STRIP variable (set by the user). +AC_DEFUN([AM_PROG_INSTALL_STRIP], +[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +# Installed binaries are usually stripped using `strip' when the user +# run `make install-strip'. However `strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the `STRIP' environment variable to overrule this program. +dnl Don't test for $cross_compiling = yes, because it might be `maybe'. +if test "$cross_compiling" != no; then + AC_CHECK_TOOL([STRIP], [strip], :) +fi +INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s" +AC_SUBST([INSTALL_STRIP_PROGRAM])]) + +# serial 4 -*- Autoconf -*- + +# Copyright 1999, 2000, 2001 Free Software Foundation, Inc. + +# 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, 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + + +# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be +# written in clear, in which case automake, when reading aclocal.m4, +# will think it sees a *use*, and therefore will trigger all it's +# C support machinery. Also note that it means that autoscan, seeing +# CC etc. in the Makefile, will ask for an AC_PROG_CC use... + + + +# _AM_DEPENDENCIES(NAME) +# ---------------------- +# See how the compiler implements dependency checking. +# NAME is "CC", "CXX", "GCJ", or "OBJC". +# We try a few techniques and use that to set a single cache variable. +# +# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was +# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular +# dependency, and given that the user is not expected to run this macro, +# just rely on AC_PROG_CC. +AC_DEFUN([_AM_DEPENDENCIES], +[AC_REQUIRE([AM_SET_DEPDIR])dnl +AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl +AC_REQUIRE([AM_MAKE_INCLUDE])dnl +AC_REQUIRE([AM_DEP_TRACK])dnl + +ifelse([$1], CC, [depcc="$CC" am_compiler_list=], + [$1], CXX, [depcc="$CXX" am_compiler_list=], + [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'], + [$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'], + [depcc="$$1" am_compiler_list=]) + +AC_CACHE_CHECK([dependency style of $depcc], + [am_cv_$1_dependencies_compiler_type], +[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + + am_cv_$1_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` + fi + for depmode in $am_compiler_list; do + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + echo '#include "conftest.h"' > conftest.c + echo 'int i;' > conftest.h + echo "${am__include} ${am__quote}conftest.Po${am__quote}" > confmf + + case $depmode in + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + none) break ;; + esac + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. + if depmode=$depmode \ + source=conftest.c object=conftest.o \ + depfile=conftest.Po tmpdepfile=conftest.TPo \ + $SHELL ./depcomp $depcc -c conftest.c -o conftest.o >/dev/null 2>&1 && + grep conftest.h conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + am_cv_$1_dependencies_compiler_type=$depmode + break + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_$1_dependencies_compiler_type=none +fi +]) +AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) +]) + + +# AM_SET_DEPDIR +# ------------- +# Choose a directory name for dependency files. +# This macro is AC_REQUIREd in _AM_DEPENDENCIES +AC_DEFUN([AM_SET_DEPDIR], +[rm -f .deps 2>/dev/null +mkdir .deps 2>/dev/null +if test -d .deps; then + DEPDIR=.deps +else + # MS-DOS does not allow filenames that begin with a dot. + DEPDIR=_deps +fi +rmdir .deps 2>/dev/null +AC_SUBST([DEPDIR]) +]) + + +# AM_DEP_TRACK +# ------------ +AC_DEFUN([AM_DEP_TRACK], +[AC_ARG_ENABLE(dependency-tracking, +[ --disable-dependency-tracking Speeds up one-time builds + --enable-dependency-tracking Do not reject slow dependency extractors]) +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' +fi +AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) +AC_SUBST([AMDEPBACKSLASH]) +]) + +# Generate code to set up dependency tracking. -*- Autoconf -*- + +# Copyright 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + +# 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, 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +#serial 2 + +# _AM_OUTPUT_DEPENDENCY_COMMANDS +# ------------------------------ +AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], +[for mf in $CONFIG_FILES; do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named `Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # So let's grep whole file. + if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then + dirpart=`AS_DIRNAME("$mf")` + else + continue + fi + grep '^DEP_FILES *= *[[^ @%:@]]' < "$mf" > /dev/null || continue + # Extract the definition of DEP_FILES from the Makefile without + # running `make'. + DEPDIR=`sed -n -e '/^DEPDIR = / s///p' < "$mf"` + test -z "$DEPDIR" && continue + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n -e '/^U = / s///p' < "$mf"` + test -d "$dirpart/$DEPDIR" || mkdir "$dirpart/$DEPDIR" + # We invoke sed twice because it is the simplest approach to + # changing $(DEPDIR) to its actual value in the expansion. + for file in `sed -n -e ' + /^DEP_FILES = .*\\\\$/ { + s/^DEP_FILES = // + :loop + s/\\\\$// + p + n + /\\\\$/ b loop + p + } + /^DEP_FILES = / s/^DEP_FILES = //p' < "$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`AS_DIRNAME(["$file"])` + AS_MKDIR_P([$dirpart/$fdir]) + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done +done +])# _AM_OUTPUT_DEPENDENCY_COMMANDS + + +# AM_OUTPUT_DEPENDENCY_COMMANDS +# ----------------------------- +# This macro should only be invoked once -- use via AC_REQUIRE. +# +# This code is only required when automatic dependency tracking +# is enabled. FIXME. This creates each `.P' file that we will +# need in order to bootstrap the dependency handling code. +AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], +[AC_CONFIG_COMMANDS([depfiles], + [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], + [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) +]) + +# Copyright 2001 Free Software Foundation, Inc. -*- Autoconf -*- + +# 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, 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 2 + +# AM_MAKE_INCLUDE() +# ----------------- +# Check to see how make treats includes. +AC_DEFUN([AM_MAKE_INCLUDE], +[am_make=${MAKE-make} +cat > confinc << 'END' +doit: + @echo done +END +# If we don't find an include directive, just comment out the code. +AC_MSG_CHECKING([for style of include used by $am_make]) +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# We grep out `Entering directory' and `Leaving directory' +# messages which can occur if `w' ends up in MAKEFLAGS. +# In particular we don't look at `^make:' because GNU make might +# be invoked under some other name (usually "gmake"), in which +# case it prints its new name instead of `make'. +if test "`$am_make -s -f confmf 2> /dev/null | fgrep -v 'ing directory'`" = "done"; then + am__include=include + am__quote= + _am_result=GNU +fi +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then + am__include=.include + am__quote="\"" + _am_result=BSD + fi +fi +AC_SUBST(am__include) +AC_SUBST(am__quote) +AC_MSG_RESULT($_am_result) +rm -f confinc confmf +]) + +# AM_CONDITIONAL -*- Autoconf -*- + +# Copyright 1997, 2000, 2001 Free Software Foundation, Inc. + +# 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, 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# serial 5 + +AC_PREREQ(2.52) + +# AM_CONDITIONAL(NAME, SHELL-CONDITION) +# ------------------------------------- +# Define a conditional. +AC_DEFUN([AM_CONDITIONAL], +[ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], + [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl +AC_SUBST([$1_TRUE]) +AC_SUBST([$1_FALSE]) +if $2; then + $1_TRUE= + $1_FALSE='#' +else + $1_TRUE='#' + $1_FALSE= +fi +AC_CONFIG_COMMANDS_PRE( +[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then + AC_MSG_ERROR([conditional \"$1\" was never defined. +Usually this means the macro was only invoked conditionally.]) +fi])]) + + +# Copyright 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + +# 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, 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# AM_PATH_PYTHON([MINIMUM-VERSION]) + +# Adds support for distributing Python modules and packages. To +# install modules, copy them to $(pythondir), using the python_PYTHON +# automake variable. To install a package with the same name as the +# automake package, install to $(pkgpythondir), or use the +# pkgpython_PYTHON automake variable. + +# The variables $(pyexecdir) and $(pkgpyexecdir) are provided as +# locations to install python extension modules (shared libraries). +# Another macro is required to find the appropriate flags to compile +# extension modules. + +# If your package is configured with a different prefix to python, +# users will have to add the install directory to the PYTHONPATH +# environment variable, or create a .pth file (see the python +# documentation for details). + +# If the MINIUMUM-VERSION argument is passed, AM_PATH_PYTHON will +# cause an error if the version of python installed on the system +# doesn't meet the requirement. MINIMUM-VERSION should consist of +# numbers and dots only. + +AC_DEFUN([AM_PATH_PYTHON], + [ + dnl Find a Python interpreter. Python versions prior to 1.5 are not + dnl supported because the default installation locations changed from + dnl $prefix/lib/site-python in 1.4 to $prefix/lib/python1.5/site-packages + dnl in 1.5. + m4_define([_AM_PYTHON_INTERPRETER_LIST], + [python python2 python2.2 python2.1 python2.0 python1.6 python1.5]) + + m4_if([$1],[],[ + dnl No version check is needed. + # Find any Python interpreter. + AC_PATH_PROG([PYTHON], _AM_PYTHON_INTERPRETER_LIST)],[ + dnl A version check is needed. + if test -n "$PYTHON"; then + # If the user set $PYTHON, use it and don't search something else. + AC_MSG_CHECKING([whether $PYTHON version >= $1]) + AM_PYTHON_CHECK_VERSION([$PYTHON], [$1], + [AC_MSG_RESULT(yes)], + [AC_MSG_ERROR(too old)]) + else + # Otherwise, try each interpreter until we find one that satisfies + # VERSION. + AC_CACHE_CHECK([for a Python interpreter with version >= $1], + [am_cv_pathless_PYTHON],[ + for am_cv_pathless_PYTHON in _AM_PYTHON_INTERPRETER_LIST : ; do + if test "$am_cv_pathless_PYTHON" = : ; then + AC_MSG_ERROR([no suitable Python interpreter found]) + fi + AM_PYTHON_CHECK_VERSION([$am_cv_pathless_PYTHON], [$1], [break]) + done]) + # Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON. + AC_PATH_PROG([PYTHON], [$am_cv_pathless_PYTHON]) + fi + ]) + + dnl Query Python for its version number. Getting [:3] seems to be + dnl the best way to do this; it's what "site.py" does in the standard + dnl library. + + AC_CACHE_CHECK([for $am_cv_pathless_PYTHON version], [am_cv_python_version], + [am_cv_python_version=`$PYTHON -c "import sys; print sys.version[[:3]]"`]) + AC_SUBST([PYTHON_VERSION], [$am_cv_python_version]) + + dnl Use the values of $prefix and $exec_prefix for the corresponding + dnl values of PYTHON_PREFIX and PYTHON_EXEC_PREFIX. These are made + dnl distinct variables so they can be overridden if need be. However, + dnl general consensus is that you shouldn't need this ability. + + AC_SUBST([PYTHON_PREFIX], ['${prefix}']) + AC_SUBST([PYTHON_EXEC_PREFIX], ['${exec_prefix}']) + + dnl At times (like when building shared libraries) you may want + dnl to know which OS platform Python thinks this is. + + AC_CACHE_CHECK([for $am_cv_pathless_PYTHON platform], + [am_cv_python_platform], + [am_cv_python_platform=`$PYTHON -c "import sys; print sys.platform"`]) + AC_SUBST([PYTHON_PLATFORM], [$am_cv_python_platform]) + + + dnl Set up 4 directories: + + dnl pythondir -- where to install python scripts. This is the + dnl site-packages directory, not the python standard library + dnl directory like in previous automake betas. This behaviour + dnl is more consistent with lispdir.m4 for example. + dnl + dnl Also, if the package prefix isn't the same as python's prefix, + dnl then the old $(pythondir) was pretty useless. + + AC_SUBST([pythondir], + [$PYTHON_PREFIX"/lib/python"$PYTHON_VERSION/site-packages]) + + dnl pkgpythondir -- $PACKAGE directory under pythondir. Was + dnl PYTHON_SITE_PACKAGE in previous betas, but this naming is + dnl more consistent with the rest of automake. + dnl Maybe this should be put in python.am? + + AC_SUBST([pkgpythondir], [\${pythondir}/$PACKAGE]) + + dnl pyexecdir -- directory for installing python extension modules + dnl (shared libraries) Was PYTHON_SITE_EXEC in previous betas. + + AC_SUBST([pyexecdir], + [${PYTHON_EXEC_PREFIX}/lib/python${PYTHON_VERSION}/site-packages]) + + dnl pkgpyexecdir -- $(pyexecdir)/$(PACKAGE) + dnl Maybe this should be put in python.am? + + AC_SUBST([pkgpyexecdir], [\${pyexecdir}/$PACKAGE]) +]) + + +# AM_PYTHON_CHECK_VERSION(PROG, VERSION, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) +# --------------------------------------------------------------------------- +# Run ACTION-IF-TRUE if the Python interpreter PROG has version >= VERSION. +# Run ACTION-IF-FALSE otherwise. +# This test uses sys.hexversion instead of the string equivalant (first +# word of sys.version), in order to cope with versions such as 2.2c1. +# hexversion has been introduced in Python 1.5.2; it's probably not +# worth to support older versions (1.5.1 was released on October 31, 1998). +AC_DEFUN([AM_PYTHON_CHECK_VERSION], + [prog="import sys, string +# split strings by '.' and convert to numeric. Append some zeros +# because we need at least 4 digits for the hex conversion. +minver = map(int, string.split('$2', '.')) + [[0, 0, 0]] +minverhex = 0 +for i in xrange(0, 4): minverhex = (minverhex << 8) + minver[[i]] +sys.exit(sys.hexversion < minverhex)" + AS_IF([AM_RUN_LOG([$1 -c "$prog"])], [$3], [$4])]) + +# Copyright 2001 Free Software Foundation, Inc. -*- Autoconf -*- + +# 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, 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# AM_RUN_LOG(COMMAND) +# ------------------- +# Run COMMAND, save the exit status in ac_status, and log it. +# (This has been adapted from Autoconf's _AC_RUN_LOG macro.) +AC_DEFUN([AM_RUN_LOG], +[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD + ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + (exit $ac_status); }]) + diff --git a/gramps2/autogen.sh b/gramps2/autogen.sh new file mode 100755 index 000000000..c34847ba0 --- /dev/null +++ b/gramps2/autogen.sh @@ -0,0 +1,96 @@ +#!/bin/sh +# Run this to generate all the initial makefiles, etc. +# shamelessly borrowed and hacked from the Galeon source distribution + +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. + +PKG_NAME="gramps" + +(test -f $srcdir/configure.in \ + && test -f $srcdir/src/gramps_main.py) || { + echo -n "**Error**: Directory "\`$srcdir\'" does not look like the" + echo " top-level $PKG_NAME directory" + exit 1 +} + +DIE=0 + + +(autoconf --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`autoconf' installed to compile $PKG_NAME." + echo "Download the appropriate package for your distribution," + echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/" + DIE=1 +} + +(automake --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`automake' installed to compile $PKG_NAME." + echo "Get ftp://ftp.gnu.org/pub/gnu/automake/automake-1.6.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 + NO_AUTOMAKE=yes +} + + +# if no automake, don't bother testing for aclocal +test -n "$NO_AUTOMAKE" || (aclocal --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: Missing \`aclocal'. The version of \`automake'" + echo "installed doesn't appear recent enough." + echo "Get ftp://ftp.gnu.org/pub/gnu/automake/automake-1.6.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 +} + +if test "$DIE" -eq 1; then + exit 1 +fi + +if test -z "$*"; then + echo "**Warning**: I am going to run \`configure' with no arguments." + echo "If you wish to pass any to it, please specify them on the" + echo \`$0\'" command line." + echo +fi + +case $CC in +xlc ) + am_opt=--include-deps;; +esac + + +dr=$srcdir +echo processing $dr +( cd $dr + aclocalinclude="$ACLOCAL_FLAGS" + echo "Running aclocal $aclocalinclude ..." + aclocal $aclocalinclude || { + echo + echo "**Error**: aclocal failed. This may mean that you have not" + echo "installed all of the packages you need, or you may need to" + echo "set ACLOCAL_FLAGS to include \"-I \$prefix/share/aclocal\"" + echo "for the prefix where you installed the packages whose" + echo "macros were not found" + exit 1 + } + + echo "Running automake --gnu $am_opt ..." + automake --add-missing --gnu $am_opt || + { echo "**Error**: automake failed."; exit 1; } + echo "Running autoconf ..." + autoconf || { echo "**Error**: autoconf failed."; exit 1; } +) || exit 1 + + echo "Setting py-compile script executable ..." + chmod 755 py-compile + +if test x$NOCONFIGURE = x; then + echo Running $srcdir/configure $conf_flags "$@" ... + $srcdir/configure $conf_flags "$@" \ + && echo Now type \`make\' to compile $PKG_NAME || exit 1 +else + echo Skipping configure process. +fi diff --git a/gramps2/configure b/gramps2/configure new file mode 100755 index 000000000..5253f28be --- /dev/null +++ b/gramps2/configure @@ -0,0 +1,4941 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.53. +# +# Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002 +# Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + + +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi + +# NLS nuisances. +# Support unset when possible. +if (FOO=FOO; unset FOO) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + +(set +x; test -n "`(LANG=C; export LANG) 2>&1`") && + { $as_unset LANG || test "${LANG+set}" != set; } || + { LANG=C; export LANG; } +(set +x; test -n "`(LC_ALL=C; export LC_ALL) 2>&1`") && + { $as_unset LC_ALL || test "${LC_ALL+set}" != set; } || + { LC_ALL=C; export LC_ALL; } +(set +x; test -n "`(LC_TIME=C; export LC_TIME) 2>&1`") && + { $as_unset LC_TIME || test "${LC_TIME+set}" != set; } || + { LC_TIME=C; export LC_TIME; } +(set +x; test -n "`(LC_CTYPE=C; export LC_CTYPE) 2>&1`") && + { $as_unset LC_CTYPE || test "${LC_CTYPE+set}" != set; } || + { LC_CTYPE=C; export LC_CTYPE; } +(set +x; test -n "`(LANGUAGE=C; export LANGUAGE) 2>&1`") && + { $as_unset LANGUAGE || test "${LANGUAGE+set}" != set; } || + { LANGUAGE=C; export LANGUAGE; } +(set +x; test -n "`(LC_COLLATE=C; export LC_COLLATE) 2>&1`") && + { $as_unset LC_COLLATE || test "${LC_COLLATE+set}" != set; } || + { LC_COLLATE=C; export LC_COLLATE; } +(set +x; test -n "`(LC_NUMERIC=C; export LC_NUMERIC) 2>&1`") && + { $as_unset LC_NUMERIC || test "${LC_NUMERIC+set}" != set; } || + { LC_NUMERIC=C; export LC_NUMERIC; } +(set +x; test -n "`(LC_MESSAGES=C; export LC_MESSAGES) 2>&1`") && + { $as_unset LC_MESSAGES || test "${LC_MESSAGES+set}" != set; } || + { LC_MESSAGES=C; export LC_MESSAGES; } + + +# Name of the executable. +as_me=`(basename "$0") 2>/dev/null || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conftest.sh + echo "exit 0" >>conftest.sh + chmod +x conftest.sh + if (PATH=".;."; conftest.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conftest.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2 + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="sed y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="sed y%*+%pp%;s%[^_$as_cr_alnum]%_%g" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH || test "${CDPATH+set}" != set || { CDPATH=$PATH_SEPARATOR; export CDPATH; } + + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +exec 6>&1 + +# +# Initializations. +# +ac_default_prefix=/usr/local +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Maximum number of lines to put in a shell here document. +# This variable seems obsolete. It should probably be removed, and +# only ac_max_sed_lines should be used. +: ${ac_max_here_lines=38} + +# Identity of this package. +PACKAGE_NAME= +PACKAGE_TARNAME= +PACKAGE_VERSION= +PACKAGE_STRING= +PACKAGE_BUGREPORT= + +ac_unique_file="src/gramps.py" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_SYS_STAT_H +# include +#endif +#if STDC_HEADERS +# include +# include +#else +# if HAVE_STDLIB_H +# include +# endif +#endif +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include +# endif +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_INTTYPES_H +# include +#else +# if HAVE_STDINT_H +# include +# endif +#endif +#if HAVE_UNISTD_H +# include +#endif" + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +ac_prev= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_option in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + eval "enable_$ac_feature=no" ;; + + -enable-* | --enable-*) + ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "enable_$ac_feature='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package| sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "with_$ac_package='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/-/_/g'` + eval "with_$ac_package=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) { echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` + eval "$ac_envvar='$ac_optarg'" + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +# Be sure to have absolute paths. +for ac_var in exec_prefix prefix +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* | NONE | '' ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# Be sure to have absolute paths. +for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ + localstatedir libdir includedir oldincludedir infodir mandir +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_confdir=`(dirname "$0") 2>/dev/null || +$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$0" : 'X\(//\)[^/]' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$0" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2 + { (exit 1); exit 1; }; } + else + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } + fi +fi +srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` +ac_env_build_alias_set=${build_alias+set} +ac_env_build_alias_value=$build_alias +ac_cv_env_build_alias_set=${build_alias+set} +ac_cv_env_build_alias_value=$build_alias +ac_env_host_alias_set=${host_alias+set} +ac_env_host_alias_value=$host_alias +ac_cv_env_host_alias_set=${host_alias+set} +ac_cv_env_host_alias_value=$host_alias +ac_env_target_alias_set=${target_alias+set} +ac_env_target_alias_value=$target_alias +ac_cv_env_target_alias_set=${target_alias+set} +ac_cv_env_target_alias_value=$target_alias +ac_env_CC_set=${CC+set} +ac_env_CC_value=$CC +ac_cv_env_CC_set=${CC+set} +ac_cv_env_CC_value=$CC +ac_env_CFLAGS_set=${CFLAGS+set} +ac_env_CFLAGS_value=$CFLAGS +ac_cv_env_CFLAGS_set=${CFLAGS+set} +ac_cv_env_CFLAGS_value=$CFLAGS +ac_env_LDFLAGS_set=${LDFLAGS+set} +ac_env_LDFLAGS_value=$LDFLAGS +ac_cv_env_LDFLAGS_set=${LDFLAGS+set} +ac_cv_env_LDFLAGS_value=$LDFLAGS +ac_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_env_CPPFLAGS_value=$CPPFLAGS +ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_cv_env_CPPFLAGS_value=$CPPFLAGS +ac_env_CPP_set=${CPP+set} +ac_env_CPP_value=$CPP +ac_cv_env_CPP_set=${CPP+set} +ac_cv_env_CPP_value=$CPP + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures this package to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +_ACEOF + + cat <<_ACEOF +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data [PREFIX/share] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --infodir=DIR info documentation [PREFIX/info] + --mandir=DIR man documentation [PREFIX/man] +_ACEOF + + cat <<\_ACEOF + +Program names: + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM run sed PROGRAM on installed program names +_ACEOF +fi + +if test -n "$ac_init_help"; then + + cat <<\_ACEOF + +Optional Features: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --disable-dependency-tracking Speeds up one-time builds + --enable-dependency-tracking Do not reject slow dependency extractors + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + CPPFLAGS C/C++ preprocessor flags, e.g. -I if you have + headers in a nonstandard directory + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +_ACEOF +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + ac_popdir=`pwd` + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d $ac_dir || continue + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac +# Don't blindly perform a `cd "$ac_dir"/$ac_foo && pwd` since $ac_foo can be +# absolute. +ac_abs_builddir=`cd "$ac_dir" && cd $ac_builddir && pwd` +ac_abs_top_builddir=`cd "$ac_dir" && cd $ac_top_builddir && pwd` +ac_abs_srcdir=`cd "$ac_dir" && cd $ac_srcdir && pwd` +ac_abs_top_srcdir=`cd "$ac_dir" && cd $ac_top_srcdir && pwd` + + cd $ac_dir + # Check for guested configure; otherwise get Cygnus style configure. + if test -f $ac_srcdir/configure.gnu; then + echo + $SHELL $ac_srcdir/configure.gnu --help=recursive + elif test -f $ac_srcdir/configure; then + echo + $SHELL $ac_srcdir/configure --help=recursive + elif test -f $ac_srcdir/configure.ac || + test -f $ac_srcdir/configure.in; then + echo + $ac_configure --help + else + echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi + cd $ac_popdir + done +fi + +test -n "$ac_init_help" && exit 0 +if $ac_init_version; then + cat <<\_ACEOF + +Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002 +Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit 0 +fi +exec 5>config.log +cat >&5 <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by $as_me, which was +generated by GNU Autoconf 2.53. Invocation command line was + + $ $0 $@ + +_ACEOF +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +hostinfo = `(hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + echo "PATH: $as_dir" +done + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Also quote any args containing shell meta-characters. +ac_configure_args= +ac_sep= +for ac_arg +do + case $ac_arg in + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n ) continue ;; + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + continue ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" + ac_sep=" " ;; + esac + # Get rid of the leading space. +done + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Be sure not to use single quotes in there, as some shells, +# such as our DU 5.0 friend, will then `close' the trap. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +{ + (set) 2>&1 | + case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in + *ac_space=\ *) + sed -n \ + "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" + ;; + *) + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} + echo + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + sed "/^$/d" confdefs.h + echo + fi + test "$ac_signal" != 0 && + echo "$as_me: caught signal $ac_signal" + echo "$as_me: exit $exit_status" + } >&5 + rm -f core core.* *.core && + rm -rf conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status + ' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo >confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 +echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { echo "$as_me:$LINENO: loading cache $cache_file" >&5 +echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . $cache_file;; + *) . ./$cache_file;; + esac + fi +else + { echo "$as_me:$LINENO: creating cache $cache_file" >&5 +echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in `(set) 2>&1 | + sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val="\$ac_cv_env_${ac_var}_value" + eval ac_new_val="\$ac_env_${ac_var}_value" + case $ac_old_set,$ac_new_set in + set,) + { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 +echo "$as_me: former value: $ac_old_val" >&2;} + { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 +echo "$as_me: current value: $ac_new_val" >&2;} + ac_cache_corrupted=: + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + + + + + + + + + + + + + + +am__api_version="1.6" +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f $ac_dir/shtool; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5 +echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;} + { (exit 1); exit 1; }; } +fi +ac_config_guess="$SHELL $ac_aux_dir/config.guess" +ac_config_sub="$SHELL $ac_aux_dir/config.sub" +ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure. + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# ./install, which can be erroneously created by make from ./install.sh. +echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 +echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6 +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in + ./ | .// | /cC/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + done + done + ;; +esac +done + + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL=$ac_install_sh + fi +fi +echo "$as_me:$LINENO: result: $INSTALL" >&5 +echo "${ECHO_T}$INSTALL" >&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +echo "$as_me:$LINENO: checking whether build environment is sane" >&5 +echo $ECHO_N "checking whether build environment is sane... $ECHO_C" >&6 +# Just in case +sleep 1 +echo timestamp > conftest.file +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. + set X `ls -t $srcdir/configure conftest.file` + fi + rm -f conftest.file + if test "$*" != "X $srcdir/configure conftest.file" \ + && test "$*" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + { { echo "$as_me:$LINENO: error: ls -t appears to fail. Make sure there is not a broken +alias in your environment" >&5 +echo "$as_me: error: ls -t appears to fail. Make sure there is not a broken +alias in your environment" >&2;} + { (exit 1); exit 1; }; } + fi + + test "$2" = conftest.file + ) +then + # Ok. + : +else + { { echo "$as_me:$LINENO: error: newly created file is older than distributed files! +Check your system clock" >&5 +echo "$as_me: error: newly created file is older than distributed files! +Check your system clock" >&2;} + { (exit 1); exit 1; }; } +fi +echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +test "$program_prefix" != NONE && + program_transform_name="s,^,$program_prefix,;$program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s,\$,$program_suffix,;$program_transform_name" +# Double any \ or $. echo might interpret backslashes. +# By default was `s,x,x', remove it if useless. +cat <<\_ACEOF >conftest.sed +s/[\\$]/&&/g;s/;s,x,x,$// +_ACEOF +program_transform_name=`echo $program_transform_name | sed -f conftest.sed` +rm conftest.sed + + +# expand $ac_aux_dir to an absolute path +am_aux_dir=`cd $ac_aux_dir && pwd` + +test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing" +# Use eval to expand $SHELL +if eval "$MISSING --run true"; then + am_missing_run="$MISSING --run " +else + am_missing_run= + { echo "$as_me:$LINENO: WARNING: \`missing' script is too old or missing" >&5 +echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;} +fi + +for ac_prog in gawk mawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_AWK+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AWK="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + echo "$as_me:$LINENO: result: $AWK" >&5 +echo "${ECHO_T}$AWK" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$AWK" && break +done + +echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \${MAKE}" >&5 +echo $ECHO_N "checking whether ${MAKE-make} sets \${MAKE}... $ECHO_C" >&6 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y,./+-,__p_,'` +if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.make <<\_ACEOF +all: + @echo 'ac_maketemp="${MAKE}"' +_ACEOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftest.make 2>/dev/null | grep temp=` +if test -n "$ac_maketemp"; then + eval ac_cv_prog_make_${ac_make}_set=yes +else + eval ac_cv_prog_make_${ac_make}_set=no +fi +rm -f conftest.make +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + SET_MAKE= +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + SET_MAKE="MAKE=${MAKE-make}" +fi + + # test to see if srcdir already configured +if test "`cd $srcdir && pwd`" != "`pwd`" && + test -f $srcdir/config.status; then + { { echo "$as_me:$LINENO: error: source directory already configured; run \"make distclean\" there first" >&5 +echo "$as_me: error: source directory already configured; run \"make distclean\" there first" >&2;} + { (exit 1); exit 1; }; } +fi + +# Define the identity of the package. + PACKAGE=gramps + VERSION=0.9.0pre1 + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE "$PACKAGE" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define VERSION "$VERSION" +_ACEOF + +# Some tools Automake needs. + +ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} + + +AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} + + +AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} + + +AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} + + +MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} + + +AMTAR=${AMTAR-"${am_missing_run}tar"} + +install_sh=${install_sh-"$am_aux_dir/install-sh"} + +# Installed binaries are usually stripped using `strip' when the user +# run `make install-strip'. However `strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the `STRIP' environment variable to overrule this program. +if test "$cross_compiling" != no; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_STRIP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + echo "$as_me:$LINENO: result: $STRIP" >&5 +echo "${ECHO_T}$STRIP" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_STRIP="strip" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_prog_ac_ct_STRIP" && ac_cv_prog_ac_ct_STRIP=":" +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + echo "$as_me:$LINENO: result: $ac_ct_STRIP" >&5 +echo "${ECHO_T}$ac_ct_STRIP" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + STRIP=$ac_ct_STRIP +else + STRIP="$ac_cv_prog_STRIP" +fi + +fi +INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s" + +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. + + + +RELEASE=rc4 + +VERSIONSTRING=$VERSION +if test x"$RELEASE" != "x" +then + VERSIONSTRING="$VERSION-$RELEASE" +fi + + + + + + +# Extract the first word of "msgfmt", so it can be a program name with args. +set dummy msgfmt; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_path_MSGFMT+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $MSGFMT in + [\\/]* | ?:[\\/]*) + ac_cv_path_MSGFMT="$MSGFMT" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_MSGFMT="$as_dir/$ac_word$ac_exec_ext" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + ;; +esac +fi +MSGFMT=$ac_cv_path_MSGFMT + +if test -n "$MSGFMT"; then + echo "$as_me:$LINENO: result: $MSGFMT" >&5 +echo "${ECHO_T}$MSGFMT" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + + +LANGUAGES="sv de fr es it pt_BR ru da_DK cs" + + +DISTLANGS= +POFILES= +MOFILES= +for lang in $LANGUAGES; do + POFILES="$POFILES $lang.po" + MOFILES="$MOFILES $lang.mo" +done + + + + + + + + if test -n "$PYTHON"; then + # If the user set $PYTHON, use it and don't search something else. + echo "$as_me:$LINENO: checking whether $PYTHON version >= 2.2" >&5 +echo $ECHO_N "checking whether $PYTHON version >= 2.2... $ECHO_C" >&6 + prog="import sys, string +# split strings by '.' and convert to numeric. Append some zeros +# because we need at least 4 digits for the hex conversion. +minver = map(int, string.split('2.2', '.')) + [0, 0, 0] +minverhex = 0 +for i in xrange(0, 4): minverhex = (minverhex << 8) + minver[i] +sys.exit(sys.hexversion < minverhex)" + if { echo "$as_me:$LINENO: $PYTHON -c "$prog"" >&5 + ($PYTHON -c "$prog") >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else + { { echo "$as_me:$LINENO: error: too old" >&5 +echo "$as_me: error: too old" >&2;} + { (exit 1); exit 1; }; } +fi + + else + # Otherwise, try each interpreter until we find one that satisfies + # VERSION. + echo "$as_me:$LINENO: checking for a Python interpreter with version >= 2.2" >&5 +echo $ECHO_N "checking for a Python interpreter with version >= 2.2... $ECHO_C" >&6 +if test "${am_cv_pathless_PYTHON+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + + for am_cv_pathless_PYTHON in python python2 python2.2 python2.1 python2.0 python1.6 python1.5 : ; do + if test "$am_cv_pathless_PYTHON" = : ; then + { { echo "$as_me:$LINENO: error: no suitable Python interpreter found" >&5 +echo "$as_me: error: no suitable Python interpreter found" >&2;} + { (exit 1); exit 1; }; } + fi + prog="import sys, string +# split strings by '.' and convert to numeric. Append some zeros +# because we need at least 4 digits for the hex conversion. +minver = map(int, string.split('2.2', '.')) + [0, 0, 0] +minverhex = 0 +for i in xrange(0, 4): minverhex = (minverhex << 8) + minver[i] +sys.exit(sys.hexversion < minverhex)" + if { echo "$as_me:$LINENO: $am_cv_pathless_PYTHON -c "$prog"" >&5 + ($am_cv_pathless_PYTHON -c "$prog") >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + break +fi + + done +fi +echo "$as_me:$LINENO: result: $am_cv_pathless_PYTHON" >&5 +echo "${ECHO_T}$am_cv_pathless_PYTHON" >&6 + # Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON. + # Extract the first word of "$am_cv_pathless_PYTHON", so it can be a program name with args. +set dummy $am_cv_pathless_PYTHON; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_path_PYTHON+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $PYTHON in + [\\/]* | ?:[\\/]*) + ac_cv_path_PYTHON="$PYTHON" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_PYTHON="$as_dir/$ac_word$ac_exec_ext" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + ;; +esac +fi +PYTHON=$ac_cv_path_PYTHON + +if test -n "$PYTHON"; then + echo "$as_me:$LINENO: result: $PYTHON" >&5 +echo "${ECHO_T}$PYTHON" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + fi + + + + echo "$as_me:$LINENO: checking for $am_cv_pathless_PYTHON version" >&5 +echo $ECHO_N "checking for $am_cv_pathless_PYTHON version... $ECHO_C" >&6 +if test "${am_cv_python_version+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + am_cv_python_version=`$PYTHON -c "import sys; print sys.version[:3]"` +fi +echo "$as_me:$LINENO: result: $am_cv_python_version" >&5 +echo "${ECHO_T}$am_cv_python_version" >&6 + PYTHON_VERSION=$am_cv_python_version + + + + PYTHON_PREFIX='${prefix}' + + PYTHON_EXEC_PREFIX='${exec_prefix}' + + + + echo "$as_me:$LINENO: checking for $am_cv_pathless_PYTHON platform" >&5 +echo $ECHO_N "checking for $am_cv_pathless_PYTHON platform... $ECHO_C" >&6 +if test "${am_cv_python_platform+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + am_cv_python_platform=`$PYTHON -c "import sys; print sys.platform"` +fi +echo "$as_me:$LINENO: result: $am_cv_python_platform" >&5 +echo "${ECHO_T}$am_cv_python_platform" >&6 + PYTHON_PLATFORM=$am_cv_python_platform + + + + + + pythondir=$PYTHON_PREFIX"/lib/python"$PYTHON_VERSION/site-packages + + + + pkgpythondir=\${pythondir}/$PACKAGE + + + + pyexecdir=${PYTHON_EXEC_PREFIX}/lib/python${PYTHON_VERSION}/site-packages + + + + pkgpyexecdir=\${pyexecdir}/$PACKAGE + + + +pythondir=\${prefix}/share +pyexecdir=\${prefix}/share +pkgpythondir=\${prefix}/share/\${PACKAGE} +pkgpyexecdir=\${prefix}/share/\${PACKAGE} + +# Extract the first word of "sh", so it can be a program name with args. +set dummy sh; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_path_BINSH+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $BINSH in + [\\/]* | ?:[\\/]*) + ac_cv_path_BINSH="$BINSH" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_BINSH="$as_dir/$ac_word$ac_exec_ext" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + ;; +esac +fi +BINSH=$ac_cv_path_BINSH + +if test -n "$BINSH"; then + echo "$as_me:$LINENO: result: $BINSH" >&5 +echo "${ECHO_T}$BINSH" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + +PYTHON_VERSION=`$PYTHON -c "import sys; print sys.version[:3]"` + +if test "$PYTHON_VERSION" != "2.2" +then + # Extract the first word of "python2.2", so it can be a program name with args. +set dummy python2.2; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_path_PYTHON22+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $PYTHON22 in + [\\/]* | ?:[\\/]*) + ac_cv_path_PYTHON22="$PYTHON22" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_PYTHON22="$as_dir/$ac_word$ac_exec_ext" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + ;; +esac +fi +PYTHON22=$ac_cv_path_PYTHON22 + +if test -n "$PYTHON22"; then + echo "$as_me:$LINENO: result: $PYTHON22" >&5 +echo "${ECHO_T}$PYTHON22" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +else + PYTHON22=$PYTHON +fi + +# Extract the first word of "zip", so it can be a program name with args. +set dummy zip; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_path_ZIP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $ZIP in + [\\/]* | ?:[\\/]*) + ac_cv_path_ZIP="$ZIP" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_ZIP="$as_dir/$ac_word$ac_exec_ext" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + ;; +esac +fi +ZIP=$ac_cv_path_ZIP + +if test -n "$ZIP"; then + echo "$as_me:$LINENO: result: $ZIP" >&5 +echo "${ECHO_T}$ZIP" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# ./install, which can be erroneously created by make from ./install.sh. +echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 +echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6 +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in + ./ | .// | /cC/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + done + done + ;; +esac +done + + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL=$ac_install_sh + fi +fi +echo "$as_me:$LINENO: result: $INSTALL" >&5 +echo "${ECHO_T}$INSTALL" >&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \${MAKE}" >&5 +echo $ECHO_N "checking whether ${MAKE-make} sets \${MAKE}... $ECHO_C" >&6 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y,./+-,__p_,'` +if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.make <<\_ACEOF +all: + @echo 'ac_maketemp="${MAKE}"' +_ACEOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftest.make 2>/dev/null | grep temp=` +if test -n "$ac_maketemp"; then + eval ac_cv_prog_make_${ac_make}_set=yes +else + eval ac_cv_prog_make_${ac_make}_set=no +fi +rm -f conftest.make +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + SET_MAKE= +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + SET_MAKE="MAKE=${MAKE-make}" +fi + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + set dummy "$as_dir/$ac_word" ${1+"$@"} + shift + ac_cv_prog_CC="$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CC" && break +done + + CC=$ac_ct_CC +fi + +fi + + +test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH" >&5 +echo "$as_me: error: no acceptable C compiler found in \$PATH" >&2;} + { (exit 1); exit 1; }; } + +# Provide some information about the compiler. +echo "$as_me:$LINENO:" \ + "checking for C compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 + (eval $ac_compiler --version &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 + (eval $ac_compiler -v &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 + (eval $ac_compiler -V &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +#include "confdefs.h" + +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.exe" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +echo "$as_me:$LINENO: checking for C compiler default output" >&5 +echo $ECHO_N "checking for C compiler default output... $ECHO_C" >&6 +ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5 + (eval $ac_link_default) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Find the output, starting from the most likely. This scheme is +# not robust to junk in `.', hence go to wildcards (a.*) only as a last +# resort. + +# Be careful to initialize this variable, since it used to be cached. +# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile. +ac_cv_exeext= +for ac_file in `ls a_out.exe a.exe conftest.exe 2>/dev/null; + ls a.out conftest 2>/dev/null; + ls a.* conftest.* 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.o | *.obj | *.xcoff | *.tds | *.d | *.pdb | *.xSYM ) ;; + a.out ) # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + # FIXME: I believe we export ac_cv_exeext for Libtool --akim. + export ac_cv_exeext + break;; + * ) break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +{ { echo "$as_me:$LINENO: error: C compiler cannot create executables" >&5 +echo "$as_me: error: C compiler cannot create executables" >&2;} + { (exit 77); exit 77; }; } +fi + +ac_exeext=$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_file" >&5 +echo "${ECHO_T}$ac_file" >&6 + +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether the C compiler works" >&5 +echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6 +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { echo "$as_me:$LINENO: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'." >&5 +echo "$as_me: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'." >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi +echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +rm -f a.out a.exe conftest$ac_cv_exeext +ac_clean_files=$ac_clean_files_save +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 +echo "$as_me:$LINENO: result: $cross_compiling" >&5 +echo "${ECHO_T}$cross_compiling" >&6 + +echo "$as_me:$LINENO: checking for suffix of executables" >&5 +echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6 +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in `(ls conftest.exe; ls conftest; ls conftest.*) 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.o | *.obj | *.xcoff | *.tds | *.d | *.pdb ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + export ac_cv_exeext + break;; + * ) break;; + esac +done +else + { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link" >&5 +echo "$as_me: error: cannot compute suffix of executables: cannot compile and link" >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +echo "${ECHO_T}$ac_cv_exeext" >&6 + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +echo "$as_me:$LINENO: checking for suffix of object files" >&5 +echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6 +if test "${ac_cv_objext+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +#include "confdefs.h" + +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile" >&5 +echo "$as_me: error: cannot compute suffix of object files: cannot compile" >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +echo "${ECHO_T}$ac_cv_objext" >&6 +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 +if test "${ac_cv_c_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +#include "confdefs.h" + +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_compiler_gnu=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 +GCC=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +CFLAGS="-g" +echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +#include "confdefs.h" + +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_prog_cc_g=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_g" >&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +# Some people use a C++ compiler to compile C. Since we use `exit', +# in C++ we need to declare it. In case someone uses the same compiler +# for both compiling C and C++ we need to have the C++ compiler decide +# the declaration of exit, since it's the most demanding environment. +cat >conftest.$ac_ext <<_ACEOF +#ifndef __cplusplus + choke me +#endif +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + for ac_declaration in \ + ''\ + '#include ' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +#include "confdefs.h" +#include +$ac_declaration +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +continue +fi +rm -f conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +#include "confdefs.h" +$ac_declaration +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +fi + +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f conftest.$ac_objext conftest.$ac_ext +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +rm -f .deps 2>/dev/null +mkdir .deps 2>/dev/null +if test -d .deps; then + DEPDIR=.deps +else + # MS-DOS does not allow filenames that begin with a dot. + DEPDIR=_deps +fi +rmdir .deps 2>/dev/null + + +ac_config_commands="$ac_config_commands depfiles" + + +am_make=${MAKE-make} +cat > confinc << 'END' +doit: + @echo done +END +# If we don't find an include directive, just comment out the code. +echo "$as_me:$LINENO: checking for style of include used by $am_make" >&5 +echo $ECHO_N "checking for style of include used by $am_make... $ECHO_C" >&6 +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# We grep out `Entering directory' and `Leaving directory' +# messages which can occur if `w' ends up in MAKEFLAGS. +# In particular we don't look at `^make:' because GNU make might +# be invoked under some other name (usually "gmake"), in which +# case it prints its new name instead of `make'. +if test "`$am_make -s -f confmf 2> /dev/null | fgrep -v 'ing directory'`" = "done"; then + am__include=include + am__quote= + _am_result=GNU +fi +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then + am__include=.include + am__quote="\"" + _am_result=BSD + fi +fi + + +echo "$as_me:$LINENO: result: $_am_result" >&5 +echo "${ECHO_T}$_am_result" >&6 +rm -f confinc confmf + +# Check whether --enable-dependency-tracking or --disable-dependency-tracking was given. +if test "${enable_dependency_tracking+set}" = set; then + enableval="$enable_dependency_tracking" + +fi; +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' +fi + + +if test "x$enable_dependency_tracking" != xno; then + AMDEP_TRUE= + AMDEP_FALSE='#' +else + AMDEP_TRUE='#' + AMDEP_FALSE= +fi + + + + +depcc="$CC" am_compiler_list= + +echo "$as_me:$LINENO: checking dependency style of $depcc" >&5 +echo $ECHO_N "checking dependency style of $depcc... $ECHO_C" >&6 +if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + for depmode in $am_compiler_list; do + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + echo '#include "conftest.h"' > conftest.c + echo 'int i;' > conftest.h + echo "${am__include} ${am__quote}conftest.Po${am__quote}" > confmf + + case $depmode in + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + none) break ;; + esac + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. + if depmode=$depmode \ + source=conftest.c object=conftest.o \ + depfile=conftest.Po tmpdepfile=conftest.TPo \ + $SHELL ./depcomp $depcc -c conftest.c -o conftest.o >/dev/null 2>&1 && + grep conftest.h conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +echo "$as_me:$LINENO: result: $am_cv_CC_dependencies_compiler_type" >&5 +echo "${ECHO_T}$am_cv_CC_dependencies_compiler_type" >&6 +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 +echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if test "${ac_cv_prog_CPP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +#include "confdefs.h" +#include + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +#include "confdefs.h" +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +echo "$as_me:$LINENO: result: $CPP" >&5 +echo "${ECHO_T}$CPP" >&6 +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +#include "confdefs.h" +#include + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +#include "confdefs.h" +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check" >&5 +echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check" >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 +if test "${ac_cv_header_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +#include "confdefs.h" +#include +#include +#include +#include + +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_cv_header_stdc=yes +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_cv_header_stdc=no +fi +rm -f conftest.err conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +#include "confdefs.h" +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +#include "confdefs.h" +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +#include "confdefs.h" +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + exit(2); + exit (0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -f core core.* *.core conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +echo "${ECHO_T}$ac_cv_header_stdc" >&6 +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. + + + + + + + + + +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +#include "confdefs.h" +$ac_includes_default + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +eval "$as_ac_Header=no" +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +if test "${ac_cv_header_libintl_h+set}" = set; then + echo "$as_me:$LINENO: checking for libintl.h" >&5 +echo $ECHO_N "checking for libintl.h... $ECHO_C" >&6 +if test "${ac_cv_header_libintl_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: $ac_cv_header_libintl_h" >&5 +echo "${ECHO_T}$ac_cv_header_libintl_h" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking libintl.h usability" >&5 +echo $ECHO_N "checking libintl.h usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +#include "confdefs.h" +$ac_includes_default +#include +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_header_compiler=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking libintl.h presence" >&5 +echo $ECHO_N "checking libintl.h presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +#include "confdefs.h" +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + egrep -v '^ *\+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc in + yes:no ) + { echo "$as_me:$LINENO: WARNING: libintl.h: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: libintl.h: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: libintl.h: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: libintl.h: proceeding with the preprocessor's result" >&2;};; + no:yes ) + { echo "$as_me:$LINENO: WARNING: libintl.h: present but cannot be compiled" >&5 +echo "$as_me: WARNING: libintl.h: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: libintl.h: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: libintl.h: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: libintl.h: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: libintl.h: proceeding with the preprocessor's result" >&2;};; +esac +echo "$as_me:$LINENO: checking for libintl.h" >&5 +echo $ECHO_N "checking for libintl.h... $ECHO_C" >&6 +if test "${ac_cv_header_libintl_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_header_libintl_h=$ac_header_preproc +fi +echo "$as_me:$LINENO: result: $ac_cv_header_libintl_h" >&5 +echo "${ECHO_T}$ac_cv_header_libintl_h" >&6 + +fi + + + +echo "$as_me:$LINENO: checking for textdomain in -lc" >&5 +echo $ECHO_N "checking for textdomain in -lc... $ECHO_C" >&6 +if test "${ac_cv_lib_c_textdomain+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lc $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char textdomain (); +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +textdomain (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_c_textdomain=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_c_textdomain=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_c_textdomain" >&5 +echo "${ECHO_T}$ac_cv_lib_c_textdomain" >&6 +if test $ac_cv_lib_c_textdomain = yes; then + LIBS="" +else + echo "$as_me:$LINENO: checking for textdomain in -lintl" >&5 +echo $ECHO_N "checking for textdomain in -lintl... $ECHO_C" >&6 +if test "${ac_cv_lib_intl_textdomain+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lintl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char textdomain (); +#ifdef F77_DUMMY_MAIN +# ifdef __cplusplus + extern "C" +# endif + int F77_DUMMY_MAIN() { return 1; } +#endif +int +main () +{ +textdomain (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_intl_textdomain=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_intl_textdomain=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_intl_textdomain" >&5 +echo "${ECHO_T}$ac_cv_lib_intl_textdomain" >&6 +if test $ac_cv_lib_intl_textdomain = yes; then + LIBS="-lintl" +else + { { echo "$as_me:$LINENO: error: \"Could not find internationalization libraries\"" >&5 +echo "$as_me: error: \"Could not find internationalization libraries\"" >&2;} + { (exit 1); exit 1; }; } +fi + + +fi + + + +# Extract the first word of "gnome-config", so it can be a program name with args. +set dummy gnome-config; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_HAVE_GNOME_CONFIG+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$HAVE_GNOME_CONFIG"; then + ac_cv_prog_HAVE_GNOME_CONFIG="$HAVE_GNOME_CONFIG" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_HAVE_GNOME_CONFIG=""YES"" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_prog_HAVE_GNOME_CONFIG" && ac_cv_prog_HAVE_GNOME_CONFIG=""NO"" +fi +fi +HAVE_GNOME_CONFIG=$ac_cv_prog_HAVE_GNOME_CONFIG +if test -n "$HAVE_GNOME_CONFIG"; then + echo "$as_me:$LINENO: result: $HAVE_GNOME_CONFIG" >&5 +echo "${ECHO_T}$HAVE_GNOME_CONFIG" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +GNOMEHELP=`gnome-config --prefix` + + +echo "$as_me:$LINENO: checking Python bindings for sax/xml" >&5 +echo $ECHO_N "checking Python bindings for sax/xml... $ECHO_C" >&6 + +cat > conftest.py <&5 +echo "$as_me: error: + +**** The python interpreter can't find the SAX/XML bindings." >&2;} + { (exit 1); exit 1; }; } +fi +echo "$as_me:$LINENO: result: ok" >&5 +echo "${ECHO_T}ok" >&6 + +echo "$as_me:$LINENO: checking Python bindings for gtk" >&5 +echo $ECHO_N "checking Python bindings for gtk... $ECHO_C" >&6 + +cat > conftest.py <&5 +echo "$as_me: error: + +**** The python interpreter can't find the python bindings for gtk 2.0." >&2;} + { (exit 1); exit 1; }; } +fi +echo "$as_me:$LINENO: result: ok" >&5 +echo "${ECHO_T}ok" >&6 + +echo "$as_me:$LINENO: checking Python bindings for GNOME" >&5 +echo $ECHO_N "checking Python bindings for GNOME... $ECHO_C" >&6 + +cat > conftest.py <&5 +echo "$as_me: error: +**** The python interpreter can't find the python bindings for GNOME." >&2;} + { (exit 1); exit 1; }; } +fi +echo "$as_me:$LINENO: result: ok" >&5 +echo "${ECHO_T}ok" >&6 + +echo "$as_me:$LINENO: checking Python/libglade bindings" >&5 +echo $ECHO_N "checking Python/libglade bindings... $ECHO_C" >&6 + +cat > conftest.py <&5 +echo "$as_me: error: + +**** The python interpreter can't find the python bindings for libglade." >&2;} + { (exit 1); exit 1; }; } +fi +echo "$as_me:$LINENO: result: ok" >&5 +echo "${ECHO_T}ok" >&6 + +echo "$as_me:$LINENO: checking Python bindings for gconf" >&5 +echo $ECHO_N "checking Python bindings for gconf... $ECHO_C" >&6 + +cat > conftest.py <&5 +echo "$as_me: error: + +**** The python interpreter can't find the python bindings for gconf." >&2;} + { (exit 1); exit 1; }; } +fi +echo "$as_me:$LINENO: result: ok" >&5 +echo "${ECHO_T}ok" >&6 + + + +echo "$as_me:$LINENO: checking for headers required to compile python extensions" >&5 +echo $ECHO_N "checking for headers required to compile python extensions... $ECHO_C" >&6 + +if test "$PYTHON22" != ""; then + py_prefix=`$PYTHON22 -c "import sys; print sys.prefix"` + py_exec_prefix=`$PYTHON22 -c "import sys; print sys.exec_prefix"` + P22_INCLUDES="-I${py_prefix}/include/python2.2" + if test "$py_prefix" != "$py_exec_prefix"; then + P22_INCLUDES="$P22_INCLUDES -I${py_exec_prefix}/include/python2.2" + fi + if test -f "${py_exec_prefix}/include/python2.2/Python.h" + then + INTLLIBS="${INTLLIBS}intl22.so " + fi +fi + +echo "$as_me:$LINENO: result: ok" >&5 +echo "${ECHO_T}ok" >&6 + +SCROLLKEEPER_REQUIRED=0.1.4 + + +# Extract the first word of "scrollkeeper-config", so it can be a program name with args. +set dummy scrollkeeper-config; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_path_SCROLLKEEPER_CONFIG+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $SCROLLKEEPER_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_SCROLLKEEPER_CONFIG="$SCROLLKEEPER_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_SCROLLKEEPER_CONFIG="$as_dir/$ac_word$ac_exec_ext" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_path_SCROLLKEEPER_CONFIG" && ac_cv_path_SCROLLKEEPER_CONFIG="no" + ;; +esac +fi +SCROLLKEEPER_CONFIG=$ac_cv_path_SCROLLKEEPER_CONFIG + +if test -n "$SCROLLKEEPER_CONFIG"; then + echo "$as_me:$LINENO: result: $SCROLLKEEPER_CONFIG" >&5 +echo "${ECHO_T}$SCROLLKEEPER_CONFIG" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +if test x$SCROLLKEEPER_CONFIG = xno; then + { { echo "$as_me:$LINENO: error: Couldn't find scrollkeeper-config. Please install the scrollkeeper package." >&5 +echo "$as_me: error: Couldn't find scrollkeeper-config. Please install the scrollkeeper package." >&2;} + { (exit 1); exit 1; }; } + DISABLE_SCROLLKEEPER=1 + +fi + +# Extract the first word of "jw", so it can be a program name with args. +set dummy jw; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_path_JW+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $JW in + [\\/]* | ?:[\\/]*) + ac_cv_path_JW="$JW" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_JW="$as_dir/$ac_word$ac_exec_ext" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_path_JW" && ac_cv_path_JW="no" + ;; +esac +fi +JW=$ac_cv_path_JW + +if test -n "$JW"; then + echo "$as_me:$LINENO: result: $JW" >&5 +echo "${ECHO_T}$JW" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +if test x$JW = xno; then + HAVE_JW="no" +else + HAVE_JW="yes" +fi + + + + + + + + + + + + + + + + +ac_config_files="$ac_config_files Makefile src/Makefile src/const.py src/docgen/Makefile src/filters/Makefile src/plugins/Makefile src/data/Makefile src/data/templates/Makefile src/po/Makefile doc/Makefile doc/gramps-manual/Makefile doc/gramps-manual/C/Makefile doc/extending-gramps/Makefile doc/extending-gramps/C/Makefile omf-install/Makefile gramps.spec gramps.sh" +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overriden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +{ + (set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} | + sed ' + t clear + : clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + : end' >>confcache +if cmp -s $cache_file confcache; then :; else + if test -w $cache_file; then + test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" + cat confcache >$cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/; +s/:*\${srcdir}:*/:/; +s/:*@srcdir@:*/:/; +s/^\([^=]*=[ ]*\):*/\1/; +s/:*$//; +s/^[^=]*=[ ]*$//; +}' +fi + +# Transform confdefs.h into DEFS. +# Protect against shell expansion while executing Makefile rules. +# Protect against Makefile macro expansion. +# +# If the first sed substitution is executed (which looks for macros that +# take arguments), then we branch to the quote section. Otherwise, +# look for a macro that doesn't take arguments. +cat >confdef2opt.sed <<\_ACEOF +t clear +: clear +s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\),-D\1=\2,g +t quote +s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\),-D\1=\2,g +t quote +d +: quote +s,[ `~#$^&*(){}\\|;'"<>?],\\&,g +s,\[,\\&,g +s,\],\\&,g +s,\$,$$,g +p +_ACEOF +# We use echo to avoid assuming a particular line-breaking character. +# The extra dot is to prevent the shell from consuming trailing +# line-breaks from the sub-command output. A line-break within +# single-quotes doesn't work because, if this script is created in a +# platform that uses two characters for line-breaks (e.g., DOS), tr +# would break. +ac_LF_and_DOT=`echo; echo .` +DEFS=`sed -n -f confdef2opt.sed confdefs.h | tr "$ac_LF_and_DOT" ' .'` +rm -f confdef2opt.sed + + +if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi + +: ${CONFIG_STATUS=./config.status} +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +SHELL=\${CONFIG_SHELL-$SHELL} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF + +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi + +# NLS nuisances. +# Support unset when possible. +if (FOO=FOO; unset FOO) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + +(set +x; test -n "`(LANG=C; export LANG) 2>&1`") && + { $as_unset LANG || test "${LANG+set}" != set; } || + { LANG=C; export LANG; } +(set +x; test -n "`(LC_ALL=C; export LC_ALL) 2>&1`") && + { $as_unset LC_ALL || test "${LC_ALL+set}" != set; } || + { LC_ALL=C; export LC_ALL; } +(set +x; test -n "`(LC_TIME=C; export LC_TIME) 2>&1`") && + { $as_unset LC_TIME || test "${LC_TIME+set}" != set; } || + { LC_TIME=C; export LC_TIME; } +(set +x; test -n "`(LC_CTYPE=C; export LC_CTYPE) 2>&1`") && + { $as_unset LC_CTYPE || test "${LC_CTYPE+set}" != set; } || + { LC_CTYPE=C; export LC_CTYPE; } +(set +x; test -n "`(LANGUAGE=C; export LANGUAGE) 2>&1`") && + { $as_unset LANGUAGE || test "${LANGUAGE+set}" != set; } || + { LANGUAGE=C; export LANGUAGE; } +(set +x; test -n "`(LC_COLLATE=C; export LC_COLLATE) 2>&1`") && + { $as_unset LC_COLLATE || test "${LC_COLLATE+set}" != set; } || + { LC_COLLATE=C; export LC_COLLATE; } +(set +x; test -n "`(LC_NUMERIC=C; export LC_NUMERIC) 2>&1`") && + { $as_unset LC_NUMERIC || test "${LC_NUMERIC+set}" != set; } || + { LC_NUMERIC=C; export LC_NUMERIC; } +(set +x; test -n "`(LC_MESSAGES=C; export LC_MESSAGES) 2>&1`") && + { $as_unset LC_MESSAGES || test "${LC_MESSAGES+set}" != set; } || + { LC_MESSAGES=C; export LC_MESSAGES; } + + +# Name of the executable. +as_me=`(basename "$0") 2>/dev/null || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conftest.sh + echo "exit 0" >>conftest.sh + chmod +x conftest.sh + if (PATH=".;."; conftest.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conftest.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5 +echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;} + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5 +echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;} + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="sed y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="sed y%*+%pp%;s%[^_$as_cr_alnum]%_%g" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH || test "${CDPATH+set}" != set || { CDPATH=$PATH_SEPARATOR; export CDPATH; } + +exec 6>&1 + +# Open the log real soon, to keep \$[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. Logging --version etc. is OK. +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX +} >&5 +cat >&5 <<_CSEOF + +This file was extended by $as_me, which was +generated by GNU Autoconf 2.53. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +_CSEOF +echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5 +echo >&5 +_ACEOF + +# Files that config.status was made for. +if test -n "$ac_config_files"; then + echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_headers"; then + echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_links"; then + echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_commands"; then + echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS +fi + +cat >>$CONFIG_STATUS <<\_ACEOF + +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTIONS] [FILE]... + + -h, --help print this help, then exit + -V, --version print version number, then exit + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + +Configuration files: +$config_files + +Configuration commands: +$config_commands + +Report bugs to ." +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +ac_cs_version="\\ +config.status +configured by $0, generated by GNU Autoconf 2.53, + with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" + +Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." +srcdir=$srcdir +INSTALL="$INSTALL" +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If no file are specified by the user, then we need to provide default +# value. By we need to know if files were specified by the user. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "x$1" : 'x\([^=]*\)='` + ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` + shift + set dummy "$ac_option" "$ac_optarg" ${1+"$@"} + shift + ;; + -*);; + *) # This is not an option, so the user has probably given explicit + # arguments. + ac_need_defaults=false;; + esac + + case $1 in + # Handling of the options. +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + echo "running $SHELL $0 " $ac_configure_args " --no-create --no-recursion" + exec $SHELL $0 $ac_configure_args --no-create --no-recursion ;; +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + --version | --vers* | -V ) + echo "$ac_cs_version"; exit 0 ;; + --he | --h) + # Conflict between --help and --header + { { echo "$as_me:$LINENO: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + echo "$ac_cs_usage"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + shift + CONFIG_FILES="$CONFIG_FILES $1" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + shift + CONFIG_HEADERS="$CONFIG_HEADERS $1" + ac_need_defaults=false;; + + # This is an error. + -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" ;; + + esac + shift +done + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +# +# INIT-COMMANDS section. +# + +AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" + +_ACEOF + + + +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_config_target in $ac_config_targets +do + case "$ac_config_target" in + # Handling of arguments. + "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "src/Makefile" ) CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; + "src/const.py" ) CONFIG_FILES="$CONFIG_FILES src/const.py" ;; + "src/docgen/Makefile" ) CONFIG_FILES="$CONFIG_FILES src/docgen/Makefile" ;; + "src/filters/Makefile" ) CONFIG_FILES="$CONFIG_FILES src/filters/Makefile" ;; + "src/plugins/Makefile" ) CONFIG_FILES="$CONFIG_FILES src/plugins/Makefile" ;; + "src/data/Makefile" ) CONFIG_FILES="$CONFIG_FILES src/data/Makefile" ;; + "src/data/templates/Makefile" ) CONFIG_FILES="$CONFIG_FILES src/data/templates/Makefile" ;; + "src/po/Makefile" ) CONFIG_FILES="$CONFIG_FILES src/po/Makefile" ;; + "doc/Makefile" ) CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; + "doc/gramps-manual/Makefile" ) CONFIG_FILES="$CONFIG_FILES doc/gramps-manual/Makefile" ;; + "doc/gramps-manual/C/Makefile" ) CONFIG_FILES="$CONFIG_FILES doc/gramps-manual/C/Makefile" ;; + "doc/extending-gramps/Makefile" ) CONFIG_FILES="$CONFIG_FILES doc/extending-gramps/Makefile" ;; + "doc/extending-gramps/C/Makefile" ) CONFIG_FILES="$CONFIG_FILES doc/extending-gramps/C/Makefile" ;; + "omf-install/Makefile" ) CONFIG_FILES="$CONFIG_FILES omf-install/Makefile" ;; + "gramps.spec" ) CONFIG_FILES="$CONFIG_FILES gramps.spec" ;; + "gramps.sh" ) CONFIG_FILES="$CONFIG_FILES gramps.sh" ;; + "depfiles" ) CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; + *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands +fi + +# Create a temporary directory, and hook for its removal unless debugging. +$debug || +{ + trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} + +# Create a (secure) tmp directory for tmp files. +: ${TMPDIR=/tmp} +{ + tmp=`(umask 077 && mktemp -d -q "$TMPDIR/csXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=$TMPDIR/cs$$-$RANDOM + (umask 077 && mkdir $tmp) +} || +{ + echo "$me: cannot create a temporary directory in $TMPDIR" >&2 + { (exit 1); exit 1; } +} + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF + +# +# CONFIG_FILES section. +# + +# No need to generate the scripts if there are no CONFIG_FILES. +# This happens for instance when ./config.status config.h +if test -n "\$CONFIG_FILES"; then + # Protect against being on the right side of a sed subst in config.status. + sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g; + s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF +s,@SHELL@,$SHELL,;t t +s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t +s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t +s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t +s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t +s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t +s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t +s,@exec_prefix@,$exec_prefix,;t t +s,@prefix@,$prefix,;t t +s,@program_transform_name@,$program_transform_name,;t t +s,@bindir@,$bindir,;t t +s,@sbindir@,$sbindir,;t t +s,@libexecdir@,$libexecdir,;t t +s,@datadir@,$datadir,;t t +s,@sysconfdir@,$sysconfdir,;t t +s,@sharedstatedir@,$sharedstatedir,;t t +s,@localstatedir@,$localstatedir,;t t +s,@libdir@,$libdir,;t t +s,@includedir@,$includedir,;t t +s,@oldincludedir@,$oldincludedir,;t t +s,@infodir@,$infodir,;t t +s,@mandir@,$mandir,;t t +s,@build_alias@,$build_alias,;t t +s,@host_alias@,$host_alias,;t t +s,@target_alias@,$target_alias,;t t +s,@DEFS@,$DEFS,;t t +s,@ECHO_C@,$ECHO_C,;t t +s,@ECHO_N@,$ECHO_N,;t t +s,@ECHO_T@,$ECHO_T,;t t +s,@LIBS@,$LIBS,;t t +s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t +s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t +s,@INSTALL_DATA@,$INSTALL_DATA,;t t +s,@PACKAGE@,$PACKAGE,;t t +s,@VERSION@,$VERSION,;t t +s,@ACLOCAL@,$ACLOCAL,;t t +s,@AUTOCONF@,$AUTOCONF,;t t +s,@AUTOMAKE@,$AUTOMAKE,;t t +s,@AUTOHEADER@,$AUTOHEADER,;t t +s,@MAKEINFO@,$MAKEINFO,;t t +s,@AMTAR@,$AMTAR,;t t +s,@install_sh@,$install_sh,;t t +s,@STRIP@,$STRIP,;t t +s,@ac_ct_STRIP@,$ac_ct_STRIP,;t t +s,@INSTALL_STRIP_PROGRAM@,$INSTALL_STRIP_PROGRAM,;t t +s,@AWK@,$AWK,;t t +s,@SET_MAKE@,$SET_MAKE,;t t +s,@RELEASE@,$RELEASE,;t t +s,@VERSIONSTRING@,$VERSIONSTRING,;t t +s,@MSGFMT@,$MSGFMT,;t t +s,@LANGUAGES@,$LANGUAGES,;t t +s,@POFILES@,$POFILES,;t t +s,@MOFILES@,$MOFILES,;t t +s,@PYTHON@,$PYTHON,;t t +s,@PYTHON_VERSION@,$PYTHON_VERSION,;t t +s,@PYTHON_PREFIX@,$PYTHON_PREFIX,;t t +s,@PYTHON_EXEC_PREFIX@,$PYTHON_EXEC_PREFIX,;t t +s,@PYTHON_PLATFORM@,$PYTHON_PLATFORM,;t t +s,@pythondir@,$pythondir,;t t +s,@pkgpythondir@,$pkgpythondir,;t t +s,@pyexecdir@,$pyexecdir,;t t +s,@pkgpyexecdir@,$pkgpyexecdir,;t t +s,@BINSH@,$BINSH,;t t +s,@PYTHON22@,$PYTHON22,;t t +s,@ZIP@,$ZIP,;t t +s,@CC@,$CC,;t t +s,@CFLAGS@,$CFLAGS,;t t +s,@LDFLAGS@,$LDFLAGS,;t t +s,@CPPFLAGS@,$CPPFLAGS,;t t +s,@ac_ct_CC@,$ac_ct_CC,;t t +s,@EXEEXT@,$EXEEXT,;t t +s,@OBJEXT@,$OBJEXT,;t t +s,@DEPDIR@,$DEPDIR,;t t +s,@am__include@,$am__include,;t t +s,@am__quote@,$am__quote,;t t +s,@AMDEP_TRUE@,$AMDEP_TRUE,;t t +s,@AMDEP_FALSE@,$AMDEP_FALSE,;t t +s,@AMDEPBACKSLASH@,$AMDEPBACKSLASH,;t t +s,@CCDEPMODE@,$CCDEPMODE,;t t +s,@CPP@,$CPP,;t t +s,@HAVE_GNOME_CONFIG@,$HAVE_GNOME_CONFIG,;t t +s,@SCROLLKEEPER_REQUIRED@,$SCROLLKEEPER_REQUIRED,;t t +s,@SCROLLKEEPER_CONFIG@,$SCROLLKEEPER_CONFIG,;t t +s,@DISABLE_SCROLLKEEPER@,$DISABLE_SCROLLKEEPER,;t t +s,@JW@,$JW,;t t +s,@HAVE_JW@,$HAVE_JW,;t t +s,@GNOMEHELP@,$GNOMEHELP,;t t +s,@P15_INCLUDES@,$P15_INCLUDES,;t t +s,@P20_INCLUDES@,$P20_INCLUDES,;t t +s,@P21_INCLUDES@,$P21_INCLUDES,;t t +s,@P22_INCLUDES@,$P22_INCLUDES,;t t +s,@INTLLIBS@,$INTLLIBS,;t t +CEOF + +_ACEOF + + cat >>$CONFIG_STATUS <<\_ACEOF + # Split the substitutions into bite-sized pieces for seds with + # small command number limits, like on Digital OSF/1 and HP-UX. + ac_max_sed_lines=48 + ac_sed_frag=1 # Number of current file. + ac_beg=1 # First line for current file. + ac_end=$ac_max_sed_lines # Line after last line for current file. + ac_more_lines=: + ac_sed_cmds= + while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + else + sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + fi + if test ! -s $tmp/subs.frag; then + ac_more_lines=false + else + # The purpose of the label and of the branching condition is to + # speed up the sed processing (if there are no `@' at all, there + # is no need to browse any of the substitutions). + # These are the two extra sed commands mentioned above. + (echo ':t + /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" + else + ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" + fi + ac_sed_frag=`expr $ac_sed_frag + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_lines` + fi + done + if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat + fi +fi # test -n "$CONFIG_FILES" + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { case "$ac_dir" in + [\\/]* | ?:[\\/]* ) as_incr_dir=;; + *) as_incr_dir=.;; +esac +as_dummy="$ac_dir" +for as_mkdir_dir in `IFS='/\\'; set X $as_dummy; shift; echo "$@"`; do + case $as_mkdir_dir in + # Skip DOS drivespec + ?:) as_incr_dir=$as_mkdir_dir ;; + *) + as_incr_dir=$as_incr_dir/$as_mkdir_dir + test -d "$as_incr_dir" || + mkdir "$as_incr_dir" || + { { echo "$as_me:$LINENO: error: cannot create \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; } + ;; + esac +done; } + + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac +# Don't blindly perform a `cd "$ac_dir"/$ac_foo && pwd` since $ac_foo can be +# absolute. +ac_abs_builddir=`cd "$ac_dir" && cd $ac_builddir && pwd` +ac_abs_top_builddir=`cd "$ac_dir" && cd $ac_top_builddir && pwd` +ac_abs_srcdir=`cd "$ac_dir" && cd $ac_srcdir && pwd` +ac_abs_top_srcdir=`cd "$ac_dir" && cd $ac_top_srcdir && pwd` + + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_builddir$INSTALL ;; + esac + + if test x"$ac_file" != x-; then + { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + rm -f "$ac_file" + fi + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + if test x"$ac_file" = x-; then + configure_input= + else + configure_input="$ac_file. " + fi + configure_input=$configure_input"Generated from `echo $ac_file_in | + sed 's,.*/,,'` by configure." + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + echo $f;; + *) # Relative + if test -f "$f"; then + # Build tree + echo $f + elif test -f "$srcdir/$f"; then + # Source tree + echo $srcdir/$f + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF + sed "$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s,@configure_input@,$configure_input,;t t +s,@srcdir@,$ac_srcdir,;t t +s,@abs_srcdir@,$ac_abs_srcdir,;t t +s,@top_srcdir@,$ac_top_srcdir,;t t +s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t +s,@builddir@,$ac_builddir,;t t +s,@abs_builddir@,$ac_abs_builddir,;t t +s,@top_builddir@,$ac_top_builddir,;t t +s,@abs_top_builddir@,$ac_abs_top_builddir,;t t +s,@INSTALL@,$ac_INSTALL,;t t +" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out + rm -f $tmp/stdin + if test x"$ac_file" != x-; then + mv $tmp/out $ac_file + else + cat $tmp/out + rm -f $tmp/out + fi + +done +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + +# +# CONFIG_COMMANDS section. +# +for ac_file in : $CONFIG_COMMANDS; do test "x$ac_file" = x: && continue + ac_dest=`echo "$ac_file" | sed 's,:.*,,'` + ac_source=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_dir=`(dirname "$ac_dest") 2>/dev/null || +$as_expr X"$ac_dest" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_dest" : 'X\(//\)[^/]' \| \ + X"$ac_dest" : 'X\(//\)$' \| \ + X"$ac_dest" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_dest" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac +# Don't blindly perform a `cd "$ac_dir"/$ac_foo && pwd` since $ac_foo can be +# absolute. +ac_abs_builddir=`cd "$ac_dir" && cd $ac_builddir && pwd` +ac_abs_top_builddir=`cd "$ac_dir" && cd $ac_top_builddir && pwd` +ac_abs_srcdir=`cd "$ac_dir" && cd $ac_srcdir && pwd` +ac_abs_top_srcdir=`cd "$ac_dir" && cd $ac_top_srcdir && pwd` + + + { echo "$as_me:$LINENO: executing $ac_dest commands" >&5 +echo "$as_me: executing $ac_dest commands" >&6;} + case $ac_dest in + depfiles ) test x"$AMDEP_TRUE" != x"" || for mf in $CONFIG_FILES; do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named `Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # So let's grep whole file. + if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then + dirpart=`(dirname "$mf") 2>/dev/null || +$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$mf" : 'X\(//\)[^/]' \| \ + X"$mf" : 'X\(//\)$' \| \ + X"$mf" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$mf" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + else + continue + fi + grep '^DEP_FILES *= *[^ #]' < "$mf" > /dev/null || continue + # Extract the definition of DEP_FILES from the Makefile without + # running `make'. + DEPDIR=`sed -n -e '/^DEPDIR = / s///p' < "$mf"` + test -z "$DEPDIR" && continue + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n -e '/^U = / s///p' < "$mf"` + test -d "$dirpart/$DEPDIR" || mkdir "$dirpart/$DEPDIR" + # We invoke sed twice because it is the simplest approach to + # changing $(DEPDIR) to its actual value in the expansion. + for file in `sed -n -e ' + /^DEP_FILES = .*\\\\$/ { + s/^DEP_FILES = // + :loop + s/\\\\$// + p + n + /\\\\$/ b loop + p + } + /^DEP_FILES = / s/^DEP_FILES = //p' < "$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`(dirname "$file") 2>/dev/null || +$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$file" : 'X\(//\)[^/]' \| \ + X"$file" : 'X\(//\)$' \| \ + X"$file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { case $dirpart/$fdir in + [\\/]* | ?:[\\/]* ) as_incr_dir=;; + *) as_incr_dir=.;; +esac +as_dummy=$dirpart/$fdir +for as_mkdir_dir in `IFS='/\\'; set X $as_dummy; shift; echo "$@"`; do + case $as_mkdir_dir in + # Skip DOS drivespec + ?:) as_incr_dir=$as_mkdir_dir ;; + *) + as_incr_dir=$as_incr_dir/$as_mkdir_dir + test -d "$as_incr_dir" || + mkdir "$as_incr_dir" || + { { echo "$as_me:$LINENO: error: cannot create $dirpart/$fdir" >&5 +echo "$as_me: error: cannot create $dirpart/$fdir" >&2;} + { (exit 1); exit 1; }; } + ;; + esac +done; } + + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done +done + ;; + esac +done +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + exec 5>/dev/null + $SHELL $CONFIG_STATUS || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi + diff --git a/gramps2/configure.in b/gramps2/configure.in new file mode 100644 index 000000000..3b8dd3bee --- /dev/null +++ b/gramps2/configure.in @@ -0,0 +1,293 @@ +dnl Process this file with autoconf to produce a configure script. +dnl May need to run automake && aclocal first +AC_INIT(src/gramps.py) +AM_INIT_AUTOMAKE(gramps, 0.9.0pre1) +RELEASE=rc4 + +VERSIONSTRING=$VERSION +if test x"$RELEASE" != "x" +then + VERSIONSTRING="$VERSION-$RELEASE" +fi + +AC_SUBST(PACKAGE) +AC_SUBST(VERSION) +AC_SUBST(RELEASE) +AC_SUBST(VERSIONSTRING) + +AC_PATH_PROG(MSGFMT, msgfmt) +AC_SUBST(MSGFMT) + +LANGUAGES="sv de fr es it pt_BR ru da_DK cs" +AC_SUBST(LANGUAGES) + +DISTLANGS= +POFILES= +MOFILES= +for lang in $LANGUAGES; do + POFILES="$POFILES $lang.po" + MOFILES="$MOFILES $lang.mo" +done +AC_SUBST(POFILES) +AC_SUBST(MOFILES) + +dnl Checks for programs. +dnl We first only check for python >= 1.5 +AM_PATH_PYTHON(2.2) + +dnl override automatic python detection with our own place +pythondir=\${prefix}/share +pyexecdir=\${prefix}/share +pkgpythondir=\${prefix}/share/\${PACKAGE} +pkgpyexecdir=\${prefix}/share/\${PACKAGE} + +AC_PATH_PROG(BINSH, sh) + +changequote(<<, >>)dnl +PYTHON_VERSION=`$PYTHON -c "import sys; print sys.version[:3]"` +changequote([, ])dnl + +if test "$PYTHON_VERSION" != "2.2" +then + AC_PATH_PROG(PYTHON22, python2.2) +else + PYTHON22=$PYTHON +fi + +AC_PATH_PROG(ZIP, zip) + +AC_PROG_INSTALL +AC_PROG_MAKE_SET + +AC_PROG_CC + +AC_CHECK_HEADER(libintl.h) + +AC_CHECK_LIB(c,textdomain,LIBS="", + [ AC_CHECK_LIB(intl,textdomain, + LIBS="-lintl", + AC_MSG_ERROR("Could not find internationalization libraries")) + ]) + +dnl Check for programs + +AC_CHECK_PROG(HAVE_GNOME_CONFIG, gnome-config, "YES", "NO") +GNOMEHELP=`gnome-config --prefix` + +dnl Check if python bindings for gtk are installed + +AC_MSG_CHECKING(Python bindings for sax/xml) +changequote(,) +cat > conftest.py < conftest.py < conftest.py < conftest.py < conftest.py < 0.3) but basic scrollkeeper instructions +dnl == use 0.1.4 example so we'll just check for that +dnl ====================================================== +SCROLLKEEPER_REQUIRED=0.1.4 +AC_SUBST(SCROLLKEEPER_REQUIRED) + +dnl First see that *some* version of scrollkeeper is installed +AC_PATH_PROG(SCROLLKEEPER_CONFIG, scrollkeeper-config, no) +if test x$SCROLLKEEPER_CONFIG = xno; then + AC_MSG_ERROR(Couldn't find scrollkeeper-config. Please install the scrollkeeper package.) + DISABLE_SCROLLKEEPER=1 + AC_SUBST(DISABLE_SCROLLKEEPER) +fi + +dnl ====================================================== +dnl == GNOME modified DTD's need jw, not db2html +dnl ====================================================== +AC_PATH_PROG(JW, jw, no) +if test x$JW = xno; then + HAVE_JW="no" +else + HAVE_JW="yes" +fi +AC_SUBST(HAVE_JW) + +dnl ====================================================== +dnl == end of modern doc tests +dnl ====================================================== + + +AC_SUBST(BINSH) +AC_SUBST(PYTHON) +AC_SUBST(PYTHON_VERSION) +AC_SUBST(GNOMEHELP) +AC_SUBST(LIBS) + +AC_SUBST(P15_INCLUDES) +AC_SUBST(P20_INCLUDES) +AC_SUBST(P21_INCLUDES) +AC_SUBST(P22_INCLUDES) +AC_SUBST(INTLLIBS) + +AC_OUTPUT([ +Makefile +src/Makefile +src/const.py +src/docgen/Makefile +src/filters/Makefile +src/plugins/Makefile +src/data/Makefile +src/data/templates/Makefile +src/po/Makefile +doc/Makefile +doc/gramps-manual/Makefile +doc/gramps-manual/C/Makefile +doc/extending-gramps/Makefile +doc/extending-gramps/C/Makefile +omf-install/Makefile +gramps.spec +gramps.sh]) diff --git a/gramps2/doc/Makefile.am b/gramps2/doc/Makefile.am new file mode 100644 index 000000000..a24f2349f --- /dev/null +++ b/gramps2/doc/Makefile.am @@ -0,0 +1,11 @@ +# Process this file with automake to produce Makefile.in + +SUBDIRS = gramps-manual extending-gramps + +man_IN_FILES = gramps.1.in +man_MANS = ${man_IN_FILES:.1.in=.1} + +EXTRA_DIST = $(man_MANS) $(man_IN_FILES) sgmldocs.make + +gramps.1: $(top_builddir)/config.status gramps.1.in + cd $(top_builddir) && CONFIG_FILES=doc/$@ $(SHELL) ./config.status diff --git a/gramps2/doc/Makefile.in b/gramps2/doc/Makefile.in new file mode 100644 index 000000000..93ac62ae5 --- /dev/null +++ b/gramps2/doc/Makefile.in @@ -0,0 +1,409 @@ +# Makefile.in generated by automake 1.6.3 from Makefile.am. +# @configure_input@ + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# Process this file with automake to produce Makefile.in +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : + +EXEEXT = @EXEEXT@ +OBJEXT = @OBJEXT@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +AMTAR = @AMTAR@ +AWK = @AWK@ +BINSH = @BINSH@ +CC = @CC@ +DEPDIR = @DEPDIR@ +DISABLE_SCROLLKEEPER = @DISABLE_SCROLLKEEPER@ +GNOMEHELP = @GNOMEHELP@ +HAVE_GNOME_CONFIG = @HAVE_GNOME_CONFIG@ +HAVE_JW = @HAVE_JW@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +JW = @JW@ +LANGUAGES = @LANGUAGES@ +LIBS = @LIBS@ +MOFILES = @MOFILES@ +MSGFMT = @MSGFMT@ +P15_INCLUDES = @P15_INCLUDES@ +P20_INCLUDES = @P20_INCLUDES@ +P21_INCLUDES = @P21_INCLUDES@ +P22_INCLUDES = @P22_INCLUDES@ +PACKAGE = @PACKAGE@ +POFILES = @POFILES@ +PYTHON = @PYTHON@ +PYTHON22 = @PYTHON22@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RELEASE = @RELEASE@ +SCROLLKEEPER_CONFIG = @SCROLLKEEPER_CONFIG@ +SCROLLKEEPER_REQUIRED = @SCROLLKEEPER_REQUIRED@ +STRIP = @STRIP@ +VERSION = @VERSION@ +VERSIONSTRING = @VERSIONSTRING@ +ZIP = @ZIP@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ + +SUBDIRS = gramps-manual extending-gramps + +man_IN_FILES = gramps.1.in +man_MANS = ${man_IN_FILES:.1.in=.1} + +EXTRA_DIST = $(man_MANS) $(man_IN_FILES) sgmldocs.make +subdir = doc +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_CLEAN_FILES = +DIST_SOURCES = + +NROFF = nroff +MANS = $(man_MANS) + +RECURSIVE_TARGETS = info-recursive dvi-recursive install-info-recursive \ + uninstall-info-recursive all-recursive install-data-recursive \ + install-exec-recursive installdirs-recursive install-recursive \ + uninstall-recursive check-recursive installcheck-recursive +DIST_COMMON = Makefile.am Makefile.in +DIST_SUBDIRS = $(SUBDIRS) +all: all-recursive + +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu doc/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe) +uninstall-info-am: + +man1dir = $(mandir)/man1 +install-man1: $(man1_MANS) $(man_MANS) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(man1dir) + @list='$(man1_MANS) $(dist_man1_MANS) $(nodist_man1_MANS)'; \ + l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ + for i in $$l2; do \ + case "$$i" in \ + *.1*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \ + else file=$$i; fi; \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + case "$$ext" in \ + 1*) ;; \ + *) ext='1' ;; \ + esac; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed -e 's/^.*\///'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " $(INSTALL_DATA) $$file $(DESTDIR)$(man1dir)/$$inst"; \ + $(INSTALL_DATA) $$file $(DESTDIR)$(man1dir)/$$inst; \ + done +uninstall-man1: + @$(NORMAL_UNINSTALL) + @list='$(man1_MANS) $(dist_man1_MANS) $(nodist_man1_MANS)'; \ + l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ + for i in $$l2; do \ + case "$$i" in \ + *.1*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed -e 's/^.*\///'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " rm -f $(DESTDIR)$(man1dir)/$$inst"; \ + rm -f $(DESTDIR)$(man1dir)/$$inst; \ + done + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @set fnord $$MAKEFLAGS; amf=$$2; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @set fnord $$MAKEFLAGS; amf=$$2; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done + +ETAGS = etags +ETAGSFLAGS = + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -f $$subdir/TAGS && tags="$$tags -i $$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$tags$$unique" \ + || $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = .. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @list='$(DISTFILES)'; for file in $$list; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkinstalldirs) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d $(distdir)/$$subdir \ + || mkdir $(distdir)/$$subdir \ + || exit 1; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" \ + distdir=../$(distdir)/$$subdir \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(MANS) +installdirs: installdirs-recursive +installdirs-am: + $(mkinstalldirs) $(DESTDIR)$(man1dir) + +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-recursive + +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-recursive + +dvi-am: + +info: info-recursive + +info-am: + +install-data-am: install-man + +install-exec-am: + +install-info: install-info-recursive + +install-man: install-man1 + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic + +uninstall-am: uninstall-info-am uninstall-man + +uninstall-info: uninstall-info-recursive + +uninstall-man: uninstall-man1 + +.PHONY: $(RECURSIVE_TARGETS) GTAGS all all-am check check-am clean \ + clean-generic clean-recursive distclean distclean-generic \ + distclean-recursive distclean-tags distdir dvi dvi-am \ + dvi-recursive info info-am info-recursive install install-am \ + install-data install-data-am install-data-recursive \ + install-exec install-exec-am install-exec-recursive \ + install-info install-info-am install-info-recursive install-man \ + install-man1 install-recursive install-strip installcheck \ + installcheck-am installdirs installdirs-am \ + installdirs-recursive maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-generic \ + mostlyclean-recursive tags tags-recursive uninstall \ + uninstall-am uninstall-info-am uninstall-info-recursive \ + uninstall-man uninstall-man1 uninstall-recursive + + +gramps.1: $(top_builddir)/config.status gramps.1.in + cd $(top_builddir) && CONFIG_FILES=doc/$@ $(SHELL) ./config.status +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/gramps2/doc/extending-gramps/C/Makefile.am b/gramps2/doc/extending-gramps/C/Makefile.am new file mode 100644 index 000000000..31cb3cd32 --- /dev/null +++ b/gramps2/doc/extending-gramps/C/Makefile.am @@ -0,0 +1,10 @@ +SGML_FILES = + +figs = + +docname = extending-gramps +lang = C +omffile = extending-gramps-C.omf +sgml_ents = +include ${top_srcdir}/doc/sgmldocs.make +dist-hook: app-dist-hook diff --git a/gramps2/doc/extending-gramps/C/Makefile.in b/gramps2/doc/extending-gramps/C/Makefile.in new file mode 100644 index 000000000..fcac383c3 --- /dev/null +++ b/gramps2/doc/extending-gramps/C/Makefile.in @@ -0,0 +1,240 @@ +# Makefile.in generated by automake 1.6.3 from Makefile.am. +# @configure_input@ + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : + +EXEEXT = @EXEEXT@ +OBJEXT = @OBJEXT@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +AMTAR = @AMTAR@ +AWK = @AWK@ +BINSH = @BINSH@ +CC = @CC@ +DEPDIR = @DEPDIR@ +DISABLE_SCROLLKEEPER = @DISABLE_SCROLLKEEPER@ +GNOMEHELP = @GNOMEHELP@ +HAVE_GNOME_CONFIG = @HAVE_GNOME_CONFIG@ +HAVE_JW = @HAVE_JW@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +JW = @JW@ +LANGUAGES = @LANGUAGES@ +LIBS = @LIBS@ +MOFILES = @MOFILES@ +MSGFMT = @MSGFMT@ +P15_INCLUDES = @P15_INCLUDES@ +P20_INCLUDES = @P20_INCLUDES@ +P21_INCLUDES = @P21_INCLUDES@ +P22_INCLUDES = @P22_INCLUDES@ +PACKAGE = @PACKAGE@ +POFILES = @POFILES@ +PYTHON = @PYTHON@ +PYTHON22 = @PYTHON22@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RELEASE = @RELEASE@ +SCROLLKEEPER_CONFIG = @SCROLLKEEPER_CONFIG@ +SCROLLKEEPER_REQUIRED = @SCROLLKEEPER_REQUIRED@ +STRIP = @STRIP@ +VERSION = @VERSION@ +VERSIONSTRING = @VERSIONSTRING@ +ZIP = @ZIP@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +SGML_FILES = + +figs = + +docname = extending-gramps +lang = C +omffile = extending-gramps-C.omf +sgml_ents = +subdir = doc/extending-gramps/C +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_CLEAN_FILES = +DIST_SOURCES = +DIST_COMMON = Makefile.am Makefile.in +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu doc/extending-gramps/C/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe) +uninstall-info-am: +tags: TAGS +TAGS: + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @list='$(DISTFILES)'; for file in $$list; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkinstalldirs) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="${top_distdir}" distdir="$(distdir)" \ + dist-hook +check-am: all-am +check: check-am +all-am: Makefile + +installdirs: + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic + +uninstall-am: uninstall-info-am + +.PHONY: all all-am check check-am clean clean-generic distclean \ + distclean-generic distdir dvi dvi-am info info-am install \ + install-am install-data install-data-am install-exec \ + install-exec-am install-info install-info-am install-man \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic uninstall uninstall-am uninstall-info-am + +include ${top_srcdir}/doc/sgmldocs.make +dist-hook: app-dist-hook +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/gramps2/doc/extending-gramps/C/extending-gramps-C.omf b/gramps2/doc/extending-gramps/C/extending-gramps-C.omf new file mode 100644 index 000000000..4f518e90b --- /dev/null +++ b/gramps2/doc/extending-gramps/C/extending-gramps-C.omf @@ -0,0 +1,14 @@ + + + + + Writing GRAMPS Extensions + + + GNOME|Applications + + + + + + diff --git a/gramps2/doc/extending-gramps/C/extending-gramps.sgml b/gramps2/doc/extending-gramps/C/extending-gramps.sgml new file mode 100644 index 000000000..3704fc45c --- /dev/null +++ b/gramps2/doc/extending-gramps/C/extending-gramps.sgml @@ -0,0 +1,774 @@ + +]> + + + +
+ + + Writing Extentions for gramps + + 2001 + Donald N. Allingham + + + + + + + + + + + Permission is granted to copy, distribute and/or modify this + document under the terms of the GNU Free Documentation + License, Version 1.1 or any later version + published by the Free Software Foundation with no Invariant + Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy + of the license can be found here. + + + Many of the names used by companies to distinguish their products + and services are claimed as trademarks. Where those names appear + in any GNOME documentation, and those trademarks are made aware to + the members of the GNOME Documentation Project, the names have + been printed in caps or initial caps. + + + + + + + + + + + + + + + + + + + + This is version 1.0 of the Writing Extentions for gramps manual. + + + + + + + Introduction + + gramps was intended from the start to + allow the user to extend it through a plugin system. Five types of + plugins are supported - filters, reports, tools, import filters, + and export filters. In a way, an export filter can be viewed as a + special type of report, and an import filter can be viewed as a + special type of tool. + + + All plugins are written in the python + language. + + + Filters + + A filter is a plugin that be used to temporarily display or hide + individuals in the People View. The + filter is the simplest form of plugin, which only needs to + determine if a person meets or fails to meet its criteria. It + operates on a single person at a time. + + + Filters should never alter a database. + + + + Reports + + A report is a plugin that generates output. The output may be in + either a interactive, graphical form, or as an output + file. Report plugins are passed a reference to the internal + database and a reference to the active person, which allows the + plugn to operate on a single person, the entire database, or + anything in between. + + + Plugins that conform to the reportplugin interface appear in the + + Reports + + menu and in the Report Selection dialog + box. + + + A report should never alter the database. + + + + Tools + + A tool is a plugin that alters the database. It may perform + something as small changing the case of some text to something + as complex as merging redundant individuals. Tools plugins are + passed a reference to the internal database, the active person, + and a callback function. The callback function is used to notify + the main program if it needs to update the display with any + modified information. + + + Plugins that conform to the tool plugin interface appear in the + + Tools + + menu and in the Tool Selection dialog + box. + + + A tool is allowed (and usually expected) to alter the database. + + + + Import Filters + + An import filter is a plugin that adds information from another + source to the database. It is similar to a tool, but is called + differently to allow gramps to distinguish it from a tool. + + + Plugins that conform to the import filter calling syntax appear + in the + + File + Import + + menu. + + + An import filter is allowed to modify the database. + + + + Export Filters + + An export filter is a plugin that translates the gramps database + into the format expected by another program. Since it generates + an output file, it is similar to a report generator. However, + its calling syntax is different, so that gramps knows how to + distiguish it from a report generator. + + + Plugins that conform to the export filter calling syntax appear + in the + + File + Export + + menu. + + + An export filter should not alter the database. + + + + + + + + Writing Filters + + Users can create their own filters and add them to + gramps. By adding the filter to the + user's private filter directory (~/.gramps/filters), the filter will + be automatically recognized the next time that the program is + started. + + + Creating a filter + + Each filter is a class derived from the + Filter.Filter class. The + __init__ task may be overridden, but if so, + should call the __init__ function on the + Filter.Filter class. The parent class + provides the variable self.text, which + contains the text string passed as the qualifier. This string + provides additional information provided by the user. For + example, if the filter is used to match names, the qualifier + would be used to provide the name that is being compared + against. + + + All filter classes must define a match + function. The function takes one argument (other than + self), which is an object of type + Person to compare against. The function + should return a 1 if the person matches the filter, or a zero if + the person does not. + + + Each filter must be registered, so that + gramps knows about it. This is + accomplished by calling the + Filter.register_filter function. This + function takes three arguments - the filter class, a + description, and flag that indicates if the qualifier string is + needed. The description string appears in the pull down + interface within gramps, and helps + the user choose the appropriate filter. The qualifier flag tells + gramps whether or not the filter + needs a qualifier string. If this flag is 0, + gramps will disable the entry of a + qualifier string. + +
+ Sample filter implementation + + +import Filter +import string + +# class definition + +class SubString(Filter.Filter): + + def match(self,person): + name = person.getPrimaryName().getName() + return string.find(name,self.text) >= 0 + +Filter.register_filter(SubString, + description="Names that contain a substring", + qualifier=1) + + +
+
+
+ + + + + Writing Reports + + Users can create their own report generators and add them to + gramps. By adding the report generator + to the user's private plugin directory (~/.gramps/plugins), the report + generator will be automatically recognized the next time that the + program is started. + + + Creating a report generator + + Fewer restrictions are made on report generators than on + filters. The report generator is passed the current + gramps database and the active + person. The generator needs to take special care to make sure + that it does not alter the database in anyway. + + + A report generator is a function that takes two arguments + — a database (of type RelDataBase) + and the currently selected person (of type + Person). When called, this task should + generate the desired report. + + + This function's implementation can be as simple as generating + output without the user's intervention, or it could display a + graphical interface to allow the user to select options and + customize a report. + + + As with filters, the report generator must be registered before + gramps will understand it. The report + generator is registered using the + Plugins.register_report. This function + takes five arguments. + + + + + The report generation task This task + that generates the report. + + + + + The report category The category in + which the report is grouped in the + Reports menu and + in the Report Selection dialog. + + + + + The report name + The name of the report. + + + + + A text description of the report The + description appears in the report selection tool to provide + the user with a description of what the tools does. + + + + + A graphic logo in XPM format This may + be either a path to a filename, or a list of strings + containting the XPM data. If a filename is specified, care + must be taken to make sure the file location is relocatable + and can be determined at runtime. + + + + + While only the task and report name are required, it is + recommended to provide all five parameters. + +
+ Sample report implementation + + +import Plugins + +def report(database,person): + ... actual code ... + +Plugins.register_report( + task=report, + category="Category", + name="Report Name", + description="A text descripition of the report generator", + xpm="%s/myfile.xpm" % os.path.dirname(__file__) +) + +
+
+ + A little help - Format Interfaces + + gramps provides some help with + writing reports. Several generic python classes exist that aid + in the writing of report generators. These classes provide an + abstract interface for a type of document, such as a drawing, + word processor document, or a spreadsheet. From these core + classes, gramps derives interfaces to + various document formats. This means that by coding to the + generic word processing class (TextDoc), a + report generator can instant access to multiple file formats + (such as HTML, OpenOffice, and AbiWord). + + + This scheme of deriving a output format from a generic base + class also makes it easier to add new formats. Creating a new + derivied class targeting a different format (such as + KWord or + LaTeX) makes it easy for existing + report generators to use the new formats. + + +
+ + + + + Writing Tools + + Users can create their own tools and add them to + gramps. By adding the tool to the + user's private plugin directory (~/.gramps/plugins), the tool will be + automatically recognized the next time that + gramps is started. + + + Unlike a report generator, a tool is allowed to modify the + database. The tool is passed the current + gramps database, the active person, + and a callback function. The callback function should be called + with a non-zero argument upon completion of the tool if the + database has been altered. + + + As with filters and report generators, tools must be registered + before gramps will understand it. The + tool is registered using the + Plugins.register_tool. This function takes + four arguments. + + + + + The tool task This task + that executes the tool. + + + + + The tool category The category in which + the tool is grouped in the + Tools menu and in + the Tool Selection dialog. + + + + + The tool name + The name of the tool. + + + + + A text description of the tool The + description appears in the Tool Selection dialog to provide + the user with a description of what the tool does. + + + + + While only the task and report name are required, it is + recommended to provide all five parameters. + +
+ Sample tool implementation + + +import Plugins + +def tool(database,person,callback): + ... actual code ... + callback(1) + +Plugins.register_tool( + task=tool, + category="Category", + name="Tool Name", + description="A text descripition of the tool" +) + +
+
+ + + + + Writing Import Filters + + Import filters are similar to tools, since they are allowed to + modify the databases. An import filter is a task that accepts + three arguments — a database, the filename of the file that + is to be imported, and a callback function. + + + The database may or may not have data already in it. The import + filter cannot assume that data neither already exists nor that the + database is empty. + + + The callback function is different from the callback function used + for tools. The import filter's callback function is used to + indicate progress and update the status bar during the import + process. The function takes a value between 0.0 and 1.0, where 0.0 + represents the start of the import and 1.0 represents the + completion of the import. + + + As with the other plugin types, an import filter must be + registered with gramps. This is + accomplished by calling the + Plugins.register_import task. The + Plugins.register_import accepts two arguments + — the function the performs the import and a string + providing a brief description. This description is used as the + menu entry under the + + File + Import + + menu. + +
+ Sample Import Implementation + + +import Plugins + +def gedcom_import(database,filename,callback): + ... actual code ... + +Plugins.register_import(gedcom_import,"GEDCOM import") + + +
+
+ + + + + Writing Export Filters + + Export filters are similar to report generators. They are not + allowed to modify the database. An export filter accepts three + arguments — a database, the filename of the file that is to + be written, and a callback function. + + + The callback function is indentical from the callback function + used for import filters. The export filter's callback function is + used to indicate progress and update the status bar during the + export process. The function takes a value between 0.0 and 1.0, + where 0.0 represents the start of the export and 1.0 represents + the completion of the export. + + + As with the other plugin types, an export filter must be + registered with gramps. This is + accomplished by calling the + Plugins.register_export task. The + Plugins.register_export accepts two arguments + — the function the performs the import and a string + providing a brief description. This description is used as the + menu entry under the + + File + Export + + menu. + +
+ Sample Export Implementation + + +import Plugins + +def gedcom_export(database,filename,callback): + ... actual code ... + +Plugins.register_export(gedcom_export,"GEDCOM export") + + +
+
+ + Common tasks + + While this manual does not document the + gramps database interface, this section + shows a few common tasks. + + + Printing names of people + + This example shows how to display the name of people in the + database. It assumes that the database is called + db. To get a list of people, it calls the + getPersonMap method, which returns a map of + gramps ID to + Person objects. Calling the + valus method of the returned map returns a + list of people. For each person, the primary name is extracted, + and then the Name object's + getName method is called to build a + presentable name from the individual name components. + +
+ Displaying names + + +for person in db.getPersonMap().values(): + name = person.getPrimaryName() + print name.getName() + + +
+
+ + Displaying the events of person + + This example shows how to display the public events associated + with a person. It assumes that the person is called + person. + +
+ Displaying Event Information + + +for event in person.getEventList(): + if event.getPrivacy() == 0: + print "Event:",event.getName() + print "Date:",event.getDate() + print "Place:",event.getPlaceName() + + +
+
+ + Print the members of each family + + This example shows how to display the parents and children of + each family in the database. It assumes that the database is called + db. + +
+ Displaying Family Information + + +for family in db.getFamilyMap().values: + print "-------------------" + print "Family ID:",family.getId() + father = family.getFather() + if father != None: + print "Father:",father.getPrimaryName().getName() + mother = family.getMother() + if mother != None: + print "Mother:",mother.getPrimaryName().getName() + for child in family.getChildList(): + print "Child:",child.getPrimaryName().getName() + + +
+
+ + Display the marriages/relationships of a person + + This example shows how to display the families and relationships + in which the person is considered a spouse or parent. It assumes + that the person is called person. + + + Relationships between people can be complex. Because someone is + male, does not necessarily mean that the person will be + considered the "Father" of a relationship. In relationships of + type "Partners", the "father" and "mother" of the relationship + should be of the same gender. So to determine the spouse of a + person, it is usually best to compare the person against what is + returned by getFather and + getMother to find the one that is not + equal. It should also be noted that the + getFather and + getMother methods will return None if noone + has been associated with that role in the family. + +
+ Displaying Relationship Information + + +for family in person.getFamilyList(): + print "-------------------" + print "Family ID:",family.getId() + print "Relationship Type:",family.getRelationship() + father = family.getFather() + if father != None and father != person: + print "Spouse:",father.getPrimaryName().getName() + mother = family.getMother() + if mother != None and mother != person: + print "Spouse:",mother.getPrimaryName().getName() + + +
+
+
+ + + + + Authors + + gramps was written by Don Allingham + (dallingham@users.sourceforge.net). To find more + information about gramps, please visit + the gramps + web page. + + + This manual was written by Don Allingham + (dallingham@users.sourceforge.net). + + + + + + + + + License + + 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. + + + A copy of the GNU General Public License is + included as an appendix to the GNOME Users + Guide. You may also obtain a copy of the + GNU General Public License from the Free + Software Foundation by visiting their Web site or by writing to +
+ Free Software Foundation, Inc. 59 Temple Place - + Suite 330 Boston, MA + 02111-1307 USA +
+
+
+
+ + + + + + + + + diff --git a/gramps2/doc/extending-gramps/C/extending-gramps/index.html b/gramps2/doc/extending-gramps/C/extending-gramps/index.html new file mode 100644 index 000000000..42274a813 --- /dev/null +++ b/gramps2/doc/extending-gramps/C/extending-gramps/index.html @@ -0,0 +1,345 @@ + +Writing Extentions for gramps

Writing Extentions for gramps

Copyright © 2001 by Donald N. Allingham


+ + + +

Introduction

+

gramps was intended from the start to + allow the user to extend it through a plugin system. Five types of + plugins are supported - filters, reports, tools, import filters, + and export filters. In a way, an export filter can be viewed as a + special type of report, and an import filter can be viewed as a + special type of tool. +

+

All plugins are written in the python + language. +

+
+ + + + +
+ + + + + + + + + + + + + + +

  Next >>>
  Writing Filters
\ No newline at end of file diff --git a/gramps2/doc/extending-gramps/C/extending-gramps/ln7.html b/gramps2/doc/extending-gramps/C/extending-gramps/ln7.html new file mode 100644 index 000000000..507e5597e --- /dev/null +++ b/gramps2/doc/extending-gramps/C/extending-gramps/ln7.html @@ -0,0 +1,133 @@ + +
Writing Extentions for gramps

Permission is granted to copy, distribute and/or modify this + document under the terms of the GNU Free Documentation + License, Version 1.1 or any later version + published by the Free Software Foundation with no Invariant + Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy + of the license can be found here. +

+

Many of the names used by companies to distinguish their products + and services are claimed as trademarks. Where those names appear + in any GNOME documentation, and those trademarks are made aware to + the members of the GNOME Documentation Project, the names have + been printed in caps or initial caps. +

+


 Home 
 Up 
\ No newline at end of file diff --git a/gramps2/doc/extending-gramps/C/extending-gramps/stylesheet-images/caution.gif b/gramps2/doc/extending-gramps/C/extending-gramps/stylesheet-images/caution.gif new file mode 100644 index 0000000000000000000000000000000000000000..54223291154fed2f9639ce2707976ec58f44169b GIT binary patch literal 1039 zcmZ?wbh9u|lwgox_|5Sh{s9!`7`) zTel`}-MV$}UWUDUlh2)FICt*cwQCI5t^vtw_wF&=yJvXsp5?uJPS2k+Jb!NZ{JG`x z=g;52XL$eq{rB$--@gld|1RgdJ(|D$SrA+HAyGpqN^7x16zrMq{!ibMp=6~-(4c99<%wR z=lEzgH#IP+?pT$g`C;jexlBBNo_IO0=v$GV+9Oerpc~DlP;%tKgT`Z#V$4EkL{=v; zaW)#7)%@r_aPX#>5Fh`Y-v{$g32`gkn0k?^DW$2?_JPMq28Kof%}^J&9pNUYcvyH^ zranA$=y~&6-RTjl4}Cj!qXy}<-Pfit2&px)3vi@}Ecoi+!6qxs r(ZS&8c(6lQJMIjJqGMAJzuvhVhQ!S+Y#bZ9V$SZ``nrvgk--`Oh`uWh literal 0 HcmV?d00001 diff --git a/gramps2/doc/extending-gramps/C/extending-gramps/stylesheet-images/important.gif b/gramps2/doc/extending-gramps/C/extending-gramps/stylesheet-images/important.gif new file mode 100644 index 0000000000000000000000000000000000000000..8793ff2b1fe6ff180580d1f263b157a64934137c GIT binary patch literal 1081 zcmeH`!AlfT0LI^{*tG3oR-;A|YnLrqUC32LCk@;w+gcQ35{x-%&ki*qObA0rgpfEP zNkSw-AR!7N#PfjXv7VRoJi+rkN?A%dN|TiGlx8TUz7Kq#@qO0!BmRH>r3Vsw2JE>2 zm_OhhQ+xjA{Zy`^x}o%D+QinvT2n3`8xZIFU!2Jm zCibJ@!slUgUGME#Ebw1WR0r9yQfVQ3>|s^wfCxZWN}THL422hC6`5zjr-#7krF3w- z{bK1ubo^RbJ>(N#qtUg^aL?i4n#r=&s&L=H5(@^GHyh73m)2i4?+XNCe=6%bchZ4C z7&~1*-O!NFz@VXc@G8x|gJ5|Hx|&`^AIq@7DufTJL=k*SSS$s)v|q2U0#fW|VLhy+z< zR$&&E6#^R%H8b;b_*e)mU}9q9Vv)(X@FBT{fiEEJLgc~*4<eAwF5u#VYwR?e%Shb+u`oHi>KJaB03@|>=hvC84eAr3KCza2Lm7+W|6 WloX~(95gs~e1fv~yck6V25SH;%O9}- literal 0 HcmV?d00001 diff --git a/gramps2/doc/extending-gramps/C/extending-gramps/stylesheet-images/note.gif b/gramps2/doc/extending-gramps/C/extending-gramps/stylesheet-images/note.gif new file mode 100644 index 0000000000000000000000000000000000000000..45fe0864971e4c352a37ce4df664b00f1e054b7e GIT binary patch literal 1070 zcmV+}1kw9PNk%v~VHf}y0QUd@000010RaL60s{jB1Ox;H1qB8M1_uWR2nYxX2?+`c z3JVJh3=9kk3JMMm4i66x5D*X%5fKs+5)%^>6ciK{6%`g178e&67#J8C85tTH8XFrM z92^`S9UUGX9v>ecARr(iAt53nA|oRsBqSsyB_$>%CMPE+C@3f?DJd!{Dl021EG#T7 zEiEoCE-x=HFfcGNF)=bSGBYzXG&D3dH8nOiHa9mnI5;>tIXOByIy*Z%JUl!-Jv}}? zK0iM{KtMo2K|w-7LPJACL_|bIMMXwNMn^|SNJvOYNl8jdN=r*iOiWBoO-)WtPESuy zP*6}&QBhJ-Qd3h?R8&+|RaI72R##V7SXfwDSy@_IT3cINTwGjTU0q&YUSD5dU|?Wj zVPRroVq;@tWMpJzWo2e&W@l$-XlQ6@X=!R|YHMq2Y;0_8ZEbFDZf|dIaBy&OadC2T za&vQYbaZreb#-=jc6WDoczAeud3kzzdV70&e0+R;eSLm@et&;|fPjF3fq{a8f`fyD zgoK2Jg@uNOhKGlTh=_=ZiHVAeii?YjjEszpjg5|uj*pLzkdTm(k&%*;l9Q8@l$4Z} zm6ev3mY0{8n3$NEnVFiJnwy)OoSdAUot>VZo}ZteprD|kp`oIpqNAguq@<*!rKP5( zrl+T;sHmu^si~@}s;jH3tgNi9t*x%EuCK4Ju&}VPv9YqUva_?Zw6wIfwY9dkwzs#p zxVX5vxw*Q!y1To(yu7@dCU$jHda z$;ryf%FD~k%*@Qq&CSlv&d<-!(9qD)(b3Y<($mw^)YR0~)z#M4*4Nk9*x1lt)=I7_<=;-L_>FMg~>g((4 z?Ck9A?d|UF?(gsK@bK{Q@$vHV^7Hfa^z`)g_4W4l_V@Sq`1ttw`T6?#`uqF){QUg= z{r&#_{{R2~EC2ui02lxm000R70RIUbNbsCNg9H;Q6xgd2rBVHES(3_Ku{tyHgqN;d+?Tzju!Z);;TuQDVJwRQAHSkR)YFdrEO!v&XBk zJwOwl{a4GHz*-^KY@Rd5rovJavBo?tB{Lyo6k9@$D)OJx<0pIB#+rEO&f%|l4*kcd o_`qJ2pi}&6ogi$&oxc>r#EMbwId40)HiwBGBuL!a2L=QHJ03R*g8%>k literal 0 HcmV?d00001 diff --git a/gramps2/doc/extending-gramps/C/extending-gramps/stylesheet-images/prev.gif b/gramps2/doc/extending-gramps/C/extending-gramps/stylesheet-images/prev.gif new file mode 100644 index 0000000000000000000000000000000000000000..0894d9ecb26316a61e914879c48520cc8aee392d GIT binary patch literal 944 zcmZ?wbh9u|RA%63_|Cw<(83_lBcRx$s5wQ`aE_tV94D`(UO{VvvbSaz?=5cG+tPEa zXUe%LOV2G`du{Epd&kZ_KX>i@wde1jzyJRJ|0oy@fgv6OI>0Qz0Llvt98(!IIb=LG zEI8QAA*>a1V#7k`W+6F`nhzV2ni%sMgrT8{g_D_uZNY^D443EGIST1`39V>a$f&~eRijiP ybPlVqjKR$hN{*ZiRYDmKlawRc1?88Ba5ye<;N%wZnBfq<^#1+^W@{b}25SIMS`<+L literal 0 HcmV?d00001 diff --git a/gramps2/doc/extending-gramps/C/extending-gramps/stylesheet-images/tip.gif b/gramps2/doc/extending-gramps/C/extending-gramps/stylesheet-images/tip.gif new file mode 100644 index 0000000000000000000000000000000000000000..0258195484893202b233c111424f6f6812eacb47 GIT binary patch literal 1029 zcmV+g1p50&Nk%v~VHp4z0QUd@000010RaL60s{jB1Ox;H1qB8M1_uWR2nYxX2?+`c z3JVJh3=9kk3JMMm4i66x5D*X%5fKs+5)%^>6ciK{6%`g178e&67#J8C85tTH8XFrM z92^`S9UUGX9v>ecARr(iAt53nA|oRsBqSsyB_$>%CMPE+C@3f?DJd!{Dl021EG#T7 zEiEoCE-x=HFfcGNF)=bSGBYzXG&D3dH8nOiHa9mnI5;>tIXOByIy*Z%JUl!-Jv}}? zK0iM{KtMo2K|w-7LPJACL_|bIMMXwNMn^|SNJvOYNl8jdN=r*iOiWBoO-)WtPESuy zP*6}&QBhJ-Qd3h?R8&+|RaI72R##V7SXfwDSy@_IT3cINTwGjTU0q&YUSD5dU|?Wj zVPRroVq;@tWMpJzWo2e&W@l$-XlQ6@X=!R|YHMq2Y;0_8ZEbFDZf|dIaBy&OadC2T za&vQYbaZreb#-=jc6WDoczAeud3kzzdV70&e0+R;eSLm@et&;|fPjF3fq{a8f`fyD zgoK2Jg@uNOhKGlTh=_=ZiHVAeii?YjjEszpjg5|uj*pLzkdTm(k&%*;l9Q8@l$4Z} zm6ev3mY0{8n3$NEnVFiJnwy)OoSdAUot>VZo}ZteprD|kp`oIpqNAguq@<*!rKP5( zrl+T;sHmu^si~@}s;jH3tgNi9t*x%EuCK4Ju&}VPv9YqUva_?Zw6wIfwY9dkwzs#p zxVX5vxw*Q!y1To(yu7@dCU$jHda z$;ryf%FD~k%*@Qq&CSlv&d<-!(9qD)(b3Y<($mw^)YR0~)z#M4*4Nk9*x1lt)=I7_<=;-L_>FMg~>g((4 z?Ck9A?d|UF?(gsK@bK{Q@$vHV^7Hfa^z`)g_4W4l_V@Sq`1ttw`T6?#`uqF){QUg= z{r&#_{{R2~EC2ui02u%n000Q;0RIUbNbsCNg9H;Q6nM^9DN3axLUeX-VZwX8CZ4;u zu^}Z&&MY#7=uMQzzXzjLK(DOA}@A62gx007pM9e9q?%bp7<-HvA!GFEE{U`SDUWKJX{uk@&whU_+wL7l%xqVSOWl-?jltH literal 0 HcmV?d00001 diff --git a/gramps2/doc/extending-gramps/C/extending-gramps/stylesheet-images/toc-minus.gif b/gramps2/doc/extending-gramps/C/extending-gramps/stylesheet-images/toc-minus.gif new file mode 100644 index 0000000000000000000000000000000000000000..40ebe61e46a6bd1d8c2c5292af72365d489c6c56 GIT binary patch literal 843 zcmZ?wbhEHb~0HBcxg#Z8m literal 0 HcmV?d00001 diff --git a/gramps2/doc/extending-gramps/C/extending-gramps/stylesheet-images/toc-plus.gif b/gramps2/doc/extending-gramps/C/extending-gramps/stylesheet-images/toc-plus.gif new file mode 100644 index 0000000000000000000000000000000000000000..3e9e7d55a3dde56cd0f6596f3cd6d555323ab273 GIT binary patch literal 846 zcmZ?wbhEHb9$=O@8TlTi}9P62KZpzks zTaVp4cJBGPYwxc;fB*db_xJxt!Dt8!mk`haW&s9JUSQy;XHe#l@z}87U^9oXR?LZl z2hFTJQZ5+>5*S|7To{< literal 0 HcmV?d00001 diff --git a/gramps2/doc/extending-gramps/C/extending-gramps/stylesheet-images/warning.gif b/gramps2/doc/extending-gramps/C/extending-gramps/stylesheet-images/warning.gif new file mode 100644 index 0000000000000000000000000000000000000000..9c1104c2b15445dd0824fd6515659ee0fe18bfd9 GIT binary patch literal 1052 zcmeHG&1=(e0DVo%7HbcgOo>=$KTg3N?8@v)L4uu*Wm2|w7mJ7m-NYeQ<`fNst{-gH zFn0)2C7>b!DFnRyWC)cilI^esda&T7hYgk~Lh8jLf;0XP9`|@W-t9deyphrKLy-&% z79D^F7}yqmfCECnSwH}iKpL00umtG6h$SORMKfh6jeZtMpZ#ji$ztIRaMmh zEfmsZnE-_2h$In3A*xCUsR1=js}Z6DI>YEJtLwS}7z|^utif?jplNHf*#cUvmI;`C zziFBlV9~T?S$5k_r?Us_?PGs||Fl3qZ2bEI=s&nmd)MP#Qi1N-@~vmDl{m)~TAi))WhOkCPn3Z5%1`%*jnZAb7!@1gPPg@=K{SYIGf{obyZgePn&I~mRM zIoCk^vfShUy4LNWnD(!*OLB*2uQN=Ym!3v^_4dl=Pj;;Gz#|UMNR;Eq)oWvScO<97 zyK(r@;K@9Ce=cu6i!Z>P|8001xvKE);`Av#$M(C9mQy#DzRgV?^bGMYDqk{u&KaJ3 zU-1=2b{nx{ugB+K65i`gFp1Iaf%x&j9n1JZecW7F%}mcv#I|oXB2&Z9&%7$#GO4+h QO1&1l=Xra8>T)6W7esUR#{d8T literal 0 HcmV?d00001 diff --git a/gramps2/doc/extending-gramps/C/extending-gramps/t1.html b/gramps2/doc/extending-gramps/C/extending-gramps/t1.html new file mode 100644 index 000000000..42274a813 --- /dev/null +++ b/gramps2/doc/extending-gramps/C/extending-gramps/t1.html @@ -0,0 +1,345 @@ + +Writing Extentions for gramps

Writing Extentions for gramps

Copyright © 2001 by Donald N. Allingham


+ + + +

Introduction

+

gramps was intended from the start to + allow the user to extend it through a plugin system. Five types of + plugins are supported - filters, reports, tools, import filters, + and export filters. In a way, an export filter can be viewed as a + special type of report, and an import filter can be viewed as a + special type of tool. +

+

All plugins are written in the python + language. +

+
+ + + + +
+ + + + + + + + + + + + + + +

  Next >>>
  Writing Filters
\ No newline at end of file diff --git a/gramps2/doc/extending-gramps/C/extending-gramps/x131.html b/gramps2/doc/extending-gramps/C/extending-gramps/x131.html new file mode 100644 index 000000000..59d008e61 --- /dev/null +++ b/gramps2/doc/extending-gramps/C/extending-gramps/x131.html @@ -0,0 +1,282 @@ + +Writing Tools
Writing Extentions for gramps
<<< PreviousNext >>>

Writing Tools

+

Users can create their own tools and add them to + gramps. By adding the tool to the + user's private plugin directory (~/.gramps/plugins), the tool will be + automatically recognized the next time that + gramps is started. +

+

Unlike a report generator, a tool is allowed to modify the + database. The tool is passed the current + gramps database, the active person, + and a callback function. The callback function should be called + with a non-zero argument upon completion of the tool if the + database has been altered. +

+

As with filters and report generators, tools must be registered + before gramps will understand it. The + tool is registered using the + Plugins.register_tool. This function takes + four arguments. +

+

  • The tool task This task + that executes the tool. +

    +
  • The tool category The category in which + the tool is grouped in the + Tools menu and in + the Tool Selection dialog. +

    +
  • The tool name + The name of the tool. +

    +
  • A text description of the tool The + description appears in the Tool Selection dialog to provide + the user with a description of what the tool does. +

    +
+

While only the task and report name are required, it is + recommended to provide all five parameters. +

+
+

<<< PreviousHomeNext >>>
Writing Reports Writing Import Filters
\ No newline at end of file diff --git a/gramps2/doc/extending-gramps/C/extending-gramps/x162.html b/gramps2/doc/extending-gramps/C/extending-gramps/x162.html new file mode 100644 index 000000000..790ff3932 --- /dev/null +++ b/gramps2/doc/extending-gramps/C/extending-gramps/x162.html @@ -0,0 +1,219 @@ + +Writing Import Filters
Writing Extentions for gramps
<<< PreviousNext >>>

Writing Import Filters

+

Import filters are similar to tools, since they are allowed to + modify the databases. An import filter is a task that accepts + three arguments a database, the filename of the file that + is to be imported, and a callback function. +

+

The database may or may not have data already in it. The import + filter cannot assume that data neither already exists nor that the + database is empty. +

+

The callback function is different from the callback function used + for tools. The import filter's callback function is used to + indicate progress and update the status bar during the import + process. The function takes a value between 0.0 and 1.0, where 0.0 + represents the start of the import and 1.0 represents the + completion of the import. +

+

As with the other plugin types, an import filter must be + registered with gramps. This is + accomplished by calling the + Plugins.register_import task. The + Plugins.register_import accepts two arguments + the function the performs the import and a string + providing a brief description. This description is used as the + menu entry under the + + + + + + +File+ ++ + + + + + ->Import+ ++ + + + + menu. +

+
+

<<< PreviousHomeNext >>>
Writing Tools Writing Export Filters
\ No newline at end of file diff --git a/gramps2/doc/extending-gramps/C/extending-gramps/x177.html b/gramps2/doc/extending-gramps/C/extending-gramps/x177.html new file mode 100644 index 000000000..6654c0a36 --- /dev/null +++ b/gramps2/doc/extending-gramps/C/extending-gramps/x177.html @@ -0,0 +1,180 @@ + +Writing Export Filters
Writing Extentions for gramps
<<< Previous 

Writing Export Filters

+

Export filters are similar to report generators. They are not + allowed to modify the database. An export filter accepts three + arguments a database, the filename of the file that is to + be written, and a callback function. +

+

The callback function is indentical from the callback function + used for import filters. The export filter's callback function is + used to indicate progress and update the status bar during the + export process. The function takes a value between 0.0 and 1.0, + where 0.0 represents the start of the export and 1.0 represents + the completion of the export. +

+

As with the other plugin types, an export filter must be + registered with gramps. This is + accomplished by calling the + Plugins.register_export task. The + Plugins.register_export accepts two arguments + the function the performs the import and a string + providing a brief description. This description is used as the + menu entry under the + + + + + + +File+ ++ + + + + + ->Export+ ++ + + + + menu. +

+

<<< PreviousHome 
Writing Import Filters  
\ No newline at end of file diff --git a/gramps2/doc/extending-gramps/C/extending-gramps/x57.html b/gramps2/doc/extending-gramps/C/extending-gramps/x57.html new file mode 100644 index 000000000..52c5b09db --- /dev/null +++ b/gramps2/doc/extending-gramps/C/extending-gramps/x57.html @@ -0,0 +1,276 @@ + +Writing Filters
Writing Extentions for gramps
<<< PreviousNext >>>

Writing Filters

+

Users can create their own filters and add them to + gramps. By adding the filter to the + user's private filter directory (~/.gramps/filters), the filter will + be automatically recognized the next time that the program is + started. +

+

Creating a filter

+

Each filter is a class derived from the + Filter.Filter class. The + __init__ task may be overridden, but if so, + should call the __init__ function on the + Filter.Filter class. The parent class + provides the variable self.text, which + contains the text string passed as the qualifier. This string + provides additional information provided by the user. For + example, if the filter is used to match names, the qualifier + would be used to provide the name that is being compared + against. +

+

All filter classes must define a match + function. The function takes one argument (other than + self), which is an object of type + Person to compare against. The function + should return a 1 if the person matches the filter, or a zero if + the person does not. +

+

Each filter must be registered, so that + gramps knows about it. This is + accomplished by calling the + Filter.register_filter function. This + function takes three arguments - the filter class, a + description, and flag that indicates if the qualifier string is + needed. The description string appears in the pull down + interface within gramps, and helps + the user choose the appropriate filter. The qualifier flag tells + gramps whether or not the filter + needs a qualifier string. If this flag is 0, + gramps will disable the entry of a + qualifier string. +

+
+
+

<<< PreviousHomeNext >>>
Writing Extentions for gramps Writing Reports
\ No newline at end of file diff --git a/gramps2/doc/extending-gramps/C/extending-gramps/x83.html b/gramps2/doc/extending-gramps/C/extending-gramps/x83.html new file mode 100644 index 000000000..1faa64d78 --- /dev/null +++ b/gramps2/doc/extending-gramps/C/extending-gramps/x83.html @@ -0,0 +1,369 @@ + +Writing Reports
Writing Extentions for gramps
<<< PreviousNext >>>

Writing Reports

+

Users can create their own report generators and add them to + gramps. By adding the report generator + to the user's private plugin directory (~/.gramps/plugins), the report + generator will be automatically recognized the next time that the + program is started. +

+

Creating a report generator

+

Fewer restrictions are made on report generators than on + filters. The report generator is passed the current + gramps database and the active + person. The generator needs to take special care to make sure + that it does not alter the database in anyway. +

+

A report generator is a function that takes two arguments + a database (of type RelDataBase) + and the currently selected person (of type + Person). When called, this task should + generate the desired report. +

+

This function's implementation can be as simple as generating + output without the user's intervention, or it could display a + graphical interface to allow the user to select options and + customize a report. +

+

As with filters, the report generator must be registered before + gramps will understand it. The report + generator is registered using the + Plugins.register_report. This function + takes five arguments. +

+

  • The report generation task This task + that generates the report. +

    +
  • The report category The category in + which the report is grouped in the + Reports menu and + in the Report Selection dialog. +

    +
  • The report name + The name of the report. +

    +
  • A text description of the report The + description appears in the report selection tool to provide + the user with a description of what the tools does. +

    +
  • A graphic logo in XPM format This may + be either a path to a filename, or a list of strings + containting the XPM data. If a filename is specified, care + must be taken to make sure the file location is relocatable + and can be determined at runtime. +

    +
+

While only the task and report name are required, it is + recommended to provide all five parameters. +

+
+
+

A little help - Format Interfaces

+

gramps provides some help with + writing reports. Several generic python classes exist that aid + in the writing of report generators. These classes provide an + abstract interface for a type of document, such as a drawing, + word processor document, or a spreadsheet. From these core + classes, gramps derives interfaces to + various document formats. This means that by coding to the + generic word processing class (TextDoc), a + report generator can instant access to multiple file formats + (such as HTML, OpenOffice, and AbiWord). +

+

This scheme of deriving a output format from a generic base + class also makes it easier to add new formats. Creating a new + derivied class targeting a different format (such as + KWord or + LaTeX) makes it easy for existing + report generators to use the new formats. +

+
+

<<< PreviousHomeNext >>>
Writing Filters Writing Tools
\ No newline at end of file diff --git a/gramps2/doc/extending-gramps/C/index.html b/gramps2/doc/extending-gramps/C/index.html new file mode 100644 index 000000000..42274a813 --- /dev/null +++ b/gramps2/doc/extending-gramps/C/index.html @@ -0,0 +1,345 @@ + +Writing Extentions for gramps

Writing Extentions for gramps

Copyright © 2001 by Donald N. Allingham


+ + + +

Introduction

+

gramps was intended from the start to + allow the user to extend it through a plugin system. Five types of + plugins are supported - filters, reports, tools, import filters, + and export filters. In a way, an export filter can be viewed as a + special type of report, and an import filter can be viewed as a + special type of tool. +

+

All plugins are written in the python + language. +

+
+ + + + +
+ + + + + + + + + + + + + + +

  Next >>>
  Writing Filters
\ No newline at end of file diff --git a/gramps2/doc/extending-gramps/C/omf_timestamp b/gramps2/doc/extending-gramps/C/omf_timestamp new file mode 100644 index 000000000..e69de29bb diff --git a/gramps2/doc/extending-gramps/Makefile.am b/gramps2/doc/extending-gramps/Makefile.am new file mode 100644 index 000000000..dbed85073 --- /dev/null +++ b/gramps2/doc/extending-gramps/Makefile.am @@ -0,0 +1,4 @@ +# Process this file with automake to produce Makefile.in + +SUBDIRS = C + diff --git a/gramps2/doc/extending-gramps/Makefile.in b/gramps2/doc/extending-gramps/Makefile.in new file mode 100644 index 000000000..1a6b4eceb --- /dev/null +++ b/gramps2/doc/extending-gramps/Makefile.in @@ -0,0 +1,352 @@ +# Makefile.in generated by automake 1.6.3 from Makefile.am. +# @configure_input@ + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# Process this file with automake to produce Makefile.in +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : + +EXEEXT = @EXEEXT@ +OBJEXT = @OBJEXT@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +AMTAR = @AMTAR@ +AWK = @AWK@ +BINSH = @BINSH@ +CC = @CC@ +DEPDIR = @DEPDIR@ +DISABLE_SCROLLKEEPER = @DISABLE_SCROLLKEEPER@ +GNOMEHELP = @GNOMEHELP@ +HAVE_GNOME_CONFIG = @HAVE_GNOME_CONFIG@ +HAVE_JW = @HAVE_JW@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +JW = @JW@ +LANGUAGES = @LANGUAGES@ +LIBS = @LIBS@ +MOFILES = @MOFILES@ +MSGFMT = @MSGFMT@ +P15_INCLUDES = @P15_INCLUDES@ +P20_INCLUDES = @P20_INCLUDES@ +P21_INCLUDES = @P21_INCLUDES@ +P22_INCLUDES = @P22_INCLUDES@ +PACKAGE = @PACKAGE@ +POFILES = @POFILES@ +PYTHON = @PYTHON@ +PYTHON22 = @PYTHON22@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RELEASE = @RELEASE@ +SCROLLKEEPER_CONFIG = @SCROLLKEEPER_CONFIG@ +SCROLLKEEPER_REQUIRED = @SCROLLKEEPER_REQUIRED@ +STRIP = @STRIP@ +VERSION = @VERSION@ +VERSIONSTRING = @VERSIONSTRING@ +ZIP = @ZIP@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ + +SUBDIRS = C +subdir = doc/extending-gramps +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_CLEAN_FILES = +DIST_SOURCES = + +RECURSIVE_TARGETS = info-recursive dvi-recursive install-info-recursive \ + uninstall-info-recursive all-recursive install-data-recursive \ + install-exec-recursive installdirs-recursive install-recursive \ + uninstall-recursive check-recursive installcheck-recursive +DIST_COMMON = Makefile.am Makefile.in +DIST_SUBDIRS = $(SUBDIRS) +all: all-recursive + +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu doc/extending-gramps/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe) +uninstall-info-am: + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @set fnord $$MAKEFLAGS; amf=$$2; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @set fnord $$MAKEFLAGS; amf=$$2; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done + +ETAGS = etags +ETAGSFLAGS = + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -f $$subdir/TAGS && tags="$$tags -i $$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$tags$$unique" \ + || $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @list='$(DISTFILES)'; for file in $$list; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkinstalldirs) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d $(distdir)/$$subdir \ + || mkdir $(distdir)/$$subdir \ + || exit 1; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" \ + distdir=../$(distdir)/$$subdir \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: + +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-recursive + +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-recursive + +dvi-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic + +uninstall-am: uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) GTAGS all all-am check check-am clean \ + clean-generic clean-recursive distclean distclean-generic \ + distclean-recursive distclean-tags distdir dvi dvi-am \ + dvi-recursive info info-am info-recursive install install-am \ + install-data install-data-am install-data-recursive \ + install-exec install-exec-am install-exec-recursive \ + install-info install-info-am install-info-recursive install-man \ + install-recursive install-strip installcheck installcheck-am \ + installdirs installdirs-am installdirs-recursive \ + maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-generic \ + mostlyclean-recursive tags tags-recursive uninstall \ + uninstall-am uninstall-info-am uninstall-info-recursive \ + uninstall-recursive + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/gramps2/doc/gramps-manual/C/Makefile.am b/gramps2/doc/gramps-manual/C/Makefile.am new file mode 100644 index 000000000..dde918710 --- /dev/null +++ b/gramps2/doc/gramps-manual/C/Makefile.am @@ -0,0 +1,53 @@ +SGML_FILES = gramps-manual.sgml + +figs = \ + figures/addmedia.png \ + figures/druidpg1.png \ + figures/editbookmarks.png \ + figures/ep-address.png \ + figures/ep-altname.png \ + figures/ep-attributes.png \ + figures/ep-event.png \ + figures/ep-gallery.png \ + figures/ep-general.png \ + figures/ep-internet.png \ + figures/ep-lds.png \ + figures/ep-notes.png \ + figures/familyview.png \ + figures/filter.png \ + figures/globalmedia.png \ + figures/gotobookmark.png \ + figures/localmedia.png \ + figures/mainwin.png \ + figures/mediaview.png \ + figures/opendb.png \ + figures/pedegreesel.png \ + figures/pedegreeview.png \ + figures/peoplelist.png \ + figures/placelist.png \ + figures/prefs-bars.png \ + figures/prefs-colors.png \ + figures/prefs-dates.png \ + figures/prefs-disp.png \ + figures/prefs-find.png \ + figures/prefs-gen.png \ + figures/prefs-guess.png \ + figures/prefs-ids.png \ + figures/prefs-main.png \ + figures/prefs-media.png \ + figures/prefs-report.png \ + figures/prefs-research.png \ + figures/prefs-revision.png \ + figures/reportsel.png \ + figures/revcontrol.png \ + figures/savecomment.png \ + figures/sourcelist.png \ + figures/sourcerefsel.png \ + figures/toolsel.png + +docname = gramps-manual +lang = C +omffile = gramps-manual-C.omf +sgml_ents = +include $(top_srcdir)/doc/sgmldocs.make +dist-hook: app-dist-hook diff --git a/gramps2/doc/gramps-manual/C/Makefile.in b/gramps2/doc/gramps-manual/C/Makefile.in new file mode 100644 index 000000000..e946dd85d --- /dev/null +++ b/gramps2/doc/gramps-manual/C/Makefile.in @@ -0,0 +1,461 @@ +# Makefile.in generated by automake 1.6.3 from Makefile.am. +# @configure_input@ + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# To use this template: +# 1) Define: figs, docname, lang, omffile, sgml_ents although figs, +# omffile, and sgml_ents may be empty in your Makefile.am which +# will "include" this one +# 2) Figures must go under figures/ and be in PNG format +# 3) You should only have one document per directory +# +# Note that this makefile forces the directory name under +# $prefix/share/gnome/help/ to be the same as the SGML filename +# of the document. This is required by GNOME. eg: +# $prefix/share/gnome/help/fish_applet/C/fish_applet.sgml +# ^^^^^^^^^^^ ^^^^^^^^^^^ +# Definitions: +# figs A list of screenshots which will be included in EXTRA_DIST +# Note that these should reside in figures/ and should be .png +# files, or you will have to make modifications below. +# docname This is the name of the SGML file: .sgml +# lang This is the document locale +# omffile This is the name of the OMF file. Convention is to name +# it -.omf. +# sgml_ents This is a list of SGML entities which must be installed +# with the main SGML file and included in EXTRA_DIST. +# eg: +# figs = \ +# figures/fig1.png \ +# figures/fig2.png +# docname = scrollkeeper-manual +# lang = C +# omffile=scrollkeeper-manual-C.omf +# sgml_ents = fdl.sgml +# include $(top_srcdir)/help/sgmldocs.make +# dist-hook: app-dist-hook +# +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : + +EXEEXT = @EXEEXT@ +OBJEXT = @OBJEXT@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +AMTAR = @AMTAR@ +AWK = @AWK@ +BINSH = @BINSH@ +CC = @CC@ +DEPDIR = @DEPDIR@ +DISABLE_SCROLLKEEPER = @DISABLE_SCROLLKEEPER@ +GNOMEHELP = @GNOMEHELP@ +HAVE_GNOME_CONFIG = @HAVE_GNOME_CONFIG@ +HAVE_JW = @HAVE_JW@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +JW = @JW@ +LANGUAGES = @LANGUAGES@ +LIBS = @LIBS@ +MOFILES = @MOFILES@ +MSGFMT = @MSGFMT@ +P15_INCLUDES = @P15_INCLUDES@ +P20_INCLUDES = @P20_INCLUDES@ +P21_INCLUDES = @P21_INCLUDES@ +P22_INCLUDES = @P22_INCLUDES@ +PACKAGE = @PACKAGE@ +POFILES = @POFILES@ +PYTHON = @PYTHON@ +PYTHON22 = @PYTHON22@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RELEASE = @RELEASE@ +SCROLLKEEPER_CONFIG = @SCROLLKEEPER_CONFIG@ +SCROLLKEEPER_REQUIRED = @SCROLLKEEPER_REQUIRED@ +STRIP = @STRIP@ +VERSION = @VERSION@ +VERSIONSTRING = @VERSIONSTRING@ +ZIP = @ZIP@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +SGML_FILES = gramps-manual.sgml + +figs = \ + figures/addmedia.png \ + figures/druidpg1.png \ + figures/editbookmarks.png \ + figures/ep-address.png \ + figures/ep-altname.png \ + figures/ep-attributes.png \ + figures/ep-event.png \ + figures/ep-gallery.png \ + figures/ep-general.png \ + figures/ep-internet.png \ + figures/ep-lds.png \ + figures/ep-notes.png \ + figures/familyview.png \ + figures/filter.png \ + figures/globalmedia.png \ + figures/gotobookmark.png \ + figures/localmedia.png \ + figures/mainwin.png \ + figures/mediaview.png \ + figures/opendb.png \ + figures/pedegreesel.png \ + figures/pedegreeview.png \ + figures/peoplelist.png \ + figures/placelist.png \ + figures/prefs-bars.png \ + figures/prefs-colors.png \ + figures/prefs-dates.png \ + figures/prefs-disp.png \ + figures/prefs-find.png \ + figures/prefs-gen.png \ + figures/prefs-guess.png \ + figures/prefs-ids.png \ + figures/prefs-main.png \ + figures/prefs-media.png \ + figures/prefs-report.png \ + figures/prefs-research.png \ + figures/prefs-revision.png \ + figures/reportsel.png \ + figures/revcontrol.png \ + figures/savecomment.png \ + figures/sourcelist.png \ + figures/sourcerefsel.png \ + figures/toolsel.png + + +docname = gramps-manual +lang = C +omffile = gramps-manual-C.omf +sgml_ents = + +docdir = $(datadir)/gnome/help/$(docname)/$(lang) + +doc_DATA = index.html + +sgml_files = $(sgml_ents) $(docname).sgml + +omf_dir = $(top_srcdir)/omf-install + +EXTRA_DIST = $(sgml_files) $(doc_DATA) $(omffile) $(figs) + +CLEANFILES = omf_timestamp + +# when doing a distclean, we also want to clear out html files: +CONFIG_CLEAN_FILES = index.html $(docname)/*.html $(docname)/stylesheet-images/*.gif +subdir = doc/gramps-manual/C +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +DIST_SOURCES = +DATA = $(doc_DATA) + +DIST_COMMON = Makefile.am Makefile.in +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/doc/sgmldocs.make $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu doc/gramps-manual/C/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe) +uninstall-info-am: +docDATA_INSTALL = $(INSTALL_DATA) +install-docDATA: $(doc_DATA) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(docdir) + @list='$(doc_DATA)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + f="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " $(docDATA_INSTALL) $$d$$p $(DESTDIR)$(docdir)/$$f"; \ + $(docDATA_INSTALL) $$d$$p $(DESTDIR)$(docdir)/$$f; \ + done + +uninstall-docDATA: + @$(NORMAL_UNINSTALL) + @list='$(doc_DATA)'; for p in $$list; do \ + f="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " rm -f $(DESTDIR)$(docdir)/$$f"; \ + rm -f $(DESTDIR)$(docdir)/$$f; \ + done +tags: TAGS +TAGS: + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + $(mkinstalldirs) $(distdir)/figures + @list='$(DISTFILES)'; for file in $$list; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkinstalldirs) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="${top_distdir}" distdir="$(distdir)" \ + dist-hook +check-am: all-am +check: check-am +all-am: Makefile $(DATA) + +installdirs: + $(mkinstalldirs) $(DESTDIR)$(docdir) + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: install-docDATA + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic + +uninstall-am: uninstall-docDATA uninstall-info-am uninstall-local + +.PHONY: all all-am check check-am clean clean-generic distclean \ + distclean-generic distdir dvi dvi-am info info-am install \ + install-am install-data install-data-am install-docDATA \ + install-exec install-exec-am install-info install-info-am \ + install-man install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic uninstall uninstall-am \ + uninstall-docDATA uninstall-info-am uninstall-local + + +all: index.html omf + +omf: omf_timestamp + +omf_timestamp: $(omffile) + -for file in $(omffile); do \ + scrollkeeper-preinstall $(docdir)/$(docname).sgml $$file $(omf_dir)/$$file; \ + done + touch omf_timestamp + +index.html: $(docname)/index.html + -cp $(docname)/index.html . + +# The weird srcdir trick is because the db2html from the Cygnus RPMs +# cannot handle relative filenames. +# The t1 test is for certain versions of jw that create cryptic +# html pages, o fwhich the index is called "t1". Also, the jw +# script from docbook-utils 0.6.9 does not copy the template +# stylesheet-images directory like the db2html script does, so +# we give it a little help (at least for now) + +$(docname)/index.html: $(docname).sgml + -srcdir=`cd $(srcdir) && pwd`; \ + if test "$(HAVE_JW)" = 'yes' ; then \ + if test -f /usr/share/sgml/docbook/dsssl-stylesheets/images/next.gif ; then \ + mkdir -p $$srcdir/$(docname)/stylesheet-images ; \ + cp /usr/share/sgml/docbook/dsssl-stylesheets/images/*.gif $$srcdir/$(docname)/stylesheet-images/ ; \ + fi; \ + jw -c /etc/sgml/catalog $$srcdir/$(docname).sgml -o $$srcdir/$(docname); \ + else \ + db2html $$srcdir/$(docname).sgml; \ + fi + if test -f $(docname)/t1.html; then \ + cd $(srcdir)/$(docname) && cp t1.html index.html; \ + cd $(srcdir); \ + fi + +$(docname).sgml: $(sgml_ents) + -ourdir=`cd . && pwd`; \ + cd $(srcdir); \ + cp $(sgml_ents) $$ourdir + +app-dist-hook: index.html + -$(mkinstalldirs) $(distdir)/$(docname)/stylesheet-images + -$(mkinstalldirs) $(distdir)/figures + -cp $(srcdir)/$(docname)/*.html $(distdir)/$(docname) + -for file in $(srcdir)/$(docname)/*.css; do \ + basefile=`echo $$file | sed -e 's,^.*/,,'`; \ + cp $$file $(distdir)/$(docname)/$$basefile ; \ + done + -for file in $(srcdir)/$(docname)/stylesheet-images/*.gif; do \ + basefile=`echo $$file | sed -e 's,^.*/,,'`; \ + cp $$file $(distdir)/$(docname)/stylesheet-images/$$basefile ; \ + done + -if [ -e topic.dat ]; then \ + cp $(srcdir)/topic.dat $(distdir); \ + fi + +install-data-am: index.html omf + -$(mkinstalldirs) $(DESTDIR)$(docdir)/stylesheet-images + -$(mkinstalldirs) $(DESTDIR)$(docdir)/figures + -cp $(srcdir)/$(sgml_files) $(DESTDIR)$(docdir) + -for file in $(srcdir)/$(docname)/*.html $(srcdir)/$(docname)/*.css; do \ + basefile=`echo $$file | sed -e 's,^.*/,,'`; \ + $(INSTALL_DATA) $$file $(DESTDIR)$(docdir)/$$basefile; \ + done + -for file in $(srcdir)/figures/*.png; do \ + basefile=`echo $$file | sed -e 's,^.*/,,'`; \ + $(INSTALL_DATA) $$file $(DESTDIR)$(docdir)/figures/$$basefile; \ + done + -for file in $(srcdir)/$(docname)/stylesheet-images/*.gif; do \ + basefile=`echo $$file | sed -e 's,^.*/,,'`; \ + $(INSTALL_DATA) $$file $(DESTDIR)$(docdir)/stylesheet-images/$$basefile; \ + done + -if [ -e $(srcdir)/topic.dat ]; then \ + $(INSTALL_DATA) $(srcdir)/topic.dat $(DESTDIR)$(docdir); \ + fi + +$(docname).ps: $(srcdir)/$(docname).sgml + -srcdir=`cd $(srcdir) && pwd`; \ + db2ps $$srcdir/$(docname).sgml + +$(docname).rtf: $(srcdir)/$(docname).sgml + -srcdir=`cd $(srcdir) && pwd`; \ + db2ps $$srcdir/$(docname).sgml + +uninstall-local: + -for file in $(srcdir)/$(docname)/stylesheet-images/*.gif; do \ + basefile=`echo $$file | sed -e 's,^.*/,,'`; \ + rm -f $(docdir)/stylesheet-images/$$basefile; \ + done + -for file in $(srcdir)/figures/*.png; do \ + basefile=`echo $$file | sed -e 's,^.*/,,'`; \ + rm -f $(docdir)/figures/$$basefile; \ + done + -for file in $(srcdir)/$(docname)/*.html $(srcdir)/$(docname)/*.css; do \ + basefile=`echo $$file | sed -e 's,^.*/,,'`; \ + rm -f $(DESTDIR)$(docdir)/$$basefile; \ + done + -for file in $(sgml_files); do \ + rm -f $(DESTDIR)$(docdir)/$$file; \ + done + -rmdir $(DESTDIR)$(docdir)/stylesheet-images + -rmdir $(DESTDIR)$(docdir)/figures + -rmdir $(DESTDIR)$(docdir) +dist-hook: app-dist-hook +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/gramps2/doc/gramps-manual/C/figures/addmedia.png b/gramps2/doc/gramps-manual/C/figures/addmedia.png new file mode 100644 index 0000000000000000000000000000000000000000..312823bcc5a1b76b89b8d33c65ada5a3c7d710c9 GIT binary patch literal 11061 zcmZvCRa9I-vo#PBU|<0ugS)%C1ew7t$PnD!f`D=A6=2mo+!Z~z%;aTPc?c<5XDhWzeLLprP;4+r-SPDvgj zfq;O3^$}g*3ypEcTX65w{7G)^L(+ zl2ja=??A_*|18e@HGVWE)6XMR-$O@@g_hngXN*z5*uz4>E2B51D2Csnj-Qu4S4_@0 zNH#3OzPTV^JI|Y!?f`Apxo45%xO!sD%xI6kRdMP>gN-x0h#&ii&iNtSu!K zJ3BkOq5{*?)7@R9zjO0@g}Mhba!7f1^9Y-oD6@+4a>WFM%+F3#w9MzbT2GF3t?lgE zk+XaYvN8%#?5HdnZK?8buvJsHsK|*ovv$^Uf+XjKFDxu{Y8h#$u_#Ha1&2p{Wuk-i z_vBVhTWKpLhKDdw;fjhYT-=_2507kWYBcteN_lH}pi@DTVtsPhfM15suBm-sw|`!_1YJm(a)Zn_Kjc^_3Y(DNS|?rn0hf z0t)HY*4E!gC&^e8AXx{9zM_tVXjpD9Shph1>pLzURuXi0d~`%poTag@=&NsQQBw#h z3(ofDvP0IGQS6X{Q-iUqq`Ir%SECr)pxWNSfzsr7O#{}j+=NxwI17)imAX4U9baBv z9uqURtcp+D+)`Clm9(;kxJ5%=!;FY5si}8Ua$c`(LQkTmW=_$lM|j<4LNPJcF-&anu0HP4Jhga5!?ijyD zi&mzfj1Vm*)8A%vajusq3J+QuJj%Zb8_pZQbln60bo&<)6Ka9vo{28&him}E zxk?Mgx>S8E%LkqBsS={1(1uFc_yx@F?!8xBeg)233nqZ%IJYmM1!|R0LkK%yxdDXa z+yi^&FwsBt&*890EFk?^(`;iqIyO)kbAQi;iZ(k%q9E@Ym;^;d8s79rMLIPm>qw&8 zMOxel6q!M|H%%%cz3aZ8T#3O!LV-9D1M?j$bFXBUV~G-(O@9ZVYtVb~K8($%W1vib zLd4#J8gV?)$@bBNB@Hmr3Y?L{FtYe1ii>yQ-8nRk9GhUa5&c){=a+I_e?1QTZ+(7zv_r>2@uCjT3Cg3wO1mB!<^aUR-;Kb^zp0qz z)v1bzDJFaLj=mwOrssp8Ms*YJ|i-Bu`&GQdG?Q89?Dkbds<_R1sc8g{rf> zbW;=*LwHHUm_9#t; z&O_yMSp?`p7bar=vfMJ^1g`Cm|4Nts@-FdHA`Ww5>__l|v<*RJS`_&~M+5)tEESRo zYN$#}4~h)#m6QNGP9bdK$WWHY{8SPk&zvSsu_`Z|0Oe#g&5S94P>AG;Q$sIGV3|K0 z3Sqibhm(X;&udMt;1VhkQo!1eCcQh#!W@MmX>66jt5G8z8TqD6Xora4<`+sS^(1Kz zOM(03Ls+ac!w*)N+#$eOz$`TZho94-)eD*+-^ydOFPlgmH#KtOymvSfGrE+_GEb-L z8ZkM!Sk*WH0`I1Nq5KFV<95$lm?m3K#GfULwlH8r1Y`-iotl>_`1~SDBZA@6Fk5Mc zbAsxkMC}!41hYT!^RZ#8;N&^N=NLs4 zBn3yB4yegvm!AzTwWUtBYosA@>+Cyco8aCHN1OnZP2rc7_W<;;;qk+j*n_D?v7Gr> zv*8(Kt%sNxM>Gqgi4 znaLlNcaVsw#F9evV*uRmB%`SKw=vTqks160s&v@OB~fA|HYC$rPJ^y?N=z`S5no6P zAK?f~YJuTB@|7~}`715=?9omv;ToDaBd8ittyR0Lfm-EmXOMtql9g0)hnFV5!M(z}vK}=~jQE>@F@`PpzMhNgIdK+#(!*jt(@_qN0D>XG6;AQhrdw? zO)RRm2py;M=E~xfJ=&Z(!KB*_F0vjpK*okiSSxdgFFI;aw3c9UT;;L_0S@`VlNw{> zV2dSm1ts@kVFm;&&hpMQ7cj)LpEPW z5Dfx4u?Hl~vB@g6j!a|eLZn>>#^fNbhl#-mY}A(W!9bZkH1-7cB?4jVKN!)FF?V(r z*9d$gh%T^xoc=zuHgUAm3ZUF)R;i74SWukX-WwVxnFRmFb0mie@CjPV@(WH{ zWu*sLl@{D&-SK38G_KhLEXN6kfRcOkN=Gcqzk)vmw{tDV^|9|3uA-^^E-Ds%u5*%#($VMIMeR9|0_H_DaN_pm;h8;1CkBkiWw3b*p-QOf-@xVtM*vD zn7FruV8MM6pBXaEGkcWk(Xjw{Szt^F^>f&8JF#+UvJSysRv!##Ok5%!GFXH;E2Grm z4tai29gQR~rVd4mZU!ZlJ?1v%Ob4Jv=Yt~5Yw+9*V($!yIk)ZN$?s`T6MHrRcr!%wrq^FH_f#86Qy!7;d=x8v=!_zZ= zlXm}h1&5i6s(IKu zmjfzqdF+~2yl{bappe1*ol2^q+r-B7?H(lb!o%YDX- z!oNEHhk%TeZ)<3+$idj9`)SRbs&>T-kI3oyZ^n`tL;UL}N#3KZj4)lTXCZ^Qf<~03 z^{w#}K5R1c3O;*tqeHy;kH~aY&*$lVScjZAlyR&Yv;Jdg)3Or!~&Y-eamdP9|p zKYH_M`@7YoS-j)a%G?_>tV25uzWKn1^3i0&s$+gdprf&I9*gEuUP&Ao@Toy$5Rq)l zmYfR`5TX5}xN1q%wYXC#7|qhyQpvTg>j@F=Ap~M8f;mJ$_JH_%S^ksw)ySpcr(h4m z{f*U3D-;UquT3xlO|5U-xB$TWiHV8sShhyN`nCWCE3nvcsp^=>^3zD^7sZw$a_|a! z((k#frIa7ndxfm5wlDn=7VoOyE&ZyzKzOZ2#;TiZ&L5u|EpDqlGd7IgQS?4kCO3~W zGc#*5Ix#bwBda=DkB7UM@4LQ(G}o(eGBZNq--RdV$bv%}uJ*7VAFv)-7WJ#z+v^<< z=wV2CjC|YO@SeNDaH}8QHfZ@@}=$`wn=+<7fPk3i$6TpR*hURoRaVhKPf#a>YK4=edTY3-P-{xX` z!uH}DuM{`080-99Wl3|le!1+#_*yzWaDRUyK7AsSpWEH(*AymE8y(El>c#7zG)q0% zPIxaN^dzncVCoVmkmlLi)_k+xnmOMPdiQ|?10J!ibfkV{f;xsE?j%vF1=!84pS{zd;gfb4jp3Q(u4DYHyT| zF6f%-Sq{g!00N%>K3j^eB zT8*u;1&N2_m#+v^7WYsP?YQsf2p1l?K1~T;FH~yldj+Cdp3v)bdhws%WS>@ESGrfK z3IB;RXmT$Eh)FX{XU;*KprWZ(*$h-WMWrDjr6Hxgkgce-!`YFNfUPS6INC+W$xH}X zERn1XAIMRGi`>=*9hytW3jb$DHRnL_b=tX$;e{j3+3!7AM z$f6y)Jqvdt`!1bkL5EmQ;Ek^W)rt@au9Pb-V>+OZp)A7!VGKSx$kn59z3{rc9pzG#voDgNZc2aD#Ms4Y^HivHtNj3DX zyu4PDW#krSYlynkN`*AspSsJWD|vz9@$l{AYudfqJg)J&!*ApDzSp!q&i<@6jn+uh zL}PZcoG$Ib&$c(e$%DJU04e$qdevE%S9PlDYHw%f+;Bhe+rrK5*22CmAB5I3U=4tm z`l7+RL^3=pC6uYJ@dhN>6@G40wM~^6Vu_*W!0r=dWjjHjJIE{ ztODfro$Uu$y}O`=X!x+vhaOewvdiT}mvY&^nj7YV^<{MomsS@SxoRVvUCfBl>C1!U zJv}b;^yO`>tsWy&E_FF4QGwC#n9lt!s-P zLd6sHRYzi70_biQ&+I%zs8-lpOFK1x8(Z*wjx~TJGO|sr=cqgJRa;G@Bc%NJXZGj& z$ZK45-Rsh_*7fveq)dMMJ*IF;#=PRi#0YdFJs)?lltKtV0WXew7h(QaVeK=&wEsgVXyz-OXI= z*3|mzg6*ZS6K2VobzIrT5IG*AWlLI;N^_Hq3v9@ZA;_d1uC+Skw3@neVf{Se;0QZ= zcZjlY?Qmj(6LFeUcEVIjSNHiLi?T~r|EGm79!EhX=sGk;U*lOe`GB|bNB?UXm70!1 zwCT=7q;Aid+c+u;j~VT{j*@zcifeIhEtBP76n+RN;9uw5BK8+W(!_k6@!u1za|Kt>dk4oA+)oP9$HeJNHI2g+HTq0iUv$4XS0cU@odsvZ`+)6StVZnj+9aRbvcA#6;RUB-uxK@fvoMe_XArxR&DyJ)ZDQ&p=W~@nU#L zxoMpebiRtYq?lFE-th69&s!2Cv|Hmhpu+^9EzgiUxSsJ4HI*N)v^k{I*4B#FfDGu^ zf`nmB>!HD+lj32NbPNg{D zN~|qkZ*=LuxJkJURiQU4kl1ZrAv4-vI5Vu0aa~npJR2`J0o#Vv1&;JI3EcaQs-f$r zt<^_=C{F%toOs->yJhVN;S2^>V)18sQInH;^V?!yeBU_SX{d{qiXoyG#5!(&xh_bm zDL1s1C*Hn@ByxJv5_oADEHqCXn#k}eCyH2XCt5jgJa6hl;#qNVDU<>>Gy*F+oJ=+( z+uSxA7~z5`PGTe^Pg5V8iDN{vAD zLkFY4tnOKBd-fN@4|by&c2ys{Y6saHDk~kHcYGe3&B@-t2fTSkIeV%pIMa?mh`D7# zMP2ozrEf48f?mUyyI%7h4YlGTVgqdj9A;Mn2r=8Xcf)lH+wN27>!%VcKKnfQ-1G$H z^a?jg^ZAUkTy)VYI-i_reAKjN(>6JuZ$4Qisyd_l&eBBukJ+h^LbD6hUr(M!jWdKu zB5nWN&iV4wTlU4z(^Bi5pJ!00zpuEkua7x9P+;VPw0AlzJ}9|l;*mG?$jwG@Q%zOV~@1sn78 zStM97UR!88dl}364ChLv&ibhuchL5sEki0*s;(mxHHFs0rKc_+slen$!q^yfGW4a^p#=GUg%T0Nx?SJ=N#`pc5zA_$b=rZ;>?anM zlM{+lFI?Q1(x1~5_F~YI^#O_TkAx22+Y>}*IAT)Awj3w`{6v(fiiRyRk2)!$iUw_T zO8w>zW4iKX3T5Vxu8sp)G+FJFlR>jWiU*eeJoab1=+7I?#TJ%40oR88n_w4(M>}R!>oEi?=i z3$`@n9VrGPcPP$udQ0xL-#2;Tj5VoFEiCX59SJ2aV~W158N)={5S@RC+3u|_FKhO4 z!lOhfu3mK0(rua>QQ%CK=?!kBhkd7Bb4*k`Jxs9buH0=1`}zK9wzK|zo~e^0N58Uo z=wM>8yeg!IB*?I*IoO^1-@mH!BZ`w9VrR-<*pujH^_7ot|K@ua?5@@YBeBLI!Y4wH zPLDGe$9>jTmhA`}<`vVP7I*|Dk9iN?t2-V2j-U&xmuM^D*AUjt=5*d1-)uYWiRz}k zoKU_^!g)5Sw^2(^UGX-y@eQR^raoR&aaRz&_t00%I6PeEdsSoheI?|*q67G`A=LfU zp-edm)k6Y(TZ3CKvxDP(vYNfm^57j^m^z?k@_6<@uv-b}J0^KtVyU97q7@P0-w@nj zCj7?{H?f`GJ}-mEL$6OT&g!m)-(>@Y^i%CeE%{_uyXw{vZYD%Rzgq`v$Gm1-zJd6^ zK+?}rldg_JOdGSa&8;Nl%u`s6i5YPnH1lq@U2>|mwI+=^R+^6EisvWSeXF}TMW8m` zt1BA+V87vMwdgBv{Q49>cD^$ubouA&#XvM<~h^-?H{7Y-CVduujZ)CUBB$? z?`%XVvtOaewf^xx7rG5{{O{rmPPSMHYV@K~`N@2SHhq^XXtPuKtrsc=jH;yj%Xzkd zEI#Hq?D?8!~zk#Yzfpt{XCQ?7OUrilH=LC`-toQdTBH6_r}de_lm{}pMIo1 zsp=eG5b*9#ZELcdBN>@ZMueZ|9Fu`#*tM@`g4;i`hmvMdPUH1+F;olij`e6D*KjUtp2Kr;i=I#{G|IXuU|iNRt78ezZ8#an09M zT%4iyUuRu+QU0&X^&hrdl8_q;h7tYcpfsQWMj)ef^(aFJ=w`E!-fYQ~` za4EeeSkRT}yGvD=>M%EES{BohzvEiW5_OF#ZM_+R*b ze9t#XP8Cj$j(;Mv)bT<0_X7i_{fV_)m~_j6 zbkaqNAC>b(0Ki(>yrts3t5xlHVwMX?TdbjB5=LSIE8+Qs6vJfv_lwKlMwXXIVOr#& z+cj4;u$nOXjTnOO3RY5OQe$hubr$y&(i z@V)ACcXNF_!n#{?YK&|FC(+y;e?oIRxGO)N@A&055Lr{G5a^DWwzYbI_31>&HfKKK z!!}Y9gJh_Dq8(zCsKqcot+8&0`m-P+!~V<~!O=<7z`1_|fyEye$y8S+xE{O4t^_P{ z%(4>E0zQaQ&P~HTAntlnf_J7#6Pm?HNIo|Z9-L%lW@Q4N5UFPab8jrgp0C0PF_Yd^ z0?t1nb1ax(2i4O|g3-lA>y!2seXtxAXtJnzn{CdrzkB&eTL=H{jO))IVi%t5TP38E z{SY3|4owXVqg=%JtnBF_4;fX7_x8m+$F7L1m<_X6dLMuUZ@-7xksBlD^q!U2tL0C& z-`$ZEL zybKx+GNejOI+P6_E(5VD_>qzGxM(RM=c<1ms5QTYxBn9Wxge`*a`TF9c5~&B-UVE% zDPcMQ|5QQ?TVzBT>M-h90eRSU%_$U;){;aKCJ(se+ZMEK14EvOe)`CrhD85X1bTWM zRt}a8r0AsT>h57A9DbOZviT;c?g)eOO@U0sZ^xIO!9}CWMJ!;9M$j-OM-YG`@c&2pZ)5){FEj3Eab^E}INm>}Dhaa>I^{;Ov3uiFqb z=)ZRf{@eNr6~|ma5f4w58IZn^9erD6rw8*-#9)n>@s3LHpd>ms3!3tSMN|&U9JbXA z2%79*glw8d_1}YavKWh}n77LTUH`5EgcLB7t=lMr8N?4`3xETu)*CF=mh&(raA5%# zso0Ki(hIWGVlj;lxhA_J95`w4mpC1j92+3m$C6xihoKJ-n;;Ky$e(;KAwdrih@DrU z&O@B!K*nmdJfJA0QWwk=yy6SR(_~k%5+Ig!z+yO$l9Or~TPjSV7aw6H z0VrQZCEyk_C0xKDO(?kT4ZswsUkI-f?LR|S4cbox}KWy9(C5SbpK zR*yUsHEHDt+29a{RsCsnKy{oJyJ}hD2XY8Ns#da8X9@h9!$#Ko+3?4-mqsJDY{rq2 z-WWYW)>}*_k;tqxrexXzU&Z=N5+bdY3T7;5)%C>-zM5yCiRM>1`pZ^djJI7z?3#(K z0?|mfYUu&UShw_;3MRXT!FK7Ry5nBh0G4#ghE1~1e2f_L9@2Y`|)>Im~} zhRTWauB6KL=F(He_QVcjo7SXq9FtBkxj&vv+jnMaDI9R?pmOMS5*30_3ct!H)uAX~ z?j^4>IibvcGR6`NWO> ztWK1Yff!7utM+@#OnTrI&yzCIOAnr@?G(e$ymLg&m6xv3Ogr8uQtAk!TEiS|ivXnh%W8`}J zT%42=MRhGmpoUG*RV6&b7aYgURq+JL!Ch6E>R-wx5SEye6%sDv9Cm8g+rJsKVplzih#EkvNi~h%br-A0kwsoge zJ&${TefYWB4}ka}aLDw(;1d8J6r%Ls=bM7--;}{2|H;^L;HkKG6QQai;Tv`SRXv#? z2NPUJ=7p|Hl7lJFDu41*YwK%T02vAd(ddCz26P3kq{0JU&Hh5^DnfsIz4qWQPrc1P zj4o1_bkOw3feS|4eBz)`pivTL;e~S~hst?w%QnD-!Lt;x6pEu4NWm>47l>wxkw1+& zP}T}^2XtO=Gwc>(@vj-iL1#F)+Cg=^5h_P9Asl=fET&lM@P_f3$MdZfJ zl%htlP~~=VRD4)>+&+#eRWR4Fz=ErVEIn5h8vPLsx4QhHwQ`^}Ked=uClPlPs}$gP zOaWits~x}fRZ^jLBcTb6dzeX3dl8tP$83mpDWJTsErxxa?E*A*Gy&Ho?s|JT}Y-{F7FKX^!} zFdD|mQ82)p{`=i;%KzolX=H%Hv|aX`K`_mQv}zg@<$)v}nBGnK7E7in=}lt_){287 z$w5OC$Um2ogImim8%@WO^-q>=ev)t${O1s(8gm^WoBt@z$HGgaAESS>bTH-eG~z6XnZ$DnXlWh& zdAFXwQ##!@PYUDYo}b-$?cF5yQT7eH`Pt|hy43f%$Y2mQTefz(dImPbKmkANdFWxJ zg8%@tT%~0EjpE#Tw6qgyzzH`v*cn1zUqRgfk{d6I1NChXo{4|yTH|8iB6mA0r!!e- zUbHEr7gvY_e6^s6rBW{mkOnJztI9yb8%DYqHHwtpGZd5qO!+Cn0#$KIP$r1umvrDb zy|f8z(P}a7imZUEu#C>^Q1yGvNKsQx3&yi`j7zgPUN;9RQECEvB4uc5Tlny-P^D|; ze82~yS+dph6i0n%km0NcLZrC@@eiHeMz!Rw61=A|_56B1jehkJZW|De(dTwrVIX+WKPHJvzWh*gB93B>KZf+_nDkdf- zT2w%Gc6MG)EniA1VoomO;??Hf#^&D4>fXv(N+)7cG3MIF|a zn7*!+;?}|D*1@)}ingYVuAYRlqJ_Syk;1N)%C?=lri`kZf2NjuqLOyHs*tv#h_a)K znu=_ymV2(6fTE6ao{n*rhHI9BXPSmhH_GpeqNG- zW0r-dR~%xT#9m3YgRYa%eIboSc-O5hHg-5S~-GhOpPX{Ry0^fC0<4-T244mJ{($0E>=P!Qc5&V zJrzhc7g0kWK{FIoH5*Af7DhS?Kr#wHE($v;1V%LwN3N$Gb7ZL&}9tJ8R4l5!CB_0ATCj=rH0}=`X6&48*4g(Sr3ML`~ z4haGZ3Ijq)KqejvA|etM5(XwB7AztPFe)5YcusP5awZ-Tber~S9s)v$>gQt3& zvz4m6rIMPGkhF;A?&forX*N(U#%^|=K1Q}iPQF@Vjy66cbVW!2000SaNLh0L01FZT z01FZU(%pXi00007bV*G`2hsx&6aW(&3UIjq03ZNKL_t(|+U&iFW1GiyFUohjByH2y zZZq==0P@}3x=y{&k(VUyd-tWi>3y3mOL`S$E-@D^G$~OsDN5odZX!egu?UbNK!Oq$ z5`Y50R1C|q>YMhzd*{r|IWwRnTS>#V*TF{;`0(LdK+O5h`a8dAYilj7`@qmT%bdf56Z0|Ni%W^5Y-A_2&0~@Y2h_ z`SO#Nfc$#PYp;F(`#<=PzI^|+*IxTizxVrp@cV!F%HO_l40@~r#zu^U><>i-N`T8y7gZ#m_TFbL6M_zyJ_19lFqXH}5X!7Wrw;vy5 zyZf&%Al{*L$!1~m-{XB=$Itotkw5tM-+$|;KlvegA&(FKQ?|6>%Qh=As8v8fzV)qd zw|>6%2L;3~dLZl?uIoqpV1HldXWE~3;m6m_jZYi=173g9mH0vbzD@_Vz21C1cgP#x z`lIil3VG{wtwFx~H{TWCg@^Bcm&D<_-!&iMyMOZ&+5)fs1|Q5ms8v8fzTH}$VfmwP zzwzdqufK^)Tics&@(TUYN}K+pHQKN3;Qo4Knm2d{yy;!QTl25iX16q7Uhj|60Z&K1 z{YT&V2bhyKUFk^k;r1Fwo^l<%yz$0&{`gP+^sjyjUh4N>HZH|WnA>>i zWzrxozto0X+WajqX#si39$+=dpM0maeBA=y(OdYXzwySAH((6lxvdI+?FPG5Z}Xq@ z`})~zIHF(J_m8-}j`#=A;;v-h(sJ|$U%Txo*TK&T4{v?vPyXy5tcgL3@sg1(t4_?8 zmtN8j-WGo!8^wJ4PyV#EeBA=y(FAZ-E&N(xc+B%#_-r1`Fa4SK-ZH!Ke)>@Rnl`w@o0g_4`Mc7PIMpCQb}Oc{)~$)RBujj|rE>6cAgzTbv>wV5sKgZz1G`Re7* z^zWbl=tcP2UwrGu1It_d;JwDh4A$e@>E#oS@x|lC=0oXB2T%EyJPxJIQ%|-ai zbRGTtqrd$zeAvJE`=7q@;}_rjzOLIJ1Oh$V7AGLjwSJ%SOX5LNVpp|VanDZC=)<0K z@0+)ud!8Skr^n}>d!7z{-X81Aqrd-iO}p?ppYuQ1bN=&lfBBdH*MHN1A*|)T{5WRl z8Mjyg`HR2&i`MT`fB$D({__9+&VTuT{_1<*`@6sXe}D25jR<<>;PSOw-sU{W^MBDo z=C+o{msgzzY5Q(#X)Om9Cm_$Y1f;cmg@F7~OM|qQCkx0+Eeg_Fo9o+VW5Ban$%|3`V zF@cn=--Ew_6alW71y*4(Zv=X45Hx+U2VTm+-=GhrEkSVL-R9%m6`D4B=QJm9r`GF% z_c?;5qkg^Ri2|a<4t`b8dKCJrFn2r(>Jz2EBY0n$-IRGnf8I+)d#ixn)34|R_9V13 zozV;eT|wx#==dOLx-^|qs-RgUal0?CcvA0E2IjQhCq1}Duz>u;H!(q=@K?fL>rt6+ zL+D=|K4tD$1uLLa;7MqGfa6Dh^0Czb4>MzIUeI!;(b!_*cN<60`hi~4+5Vm(EmRC{4PJ@IO1TM z?-sTy{lzOg=7Ck{`nA;eule&{YF(h%48VIvOW@mfg!Ra3fm25CwCpKGVuRZPt$-9A zZ;nLi(x=h7LP@hxe?Y5Ev_4SyCO+Xi-lx>EC+)6$ac>K%rRFH+d3I8zzIpT~{GEL? zdcu4W(%%r60YK%U%s!8LTL!hzhTbePGCNzGCIvuD6)BrJ?1G+sJBLDwU!0+J(1O2`PJA{7%@(J})qobcL# z$8!o}7mRcYeThH`c3~tP9oQI5kZuyr57PgTFdx%wQC2&kHhZ#wDEC2w-^oYg7AVs4 z#<*)$FN|A4>cg4{y+W2;SwYeFr4|%Eudr4|kF|8C5t2L*k{qdZh&|k>5{g#zogUa> zL;<6vena2#ce1=J$8#F)(+va?y&7 z3asg|Ln_S9%aVPdg>Bt@8Imn(Rp4r3$` z5$0V%ro?F$t#RCb>M15BuqMuYaPy($@6fur0fiUFa8zQxZsP^={YsIJq6opNAm=_> z6+^eK;1zw>OJkt|QWGLDTZQphq?J}0L#Gn#XEp*Le7(d<%lgB<BwnAcZ3yOrxYI>cm z*CTAD%sdO0ICExU6_eIi${sE$t7cso6kWwS&L}NqB+ACSp5tuf!1FptQoKkQFYv@X zNMN%==$2U%seV`1&3DN<*=&(EGPI6_3L~5DM8$VD`xRPx%=nRz*f7+FLqbP4WHW^H z6uxvRGA$}*CwmFfHRcm36OIe^u1)x&$ks^fYp_{qmJ0S+-+|@bckxkCB1JyM2ay5A zJ7gjQ&s=_!*XcgPAr&M9(rR=2_wI5_PjvYIG{3s<-gnXKb zA-~yXUQP5azTxXOu%sHT%{>V6?yY9A9;o10K}zR@lD?`Q!h>B)aJlMK+%USn+RKKQ}$jr0gM>}A)VrJ zDOfJlaBj+d#qJBI@Wf`=m33j2N$pawqP&E>$+lVL=B4KXD)X_sa?oGp+8_-7(G$my zAAj$}v13Quyb9hE^B`0SvWdiib0Xj5^?$W%uA1@+5>j+0|R)lvD7zm-TDf; zDr)>s&)<}iYNOJ+_sV!Kf|HSg%O}d_+LTR0DeK_aqM0p$Qe97(N{r632#Q@5hc2cp zs5qxjXV7~ZZsfM)Vn@;JKuf(!=$4bkw3BVW&139DVoQqqw|_CWb20Pc7E5 zMhm1D(tShMt)D>|^gw#0a~D34?vP%DD7cLytrB~eXTcl&~nL(-2nttTvnRVBin zfla*^9l2wzlPg%7F`LAfF-71GRb3p32ZjA6M7iLZ9Cc>vDJDjEX;iYkp>QPBZp|c#zP~Obk;s#_ z`|GevBoz7TeV()~LwMA)@Bek{bdyq=fPGN9zxjP(adkSx38;cXUPeF#HG>{hT47b##Q_9I%_hL!PitBounmu^@b*;7R-cM(fI&dLi8{N8Jm`YfG@B zV5P_0(#AEF3=kDf-3RQ065>euC_+w91QsWv81u?EIAzU@&5-mVy`YOLC;`i-lAIEg z(kc7qLanJy3!CR{)8}J)2lP$bjvha5G9gFXD4=sb$V*Ml+kpeX8;ctWcZA_<33o&o zz^}iGa5w`0HH^Bnc_&F2B}M!2I-DeehjhST2OXepp0E!{;S-PT=y>w>-(Z~!wptrd zZG?*{gwIk^Hen|G(wf6z1vJR6tmk==79n6l6h?~pX21_)tj{4FuFOSLi~*%s++b(9 zw5+(^YojL{^61lBbS=9GIz8AwjEJgWoy4_w;o8)AGjBuE9eME@IVz75oQu0!sefBI@Bg$35m{43R=-iN_e`|XuxoH>Q1q} zG{!e2Xok&+&!!x(FO^gWagFfo`(H6i&^dvgim zWfrAtS<~b>9ZgKj!*86e)D_4Rj2&RZZ2A=M+6(s3p@dW2= z&9-;m{r_~{^dC*y!{Akf{xf}<=#!XaY8y3fh=bMK=l@qmlrg?d;c#_WEVh0K!FWYb z9dzIV)(0g-hS3wj0R~N>{R|lG2&fozQ%uij2RboY=251?C_K1J2>u0lg!91JwB&{C zcJypuGl-KriPg8i);gz%xTML+a7Bp-Wp8@!l$658waVS&riP{~B!o~TY`Sfr6m+Yb z_qeyfekN}DR$c;&a;{XPp};AslV`mrJ?i6sZ79EyvzFaqPXL zm=5z5h)os;Qo2kkrRNWz+W+D|Q*W+uyKUMQ{03>;fVKWWgM*p@>>@M(DkRj~3Pt=HNS!ByE|+!kT34+0Vj zY0(JVZwUh%5Ey$-6fb?Gc6iV`HhxP-g!)ev3iPwCdu0i1pgWD+nf^2L|bs(=1%4i_h;UZ~=fWWS{ z@kR>k(F)3|7)0T*<0p<|a{XP@5tPvr4wJ&jC+jB}ardK`|MFKa{I#vcczwm_GDJdx z?1BzyA`!GJc=v132%`lGNA$gp1FF>om>QG;*x7yuhJGNd)l1k0KA?wmfTTBCQNtr) zdj~qEa7DZ?@NFuK1{QYrtm~{k6j!(O{laZyPUi|)Flap#=byH^0F^43ZparD^BT{{5qwA`b0!l>) zCB|r+u!(IVs8h6BZ`a8Tvx`1jJ2B{f*o^_&79BjJYokq%Ha3CT-CA906|wid3NOE6 zol2`#fe0V+Ya2;i3PkuRPT`@{$!j50SrbhICq)95345oricsWSl713iYmLpPQDR0~ zjE8dm2RXAG@k#?OqPUB^E<#ra3^5|V>!{Wr?;W#=Q?LEO%fWNQCbfe#IjMhxa{-tz z*LH&t5FZ5t4~(Fn0p0@9)tCg9*#N2lG(!k?>_F{;gRhNByZtwfdI0G`)e{N{JS2=e zM{pzRD6@`Mhm2uIL<Xe$ zwo-5#ud#(kQF|OefoYC5^g)DkP|erm03wQN&IbfUKUoUG8Ge&S4`WOLBWIp))U+?b z7A2s84x7YCNSlw46cO!K=pl%pj?n=unUoO9kk&#GJWd256vQAfmMlVg3B&A&BP`&2N-TNI3W;|4pKGRRSJwJ z5y2#=FupEkQ6pNhpf?jjD}=d^4xL0(+SkC%5~JfthxT`M+>DpuJ|c4~QvyRWdCz0( zibD`KC&r-@&CRM77GeuE(s^s9EF+W)8CD)BwLF10F z5+O<%))SV6gzPFO-fRzzba$~5f%ISi+}S5x5*aVK!#tfZ*Pc)=65ptQ|w4k{u?KD*0CSiP$2~JW|n<9=NB~{Rb;ETWYuY&u@m%vX&W>hM`FOtduBa& zBD0^z--m)A0_NExT0Yu?&tg&~fxCii`i~@sLey-aodwBIzNU2_Af_l-kQT_z8Bcb~ zq^spq@1yBP5**OV!0UOkn1 zOv!AFJl4%(=}C#@+Kiyh(W*vS6a*jWQ9RNKNe4R-v>7EyNGD-?0h~H8js@pBcwN!q zf`UfDj60SxOi1R3Q-P=0t-~iQl7e}gc)Tsi4@DuS256*($B|PVRClpIf=!4my*Plp z^^yWgjan8G$r+G?n$b5pL88G58hj?j9MfDNKImLWG)~lcvw*$p@ z>qK&KS8{KRa-2U>IT#|9Xl)9~fXlf5(}i93!%AWb;jTU90w8ia+H_Qd9_g`T@4X{5 zs{LICh_unC6Hkv~7_@53l*Dbr?T5meHmlndXBoMopk0g*-6n$EM`~2pcb$!=xfaQ& ze3Nrx>J~w{XzFj4iV8+jxRe+1aR^&%6^!3?QLekoiFZ(wf>FsuFxt+jE3fKr%CsJO zgVI$NO3q+%t2cyVsxsocQ{0eaSu{PkDMi%880Gydj%LoQ{Td2HXa)~c7M+&CS_jvA-L^J1OwK3PDlHaq|`l+2wcsb`ezr*Zui_l78AMQkIx;kzJ6 zl*Mj)VTMT$*gHfj4`t((GMx!`p)y~ZxkNF@J#julSsIH`Ies}c>N6ux@R)MAe zvpH?g%doc`_jfU`lC))b_VYNJ2$a&R( z1^4lAy}?m$>EgKL_7264hR@|Bs2^H5ePBI(rN|#Oz#50OQC>{oIpQwXBs;HMvjNA0 z^dvmSW7!1mbL?%xp_ADmm6Vp3fh%tZ&QlajHbg}<2ECKRMR`FCHBDer;UKME z)FVkNb1ykHZ+h)JuB%6}`XHa(VLuFrsWbqZ1!ypk}O<0{ty>*gQi*PM;OeW%jAzX!%ykq+;kDAn~ zBvkvcHp?g%T2r5u3{4 z!(E4h^dY;-!p^yjN;2Yl;5_{&onKBV5G9+^|WbIU-jg!14jQLQ#Xc44V4ix&2&F0mWkb z$kuZMn}-tij`ljrRN!SPOnQkD3{onOm4TuZ!98%+m!SAXan*&qSxF+zP!Z~NAR-we z)mCIZsJ)IMCnkhZ4nRWnKaRqrWaf4fXc-)A`e*S}&e>8_CX~(Kbo$2w$qKjuXLyyz z?7}wLb3mMYb7DkTESW{Mzjy3tn~f~R)1nwk{1QT1I-4&QqZ>T;m^*anYg88X%LMZ- z!__3p7Rh9oh<$jCB}2CsQ`s1avdA=MOoO1P6W*4u1PT==C4CP}dN@-@H05~ch(=B- z$L|wR(Zk~=Q+@*;Rf&}25>k}OB=u!OfIHAMOzGF7!LsvSa`ciX%-o)YE;>L6QUklk3`DzNR;E_Cy}H?;m#v9OgD)d^i)O zQJz}Rd;cv2(Do}B5)bRMscfC<;!Jb#3R z3Ml2z@nPAwbZ)}Owlrl@WfMk$lf3;6k)x?bPnI zSrIaDWVDVC!Tx)$bx9szU?bi>83dlnA@1{l^7mH4Ip~rUX$aR%;A(YLGi5!;IS!8g zlu%44C7f~8%(6DeL9-<&7i~(98AdbhvvWc~H`NrkI6xw2+pUA-m`r37QLDAF!b?DU zKCV(tB$%F`p$?$hg-2`i#3+Uom7vUxF_;cV(nQo|3pN4ct%c&0r}06o-C=NOGqp5t zkvp^}o&`4@Vl51nrJ8m)#UDyZMa15q=mc8RO2*r?q0-nr567Dyc~6r+tVg)v@Q=nRcjc z$_8LE@FEy)tg%c@%+4>}{7|0prl~b3jpAl&@OZ_Tc2aQLI2AQWNAq4LWROhts)Tn# zTKg{ytD@H==#|c$@Ab55uDPGMuhz+=a*TY!O?4y~EiC&9XpEah}^oy2nbK^j%>>`Zdkm zLpU#*YDKwT|K^4oDozIO-*;#ju6;|vnQ`fu0QiEOp7vY-saT}Az0RA_!J7of0iT-d z(#=T-{G3k?bETkkg9U8bI=5r~Q#1;JOP&g+Q3OZql4ru9$eHG%VLrjY`lW(z7scKT zCt0ZqzG)&38x%+nQKdWv2jMX|NWutNqrk;1N}(}ug!XNkaKqM|gv<3(m6zrTF{kH_ z3gZ1Y-=7jp>17l8+1@?&NY`lO|~NUh^&E-bYC%iN>HuIN~lm$9iT5$ff7wk{#3;Ci+L98 zLkaAsiVg4PZJU!Wj2a%2@Oi)U} z;!D_@a}I-WTuVwa1&o)OP;|RXZ^8_F^V&GqVF#`I;Y}pt^Mp|Tgv+%qEg~6t&grj! z?Xn>xGtrM?_Ci94*x{ynInAo+SUD~6I_czqCc7c{iU8*N#FGjHMtMQb8x znns4K^AoGN);LS)2mwXYRlQ%)0} z&gTe~$^(GWyp4=%byYoTQ)G0pbjWcw$kGG3dPVm@x#=yX4@xx!A;FUXO6O)M;$Uim zxOzY~*ZmZD_le+KEB8*Rz)Sr!m6jbxyHi45+XuJpa_&w#nh;k(_irh|J18oW>n@w% z$x#d;IFy}qhNfa=-Y$ZcS>Tu=`>eS8O}d5*E`!_qP`Eixj6cF?P|s=id*qetVKv^q zMFI)sf*6}cveg^ga-w{?O74=A(zV?=E)L;%X}OL=orRWphNt>*BFde(Mlje3BlHVt zC#LtN)Q*M2$sq7J6RB?Frk?XajlxZ)%p{7G(B@*fWK3>zs+1iYZuB8N=Q;o+XS+F! ze=L(qc@VP6k{n%UNQ5oEilF&+c6BD90*{!2NylCHpbB1;YMMEvIh#Uw2&KGAwrtwj zbrndz!^7j$Asz~wrh$Ml zA(AIIHB{{EeG9+fYGv6c1y?@%reuXANO7@xw8e7cunE?-(v`g!HOC!T%BN?xc*v)> zUOJSFB1$HLL=ZcVJ)>gQa=%*cn2dQUM@MGBkPxX47TlaDX_7Wi{3xeP4ux>7s`iGn z`4Q!aU@xWGBsjcjS{O>49w=qbI}pDTW9fKP&5Lj=#v~;>Jw}CJGMBJ!HR=Cx^L$ z(|I<<=|haY3K$3Fs;Mni`K-cLA#hEW$QzkYiCK4qD-#QYN1@5QlsC<}V!|FTPv{K4 z&>lY}WjW3Yno1O|Xx}{LBL_vorMQ#@TaqO**)7FK(R`>JGANI|$Z~a1%Eh^>f$Q$J z(`LH-3)gJ}A1`Qn0NCm_!Awm{p>PE-ThVayo=87_VH zfk#}RTo0kL?I<)0w9RF^5~=Yfyq+OyT;;Pj(N_zXv9P`Df#=@|ngvC2sm1obNS;=f zr%SMt>RCw~%vaGdzN)+OjSW8)6m6 zr&Mz-`KF@`_BrVV&+^PSU*P(NT&m9Di>UO%Y+XFdmtyo80lKX7!0ARisMfP5yD5Ue zCx3IzZN@^p+FE`~%aeO^IS1m}Uo41`K?g*-#hD-r0QG3{-7G5pNTJP~{f-`W%P3g<6e;e>OZflt_`TvLaz3_gl`Wyx)2ub#35|w;+`_Cw)tU9EN}#tmYDDuWVx4bIm&3FXn*zf@ZLBR2zay0K|+T zTqm0dls}b|Bw{A!zNU$HB=c}Fy4(>`bMs|DgzI|e*1#TW6eFA3+=XJQP9YiIDI9{2 zDgwemnXDV3cp_R#_IRaL<T(HS@w$7d%F z^rwCaXaZ~>%gtCCBW|sMJ>)Q#$}5VBsLFwOJzhTP7=;;PF8yvQnxd^~J`~pk#6vFw zFOU^PBjiov_@&9QFQY|+vnhaSaP)^Ev;4uNa5bd%QDN( z(nL>;oC@3=(UM#47ziQUv<0p+M0lKRQo3@rL;PDneM^HJZXQH3L`yhYEuR;`acBhI zwB|mam?IIhRUE=D6WEtiBHpwJ(#?`UjH1c|$5FkKiGdgz$G8AO3bzLKkObsyo-c+O zl9Xs_wc+uiK3L{ACoBL36O=9C)T;_i1(_Y!px{VHDUr^g8EW_!f$O>pXd)QlgW;t| z(bgK+LycmH&zky#tTJF$Uy4MyipzIyQYOF6UFo6FoIhOnh%GYcx+tj zAX$RxrlikjWs&$g<=z_DL(PK-?q4A)FgJ+`Pd-Vs8?L2RxheS^hpA*1}PVy(Hd^prt_$JBo2pNZ{b$jCl;W*WAG@&5ch$Wk;>% z^HB5bO^&dPTO=I}Ofhc;Rg)bhtOr5xCMafZG)LUk(jbQ%<{A%yprV53)gX#;%kf7| zEsj?y@My}cAL9u6tfL`ZE59Uj#rz-A&Aa2;ou%+`*4+OQFg`%J>9utpzokJAIf`-k zY*qpmSt?ZEpk6+!wxqHgb=4Bx1@mP{ZugT+Cn}q&cZ!$=0|s|G%3C|#5oMCQQK{3+L5rNY za9^Nf<>kVSVo)rxd=L+PwYl9w za3{a*CX~K^$^0B1kC~h1mC2JlMm|N<)T}pz-wQ`gV+0dv3hbfgL5LbfNRQoFFvW%7 ziBY6&r%(YiZqXEJf!~ngn)e@{(^d&;8@0ZwAt2)!6_rPhp-B{p2 zT`65NEcfjQMRUQlG{~U~NZ@*3-K4F|P3uovO)-UI(~~~Rkg1Ga=RBudIY;m2`j9l+ zmSr83ih3pk*V=1wAFN@7Qa^tx{3mEC9BJ z)y|LNC;^o>5q97;#(VQGIgA>!(ohoyQ`E5}YFJxKgB)@mgm@*IXl?9tk=$v@w{jqO za=t(+Zm4aV=0-R^Leqlk7>X?H-EuWIad1;KW0CL5!_9l)k5sN(-V#l=G{|8I$lH#J z+v1ijdzJ9YA&#I)x;fndZ-Xkgf9Ic>K+QFHyZ53HDQ>`T<)?GI|MVRfgYUM%x&GH}w;c(QfsyWsm zE(bSDoRB_!oFJZJo{H4XKPZW!elA^nMK(}QLd{Z;9h@X4&!OGdFJYt2082~ zMtIY|(mYU#sUD;^brMk}3!dmivSXqo!xIHD5piNfxEZIdnsU==HiuBkpCnT<$ACYV zla#IktVOdmu!oulQ8arSx9NKwZp<}EJ_P^ux628ok8a!-D68CAhm}5=e@l zs6pKEH8bwRhQoqZEW1EUgB-ema7Yp`*`>!SIw#FGAX>>Fp%lOrIuN8sy&~4Z>idom) zbeA&ldDI!<75JRWr1@pj+>1P8H7gzJg&`tVrt+uBrHH7=sUpgPxEXqdt?st+AcrX+ zrkZ=~SOCAQ#sk@r zcG6dCuKmFW2(LP;OAH?4|2Fs%*hicPyXy@ z$4-&}bbZi2FxcJO-Q7FX-80zJr}|)cK|;DI%5P$uGjXJb6)G z)W_+gNhVPmTLXKjd645LkHhk_W74XHuD$_~jiI5_r-z1mdwU0aPMzw*!G)1I)px3= zr|13m@v*1-eRv0?#NOt5)a)I8lC*`wWBO2@`al`=3=F^-PQf<#;r&nQy)R0<2%P_v z>eoV{fAKiD^hJ=GK76;YZ{U>WCD zdg$y~5RqQcBw%fDoVxn@(DHPjGfTH#VAN0HqE4BCW4i-uW-|yANJw9gmaIWM=6ziB zLCOfo$xA0eIrN+wfRpv0JZgDBDLkc*KZTOfHvlK-hEMRm-VS<57$4|lOM@J)fSf!I zMh46bxE5#tdIow1K|ao&9UeY=_QNy1piIv7f{4IKb@!e-0}H%|%b7Dn=j?dn$k|cv z!W$n1LY=Z3QM}+#60Ba7E&PR}20#+Pc3iq7FZQc`IA9P=TF)SuwI28su=a~z^!{Q{ zZ`9%i*FQG|$A8$}t;I{&`O;ehd#F(i+`-3D&v$jfO%Il%r}y0Hv%@1Jqa(1K9X7H6 zg3^5sMB_YcyKrIb0xajx!+WRCoYC5ZvS6E~<|Mz+=lO*ehBN2%=|N0>0hbs$bLOo1JRg2|?ibzfp8_QkoIKfz zVh%YEa`L#icnJau&=IFlBb+{S`ZUVNh0&3*(b4mxqoW`zr_Y`l9v&VU9s6i}e0*YJ z;_~?T*w}?DBO}AZ=Z9(7&we;GWQ`9DuhunZ&YlNDh36X?H4BK=$ZPOY%ZRp8gP<)2 zdwWlVSc2Aqi=UIE207#?2J;eqrv`grq|Tj#d;QAjRT!v`W~1uP$Zy9X2&j!;ZCsU*ysohCXDde(`Qj-jEsQ% zj0&H=aBX}Jcb>a^eQx&p#P#dAv))^e{qRul5NM~-D_15bC*e9{Ae*C;Ae&bvXRpBe zRs29#;ML?c&|l-%;OGk@AdB6n`Yy`jEedkTc@RuT^_}YOJ$*M3s;TiUuxO@uL3W(E{D@I++ z&R&@u9UY&VT3lG2hGlAcYH?}-chzzNatGrN>gnp2lh-Cu%&yJO&W&H2ymsx{LxdmD%xG)EE;t$0sM}R+pEiS67$T)|O^gm!@Z6SJ+p}%gwn-R2!3Dj$ONXeeUMs z;?0{k=kRZC?&F&u!^-u!YqQtj7dG5n{9+M51Rl3IJ~n!OXz+cAgIcwie**#OKKJ3U zb|ZiOG2G)5AQlss!6aP12`Xdh*7EYwDlE%O%WKQ?(RebozP{dQluGffjkV<^7_e0s zaZoKwC@z;TPoNV!cX@mc4u{))^5;_F zcXxLPnw|gb?o&T^SJ%Ct)vW+E-JC4lysgY%Tw#y?a`SRcR>E&N zdxNGT6jQsNXV^<-G2pJhh%q<2eFKUmTFj32smUh~&U{8psYxv<@^ydR@j}?WZQX3J zrC|_kt?_vV9z_5fvrGm!2bK?LwJZ#RKr$f3im5LmyUQJk+{pAc=wC5(^YXxQF05;m zD*OGVI*yMlCnj6BV*ABDK2@UxW@>?8vs*NAleT9nS~#+2!VH5AP&e9}aaU_JZ_l0| z^cK)Ps0rd_9HoyWxE9mWR5Qna2ffy}dEVWg%Cu^2FcT&{`-k^2IE5K}tTuh<0u)XcTrO|Q{6H8(GKC1axjg>4R`r+~RA8mt zPXH6Soe~0p4SO1%NByx`F?Q9K{0>z?1sN+kjOmXc7jv!^$$Gu=uVZ!U?~k1-?vgu` zP68djl(k8Y6EsyN>pydl-i_ruX*Fr_Ev69=XcMxjC|6tjfvj(C8hzS8eGR8MQ5K=j z41nJ$Gi(&pGh+dx3?!dD?J?( zM;fN{vKN-y={x3Be2igUhSAz)K)c7k#|v*09{}EmuPc9^+`e<@_@h=+ZEod~?ZWHR zT=UiY?PjU)ub==|_Y$GGhnJUr^I~<@RH&~g(oVjyY#2QR{#HOYWJ%@v{`Yo%JXYuY zLg#l1VB`I*;Xz1Frd~aKj|pF-T5mEas|AABS?5c$vVwa02}7nbM1f2iej{fk?$Qvwtnw4aqu+GJA%!y@Sd$-RMAk*4(pI;{1N_>2TK)C*^>xAd`Qyd%*T?N8|G}Ha<-*Fllnvu0W56hFO?$o9akD%xc6DBfH21L zDt7k&&jMVe;uK4kqyI$3B3ee60#e&n@*k-Nqdz`1SG0z+01e9|CgWpOChUR%Qx<2x zHrqRmR$(fCf-|gm)XRP0S){A#x3E`hwzev@&I;NwFE`qswkN@pa+XE!?kI{%vcvvF zNH1axiKf&|BD`YW-rr!TWc>Un(b4nBGjlynYz{A6kJXMF-s9Gq9X-ykJX}MBrsXb{ zp5~rU7l*W1_{5Fvii9-EzAb9FVI=}0EUVNMXmJ?WMju=)E7WQNOe3q6Z2?>y(@@SY z8YLJ?V*Yr0>a2P8@UHUn*^7(RP*1-d+qOt}?!;HDPP^(r_?zY#ri7AapNF^~%-AGY zu816ApHJZTN!)wfy%Z1EfqK1w+kq>4jNvbt{P#Izc+m|?qrX<}70b`h7APUscXPQD zYvknQJ5xPk2WpyqC#PCeiOad{bn4csWRd1oiOQuKC$6Gj^=tO;o?R)7!}_hUemTF% zxLQBF@Zu$WU_w!T!X41C;)n%hWa1>#ifr8X;m-JjOEYRSNM~Qhu9KTAux@n8hC}87 zD@u-h{LAp+M!N^)&g@57r|5iUc=K8A1MOejQ5mK>5#jkc_}f6MTDFCSt*tFyr^~m{ zNYdPkZ1Mu{zn7Dx9uR_2FfLqpnDC?Rw|Zn-1>k?P?&lDVC}#^IpO8mZz~PEmg5XW*~5zyDt{a z0yHr1{bK}^mQk$ThWerOFV zicdb(RRHf2rQq=IdBfD>4I{wAuZ)~mPB^6MM4r`nO4~KcOjOrE?ifHPd1}ARcU=?3^)KcX^sfxz^6LQ!et7$Pl%H6 zbV!Jwa*fyr!Oa8&HO8b1Vi8u>3H_77R+Q$Z7IbZ$^*HR}Z*QBHdY~q4VAz*|9#)jC z&5&h6$Dd9_k55uoq_aCHEV<>HpSH@h?zV;KDpDW*y#Jlo@z733Dx{t^j8LF-H5Br? zEjquXAj#BcliIGw1kj|<6~xDxRUAl~)TEUa_Y_A4^jvt@KoYALW(ywIJSYOj>WSe} zljH1~f@RT_<$h2oqN9vV_xwk0-SL8p*Bb~ehD%G*3z=^`Mc$uFQ;XTVbP%I8`tTT~ zTTD=_=ODf>4SuRXp+dye#Le&oxS$+pH>^A$t#jtnVydEF8uDQe`chZx_Gw{I!4PTK zx1#%}m8)APAkn_dyIZ{~-vjUmVg5@ok^&9B*L@joj>fgMP5Ct)QR<`|t?%n3v_o8q zwBX8@K1z8=0foM(QYoPE57!ucCY}3FxWj}akwzQoQ^fmJ_bz(SaRQ_n0&nEQg{gC2<0+s= z{Skh(&>-?Lzp(Er;K~3j@&IF>UDgsU>Ap zjNl~HDwttD&5ApDJ5<4tAovnpOZ{1S<<*qOAFmITfeSCyVZn)>pkYQ|V^z_5&nD70s>`fyKXu7;ke?i3M7Hw#1BA5umboTgpK%jML zad_mo-cko?Pb?2wQXR1vJ9s^?!zW0HcV;7uHrd3DQDjVHsR{^K*NLo|0J!-D7<$3O z!yD4i2zPppx<6J_SAYCG&Fx5?I5@R-Qaf8PsE*1^@D}UNQz3?3+1U7F-qZvLHK@?) zS4eSrcCrNIEbGzeP7*xcVkyxl-xP;Nwtl$?hDxqGvYeKa@i7x@%B&l05HdqX(cfFf zGd+3Fq|Af6@~-~=gkhyQm0Gr`;ok^tfK;L5S_K)S1E%?()d4LH(fkep0n%HWMnvRf zWEAA&m(a)Y85}qGMG1}0{i|E<-*5Me@A3eiFt+}AV9Nr2J4p+$jLS517sbL$%FpGc-0kh{pRuqx(D@x$fr8=Q-aT=Ua&4!ndIJ1(i z2f_jeqhe2!<>P$Q)n8*+WNN*Ly|CfU-k5Z-v#f`rGMN&cfsb%Q6yKibj9;*R4P2Cl z?SftpV}tAj!?NMnI0Dm4nBsm-`t3FUej(8}9A5?f3(Sv4_vAUah>dlw(z3Vup0aE> z$zv1xyrKBR!_1$iVTr-c4)4)4bX_P!VK6o}Rv!o?IyB1WF>!#_zovkD%zKPnL;#n+ z-w*uX=yWUKyEX#c_+f26t@NYj`a#x~NC`U7QNPd&3Zm*W;(A2T4>Nmf{70mMEZq!5 z4-0>T=0s2-;kYY$w5*xb#aFSj;JmS{RWF`NOXWEK@z|QG}4bUGHU`Tj|NOKWK z_irmHDo%{EtHj3Viu*jSGcGJGY}M4BZ*d1We0e%Bt(qBGve|K>QBw%u)+@*7>lq#W z&XpOf1J+t+rQLovK&noR{&c(Z`0guoe?pH*0WDRw`SPF}X^HMCWuQZ=$F?b&n)`@X zg8wwoQaL!?`{Ho^6rBk5?@KiEo$c}g2jvE$Tb(fF!pP&X3};-08D=Pa&Vt@=on_ne zRGdh4vcg}kG?^pc(@H>MQT!$J77~(^uD{d`-~Du!LtC2L+e~I$xz@uAar}fk~Jz0~yi^=jNjKj4^)M zLiJ-RpwhznNA!E=k@7D}((Fz#-Ub9N*rvi3XLxf{e?&X92x)hkD9$UAtYDnY@)b02TYnp=7a8!7{8(bMFi(S!>qJ#n z)>0^CQzaOth=x3u7bQ&n#EB^!+`*XeN19i=GF+e}#s@KbkOf`bT-|+5NlZiV6`L)!9exMgG7M2g1&-jp4IV&ZQ>#2X3?jZq z*NI+}FQ)Nvwdxe4tPjuh-&@p^smyb6qgBqR?+qQ8nA4L4X#64-n%jWSI|cm%I*>5C zhY1PaJwxZzISKuWTUVx9M}VbYfRk<>v#ldTXn`UIgut{v^p7AQDQ_pwZ0?TSs|7-94LyLd3 z>RJ%lPA@M-9_Jf2Jv>C$TM-2)ei&P7FIM6jT2{N2yc zPjn+#l!mk%i#q7^ghv|ktVuE>laU%Lv5fv$tQuXA+>joSER_L}RAaX65uLpFbgQ%1 zS;d|n!=^YMYbfgH;Pjs_Dj0RrEik?23#_$7+vCmN)ZPJ?rzS1@2?k_U7~T*Yy9u;#>6QR zeB*P-RZ`{tM?^-rY>dGXgn75sjzeYQdH9_3KOo0o2!5L(2%q$KR& zIQbGrtzy5PC_q-55LGZeoIm6-XU|_GyQ5a`(TcZeNEOgS0Yqdw%Y`HvF;yUvn zRbiyV`Q^}z8LKvFssR$|#WpN*Ron}OIirzLe8_C9o`stmjeZxR&A$8a;UvAR_=M89 z^1pZ$AdWT5PgjRp%UX|)CgPQK)Z*D(@(B1<)bPF&y@CjAY%5j{+E{6}t(#*BTI0CD z!WB#p_mWOB6_ys86zA3k?{M4&FaNC3uPP zhjQzQ301u*sh%uouwR4-a~?NRx*RJW+{cawOc?#zlV5b3Dg$tMN=-FIr3ENywE1xx zE^oF#fmy)Iaz{I{@>N{$8YekC_-oFu7N_cN$7_p=$y^z7)%_Cw+ei*Tk z{qQH?k;5<4QU!#@p3JzEwy`U+KoJCwu(x~d)%x>^!E3Pb`&}^j>SL^Cma~?GJog!) zWQCbG!rplqGx<;H6HS3;aoPxelgAVAvZ~+}ds9U!B)PKpHP+nq;pHz8!P$$x*a+2; z1e+lV3|cUc+FXXPVI(0GFC?8{Bw%t-;!l4naI*0eRU0fBt{NX=BhW$%m1ZNX?9Ged z>SKGq5RmRDcvS7o>VzbYel@K_XerqPOTm{;=63tP_qDooiu%D@4s$;{z0wN{|9abN z@3xr==jW&Srk>Pl!#dNmjg;ON_wz22jN$i2)wq$^!tz)#lb4eWD0BYqS)NO*n?p=^ zRx;&1Tbi>>LKT$o8PL(~*@OT9&#dUmwkOJ8g|Dp%gDy%Jlj|gZeD|OUF3!+l`!#tr zXtN;cTdBi@zr+Cp*Wb|UBK%AD-PFy?%-`DE*Y)xC?@C^ES03inBZjG@n-c;e?K%Z& zxclYCn)TMoOfLJ5oF$R#us?~36eB_>q4Rd7IgAkvf-AkR8aq0H}G2W4*6 zN^v<9BA7T}C&2MIiBL*DO@GXM(tb_pPyPing+C_K@a9Y0Ec zn~l~c(wBmd|B>U1TFkI~zC;sXn<8Y5@*;%0GTMcoeJakVQ;3U`TXZqnEhDemVPEQk z{KKjhTRE5j#UqTOpr-A)OtjJKs8<=KV|P-scxGfl{Xx&pu||DkY|zw)*RD;Gzb97d z*g`QP0`11m)y;-1ySW{@&;_!b@9&~gWfIjYEaT&ZxT)sl0QhAGmT_;h z&dp3DpLz(_FV=aRR9}*zCwD~C_&z|ykP#LQcP&h z$D*v=2WIYKqq85(?~c=_zHkS3=G%ehEHFe!k3tD z?4C{hI=kXG)E~^<$g@o_oP?Tq_+Jj7 zfUdRJ)WXdA6r4B{U+wOJ>CeF-FfH7nRk?KgpogyrkQOSCD{H`A6Yv1q$@AN+K%OC0 zl88Q{yl{N4SOv4GHsu_@zR>Qrw-26I6+==X3kx@+*hN3B9u6sj0(icG;8(ZND z>Df*V`V5C%e&kL_*Zp>T6sZ%;^WpFM$4xV%3aQiYciA0}w5GFL?y>w)g*_?bZD2 z(PSJb!-rfG1x{UI!ME6`y*vDPZeI+c8c6>DSRc$6I)&W6+j#l3tA_n(H{O&AIHi!3 z>Ny3G&5n4;%syzgC1!p+&yNqui>&xxP3DeV4h)Q6PIZ%!2#I=nvn>%j6eW6=aFa=_ zv^1>5p)MHv;RH3DpR6jtPU?<*Z&tg-qBE>YtDkum5SZL1r1D!Cy6qcY8bW`Ug7N}q z3WPNZ5U4sZQew57RF{Y9;Z3TDwmtqL5x>D`{IPtv+jR6lRff&MdboLGV z^c8ykrii*6k6P4}G&2^We^)#KEr#^*?&2hw3~XCu#1v4^i#xp6Ms#CyrWG@b#`U$g zbcFiHl`#c~Oe5MJzgArv7B_7Tey7D4X=Nl$q4?t1nKSU9pbse{<9K|2eLvM?8VFC> z2(|Xf2FtpPeLX1@#~;q3mlN7M>k9OZL2wBvup!0H-RLxW$a_IH@Y#9amK!mXn5`ZH zjTBhJmA|tr9*49LHTL@pbVGz7Y3LWeSIS4@*O@B^>$mIa{Zhu&1h*L^O1;X76Udmg zXHXzFeJK8KjQ#{FLS$}zP9c$$uE4xIc^5I@XwR)m`?EHaYJNVnDpQ9pcAeyqJqtGr z)wH_;0+0m7x!m6m@vIW`*VfiZzuPs}EItXJE_Bvk8jO|-*G40zk&P&k8=VaiaEL)( zpJw{-xMIngLSac;ZHNDUnD`eDg#I9N6`nji&YEIx@bIOhzb8* zr8jQ!pK2rSLV;k8!81arFvBqP>6pCaDG?sU044?Vyngc^dLxQn%DagBz8Nlm1iA&J zhmi3bKN<_CUFD#pQWqX`Rk*u5g{TdS@rM<{vn*SDjDar8e!?qNcCSqMHCR@-jZ+v8 zL$qxzQRV#NVgZ;IX>VU}I;)6{^`(~*LG*@tWa|y75Rg#%v^UBgi{w?0IeYlt%4*zB zwq6eCX1tp}yqYyo{wEdJI>N+#b?xf%97Gg64~(Fc zL@{;496%?IpwOB|d$AMMtr`N~-ThEix0SmUYjE;&zdb!E=UTYdl`B4+c*jf zo5a90){U_4YLFAa#3>xTQBRzhwwDgmOt9sK{8BbS55DIiCx1C=(=Y z!w=)uneqi2Y&2^fS-9P8&RqlTMQrSmjb}8n|Go2XJld<^Vm*0)26CqVQs!SUPyJn{ zPcXoaXxN@69~q>im3_Si9^FI>F)RuwWH z-QtHNp4t)f86E*bx#R#LJ>NK-1t%DAWg=7rnw%a^QwLMcU2X2sAB{24(E-M-9XaRc z<4d&``jthta=SSS`=l9CRiC2jNM;v8nITB^h4$|2o9oDEkGhBmJb2XuxKQd0%ZBlY zI90>S$}(=)3V`D(+t)xSiVdIncWLDJ_WC9dOI1AU4w8FYn`6u$_Dd+uyg$F0<^csVTRq#EJ9XuxXFIAi&81$E zB+Fkl#?70!TSZ=~TIUZtRW9vhuuL;B952teHfI{Mzf5bLxE80NH_Kv)bR(NwuD;FA zLkH%ew3PexE}_cl>#u2}$t9z`EWckm-h~xi+!XlC3N(qX47ySN*#e&=i>ZOuO(ysn zW8Id`j$W1kC?%^?zbMb30R~sjn-iQskQXCxKh8 zvYl`@%5>QqNcKtnJp$n|?2NQM07o&v73+G(0KG^e0)KQ<$kv(V!CUU@f*r2Gr$`EssNr1unJZ}Qz_br1g!V0tk*+j%#QZ? zmTw^aXQ#yI99H&?!+KJHtGu=k?moLy2J!#@*cMwd_*ykC0*-ZS+a zY%4R?n9YHgvFjM2d>#}cEl=MpxpCU=MkoEf?>5=5Hk6k*lggJUhxX1g^il1_qIha4 z@M77NjJx@SD-dZtkzl=EmOj4Hg&9WEqmK0M{Ih}enkDYuJ!w7)7E#mkpAbPII2)oX zDd`lQ#Z{>jjPv%J?~rzO2lBsQh@U;)+~c^K;#wtgFnB9JYwbIt`45wm zMR~@^JGsE#kwCI<0qSG;f4(_`M4Sa|(XA3~__8Oy{)nslZFCtN$M|i_t-3{oyFr8- zGZ8RQPsKs{RkU&gebS7-L0DYWk7fh=#J~}FPPDSLbjRqV!yC)h0kv+WJSakE3!oPE zY^}<$Ju8s>mq(A6{0WpM`@DjM3pBZM*)}qCb#hw7!X7?KZ4!Pq7hj0qpZLEs$mG5Z z5}#LQXX+Y8rzt>$w0=k+*^C5c!D+*aAkLEk*`6dkzF9!DkaSmiWLk7g_`}O!a)YO+ z3`cO%?oZ`fqBZ;j>?QTWHxx}fIk~Zw`WNrL#mM9r9{HqlW4SWY<=Qqg!*NZL-NkZG z5xmO3@ii^N@M?a!f_`~3QW>1xy~o@~Cx1~xc2-v3Iw4MeK_Tz9=57^#mjm#{mMjQE z%VkSE-26iW4F$P3^SFLnIL-E}6BCwN1|uz24@MWL?e^LP@$%IByCYhM#3g?^PXq=qgm5{gxG4 zfa&H7oIEwEZmFx3+Ek=V>+>Lq)ogDPLZLWO97=p{MWAwHM7o?fC3IQVeQFeuvwf~Y zjTj0ZS7u?Wt{%iz&|))}unk!fyp14&Q;b1ptMk8|k0UD4G1<~ZN+1mt2TMVmrewpsbyuQv zK>`eHpqwQ;YG0yFkJ#DCkda7QS#R}@ub*hEvn|}(YxoEJ3vjE^#=)3!QWyHcY}~~3 zD=rH@GaNb9a&fv!nL;!v{F2_p7B|OG?*0soEtt3AME9~9_9q|*WyE}p9SA@6lV?36 zE-2%#VDH4H9)zBmm8xtRd|7;=xya7WCO>&VX`!)>Fyf=~ZgrtIV0N$%tZ5*G|BiT@ z*^kkt*9w!)n3}*yohrw73z0ca_Q3*#FO7|uxPCWpY5up%ZIe*Lwg&NAIBS9qQNJPm z`mKAY!E3g4)!&C?;7|etnk0eM-HfA=Lx*37Cd5`)3j_9To@Ln5q~5T~LS9J%vkYoz zHHuBKKcSeH!^qPI@2efykRXv+C!f9M9tYq7NRL}(BtSAgJ~f|vQCF^7UZ4N1wf=UFA{}vNx(Yis9r^Fx?*4=JUwmW3ijsPk@3cBqiAk~ z{9KKTITlx!m&|4gYW|4KHeyD3ViMr=48~IQ2`OBj#7I6as(%S{OJ6tmsPw@WBS~8utrk4r(xm}PXwiEsxpMokm!ns_tCzC5g4hC=R z(VYd;S6~X<&y{P|-0T9!Xpbfzs9Ba0=v1PP60UGt{Z;71b#+3)YRSid{V>$wgepK} zOU|#UN$tt>=|VSvY%bIfIuOn`z2&m?^iOxH<|)Qk;ZuxG=?xd&8p3}pZtP9fXL+`A zOOSa#*$%Rc#AyOrd#DOQp(A+{gCI2+^y%^LvcIEk36sc8QC~=4 zXkjPTVF>Dp2YNvfViH=3YWwPB*T^U|CuaeqJc~T1ieupZ{;~8WF%~1_s6Wp|EaegA z^2LhyvTJpAg*eVXgwpA~NtrS75rbc5Ob_u&+n zRU+0(7`#NmS5B*~2o~2Q*=JM;PWq$^%fAy)Qc~OeZ_n4Iu7v^vmu%2UjM5iy(EJyd zili}o$$>CZoerqy8zn-ROlVh!zI#v7hzox`A=Y;6h0NoCt;t3&Azh}$Bzj>&C4+U6 z!GuEx{*k4$=Oj{V#x)eRR-q^znS5K}RGv_-DbK{yV5@}#*zH%U>Cewi@#9d5=o!Xu z*H^yGr#N+*7tPL!tDK~K63d+r6fOL&;qkT=&U`5A?t+=RX{<14^?@aEzB!?qB!xtU z2a8QzulXfPEO))bFTOpEIB3sEq-?<*y?j+ z$*T^A1b(v8QL8yaiefz(a}=r#cOdB&^&n8no(^%+;m!_j>H5H#tbIb&edBx}z{20t z>Z7kG32^rOg)U_PqqRGH%WqHaDe??kIjZM29Dg6kESo@rwjgKXwFVrXr-M z54WnW(APV&&NH@BA-P9gC5xfb#~STlE2WX$+afvKh(eYfE353MGHqnJ4UZ_`)IBxk zU|$+POhUJp#s2lv9x$cj2eA}*EgAmRU$;A<(r=XcznWCYPe5}62} zJ07V%e@`O8b$Yy)5~+%7o1e??CGxmdJ7+NnGuP7v?* z-3umYc>V27b?f+ccK3Vsa8UD2lAwBE=f>{@jF-BXL^DnytcgG;olN@b)HRQ*jdF-~ zb<>hf)k3}GV!w{|Clt!`1F>~i;t7p0LfMyiZO*Ajjd*f6@20aAXS*oD-MO>8lgF;< zf+}0V^}4D`VFL}p`G)uAba4XU6wXxJQKOo|hKu^4&$s=Sjdg`&ZqKvcv-P5V9}@vC z0>V9>t_Q0*Y^ea4To{S$wmhZK>yUQeEevKVoLKtrWE8=O*W0{A1nbebW68+@*p={N zm$g-jG3Yxc1ur`@2ZK=6AoZ*0xA4Ni?86lS^1)fgXyj05PW57S#wPh9oQ0M&l#YJz3roE%Bt*ztmmqJ_EB<(`>!b08a8-rQvy6o>Y2VvquVnz)o zFB|?|8>DP;os#^7G$=1l`eo*e^ajJCLar~PGuIoe2fLBdTA-_NRVSCZ&;<-H^pHpw zPe~Tks}zaSxhfm&8M>KW=TnFoVVJD>+C}0WcuZrpJdg?|!rTA4uLo00E6pN(8#ZYd zvP~8+a`>EqM+k;{abfP?0hP>dhCReNmE&PpuCBGM)rt|sHCo^u>xBA|QNZ*Gu*&dX zIzI7#-0Th8@)>roK_hM}j=%SO8pp|rJ9$JAb9#@9iTK9q?3DZ5&JCusM{WeM+J6Yq ze-ZThZ8rzK^2M#i5)+P|Q7BhAe9&sHI>Vm1@{*Jy-FD&|3Hi(MdMlM2bMBo>XQwAZ zQFHl6xBFsuF>&6sjiqIJNm`nPCB&g~#$*LFg(9*!j^>qzuDQC}<^w36B`8XKkYfx! zDef#)52#hAcAm+>8cA7A1ER3NfKzLzB1`Fa37d+3M zmFMQyTw(o#kk7JCk4|~#P!d)$Gs}yK&Fl4YH;?fSUMpDWu>#eLldPvg@a2o^b@3$*h0kS9CxOES+@b2}n z25eb@ZeX0W+G#A@`Qj9_=Fd@p&VI&JvBeVz9LK$P_NIOCwAoF|#^o6Q;vz7}YMAG= zx0(&2H@CL7H9x*fyl@TjuZ!l2?-NKV$cZq~BoC1O}ZLG^LF4{=F5NqjgGXKykLg-hWaa%lZgZ z@I;r6*SZ*`4TY!pp#^6X02Vv8FQ>h;QX6&H^RkZwGe&RZ^Pk;&i@t#yh;dlX>W$ok z0}S=oc(Ulj7EUoDfLYL+^h#QLd#fefG#nj`jUC^&TTp36&d2BH=ULvkBTdW~i;%5u z1D$9)s{7Ps6?gGNA00BYnGjg2nBXVO(I?gC5RLd9f;f@;nm-q^>E&k~u2!0Cac&}kZ zq8`~t*ZTpFhD|(k1sM9Qr@nhDuPjDM@O>pib^k|Atm-UA!=fD@_@-Lo%1Fm>I*R}u z1AH`eFvdqqg>+F|(97nQY~fH%L9q>d2+qxoy?TVED~#s;{$4Tn{`mu`fB3gG0rc|3 zOwP;_f`>DCDx~QBnGHk9JJK1J{fGfm4+}Ru*dSFdZYiob7$WiFh>7 zkqqca$u&!!otKrvdnSsZ9TEMC>ncJN19&DvLkI!>wc?3u&O<-ze#N8VVm$vZ!)Dc-IA? zIBTL%6DcTaXmFR{cSh4tFZD%%%1f&{;e2UhCJ4Els$2n{s9`v$e(w^-YIu+n$O&iw z2Khu3y26#Fhhbqg24vX-4lx@$=Jhc@U9!Wp3 zLkb`Qc(3*K1?iX0tvKW-zy|y#c7wKhDJd71yWm_y!v~mzUI{n9;r3W1kYqqG3`h}6 z(!xTCj`lZ}EM2Jufp#Oa17@n$Z~kz!3oKE%D6Z*wbr3egH%@%0qDOzCuksz$kxx@4(t~asK*5F9s{MZ{*{Di=K~uR;MiwS#Yr@rzsQ>-2J)RY zt|AgsAgu*SI2!8A=t@i&grV%-c*c0ojo;(PmUMNw)KWBzf77-!lih2NjRibp{YdWp z&C4n|uLGAeZt9(BsE1IN0c1Mq5c>?wX8}G)Aot#O}VTI9Sjz|?sgPEsaLF5xM}yx7XwPMsb(~7+BZZO z&pZD(Vl+K)fQP69Q4MYHV`5 zT3?$;F%uLTpyj`CCN{T|b|H}16aj5|b*V0j`(widg<^$=%7^X#;7l#X<;fjG!;&yn zXJdj+XhKhx=PVrD;Ry1cA!Rj+-m1+v(+yWAAStx{=`yJ@Nqp@Yw?6wOp8QcYw`f!}dzQSFWU zNtbM0qq*vFwYd@6NHdLEx`9+~=WO?FfBfLO@$2{-76p4Q@91Brk``%I^cg#{BzcnI za5J0;12VYrPoL>dXi#TNQqjz1qCde#$*44-R^s2stkKCPkjiY#r#`c77v~!Z9UX19 z1u&L?>%axzf&yEoaXTH33DA5-lE(P_EDM~uiG-!wN@J7ik4nwa6!}C+M&~L`Ch5N7 zQ&uuU01P~A!20HzeI*1xOYn~acZQ`ze~uUAmp8e&gOzmC?xMtGZNwKg3cMf8`U7a< zIwt9bHv-hOe*`|G@*rLD9NY1AF68A)&s5J1daa@`9)>BZV^g;kGgB5g!+y*NmU`I| zQOnT-5n#AVwT$Oa3AGfj;9mneOp^xLs`v9p8z$`Y|5mBflJ%;6U3Xy!g*PGN8&A=U z0;v?wHnp#1jT%gZq~?sZ+2~QER<%q~kDOFw6zqHi)_qAPz*KIvic78v+PxveAcd2d zILO1Wzizb;J4{Ke{Qm26jeae0Oq#T02p&yR5aK>tz@at5%$Wt!;eyQr5Mc12-!FBi zLL;}=jwLw{-*6qR6s;0DeDxd-MF+jdbtLE6x`lWc#?0GQ1r{JVB2g*oOq&d>5XF+& z#YJ}r9s@G8YkcQrSB}HL1mtS`S5c~i10s5j(4!3K&+h6>ndm}y>qOAxdIi;lur2T4 zC>1P|bMgU%cx5hx=oHaozpj6?A*3?RfIZsT*YfJ;yIrT^;6VXJ(EDWpd=D5)QV43n zBNxY3svg#K_d@*C{XDjMIsv~j#`PMEYWM}tDZ&Q_|1MJfD*Llj<*Wmp3i${2uXZ^e z@W(^Jsunyi{0&$Rg(}_`-_7dde6`=Yl1S-aOOENicRcf8vERzsiYCFtJ$U6zErMLY zKO#MdHV4=+ElstppkvRMJ$#8{s8u-(X0TgZ=LZe7aQ3fHuPK^I-JQ02sdD7zW{Hz# zQs!Jq+PU`3Er^4cYze8FMNVz3LaT|=XQ>Q4RA8R>vWkh7RF0weTM~74nH$84Gn z4CnLLS2*XBA>jmRge?P1XEM??lhirb5-V(c))vMgFqw@#mE$q7PrMVnWWoy{Opa0; z%s;Q&JD@E0U)Dct*xT2Q^{%_U;~?u=pIp4;1Lu1C{_yf|99gNXE;k8S1v*oE=)^R$ zicE~&uvA*@$v<=h4NK&bZoZKt08JZoAAVZ56~PM3(}=Zgws9HNB4(-!r7D-;u3@ZcZztqGw=IPLo`y*|xCC`d*^J-?_bU1P zjO|aS$`g&7*Lc8&29?UP%1XV4nVox5S2XWGafIJ7r5Q7vM@d{8mt>g3n z`1$aV@-UeVEK!=9NpW>%ZzJH)aES>LobhitXjKvR?teJ`T-HXjsCgRqw!qGIM)BZd zE!gY+e4IK(!%Cv!EGKgN+1LpA<~Bh%_8df`m-k zHt%BSLb!n>dP;~Zjk!$?tbyod&t%GhU^wXpT8IWu{aFa(QO~|Ba#^p31E(Lv^Ko{S znVxstV9LHC!?g1F8@aqKjJ4rDmMl0o;rnKrb2*`UOk07ADj>3?!QTLlMXNn%P;(o2{oUT8 zVUw9FW!Ov+2GgR@j$l|^IuSnwVf4;Q9e=o0M*>^b0+%fcpk_-EuyDRV$lR%PihBy2 z@+Rv3*5=!2d4me$B{t}*6424Y?Q^eh^z{4vNdfk@Bla%Qe`2a*wt&!25R?Vo0?FJB zbj=H-@|>E-{T)~UhmvV@T5=_+;}Nj2oMO4^Es;dMXGJcU(J^Tp7SutC-jDtH&!K;o z$@-!=ugVk6m#<5~iwkcnjY$6L;!DNVS0M&qv6@g6I7W*Z+NW2DMXtUjgu}FNXE{Ha z`qQo&j1kOD$kEw8q$}fmI(rXQOi2UdYIQZM-;6sO7yyH)=E0LAt^R9tnRU2Iy^92- z{2}x6Q^0(uGlC!$3)%b1@H7#jTl^rAjIeh1e)GK=(;UAGTbD&%OLt00+e@#D-$SVZB=8SND9dru|42AG z19o=$?RoNRpYi?nTIw*0AYKB`By5qUu(He`)8#6l0n`gp1S7^whB@8amr8fvJ_JTo z^LCq}+?m6$__w7ThrX6SPqw!9db?fSV-6j0}HSE^br3*dJ}%+qEH2^lu?=oQc7g|d*WtS(O@?xmR@ z&gA=p7Ymkt&-$lvnw+e|8C2qxrg6Hjevh4H!n*{c-SW}>>|4%77q@pc|Hw5JH%v;8 zz{d9n8wN`36fqJ3Iu|Wnxszy1-?4FDI8y%0{%Hf-oksTljP_dP65CaJw}kc_@Lx2)-Lw%!svOK>Nr)*9SIbIy@;66Vj*M6!`YZI)4BdLU z3tt*XS28W3B>bvKN-9WMY63m~8Px1F;^)PHAs!IKf*f9){FqoLL)l;h$t`Way|r@A z!69IIF$^@q;UK~G(PEQTOH-Sm)?;x1jZ(${L9}?VWHJ*w4j9C1?p~ceYNGshmCOy? zK+~6<;wfsmu{-<@{*B+lPp@NZLczm3g-O<`Ei^>6}=wiW;Ovp=Or3A{gj>woLIGg6onYez7s%z6G zBrGi%9-;~v&xHnZocW*D=f>9P8UWN0V-VD58U&cY`y{AuvZ^;6OeQTRdxVH8jiiJI zl7*9=$6;AoRoEHPD#%Co67O)Y6g>H1j1=)!{=rUIC$NZ0W?tRZb!EyP1g@;aZEw#t zJnc-$c#1pHgnA;AV@DOV+)Mocjv)y8e3m;CgwuUgzZHkU1%|bhSuiiy^r8$x57?N z9w8>iOREV@2O$rRJIQJpiwmqR>P4kcrB+2b(bW6qTzl^6oB*p8;y}T-KeYhI5YD9q z?agJBC&IH6Sf3n{3>}Wb5DpljiMF4+4xPKZmf?e=GeykX@qMSva+FaxjI#_^7<$cu z0l7LIiwk%d2s9>>&f3;lnOT1JuN`H_w4E90uA1G4rfZjLNUwIezl}w1OM&g}X~kBI zo4i3nwZU9m_k~Cc6H%)dD|aixm=daDq7v5(CWIZJv`}8g{BcHPIb6!oJ)<@nBGdzU zLUGPac{w@$jM^U*A5ZoBq{ZRLuz=&Utjn5wP4@>_F#vv#n7{(r&!1$$)W&MnJ*q8D zIzj_h+$&^ysM1W@`>CVb z&%Lcsv~uC6Rje8Mjl87f5`>J5&*Lv2r{$uh=7TBH^#8a3&1Ov+kSboU8ua4X`#4of zZEV7Vt9C&Z)>PbZpobFQ_nQZ6TKeWuyGw?Qtx` z>=!Ecr$y1M0|4F05UXykm5q%BA@S$q37uP}t^OKbj~c|_S#Yho9146mA$(GnCcL`z z@;N8{;8RDVw$Uxk_U$u`+2pQtfYxMNi{~*FdMUZH=L;XjKCcVWx&G$2)zHeBE6kyY*NSUg*q&`tR9gNVoV=Rbti-pY|7e36o#` zI@X90Bg~yL2{c~e`QKBYLteF5-a!XE*e+-nH@6&lAaDr{G6w})$)@(_zqVe*et7A1 zbo7WaVc~~*ll1ovo#4`aZr0r{59=GL%l*rls9M2;50RM7WSF@dUtRWi6^@(P_DVcb z7T%Sm*Iom5y#DXU3{swXz&RBmJ_IM&C3X~Ay$m&YSRo*3iN@sJAG_~zL;DJdc5i1m zsq9qD!cx@W`?R29k)fORrBg+I|}M#S!=s zS_y;jHDMqzC!f5pPAuc>Iq^8>8jwlQqm#LUni4Z5y>|}{b0L)Q8_*4S8mqRU$Q5Yo@QYw*Oa z)w}Ccir?7p?}Jaak=;ly zf#!K|Ql_upiVDdfNhL7OpW%9uqVsmL1U2Uvn7nne?JkmSI{Jzz=b|MPj}^EMbN!6x z13xnXp2-;xJn@1V%}4=84qFc4qN!sC4Q-#cuEHZoTs>i$zv9xE$7v#}AAKfdz*BEn z)37aHTm%9rEyglgS}!mBu>bzj(|a@c4r%jo^y#oi$DYV3qmX4fNFRzrL<8MZ0Aa-@ zs>M_%WdceDj~)21lcI1fbr6wd5_A>EW}R9o%9XD4ynR0R&M4L+WWOn<_jUvgQKie& zqhWCS@}ke(m_hfFDj;VjN;odqM>N2P0{tdvp0iF*aoPfi|MbxOS(k?m_tqkl zBDp-cq-Frt;xB`NG{B8mnK7kGmG0Zqn5|%|H3TA>MTZET zK`blQb2a{fPf!;sBN`Tz z7Vpl0zotr{nV$WYTK@*sG+qbr81&!cfQz1Ws)%tNa&*6tKfrm zLKqn-XfG^oxMh?;goLHL2$X>AApLgkhQX|qjGhr7ad=sY7zr=QeDiKNhFQe^Eki(> zF{sz3QFpH8;AW?1`%g0(roz1c5p+7~Uqv8VmL1LMK4;=!@QQy;tlgeDg0NfQG#leh zf>Gb(F|Sdmx|NGCRgOj=|0AgA{Gwd&%14#zuSptn>&4=V4$A31M)OhbD#{Z8s45yl zbHA?0-~ES|o15DLu^uEhNL8gv)5HhB17H#>{hDiT6Sp-uL-y)?SLhyO^ODYTibV5C zf&<#|L6&ydk61szZdNV^K0YQE729YO<@3?c{ezY7kE{yQRFNDwH8xzxfIPMlzVxvdSW4fDYsAIQxj}^CuaqE(Lp= zzd!xD$GcIw`6GA(=rIP!f<0YL9iedNmi)(>|K9oghshJ4nFW#h#ob;F(JI*kx6Fv1 z5&_;|1uw%jc3#~*9s+A-s*ec8h{9M9P`EyN?7F1r?P8<+(ap_@C={bFs>rGOw>PBI z6UaGnCe_`JMJ;>y`g-{t%_?JPs4NU|5%jiTBL|V04YQbm zUfMZFme!YaH;5%gwvELPvb5j)x3-bE^+Y2eSeKNHh$u_+7Kz_9fQwSh>{}0EXU~U3 zmo5(?V04Fyr0XXOW&q2Es6syp64&C%H6akuo1Gr>+}jDc^80t#ycnkD1uH?@(n%5u zmMKNu%*z4fIey-y&5+bacgkTdF4)augaC_sF$oh0O1geZ=NDN#KnT4YEs9)>BUqx6 zX7@%QSq=yr&a9}WzIIwJp;)|Xa!(km(d1_3N%Z|9JC{KaZe$iZxSvnfA(OI`<&YDq z%cY0sA&kGFaZuN}G2Qmg{JJZ6(6Hhn@CL0~F_E?rAaCz{KOmseCXpkLUfyC_xw>R& zEjn6l7s|co<~cg>;$AUNtR^Gx97|Emgm<1Pszi z+ItBj3epL8uZs`9`6=_EDby}&2$OUD%FNA5AR7w{`@?p6kT;fh_89jxT&uE3wv_FA zU$6bRVtIH3y=HVhmYHh+Kzzhw&vR`%8NlaYt`N+$I451Eff7$S7g!OyziZVuFbe}b zfdDWP4@3F;ZoXb# zu5Ml%Jn_N)>RWje{YyK*6Cq>KH$L633E+9Cj}*qLpePZ=u~ON*)o-kJt;@|BVtZ_dHy8(9|yRkp}$jSh!!C04FiE?AvQzc4u@@Vio%WRy<| zw6w!mS12gNc6RP28(;2bL6N+Gw;uvXBg&Oj0L$!Vmy-|UR~g2REdEN;Vy zO^G2UUyu_HI`r1EvvPAX2s=5Q*38yVFGfW+bACBtG;(K@Hak^4AyEDfI=d=i^X*nqAh?oZO>ecixU{1xmX`J7i@Ng=>1<0+lR6yo7?874D|vuF*8-f_hM&Lu zp$h`%0@K9f} zE*XoHC&0CF*NdaRm45X`9<_P;{(Ic=k+9qFe#ZN0Bvc_}*GKxuRIiUTdzagNr~l0? zxVJqG*XOuq{bKkhZV_R!Ru9s+P1i1UGHdvxvHoYTv$T-e08L5|r8@iITHS&Ubpo+$ zBwO%I?Rxt{*;N*u4B|q8@h|*2_01W;lSPHSl}!+6POuP1n;tN5@>fjqP^c0azAu!s zP;0n26e!J)O?iEVly0(bXSinAQtsrKJ~(*eQA?8|?`f+6Q9u=NcWL(5ns<$Bbp~y9rvkbF zc2!(uncxkWHp+3{RSGEc{W(|1-THVFFb70t3}sc)d#*6l`?#_4a&ec{KT?=e2s?^2a7mPx2!v3H$y^-4=)QH=+~DVXOt!{$!c7--LH=(IYky$3|d> zmbne`DO67ilh1Uc)noGbdhff)Y>eCG8?k)h1hO?e@ySKW9_;hgP1@^r=YRGFj(TK2 z9-~37^|<1CJYJ4x<>cPj_jp~8`tM)(c-j^^$u)(&cv~M|?_x%|tPulflC>Gh-~!-z zB~@Ukgz?o7lJ*$nzcKZ}?miO3P((^$ZuH8Y8+`O0x?_TPu|OQAEfB~{a&@a+sN{dV z&a$nKDv)efE9`7iAYdTyZ;Zi-AhlgiZ^;)MCQVXfgG25k7Z($|YLXp+) z8JfS5H4+ThJb%?-PSBtmWv`^!Z}74q6d#g_hY&b3c{j3UBY3Aqw{LO}lFFGW3JwZ& zXRo^Z5r-h}gP!q-f@7Dzi(?P_$^>|^}_2;vH%Afz?>Fs;N?Bx>hjohJu1`G<5za@Pw zm#DO1Z6^cDXy?eb{3Xgy&@@@`!WA!`rHHQ@3$=nVk|{at?GJwQMov&Af}y%jTvS>S z0k5FvTjc|iR%QYvCCEalYqwHX zvzg#M<-jEkiTwVRlpR;#Fye7Y|CUjxQ%ku7JlQ)Te57Ej=*wA?;CZMOGhnZ`^WtKW zyhi^DyAetOd*CU+O?>*p-^?xb@^#|Vl_dQDHn`K<(68qVuyeeEDczi*Y`S76>H%Q0 zZ*AAq)HJatY8JlR%|YHH$?uufP297<8VQds)U{@X*nDq_l5ls~diB4vyYLqY5Yk8D zUlR4RyzyYnjmg0b>bIkI^3M?6+$xBsYt2FjnfID5?{Xe$e--4&D7OWn7 zi-VE!x7Y8Gr7+3orY3=aJ&l)zhi`6<$QhROiiK`t$d*SQtzXx*H?=moHo1+Qv^wya zQi^6TvLgUu4-hzT!SL^@pS>-1^FhNHvwf$(Y$8&k%@}knSe?+Q+Zk#ez3nnI)&x42 zX`23Yef+%zxqf6m_{OJ#)6JT_#s%eonrzO`6wDFl>Kc1;#F|*7R1}pmBNdH+%B?Mf zkFBlu_0{#-+ExmRvrADx+{(Hda`mrTdhr#@bIb0PMU5STsoc{On2oMyI z$;~|}|C;gEqHp+Xw2{jL%r1(vS6yNdbr(bk*VoFfF zLZo6(_`2PXpSs{PL?0GOcz%es`wHNgB0mo?AcP^^_)`jeT-J2?@p%UgKn|gZZ~}pe zR$@WV`m_?i4goo|N_kSMs`llP;%o!#TmGVGtwgChM8=#Jg3(GXR>xG6<(y%7m4@k{ zZnN6Cx*Yi!`iBCDKD|YQjAx{}oB?8}ZhgGm-1NT!fT#vLHO`SJj$Sj?I%J}eG5VGD zpxkwB#W5(kp!wKoxfVJa?~E9H2n=kBRx4kdl#XsZrm(nI3}4vY3DM%oj;$s3V6(?nAVKz! zix7Q!^vMzo{@gnQfrp6UbU^&@*?rtrT5*u9vLptDh}Y8*0mel$eu~LV1#c9~07md^ zXcJ(yp!VDifJ-kMlPpgqwFT$aX{lY;Lh*UQPpb0BTjr(xa*5ZwRB`3Dd%T@@*1>ys zeNAr*grmkR5ZoIT6#7-V-n><<2S`Iayi(jCmZ98o{(HCxjGWgzA z*eHa(ZuVx2zhF>Y`umkv^-a*}h+Y0;l;22mtnuvJfIm-WNKbb2Tw27W)c7QY2|rRT z@X3Gkaem7=IwpqHsoV4 z*b2PzvsxU!68~4z&>WSIz;cHxcQRw-e3mm%0eC}38ACCm)qplv@LqAcmZ6wJ6K0si zfA@6p{Q6!Tj^yoL)zNRju456W{LdmW$r1<%C7jXoru|Ht}jwvh> zzZLh{W@I|4BO^k7&P%H|%t>CFuY+}O$F0^5rlca!MzBpSq8V1s7YMD^vF)Ew!giyU zo=;dQFdcMF&=HH>>9g?Tt{Jqxnb~}NA>%iDb z;*TzRm_2^xLmZu2wmQswgPYfH2K+ZRfDVvH_X(W0+16INhq$Qyh>h7 z;M`RMn4yzQ>8~(PtM@kCJkr-_^4~4Bsv$Utjk)*^7pLml!r5VjttGRtU z74G>g{|4Tg++Xgv$eIt&gvQIbHX8p_7CBPDxAT7 zIO32c>uZRVMhS0+Tl&(Li|0>8u?Uhwam27gr93&|wQ(y?FvM|uwhV1kIH;MFKpdT; zG<~9+^d99?IeJ3(Go0AfUW(W|&!?{zD+Tf4Fw7)!$^T{VAwd*QkGAsJGAJNST}4*_>O zhd*I)U=JRwE&$Z(_hWlw7^`$|p!N-KITHxzqszq~%Pbi5z${3aa8gD07%y82W#+8o zhS-@B@tz)#5bGm=>JyCgqJ#hB&h!d57B}$VSe5 zl{A&025_8Nu!W=?a^N@b0fOclai1?Cx_Vk~2BwZymV?Ghd$^@zpZG(X&3{K(tmG<` z^1NkJ)@e7!p>f~66M>`_f8V#!!p*{Ca`N$sR(-iA9uB_b2xL1WJ!`=srEe%&hgNr8 zuFQ3_U^vyZ!OUL~mICBOt*yry@ygga$Xs5|z_zI2-<**EF#F>iveR79MZ?9=m#Pe7 zohG&?6ta{59zq)!|D@5qLZbK^Mc-c0FG&C+g3}b-q#~jMfrs?>dgUWnQCd7{sQ=Vd zifTY9@|4Pz_YBD{T)0wL##e0wYw2wDH8pLT>uzkcCM$O9NzUJ#IdT=ui!EkJS6Cav zZV%h1V&f%|djm0J$P}QPNra2bHo9Zq;aP)U*nGQ%dCI^vWH(LOhfd#=_ z5jv3=A$T3iG~dr5wxwbqY!xhU+hE*f>_!y*-*vm}`!MEwFc1^G;KyAc)_wMR-{Eu1 zYEDRTW+m|P@T|90o-3x7tvugRZFfX?mX&?yRD-*F4XdCC8ehOZp362}GQ9=HW(7vwHUG!7Rkqn&Ke4 zRsP)8cKegGvvX6BnE62lXt|u54CuRSA3y#LFTX6TZm3Kz0(5AWbF;`smysa`gthjx zdTOxX$!?*G0IlFPE%YfLbxbq{RjD&E-*zbNG-7(f$HDZT=Ka*k%1VH_8IA&dpvSKG zU+P_PxI16pJf!$IMH#6wM$fVic`y>7roezWWH|)Az1<%l?3U`PW-^$vgZR3S8~feC z3vix8R(D8EgAR1TU+mxlYY(WzG38Zt!7hsHTz;1SI-_^rh!v8{pcjLjZkOxV8L%G? z^~jGlp>`Wi#bUkIb|alESR*(9OqvzvWZh8=ez+vAWqv$b3R9^W<7Izh0wy}GN36zU zl%<=ejf;yx$zx5ncRL_+{tiQk`*s+Y+71em<~aQvk&6PLkOtD%wZKTYxvioC^Gsm` zbU8!5{z)*X(a83^Z(Mcj$C1Y={REKZ`_;11QKxK!K-2$i6I^zi?En5gap!&b;{D=1 zoi8f-_I_5nz3uq9dhZ$$t&+ysG2G74YRDw3&m0AJ4oGFPXtF3sicU8w5kNUsikx^S zCzrl-9C8(!{)M$Zi$G-%4bA%h#SFPfse&B*+}3cWTbPXDlMFz3AMfC`Nz+|930Ul# z;|;t0EKUME*wQ{FA2DoOp2sR6Z{^I>P1uIj6%^a0DiQY zG5pBSr1UJ`v2UkS=RasEHPF=CSG{0GqLCB`YqWMk^0!&(0PrJn>}5>%%?oxdn#|`5 zjV0ZDe@bTW6#A^$6k^aZpk}QW3dzNtS{!gz z)hh_O8@tlh$Z2Kb;F+Q&qw98t^7UYHO`huWo1G$|1wZIWdu4s?8T<|-gcMZVmuA9A z7-L>6E;^YEOM(g?6{3oD0dASsL6)j&SryDz&rViS5a{50HGd?W!GQg><3xB~m%^4{ zX>I-Tl>9(}=kZoy=3!xB=V4&v1cj$P{7j78+?;eji!QliWd|b$t8_;Vr^0|3bX~-x zuEu7PUIjUb$z)_bl~-3gbskjOa<;@7jUPRherZyP)^QhO zu_w`tr7GF^=0rODYbbPfj!1|$HnzwT<_zP@%qBU>RpX7-O|)kFs`j-h`YOX=Ouc;G zYMXxLd*?bVN54@EaV}{kas^*K62M|Oen>jr*40!R5_CxJu;Z{51LZ7`nUH^vK}$f$ z9hS3==$!s({qCz$G|2mjZ$G& ztYYtMtvQKUF2y9P;X+8Jr~v?fDsqopN-8Ky!o1mN>j)~)dF|x{TtZjZ9z{e>M3J2n zG!dW1$~tC$jWo+GECVc2jgoIuu0?AWe*#oO)EF5p;x~2+RMGlvN_NC^gwrXCdyTn{ zKg#Cixc7u;6}6>mt!Q+T!ecg zox$-bknMfKF`qOT7bF)L_F-#?5V-2#LF(sk^qHJ`UZwj>YkM0!DLk{OInzNi3?~K+ z9t2ERySmllaV}$zDJ)TRq+iW26cB9SS^Vd>v-jnEr=abE>ISTdBy3zzYD|tr1D}}h z&K1Fm-;oQQO(VK)U=2dAp$Y_$07GT%(uUS%E9_KPXsB3hnMFd}r7S8WfMU<}e(#?F z3W5&n1;r%K-DFvAsM3-Owt4r8l2=#7zZuoRH|53+FnMI0YbA1mtS;@6TPZ!Y6tQwkXx1BmiK4 zFo|JYr4%6ShtA8Xpc~L31}}ts+wJ>u>kKy!5cqV072KbMsU5xW`>OXQpbQGEXw{Mv zlS1v2-+JeE^5MkN`~+H>8;y9{2*ZlDTC^_6%EwRl@Ps}UMaQ&tk|Fi&nplbh>1_FZ#LL(tXNx;h0*m2qgl zg)f(jSP4aBmSp&AJ5Ax%eng{LYaQz+YKgFEz7Z&|8xyym8dLmR8GV^K+mF$<8euN}^1;4(>#!e~#kV2z9^DsbH@p?pd zb9ayTkCHV<`CP|1LTp(35Atp5;4VIPfJKfD*K%6gmoMq|9mX?8zBaSXjVHgs_90Q4 zpU*X0;o}c|Vf~PN%3wpi+?os2Lz4B3+qA zb9?#J@LW3;J~b?qvZW48uS=sk-FIrMQDubHbQcW;+-%Lm^^Ds6zytKzhV$@BUVF%v zT&aE^aW%%e;#jHAv_3f(6h$vIx|=JBV@*%p7xN}DDceCBSEDPavG(8RtWo^LR{M6z zD!jp~s|~hs*4Jy{oLuso>nO!kbqm(S%|(fFbd36yWU3_|R+IGUK#i3YDKjezK23mq z%>^HNh#yR0+#4^9l`O!Fy1*C1Zd8Al9)rr;k}5U6ZgYZY3P2u3y2tN#P+SfvU&P~A ztD1E@1G4xEdXx)S*0_@eYm(FYlYyn9)mfAboPfr>zn_bnVXy2LzX!*b@|7;{mtH$) zhLAy$YSImH?ZKd2at2n;(V6|C;XS!#>rJ7$0FceZH4;}R@rjW_WpAebyE5V!P?j%d zy{fx|?>?n84lNfd4nl|^cDZ=^yNCAha?j(ftIV+F?aEB53>DliRBNJQrAid4V-9AB zRZ!V_BVk;Kr!7N#y<3IVfD8yiaFhwKqsh{}Up#GO!oLU#yIt>ieq--f6F_|~&JFI& z&F#!xMM7%%CC210+X(H?tg~{nxEe&6JB+zcUA&5;#y%Gbn|GH>%ox62lNN>HPOGHD#lFlqNQDIR`#)Sel~^Z`ZN@RsCWCXg$R@!nhu=(DkDxgHZw0%FAut!eO zXZ&NVVo3o8%z+P}@P2fActGzGs*nzFN-EkXi=@vLNM0o5e@d@Xv;Q??vY-4z^j{>~ z9xzmuF@E7+Hi+8u`sD1AP~t}#11D{$}D|v{T$UCYwr)>raAkEs3;dZ zV!K3n)3Ht1Bq_$p@40oopbBM;6jaTnrSJO=0Wmw$rj*k%c;|xousi|LXQCdzFrX;+ zgRP5J*Y_7~rNWw&wvr>QnXYua?vSh>yfvnoTn z{RXb9K>K~P{-Q;&iu$XC{b6zd+xb^XL`U0po?VuyoJ3O7RvQ~<-KZgEOxE!JKHrRH z2ows}Of$iNKt-r4Oo)84n`!}sGf#|646xhZlxWqU8lTB3RVB(sbf65Q)G~v@mt7S( z?Ye?R=@2o(WzoOgkZ$+OTS0fXiv^L&u+*S?w6g> z+I^6&ov&;8htLeS%Pxvpc0t7SUq_3qR)KTH3i7& z{aAZAOTd6wRT_z{!mp*Oa#xiG5);=YQpqjB`a(Wv$eup_{{A1;^%s7wmr5cZ%elGv z=YFbB+wmxrme7_KrE5m58Z(B$sT7*4(Ul-9_WpkP=p|BT1RuH9AxH9eg+N~v4Am$& zJ2*t&c~vSz$?f~A>Ja>;8COL|N-Y`mNE-CDO{0e4bpe8;_yeN@(EF@jo}1)P7~x9% zEIa@vb)y|y4eTP{DRDNKzU!>k2laL80K;3aGB_!sQJ4_Ks1?7+MRt0c`+sap#!$Y* zKTwDygasOU z_uCsrf4DN<^^F5t3c|Ugb&2xA^im-mrU!z+_*4GyRtx=hm7}C# z?so>JW!@Dd`+8_UEP>I6<8uDL|LQ^OgVBKZz?eA2oj?luuS|WVfRf|`htgVN4a}(f z0D0{gIsVZvm{Kfu4O&O=wKcwemw=w@=x~g{FjAo|Z}$(@u!PRxmlh}IrXtc_e0?Q% z_aCLrp&ODl>KD(;=Ke3|yqi9^xA`A82Ij2~*VAtPi?`Rk$@KbX>eTM!!k#Yv8X!iH_2t`<8|p-Wh0- z30?6;G@Cos>10(lr&!f`ZK^6=vB@wF&;H1*F#!;r47k7oPf460rpMf_iH^*^RXpSKlgcZ$nf%DJi8MgW$*Un%o_@2^(< zpANF=q1k&&-OuOs{%;?z5BB39w!VkaY!7Y^0eu_(F5SXue-C_X9LT6-lL}vxZpyGn zIpnaf2vBZ*0LX77OWC9ggJVccE^hwz%q%aAL)&OBOBosZ_wk}-0~FfZABNKJUObB( z-^T!AH_tu|U~mQ-b`V>pfrS98H##B=C?LUCnd2q?5)Q8yapQfrzU$w^DRgIkye`L6 z$tqLVE?8CSR;9@4QK7CwSNwF{+Wm5%+5PW%N#FlX3A$=~|J&>j?YHcToyFeXi`rGB zCt?Utpli}JFjWFGa-RmlB&AWl5;>(6mT*9wY3>UT0RqMFmI2iiPh4!S?P6|w)bV-u z%|^3+91(zBVgNEUOQKJ!o1!)r$TXw0usT{fKK1C|_E(6Gy)fyotNv60|m0M>J@ zFkb-0!^gw*WmnzHq5u2C^~d^8ga|3BRI9Q%97cJc?c29u3<}ZL2k1k7a94sBu~Yxo z!_C)!`Tn;MQLtr$es|WhkiDTAy*=EmHzpR?lcCNz0(7SD2}6dn6W%H9`7u+Sf5G@_2d#n930#qP#h*x za-^DIi>Z*w#A8v9Ks!R;-ye9dv_D!!`Px2?3_k2X-j`eh67>CFmUB-ON{e(YtIvN+ zz^nZB``!Ja6-T-0_W}g%wRXQjH^ob~zQ1?)-HN2OhEfH%5$fvejTaPaZ$7Ur79ob) zfT$LRjLmpwqTVAJ0T7X2yH<)~ttT4?Y03XbabIKP^)giMc#R!j^bcMnaV$CfJ z{FtMpxI-(K{O2h%(+BA_@bC5IzVM~D-)gG$A=|I@nMGW8$HZ*>Z^rpRCY=#Z`wsB+ zzx`XydcDp#qkEgMDp3YLh7&bl2y7i8LO2uu8;&16wZQj@$YJvx@wMsfeIw;c7htSR zp<3ot#3=Ul@gM94Sy@1cNrDfINi}aPEYY4^qzr@v`&-0;0iV$9FQl_ise#OW><;G~ zq@T=)erL(Z$fOn*CmI-e@`qk&X^C+pO&%OszEABV=F6JYq ztP3@aB9v7Za3h%d<**{ng-EqP9v#+ih@|tg=+9KGHI4b>AsAiNEm?e`2P8&}9zVX@ z0B6r9i3V90zhBiOCTaerg9!W*{tKPcXeMR|``Cq8ZK8h!G(vL+}mQ$ci>+D?Q>=`H`(f`~4B)8HT#w z4QcQq0w1Z*MIcl}m<+f(gWHN)B0F&r*G|gkH)%M3;`oJ9kCwlQb(0kl0Lx0iU4?*} zw)KO7lB*3Dh(LqvI>*PygSg|WYfakA2%&ycHATA;x4;A?pwSUajjmwxhztyYV;2?% zrMQUmO)G7z(mj~DSGz?=sMnrXopn<-m0EdS&c{3eG{I4{^sgsFwjwgfQ3W^m{d~gaZd&N%ZJvF{ZIGjzI?3&R~&lTO$VC zEK+iEH%>i2B4D|{52kLpt8ouTk>B` z&UJq?95_srs9tZ4&VzMXQN>o~DoG5C0o3Wdyu6W=R1OVxsm+v9e>UX8oGlpOU$I;f zU=5s%p;`wk_b}yvwjdZJjmZp#IEyrD;!4?oy6iNweo((~h?}jwq#! zFiedW1_m}~A64zskFWUP9ti^RxWeb~y>Riy6!)%+KK_?xlc zH>OsX z(dAIT#h6&p2_Ym3<&GXS!iTV9bY82CaCitrGwr<&I!{ZNP&vkr*%*2BDI)fh$F7jE z;0yM$8aYPsiz5XeyA%b&Fz$#UNEqsPmA2vYDt6Kk?d8Gk{oYt>j2fZ?OPDTN4%?v3 zgeOx%m~F~~bkUw?onck!7*iq?!@ZkGWDuLf!=ZmempsP8A{xV|oiSd@?j%M9lE$Q7 zZm|jCFL)rJ4?>^5PCu7+U`s$dNdU(1Zqkgvft=oVTB0>s>pIMmwlva%`ybc`$&EaW zaFMF{5*2fpb|&C;UD$tfg)l4g9K*HEr*K~ahAj?Bsz(u~p?G!KC94NfhR)fGHB~@38UT}}^>Hm=mSrPA zOx!p{vxEL}t?a?W8?J3>C<18?)AMk?y;;6BpElmSNM+WX(rfoHWh_&#UUuMkEVs|; zoMFt~Z?g33-&Edj0qn{mt%yEkdgn}BK4a4U_MpDqSGt0|n|;O|SGQryK zNi+iU{RCDSd0V}akl&4^Z?7cUXiG@9sr(Ss&r&e^Q(M^i;f&UyJ;5xm)9YS9<8b@x zVVX0Zp>(WYV{uf>qsoJc__udNQ0_dDS0*;A+2r5;&oD1nCK1~%ivN5>YGtZ=-u;rq zZ_bUF#)umr`lUS9zWCz<18x3%QYqs`9PJBcrgIeoQ7`YKh7Q;q;lY;0j%J^mqyVuj z<($X3Op9PrDtIF-b5pY>xz7QykA@@*O9zy)$|}GV zL%tg3#FPaxnox${H=e!v@bIR8g=X^@6fq#yg%HWod}cIq>DieBe>P{U4MOIRe{+@U zzWA}U!Nkz!xKB_oT%Q>00RRPx{k^!7!on^H1AJ0zDo;*u69aMRxpCMSZ&kQ(es2vx zLT02UqR|H|`;L2HM#@wV9@FFW_kjFB(KGDa2X6D3-{|X{ky#{K<{9rYm}QKIqLPle z%!sZyD-o2bQ8UrRXp+UzM<=Hb4;-hUZa>FGxPr1U>wOW8*pYu=i_C2u_b0|kddMrp z>Zl~@U_t>Bb?a>|Vuyqc6q{v@>&So0nGoPUJ*cyhfZx^iCP-f4Y{jtKR z^l})$tnbP(NqB9~=mQotDWY5v{voFCX)EX)oJ2e3+nM(IqvSfkpiU=zQWgv>%dV-@ zOdjR3__K_br6~lGh>*N63ow|Z-4S#sNUX-n560Nmsc>iVmWhfk})*ns+d0vyj{^Gi0JokTqQ-;LPFk_6;V?0}>sq zw2&Zxp#3&4X#ACGl1r*Duu{w*?0b#@!dhGgGqG6!dnv3{;8@P?P=ol)+z_>a5zWBH zYKIK;i?DS*g#j7GI<;;d+$w}P6g1LX<-<>ZT6}!Ww{J6!WRuIy-dV|=uR}woP=CH#0vOPe zayd3hub$^y%EFF=G@ROCq}0t`$~d}ZsDR_2X9l@h;8KUqrC^F}s2s+6|k5`O2ocqJH z=KXi8F_Eg2C2VCtr?cjy6ci**0;F!Tbl>P~1@Pg_QBQ&+$H9EHbe92vJjqf4nD9YF z2mp@sWo+sYx>2#D%uq=tMQ0i{70tgmH*leM7#VcOcI^w+1)ub_+5rNt)pPHQKkTsp zlni^Xx)T<~ZEGqRgD_jo^T+B|Bl50+$i%;?e3coKyTog!%Y|v+5EI7{I=rB7^9K?m zi}ltxo+?o5OSZp_!^ma+fWjal2U2d>xC6vNL2&D-*jO@gyRcUU?i{?##o8|P1x;?! z#t@d98H;E_i-&IEmPG(okW2Y5>s_8lz);`6j8?A2LtGFMwS*l-!nsSwwaF z&e7C?5C`ual{~b1(8!3so;%h6y@!xpaT7VoAWq2VbjTUk+X9uOLIAP4z7MvTm6{tv zaga%H#=KH#2#ZKDhI?fE6Lp##UFsB9maADTy7_+9LO6P6x2r@<@yc^YyRqO%*kpY) zjClyc!z=`NyuZ_Dt6DL#>r zuG=S8l3xD&0KA7gY8HLF%qjV z%3p6`Uh>OAA>QV&id0|e#kmS4&JN_a{rn)mm#Zu84e2{9h9Ak^mtz~gB2%<;k;`OUQA6HSULdN;oxmku3S5fBkYioFQH9Un} z`-nv%;_(Cmz5r}XCzem^>gsT6e{IbV50GP0^Xy`<4gwlPs!?inhmIJ=2L{ImN5+T7 zMn-UTF)GatY@r|k02@e2L_t(x9Xs6bb;f4g4o8sVutjJb+^8LU>##2-v*lZ66VZsW ze^jZ_s9uyxl~(t1x+v%_UR+%|iFNa2k;Bc(EY7i~N8vHv>Gaadg_SeJw1+F_H?M(z zc^lN~=CkK7|6*xADCJgixj1XCvJPM&(75R4E}^KaT`rbN>TfD{pLSR)}S0TsrW`*w`S>+8#c7WVHYAVS|ZDvs=f!BcM&FPA4TAci5~kR)XlP zOA~#%HzUEJA652yN0nyt3AI$BF-*tf)6<;n%2G1!inG#emwP5NKc94)5FoUo^|h6y zRbmp}we#n%UAb}XB91_O_VSw>YxB=(c(~Ow4^hw|7j=1Og1dV}VsW2Xua+uAav?wj z*rfBZ+KP&Og*!CJsoEK_Q=Ui2|9`LS2bsO}%_9X)Ka(wIlRMxUWy*=vYU zU4a;t>N*7}2A%E)_{gMR3PzMN&A=%5juU!`L@G1;B9p}eA^qU8X|Ao+DM6&2;g@*vvXV?G;|>Kvau?r{wFt8{>gUZcfo z(JK27Yt=m}ozF>)#VE|!DM&8KB*7u+o0YM2oMup}l>-C)W_=GZh)iZ;7QNTUA)C)`zDbODpG!|m`MgdZND6-E zqa6Z;!WefO^uQkgJ6xDdfcNnExZitCRn^WCl0ouY2f?YA#QHJDF-yqdpiI7O_tgW&(n+0$`ul`tJOH0d6l0ow8Vk&Sv zi2ToFkV7m~kITiNc*2h1L7fq&(P_Qj!-xCL(B?9IztO8TMw}K~>?EN&(s)Xv97%&= zu^N;znR0a0d_>)^9F4paU;BrR*EqRVNHMV($dB0|03@4TUv^tZl}eL4w+0NdbPfRW z`bPHQhaXPA28V}Vdv4mGkvDLgK!>7j;qiD~-NtAzqLK7SWICn3PbBXWDY}Hsd~QR1 zWkq323CSRN2FSiLNHOw0y}8%uo1}O=3{N;a^pw z-A0@H)T@b1`sS!Iq?05Y7{ma4V;CA4>mM*!gRiY!zPP!ZoL@bc;>6sX%wmocXXmnu z>&t8!s*vgQ;?mOUxpPaa8yjn{PMb`-O@@f|orqE*Z*2k(TVGen?R=^ObckG}3c5`) zu@q#qSt1h2MGA#n$g8j8Hk8*?m69$d-!7)Gto(^)xk94s^#MQ}u9xiuKqkgbK5cI= z2vo05uRdf}OO;1}NsK<=2@53_e06K!^759)EsDq7F&aJQb~|2v$!48quU|Z~xtdOc z&CRgfvB2zNwuqC!tsxecg9fj1l$Mx>6tlFrcy4WDeOlJq+WKUxytVC{txZk1lRKAN zSIHA}bUe!Cip0J^(54aVWlFCS0Mexpi$wx%V`W`^IWWi`l0oua2dQXm;BT!_W&w=> z76wErb;4*k+&em|)S7&QX0;kr$cYnr46_+btjR`;&(GuXWb+wZUuZGAoMxv|8Mww~ zjE`Aj>5a>m&c2yR%%43o%SHo}TTHXlxg~%Is1UExJP|#;w0P#+(wU_T8*33oQxmto zskynexwTdF3NCM1S6hpfbqKlGl;C*HGD)w=i!=J=Vu?b?Z{XI|)i;)t43Zas+}4|0 zh)WE$D#S9JITd0!j@#`XkgDSrlR?`Ha+2xOsr72LOxy=Xv!_R*mrBiAtazlkImwYrB~0sadra$a%L3(5(~03SpdlTQg%JNkaiD^nhNwJE}pr# zlv}&777+tLxC(qhbF;9uO^(}qaJjgZZ~N2TeSK12;#HGbC6*bDI(-{(okS#P;?~vI zHI|oGks2iLE@mGwFQ!YOlIeUuqB`6j$8iRO;j}t!5wB7iiP>%D9&wNUkgBg6cP{M{ ztE5t?SmVsh&1dIk+3`dwmj#FbKF*w3U0F?gCM`C4Vr^q}<0s3BIS{Eu?&%mOy^v0) zvN&NTw-C3E^bdxLPa{5-vP&1%Ol^D~w*hzQ7PcO26Cpx)SS#xkw0Da|3hgPDMJ7^6 zF~z{!tHg3aXMJN`U1Md%zS5l}gXFmmf}`3&rHUjt`I7mv!{Y#WP(xIz&2(6)^U)}N zb5G9+jZWRy)zvNP5{cTxZEbQ%BsrUz%Os29kU170x!`Pu)up9#iQ|(I8|_qfV`XJ+ z0g}pMx+ofpaS}N7GP?u-S)41T42|{=%@BLvtu8LEtsC0-O#lhLK-ezFUOFMT7r@99 zojk$Q-9ovU!|_SwePUU!QYw*Y^kQ89vK|?vqN;_ggXG=Cl-EDO2bJ0<(VJ1FhCRna z)DVS2vzffwUaxP`aOgyjOgE-^v7-YdLAMy3rcfxgCUD?kI?YZHb90FWb(atz6u-qv zE6Rd7SzAtJmvU*gn;r`m0fFS!h&gwQ$*6NcdDxyjO(dDM#hka5-^uF)P1-JRYZHkS zavZIWMG%3YUDP4c7C&c@b+?K2N~KKQ+bf0pA;&b zI6t4u&9j`AiHMc64g|8cu#jELB-ruTbP$LpMQm}G%PpnijIsWH|I}>mG$_@@Y^J}x zlSd>Nxm?sH>Q;!_!O3)X3IrYPB2lx#mweSA1Mi|TYrS5hMl2KxK%@dd$||Z0Ne0Qc zi>WGW+~4%)B1NCLN3FF{>Bp%dzri?$TWOmBA4;Q6tvaDr4w`$q`g*!wR4FxLIlogv zV-r=fR4RpYsDMf`+1z4oKFjgHWQthb%WG>tUSD5WTuh+_#vO(d$RJtV*gZ8xnK&|V zBz*F8F146joS#(+dB_$>P$H37(cJ|U(fQ|sPQg%B6oLqmDCEMYIywL@0tjITPi~leKyW|VaU zKCq{^u98SGr6hyo3Lsm(xusRLbzIOO3W;8(*7?{sxPbJ};O0#eSA*MUMmc{jkvI_||$30D(cE^j- z>)CbGvkNJXn`s&yHMx_Jq88^DlNwsT=e^Rd zW1XaG)HoYd607s$bQkKBODentM9hg@R5#uc00oq@t>-u!S5ClIJ=|bqc{CVg-&` z_Sr&8Nw)$EVk#*y2BS}=mWVn)K=PaT0s)^dkhjX48k$7bU_2NHkQARhnLM4G1!$ba z^T{bD4n(7~iRm#5xpYz7jSPY^6=&Vy+C=H1Ak8$Y^wPlEWad*Dmj0DG@G!67NRRd& z^|t|`QoX*fLnsF@RC47$7R#iP$}~oULEY94`h@7js|S}`Q&mll2g$dKsjexn=QYdQ z`f$+UZp&AXNc$u*X^&Vc)k8J`+~~z}0bkG|;PLsu?VSRCD_D*;Ys~GA;!36`0U-o5 zPM$1g6|;g?uh(Q?&TI-d%dkO!;Lc*xZVo#&y9|njokmi@(eII95j&m9r2?vU-oduE zw!XfugMDhPStC;)`mRnQf{X?X(xLE0-MjU&UW3hP(n;II-QcnXO*}5vLG}TKkPMPv zfE4blt!v;5MG7pD9y0#OtP<-r8od=(^fD@20Eu0 zBup~iukKOfJ|joWI-k?_(r#_9PN&oNbt}3>q7Het%xBR_Wy;+)n@K4Zw~5dNcQz0J zsj8|XHAtRaOiNWwCAX-9+5!M-q)i5fbbgNHw*Zv=W^=- zAbh#O%5*Ymv6w5)nc=WlGgE=UQ~>be0QqScHBAQ+00va3G%C|N92sWDf-@NLU@+)* z$E*VbqXX19WT>fPR>(d{ec8!iP;HYG&tJXNd+1wgl~f@TiMqPM@(P8J^7?x8nqDv9 zqg&M8EIa+>fS?AiKJWDEEfp7o&?7rR45?D zK!X4_@C53xp&kG-8ZkwzRwl&}jRpe2D1A*B)^LW`X$!o8K_ZVGjHcxYtE zAF{i{4wuUlbp!9jm_q}~{$WobzzR>X=uu+h5eFj_VjO?zyDGqgNGR;+>PFur2d~=& zp9(~XMkeVK$y+;+K4p1c2ZW9h|#VU&*a+XcB4+2E#9gK#;R6gtzZ(I_>eR!1Cy zQxuO;ow)kL$k1?zVyEC*>kKE=PLB)>sMSN1a5&(JhJWN7pBNt+|B>H68DeN9T_T0( z=?)x%fMqkBqAiyz#2S2EDPjj?Dj)lML8g}N+eb1;o?Xn29s9~F>*^YLe7P7@sia-r z-u?>KsBktF00iIx2Bv|FgS;vmE9)yO>+2eu6dI5k-l2(!VJ3}EqtbBC4mxv~iF4~D z(2@pg3{nYTB<7qL85tPp9~d8=v`!9BKuk_LC;bxxW_7=xLI>0gkMyg}{kkJs9cU1n zMhc+l!jY#0Hs#F#4+MxrB9%y`Qi-@v)GEX=soXl$Af==R$#Wf~q@|^zyu7g-cZ~t# zAR+`Q*hpZD4#+M1r|{R>fb@X~sjRPubA5wIrd0MSN4+CQO%X^oHmfCKb;6a%fJPz# z7o;>Xr(^V(J4PKI8t6Bh4=Htr$3_MR2aX)+9~c=K8X4$UtMmhYfXw)S{>6iBFG^G= zdS2`~qy<+BA{2+MKh-72IWSBTgl@$aqD;rfCu8hZ^C0eJfMLu*2o|R5M-`}t6Kcol6b9lr6xu%WJ?d<3hf$k7V4t-l8 zB*ZYUvyp`#{VzqK~ zz%Vpn7|}?3KsJcF#Xb0hHVk;G1Rn~|QAq(&{L0$8hE6QR37+omB9_N(Y{c2w_28Na zd;n(QQ`I-|n}s+tx0AJMX$gT|{$>#q#FnR_vw$mf;IpUuiuEjz|LWCH1Qgs3PIyG9~8GiR?$l(k~U#8 z|hX7Ap;yGnNLEQKTk%~D&5(7>N?bxm!}fdf@F zEiGsjTB@Kq3QOS!Tl#|+UU;Fjl8;gm+8M`z;Z#T-jxz^s3E=AN7zYmQXxR?fgip~@0?A}YHNdo@vK*g)J^h84 z7fA-mw~K-H-m?eJkYk`xwjVf9vg1I@0c;%nyJZU{2#>;@Td@nC(Nb}YyT$du8W3PQ z(J9x0H?FM(^;izv04WE!qq(UGRPM23jdggjm9J{5Fk_Wf0V*osR8>`q2-w~N98uj; zT}`|@zWsrc18_$Pe4-sCB|ms!2f<@o`3?sgq5y$UPBKV-0kU^5!ebYJWG^{O-w(&K zW5+6LDvp&^e7y!5wz`mDgci&gEiI)xabgQ-puHsr4&WU-02({??u9glpa2$Wfs2wC zUVucn7ZCzVsR~~M@c{(#gBSMfAQ>dTE(Wl%YuDZ#JNNE^)2_WG@Nd^Hz(~nnytcCh z1}|oi3A9v2O-)73z8YkOmg>SP6bmS3O7V9N5UIz6Qbv#j@&{P&BydUMGj;|_q_?qbLw<4y*-tqwv486OT1l0nEI0Yb)S0mw5X zK*;z64Dt*K5HdavAiGI`kU_eb?c48p!Li`}zW)9FN}t0z$m93?M|OW-|NefZ&!LOC z*Z-T}F1Ww1e}BIc2@nz>pA;a^kN_b8@}U5EhGY;DARh;i-6TLrfP4%K5K0Qt%{@&u5-edPWDq~Je(PdugI-2xII_YxqFJ-z$U_W|-e z#$6A2{xbvQ>A(HXQ@1zchyKV5?gc>JC6H0@pSJ)6n)naBOabX)J{BNPJoT-ozqkDZ zfMCV>E<8N{_-6#j-#q?*zwzboe)Fz8kPmYeA?qN(9)I!8 zC;#qy|GfQQZi50IZW77X1>ErbX9URizyHtg{PkZybxV5`+-w!%$6fG|zw^%mkar7! zKkmTCdrY$R-UT20_5O{203f?R#{l`(Up%_~dk|my#+?9pemg-V?_I#*@y`m7?|=X6 z-~OvF-vy8lE(-1ugCIiQefReTcay_=?^STeJKyovq>H&zn*Qt8F#hiEzV?^5Ymmo@ zha*lNC!YEI-T3%q0Qq|a$UlGYd)t5gCy%_}R<||N_B(&Ze=I->J|c151&Kcd5V8*P zm1lnWt6%@-Um();S5X8sN zfA_!t{&u#%?RR{SQVhtCcRvCk1s}yA+ev`@A(8r*-}(37{N~qp1LT7g^ZXwnQUM^p z{rQjo{*k9{Z?q47&IKRnl0On4ggh;{MWWu~VhaAi1?gfw&gQ=J%U|CDkoPnQUVL@y z;(eTadVpO0)z5$T>~Ej^&^pL1E@t~5?_vu61F=!?``fEz1-BTMJN(lhb}?87*+v%1 zwmC1TW_}0aTKKT;?uK=>`Aq2=n+em=in+E(Z(wkdQu>IZuWZMH@ zK!7~>#cd=&{wD$C9=&D3HsB5tAS6IO5+Dye^aTPS50L;N0rKGh`69_6BtSj}AP?RI z$d^cfkO28OfRGGA0)%9ct^dt$-`gno*w_Czh*xI|ZF}IsFWeCX3IHGvZu^M%@+1Fi_cw@m<{KXpyFc{S4}UB^{O-GV-}%&MZhMty zZn=8v-=99daqnaI{e1oWf2D1ZO+GY8B}T@_$NvZ7_K;l~;yX?N0000ju-`B>o^lWHp!nW9vFj7>+3(CVmGE#|cH<4lIBh1t~9)G%40hB=YR z)Eq>9ZHn?MrU+p+CRSpr6%U*7S)V@N>wA5_*XMd)_aD#wzMt3qy6@+HzpnRl>ln%% zWCGID(*qs$aP`sCTSV5G{ffmpPx7ujjh>!<(=qQ8Zu;C=SRfDxYU6`OqnS)5)Cx%=k?0%}j_Simry*QWL>7+4q0?wI zBV(u!8qeeLs)ZFj{XHh82wpXZ$)=Z9lwxCih%xxmdakjFm8TDt#bMYY0&Q$PSuBnog&iS$SI7qK!;oC^WUR zr<2McvDs`6hhqvw^7(u^gRM{~Y+x9K3%0Sb-qRB!tgkk+Lg9%xnM~5(-@{-qz-CB^ zq<>m9U0+|%vN+&~zTj6zaodanM*Fp-^aMg|4WmVDhOjmq5k%I1)uL zHAAKI7@nS&!adGJ>ggF6{jf!ygVZ^lbNQviN8FZoF8`jQthCFMLCP|Hs~jyAA}iDeN_M zyN+_TE)3dK9&jx0;P}B{r_V{Rwz8U+K!-h>-i9m<)E{}6%FCnwfh_p+#@hgPUTYqU zy^=$z5}%zWDHWP_3B0CG-WOyo)ryrIGLgKMmV4a%M6ys@o->Evb^M-po3IQ1^-vM1 zJzOcJL3>|?`OUrAC{=%uEY^N%YyKefZueZ-)TD7aci{qT+Qee5&oig*GeYZ45HRF* zn|HPS;;czf5oBm2Fh zp>4+1>)lZ=6%KB=2zA7GmaR*CkTF&Lk?vSgaHjJd8d3ha(9gFl4vgLq*lRVLbU(X% z+eI(eE4OaSgxm1DSB`xvbXuQkvtmmd)RPr8cmJTX2Fv;qVbRF!k4YIsC7evCn;KuPSv6Kr1>E9aXK|fUZ=$73U$gJ88b0K#Mb^XS@o{q;?A{Ox1W3r|B`|khFZQGIx zeaV9+LCu5L?40P8HuX_=4w_|n zF9U!0wPPN15?Xm=nkP){3OqKIPcF&7a@?^m;f`heUwMb*UvPF64>c)sYJNxA-oQZD zC}-0I?L>P9*O$OftzYrgF6f~tTVt?#e1)bee5+j}^+Cz^d#6ls==^w)fW7K{U!P6D zyIY&PNE`hV{@S;b&21vOQzLo^xQbA^2K%ZZ@sno0G*|Nex zGhlU$^ZrZcZb~5^At9zykl)zqUXUou1$VD7W%q=cMb3uwv&e@>pAIj&qq$Mlynp|8 z#X{=j05v>GyCS*7#|oFWzPfeQ$4c_a&}0Wqv+%nFQ~dyuZ>Qj$I#hJYuXr7k1^dZ9^VX_ zG@AyKlKCC2uqvnwKlk=P2zjD3WmVa(`_^ARkr6vAlpk=# zeMPFqft!z#K1PVd2cPyo@Up@_3OFda-+m!+(O`H&(Z+Se{#_BzHw(u%PsfMDzQOl! zt>Zq#*Hul9gA6y-jW7ib(Bf}3h_v=@r1YQF8+!V%?h`sJy?OlLpJzxy$QA?q-yTd# zC2*)s6a6THSm}Q+9sr#b4IvDa8x`=%lq}8Ca6?6^a%*@>tl>5C2l5K>HtC@u@GMv# zXaUKyqd@_CQa4=2`Kc%Dh(K3s@Xae)K>bw@KpuilWN-f-qGq#}2Ge<_*usZCu5V2i}I~Y}|MaSRLW) z*iwgE!gPHhm{TL|jg|tc&l`t5wu{Z5Q!a=*u99_)c;Hdl@%y==?Z6Bv?ahtY6x}jZ zynt6GJpV6-9}x-`_*>Vtt}j0IoWc)XfLtA>tay5ShxmA=4}=d?wN`eNn@@qJgxi78 z8EgBoCzaub${%PzvK(8xo3g31N|+g(ZG zuJT-VtH4T$v{qQsl}JkuFJ+F2)F0U!E;%(7F}%d#`_WQ|EyL zXtxw`sR8+GHzt3YH?tG;9g%cuEiYZetMy;^=bVwGm{9_}_4EHc&B@+ZVg@SDw@n|C zl%733&v}=^IGx_DmdNqL5}zBR{lph0LxNF|hOCC@9hyc2YZd`qAmFNXP9!?5*`cUba81Jy6x&I+$?tXm=?U*xpD9nIn!fUc>KemAa?6em-}yZU0<*fz zdNMj3F3r~r2Gu~KKYT27Y|)xe1;~Z%^1WI~apk9MMcJAHG~!BCM9ZP28QrqT=F4%m zc<03n6E9!39`DfhkPB04n;A((Pj-G(uZdk%HQkZ_cK*=d8q1XV(flpS@{wuwLbYYM ze^ZeD?9>hS6v?NWr(Ko8H@>Y=hKeE;l{)#Li0>!xmc(m#2m>#SoJ9rMMJ;i|nb^$YR&uz1J}{to@I@XuM7d?)6p?+-;0NY+ zaBpwLbzta+%;=t9P-@7KgwG)ZU7F_N;?VQT2*F89@=uNU5!CQQMTX&(%Yo26IcsP- z>vI}&8jIYc8$I30U;b0CegMI1tuG(mz4Y-l&Fxy?y>AgPBt$B+z3|D2A(M{bkP3kB zW#HwHU7;9}!z+uM$eq1kH9gNMiz}^5mMIQ_ja%`{Yg$DBzak-9yB>q1X zQc{3Qp)FJyK?%Ji8Tkf0nfQ_aKLI~w=>HJ@x03!Zj;`MSDdqp|{5!)>fbIxqDV1RW zpdF!LamTX)*>gcRpTQyTgekV_-u`d)pj0gX`=666vH||3bj59Ds&4(eVZZ9w{WQat zGA-zDOE1)t{rv2IdGvvmEot=%c3E&RQ6rXZF+yQ3b9-47Xp?7)iqVIvdu~fJ=jExf`Br z4Tn5@iwis2p@qZE8SImXEqDKNF+fthn%lLG4)Eg`n73cbPgcpP%J-dR?Zc9atn{{Z z>&CJUtc(rRHZ_l0b`Mithx=dn$3Q{e51?q=9dO<|&4HJXx(`N3(9NtnEY@kRK z6wD5>3N%Nv=UE+OUR+i~oB#RK&Pv!X+x?ST`q+l{qfc5WB|xG%CkRGSf7jSbHIIAA zJgLTN7@p=~j(BGIRd}FseJ$|)pW)H5c6eNEiM@%7sH#3xTMHcM)t&5%p}H(NAKzAM z`Zgw7MFyY2hJ0dJdg)ZXGAtq1UtBLaW&!{DI9K8*Dhx}qzy`E<1E}H?+~5d>-Qbfa zar2F`+AuwomvGyY=j3W;pTzA{we)oA-iKR*_6!7$WXZUi(IJ&zhOxVQNX)aUrvNKz zZ(~y|Q=^>-R%rn#MfS48&JXjrJ9URZY4oe+xo$7($or?CHXgQpUd&eW#`YEpl{O(Q zU$r0Sf>}YCCRr)5^N7B=Zw{Koyvq(o*MM8DtZ3=|i_^1NZ{NZ;-=b+g=mXSqdU3^} zl|YNgLP2RdQEE|ZI9gVCO_FeB`=nNH8#coxR8=W>3T z@V!R^QlqV_lvj0Q0d`m}Zfe#bDH%7W!D2i0$_9;xz+IS2wIfzbIf{OFn;R-<#5%eV>3{ z5wM7D5;Zw=NguNew8XJZp#1H(-#n^cRh;=kPyS4QP@Q(+CRw|VIdan4MGJ}UxWIs`-S zC=it1K|tWpqzN3VbY48?j`#j@?|bimf5zA|d(XXg_RKftoZq+B3VooX#=yZqM@Pq? zp{|0aqdN`I4(x?9G!Gv`kS^_l;enO`^7QG`OiWA|GxYH>J~Aoz^y$>%;^Lvvq4JhI zgsP*pwKd)jCyhY)1iElQ@CbQnA5m!?G@gk?0E5A(s;c5}xZ=jF#+F7jF1Y^pC>MDpM09E}3Wf6U z@E{V2#pT6GS&>UiO98BuYTa6nKS8yjehU0z-uL_o5o zr4fa~x%k*!;}Aum1EN!b;NW0sd22}oT2dMn9UV=sCI&}lO-zhtWo7yJ_^@1)jE|4k z!8iiZSyk^4bMYEawNn$Wk>3NgT{I$JMC_?wGojZAa@Ujb%;igdEvdG| zGBonKCSB{>Talcx0%i8oUoGn9P<|wr`nj~zEE#1dE zm(8l(51!3GVi`|MjPNJPAR?nKGm#lzznYoZ?hcrd(>+|gbs%=#Q2_V7(xRBsYuDCt zWB{ZEt$!@xzO?>`*F>Wpv@DnAzux%8+yE2g{ z=ap)2P}H1O1Lo7r4vuVgoYuH?SrXY#J$m=1z#Apdt*Kjk!P4Ru^Yr?8k}Rb{YXQ^R`NV}w{0J>+n@y^q*}+hW*kpBu_uESRu0FNs3s1DrM{m?Bpj*qp zXJK>X{s&v;4ckBG&A&z3FBm;kkoon>_2T6d8-=3C?v(b! zt*ItF>JXaZBbpLjGkuQjVs=e(@^bP;h#l|Za|wPPjmVm$!U#@#w%Ds1+uJ91k(OrV zhd$M^a@j@xCt_*-KZd4!mH<}QNYPcBh*&RFIXkPrz6^pCBP78l`&LcqPF>X{7?VMlje5MP3v|rKNa3~rpTl<%LJNDN{hbJ z1U3&MJoA+klq+r(fBw|QsYTp8oo!ekHwaX-r*&JCKPlp;<5>RO$XZs>%2-kTalLu% zLFSkoeJ<#7aeo9oJ$>7mlBoUInKK$)1WkEl)%3FXX*2)r?co#WHF5ckkg*WyMbPbh z6MT)=t@9&F{J6lq{SeqaOP~6rRHs_UyynScHxEEAH+?uwS4X8^Y%0#&Imh}(C|Fs< z{b`wU99r6Is^t|=z~;T8+O5W&A91Y(3Ccp)*#}W7;lncx^qy-IT{kHetS8w;ZlzYb z{$2BVxl8`t2Ggmy=S7nFS|3fG>hh=R)cF4T{L1O1)7Na|`HiDYFRK&Y(*F?+b*|>o>!K3Oe>IPR;s{gqGJ6JyC zi_^)`mtQlVEH-7TOeCtl3i!60QH>;$js)9D`X$i3b4Pzjh6i5Gu~3ryYCg)sBa~3- z(2b4>Z+%$2sAU;n6;2tY-y+rT*);5^r3I{xcq(k}bvKlzeRU!ExT+e0E{wDfs8b-}P0{>YfP;^<|Rp>-`CL5jbn?A5E4=>W_x^EKPObz&G1f2%N zg|T<3;8WPMyqL2l%rA_XpCRe+$WP!?Z~haf3Eji&?|vp}j-rN4)RH%KR)oCztOtKo z60v~UUSi-Ny$DGt^4wbp1rLX|NXBgG@1!lMl3>VIVKJfkF!?KEs+sB%?Wb-}1Ka3W zlkjQ~Qa@hcL0Rm(G_6TE6+UVgc#8!tO$CTwT!UM_qZteK%0aFY`!UU6XJiq42&Fgt zfuCy9o=JjhgGuTIGOs{Xa!4}CcJ#xbgCfvLt_%WkQHa*=Df`ymDDa@Q+fxDOBbv0tj5bMGO{aj z=$(m)B;^Pu9{w`Ba4WN~7U<{&*ThVVJPec~Mv}h0?abKSR*&e+xMGdU^o}!@RKp(n z*r03SGrAeKuR{#e3h$AIgW>Hglvw{#DqwTd2r2wly$%ReiKHZ92YDag8U+nA5x35fG;#Lama zX=gTQ6(HVIv$TfhsrL#2UcfFN)rG!PT~F+_M!DKH0Z#yIGv}fnG#l}c6%@~I61;2h z3-VP1)Al0TjyIp`=V@Aw_CLaLpnn2FA095nEfEUT^~T|tHcprP`O<~8QF;-K0fA@= zl1wSY4#$@xM_}D9oE=N8tgMknXk|P!wXT=GAf2%4BvQNJ0 zyP0*vF2YJtZ-jMB6n3gigs=+ii$s;#kO$;38I|B)X3Ba->=hzs3uimgg|gT2ahNK= z!)Q~x$}o<|e-L1a)I0yd1J~v+CO=}{@pZ-4rz&$9&_2E4oi5sqc#AZRX(# zg9_>m>QddLW^ZWyM=wh%N{`J_m1OkuK06e{q#>E>qq3#0S5b*iU4lL%hhfaII4Y zAd4y@+pmB-YZr@F`HChwHG)8fq#SS%WzJ6Ad4=}6_z){J0bg&qr{ps>i@7%O#9Jra zP+;h;f1Hp4{%Tx7TkWKg-hpK3>dU$8P!PJ%S*k2KFM7aVYXeXcc-r^T-dO*^gcM-X zk@1#cLscu5e*Q7S;!$H~hjT0pLG zepkRcXyM-H1xBmyCFAjqH&?%ouus1+6Eny+c)}&Xeu_ZRKIUz0lV(j5R8i;541zDg zu5Rx6l~4g(PQl~r+Iklv0|#)d^>6yW+1zsrEgemNhEywHL*g=WyNx6cxK;32I&LSi zhSxf3VUqS@1-1~(?J-D%M!=}HKunwTAcH~ShoXl9F)M<-<#cuSSZFlnT3ls6ekU--16-XTulFt*K(pK*Fi_?uATFp>~{^Pdcs~X;j;wxAfVbmt06f$oy zh;`n1F2|h_9(fIk#K%Cux8^kRrL^u0mP0qP^w^?(ki8@=Sdu~c<30+! zT!#s0S;& z>pB#Z7yLuL*Hb1d3^WLf8C5v|evd^!B69hV<}a>l3v5P$Gxl15UMb(lKkp~2Q}Xk< zR}Ay8M>}A6DimGH^)z0+H?bv_fRAqZNx=W2Ob-z77<@nN%_JCleSvV#ggICMY4V$-|1WbM*!*H+!hDSvxm=<(5A2yT z!7duLe{(G~ej1t*n^WNeiHb3jfUauK1AR(2r+3rOqnSu~+Wy3Js$l}Y4j$jvI!9Wa z%|g7_Tna^dWenMj#}b=F3psswktP&Tj1<+1A)Io*K{sS!Kl&4#dcP>O7eZceq1L_Y z$uXrW#SH*iWLEY0p z9M8E^KUM&`GtS5tOeEXXqZY7*A{xX=!-@~t%|hdCZJ-iD<809U6o7ZX_h(9If%c4+ z>^0UD4fh8cCLLvuhw9v{TSkV0s)^&<_pN(Iw?ZD|km0N56-qk)b5UNEr{=trLj+go+=r#LfR#n{9 zm_DzWzw;`VvGV%qXEZJ8{98}bv?fg%p__lp%74hN5cqyk)J0qYw#uQU8g1@ZWfZm< zbDb)JQ{#5;r zu8Cd)$hhX)==#8EXTvH(=y{KMpA6SL?|T(w!^rKt8shfsDbMaJ=0z6SAZ-adG~;Q{ zle2iFiC!7UU9_+$B`q(IER9yt?X8eOo48}$wpK6Q9iPig0a4+|?lWVwx7%t`e9X_; z6v&i;QldJPfJ&ko;Bg7@XL))dXZ&s#ygD_|@Vl*`xNjf*OZT*^MxTwCAMuLV{vTCJ zy6`*C{?4_(LFt+Wh?BB4pZtVS;K^IZq;-OIq|I8y@Em4v$2D|{LBNMw)+Qr zZcD&u=4-Nj&SMT}dDZiVnTydg(fDmVN)zB(A@s67&B+|dgahmDk*kzcw74h~!|iUgQzH_Bh9U}uf5H*VB$@9{4hdwTEQV^*f*&k(ll?hn z{o$D})pN6YhCM!YQ=)f%ojG^!@*TJ3j-_j|0Z^SOpx#kS;S>vbW}yYW$$T@gmH> z7t;kk^F)k#|Gn;1OgWh?TpyU;PzvcRBj7Ve6EuKA}=D{oT{A1nyB{;Fjg9tEz=f$$Q_5BT{paA>Z&QFt;+c#v@TuQ< z`vaIUaIuU_>6Pub#P{z_ z15Zf{D=YIqi?X;_Ew}m6L|uSPKPppwTBhg?4@F~V0nn~HYQ`3FM$MQ)_?IZejOS_2 zPOos#$cnHLaTAGW&lgjevjWoN)k1e>%;5JP-u#F-Gc+V=DVF-O67L_?FiKB~#D;34 z9ZVI)zPy_8aZo()IVgM&TG6TQLVV(c4PjRyUV%`(}8N33Qi@4;xJ92s7 z6Zw0<@A;Wsfaz8@SSl ze1`$XD~UU2Qf@`e8}Jul%KP0%g%6!_}J`?{~U$wK)DGj-37 zl!~ZauNlad)$7D7uwS_EiWzXSBVz>U;A!z6fV#66tpO4sw1p4?9**6bwDn!g6e@cu z{p)0L3~cXITGxkuU2jSe@;*=VP)yaapUifirLikigW`0GP#oYxT#2_$rbs#L;V%v-*GN@P~%ntR71W5_#Omp z?Y>gH(lc}Uyo>Lw0M?H&ot49MopSpHw^zXk7v88xWuRp4r%_?_ z!-d28p#Iece(J~TuMM9rU2Pg1!pmW`n9xStg&&2Kj=ttSwD8U;n^T^76SSJK5{*xK z-NaXT*tmaJIXmEIMDSP;uZzWxjd8Vi9DBX|VMNUMhynwzTshuFTy)IeWaI zQxqAchTFS6w@7|GrrygEb6dP+sFYINSJqH#kr^m`=K^%71^D2ThCvRDV({KM_ zCqX|r+bGUh*H+ltZx~S?Jw2MF(1d#VdT`t+n#jV3c31%z&>c7NnoX%jJbwnIzHA4N z@9k~6MbOB%@Vq-8V^hzbwiJV~TXXt{{M#XLokhSRpYO4%lBs%cT#bTmg<^RmxT&06 zDP0EYd?%p-l++M^ecR1s+vmtF`2yH8czf_tKYAz}QHWsSU0jd4GYl&{_U410A$hUT zq3+^C9k+pQi}rs|bnsLX-?YPXGYPk#aPQQqq=s`(zxK$S7+G_ZZ&2l*KfTyZqiuqB z@OX+r(5*@cYD-1{{j(ojW5JBw{d#yctUXNAZogVGWO?Bc?=T##($fI?i?;r~IsQkR z{Fi5c_s{=@{C_vnv;VbG`7Ia7f0dp(GnTeCMfcw`1jy;%()f2G({$@URm%TNJ^#b^ z|MA%WM(qDhg#M@Ckly@-29(y4{-4148^r(VV~y!15_Ah8=drWx40U?T0Nv4V6GoFq zG_dJt2A`yCv*kJzx?+mM{uB*KWfB>yDJ6efG5c7TF)=as49T>D)?mh3`8p$Mx8!WG zAYZLuKU@CHZx3=Awppv{Mm?&-*s%pLJym{c+wOx*+oxl6nKDvX19Yr|iq%bYhqS@z ze&6!~)LL~U4}9_&6%{&n0--+IMGutUVJinwFYt#;nDp{dlO#FJ3WQ!ofkVWoAF7D9 z+h5p7wgEvcywvF$efB2sazl+e#TUC3J_V$n=EJ<#yp&|*jDkjHoikMdu`urE>koa# zTtcLu8z1>Ac(Z_4*1q(DNE5;e6)aooAV&VWGYvds^5yCs{_xm0VE~*d=R@$8VW z^6uM9gFKyzz>;2UUkb>XIu|$H+q$<&VJWKbtpm+9>1Kts&h#bH?qqsURYHT{c_AeA z!j!U@>6zT~bFN7s(9u z`?q7KHHZx{o4N^Pqw}6wV`5AREX1aB+BDzFI-B+s22>b(Z#{?M+#T#H%;Ug%m?**^rdqu|tgK$#^j%jpTI=>Z0~$B^q2CK0P3GJj|$fWTi)e{{~Hv`lbK? literal 0 HcmV?d00001 diff --git a/gramps2/doc/gramps-manual/C/figures/ep-altname.png b/gramps2/doc/gramps-manual/C/figures/ep-altname.png new file mode 100644 index 0000000000000000000000000000000000000000..2aaa1a5288ef5a162f044644834af185aba282cc GIT binary patch literal 7992 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 aRlUdW8-l#*7{&U7lkGMNqgkL~?*Bg{8S}UR literal 0 HcmV?d00001 diff --git a/gramps2/doc/gramps-manual/C/figures/ep-attributes.png b/gramps2/doc/gramps-manual/C/figures/ep-attributes.png new file mode 100644 index 0000000000000000000000000000000000000000..73a15083d0371927159663e660de8e63fc9aa3d2 GIT binary patch literal 7333 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 S#h3LqiOm>}GAP!&680ZNLzuAu literal 0 HcmV?d00001 diff --git a/gramps2/doc/gramps-manual/C/figures/ep-event.png b/gramps2/doc/gramps-manual/C/figures/ep-event.png new file mode 100644 index 0000000000000000000000000000000000000000..6d43d6c04f26c87baf4626f1ebd8633225b38281 GIT binary patch literal 8248 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 literal 0 HcmV?d00001 diff --git a/gramps2/doc/gramps-manual/C/figures/ep-gallery.png b/gramps2/doc/gramps-manual/C/figures/ep-gallery.png new file mode 100644 index 0000000000000000000000000000000000000000..0a0810a9bcf1ac5a8067db3191a3db42e53495be GIT binary patch literal 13410 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 JT=`|h{{UU?pE>{l literal 0 HcmV?d00001 diff --git a/gramps2/doc/gramps-manual/C/figures/ep-general.png b/gramps2/doc/gramps-manual/C/figures/ep-general.png new file mode 100644 index 0000000000000000000000000000000000000000..212b067fa78bda2644470cad7e94981ac4c23cfb GIT binary patch literal 45280 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

V3GzucVn_ELUudrQQuh)X^nmZn%;CT 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 literal 0 HcmV?d00001 diff --git a/gramps2/doc/gramps-manual/C/figures/gotobookmark.png b/gramps2/doc/gramps-manual/C/figures/gotobookmark.png new file mode 100644 index 0000000000000000000000000000000000000000..f6c724d93df1e876dd1c057a01afcfc7b841be49 GIT binary patch literal 16647 zcmYj&2Q*v%AGWOxhqg8~N>!KL(uz^EwAG?j)K;^0)kq>FrL7LTT8auSMeSK7iBNm* zO=8b-2@xWZH-7K`J?G6iIp^MQl6!By_w#w4=kt8N_k*diAxIR&#KZ);clY*VCMFhu z@s>To${2a9uvEsx#L8s);K>~p78dRkCuFS^)oML2&z@&tK?%V5KzckEBhEgb($l4! zlNk^NX|rfm#VIH#Si60a&O71i=6e4_zd_R9fws?+!{z+KXFf)_pq!lU87;hwwKgD1 zsIa&_wK@7&9BE)+pm15bI9BC8QQFA(!H*w5^zu{Nak0`)dXI_HFDLH*{ry(Y_wVHk z64Fwl^(}vcQTaKZp5bYMo0}tZzsjbk8jY;of7F$JEk)_wf0_Cz`YrJZdaN;)ufIR4-3=ZEbB7Dz@W8;pJF8XD6q!U*RB)oF?@98wU4d5e6I=ST6f2 z`1$%=u&ApokFc>a+uNHM=}E0Aj*ZMOU#V{k88LqT-r=#6n;7U}ZTY9v)Rel&yg6Kn zeeiv~+Z`$zPAV!Y(rf&610zdIJwGOhJbF9^7WkZ;oR-Ss$JUV&+L3z3!9D04rw~L> zOMZf-<0NGwztE^HfN3np<3$sv`{UcuP>Q1CIFFBys`Pu?9m~SxYd(`=Q(qEB!3QpKuAkV-+zqI z4!#WT_LjcVY=X~ zRAJnMYL;;I8CWb&f`FJAOP zALnC#({-Nm7wYU&U^1b#h>jBw1e_^lFDd zS#5WRC3bmwB{!KdtMNL4kKwon7^EYa0@%6S8RW!t8L>%4wZ9Ck7+$?Ws(5g2hWKvP~ zC-1t8zw!c2l$*O~OtosVH4MV4T(z9)ryabJ1UED}`_=X%@tVMGzcwix^0dChsO@84 zqL)HVj@d>(cqS2&b8-rjk+K%%Ki%%>wzD0v#c}t=^S5Qj`w86TyN!ijyVpFpU&C%cEcczRO%$hCRr~f1Nrj&JMPTaO z^1mDcQZAikglVK&PfW^X8 zP4%gk>TGnkRcf)@#-Eq_+lkXL3MDovZYQBSGn}vJVn^)C(6E_XME;d?53*VRx(_{> zb$Lx6g3s=o_2cmEt;QRnPMza?r-9W=gG!Kh`afhJ^L*Gj^j8k1;J~hpuB^%n@?&LV z^R2IjwN8)qpF)wfl=u2q&H~%oXPP7;Av1)@3b|16o@P3PxHN6aMShSE*tdWe&B z#EpM3+p-U?$!fVMiy={Zq2W%D-*-1;Y7Gr)y{4TLl}&%it3`CAG<=5V{Yvb;@9=E`7N{^2 z=K1nc6QrF^a+dB{GBP@ai41&CM<|g?Sra>efY}_a>&etCdoB}`jRyv~?<@oZsGT*b z+Lx@R(*rmvzILWR6O*w`Je^U4lGKhka`1cQr5J)=_cwV>pJf$j_>2vVZW!C<%gn_m zMpwu|H<84)Uo}|)-%%%b4)>vz1%(oE6$fj>r6ECEiXtj?8lvu@-`atIfg9J~h3hsy z^*jG6;JjYId4u5dA=;lb-#(8{IE^AdzpMFD^BJ`XsUGH6Ds-DQ;!%iCj*s)=#SxUI_hPywnl@vvWMX(<9%(?IC2aWtj&%-p;2m@09-viyWjjEL!U zE{~h0`o2)uz_)g}o=5XN!vor++jpD#YwY9e@->9Rx$IAcOm5Z*)osq7`kcLnl-|urNZ@4BLa5n`%ToWo>hQVmINn-{ z*G1?cl+;nz&&KvQeAPx@IhVSu1tNieI_>_e3(E-e?o2qUHz|cu+7e_MG9(|VIcT2Y zx%rUyW;geT9eAn2tlw6(U&sV~QmEK*e_qGf4!<wVctEYG>|iqJ}AC_@^>XpcJE*v5)$~Y=|frBEw|_xc76_e zE0z9a-;e$En^^=KtMLhaS$`)byraYsu7B__M+Y4+k99wWH~El;A6@C{=SP6S2mi8A zcY1k^2YFY84|C9GIdhfSF7}|Nhb6_<$yvx*WbClx*i&TTlcpW31WDutNRNKG?S_fFwkeqiI=Y=Km{Gm(8g*u{Q z#RVVDflPNpZ3R=mprybpYP)B@qRFCmkeY;!F znq9|1>sc3>UB4-_461*Wtp);^J!>$mWTjW4uOw3IRKJ>{2)a*iEeW_Rd8Z$On4tew z>XC0yBhCgbc@{XX5RSa)tOYHB0~9k*?r%lw**4zbh(D2XtxP`$VToUsVZAB~TxJLE z0b>b=_oV!E7qBmBs3Ld%Rz&@oqMoFC#Xor?8V0+8vp~PG?#Yyf*Ix+@SvqFfN@l%x zbK1^qQ)^d4&)k(Wzx&pUV6o+GNv<+LXanbj8%buJ|&u&Km3(J z>*St8_e|Z91jzC(IM@9a;<4RCT8{de=UWnSo-(){$%5{LB^EDry%u(Cn2zX=Uw_>LaFH#G-I+#gS@atY#r|F#yNezvrbYUr=+p4c zgU#yA!!P1GUK9_r0_;1<(99c%JKjd6C)Kb_-CNpi0GG*C^*+@*shl&dekl&B=P?cv z1_=gh)3w1H6QKT|1mus&t`cf*Nyvd>=+Qa>L79}Kp!!=MKHT7e(i@tEgqnD@kJ24# zwYgu1q@=0&I8@Qpp1&bq#Fd0Pc}EqawhbL{64B}l?+Cbk(_zV4 zfvrR0QM@|)Sy4LMJx=@Kafj=#U#W5w)P$|4r)7%Zv#59-Wte3#O5_QP9quTB^j)aI zF>Wv5Fz$7Q%HinBmNuo~%e}%(Wp;Mp@%y4}3Y#00s*ty_MEO8{j4@>%J z;`e8*g*SEf$V`>2c`!YhK?NKrLv(13+SZHhLj|-md9(Q)A#5An8a4QqSQFZJ7M(392@D(>Wu{J)&@vUgRKo({DFGvmU*<{~}O>*bgaU1K|%x?I4be z`$33*$y=zAm$R^CujYD3>E9Pht*SiZYD4VyBbmd=rfvKSVRSj%%faF+YTjcnn(qj$ z&exFzR8is%(XL!?xm1M4_Bwp zGPZ&{oxHT_^G=PAjL~7~(1FpA9=08#CFY z(wu2WPm!|v!j>1f->`LQwq2HFhDz`k#4rb1SZ*Vbx5LiidBsDH3Ka<@qN)vHdtX%P z%YH}zH%)GY+uz*T(V@{Y5R~wW+2^gya?~%l*fY@sF#x_0x9vIP9+LNAgwnj4iyRjsGMdDYgT1 zI5(eOu$g<2jk5{-32avJB>Ey}oyhb3t0Z&yE!0f+3}=x=M$R0ignpHOH7 zn|nOXcmMmY@F+$55wY&&=tHsKY2n!;^X|8{(C$G$D6u^k6-vYqzMY(lbGJwQ!iQ)b z?a%XSLyfj}llTSGHq`Zv*emVZl+cv^Q!H6Im^7&W@wvI4`6Wv@c0?p zuD0jI$u#VRGMgqU3V{5ePg0(HeamqUtMr$uYfgIjrX(G-tMkY zfbegO8N6w=M@m6bO@jaR*%W>F*{;g@`%TDF31T-(lJ-j(7^M%aXT2)x2VFDxUQ}Z8A`b}1@ zo=$+=qjTGQODSaj?U_i{=zlV_Ow(L2Y~B^eV!I7_sC^AO;)P6}C(qFCvoB>j}`Vx&Sfm_)+M_=Ch;~&CXC&}4bP=YSir7=K&1#bu` zQgoDqdUOZ8BWx#Ej6R%Zx27dz*w%$r^d73>+EzH<6sgQo42Om8m;RaSqDI~?Yeus*DdA{^mM!ncVEtoucrVKclj zLcdly!ZT|M4M@III(*Nn@z-h$L5S|u<^xvz3P@b1VPR!uMG?Q}B{^QV+M`}|9xE3k zcsV8FVET2G^u(y>^)1dG^s-IK^lT%d;b$`CAJS77Sz#MS8rlpPtpfri;fnlLcqy(h z_w549Z}KnlJ=fpr(hrxqGsbJ%hmTTTuuES03Z+<3=&_M+knnk#o(hR84*Jt z|GjPeD^;3`+3*p|+>DbNyvY62%?kWZ~#Hk~Tab{iL&J@4^K^9SxOnV>}nB_YG*?lCalz zU&+gc7lmC!S1W2({i|~&Ymq*C7~b2ATur3H_&t=j5(u@}OZ2X$)1$?sb?sheN-@D? z+MS*fgOhdd1?xELUMz4SX$}g1fCwAGezZRj+Hz=q2*w!lPuIyE+g116b6Egn(5 z)9ao3a5MxT^ey?Cj;yipU^1()al6)q5~&RG6yB)K#^o6?*;&3CMjp;t z*~;tO=1**vpPc*yeU3D!V5)v%ZXvg|3*z5RS$8>yee%u!^1CbdUZxtVh~K&ZsDLX& z2Kz9%^AVndr!E0knO3j7>e4pfdc%GlH&;kJ68Q+(dJ~lq^zuXQpKJFz;C`J)`iHy) zkaTD(>-dGfBI8U#lL$NLIX-3NyS-~s83x9mT#(J>HY#^EAMRMOtw%@ZyZ!O~w z-zwH9w5A`4Y`$C$ebM~TvmWj@e>Bt5aQG#kZ$GM(>xrDw^SNj)$_@Nas;m0ve|iCX zXs#z{P!>VgJ(U}Oz%hKovu$ip(;1(>mT?-EtAH4pV;`WV`uFiE==vQrj&|#4A{!gk z?~%ZHyJ)MR2p0C|H}Ei;!m`#;q^0DNtLy2zibWk7YuSqDdQ&5letz0GabVc@S(<$@ zVtG}h{YllTNYRmq2ZDSJy8KcxNGshMM@D_s37noiI5-JxWyNh~eDZaOp`t9)>ur1Q zg7}{=LKO9_7PgDE*W!^rZYDBB1uru@t;FqSmppe2WjFd6o%>Y+Y8K``V)@hEVvk3B zLPrNYTaaql>%A_ppoP;257TN~CEIGrDAa~(^ex$$;5u8Xz7w#&S0N@rb(dc8WaoCsJ1eW#1pJ9qf=7aBVT zVqu7Imb(7Pc?t0i>rUxW_2GK-a?w-7UK4ee_RjAHkvut=M80`<%iX6p=M@a0$U3zph$Xc?G0Wzt=XU}= zNkHDyXyI2FWjdgZSF@Z*#7YojAK4)w|0p@_j~2wU{!;OAEELe40vZs;J^H#E=r$~X zwLaHXI=~)mS$_kch+IFnj`UNZ%Dn&0u|`(=rVzi|d#w{5MdF?tsyl)#h{Ghyk03*; zZ$Fl(+M(e3tAS_LUe6Epw1DN3f!WOVL2jVMo@tUp<{jWd0doFYWIrzOD{1eK_ zxFC2C#_*(FJNUdvx(v9R*yW<+kP5klSH@W~Dp^fj07k!d@aYWWd-@nL`*v?IPVP?s zH9^+3#W0k`_P?~BBUJu-uikiIez5J`O%v=(}oE?V&^{fPzz6Lsaa+V@?VFmcE0?*L%n=@;%ZTssQGrHN{_%k>r(I~ejwj7M#7Aa z4Z|p&NmYs~@i@6VDm#0I8FK)+jZYkZ_4KEOI*yCouwih1y0gf3@+SV$-SEqH?bB>; ziw--8J)t@ww#*NLuH#1-Mf_RV0G_zyxS!UW{8^FB)ZJX3w%N}0?>cSRPf$YK{} zb@{n9gYCjG8p@66e{~=!LBukQy#II0XE`k5?#S&&URtsnF@D_TO$Rucc`ccO)Mdz$ z#F>AOUqJ(t5;wlrZ+nVCdoMQ2dyGH8dyIvfxJY>?1h0!M`3ir6x0;45#VoU8=%Mk; zZm!({GPL5_Y7;@=EeUQCeOh5m_tWWXS?U8<1Xtxa8zf6ai98tZ_IbMya2(G9WN%aP zC0>ZeMwfv00pF+(>GETKrmHgC^La3=OhbB+e30f0$RKL&I z1yAyFbgQeZruX~~wRrfUU_!?%nw&IJIN&9pC!C$M1aTgS&>tz}ND4l(*vJT{R71PP z6uQ@%n5QNi59g#VTmTU_8{mO3&W)V{R~Qg#T4mR-<2>NKJDkRTkJTg&(x-#_h>-z! z?ikVlp9z*W_!U?y6;EwBj_YbqXGbmExpn~B`M#8ng$c0{?e!6@lXti&1hM6?>>W&U zo%q{jM_Sv+nuyR^T2Rdgc^BE<21-I3c!_*JZdDmKxsYD6%vtW`(%>n7e55^SE7dm! zVx@&kWo$9BT?GHDpz*a_fAg%=qSmi}hFV?v-}7&LB`D+6zq`+IW?q(NN}E5>(5R0# zFLh&CBI_N99}XDp;a+1_K#gG2AoSaa8|+oa@dSVU7tP-UdtaVgfMD(gt{y?!7Sl&m zr2*5seQXEfuct3o;H~!fuZ_ztkiX;ZUHkm!%E_xApo<{Wn{{aKA=)_J!_yB>;+L6k(-b~$Iv={<`r!^@ z^26&4u0EXR-GSGGEB<~ZY!Fe-t1s86Yp1q(wn4yOs=Xv&J{Nmn*K+98beOrzwM0(c zIn-PtTf;J9h;UCqFH;;V3CE7bsB3radgmI|*_kJz#zVB0ZRk^}dEm8nA8)T491|MG-+KR>)@TOldzZC=4BsC^9td|F$!^%qWuGmi~0 zY`U0L$c$%-kwsnb)G(&AbBy6frb2oXM-O_S)u~5!<%nRr4V$Z~y(^sRL^N>|^a^8? z_F=qI8dyc|Rakd`!`QvDDCsrb3Awd=Qc&aVo({%krgop7*COME)ONw{=e*Q#@Q>)) zYvVpyn;FbiN_X(%#mR+-9d`}#O>ri%gR{5kU?vEXi>dcIem-JdPh{^8vSpX+-pd(j zpR=wy35giF+Ba(%%(Y!ur8Cgsz22)IF!Kk)1N{6;(}~E8<&5ZnPmo+S=g)*zD}29> z*-7AI+Rvp0h$-ojTs71QUX(zl&JS<@95!lKTq1iyEvK&vsK9zPauV@B_+g_n(*iVY zZVcR0F94d`TM<4ddufQo8QcJS9(->}PK#=@>GeWC!i&q({zP8)>2-@H!Owi8#y;9SDi8_D`GXi8R22>kdSXXI+15%>x$q}Z z>^kj=xBXZlRdCHm8g^8vG#Q&?t?$Rn5C-WL!L zf#yd)Ols`|>kD1jIuAX+AaYlu%ierlUbv&j+#CQ>R!Zz#YKvR5H;)YrVkQcX?`qoI zF_d6C1rY%5)3oopI*AH?=w3)K#jqx>kEh(|23Kb;Lf_qwt5n8uVM^>lU;--%l}s?Z z?}`bic{8DZi{oaW3^AnTAwE&oRgixyZ1fsV26(tcen2z+qbdKMf5yxF-oQDBu6(LB0m{UMZlQJ|U-wzNOAc!@hq_5^>Hq~Uh-|9gBKwglz zc7ywYAv-5b4s$C?@LB~j=b08O6mxkHU|dH6p=>4R*?g^k4tG{2!f@&#dOiKd#@lnt zVV6vB&W_xPh}Qa*XoQ27@1x`01jg=*bh7LaAe=`?q4r2_78qaETQc&!_&j>uI5}!9 z#;GEan`!SMlht9VoJuKQ%V+Y>ixunp?zgaS2cBtgv%1JENN=*^+rNolTxMJS0H(aT zuKGmNA?BGGjuo5E;ck&t5d|6qU$?HUdiAOv?3@Qtb4Z~OuEQj+@tUf-2A?|>%(;NQ z3K&^av_|ed{>#K99(TMJ;12(ak=BzOvdgs@pPkhajs{a;Yfp6-=M`$xB*-1Sv9l|| zsj-&waToqA=4a9+e{e^bQ&;tEN;*C=)O7_px@b3{`LTfMm3F+7?U1m+fn7CSoCsTf z2;4JfbA-xCeQjGbc$o}U-@+|-%Cs$tKj8%FrPk-&#w`&Lxwuqb6mLfITreqDzZS%f5s!^GWLigku&V(HC~X z90ry|FomFOigQM^-lD~ZD|1t@@9E+$yl()D;-y=?6kpGAy03=S6!o%6%)V*pohmn` zhl=i}@xaIiQgH#9Y(1-c{g=p9Q&qt6FtHx$ed^S&CO$?9cqY}pNAH04^!gFx%w+Q* zU;K}Ek&u?CmY1jGce`5q_MCfMxE!yCkL}?Cns*QQdWP7U$E7Tq(b*TyR__t7hy^BB zBjE|4tsgd*RTExO?Qle z)HGf??BMvJ1S!X;zYMc!J|ahdW3@t*T{Xd^LK=cx(eW5NI=J1=r1fI4vyxZR$Yg;L>I9jI#{_j4%Cjl2>Pt{U{!c{Rm7>A&U3)~`Euk1sO4_?qV0HQ zC-_nDq=OXf-M6y17yt)=g-+pD!5PS;ZG2I&eS z^7Gu}En>0yJMXBQze+Vjl>-p?GYJOh*eiMTo(vu~=?ZLhrggS=nzcVx^+Vj}vaehV zV@uOx-!jm*6}Ehb={!^kMP>dn)#toGuq}h)WUns0tbN~g!}>XMOxMBfBKM1uKUp!7 z6>CUNSXw_t{5CJlW;Y5n${;?c=gPfZIo@CP|ArV$e#>ZK*l6etVsu$+XCOOx$9{@b+y-QSZN{kJqf&Nu zpqz40++;D`?wK#31jrKM_o85~tp&|p2ibEnD<6sQn{@c)-ly)6++Dc;C^&2HzD<`% zR!#R2#5n-5Xyoz*Qb@H|h4g-C#_$pe?oWp=ooMVvQzK8$jCi!q4grk(mZ}$R zK3Ge5q8rpdy>WpcNCVnG8j0O{Ce~Wo0Kanti608jt^PTq=TAGFsHKa421)(;!ox;- zfTc`#(G~tQ-smiN10j>5wCLb-_l3?UjNIp}KkHC{5K>Fv04jAK1P`jj*P_#xrz7a; z9-`lw&*q88P^P~@R)uiS&-r}NtJ*{+PhDbzmB`^Nmynh#WQ+Bbj$QD%kMrgWSZ12< z5^@H8LSX&q^@e9nvMAF?ExjP}~W%`g3Zo`6tfS4D_9|M?4-KY~!z zT3);+JFg&Hg@6Aq{1{@hw&ig8cxZ`g-$OSRf+dfFavXGvRmWz-K;93y#|_B`r0KHB z&To*YBS;^Mbd~i|4BiUwDu1h2VhhV_kths3qvmBY>MbT=UheMO4m9`KGW6Z5slIp|aEy6XVnDrPWiU%gd=UL_)rK-YpiAo*Wn(1HvxUUKpmt_*VX@EEYyNJYc@-NvT{DQBZ^?QU%J0N_& z^Ojh7x@r}Fya+r#uTUe(hd1otYkOZtchwzXF zd=d@0?d$K;nq2Bi2cPbEet$}pdm_^eB;jBElfX5C6vCz3P{h4Q=?aR)?bmFKVcs6s zpuWeVhCH~6mPF0}QCk-6w!^mHKOJKphra&<1Ucvpt(g6Hh7uS4x(j40Uefoj+4v(ETGV8l}fk`zPO?a^PBqN@RqLCelpvO?CWVmBUj#*z0?eT zba$Y7_ccF>yH=sy1awm z-OhKV!$Yc+vq&0Ua2XQeVXWo%{ei_bi1@LYTDF0V>z*x4M8iXJW+@B`8%OP*F-qQ@ zWq`>*zb?ezdVp^4>bKixSiOcrcd(2g0giL;;fz>w7CycDB$F=g+y*tIgE<&hn|M|h zChJ&<`Hs_j_`g1hz5bx}Yicg4Vq?kN{Ed(&Wjz{HrQk%OV58}Fh*Y*n#@%dy4-3rd zBDKyrOD}&M8C^s!KOCCsvTm$4XCxulk$LPPcH-k!S1u?g6al}oA)wOvx@pEt5vbZH z$i_zPeYsN2;MS@Ji&mQP%VxdQcbRReZYhM5&$kt(|6T=5{TikwsDM<0PT%i2lm!hC zISTe6!-Em;=JJ&rKbQ2Pr}SGSu{Gb_y$=Z@gU}?3A15G41-^Nn`$d1yN<*2SuET&I zQ=f#O{$0KT-1~8A@6&pP?Fhh5176Mt+Q6z88O*L6`e_}ZUKnq80p~7m)lrvPDVT5# zim{HOV>>a)+ql6BrqxF#Nz)h&yslAio3EDni_>;PfGic*KBuh|>oHKwLi;j|9RlnyOhQ~jI!IK!k{@!-7l*{e6KDkW$@`iU3FM^jI4@8V=O_1}Og?n&%7;x%Y;+&111-|_sKBAKlaV2PG^_a_fA3y0$wyv}{Qov6V_}+w;$5^J_$+W%4_$&iS zjCkDeocw8pkn+ddMKrEdzBpU>3kFIC86YvFMQkP4;NndyP%8+F1w+zuxH~`&V4*bj_Arc~=}} zvLB#e5~JT)ERzG8zQ{Gd1h*rF@9qb<#9W)BYM1a-Dg6pCC`-QaHs>z7Wo8NaIU_M2@j9gR76-{^%@bh%+WJal$((bnBkNkKDn4(3V z;mV4@a!;a}X|aD~HXE}E%4h$IXWgg8_IN};BmQ>-QXvd}5?>TAacCBn#`}ySdyBjj zMx4b)3KCt-iQ^jbQXVg*lYE#tM~t(k;I^oengP1N5VChUJq!tjB`2|PMoMg&We*P) z?UuV5L93Y%&K!F8P6x){MKEmdoBV!-7bKdSHz zWRGF%-U|BVc)+3_3lAa@-hP@Tb|mD_EyWUj8TtXPb0WtjOBKVFrc}X?snq-tfUzl| z^GIFU1Vtc{1?L0paAO7Zo|$tVY&S&$Nr7VK9~tOWS@^4PN-ZyTJMTMa-rj=-2`+dM z5~3rRG9_c&TiJOzgKo6qm1yf1O9Je8&yOQ zGx26jkUPU2D{}v2Y*qIM2HuUXq5$5&yKm&KwN>-Vv!s(<7hEjyf2Po^7-t%JiTq>l zw|u2nc-ayB3e<4we|4kn?tg_Nnw23a7_K5DdXpH-z_RyHhKj|X?DVU8=hb|5>HdG^ zV!Mkf)5dkvDN2E@(KcM`i}MOQgPRh zptBQg4Df>MM%8FG-qng;gxn5U-8T{#D-hc+0_BV6NT^qUXukiD?$JVpL714EgkpO7 zSDP-z6c-iV3;Xl}#9DFF+*0Y)1OE1;N?-hy9o&w#fBaKvn_P#h6vBs^W=$O&U#V== zqP=VTUb4f2lz`?ZS#}5s%}vlmo`0p}3C3)WVK&`Zs-g z%BdKJ@l1sLLM$gGP3CK0ELZRhmhYwl(dO1};(pb~LRr1~YXA)}Wl+H+ob5rJm@9s{ z*s)Kw5sjz&j-^D)xfX4>8Z>&@z6rVDcONHK%umD%Oc^1P^rfDxWav)W-7ONgeor}8 zE9B9 zgtLU6{~F2y`EstLIWQlq&qb|YL6!t4Ufy>DTQpQ1-QC5Zxwz)=nFzpBMoM0v@3@js z!80n{a7O#8b>$-C91N1RAE4v=^*ccy+1t>$UYov#|BXDH;=*Jpo0x8}R;3%?Iac9j zxNDYOn2m9}LZtoQlNt6jRwuDxKVJRgc;=A`aHvGuw6A3({xBBBr!@RNU24r)zQ}JN zOR?*vqaJl(z?ez@zXf)I-UvwVQ!qD$v92c-Q$4n=FxP&QVsv9R<>t1}d1X;);Nc1+ z06+PU$wJ)5`o-TXb(hX#3c9L=hu6p~7=0la=M`gU--v1(T%Te$`rnZ^Fgv7P#t{a+ zSYMT7D9A5F&=?0HdyDRJI5}3{!lli{I@AmMi~vgn`aXMfztjIDn)3-pl(fXJL%6xs zNpKwy!*Y_0v%Jn*cVEWrt~i{zV@<@7g$5LF@n9Uz_-t7~GtTB_-KV3Wy-UH{#|6|R zci_g8x~sn&FWtf$t&(LE$oHv$9{YZhk9q-#@Uv>z{Ek@yB#jdGZE?DsZs#N7Ck|Mw z|zZTXmEMO~9A`aHr&(zq-JzRnO{Kn$61r4%*J&gzL!j4)>s;nxH5| zukq6mR%IpYOu|F|RzG|0R+xjGG_Dg@rmQrP&nSa58V=WfmhNbo0%|*qjB!6sn@*@h zc_HJoi9&`M;tMkexo76F##PNlt&qb*MsMIQOUGeGm01rycoYgad_fq}(F}?8j4P>M zeLN@i!RpA&UTTkkbMZIwPg@$pM`0AEN%l1$2^UiMBBr8hj@hf30dkCl1CVm((JtCM z%$XX|b6(A?OZ#VObs?ITxQa9p8X!vpZV!lNing_fT?d~3s4QSyjC~?q>*kXg#kI}_ zaW8i-V%_JGX`>?}Gq9izCq3elpY)c`gI(4ag23-oK+>+6C>ZvMIU><~;;XmvB) zI3B`vTnhB~3Y=l4sJqvy*yk}t zKVF{w`pT`TogMUbI$QzxhC1!WKBEnE?-NvEa?fK6^>FL^;Z-n8q|Kjhc)$@^6L05R z$LRSlU}1Fd^L<7&X7t}__n4N~MOY$G{uPiMe>#}Ztgr&fA7=*7m}3Y@0A=_+``dJ*|qNH%N|ZA+^N$<7(y#P4yT(29B+X=$>WBF3vdLfoQ6N;6P*BY*>2^U3~0J6^M==(3rahFl2W9(WQK!wH=V;3jPBRu(=FLJAx!Y8n%8G5Pk}cIDQ@i{<-!1;=Ow5uz1NXWIVR>n73Hlt_D@2 z`sc+-@GlIk;4OUcy1WxI(Snv|cuJ#Zr2v5?NWl?g)yJC%dlyXP`3d7(ef^!$*Y{b6 zOj7`5>`;2g2{`TYWOafBNA=y_&m^3g;Y}%|)NQ;@qHAdD5!n_G{9eeo6gIBH;TUi; zn{b+(Z^h|dtO0fK9MX9`8F%W5^mQVF``F}4I(8Nrh2aj^U=)fIJ0AK**kBYN&I5+I zeJ#!wrBAQk8vGD{@Ih)^y2&CAQ=515Zd}Ra3$;u^1{DU!Ka3sxBLtO$pSif7651~N zEbpyq@n!tD$*-k~^gh7th@71VZpxKm$Jbn5-kb~%zhvUjbJ8TkLBLFUuw`l{db-)o zL#xV^zyr(1OK`oHV^PJNy|s^s)S#XFEaeNo^lnx z7}Daej;K0RSRrN>)P2m*_1K3m4wvMxW{MYEmslL+7cpp<)oSoAi-)Ey+J&CD=a zeF-2Ng+_D@I}DxVoxA!=-!vRjcePF%XgUMj3WNfw2=#5;>uj-ibJ!r+Z;R@(msZ|z z!dkyjSkJe{n+gEsR)HVLo>btYB7N%2w)}vqED3Jm^=hugm_+1p!&H|cTVmUrV zb{E5V7~$~ux~{)mXg+d*rrNIPVC)d=;)&(eOi3Eay&m@4jDo!jTr&WHoy;=&^SdRs zd*qSR41>1`YkzDO(Nu-)y4|D!?MX(2uROoY=rhOm2jMv(SmWbXu=0cd$dZB8GpJt# z5)H`Dk_hJUA;fgZQiHKJC=`Rv-L+N*6ytFqNtvOAfAgMktQyWUV6Q0u#BG zM1HMji;J`|yoh-9&nc`Zm)gDr2ncUPA+VRRMj(V>eo=_Br}XrE7>IbG|3y8k%b&hP zeYOQgL6}Md7%sc(*it8A;DR51beae6GlZ0Tr;=t}Z;n3qQ3H>g- z|LqA9w-TcX{#971_R}m3dH(b<G;GnqjJwR{y_y|0dok;XF&_bf8zc=Icv zU{%^PQwAH0QnW2fDzxZ6z=i{H*7fznp6#B)=>)NTmi6<(f2_X}PIKGJcL66jiUpS- z$6F7VAu}1xystZXZJ0Ssjkvb`ws`Y&2ddPqJ6oX3LVW!1Qk&H#lola(ZMd5_pK}2$ zT$+Ft9Q>yjT>bSvp3A55SqE_POg|q}IQgRUo5rlE1a2lWG%iWnaIFGEhW7ISc>^`Zpo!Ni4M?^`K-72dJl)+s=2@sw)CK6VIKd(aG3cpAppMU$> zDZ?#k%3D4S$ms6}TEu)#>@a$aEzTiqfAqsRN$>%kMzWP@y5M6xX=H%mlovCWW$pF~ z`(4w-StHoRSXeDPn}-?A^5T)wZH8T9$+ZLiEpwj8{SV6epPqMG44<& z^2>>;*J&s5y#PE6?}uM^CbUjVHX87NV30@kd^$MAZ`@Fw8ub}*UJs?Kri&^7+z-VA z)zQWZ!CF@|8GG6iYbl9s;FeQch@I^)E`9(OL>QRa(26&gIL!)!ydg8~vS2zdvq=NO zKC`qNy7N6aHh-#^viK4?kqhoxR$4mOfsS=_tW*+= oHBo$j>yW;6k%RGwF`X3$oUk9M5;tKyxy^L%j`8hcJ;#Xu1G{s6Bme*a literal 0 HcmV?d00001 diff --git a/gramps2/doc/gramps-manual/C/figures/localmedia.png b/gramps2/doc/gramps-manual/C/figures/localmedia.png new file mode 100644 index 0000000000000000000000000000000000000000..b7a6f54cde7a5f9e62a9adf029a4eb43db8e9fd7 GIT binary patch literal 12342 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 B8^{0v literal 0 HcmV?d00001 diff --git a/gramps2/doc/gramps-manual/C/figures/mediaview.png b/gramps2/doc/gramps-manual/C/figures/mediaview.png new file mode 100644 index 0000000000000000000000000000000000000000..3080d73fb1f2b488e2b1c9123f209e0f1d9ea3b9 GIT binary patch literal 18852 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 literal 0 HcmV?d00001 diff --git a/gramps2/doc/gramps-manual/C/figures/opendb.png b/gramps2/doc/gramps-manual/C/figures/opendb.png new file mode 100644 index 0000000000000000000000000000000000000000..839c6181597b592e8c629463ee644648f6001ad5 GIT binary patch literal 5472 zcma)A2{=^mzdur9^hAn6_zl01EM;FZ)(9!4q-4v^q)C?1j4h#*t%MAQWEtB`mZ8Cr zRMKQ0WSb$oF?J)ya>xJnzxVmyd!Kvn_c`ah=lwqKdC%v0-_Q5+{hkwRb=4Fk0s;U4 zWOmsY3IH6_?E4As1MI6p97>qo@I1JD+aCaU1@=#l?vb!JMC)xRegL-^LNE)HLFk8j<-?eEDeX z=NBkMYxKY>UWGWJn5LPL8E${&%FV^ZRYk;o5AeaPQ^Ho~aS1PHsGq!y=Q%zLMoPYa zc0lFFm432+_(daMUs7P;HN>^X5WVmuba5f37+Jg7+jGsvmX`y-N}&0FMLX477y({K z#?zG0TT!YqD&CA*T!1O+4%?)YXJ_`!3l4|-`dSwD#2QNqz2E_>T9%|4iHeCi5Fff5 z?kqPwPJC3(p?bt28-xBJIgwAVbEz2_PHlA?92(MLo3^%c`V|-qCYSQOWgK15Ywg3w z$Hxoyfa>6ccXr{1T79mrhzuiJxNlWC1tDYle za1eb(n$iB+{kE!w>x-80cDm#7! zIiZ}L|N1433EP_O>atdIXd?_4`F8f3xDM3Lw%?FTG0)F?4K)|Z8_0v6?NOC**lQVb z(iOj!r{t(U?)lZGR3^DKKs(DkpVC%!`4_d7pBc;(c)IEK2+IEbN`%_Rcuj#+Hnj-CCzdyUP0Su-m?}b3SgBTbV3- z9i7vZPww(o2P}K$G56$Bwp!njcm0=)EI-Q>OhmufyfV}q8ym}HGG}MKo6pOW9|oZ; zn_r_lqmm50pd)T4%f#~d(I2W~P86pefC(FVy*Q}=S>EJ9wS9RZ6dv@;`1g8OI;Hx` z)}Ytv6_LfsaC~k^h@J{WQuQrzKxIJ5_<_&#dcDwjJ-4JY_-QrExG>FHmrW3AYUb%X6&C*) z0&$ZM)U0+(m;xUmz|#0#R;Sw9!{|7Nx_NKHx1pNAAXI3fg9-yw^!|FdwwA_(gWY>P zqmHlLAKIkLr$^06_tADM6R_*Pqnx)MlGvqVm`ktaPrW-+Zs>$9cYRRMtb08BkjV!I zA#5kSvD%Z0>L881)vlxj4n9k2PH3n$a1sT%Ad%WB<|%z}f=uh7sorkmu;%lXh&H)v?rSHWQet&ILa z1w*GH!6u|*n%%b#>E+2}A^RBCqR8(2zYD5I�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 VtT=y>``ZhTnX%<1ywR;E{|~<+>Wu&Z literal 0 HcmV?d00001 diff --git a/gramps2/doc/gramps-manual/C/figures/pedegreeview.png b/gramps2/doc/gramps-manual/C/figures/pedegreeview.png new file mode 100644 index 0000000000000000000000000000000000000000..f29b00efba57841b19a03b719109d9a36fc932a6 GIT binary patch literal 9883 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 WuV`NA3-@ap9s?Z{?IO(wFa8&ZlP2r{ literal 0 HcmV?d00001 diff --git a/gramps2/doc/gramps-manual/C/figures/peoplelist.png b/gramps2/doc/gramps-manual/C/figures/peoplelist.png new file mode 100644 index 0000000000000000000000000000000000000000..df634a55611b12efcb45bfe91f1a1b81a96c0943 GIT binary patch literal 16110 zcmYkj2{=@3_&+}KmcqpwA=%oHJqcwjZL&soS&L9AvScuuC6qN;3K=TdmqL_bCS+g6 zF6+VA#W5JOnB_n3_xHX2zu#QfIdkUBdFEN}=eh6C=kwh2`mTi$?JTR4V~iEKcrI|V?dKRt7v_;^pKPH+xvmmrmqKYXSR6E-WcjkA0ENZF3cL-HpX~( z#KZ&%h)SC7oDIR0UOMD`_wK%#Hayw@_AlH(>9qs7HTKQDaR^cxFg@Q=*>UHB?CD*49`;a+KfXjdB>g zt}tpin_z8iosUU2bMlETt@ev|`ilLQS>3QuUC4h!AaK&O`*ryw&Nsd?et&=Rh}@w| zIv$I-O1*?EML$(PKfl7lFB%tfQym;kJ-jXKqqh3%rl#AkYJF>|i!b!`y?(P@lJ{bcEMw&T5j2Ls`fW5 zLbUX~GfQJV!ci49AG^D|)wHy;-Q3RFE86?r$o$lD+kRF+KmgAVL9y1>yvHO@oxL4vVc}@55g$9Is^wN$S$UDJ_;+@vp{{h` zdvDa+@csSShK2?l4ySY)Sz6c-4u5vTqI+j&aw?;MMyE@N8ZNeU>+3h53M);mQHqLD zQ%$7!_;^2G>;C?R=R z6WHF@nwXZQZ(X;u+EPz(w%WA{ zlEA>XnP=rJ7MiBA--ch>GPm&y85|jGX=yo?B}g2rS5=MeFPy!O`LejauCJ4woLt!P zrn-_gNt=pd}D`zj9_bV+ey>uygY;4TK!&6lH#)(6xre~((QKbSxa@m=0t81$J z`}^xEqe48+Uea-~c8cw;tXx)He8*W5$DSMC3xYuSg#J6YhGtL<&P|?RlRJhyL)`z0 zD2g8LI;#MIoQ9a(x^5dfxryYfm2wE11!Ikl9@~ljs+!x2y(cwT3hkwPm@W05n^xAs9X=|H8`9ajAfB>$PVgmM z2a5f1LStAONQpml)miKPLKnOk1i2$5&Hv8Aejk(^V%euiUezGsHuQJv%98u

=Un z2}K1i8!3j_=!OZBndjX9iFo>Ve**RU#4S4PLTK88JgIk@Psn?xeq9(v5vzhWGQXAC zTWnzjs4PUHgG|z&T&V02CB=mYlI5sN^bE!chm{?>Dy4Vz(xO%eWxMI2GX|`#VSoE?ZX}x@xL~rg!)Mu2<}?>Kfx?#n z|MUrPbYz!>zi(Oq#&MmNNWdbgw$oC9lSOhA#W2&s^hZ&~q3DIJS2S#N%qP3UGAT2I zY--b--9>anTWcO|+CV>K^kbI>pI!Y-AD z$bf@=iAwZ+W_OpyaFcQ6&ELMbMoHB!%XnLV(W%=MA0_8=mh2zsJL9eDVNm_ojl8iAgHycZP`t(Qg#8f}ZWBleuN;}P%t!o>~t8Bl!Eo-pUslQ`s zZ*^I=pP0PT6 zf6rU(DQ9bheYjJ2+pI6pjL^Q3RCMQdLOXSG(AA8BddeKUS%WR&W+(nVtdS+u0h&SH zB)OWI#R<$TYxw*54Y5fwp1m>Xp!wSQy;>g6%xY=)cNh z2$NeqF9pp=hgvnaOz%v|A{Y8mtyXHqPSYDDSnwgGYSwWp&N3SR+!V4^ao^e?`_c#{ zh@dCUoI8fOWiVewA$0s4WIvkx*=o|f7!;@RTsWod-zsWoqwLzuLtgg&Pcwk}M?yMu z?%TNQixt-CQr*1Gjqv=+fKBiz_zL2p*>%*mWTKHRtwmq>(Onuqkky z0C9~6zJ$s(h(5n|?`lqH;oMI@w}sh$8AHMGcTUlPsErrm%H{=5m$70mX`NRHhC`)i zw3jvx_7Chx{1Zb-!MktUQqM1yAExHH|ckNxO$zr~JR zuZP|P?|fw>fHM;(yeY0&Z_W6J#?6QG`Az@*y5yS0<1^R)C}^PM$wn76b_RA_#hS~g zeqUv+DW|D!-VB_Q0cJ3fY5cI(O(pK-5>NAy?DA!((eVWl`(5=k@wOU8C5)AFs>tu2 z1Mg~CYgK-n!<#&H6YW2{dRf;*5teARqu&NkQ))h^(IV}1JsYGh&A$DBJp^=!;5T+O z1Qsa4?@RTHb#OjByoL*u$Qr8NjJI{%;w#|E3A|Vjf#37tLb5&T)C~emDDW)8_g13M zZ!7;)hWYRE!CejRZ+$yoPtmf%+rj9}1E1nEQ%5E`gz!Bie(Kh9>>PY^0j7XCLpFKg zrPcRTE{*s|y^P$@8FF2*PHL22D~HnxVZ7iR&yfaUe7!z~D<9nE=5|f`&-_yH|2qj* zZVX65YX9|M2tZs*)Iv^@+ad%;#msiVD^pyxBAs~dPJA?Y?O+!ydV}U^NJ9%!lS7ZJ zrsL`rF;_1+h@_+Sm8^O0NI#<%$VuLPpvL`#QwtV_Wg6hbKPdYt)@?I*Rdeh$#Na&cNaRXnNT9 zThc(Q#F^|}W*otcs8WYucR>zA*XmT9aStkX@d3?;LDq5&+ixmm(r?q;pS9J&F22Hi z^|y-f@q6w5@zPi@6(@uKdo>}n(3tWsb8pnLs@na{N!~}e3xH)DSJz`GF3)1FY4Ra~ z5~tSCd7v_6BJ8|1RlStGJp|@y&-^uGEsza%eBi7V2?`>7+1Wk^OX~G%YMx19kH+#I zUNb^dm)W5EH-w~&;mebAL=8?kB7XYq$m_gikBe8U(n zXc>vK40i#r1fND;z%uCL!P*Tv>kY-T#5SfujA*8FJQAqG@XL_yM$Nw|Ix%N zLtor{x9N;KsM2CA*!a^QeOzS=&LV8CQF5MAS+Bnz)cd(OU$%Jh5wzi(y=kFKENh^^ zRu`Lpg?UzzZutvN^#5t@alcs?>`oe815|%e4s?+e8g56){odnN82jeVj5DrBcs3S9 zNd^sCy4k)0p=y)Zg}OfHy1y>ZVPi>8g*+}!$OxE&mI#})o}(zD*(Nf|}Speso~rJ;IM>RajWf*wwcp;~(^! zb|VhYthm#2pUE(&6Kr}k`oI~lqd_!dcJR_zRH8eJ6-!1rk{VL82L3_Q& z6qLIeJjKOh+GuwdMXOB`yZuK;t0jnz`yfN$zk%c7YD~G=PU;=C?0AX}A5{#Bq&%aJ z$07{ssHU49LCQA3y$j$n30ps)C#DwrL9H0%Rx2G`_6go3C{KJT(<(rVbaDZhkOR=>({LQYa$_*l~vYqPhv9u+**Y0sPZt z2lw!1SMR1NcgH{V&{sZV=z|m-V@XYH4sJ}z>DTd80&53X4q}v$N%-eVNhx3y&Zk2| ze_jvgzEhI+&T$QXdRu==4NV`da`sZ))(4ovFzJ<)iCw)BSl-r|d6hk;hfD&dc8WA} z88yD+6PTdwI772xUkg51?&`})$$t6*HC1Gwr?D$+puStWo99~NJyquIwpU-JJwkzp zfbT#22w=KuSFtm|CUOP9s*?rQxNrIBF?i6SX=-5I%HmoKpB@e!N{n;(u-o-*$8~`6 zH&o8tMBuY{j&SEYc&a)3_v>9ZBoXe=1jF(IDN;(ElsU$_cgkP(oCd0fMk~BiglqtW znXU*@;UQX9`EHpwXqXNp?a{p4V0R~>o032d8P2O$S zpb%rQWBMtK_~p-uK6f+Lo6&60Ce{6EP{eLI6}PtVPfq{TB9&3<2JrY#{R|uKzr!QY zxEo=3i7_z>hOv7wyAJ5>3C$G)>`F&{1+8k?vmHP#XGv5c*KbNxhVF`t(W)Zl|K>UE zUqE|2`1C5TTksL$1>iK!Fh70$p?LQ#C}pwKF1iRNC|#@5WIB71Y|kFQnM!3g{^mzu z4g-0!>5LCw$wu@d2buofp;a;xqxEd(4{Gk0XpUR90h^(Q*o98%R#$})4jk}obk$X_ z*=9DFHS?|_bp0ksO;h?yG_~YfcqJ|BNvPM(%||16>n_c3^sf!k4Y!(8cLg5povHQlAQK0o*HKHEO=b<$V13+wOsJ;O-rv9^3t48q52V9dVHDb|t1 z8!lEB9@o^KB0ipFC*DI9W)4WD&@TH|;BAZN`ff|3ij7h2@(if_Bh+l*6B`PK z%t~N`0JGi_s9vrldTzM$6!*$JexLXF1i%x$@=xQ|&y~b6$_L)VlU})buki*&@fTOz zxA43DOu>QOXgLyc+WcET;c^1l_XM;Z%&;(&sS4Yx>S6FxllSXPfGH6|e$(8+E1I~Z zU~Rn~y)wlo-dmVY#b_G6K}^lE`1ql)H1qt7M?9@7e*ytFG9GGoR-0_vc{^Fi$IQto z?*<=KJAQhK9B-A3KTuISyozU6^YoemA&xgEus;VTi zdsJ-UhmyhKuHFQHYFCtkbf-$66AxQc?b+oVa5^sPr4J2Wz9nrPeA4T0Q8kPl7ki|X zpphkW%C5c;di4Nm<#)mE6PO(udnCH~8t`2%v3>zAxzeTQQKx63%=7a`uD~4=a?S$Q z&uqxgTgwHoe=UBM1kF-TYlb~OYzWs*X>h`gD;lINV}BWOx5=s+cS`NurSD#6c=z4* z+%b~#mpU(Sb~IgkQs`Lfk)_MJW`?nQpZ->zTfjZm$eeGrZVM=29KS4f;%(i07%9=C zC*a~fj& z@=S1g`I8@Bx*8|lbv?84&Q!UYUEDhilJ6yhE@IFwBT*mz=AE~~`@D~P6y7C&lYRofElu2&@=)cS z4B_Nbur)!+C1fm`X&fDAa+66tb~=a@XMi2XI_f_T0eU0*DUfiAMl!#v@&5OcoJ z$n8F#gsDV`of;S{TJimT&I;hl-~4w^3gX0T$Kssf4cAgR#rqdI_dRJaNCb`>!ud8I zgv^~3uuc`&lR7pNoOkHUHgZw(4L7nw>_&rSq|*+v|22+yt$==_SR42V9)>;_K*Zr- zuRwmzTQmsSYnnmGZ#NynIlCPPoq(9P_g#%W`DvJuvk>4;te)iUIjAdAL1Y1Y8~DlF z6fUHZ+%jT?3(C0L3=^IW7piH0TGIYegL=r)B2i2}<&7C>({UWQc!q;O)vPKj5`WrMklW^OSMtP&~?H*T#bcCjc3V*Hi%~ zL7wVf!pSxbQ=}2<$k!3r=#0Tb9Q+V^>C30ao>oZr0yYaLjMj*aR5dH;JK}ZcX{c^A z0}DlpZL!Ww@88T{&UnWP(gcw}+&WfeczJEZ!F%@2E89e+DT84t zT&sa+2W9Cf)64$NZU@)-<*pbw7zTQf>eo-#CssGAWF9`iW8q^Dlfn7f^D*Fb1> z48lhj2u=V8U=X?hgmZqBfjbu?PGB zWjmEI8g97(D-y{Am12(O{&8^vu+pv2%?KxocbVXePRdR5qqAA209OCg15MX^6_i>O zv}obr`YMz<>y0xa@m~j;DFWL_#Suzp2M>}5!2U}(@BNLJocn{#dp~k)!)!~T%^^;r zQ?v5nxccZT@RU*TJ?Q;T%13CmHB#PJ2p}h^M1u?A3a}H_kr4kw=wg(JHi>J~Cm76HCixv^5l;$L+QW#Z@SI@gx!{ zI*UFRTum~JD^EE>aYa2iUtS`10os)@AoBe>UnWK+6^vhBk^Os36bbp1Q1sy^Z8ULfks|p7(j1kh8mr0u>;YqkroU5h&UB0t zDd!PT&n-v7J~~t82=Ell!($#xAW^Z^hlWY(P~LS3{K||&JEzj$Am1V1RCG!^*oAR$ z8oiN+7PD4wO^LYjEuHGxxYo9fHRytm$QhigP4f3pfZi9vCm=d#qk;D#cFRULT~6Zp zg2843Zd`pf7#A*?56;1-XaXGbKzm^Sna&qS*hPSAdrd@HlxxVf3V6+EoX!|bHQZqX z3aR~M9h46?A&@I-?iAp@Rao?PjP}*g?vvV)Cmfn=b_A@3RbwnS)$rIc=&cst&wf_V zq85`y?5|lHa-EWvrE%Twy@K-5eL&>dit&z$2S@%ypZ+UtRjTnf2D^>J~Ro6#wjNWeu z9-;T!Xor2&MRbRHQ^~+B93RwRI%zTY_}Ke(Ym+~(tr3YyGU(fpd))=5HAi$M2 z@Vp%FkMdhWgjY9ZZ%-M4?gxs~nQKIJ+_oP-K zz`+14A5Jot;jG{&5GjE!Ak8I=tVo;G$W2w)UMxNj`+TR{#nvK=@9~Q@JCSpb?s((3 zCfIMlVLnM>5L3h6^^4A+#m^_D3P6`vwCDO;KV-VMSVHdQ_^|eFScTF>rgugERxASS z7&Hf_*{TPB>_T7@ju{JZk2O&X>#;pVz9$>n0V92=jrTv>+R;Wi<7H=Bkx@Oli)?tq z)@!}^We(nbM7hGmQ>Y{dhuy^sr&X@sPd`3v*8>}>O*v`BO`GNWl!bf1uuvY(eD(`A zJ8Bd)UZ{?cnPK;nauwcRH?`wB+9kBwQ6c#5+*jF9)zP3xjec7S|(HM?a6 z!9R9s;`%qVdkO!VRaF$Co)xiRVO4Fb@6XpC{MZG%tiFtVT%I!O3j~5za>7*^;FaSW zT1$6$pTS(4586b{cffq;>g-%=e+RgYkJfs|!vL>o(`KVNj+p;{tZ74^HDU+^tDp!~ zYNC3}CtnV~KC(2i-;PXlp#w>D%T$8gjjLH!v{|C?W%_QJVn^K>z&Z8J^L4cLs==4M z#?%{D9iejCs1+*aLA0esyFzkl@F~ruua!R=uzPr=12xRsQmM?+X8y!-kj4^c-Q9S) zEiU>kF)(f9<>%EuY|I3DE%XQiCXHXAo%6zJ(46gk#0qRjS9*6!$t&12I!JR6ezW(* zI3Fv4qx7wFr>)=L74=IVE0B%|@Kl}Po%#odEn`<&LG*wu7YUJ*hCoo2d*1@K8*dSg z+wIXx0N)m**t4Y&RWHFNObPDEx#O)=SIt9DB!OHyF#L{dgF5>G-GvW#~)2(z^eaY=zy$%_Zz>gt9 zQk%ZWcaN(o;@rVd@8EIl`6H7NFN;qCx$97~woA|E4oriP4z4o=KPMH4&t|^dNpGe& z{nk*8R=#8RynB5#o@$}lXw&h2&oyjHZu7Yb+B}G=YCl|ghoRrF;X0#TfVj{?30$Cz zy$37E|A}_E8m=^a!|*5&^YPt92R&j|R3*%7%ar-%Z>=pw&$H3Vo0a?Bvr)&Z6eRYT z!$e;8S6HEr1gqt2{CC9=NH+^j?<#ZPDEhAe}zgz&Wd=o^T+8GoN%aG1tI?P9(Z_ApLF9+U;iA1BtMnA>;0FQc{2Z3F2*a(=QdU>5+i+ zEX_wQOV5-wQ7cK2*@~;5GIEJf5bc2XjcR~au={NfZ=1M!+uP>^OdtIE8o=9_*O-L9 zeJT8*^Tc7_f&^LXXx|5B{LhIm30r)2<)_n%r$gm7uBB?}-kBNsff6dzUUOV`z5q6+sBKnvYFp<4dqS;+DWl;@G z`nZwxBd}(X+-hSRr`St;x7WMP#iCbUM!PCi9Nj1 z_s1M(wsTI$ye(rS8~l6#XnJFxD!B>F9A;nLn~A~I1aHESSs`1(Hx%FQ&R%#+%nIL` z$zr?W_7kwLV|UsW&b#P&&l&IluD?O^p~rR;fp&qnOQs4WQKNd82Lep6;HSykWtQC4 zcg`cT=vi~5Pi+0YPvvTUU@`)pQ$pm}NQxa#qRIu>?4I8`U3KiwwvzM37NSs!XIsVOX=J7K0`8Vbm?Jy4{{Aa?o)~Cy%ShM82^D(+r9it@##^eWVmUE7ztIQdAp@`1 zOuKm_d2KqtNyWv8VFv%wZmxBvd`673yA-aDJU26jc5u~Cldf7n!aIpIhgc9PF1WOH z&yRJ_ZTdBJHAi8~y==^P%GL4eXwb8V;JEAhWZutFFO{?-K~>dS=*B4 zX9cYucu%vatbd5kvoy6TIAPg%?-~8W!k*_N&QpGCXrFJcx>vcf_6IuqskPre42gz6 z&}d{jUYZcPD)R3*3sy3VZ8+N4U2*+Xpw2S(liUZ_0Q!fJOtQ*fWmNqhzKE$Y&FoCG ze7TB#m$Wol{q`|bWsRxQzc=%dovU6!asSY19AC6U{Kp3&Q*s3<2Ppl-RXk+FW0&t{ zx-0hxSFOOiUo178s&!^VduA*T2l0}TBjF3Bo*gctMMb{rjCX736$8m^g5jii)y2#w z0-tB~Em}Idysi;P*!_VNn+I>SmarN~F;%CpN)}a3et;{qCk5zZ{_0)V*HWf@x-c|( zF(W8ljBqiI3U7l~%3|lr+%KeG z$hwdmW~_NiS(9DaPk4W#NXF>D)^vo16#X66=H=2v_-~B#?^6M1xShnRn+>w6!M(&L zm`8Dp!&Q&`zb-0w5XOOe^xryY9j6BhtBa09uiF}XcedMlY4=n<6>ND4@68ObE8nXS z`C%}cUtR3egsV#5bP?)7D7OU1FC|1F2Dr0!%|met&RuF)np`40jy;WddLkh#{%R%7 z?^!?LnH4W}Ym9BNBk%U$f{??2Bn}(UWQLGw_Neq0vk^Dq(;VpjK-eSkuY7*`2B&e3 z4J|oHOaT-&&-j}%7Fx)aE6|$S_dca1j8}{BxLP! zG7ZRcSZ6l2eR|ddmy!>jbLusds1ns!sd;NOKuiXb7@$e0oDSAm1TO|_;v1ap6b)*A zv~`Ki>6KcOp-3Aj>-9PExWOyUslYp35p+%@h+7q4^AGe2I*~a(sc#O-Asj*+ae<>f zaoC0nU_{0TfeyAZb+N6QFD{*;+^cW{5)J?jpH(ejR68+0JU8=iA|;$4|h z;Nj8Xt*!8#sgG>r4`R2#z@PTWfOasG1+yNBTuJfjjtfeV=^UI%6oau~?X{}Lh2-)0M<*{wG4R;l1LBB zD-kkUN#8zk@ewkJn*^I-T9U~+LPVF#8^HV*ydkF=w_k?H1x?W_)13!7Tn|i5w2arRbQTYSkUvv{9w(>c)xR9~+>knC~ z|1o4-n+sCue+aS)g&MFU@vuhZGh3$kXHj%0lUXW{>e!M4L^$Nrc#q_&$-|8%Cycqv)vSmYA;|xkHui7zJPukcr&|g zyEaD?kq-|CTt{FxbTxcY?LVYL%4l1kSn$U7f<3GYpPNv~w_Cd35F9a5<|>K@kQzf< z@=U~of@IeZuOdNDPH(}=b-Y=?PdSWL=Bx2eD0S;1O1qRj`%v->sc0MyTgetkpFplMNE`*HJ;WH``d=lGgF=42-$SZK znz6Cn-38KFnmfXVUTOr{sh>P+&lV@2k#qk^pF{HiqX}sfM@Cjm=3l6FQx0=+Ak)Gz zW7g`i+jgJ4CC?SHC})5+w>h_C8}FEZ(WJQuZtT+kBt zF~ujA@4OLD=fVNQG;&Liv*f>T-Sxyr&v3XQ!g8X;xb3MSL$y@kXx?W~Pv9QC|9!5( zPz0FNz-q5x=fV(Q3>crTCVFk>KE@eweGV;L>1Sg)V7GZW@s152dncuEsM!VQ*|@HX zQtO3vFF`f4z{{5a6WaZD*rLxg_XR?sun8 zisqOYkvs8I8&I((j?fdv^R@FjPk94(uGS|GVI@lit7UEcs4TP~!3_h)#9Ph@iNhGP z64yD%?g8oG2S>%>crrn{z-d$*j{Dg%-2bD0I(nrer_?5KBqP<~3mK?cH;V6~-oV5K zA7}qve&u6Zoo%!93vm)p(W}hCxZknSZ%X(Fm!qtEg9(#`rDLdP?_@Z?>!1{Ldie#F z>K^_O?-L%z68Bz5i{)CX%)@fm@e(6E-s`7G?MV__`F|?Q1NW}>1?=>##)HUrESP*@ zX*Q|Jo(${~#l;Yo<7@q@MtNP7ZWa1X4!!TM%BBKw*}o=3z{0{&w56$Sbt<)gPv7G6 z@<_)g!Ui0;VBoo$QV`Ovp;^lIYoWA!R^Y}tr+@Y-R~1E?0B{-!s|PrrAdD2V;7f?{ z0o^?CyLi=pE)_^CONw!ULc+-vsOmd-9zk#i%6_;SKwcn;8 zGLC*n#caNo`S_w$5~pgA)@xDNSJE6hdcnZPL#tu;kpq*`5O`*vuvbRHb0;?l2G@^L z^cfoY#v5mr&K+zU9dhLiI?Fg8iU4Ci9vG82Uf?!xkScYO6Ufe?>O1kfp2{|C?A`m; zP=|Nz?_-NFHJi&`b}ubQ_q4Mz+9z*`5l43z;QH|S5ii{EQG^=ytZo)4@1<;u{X~At zZgR+EyJ}gPDLvuGb0my5yUwb>X>iH@rC43|rKO$@=O zZ;@5C3Kmr1NKV!kEFpfot&~tu*ZnNd8F+4{tm#H#9_FNV&gXGC?RF_Sb5_g#$+1?g5}9L=iaG3K)DehND@S}D5W!K(xA5I5 z7RC|mUyFM0_qthKwWspXl;q;-oD|bIY z+jeHSZNez1MJe1wtNF>yE9;aLmrK<_2vFq;2a|8`S}(P6yRO2!G5xm#h925^**5fL z2=Jf5JceCcO9Yt_TSze$>}xbDm(3F{ZV8|ptlbI~*;Hxi`Lw>Vlv8ea~d zEH$Sa)`ii}{_lZ@ah{oez;GPp?fv#9c9TA*_!=~5;bjrS?bG}tlfvgL!?HOz zEA%_q!k$Ip(uCz3_b=geal%x6Mc{xFZxT5B0tg%JeaEBv+o`$RE-fFb>`qsV4p~`T zzR^di+(u5GMG>SBT(XGn0YH^D3!BfoPKn;?dkq{E3r;IHaDPs5m`C&IG*dbuKs1hf z0JbglNF0FwK#8mh{7(%llNm$gKMgvWwsu`&HR}a?-35u;w$V)*7gsvG6W4WR5iDf$ z9VP{VO8{Z-z;0px^Iwf7Ux*A@l#{A(aCTFE|nV)Y|%jw?e^XKq~ zxPu&*)A^VGSuHb3h>vDJYEl|RDn(6*{s*ieI`&0OvhIP9gB z@wcF!5g8z}VCpCbQDQlqu~5mvUK4DhIY_CPhRk2$IICrZ+|O`D=n#WuSHpl?v9Tuz z7n@BnmHh5OI4DOsp-xNe=9-X;dJ=tH-tR6waBf3@&MtVT5y9R|zVEe{-b$`;-0jxf zgI>y|A*tj(nkMn!Jp<&$XT=UvlQte1VJDK1x+8S61B-A4Nr>x2_K_#$G~ z18aL#zuCml_Ipg2`oYI#xHoz4Qh4QwAe1W=H&=i$oT4ESNA6-lf0OYp30^SxLk`U}m+jVB4-WSG+1U>O0{t;6C#qRin+8doxHT ztmM?Yr&2|7JMcsn1##{<4PhyOxzT6BJS=-4x!wumaOm%B*Nbki#z=C$EJr4&e9rp* zP5X7jpc;4(jiaH(8-~hv5 z^Q=2P0ql50(WxEenTDLCCv3Pj)h(Vq^NXr4inAG0d2K$uMP7s-BLj!aYP)r2|ArPm zWMf~GntrFwJ?9JDL@9`B1YpPg8`pmu+!Uob|N5F&OvB!7MLYzqGmSmwutC)kTe3?& z)8lBKnL#VfCAdMdH=Ppfo&svIvEIb~C|?kp;?>xvXYmXB@((eyVt z%bx*y^Vg|-!#u7Cu>EdP$<<-|h2FN#+Og`Z>3HYT2h9Op!qlmN25ym~d}SeDlNq7y z8NNcV-4)iXnO2soid3Zvur$-!ki^dbWA}oW&_4UJW4q*;Turx4J9|m=i&V7Nug{MZ z>_qUCz8#HiJaz}Ku_3e}hw#}dCwX|Ge%cx+l#_0^&BbIUPw0uS0Pv*|bR1g`z1LN4 zS}w6OGa8z)SogM0{`YZdPi6M9mX}?Xc7jhh-!@)s0eW=_n)DkMNPu}URKMH%dlxcN zUx<**fdc)D43GosIF0$jfR~Jgo>c(fDvnyl4;>EKr~2-K=f-(r*-f>US)+UES`Ty+ zuq)UE`mb$#edg9Vzp~r6E({uQH;i9*avnKagaALXpaZD$2$0BMG5QwJUIO2;1OfV3R(a*NsA3$*~SZ(^q%fBuvcVjE3Fr# z=8AjBq@LWk8*=5a<2G`3fi)?|39lM3ICZQ2SQTvWHQf#0GSrt0z!@SIoS^dEdrNfN zbqAd?_fl-^JSFwhfGSmgnZYkYgr`aUYWVhrTfT8Q=SDLu7b|*!ljfF+E`$IjNeFp) zi_lBSJ{-=pH{-?puZCfo>H*&9d5iq`kvD&39A-OD&|vRK2dk8EIpS-`U0FajR%ZuK zh3isPJC}OUgPW*fB3&y2NAfOqF0K>1u5rtV`h9i{gUnhN>tWKKJ~B8yb~1x z8}P&K_bbjH3E?Cwc-*hg2T%WZnLHDPbEp8%8zjhl>JgN<&cEPfMyg_`{4nWms-NuX zrmAYEUB21p!Bb&Iw=hYuqDN-8P)>+F#Q7j6OIV0^*(FQxkI`eYNEz$1h#Tn(@Zyur zdv~F+!o?_r;FRS9iN~jzUBb^O)lN9R%|wbf)&FJm*cs$XGV0cmw^Y?8CCg$lASSjO zee=i#s$bo^(cRo5f!wqvh0p<=0?^d-=*Ro(xqId0*T7++fong5es}DK!0(}PVmgmp zhhVNevTw64KF-hjZckUW$rc)v*g7UM-E(SNZ-R$*j}$#7g>+cG9e-@+W)KaN=ve&6 zZR{qXNr;!J-CBhLDWz0bVg8XU@a8f3J|Cw?KWE(R^YYYR0~LZ!JY?6jJn^IAT#t+0 zTdBzRd8t97E<-oEU^PwU*IBOJ1pX5=@r~%%-LlW%eZ$|s{6+)n=kl8UULg_E zO79hCWIywKa~^;tNPm#fZokR@*Ipy!NiW?(&{*Nm3Uq;e^ zA~KIV!h2!wG3fB}?7RYpaMwy5AGD;^#u-B_{{1cR{bdzcq>BTuEDfRrWYE82VviuI zJRt;Kq``|nD+lv7`ha=?m*m&deuTkyPldUIAq5Up(F0Q8-2ymn*ZG6wO*37<#LD(M zO4Tj1Jj)PcbBWu^UfmFaP)h>i3~^7cs(#I&@^ItI){jMTau>+-eyi*%+U*;C2Fc)! z)N5P3!L06J7T=i?c`-k(bRM8=fU~OlHVtdJeAQEcf5WSIBu%@6f)KQ0DRxo-_8>f&zSvcyA&N z5TV$Ytt!x^Z4xbc^`Yf-p}{*^?bkHpS?9RF#wB!K_C#=@Xl=u5Qvik zyk!q_0hSEkYsWz#E|8hAl^!Q2CzzL8+UDx%%;WMivYebb^4^MUw(Iyrvvd(BSV!P^ zh@e_k7+8~2u37nPtfZXc1GVlzffJ{t`%X=frgB_eb#!&fE29xnPaasXl{ww@Gi&7f zBufjCfv(aVNT1M@6qr+_ zrDG*8Cx;5eeE$4dK2!g?V`l2-dg&9c@hJTQ4-ZeTXX+X{e}BIjOUkS)jL<2{G>IS6 zx$B$!@S(tUPR-oRsG_ogj?_y@7T@8ab2!XUcczKOZpe^H)$cFuZ@*i32iiP*xVt;{ z8CTCE$EmF5I#pFPhV#hG%q%GQf`Z$4j_cKwMdA8}l9D0>bOiM8j@@;2*U{^`dA-!a zi*@a4-dzjSX|S2}%5hE2dP95X-lmKPuJH?>3r?8GlSn;-zsr=9yq^ZSzeN5?#iN~BIyEwva-^?#-iatys}bieLP0#x@t#Xt9OX2(dJnJ z0fA`@&cb8&iu6N%X|chvQO!X4n#!UacMn?~E*+EhC=-*m&K5sk--@WHnwpxJ_y`Nv zJvB_Uw}*wM4o*x>`OTZ(-QC@4@kt8{gF~Ok)YNLgM^1}L>nqW;2n`cf4@^Gg#3^2P zR1A5rHnmCW+ikUo2D#8MSybP=xjEKa`J1t~Cw@XNB=i|3UKxYIXlm9B z4Gmq|kd~K^$jQlxkB`4{<(+1HYFQacGo%Rnw>0Z}k($1DWo6}H@oZ#dpqj^LlRL^a zWf3S8N=*80W>Sdq^-N6@i~9QdYYX>L1(>3On$3lFX=!NzA^EWyvW}*5YHBK*&At=a zDtX%4(a|wB;WLFwxv?iHEoZXSN4BwJzkByi$0Ts7=#zzQlxB0pg?;h4m6e6|B~7(V zQq{n9{m(1iYqsu57|c{-^IseeZ*HV{LDt*V)$KC#;t5HelN0uV2tsn*J5-T? zkX&YJl733#uO*e2z>+xf{LVu^5QtCc-$YfG?}aImh$AcoF!b_r>#= z(|0}(ToAc1e(iewLIU_L@^SdnGV@p8WmejoQX|Vm?S6y%Y=59KcQ};R4#~O=1)h|u zYHR8 z)m+J6Pv~U3Plv2sKP5!nR|H-8+d8*$yNvf0a-WL+avbp%#d>WANcHdItl$+5Rfo?X0?hvLHJ3@2U;``YH7j zanp$cZ>!hf5;2)A1nTdKdoOzxl#ttf)|Fj9a@6KMX>?CEI+~j2Qb7hE2@`Ej)^MI+ zZf9z#cnL2v&w{S}X&spx$>)`(9Ah!xXquYH`Mi~=KPz#x)-8m4@-xiceq;*0Ix=Dz zU8%mewB@-d5X-$sK9v5vQtc{M;=H{ytXM_%UsInhgA$^}t71*!_twPEiR~P;g-L+5 zLK|b*{U^mFM3ywg5^p>?MpiH;JUQvw%qXWi?PI1otGpiWErC4+1>a|{y!M@>>ZZJX zYeR%)|2z+Q6NYzoBdk-HWHV7WCgYod@)$gD6NZLL9SlnPg zFDK8K^2l*HTHMFJW?&Hu|FhB$cljjDZt}qOeBu^N5ulE}M$k0ZjCPRAd7y-t!Mr>f z8JRp8N$$FX#5Wk3-ca|S2g)QtK}Vt2_w@Tt{GB>h$uH$aN$mX{H{AQNzjehREeEgi z2F8htwvx-ti62CSj4d)%AhJ)(|1?pIfXQu;DK-!av@jk`|FJy&rdFny8~6UUE~)uN zOJe^1ivPr&F}d{hmBg7ZM)+XD7MtpE6pvOD@nsD7XT_QGQ?)zn8H=hkQrotBS@7c3gXS4-mdV315LK6qi5@+8wZXGGODir7t#^#GjbM*HQd=z?S z*xVMy++8otjFuWbYdPMg<)YV{a3d#}161|`cc2pLytBepEJ#12_Qx$?n-cd>qHfF4 zrjcv5`KkC*>0(CsU*?Wd?awWicxCVmbN7rk8r;7*<#+lA@52!9R;z^8ZQc;6z;jgQ zRZwd)AG?2I@Aaw>QFy7vKXS*f<-|$C!_e(UOUti;!L^UUt}fdo@kRkZ-PhL*Q41rb zdaM~!#^MXN#qR8wsgK!&%aSP9Ao`zh7p=}yyTo8hUE0jd6658;8-2Z3mZuL#HUCH- zrr#SlzjD$9D(XktAdzNiUgKI-w#z3!UJ(;oWlDqQ>SgvxrPb9t<>d`iZX=;!Cn0^p z-uov5Ki@nonE!s`rH|@q6LrQ7Ec@+EpQ#Q-{L+$e15RoLx^cW@HXUeW8H<;1+4!Ry zB5Oqy?a^TU><^=tv~_el`y7^}{@egpRju)3ZzqGHvceOh_?YN*ObrFXtRxggieN8|FV?%PF} zQM+(YgXUmGAvtjMavjd+X2CmHYpQp?r_l+b?8T2K=CI~mv*wR(NR_!7Ui@e_%YSY=J0tR8MDn~ zgEjuGJt2I-p&@HD`t0)JP2DYN(v$qSPw>uubzJg>(6OjD5_dZ*THCTueA6;7aQEx@ zY^UW*Uy$vtzce%}##PrxyA1t6<)@Anv1|Iy5KE^+^}n0Oru)tvS9?)!l0k)jgMaF5 z)$M>g94aAU|7P8(3zY{Kvj`y8S4;5eM?vwIxWt7@h6KChQP!scrn=h9ANMK9ivo4zwP3|w8Q)T!Eq<{qByEP(A$l# zdl4Vz^kVPWs1sUOVX2jSKniEDr|H+AaDr-_*rn^g#dNfNOiZ-DH!2{5Lkn;v^#>xf zvOk|9qU$CO6v`6yQzz;kwKZ?_QJhq>*X_b)B(&ICPW#Px-}27$Sdcv8R)PtCABtLf6v8jXmk=-8w{>-<5|%X26fiz<+>{N{5p5egv3dq z05Elb;%l@9ro?U$G8IMnn$ffGe-?+H)p~!@nK=m$^(WT7=sYlI?l`Grt!sTmqYpO1 z7#nt~tPxem?67hCbu=;Oo|D+e96IY{*v2^iAjfq(&l9!0R$j8ne$wuG?Y(_%823&z ze;fBE~}7-og;~Y3#*#3SXA$ zf|%(xS!wdLFN}yi$h@>n_F7jDb~%OIx6O=Yq@4zu|Z*2ybDnd3!I=Ys`rRTEQ?`iq#iX zFM&OrV-n(z5AJ_pI>Wb&Eg29D{W;IvrxB$Ty|obvNWql4GepyzTGrRvY2+-~@A4A4 zjVZ-WcG^!aLG3vBWFItE;dY3Q`^_+L#~P9Lib)PVFc?N{CjP?XpKkuyYp}{9JKk_B z#rJTtsVm2nFgHnB<{uD(I~s>){bn;>u7_7LJ5?8$k-VnLh^jZ!`_AsG1>IdAy-}Nv z$db{~&LDU4Of+qLb01JeTqHZ{{^kTL);m~2jW*FCg&;J~etkADIm;~Z`aN&Cc(8C8 zy|+Q=B806DmLPU%{0w#z3`c*(9NKef(&L3G+2`>+spweMgK$iiY%Xi=3tVCzdAqm}t*9bw*ASu6QYG<0`yAd4a%l+a`?y0I7pPJy+f*;xUvR`@r znsj&A*$DWA!+xs$xHY-DJ@D8xvX6cHDiY2rT-fYbPaXdB0gfToJPMqTe@PAnFW%fz za+>^!V9XBJ1$9trS0t*s4<=X#jP%ik{oN}3!M>6v39sSS_4nKQc$;r9S1`WaaccI} z{J2u}+{(BQvHYtol5%A)CmH=|R&rAJSyc}_n$4aJ|AXEhLGAA}b5fL2SKF9FCSS{! z9rr)Qk(qDaTT7uv$U%dv{-cl6BKyd~#1zV>!bJn+5M2KaI6Jdq zR2a6GgT%F22CACTs%Oy|kVWQ=I)X4=DaZ@IUb$-R;B#)nPU8iQl<`CAtS38Y4>z8N zh1Z4zRb}JQ8+3Lfaq>WGLThI#>&g(^bbxTRwR4G49@e&(PIj{K_3*FiwGZ2-&mO!$ zvAeWNT^d9Q8tgou&ai1~Rdv_O0QhBNA2dHHlOg+LsKy3-(+zm+sBFmqVu*a#}|+ZbKIMZkq7c-C0;jJ`ZQjjXW#%#tuYLvg2tZ{(e_+ zT6N5s!`Lo@>JN2^J`EOrt?a`CDzp6TDtZv@)b%D>HN>BIYwpG5EHtAT(m~uVcbt&i zM6w5~>KNHh*CuL<1x+x_NhPgn#y(}AzFLC!7FX)KMsy7JonP95nI$eETi?SlxVF%m z{4%Zw`o#16n-Go}4V;uFiggQ{9;1k$6)h{Ver)x5&XyOmGr!YugP~rNkE93_67(Zc zVR8l7CdS}Q`U<;zk}-IPsI)`C#M^BlpUgH-3X;S68cHr-&6$@W^lGvHu(U$fstp`9 zck>t}waZQt5DjhHI`7FGB~wK&+-@3$PH!Q4cF~;15Xz0)10kNxLhIFYfd_APk~ENS ziE*Ti1WL>Mux8&Qb%#-dJLi=guCd2Ph~rtQ0WD99Ti}9H7Yisk^}IKRBPa^inA%HC zZGIh^_@YH7JzD`DT292$hIiR1)YC0aII^2m=D|c*4LQ}VLyNw-$aZPi)KX^k~M-oD&C+y2suXTYu_$~EN()CxRTX@XN#4UH zw)3?r3t7MWSN!{jLYJpPmt|xJ}9sL_T30uzPfU(tW05@aomxZ3~_h<~+2pb#w1#g{Pbe zIY;4n(MoHNq?E3%!GnQoTEd%ktpvi2{pO-or>%uWuU~szGF;ei*R{4@iaU+Y?Y-~P zN8nx2i0YQfAylbR%rf%^0}(c+EQVs$g2Q%{ByUnw-)GZ)m1NWCm!-hnye>KJ1* z81m~wgdje@obTbWs8&8w-B*$Q8+bLB6NKaVM|toxktN*wk2xY6c>aY0-N*NfAnn_D z*vfoOU8w5hQ{)D1LMCi?Gxj7NP5rf)1I_zG(}~>tI{Cs?Ay`Z4q|~>PumVgELTWa? z({5EtL_VlI@r=gSca5;-r#{E3LsTxoA99P{|#JX&JLL=k{r6wo}tY}k;nxtwJD~T`*TK(r+y@S8SP(B*dl(^SN(kAp4 z8BuH&9YQ^;Q#4&#PC#oqbH8oTAD`rgH3qA2+`ecv;TI_|b{ zCU3@XY-S}z^+JVGb?hB?7RCN7;EFC^G_t}By=2~p})`AF$Bxix`hU3=goY2kIH-Ntl& zXfW*BT;VQE`M|^Kw}cz}+C)5V zs+=3tC1Jfy>OI^Qf1@69yU#UJ9`&7nMX5jZ&U;dQ#e?Bk%!=)V$G_)!XJnj^5Za@= z$-cGb)fL~|=pg4K)+lwm=%SCxd6G1^{o%$X5yK_DQ7wZ;R-gN}OM^ud?GD4B$Jp%7 z7&jpXcDznL1{S*sxr>DJ(-m2)bA7HswMBjKjge41dUNT`(s-$4uP+e1%Kv*j04St#$!4ORfv%U~!;)-fEpNIK> zJYVdDQg=(~Po!hI31*taYmAI-?Ydq@21?ebgZsY5+rWvuJQo;=&z?9irWCc789~{Z z!+r@X_=Tt{ZqdG6&28DRBSFhM#B=J^3#2e8B?ry%@^25V*$LRoaL>|4kE)Dcc&*x} zFh=pGj_y8w%*1{ZuU*qDt)@$WcqFoLTz|B6KZNnV4A*ctGY50$aMqsgWp124sB-Ki zbV~o>;*m9@(KTCmAZ zmsF(s0IfAkVl|X^PXH(QTwOTLbLk<=+hXKrjc1=A+x>@!I2>#|ec` zj&!l~QY%k>Xrt2 zG%-dgP@mPkQD?o^?Irx!MK}6~ZnF8=vXLLjUX?fV2$`Jmje`clCvR3)I6l17Z#6g) zd1fod=~wdk2Un_pNuP(E&_}#4P*4x(g1>6IKB;EUnp$NUJG$t`Qyz*)7tcYL!8gDG zu__|ZQX*B>8RP^@IpPnajr2xH{A~Jz3zu5q>#pKH*`q90=vQkhwoEL;X7cX-{ge>F zj?+kj;`9Z@xFWR&K*f|l@Pg^b_Yr9emMIjCcEa3=>;AvTLUc;?<4KPl7l|4~MuR-Y z%B&i3{_7&*EBQ@Zm|u07l{~TS>O9k>n`5+}KG}wW7AyWVSxM@@Hj6E?aea$`7cL@( z3H79)!Gao}V1(+p+6z{tvE*^pVr#VaxtlM9V-u#)>ebjJwX zqGIJ`qF&e|%8rA>NY62a+er>Uwtk-YAyVoZl|RjSMLB0?lX8NIy~TFG2xLI`W?7+` z$`>zdC1XbxE{;F^q9w%62&yFsA;ss=0j;KY6#oOl67z7yb9 zhpvBx=2TKC1<}`i)wvEr8x{}aZM;!DZ1_f<20|5!d+MxYQ-YcdQEzcH#u>!e?^LSd za7REq9M#lN*z~F}=yHT6YH_rwH%a74T^cn5&)7s4YY>lJ#w`6f+hSF zFI0P&c#Yk7&q(Wm*v!~vwJX07QmR96+V@LQqC%s;=0claJMiJ`l+=2Xg7iP2gZWI; zZ0uy}ulh&}HP1TGF6l>FCe=_?`ce&P&bQTg)3VVDWf`k&Ch1SJ0uu@%H9!dK1=yja z$TjethB`z2tR_ua9OPc40OGv$&ReJBkNI#{Tj0U%AQJpBOOfiZlMvLgnSG?j;ku;x zPBKm81;KpxkzFAjl{&zo&BDYI`L-Nlb?j4_04F1N`?K zF6L=g#W#hdq08pP`BcEX#xg!Z1csoXc9Bs$UFd3Vf6>fWgdw^iLB+bD=yMV>KcfZOpK`n*Lg%xc0z&&Ld@K*y3gKUo zwe3hWQf`h4_sh@hJISlo9L~A>l=_`w(3LX?)gmn}5LY|=@xCbN)N$O$7Rq0VY9s7p zWl>{{sj4f_SOKMop3PqfYk+UFDpSGVA~oJayNM=>8ECT%o-sl)<~8m{|`Fn@%g zAe7r=i>umHnb>qOK?S@av1G?mEUpY+!jA5d+YyTe9Q9oL?S#TvxPw=c1R=VJE+8uc_Q&KL))7Z#!tu_~|(-_Pk^nbBxIci2Czp zCZI3Y+v&J-Kk;G-fY#%P9H2b`P!AmRBm(D$;w)3&t5{3u&FmU*TjXjVhG&O0M?DLa zP`34hxh4`0$-gE3LIt>-F}3U$_>6VThi>t_=9Pa#3_t}u&25Ay3w#y_BX~VNQ+OWS zVG01AvPLehuf07NTY*qr0^zu>br62~uHW7MQ2k_(11Ok@KDNHLcDV1$eP8Hda>yL) zrDhAo10V8vR#v&MR=3Z-H(er<1vLYxHNNb^5f85Yet3_>OCSn3FTg;#_ZXreZD1I0 zh#a}gdnV1Oz`=_B&IE~y0x`XQ#(e&vr zpEvTAM1j=n&O2i8R)`{TwC?tj>A&gq5s z2g$vsw9_XW`GA`D=V9qqe0IeS0dC4{u?Kg3Dh=9Mv6#NvwMbrK_|yVy&Fs4HU$5LT zx`aRP*l|1;Xj=l5O9A!V24Yb7gC!`&htpOHpKPPt91s_ad)Hr((reF3fkg8ctK(d9 zgIFeaF$-Ihr1+Fw1VMTD6-E06a?#;({Wo(}Nx6;p=`j#es_aAnG{Z`T+q+NE^S1kV z2lKXXpmpFNUp8n7%htY`+sHN&|*D@r~6Jxpa_ z5W8YXJn|Cvaf+y$bPuU6<4C%Fv%j@=#jwCT4cEZv5M#?6&sGDhm z30Ig(r(j4Hx{VNEAf`eJu@4=90)`xFp~sh-j|sw}7n=_!2WA*?GIs4#8Xlwp{PhQ( zo5s$_Dd*ef4#`AwOom^d2e9Hmg@VqYctJ~0kTw99?!75<2Jze}@xYmvyer=*+587s z4I*$Yb`lREbyjyi91fXtHHS7&1E%A6h{*aj=H#fS0IzzF4RTSU_O4(4qooKg$tM^b zU%p)XJoNO2w(u6@F3!___4ywzu0(Vq;DEgRPcp!bS0s4%LH7d8{<>B}zMVZ8#vu@t zYfi;poWtJTJ)ElqAgAi{*(CuWh0l@O27vm>eH0JqD4t8Zcoq&ibqHBiJNvDD#OQ=Z z8FGEIZjmF`3Q38gq%2!lZ)xQ5UNt?VZkCBFW^`v81#pj7KliXALC4zz2G8pT7Z@p* zJQwHE23q|^v#!kE8cOp&|ALzUwahYA!9LILu89<|=hOzYh_s?ts`?_PTl%6v-lg}l zbb*0ODAUvPh$CJ?B|I|%N`}1;E59lT+=ei6UKsO%zRbZIk4BYduU_70$4;3=4GzDi zq(=9E=u?CLAaql>`QxSoaFo&<42%WAD-X2$EwiGO_kkp%xK_~aLkOFITw0zCJKf8n_zd-1CuU#7NDg4@U} zswyRk?fZIB+ZYY0VE;S&{^uTZTYDRzIyug3~5n{wy++G8- zBBW-gs1waQB$g~ua#XxRU2JTJ5!CS-_sL%q;kFAfm6gtssX@fa)smRO*GB-GS_@?* z#(L80s@$+_&aKPvTnrWKPxUy_+xCkD^{D0w=pe}IUT90Z0A5`VazI7>`xe=_jthx? zxk_f`EPh~CFKThcb3S_q%7bw7+xnq=a;d?F)(|;qB##MJH4F$jyPyuQ?Q2x6J3&c z0S(roH+(IAd+w+e#(G-b2wyDM+<4VqkZp4KN*kBPdm{KO+F2e9I3XK1o+#wcYx@OY zu_YF8Bmwy2Kgu2iyutq(Cq9XFNMm#_6=q+u&N;$cul;M!L@vL45sNB&J^qAia0Q!0 zY2PG+abIB?^R65DzsYa&vCWpvl7~R^%b^A(l$A{~|I9gj{(x=GaBWgQ5O!YCHOAd@ z(of7!kLOU{F)xC&1E|pIq;Y!!t#NDK!+wi_Fje3KK-RR^eWY?I&;AHC+Fu`2R%H&%j1OTVY#bSe4`Qdtw}mlCgeaHrex<;K_%lb6coT(XvYoJvjup zpBzxmXGa4?Vo}P#j^1CrI|joS?|jdEYo_G5as*V@3;%myayPo9tU&hSJAI)x^3_3O zdIl4%edemdmdYsO(yfmSLNd_LQnYfhGd`dB6#=hC%V&p(l-<~xAbm77xyE}Xu1{2GTnv3Deprrtm{%?^u8?3{2Tg)>(AwT4oj07_s z$>9plxi^}^d;dR|PVG%yoDL&+^9ib(=^`n|X?R#wB(LNXeU|r1nDu{` z5B^;a|N3W5!HMq?cb?6J^Jadb$g$xcaM+DMT=zN%zcGmib?P|m8j43yy(f(oYp&jZ znFy3yNks!(ttSOl-_X{bh0FLiXkIy4n238t{udm1AOM`6wG%*qD*aCl#0|sw4xoH` z=%j*_tD;TdRgR6ilRLyMhGSofvYc+G@L|F96>KBz4zxdQ>BN{=bcnjiOgtsz?u10m zFDMTXf;tID7nA2EmqVg!>K!b*Q;x%brZEIsILy?w8a#^s)EFD2MI=s|0&oe*LC@?j zdW!`-zjXi9@cgST@2`)VX%W%Q+N(PbGt>aRRhg%8t2e!8I4zZDpl9{C{4>zz;>2g9 zjHCitV?d&F5W_z+MALREgjY>WC6@DjTa%aKm0a%jJvuZ7872TA`@}M)E#0L4@yc!d zk4+SS;R1RM5JB1v+rTNF?I+4_+>s}tEKJFA4tz+{CwjRzC5}Y}ZBO9iT28%8*3p9Ew692aP`3Nq4b~sP{^v4ZwZN#iaPs9 zu)u2$eJGc*$BJFDJt?o*?#A$4PZF_kMPyj=5W_KB5S{&it1Cxa$}Z(!!qJ@DfrID( zR*vY+!VX95SQ44k>sI}*pz^v;uaW)Ml{o<{4!SW=Q@2k=O5&q0DcVi|9j4X%174FMjeWDtor9Vgvb@0qe|W?`0t<@+&?WrN|Hhkw<^VNP zKrexWhgb<^^#0gochA|#DsP#%`YG`7URsv?`a(t7X1!h2F@wJn{HIn!TWZ zPGe+vGrZR$hnpzR8@Pb;(K)N9i^i+6gHPZhO~ci_VoK_;B7!%Us=Apws=&_bui6#y z3rbpCHC*fe*6IINv{(M0q8*dOi;oP&bx)0>XQBxbh3Y8P(jOmh%&b$MfzeR2I-Gdg zb0rI~<6`z=h5%py&L}EghxS@*$WzquuYParbBZj$HjRD@Q{MJQF))1so#e9s91mEr zw~oNcXqD)XDIIY7L^!*JB6n`${+H={cirHAlah!8?f09I6Ns-CbfDQDUZm>984Q7t z@Nw4GRP)&C7sATlcurz?*%oB-v+?zaQEkc=Bp2F)`4`2`0b&xEClK;KO*vpoyuSmh z&@1W+B;24uO$*GpCEIKL(A{Qj@<46jDGGR1>H`#ygN5HofM(5s3&bGJRC zk{uKea_rG(hxmmhsI_=ajhS}hu$_Zbj)Ft;u5}>iQT*yY<(pAWAW)u>69>fs+B4zXUF$7kk;3SlE zU+ZHT_&IDS?JFG=ZuP@qLt@W66W#lI=sIFpr6cF1Cd!(i%{obf|GP04rur5(BEK;w zUy<<|(@2|ZmSqPgDJIX4`tVBD5_JmWwtfO4{|!-K~v#ltDy$VVds}?&#hX%M<{I6a&-YoNLTyC zyA=xStN%U{2v3mn@=`KDz1b}Y*y$$4*?LPN2cG+M53|V?W8DAWhYW`34@S1|GoVTL zMVRaX)Os$vi*RN6L~1e6$h~%gjW+JXqxYaa&bki5NZ(Ol*PM;!5d619D{KK3M+Fr6 zDJx{l&OPn%S@@5ZER_c3;wFWfN2XG65uT!0;*R8&FFoffazanM(4HuIfHhmlzIyBe zpwFY{L$KfSg^RaHIohJ3M?u><|876BUqn7Mrk@RZm_LQtRMT1pp9d7NE-GryHleXu zT-xw0{^FZe_Zxi>28QausqM1`$-l7G4aO%?B21?~#4ZDJMvl9NOQe~S=^15(67}Uq zV+yMHfDJd`pn?|;1bBOOy~zkVzZ0AU(f+WPvq$V708`I#sHkO!jEdY8S5#^^hmvLf zlM3nR7Y@SBAO4dyFjVu)*SXLu+6>X3N1qr0TkXrS|4-v2;o$tMc(?Z;F@Th@dlX>B zok61SC9Z}U#0BJz?wOq4WKjxB1%MoU|7q}Gn87`Z+)s!q%t5@^s&*a;9#7^QrTORp zWtaDGf6ffizO(o}zH@O26E4jSqi+HB%l{POQR2z6e{nv7V!SPYb&5_$a!{c?JUHejCg&zZ+bG`J?HQqu)@t(L(e=$AHV)P5$vwcL zv=reOLi|z78U2t)(fgLC&ildg@YFlX&jqe#gjTIW{M1*!O~}(I~ z=BDH3aZjGFdePi|nKoXMK$j^LKI3iRkiq&o?HT(r+AsYF<-4P`edKT4Z z3Z}PEut^|ICDia%w(6f``5c#(ref&DcO6Dl{-+dvpgZSNc%`*WvIWtwgo3$YGi(U8 zaj87d2oeE0xJ~R|s^dd0-nP?i$;xh2M*-jm%x6$e7gEsXO^~)v(rq{4LZ440seN%v zPol;$9D8$Oe4V>^`ROQ+u`60`vEIVFLl91o8;*}({;!s9F4W8k2ia-LvRE;Qh}K4K zK?;GVjqd;R_mgm(!Gb{^Q?XOymzrJ|gI*4?{0HeJ=OG_Zj z(S}o%h-Wq=o>;NS!kS824KG$R!$J+ zh!{|Eiw^!M()g+H$*=Q@sJCMM{lMs z$X$x}A(6xR<=JHbJ2!~c09W(Z;NZCwZRWNF75b81#~b_^Z2mA%L}BpFhm~lYHABWi z;@VG(dp6&2OQ9dMoVf0Qzgv}WLUMT?6rm9%YFb<#&RurlDM=sW@-j>H1a5mrv5-2l$mcL(@4xKw7Y0n0H=OAY284sTemfwv0 z1<`6i92}FlvjDyME=_DR`{5CTy|0fJA8cxPN_f`8EiPTU7?yAk_cL$-mXdE4`Ks~R za3)p5_CY+)+y(2n-}kTDwM1=9lKDJgtZr}FNc(&(w6dYcH;VL^iy!J9oofx0Sgoh| zgB#)8xStQ2Dd2Up=i#SC3^SW)+d+|+hD-JLFu~T-e4bx$<>MA2cxYT4(XiaWFUIG0 z{IxbhdLzZ;@+@s{87l{5H@FI$85y$jd5?(a?gi?$(7p$JH)1=q5^lU>#WGIv92nm$ z9ySHs=}~2|L2&*u)(oe^ep>wKgVTwn7Cmq;z3<1j>r#H)uoIQf2i(u0F!-LufB$V! zVfY7+#=D=`{5^8Hz`yIbJe2JJ4!!*EaM3g<=%qWU;-E!0ad&PL0=lyF=8fiT^tGc$ za*rP>)xA8^LV-vUhdYBR&`o5)luw^Hh^wdmU?qt#0R}=#Wp+ygzq=Efek@LB!#2b5|{kBAcF2#BW68#+v}oDM4@)DItZZ@N3oN}dEV?U z{V;Np3n;@u7r@cNDRx0f*oCeWh#2{^?Fg9B5&4xvcXzn^TzzLwYX@)Cp{e{bIB6n7 z?bgNM$fWo2pW6k>qmTB-4IYgg?M(1DlGiRR5Kyc&0E85EvVELsV#3MP?T~`c7>C z_YsEZ`5*pWlHVgJKo>w>=}GecNNS#~AQ8|XAkWlDpX7cibabOG)u_3N0{HOG4)R5Z z7)MNbx8@s>9{bu-eVxygXn|+PoIzGj=BaqVhya?7W=pnw-IzP@Z~ z$`FsLGWFkx#mOGewDv8QU<7@LBZP2nn)r#Bb_6XLWU;E{voJbdu~k5Ew742uS;s;b_+dE@MvVsCG+t!-crM@cD|qtWQTzCJ0QMfOO4fB&%P z5PW?BZF00@lw4I`g|EY>7(@-n7G=((MXhQTne7lL9D8)T3A>Z8X8hoF@YoFa&vR*>Z%l!?Nl`F zhen4)rL=)Cz|P*B+(C{`DpH18kJ4xc<|t7qT|0P~t~t7Is1E=D5}9OfhtA5%Qcy96 zd)ljLI-^i1YisK|@*7QEXEK?j47Ka&>4}cZGO;#TTU)Cq*4Ne7jgAh5goPATrAI_W z`6KH(>XK4&L!$lRNVqZ-p8F;@7tb@Y&cxh6 zR8&Mx*<4Q9+Qb?S)rM>8@}ODQk?>V@b+mC>M@I+B9|hHRb#}FvQ#MI2ND`MbA(2Sw z>FGowvA(`OEG%qvbd)qi@()AV!>xx#M>{&mCMG6!@Cb7|o*A@|2!A^}>jHebl$-%? zSX@e5TuRp&3D-42;YoNjnrBiHiA+YJ&}1^%#M)m|7nxO*Lh2xyn48!;+gn@P)sd<~ zB7hBvodLWpyZf%C6Ynl0`scH)j~cel8x#z*vNaRz+rC#^O049it1us*Y^{~4@#Wyr z*_`BL8LxY7c>aP)g#7y#pm@myZi zXZ3XH83Mg46qdna&!T~1#6_2&9WSP*d$)eGHvMq7yqYi*$p%X=Y*5(Qos7*LFK$gE z4DSs;YFhDX0zA0`YklmEmd46Kz57(OkJqm?whyF_os-i_z;PT18$BJ()9f-n8qLF@ z(e-sY&Gi#=?4m_&%~Wb=qwxa0t5zKfZJd2!LBBiGHM%xl+tTvb*cMfO*+zYV%6ftR z{c(sbyH(74GhxfNB52$pJYa=|mwB#RSuKC}){aV*eE(pZkBd*nXU`0n zsi`qfK%voJISxXD`M&mn6E8gucY8G=#zu1w zb~umQMikG{k@XIB&@ttK*k9<)S-m9mbJw{@0r^&S{e?mcekvz9^0Z0tk0+lh?|ij% zSud<$KdDe!rdnK*2wMC&G&BxB^knWhyymU`60lpIB85Y$_{8ifHyoEZHDorX9)0K5 z^dwcDoBC39Y$pB%%V6)!*X@E|o;{fCW9Rs^ZJ5mt=U;p0_}t~JH9 zzIdsSXqG{;Zn0Nxc-Zkv_gyt(&$!0PY4qEWv3*Z{WxizBObK?2c*yCkKUpeF#r3ax zwvk(NZ-hKyr^OL5loBK(9eY>yQ z95o$pw?8Yd_G7Gnvf{Y!lG2y3*9B@9Za&)mAh|AK)2gnAewLyfY_s`9B6pQhe0^V~ zG>5smv5UL07~8p~_N$kB!Krg|)jPXoF`5-76m&ihk_&x)HO44@Tij~9{?c+mdqgWU z_MOt1^C8azxly-sHb;e;o1%p1A+0lp04vgG{{3j}u<148r1N;yFy2gUxGMeb@nB8t z%1nv-8hkDgbvQjndv}bbc)`6!{ZMb|BVvf7<8Z4zX>wkNk(k4J0Cv}I%;J{X^R+d? z^{wkGd1q<^^>41M^|&X`o)y1lW_4{jq{6#~aaK8b)y3*-3WrI!^lOQo9JcgwBS=1# z9mQs|gGKdQ`=f5d^Dby>=0d70JGFXxK_ zv)Mut3AZ%GSYj*!T5fLXXXyp(LL#B(x$8)Mogo@(Hx91X>RFU;U0n{x+#{bPL0;i`9=RBsV)InXJ@A)d)a%e!tV@4`S}T#yoA7?{vqp2Bh6j@ z4Hp{d{=h5Cy!9n%k@a9tpQDy^h&`#Gc^ZmITSJmFU5VuT`eOUOq!GDnO#X+n|fOuLKx56A&XzQj@sHiG9UeX=K{|p zc}_%i3OaWhi_R*m-7;($ykb840&Z0}&wO+2p|fJ_njAlHVT}>8tJdrpi`D=EOU!=z z5J6$E#Dg&omi#1!iqXwDP?L|~uJ2$>u*3@iF1iUfe$JvnTC0xv`w;J?-5>{w@@kl= z9;*se7-_}8fU*4=!4)Yj`WM`8JY5e$_dTkG)%WZ2HKvR4jPDGgyrqQ3=JCs)4x?&=m+diJjFLQ^tP%NR zvQJ5A{B(6BwV7D@z3AL=X!8DEgESjpAFd~}d)c~ISdZ5GnB{nvEL^wIvA>sI--;G^ zcAAo=0O|9q(S z&=M@6ziB4z=I|6?6dN4V{d3>FXY1BWCPX=C35z;JXR{ z^%=x7SA8xe)BK4G)yPK;_S|+hG zNbA&0Fy{83T`&QdYEj!`J1TrG&+g~q5A$~}u~4__ z%{GJ!cJi%mB7B8ZA*X$x?n4UnHn3vgYG*Kd`%V#vWAA2A#?Fl@p2O9pW2)wdYpmXw z(0%1VKj#fMpgJ_>ku#jYe%ia*W4e6>DMjWMVIZsgL%gO8(>eE zz!g8kbA}0)_a*3Mt_O$_@l=k^r*F1xaee zU3VPndYi2Wp7d>ixR#)@O@yOiBDdUYzk0i_iBa$JWz zsIA)Gl~y^?fS=qlXr$#zl?}tEe&igbANciQSG$+R=*Ss|djaqFiU0vldze^(>b+*i z4;o`Ou{wTsm7GCTnpb5-N%QLlD4~{8b|m?{kpx}qeP;)2vP#jWa6#9anIr}~gvA^I z7U{FfpuIvG!Fwrczvn6mO4JK8jND)0BkFIqY2 zrkmd~OBD72Z-J0GMJN89KK>Plf22Z#?F{$?$gFXeOrf6+F6Jd4`@C)R90oC6F8^9NgU+;STL?q^9s%orlVDXN}?$u;4R!qjQ6inc40A&0s)5iq!& z;6X>;LVDq%IgcyG;B?=qpjzD-SmuWCQxESlmNq-&w2bifw_b4A(2($ zZW7%mX{61vQ=e629Ip z#-0PQgct8mk5M*`Ut^@d>2-rRRX-B?HFli-8A|s(2Kq>X+zy_{YYfg^Dinap9DJyM z?%O;y7K;ft>N;6r5SfaAc^j$q56t6RFpZjQ$*&BNIrj_?G1#dwOipcJF$FU@ z<#pA@Rc*JVd3o=lovEZZ7l1CK)o3yoP2q2F@E9I*6>^kS@!Dd3UK36V6R_y|7E8;m zuvv|+C8Pq3>>2uLPb!#ZIFl<@CX{k9Qa*R&j7t$NskohxEt|1z<^GCKjwOHsLu{w)W|Uk z?k!MH|EO^h(isL|bq;22(TK}=tT*3(Im_jWD3Q!did^fC$9?W za=W|~S%-34itV14We*V}WuGutJYRKG3}@;DfyS<9-5ED}oS3&|1b|CX#F?8qkBa4v z98-LFceb<*e7|u+0+?B%a-N=lPjR?qe}HxQpe|# z*as$UILuy9^CT@QF}>2oO4hP{cnH?9(D9VQuIZcbtG#5Un&_z!=^bS~?UAWNPmAy< zJ;55Fm~oi9h^n{B5ROi+rAO2m1<@S5-}M_j$?<{fh*|d>;&?)M>lN2Mz$JIA<8clZ!U_u%qJts8{NiC$Ef4XWkx`W7T7R>&TxC*{HteCcqw;j;7 z47>+bilP=J2C%>TtaVrPmnFEk6`y->Bz*hqHZYiM9H1w(YXU?Aglat>rfTU45X3tQ|;C*K^upX2s&=YL&NBYU1o#H-vLDd=Bn~y=oL5XL?l_ zz)E-r{e;}mYNoNgh(>jO^F73DAFqA+{RQgwFIC16z4s6^ELt}}sL90w?C|c zd~8sSE|sL0eZY9N$ei7g4CbjuA1Hp7@0VX&6NOjwg#qN5Lj zw5Kz$U|PPTm|{^-MQJOEALLcAytur@YAUWtEpQt2`pk5E?lpY7pH}ov<;*Syd=gA- zS9FNP5&YPaGOUg`i`&3X0GtLVgKC!~PMSnd4tSl^2k#@e+HFg)K&|Ai2K*4~SBPW+ z$6tUgX*%|~dgv)KW0QKOU2!p7?K)~rpRkLb(_j#3|MDfTIzamb9J+x?TEjdV^iLU| zRjGeyEOLaXw^_UUcr!Xo@AdpO$anB3#kQ}#3v?<%XAefE1p6+((sCBOXnt6Zy#4_# zOH>6!%3Vv&(|hhEe-4M_NHttc=Y!BAL^2+}M9yGDB zc03VRU*>nzm&>O+PkJx)MwQkr{gV$!9H_qbvKBcNEBgz@K||)z8lVzVU>tF(Gc0;( zhSuQtxDGCpxCF~T&MOOrV0ZD+EO=(`Nj*USH|!fYdvn;p@uk6b;#l9=s-Xd|3R=Qx z_={-{`rL@GElxnNvO;f|IF_|0JbLK6w_8-E`jbYdQ%8woZ`Op(U!FGa+P&=Ju*I}o zLCQMMUygp|U`_Z93vQJ^GEzpxc)X%T-C%T_&N--t?{#BMcslNq`aO^ExW22?gD&p* zD=sVmX<=Rd$}SuK>3Xy3G}v9%d$9v^DUs-NQ!Yj&Bd1j9XN+p0bCZVR?7Qxl(Z+fj5T2nT4|n{&-gkzk~4S! zHjoo0aJ;8St>hUrX?{P6}{BZzv7l$I+5dJAlTw~~By^L2c#crwfMJoEFm*$!e<#Tf;!SJACsdC8!V z)e)?7D~bo@n#`ZEDd}n<@WKlmD>j@Ba&NF24|$~wuRrx)7u}5K&c8fr!s}f9b^XS(miM9R zAUMav1M4-$;|SA+_`Ms#f4=bd+ZBJoI*gUzU4N8s9X>k#)pl3zY#!xN|Ran#%<5Y^VDwfb@64Uma~3 z=*`v)N9P`9)Ey!tJbW!wkb|BW(I?EjS0*c8sx`W3!9F}CNRdTHasZy?MKqmv++WEl1{SQPf^lgJ^r9e~dD zM3kx_u*ko_|H8Ci!1sQVv5Uc z?x%T2vU_RlfsQ@G zW03wDZ5hT|sodp>R?z++D{f=f04@IvGgGJ@J{bKAiTSi8PIM6hK?(V$2~OH$+C=%r zd_VZ>tr)wV`$?NsKQrIv=r0S|Rwhk0Ej;Syo?H5!E2$EXX-so$nvyGzL5x#hSBIik z^wf9NOJD=@us{33)jKziGdGX#9xC$+pQG6x1yxSlb}BzPnoP0qY{_P`-~+wi?!^~h zxPG_F#<1l!Qb3+Y5G>--${QA1U(cjFSOGfpm44ec?Zag%aT27E<&I9wLegDdl9=R- zp~u63+LtTBdL#Mj$FPKjfZ6~se05m@`?Ecq(-V(5$}N>3ek?22&f&J@qZ&r*#@=G1 zyZ2xxa#eWkI#u^!42`vZ{&RCEUKwbNI3Fzoo(Ysrk6$cr^4m}M6$KwG68%=bz7I&< z9JCO$p>U6tw=+VD9hL9x?!+X22M<)tw1na#n%4Gz$kG5)8y`-BZCD^#rUj$QYXRIJ zga9i^RN;}v_Yg3Z3uV})QQa+aca7kdV%;nBcy*chqf`;1X;WQ>c7VI~u@uG;53{6TOZ!l<=Yf5w)Foa=9bTDD1|9FnRW6xskss|h zN&H-?ct%O1MNB6kB#=#GZ|Fai%6GR|z!uHpZAvZFugx4j6;M{c8#00m3<_drzV==v uhRUBc@+Rv}y1ec7vOICg5LLD*zhB7BxTI+QB=0|6J}WajQ@n{=#Qy;`VQql` literal 0 HcmV?d00001 diff --git a/gramps2/doc/gramps-manual/C/figures/prefs-colors.png b/gramps2/doc/gramps-manual/C/figures/prefs-colors.png new file mode 100644 index 0000000000000000000000000000000000000000..1c1fe960e7c3ef457e3de5a7e8b8bad9ec147be8 GIT binary patch literal 8333 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 NFg1c3k_;R}{}251SgQa4 literal 0 HcmV?d00001 diff --git a/gramps2/doc/gramps-manual/C/figures/prefs-dates.png b/gramps2/doc/gramps-manual/C/figures/prefs-dates.png new file mode 100644 index 0000000000000000000000000000000000000000..fd21ddbeaf022e343cd64f9b8fdd977132f86189 GIT binary patch literal 8818 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

eSLLDv;Xy}w1L622pezM{fs%QK8e-%uV~5hv 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 literal 0 HcmV?d00001 diff --git a/gramps2/doc/gramps-manual/C/figures/prefs-guess.png b/gramps2/doc/gramps-manual/C/figures/prefs-guess.png new file mode 100644 index 0000000000000000000000000000000000000000..5ad52ac3f29d1cb8774aef5317b35a2ba08e296e GIT binary patch literal 7460 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 Tuo{Bj771Pa6?1`#z7_mmF5`yz literal 0 HcmV?d00001 diff --git a/gramps2/doc/gramps-manual/C/figures/prefs-ids.png b/gramps2/doc/gramps-manual/C/figures/prefs-ids.png new file mode 100644 index 0000000000000000000000000000000000000000..5522b155a5cb33e64ea0a17b56fb418428fb199a GIT binary patch literal 8384 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 X<`1dHXwX7lCMj{{}Bd927 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 literal 0 HcmV?d00001 diff --git a/gramps2/doc/gramps-manual/C/figures/prefs-media.png b/gramps2/doc/gramps-manual/C/figures/prefs-media.png new file mode 100644 index 0000000000000000000000000000000000000000..8a165cfb93132dafd8ea6ea95e30449b67a82dee GIT binary patch literal 9062 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 G`2PX&pTFDy literal 0 HcmV?d00001 diff --git a/gramps2/doc/gramps-manual/C/figures/prefs-report.png b/gramps2/doc/gramps-manual/C/figures/prefs-report.png new file mode 100644 index 0000000000000000000000000000000000000000..613214d7657cc26a3fb970b84c4b410371fbbe76 GIT binary patch literal 9068 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 literal 0 HcmV?d00001 diff --git a/gramps2/doc/gramps-manual/C/figures/reportsel.png b/gramps2/doc/gramps-manual/C/figures/reportsel.png new file mode 100644 index 0000000000000000000000000000000000000000..eb016fd6e44ef219fa7ec068a996500d15ced115 GIT binary patch literal 8195 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 WBBzlI>xC&B+ZA1mP9@qQ>i+=hghpBb literal 0 HcmV?d00001 diff --git a/gramps2/doc/gramps-manual/C/figures/revcontrol.png b/gramps2/doc/gramps-manual/C/figures/revcontrol.png new file mode 100644 index 0000000000000000000000000000000000000000..a6046ad22dd3394440e6a58bea16f4924bb3d074 GIT binary patch literal 10447 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 DKw`w? literal 0 HcmV?d00001 diff --git a/gramps2/doc/gramps-manual/C/figures/savecomment.png b/gramps2/doc/gramps-manual/C/figures/savecomment.png new file mode 100644 index 0000000000000000000000000000000000000000..f16abbf4d087718400453ce081b7a46c96e46586 GIT binary patch literal 4456 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 literal 0 HcmV?d00001 diff --git a/gramps2/doc/gramps-manual/C/figures/sourcerefsel.png b/gramps2/doc/gramps-manual/C/figures/sourcerefsel.png new file mode 100644 index 0000000000000000000000000000000000000000..68a782ddcaaf906b203e545480abab69cd6311d6 GIT binary patch literal 6009 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 + + + + GRAMPS User Manual + + + GNOME|Applications + + + + + + diff --git a/gramps2/doc/gramps-manual/C/gramps-manual.sgml b/gramps2/doc/gramps-manual/C/gramps-manual.sgml new file mode 100644 index 000000000..61993dd57 --- /dev/null +++ b/gramps2/doc/gramps-manual/C/gramps-manual.sgml @@ -0,0 +1,1863 @@ + +]> + + + +

+ + + GRAMPS User Manual + + 2001 + Donald N. Allingham + + + + + + + + + + + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation + License, Version 1.1 or any later version + published by the Free Software Foundation with no Invariant Sections, + no Front-Cover Texts, and no Back-Cover Texts. A copy of the license + can be found here. + + + Many of the names used by companies to distinguish their products and + services are claimed as trademarks. Where those names appear in any + GNOME documentation, and those trademarks are made aware to the members + of the GNOME Documentation Project, the names have been printed in caps + or initial caps. + + + + + + + + + + + + + + + + + + + + This is version 1.1 of the GRAMPS manual. + + + + + + + + + Introduction + + GRAMPS is an acronym for the + Genealogical Research and Analysis Management Programming System. + It was conceived under the concept that most genealogy programs + were designed to provide the researcher the capability to input + information related to a particular family tree. Most of these + programs have allowed for the arranging and storing of information + consistent with the GEDCOM standards. They usually provide a + means for displaying descendant or ancestral relationships by + means of graphical displays, charts, or reports. These may be + augmented with pictures or other media to enhance the data. Most + provide for inputting data on unconnected individuals/families + that may or may not have a relationship to the primary surname + being researched. Various other enhancements may also be provided + in the genealogical program that allows for different degrees of + importing and exporting data from other programs and printing of + the data contained in the various reports. GRAMPS, on the other + hand, attempts to provide all of the common capabilities of these + programs, but, more importantly, to provide a capability not + common to these programs. This is the ability to input any bits + and pieces of information directly into GRAMPS and + rearrange/manipulate any/all data events in the entire data base + (in any order or sequence) to assist the user in doing research, + analysis and correlation with the potential of filling + relationship gaps. In short, a tool that provides a way to input + all your research into one place and do your analysis and + correlation using the speed, power, and accuracy of your computer + instead of pencils and unmanageable reams of paper. + + + To run GRAMPS, select + + Programs + Applications + gramps + + from the Main Menu, or type + gramps on the command line. + + + This document describes version &version; of + GRAMPS. + + + + + + + + Running GRAMPS for the first time. + + The first time you run the program, + GRAMPS will display its Getting Started + screens. + +
+ Getting Started screen, page 1 + + Getting Started screen, page 1 + + + +
+ + GRAMPS will guide you through a few pages + that prompt you for some setup information. The information it requests + includes information about yourself and your preferences. + + + Although GRAMPS requests information about + your, this information is used only so that it can create valid GEDCOM + output files. A valid GEDCOM file requires information about the file"s + creator. If you chose, you may leave the information empty. + +
+ + + Getting Started + + Starting GRAMPS opens the + Main window, shown in . You will be prompted to either open an + existing database, or to create a new + database. GRAMPS requires that a + database always be open. + +
+ GRAMPS Main Window + + GRAMPS Main Window + + + +
+ + Importing data + + If you already have a family file created using another + genealogy program you can import your GEDCOM file into GRAMPS. + To do this you select File + Import Import from + GEDCOM . The GEDCOM + Import box will open. Select New + Database and click the + Browse... button to select your saved + GEDCOM file (filename.ged). Click + OK to select the file and then click + OK to import the file. The + GEDCOM Import Status will tell you what + the importer is doing and a little bit about your file (file + location, which program created it, the version, Encoding, + Number of Families, Number of People, and the Number of Errors). + Once the Importer is done, you can click + Close and start editing/adding to your + file. + + + + Entering Data + + If you have never used a genealogy program or you do not have a + GEDCOM file to import, you can start creating your database + right away. From the main window click the Add + Person button and the Edit + Person dialog will open. Enter in the information + you have on the first person. Start with their general + information (Name, Birth and Death Date/Place) and then move on + to the Names, + Events, + Attributes, + Addresses, Notes, + Gallery, and + Internet tabs and fill in the known + information you have. Some of the information you enter has a + Source button and/or a + Note button. These buttons are there to + add more information (Source button to + add the source of where you acquired the information and the + Note button to add more detail to the + information) + + +
+ + + + + People View + + The People View window is the initial view seen on the main + window. It displays the name, gender, birth date, and death + date of all individuals in the database. At any time, you can + return to this view either by pressing the + People button at the top of the screen, or + by choosing the + + View + People + + entry from the menus. + +
+ People View + + People View + + + +
+ + Selecting and Editing Individuals + + The People View lists the individuals in the database. An + individual can be selected as the active person by clicking on + an entry in the list. Once a person has been selected as the + active person, the person's name appears in the status bar in + the lower left hand corner of the window. + + + Once the active person has been selected, pressing the + Edit Person button will display the + Edit Person dialog allowing you to edit + the individual's personal information. If the Edit + Person button is pressed without an active person + being set, a blank Edit Person dialog is + presented, allowing you to enter a new person. + + + Double-clicking on a entry in the list will set the active + person and bring up the individual in the Edit + Person dialog. + + + Pressing the Add Person button will + display a blank Edit Person dialog, + allowing you to add a new person to the database. + + + If the Delete Person button is pressed, + the active person and all of the personal information related to + the active person are removed from the database. + + + + Applying Filters + + GRAMPS allows you to apply filters to + the People View. When a filter is applied, the People View will + only display the entries matching the filter. All of the entries + remain in the database, but some entries may be temporarily hidden. + + + There are up to three parts to a filter. The first part is the + selection of the filter to be applied. A filter is selected from + the option menu directly above the People View. The second part + is an optional argument. This qualifier provides more specific + information for the filter. Many filters do not require the + argument, and it will not be displayed if it is not needed. If + the argument is required, a text box with a descriptive label + will appear. The third part of the filter is the invert + selection. When this option is selected, + GRAMPS will display the entries that + do not match the filter. + +
+ Filter that requires an argument + + Filter that requires an argument + + + +
+ + A filter is not applied until the Apply + button is pressed. The filter will remain in effect until the + next time the Apply button is pressed. + +
+ + Sorting + + Four columns are shown in the People View display. The entries + in the list can be sorted by three of the fields: Name, Birth + Date, or Death Date. Clicking on the column label will cause + the list to be re-sorted by that column. Arrows on the label + indicate whether the list is sorted by ascending or descending + order. + + + If the list is already sorted by a particular column, clicking + on the same column label will switch the sorting order. For + example, if the list is currently sorted in ascending order by + Name, clicking on the Name column header will re-sort the list + in descending order. + + +
+ + Editing a person's data + + A person's personal information can be edited in the + Edit Person dialog. + + + General Information Tab +
+ General Information Tab + + General Information Tab + + + +
+ + The General Information tab contains the basic information about + the person. This includes the person's name, gender, birth + information, and death information. + + + If images have been associated with the person, the primary + image is displayed on the right side of the window. + +
+ + Alternate Names Tab +
+ Alternate Names Tab + + Alternate Names Tab + + + +
+ + It is possible for people to use more than one name during their + lifetime. These may be legal name changes, or just informal + names. An example would be a person changing his or her name due + to marriage or adoption. GRAMPS + allows multiple alternate names to be specified for each person. + + + The Alternate Names tab allows additional + names to be added or removed from list. Clicking the + Add button allows a new name to be added + to the list. The Edit/View allows the + selected alternate name to be edited. The + Delete button removes the selected name. + +
+ + Events Tab + + The Events tab allows information about + various events in a person's life to be + recorded. GRAMPS provides a list of + common events, but allows you to name an event anything that you + choose. + + + An event consists of the name of an event (such as "Baptism" or + "Education"), a date or date range on which the event occurred, + the place where the event occurred, and a description of the + event. A note or a source may also be attached to the event. + +
+ Events Tab + + Events Tab + + + +
+ + The Event tab displays information about + the currently selected event at the top of the window. Below + this information is a list of the events that have been + previously entered. Clicking on one of the events in the list + selects the event, and displays its information at the top of + the window. + + + An event may be added by clicking the Add + button. This displays a form that allows you to enter the + information about the particular event. The + Edit/View button allows to view or to + alter the information of the currently displayed event. The + Delete button allows you to delete the + currently displayed event. + +
+ + Attributes Tab + + Attributes are similar to events, but are for information items + that do not necessarily have the concept of a place or a + date. An example would be a person's Social Security Number or + national origin. Attributes consist of an attribute name and its + value. + + + Like events, attributes may also have a note, source, privacy + marker, and confidence level associated with them. + +
+ Attributes Tab + + Attributes Tab + + + +
+ + The Attribute tab displays information + about the currently selected attribute at the top of the + window. Below this information is a list of the attributes that + have been previously entered. Clicking on one of the attributes + in the list selects the attribute, and displays its information at + the top of the window. + + + An attribute may be added by clicking the + Add button. This displays a form that + allows you to enter the information about the particular + attribute. The Edit/View button allows to + view or to alter the information of the currently displayed + attribute. The Delete button allows you to + delete the currently displayed attribute. + +
+ + Addresses Tab + + Addresses are used to record information about where a person + has lived. Addresses are different from + GRAMPS' concept of a place. An + address, as GRAMPS sees, it consists + of an equivalent of a mailing address and the date or date range + when the person lived at the address. + + + Like events and attributes, addresses may also have a note, + source, privacy marker, and confidence level associated with + them. + +
+ Addresses Tab + + Addresses Tab + + + +
+ + The Address tab displays information + about the currently selected address at the top of the + window. Below this information is a list of the addresses that + have been previously entered. Clicking on one of the addresses + in the list selects the address, and displays its information at + the top of the window. + + + An address may be added by clicking the + Add button. This displays a form that + allows you to enter the information about the particular + address. The Edit/View button allows to + view or to alter the information of the currently displayed + address. The Delete button allows you to + delete the currently displayed address. + +
+ + Notes Tab + + In addition to the notes that may be attached to any particular + event, attribute, or address, GRAMPS + has a generic note attached to the person. + +
+ Notes Tab + + Notes Tab + + + +
+ + The note window is a free-form edit window, allowing you to + enter any information that you want. + +
+ + Gallery Tab + + The Gallery tab allows you to associate + files (known in GRAMPS as media + objects) with a particular person. These files are typically + images or photographs, but may be of any file type, such as (but + not limited to) sound files and word processing documents. + + + + GRAMPS provides a central repository + for all media objects in the Media View. This allows the same + media object to appear in multiple galleries. Adding a media + object to a gallery actually adds the object to the Media View, + and makes a local reference in the gallery. + + + While each media object can have a note and attributes attached + to it, each gallery can add its own notes and attributes to the + reference in its gallery. This allows media objects to have + global and local properties. For example, a photo of a family + reunion may have many people in it. A global note may describe + the picture in general, identifying the place and date. When + this object is added to a gallery, you can attach a note to the + reference in the gallery adding some specific information, such + as "Aunt Martha is the third person from the right in the + second row". Clicking the Edit + Properties allows you to edit the local properties. + +
+ Local Media Properties + + Local Media Properties + + + +
+ + The first object in the gallery is considered to be the primary + image. If this object is an image, it will appear on the + General Information tab, and will be the + primary image used by report generators. An image can be made + the default at any time by selecting the thumbnail image and + dragging it to the first position in the gallery. In this same + manner, the order of the images can be changed using the same + drag and drop technique. + + + Objects may be added to the gallery in several ways. By clicking + the Add Media Object button, a dialog box + is presented which allows you to choose an object from the file + system. This method adds a new object to the Media View and + creates a reference in the gallery. Objects may also be added + by either dragging and dropping from one gallery to another, or + by dragging from the Media View to a gallery. In this case, a + new media object is not created, but a reference to an existing + media object is made in the gallery, sharing the same media + object between galleries. Finally, new objects may be added to a + gallery and the Media View by dragging and dropping from a file + manager (such as Nautilus or + Konqueror) or a web browser (such as + Galeon, + Mozilla, or + Konqueror) into a gallery. + + + Media objects can be removed from a gallery by clicking the + Delete Media Object button. This action + only removes the reference to the current gallery. It does not + remove the media object from the Media View or from any other + galleries that are referencing it. + + + Right clicking on a selected object brings up a menu. + + Menu options + + View in default viewer + + + Allows you to view an object image using GNOME's default + viewer for the file type. + + + + + Edit with the GIMP + + + Launches the gimp program, + allowing you to edit the image. This option only shows up + if the media object is an image. + + + + + Edit Object Properties + + + Allows you to change the attributes and note attached to the + media object. + + + + + Convert to local copy + + + This option is only displayed if the media is a reference + to a file that is not controlled by + GRAMPS. Selecting the option + causes GRAMPS to make its own + copy of the media object. + + + + +
+ + Internet Tab + + Frequently, information about a person is available on the + internet, frequently on someone else's web site. With multiple + people researching the same family, it is desirable to keep track + of internet sites that contain information about someone in your + database. This allows you to keep track of the web sites you + can periodically check them for any addition information. + +
+ Internet Tab + + Internet Tab + + + +
+ + The Internet tab displays information about + the currently selected internet address at the top of the window. Below + this information is a list of the internet address that have been + previously entered. Clicking on one of the events in the list + selects the event, and displays its information at the top of + the window. + + + Clicking on the internet address displayed at the top part of + the window will cause GRAMPS to attempt + to display the site using the GNOME default browser. + + + An internet address may be added by clicking on the + Add button. This displays a form that + allows you to enter the information about the internet + address. This information consists of the web address (URL) and + a description of the location. The + Edit/View button allows to view or alter + the information of the currently displayed internet address. The + Delete button allows you to delete the + currently displayed internet address. + +
+ + LDS Tab + + If you have chosen to enable support for the LDS (Latter Day Saints) + ordinances, the LDS tab is visible, and can + be selected. This tab allows you to enter specific information used + by the Church of Jesus Christ of Latter Day Saints. + +
+ LDS Tab + + LDS Tab + + + +
+
+
+ + Family View + + The Family View window displays the spouses, parents, and children + of the active person. At any time, you can return to this view + either by pressing the Family button at the + top of the screen, or by choosing the + + View + Family View + + entry from the menus. + +
+ Family View + + Family View + + + +
+ + In the Family View, the family information related to the active + person is displayed. This information falls into two categories: + families in which the person is a child, and families in which the + person is a spouse or parent. + + + Relationships to Parents + + On the right hand side of the window displays the parents of the + active person. By default, a birth relationship is + assumed. GRAMPS supports multiple + family relationships for each person. For example, a person may + have natural birth parents and adopted parents. In this case, an + option menu will appear below the parents names, allowing you to + choose which set of parents you wish to view. + + + Pressing the Add/Edit Parents + allows you to choose the active person's parents and specify the + person's relationship to the parents. + + + Pressing the Delete Parents does not + remove the parents from the database, but instead deletes the + relationship between the active person and the currently displayed + parents. + + + To right of the names of the parents are two "arrow" + buttons. Selecting the button next to the father changes the + father to the active person, and displays the fathers + information in the Family View window. Similarly, selecting the + button next to the mother changes the mother to the active + person. + + + To the left of the parents' names are buttons indicating the + relationship to the active person. These are typically labeled + "Father" and "Mother", but in some cases may simply be labeled + "Parent". Pressing one of these buttons will display the + Edit Person for the corresponding person. + + + + Marriage/Relationship Information + + On the left side of the window, below the active person's name, + is the information related to the person's marriages and + relationships. If the person has one or no relationships, the + spouse will appear within a non-editable text box. If more than + one relationship exists, the text box will be replaced with an + option menu that allows you to select the relationship to view. + + + Between the active person and the relationship information is a + button with two arrows. Pressing this button will exchange the + active person and spouse on the display. The currently displayed + spouse will become the active person, and the family information + on the right hand side of the screen will change to reflect + this. + + + Pressing the Spouse next to the spouse's + name will display the currently displayed spouse's information + in an Edit Person dialog, allowing you + change the information + + + Pressing the Add located below the entry + for the spouse's name allows a new relationship to be + added. This gives you the opportunity to select and existing + person or to add a new person as the new spouse. The type of + relationship can also be specified. All relationship types, + except "Partners" require that the spouses be of opposite + sex. The "Partners" relationship type requires that the spouses + be of the same sex. + + + Pressing the Edit button allows you to + edit the information related to the marriage. The information + includes events, attributes, and images. + + + The Remove button removes the current + spouse from the relationship. If no children exist in the + relationship, the entire relationship is removed. If children + exist in the relationship, the current spouse is removed, + and the children remain in a family with the active person as the + only parent. + + + + Children of a Relationship + + The bottom of the window contains the list of children related + to the active person and the currently selected spouse. Clicking + on an entry in the list makes that child the active child. + + + Clicking the Add New Child creates a new + child and adds him or her as a child of the current + relationship. Clicking the Add Existing + Child allows you to select an existing person and + assign the person as a child of the current + relationship. Clicking the Remove Child + removes the active child from the current relationship, but does + not delete the person from the database. + + + Double clicking on an entry in the list brings up the + Edit Person dialog for the child. + + + You are able to make the selected child the active person by + clicking the arrow button next to the child list. The + highlighted child in the child list becomes the active person. + + +
+ + Pedigree View + + The Pedigree View window displays the active person, the active + person's parents, and the active parent's grandparents in a + somewhat graphical manner. At any time, you can return to this + view either by pressing the Pedigree button at the top of the + screen, or by choosing the + + View + Pedigree + + entry from the menus. + +
+ Pedigree View + + Pedigree View + + + +
+ + Moving the mouse over a displayed name will display additional + information about a person, including their date of birth and date + of death. Double-clicking the box will display the Edit + Person dialog box for the person. Holding down the + Shift key while double clicking will will make that person the + active person. + + + Navigation around the tree can be accomplished several + ways. Clicking on the arrow next to the active person will display + a menu listing the children of the active person. Selecting a + person from this list will change the active person to the + selected child, effectively shifting the pedigree view to the + left, or down one generation. Clicking one of the arrow buttons on + the left side of the screen will make the select either the active + person's father (top button) or mother (bottom button), + effectively shifting the pedigree view to the right, or up one + generation. If the active person does not have any children, then + the button on the left-hand side of the screen will not + appear. Similarly, if the active person does have a father or + mother, the corresponding button on the right-hand side of the + screen will not appear. + + + + As a quick short cut, double clicking on a line between two people + will make the person on the right-hand side of the line the active + person. shows navigation using this + method. When the mouse is over one of the lines connecting + individuals, the line widens and becomes highlighted. In this + case, double clicking on the line would make Hjalmar Smith the + active person. + +
+ + + Source View + + The Source View window displays the different sources that have + been entered into the database. At any time, you can return to + this view either by pressing the Sources + button at the top of the screen, or by choosing the + + View + Sources + + entry from the menus. + +
+ Source list + + Source View + + + +
+ + From this screen you are able to Add and Edit sources. Currently, + deleting of sources is not available. This will be implemented in + a future version. + +
+ + + Place View + + The Place View window displays the different sources that have + been entered into the database. At any time, you can return to + this view either by pressing the Places + button at the top of the screen, or by choosing the + + View + Places + + entry from the menus. + +
+ Place View + + Place View + + + +
+ + From this screen you are able to Add, Edit, and Delete places. + +
+ + + Media View + + The Media View window displays the files associated with the + database. Typically, these files are images, but + GRAMPS allows you to attach any type of + file to the database. GRAMPS refers to + attached files as media objects. You can access the Media View at + any time by either pressing the Media + button at the top of the screen, or by choosing the + + View + Media + + entry from the menus. + +
+ Media View + + Media View + + + +
+ + Media Objects + + Media objects can be either local or external to a + GRAMPS database. If + GRAMPS is told to import an object as + a local object, it will make its own copy of the file in the + database directory. If the object is not imported as a local + object, the original file is used. + + + There are advantages and disadvantages to both methods. If the + object is local, then if the original file is moved or deleted, + then GRAMPS will still have its own + copy. However, this is at the price of having two copies of the + file. If the file is not imported as a local object, then a copy + is not made, saving disk space. However, altering or deleting + the original copy will affect the + GRAMPS database. + + + + Adding a Media Object + + Media objects can be imported in several ways. Adding an object + to any gallery adds the object to the Media View. The gallery is + will actually contain a reference to the object in the Media + View. + + + Objects may also be added using the Add Media + Object button. This will add the object to the Media + View, but not to any gallery. When you select the file to be + added, a preview will be displayed in the preview window. If the + file is an image, the image will be displayed. Otherwise, an + icon representing the file type will be displayed. In the dialog + box, you may choose to either import the object as a local + object, or leave it as an external object. +
+ Add Media Object Dialog Box + + Add Media Object Dialog Box + + + +
+
+ + Finally, you may drag-and-drop an object from either a file + manager or a web browser. If the object is dropped into a + gallery, then a reference is made in the gallery, and the object + appears in the Media View. If the object is dropped directly + into the Media View, then it appears in the Media View, but will + not appear in a gallery. Currently, all objects imported via + drag-and-drop are imported as external (not local) objects. + +
+ + Making a Reference to a Media Object + + Once an object is in the Media View, it is possible to make a + reference to it in any gallery. You may place the object in as + many galleries as you like, and only one copy of the file will + exist. + + + To make a reference to a media object in a gallery, you may + simply drag-and-drop the object from the Media View directly to + a gallery. The object will then appear in the + gallery. Similarly, you may drag-and-drop from one gallery to + another gallery, and a new object reference is created in the + target gallery. + + + + Changing a Media Object's Properties + + Media objects have global and local properties. The title of the + object is a global property. It may only be changed from the + Media View, and it will affect all references. An object also + has a global note and a set of user defined global attributes. + A reference in a gallery may a have a local note and local + attributes as well. All references share the global properties, + but each gallery has its own set of notes a attributes. + + + The global note can be used to provide a general + description. For example, in a family reunion image, you may + wish to use the global note to indicate the place, date, and + occasion of the photograph. In a local note in Aunt Martha's + gallery, you may wish to add a local note indicating that + "Aunt Martha is the third person from the right in the + second row". + + + The global properties may be changed by selecting the + Edit Media Object button. +
+ Edit a Media Object's Global Properties + + Edit a Media Object's Global Properties + + + +
+
+
+
+ + + Bookmarking People + + GRAMPS supports two mechanisms to + quickly find people - the home person and bookmarks. + + + Home Person + + The home person is the default person of the database. Upon + loading the database, GRAMPS will set + the active person to the default person. At any time, clicking + the Home button will return the active + person to the home person. + + + The home person can be set by choosing + + Settings + Set Default Person + . + + + + Bookmarks + + Bookmarks work similar to bookmarks in HTML browsers. They + allow you to quickly jump to a person, making that person the + active person. This allows you to avoid searching for them + every time you want to add/change something in their information. + +
+ Using Bookmarks + + Using Bookmarks + + + +
+ + Choosing + + Bookmarks + Add Bookmark + + adds the current active person to the bookmark list. The person + will then appear in the bookmark list, allowing you to quickly + select the person. + + + Choosing + + Bookmarks + Go to Bookmark + + displays a sub-menu which allows you to choose a person who was + previously bookmarked. Selecting a person from this menu will + make that person the active person. + + + Choosing + + Bookmarks + Edit Bookmarks + + displays a dialog box that allows you to reorder or delete + bookmarks in the list. + +
+ Editing Bookmarks + + Editing Bookmarks + + + +
+
+
+ + + Using Revision Control + + Revision control allows you to keep a history of the changes that + you have made to your database. Instead of needing to keep + multiple sets of back up files, a single revision control database + is maintained. At any point, you can revert back to a previously + saved version. + + + GRAMPS uses the standard + RCS system to handle revisions. + + + Revision control is enabled in the Revision Control tab of the + preferences dialog. Once enabled, every save is logged into the + revision control database. If you have enabled prompting for a + comment, then a dialog box will be displayed on every save asking + you to provide a comment about the changes you have made. +
+ Providing a revision control comment + + Providing a revision control comment + + + +
+
+ + Reverting to a previous version + + If revision control has been enabled, you have the option of + reverting to a previous version of the database. Selecting the + check box will allow you to select a previous version. +
+ Opening a database + + Opening a database + + + +
+
+ + If the check box has be selected, GRAMPS + will display a dialog box that will allow you to choose which version + you would like to view. The dialog box displays the version number, the + date the version was saved, who saved the database, and any comment supplied + when the database was saved. +
+ Choosing a revision + + Choosing a revision + + + +
+
+ + Choosing a previous revision does not replace your current + database. If you do not save the retrieved database, it will not + replace the current version. If for some reason you accidentally + save the retrieved database when you did not want to replace the + current version, you can always use the revision control + mechanism to get back the version you replaced. + + + Revision control is applied only to the database itself, not to + any media objects have been associated with the database. + +
+
+ + + + + Customization + + To change the application settings, select + + Settings + Preferences... + . This opens the + Preferences dialog, shown in . + + + + Preferences Dialog +
+ Preferences Dialog + + Preferences Dialog + + + +
+ + GRAMPS groups is options into + categories visible in the left hand side of the + dialog. Selecting one of these entries will display the + corresponding settings in the right hand side of the dialog. + +
+ + General Database options +
+ General Database options + + General Database Options + + + +
+ + The General Database page contains basic information to + control the operation of GRAMPS. + + + General Database options + + Automatically load last database + + + With this selected it will automatically load your last + database. + + + + + Do not compress XML data file + + + GRAMPS normally compresses its + data file to conserve disk space. If you do not wish to + have the file compressed, selecting this option will cause + GRAMPS to leave the file + uncompressed. This may be desirable if other applications + need to process the generated XML file. + + + + + Autosave interval (minutes) + + + If this value is set to a non-zero value, GRAMPS + will save an autosave file every few minutes, depending on the + value set. If for some reason the execution of GRAMPS + is interrupted, you can recover to the last autosave point. + + + + + Default database directory + + + This value indicates the default directory for loading and saving + databases. + + + + +
+ + Dates and Calendars +
+ Dates and Calendars + + Dates and Calendars + + + +
+ + The Dates and Calendars page allows you to change the + display and entry formats of dates. + + + + + Dates and Calendars options + + Display Formats + + + Allows you to choose your preferences for displaying dates + and names. Options exist for several different date + formats. Names can be displayed with either the given name + or the surname first. This option typically does not + affect lists that are sorted by last name, in which case + the surname is displayed first. + + + + + Entry Formats + + + Numerical date formats can be ambiguous. Some people enter + the day, month, and year (European style), while others + prefer month, day, year (American style). Selecting the + option here informs GRAMPS how + it should interpret numerical dates. + + + + + Calendars + + + GRAMPS can support calendars + other than the typical Gregorian calendar. If enabled, + GRAMPS will display a menu + allowing you to specify the calendar that a date + represents. Calendars currently supported are Gregorian, + Hebrew, Julian, and French Republican. + + + + +
+ + Media Options +
+ Media Options + + Media Options + + + +
+
+ + GRAMPS ID Options +
+ GRAMPS ID Options + + GRAMPS ID Options + + + +
+
+ + Revision Control Options +
+ Revision Control Options + + Revision Control Options + + + +
+
+ + General Display Options +
+ General Display Options + + General Display Options + + + +
+
+ + Tool and Status Bar Options +
+ Tool and Status Bar Options + + Tools and Status Bar Options + + + +
+
+ + List Color Options +
+ List Color Options + + List Color Options + + + +
+
+ + Find Options +
+ Find Options + + Find Options + + + +
+
+ + Report Options +
+ Report Options + + Report Options + + + +
+ + Many of the reports that GRAMPS + produces can be generated in different file formats and + different paper sizes. Selecting a Preferred Output + Format and a Preferred Output + Format tells the report generator your + preferences. It should be noted that a report generator might + not support all possible formats. + +
+ + Researcher Information +
+ Researcher Information + + Researcher Information + + + +
+ + This is where you are able to change the information you entered + when you started GRAMPS for the first time and was asked to + enter in some information. (This information shows up in your + GEDCOM files as being the researcher/author of the file) + +
+ + Data Guessing Options +
+ Data Guessing Options + + Data Guessing Options + + + +
+ + +
+
+ + + + + Generating Reports + + GRAMPS can produce a wide variety of + reports. A new report generator can be written by the user without + modifying the main program. For this reason, there may be more + reports available than are documented by this manual. + +
+ Report Generation Dialog + + Report Generation Dialog + + + +
+ + Unlike many genealogy programs, GRAMPS + does not directly print reports. Instead, + GRAMPS produces reports in formats that + are understood by other programs. These formats include + OpenOffice, AbiWord, PDF, and HTML, among others. This allows the + generated reports to be modified after they are generated, stored + for use at a later time, or e-mailed to another person. + + + After selecting the report you would like generated there are + options you must select. In the Save As option specify your file + name (use /full path/filename to specify a different directory + than in your Default Report Directory preference in the + preferences). The next step is to select the Report Format. + After choosing the Format you can select the style you would like + to use for your report (this does not apply to the HTML format). + You can Add/Edit/Delete a style for that particular report by + clicking the Style Editor button. + Selecting one of those options you can then change the font (font + face, size, color, and options) for each Paragraph Style along + with the Paragraph Options (Alignment, background color, margins, + and borders). Once you are satisfied with the style you are ready + to proceed with the generation of your report. The next step is + to choose the options (if any for that specific report) and then + Choose the Template (for HTML format only) and click OK. Your + report will now be in default report directory (unless otherwise + specified). + + + Using HTML templates + + Many programs exist to convert GEDCOM files into HTML files that + can be viewed in a web browser. Most of these programs generate + HTML files according to their own predefined style. Since most + people have a style that they prefer, they are left with the + option of modifying hundreds of files by hand. + + + To solve this problem, GRAMPS allows + the user to specify a template to be used for generating HTML + files. At the time the report is generated, if HTML is selected + as the target format, the user can select an HTML template to be + used. Since the template is chosen at report generation time, a + different template may be chosen each time, allowing the user to + change the appearance of the generated files at any time. + Nearly any existing HTML file can be used as an HTML template + for GRAMPS. + + + When a file has been established as the HTML template file, + GRAMPS uses the template for each + file that it generates. GRAMPS starts + each file by copying data from the template until it reaches the + HTML comment, which it uses as a marker. At that point, + GRAMPS inserts its data into the + output file. GRAMPS the continues + reading the until it reaches a second comment that tells it to + resume copying from the template. + + + GRAMPS uses the string + <!-- START --> to indicate where it + should start inserting its information, and the string + <!-- STOP --> to indicate where it + should resume copying data from the template. The effect is + that GRAMPS will create a new + document, replacing everything between the <!-- + START --> and <!-- STOP + --> comments with the report information. + + + The comment markers should be at the beginning of a line in the + HTML template file. Adding the comments to an existing HTML + document will not affect the original HTML document in any way. + + + If no HTML template is specified, or if the specified template + cannot be read, GRAMPS will use a + default, predefined template. + +
+ Sample HTML Template Example + +<HTML> +<HEAD> +<TITLE> +This is my Title +</TITLE> +</HEAD> +<BODY BGCOLOR="#FFFFFF"> +<P> +This is a simple template. This text will appear in the HTML output. +</P> +<!-- START --> +<P> +This is where GRAMPS will place its report information. Any +information between the two comments, including this paragraph, +will not appear in the GRAMPS generated output. +</P> +<!-- STOP --> +<P> +This text, since it appears after the stop comment, will also +appear in every GRAMPS generated file. +</P> +</BODY> +</HTML> + +
+
+
+ + + + + Running Tools + + GRAMPS supports standard and user + written tools. These tools can operate on the database to perform + a specified task. + +
+ Tool Selection Dialog + + Tool Selection Dialog + + + +
+ + Analysis and Exploration + + Compare individual events + + + Aids in the analysis of data by allowing the development of + custom filters that can be applied to the database to find + similar events. + + + + + Interactive descendant browser + + + Provides a browsable hierarchy based on the active person. + + + + + + Data Processing + + Check and repair database + + + Checks the database for integrity problems, fixing the + problems that it can. + + + + + Extract information from names + + + Searches the entire database and attempts to extract titles + and nicknames that may be embedded in a person's given name + field. + + + + + Merge people + + + Searches the entire database, looking for individual entries + that may represent the same person. + + + + + Rename personal event types + + + Allows all the events of a certain name to be renamed to a + new name. + + + + + Reorder GRAMPS ID's + + + Reorders the GRAMPS ID's according to GRAMPS' default rules. + + + + + + Utilities + + Generate SoundEx codes + + + Generates SoundEx codes for names. + + + + + Relationship calculator + + + Calculates the relationship between two people. + + + + +
+ + + + + + + Authors + + GRAMPS was written by Don Allingham + (dallingham@users.sourceforge.net). To find more + information about GRAMPS, please visit + the GRAMPS + Web page. + + + This manual was written by Don Allingham + (dallingham@users.sourceforge.net), Larry Allingham + (llkla@erinet.com), and Shawn Ann Griffith + (shawnann1@home.com). + + + + + + + + + + License + + 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. + + + A copy of the GNU General Public License is + included as an appendix to the GNOME Users + Guide. You may also obtain a copy of the + GNU General Public License from the Free + Software Foundation by visiting their Web site or by writing to +
+ Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307 + USA +
+
+
+
diff --git a/gramps2/doc/gramps-manual/C/gramps-manual/index.html b/gramps2/doc/gramps-manual/C/gramps-manual/index.html new file mode 100644 index 000000000..0e1d66245 --- /dev/null +++ b/gramps2/doc/gramps-manual/C/gramps-manual/index.html @@ -0,0 +1,222 @@ + +GRAMPS User Manual

GRAMPS User Manual

Copyright © 2001 by Donald N. Allingham


+ + + + + + +

Introduction

+

GRAMPS is an acronym for the + Genealogical Research and Analysis Management Programming System. + It was conceived under the concept that most genealogy programs + were designed to provide the researcher the capability to input + information related to a particular family tree. Most of these + programs have allowed for the arranging and storing of information + consistent with the GEDCOM standards. They usually provide a + means for displaying descendant or ancestral relationships by + means of graphical displays, charts, or reports. These may be + augmented with pictures or other media to enhance the data. Most + provide for inputting data on unconnected individuals/families + that may or may not have a relationship to the primary surname + being researched. Various other enhancements may also be provided + in the genealogical program that allows for different degrees of + importing and exporting data from other programs and printing of + the data contained in the various reports. GRAMPS, on the other + hand, attempts to provide all of the common capabilities of these + programs, but, more importantly, to provide a capability not + common to these programs. This is the ability to input any bits + and pieces of information directly into GRAMPS and + rearrange/manipulate any/all data events in the entire data base + (in any order or sequence) to assist the user in doing research, + analysis and correlation with the potential of filling + relationship gaps. In short, a tool that provides a way to input + all your research into one place and do your analysis and + correlation using the speed, power, and accuracy of your computer + instead of pencils and unmanageable reams of paper. +

+

To run GRAMPS, select + ->Programs+ ++ ->Applications+ ++ ->gramps+ ++ + + + + + + from the Main Menu, or type + gramps on the command line. +

+

This document describes version 0.7.3 of + GRAMPS. +

+
+ + + + + + + + + + + +

  Next >>>
  Running GRAMPS for the first time.
\ No newline at end of file diff --git a/gramps2/doc/gramps-manual/C/gramps-manual/ln7.html b/gramps2/doc/gramps-manual/C/gramps-manual/ln7.html new file mode 100644 index 000000000..603ce59c6 --- /dev/null +++ b/gramps2/doc/gramps-manual/C/gramps-manual/ln7.html @@ -0,0 +1,133 @@ + +
GRAMPS User Manual

Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation + License, Version 1.1 or any later version + published by the Free Software Foundation with no Invariant Sections, + no Front-Cover Texts, and no Back-Cover Texts. A copy of the license + can be found here. +

+

Many of the names used by companies to distinguish their products and + services are claimed as trademarks. Where those names appear in any + GNOME documentation, and those trademarks are made aware to the members + of the GNOME Documentation Project, the names have been printed in caps + or initial caps. +

+


 Home 
 Up 
\ No newline at end of file diff --git a/gramps2/doc/gramps-manual/C/gramps-manual/stylesheet-images/caution.gif b/gramps2/doc/gramps-manual/C/gramps-manual/stylesheet-images/caution.gif new file mode 100644 index 0000000000000000000000000000000000000000..54223291154fed2f9639ce2707976ec58f44169b GIT binary patch literal 1039 zcmZ?wbh9u|lwgox_|5Sh{s9!`7`) zTel`}-MV$}UWUDUlh2)FICt*cwQCI5t^vtw_wF&=yJvXsp5?uJPS2k+Jb!NZ{JG`x z=g;52XL$eq{rB$--@gld|1RgdJ(|D$SrA+HAyGpqN^7x16zrMq{!ibMp=6~-(4c99<%wR z=lEzgH#IP+?pT$g`C;jexlBBNo_IO0=v$GV+9Oerpc~DlP;%tKgT`Z#V$4EkL{=v; zaW)#7)%@r_aPX#>5Fh`Y-v{$g32`gkn0k?^DW$2?_JPMq28Kof%}^J&9pNUYcvyH^ zranA$=y~&6-RTjl4}Cj!qXy}<-Pfit2&px)3vi@}Ecoi+!6qxs r(ZS&8c(6lQJMIjJqGMAJzuvhVhQ!S+Y#bZ9V$SZ``nrvgk--`Oh`uWh literal 0 HcmV?d00001 diff --git a/gramps2/doc/gramps-manual/C/gramps-manual/stylesheet-images/important.gif b/gramps2/doc/gramps-manual/C/gramps-manual/stylesheet-images/important.gif new file mode 100644 index 0000000000000000000000000000000000000000..8793ff2b1fe6ff180580d1f263b157a64934137c GIT binary patch literal 1081 zcmeH`!AlfT0LI^{*tG3oR-;A|YnLrqUC32LCk@;w+gcQ35{x-%&ki*qObA0rgpfEP zNkSw-AR!7N#PfjXv7VRoJi+rkN?A%dN|TiGlx8TUz7Kq#@qO0!BmRH>r3Vsw2JE>2 zm_OhhQ+xjA{Zy`^x}o%D+QinvT2n3`8xZIFU!2Jm zCibJ@!slUgUGME#Ebw1WR0r9yQfVQ3>|s^wfCxZWN}THL422hC6`5zjr-#7krF3w- z{bK1ubo^RbJ>(N#qtUg^aL?i4n#r=&s&L=H5(@^GHyh73m)2i4?+XNCe=6%bchZ4C z7&~1*-O!NFz@VXc@G8x|gJ5|Hx|&`^AIq@7DufTJL=k*SSS$s)v|q2U0#fW|VLhy+z< zR$&&E6#^R%H8b;b_*e)mU}9q9Vv)(X@FBT{fiEEJLgc~*4<eAwF5u#VYwR?e%Shb+u`oHi>KJaB03@|>=hvC84eAr3KCza2Lm7+W|6 WloX~(95gs~e1fv~yck6V25SH;%O9}- literal 0 HcmV?d00001 diff --git a/gramps2/doc/gramps-manual/C/gramps-manual/stylesheet-images/note.gif b/gramps2/doc/gramps-manual/C/gramps-manual/stylesheet-images/note.gif new file mode 100644 index 0000000000000000000000000000000000000000..45fe0864971e4c352a37ce4df664b00f1e054b7e GIT binary patch literal 1070 zcmV+}1kw9PNk%v~VHf}y0QUd@000010RaL60s{jB1Ox;H1qB8M1_uWR2nYxX2?+`c z3JVJh3=9kk3JMMm4i66x5D*X%5fKs+5)%^>6ciK{6%`g178e&67#J8C85tTH8XFrM z92^`S9UUGX9v>ecARr(iAt53nA|oRsBqSsyB_$>%CMPE+C@3f?DJd!{Dl021EG#T7 zEiEoCE-x=HFfcGNF)=bSGBYzXG&D3dH8nOiHa9mnI5;>tIXOByIy*Z%JUl!-Jv}}? zK0iM{KtMo2K|w-7LPJACL_|bIMMXwNMn^|SNJvOYNl8jdN=r*iOiWBoO-)WtPESuy zP*6}&QBhJ-Qd3h?R8&+|RaI72R##V7SXfwDSy@_IT3cINTwGjTU0q&YUSD5dU|?Wj zVPRroVq;@tWMpJzWo2e&W@l$-XlQ6@X=!R|YHMq2Y;0_8ZEbFDZf|dIaBy&OadC2T za&vQYbaZreb#-=jc6WDoczAeud3kzzdV70&e0+R;eSLm@et&;|fPjF3fq{a8f`fyD zgoK2Jg@uNOhKGlTh=_=ZiHVAeii?YjjEszpjg5|uj*pLzkdTm(k&%*;l9Q8@l$4Z} zm6ev3mY0{8n3$NEnVFiJnwy)OoSdAUot>VZo}ZteprD|kp`oIpqNAguq@<*!rKP5( zrl+T;sHmu^si~@}s;jH3tgNi9t*x%EuCK4Ju&}VPv9YqUva_?Zw6wIfwY9dkwzs#p zxVX5vxw*Q!y1To(yu7@dCU$jHda z$;ryf%FD~k%*@Qq&CSlv&d<-!(9qD)(b3Y<($mw^)YR0~)z#M4*4Nk9*x1lt)=I7_<=;-L_>FMg~>g((4 z?Ck9A?d|UF?(gsK@bK{Q@$vHV^7Hfa^z`)g_4W4l_V@Sq`1ttw`T6?#`uqF){QUg= z{r&#_{{R2~EC2ui02lxm000R70RIUbNbsCNg9H;Q6xgd2rBVHES(3_Ku{tyHgqN;d+?Tzju!Z);;TuQDVJwRQAHSkR)YFdrEO!v&XBk zJwOwl{a4GHz*-^KY@Rd5rovJavBo?tB{Lyo6k9@$D)OJx<0pIB#+rEO&f%|l4*kcd o_`qJ2pi}&6ogi$&oxc>r#EMbwId40)HiwBGBuL!a2L=QHJ03R*g8%>k literal 0 HcmV?d00001 diff --git a/gramps2/doc/gramps-manual/C/gramps-manual/stylesheet-images/prev.gif b/gramps2/doc/gramps-manual/C/gramps-manual/stylesheet-images/prev.gif new file mode 100644 index 0000000000000000000000000000000000000000..0894d9ecb26316a61e914879c48520cc8aee392d GIT binary patch literal 944 zcmZ?wbh9u|RA%63_|Cw<(83_lBcRx$s5wQ`aE_tV94D`(UO{VvvbSaz?=5cG+tPEa zXUe%LOV2G`du{Epd&kZ_KX>i@wde1jzyJRJ|0oy@fgv6OI>0Qz0Llvt98(!IIb=LG zEI8QAA*>a1V#7k`W+6F`nhzV2ni%sMgrT8{g_D_uZNY^D443EGIST1`39V>a$f&~eRijiP ybPlVqjKR$hN{*ZiRYDmKlawRc1?88Ba5ye<;N%wZnBfq<^#1+^W@{b}25SIMS`<+L literal 0 HcmV?d00001 diff --git a/gramps2/doc/gramps-manual/C/gramps-manual/stylesheet-images/tip.gif b/gramps2/doc/gramps-manual/C/gramps-manual/stylesheet-images/tip.gif new file mode 100644 index 0000000000000000000000000000000000000000..0258195484893202b233c111424f6f6812eacb47 GIT binary patch literal 1029 zcmV+g1p50&Nk%v~VHp4z0QUd@000010RaL60s{jB1Ox;H1qB8M1_uWR2nYxX2?+`c z3JVJh3=9kk3JMMm4i66x5D*X%5fKs+5)%^>6ciK{6%`g178e&67#J8C85tTH8XFrM z92^`S9UUGX9v>ecARr(iAt53nA|oRsBqSsyB_$>%CMPE+C@3f?DJd!{Dl021EG#T7 zEiEoCE-x=HFfcGNF)=bSGBYzXG&D3dH8nOiHa9mnI5;>tIXOByIy*Z%JUl!-Jv}}? zK0iM{KtMo2K|w-7LPJACL_|bIMMXwNMn^|SNJvOYNl8jdN=r*iOiWBoO-)WtPESuy zP*6}&QBhJ-Qd3h?R8&+|RaI72R##V7SXfwDSy@_IT3cINTwGjTU0q&YUSD5dU|?Wj zVPRroVq;@tWMpJzWo2e&W@l$-XlQ6@X=!R|YHMq2Y;0_8ZEbFDZf|dIaBy&OadC2T za&vQYbaZreb#-=jc6WDoczAeud3kzzdV70&e0+R;eSLm@et&;|fPjF3fq{a8f`fyD zgoK2Jg@uNOhKGlTh=_=ZiHVAeii?YjjEszpjg5|uj*pLzkdTm(k&%*;l9Q8@l$4Z} zm6ev3mY0{8n3$NEnVFiJnwy)OoSdAUot>VZo}ZteprD|kp`oIpqNAguq@<*!rKP5( zrl+T;sHmu^si~@}s;jH3tgNi9t*x%EuCK4Ju&}VPv9YqUva_?Zw6wIfwY9dkwzs#p zxVX5vxw*Q!y1To(yu7@dCU$jHda z$;ryf%FD~k%*@Qq&CSlv&d<-!(9qD)(b3Y<($mw^)YR0~)z#M4*4Nk9*x1lt)=I7_<=;-L_>FMg~>g((4 z?Ck9A?d|UF?(gsK@bK{Q@$vHV^7Hfa^z`)g_4W4l_V@Sq`1ttw`T6?#`uqF){QUg= z{r&#_{{R2~EC2ui02u%n000Q;0RIUbNbsCNg9H;Q6nM^9DN3axLUeX-VZwX8CZ4;u zu^}Z&&MY#7=uMQzzXzjLK(DOA}@A62gx007pM9e9q?%bp7<-HvA!GFEE{U`SDUWKJX{uk@&whU_+wL7l%xqVSOWl-?jltH literal 0 HcmV?d00001 diff --git a/gramps2/doc/gramps-manual/C/gramps-manual/stylesheet-images/toc-minus.gif b/gramps2/doc/gramps-manual/C/gramps-manual/stylesheet-images/toc-minus.gif new file mode 100644 index 0000000000000000000000000000000000000000..40ebe61e46a6bd1d8c2c5292af72365d489c6c56 GIT binary patch literal 843 zcmZ?wbhEHb~0HBcxg#Z8m literal 0 HcmV?d00001 diff --git a/gramps2/doc/gramps-manual/C/gramps-manual/stylesheet-images/toc-plus.gif b/gramps2/doc/gramps-manual/C/gramps-manual/stylesheet-images/toc-plus.gif new file mode 100644 index 0000000000000000000000000000000000000000..3e9e7d55a3dde56cd0f6596f3cd6d555323ab273 GIT binary patch literal 846 zcmZ?wbhEHb9$=O@8TlTi}9P62KZpzks zTaVp4cJBGPYwxc;fB*db_xJxt!Dt8!mk`haW&s9JUSQy;XHe#l@z}87U^9oXR?LZl z2hFTJQZ5+>5*S|7To{< literal 0 HcmV?d00001 diff --git a/gramps2/doc/gramps-manual/C/gramps-manual/stylesheet-images/warning.gif b/gramps2/doc/gramps-manual/C/gramps-manual/stylesheet-images/warning.gif new file mode 100644 index 0000000000000000000000000000000000000000..9c1104c2b15445dd0824fd6515659ee0fe18bfd9 GIT binary patch literal 1052 zcmeHG&1=(e0DVo%7HbcgOo>=$KTg3N?8@v)L4uu*Wm2|w7mJ7m-NYeQ<`fNst{-gH zFn0)2C7>b!DFnRyWC)cilI^esda&T7hYgk~Lh8jLf;0XP9`|@W-t9deyphrKLy-&% z79D^F7}yqmfCECnSwH}iKpL00umtG6h$SORMKfh6jeZtMpZ#ji$ztIRaMmh zEfmsZnE-_2h$In3A*xCUsR1=js}Z6DI>YEJtLwS}7z|^utif?jplNHf*#cUvmI;`C zziFBlV9~T?S$5k_r?Us_?PGs||Fl3qZ2bEI=s&nmd)MP#Qi1N-@~vmDl{m)~TAi))WhOkCPn3Z5%1`%*jnZAb7!@1gPPg@=K{SYIGf{obyZgePn&I~mRM zIoCk^vfShUy4LNWnD(!*OLB*2uQN=Ym!3v^_4dl=Pj;;Gz#|UMNR;Eq)oWvScO<97 zyK(r@;K@9Ce=cu6i!Z>P|8001xvKE);`Av#$M(C9mQy#DzRgV?^bGMYDqk{u&KaJ3 zU-1=2b{nx{ugB+K65i`gFp1Iaf%x&j9n1JZecW7F%}mcv#I|oXB2&Z9&%7$#GO4+h QO1&1l=Xra8>T)6W7esUR#{d8T literal 0 HcmV?d00001 diff --git a/gramps2/doc/gramps-manual/C/gramps-manual/t1.html b/gramps2/doc/gramps-manual/C/gramps-manual/t1.html new file mode 100644 index 000000000..0e1d66245 --- /dev/null +++ b/gramps2/doc/gramps-manual/C/gramps-manual/t1.html @@ -0,0 +1,222 @@ + +GRAMPS User Manual

GRAMPS User Manual

Copyright © 2001 by Donald N. Allingham


+ + + + + + +

Introduction

+

GRAMPS is an acronym for the + Genealogical Research and Analysis Management Programming System. + It was conceived under the concept that most genealogy programs + were designed to provide the researcher the capability to input + information related to a particular family tree. Most of these + programs have allowed for the arranging and storing of information + consistent with the GEDCOM standards. They usually provide a + means for displaying descendant or ancestral relationships by + means of graphical displays, charts, or reports. These may be + augmented with pictures or other media to enhance the data. Most + provide for inputting data on unconnected individuals/families + that may or may not have a relationship to the primary surname + being researched. Various other enhancements may also be provided + in the genealogical program that allows for different degrees of + importing and exporting data from other programs and printing of + the data contained in the various reports. GRAMPS, on the other + hand, attempts to provide all of the common capabilities of these + programs, but, more importantly, to provide a capability not + common to these programs. This is the ability to input any bits + and pieces of information directly into GRAMPS and + rearrange/manipulate any/all data events in the entire data base + (in any order or sequence) to assist the user in doing research, + analysis and correlation with the potential of filling + relationship gaps. In short, a tool that provides a way to input + all your research into one place and do your analysis and + correlation using the speed, power, and accuracy of your computer + instead of pencils and unmanageable reams of paper. +

+

To run GRAMPS, select + ->Programs+ ++ ->Applications+ ++ ->gramps+ ++ + + + + + + from the Main Menu, or type + gramps on the command line. +

+

This document describes version 0.7.3 of + GRAMPS. +

+
+ + + + + + + + + + + +

  Next >>>
  Running GRAMPS for the first time.
\ No newline at end of file diff --git a/gramps2/doc/gramps-manual/C/gramps-manual/x129.html b/gramps2/doc/gramps-manual/C/gramps-manual/x129.html new file mode 100644 index 000000000..7c1a96b23 --- /dev/null +++ b/gramps2/doc/gramps-manual/C/gramps-manual/x129.html @@ -0,0 +1,355 @@ + +Editing a person's data
GRAMPS User Manual
<<< Previous 

Editing a person's data

+

A person's personal information can be edited in the + Edit Person dialog. +

+
+ +

Events Tab

+

The Events tab allows information about + various events in a person's life to be + recorded. GRAMPS provides a list of + common events, but allows you to name an event anything that you + choose. +

+

An event consists of the name of an event (such as "Baptism" or + "Education"), a date or date range on which the event occurred, + the place where the event occurred, and a description of the + event. A note or a source may also be attached to the event. +

+
+

The Event tab displays information about + the currently selected event at the top of the window. Below + this information is a list of the events that have been + previously entered. Clicking on one of the events in the list + selects the event, and displays its information at the top of + the window. +

+

An event may be added by clicking the Add + button. This displays a form that allows you to enter the + information about the particular event. The + Edit/View button allows to view or to + alter the information of the currently displayed event. The + Delete button allows you to delete the + currently displayed event. +

+
+

<<< PreviousHome 
People View  
\ No newline at end of file diff --git a/gramps2/doc/gramps-manual/C/gramps-manual/x28.html b/gramps2/doc/gramps-manual/C/gramps-manual/x28.html new file mode 100644 index 000000000..fad114536 --- /dev/null +++ b/gramps2/doc/gramps-manual/C/gramps-manual/x28.html @@ -0,0 +1,181 @@ + +Running GRAMPS for the first time.
GRAMPS User Manual
<<< PreviousNext >>>


<<< PreviousHomeNext >>>
GRAMPS User Manual Getting Started
\ No newline at end of file diff --git a/gramps2/doc/gramps-manual/C/gramps-manual/x41.html b/gramps2/doc/gramps-manual/C/gramps-manual/x41.html new file mode 100644 index 000000000..7f093481a --- /dev/null +++ b/gramps2/doc/gramps-manual/C/gramps-manual/x41.html @@ -0,0 +1,306 @@ + +Getting Started
GRAMPS User Manual
<<< PreviousNext >>>

Getting Started

+

Starting GRAMPS opens the + Main window, shown in . You will be prompted to either open an + existing database, or to create a new + database. GRAMPS requires that a + database always be open. +

+
+ +

Entering Data

+

If you have never used a genealogy program or you do not have a + GEDCOM file to import, you can start creating your database + right away. From the main window click the Add + Person button and the Edit + Person dialog will open. Enter in the information + you have on the first person. Start with their general + information (Name, Birth and Death Date/Place) and then move on + to the Names, + Events, + Attributes, + Addresses, Notes, + Gallery, and + Internet tabs and fill in the known + information you have. Some of the information you enter has a + Source button and/or a + Note button. These buttons are there to + add more information (Source button to + add the source of where you acquired the information and the + Note button to add more detail to the + information) +

+
+

<<< PreviousHomeNext >>>
Running GRAMPS for the first time. People View
\ No newline at end of file diff --git a/gramps2/doc/gramps-manual/C/gramps-manual/x84.html b/gramps2/doc/gramps-manual/C/gramps-manual/x84.html new file mode 100644 index 000000000..321eb135d --- /dev/null +++ b/gramps2/doc/gramps-manual/C/gramps-manual/x84.html @@ -0,0 +1,334 @@ + +People View
GRAMPS User Manual
<<< PreviousNext >>>

People View

+

The People View window is the initial view seen on the main + window. It displays the name, gender, birth date, and death + date of all individuals in the database. At any time, you can + return to this view either by pressing the + People button at the top of the screen, or + by choosing the + + + + + + + + +View+ ++ + + + + + + + ->People+ + ++ + + + + + + entry from the menus. +

+
+

Selecting and Editing Individuals

+

The People View lists the individuals in the database. An + individual can be selected as the active person by clicking on + an entry in the list. Once a person has been selected as the + active person, the person's name appears in the status bar in + the lower left hand corner of the window. +

+

Once the active person has been selected, pressing the + Edit Person button will display the + Edit Person dialog allowing you to edit + the individual's personal information. If the Edit + Person button is pressed without an active person + being set, a blank Edit Person dialog is + presented, allowing you to enter a new person. +

+

Double-clicking on a entry in the list will set the active + person and bring up the individual in the Edit + Person dialog. +

+

Pressing the Add Person button will + display a blank Edit Person dialog, + allowing you to add a new person to the database. +

+

If the Delete Person button is pressed, + the active person and all of the personal information related to + the active person are removed from the database. +

+
+

Applying Filters

+

GRAMPS allows you to apply filters to + the People View. When a filter is applied, the People View will + only display the entries matching the filter. All of the entries + remain in the database, but some entries may be temporarily hidden. +

+

There are up to three parts to a filter. The first part is the + selection of the filter to be applied. A filter is selected from + the option menu directly above the People View. The second part + is an optional argument. This qualifier provides more specific + information for the filter. Many filters do not require the + argument, and it will not be displayed if it is not needed. If + the argument is required, a text box with a descriptive label + will appear. The third part of the filter is the invert + selection. When this option is selected, + GRAMPS will display the entries that + do not match the filter. +

+
+

A filter is not applied until the Apply + button is pressed. The filter will remain in effect until the + next time the Apply button is pressed. +

+
+ +

<<< PreviousHomeNext >>>
Getting Started Editing a person's data
\ No newline at end of file diff --git a/gramps2/doc/gramps-manual/C/index.html b/gramps2/doc/gramps-manual/C/index.html new file mode 100644 index 000000000..0e1d66245 --- /dev/null +++ b/gramps2/doc/gramps-manual/C/index.html @@ -0,0 +1,222 @@ + +GRAMPS User Manual

GRAMPS User Manual

Copyright © 2001 by Donald N. Allingham


+ + + + + + +

Introduction

+

GRAMPS is an acronym for the + Genealogical Research and Analysis Management Programming System. + It was conceived under the concept that most genealogy programs + were designed to provide the researcher the capability to input + information related to a particular family tree. Most of these + programs have allowed for the arranging and storing of information + consistent with the GEDCOM standards. They usually provide a + means for displaying descendant or ancestral relationships by + means of graphical displays, charts, or reports. These may be + augmented with pictures or other media to enhance the data. Most + provide for inputting data on unconnected individuals/families + that may or may not have a relationship to the primary surname + being researched. Various other enhancements may also be provided + in the genealogical program that allows for different degrees of + importing and exporting data from other programs and printing of + the data contained in the various reports. GRAMPS, on the other + hand, attempts to provide all of the common capabilities of these + programs, but, more importantly, to provide a capability not + common to these programs. This is the ability to input any bits + and pieces of information directly into GRAMPS and + rearrange/manipulate any/all data events in the entire data base + (in any order or sequence) to assist the user in doing research, + analysis and correlation with the potential of filling + relationship gaps. In short, a tool that provides a way to input + all your research into one place and do your analysis and + correlation using the speed, power, and accuracy of your computer + instead of pencils and unmanageable reams of paper. +

+

To run GRAMPS, select + ->Programs+ ++ ->Applications+ ++ ->gramps+ ++ + + + + + + from the Main Menu, or type + gramps on the command line. +

+

This document describes version 0.7.3 of + GRAMPS. +

+
+ + + + + + + + + + + +

  Next >>>
  Running GRAMPS for the first time.
\ No newline at end of file diff --git a/gramps2/doc/gramps-manual/C/omf_timestamp b/gramps2/doc/gramps-manual/C/omf_timestamp new file mode 100644 index 000000000..e69de29bb diff --git a/gramps2/doc/gramps-manual/Makefile.am b/gramps2/doc/gramps-manual/Makefile.am new file mode 100644 index 000000000..dbed85073 --- /dev/null +++ b/gramps2/doc/gramps-manual/Makefile.am @@ -0,0 +1,4 @@ +# Process this file with automake to produce Makefile.in + +SUBDIRS = C + diff --git a/gramps2/doc/gramps-manual/Makefile.in b/gramps2/doc/gramps-manual/Makefile.in new file mode 100644 index 000000000..563986e5e --- /dev/null +++ b/gramps2/doc/gramps-manual/Makefile.in @@ -0,0 +1,352 @@ +# Makefile.in generated by automake 1.6.3 from Makefile.am. +# @configure_input@ + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# Process this file with automake to produce Makefile.in +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : + +EXEEXT = @EXEEXT@ +OBJEXT = @OBJEXT@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +AMTAR = @AMTAR@ +AWK = @AWK@ +BINSH = @BINSH@ +CC = @CC@ +DEPDIR = @DEPDIR@ +DISABLE_SCROLLKEEPER = @DISABLE_SCROLLKEEPER@ +GNOMEHELP = @GNOMEHELP@ +HAVE_GNOME_CONFIG = @HAVE_GNOME_CONFIG@ +HAVE_JW = @HAVE_JW@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +JW = @JW@ +LANGUAGES = @LANGUAGES@ +LIBS = @LIBS@ +MOFILES = @MOFILES@ +MSGFMT = @MSGFMT@ +P15_INCLUDES = @P15_INCLUDES@ +P20_INCLUDES = @P20_INCLUDES@ +P21_INCLUDES = @P21_INCLUDES@ +P22_INCLUDES = @P22_INCLUDES@ +PACKAGE = @PACKAGE@ +POFILES = @POFILES@ +PYTHON = @PYTHON@ +PYTHON22 = @PYTHON22@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RELEASE = @RELEASE@ +SCROLLKEEPER_CONFIG = @SCROLLKEEPER_CONFIG@ +SCROLLKEEPER_REQUIRED = @SCROLLKEEPER_REQUIRED@ +STRIP = @STRIP@ +VERSION = @VERSION@ +VERSIONSTRING = @VERSIONSTRING@ +ZIP = @ZIP@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ + +SUBDIRS = C +subdir = doc/gramps-manual +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_CLEAN_FILES = +DIST_SOURCES = + +RECURSIVE_TARGETS = info-recursive dvi-recursive install-info-recursive \ + uninstall-info-recursive all-recursive install-data-recursive \ + install-exec-recursive installdirs-recursive install-recursive \ + uninstall-recursive check-recursive installcheck-recursive +DIST_COMMON = Makefile.am Makefile.in +DIST_SUBDIRS = $(SUBDIRS) +all: all-recursive + +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu doc/gramps-manual/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe) +uninstall-info-am: + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @set fnord $$MAKEFLAGS; amf=$$2; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @set fnord $$MAKEFLAGS; amf=$$2; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done + +ETAGS = etags +ETAGSFLAGS = + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -f $$subdir/TAGS && tags="$$tags -i $$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$tags$$unique" \ + || $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @list='$(DISTFILES)'; for file in $$list; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkinstalldirs) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d $(distdir)/$$subdir \ + || mkdir $(distdir)/$$subdir \ + || exit 1; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" \ + distdir=../$(distdir)/$$subdir \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: + +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-recursive + +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-recursive + +dvi-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic + +uninstall-am: uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) GTAGS all all-am check check-am clean \ + clean-generic clean-recursive distclean distclean-generic \ + distclean-recursive distclean-tags distdir dvi dvi-am \ + dvi-recursive info info-am info-recursive install install-am \ + install-data install-data-am install-data-recursive \ + install-exec install-exec-am install-exec-recursive \ + install-info install-info-am install-info-recursive install-man \ + install-recursive install-strip installcheck installcheck-am \ + installdirs installdirs-am installdirs-recursive \ + maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-generic \ + mostlyclean-recursive tags tags-recursive uninstall \ + uninstall-am uninstall-info-am uninstall-info-recursive \ + uninstall-recursive + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/gramps2/doc/gramps.1 b/gramps2/doc/gramps.1 new file mode 100644 index 000000000..326bc4e93 --- /dev/null +++ b/gramps2/doc/gramps.1 @@ -0,0 +1,49 @@ +.TH gramps 1 "0.9.0pre1" "man page by Brandon L. Griffith" "" +.SH "NAME" +.LP +gramps \- Genealogical Research and Analysis Management Programming System +.SH "SYNTAX" +.LP +gramps [\fIDatabase\fP] +.br +Currently \fBgramps\fR does not require any command line arguments. +However, if provided, it is the path to either a gramps database or a GEDCOM +file to be imported. +.SH "DESCRIPTION" +.LP +Gramps is an open source genealogy program. It is written in Python, using the GTK/GNOME interface. +.br +Gramps should seem familiar to anyone who has used other geneology programs before such as \fIFamily Tree Maker for Windows(TM)\fR or the GNU Geneweb. +.br +It supports importing of the ever popular GEDCOM format which is used world wide by almost all other geneology software. +.SH "Concepts" +Supports a python\-based plugin system, allowing import and export writers, report generators, tools, and display filters to be added without modification of the main program. +.LP +Data is stored in an gzip'ed XML format +.LP +Instead of generating direct printer output, report generators target other systems, such as \fIOpen Office\fR, \fIAbiWord\fR, HTML or LaTeX to allow the user to modify the format to suit his or her needs. +.SH "FILES" +.LP +\fI${PREFIX}/bin/gramps\fP +.br +\fI${PREFIX}/share/gramps\fP +.br +\fI${HOME}/.gramps\fP +.SH "Authors" +Donald Allingham \fI\fR +.br +\fIhttp://gramps.sourceforge.net\fR +.LP +This manpage was written by: +.br +Brandon L. Griffith \fI\fR +.br +Any ammendants or errors should be reported to him. +.br +It was originally written for inclusion in the Debian GNU/Linux system. +.SH "DOCUMENTATION" +See also the file \fBgramps.sgml\fR +.br +On a Debian system this can be found in \fI/usr/doc/gramps\fR +.br +Or in the doc directory of the official source distribution. diff --git a/gramps2/doc/gramps.1.in b/gramps2/doc/gramps.1.in new file mode 100644 index 000000000..fae3c52a1 --- /dev/null +++ b/gramps2/doc/gramps.1.in @@ -0,0 +1,49 @@ +.TH gramps 1 "@VERSION@" "man page by Brandon L. Griffith" "" +.SH "NAME" +.LP +gramps \- Genealogical Research and Analysis Management Programming System +.SH "SYNTAX" +.LP +gramps [\fIDatabase\fP] +.br +Currently \fBgramps\fR does not require any command line arguments. +However, if provided, it is the path to either a gramps database or a GEDCOM +file to be imported. +.SH "DESCRIPTION" +.LP +Gramps is an open source genealogy program. It is written in Python, using the GTK/GNOME interface. +.br +Gramps should seem familiar to anyone who has used other geneology programs before such as \fIFamily Tree Maker for Windows(TM)\fR or the GNU Geneweb. +.br +It supports importing of the ever popular GEDCOM format which is used world wide by almost all other geneology software. +.SH "Concepts" +Supports a python\-based plugin system, allowing import and export writers, report generators, tools, and display filters to be added without modification of the main program. +.LP +Data is stored in an gzip'ed XML format +.LP +Instead of generating direct printer output, report generators target other systems, such as \fIOpen Office\fR, \fIAbiWord\fR, HTML or LaTeX to allow the user to modify the format to suit his or her needs. +.SH "FILES" +.LP +\fI${PREFIX}/bin/gramps\fP +.br +\fI${PREFIX}/share/gramps\fP +.br +\fI${HOME}/.gramps\fP +.SH "Authors" +Donald Allingham \fI\fR +.br +\fIhttp://gramps.sourceforge.net\fR +.LP +This manpage was written by: +.br +Brandon L. Griffith \fI\fR +.br +Any ammendants or errors should be reported to him. +.br +It was originally written for inclusion in the Debian GNU/Linux system. +.SH "DOCUMENTATION" +See also the file \fBgramps.sgml\fR +.br +On a Debian system this can be found in \fI/usr/doc/gramps\fR +.br +Or in the doc directory of the official source distribution. diff --git a/gramps2/doc/gramps.dtd b/gramps2/doc/gramps.dtd new file mode 100644 index 000000000..6bd69e7c1 --- /dev/null +++ b/gramps2/doc/gramps.dtd @@ -0,0 +1,320 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gramps2/doc/gramps.sgml b/gramps2/doc/gramps.sgml new file mode 100644 index 000000000..3f2beee0d --- /dev/null +++ b/gramps2/doc/gramps.sgml @@ -0,0 +1,793 @@ + +]> + + + + + + + + + +
+ + + gramps User Manual + + 2001 + Donald N. Allingham + + + + + + + + + + + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation + License, Version 1.1 or any later version + published by the Free Software Foundation with no Invariant Sections, + no Front-Cover Texts, and no Back-Cover Texts. A copy of the license + can be found here. + + + Many of the names used by companies to distinguish their products and + services are claimed as trademarks. Where those names appear in any + GNOME documentation, and those trademarks are made aware to the members + of the GNOME Documentation Project, the names have been printed in caps + or initial caps. + + + + + + + + + + + + + + + + + + + + This is version 1.0 of the gramps manual. + + + + + + + + + Introduction + + + gramps is an acronym for the Genealogical Research and Analysis + Management Programming System. It was conceived under the concept + that most genealogy programs were designed to provide the + researcher the capability to input information related to a + particular family tree. Most of these programs have allowed for + the arranging and storing of information consistent with the + GEDCOM standards. They usually provide a means for displaying + descendant or ancestral relationships by means of graphical + displays, charts, or reports. These may be augmented with + pictures or other media to enhance the data. Most provide for + inputting data on unconnected individuals/families that may or may + not have a relationship to the primary surname being researched. + Various other enhancements may also be provided in the + genealogical program that allows for different degrees of + importing and exporting data from other programs and printing of + the data contained in the various reports. gramps, on the other + hand, attempts to provide all of the common capabilities of these + programs, but, more importantly, to provide a capability not + common to these programs. This is the ability to input any bits + and pieces of information directly into gramps and + rearrange/manipulate any/all data events in the entire data base + (in any order or sequence) to assist the user in doing research, + analysis and correlation with the potential of filling + relationship gaps. In short, a tool that provides a way to input + all your research into one place and do your analysis and + correlation using the speed, power, and accuracy of your computer + instead of pencils and unmanageable reams of paper. + + + To run gramps, select + + Programs + Applications + gramps + + from the Main Menu, or type + gramps on the command line. + + + gramps is included in the + gramps package, which is part of the + GNOME desktop environment. This document describes version + &version; of gramps. + + + + + + + + Using gramps + + gramps is a genealogy program. + This section describes basic usage of + gramps. + + + + + Running gramps for the first time. + + This section should discuss the start up druid. + + + + + Main Window + + Starting gramps opens the + Main window, shown in . The window is at first empty. + + +
+ gramps Main Window + + gramps Main Window + + + +
+ +
+
+ + + + Person List + + The Person List window is the initial view seen on the main + window. It displays the name, gender, birth date, and death + date of all individuals in the database. At any time, you can + return to the this view either by pressing the People button at + the top of the screen, or by choosing the + + View + Person List + + entry from the menus. + + + Selecting and Editing Individuals + + The Person List view lists the individuals in the database. A + individual can be selected as the active person by clicking on + an entry in the list. Once a person has been selected as the active + person, the person's name appears in the status bar in the lower + left hand corner of the window. + + + Once the active person has been selected, pressing the Edit + Person button will display the Edit Person dialog allowing you + to edit the individual's personal information. If the Edit + Person button is pressed without an active person being set, a + blank Edit Person dialog is presented, allowing you to enter a + new person. Double-clicking on a entry will set the active + person and bring up the individual in the Edit Person dialog. + + + Pressing the Add Person button will display a blank Edit Person + dialog, allowing you to add a new person to the database. + + + If the Delete Person button is pressed, the active person and + all of the personal information related to the active person are + removed from the database. + + + + Applying Filters + + gramps allows you to apply filters to + the Person List. When a filter is applied, the Person List will + only display the entries matching the filter. All of the entries + remain in the database, but are temporarily hidden. + + + There are three parts to a filter. The first part is the + selection of the filter to be applied. A filter is selected from + the option menu directly above the Person List. The second part + is an option qualifier. This qualifier provides more specific + information for the filter. Many filters do not require the + qualifier, and it will be grayed out if not needed. The third + part of the filter is the invert selection. When this option is + selected, gramps will display the + entries that do not match the filter. + + + A filter is not applied until the Apply button is pressed. The + filter will remain in effect until the next time the Apply + button is pressed. + + + + Sorting + + Four columns are shown in the Person List display. The entries in + the list can be sorted by three of the field: Name, Birth Date, or + Death Date. Clicking on the column label will cause the list to + be resorted by that column. Arrows on the label indicate whether + the list is sort by ascending or descending order. + + + If the list is already sorted by a particular column, clicking on + the same column label will switch sorting order. For example, if + the list is currently sorted in ascending order by Name, clicking + on the Name column header will resort the list in descending order. + + + + + + + Family View + + The Family View window displays the spouses, parents, and children + of the active person. At any time, you can return to the this view + either by pressing the Family button at the top of the screen, or + by choosing the + + View + Family View + + entry from the menus. + + + This section should describe the family view. + + + + + Pedegree View + + The Pedegree View window displays the active person, the active + person's parents, and the active parent's grandparents in a somewhat + graphical manner. At any time, you can return to the this view + either by pressing the Pedegree button at the top of the screen, or + by choosing the + + View + Pedgree + + entry from the menus. + + + This section should describe the pedegree view. + + + + + Source List + + The Source List window displays the different sources which have been + entered into the database. At any time, you can return to the this view + either by pressing the Sources button at the top of the screen, or + by choosing the + + View + Sources + + entry from the menus. + + + This section should describe the source list. + + + + + + + Customization + + To change the application settings, select + + Settings + Preferences... + . This opens the + Preferences dialog, shown in . + + +
+ Preferences Dialog + + Preferences Dialog + + + +
+ + + + +
+ + + + + Generating Reports + + gramps can produce a wide variety of + reports. New report generators can be written by the user without + modifying the main program. For this reason, there may be more + reports available than are documented by this manual + + + Unlike many genealogy programs, gramps + does not directly print reports. Instead, + gramps produces reports in formats that + are understood by other programs. These formats include OpenOffice, + AbiWord, PDF, and HTML, among others. This allows the generated + reports to be modified after they are generated, stored for use + later, or emailed to another person. + + + Using HTML templates + + Many programs exist to convert GEDCOM files into HTML files that + can be viewed in a web browser. Most of these programs generate + HTML files according to their own predefined style. Since most + people have a style that they prefer, they are left with the option + of modifying hundreds of files by hand. + + + To solve this problem, gramps allows the + user to specify a template to be used for generating HTML files. At + the time the report is generated, if HTML is selected as the target + format, the user can select an HTML template to be used. Since the + template is chosen at report generation time, a different template + may be chosen each time, allowing the user to change the appearence + of the generated files at any time. Nearly any existing HTML file + can be used as an HTML template for + gramps. + + + When a file has been established as the HTML template file, + gramps uses the template for each file + that it generates. gramps starts each + file by copying data from the template until it reaches an HTML + comment uses as a marker. At that point, + gramps inserts its data into the output + file. gramps the continues reading the + until it reaches a second comment that tells it to resume copying + from the template. + + + gramps uses the string <!-- + START --> to indicate where it should start inserting + its information, and the string <!-- STOP + --> to indicate where it should resume copying data + from the template. The effect is that + gramps will create a new document, + replacing everything between the <!-- START + --> and <!-- STOP --> comments + with the report information. + + + The comment markers should be at the beginning of a line in the HTML + template file. Adding the comments to an existing HTML document will + not affect the original HTML document in any way. + + + If no HTML template is specified, or if the specified template + cannot be read, gramps will use a + default, predefined template. + + +
+ Sample HTML Template Example + +<HTML> +<HEAD> +<TITLE> +This is my Title +</TITLE> +</HEAD> +<BODY BGCOLOR="#FFFFFF"> +<P> +This is a simple template. This text will appear in the html output. +</P> +<!-- START --> +<P> +This is where gramps will place its report information. Any +information between the two comments, including this paragraph, +will not appear in the gramps generated output. +</P> +<!-- STOP --> +<P> +This text, since it appears after the stop comment, will also +appear in every gramps generated file. +</P> +</BODY> +</HTML> + +
+
+
+ +
+ + + + + Writing Filters + + Users can create their own filters and add them to + gramps. By adding the filter to the + user's private filter directory + (~/.gramps/filters), + the filter will be automatically + recognized the next time that the program is started. + + + Creating a filter + + Filters are written in the python + language. Each filter is initialized with the qualifier string. + The qualifier string passes an additional text string to the + filter. This string can be used to further qualify the filter. + For example, if the filter is used to match names, the qualifier + would be used to provide the name that is being compared against. + + + Each filter is a python class, and should be in its own separate + module (file). The module should consist of the filter class + definition, and three functions — + create, need_qualifier, + and get_name. + + + The create function takes a string as its + only argument, returns a instance of the filter class. The string + argument is the qualifier string used to provide more specific + information. + + + The need_qualifier function takes no + arguments, and returns either a 0 or 1 to indicate if a qualifier + string is needed by the filter. Regardless of what + need_qualifier indicates, a text string is + always passed to the filter and the create + function. The value returned by + need_qualifier indicates to the program + whether or not the qualifier field in the display should be + enabled or disabled. + + + The get_name function is used to provide a + description for the filter. This description is entered into + filter selection menus. If the filter is intended to be used by + others, it should be prepared for internationalization. This is + accomplished by importing the intl module, + add defining _ to be + intl.gettext. The string returned by + get_name should be passed through the + _ function to allow for conversion to the + target langauge. + + + All filters must be derived from the + Filter.Filter class. The + __init__ task may be overridden, but if so, + should call the __init__ function on the + Filter.Filter class. The parent class + provides the variable self.text, which + contains the text string passed as the qualifier. + + + All filter classes must define a match + function. The function takes one argument (other than + self), which is an object of type + Person to compare against. The function + should return a 1 if the person matches the filter, or a zero if + the person does not. + +
+ Sample filter implementation + +import Filter +import string +import intl +_ = intl.gettext + +# class definition + +class SubString(Filter.Filter): + + def match(self,person): + name = person.getPrimaryName().getName() + return string.find(name,self.text) >= 0 + +# module functions + +def get_name(s): + return _("Names that contain a substring") + +def create(text): + return SubString(text) + +def need_qualifier(): + return 1 + +
+
+
+ + + + + Writing Reports + + Users can create their own report generators and add them to + gramps. By adding the report generator + to the user's private filter directory (~/.gramps/plugins), the report + generator filter will be automatically recognized the next time + that the program is started. + + + Creating a report generator + + Like filters, report generators are written in the + python language. Fewer restrictions + are made on report generators than on filters. The report + generator is passed the current gramps + database and the active person. The generator needs to take + special care to make sure that it does not alter the database in + anyway. + + + The function get_name is used to provide the + name of the the report generator. As with a filter definition, + this string should support internationalization via the + intl module. The returned string consists of + two parts, separated by a forward slash. The first part of the + string is the category of the report generator. + gramps uses this part to group similar + reports together in the interface. The second part of the string + is the actual name of the reprot generator, and will be displayed + in the report menu. + + + A report generator module must supply the + report function, and can optionally define + the get_description and + get_xpm_data functions. The + report takes two arguments — a database + (of type RelDataBase) and the currently + selected person (of type Person). The + report is reponsible for generating the + actual report. + + + If the get_description is defined, it is used + to provide a more detailed description of the report. The + description is used to provide the user with more information in + the report selection window. The function takes no arguments, and + should return a text string. + + + If the get_xpm_data is defined, it is used to + provide an graphic logo for the report in the report selection + window. The function takes no arguments, and should return a list + of strings containing the XPM file data. The XPM image should be + 48x48 pixels in size. + +
+ Sample report implementation + +import intl +_ = intl.gettext + +def report(database,person): + ... actual code ... + +def get_description(): + return "A detailed text description of what the report generator does" + +def get_name(): + return _("Category/report name") + +def get_xpm_image(): + return [ + "... XPM image data" + ] + +
+
+ + A little help - Format Interfaces + + gramps provides some help with writing + reports. Several generic python classes exist that aid in the + writing of report generators. These classes provide an abstract + interface for a type of document, such as a drawing, word + processor document, or a spreadsheet. From these core classes, + gramps derives interfaces to various + document formats. This means that by coding to the generic word + processing class (TextDoc), a report + generator can instant access to multiple file formats (such as + HTML, OpenOffice, and AbiWord). + + + This scheme of deriving a output format from a generic base class + also makes it easier to add new formats. Creating a new + derivied class targeting a different format (such as + KWord or + LaTeX) makes it easy for existing + report generators to use the new formats. + + +
+ + + + + Writing Tools + + + + + + + + + + + + + + Known Bugs and Limitations + + This application has no known bugs. + + + + + + + + Authors + + gramps was written by Donald N. Allingham + (donaldallingham@home.com). To find more information about + gramps, please visit the gramps Web + page. + + + + This manual was written by Donald N. Allingham + (donaldallingham@home.com) and Lawrence L. Allingham + (llkla@erinet.com). + + + + + + + + + + + License + + 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. + + + A copy of the GNU General Public License is + included as an appendix to the GNOME Users + Guide. You may also obtain a copy of the + GNU General Public License from the Free + Software Foundation by visiting their Web site or by writing to +
+ Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307 + USA +
+
+
+
+ + + + + + + + + diff --git a/gramps2/doc/sgmldocs.make b/gramps2/doc/sgmldocs.make new file mode 100644 index 000000000..a5e087a5f --- /dev/null +++ b/gramps2/doc/sgmldocs.make @@ -0,0 +1,155 @@ +# To use this template: +# 1) Define: figs, docname, lang, omffile, sgml_ents although figs, +# omffile, and sgml_ents may be empty in your Makefile.am which +# will "include" this one +# 2) Figures must go under figures/ and be in PNG format +# 3) You should only have one document per directory +# +# Note that this makefile forces the directory name under +# $prefix/share/gnome/help/ to be the same as the SGML filename +# of the document. This is required by GNOME. eg: +# $prefix/share/gnome/help/fish_applet/C/fish_applet.sgml +# ^^^^^^^^^^^ ^^^^^^^^^^^ +# Definitions: +# figs A list of screenshots which will be included in EXTRA_DIST +# Note that these should reside in figures/ and should be .png +# files, or you will have to make modifications below. +# docname This is the name of the SGML file: .sgml +# lang This is the document locale +# omffile This is the name of the OMF file. Convention is to name +# it -.omf. +# sgml_ents This is a list of SGML entities which must be installed +# with the main SGML file and included in EXTRA_DIST. +# eg: +# figs = \ +# figures/fig1.png \ +# figures/fig2.png +# docname = scrollkeeper-manual +# lang = C +# omffile=scrollkeeper-manual-C.omf +# sgml_ents = fdl.sgml +# include $(top_srcdir)/help/sgmldocs.make +# dist-hook: app-dist-hook +# + +docdir = $(datadir)/gnome/help/$(docname)/$(lang) + +doc_DATA = index.html + +sgml_files = $(sgml_ents) $(docname).sgml + +omf_dir=$(top_srcdir)/omf-install + +EXTRA_DIST = $(sgml_files) $(doc_DATA) $(omffile) $(figs) + +CLEANFILES = omf_timestamp + +# when doing a distclean, we also want to clear out html files: +CONFIG_CLEAN_FILES = index.html $(docname)/*.html $(docname)/stylesheet-images/*.gif + +all: index.html omf + +omf: omf_timestamp + +omf_timestamp: $(omffile) + -for file in $(omffile); do \ + scrollkeeper-preinstall $(docdir)/$(docname).sgml $$file $(omf_dir)/$$file; \ + done + touch omf_timestamp + +index.html: $(docname)/index.html + -cp $(docname)/index.html . + + +# The weird srcdir trick is because the db2html from the Cygnus RPMs +# cannot handle relative filenames. +# The t1 test is for certain versions of jw that create cryptic +# html pages, o fwhich the index is called "t1". Also, the jw +# script from docbook-utils 0.6.9 does not copy the template +# stylesheet-images directory like the db2html script does, so +# we give it a little help (at least for now) + +$(docname)/index.html: $(docname).sgml + -srcdir=`cd $(srcdir) && pwd`; \ + if test "$(HAVE_JW)" = 'yes' ; then \ + if test -f /usr/share/sgml/docbook/dsssl-stylesheets/images/next.gif ; then \ + mkdir -p $$srcdir/$(docname)/stylesheet-images ; \ + cp /usr/share/sgml/docbook/dsssl-stylesheets/images/*.gif $$srcdir/$(docname)/stylesheet-images/ ; \ + fi; \ + jw -c /etc/sgml/catalog $$srcdir/$(docname).sgml -o $$srcdir/$(docname); \ + else \ + db2html $$srcdir/$(docname).sgml; \ + fi + if test -f $(docname)/t1.html; then \ + cd $(srcdir)/$(docname) && cp t1.html index.html; \ + cd $(srcdir); \ + fi + +$(docname).sgml: $(sgml_ents) + -ourdir=`cd . && pwd`; \ + cd $(srcdir); \ + cp $(sgml_ents) $$ourdir + +app-dist-hook: index.html + -$(mkinstalldirs) $(distdir)/$(docname)/stylesheet-images + -$(mkinstalldirs) $(distdir)/figures + -cp $(srcdir)/$(docname)/*.html $(distdir)/$(docname) + -for file in $(srcdir)/$(docname)/*.css; do \ + basefile=`echo $$file | sed -e 's,^.*/,,'`; \ + cp $$file $(distdir)/$(docname)/$$basefile ; \ + done + -for file in $(srcdir)/$(docname)/stylesheet-images/*.gif; do \ + basefile=`echo $$file | sed -e 's,^.*/,,'`; \ + cp $$file $(distdir)/$(docname)/stylesheet-images/$$basefile ; \ + done + -if [ -e topic.dat ]; then \ + cp $(srcdir)/topic.dat $(distdir); \ + fi + +install-data-am: index.html omf + -$(mkinstalldirs) $(DESTDIR)$(docdir)/stylesheet-images + -$(mkinstalldirs) $(DESTDIR)$(docdir)/figures + -cp $(srcdir)/$(sgml_files) $(DESTDIR)$(docdir) + -for file in $(srcdir)/$(docname)/*.html $(srcdir)/$(docname)/*.css; do \ + basefile=`echo $$file | sed -e 's,^.*/,,'`; \ + $(INSTALL_DATA) $$file $(DESTDIR)$(docdir)/$$basefile; \ + done + -for file in $(srcdir)/figures/*.png; do \ + basefile=`echo $$file | sed -e 's,^.*/,,'`; \ + $(INSTALL_DATA) $$file $(DESTDIR)$(docdir)/figures/$$basefile; \ + done + -for file in $(srcdir)/$(docname)/stylesheet-images/*.gif; do \ + basefile=`echo $$file | sed -e 's,^.*/,,'`; \ + $(INSTALL_DATA) $$file $(DESTDIR)$(docdir)/stylesheet-images/$$basefile; \ + done + -if [ -e $(srcdir)/topic.dat ]; then \ + $(INSTALL_DATA) $(srcdir)/topic.dat $(DESTDIR)$(docdir); \ + fi + +$(docname).ps: $(srcdir)/$(docname).sgml + -srcdir=`cd $(srcdir) && pwd`; \ + db2ps $$srcdir/$(docname).sgml + +$(docname).rtf: $(srcdir)/$(docname).sgml + -srcdir=`cd $(srcdir) && pwd`; \ + db2ps $$srcdir/$(docname).sgml + +uninstall-local: + -for file in $(srcdir)/$(docname)/stylesheet-images/*.gif; do \ + basefile=`echo $$file | sed -e 's,^.*/,,'`; \ + rm -f $(docdir)/stylesheet-images/$$basefile; \ + done + -for file in $(srcdir)/figures/*.png; do \ + basefile=`echo $$file | sed -e 's,^.*/,,'`; \ + rm -f $(docdir)/figures/$$basefile; \ + done + -for file in $(srcdir)/$(docname)/*.html $(srcdir)/$(docname)/*.css; do \ + basefile=`echo $$file | sed -e 's,^.*/,,'`; \ + rm -f $(DESTDIR)$(docdir)/$$basefile; \ + done + -for file in $(sgml_files); do \ + rm -f $(DESTDIR)$(docdir)/$$file; \ + done + -rmdir $(DESTDIR)$(docdir)/stylesheet-images + -rmdir $(DESTDIR)$(docdir)/figures + -rmdir $(DESTDIR)$(docdir) diff --git a/gramps2/gramps.sh.in b/gramps2/gramps.sh.in new file mode 100644 index 000000000..a8ad8d11f --- /dev/null +++ b/gramps2/gramps.sh.in @@ -0,0 +1,48 @@ +#! /bin/sh +# @configure_input@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +prefix=@prefix@ + +export GRAMPSDIR=@datadir@/@PACKAGE@ +export PYTHONPATH=$GRAMPSDIR +export GRAMPSI18N=@prefix@/share/locale + +preload="" + +# +# Mandrake has a tendency to build dynamic libraries without the proper +# dependencies in them. This causes havoc with the python gtk libraries. +# Preload the know problem libraries so that gramps does not fail under +# Mandrake +# +for l in /usr/X11R6/lib/libX11.so +do + if [ -f $l ]; then + preload="$preload $l" + fi +done + +if [ "$preload" != "" ]; then + export LD_PRELOAD="$preload" +fi + +@PYTHON@ $GRAMPSDIR/gramps.py $* diff --git a/gramps2/gramps.spec b/gramps2/gramps.spec new file mode 100644 index 000000000..ae1e06bfa --- /dev/null +++ b/gramps2/gramps.spec @@ -0,0 +1,95 @@ +%define ver 0.9.0pre1 +%define rel rc4 +%define prefix /usr + +Summary: Genealogical Research and Analysis Management Programming System. +Name: gramps +Version: %ver +Release: %rel +Copyright: GPL +Group: Applications/Genealogy +Source: http://download.sourceforge.net/gramps/gramps-%{ver}.tar.gz +BuildRoot: /var/tmp/%{name}-%{version}-root + +URL: http://gramps.sourceforge.net + +Requires: python >= 1.5.2 +Requires: pygnome >= 1.0.53 +Requires: _gladegnomemodule.so +Requires: pyexpat.so + +BuildRequires: scrollkeeper >= 0.1.4 +BuildRequires: automake >= 1.6 +BuildRequires: autoconf >= 2.52 + +%description +gramps (Genealogical Research and Analysis Management Programming +System) is a GNOME based genealogy program supporting a Python +based plugin system. + +%prep +%setup + +%build +if [ ! -f configure ]; then + CFLAGS="$MYCFLAGS" ./autogen.sh $MYARCH_FLAGS --prefix=%prefix +else + CFLAGS="$MYCFLAGS" ./configure $MYARCH_FLAGS --prefix=%prefix +fi + +make + + +%install +rm -rf $RPM_BUILD_ROOT + +make GNOME_DATADIR=$RPM_BUILD_ROOT%{prefix}/share prefix=$RPM_BUILD_ROOT%{prefix} install + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%defattr(-, root, root) + +%doc README COPYING TODO INSTALL + +%{prefix}/bin/gramps + +%{_datadir}/gnome/help/gramps-manual/C/* +%{_datadir}/gnome/help/extending-gramps/C/* + +%{_datadir}/gnome/apps/Applications/gramps.desktop +%{_datadir}/pixmaps/gramps.png +%{_datadir}/locale/*/LC_MESSAGES/gramps.mo + +%{_datadir}/gramps/*.xpm +%{_datadir}/gramps/*.jpg +%{_datadir}/gramps/*.png +%{_datadir}/gramps/*.py +%{_datadir}/gramps/*.pyo +%{_datadir}/gramps/*.glade +%{_datadir}/gramps/*.so +%{_datadir}/gramps/docgen/*.py +%{_datadir}/gramps/docgen/*.pyo +%{_datadir}/gramps/filters/*.py +%{_datadir}/gramps/filters/*.pyo +%{_datadir}/gramps/plugins/*.py +%{_datadir}/gramps/plugins/*.pyo +%{_datadir}/gramps/plugins/*.glade +%{_datadir}/gramps/data/gedcom.xml +%{_datadir}/gramps/data/templates/*.tpkg +%{_datadir}/gramps/data/templates/*.xml + +%{prefix}/man/man1/gramps.1* + +%{_datadir}/omf/gramps + +%post +if which scrollkeeper-update>/dev/null 2>&1; then scrollkeeper-update; fi + +%postun +if which scrollkeeper-update>/dev/null 2>&1; then scrollkeeper-update; fi + +%changelog +* Fri Jun 14 2002 Donald Peterson +- add scrollkeeper dependencies and some file cleanup diff --git a/gramps2/gramps.spec.in b/gramps2/gramps.spec.in new file mode 100644 index 000000000..38b9c751c --- /dev/null +++ b/gramps2/gramps.spec.in @@ -0,0 +1,95 @@ +%define ver @VERSION@ +%define rel @RELEASE@ +%define prefix /usr + +Summary: Genealogical Research and Analysis Management Programming System. +Name: gramps +Version: %ver +Release: %rel +Copyright: GPL +Group: Applications/Genealogy +Source: http://download.sourceforge.net/gramps/gramps-%{ver}.tar.gz +BuildRoot: /var/tmp/%{name}-%{version}-root + +URL: http://gramps.sourceforge.net + +Requires: python >= 1.5.2 +Requires: pygnome >= 1.0.53 +Requires: _gladegnomemodule.so +Requires: pyexpat.so + +BuildRequires: scrollkeeper >= 0.1.4 +BuildRequires: automake >= 1.6 +BuildRequires: autoconf >= 2.52 + +%description +gramps (Genealogical Research and Analysis Management Programming +System) is a GNOME based genealogy program supporting a Python +based plugin system. + +%prep +%setup + +%build +if [ ! -f configure ]; then + CFLAGS="$MYCFLAGS" ./autogen.sh $MYARCH_FLAGS --prefix=%prefix +else + CFLAGS="$MYCFLAGS" ./configure $MYARCH_FLAGS --prefix=%prefix +fi + +make + + +%install +rm -rf $RPM_BUILD_ROOT + +make GNOME_DATADIR=$RPM_BUILD_ROOT%{prefix}/share prefix=$RPM_BUILD_ROOT%{prefix} install + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%defattr(-, root, root) + +%doc README COPYING TODO INSTALL + +%{prefix}/bin/gramps + +%{_datadir}/gnome/help/gramps-manual/C/* +%{_datadir}/gnome/help/extending-gramps/C/* + +%{_datadir}/gnome/apps/Applications/gramps.desktop +%{_datadir}/pixmaps/gramps.png +%{_datadir}/locale/*/LC_MESSAGES/gramps.mo + +%{_datadir}/gramps/*.xpm +%{_datadir}/gramps/*.jpg +%{_datadir}/gramps/*.png +%{_datadir}/gramps/*.py +%{_datadir}/gramps/*.pyo +%{_datadir}/gramps/*.glade +%{_datadir}/gramps/*.so +%{_datadir}/gramps/docgen/*.py +%{_datadir}/gramps/docgen/*.pyo +%{_datadir}/gramps/filters/*.py +%{_datadir}/gramps/filters/*.pyo +%{_datadir}/gramps/plugins/*.py +%{_datadir}/gramps/plugins/*.pyo +%{_datadir}/gramps/plugins/*.glade +%{_datadir}/gramps/data/gedcom.xml +%{_datadir}/gramps/data/templates/*.tpkg +%{_datadir}/gramps/data/templates/*.xml + +%{prefix}/man/man1/gramps.1* + +%{_datadir}/omf/gramps + +%post +if which scrollkeeper-update>/dev/null 2>&1; then scrollkeeper-update; fi + +%postun +if which scrollkeeper-update>/dev/null 2>&1; then scrollkeeper-update; fi + +%changelog +* Fri Jun 14 2002 Donald Peterson +- add scrollkeeper dependencies and some file cleanup diff --git a/gramps2/install-sh b/gramps2/install-sh new file mode 100644 index 000000000..178cdac7d --- /dev/null +++ b/gramps2/install-sh @@ -0,0 +1,250 @@ +#! /bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5 (mit/util/scripts/install.sh). +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# Copyright 1991 by the Massachusetts Institute of Technology +# +# Permission to use, copy, modify, distribute, and sell this software and its +# documentation for any purpose is hereby granted without fee, provided that +# the above copyright notice appear in all copies and that both that +# copyright notice and this permission notice appear in supporting +# documentation, and that the name of M.I.T. not be used in advertising or +# publicity pertaining to distribution of the software without specific, +# written prior permission. M.I.T. makes no representations about the +# suitability of this software for any purpose. It is provided "as is" +# without express or implied warranty. +# +# This script is compatible with the BSD install script, but was written +# from scratch. +# + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/gramps2/py-compile b/gramps2/py-compile new file mode 100755 index 000000000..0429357ab --- /dev/null +++ b/gramps2/py-compile @@ -0,0 +1,84 @@ +#!/bin/sh + +# py-compile - Compile a Python program +# Copyright 2000, 2001 Free Software Foundation, Inc. + +# 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, 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., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# called as "py-compile [--basedir DIR] PY_FILES ... + +# GRAMPS-SPECIFIC COMMENTS: +# This is a modified version of the py-compile script distributed with +# GNU automake 1.6. The only difference is that this file has had the +# normal byte-compiling section removed since Gramps only uses optimized +# versions of byte-compiled code. +if [ -z "$PYTHON" ]; then + PYTHON=python +fi + +basedir= + +case "$1" in + --basedir) + basedir=$2 + shift 2 + ;; + --help) + echo "Usage: py-compile [--basedir DIR] PY_FILES ..." + echo "Byte compile some python scripts. This should be performed" + echo "after they have been moved to the final installation location" + exit 0 + ;; + --version) + echo "py-compile version 0.0" + exit 0 + ;; +esac + +if [ $# = 0 ]; then + echo "No files given to $0" 1>&2 + exit 1 +fi + +# if basedir was given, then it should be prepended to filenames before +# byte compilation. +if [ -z "$basedir" ]; then + trans="path = file" +else + trans="path = os.path.join('$basedir', file)" +fi + +# this will fail for python < 1.5, but that doesn't matter ... +$PYTHON -O -c " +import sys, os, string, py_compile + +files = '''$*''' +print 'Byte-compiling python modules (optimised versions) ...' +for file in string.split(files): + $trans + if not os.path.exists(path) or not (len(path) >= 3 and path[-3:] == '.py'): + continue + print file, + sys.stdout.flush() + py_compile.compile(path) +print" 2>/dev/null || : + + diff --git a/gramps2/src/.cvsignore b/gramps2/src/.cvsignore new file mode 100644 index 000000000..7fba86ca0 --- /dev/null +++ b/gramps2/src/.cvsignore @@ -0,0 +1,5 @@ +Makefile +*.pyc +*.pyo +gramps.strings +glade.c diff --git a/gramps2/src/.thumbnails/logo.png b/gramps2/src/.thumbnails/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..a1bc37cf9969d5505b649772683ae9c3ff63372b GIT binary patch literal 5230 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 literal 0 HcmV?d00001 diff --git a/gramps2/src/.xvpics/family48.png b/gramps2/src/.xvpics/family48.png new file mode 100644 index 0000000000000000000000000000000000000000..5b54f3102e4b9a60a0c432c99937ce07dccc5b10 GIT binary patch literal 2370 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≪V->1r;l#JK$R#kR};Wz#t;q#6i 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 wFY literal 0 HcmV?d00001 diff --git a/gramps2/src/AUTHORS b/gramps2/src/AUTHORS new file mode 100644 index 000000000..afc5ea57a --- /dev/null +++ b/gramps2/src/AUTHORS @@ -0,0 +1,2 @@ +Don Allingham +Don Peterson diff --git a/gramps2/src/AddMedia.py b/gramps2/src/AddMedia.py new file mode 100644 index 000000000..889d90e21 --- /dev/null +++ b/gramps2/src/AddMedia.py @@ -0,0 +1,151 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +""" +Provides the interface to allow a person to add a media object to the database. +""" + +#------------------------------------------------------------------------- +# +# Standard python modules +# +#------------------------------------------------------------------------- +import os + +#------------------------------------------------------------------------- +# +# internationalization +# +#------------------------------------------------------------------------- + +from intl import gettext +_ = gettext + +#------------------------------------------------------------------------- +# +# GTK/Gnome modules +# +#------------------------------------------------------------------------- +from gnome.ui import GnomeErrorDialog +import gtk.glade + +#------------------------------------------------------------------------- +# +# gramps modules +# +#------------------------------------------------------------------------- +import const +import Utils +import RelImage +import RelLib + +#------------------------------------------------------------------------- +# +# AddMediaObject +# +#------------------------------------------------------------------------- +class AddMediaObject: + """ + Displays the Add Media Dialog window, allowing the user to select + a media object from the file system, while providing a description. + """ + + def __init__(self,db,update): + """ + Creates and displays the dialog box + + db - the database in which the new object is to be stored + update - a function to call to update the display + """ + self.db = db + self.glade = gtk.glade.XML(const.imageselFile,"imageSelect") + self.window = self.glade.get_widget("imageSelect") + self.description = self.glade.get_widget("photoDescription") + self.image = self.glade.get_widget("image") + self.file_text = self.glade.get_widget("fname") + self.update = update + self.temp_name = "" + + self.glade.signal_autoconnect({ + "on_savephoto_clicked" : self.on_savephoto_clicked, + "on_name_changed" : self.on_name_changed, + "destroy_passed_object" : Utils.destroy_passed_object + }) + + self.window.editable_enters(self.description) + self.window.show() + + def on_savephoto_clicked(self,obj): + """ + Callback function called with the save button is pressed. + A new media object is created, and added to the database. + """ + filename = self.glade.get_widget("photosel").get_full_path(0) + description = self.description.get_text() + external = self.glade.get_widget("private") + + if os.path.exists(filename) == 0: + msgstr = _("%s is not a valid file name or does not exist.") + GnomeErrorDialog(msgstr % filename) + return + + type = Utils.get_mime_type(filename) + if description == "": + description = os.path.basename(filename) + + mobj = RelLib.Photo() + mobj.setDescription(description) + mobj.setMimeType(type) + self.db.addObject(mobj) + + if external.get_active() == 0: + path = self.db.getSavePath() + name = RelImage.import_media_object(filename,path,mobj.getId()) + mobj.setLocal(1) + else: + name = filename + mobj.setPath(name) + + Utils.modified() + self.update() + Utils.destroy_passed_object(obj) + + def on_name_changed(self,obj): + """ + Called anytime the filename text window changes. Checks to + see if the file exists. If it does, the imgae is loaded into + the preview window. + """ + filename = self.file_text.get_text() + basename = os.path.basename(filename) + (root,ext) = os.path.splitext(basename) + old_title = self.description.get_text() + + if old_title == '' or old_title == self.temp_name: + self.description.set_text(root) + self.temp_name = root + + if os.path.isfile(filename): + type = Utils.get_mime_type(filename) + if type[0:5] == 'image': + image = RelImage.scale_image(filename,const.thumbScale) + self.image.load_imlib(image) + else: + self.image.load_file(Utils.find_icon(type)) diff --git a/gramps2/src/AddSpouse.py b/gramps2/src/AddSpouse.py new file mode 100644 index 000000000..dd06b9892 --- /dev/null +++ b/gramps2/src/AddSpouse.py @@ -0,0 +1,382 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +""" +The AddSpouse module provides the AddSpouse class that allows the user to +add a new spouse to the active person. +""" + +__author__ = "Donald N. Allingham" +__version__ = "$Revision$" + +#------------------------------------------------------------------------- +# +# internationalization +# +#------------------------------------------------------------------------- +from intl import gettext as _ + +#------------------------------------------------------------------------- +# +# GTK/Gnome modules +# +#------------------------------------------------------------------------- +import gobject +import gtk.glade + +#------------------------------------------------------------------------- +# +# gramps modules +# +#------------------------------------------------------------------------- +import RelLib +import const +import Sorter +import Utils +import GrampsCfg + +#------------------------------------------------------------------------- +# +# AddSpouse +# +#------------------------------------------------------------------------- +class AddSpouse: + """ + Displays the AddSpouse dialog, allowing the user to create a new + family with the passed person as one spouse, and another person to + be selected. + """ + def __init__(self,db,person,update,addperson): + """ + Displays the AddSpouse dialog box. + + db - database to which to add the new family + person - the current person, will be one of the parents + update - function that updates the family display + addperson - function that adds a person to the person view + """ + self.db = db + self.update = update + self.person = person + self.addperson = addperson + + self.glade = gtk.glade.XML(const.gladeFile, "spouseDialog") + + self.rel_combo = self.glade.get_widget("rel_combo") + self.relation_type = self.glade.get_widget("rel_type") + self.spouse_list = self.glade.get_widget("spouse_list") + self.model = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, + gobject.TYPE_STRING, gobject.TYPE_STRING, + gobject.TYPE_STRING) + self.spouse_list.set_model(self.model) + self.selection = self.spouse_list.get_selection() + self.selection.connect('changed',self.select_row) + + self.relation_def = self.glade.get_widget("reldef") + self.ok = self.glade.get_widget('spouse_ok') + + colno = 0 + for title in [ (_('Name'),3,200), + (_('ID'),1,50), + (_('Birth Date'),4,50), + ('',0,50), + ('',0,0)]: + renderer = gtk.CellRendererText () + column = gtk.TreeViewColumn (title[0], renderer, text=colno) + colno = colno + 1 + column.set_clickable (gtk.TRUE) + if title[0] == '': + column.set_clickable(gtk.TRUE) + column.set_visible(gtk.FALSE) + else: + column.set_resizable(gtk.TRUE) + column.set_sort_column_id(title[1]) + column.set_min_width(title[2]) + self.spouse_list.append_column(column) + if colno == 1: + column.clicked() + + self.ok.set_sensitive(0) + + self.rel_combo.set_popdown_strings(const.familyRelations) + title = _("Choose Spouse/Partner of %s") % GrampsCfg.nameof(person) + self.glade.get_widget("spouseTitle").set_text(title) + + self.glade.signal_autoconnect({ + "on_select_spouse_clicked" : self.select_spouse_clicked, + "on_new_spouse_clicked" : self.new_spouse_clicked, + "on_rel_type_changed" : self.relation_type_changed, + "destroy_passed_object" : Utils.destroy_passed_object + }) + + self.relation_type.set_text(_("Married")) + self.relation_type_changed(None) + + def select_row(self,obj): + """ + Called with a row has be unselected. Used to ensable the OK button + when a row has been selected. + """ + + model,iter = self.selection.get_selected() + if iter: + self.ok.set_sensitive(1) + else: + self.ok.set_sensitive(0) + + def new_spouse_clicked(self,obj): + """ + Called when the spouse to be added does not exist, and needs + to be created and added to the database + """ + import QuickAdd + + relation = const.save_frel(self.relation_type.get_text()) + if relation == "Partners": + if self.person.getGender() == RelLib.Person.male: + gen = "male" + else: + gen = "female" + elif self.person.getGender() == RelLib.Person.male: + gen = "female" + else: + gen = "male" + + QuickAdd.QuickAdd(self.db,gen,self.update_list) + + def update_list(self,person): + """ + Updates the potential spouse list after a person has been added + to database. Called by the QuickAdd class when the dialog has + been closed. + """ + self.addperson(person) + self.update_data(person.getId()) + + def select_spouse_clicked(self,obj): + """ + Called when the spouse to be added already exists and has been + selected from the list. + """ + + model,iter = self.selection.get_selected() + if not iter: + return + + row = model.get_path(iter) + id = self.entries[row[0]] + spouse = self.db.getPerson(id) + + # don't do anything if the marriage already exists + for f in self.person.getFamilyList(): + if spouse == f.getMother() or spouse == f.getFather(): + Utils.destroy_passed_object(obj) + return + + Utils.modified() + family = self.db.newFamily() + self.person.addFamily(family) + spouse.addFamily(family) + + if self.person.getGender() == RelLib.Person.male: + family.setMother(spouse) + family.setFather(self.person) + else: + family.setFather(spouse) + family.setMother(self.person) + + family.setRelationship(const.save_frel(self.relation_type.get_text())) + Utils.destroy_passed_object(obj) + self.update(family) + + def relation_type_changed(self,obj): + self.update_data() + + def update_data(self,person = None): + """ + Called whenever the relationship type changes. Rebuilds the + the potential spouse list. + """ + + text = self.relation_type.get_text() + self.relation_def.set_text(const.relationship_def(text)) + + # determine the gender of the people to be loaded into + # the potential spouse list. If Partners is selected, use + # the same gender as the current person. + gender = self.person.getGender() + if text == _("Partners"): + if gender == RelLib.Person.male: + sgender = const.female + else: + sgender = const.male + else: + if gender == RelLib.Person.male: + sgender = const.male + else: + sgender = const.female + + index = 0 + + self.entries = [] + self.model.clear() + for key in self.db.getPersonKeys(): + data = self.db.getPersonDisplay(key) + if data[2] == sgender: + continue + iter = self.model.append() + self.entries.append(key) + self.model.set(iter,0,data[0],1,data[1],2,data[3],3,data[5],4,data[6]) + if person == key: + self.selection.select_iter(iter) + +#------------------------------------------------------------------------- +# +# SetSpouse +# +#------------------------------------------------------------------------- +class SetSpouse: + """ + Displays the AddSpouse dialog, allowing the user to create a new + family with the passed person as one spouse, and another person to + be selected. + """ + def __init__(self,db,person,family,update,addperson): + """ + Displays the AddSpouse dialog box. + + db - database to which to add the new family + person - the current person, will be one of the parents + update - function that updates the family display + addperson - function that adds a person to the person view + """ + self.db = db + self.update = update + self.person = person + self.family = family + self.addperson = addperson + + self.glade = gtk.glade.XML(const.gladeFile, "spouseDialog") + + self.rel_combo = self.glade.get_widget("rel_combo") + self.relation_type = self.glade.get_widget("rel_type") + self.spouse_list = self.glade.get_widget("spouseList") + self.relation_def = self.glade.get_widget("reldef") + + self.rel_combo.set_popdown_strings(const.familyRelations) + title = _("Choose Spouse/Partner of %s") % GrampsCfg.nameof(person) + self.glade.get_widget("spouseTitle").set_text(title) + + self.glade.signal_autoconnect({ + "on_select_spouse_clicked" : self.select_spouse_clicked, + "on_new_spouse_clicked" : self.new_spouse_clicked, + "on_rel_type_changed" : self.relation_type_changed, + "on_combo_insert_text" : Utils.combo_insert_text, + "destroy_passed_object" : Utils.destroy_passed_object + }) + + self.relation_type.set_text(_("Married")) + + def new_spouse_clicked(self,obj): + """ + Called when the spouse to be added does not exist, and needs + to be created and added to the database + """ + import QuickAdd + + relation = const.save_frel(self.relation_type.get_text()) + if relation == "Partners": + if self.person.getGender() == RelLib.Person.male: + gen = "male" + else: + gen = "female" + elif self.person.getGender() == RelLib.Person.male: + gen = "female" + else: + gen = "male" + QuickAdd.QuickAdd(self.db,gen,self.update_list) + + def update_list(self,person): + self.addperson(person) + self.relation_type_changed(self.relation_type) + + def select_spouse_clicked(self,obj): + """ + Called when the spouse to be added already exists and has been + selected from the list. + """ + if len(self.spouse_list.selection) == 0: + return + row = self.spouse_list.selection[0] + spouse = self.db.getPerson(self.spouse_list.get_row_data(row)) + + # don't do anything if the marriage already exists + for f in self.person.getFamilyList(): + if spouse == f.getMother() or spouse == f.getFather(): + Utils.destroy_passed_object(obj) + return + + Utils.modified() + if self.family.getFather() == self.person: + self.family.setMother(spouse) + else: + self.family.setFather(spouse) + + spouse.addFamily(self.family) + + reltype = self.relation_type.get_text() + self.family.setRelationship(const.save_frel(reltype)) + Utils.destroy_passed_object(obj) + self.update(self.family) + + def relation_type_changed(self,obj): + """ + Called whenever the relationship type changes. Rebuilds the + the potential spouse list. + """ + text = obj.get_text() + self.relation_def.set_text(const.relationship_def(text)) + + # determine the gender of the people to be loaded into + # the potential spouse list. If Partners is selected, use + # the same gender as the current person. + gender = self.person.getGender() + if text == _("Partners"): + if gender == RelLib.Person.male: + sgender = const.female + else: + sgender = const.male + else: + if gender == RelLib.Person.male: + sgender = const.male + else: + sgender = const.female + + index = 0 + self.spouse_list.clear() + self.spouse_list.freeze() + for key in self.db.getPersonKeys(): + data = self.db.getPersonDisplay(key) + if data[2] == sgender: + continue + + self.spouse_list.append([data[0],data[3],data[5],data[6]]) + self.spouse_list.set_row_data(index,key) + index = index + 1 + self.spouse_list.thaw() diff --git a/gramps2/src/AddrEdit.py b/gramps2/src/AddrEdit.py new file mode 100644 index 000000000..3ae80f316 --- /dev/null +++ b/gramps2/src/AddrEdit.py @@ -0,0 +1,157 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +""" +The AddrEdit module provides the AddressEditor class. This provides a +mechanism for the user to edit address information. +""" + +#------------------------------------------------------------------------- +# +# GTK/Gnome modules +# +#------------------------------------------------------------------------- +import gtk.glade + +#------------------------------------------------------------------------- +# +# gramps modules +# +#------------------------------------------------------------------------- +import const +import Utils +import Date +import RelLib +import Sources + +from DateEdit import DateEdit +from intl import gettext +_ = gettext + +#------------------------------------------------------------------------- +# +# AddressEditor class +# +#------------------------------------------------------------------------- +class AddressEditor: + """ + Displays a dialog that allows the user to edit an address. + """ + def __init__(self,parent,addr): + """ + Displays the dialog box. + + parent - The class that called the Address editor. + addr - The address that is to be edited + """ + # Get the important widgets from the glade description + self.top = gtk.glade.XML(const.dialogFile, "addr_edit") + self.window = self.top.get_widget("addr_edit") + self.addr_start = self.top.get_widget("address_start") + self.street = self.top.get_widget("street") + self.city = self.top.get_widget("city") + self.state = self.top.get_widget("state") + self.country = self.top.get_widget("country") + self.postal = self.top.get_widget("postal") + self.note_field = self.top.get_widget("addr_note") + self.priv = self.top.get_widget("priv") + self.slist = self.top.get_widget("slist") + + self.parent = parent + self.addr = addr + + name = parent.person.getPrimaryName().getName() + text = _("Address Editor for %s") % name + self.top.get_widget("addrTitle").set_text(text) + + if self.addr: + self.srcreflist = self.addr.getSourceRefList() + self.addr_start.set_text(self.addr.getDate()) + self.street.set_text(self.addr.getStreet()) + self.city.set_text(self.addr.getCity()) + self.state.set_text(self.addr.getState()) + self.country.set_text(self.addr.getCountry()) + self.postal.set_text(self.addr.getPostal()) + self.priv.set_active(self.addr.getPrivacy()) + self.note_field.set_point(0) + self.note_field.insert_defaults(self.addr.getNote()) + self.note_field.set_word_wrap(1) + else: + self.srcreflist = [] + + self.sourcetab = Sources.SourceTab(self.srcreflist,self.parent, + self.top, self.slist, + self.top.get_widget('add_btn'), + self.top.get_widget('del_btn')) + + date_stat = self.top.get_widget("date_stat") + self.date_check = DateEdit(self.addr_start,date_stat) + + self.top.signal_autoconnect({ + "destroy_passed_object" : Utils.destroy_passed_object, + "on_addr_edit_ok_clicked" : self.ok_clicked, + }) + + def ok_clicked(self,obj): + """ + Called when the OK button is pressed. Gets data from the + form and updates the Address data structure. + """ + date = self.addr_start.get_text() + street = self.street.get_text() + city = self.city.get_text() + state = self.state.get_text() + country = self.country.get_text() + postal = self.postal.get_text() + note = self.note_field.get_chars(0,-1) + priv = self.priv.get_active() + + if self.addr == None: + self.addr = RelLib.Address() + self.parent.plist.append(self.addr) + self.addr.setSourceRefList(self.srcreflist) + + self.update(date,street,city,state,country,postal,note,priv) + self.parent.redraw_addr_list() + Utils.destroy_passed_object(obj) + + def check(self,get,set,data): + """Compares a data item, updates if necessary, and sets the + parents lists_changed flag""" + if get() != data: + set(data) + self.parent.lists_changed = 1 + + def update(self,date,street,city,state,country,postal,note,priv): + """Compares the data items, and updates if necessary""" + d = Date.Date() + d.set(date) + + if self.addr.getDate() != d.getDate(): + self.addr.setDate(date) + self.parent.lists_changed = 1 + + self.check(self.addr.getStreet,self.addr.setStreet,street) + self.check(self.addr.getCountry,self.addr.setCountry,country) + self.check(self.addr.getCity,self.addr.setCity,city) + self.check(self.addr.getState,self.addr.setState,state) + self.check(self.addr.getPostal,self.addr.setPostal,postal) + self.check(self.addr.getNote,self.addr.setNote,note) + self.check(self.addr.getPrivacy,self.addr.setPrivacy,priv) + diff --git a/gramps2/src/AttrEdit.py b/gramps2/src/AttrEdit.py new file mode 100644 index 000000000..a0dc436f8 --- /dev/null +++ b/gramps2/src/AttrEdit.py @@ -0,0 +1,151 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +""" +The AttrEdit module provides the AddressEditor class. This provides a +mechanism for the user to edit address information. +""" + +__author__ = "Donald N. Allingham" +__version__ = "$Revision$" + +#------------------------------------------------------------------------- +# +# GTK/Gnome modules +# +#------------------------------------------------------------------------- +import gtk.glade + +#------------------------------------------------------------------------- +# +# gramps modules +# +#------------------------------------------------------------------------- +import const +import Utils +import Sources +import AutoComp + +from RelLib import Attribute + +from intl import gettext as _ + +#------------------------------------------------------------------------- +# +# AttributeEditor class +# +#------------------------------------------------------------------------- +class AttributeEditor: + """ + Displays a dialog that allows the user to edit an attribute. + """ + def __init__(self,parent,attrib,title,list): + """ + Displays the dialog box. + + parent - The class that called the Address editor. + attrib - The attribute that is to be edited + title - The title of the dialog box + list - list of options for the pop down menu + """ + + self.parent = parent + self.attrib = attrib + self.top = gtk.glade.XML(const.dialogFile, "attr_edit") + self.window = self.top.get_widget("attr_edit") + self.type_field = self.top.get_widget("attr_type") + self.slist = self.top.get_widget("slist") + self.value_field = self.top.get_widget("attr_value") + self.note_field = self.top.get_widget("attr_note") + self.attrib_menu = self.top.get_widget("attr_menu") + self.source_field = self.top.get_widget("attr_source") + self.priv = self.top.get_widget("priv") + + if attrib: + self.srcreflist = self.attrib.getSourceRefList() + else: + self.srcreflist = [] + + self.sourcetab = Sources.SourceTab(self.srcreflist,self.parent,self.top, + self.slist, + self.top.get_widget('add_src'), + self.top.get_widget('del_src')) + + title = _("Attribute Editor for %s") % title + self.top.get_widget("attrTitle").set_text(title) + + AutoComp.AutoEntry(self.attrib_menu.entry,list) + + if attrib != None: + self.type_field.set_text(attrib.getType()) + self.value_field.set_text(attrib.getValue()) + self.priv.set_active(attrib.getPrivacy()) + + self.note_field.get_buffer().set_text(attrib.getNote()) + + self.top.signal_autoconnect({ + "destroy_passed_object" : Utils.destroy_passed_object, + "on_attr_edit_ok_clicked" : self.on_ok_clicked, + "on_add_src_clicked" : self.add_source, + "on_del_src_clicked" : self.del_source, + }) + + def add_source(self,obj): + pass + + def del_source(self,obj): + pass + + def on_ok_clicked(self,obj): + """ + Called when the OK button is pressed. Gets data from the + form and updates the Attribute data structure. + """ + type = self.type_field.get_text() + value = self.value_field.get_text() + + buf = self.note_field.get_buffer() + note = buf.get_text(buf.get_start_iter(),buf.get_end_iter(),gtk.FALSE) + priv = self.priv.get_active() + + if self.attrib == None: + self.attrib = Attribute() + self.parent.alist.append(self.attrib) + + self.attrib.setSourceRefList(self.srcreflist) + self.update(type,value,note,priv) + + self.parent.redraw_attr_list() + Utils.destroy_passed_object(obj) + + def check(self,get,set,data): + """Compares a data item, updates if necessary, and sets the + parents lists_changed flag""" + if get() != data: + set(data) + self.parent.lists_changed = 1 + + def update(self,type,value,note,priv): + """Compares the data items, and updates if necessary""" + ntype = const.save_pattr(type) + self.check(self.attrib.getType,self.attrib.setType,ntype) + self.check(self.attrib.getValue,self.attrib.setValue,value) + self.check(self.attrib.getNote,self.attrib.setNote,note) + self.check(self.attrib.getPrivacy,self.attrib.setPrivacy,priv) + diff --git a/gramps2/src/AutoComp.py b/gramps2/src/AutoComp.py new file mode 100644 index 000000000..438ee8605 --- /dev/null +++ b/gramps2/src/AutoComp.py @@ -0,0 +1,264 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2002 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +""" +Adds autocompletion to a GtkEntry box, using the passed list of +strings as the possible completions. This work was adapted from code +written by David Hampton. +""" + +__author__ = "David R. Hampton, Donald N. Allingham" +__version__ = "$Revision$" + +#------------------------------------------------------------------------- +# +# python modules +# +#------------------------------------------------------------------------- +import string + +#------------------------------------------------------------------------- +# +# GNOME modules +# +#------------------------------------------------------------------------- +import gtk + +#------------------------------------------------------------------------- +# +# AutoCompBase +# +#------------------------------------------------------------------------- +class AutoCompBase: + + def __init__(self,widget,plist,source=None): + """ + Creates a autocompleter for the specified GNOME/GTK widget, using the + list of strings as the completion values. The AutoCompBase class + should never be instantiated on its own. Instead, classes should be + derived from it. + + widget - widget instance the completer is assocated with + plist - List of completion strings + source - If not None, uses the completion values of an already existing AutoCompBase instance + """ + if source: + self.nlist = source.nlist + else: + self.nlist = [] + self.nlist = map((lambda n: (string.lower(n),n)),plist) + self.nlist.sort() + self.nl = "xzsdkdjecsc" + self.l = 0 + + def insert_text(self,entry,new_text,new_text_len,i_dont_care): + """ + Sets up a delayed (0.005 sec) handler for text completion. Text + completion cannot be handled directly in this routine because, for + some reason, the select_region() function doesn't work when called + from signal handlers. Go figure. + + Thanks to iain@nodata.demon.co.uk (in mail from 1999) for the idea + to use a timer to get away from the problems with signal handlers + and the select_region function. + """ + + # One time setup to clear selected region when user moves on + if (not entry.get_data("signal_set")): + entry.set_data("signal_set",1) + entry.connect("focus-out-event", self.lost_focus, entry) + + # Nuke the current timer if the user types fast enough + timer = entry.get_data("timer"); + if (timer): + gtk.timeout_remove(timer) + + # Setup a callback timer so we can operate outside of a signal handler + timer = gtk.timeout_add(5, self.timer_callback, entry) + entry.set_data("timer", timer); + + def lost_focus(self,entry,a,b): + """ + The entry box entry field lost focus. Go clear any selection. Why + this form of a select_region() call works in a signal handler and + the other form doesn't is a mystery. + """ + #entry.select_region(0, 0) + pass + + def timer_callback(self,entry): + """ + Perfroms the actual task of completion. This method should be + overridden in all subclasses + """ + pass + +#------------------------------------------------------------------------- +# +# AutoCombo +# +#------------------------------------------------------------------------- +class AutoCombo(AutoCompBase): + """ + Allows allow completion of the GtkCombo widget with the entries + in the passed string list. This class updates the drop down window + with the values that currently match the substring in the text box. + """ + + def __init__(self,widget,plist,source=None): + """ + Creates a autocompleter for the a GtkCombo widget, using the + list of strings as the completion values. The + + widget - GtkCombo instance the completer is assocated with + plist - List of completion strings + source - If not None, uses the completion values of an already existing + AutoCompBase instance + """ + AutoCompBase.__init__(self,widget,plist,source) + self.entry = widget + widget.entry.connect("insert-text",self.insert_text) + button1 = widget.get_children()[1] + button1.connect("button-press-event",self.build_list) + button1.connect("button-release-event",self.setval) + self.vals = [""] + self.inb = 0 + + def setval(self,widget,event): + """Callback task called on the button release""" + + self.inb = 0 + text = self.entry.entry.get_text() + if self.nl == string.lower(text): + #self.entry.entry.set_position(self.l) + #self.entry.entry.select_region(self.l, -1) + pass + + def build_list(self,widget,event): + """Internal task that builds the popdown strings. This task is called when the + combo button that activates the dropdown menu is pressed + """ + self.inb = 1 + if self.vals[0] == "": + self.entry.set_popdown_strings([self.entry.entry.get_text()]) + else: + self.entry.set_popdown_strings(self.vals) + + def timer_callback(self,entry): + """ + The workhorse routine of file completion. This routine grabs the + current text of the entry box, and grubs through the list item + looking for any case insensitive matches. This routine relies on + public knowledge of the GtkEntry data structure, not on any private + data. + """ + # Clear any timer + timer = entry.get_data("timer"); + if timer: + gtk.timeout_remove(timer) + + if self.inb == 1: + return + + # Get the user's text + typed = entry.get_text() + if (not typed): + return + typed_lc = string.lower(typed) + + if typed_lc == self.nl: + return + + self.l = len(typed_lc) + + self.vals = [] + + # Walk the GtkList in the entry box + for nl,n in self.nlist: + # If typed text is a substring of the label text, then fill in + # the entry field with the full text (and correcting + # capitalization), and then select all the characters that + # don't match. With the user's next keystroke these will be + # replaced if they are incorrect. + if nl[0:self.l] == typed_lc: + self.vals.append(n) + + if len(self.vals) > 0: + n = self.vals[0] + self.nl = string.lower(n) + entry.set_text(n) + #entry.set_position(self.l) + #entry.select_region(self.l, -1) + else: + self.vals = [""] + +#------------------------------------------------------------------------- +# +# AutoEntry +# +#------------------------------------------------------------------------- +class AutoEntry(AutoCompBase): + """ + Allows allow completion of the GtkEntry widget with the entries + in the passed string list. + """ + def __init__(self,widget,plist,source=None): + AutoCompBase.__init__(self,widget,plist,source) + self.entry = widget + self.entry.connect("insert-text",self.insert_text) + + def timer_callback(self,entry): + """ + The workhorse routine of file completion. This routine grabs the + current text of the entry box, and grubs through the list item + looking for any case insensitive matches. This routine relies on + public knowledge of the GtkEntry data structure, not on any private + data. + """ + # Clear any timer + timer = entry.get_data("timer"); + if (timer): + gtk.timeout_remove(timer) + + # Get the user's text + typed = entry.get_text() + if (not typed): + return + typed_lc = string.lower(typed) + + if typed_lc == self.nl: + return + + self.l = len(typed_lc) + + # Walk the GtkList in the entry box + for nl,n in self.nlist: + # If typed text is a substring of the label text, then fill in + # the entry field with the full text (and correcting + # capitalization), and then select all the characters that + # don't match. With the user's next keystroke these will be + # replaced if they are incorrect. + if nl[0:self.l] == typed_lc: + self.nl = nl + entry.set_text(n) + #entry.set_position(self.l) + #entry.select_region(self.l, -1) + return + diff --git a/gramps2/src/Bookmarks.py b/gramps2/src/Bookmarks.py new file mode 100644 index 000000000..64da9c652 --- /dev/null +++ b/gramps2/src/Bookmarks.py @@ -0,0 +1,177 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"Handle bookmarks for the gramps interface" + +__author__ = "Donald N. Allingham" +__version__ = "$Revision$" + +#------------------------------------------------------------------------- +# +# GTK/Gnome modules +# +#------------------------------------------------------------------------- +import gtk +import gnome.ui + +#------------------------------------------------------------------------- +# +# gramps modules +# +#------------------------------------------------------------------------- +import Utils +from intl import gettext as _ + +#------------------------------------------------------------------------- +# +# Bookmarks +# +#------------------------------------------------------------------------- +class Bookmarks : + "Handle the bookmarks interface for Gramps" + + def __init__(self,bookmarks,menu,callback): + """ + Creates a the bookmark editor + + bookmarks - list of People + menu - parent menu to attach users + callback - task to connect to the menu item as a callback + """ + self.menu = menu + self.bookmarks = bookmarks + self.callback = callback + self.redraw() + + def redraw(self): + """Create the pulldown menu""" + if len(self.bookmarks) > 0: + self.myMenu = gtk.Menu() + for person in self.bookmarks: + self.add_to_menu(person) + self.menu.set_submenu(self.myMenu) + self.menu.set_sensitive(1) + else: + self.menu.remove_submenu() + self.menu.set_sensitive(0) + + def add(self,person): + """appends the person to the bottom of the bookmarks""" + if person not in self.bookmarks: + Utils.modified() + self.bookmarks.append(person) + self.redraw() + + def add_to_menu(self,person): + """adds a person's name to the drop down menu""" + item = gtk.MenuItem(person.getPrimaryName().getName()) + item.connect("activate", self.callback, person) + item.show() + self.myMenu.append(item) + + def draw_window(self): + """Draws the bookmark dialog box""" + title = "%s - GRAMPS" % _("Edit Bookmarks") + self.top = gtk.Dialog(title) + self.top.set_default_size(350,250) + self.top.vbox.set_spacing(5) + self.top.vbox.pack_start(gtk.Label(_("Edit Bookmarks")),0,0,5) + self.top.vbox.pack_start(gtk.HSeparator(),0,0,5) + box = gtk.HBox() + self.top.vbox.pack_start(box,1,1,5) + self.namelist = gtk.CList(1) + slist = gtk.ScrolledWindow() + slist.add_with_viewport(self.namelist) + slist.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + box.pack_start(slist,1,1,5) + bbox = gtk.VButtonBox() + bbox.set_layout(gtk.BUTTONBOX_START) + up = gtk.Button() + up.set_label(gtk.STOCK_GO_UP) + up.set_use_stock(1) + down = gtk.Button() + down.set_label(gtk.STOCK_GO_DOWN) + down.set_use_stock(1) + delete = gtk.Button() + delete.set_label(gtk.STOCK_REMOVE) + delete.set_use_stock(1) + up.connect('clicked', self.up_clicked) + down.connect('clicked',self.down_clicked) + delete.connect('clicked',self.delete_clicked) + self.top.add_button(gtk.STOCK_OK,0) + self.top.add_button(gtk.STOCK_CANCEL,1) + bbox.add(up) + bbox.add(down) + bbox.add(delete) + box.pack_start(bbox,0,0,5) + self.top.show_all() + + def edit(self): + """ + display the bookmark editor. + + The current bookmarked people are inserted into the namelist, + attaching the person object to the corresponding row. The currently + selected row is attached to the name list. This is either 0 if the + list is not empty, or -1 if it is. + """ + self.draw_window() + index = 0 + for person in self.bookmarks: + self.namelist.append([person.getPrimaryName().getName()]) + self.namelist.set_row_data(index,person) + index = index + 1 + + if self.top.run() == 0 : + self.ok_clicked() + else: + self.cancel_clicked() + + def delete_clicked(self,obj): + """Removes the current selection from the list""" + if len(self.namelist.selection) > 0: + self.namelist.remove(self.namelist.selection[0]) + + def up_clicked(self,obj): + """Moves the current selection up one row""" + if len(self.namelist.selection) > 0: + index = self.namelist.selection[0] + self.namelist.swap_rows(index-1,index) + + def down_clicked(self,obj): + """Moves the current selection down one row""" + if len(self.namelist.selection) > 0: + index = self.namelist.selection[0] + if index != self.namelist.rows-1: + self.namelist.swap_rows(index+1,index) + + def ok_clicked(self): + """Saves the current bookmarks from the list""" + del self.bookmarks[0:] + for index in range(0,self.namelist.rows): + person = self.namelist.get_row_data(index) + if person: + self.bookmarks.append(person) + self.redraw() + self.top.destroy() + + def cancel_clicked(self): + """Closes the current window""" + self.top.destroy() diff --git a/gramps2/src/Calendar.py b/gramps2/src/Calendar.py new file mode 100644 index 000000000..56d610e60 --- /dev/null +++ b/gramps2/src/Calendar.py @@ -0,0 +1,513 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2001 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +""" +Calendar conversion routines for GRAMPS. + +The original algorithms for this module came from Scott E. Lee's +C implementation. The original C source can be found at Scott's +web site at http://www.scottlee.com +""" + +__author__ = "Donald N. Allingham" +__version__ = "$Revision$" + +_FR_SDN_OFFSET = 2375474 +_FR_DAYS_PER_4_YEARS = 1461 +_FR_DAYS_PER_MONTH = 30 +_FR_FIRST_VALID = 2375840 +_FR_LAST_VALID = 2380952 +_GR_SDN_OFFSET = 32045 +_GR_DAYS_PER_5_MONTHS = 153 +_GR_DAYS_PER_4_YEARS = 1461 +_GR_DAYS_PER_400_YEARS = 146097 +_J_SDN_OFFSET = 32083 +_J_DAYS_PER_5_MONTHS = 153 +_J_DAYS_PER_4_YEARS = 1461 + +_HALAKIM_PER_HOUR = 1080 +_HALAKIM_PER_DAY = 25920 +_HALAKIM_PER_LUNAR_CYCLE = ((29 * _HALAKIM_PER_DAY) + 13753) +_HALAKIM_PER_METONIC_CYCLE = (_HALAKIM_PER_LUNAR_CYCLE * (12 * 19 + 7)) + +_H_SDN_OFFSET = 347997 +_NEW_MOON_OF_CREATION = 31524 + +_SUNDAY = 0 +_MONDAY = 1 +_TUESDAY = 2 +_WEDNESDAY= 3 +#_THURSDAY = 4 +_FRIDAY = 5 +#_SATURDAY = 6 + +_NOON = (18 * _HALAKIM_PER_HOUR) +_AM3_11_20 = ((9 * _HALAKIM_PER_HOUR) + 204) +_AM9_32_43 = ((15 * _HALAKIM_PER_HOUR) + 589) + +#------------------------------------------------------------------------- +# +# Conversion tables +# +#------------------------------------------------------------------------- +monthsPerYear = [ + 12, 12, 13, 12, 12, 13, 12, 13, 12, + 12, 13, 12, 12, 13, 12, 12, 13, 12, 13 +] + +yearOffset = [ + 0, 12, 24, 37, 49, 61, 74, 86, 99, 111, 123, + 136, 148, 160, 173, 185, 197, 210, 222 +] + +#------------------------------------------------------------------------- +# +# Tasks +# +#------------------------------------------------------------------------- + +def french_to_sdn(y,m,d): + """Converts a French Republican Calendar date to an SDN number""" + if (y < 1 or y > 14 or m < 1 or m > 13 or d < 1 or d > 30): + return 0 + return (y*_FR_DAYS_PER_4_YEARS)/4+(m-1)*_FR_DAYS_PER_MONTH+d+_FR_SDN_OFFSET + +def sdn_to_french(sdn): + """Converts an SDN number to a French Republican Calendar date""" + if (sdn < _FR_FIRST_VALID or sdn > _FR_LAST_VALID) : + return (0,0,0) + temp = (sdn-_FR_SDN_OFFSET)*4 - 1 + year = temp/_FR_DAYS_PER_4_YEARS + dayOfYear = (temp%_FR_DAYS_PER_4_YEARS)/4 + month = (dayOfYear/_FR_DAYS_PER_MONTH)+1 + day = (dayOfYear%_FR_DAYS_PER_MONTH)+1 + return (year,month,day) + + +def sdn_to_gregorian(sdn): + """Converts an SDN number to a gregorial date""" + if sdn <= 0: + return (0,0,0) + + temp = (sdn + _GR_SDN_OFFSET) * 4 - 1 + + # Calculate the century (year/100) + century = temp / _GR_DAYS_PER_400_YEARS + + # Calculate the year and day of year (1 <= dayOfYear <= 366) + + temp = ((temp % _GR_DAYS_PER_400_YEARS) / 4) * 4 + 3 + year = (century * 100) + (temp / _GR_DAYS_PER_4_YEARS) + dayOfYear = (temp % _GR_DAYS_PER_4_YEARS) / 4 + 1 + + # Calculate the month and day of month + temp = dayOfYear * 5 - 3 + month = temp / _GR_DAYS_PER_5_MONTHS + day = (temp % _GR_DAYS_PER_5_MONTHS) / 5 + 1 + + # Convert to the normal beginning of the year + if month < 10 : + month = month + 3 + else: + year = year + 1 + month = month + 9 + + # Adjust to the B.C./A.D. type numbering + year = year - 4800 + if year <= 0: + year = year - 1 + + return (year,month,day) + +def gregorian_to_sdn(iyear,imonth,iday): + """Converts a gregorian date to an SDN number""" + # check for invalid dates + if iyear==0 or iyear<-4714 or imonth<=0 or imonth>12 or iday<=0 or iday>31: + return 0 + + # check for dates before SDN 1 (Nov 25, 4714 B.C.) + if iyear == -4714: + if imonth < 11 or imonth == 11 and iday < 25: + return 0 + + if iyear < 0: + year = iyear + 4801 + else: + year = iyear + 4800 + + # Adjust the start of the year + + if imonth > 2: + month = imonth - 3 + else: + month = imonth + 9 + year = year - 1 + + return( ((year / 100) * _GR_DAYS_PER_400_YEARS) / 4 + + ((year % 100) * _GR_DAYS_PER_4_YEARS) / 4 + + (month * _GR_DAYS_PER_5_MONTHS + 2) / 5 + + iday + - _GR_SDN_OFFSET ); + + +def sdn_to_julian(sdn): + """Converts an SDN number to a Julian date""" + if sdn <= 0 : + return (0,0,0) + + temp = (sdn + _J_SDN_OFFSET) * 4 - 1 + + # Calculate the year and day of year (1 <= dayOfYear <= 366) + year = temp / _J_DAYS_PER_4_YEARS + dayOfYear = (temp % _J_DAYS_PER_4_YEARS) / 4 + 1 + + # Calculate the month and day of month + temp = dayOfYear * 5 - 3; + month = temp / _J_DAYS_PER_5_MONTHS; + day = (temp % _J_DAYS_PER_5_MONTHS) / 5 + 1; + + # Convert to the normal beginning of the year + if month < 10: + month = month + 3 + else: + year = year + 1 + month = month - 9 + + # Adjust to the B.C./A.D. type numbering + year = year - 4800 + if year <= 0: + year = year - 1 + + return (year,month,day) + +def julian_to_sdn(iyear,imonth,iday): + """Converts a Julian calendar date to an SDN number""" + + # check for invalid dates + if iyear==0 or iyear<-4713 or imonth<=0 or imonth>12 or iday<=0 or iday>31: + return 0 + + # check for dates before SDN 1 (Jan 2, 4713 B.C.) + if iyear == -4713: + if imonth == 1 and iday == 1: + return 0 + + # Make year always a positive number + if iyear < 0: + year = iyear + 4801 + else: + year = iyear + 4800 + + # Adjust the start of the year + if imonth > 2: + month = imonth - 3 + else: + month = imonth + 9 + year = year - 1 + + return (year*_J_DAYS_PER_4_YEARS)/4 + (month*_J_DAYS_PER_5_MONTHS+2)/5 + iday - _J_SDN_OFFSET + +def Tishri1(metonicYear, moladDay, moladHalakim): + + tishri1 = moladDay + dow = tishri1 % 7 + leapYear = metonicYear == 2 or metonicYear == 5 or metonicYear == 7 or \ + metonicYear == 10 or metonicYear == 13 or metonicYear == 16 or \ + metonicYear == 18 + lastWasLeapYear = metonicYear == 3 or metonicYear == 6 or metonicYear == 8 or \ + metonicYear == 11 or metonicYear == 14 or metonicYear == 17 or \ + metonicYear == 0 + + # Apply rules 2, 3 and 4. + if ((moladHalakim >= _NOON) or + ((not leapYear) and dow == _TUESDAY and moladHalakim >= _AM3_11_20) or + (lastWasLeapYear and dow == _MONDAY and moladHalakim >= _AM9_32_43)) : + tishri1 = tishri1 + 1 + dow = dow + 1 + if dow == 7: + dow = 0 + + # Apply rule 1 after the others because it can cause an additional + # delay of one day + + if dow == _WEDNESDAY or dow == _FRIDAY or dow == _SUNDAY: + tishri1 = tishri1 + 1 + + return tishri1 + + +def MoladOfMetonicCycle(metonicCycle): + + # Start with the time of the first molad after creation. + + r1 = _NEW_MOON_OF_CREATION; + + # Calculate metonicCycle * HALAKIM_PER_METONIC_CYCLE. The upper 32 + # bits of the result will be in r2 and the lower 16 bits will be + # in r1. + + r1 = r1 + (metonicCycle * (_HALAKIM_PER_METONIC_CYCLE & 0xFFFF)) + r2 = r1 >> 16 + r2 = r2 + (metonicCycle * ((_HALAKIM_PER_METONIC_CYCLE >> 16) & 0xFFFF)) + + # Calculate r2r1 / HALAKIM_PER_DAY. The remainder will be in r1, the + # upper 16 bits of the quotient will be in d2 and the lower 16 bits + # will be in d1. + + d2 = r2 / _HALAKIM_PER_DAY + r2 = r2 - (d2 * _HALAKIM_PER_DAY) + r1 = (r2 << 16) | (r1 & 0xFFFF) + d1 = r1 / _HALAKIM_PER_DAY + r1 = r1 - ( d1 * _HALAKIM_PER_DAY) + + MoladDay = (d2 << 16) | d1 + MoladHalakim = r1 + + return (MoladDay,MoladHalakim) + +def FindTishriMolad(inputDay): + + # Estimate the metonic cycle number. Note that this may be an under + # estimate because there are 6939.6896 days in a metonic cycle not + # 6940, but it will never be an over estimate. The loop below will + # correct for any error in this estimate. */ + + metonicCycle = (inputDay + 310) / 6940 + + # Calculate the time of the starting molad for this metonic cycle. */ + + (moladDay, moladHalakim) = MoladOfMetonicCycle(metonicCycle) + + # If the above was an under estimate, increment the cycle number until + # the correct one is found. For modern dates this loop is about 98.6% + # likely to not execute, even once, because the above estimate is + # really quite close. + + while moladDay < (inputDay - 6940 + 310): + metonicCycle = metonicCycle + 1 + moladHalakim = moladHalakim + _HALAKIM_PER_METONIC_CYCLE + moladDay = moladDay + ( moladHalakim / _HALAKIM_PER_DAY) + moladHalakim = moladHalakim % _HALAKIM_PER_DAY + + # Find the molad of Tishri closest to this date. + + for metonicYear in range(0,18): + if moladDay > inputDay - 74: + break + + moladHalakim = moladHalakim + \ + (_HALAKIM_PER_LUNAR_CYCLE * monthsPerYear[metonicYear]) + moladDay = moladDay + (moladHalakim / _HALAKIM_PER_DAY) + moladHalakim = moladHalakim % _HALAKIM_PER_DAY + else: + metonicYear = metonicYear + 1 + return (metonicCycle, metonicYear, moladDay, moladHalakim) + +def FindStartOfYear(year): + + pMetonicCycle = (year - 1) / 19; + pMetonicYear = (year - 1) % 19; + (pMoladDay, pMoladHalakim) = MoladOfMetonicCycle(pMetonicCycle) + + pMoladHalakim = pMoladHalakim + (_HALAKIM_PER_LUNAR_CYCLE * yearOffset[pMetonicYear]) + pMoladDay = pMoladDay + (pMoladHalakim / _HALAKIM_PER_DAY) + pMoladHalakim = pMoladHalakim % _HALAKIM_PER_DAY + + pTishri1 = Tishri1(pMetonicYear, pMoladDay, pMoladHalakim); + + return (pMetonicCycle, pMetonicYear, pMoladDay, pMoladHalakim, pTishri1) + +def sdn_to_jewish(sdn): + """Converts an SDN number to a Julian calendar date""" + + if sdn <= _H_SDN_OFFSET : + return (0,0,0) + + inputDay = sdn - _H_SDN_OFFSET + + (metonicCycle, metonicYear, day, halakim) = FindTishriMolad(inputDay) + tishri1 = Tishri1(metonicYear, day, halakim); + + if inputDay >= tishri1: + # It found Tishri 1 at the start of the year + + pYear = (metonicCycle * 19) + metonicYear + 1 + if inputDay < tishri1 + 59: + if inputDay < tishri1 + 30: + pMonth = 1 + pDay = inputDay - tishri1 + 1 + else: + pMonth = 2 + pDay = inputDay - tishri1 - 29 + return (pYear, pMonth, pDay) + + # We need the length of the year to figure this out, so find + # Tishri 1 of the next year. */ + + halakim = halakim + (_HALAKIM_PER_LUNAR_CYCLE * monthsPerYear[metonicYear]) + day = day + (halakim / _HALAKIM_PER_DAY) + halakim = halakim % _HALAKIM_PER_DAY; + tishri1After = Tishri1((metonicYear + 1) % 19, day, halakim); + else: + # It found Tishri 1 at the end of the year. + + pYear = metonicCycle * 19 + metonicYear + if inputDay >= tishri1 - 177: + # It is one of the last 6 months of the year. + if inputDay > tishri1 - 30: + pMonth = 13 + pDay = inputDay - tishri1 + 30 + elif inputDay > tishri1 - 60: + pMonth = 12 + pDay = inputDay - tishri1 + 60 + elif inputDay > tishri1 - 89: + pMonth = 11 + pDay = inputDay - tishri1 + 89 + elif inputDay > tishri1 - 119: + pMonth = 10 + pDay = inputDay - tishri1 + 119 + elif inputDay > tishri1 - 148: + pMonth = 9 + pDay = inputDay - tishri1 + 148 + else: + pMonth = 8 + pDay = inputDay - tishri1 + 178 + return (pYear,pMonth,pDay) + else: + if monthsPerYear[(pYear - 1) % 19] == 13: + pMonth = 7 + pDay = inputDay - tishri1 + 207 + if pDay > 0: + return (pYear,pMonth,pDay) + pMonth = pMonth - 1 + pDay = pDay + 30 + if pDay > 0: + return (pYear,pMonth,pDay) + pMonth = pMonth - 1 + pDay = pDay + 30 + else: + pMonth = 6 + pDay = inputDay - tishri1 + 207 + if pDay > 0: + return (pYear,pMonth,pDay) + pMonth = pMonth - 1 + pDay = pDay + 30 + + if pDay > 0: + return (pYear,pMonth,pDay) + pMonth = pMonth - 1 + pDay = pDay + 29 + if pDay > 0: + return (pYear,pMonth,pDay) + + # We need the length of the year to figure this out, so find + # Tishri 1 of this year. */ + tishri1After = tishri1; + (metonicCycle,metonicYear,day,halakim) = FindTishriMolad(day-365) + tishri1 = Tishri1(metonicYear, day, halakim) + + yearLength = tishri1After - tishri1; + day = inputDay - tishri1 - 29; + if yearLength == 355 or yearLength == 385 : + # Heshvan has 30 days + if day <= 30: + pMonth = 2 + pDay = day + return (pYear,pMonth,pDay) + day = day - 30 + else: + # Heshvan has 29 days + if day <= 29: + pMonth = 2 + pDay = day + return (pYear,pMonth,pDay) + + day = day - 29 + + # It has to be Kislev + return (pYear,3,day) + + +def jewish_to_sdn(year, month, day): + """Converts a Jewish calendar date to an SDN number""" + if year <= 0 or day <= 0 or day > 30 : + return 0 + + if month == 1 or month == 2: + # It is Tishri or Heshvan - don't need the year length. + (metonicCycle,metonicYear,moladDay,moladHalakim,tishri1) = FindStartOfYear(year) + if month == 1: + sdn = tishri1 + day - 1 + else: + sdn = tishri1 + day + 29 + elif month == 3: + # It is Kislev - must find the year length. + + # Find the start of the year. + (metonicCycle,metonicYear,moladDay,moladHalakim,tishri1) = FindStartOfYear(year) + + # Find the end of the year. + moladHalakim = moladHalakim + (_HALAKIM_PER_LUNAR_CYCLE*monthsPerYear[metonicYear]) + moladDay = moladDay + (moladHalakim / _HALAKIM_PER_DAY) + moladHalakim = moladHalakim % _HALAKIM_PER_DAY + tishri1After = Tishri1((metonicYear + 1) % 19, moladDay, moladHalakim) + + yearLength = tishri1After - tishri1 + + if yearLength == 355 or yearLength == 385: + sdn = tishri1 + day + 59 + else: + sdn = tishri1 + day + 58 + elif month == 4 or month == 5 or month == 6: + # It is Tevet, Shevat or Adar I - don't need the year length + + (metonicCycle,metonicYear,moladDay,moladHalakim,tishri1After) = FindStartOfYear(year+1) + + if monthsPerYear[(year - 1) % 19] == 12: + lengthOfAdarIAndII = 29 + else: + lengthOfAdarIAndII = 59 + + if month == 4: + sdn = tishri1After + day - lengthOfAdarIAndII - 237 + elif month == 5: + sdn = tishri1After + day - lengthOfAdarIAndII - 208 + else: + sdn = tishri1After + day - lengthOfAdarIAndII - 178 + else: + # It is Adar II or later - don't need the year length. + (metonicCycle,metonicYear,moladDay,moladHalakim,tishri1After) = FindStartOfYear(year+1) + + if month == 7: + sdn = tishri1After + day - 207 + elif month == 8: + sdn = tishri1After + day - 178 + elif month == 9: + sdn = tishri1After + day - 148 + elif month == 10: + sdn = tishri1After + day - 119 + elif month == 11: + sdn = tishri1After + day - 89 + elif month == 12: + sdn = tishri1After + day - 60 + elif month == 13: + sdn = tishri1After + day - 30 + else: + return 0 + return sdn + _H_SDN_OFFSET diff --git a/gramps2/src/ChangeLog b/gramps2/src/ChangeLog new file mode 100644 index 000000000..e69de29bb diff --git a/gramps2/src/ChooseParents.py b/gramps2/src/ChooseParents.py new file mode 100644 index 000000000..f3a199f1d --- /dev/null +++ b/gramps2/src/ChooseParents.py @@ -0,0 +1,440 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +""" +ChooseParents interface allows users to select the paretns of an +individual. +""" + +__author__ = "Donald N. Allingham" +__version__ = "$Revision$" + +#------------------------------------------------------------------------- +# +# internationalization +# +#------------------------------------------------------------------------- +from intl import gettext as _ + +#------------------------------------------------------------------------- +# +# GTK/Gnome modules +# +#------------------------------------------------------------------------- +import gobject +import gtk.glade + +#------------------------------------------------------------------------- +# +# gramps modules +# +#------------------------------------------------------------------------- +import RelLib +import const +import sort +import Utils +import GrampsCfg + +#------------------------------------------------------------------------- +# +# ChooseParents +# +#------------------------------------------------------------------------- +class ChooseParents: + """ + Displays the Choose Parents dialog box, allowing the parents + to be edited. + """ + def __init__(self,db,person,family,family_update,full_update): + """ + Creates a ChoosePerson dialog box. + + db - database associated the person + person - person whose parents we are selecting + family - current family + family_update - task that updates the family display + full_update - task that updates the main display + """ + self.db = db + self.person = person + self.family = family + self.family_update = family_update + self.full_update = full_update + self.old_type = "" + self.type = "" + + if self.family: + self.father = self.family.getFather() + self.mother = self.family.getMother() + else: + self.mother = None + self.father = None + + self.glade = gtk.glade.XML(const.gladeFile,"familyDialog") + self.top = self.glade.get_widget("familyDialog") + self.mother_rel = self.glade.get_widget("mrel") + self.father_rel = self.glade.get_widget("frel") + self.fcombo = self.glade.get_widget("prel_combo") + self.prel = self.glade.get_widget("prel") + self.title = self.glade.get_widget("chooseTitle") + self.father_list = self.glade.get_widget("father_list") + self.mother_list = self.glade.get_widget("mother_list") + self.flabel = self.glade.get_widget("flabel") + self.mlabel = self.glade.get_widget("mlabel") + self.fcombo.set_popdown_strings(const.familyRelations) + + + self.fmodel = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, + gobject.TYPE_STRING, gobject.TYPE_STRING, + gobject.TYPE_STRING) + self.father_list.set_model(self.fmodel) + self.fselection = self.father_list.get_selection() + self.fselection.connect('changed',self.father_list_select_row) + + self.mmodel = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, + gobject.TYPE_STRING, gobject.TYPE_STRING, + gobject.TYPE_STRING) + self.mother_list.set_model(self.mmodel) + self.mselection = self.mother_list.get_selection() + self.mselection.connect('changed',self.mother_list_select_row) + + self.build_list(self.father_list) + self.build_list(self.mother_list) + + for (f,mr,fr) in self.person.getParentList(): + if f == self.family: + self.mother_rel.set_text(_(mr)) + self.father_rel.set_text(_(fr)) + break + else: + self.mother_rel.set_text(_("Birth")) + self.father_rel.set_text(_("Birth")) + + if self.family: + self.type = self.family.getRelationship() + else: + self.type = "Married" + + self.prel.set_text(_(self.type)) + self.redraw() + + self.glade.signal_autoconnect({ + "on_save_parents_clicked" : self.save_parents_clicked, + "on_add_parent_clicked" : self.add_parent_clicked, + "on_prel_changed" : self.parent_relation_changed, + "destroy_passed_object" : Utils.destroy_passed_object + }) + + text = _("Choose the Parents of %s") % GrampsCfg.nameof(self.person) + self.title.set_text(text) + + def build_list(self,clist): + colno = 0 + for title in [ (_('Name'),3,200), + (_('ID'),1,50), + (_('Birth Date'),4,50), + ('',0,50), + ('',0,0)]: + renderer = gtk.CellRendererText () + column = gtk.TreeViewColumn (title[0], renderer, text=colno) + colno = colno + 1 + column.set_clickable (gtk.TRUE) + if title[0] == '': + column.set_clickable(gtk.TRUE) + column.set_visible(gtk.FALSE) + else: + column.set_resizable(gtk.TRUE) + column.set_sort_column_id(title[1]) + column.set_min_width(title[2]) + clist.append_column(column) + if colno == 1: + column.clicked() + + def redraw(self): + """Redraws the potential father and mother lists""" + + self.fmodel.clear() + self.mmodel.clear() + + pkey = self.person.getId() + gender = self.person.getGender() + if self.father: + fid = self.father.getId() + else: + fid = None + if self.mother: + mid = self.mother.getId() + else: + mid = None + + for key in self.db.getPersonKeys(): + if pkey == key: + continue + if gender == const.unknown: + continue + + dinfo = self.db.getPersonDisplay(key) + if self.type == "Partners": + self.set_data(self.fmodel,self.fselection,dinfo,fid) + self.set_data(self.mmodel,self.mselection,dinfo,mid) + elif dinfo[2] == const.male: + self.set_data(self.fmodel,self.fselection,dinfo,fid) + else: + self.set_data(self.mmodel,self.mselection,dinfo,mid) + + if self.type == "Partners": + self.mlabel.set_label(_("Parent")) + self.flabel.set_label(_("Parent")) + else: + self.mlabel.set_label(_("Mother")) + self.flabel.set_label(_("Father")) + + def set_data(self,model,selection,dinfo,key): + iter = model.append() + model.set(iter,0,dinfo[0],1,dinfo[1],2,dinfo[3],3,dinfo[5],4,dinfo[6]) + if key == dinfo[1]: + selection.select_iter(iter) + + def parent_relation_changed(self,obj): + """Called everytime the parent relationship information is changegd""" + self.old_type = self.type + self.type = const.save_frel(obj.get_text()) + if self.old_type == "Partners" or self.type == "Partners": + self.redraw() + + def find_family(self,father,mother): + """ + Finds the family associated with the father and mother. + If one does not exist, it is created. + """ + if not father and not mother: + return None + + families = self.db.getFamilyMap().values() + for family in families: + if family.getFather() == father and family.getMother() == mother: + return family + elif family.getFather() == mother and family.getMother() == father: + return family + + family = self.db.newFamily() + family.setFather(father) + family.setMother(mother) + family.addChild(self.person) + + if father: + father.addFamily(family) + if mother: + mother.addFamily(family) + return family + + def mother_list_select_row(self,obj): + """Called when a row is selected in the mother list. Sets the + active mother based off the id associated with the row.""" + + model, iter = self.mselection.get_selected() + if iter: + id = model.get_value(iter,1) + self.mother = self.db.getPerson(id) + else: + self.mother = None + + def father_list_select_row(self,obj): + """Called when a row is selected in the father list. Sets the + active father based off the id associated with the row.""" + model, iter = self.fselection.get_selected() + if iter: + id = model.get_value(iter,1) + self.father = self.db.getPerson(id) + else: + self.father = None + + def save_parents_clicked(self,obj): + """ + Called with the OK button nis pressed. Saves the selected people as parents + of the main perosn. + """ + mother_rel = const.childRelations[self.mother_rel.get_text()] + father_rel = const.childRelations[self.father_rel.get_text()] + + if self.father or self.mother: + if self.mother and not self.father: + if self.mother.getGender() == RelLib.Person.male: + self.father = self.mother + self.mother = None + self.family = self.find_family(self.father,self.mother) + elif self.father and not self.mother: + if self.father.getGender() == RelLib.Person.female: + self.mother = self.father + self.father = None + self.family = self.find_family(self.father,self.mother) + elif self.mother.getGender() != self.father.getGender(): + if self.type == "Partners": + self.type = "Unknown" + if self.father.getGender() == RelLib.Person.female: + x = self.father + self.father = self.mother + self.mother = x + self.family = self.find_family(self.father,self.mother) + else: + self.type = "Partners" + self.family = self.find_family(self.father,self.mother) + else: + self.family = None + + Utils.destroy_passed_object(obj) + if self.family: + self.family.setRelationship(self.type) + self.change_family_type(self.family,mother_rel,father_rel) + self.family_update(None) + + def add_new_parent(self,person): + """Adds a new person to either the father list or the mother list, + depending on the gender of the person.""" + id = person.getId() + self.type = const.save_frel(self.prel.get_text()) + dinfo = self.db.getPersonDisplay(id) + rdata = [dinfo[0],dinfo[3],dinfo[5],dinfo[6]] + + if self.type == "Partners": + self.parent_relation_changed(self.prel) + elif person.getGender() == RelLib.Person.male: + self.father_list.insert(0,rdata) + self.father_list.set_row_data(0,id) + self.father_list.select_row(0,0) + self.father_list.sort() + self.father_list.moveto(self.father_list.selection[0],0) + else: + self.mother_list.insert(0,rdata) + self.mother_list.set_row_data(0,id) + self.mother_list.select_row(0,0) + self.mother_list.moveto(0,0) + self.mother_list.sort() + self.mother_list.moveto(self.mother_list.selection[0],0) + self.full_update() + + def add_parent_clicked(self,obj): + """Called with the Add New Person button is pressed. Calls the QuickAdd + class to create a new person.""" + + import QuickAdd + QuickAdd.QuickAdd(self.db,"male",self.add_new_parent) + + def change_family_type(self,family,mother_rel,father_rel): + """ + Changes the family type of the specified family. If the family + is None, the the relationship type shoud be deleted. + """ + if self.person not in family.getChildList(): + family.addChild(self.person) + for fam in self.person.getParentList(): + if family == fam[0]: + if mother_rel == fam[1] and father_rel == fam[2]: + return + if mother_rel != fam[1] or father_rel != fam[2]: + self.person.removeAltFamily(family) + self.person.addAltFamily(family,mother_rel,father_rel) + break + else: + self.person.addAltFamily(family,mother_rel,father_rel) + Utils.modified() + + +class ModifyParents: + def __init__(self,db,person,family,family_update,full_update): + """ + Creates a ChoosePerson dialog box. + + db - database associated the person + person - person whose parents we are selecting + family - current family + family_update - task that updates the family display + full_update - task that updates the main display + """ + self.db = db + self.person = person + self.family = family + self.family_update = family_update + self.full_update = full_update + + self.father = self.family.getFather() + self.mother = self.family.getMother() + + self.glade = gtk.glade.XML(const.gladeFile,"modparents") + self.top = self.glade.get_widget("modparents") + self.mother_rel = self.glade.get_widget("mrel") + self.father_rel = self.glade.get_widget("frel") + self.title = self.glade.get_widget("title") + self.flabel = self.glade.get_widget("flabel") + self.mlabel = self.glade.get_widget("mlabel") + + self.orig_mrel = _("Birth") + self.orig_frel = _("Birth") + for (f,mr,fr) in self.person.getParentList(): + if f == self.family: + self.orig_mrel = _(mr) + self.orig_frel = _(fr) + + self.mother_rel.set_text(self.orig_mrel) + self.father_rel.set_text(self.orig_frel) + + self.glade.signal_autoconnect({ + "on_save_parents_clicked" : self.save_parents_clicked, + "destroy_passed_object" : Utils.destroy_passed_object + }) + + text = _("Modify the Parents of %s") % GrampsCfg.nameof(self.person) + self.title.set_text(text) + self.title.set_use_markup(gtk.TRUE) + + if self.family.getRelationship() == "Partners": + self.mlabel.set_label(_("Parent")) + self.flabel.set_label(_("Parent")) + else: + self.mlabel.set_label(_("Mother")) + self.flabel.set_label(_("Father")) + + + if self.father: + self.glade.get_widget("fname").set_text(self.father.getPrimaryName().getName()) + else: + self.father_rel.set_senstive(0) + + if self.father: + self.glade.get_widget("mname").set_text(self.mother.getPrimaryName().getName()) + else: + self.mother_rel.set_senstive(0) + + + def save_parents_clicked(self,obj): + """ + Called with the OK button nis pressed. Saves the selected people as parents + of the main perosn. + """ + mother_rel = const.childRelations[self.mother_rel.get_text()] + father_rel = const.childRelations[self.father_rel.get_text()] + + Utils.destroy_passed_object(self.top) + if mother_rel != self.orig_mrel or father_rel != self.orig_frel: + self.person.removeAltFamily(self.family) + self.person.addAltFamily(self.family,mother_rel,father_rel) + self.family_update(None) + Utils.modified() + diff --git a/gramps2/src/Date.py b/gramps2/src/Date.py new file mode 100644 index 000000000..3aa6421d0 --- /dev/null +++ b/gramps2/src/Date.py @@ -0,0 +1,1037 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"Support for dates" + +__author__ = "Donald N. Allingham" +__version__ = "$Revision$" + +#------------------------------------------------------------------------- +# +# python modules +# +#------------------------------------------------------------------------- +from re import IGNORECASE, compile +import string +import time + +#------------------------------------------------------------------------- +# +# gramps modules +# +#------------------------------------------------------------------------- +import Calendar +from intl import gettext +_ = gettext + +#------------------------------------------------------------------------- +# +# Calendar Mappings +# +#------------------------------------------------------------------------- +GREGORIAN = 0 +JULIAN = 1 +HEBREW = 2 +FRENCH = 3 + +#------------------------------------------------------------------------- +# +# Month mappings +# +#------------------------------------------------------------------------- +_fmonth = [ + "Vendémiaire", "Brumaire", "Frimaire", "Nivôse", "Pluviôse", + "Ventôse", "Germinal", "Floréal", "Prairial", "Messidor", + "Thermidor", "Fructidor", "Extra", + ] + +_fmonth2num = { + "vend" : 0, "brum" : 1, "frim" : 2, "nivo" : 3, "pluv" : 4, + "vent" : 5, "germ" : 6, "flor" : 7, "prai" : 8, "mess" : 9, + "ther" :10, "fruc" :11, "extr" : 12,"comp" :12, "nivô" : 3 + } + +_hmonth = [ + "Tishri", "Heshvan", "Kislev", "Tevet", "Shevat", "AdarI", + "AdarII", "Nisan", "Iyyar", "Sivan", "Tammuz", "Av", + "Elul", + ] + +_hmonth2num = { + "tishri" : 0, "heshvan" : 1, "kislev" : 2, "tevet" : 3, + "shevat" : 4, "adari" : 5, "adarii" : 6, "nisan" : 7, + "iyyar" : 8, "sivan" : 9, "tammuz" :10, "av" : 11, + "elul" : 12,"tsh" : 0, "csh" : 1, "ksl" : 2, + "tvt" : 3, "shv" : 4, "adr" : 5, "ads" : 6, + "nsn" : 7, "iyr" : 8, "svn" : 9, "tmz" : 10, + "aav" :11, "ell" :12, + } + +_mname = [ + _("January"), _("February"), _("March"), _("April"), + _("May"), _("June"), _("July"), _("August"), + _("September"), _("October"), _("November"), _("December") + ] + +_m2num = { + string.lower(_mname[0][0:3]): 0, string.lower(_mname[1][0:3]): 1, + string.lower(_mname[2][0:3]): 2, string.lower(_mname[3][0:3]): 3, + string.lower(_mname[4][0:3]): 4, string.lower(_mname[5][0:3]): 5, + string.lower(_mname[6][0:3]): 6, string.lower(_mname[7][0:3]): 7, + string.lower(_mname[8][0:3]): 8, string.lower(_mname[9][0:3]): 9, + string.lower(_mname[10][0:3]): 10, string.lower(_mname[11][0:3]): 11 + } + +UNDEF = -999999 + +#------------------------------------------------------------------------- +# +# Date class +# +#------------------------------------------------------------------------- +class Date: + """ + The core date handling class for GRAMPs. Supports partial dates, + date ranges, and alternate calendars. + """ + formatCode = 0 + entryCode = 0 + + Error = "Illegal Date" + + fstr = _("(from|between|bet|bet.)") + tstr = _("(and|to|-)") + + fmt = compile("\s*%s\s+(.+)\s+%s\s+(.+)\s*$" % (fstr,tstr),IGNORECASE) + + def __init__(self,source=None): + if source: + self.start = SingleDate(source.start) + if source.stop: + self.stop = SingleDate(source.stop) + else: + self.stop = None + self.range = source.range + self.text = source.text + self.calendar = source.calendar + else: + self.start = SingleDate() + self.stop = None + self.range = 0 + self.text = "" + self.calendar = GREGORIAN + + def get_calendar(self): + return self.calendar + + def set_calendar(self,val): + self.calendar = val + self.start.convert_to(val) + if self.stop: + self.stop.convert_to(val) + + def get_start_date(self): + return self.start + + def get_stop_date(self): + if self.stop == None: + self.stop = SingleDate() + self.stop.calendar = self.calendar + return self.stop + + def getLowYear(self): + return self.start.getYear() + + def getHighYear(self): + if self.stop == None: + return self.start.getYear() + else: + return self.stop.getYear() + + def getYear(self): + return self.start.year + + def getYearValid(self): + return self.start.year != UNDEF + + def getMonth(self): + if self.start.month == UNDEF: + return UNDEF + return self.start.month+1 + + def getMonthValid(self): + return self.start.month != UNDEF + + def getDay(self): + return self.start.day + + def getDayValid(self): + return self.start.day != UNDEF + + def getValid(self): + """ Returns true if any part of the date is valid""" + return self.start.year != UNDEF or self.start.month != UNDEF or self.start.day != UNDEF + + + def getIncomplete(self): + return self.range == 0 and self.start.year == UNDEF or \ + self.start.month == UNDEF or self.start.day == UNDEF + + def getStopYear(self): + if self.stop == None: + self.stop = SingleDate() + self.stop.calendar = self.calendar + return self.stop.year + + def getStopMonth(self): + if self.stop == None: + self.stop = SingleDate() + self.stop.calendar = self.calendar + return self.stop.month+1 + + def getStopDay(self): + if self.stop == None: + self.stop = SingleDate() + self.stop.calendar = self.calendar + return self.stop.day + + def getText(self): + return self.text + + def greater_than(self,other): + return compare_dates(self,other) > 0 + + def less_than(self,other): + return compare_dates(self,other) < 0 + + def equal_to(self,other): + return compare_dates(self,other) == 0 + + def set(self,text): + match = Date.fmt.match(text) + try: + if match: + matches = match.groups() + self.start.set(matches[1]) + if self.stop == None: + self.stop = SingleDate() + self.stop.calendar = self.calendar + self.stop.set(matches[3]) + self.range = 1 + else: + self.start.set(text) + self.range = 0 + except Date.Error: + if text != "": + self.range = -1 + self.text = text + + def set_text(self,text): + self.range = -1 + self.text = text + + def set_range(self,val): + self.range = val + + def get_fmt(self,func): + if self.range == 0: + return func(self.start) + elif self.range == -1: + return self.text + else: + d1 = func(self.start) + d2 = func(self.stop) + return "%s %s %s %s" % ( _("from"),d1,_("to"),d2 ) + + def getDate(self): + return self.get_fmt(SingleDate.getDate) + + def getQuoteDate(self): + if self.calendar == GREGORIAN: + return self.getGregorianQuoteDate() + elif self.calendar == JULIAN: + return self.get_quote_date(_mname,_("Julian")) + elif self.calendar == HEBREW: + return self.get_quote_date(_hmonth,_("Hebrew")) + else: + return self.get_quote_date(_fmonth,_("French")) + + def getGregorianQuoteDate(self): + if self.range == 0: + return _func(self.start) + elif self.range == -1: + if self.text: + return '"%s"' % self.text + else: + return '' + else: + d1 = _func(self.start) + d2 = _func(self.stop) + return "%s %s %s %s" % ( _("from"),d1,_("to"), d2) + + def get_quote_date(self,month_map,cal_str): + if self.range == 0: + return "%s (%s)" % (self.start.display_calendar(month_map),cal_str) + elif self.range == -1: + if self.text: + return '"%s (%s)"' % (self.text,cal_str) + else: + return '' + else: + d1 = self.start.display_calendar(month_map) + d2 = self.stop.display_calendar(month_map) + return "%s %s %s %s (%s)" % ( _("from"),d1,_("to"), d2,cal_str) + + def isEmpty(self): + s = self.start + return s.year==UNDEF and s.month==UNDEF and s.day==UNDEF + + def isValid(self): + return self.range != -1 + + def isRange(self): + return self.range == 1 + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +def set_format_code(code): + global _func + Date.formatCode = code + _func = SingleDate.fmtFunc[code] + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +def get_format_code(): + return Date.formatCode + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +class SingleDate: + "Date handling" + + exact = 0 + about = 1 + before = 2 + after = 3 + + emname =[ + 'JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', + 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC' + ] + + em2num ={ + "jan" : 0, "feb" : 1, "mar" : 2, "apr" : 3, + "may" : 4, "jun" : 5, "jul" : 6, "aug" : 7, + "sep" : 8, "oct" : 9, "nov" : 10,"dec" : 11 + } + + m2v = { + _("abt") : about , _("about") : about, + _("abt.") : about, _("est") : about , + _("est.") : about , _("circa") : about, + _("around") : about, _("before") : before, + _("bef") : before, _("bef.") : before, + _("after") : after, _("aft.") : after, + _("aft") : after, + # And the untranslated versions for reading saved data from XML. + "abt" : about, "about" : about, + "bef" : before, "bef." : before, + "aft." : after, "abt." : about, + "est." : about, "est" : about, + "after" : after, "before": before, + "aft" : after, + } + + modifiers = '(' + \ + _("abt\.?") + '|' + \ + _("about") + '|' + \ + _("est\.?") + '|' + \ + _("circa") + '|' + \ + _("around") + '|' + \ + _("before") + '|' + \ + _("after") + '|' + \ + _("aft\.?") + '|' + \ + _("bef\.?") + \ + '|abt|aft|after|before|bef)' + + start = "^\s*" + modifiers + "?\s*" + + fmt1 = compile(start+"(\S+)(\s+\d+\s*,)?\s*([?\d]+)?\s*$", IGNORECASE) + fmt2 = compile(start+"(\d+)\.?\s+(\S+)(\s+\d+)?\s*$", IGNORECASE) + fmt3 = compile(start+r"([?\d]+)\s*[./-]\s*([?\d]+)\s*[./-]\s*([?\d]+)\s*$", + IGNORECASE) + fmt7 = compile(start+r"([?\d]+)\s*[./-]\s*([?\d]+)\s*$", IGNORECASE) + fmt4 = compile(start+"(\S+)\s+(\d+)\s*$", IGNORECASE) + fmt5 = compile(start+"(\d+)\s*$", IGNORECASE) + fmt6 = compile(start+"(\S+)\s*$", IGNORECASE) + + def __init__(self,source=None): + if source: + self.month = source.month + self.day = source.day + self.year = source.year + self.mode = source.mode + self.calendar = source.calendar + else: + self.month = UNDEF + self.day = UNDEF + self.year = UNDEF + self.mode = SingleDate.exact + self.calendar = GREGORIAN + + def setMode(self,val): + if not val: + self.mode = SingleDate.exact + else: + try: + self.mode = SingleDate.m2v[string.lower(val)] + except KeyError: + raise Date.Error,val + + def setMonth(self,val): + if val > 14 or val < 0: + self.month = UNDEF + else: + self.month = val - 1 + + def setMonthVal(self,s): + try: + val = int(s) + self.month = val - 1 + except: + self.month = UNDEF + + def setDayVal(self,s): + try: + val = int(s) + self.day = val + except: + self.day = UNDEF + + def setYearVal(self,s): + try: + val = int(s) + self.year = val + except: + self.year = UNDEF + + def getMonth(self): + if self.month == UNDEF: + return UNDEF + return self.month + 1 + + def getMonthValid(self): + return self.month != UNDEF + + def setDay(self,val): + self.day = val + + def getDay(self): + return self.day + + def getDayValid(self): + return self.day != UNDEF + + def setYear(self,val): + self.year = val + + def getYear(self): + return self.year + + def getYearValid(self): + return self.year != UNDEF + + def getValid(self): + """ Returns true if any part of the date is valid""" + return self.year != UNDEF or self.month != UNDEF or self.day != UNDEF + + def setMonthStr(self,text): + try: + self.month = _m2num[string.lower(text[0:3])] + except KeyError: + self.setMonthStrEng(text) + + def setMonthStrEng(self,text): + try: + self.month = SingleDate.em2num[string.lower(text[0:3])] + except KeyError: + self.month = UNDEF + raise Date.Error,text + + def getMonthStr(self): + return _mname[self.month] + + def getIsoDate(self): + if self.year == UNDEF: + y = "????" + else: + y = "%04d" % self.year + if self.month == UNDEF: + if self.day == UNDEF: + m = "" + else: + m = "-??" + else: + m = "-%02d" % (self.month+1) + if self.day == UNDEF: + d = '' + else: + d = "-%02d" % self.day + return "%s%s%s" % (y,m,d) + + def _format1(self): + if self.month == UNDEF and self.day == UNDEF and self.year == UNDEF: + return "" + elif self.day == UNDEF: + if self.month == UNDEF: + retval = str(self.year) + elif self.year == UNDEF: + retval = _mname[self.month] + else: + try: + retval = "%s %d" % (_mname[self.month],self.year) + except: + retval = "**** %d %d %d ****" % (self.year,self.month,self.day) + elif self.month == UNDEF: + retval = str(self.year) + else: + try: + month = _mname[self.month] + except: + month = "" + if self.year == UNDEF: + retval = "%s %d, ????" % (month,self.day) + else: + retval = "%s %d, %d" % (month,self.day,self.year) + + if self.mode == SingleDate.about: + retval = _("about") + ' ' + retval + elif self.mode == SingleDate.before: + retval = _("before") + ' ' + retval + elif self.mode == SingleDate.after: + retval = _("after") + ' ' + retval + return retval + + def _format2(self): + if self.month == UNDEF and self.day == UNDEF and self.year == UNDEF : + return "" + elif self.month != UNDEF and self.month != UNDEF: + month = _mname[self.month] + if self.year == UNDEF: + retval = "%s %d, ????" % (string.upper(month[0:3]),self.day) + else: + retval = "%s %d, %d" % (string.upper(month[0:3]),self.day,self.year) + elif self.day == UNDEF: + if self.month == UNDEF: + retval = str(self.year) + elif self.year == UNDEF: + month = _mname[self.month] + retval = string.upper(month[0:3]) + else: + month = _mname[self.month] + retval = "%s %d" % (string.upper(month[0:3]),self.year) + else: + retval = str(self.year) + + if self.mode == SingleDate.about: + retval = "%s %s" % (_("abt"),retval) + elif self.mode == SingleDate.before: + retval = "%s %s" % (_("before"),retval) + elif self.mode == SingleDate.after: + retval = "%s %s" % (_("after"),retval) + + return retval + + def _format3(self): + if self.month == UNDEF and self.day == UNDEF and self.year == UNDEF : + return "" + elif self.day == UNDEF: + if self.month == UNDEF: + retval = str(self.year) + elif self.year == UNDEF: + month = _mname[self.month] + retval = string.upper(month[0:3]) + else: + month = _mname[self.month] + retval = "%s %d" % (string.upper(month[0:3]),self.year) + elif self.month == UNDEF: + retval = str(self.year) + else: + month = _mname[self.month] + if self.year == UNDEF: + retval = "%d %s ????" % (self.day,string.upper(month[0:3])) + else: + retval = "%d %s %d" % (self.day,string.upper(month[0:3]),self.year) + + if self.mode == SingleDate.about: + retval = "%s %s" % (_("ABOUT"),retval) + elif self.mode == SingleDate.before: + retval = "%s %s" % (_("BEFORE"),retval) + elif self.mode == SingleDate.after: + retval = "%s %s" % (_("AFTER"),retval) + return retval + + def _format10(self): + if self.month == UNDEF and self.day == UNDEF and self.year == UNDEF : + return "" + elif self.day == UNDEF: + if self.month == UNDEF: + retval = str(self.year) + elif self.year == UNDEF: + retval = _mname[self.month] + else: + month = _mname[self.month] + retval = "%s %d" % (month,self.year) + elif self.month == UNDEF: + retval = str(self.year) + else: + month = _mname[self.month] + if self.year == UNDEF: + retval = "%d. %s ????" % (self.day,month) + else: + retval = "%d. %s %d" % (self.day,month,self.year) + + if self.mode == SingleDate.about: + retval = "%s %s" % (_("ABOUT"),retval) + elif self.mode == SingleDate.before: + retval = "%s %s" % (_("BEFORE"),retval) + elif self.mode == SingleDate.after: + retval = "%s %s" % (_("AFTER"),retval) + + return retval + + def _get_mmddyyyy(self,sep): + if self.month == UNDEF and self.day == UNDEF and self.year == UNDEF : + return "" + elif self.day == UNDEF: + if self.month == UNDEF: + retval = str(self.year) + elif self.year == UNDEF: + retval = "%02d%s??%s??" % (self.month+1,sep,sep) + else: + retval = "%02d%s??%s%04d" % (self.month+1,sep,sep,self.year) + elif self.month == UNDEF: + retval = "??%s%02d%s%04d" % (sep,self.day,sep,self.year) + else: + if self.year == UNDEF: + retval = "%02d%s%02d%s????" % (self.month+1,sep,self.day,sep) + else: + retval = "%02d%s%02d%s%04d" % (self.month+1,sep,self.day,sep,self.year) + + if self.mode == SingleDate.about: + retval = "%s %s" % (_("ABOUT"),retval) + elif self.mode == SingleDate.before: + retval = "%s %s" % (_("BEFORE"),retval) + elif self.mode == SingleDate.after: + retval = "%s %s" % (_("AFTER"),retval) + + return retval + + def _get_yyyymmdd(self,sep): + retval = "" + + if self.month == UNDEF and self.day == UNDEF and self.year == UNDEF : + pass + elif self.day == UNDEF: + if self.month == UNDEF: + retval = str(self.year) + elif self.year == UNDEF: + retval = "????%s%02d%s??" % (sep,self.month+1,sep) + else: + retval = "%04d%s%02d" % (self.year,sep,self.month+1) + elif self.month == UNDEF: + retval = "%04d%s??%s%02d" % (self.year,sep,sep,self.day) + else: + if self.year == UNDEF: + retval = "????%s%02d%s%02d" % (sep,self.month+1,sep,self.day) + else: + retval = "%02d%s%02d%s%02d" % (self.year,sep,self.month+1,sep,self.day) + + if self.mode == SingleDate.about: + retval = "%s %s" % (_("ABOUT"),retval) + + if self.mode == SingleDate.before: + retval = "%s %s" % (_("BEFORE"),retval) + elif self.mode == SingleDate.after: + retval = "%s %s" % (_("AFTER"),retval) + + return retval + + def _format4(self): + return self._get_mmddyyyy("/") + + def _format5(self): + return self._get_mmddyyyy("-") + + def _format8(self): + return self._get_mmddyyyy(".") + + def _get_ddmmyyyy(self,sep): + retval = "" + + if self.month == UNDEF and self.day == UNDEF and self.year == UNDEF : + pass + elif self.day == UNDEF: + if self.month == UNDEF: + retval = str(self.year) + elif self.year == UNDEF: + retval = "??%s%02d%s??" % (sep,self.month+1,sep) + else: + retval = "??%s%02d%s%04d" % (sep,self.month+1,sep,self.year) + elif self.month == UNDEF: + retval = "%02d%s??%s%04d" % (self.day,sep,sep,self.year) + else: + if self.year == UNDEF: + retval = "%02d%s%02d%s????" % (self.day,sep,self.month+1,sep) + else: + retval = "%02d%s%02d%s%04d" % (self.day,sep,self.month+1,sep,self.year) + + if self.mode == SingleDate.about: + retval = "%s %s" % (_("ABOUT"),retval) + if self.mode == SingleDate.before: + retval = "%s %s" % (_("BEFORE"),retval) + elif self.mode == SingleDate.after: + retval = "%s %s" % (_("AFTER"),retval) + + return retval + + def _format6(self): + return self._get_ddmmyyyy("/") + + def _format7(self): + return self._get_ddmmyyyy("-") + + def _format9(self): + return self._get_ddmmyyyy(".") + + def _format11(self): + return self._get_yyyymmdd("/") + + def _format12(self): + return self._get_yyyymmdd("-") + + def _format13(self): + return self._get_yyyymmdd(".") + + #-------------------------------------------------------------------- + # + # + # + #-------------------------------------------------------------------- + fmtFunc = [ _format1, _format2, _format3, _format4, _format5, _format6, + _format7, _format8, _format9, _format10, _format11, _format12, + _format13] + + def display_calendar(self,month_map): + d = '' + if self.year==UNDEF: + if self.month == UNDEF: + d = "" + elif self.day == UNDEF: + d = month_map[self.month] + else: + d = "%02d %s" % (self.day,month_map[self.month]) + elif self.month == UNDEF: + d = str(self.year) + elif self.day == UNDEF: + d = "%s %d" % (month_map[self.month],self.year) + else: + d = "%02d %s %d" % (self.day,month_map[self.month],self.year) + if self.mode == SingleDate.about: + d = _("about") + ' ' + d + elif self.mode == SingleDate.before: + d = _("before") + ' ' + d + elif self.mode == SingleDate.after: + d = _("after") + ' ' + d + return d + + def getDate(self): + if self.calendar == GREGORIAN: + return _func(self) + elif self.calendar == JULIAN: + return self.display_calendar(_mname) + elif self.calendar == HEBREW: + return self.display_calendar(_hmonth) + else: + return self.display_calendar(_fmonth) + + def setIsoDate(self,v): + data = string.split(v) + if len(data) > 1: + self.setMode(data[0]) + v = data[1] + + vals = string.split(v,'-') + self.setYearVal(vals[0]) + if len(vals) > 1: + self.setMonthVal(vals[1]) + if len(vals) > 2: + self.setDayVal(vals[2]) + + def getModeVal(self): + return self.mode + + def setModeVal(self,val): + self.mode = val + + def set(self,text): + if self.calendar == GREGORIAN: + self.set_gregorian(text) + elif self.calendar == JULIAN: + self.set_calendar(text,_m2num,3) + elif self.calendar == HEBREW: + self.set_calendar(text,_hmonth2num,0) + else: + self.set_calendar(text,_fmonth2num,4) + + def set_calendar(self,text,month_map,l): + match = SingleDate.fmt2.match(text) + if match: + matches = match.groups() + self.setMode(matches[0]) + if l == 0: + mon = string.lower(matches[2]) + else: + mon = string.lower(matches[2])[0:l] + self.setYear(int(matches[3])) + self.setMonthStr(mon) + self.setDay(int(matches[1])) + return + match = SingleDate.fmt3.match(text) + if match: + matches = match.groups() + self.setMode(matches[0]) + self.setYearVal(matches[3]) + self.setMonthVal(matches[2]) + self.setDayVal(matches[1]) + return + + match = SingleDate.fmt4.match(text) + if match: + matches = match.groups() + self.setMode(matches[0]) + if l == 0: + mon = string.lower(matches[1]) + else: + mon = string.lower(matches[1])[0:l] + self.setYearVal(matches[2]) + self.setMonthStr(mon) + self.day = UNDEF + return + + match = SingleDate.fmt5.match(text) + if match: + matches = match.groups() + self.setMode(matches[0]) + self.setYearVal(matches[1]) + self.month = UNDEF + self.day = UNDEF + return + + self.year = UNDEF + self.month = UNDEF + self.day = UNDEF + + def set_gregorian(self,text): + match = SingleDate.fmt2.match(text) + if match != None: + matches = match.groups() + self.setMode(matches[0]) + self.setMonthStr(matches[2]) + self.day = int(matches[1]) + if len(matches) == 4 and matches[3] != None: + self.setYearVal(matches[3]) + else: + self.year = UNDEF + return 1 + + match = SingleDate.fmt5.match(text) + if match != None: + matches = match.groups() + self.setMode(matches[0]) + self.month = UNDEF + self.day = UNDEF + self.year = int(matches[1]) + return 1 + + match = SingleDate.fmt7.match(text) + if match != None: + matches = match.groups() + self.setMode(matches[0]) + if Date.entryCode == 2: + self.setMonthVal(matches[2]) + self.setYearVal(matches[1]) + else: + self.setMonthVal(matches[1]) + self.setYearVal(matches[2]) + if self.getMonth() > 13: + raise Date.Error + return 1 + + match = SingleDate.fmt3.match(text) + if match != None: + matches = match.groups() + self.setMode(matches[0]) + if Date.entryCode == 0: + self.setMonthVal(matches[1]) + self.setDayVal(matches[2]) + self.setYearVal(matches[3]) + elif Date.entryCode == 1: + self.setMonthVal(matches[2]) + self.setDayVal(matches[1]) + self.setYearVal(matches[3]) + else: + self.setMonthVal(matches[2]) + self.setDayVal(matches[3]) + self.setYearVal(matches[1]) + return 1 + + match = SingleDate.fmt1.match(text) + if match != None: + (mode,mon,day,year) = match.groups() + self.setMode(mode) + self.setMonthStr(mon) + if day: + self.setDayVal(int(string.replace(day,',',''))) + else: + self.day = UNDEF + self.setYearVal(year) + return 1 + + match = SingleDate.fmt4.match(text) + if match != None: + matches = match.groups() + self.setMode(matches[0]) + self.setMonthStr(matches[1]) + self.day = UNDEF + if len(matches) == 4: + self.setYearVal(matches[3]) + return 1 + + match = SingleDate.fmt6.match(text) + if match != None: + matches = match.groups() + self.setMode(matches[0]) + self.setMonthVal(matches[1]) + self.day = UNDEF + self.year = UNDEF + return 1 + + raise Date.Error,text + + def get_sdn(self): + if self.year == UNDEF: + return 0 + if self.month == UNDEF: + month = 1 + else: + month = self.month + 1 + if self.day == UNDEF: + day = 1 + else: + day = self.day + + if self.calendar == GREGORIAN: + sdn = Calendar.gregorian_to_sdn(self.year,month,day) + elif self.calendar == FRENCH: + sdn = Calendar.french_to_sdn(self.year,month,day) + if self.calendar == HEBREW: + sdn = Calendar.jewish_to_sdn(self.year,month,day) + if self.calendar == JULIAN: + sdn = Calendar.julian_to_sdn(self.year,month,day) + return sdn + + def convert_to(self,val): + if val == GREGORIAN: + self.convert_calendar(Calendar.sdn_to_gregorian,val) + elif val == JULIAN: + self.convert_calendar(Calendar.sdn_to_julian,val) + elif val == HEBREW: + self.convert_calendar(Calendar.sdn_to_jewish,val) + else: + self.convert_calendar(Calendar.sdn_to_french,val) + + def convert_calendar(self,func,mode): + sdn = self.get_sdn() + (y,m,d) = func(sdn) + self.calendar = mode + if y == 0 and m == 0 and d == 0: + self.year = UNDEF + self.month = UNDEF + self.day = UNDEF + else: + self.year = y + self.month = m-1 + self.day = d + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +def not_too_old(date): + time_struct = time.localtime(time.time()) + current_year = time_struct[0] + if date.year != UNDEF and current_year - date.year > 110: + return 0 + return 1 + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +def compare_dates(f,s): + if f.calendar != s.calendar: + return 1 + if f.range == -1 and s.range == -1: + return cmp(f.text,s.text) + if f.range == -1 or s.range == -1: + return -1 + + first = f.get_start_date() + second = s.get_start_date() + if first.year != second.year: + return cmp(first.year,second.year) + elif first.month != second.month: + return cmp(first.month,second.month) + elif f.range != 1: + return cmp(first.day,second.day) + else: + first = f.get_stop_date() + second = s.get_stop_date() + if first.year != second.year: + return cmp(first.year,second.year) + elif first.month != second.month: + return cmp(first.month,second.month) + else: + return cmp(first.day,second.day) + +_func = SingleDate.fmtFunc[0] + diff --git a/gramps2/src/DateEdit.py b/gramps2/src/DateEdit.py new file mode 100644 index 000000000..b87920a97 --- /dev/null +++ b/gramps2/src/DateEdit.py @@ -0,0 +1,83 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2002 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +""" +The DateEdit interface provides visual feedback to the user via a pixamp +to indicate if the assocated GtkEntry box contains a valid date. Green +means complete and valid date. Yellow means a valid, but incomplete date. +Red means that the date is not valid, and will be viewed as a text string +instead of a date. +""" + +__author__ = "Donald N. Allingham" +__version__ = "$Revision$" + +#------------------------------------------------------------------------- +# +# GNOME modules +# +#------------------------------------------------------------------------- +import gtk +import gtk.gdk + +#------------------------------------------------------------------------- +# +# gramps modules +# +#------------------------------------------------------------------------- +import Date +import const + +#------------------------------------------------------------------------- +# +# DateEdit +# +#------------------------------------------------------------------------- +class DateEdit: + """Class that associates a pixmap with a text widget, providing visual + feedback that indicates if the text widget contains a valid date""" + + good = gtk.gdk.pixbuf_new_from_file(const.good_xpm) + bad = gtk.gdk.pixbuf_new_from_file(const.bad_xpm) + caution = gtk.gdk.pixbuf_new_from_file(const.caution_xpm) + + def __init__(self,text_obj,pixmap_obj): + """Creates a connection between the text_obj and the pixmap_obj""" + + self.text_obj = text_obj + self.pixmap_obj = pixmap_obj + self.checkval = Date.Date() + self.text_obj.connect('focus-out-event',self.check) + self.check(None,None) + + def check(self,obj,val): + """Called with the text box loses focus. If the string contains a + valid date, sets the appropriate pixmap""" + + text = self.text_obj.get_text() + self.checkval.set(text) + if not self.checkval.isValid(): + self.pixmap_obj.set_from_pixbuf(DateEdit.bad) + elif self.checkval.getIncomplete(): + self.pixmap_obj.set_from_pixbuf(DateEdit.caution) + else: + self.pixmap_obj.set_from_pixbuf(DateEdit.good) + + diff --git a/gramps2/src/DbPrompter.py b/gramps2/src/DbPrompter.py new file mode 100644 index 000000000..7551e50ee --- /dev/null +++ b/gramps2/src/DbPrompter.py @@ -0,0 +1,139 @@ +#! /usr/bin/python -O +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +#------------------------------------------------------------------------- +# +# GNOME modules +# +#------------------------------------------------------------------------- +import gtk +import gtk.glade + +#------------------------------------------------------------------------- +# +# gramps modules +# +#------------------------------------------------------------------------- +import Utils +import const +import GrampsCfg +import VersionControl +from intl import gettext +_ = gettext + +try: + import ZODB + _zodb = 1 +except: + _zodb = 0 + +#------------------------------------------------------------------------- +# +# DbPrompter +# +#------------------------------------------------------------------------- +class DbPrompter: + """Make sure a database is opened""" + + def __init__(self,db,want_new): + self.db = db + self.want_new = want_new + self.show() + + def show(self): + opendb = gtk.glade.XML(const.gladeFile, "opendb") + opendb.signal_autoconnect({ + "on_open_ok_clicked" : self.open_ok_clicked, + "on_open_cancel_clicked" : self.open_cancel_clicked, + "on_opendb_delete_event": self.open_delete_event, + }) + self.new = opendb.get_widget("new") + self.zodb = opendb.get_widget("zodb") + if self.want_new: + self.new.set_active(1) + if _zodb: + self.zodb.show() + + def open_ok_clicked(self,obj): + if self.new.get_active(): + self.db.clear_database(0) + self.save_as_activate() + elif self.zodb.get_active(): + self.db.clear_database(1) + self.save_as_activate() + else: + self.open_activate() + Utils.destroy_passed_object(obj) + + def save_as_activate(self): + wFs = gtk.glade.XML (const.gladeFile, "fileselection") + wFs.signal_autoconnect({ + "on_ok_button1_clicked": self.save_ok_button_clicked, + "destroy_passed_object": self.cancel_button_clicked, + }) + + def save_ok_button_clicked(self,obj): + filename = obj.get_filename() + if filename: + Utils.destroy_passed_object(obj) + if GrampsCfg.usevc and GrampsCfg.vc_comment: + self.db.display_comment_box(filename) + else: + self.db.save_file(filename,_("No Comment Provided")) + + def open_activate(self): + wFs = gtk.glade.XML(const.revisionFile, "dbopen") + wFs.signal_autoconnect({ + "on_ok_button1_clicked": self.ok_button_clicked, + "destroy_passed_object": self.cancel_button_clicked, + }) + + self.fileSelector = wFs.get_widget("dbopen") + self.dbname = wFs.get_widget("dbname") + self.getoldrev = wFs.get_widget("getoldrev") + self.dbname.set_default_path(GrampsCfg.db_dir) + self.getoldrev.set_sensitive(GrampsCfg.usevc) + + def cancel_button_clicked(self,obj): + Utils.destroy_passed_object(obj) + self.show() + + def ok_button_clicked(self,obj): + filename = self.dbname.get_full_path(0) + + if not filename: + return + + Utils.destroy_passed_object(obj) + + if self.getoldrev.get_active(): + vc = VersionControl.RcsVersionControl(filename) + VersionControl.RevisionSelect(self.db.database,filename,vc, + self.db.load_revision,self.show) + else: + self.db.read_file(filename) + + def open_delete_event(self,obj,event): + gtk.mainquit() + + def open_cancel_clicked(self,obj): + gtk.mainquit() + diff --git a/gramps2/src/DisplayTrace.py b/gramps2/src/DisplayTrace.py new file mode 100644 index 000000000..cc796ea1c --- /dev/null +++ b/gramps2/src/DisplayTrace.py @@ -0,0 +1,73 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2002 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +#------------------------------------------------------------------------- +# +# Standard python modules +# +#------------------------------------------------------------------------- +import cStringIO +import traceback +import sys + +#------------------------------------------------------------------------- +# +# GTK/GNOME modules +# +#------------------------------------------------------------------------- +import gtk.glade + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +import const +import intl + +_ = intl.gettext + +#------------------------------------------------------------------------- +# +# DisplayTrace +# +#------------------------------------------------------------------------- +class DisplayTrace: + + def __init__(self): + data = sys.exc_info() + msg = cStringIO.StringIO() + msg.write(_('GRAMPS has encountered an internal error.\n' + 'Please copy the message below and post a bug report ' + 'at http://sourceforge.net/projects/gramps or send an ' + 'email message to gramps-users@lists.sourceforge.net\n\n')) + + traceback.print_exception(data[0],data[1],data[2],None,msg) + + self.glade = gtk.glade.XML(const.pluginsFile,"plugstat") + self.top = self.glade.get_widget("plugstat") + window = self.glade.get_widget("text") + self.top.set_title(_('Internal Error - GRAMPS')) + + window.get_buffer().set_text(msg.getvalue()) +# self.top.run_and_close() + + + diff --git a/gramps2/src/DrawDoc.py b/gramps2/src/DrawDoc.py new file mode 100644 index 000000000..07004dfaa --- /dev/null +++ b/gramps2/src/DrawDoc.py @@ -0,0 +1,153 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +#------------------------------------------------------------------------ +# +# python modules +# +#------------------------------------------------------------------------ +import string +import os + +#------------------------------------------------------------------------ +# +# gramps modules +# +#------------------------------------------------------------------------ +from TextDoc import * + +#------------------------------------------------------------------------ +# +# GraphicsStyle +# +#------------------------------------------------------------------------ +class GraphicsStyle: + def __init__(self,obj=None): + if obj: + self.height = obj.height + self.width = obj.width + self.para_name = obj.para_name + self.shadow = obj.shadow + self.color = obj.color + else: + self.height = 0 + self.width = 0 + self.para_name = "" + self.shadow = 0 + self.color = (255,255,255) + + def set_height(self,val): + self.height = val + + def set_width(self,val): + self.width = val + + def set_paragraph_style(self,val): + self.para_name = val + + def set_shadow(self,val): + self.shadow = val + + def set_color(self,val): + self.color = val + + def get_height(self): + return self.height + + def get_width(self): + return self.width + + def get_paragraph_style(self): + return self.para_name + + def get_shadow(self): + return self.shadow + + def get_color(self): + return self.color + +#------------------------------------------------------------------------ +# +# DrawDoc +# +#------------------------------------------------------------------------ +class DrawDoc: + def __init__(self,styles,type,orientation=PAPER_PORTRAIT): + self.orientation = orientation + if orientation == PAPER_PORTRAIT: + self.width = type.get_width() + self.height = type.get_height() + else: + self.width = type.get_height() + self.height = type.get_width() + self.tmargin = 2.54 + self.bmargin = 2.54 + self.lmargin = 2.54 + self.rmargin = 2.54 + + self.style_list = styles.get_styles() + self.draw_styles = {} + self.name = "" + + def get_usable_width(self): + return self.width - (self.rmargin + self.lmargin) + + def get_usable_height(self): + return self.height - (self.tmargin + self.bmargin) + + def get_right_margin(self): + return self.rmargin + + def get_left_margin(self): + return self.lmargin + + def get_top_margin(self): + return self.tmargin + + def get_bottom_margin(self): + return self.bmargin + + def creator(self,name): + self.name = name + + def add_draw_style(self,name,style): + self.draw_styles[name] = GraphicsStyle(style) + + def open(self,filename): + pass + + def close(self): + pass + + def start_page(self,orientation=None): + pass + + def end_page(self): + pass + + def draw_box(self,style,text,x,y): + pass + + def write_at(self,style,text,x,y): + pass + + def draw_line(self,style,x1,y1,x2,y2): + pass + diff --git a/gramps2/src/EditPerson.py b/gramps2/src/EditPerson.py new file mode 100644 index 000000000..3dca00f1f --- /dev/null +++ b/gramps2/src/EditPerson.py @@ -0,0 +1,1516 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +#------------------------------------------------------------------------- +# +# Standard python modules +# +#------------------------------------------------------------------------- +import string +import pickle + +#------------------------------------------------------------------------- +# +# GTK/Gnome modules +# +#------------------------------------------------------------------------- +import gobject +import gtk +import gnome.ui +import gtk.glade + +#------------------------------------------------------------------------- +# +# gramps modules +# +#------------------------------------------------------------------------- +import const +import Utils +import GrampsCfg +import Date +from RelLib import * +import ImageSelect +import sort +import AutoComp +from DateEdit import DateEdit +from QuestionDialog import QuestionDialog + +from intl import gettext as _ + +_temple_names = const.lds_temple_codes.keys() +_temple_names.sort() +_temple_names = [""] + _temple_names + +pycode_tgts = [('url', 0, 0), + ('pevent', 0, 1), + ('pattr', 0, 2), + ('paddr', 0, 3)] + +#------------------------------------------------------------------------- +# +# EditPerson class +# +#------------------------------------------------------------------------- +class EditPerson: + + def __init__(self,person,db,callback=None): + """Creates an edit window. Associates a person with the window.""" + self.person = person + self.original_id = person.getId() + self.db = db + self.callback = callback + self.path = db.getSavePath() + self.not_loaded = 1 + self.lds_not_loaded = 1 + self.lists_changed = 0 + self.update_birth = 0 + self.update_death = 0 + self.pmap = {} + self.add_places = [] + + for key in db.getPlaceKeys(): + p = db.getPlaceDisplay(key) + self.pmap[p[0]] = key + + self.load_obj = None + self.top = gtk.glade.XML(const.editPersonFile, "editPerson") + self.icon_list = self.top.get_widget("iconlist") + self.gallery = ImageSelect.Gallery(person, self.path, self.icon_list,self.db,self) + + self.name_delete_btn = self.top.get_widget('aka_delete') + self.web_delete_btn = self.top.get_widget('delete_url') + self.event_delete_btn = self.top.get_widget('event_delete_btn') + self.attr_delete_btn = self.top.get_widget('attr_delete_btn') + self.addr_delete_btn = self.top.get_widget('addr_delete_btn') + + self.top.signal_autoconnect({ + "destroy_passed_object" : self.on_cancel_edit, + "on_up_clicked" : self.on_up_clicked, + "on_down_clicked" : self.on_down_clicked, + "on_add_address_clicked" : self.on_add_addr_clicked, + "on_add_aka_clicked" : self.on_add_aka_clicked, + "on_add_attr_clicked" : self.on_add_attr_clicked, + "on_add_url_clicked" : self.on_add_url_clicked, + "on_addr_button_press" : self.addr_double_click, + "on_web_button_press" : self.url_double_click, + "on_addphoto_clicked" : self.gallery.on_add_photo_clicked, + "on_aka_delete_clicked" : self.on_aka_delete_clicked, + "on_aka_update_clicked" : self.on_aka_update_clicked, + "on_apply_person_clicked" : self.on_apply_person_clicked, + "on_attr_button_press" : self.attr_double_click, + "on_edit_birth_clicked" : self.on_edit_birth_clicked, + "on_edit_death_clicked" : self.on_edit_death_clicked, + "on_delete_address_clicked" : self.on_delete_addr_clicked, + "on_delete_attr_clicked" : self.on_delete_attr_clicked, + "on_delete_event" : self.on_delete_event, + "on_delete_url_clicked" : self.on_delete_url_clicked, + "on_deletephoto_clicked" : self.gallery.on_delete_photo_clicked, + "on_edit_properties_clicked": self.gallery.popup_change_description, + "on_editperson_switch_page" : self.on_switch_page, + "on_event_add_clicked" : self.on_event_add_clicked, + "on_event_button_press" : self.event_double_click, + "on_event_delete_clicked" : self.on_event_delete_clicked, + "on_event_update_clicked" : self.on_event_update_clicked, + "on_name_button_press" : self.aka_double_click, + "on_name_note_clicked" : self.on_name_note_clicked, + "on_ldsbap_note_clicked" : self.on_ldsbap_note_clicked, + "on_ldsendow_note_clicked" : self.on_ldsendow_note_clicked, + "on_ldsseal_note_clicked" : self.on_ldsseal_note_clicked, + "on_ldsbap_src_clicked" : self.on_ldsbap_source_clicked, + "on_ldsendow_src_clicked" : self.on_ldsendow_source_clicked, + "on_ldsseal_src_clicked" : self.on_ldsseal_source_clicked, + "on_name_source_clicked" : self.on_primary_name_source_clicked, + "on_photolist_button_press_event" : self.gallery.on_button_press_event, + "on_photolist_select_icon" : self.gallery.on_photo_select_icon, + "on_update_address_clicked" : self.on_update_addr_clicked, + "on_update_attr_clicked" : self.on_update_attr_clicked, + "on_update_url_clicked" : self.on_update_url_clicked, + "on_web_go_clicked" : self.on_web_go_clicked, + }) + + self.window = self.get_widget("editPerson") + self.notes_field = self.get_widget("personNotes") + self.event_name_field = self.get_widget("eventName") + self.event_place_field = self.get_widget("eventPlace") + self.event_cause_field = self.get_widget("eventCause") + self.event_date_field = self.get_widget("eventDate") + self.event_descr_field = self.get_widget("eventDescription") + self.event_src_field = self.get_widget("event_srcinfo") + self.event_conf_field = self.get_widget("event_conf") + self.attr_conf_field = self.get_widget("attr_conf") + self.addr_conf_field = self.get_widget("attr_conf") + self.name_conf_field = self.get_widget("name_conf") + self.attr_src_field = self.get_widget("attr_srcinfo") + self.name_src_field = self.get_widget("name_srcinfo") + self.addr_src_field = self.get_widget("addr_srcinfo") + self.attr_list = self.get_widget("attr_list") + self.attr_type = self.get_widget("attr_type") + self.attr_value = self.get_widget("attr_value") + self.web_list = self.get_widget("web_list") + self.web_url = self.get_widget("web_url") + self.web_go = self.get_widget("web_go") + self.web_description = self.get_widget("url_des") + self.addr_label = self.get_widget("address_label") + self.addr_list = self.get_widget("address_list") + self.addr_start = self.get_widget("address_start") + self.addr_street = self.get_widget("street") + self.addr_city = self.get_widget("city") + self.addr_state = self.get_widget("state") + self.addr_country = self.get_widget("country") + self.addr_postal = self.get_widget("postal") + self.event_list = self.get_widget("eventList") + self.edit_person = self.get_widget("editPerson") + self.name_list = self.get_widget("nameList") + self.name_frame = self.get_widget("name_frame") + self.alt_given_field = self.get_widget("alt_given") + self.alt_last_field = self.get_widget("alt_last") + self.alt_title_field = self.get_widget("alt_title") + self.alt_suffix_field = self.get_widget("alt_suffix") + self.name_type_field = self.get_widget("name_type") + self.surname_field = self.get_widget("surname") + self.ntype_field = self.get_widget("ntype") + self.suffix = self.get_widget("suffix") + self.given = self.get_widget("givenName") + self.nick = self.get_widget("nickname") + self.title = self.get_widget("title") + self.bdate = self.get_widget("birthDate") + self.bplace = self.get_widget("birthPlace") + self.bpcombo = self.get_widget("bpcombo") + self.dpcombo = self.get_widget("dpcombo") + self.sncombo = self.get_widget("sncombo") + self.ddate = self.get_widget("deathDate") + self.dplace = self.get_widget("deathPlace") + self.is_male = self.get_widget("genderMale") + self.is_female = self.get_widget("genderFemale") + self.is_unknown = self.get_widget("genderUnknown") + self.addr_note = self.get_widget("addr_note") + self.addr_source = self.get_widget("addr_source") + self.attr_note = self.get_widget("attr_note") + self.attr_source = self.get_widget("attr_source") + self.name_note = self.get_widget("name_note") + self.name_source = self.get_widget("name_source") + self.gid = self.get_widget("gid") + + self.death = Event(person.getDeath()) + self.birth = Event(person.getBirth()) + self.pname = Name(person.getPrimaryName()) + + self.elist = person.getEventList()[:] + self.nlist = person.getAlternateNames()[:] + self.alist = person.getAttributeList()[:] + self.ulist = person.getUrlList()[:] + self.plist = person.getAddressList()[:] + + # event display + self.event_model = gtk.ListStore(gobject.TYPE_STRING,gobject.TYPE_STRING, + gobject.TYPE_STRING,gobject.TYPE_STRING) + + self.build_columns(self.event_list, [(_('Event'),150), (_('Description'),150), + (_('Date'),100), (_('Place'),100)]) + self.event_list.set_model(self.event_model) + self.event_list.get_selection().connect('changed',self.on_event_select_row) + + # attribute display + self.attr_model = gtk.ListStore(gobject.TYPE_STRING,gobject.TYPE_STRING) + self.build_columns(self.attr_list, [(_('Attribute'),150),(_('Value'),150)]) + self.attr_list.set_model(self.attr_model) + self.attr_list.get_selection().connect('changed',self.on_attr_select_row) + + # address display + self.addr_model = gtk.ListStore(gobject.TYPE_STRING,gobject.TYPE_STRING, + gobject.TYPE_STRING) + self.build_columns(self.addr_list, [(_('Date'),150),(_('Address'),150)]) + self.addr_list.set_model(self.addr_model) + self.addr_list.get_selection().connect('changed',self.on_addr_select_row) + + # name display + self.name_model = gtk.ListStore(gobject.TYPE_STRING,gobject.TYPE_STRING) + self.build_columns(self.name_list,[(_('Name'),250),(_('Type'),100)]) + self.name_list.set_model(self.name_model) + self.name_list.get_selection().connect('changed',self.on_name_select_row) + + # web display + self.web_model = gtk.ListStore(gobject.TYPE_STRING,gobject.TYPE_STRING) + self.build_columns(self.web_list, [(_('Path'),250),(_('Description'),100)]) + self.web_list.set_model(self.web_model) + self.web_list.get_selection().connect('changed',self.on_web_select_row) + + self.autoplace = AutoComp.AutoCombo(self.bpcombo,self.pmap.keys()) + self.autodeath = AutoComp.AutoCombo(self.dpcombo,self.pmap.keys(), + self.autoplace) + self.comp = AutoComp.AutoCombo(self.sncombo,self.db.getSurnames()) + + self.gid.set_text(person.getId()) + self.gid.set_editable(GrampsCfg.id_edit) + + self.event_list = self.get_widget("eventList") + + if GrampsCfg.display_attr: + self.get_widget("user_label").set_text(GrampsCfg.attr_name) + val = "" + for attr in self.person.getAttributeList(): + if attr.getType() == const.save_pattr(GrampsCfg.attr_name): + val = attr.getValue() + break + self.get_widget("user_data").set_text(val) + self.get_widget("user_colon").show() + else: + self.get_widget("user_label").hide() + self.get_widget("user_colon").hide() + self.get_widget("user_data").hide() + + self.lds_baptism = LdsOrd(self.person.getLdsBaptism()) + self.lds_endowment = LdsOrd(self.person.getLdsEndowment()) + self.lds_sealing = LdsOrd(self.person.getLdsSeal()) + + if GrampsCfg.uselds or self.lds_baptism or self.lds_endowment or self.lds_sealing: + self.get_widget("lds_tab").show() + self.get_widget("lds_page").show() + + types = const.NameTypesMap.keys() + types.sort() + self.ntype_field.set_popdown_strings(types) + self.autotype = AutoComp.AutoEntry(self.ntype_field.entry,types) + self.write_primary_name() + + if person.getGender() == Person.male: + self.is_male.set_active(1) + elif person.getGender() == Person.female: + self.is_female.set_active(1) + else: + self.is_unknown.set_active(1) + + self.nick.set_text(person.getNickName()) + self.update_birth_death() + + self.load_person_image() + + # set notes data + self.notes_buffer = self.notes_field.get_buffer() + self.notes_buffer.set_text(person.getNote()) + + self.event_list.drag_dest_set(gtk.DEST_DEFAULT_ALL,pycode_tgts,gtk.gdk.ACTION_COPY) + self.event_list.drag_source_set(gtk.gdk.BUTTON1_MASK, pycode_tgts, gtk.gdk.ACTION_COPY) + self.event_list.connect('drag_data_get', self.ev_drag_data_get) + self.event_list.connect('drag_data_received', + self.ev_drag_data_received) + +# self.web_list.drag_dest_set(gtk.DEST_DEFAULT_ALL, +# pycode_tgts,ACTION_COPY) +# self.web_list.drag_source_set(BUTTON1_MASK, pycode_tgts, gtk.gdk.ACTION_COPY) +# self.web_list.connect('drag_data_get', self.url_drag_data_get) +# self.web_list.connect('drag_data_received', +# self.url_drag_data_received) + +# self.attr_list.drag_dest_set(gtk.DEST_DEFAULT_ALL,pycode_tgts, +# gtk.gdk.ACTION_COPY) +# self.attr_list.drag_source_set(gtk.gdk.BUTTON1_MASK, pycode_tgts, +# gtk.gdk.ACTION_COPY) +# self.attr_list.connect('drag_data_get', self.at_drag_data_get) +# self.attr_list.connect('drag_data_received', +# self.at_drag_data_received) + +# self.addr_list.drag_dest_set(gtk.DEST_DEFAULT_ALL, +# pycode_tgts,ACTION_COPY) +# self.addr_list.drag_source_set(gtk.gdk.BUTTON1_MASK, pycode_tgts, +# gtk.gdk.ACTION_COPY) +# self.addr_list.connect('drag_data_get', self.ad_drag_data_get) +# self.addr_list.connect('drag_data_received', +# self.ad_drag_data_received) + + self.redraw_event_list() + self.redraw_attr_list() + self.redraw_addr_list() + self.redraw_name_list() + self.redraw_url_list() + + def build_columns(self,tree,list): + cnum = 0 + for name in list: + renderer = gtk.CellRendererText() + column = gtk.TreeViewColumn(name[0],renderer,text=cnum) + column.set_min_width(name[1]) + cnum = cnum + 1 + tree.append_column(column) + + def lds_field(self,ord,combo,date,place): + combo.set_popdown_strings(_temple_names) + if ord: + stat = ord.getStatus() + date.set_text(ord.getDate()) + if ord.getTemple() != "": + name = const.lds_temple_to_abrev[ord.getTemple()] + else: + name = "" + combo.entry.set_text(name) + else: + stat = 0 + combo.entry.set_text("") + +# AutoComp.AutoEntry(place,None,self.autoplace) + if ord and ord.getPlace(): + place.set_text(ord.getPlace().get_title()) + return stat + + def draw_lds(self): + """Draws the LDS window. This window is not always drawn, and in + may cases is hidden.""" + + self.ldsbap_date = self.get_widget("ldsbapdate") + self.ldsbap_temple = self.get_widget("ldsbaptemple") + self.ldsend_date = self.get_widget("endowdate") + self.ldsend_temple = self.get_widget("endowtemple") + self.ldsseal_date = self.get_widget("sealdate") + self.ldsseal_temple = self.get_widget("sealtemple") + self.ldsseal_fam = self.get_widget("sealparents") + self.ldsbapstat = self.get_widget("ldsbapstat") + self.ldssealstat = self.get_widget("sealstat") + self.ldsendowstat = self.get_widget("endowstat") + self.ldsbapplace = self.get_widget("lds_bap_place") + self.ldssealplace = self.get_widget("lds_seal_place") + self.ldsendowplace = self.get_widget("lds_end_place") + + self.bstat = self.lds_field(self.lds_baptism, + self.ldsbap_temple, + self.ldsbap_date, + self.ldsbapplace) + + self.estat = self.lds_field(self.lds_endowment, + self.ldsend_temple, + self.ldsend_date, + self.ldsendowplace) + + self.seal_stat = self.lds_field(self.lds_sealing, + self.ldsseal_temple, + self.ldsseal_date, + self.ldssealplace) + if self.lds_sealing: + self.ldsfam = self.lds_sealing.getFamily() + else: + self.ldsfam = None + + myMenu = gtk.Menu() + item = gtk.MenuItem(_("None")) + item.set_data("f",None) + item.connect("activate",self.menu_changed) + item.show() + myMenu.append(item) + + index = 0 + hist = 0 + flist = [self.person.getMainParents()] + for (fam,mrel,frel) in self.person.getParentList(): + flist.append(fam) + for fam in flist: + if fam == None: + continue + f = fam.getFather() + m = fam.getMother() + if f and m: + name = _("%(father)s and %(mother)s") % { + 'father' : GrampsCfg.nameof(f), + 'mother' : GrampsCfg.nameof(m) } + elif f: + name = GrampsCfg.nameof(f) + elif m: + name = GrampsCfg.nameof(m) + else: + name = _("unknown") + item = gtk.MenuItem(name) + item.set_data("f",fam) + item.connect("activate",self.menu_changed) + item.show() + myMenu.append(item) + index = index + 1 + if fam == self.ldsfam: + hist = index + self.ldsseal_fam.set_menu(myMenu) + self.ldsseal_fam.set_history(hist) + + self.build_bap_menu() + self.build_seal_menu() + self.build_endow_menu() + + def build_menu(self,list,task,opt_menu): + menu = gtk.Menu() + index = 0 + for val in list: + menuitem = gtk.MenuItem(val) + menuitem.set_data("val",index) + menuitem.connect('activate',task) + menuitem.show() + menu.append(menuitem) + index = index + 1 + opt_menu.set_menu(menu) + opt_menu.set_history(self.bstat) + + def build_bap_menu(self): + self.build_menu(const.lds_baptism,self.set_lds_bap,self.ldsbapstat) + + def build_endow_menu(self): + self.build_menu(const.lds_baptism,self.set_lds_endow,self.ldsendowstat) + + def build_seal_menu(self): + self.build_menu(const.lds_csealing,self.set_lds_seal,self.ldssealstat) + + def set_lds_bap(self,obj): + self.bstat = obj.get_data("val") + + def set_lds_endow(self,obj): + self.estat = obj.get_data("val") + + def set_lds_seal(self,obj): + self.seal_stat = obj.get_data("val") + + def ev_drag_data_received(self,widget,context,x,y,sel_data,info,time): + if sel_data and sel_data.data: + exec 'data = %s' % sel_data.data + exec 'mytype = "%s"' % data[0] + exec 'person = "%s"' % data[1] + if person == self.person.getId() or mytype != 'pevent': + return + foo = pickle.loads(data[2]); + for src in foo.getSourceRefList(): + base = src.getBase() + newbase = self.db.findSourceNoMap(base.getId()) + src.setBase(newbase) + place = foo.getPlace() + if place: + foo.setPlace(self.db.findPlaceNoMap(place.getId())) + self.elist.append(foo) + self.lists_changed = 1 + self.redraw_event_list() + + def ev_drag_data_get(self,widget, context, sel_data, info, time): + ev = widget.get_row_data(widget.focus_row) + + bits_per = 8; # we're going to pass a string + pickled = pickle.dumps(ev); + data = str(('pevent',self.person.getId(),pickled)); + sel_data.set(sel_data.target, bits_per, data) + + def url_drag_data_received(self,widget,context,x,y,sel_data,info,time): + if sel_data and sel_data.data: + exec 'data = %s' % sel_data.data + exec 'mytype = "%s"' % data[0] + exec 'person = "%s"' % data[1] + if person == self.person.getId() or mytype != 'url': + return + foo = pickle.loads(data[2]); + self.ulist.append(foo) + self.lists_changed = 1 + self.redraw_url_list() + + def url_drag_data_get(self,widget, context, sel_data, info, time): + ev = widget.get_row_data(widget.focus_row) + + bits_per = 8; # we're going to pass a string + pickled = pickle.dumps(ev); + data = str(('url',self.person.getId(),pickled)); + sel_data.set(sel_data.target, bits_per, data) + + def at_drag_data_received(self,widget,context,x,y,sel_data,info,time): + if sel_data and sel_data.data: + exec 'data = %s' % sel_data.data + exec 'mytype = "%s"' % data[0] + exec 'person = "%s"' % data[1] + if person == self.person.getId() or mytype != 'pattr': + return + foo = pickle.loads(data[2]); + for src in foo.getSourceRefList(): + base = src.getBase() + newbase = self.db.findSourceNoMap(base.getId()) + src.setBase(newbase) + self.alist.append(foo) + self.lists_changed = 1 + self.redraw_attr_list() + + def at_drag_data_get(self,widget, context, sel_data, info, time): + ev = widget.get_row_data(widget.focus_row) + + bits_per = 8; # we're going to pass a string + pickled = pickle.dumps(ev); + data = str(('pattr',self.person.getId(),pickled)); + sel_data.set(sel_data.target, bits_per, data) + + def ad_drag_data_received(self,widget,context,x,y,sel_data,info,time): + if sel_data and sel_data.data: + exec 'data = %s' % sel_data.data + exec 'mytype = "%s"' % data[0] + exec 'person = "%s"' % data[1] + if person == self.person.getId() or mytype != 'paddr': + return + foo = pickle.loads(data[2]); + for src in foo.getSourceRefList(): + base = src.getBase() + newbase = self.db.findSourceNoMap(base.getId()) + src.setBase(newbase) + self.plist.append(foo) + self.lists_changed = 1 + self.redraw_addr_list() + + def ad_drag_data_get(self,widget, context, sel_data, info, time): + ev = widget.get_row_data(widget.focus_row) + + bits_per = 8; # we're going to pass a string + pickled = pickle.dumps(ev); + data = str(('paddr',self.person.getId(),pickled)); + sel_data.set(sel_data.target, bits_per, data) + + def menu_changed(self,obj): + self.ldsfam = obj.get_data("f") + + def get_widget(self,str): + """returns the widget related to the passed string""" + return self.top.get_widget(str) + + def redraw_name_list(self): + """redraws the name list""" + Utils.redraw_list(self.nlist,self.name_model,disp_name) + + def redraw_url_list(self): + """redraws the url list, disabling the go button if no url + is selected""" + length = Utils.redraw_list(self.ulist,self.web_model,disp_url) + if length > 0: + #self.web_go.set_sensitive(1) + self.web_go.set_sensitive(0) + else: + self.web_go.set_sensitive(0) + self.web_url.set_text("") + self.web_description.set_text("") + + def redraw_attr_list(self): + """Redraws the attribute list""" + Utils.redraw_list(self.alist,self.attr_model,disp_attr) + + def redraw_addr_list(self): + """redraws the address list for the person""" + Utils.redraw_list(self.plist,self.addr_model,disp_addr) + + def redraw_event_list(self): + """redraw_event_list - Update both the birth and death place combo + boxes for any changes that occurred in the 'Event Edit' window. + Make sure not to allow the editing of a birth event to change + any values in the death event, and vice versa. Since updating a + combo list resets its present value, this code will have to save + and restore the value for the event *not* being edited.""" + + Utils.redraw_list(self.elist,self.event_model,disp_event) + + # Remember old combo list input + prev_btext = Utils.strip_id(self.bplace.get_text()) + prev_dtext = Utils.strip_id(self.dplace.get_text()) + + # Update birth with new values, make sure death values don't change + if self.update_birth: + self.update_birth = 0 + self.update_birth_info() + self.dplace.set_text(prev_dtext) + self.bdate_check = DateEdit(self.bdate,self.get_widget("birth_stat")) + + # Update death with new values, make sure birth values don't change + if self.update_death: + self.update_death = 0 + self.update_death_info() + self.bplace.set_text(prev_btext) + self.ddate_check = DateEdit(self.ddate,self.get_widget("death_stat")) + + def on_add_addr_clicked(self,obj): + """Invokes the address editor to add a new address""" + import AddrEdit + AddrEdit.AddressEditor(self,None) + + def on_add_aka_clicked(self,obj): + """Invokes the name editor to add a new name""" + import NameEdit + NameEdit.NameEditor(self,None) + + def on_add_url_clicked(self,obj): + """Invokes the url editor to add a new name""" + import UrlEdit + pname = self.person.getPrimaryName().getName() + UrlEdit.UrlEditor(self,pname,None) + + def on_add_attr_clicked(self,obj): + """Brings up the AttributeEditor for a new attribute""" + import AttrEdit + pname = self.person.getPrimaryName().getName() + AttrEdit.AttributeEditor(self,None,pname,const.personalAttributes) + + def on_up_clicked(self,obj): + sel = obj.get_selection() + store,iter = sel.get_selected() + if iter: + row = store.get_path(iter) + sel.select_path((row[0]-1)) + + def on_down_clicked(self,obj): + sel = obj.get_selection() + store,iter = sel.get_selected() + if iter: + row = store.get_path(iter) + sel.select_path((row[0]+1)) + + def on_event_add_clicked(self,obj): + """Brings up the EventEditor for a new event""" + import EventEdit + pname = self.person.getPrimaryName().getName() + EventEdit.EventEditor(self,pname,const.personalEvents, + const.save_fevent,None,None,0,self.callback) + + def on_edit_birth_clicked(self,obj): + """Brings up the EventEditor for the birth record, event + name cannot be changed""" + + import EventEdit + self.update_birth = 1 + pname = self.person.getPrimaryName().getName() + event = self.birth + event.setDate(self.bdate.get_text()) + def_placename = self.bplace.get_text() + p = self.get_place(self.bplace) + if p: + event.setPlace(p) + EventEdit.EventEditor(self,pname,const.personalEvents, + const.save_fevent,event,def_placename,1, + self.callback) + + def on_edit_death_clicked(self,obj): + """Brings up the EventEditor for the death record, event + name cannot be changed""" + + import EventEdit + self.update_death = 1 + pname = self.person.getPrimaryName().getName() + event = self.death + event.setDate(self.ddate.get_text()) + def_placename = self.dplace.get_text() + p = self.get_place(self.dplace) + if p: + event.setPlace(p) + EventEdit.EventEditor(self,pname,const.personalEvents, + const.save_fevent,event,def_placename,1, + self.callback) + + def on_aka_delete_clicked(self,obj): + """Deletes the selected name from the name list""" + if Utils.delete_selected(obj,self.nlist): + self.lists_changed = 1 + self.redraw_name_list() + + def on_delete_url_clicked(self,obj): + """Deletes the selected URL from the URL list""" + if Utils.delete_selected(obj,self.ulist): + self.lists_changed = 1 + self.redraw_url_list() + + def on_delete_attr_clicked(self,obj): + """Deletes the selected attribute from the attribute list""" + if Utils.delete_selected(obj,self.alist): + self.lists_changed = 1 + self.redraw_attr_list() + + def on_delete_addr_clicked(self,obj): + """Deletes the selected address from the address list""" + if Utils.delete_selected(obj,self.plist): + self.lists_changed = 1 + self.redraw_addr_list() + + def on_web_go_clicked(self,obj): + """Attempts to display the selected URL in a web browser""" + import gnome.url + text = obj.get() + if text: + gnome.url.show(text) + + def on_cancel_edit(self,obj): + """If the data has changed, give the user a chance to cancel + the close window""" + if self.did_data_change(): + QuestionDialog(_('Abandon Changes'), + _("Are you sure you want to abandon your changes?"), + _("Abandon Changes"), + self.cancel_callback, + _("Continue Editing")) + else: + Utils.destroy_passed_object(obj) + + def on_delete_event(self,obj,b): + """If the data has changed, give the user a chance to cancel + the close window""" + if self.did_data_change(): + QuestionDialog(_('Abandon Changes'), + _("Are you sure you want to abandon your changes?"), + _("Abandon Changes"), + self.cancel_callback, + _("Continue Editing")) + return 1 + else: + Utils.destroy_passed_object(obj) + return 0 + + def cancel_callback(self): + """If the user answered yes to abandoning changes, close the window""" + Utils.destroy_passed_object(self.window) + + def did_data_change(self): + """Check to see if any of the data has changed from the + original record""" + surname = self.surname_field.get_text() + ntype = self.ntype_field.entry.get_text() + suffix = self.suffix.get_text() + given = self.given.get_text() + nick = self.nick.get_text() + title = self.title.get_text() + male = self.is_male.get_active() + female = self.is_female.get_active() + unknown = self.is_unknown.get_active() + text = self.notes_buffer.get_text(self.notes_buffer.get_start_iter(), + self.notes_buffer.get_end_iter(),gtk.FALSE) + idval = self.gid.get_text() + + changed = 0 + name = self.person.getPrimaryName() + + if self.person.getId() != idval: + changed = 1 + if suffix != name.getSuffix(): + changed = 1 + if surname != name.getSurname(): + changed = 1 + if ntype != name.getType(): + changed = 1 + if given != name.getFirstName(): + changed = 1 + if nick != self.person.getNickName(): + changed = 1 + if title != name.getTitle(): + changed = 1 + if self.pname.getNote() != name.getNote(): + changed = 1 + if self.lds_not_loaded == 0 and self.check_lds(): + changed == 1 + + bplace = string.strip(self.bplace.get_text()) + dplace = string.strip(self.dplace.get_text()) + + if self.pmap.has_key(bplace): + p1 = self.db.getPlaceMap()[self.pmap[bplace]] + else: + p1 = None + if bplace != "": + changed = 1 + self.birth.setPlace(p1) + + if self.pmap.has_key(dplace): + p1 = self.db.getPlaceMap()[self.pmap[dplace]] + else: + p1 = None + if dplace != "": + changed = 1 + self.death.setPlace(p1) + + if not self.birth.are_equal(self.person.getBirth()): + changed = 1 + if not self.death.are_equal(self.person.getDeath()): + changed = 1 + if male and self.person.getGender() != Person.male: + changed = 1 + elif female and self.person.getGender() != Person.female: + changed = 1 + elif unknown and self.person.getGender() != Person.unknown: + changed = 1 + if text != self.person.getNote() or self.lists_changed: + changed = 1 + + if self.lds_not_loaded == 0: + if not self.lds_baptism.are_equal(self.person.getLdsBaptism()): + changed= 1 + + if not self.lds_endowment.are_equal(self.person.getLdsEndowment()): + changed = 1 + + if not self.lds_sealing.are_equal(self.person.getLdsSeal()): + changed = 1 + + return changed + + def check_lds(self): + self.lds_baptism.setDate(self.ldsbap_date.get_text()) + temple = self.ldsbap_temple.entry.get_text() + if const.lds_temple_codes.has_key(temple): + self.lds_baptism.setTemple(const.lds_temple_codes[temple]) + else: + self.lds_baptism.setTemple("") + self.lds_baptism.setPlace(self.get_place(self.ldsbapplace,1)) + + self.lds_endowment.setDate(self.ldsend_date.get_text()) + temple = self.ldsend_temple.entry.get_text() + if const.lds_temple_codes.has_key(temple): + self.lds_endowment.setTemple(const.lds_temple_codes[temple]) + else: + self.lds_endowment.setTemple("") + self.lds_endowment.setPlace(self.get_place(self.ldsendowplace,1)) + + self.lds_sealing.setDate(self.ldsseal_date.get_text()) + temple = self.ldsseal_temple.entry.get_text() + if const.lds_temple_codes.has_key(temple): + self.lds_sealing.setTemple(const.lds_temple_codes[temple]) + else: + self.lds_sealing.setTemple("") + self.lds_sealing.setFamily(self.ldsfam) + self.lds_sealing.setPlace(self.get_place(self.ldssealplace,1)) + + def on_event_delete_clicked(self,obj): + """Delete the selected event""" + if Utils.delete_selected(obj,self.elist): + self.lists_changed = 1 + self.redraw_event_list() + + def update_birth_death(self): + self.bdate.set_text(self.birth.getDate()) + self.bplace.set_text(self.birth.getPlaceName()) + self.ddate.set_text(self.death.getDate()) + self.dplace.set_text(self.death.getPlaceName()) + self.dplace.set_text(self.death.getPlaceName()) +# self.bdate.set_position(0) +# self.ddate.set_position(0) +# self.bplace.set_position(0) +# self.dplace.set_position(0) + + def attr_double_click(self,obj,event): + if event.button == 1 and event.type == _2BUTTON_PRESS: + self.on_update_attr_clicked(obj) + + def on_update_attr_clicked(self,obj): + import AttrEdit + if obj.selection: + attr = obj.get_row_data(obj.selection[0]) + pname = self.person.getPrimaryName().getName() + AttrEdit.AttributeEditor(self,attr,pname,const.personalAttributes) + + def addr_double_click(self,obj,event): + if event.button == 1 and event.type == _2BUTTON_PRESS: + self.on_update_addr_clicked(obj) + + def on_update_addr_clicked(self,obj): + import AddrEdit + if obj.selection: + AddrEdit.AddressEditor(self,obj.get_row_data(obj.selection[0])) + + def url_double_click(self,obj,event): + if event.button == 1 and event.type == _2BUTTON_PRESS: + self.on_update_url_clicked(obj) + + def on_update_url_clicked(self,obj): + import UrlEdit + if obj.selection: + pname = self.person.getPrimaryName().getName() + url = obj.get_row_data(obj.selection[0]) + UrlEdit.UrlEditor(self,pname,url) + + def event_double_click(self,obj,event): + if event.button == 1 and event.type == _2BUTTON_PRESS: + self.on_event_update_clicked(obj) + + def on_event_update_clicked(self,obj): + import EventEdit + if obj.selection: + pname = self.person.getPrimaryName().getName() + event = obj.get_row_data(obj.selection[0]) + EventEdit.EventEditor(self,pname,const.personalEvents, + const.save_fevent,event,None,0, + self.callback) + + def on_event_select_row(self,obj): + store,iter = obj.get_selected() + if iter: + row = store.get_path(iter) + event = self.elist[row[0]] + self.event_date_field.set_text(event.getDate()) + self.event_place_field.set_text(event.getPlaceName()) + self.event_name_field.set_label(const.display_pevent(event.getName())) + self.event_cause_field.set_text(event.getCause()) + self.event_descr_field.set_text(event.getDescription()) + if len(event.getSourceRefList()) > 0: + psrc = event.getSourceRefList()[0] + self.event_src_field.set_text(psrc.getBase().getTitle()) + self.event_conf_field.set_text(const.confidence[psrc.getConfidence()]) + else: + self.event_src_field.set_text('') + self.event_conf_field.set_text('') + self.event_delete_btn.set_sensitive(1) + else: + self.event_date_field.set_text('') + self.event_place_field.set_text('') + self.event_name_field.set_label('') + self.event_cause_field.set_text('') + self.event_descr_field.set_text('') + self.event_src_field.set_text('') + self.event_conf_field.set_text('') + self.event_delete_btn.set_sensitive(0) + + def on_addr_select_row(self,obj): + store,iter = obj.get_selected() + if iter: + row = store.get_path(iter) + addr = self.plist[row[0]] + label = "%s %s %s" % (addr.getCity(),addr.getState(),addr.getCountry()) + self.addr_label.set_label(label) + self.addr_start.set_text(addr.getDate()) + self.addr_street.set_text(addr.getStreet()) + self.addr_city.set_text(addr.getCity()) + self.addr_state.set_text(addr.getState()) + self.addr_country.set_text(addr.getCountry()) + self.addr_postal.set_text(addr.getPostal()) + if len(addr.getSourceRefList()) > 0: + psrc = addr.getSourceRefList()[0] + self.addr_conf_field.set_text(const.confidence[psrc.getConfidence()]) + self.addr_src_field.set_text(psrc.getBase().getTitle()) + else: + self.addr_src_field.set_text('') + self.addr_conf_field.set_text('') + self.addr_delete_btn.set_sensitive(1) + else: + self.addr_label.set_label('') + self.addr_start.set_text('') + self.addr_street.set_text('') + self.addr_city.set_text('') + self.addr_state.set_text('') + self.addr_country.set_text('') + self.addr_postal.set_text('') + self.addr_conf_field.set_text('') + self.addr_src_field.set_text('') + self.addr_delete_btn.set_sensitive(0) + + def on_name_select_row(self,obj): + store,iter = obj.get_selected() + if iter: + row = store.get_path(iter) + name = self.nlist[row[0]] + self.name_frame.set_label(name.getName()) + self.alt_given_field.set_text(name.getFirstName()) + self.alt_title_field.set_text(name.getTitle()) + self.alt_last_field.set_text(name.getSurname()) + self.alt_suffix_field.set_text(name.getSuffix()) + self.name_type_field.set_text(name.getType()) + if len(name.getSourceRefList()) > 0: + psrc = name.getSourceRefList()[0] + self.name_src_field.set_text(psrc.getBase().getTitle()) + self.name_conf_field.set_text(const.confidence[psrc.getConfidence()]) + else: + self.name_src_field.set_text('') + self.name_conf_field.set_text('') + self.name_delete_btn.set_sensitive(1) + else: + self.name_frame.set_label('') + self.alt_given_field.set_text('') + self.alt_title_field.set_text('') + self.alt_last_field.set_text('') + self.alt_suffix_field.set_text('') + self.name_type_field.set_text('') + self.name_src_field.set_text('') + self.name_conf_field.set_text('') + self.name_delete_btn.set_sensitive(0) + + def on_web_select_row(self,obj): + store,iter = obj.get_selected() + if iter: + row = store.get_path(iter) + url = self.ulist[row[0]] + path = url.get_path() + self.web_url.set_text(path) + self.web_description.set_text(url.get_description()) + self.web_go.set_sensitive(0) + #self.web_go.set_sensitive(1) + self.web_delete_btn.set_sensitive(1) + else: + self.web_url.set_text('') + self.web_description.set_text('') + self.web_go.set_sensitive(0) + self.web_delete_btn.set_sensitive(0) + + def on_attr_select_row(self,obj): + store,iter = obj.get_selected() + if iter: + row = store.get_path(iter) + attr = self.alist[row[0]] + self.attr_type.set_label(const.display_pattr(attr.getType())) + self.attr_value.set_text(attr.getValue()) + if len(attr.getSourceRefList()) > 0: + psrc = attr.getSourceRefList()[0] + self.attr_src_field.set_text(psrc.getBase().getTitle()) + self.attr_conf_field.set_text(const.confidence[psrc.getConfidence()]) + else: + self.attr_src_field.set_text('') + self.attr_conf_field.set_text('') + self.attr_delete_btn.set_sensitive(1) + else: + self.attr_type.set_label('') + self.attr_value.set_text('') + self.attr_src_field.set_text('') + self.attr_conf_field.set_text('') + self.attr_delete_btn.set_sensitive(0) + + def aka_double_click(self,obj,event): + if event.button == 1 and event.type == _2BUTTON_PRESS: + self.on_aka_update_clicked(obj) + elif event.button == 3: + menu = gtk.Menu() + item = gtk.TearoffMenuItem() + item.show() + menu.append(item) + msg = _("Make the selected name the preferred name") + Utils.add_menuitem(menu,msg,None,self.change_name) + menu.popup(None,None,None,0,0) + + def on_aka_update_clicked(self,obj): + import NameEdit + if obj.selection: + NameEdit.NameEditor(self,obj.get_row_data(obj.selection[0])) + + def load_photo(self,photo): + """loads, scales, and displays the person's main photo""" + self.load_obj = photo + if photo == None: + self.get_widget("personPix").hide() + else: + try: + i = gtk.gdk.pixbuf_new_from_file(photo) + ratio = float(max(i.get_height(),i.get_width())) + scale = float(const.picWidth)/ratio + x = int(scale*(i.get_width())) + y = int(scale*(i.get_height())) + i = i.scale_simple(x,y,gtk.gdk.INTERP_BILINEAR) + self.get_widget("personPix").set_from_pixbuf(i) + self.get_widget("personPix").show() + except: + self.get_widget("personPix").hide() + + def update_lists(self): + """Updates the person's lists if anything has changed""" + if self.lists_changed: + self.person.setEventList(self.elist) + self.person.setAlternateNames(self.nlist) + self.person.setUrlList(self.ulist) + self.person.setAttributeList(self.alist) + self.person.setAddressList(self.plist) + self.person.setBirth(self.birth) + self.person.setDeath(self.death) + Utils.modified() + + def on_apply_person_clicked(self,obj): + + surname = self.surname_field.get_text() + suffix = self.suffix.get_text() + ntype = self.ntype_field.entry.get_text() + given = self.given.get_text() + nick = self.nick.get_text() + title = self.title.get_text() + idval = self.gid.get_text() + + name = self.pname + + if idval != self.person.getId(): + m = self.db.getPersonMap() + if not m.has_key(idval): + if m.has_key(self.person.getId()): + del m[self.person.getId()] + m[idval] = self.person + self.person.setId(idval) + Utils.modified() + else: + n = GrampsCfg.nameof(m[idval]) + msg1 = _("GRAMPS ID value was not changed.") + msg2 = _("%(grampsid)s is already used by %(person)s") % { + 'grampsid' : idval, + 'person' : n } + GnomeWarningDialog("%s\n%s" % (msg1,msg2)) + + if suffix != name.getSuffix(): + name.setSuffix(suffix) + + if const.NameTypesMap.has_key(ntype): + ntype = const.NameTypesMap[ntype] + else: + ntype = "Birth Name" + + if ntype != name.getType(): + name.setType(ntype) + + if surname != name.getSurname(): + name.setSurname(surname) + self.db.addSurname(surname) + + if given != name.getFirstName(): + name.setFirstName(given) + + if title != name.getTitle(): + name.setTitle(title) + + name.setSourceRefList(self.pname.getSourceRefList()) + + if not name.are_equal(self.person.getPrimaryName()): + self.person.setPrimaryName(name) + Utils.modified() + + if nick != self.person.getNickName(): + self.person.setNickName(nick) + Utils.modified() + + self.pmap.clear() + for key in self.db.getPlaceKeys(): + p = self.db.getPlaceDisplay(key) + self.pmap[p[0]] = key + + self.birth.setDate(self.bdate.get_text()) + self.birth.setPlace(self.get_place(self.bplace,1)) + + if not self.person.getBirth().are_equal(self.birth): + self.person.setBirth(self.birth) + + # Update each of the families child lists to reflect any + # change in ordering due to the new birth date + family = self.person.getMainParents() + if (family): + new_order = reorder_child_list(self.person,family.getChildList()) + family.setChildList(new_order) + for (family, rel1, rel2) in self.person.getParentList(): + new_order = reorder_child_list(self.person,family.getChildList()) + family.setChildList(new_order) + + self.death.setDate(self.ddate.get_text()) + self.death.setPlace(self.get_place(self.dplace,1)) + + if not self.person.getDeath().are_equal(self.death): + self.person.setDeath(self.death) + + male = self.is_male.get_active() + female = self.is_female.get_active() + unknown = self.is_unknown.get_active() + error = 0 + if male and self.person.getGender() != Person.male: + self.person.setGender(Person.male) + for temp_family in self.person.getFamilyList(): + if self.person == temp_family.getMother(): + if temp_family.getFather() != None: + error = 1 + else: + temp_family.setMother(None) + temp_family.setFather(self.person) + Utils.modified() + elif female and self.person.getGender() != Person.female: + self.person.setGender(Person.female) + for temp_family in self.person.getFamilyList(): + if self.person == temp_family.getFather(): + if temp_family.getMother() != None: + error = 1 + else: + temp_family.setFather(None) + temp_family.setMother(self.person) + Utils.modified() + elif unknown and self.person.getGender() != Person.unknown: + self.person.setGender(Person.unknown) + for temp_family in self.person.getFamilyList(): + if self.person == temp_family.getFather(): + if temp_family.getMother() != None: + error = 1 + else: + temp_family.setFather(None) + temp_family.setMother(self.person) + if self.person == temp_family.getMother(): + if temp_family.getFather() != None: + error = 1 + else: + temp_family.setMother(None) + temp_family.setFather(self.person) + Utils.modified() + + if error == 1: + msg = _("Changing the gender caused problems " + "with marriage information.\nPlease check " + "the person's marriages.") + GnomeErrorDialog(msg) + + text = self.notes_buffer.get_text(self.notes_buffer.get_start_iter(), + self.notes_buffer.get_end_iter(),gtk.FALSE) + + if text != self.person.getNote(): + self.person.setNote(text) + Utils.modified() + + if self.lds_not_loaded == 0: + self.check_lds() + ord = LdsOrd(self.person.getLdsBaptism()) + if not self.lds_baptism.are_equal(ord): + self.person.setLdsBaptism(self.lds_baptism) + Utils.modified() + + ord = LdsOrd(self.person.getLdsEndowment()) + if not self.lds_endowment.are_equal(ord): + self.person.setLdsEndowment(self.lds_endowment) + Utils.modified() + + ord = LdsOrd(self.person.getLdsSeal()) + if not self.lds_sealing.are_equal(ord): + self.person.setLdsSeal(self.lds_sealing) + Utils.modified() + + self.update_lists() + if self.callback: + self.callback(self,self.add_places) + Utils.destroy_passed_object(obj) + + def get_place(self,field,makenew=0): + text = string.strip(field.get_text()) + if text: + if self.pmap.has_key(text): + return self.db.getPlaceMap()[self.pmap[text]] + elif makenew: + place = Place() + place.set_title(text) + self.db.addPlace(place) + self.pmap[text] = place.getId() + self.add_places.append(place) + Utils.modified() + return place + else: + return None + else: + return None + + def on_primary_name_source_clicked(self,obj): + import Sources + Sources.SourceSelector(self.pname.getSourceRefList(),self,self.update_primary_name) + + def update_primary_name(self,list): + self.pname.setSourceRefList(list) + self.lists_changed = 1 + + def on_name_note_clicked(self,obj): + import NoteEdit + NoteEdit.NoteEditor(self.pname) + + def on_ldsbap_source_clicked(self,obj): + import Sources + Sources.SourceSelector(self.lds_baptism.getSourceRefList(),self,self.update_ldsbap_list) + + def update_ldsbap_list(self,list): + self.lds_baptism.setSourceRefList(list) + self.lists_changed = 1 + + def on_ldsbap_note_clicked(self,obj): + import NoteEdit + NoteEdit.NoteEditor(self.lds_baptism) + + def on_ldsendow_source_clicked(self,obj): + import Sources + Sources.SourceSelector(self.lds_endowment.getSourceRefList(),self,self.set_ldsendow_list) + + def set_ldsendow_list(self,list): + self.lds_endowment.setSourceRefList(list) + self.lists_changed = 1 + + def on_ldsendow_note_clicked(self,obj): + import NoteEdit + NoteEdit.NoteEditor(self.lds_endowment) + + def on_ldsseal_source_clicked(self,obj): + import Sources + ord = self.person.getLdsSeal() + Sources.SourceSelector(self.lds_sealing.getSourceRefList(),self,self.lds_seal_list) + + def lds_seal_list(self,list): + self.lds_sealing.setSourceRefList(list) + self.lists_changed = 1 + + def on_ldsseal_note_clicked(self,obj): + import NoteEdit + NoteEdit.NoteEditor(self.lds_sealing) + + def load_person_image(self): + photo_list = self.person.getPhotoList() + if photo_list: + ph = photo_list[0] + object = ph.getReference() + if self.load_obj != object.getPath(): + if object.getMimeType()[0:5] == "image": + self.load_photo(object.getPath()) + else: + self.load_photo(None) + else: + self.load_photo(None) + + def update_birth_info(self): + self.bdate.set_text(self.birth.getDate()) + self.bplace.set_text(self.birth.getPlaceName()) + + def update_death_info(self): + self.ddate.set_text(self.death.getDate()) + self.dplace.set_text(self.death.getPlaceName()) + + def on_switch_page(self,obj,a,page): + if page == 0: + self.load_person_image() + self.update_death_info() + self.update_birth_info() + elif page == 2: + self.redraw_event_list() + elif page == 6 and self.not_loaded: + self.not_loaded = 0 + self.gallery.load_images() + elif page == 8 and self.lds_not_loaded: + self.lds_not_loaded = 0 + self.draw_lds() + + def change_name(self,obj): + if self.name_list.selection: + old = self.pname + row = self.name_list.selection[0] + new = self.name_list.get_row_data(row) + self.nlist.remove(new) + self.nlist.append(old) + self.redraw_name_list() + self.pname = Name(new) + self.lists_changed = 1 + self.write_primary_name() + + def write_primary_name(self): + # initial values + name = GrampsCfg.nameof(self.person) + self.get_widget("activepersonTitle").set_text(name) + self.suffix.set_text(self.pname.getSuffix()) + + self.surname_field.set_text(self.pname.getSurname()) + self.given.set_text(self.pname.getFirstName()) + + self.ntype_field.entry.set_text(_(self.pname.getType())) + self.title.set_text(self.pname.getTitle()) + +#------------------------------------------------------------------------- +# +# disp_name +# +#------------------------------------------------------------------------- +def disp_name(name): + return [name.getName(),_(name.getType())] + +#------------------------------------------------------------------------- +# +# disp_url +# +#------------------------------------------------------------------------- +def disp_url(url): + return [url.get_path(),url.get_description()] + +#------------------------------------------------------------------------- +# +# disp_attr +# +#------------------------------------------------------------------------- +def disp_attr(attr): + detail = Utils.get_detail_flags(attr) + return [const.display_pattr(attr.getType()),attr.getValue()] + +#------------------------------------------------------------------------- +# +# disp_addr +# +#------------------------------------------------------------------------- +def disp_addr(addr): + location = "%s %s %s" % (addr.getCity(),addr.getState(),addr.getCountry()) + return [addr.getDate(),location] + +#------------------------------------------------------------------------- +# +# disp_event +# +#------------------------------------------------------------------------- +def disp_event(event): + attr = Utils.get_detail_flags(event) + return [const.display_pevent(event.getName()),event.getDescription(), + event.getQuoteDate(),event.getPlaceName()] + +#------------------------------------------------------------------------- +# +# birth_dates_in_order +# +# Check any *valid* birthdates in the list to insure that they are in +# numerically increasing order. +# +#------------------------------------------------------------------------- +def birth_dates_in_order(list): + inorder = 1 + prev_date = "00000000" + for i in range(len(list)): + child = list[i] + bday = child.getBirth().getDateObj() + child_date = sort.build_sort_date(bday) + if (child_date == "99999999"): + continue + if (prev_date <= child_date): # <= allows for twins + prev_date = child_date + else: + inorder = 0 + return inorder + + +#------------------------------------------------------------------------- +# +# reorder_child_list +# +# Reorder the child list to put the specified person in his/her +# correct birth order. Only check *valid* birthdates. Move the person +# as short a distance as possible. +# +#------------------------------------------------------------------------- +def reorder_child_list(person, list): + if (birth_dates_in_order(list)): + return(list) + + # Build the person's date string once + person_bday = sort.build_sort_date(person.getBirth().getDateObj()) + + # First, see if the person needs to be moved forward in the list + index = list.index(person) + target = index + for i in range(index-1, -1, -1): + other_bday = sort.build_sort_date(list[i].getBirth().getDateObj()) + if (other_bday == "99999999"): + continue; + if (person_bday < other_bday): + target = i + + # Now try moving to a later position in the list + if (target == index): + for i in range(index, len(list)): + other_bday = sort.build_sort_date(list[i].getBirth().getDateObj()) + if (other_bday == "99999999"): + continue; + if (person_bday > other_bday): + target = i + + # Actually need to move? Do it now. + if (target != index): + list.remove(person) + list.insert(target,person) + return list + diff --git a/gramps2/src/EditPlace.py b/gramps2/src/EditPlace.py new file mode 100644 index 000000000..33d942abe --- /dev/null +++ b/gramps2/src/EditPlace.py @@ -0,0 +1,419 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +#------------------------------------------------------------------------- +# +# python modules +# +#------------------------------------------------------------------------- +import pickle + +#------------------------------------------------------------------------- +# +# GTK/Gnome modules +# +#------------------------------------------------------------------------- +import gobject +import gtk +import gtk.glade +import gnome.ui + +#------------------------------------------------------------------------- +# +# gramps modules +# +#------------------------------------------------------------------------- +import const +import Utils +import GrampsCfg +from RelLib import * +import Sources +import ImageSelect + +from intl import gettext as _ + +#------------------------------------------------------------------------- +# +# Constants +# +#------------------------------------------------------------------------- +pycode_tgts = [('url', 0, 0)] + +#------------------------------------------------------------------------- +# +# EditPlace +# +#------------------------------------------------------------------------- +class EditPlace: + + def __init__(self,parent,place,func=None): + self.place = place + self.db = parent.db + self.parent = parent + self.callback = func + self.path = parent.db.getSavePath() + self.not_loaded = 1 + self.ref_not_loaded = 1 + self.lists_changed = 0 + if place: + self.srcreflist = place.getSourceRefList() + else: + self.srcreflist = [] + + self.top_window = gtk.glade.XML(const.placesFile,"placeEditor") + adj = gtk.Adjustment() + self.iconlist = self.top_window.get_widget('iconlist') + + self.glry = ImageSelect.Gallery(place, self.path, self.iconlist, self.db, self) + self.title = self.top_window.get_widget("place_title") + self.city = self.top_window.get_widget("city") + self.parish = self.top_window.get_widget("parish") + self.county = self.top_window.get_widget("county") + self.state = self.top_window.get_widget("state") + self.country = self.top_window.get_widget("country") + self.longitude = self.top_window.get_widget("longitude") + self.latitude = self.top_window.get_widget("latitude") + self.note = self.top_window.get_widget("place_note") + + self.web_list = self.top_window.get_widget("web_list") + self.web_url = self.top_window.get_widget("web_url") + self.web_go = self.top_window.get_widget("web_go") + self.web_description = self.top_window.get_widget("url_des") + + # event display + self.web_model = gtk.ListStore(gobject.TYPE_STRING,gobject.TYPE_STRING) + self.build_columns(self.web_list, [(_('Path'),150), (_('Description'),150)]) + self.web_list.set_model(self.web_model) + self.web_list.get_selection().connect('changed',self.on_web_list_select_row) + + self.loc_list = self.top_window.get_widget("loc_list") + self.loc_city = self.top_window.get_widget("loc_city") + self.loc_county = self.top_window.get_widget("loc_county") + self.loc_state = self.top_window.get_widget("loc_state") + self.loc_parish = self.top_window.get_widget("loc_parish") + self.loc_country = self.top_window.get_widget("loc_country") + + self.ulist = place.getUrlList()[:] + self.llist = place.get_alternate_locations()[:] + + self.loc_model = gtk.ListStore(gobject.TYPE_STRING,gobject.TYPE_STRING, + gobject.TYPE_STRING,gobject.TYPE_STRING) + self.build_columns(self.loc_list, [(_('City'),150), (_('County'),100), + (_('State'),100), (_('Country'),50)]) + self.loc_list.set_model(self.loc_model) + self.loc_list.get_selection().connect('changed',self.on_loc_list_select_row) + + self.title.set_text(place.get_title()) + mloc = place.get_main_location() + self.city.set_text(mloc.get_city()) + self.county.set_text(mloc.get_county()) + self.state.set_text(mloc.get_state()) + self.parish.set_text(mloc.get_parish()) + self.country.set_text(mloc.get_country()) + self.longitude.set_text(place.get_longitude()) + self.latitude.set_text(place.get_latitude()) + self.refinfo = self.top_window.get_widget("refinfo") + self.slist = self.top_window.get_widget("slist") + + self.note_buffer = self.note.get_buffer() + self.note_buffer.set_text(place.getNote()) + + self.top_window.signal_autoconnect({ + "destroy_passed_object" : Utils.destroy_passed_object, + "on_switch_page" : self.on_switch_page, + "on_addphoto_clicked" : self.glry.on_add_photo_clicked, + "on_deletephoto_clicked" : self.glry.on_delete_photo_clicked, + "on_edit_properties_clicked": self.glry.popup_change_description, + "on_add_url_clicked" : self.on_add_url_clicked, + "on_delete_url_clicked" : self.on_delete_url_clicked, + "on_update_url_clicked" : self.on_update_url_clicked, + "on_add_loc_clicked" : self.on_add_loc_clicked, + "on_delete_loc_clicked" : self.on_delete_loc_clicked, + "on_update_loc_clicked" : self.on_update_loc_clicked, + "on_web_go_clicked" : self.on_web_go_clicked, + "on_apply_clicked" : self.on_place_apply_clicked + }) + + self.top = self.top_window.get_widget("placeEditor") + + self.sourcetab = Sources.SourceTab(self.srcreflist,self, + self.top_window,self.slist, + self.top_window.get_widget('add_src'), + self.top_window.get_widget('del_src')) + + if self.place.getId() == "": + self.top_window.get_widget("add_photo").set_sensitive(0) + self.top_window.get_widget("delete_photo").set_sensitive(0) + + self.web_list.drag_dest_set(gtk.DEST_DEFAULT_ALL, + pycode_tgts,gtk.gdk.ACTION_COPY) + self.web_list.drag_source_set(gtk.gdk.BUTTON1_MASK, + pycode_tgts, gtk.gdk.ACTION_COPY) + self.web_list.connect('drag_data_get', + self.url_source_drag_data_get) + self.web_list.connect('drag_data_received', + self.url_dest_drag_data_received) + + self.redraw_url_list() + self.redraw_location_list() + + def build_columns(self,tree,list): + cnum = 0 + for name in list: + renderer = gtk.CellRendererText() + column = gtk.TreeViewColumn(name[0],renderer,text=cnum) + column.set_min_width(name[1]) + cnum = cnum + 1 + tree.append_column(column) + + def url_dest_drag_data_received(self,widget,context,x,y,sel_data,info,time): + if sel_data and sel_data.data: + exec 'data = %s' % sel_data.data + exec 'mytype = "%s"' % data[0] + exec 'place = "%s"' % data[1] + if place == self.place.getId() or mytype != 'url': + return + foo = pickle.loads(data[2]); + self.ulist.append(foo) + self.lists_changed = 1 + self.redraw_url_list() + + def url_source_drag_data_get(self,widget, context, sel_data, info, time): + ev = widget.get_row_data(widget.focus_row) + + bits_per = 8; # we're going to pass a string + pickled = pickle.dumps(ev); + data = str(('url',self.place.getId(),pickled)); + sel_data.set(sel_data.target, bits_per, data) + + def update_lists(self): + self.place.setUrlList(self.ulist) + self.place.set_alternate_locations(self.llist) + if self.lists_changed: + Utils.modified() + + def redraw_url_list(self): + length = Utils.redraw_list(self.ulist,self.web_model,disp_url) + if length > 0: + self.web_go.set_sensitive(1) + else: + self.web_go.set_sensitive(0) + self.web_url.set_text("") + self.web_description.set_text("") + + def redraw_location_list(self): + Utils.redraw_list(self.llist,self.loc_model,disp_loc) + + def on_web_go_clicked(self,obj): + import gnome.url + + text = obj.get() + if text != "": + gnome.url.show(text) + + def set(self,field,getf,setf): + text = field.get_text() + if text != getf(): + setf(text) + Utils.modified() + + def on_place_apply_clicked(self,obj): + + note = self.note_buffer.get_text(self.note_buffer.get_start_iter(), + self.note_buffer.get_end_iter(),gtk.FALSE) + mloc = self.place.get_main_location() + + self.set(self.city,mloc.get_city,mloc.set_city) + self.set(self.parish,mloc.get_parish,mloc.set_parish) + self.set(self.state,mloc.get_state,mloc.set_state) + self.set(self.county,mloc.get_county,mloc.set_county) + self.set(self.country,mloc.get_country,mloc.set_country) + self.set(self.title,self.place.get_title,self.place.set_title) + self.set(self.longitude,self.place.get_longitude, + self.place.set_longitude) + self.set(self.latitude,self.place.get_latitude, + self.place.set_latitude) + + if self.lists_changed: + self.place.setSourceRefList(self.srcreflist) + Utils.modified() + + if note != self.place.getNote(): + self.place.setNote(note) + Utils.modified() + + self.update_lists() + + Utils.destroy_passed_object(self.top) + if self.callback: + self.callback(self.place) + + def on_switch_page(self,obj,a,page): + if page == 3 and self.not_loaded: + self.not_loaded = 0 + self.glry.load_images() + elif page == 5 and self.ref_not_loaded: + self.ref_not_loaded = 0 + self.display_references() + + def on_update_url_clicked(self,obj): + import UrlEdit + if len(obj.selection) > 0: + row = obj.selection[0] + if self.place: + name = _("Internet Address Editor for %s") % self.place.get_title() + else: + name = _("Internet Address Editor") + UrlEdit.UrlEditor(self,name,obj.get_row_data(row)) + + def on_update_loc_clicked(self,obj): + import LocEdit + if obj.selection: + row = obj.selection[0] + LocEdit.LocationEditor(self,obj.get_row_data(row)) + + def on_delete_url_clicked(self,obj): + if Utils.delete_selected(obj,self.ulist): + self.lists_changed = 1 + self.redraw_url_list() + + def on_delete_loc_clicked(self,obj): + if Utils.delete_selected(obj,self.llist): + self.lists_changed = 1 + self.redraw_location_list() + + def on_add_url_clicked(self,obj): + import UrlEdit + if self.place: + name = _("Internet Address Editor for %s") % self.place.get_title() + else: + name = _("Internet Address Editor") + UrlEdit.UrlEditor(self,name,None) + + def on_add_loc_clicked(self,obj): + import LocEdit + LocEdit.LocationEditor(self,None) + + def on_web_list_select_row(self,obj): + store,iter = obj.get_selected() + if not iter: + self.web_url.set_text("") + self.web_go.set_sensitive(0) + self.web_description.set_text("") + else: + row = store.get_path(iter) + url = self.ulist[row[0]] + path = url.get_path() + self.web_url.set_text(path) + self.web_go.set_sensitive(1) + self.web_description.set_text(url.get_description()) + + def on_loc_list_select_row(self,obj,row,b,c): + loc = obj.get_row_data(row) + + self.loc_city.set_text(loc.get_city()) + self.loc_county.set_text(loc.get_county()) + self.loc_state.set_text(loc.get_state()) + self.loc_parish.set_text(loc.get_parish()) + self.loc_country.set_text(loc.get_country()) + + def display_references(self): + pevent = [] + fevent = [] + msg = "" + for key in self.db.getPersonKeys(): + p = self.db.getPerson(key) + for event in [p.getBirth(), p.getDeath()] + p.getEventList(): + if event.getPlace() == self.place: + pevent.append((p,event)) + for f in self.db.getFamilyMap().values(): + for event in f.getEventList(): + if event.getPlace() == self.place: + fevent.append((f,event)) + + if len(pevent) > 0: + msg = msg + _("People") + "\n" + msg = msg + "_________________________\n\n" + t = _("%s [%s]: event %s\n") + + for e in pevent: + msg = msg + ( t % (GrampsCfg.nameof(e[0]),e[0].getId(),e[1].getName())) + + if len(fevent) > 0: + msg = msg + "\n%s\n" % _("Families") + msg = msg + "_________________________\n\n" + t = _("%s [%s]: event %s\n") + + for e in fevent: + father = e[0].getFather() + mother = e[0].getMother() + if father and mother: + fname = "%s and %s" % (GrampsCfg.nameof(father),GrampsCfg.nameof(mother)) + elif father: + fname = "%s" % GrampsCfg.nameof(father) + else: + fname = "%s" % GrampsCfg.nameof(mother) + + msg = msg + ( t % (fname,e[0].getId(),e[1].getName())) + + self.refinfo.get_buffer().set_text(msg) + +#------------------------------------------------------------------------- +# +# disp_url +# +#------------------------------------------------------------------------- +def disp_url(url): + return [url.get_path(),url.get_description()] + +#------------------------------------------------------------------------- +# +# disp_loc +# +#------------------------------------------------------------------------- +def disp_loc(loc): + return [loc.get_city(),loc.get_county(),loc.get_state(),loc.get_country()] + +#------------------------------------------------------------------------- +# +# DeletePlaceQuery +# +#------------------------------------------------------------------------- +class DeletePlaceQuery: + + def __init__(self,place,db,update): + self.db = db + self.place = place + self.update = update + + def query_response(self): + del self.db.getPlaceMap()[self.place.getId()] + Utils.modified() + + for key in self.db.getPersonKeys(): + p = self.db.getPerson(key) + for event in [p.getBirth(), p.getDeath()] + p.getEventList(): + if event.getPlace() == self.place: + event.setPlace(None) + for f in self.db.getFamilyMap().values(): + for event in f.getEventList(): + if event.getPlace() == self.place: + event.setPlace(None) + self.update(0) diff --git a/gramps2/src/EditSource.py b/gramps2/src/EditSource.py new file mode 100644 index 000000000..ae7c11dd3 --- /dev/null +++ b/gramps2/src/EditSource.py @@ -0,0 +1,263 @@ +#! /usr/bin/python -O +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +#------------------------------------------------------------------------- +# +# GTK/Gnome modules +# +#------------------------------------------------------------------------- +import gtk.glade + +#------------------------------------------------------------------------- +# +# gramps modules +# +#------------------------------------------------------------------------- +import const +import Utils +import GrampsCfg +from RelLib import * +import ImageSelect + +from intl import gettext as _ + +#------------------------------------------------------------------------- +# +# Constants +# +#------------------------------------------------------------------------- + +class EditSource: + + def __init__(self,source,db,func=None): + self.source = source + self.db = db + self.callback = func + self.path = db.getSavePath() + self.not_loaded = 1 + self.ref_not_loaded = 1 + + self.top_window = gtk.glade.XML(const.gladeFile,"sourceEditor") + plwidget = self.top_window.get_widget("iconlist") + self.gallery = ImageSelect.Gallery(source, self.path, plwidget, db, self) + self.title = self.top_window.get_widget("source_title") + self.author = self.top_window.get_widget("author") + self.pubinfo = self.top_window.get_widget("pubinfo") + self.note = self.top_window.get_widget("source_note") + self.notes_buffer = self.note.get_buffer() + + self.refinfo = self.top_window.get_widget("refinfo") + + self.title.set_text(source.getTitle()) + self.author.set_text(source.getAuthor()) + self.pubinfo.set_text(source.getPubInfo()) + + self.notes_buffer.set_text(source.getNote()) + + self.top_window.signal_autoconnect({ + "destroy_passed_object" : Utils.destroy_passed_object, + "on_switch_page" : self.on_switch_page, + "on_addphoto_clicked" : self.gallery.on_add_photo_clicked, + "on_deletephoto_clicked" : self.gallery.on_delete_photo_clicked, + "on_edit_properties_clicked": self.gallery.popup_change_description, + "on_sourceapply_clicked" : self.on_source_apply_clicked + }) + + self.top = self.top_window.get_widget("sourceEditor") + + if self.source.getId() == "": + self.top_window.get_widget("add_photo").set_sensitive(0) + self.top_window.get_widget("delete_photo").set_sensitive(0) + + def display_references(self): + p_event_list = [] + p_attr_list = [] + p_addr_list = [] + p_name_list = [] + m_list = [] + f_event_list = [] + f_attr_list = [] + p_list = [] + for key in self.db.getPlaceKeys(): + p = self.db.getPlace(key) + name = p.get_title() + for sref in p.getSourceRefList(): + if sref.getBase() == self.source: + p_list.append(name) + for key in self.db.getPersonKeys(): + p = self.db.getPerson(key) + name = GrampsCfg.nameof(p) + for v in p.getEventList() + [p.getBirth(), p.getDeath()]: + for sref in v.getSourceRefList(): + if sref.getBase() == self.source: + p_event_list.append((name,v.getName())) + for v in p.getAttributeList(): + for sref in v.getSourceRefList(): + if sref.getBase() == self.source: + p_attr_list.append((name,v.getType())) + for v in p.getAlternateNames() + [p.getPrimaryName()]: + for sref in v.getSourceRefList(): + if sref.getBase() == self.source: + p_name_list.append((name,v.getName())) + for v in p.getAddressList(): + for sref in v.getSourceRefList(): + if sref.getBase() == self.source: + p_addr_list.append((name,v.getStreet())) + for p in self.db.getObjectMap().values(): + name = p.getDescription() + for sref in p.getSourceRefList(): + if sref.getBase() == self.source: + m_list.append(name) + for p in self.db.getFamilyMap().values(): + f = p.getFather() + m = p.getMother() + if f and m: + name = _("%(father)s and %(mother)s") % { + "father" : GrampsCfg.nameof(f), + "mother" : GrampsCfg.nameof(m)} + elif f: + name = GrampsCfg.nameof(f) + else: + name = GrampsCfg.nameof(m) + for v in p.getEventList(): + for sref in v.getSourceRefList(): + if sref.getBase() == self.source: + f_event_list.append((name,v.getName())) + for v in p.getAttributeList(): + for sref in v.getSourceRefList(): + if sref.getBase() == self.source: + f_attr_list.append((name,v.getType())) + + slist = self.top_window.get_widget('slist') + if len(p_event_list) > 0: + for p in p_event_list: + slist.append([_("Individual Events"),p[0], + const.display_pevent(p[1])]) + if len(p_attr_list) > 0: + for p in p_attr_list: + slist.append([_("Individual Attributes"),p[0], + const.display_pattr(p[1])]) + if len(p_name_list) > 0: + for p in p_name_list: + slist.append([_("Individual Names"),p[0],p[1]]) + if len(f_event_list) > 0: + for p in f_event_list: + slist.append([_("Family Events"),p[0], + const.display_fevent(p[1])]) + if len(f_attr_list) > 0: + for p in f_event_list: + slist.append([_("Family Attributes"),p[0], + const.display_fattr(p[1])]) + if len(m_list) > 0: + for p in m_list: + slist.append([_("Media Objects"),p,'']) + if len(p_list) > 0: + for p in p_list: + slist.append([_("Places"),p,'']) + + def on_source_apply_clicked(self,obj): + + title = self.title.get_text() + author = self.author.get_text() + pubinfo = self.pubinfo.get_text() + note = self.notes_buffer.get_text() + + if author != self.source.getAuthor(): + self.source.setAuthor(author) + Utils.modified() + + if title != self.source.getTitle(): + self.source.setTitle(title) + Utils.modified() + + if pubinfo != self.source.getPubInfo(): + self.source.setPubInfo(pubinfo) + Utils.modified() + + if note != self.source.getNote(): + self.source.setNote(note) + Utils.modified() + + Utils.destroy_passed_object(self.top) + if self.callback: + self.callback(self.source) + + def on_switch_page(self,obj,a,page): + if page == 2 and self.not_loaded: + self.not_loaded = 0 + self.gallery.load_images() + elif page == 3 and self.ref_not_loaded: + self.ref_not_loaded = 0 + self.display_references() + + +class DelSrcQuery: + def __init__(self,source,db,update): + self.source = source + self.db = db + self.update = update + + def delete_source(self,object): + m = 0 + l = [] + for sref in object.getSourceRefList(): + if sref.getBase() != self.source: + l.append(sref) + else: + m = 1 + if m: + object.setSourceRefList(l) + + def query_response(self): + del self.db.getSourceMap()[self.source.getId()] + Utils.modified() + + for key in self.db.getPersonKeys(): + p = self.db.getPerson(key) + for v in p.getEventList() + [p.getBirth(), p.getDeath()]: + self.delete_source(v) + + for v in p.getAttributeList(): + self.delete_source(v) + + for v in p.getAlternateNames() + [p.getPrimaryName()]: + self.delete_source(v) + + for v in p.getAddressList(): + self.delete_source(v) + + for p in self.db.getFamilyMap().values(): + for v in p.getEventList(): + self.delete_source(v) + + for v in p.getAttributeList(): + self.delete_source(v) + + for p in self.db.getObjectMap().values(): + self.delete_source(p) + + for key in self.db.getPlaceKeys(): + self.delete_source(self.db.getPlace(key)) + + self.update(0) + + + diff --git a/gramps2/src/EventEdit.py b/gramps2/src/EventEdit.py new file mode 100644 index 000000000..405cb4c83 --- /dev/null +++ b/gramps2/src/EventEdit.py @@ -0,0 +1,226 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +from string import strip + +#------------------------------------------------------------------------- +# +# GTK/Gnome modules +# +#------------------------------------------------------------------------- +import gtk +import gtk.glade + +#------------------------------------------------------------------------- +# +# gramps modules +# +#------------------------------------------------------------------------- +import Sources +import const +import Utils +import GrampsCfg +import AutoComp + +from DateEdit import DateEdit +from Date import compare_dates +from RelLib import * +from intl import gettext as _ + +#------------------------------------------------------------------------- +# +# EventEditor class +# +#------------------------------------------------------------------------- +class EventEditor: + + def __init__(self,parent,name,list,trans,event,def_placename,read_only,cb): + self.parent = parent + self.event = event + self.trans = trans + self.callback = cb + self.plist = [] + self.pmap = {} + + for key in self.parent.db.getPlaceKeys(): + p = self.parent.db.getPlaceDisplay(key) + self.pmap[p[0]] = key + + if event: + self.srcreflist = self.event.getSourceRefList() + self.date = Date(self.event.getDateObj()) + else: + self.srcreflist = [] + self.date = Date(None) + + self.top = gtk.glade.XML(const.dialogFile, "event_edit") + self.window = self.top.get_widget("event_edit") + self.name_field = self.top.get_widget("eventName") + self.place_field = self.top.get_widget("eventPlace") + self.cause_field = self.top.get_widget("eventCause") + self.slist = self.top.get_widget("slist") + self.place_combo = self.top.get_widget("eventPlace_combo") + self.date_field = self.top.get_widget("eventDate") + self.cause_field = self.top.get_widget("eventCause") + self.descr_field = self.top.get_widget("event_description") + self.note_field = self.top.get_widget("eventNote") + self.event_menu = self.top.get_widget("personalEvents") + self.priv = self.top.get_widget("priv") + self.calendar = self.top.get_widget("calendar") + + if GrampsCfg.calendar: + self.calendar.show() + else: + self.calendar.hide() + + self.top.get_widget("eventTitle").set_text(name) + if read_only: + self.event_menu.set_sensitive(0) + self.date_field.grab_focus() + + self.sourcetab = Sources.SourceTab(self.srcreflist,self.parent, + self.top,self.slist, + self.top.get_widget('add_src'), + self.top.get_widget('del_src')) + + AutoComp.AutoEntry(self.event_menu.entry,list) + AutoComp.AutoEntry(self.place_field,self.pmap.keys()) + + if event != None: + self.name_field.set_text(event.getName()) + if (def_placename): + self.place_field.set_text(def_placename) + else: + self.place_field.set_text(event.getPlaceName()) + + self.date_field.set_text(self.date.getDate()) + self.cause_field.set_text(event.getCause()) + self.descr_field.set_text(event.getDescription()) + self.priv.set_active(event.getPrivacy()) + + self.note_field.get_buffer().set_text(event.getNote()) + else: + if (def_placename): + self.place_field.set_text(def_placename) + self.date_check = DateEdit(self.date_field,self.top.get_widget("date_stat")) + +# if not read_only: +# self.name_field.select_region(0, -1) + + self.top.signal_autoconnect({ + "destroy_passed_object" : Utils.destroy_passed_object, + "on_add_src_clicked" : self.add_source, + "on_del_src_clicked" : self.del_source, + "on_event_edit_ok_clicked" : self.on_event_edit_ok_clicked, + }) + + menu = gtk.Menu() + names = [ _("Gregorian"), _("Julian"), _("Hebrew"), ("French") ] + for index in range(0,len(names)): + item = gtk.MenuItem(names[index]) + item.set_data("d",index) + item.connect("activate",self.on_menu_changed) + item.show() + menu.append(item) + menu.set_active(self.date.get_calendar()) + self.calendar.set_menu(menu) + + def add_source(self,obj): + pass + + def del_source(self,obj): + pass + + def on_menu_changed(self,obj): + self.date.set_calendar(obj.get_data("d")) + self.date_field.set_text(self.date.getDate()) + + def get_place(self,field,makenew=0): + text = strip(field.get_text()) + if text != "": + if self.pmap.has_key(text): + return self.parent.db.getPlaceMap()[self.pmap[text]] + elif makenew: + place = Place() + place.set_title(text) + self.parent.db.addPlace(place) + self.pmap[text] = place.getId() + self.plist.append(place) + Utils.modified() + return place + else: + return None + else: + return None + + def on_event_edit_ok_clicked(self,obj): + + ename = self.name_field.get_text() + self.date.set(self.date_field.get_text()) + ecause = self.cause_field.get_text() + eplace_obj = self.get_place(self.place_field,1) + buf = self.note_field.get_buffer() + + enote = buf.get_text(buf.get_start_iter(),buf.get_end_iter(),gtk.FALSE) + edesc = self.descr_field.get_text() + epriv = self.priv.get_active() + + if self.event == None: + self.event = Event() + self.event.setSourceRefList(self.srcreflist) + self.parent.elist.append(self.event) + + self.update_event(ename,self.date,eplace_obj,edesc,enote,epriv,ecause) + self.parent.redraw_event_list() + self.callback(None,self.plist) + Utils.destroy_passed_object(obj) + + def update_event(self,name,date,place,desc,note,priv,cause): + if self.event.getPlace() != place: + self.event.setPlace(place) + self.parent.lists_changed = 1 + + if self.event.getName() != self.trans(name): + self.event.setName(self.trans(name)) + self.parent.lists_changed = 1 + + if self.event.getDescription() != desc: + self.event.setDescription(desc) + self.parent.lists_changed = 1 + + if self.event.getNote() != note: + self.event.setNote(note) + self.parent.lists_changed = 1 + + dobj = self.event.getDateObj() + + self.event.setSourceRefList(self.srcreflist) + + if compare_dates(dobj,date) != 0: + self.event.setDateObj(date) + self.parent.lists_changed = 1 + + if self.event.getCause() != cause: + self.event.setCause(cause) + self.parent.lists_changed = 1 + + if self.event.getPrivacy() != priv: + self.event.setPrivacy(priv) + self.parent.lists_changed = 1 diff --git a/gramps2/src/FamilyView.py b/gramps2/src/FamilyView.py new file mode 100644 index 000000000..83e27a31a --- /dev/null +++ b/gramps2/src/FamilyView.py @@ -0,0 +1,438 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +#------------------------------------------------------------------------- +# +# GTK/Gnome modules +# +#------------------------------------------------------------------------- +import gobject +import gtk +import gtk.glade + +#------------------------------------------------------------------------- +# +# gramps modules +# +#------------------------------------------------------------------------- +import const +import sort +from intl import gettext as _ +import Utils +import GrampsCfg +from RelLib import Person + +import AddSpouse +import DisplayTrace +import Marriage +import ChooseParents + +#------------------------------------------------------------------------- +# +# FamilyView +# +#------------------------------------------------------------------------- +class FamilyView: + + def __init__(self,parent): + self.parent = parent + self.top = parent.gtop + self.ap_data = self.top.get_widget('ap_data').get_buffer() + + self.swap_btn = self.top.get_widget('swap_spouse_btn') + self.add_spouse_btn = self.top.get_widget('add_spouse') + self.remove_spouse_btn = self.top.get_widget('remove_spouse') + + self.ap_parents = self.top.get_widget('ap_parents') + self.ap_parents_model = gtk.ListStore(gobject.TYPE_STRING) + self.ap_parents.set_model(self.ap_parents_model) + self.ap_selection = self.ap_parents.get_selection() + column = gtk.TreeViewColumn('',gtk.CellRendererText(),text=0) + self.ap_parents.append_column(column) + self.ap_parents.connect('button-press-event',self.edit_ap_parents) + + self.sp_parents = self.top.get_widget('sp_parents') + self.sp_parents_model = gtk.ListStore(gobject.TYPE_STRING) + self.sp_parents.set_model(self.sp_parents_model) + self.sp_selection = self.sp_parents.get_selection() + column = gtk.TreeViewColumn('',gtk.CellRendererText(),text=0) + self.sp_parents.append_column(column) + self.sp_parents.connect('button-press-event',self.edit_sp_parents) + + self.spouse_list = self.top.get_widget('sp_list') + self.spouse_model = gtk.ListStore(gobject.TYPE_STRING) + self.spouse_list.set_model(self.spouse_model) + self.spouse_selection = self.spouse_list.get_selection() + self.spouse_selection.connect('changed',self.spouse_changed) + + self.top.get_widget('add_parents').connect('clicked',self.add_parents_clicked) + self.top.get_widget('del_parents').connect('clicked',self.del_parents_clicked) + self.top.get_widget('add_spparents').connect('clicked',self.add_sp_parents) + self.top.get_widget('del_spparents').connect('clicked',self.del_sp_parents) + + column = gtk.TreeViewColumn('',gtk.CellRendererText(),text=0) + self.spouse_list.append_column(column) + self.selected_spouse = None + + self.child_list = self.top.get_widget('chlist') + self.child_model = gtk.ListStore(gobject.TYPE_INT, gobject.TYPE_STRING, + gobject.TYPE_STRING,gobject.TYPE_STRING, + gobject.TYPE_STRING,gobject.TYPE_STRING, + gobject.TYPE_STRING) + self.child_selection = self.child_list.get_selection() + + self.child_list.connect('button-press-event',self.on_child_list_button_press) + + self.top.get_widget('ap_parents_btn').connect('clicked',self.ap_parents_clicked) + self.top.get_widget('sp_parents_btn').connect('clicked',self.sp_parents_clicked) + + self.swap_btn.connect('clicked',self.spouse_swap) + self.remove_spouse_btn.connect('clicked',self.remove_spouse) + self.add_spouse_btn.connect('clicked',self.add_spouse) + self.spouse_list.connect('button-press-event',self.edit_releationship) + + + self.child_list.set_model(self.child_model) + self.child_list.set_search_column(0) + self.child_selection = self.child_list.get_selection() + + colno = 0 + for title in [ (_('Order'),0), (_('Name'),1), (_('ID'),2), + (_('Gender'),3), (_('Birth Date'),4), + (_('Status'),5), ('',6) ]: + renderer = gtk.CellRendererText () + column = gtk.TreeViewColumn (title[0], renderer, text=colno) + colno = colno + 1 + column.set_clickable (gtk.TRUE) + if title[0] == '': + column.set_clickable(gtk.TRUE) + column.set_visible(gtk.FALSE) + column.set_sort_column_id(title[1]) + self.child_list.append_column (column) + + def on_child_list_button_press(self,obj,event): + if event.type == gtk.gdk._2BUTTON_PRESS and event.button == 1: + model, iter = self.child_selection.get_selected() + if iter: + id = self.child_model.get_value(iter,2) + self.parent.load_person(self.parent.db.getPerson(id)) + + def spouse_changed(self,obj): + model, iter = obj.get_selected() + if not iter: + self.display_marriage(None) + else: + row = model.get_path(iter) + self.display_marriage(self.person.getFamilyList()[row[0]]) + + def edit_releationship(self,obj,event): + if event.type == gtk.gdk._2BUTTON_PRESS and event.button == 1: + if self.person: + try: + Marriage.Marriage(self.family,self.parent.db, + self.parent.new_after_edit) + except: + DisplayTrace.DisplayTrace() + + def add_spouse(self,obj): + if not self.person: + return + try: + AddSpouse.AddSpouse(self.parent.db, self.person, + self.load_family, + self.parent.redisplay_person_list) + except: + DisplayTrace.DisplayTrace() + + def remove_spouse(self,obj): + """Delete the currently selected spouse from the family""" + if self.person == None: + return + if self.selected_spouse == self.family.getFather(): + self.family.setMother(None) + else: + self.family.setFather(None) + + if self.selected_spouse: + self.selected_spouse.removeFamily(self.family) + + if len(self.family.getChildList()) == 0: + self.person.removeFamily(self.family) + self.parent.db.deleteFamily(self.family) + if len(self.person.getFamilyList()) > 0: + self.load_family(self.person.getFamilyList()[0]) + else: + self.load_family() + else: + self.load_family() + + if len(self.person.getFamilyList()) <= 1: + self.spouse_selection.set_mode(gtk.SELECTION_NONE) + + Utils.modified() + + def spouse_swap(self,obj): + if self.selected_spouse: + self.parent.active_person = self.selected_spouse + self.load_family() + + def ap_parents_clicked(self,obj): + self.change_families(self.person) + + def sp_parents_clicked(self,obj): + self.change_families(self.selected_spouse) + + def change_families(self,person): + if not person: + return + plist = person.getParentList() + + if len(plist) == 0: + return + if len(plist) == 1: + family,m,r = plist[0] + else: + model, iter = self.ap_selection.get_selected() + path = model.get_path(iter) + family,m,r = plist[path[0]] + + if family.getFather(): + person = family.getFather() + else: + person = family.getMother() + self.parent.change_active_person(person) + self.load_family(family) + + + def load_family(self,family=None): + self.person = self.parent.active_person + if not self.person: + return + + n = "%s\n\tb. %s\n\td. %s" % (self.person.getPrimaryName().getName(), + self.person.getBirth().getDate(), + self.person.getDeath().getDate()) + self.ap_data.set_text(n,len(n)) + + self.selected_spouse = None + self.spouse_model.clear() + splist = self.person.getFamilyList() + f = None + first_family = None + first_spouse = None + for f in splist: + if not f: + continue + if f.getFather() == self.person: + sp = f.getMother() + else: + sp = f.getFather() + + iter = self.spouse_model.append() + if f == family: + first_spouse = sp + first_family = f + elif first_spouse == None: + first_spouse = sp + first_family = f + + if len(splist) > 1: + self.spouse_selection.set_mode(gtk.SELECTION_SINGLE) + self.spouse_selection.select_iter(iter) + else: + self.spouse_selection.set_mode(gtk.SELECTION_NONE) + + if sp: + if f.getMarriage(): + mdate = " - %s" % f.getMarriage().getDate() + else: + mdate = "" + v = "%s\n\t%s%s" % (sp.getPrimaryName().getName(), + f.getRelationship(),mdate) + self.spouse_model.set(iter,0,v) + else: + self.spouse_model.set(iter,0,"unknown\n") + + if first_family: + self.display_marriage(first_family) + + self.update_list(self.ap_parents_model,self.ap_parents,self.person) + self.family = first_family + + def update_list(self,model,tree,person): + model.clear() + sel = None + selection = tree.get_selection() + list = person.getParentList() + + for (f,mrel,frel) in list: + + father = self.nameof(_("Father"),f.getFather(),frel) + mother = self.nameof(_("Mother"),f.getMother(),mrel) + + iter = model.append() + if not sel: + sel = iter + v = "%s\n%s" % (father,mother) + model.set(iter,0,v) + if len(list) > 1: + selection.set_mode(gtk.SELECTION_SINGLE) + selection.select_iter(sel) + else: + selection.set_mode(gtk.SELECTION_NONE) + + def nameof(self,l,p,mode): + if p: + n = p.getPrimaryName().getName() + return "%s: %s\n\tRelationship: %s" % (l,n,mode) + else: + return "unknown\n" + + def display_marriage(self,family): + + self.child_model.clear() + if not family: + return + else: + self.family = family + + if family.getFather() == self.person: + self.selected_spouse = family.getMother() + else: + self.selected_spouse = family.getFather() + + if self.selected_spouse: + self.update_list(self.sp_parents_model,self.sp_parents, + self.selected_spouse) + + i = 0 + fiter = None + child_list = list(family.getChildList()) + child_list.sort(sort.by_birthdate) + + self.child_map = {} + + attr = "" + for child in child_list: + status = _("Unknown") + if child.getGender() == Person.male: + gender = const.male + elif child.getGender() == Person.female: + gender = const.female + else: + gender = const.unknown + for fam in child.getParentList(): + if fam[0] == family: + if self.person == family.getFather(): + status = "%s/%s" % (_(fam[2]),_(fam[1])) + else: + status = "%s/%s" % (_(fam[1]),_(fam[2])) + + iter = self.child_model.append() + self.child_map[iter] = child.getId() + + if fiter == None: + fiter = self.child_model.get_path(iter) + self.child_model.set(iter, + 0,(i+1), + 1,GrampsCfg.nameof(child), + 2,child.getId(), + 3,gender, + 4,Utils.birthday(child), + 5,status, + 6,attr) + + def edit_ap_parents(self,obj,event): + if event.type == gtk.gdk._2BUTTON_PRESS and event.button == 1: + self.parent_editor(self.person,self.ap_selection) + + def edit_sp_parents(self,obj,event): + if event.type == gtk.gdk._2BUTTON_PRESS and event.button == 1: + self.parent_editor(self.selected_spouse,self.sp_selection) + + def add_parents_clicked(self,obj): + self.parent_add(self.person) + + def add_sp_parents(self,obj): + self.parent_editor(self.selected_spouse,self.sp_selection) + + def del_parents_clicked(self,obj): + self.parent_deleter(self.person,self.ap_selection) + + def del_sp_parents(self,obj): + self.parent_deleter(self.selected_spouse,self.sp_selection) + + def parent_editor(self,person,selection): + if not person: + return + + plist = person.getParentList() + + if len(plist) == 0: + return + elif len(plist) == 1: + parents,mrel,frel = plist[0] + else: + model, iter = selection.get_selected() + if not iter: + return + + row = model.get_path(iter) + parents,mrel,frel = plist[row[0]] + + try: + ChooseParents.ModifyParents(self.parent.db,person,parents, + self.load_family,self.parent.full_update) + except: + DisplayTrace.DisplayTrace() + + def parent_add(self,person): + if not person: + return + + try: + ChooseParents.ChooseParents(self.parent.db,person,None, + self.load_family,self.parent.full_update) + except: + DisplayTrace.DisplayTrace() + + def parent_deleter(self,person,selection): + if not person: + return + + plist = person.getParentList() + + if len(plist) == 0: + return + + if len(plist) == 1: + person.clearAltFamilyList() + else: + model, iter = selection.get_selected() + if not iter: + return + + row = model.get_path(iter) + fam = person.getParentList()[row[0]] + person.removeAltFamily(fam[0]) + self.load_family() + + + diff --git a/gramps2/src/Filter.py b/gramps2/src/Filter.py new file mode 100644 index 000000000..0f9773b4f --- /dev/null +++ b/gramps2/src/Filter.py @@ -0,0 +1,142 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +#------------------------------------------------------------------------- +# +# Standard python modules +# +#------------------------------------------------------------------------- +import re +import os +import sys +import gtk + +from intl import gettext +_ = gettext + +#------------------------------------------------------------------------- +# +# Base filter class +# +#------------------------------------------------------------------------- +class Filter: + + def __init__(self,text): + self.text = text + self.invert = 0 + + def get_text(self): + return self.text + + def get_invert(self): + return self.invert + + def set_invert(self,invert): + self.invert = invert + + def compare(self,person): + val = self.match(person) + if self.invert: + return not val + else: + return val + + def get_name(self): + return str(self.__class__) + + def match(self,person): + return 1 + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- + +_filter_list = [(Filter, _("All people"), 0, _("Qualifier"))] +_filter2class = {} +_filter2descr = {} + +def register_filter(class_name, description=None, qualifier=0, label=None): + name = str(class_name) + if label == None: + label = _("Qualifier") + if description == None: + description = _("No description") + _filter2class[name] = class_name + _filter2descr[name] = description + _filter_list.append((class_name,description,qualifier,label)) + +def get_filter_description(name): + if _filter2descr.has_key(name): + return _filter2descr[name] + else: + return "" + +def make_filter_from_name(name,qualifier,invert): + a = _filter2class[name](qualifier) + a.set_invert(invert) + return a + +#------------------------------------------------------------------------- +# +# load_filters - loads all filters in the specfied directory. Assumes +# that the filters will register themselves +# +#------------------------------------------------------------------------- +def load_filters(dir): + pymod = re.compile(r"^(.*)\.py$") + + if not os.path.isdir(dir): + return + sys.path.append(dir) + + for file in os.listdir(dir): + name = os.path.split(file) + match = pymod.match(name[1]) + if match: + plugin = match.groups()[0] + try: + __import__(plugin) + except: + print _("Failed to load the module: %s") % plugin + import traceback + traceback.print_exc() + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +def build_filter_menu(callback,fw): + myMenu = gtk.Menu() + for filter in _filter_list: + menuitem = gtk.MenuItem(filter[1]) + myMenu.append(menuitem) + menuitem.set_data("filter",fw) + menuitem.set_data("name",filter[1]) + menuitem.set_data("function",filter[0]) + menuitem.set_data("qual",filter[2]) + menuitem.set_data("label",filter[3]) + menuitem.connect("activate",callback) + menuitem.show() + return myMenu + + diff --git a/gramps2/src/Find.py b/gramps2/src/Find.py new file mode 100644 index 000000000..dec927e41 --- /dev/null +++ b/gramps2/src/Find.py @@ -0,0 +1,255 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""interface for opening a find person dialog for gramps +""" + +__author__ = 'Don Allingham' + +#------------------------------------------------------------------------- +# +# python modules +# +#------------------------------------------------------------------------- +import string + +#------------------------------------------------------------------------- +# +# Gnome modules +# +#------------------------------------------------------------------------- +import gtk +from gnome.ui import * + +#------------------------------------------------------------------------- +# +# gramps modules +# +#------------------------------------------------------------------------- +import GrampsCfg +import AutoComp +from intl import gettext +_ = gettext + +#------------------------------------------------------------------------- +# +# FindBase +# +#------------------------------------------------------------------------- +class FindBase: + """Opens find person dialog for gramps""" + + def __init__(self,clist,task,name,db): + """Opens a dialog box instance that allows users to + search for a person. + + clist - GtkCList containing the people information + task - function to call to change the active person""" + + self.db = db + self.clist = clist + self.nlist = [] + self.task = task + title = "%s - GRAMPS" % name + self.top = GnomeDialog(title,STOCK_BUTTON_PREV, + STOCK_BUTTON_NEXT,STOCK_BUTTON_CLOSE) + self.top.set_policy(0,1,0) + self.top.vbox.set_spacing(5) + self.top.vbox.pack_start(gtk.GtkLabel(name),0,0,5) + self.top.vbox.pack_start(gtk.GtkHSeparator(),0,0,0) + self.entry = gtk.GtkEntry() + self.top.vbox.pack_start(self.entry,0,0,25) + self.top.button_connect(0,self.on_prev_clicked) + self.top.button_connect(1,self.on_next_clicked) + self.top.button_connect(2,self.on_close_clicked) + self.top.set_usize(350,175) + self.top.set_default(1) + self.top.show_all() + self.top.editable_enters(self.entry) + self.entry.grab_focus() + + def get_value(self,id): + return None + + def enable_autocomp(self): + if GrampsCfg.autocomp: + self.comp = AutoComp.AutoEntry(self.entry,self.nlist) + + def advance(self,func): + try: + self.row = self.clist.selection[0] + except IndexError: + gtk.gdk_beep() + return + + text = self.entry.get_text() + if self.row == None or text == "": + gtk.gdk_beep() + return + orow = self.row + func() + while self.row != orow: + id = self.clist.get_row_data(self.row) + if id == None: + func() + continue + if string.find(string.upper(self.get_value(id)),string.upper(text)) >= 0: + self.task(self.row) + return + func() + gtk.gdk_beep() + + def forward(self): + self.row = self.row + 1 + if self.row == self.clist.rows: + self.row = 0 + + def backward(self): + self.row = self.row - 1 + if self.row < 0: + self.row = self.clist.rows + + def on_close_clicked(self,obj): + """Destroys the window in response to a close window button press""" + self.top.destroy() + + def on_next_clicked(self,obj): + """Advances to the next person that matches the dialog text""" + self.advance(self.forward) + + def on_prev_clicked(self,obj): + """Advances to the previous person that matches the dialog text""" + self.advance(self.backward) + +#------------------------------------------------------------------------- +# +# FindPerson +# +#------------------------------------------------------------------------- +class FindPerson(FindBase): + """Opens a Find Person dialog for GRAMPS""" + + def __init__(self,clist,task,db): + """Opens a dialog box instance that allows users to + search for a person. + + clist - GtkCList containing the people information + task - function to call to change the active person""" + + FindBase.__init__(self,clist,task,_("Find Person"),db) + for n in self.db.getPersonKeys(): + val = self.db.getPersonDisplay(n) + self.nlist.append(val[0]) + self.enable_autocomp() + + def get_value(self,id): + return self.db.getPersonDisplay(id)[0] + + +#------------------------------------------------------------------------- +# +# FindPlace +# +#------------------------------------------------------------------------- +class FindPlace(FindBase): + """Opens a Find Place dialog for GRAMPS""" + + def __init__(self,clist,task,db): + """Opens a dialog box instance that allows users to + search for a place. + + clist - GtkCList containing the people information + task - function to call to change the active person""" + + FindBase.__init__(self,clist,task,_("Find Place"),db) + for n in self.db.getPlaceKeys(): + self.nlist.append(self.db.getPlaceDisplay(n)[0]) + self.enable_autocomp() + + def get_value(self,id): + return self.db.getPlaceDisplay(id)[0] + +#------------------------------------------------------------------------- +# +# FindSource +# +#------------------------------------------------------------------------- +class FindSource(FindBase): + """Opens a Find Place dialog for GRAMPS""" + + def __init__(self,clist,task,db): + """Opens a dialog box instance that allows users to + search for a place. + + clist - GtkCList containing the people information + task - function to call to change the active person""" + + FindBase.__init__(self,clist,task,_("Find Source"),db) + for n in self.db.getSourceKeys(): + self.nlist.append(n[0]) + self.enable_autocomp() + + def get_value(self,id): + return self.db.getSourceDisplay(id)[0] + +#------------------------------------------------------------------------- +# +# FindMedia +# +#------------------------------------------------------------------------- +class FindMedia(FindBase): + """Opens a Find Media Object dialog for GRAMPS""" + + def __init__(self,clist,task,db): + """Opens a dialog box instance that allows users to + search for a place. + + clist - GtkCList containing the people information + task - function to call to change the active person""" + + FindBase.__init__(self,clist,task,_("Find Media Object"),db) + for n in self.db.getObjectMap().values(): + self.nlist.append(n.getDescription()) + self.enable_autocomp() + + def advance(self,func): + try: + self.row = self.clist.selection[0] + except IndexError: + gtk.gdk_beep() + return + + text = self.entry.get_text() + if self.row == None or text == "": + gtk.gdk_beep() + return + orow = self.row + func() + while self.row != orow: + value = self.clist.get_row_data(self.row) + if value == None: + func() + continue + name = value.getDescription() + if string.find(string.upper(name),string.upper(text)) >= 0: + self.task(self.row) + return + func() + gtk.gdk_beep() diff --git a/gramps2/src/FontScale.py b/gramps2/src/FontScale.py new file mode 100644 index 000000000..b6de44cdd --- /dev/null +++ b/gramps2/src/FontScale.py @@ -0,0 +1,260 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2001 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +_swiss = [ +0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, +0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, +0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, +0.000, 0.000, 0.278, 0.278, 0.355, 0.556, 0.556, 0.889, 0.667, 0.191, +0.333, 0.333, 0.389, 0.584, 0.278, 0.333, 0.278, 0.278, 0.556, 0.556, +0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.278, 0.278, +0.584, 0.584, 0.584, 0.556, 1.015, 0.667, 0.667, 0.722, 0.722, 0.667, +0.611, 0.778, 0.722, 0.278, 0.500, 0.667, 0.556, 0.833, 0.722, 0.778, +0.667, 0.778, 0.722, 0.667, 0.611, 0.722, 0.667, 0.944, 0.667, 0.667, +0.611, 0.278, 0.278, 0.278, 0.469, 0.556, 0.333, 0.556, 0.556, 0.500, +0.556, 0.556, 0.278, 0.556, 0.556, 0.222, 0.222, 0.500, 0.222, 0.833, +0.556, 0.556, 0.556, 0.556, 0.333, 0.500, 0.278, 0.556, 0.500, 0.722, +0.500, 0.500, 0.500, 0.334, 0.260, 0.334, 0.584, 0.350, 0.556, 0.350, +0.222, 0.556, 0.333, 1.000, 0.556, 0.556, 0.333, 1.000, 0.667, 0.333, +1.000, 0.350, 0.611, 0.350, 0.350, 0.222, 0.222, 0.333, 0.333, 0.350, +0.556, 1.000, 0.333, 1.000, 0.500, 0.333, 0.944, 0.350, 0.500, 0.667, +0.278, 0.333, 0.556, 0.556, 0.556, 0.556, 0.260, 0.556, 0.333, 0.737, +0.370, 0.556, 0.584, 0.333, 0.737, 0.333, 0.400, 0.584, 0.333, 0.333, +0.333, 0.556, 0.537, 0.278, 0.333, 0.333, 0.365, 0.556, 0.834, 0.834, +0.834, 0.611, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 1.000, 0.722, +0.667, 0.667, 0.667, 0.667, 0.278, 0.278, 0.278, 0.278, 0.722, 0.722, +0.778, 0.778, 0.778, 0.778, 0.778, 0.584, 0.778, 0.722, 0.722, 0.722, +0.722, 0.667, 0.667, 0.611, 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, +0.889, 0.500, 0.556, 0.556, 0.556, 0.556, 0.278, 0.278, 0.278, 0.278, +0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.584, 0.611, 0.556, +0.556, 0.556, 0.556, 0.500, 0.556, 0.500] + +_swiss_b = [ +0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, +0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, +0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, +0.000, 0.000, 0.278, 0.333, 0.474, 0.556, 0.556, 0.889, 0.722, 0.238, +0.333, 0.333, 0.389, 0.584, 0.278, 0.333, 0.278, 0.278, 0.556, 0.556, +0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.333, 0.333, +0.584, 0.584, 0.584, 0.611, 0.975, 0.722, 0.722, 0.722, 0.722, 0.667, +0.611, 0.778, 0.722, 0.278, 0.556, 0.722, 0.611, 0.833, 0.722, 0.778, +0.667, 0.778, 0.722, 0.667, 0.611, 0.722, 0.667, 0.944, 0.667, 0.667, +0.611, 0.333, 0.278, 0.333, 0.584, 0.556, 0.333, 0.556, 0.611, 0.556, +0.611, 0.556, 0.333, 0.611, 0.611, 0.278, 0.278, 0.556, 0.278, 0.889, +0.611, 0.611, 0.611, 0.611, 0.389, 0.556, 0.333, 0.611, 0.556, 0.778, +0.556, 0.556, 0.500, 0.389, 0.280, 0.389, 0.584, 0.350, 0.556, 0.350, +0.278, 0.556, 0.500, 1.000, 0.556, 0.556, 0.333, 1.000, 0.667, 0.333, +1.000, 0.350, 0.611, 0.350, 0.350, 0.278, 0.278, 0.500, 0.500, 0.350, +0.556, 1.000, 0.333, 1.000, 0.556, 0.333, 0.944, 0.350, 0.500, 0.667, +0.278, 0.333, 0.556, 0.556, 0.556, 0.556, 0.280, 0.556, 0.333, 0.737, +0.370, 0.556, 0.584, 0.333, 0.737, 0.333, 0.400, 0.584, 0.333, 0.333, +0.333, 0.611, 0.556, 0.278, 0.333, 0.333, 0.365, 0.556, 0.834, 0.834, +0.834, 0.611, 0.722, 0.722, 0.722, 0.722, 0.722, 0.722, 1.000, 0.722, +0.667, 0.667, 0.667, 0.667, 0.278, 0.278, 0.278, 0.278, 0.722, 0.722, +0.778, 0.778, 0.778, 0.778, 0.778, 0.584, 0.778, 0.722, 0.722, 0.722, +0.722, 0.667, 0.667, 0.611, 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, +0.889, 0.556, 0.556, 0.556, 0.556, 0.556, 0.278, 0.278, 0.278, 0.278, +0.611, 0.611, 0.611, 0.611, 0.611, 0.611, 0.611, 0.584, 0.611, 0.611, +0.611, 0.611, 0.611, 0.556, 0.611, 0.556] + +_swiss_i = [ +0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, +0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, +0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, +0.000, 0.000, 0.278, 0.278, 0.355, 0.556, 0.556, 0.889, 0.667, 0.191, +0.333, 0.333, 0.389, 0.584, 0.278, 0.333, 0.278, 0.278, 0.556, 0.556, +0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.278, 0.278, +0.584, 0.584, 0.584, 0.556, 1.015, 0.667, 0.667, 0.722, 0.722, 0.667, +0.611, 0.778, 0.722, 0.278, 0.500, 0.667, 0.556, 0.833, 0.722, 0.778, +0.667, 0.778, 0.722, 0.667, 0.611, 0.722, 0.667, 0.944, 0.667, 0.667, +0.611, 0.278, 0.278, 0.278, 0.469, 0.556, 0.333, 0.556, 0.556, 0.500, +0.556, 0.556, 0.278, 0.556, 0.556, 0.222, 0.222, 0.500, 0.222, 0.833, +0.556, 0.556, 0.556, 0.556, 0.333, 0.500, 0.278, 0.556, 0.500, 0.722, +0.500, 0.500, 0.500, 0.334, 0.260, 0.334, 0.584, 0.350, 0.556, 0.350, +0.222, 0.556, 0.333, 1.000, 0.556, 0.556, 0.333, 1.000, 0.667, 0.333, +1.000, 0.350, 0.611, 0.350, 0.350, 0.222, 0.222, 0.333, 0.333, 0.350, +0.556, 1.000, 0.333, 1.000, 0.500, 0.333, 0.944, 0.350, 0.500, 0.667, +0.278, 0.333, 0.556, 0.556, 0.556, 0.556, 0.260, 0.556, 0.333, 0.737, +0.370, 0.556, 0.584, 0.333, 0.737, 0.333, 0.400, 0.584, 0.333, 0.333, +0.333, 0.556, 0.537, 0.278, 0.333, 0.333, 0.365, 0.556, 0.834, 0.834, +0.834, 0.611, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 1.000, 0.722, +0.667, 0.667, 0.667, 0.667, 0.278, 0.278, 0.278, 0.278, 0.722, 0.722, +0.778, 0.778, 0.778, 0.778, 0.778, 0.584, 0.778, 0.722, 0.722, 0.722, +0.722, 0.667, 0.667, 0.611, 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, +0.889, 0.500, 0.556, 0.556, 0.556, 0.556, 0.278, 0.278, 0.278, 0.278, +0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.584, 0.611, 0.556, +0.556, 0.556, 0.556, 0.500, 0.556, 0.500] + +_swiss_bi = [ +0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, +0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, +0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, +0.000, 0.000, 0.278, 0.333, 0.474, 0.556, 0.556, 0.889, 0.722, 0.238, +0.333, 0.333, 0.389, 0.584, 0.278, 0.333, 0.278, 0.278, 0.556, 0.556, +0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.333, 0.333, +0.584, 0.584, 0.584, 0.611, 0.975, 0.722, 0.722, 0.722, 0.722, 0.667, +0.611, 0.778, 0.722, 0.278, 0.556, 0.722, 0.611, 0.833, 0.722, 0.778, +0.667, 0.778, 0.722, 0.667, 0.611, 0.722, 0.667, 0.944, 0.667, 0.667, +0.611, 0.333, 0.278, 0.333, 0.584, 0.556, 0.333, 0.556, 0.611, 0.556, +0.611, 0.556, 0.333, 0.611, 0.611, 0.278, 0.278, 0.556, 0.278, 0.889, +0.611, 0.611, 0.611, 0.611, 0.389, 0.556, 0.333, 0.611, 0.556, 0.778, +0.556, 0.556, 0.500, 0.389, 0.280, 0.389, 0.584, 0.350, 0.556, 0.350, +0.278, 0.556, 0.500, 1.000, 0.556, 0.556, 0.333, 1.000, 0.667, 0.333, +1.000, 0.350, 0.611, 0.350, 0.350, 0.278, 0.278, 0.500, 0.500, 0.350, +0.556, 1.000, 0.333, 1.000, 0.556, 0.333, 0.944, 0.350, 0.500, 0.667, +0.278, 0.333, 0.556, 0.556, 0.556, 0.556, 0.280, 0.556, 0.333, 0.737, +0.370, 0.556, 0.584, 0.333, 0.737, 0.333, 0.400, 0.584, 0.333, 0.333, +0.333, 0.611, 0.556, 0.278, 0.333, 0.333, 0.365, 0.556, 0.834, 0.834, +0.834, 0.611, 0.722, 0.722, 0.722, 0.722, 0.722, 0.722, 1.000, 0.722, +0.667, 0.667, 0.667, 0.667, 0.278, 0.278, 0.278, 0.278, 0.722, 0.722, +0.778, 0.778, 0.778, 0.778, 0.778, 0.584, 0.778, 0.722, 0.722, 0.722, +0.722, 0.667, 0.667, 0.611, 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, +0.889, 0.556, 0.556, 0.556, 0.556, 0.556, 0.278, 0.278, 0.278, 0.278, +0.611, 0.611, 0.611, 0.611, 0.611, 0.611, 0.611, 0.584, 0.611, 0.611, +0.611, 0.611, 0.611, 0.556, 0.611, 0.556] + +_roman = [ +0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, +0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, +0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, +0.000, 0.000, 0.250, 0.333, 0.408, 0.500, 0.500, 0.833, 0.778, 0.180, +0.333, 0.333, 0.500, 0.564, 0.250, 0.333, 0.250, 0.278, 0.500, 0.500, +0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.278, 0.278, +0.564, 0.564, 0.564, 0.444, 0.921, 0.722, 0.667, 0.667, 0.722, 0.611, +0.556, 0.722, 0.722, 0.333, 0.389, 0.722, 0.611, 0.889, 0.722, 0.722, +0.556, 0.722, 0.667, 0.556, 0.611, 0.722, 0.722, 0.944, 0.722, 0.722, +0.611, 0.333, 0.278, 0.333, 0.469, 0.500, 0.333, 0.444, 0.500, 0.444, +0.500, 0.444, 0.333, 0.500, 0.500, 0.278, 0.278, 0.500, 0.278, 0.778, +0.500, 0.500, 0.500, 0.500, 0.333, 0.389, 0.278, 0.500, 0.500, 0.722, +0.500, 0.500, 0.444, 0.480, 0.200, 0.480, 0.541, 0.350, 0.500, 0.350, +0.333, 0.500, 0.444, 1.000, 0.500, 0.500, 0.333, 1.000, 0.556, 0.333, +0.889, 0.350, 0.611, 0.350, 0.350, 0.333, 0.333, 0.444, 0.444, 0.350, +0.500, 1.000, 0.333, 0.980, 0.389, 0.333, 0.722, 0.350, 0.444, 0.722, +0.250, 0.333, 0.500, 0.500, 0.500, 0.500, 0.200, 0.500, 0.333, 0.760, +0.276, 0.500, 0.564, 0.333, 0.760, 0.333, 0.400, 0.564, 0.300, 0.300, +0.333, 0.500, 0.453, 0.250, 0.333, 0.300, 0.310, 0.500, 0.750, 0.750, +0.750, 0.444, 0.722, 0.722, 0.722, 0.722, 0.722, 0.722, 0.889, 0.667, +0.611, 0.611, 0.611, 0.611, 0.333, 0.333, 0.333, 0.333, 0.722, 0.722, +0.722, 0.722, 0.722, 0.722, 0.722, 0.564, 0.722, 0.722, 0.722, 0.722, +0.722, 0.722, 0.556, 0.500, 0.444, 0.444, 0.444, 0.444, 0.444, 0.444, +0.667, 0.444, 0.444, 0.444, 0.444, 0.444, 0.278, 0.278, 0.278, 0.278, +0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.564, 0.500, 0.500, +0.500, 0.500, 0.500, 0.500, 0.500, 0.500] + +_roman_b = [ +0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, +0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, +0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, +0.000, 0.000, 0.250, 0.333, 0.555, 0.500, 0.500, 1.000, 0.833, 0.278, +0.333, 0.333, 0.500, 0.570, 0.250, 0.333, 0.250, 0.278, 0.500, 0.500, +0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.333, 0.333, +0.570, 0.570, 0.570, 0.500, 0.930, 0.722, 0.667, 0.722, 0.722, 0.667, +0.611, 0.778, 0.778, 0.389, 0.500, 0.778, 0.667, 0.944, 0.722, 0.778, +0.611, 0.778, 0.722, 0.556, 0.667, 0.722, 0.722, 1.000, 0.722, 0.722, +0.667, 0.333, 0.278, 0.333, 0.581, 0.500, 0.333, 0.500, 0.556, 0.444, +0.556, 0.444, 0.333, 0.500, 0.556, 0.278, 0.333, 0.556, 0.278, 0.833, +0.556, 0.500, 0.556, 0.556, 0.444, 0.389, 0.333, 0.556, 0.500, 0.722, +0.500, 0.500, 0.444, 0.394, 0.220, 0.394, 0.520, 0.350, 0.500, 0.350, +0.333, 0.500, 0.500, 1.000, 0.500, 0.500, 0.333, 1.000, 0.556, 0.333, +1.000, 0.350, 0.667, 0.350, 0.350, 0.333, 0.333, 0.500, 0.500, 0.350, +0.500, 1.000, 0.333, 1.000, 0.389, 0.333, 0.722, 0.350, 0.444, 0.722, +0.250, 0.333, 0.500, 0.500, 0.500, 0.500, 0.220, 0.500, 0.333, 0.747, +0.300, 0.500, 0.570, 0.333, 0.747, 0.333, 0.400, 0.570, 0.300, 0.300, +0.333, 0.556, 0.540, 0.250, 0.333, 0.300, 0.330, 0.500, 0.750, 0.750, +0.750, 0.500, 0.722, 0.722, 0.722, 0.722, 0.722, 0.722, 1.000, 0.722, +0.667, 0.667, 0.667, 0.667, 0.389, 0.389, 0.389, 0.389, 0.722, 0.722, +0.778, 0.778, 0.778, 0.778, 0.778, 0.570, 0.778, 0.722, 0.722, 0.722, +0.722, 0.722, 0.611, 0.556, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, +0.722, 0.444, 0.444, 0.444, 0.444, 0.444, 0.278, 0.278, 0.278, 0.278, +0.500, 0.556, 0.500, 0.500, 0.500, 0.500, 0.500, 0.570, 0.500, 0.556, +0.556, 0.556, 0.556, 0.500, 0.556, 0.500] + +_roman_i = [ +0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, +0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, +0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, +0.000, 0.000, 0.250, 0.333, 0.420, 0.500, 0.500, 0.833, 0.778, 0.214, +0.333, 0.333, 0.500, 0.675, 0.250, 0.333, 0.250, 0.278, 0.500, 0.500, +0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.333, 0.333, +0.675, 0.675, 0.675, 0.500, 0.920, 0.611, 0.611, 0.667, 0.722, 0.611, +0.611, 0.722, 0.722, 0.333, 0.444, 0.667, 0.556, 0.833, 0.667, 0.722, +0.611, 0.722, 0.611, 0.500, 0.556, 0.722, 0.611, 0.833, 0.611, 0.556, +0.556, 0.389, 0.278, 0.389, 0.422, 0.500, 0.333, 0.500, 0.500, 0.444, +0.500, 0.444, 0.278, 0.500, 0.500, 0.278, 0.278, 0.444, 0.278, 0.722, +0.500, 0.500, 0.500, 0.500, 0.389, 0.389, 0.278, 0.500, 0.444, 0.667, +0.444, 0.444, 0.389, 0.400, 0.275, 0.400, 0.541, 0.350, 0.500, 0.350, +0.333, 0.500, 0.556, 0.889, 0.500, 0.500, 0.333, 1.000, 0.500, 0.333, +0.944, 0.350, 0.556, 0.350, 0.350, 0.333, 0.333, 0.556, 0.556, 0.350, +0.500, 0.889, 0.333, 0.980, 0.389, 0.333, 0.667, 0.350, 0.389, 0.556, +0.250, 0.389, 0.500, 0.500, 0.500, 0.500, 0.275, 0.500, 0.333, 0.760, +0.276, 0.500, 0.675, 0.333, 0.760, 0.333, 0.400, 0.675, 0.300, 0.300, +0.333, 0.500, 0.523, 0.250, 0.333, 0.300, 0.310, 0.500, 0.750, 0.750, +0.750, 0.500, 0.611, 0.611, 0.611, 0.611, 0.611, 0.611, 0.889, 0.667, +0.611, 0.611, 0.611, 0.611, 0.333, 0.333, 0.333, 0.333, 0.722, 0.667, +0.722, 0.722, 0.722, 0.722, 0.722, 0.675, 0.722, 0.722, 0.722, 0.722, +0.722, 0.556, 0.611, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, +0.667, 0.444, 0.444, 0.444, 0.444, 0.444, 0.278, 0.278, 0.278, 0.278, +0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.675, 0.500, 0.500, +0.500, 0.500, 0.500, 0.444, 0.500, 0.444] + +_roman_bi = [ +0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, +0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, +0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, +0.000, 0.000, 0.250, 0.389, 0.555, 0.500, 0.500, 0.833, 0.778, 0.278, +0.333, 0.333, 0.500, 0.570, 0.250, 0.333, 0.250, 0.278, 0.500, 0.500, +0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.333, 0.333, +0.570, 0.570, 0.570, 0.500, 0.832, 0.667, 0.667, 0.667, 0.722, 0.667, +0.667, 0.722, 0.778, 0.389, 0.500, 0.667, 0.611, 0.889, 0.722, 0.722, +0.611, 0.722, 0.667, 0.556, 0.611, 0.722, 0.667, 0.889, 0.667, 0.611, +0.611, 0.333, 0.278, 0.333, 0.570, 0.500, 0.333, 0.500, 0.500, 0.444, +0.500, 0.444, 0.333, 0.500, 0.556, 0.278, 0.278, 0.500, 0.278, 0.778, +0.556, 0.500, 0.500, 0.500, 0.389, 0.389, 0.278, 0.556, 0.444, 0.667, +0.500, 0.444, 0.389, 0.348, 0.220, 0.348, 0.570, 0.350, 0.500, 0.350, +0.333, 0.500, 0.500, 1.000, 0.500, 0.500, 0.333, 1.000, 0.556, 0.333, +0.944, 0.350, 0.611, 0.350, 0.350, 0.333, 0.333, 0.500, 0.500, 0.350, +0.500, 1.000, 0.333, 1.000, 0.389, 0.333, 0.722, 0.350, 0.389, 0.611, +0.250, 0.389, 0.500, 0.500, 0.500, 0.500, 0.220, 0.500, 0.333, 0.747, +0.266, 0.500, 0.606, 0.333, 0.747, 0.333, 0.400, 0.570, 0.300, 0.300, +0.333, 0.576, 0.500, 0.250, 0.333, 0.300, 0.300, 0.500, 0.750, 0.750, +0.750, 0.500, 0.667, 0.667, 0.667, 0.667, 0.667, 0.667, 0.944, 0.667, +0.667, 0.667, 0.667, 0.667, 0.389, 0.389, 0.389, 0.389, 0.722, 0.722, +0.722, 0.722, 0.722, 0.722, 0.722, 0.570, 0.722, 0.722, 0.722, 0.722, +0.722, 0.611, 0.611, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, +0.722, 0.444, 0.444, 0.444, 0.444, 0.444, 0.278, 0.278, 0.278, 0.278, +0.500, 0.556, 0.500, 0.500, 0.500, 0.500, 0.500, 0.570, 0.500, 0.556, +0.556, 0.556, 0.556, 0.444, 0.500, 0.444] + +_font_array = [ [_swiss, _swiss_b, _swiss_i, _swiss_bi ], + [_roman, _roman_b, _roman_i, _roman_bi ] ] + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +def string_width(font,text): + i = font.get_type_face() + j = font.get_bold() + font.get_italic()*2 + s = font.get_size() + l = _font_array[i][j] + r = 0 + for c in text: + r = r + l[ord(c)] + return (r+1)*s diff --git a/gramps2/src/GedcomInfo.py b/gramps2/src/GedcomInfo.py new file mode 100644 index 000000000..bd220ce8f --- /dev/null +++ b/gramps2/src/GedcomInfo.py @@ -0,0 +1,222 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +import const +from latin_utf8 import utf8_to_latin +u2l = utf8_to_latin + +ADOPT_NONE = 0 +ADOPT_EVENT = 1 +ADOPT_FTW = 2 +ADOPT_LEGACY = 3 +ADOPT_PEDI = 4 +ADOPT_STD = 5 +CONC_OK = 0 +CONC_BROKEN = 1 +ALT_NAME_NONE = 0 +ALT_NAME_STD = 1 +ALT_NAME_ALIAS = 2 +ALT_NAME_AKA = 3 +ALT_NAME_EVENT_AKA = 4 +ALT_NAME_UALIAS = 5 +CALENDAR_NO = 0 +CALENDAR_YES = 1 +OBJE_NO = 0 +OBJE_YES = 1 +RESIDENCE_ADDR = 0 +RESIDENCE_PLAC = 1 + +#------------------------------------------------------------------------- +# +# XML parser +# +#------------------------------------------------------------------------- +import xml.parsers.expat + +class GedcomDescription: + def __init__(self,name): + self.name = name + self.dest = "" + self.adopt = ADOPT_STD + self.conc = CONC_OK + self.altname = ALT_NAME_STD + self.cal = CALENDAR_YES + self.obje = OBJE_YES + self.resi = RESIDENCE_ADDR + self.gramps2tag_map = {} + self.tag2gramps_map = {} + + def set_dest(self,val): + self.dest = val + + def get_dest(self): + return self.dest + + def set_adopt(self,val): + self.adopt = val + + def get_adopt(self): + return self.adopt + + def set_conc(self,val): + self.conc = val + + def get_conc(self): + return self.conc + + def set_alt_name(self,val): + self.altname = val + + def get_alt_name(self): + return self.altname + + def set_alt_calendar(self,val): + self.cal = val + + def get_alt_calendar(self): + return self.cal + + def set_obje(self,val): + self.obje = val + + def get_obje(self): + return self.obje + + def set_resi(self,val): + self.resi = val + + def get_resi(self): + return self.resi + + def add_tag_value(self,tag,value): + self.gramps2tag_map[value] = tag + self.tag2gramps_map[tag] = value + + def gramps2tag(self,key): + if self.gramps2tag_map.has_key(key): + return self.gramps2tag_map[key] + return "" + + def tag2gramps(self,key): + if self.tag2gramps_map.has_key(key): + return self.tag2gramps_map[key] + return key + +class GedcomInfoDB: + def __init__(self): + self.map = {} + + self.standard = GedcomDescription("GEDCOM 5.5 standard") + self.standard.set_dest("GEDCOM 5.5") + + try: + file = "%s/gedcom.xml" % const.dataDir + f = open(file,"r") + except: + return + + try: + parser = GedInfoParser(self) + parser.parse(f) + f.close() + except: + pass + + def add_description(self,name,obj): + self.map[name] = obj + + def get_description(self,name): + if self.map.has_key(name): + return self.map[name] + return self.standard + + def get_from_source_tag(self,name): + for k in self.map.keys(): + val = self.map[k] + if val.get_dest() == name: + return val + return self.standard + + def get_name_list(self): + mylist = self.map.keys() + mylist.sort() + return ["GEDCOM 5.5 standard"] + mylist + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +class GedInfoParser: + def __init__(self,parent): + self.parent = parent + self.current = None + + def parse(self,file): + p = xml.parsers.expat.ParserCreate() + p.StartElementHandler = self.startElement + p.ParseFile(file) + + def startElement(self,tag,attrs): + tag = u2l(tag) + if tag == "target": + name = u2l(attrs['name']) + self.current = GedcomDescription(name) + self.parent.add_description(name,self.current) + elif tag == "dest": + self.current.set_dest(u2l(attrs['val'])) + elif tag == "adopt": + val = u2l(attrs['val']) + if val == 'none': + self.current.set_adopt(ADOPT_NONE) + elif val == 'event': + self.current.set_adopt(ADOPT_EVENT) + elif val == 'ftw': + self.current.set_adopt(ADOPT_FTW) + elif val == 'legacy': + self.current.set_adopt(ADOPT_LEGACY) + elif val == 'pedigree': + self.current.set_adopt(ADOPT_PEDI) + elif tag == "conc": + if u2l(attrs['val']) == 'broken': + self.current.set_conc(CONC_BROKEN) + elif tag == "alternate_names": + val = u2l(attrs['val']) + if val == 'none': + self.current.set_alt_name(ALT_NAME_NONE) + elif val == 'event_aka': + self.current.set_alt_name(ALT_NAME_EVENT_AKA) + elif val == 'alias': + self.current.set_alt_name(ALT_NAME_ALIAS) + elif val == 'aka': + self.current.set_alt_name(ALT_NAME_AKA) + elif val == '_alias': + self.current.set_alt_name(ALT_NAME_UALIAS) + elif tag == "calendars": + if u2l(attrs['val']) == 'no': + self.current.set_alt_calendar(CALENDAR_NO) + elif tag == "event": + self.current.add_tag_value(u2l(attrs['tag']),u2l(attrs['value'])) + elif tag == "object_support": + if u2l(attrs['val']) == 'no': + self.current.set_obje(OBJE_NO) + elif tag == "residence": + if u2l(attrs['val']) == 'place': + self.current.set_resi(RESIDENCE_PLAC) diff --git a/gramps2/src/GenericFilter.py b/gramps2/src/GenericFilter.py new file mode 100644 index 000000000..449019cc1 --- /dev/null +++ b/gramps2/src/GenericFilter.py @@ -0,0 +1,894 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2002 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""Generic Filtering Routines""" + +__author__ = "Don Allingham" + +#------------------------------------------------------------------------- +# +# Try to abstract SAX1 from SAX2 +# +#------------------------------------------------------------------------- +try: + from xml.sax import make_parser,handler,SAXParseException +except: + from _xmlplus.sax import make_parser,handler,SAXParseException + +#------------------------------------------------------------------------- +# +# Standard Python modules +# +#------------------------------------------------------------------------- +import os +from string import find,join,strip,replace +import gtk + +from latin_utf8 import utf8_to_latin +u2l = utf8_to_latin + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +import const +from RelLib import * +import Date +from intl import gettext +_ = gettext + +#------------------------------------------------------------------------- +# +# date_cmp +# +#------------------------------------------------------------------------- +def date_cmp(rule,value): + sd = rule.get_start_date() + s = sd.mode + if s == Date.SingleDate.before: + return Date.compare_dates(rule,value) == 1 + elif s == Date.SingleDate.after: + return Date.compare_dates(rule,value) == -1 + elif sd.month == Date.UNDEF and sd.year != Date.UNDEF: + return sd.year == value.get_start_date().year + else: + return Date.compare_dates(rule,value) == 0 + +#------------------------------------------------------------------------- +# +# Rule +# +#------------------------------------------------------------------------- +class Rule: + """Base rule class""" + + labels = [] + + def __init__(self,list): + assert(type(list) == type([]),"Argument is not a list") + self.list = list + + def values(self): + return self.list + + def trans_name(self): + return _(self.name()) + + def name(self): + return 'None' + + def check(self): + return len(self.list) == len(self.labels) + + def apply(self,p): + return 1 + + def display_values(self): + v = [] + for i in range(0,len(self.list)): + if self.list[i]: + v.append('%s="%s"' % (_(self.labels[i]),self.list[i])) + return join(v,'; ') + +#------------------------------------------------------------------------- +# +# Everyone +# +#------------------------------------------------------------------------- +class Everyone(Rule): + """Matches Everyone""" + + labels = [] + + def name(self): + return 'Everyone' + + def apply(self,p): + return 1 + +#------------------------------------------------------------------------- +# +# HasIdOf +# +#------------------------------------------------------------------------- +class HasIdOf(Rule): + """Rule that checks for a person with a specific GID""" + + labels = [ _('ID') ] + + def name(self): + return 'Has the Id' + + def apply(self,p): + return p.getId() == self.list[0] + +#------------------------------------------------------------------------- +# +# IsFemale +# +#------------------------------------------------------------------------- +class IsFemale(Rule): + """Rule that checks for a person that is a female""" + + labels = [] + + def name(self): + return 'Is a female' + + def apply(self,p): + return p.getGender() == Person.female + +#------------------------------------------------------------------------- +# +# IsDescendantOf +# +#------------------------------------------------------------------------- +class IsDescendantOf(Rule): + """Rule that checks for a person that is a descendant + of a specified person""" + + labels = [ _('ID') ] + + def name(self): + return 'Is a descendant of' + + def apply(self,p): + return self.search(p) + + def search(self,p): + if p.getId() == self.list[0]: + return 1 + for (f,r1,r2) in p.getParentList(): + for p1 in [f.getMother(),f.getFather()]: + if p1: + if self.search(p1): + return 1 + return 0 + +#------------------------------------------------------------------------- +# +# IsDescendantFamilyOf +# +#------------------------------------------------------------------------- +class IsDescendantFamilyOf(Rule): + """Rule that checks for a person that is a descendant or the spouse + of a descendant of a specified person""" + + labels = [ _('ID') ] + + def name(self): + return "Is a descendant family member of" + + def apply(self,p): + return self.search(p,1) + + def search(self,p,val): + if p.getId() == self.list[0]: + return 1 + for (f,r1,r2) in p.getParentList(): + for p1 in [f.getMother(),f.getFather()]: + if p1: + if self.search(p1,0): + return 1 + if val: + for fm in p.getFamilyList(): + if p == fm.getFather(): + s = fm.getMother() + else: + s = fm.getFather() + if s: + if self.search(s,0): + return 1 + + return 0 + +#------------------------------------------------------------------------- +# +# IsAncestorOf +# +#------------------------------------------------------------------------- +class IsAncestorOf(Rule): + """Rule that checks for a person that is an ancestor of a specified person""" + + labels = [ _('ID') ] + + def name(self): + return 'Is an ancestor of' + + def apply(self,p): + return self.search(p) + + def search(self,p): + if p.getId() == self.list[0]: + return 1 + + for f in p.getFamilyList(): + for p1 in f.getChildList(): + if p1: + if self.search(p1): + return 1 + return 0 + +#------------------------------------------------------------------------- +# +# IsMale +# +#------------------------------------------------------------------------- +class IsMale(Rule): + """Rule that checks for a person that is a male""" + + labels = [] + + def name(self): + return 'Is a male' + + def apply(self,p): + return p.getGender() == Person.male + +#------------------------------------------------------------------------- +# +# HasEvent +# +#------------------------------------------------------------------------- +class HasEvent(Rule): + """Rule that checks for a person with a particular value""" + + labels = [ _('Personal Event'), _('Date'), _('Place'), _('Description') ] + + def __init__(self,list): + Rule.__init__(self,list) + if self.list[0]: + self.date = Date.Date() + self.date.set(self.list[0]) + else: + self.date = None + + def name(self): + return 'Has the personal event' + + def apply(self,p): + for event in p.getEventList(): + val = 1 + if self.list[0] and event.getName() != self.list[0]: + val = 0 + if self.list[3] and find(event.getDescription(),self.list[3])==-1: + val = 0 + if self.date: + if date_cmp(self.date,event.getDateObj()): + val = 0 + if self.list[2] and find(p.getPlaceName(),self.list[2]) == -1: + val = 0 + if val == 1: + return 1 + return 0 + +#------------------------------------------------------------------------- +# +# HasFamilyEvent +# +#------------------------------------------------------------------------- +class HasFamilyEvent(Rule): + """Rule that checks for a person who has a relationship event + with a particular value""" + + labels = [ _('Family Event'), _('Date'), _('Place'), _('Description') ] + + def __init__(self,list): + Rule.__init__(self,list) + if self.list[0]: + self.date = Date.Date() + self.date.set(self.list[0]) + else: + self.date = None + + def name(self): + return 'Has the family event' + + def apply(self,p): + for f in p.getFamilyList(): + for event in f.getEventList(): + val = 1 + if self.list[0] and event.getName() != self.list[0]: + val = 0 + v = self.list[3] + if v and find(event.getDescription(),v)==-1: + val = 0 + if self.date: + if date_cmp(self.date,event.getDateObj()): + val = 0 + if self.list[2] and find(p.getPlaceName(),self.list[2]) == -1: + val = 0 + if val == 1: + return 1 + return 0 + +#------------------------------------------------------------------------- +# +# HasRelationship +# +#------------------------------------------------------------------------- +class HasRelationship(Rule): + """Rule that checks for a person who has a particular relationship""" + + labels = [ _('Number of Relationships'), + _('Relationship Type'), + _('Number of Children') ] + + def name(self): + return 'Has the relationships' + + def apply(self,p): + rel_type = 0 + cnt = 0 + num_rel = len(p.getFamilyList()) + + # count children and look for a relationship type match + for f in p.getFamilyList(): + cnt = cnt + len(f.getChildList()) + if self.list[1] and f.getRelationship() == self.list[1]: + rel_type = 1 + + # if number of relations specified + if self.list[0]: + try: + v = int(self.list[0]) + except: + return 0 + if v != num_rel: + return 0 + + # number of childred + if self.list[2]: + try: + v = int(self.list[2]) + except: + return 0 + if v != cnt: + return 0 + + # relation + if self.list[1]: + return rel_type == 1 + else: + return 1 + +#------------------------------------------------------------------------- +# +# HasBirth +# +#------------------------------------------------------------------------- +class HasBirth(Rule): + """Rule that checks for a person with a birth of a particular value""" + + labels = [ _('Date'), _('Place'), _('Description') ] + + def __init__(self,list): + Rule.__init__(self,list) + if self.list[0]: + self.date = Date.Date() + self.date.set(self.list[0]) + else: + self.date = None + + def name(self): + return 'Has the birth' + + def apply(self,p): + event = p.getBirth() + if len(self.list) > 2 and find(event.getDescription(),self.list[2])==-1: + return 0 + if self.date: + if date_cmp(self.date,event.getDateObj()) == 0: + return 0 + if len(self.list) > 1 and find(event.getPlaceName(),self.list[1]) == -1: + return 0 + return 1 + +#------------------------------------------------------------------------- +# +# HasDeath +# +#------------------------------------------------------------------------- +class HasDeath(Rule): + """Rule that checks for a person with a death of a particular value""" + + labels = [ _('Date'), _('Place'), _('Description') ] + + def __init__(self,list): + Rule.__init__(self,list) + if self.list[0]: + self.date = Date.Date() + self.date.set(self.list[0]) + else: + self.date = None + + def name(self): + return 'Has the death' + + def apply(self,p): + event = p.getDeath() + if self.list[2] and find(event.getDescription(),self.list[2])==-1: + return 0 + if self.date: + if date_cmp(self.date,event.getDateObj()) == 0: + return 0 + if self.list[1] and find(p.getPlaceName(),self.list[1]) == -1: + return 0 + return 1 + +#------------------------------------------------------------------------- +# +# HasAttribute +# +#------------------------------------------------------------------------- +class HasAttribute(Rule): + """Rule that checks for a person with a particular personal attribute""" + + labels = [ _('Personal Attribute'), _('Value') ] + + def name(self): + return 'Has the personal attribute' + + def apply(self,p): + for event in p.getAttributes(): + if self.list[0] and event.getType() != self.list[0]: + return 0 + if self.list[1] and find(event.getValue(),self.list[1])==-1: + return 0 + return 1 + +#------------------------------------------------------------------------- +# +# HasFamilyAttribute +# +#------------------------------------------------------------------------- +class HasFamilyAttribute(Rule): + """Rule that checks for a person with a particular family attribute""" + + labels = [ _('Family Attribute'), _('Value') ] + + def name(self): + return 'Has the family attribute' + + def apply(self,p): + for f in p.getFamilyList(): + for event in f.getAttributes(): + val = 1 + if self.list[0] and event.getType() != self.list[0]: + val = 0 + if self.list[1] and find(event.getValue(),self.list[1])==-1: + val = 0 + if val == 1: + return 1 + return 0 + +#------------------------------------------------------------------------- +# +# HasNameOf +# +#------------------------------------------------------------------------- +class HasNameOf(Rule): + """Rule that checks for full or partial name matches""" + + labels = [_('Given Name'),_('Surname'),_('Suffix'),_('Title')] + + def name(self): + return 'Has a name' + + def apply(self,p): + self.f = self.list[0] + self.l = self.list[1] + self.s = self.list[2] + self.t = self.list[3] + for name in [p.getPrimaryName()] + p.getAlternateNames(): + val = 1 + if self.f and find(name.getFirstName(),self.f) == -1: + val = 0 + if self.l and find(name.getSurname(),self.l) == -1: + val = 0 + if self.s and find(name.getSuffix(),self.s) == -1: + val = 0 + if self.t and find(name.getTitle(),self.t) == -1: + val = 0 + if val == 1: + return 1 + return 0 + + +class MatchesFilter(Rule): + """Rule that checks against another filter""" + + labels = [_('Filter Name')] + + def name(self): + return 'Matches the filter named' + + def apply(self, p): + for filter in SystemFilters.get_filters(): + if filter.get_name() == self.list[0]: + return filter.check(p) + for filter in CustomFilters.get_filters(): + if filter.get_name() == self.list[0]: + return filter.check(p) + return 0 + +#------------------------------------------------------------------------- +# +# GenericFilter +# +#------------------------------------------------------------------------- +class GenericFilter: + """Filter class that consists of several rules""" + + def __init__(self,source=None): + if source: + self.flist = source.flist[:] + self.name = source.name + self.comment = source.comment + self.logical_op = source.logical_op + self.invert = source.invert + else: + self.flist = [] + self.name = '' + self.comment = '' + self.logical_op = 'and' + self.invert = 0 + + def set_logical_op(self,val): + if val in const.logical_functions: + self.logical_op = val + else: + self.logical_op = 'and' + + def get_logical_op(self): + return self.logical_op + + def set_invert(self, val): + self.invert = not not val + + def get_invert(self): + return self.invert + + def get_name(self): + return self.name + + def set_name(self,name): + self.name = name + + def set_comment(self,comment): + self.comment = comment + + def get_comment(self): + return self.comment + + def add_rule(self,rule): + self.flist.append(rule) + + def set_rules(self,rules): + self.flist = rules + + def get_rules(self): + return self.flist + + def check_or(self,p): + test = 0 + for rule in self.flist: + test = test or rule.apply(p) + if test: + break + if self.invert: + return not test + else: + return test + + def check_xor(self,p): + test = 0 + for rule in self.flist: + temp = rule.apply(p) + test = ((not test) and temp) or (test and (not temp)) + if self.invert: + return not test + else: + return test + + def check_one(self,p): + count = 0 + for rule in self.flist: + if rule.apply(p): + count = count + 1 + if count > 1: + break + if self.invert: + return count != 1 + else: + return count == 1 + + def check_and(self,p): + test = 1 + for rule in self.flist: + test = test and rule.apply(p) + if not test: + break + if self.invert: + return not test + else: + return test + + def check(self,p): + try: + m = getattr(self, 'check_' + self.logical_op) + except AttributeError: + m = self.check_and + + return m(p) + + def apply(self,list): + try: + m = getattr(self, 'check_' + self.logical_op) + except AttributeError: + m = self.check_and + + return filter(m, list) + +#------------------------------------------------------------------------- +# +# Name to class mappings +# +#------------------------------------------------------------------------- +tasks = { + _("Everyone") : Everyone, + _("Has the Id") : HasIdOf, + _("Has a name") : HasNameOf, + _("Has the relationships") : HasRelationship, + _("Has the death") : HasDeath, + _("Has the birth") : HasBirth, + _("Is a descendant of") : IsDescendantOf, + _("Is a descendant family member of"): IsDescendantFamilyOf, + _("Is an ancestor of") : IsAncestorOf, + _("Is a female") : IsFemale, + _("Is a male") : IsMale, + _("Has the personal event") : HasEvent, + _("Has the family event") : HasFamilyEvent, + _("Has the personal attribute") : HasAttribute, + _("Has the family attribute") : HasFamilyAttribute, + _("Matches the filter named") : MatchesFilter, + } + +#------------------------------------------------------------------------- +# +# GenericFilterList +# +#------------------------------------------------------------------------- +class GenericFilterList: + """Container class for the generic filters. Stores, saves, and + loads the filters.""" + + def __init__(self,file): + self.filter_list = [] + self.file = os.path.expanduser(file) + + def get_filters(self): + return self.filter_list + + def add(self,filter): + self.filter_list.append(filter) + + def load(self): + try: + parser = make_parser() + parser.setContentHandler(FilterParser(self)) + parser.parse(self.file) + except (IOError,OSError,SAXParseException): + pass + + def fix(self,line): + l = strip(line) + l = replace(l,'&','&') + l = replace(l,'>','>') + l = replace(l,'<','<') + return replace(l,'"','"') + + def save(self): + try: + f = open(self.file,'w') + except: + return + + f.write("\n") + f.write('\n') + for i in self.filter_list: + f.write(' \n') + for rule in i.get_rules(): + f.write(' \n' % self.fix(rule.name())) + for v in rule.values(): + f.write(' \n' % self.fix(v)) + f.write(' \n') + f.write(' \n') + f.write('\n') + f.close() + +#------------------------------------------------------------------------- +# +# FilterParser +# +#------------------------------------------------------------------------- +class FilterParser(handler.ContentHandler): + """Parses the XML file and builds the list of filters""" + + def __init__(self,gfilter_list): + handler.ContentHandler.__init__(self) + self.gfilter_list = gfilter_list + self.f = None + self.r = None + self.a = [] + self.cname = None + + def setDocumentLocator(self,locator): + self.locator = locator + + def startElement(self,tag,attrs): + if tag == "filter": + self.f = GenericFilter() + self.f.set_name(u2l(attrs['name'])) + if attrs.has_key('function'): + try: + if int(u2l(attrs['function'])): + op = 'or' + else: + op = 'and' + except ValueError: + op = u2l(attrs['function']) + self.f.set_logical_op(op) + if attrs.has_key('comment'): + self.f.set_comment(u2l(attrs['comment'])) + if attrs.has_key('invert'): + try: + self.f.set_invert(int(u2l(attrs['invert']))) + except ValueError: + pass + self.gfilter_list.add(self.f) + elif tag == "rule": + c = attrs['class'] + name = _(u2l(c)) + self.a = [] + self.cname = tasks[name] + elif tag == "arg": + c = attrs['value'] + self.a.append(u2l(c)) + + def endElement(self,tag): + if tag == "rule": + rule = self.cname(self.a) + self.f.add_rule(rule) + + def characters(self, data): + pass + + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +SystemFilters = None +CustomFilters = None + +def reload_system_filters(): + global SystemFilters + SystemFilters = GenericFilterList(const.system_filters) + SystemFilters.load() + +def reload_custom_filters(): + global CustomFilters + CustomFilters = GenericFilterList(const.custom_filters) + CustomFilters.load() + +if not SystemFilters: + reload_system_filters() + +if not CustomFilters: + reload_custom_filters() + +def build_filter_menu(local_filters = []): + menu = gtk.GtkMenu() + + menuitem = gtk.GtkMenuItem(_("Local Filters")) + menu.append(menuitem) + menuitem.show() + menuitem.set_sensitive(0) + + menuitem = gtk.GtkMenuItem() + menuitem.show() + menu.append(menuitem) + + for filter in local_filters: + menuitem = gtk.GtkMenuItem(filter.get_name()) + menuitem.show() + menu.append(menuitem) + menuitem.set_data("filter", filter) + + menuitem = gtk.GtkMenuItem(_("System Filters")) + menuitem.show() + menu.append(menuitem) + menuitem.set_sensitive(0) + + menuitem = gtk.GtkMenuItem() + menuitem.show() + menu.append(menuitem) + + for filter in SystemFilters.get_filters(): + menuitem = gtk.GtkMenuItem(_(filter.get_name())) + menuitem.show() + menu.append(menuitem) + menuitem.set_data("filter", filter) + + menuitem = gtk.GtkMenuItem(_("Custom Filters")) + menu.append(menuitem) + menuitem.show() + menuitem.set_sensitive(0) + + menuitem = gtk.GtkMenuItem() + menuitem.show() + menu.append(menuitem) + + for filter in CustomFilters.get_filters(): + menuitem = gtk.GtkMenuItem(_(filter.get_name())) + menuitem.show() + menu.append(menuitem) + menuitem.set_data("filter", filter) + + if len(local_filters): + menu.set_active(2) + elif len(SystemFilters.get_filters()): + menu.set_active(4 + len(local_filters)) + elif len(CustomFilters.get_filters()): + menu.set_active(6 + len(local_filters) + len(SystemFilters.get_filters())) + else: + menu.set_active(0) + + return menu diff --git a/gramps2/src/GrampsCfg.py b/gramps2/src/GrampsCfg.py new file mode 100644 index 000000000..07bfbf585 --- /dev/null +++ b/gramps2/src/GrampsCfg.py @@ -0,0 +1,1127 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + + +#------------------------------------------------------------------------- +# +# Standard python modules +# +#------------------------------------------------------------------------- +import string +import os + +import PaperMenu +import Plugins + +#------------------------------------------------------------------------- +# +# GTK/Gnome modules +# +#------------------------------------------------------------------------- +import gobject +import gtk +import gconf +import gtk.glade + +#------------------------------------------------------------------------- +# +# gramps modules +# +#------------------------------------------------------------------------- +from RelLib import * +from Date import * + +import const +import Utils + +from intl import gettext as _ + +client = gconf.client_get_default() +client.add_dir("/apps/gramps",gconf.CLIENT_PRELOAD_NONE) + +_surname_styles = [ + _("Father's surname"), + _("None"), + _("Combination of mother's and father's surname"), + _("Icelandic style"), + ] + +_date_format_list = [ + _("Month Day, Year"), + _("MON Day, Year"), + _("Day MON Year"), + _("MM/DD/YYYY"), + _("MM-DD-YYYY"), + _("DD/MM/YYYY"), + _("DD-MM-YYYY"), + _("MM.DD.YYYY"), + _("DD.MM.YYYY"), + _("DD. Month Year"), + _("YYYY/MM/DD"), + _("YYYY-MM-DD"), + _("YYYY.MM.DD"), + ] + +_date_entry_list = [ + _("MM/DD/YYYY, MM.DD.YYYY, or MM-DD-YYYY"), + _("DD/MM/YYYY, DD.MM.YYYY, or DD-MM-YYYY"), + _("YYYY/MM/DD, YYYY.MM.DD, or YYYY-MM-DD"), + ] + +_name_format_list = [ + (_("Firstname Surname"), Utils.normal_name), + (_("Surname, Firstname"), Utils.phonebook_name), + ] + +panellist = [ + (_("Database"), + [( _("General"), 1), + ( _("Dates and Calendars"), 6), + ( _("Media Objects"), 9), + ( _("GRAMPS internal IDs"), 10), + ( _("Revision Control"),2)]), + (_("Display"), + [( _("General"), 5), + ( _("Tool and Status Bars"), 4)]), + (_("Usage"), + [( _("Find"), 3), + ( _("Report Preferences"), 8), + ( _("Researcher Information"), 7), + ( _("Data Guessing"), 11)]), + ] + + +#------------------------------------------------------------------------- +# +# Visible globals +# +#------------------------------------------------------------------------- + +iprefix = "I" +oprefix = "O" +sprefix = "S" +pprefix = "P" +fprefix = "F" +autoload = 0 +autosave_int = 0 +usetabs = 0 +uselds = 0 +autocomp = 1 +usevc = 0 +vc_comment = 0 +uncompress = 0 +show_detail = 0 +hide_altnames = 0 +lastfile = None +nameof = Utils.normal_name +display_attr = 0 +attr_name = "" +status_bar = 0 +toolbar = 2 +calendar = 0 +paper_preference = "" +output_preference = "" +goutput_preference = "" +lastnamegen = None +report_dir = "./" +web_dir = "./" +db_dir = "./" +id_visible = 0 +id_edit = 0 +index_visible = 0 +mediaref = 1 +globalprop = 1 +localprop = 1 + +#------------------------------------------------------------------------- +# +# Globals +# +#------------------------------------------------------------------------- +_name_format = 0 +_callback = None + +#------------------------------------------------------------------------- +# +# Constants +# +#------------------------------------------------------------------------- +ODDFGCOLOR = "oddfg" +ODDBGCOLOR = "oddbg" +EVENFGCOLOR = "evenfg" +EVENBGCOLOR = "evenbg" +ANCESTORFGCOLOR = "ancestorfg" +INDEX = "i" +OBJECT = "o" +DATA = "d" + +#------------------------------------------------------------------------- +# +# make_path - +# Creates a directory if it does not already exist. Assumes that the +# parent directory already exits +# +#------------------------------------------------------------------------- +def make_path(path): + if not os.path.isdir(path): + os.mkdir(path) + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +def loadConfig(call): + global autoload + global autosave_int + global usetabs + global uselds + global autocomp + global calendar + global usevc + global iprefix, fprefix, pprefix, oprefix, sprefix + global vc_comment + global uncompress + global id_visible + global id_edit + global index_visible + global show_detail + global hide_altnames + global lastfile + global nameof + global display_attr + global attr_name + global _name_format + global _callback + global paper_preference + global output_preference + global goutput_preference + global lastnamegen + global report_dir + global web_dir + global db_dir + global status_bar + global toolbar + global mediaref + global globalprop + global localprop + + _callback = call + lastfile = get_string("/gramps/data/LastFile") + usetabs = get_bool("/gramps/config/UseTabs") + uselds = get_bool("/gramps/config/UseLDS") + ac = get_bool("/gramps/config/DisableAutoComplete",0) + mediaref = get_bool("/gramps/config/MakeReference",1) + globalprop = get_bool("/gramps/config/DisplayGlobal",1) + localprop = get_bool("/gramps/config/DisplayLocal",1) + calendar = get_bool("/gramps/config/ShowCalendar") + usevc = get_bool("/gramps/config/UseVersionControl") + vc_comment = get_bool("/gramps/config/UseComment") + uncompress = get_bool("/gramps/config/DontCompressXML") + id_visible = get_bool("/gramps/config/IdVisible") + id_edit = get_bool("/gramps/config/IdEdit") + index_visible = get_bool("/gramps/config/IndexVisible") + show_detail = get_bool("/gramps/config/ShowDetail") + status_bar = get_int("/gramps/config/StatusBar") + toolbar = get_int("/gramps/config/ToolBar",2) + display_attr = get_bool("/gramps/config/DisplayAttr") + attr_name = get_string("/gramps/config/DisplayAttrName") + + hide_altnames = get_bool("/gramps/config/DisplayAltNames") + autoload = get_bool("/gramps/config/autoLoad",0) + autosave_int = get_int("/gramps/config/autoSaveInterval") + dateFormat = get_int("/gramps/config/dateFormat") + dateEntry = get_int("/gramps/config/dateEntry") + paper_preference = get_string("/gramps/config/paperPreference") + output_preference = get_string("/gramps/config/outputPreference") + goutput_preference = get_string("/gramps/config/goutputPreference") + lastnamegen = get_int("/gramps/config/surnameGuessing") + _name_format = get_int("/gramps/config/nameFormat") + + iprefix = get_string("/gramps/config/iprefix") + fprefix = get_string("/gramps/config/fprefix") + sprefix = get_string("/gramps/config/sprefix") + oprefix = get_string("/gramps/config/oprefix") + pprefix = get_string("/gramps/config/pprefix") + + report_dir = get_string("/gramps/config/ReportDirectory") + web_dir = get_string("/gramps/config/WebsiteDirectory") + db_dir = get_string("/gramps/config/DbDirectory") + + if report_dir == None: + report_dir = "./" + else: + report_dir = os.path.normpath(report_dir) + os.sep + + if web_dir == None: + web_dir = "./" + else: + web_dir = os.path.normpath(web_dir) + os.sep + + if db_dir == None: + db_dir = "./" + else: + db_dir = os.path.normpath(db_dir) + os.sep + + en = get_bool("/gramps/color/enableColors") + if en == None: + en = 0 + + if paper_preference == None: + paper_preference = "Letter" + + if output_preference == None: + output_preference = "" + + if goutput_preference == None: + goutput_preference = "" + + if iprefix == None: + iprefix = "I" + if fprefix == None: + fprefix = "F" + if sprefix == None: + sprefix = "S" + if pprefix == None: + pprefix = "P" + if oprefix == None: + oprefix = "O" + + if display_attr == None: + display_attr = 0 + + if attr_name == None: + attr_name = "" + + autocomp = not ac + + set_format_code(dateFormat) + Date.entryCode = dateEntry + + if _name_format == 0: + nameof = Utils.normal_name + else: + nameof = Utils.phonebook_name + + make_path(os.path.expanduser("~/.gramps")) + make_path(os.path.expanduser("~/.gramps/filters")) + make_path(os.path.expanduser("~/.gramps/plugins")) + make_path(os.path.expanduser("~/.gramps/templates")) + +def get_string(value,defval=""): + v = client.get_string(value) + if v == None: + return defval + else: + return v + +def get_bool(key,defval=0): + v = client.get_bool(key) + if v: + return v + else: + return defval + +def get_int(key,defval=0): + v = client.get_int(key) + if v: + return v + else: + return defval + +def set_int(key,value): + client.set_int(key,value) + +def set_bool(key,value): + client.set_bool(key,value) + +def set_string(key,value): + client.set_string(key,value) + +def get_string(key): + val = client.get_string(key) + if val: + return val + else: + return "" + +def sync(): + client.suggest_sync() + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +def save_last_file(file): + set_string("/gramps/data/LastFile",file) + sync() + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +def get_researcher(): + n = get_string("/gramps/researcher/name") + a = get_string("/gramps/researcher/addr") + c = get_string("/gramps/researcher/city") + s = get_string("/gramps/researcher/state") + ct = get_string("/gramps/researcher/country") + p = get_string("/gramps/researcher/postal") + ph = get_string("/gramps/researcher/phone") + e = get_string("/gramps/researcher/email") + + owner = Researcher() + owner.set(n,a,c,s,ct,p,ph,e) + return owner + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +def save_config_color(name,color): + set_int("/gramps/color/" + name + ".r",color[0]) + set_int("/gramps/color/" + name + ".g",color[1]) + set_int("/gramps/color/" + name + ".b",color[2]) + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- + +def get_config_text(panel,key): + val = get_string("/gramps/%s/%s" % (panel,key)) + if val: + return val + else: + return "" + +def get_config_bool(panel,key): + return get_bool("/gramps/%s/%s" % (panel,key)) + +def get_config_int(panel,key): + return get_int("/gramps/%s/%s" % (panel,key)) + +ext_items = [] + +class ConfigWidget: + def __init__(self,panel,key,label,default): + self.p = panel + self.k = key + self.l = label + self.w = None + self.d = default + self.tag = "/gramps/%s/%s" % (panel,key) + + def set(self): + pass + +class ConfigEntry(ConfigWidget): + + def get_widgets(self): + l = gtk.Label(self.l) + l.show() + self.w = gtk.Entry() + self.w.show() + + val = get_string(self.tag) + if val == None: + self.w.set_text(self.d) + else: + self.w.set_text(val) + return [l,self.w] + + def set(self): + val = self.w.get_text() + set_string(self.tag,val) + +class ConfigInt(ConfigWidget): + + def set_range(self,lower,upper): + self.lower = lower + self.upper = upper + + def get_widgets(self): + l = gtk.Label(self.l) + l.show() + self.w = gtk.SpinButton(digits=0) + self.w.show() + + val = get_string(self.tag) + if val == None: + val = int(self.d) + else: + val = int(val) + + adj = gtk.Adjustment(val,self.lower,self.upper,1,1,1) + + self.w.set_adjustment(adj) + return [l,self.w] + + def set(self): + val = self.w.get_value_as_int() + set_int(self.tag,val) + +class ConfigCheckbox(ConfigWidget): + + def get_widgets(self): + self.w = gtk.CheckButton(self.l) + self.w.show() + val = get_bool(self.tag) + if val == None: + self.w.set_active(self.d) + else: + self.w.set_active(val) + return [self.w] + + def set(self): + val = self.w.get_active() + set_bool(self.tag,val) + + +class ConfigFile(ConfigWidget): + + def get_widgets(self): + self.w = gnome.ui.GnomeFileEntry(self.tag) + lbl = gtk.Label(self.l) + self.w.show() + lbl.show() + val = get_string(self.tag) + self.w.set_title("%s -- GRAMPS" % (self.l)) + if val == None: + self.w.gtk_entry().set_text(self.d) + else: + self.w.gtk_entry().set_text(val) + return [lbl,self.w] + + def set(self): + val = self.w.get_full_path(0) + set_string(self.tag,val) + + +def add_text(category,panel,frame,config_tag,label,default): + ext_items.append((category,panel,frame,ConfigEntry(panel,config_tag,label,default))) + +def add_file_entry(category,panel,frame,config_tag,label,default): + ext_items.append((category,panel,frame,ConfigFile(panel,config_tag,label,default))) + +def add_int(category,panel,frame,config_tag,label,default,range=(0,100)): + cfgint = ConfigInt(panel,config_tag,label,default) + cfgint.set_range(range[0],range[1]) + ext_items.append((category,panel,frame,cfgint)) + +def add_checkbox(category,panel,frame,config_tag,label,default): + ext_items.append((category,panel,frame,ConfigCheckbox(panel,config_tag,label,default))) + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +class GrampsPreferences: + def __init__(self,db): + self.db = db + self.top = gtk.glade.XML(const.prefsFile,"preferences") + self.top.signal_autoconnect({ + "on_close_clicked" : self.on_close_clicked, + "on_ok_clicked" : self.on_ok_clicked, + "on_apply_clicked" : self.on_propertybox_apply, + "on_help_clicked" : self.on_propertybox_help, + "on_color_toggled" : self.on_color_toggled, + "on_color_set" : self.on_color_set, + "on_object_toggled" : self.on_object_toggled, + "on_tree_select_row" : self.select + }) + + self.window = self.top.get_widget("preferences") + self.apply = self.top.get_widget("apply") + self.tree = self.top.get_widget("tree") + self.store = gtk.TreeStore(gobject.TYPE_STRING) + self.selection = self.tree.get_selection() + self.selection.connect('changed',self.select) + col = gtk.TreeViewColumn('',gtk.CellRendererText(),text=0) + self.tree.append_column(col) + self.tree.set_model(self.store) + self.panel = self.top.get_widget("panel") + self.ofmt = self.top.get_widget("output_format") + self.gfmt = self.top.get_widget("graphical_format") + + self.imap = {} + self.build_tree() + self.build() + self.build_ext() + self.apply.set_sensitive(0) + + def build_tree(self): + prev = None + ilist = [] + for (name,list) in panellist: + node = self.store.insert_after(None, prev) + self.store.set(node,0,name) + next = None + for (subname,tab) in list: + next = self.store.insert_after(node,next) + ilist.append((next,tab)) + self.store.set(next,0,subname) + for next,tab in ilist: + path = self.store.get_path(next) + self.imap[path] = tab + + def build(self): + auto = self.top.get_widget("autoload") + asave_int = self.top.get_widget("autosave_interval") + vis = self.top.get_widget("gid_visible") + idedit = self.top.get_widget("gid_edit") + index_vis = self.top.get_widget("show_child_id") + tabs = self.top.get_widget("usetabs") + lds = self.top.get_widget("uselds") + ac = self.top.get_widget("autocomp") + mr = self.top.get_widget("mediaref") + mc = self.top.get_widget("mediacopy") + dg = self.top.get_widget("globalprop") + dl = self.top.get_widget("localprop") + cal = self.top.get_widget("calendar") + vc = self.top.get_widget("use_vc") + vcom = self.top.get_widget("vc_comment") + compress = self.top.get_widget("uncompress") + detail = self.top.get_widget("showdetail") + display_attr_obj = self.top.get_widget("attr_display") + display_altnames = self.top.get_widget("display_altnames") + + auto.set_active(autoload) + asave_int.set_value(int(autosave_int)) + detail.set_active(show_detail) + tabs.set_active(usetabs) + lds.set_active(uselds) + ac.set_active(autocomp) + if mediaref: + mr.set_active(1) + else: + mc.set_active(1) + dg.set_active(globalprop) + dl.set_active(localprop) + cal.set_active(calendar) + vc.set_active(usevc) + vcom.set_active(vc_comment) + compress.set_active(uncompress) + vis.set_active(id_visible) + idedit.set_active(id_edit) + index_vis.set_active(index_visible) + + self.top.get_widget("iprefix").set_text(iprefix) + self.top.get_widget("oprefix").set_text(oprefix) + self.top.get_widget("fprefix").set_text(fprefix) + self.top.get_widget("sprefix").set_text(sprefix) + self.top.get_widget("pprefix").set_text(pprefix) + + if status_bar == 0: + self.top.get_widget("stat1").set_active(1) + elif status_bar == 1: + self.top.get_widget("stat2").set_active(1) + else: + self.top.get_widget("stat3").set_active(1) + + if toolbar == 0: + self.top.get_widget("tool1").set_active(1) + elif toolbar == 1: + self.top.get_widget("tool2").set_active(1) + else: + self.top.get_widget("tool3").set_active(1) + + display_attr_obj.set_active(display_attr) + self.top.get_widget("attr_name").set_text(attr_name) + + display_altnames.set_active(hide_altnames) + + paper_obj = self.top.get_widget("paper_size") + menu = gtk.Menu() + choice = 0 + for index in range(0,len(PaperMenu.paper_sizes)): + name = PaperMenu.paper_sizes[index].get_name() + if name == paper_preference: + choice = index + item = gtk.MenuItem(name) + item.set_data(DATA,name) + item.connect("activate", self.on_format_toggled) + item.show() + menu.append(item) + menu.set_active(choice) + paper_obj.set_menu(menu) + + lastnamegen_obj = self.top.get_widget("lastnamegen") + menu = gtk.Menu() + choice = 0 + for index in range(0,len(_surname_styles)): + name = _surname_styles[index] + item = gtk.MenuItem(name) + item.set_data(DATA,index) + item.connect("activate", self.on_format_toggled) + item.show() + menu.append(item) + menu.set_active(lastnamegen) + lastnamegen_obj.set_menu(menu) + + self.osubmenu = gtk.Menu() + choice = 0 + index = 0 + for name in [ _("No default format") ] + Plugins.get_text_doc_list(): + if name == output_preference: + choice = index + item = gtk.MenuItem(name) + item.set_data(DATA,name) + item.connect("activate", self.on_format_toggled) + item.show() + self.osubmenu.append(item) + index = index + 1 + self.osubmenu.set_active(choice) + self.ofmt.set_menu(self.osubmenu) + + self.gsubmenu = gtk.Menu() + choice = 0 + index = 0 + for name in [ _("No default format") ] + Plugins.get_draw_doc_list(): + if name == goutput_preference: + choice = index + item = gtk.MenuItem(name) + item.set_data(DATA,name) + item.connect("activate", self.on_format_toggled) + item.show() + self.gsubmenu.append(item) + index = index + 1 + self.gsubmenu.set_active(choice) + self.gfmt.set_menu(self.gsubmenu) + + date_option = self.top.get_widget("date_format") + date_menu = gtk.Menu() + for index in range(0,len(_date_format_list)): + item = gtk.MenuItem(_date_format_list[index]) + item.set_data(INDEX,index) + item.connect("activate", self.on_format_toggled) + item.show() + date_menu.append(item) + date_menu.set_active(get_format_code()) + date_option.set_menu(date_menu) + + date_entry = self.top.get_widget("date_entry_format") + date_menu = gtk.Menu() + for index in range(0,len(_date_entry_list)): + item = gtk.MenuItem(_date_entry_list[index]) + item.set_data(INDEX,index) + item.connect("activate", self.on_format_toggled) + item.show() + date_menu.append(item) + date_menu.set_active(Date.entryCode) + date_entry.set_menu(date_menu) + + name_option = self.top.get_widget("name_format") + name_menu = gtk.Menu() + for index in range(0,len(_name_format_list)): + name_tuple = _name_format_list[index] + item = gtk.MenuItem(name_tuple[0]) + item.set_data(INDEX,index) + item.connect("activate", self.on_format_toggled) + item.show() + name_menu.append(item) + name_menu.set_active(_name_format) + name_option.set_menu(name_menu) + + cname = get_string("/gramps/researcher/name") + caddr = get_string("/gramps/researcher/addr") + ccity = get_string("/gramps/researcher/city") + cstate = get_string("/gramps/researcher/state") + ccountry = get_string("/gramps/researcher/country") + cpostal = get_string("/gramps/researcher/postal") + cphone = get_string("/gramps/researcher/phone") + cemail = get_string("/gramps/researcher/email") + + self.top.get_widget("resname").set_text(cname) + self.top.get_widget("resaddr").set_text(caddr) + self.top.get_widget("rescity").set_text(ccity) + self.top.get_widget("resstate").set_text(cstate) + self.top.get_widget("rescountry").set_text(ccountry) + self.top.get_widget("respostal").set_text(cpostal) + self.top.get_widget("resphone").set_text(cphone) + self.top.get_widget("resemail").set_text(cemail) + + self.top.get_widget("dbdir").gtk_entry().set_text(db_dir) + self.top.get_widget("repdir").gtk_entry().set_text(report_dir) + self.top.get_widget("htmldir").gtk_entry().set_text(web_dir) + + def build_ext(self): + self.c = {} + self.ext_list = [] + for (c,p,f,o) in ext_items: + self.ext_list.append(o) + if self.c.has_key(c): + if self.c[c][p].has_key(f): + self.c[c][p][f].append(o) + else: + self.c[c][p][f] = [o] + else: + self.c[c] = {} + self.c[c][p] = {} + self.c[c][p][f] = [o] + + next_panel=13 + for c in self.c.keys(): + node = self.tree.insert_node(None,None,[c],is_leaf=0,expanded=1) + self.tree.node_set_row_data(node,0) + next = None + for panel in self.c[c].keys(): + next = self.tree.insert_node(node,next,[panel],is_leaf=1,expanded=1) + self.tree.node_set_row_data(next,next_panel) + next_panel = next_panel + 1 + box = gtk.VBox() + box.show() + col = 0 + panel_label = gtk.Label("") + panel_label.show() + self.panel.append_page(box,panel_label) + for frame in self.c[c][panel].keys(): + pairs = self.c[c][panel][frame] + fr = gtk.Frame(frame) + fr.show() + box.pack_start(fr,gtk.FALSE,gtk.FALSE) + table = gtk.Table(len(pairs),2) + table.show() + fr.add(table) + for wobj in pairs: + w = wobj.get_widgets() + if len(w) == 2: + table.attach(w[0],0,1,col,col+1, + gtk.FILL,gtk.SHRINK,5,5) + table.attach(w[1],1,2,col,col+1, + gtk.FILL|gtk.EXPAND,gtk.SHRINK,5,5) + else: + table.attach(w[0],0,2,col,col+1, + gtk.FILL|gtk.EXPAND,gtk.SHRINK,5,5) + col = col + 1 + + def select(self,obj): + store,iter = self.selection.get_selected() + path = store.get_path(iter) + if iter and self.imap.has_key(path): + self.panel.set_current_page(self.imap[path]) + + def on_propertybox_help(self,obj): + import gnome.help + gnome.help.display('gramps-manual','prefs.html') + + def on_close_clicked(self,obj): + Utils.destroy_passed_object(self.window) + + def on_ok_clicked(self,obj): + self.on_propertybox_apply(obj) + Utils.destroy_passed_object(self.window) + + def on_propertybox_apply(self,obj): + global nameof + global usetabs + global uselds + global autocomp + global autosave_int + global mediaref + global globalprop + global localprop + global calendar + global usevc + global iprefix + global fprefix + global pprefix + global sprefix + global oprefix + global vc_comment + global uncompress + global id_visible + global id_edit + global index_visible + global status_bar + global toolbar + global display_attr + global attr_name + global hide_altnames + global paper_preference + global output_preference + global goutput_preference + global show_detail + global report_dir + global web_dir + global db_dir + global lastnamegen + global autoload + + show_detail = self.top.get_widget("showdetail").get_active() + autoload = self.top.get_widget("autoload").get_active() + autosave_int = self.top.get_widget("autosave_interval").get_value_as_int() + display_attr = self.top.get_widget("attr_display").get_active() + attr_name = string.strip(self.top.get_widget("attr_name").get_text()) + usetabs = self.top.get_widget("usetabs").get_active() + uselds = self.top.get_widget("uselds").get_active() + autocomp = self.top.get_widget("autocomp").get_active() + mediaref = self.top.get_widget("mediaref").get_active() + localprop = self.top.get_widget("localprop").get_active() + globalprop = self.top.get_widget("globalprop").get_active() + calendar = self.top.get_widget("calendar").get_active() + usevc = self.top.get_widget("use_vc").get_active() + vc_comment = self.top.get_widget("vc_comment").get_active() + uncompress = self.top.get_widget("uncompress").get_active() + id_visible = self.top.get_widget("gid_visible").get_active() + id_edit = self.top.get_widget("gid_edit").get_active() + index_visible = self.top.get_widget("show_child_id").get_active() + hide_altnames = self.top.get_widget("display_altnames").get_active() + paper_obj = self.top.get_widget("paper_size").get_menu().get_active() + + output_obj = self.osubmenu.get_active() + goutput_obj = self.gsubmenu.get_active() + + if self.top.get_widget("stat1").get_active(): + status_bar = 0 + elif self.top.get_widget("stat2").get_active(): + status_bar = 1 + else: + status_bar = 2 + + if self.top.get_widget("tool1").get_active(): + toolbar = 0 + elif self.top.get_widget("tool2").get_active(): + toolbar = 1 + else: + toolbar = 2 + + iprefix = self.top.get_widget("iprefix").get_text() + if iprefix == "": + iprefix = "I" + sprefix = self.top.get_widget("sprefix").get_text() + if sprefix == "": + sprefix = "S" + oprefix = self.top.get_widget("oprefix").get_text() + if oprefix == "": + oprefix = "O" + fprefix = self.top.get_widget("fprefix").get_text() + if fprefix == "": + fprefix = "F" + pprefix = self.top.get_widget("pprefix").get_text() + if pprefix == "": + pprefix = "P" + + dbdir_temp = self.top.get_widget("dbdir").get_full_path(1) + if dbdir_temp != None and os.path.isdir(dbdir_temp): + db_dir = os.path.normpath(dbdir_temp) + os.sep + + repdir_temp = self.top.get_widget("repdir").get_full_path(1) + if repdir_temp != None and os.path.isdir(repdir_temp): + report_dir = os.path.normpath(repdir_temp) + os.sep + + webdir_temp = self.top.get_widget("htmldir").get_full_path(1) + if webdir_temp != None and os.path.isdir(webdir_temp): + web_dir = os.path.normpath(webdir_temp) + os.sep + + paper_preference = paper_obj.get_data(DATA) + output_preference = output_obj.get_data(DATA) + goutput_preference = goutput_obj.get_data(DATA) + + set_bool("/gramps/config/UseTabs",usetabs) + set_bool("/gramps/config/UseLDS",uselds) + set_bool("/gramps/config/DisableAutoComplete",not autocomp) + set_bool("/gramps/config/MakeReference",mediaref) + set_bool("/gramps/config/DisplayGlobal",globalprop) + set_bool("/gramps/config/DisplayLocal",localprop) + set_bool("/gramps/config/ShowCalendar",calendar) + set_bool("/gramps/config/UseVersionControl",usevc) + set_bool("/gramps/config/UseComment",vc_comment) + set_bool("/gramps/config/DontCompressXML",uncompress) + set_bool("/gramps/config/IdVisible",id_visible) + set_bool("/gramps/config/IdEdit",id_edit) + set_bool("/gramps/config/IndexVisible",index_visible) + set_bool("/gramps/config/ShowDetail",show_detail) + set_int("/gramps/config/StatusBar",status_bar) + set_int("/gramps/config/ToolBar",toolbar+1) + set_bool("/gramps/config/DisplayAttr",display_attr) + set_string("/gramps/config/DisplayAttrName",attr_name) + set_string("/gramps/config/paperPreference",paper_preference) + set_string("/gramps/config/outputPreference",output_preference) + set_string("/gramps/config/goutputPreference",goutput_preference) + set_bool("/gramps/config/autoLoad",autoload) + set_int("/gramps/config/autoSaveInterval",autosave_int) + + if autosave_int != 0: + Utils.enable_autosave(None,autosave_int) + else: + Utils.disable_autosave() + + set_bool("/gramps/config/DisplayAltNames",hide_altnames) + set_string("/gramps/config/ReportDirectory",report_dir) + set_string("/gramps/config/WebsiteDirectory",web_dir) + set_string("/gramps/config/DbDirectory",db_dir) + set_string("/gramps/config/iprefix",iprefix) + set_string("/gramps/config/fprefix",fprefix) + set_string("/gramps/config/pprefix",pprefix) + set_string("/gramps/config/oprefix",oprefix) + set_string("/gramps/config/sprefix",sprefix) + + # search for the active date format selection + format_menu = self.top.get_widget("date_format").get_menu() + active = format_menu.get_active().get_data(INDEX) + + set_format_code(active) + set_int("/gramps/config/dateFormat",active) + + format_menu = self.top.get_widget("date_entry_format").get_menu() + entry_active = format_menu.get_active().get_data(INDEX) + + Date.entryCode = entry_active + set_int("/gramps/config/dateEntry",entry_active) + + # get the name format + + format_menu = self.top.get_widget("name_format").get_menu() + active_name = format_menu.get_active().get_data(INDEX) + + name_tuple = _name_format_list[active_name] + nameof = name_tuple[1] + set_int("/gramps/config/nameFormat",active_name) + + format_menu = self.top.get_widget("lastnamegen").get_menu() + lastnamegen = format_menu.get_active().get_data(DATA) + set_int("/gramps/config/surnameGuessing",lastnamegen) + + name = self.top.get_widget("resname").get_text() + addr = self.top.get_widget("resaddr").get_text() + city = self.top.get_widget("rescity").get_text() + state = self.top.get_widget("resstate").get_text() + country = self.top.get_widget("rescountry").get_text() + postal = self.top.get_widget("respostal").get_text() + phone = self.top.get_widget("resphone").get_text() + email = self.top.get_widget("resemail").get_text() + + Sorter.set_enable(self.top.get_widget("enableColors").get_active()) + set_bool("/gramps/color/enableColors",Sorter.get_enable()) + + Sorter.oddfg = self.top.get_widget(ODDFGCOLOR).get_i16() + Sorter.oddbg = self.top.get_widget(ODDBGCOLOR).get_i16() + Sorter.evenfg = self.top.get_widget(EVENFGCOLOR).get_i16() + Sorter.evenbg = self.top.get_widget(EVENBGCOLOR).get_i16() + Sorter.ancestorfg = self.top.get_widget(ANCESTORFGCOLOR).get_i16() + + save_config_color(ODDFGCOLOR,Sorter.oddfg) + save_config_color(ODDBGCOLOR,Sorter.oddbg) + save_config_color(EVENFGCOLOR,Sorter.evenfg) + save_config_color(EVENBGCOLOR,Sorter.evenbg) + save_config_color(ANCESTORFGCOLOR,Sorter.ancestorfg) + + set_string("/gramps/researcher/name",name) + set_string("/gramps/researcher/addr",addr) + set_string("/gramps/researcher/city",city) + set_string("/gramps/researcher/state",state) + set_string("/gramps/researcher/country",country) + set_string("/gramps/researcher/postal",postal) + set_string("/gramps/researcher/phone",phone) + set_string("/gramps/researcher/email",email) + + self.db.set_iprefix(iprefix) + self.db.set_fprefix(fprefix) + self.db.set_sprefix(sprefix) + self.db.set_oprefix(oprefix) + self.db.set_pprefix(pprefix) + + for o in self.ext_list: + o.set() + + # update the config file + + sync() + _callback() + + def on_object_toggled(self,obj): + """Called by the elements on the property box to set the changed flag, + so that the property box knows to set the Apply button""" + self.apply.set_sensitive(1) + + def on_format_toggled(self,obj): + """Called by the elements on the property box to set the changed flag, + so that the property box knows to set the Apply button""" + self.apply.set_sensitive(1) + + def on_color_toggled(self,obj): + """Called by the elements on the property box to set the changed flag, + so that the property box knows to set the Apply button""" + active = self.top.get_widget("enableColors").get_active() + self.top.get_widget(ODDFGCOLOR).set_sensitive(active) + self.top.get_widget(ODDBGCOLOR).set_sensitive(active) + self.top.get_widget(EVENFGCOLOR).set_sensitive(active) + self.top.get_widget(EVENBGCOLOR).set_sensitive(active) + self.top.get_widget(ANCESTORFGCOLOR).set_sensitive(active) + self.apply.set_sensitive(1) + + def on_color_set(self,obj,r,g,b,a): + """Called by the elements on the property box to set the changed flag, + so that the property box knows to set the Apply button""" + self.apply.set_sensitive(1) + +#------------------------------------------------------------------------- +# +# Create the property box, and set the elements off the current values +# +#------------------------------------------------------------------------- +def display_preferences_box(db): + GrampsPreferences(db) + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +def get_config_color(name,defval): + r = get_int("/gramps/color/" + name + ".r") + g = get_int("/gramps/color/" + name + ".g") + b = get_int("/gramps/color/" + name + ".b") + if r == None: + return defval + else: + return (r,g,b) + +def get_sort_cols(name,col,dir): + c = get_int("/gramps/sort/%s_col" % name) + if c == None: + c = col + d = get_int("/gramps/sort/%s_dir" % name) + if d == None: + d = dir + return (c,d) + +def save_sort_cols(name,col,dir): + set_int("/gramps/sort/%s_col" % name, col) + set_int("/gramps/sort/%s_dir" % name, dir) + sync() + +def save_view(val): + set_bool("/gramps/config/view",val) + +def get_view(): + return get_bool("/gramps/config/view",1) + +def save_filter(val): + set_bool("/gramps/config/filter",val) + +def get_filter(): + return get_bool("/gramps/config/filter") diff --git a/gramps2/src/GrampsParser.py b/gramps2/src/GrampsParser.py new file mode 100644 index 000000000..fa692f7e7 --- /dev/null +++ b/gramps2/src/GrampsParser.py @@ -0,0 +1,913 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +from RelLib import * +from Date import SingleDate + +import string +import Utils +import xml.parsers.expat + +#------------------------------------------------------------------------- +# +# Remove extraneous spaces +# +#------------------------------------------------------------------------- + +def rs(text): + return string.join(string.split(text)) + +def fix_spaces(text_list): + return string.join(map(rs,text_list),'\n') + +#------------------------------------------------------------------------- +# +# Gramps database parsing class. Derived from SAX XML parser +# +#------------------------------------------------------------------------- +class GrampsParser: + + def __init__(self,database,callback,base): + self.stext_list = [] + self.scomments_list = [] + self.note_list = [] + self.tlist = [] + self.conf = 2 + + self.ord = None + self.objref = None + self.object = None + self.pref = None + self.use_p = 0 + self.in_note = 0 + self.in_stext = 0 + self.in_scomments = 0 + self.db = database + self.base = base + self.photo = None + self.person = None + self.family = None + self.address = None + self.source = None + self.source_ref = None + self.attribute = None + self.placeobj = None + self.locations = 0 + self.place_map = {} + + self.resname = "" + self.resaddr = "" + self.rescity = "" + self.resstate = "" + self.rescon = "" + self.respos = "" + self.resphone = "" + self.resemail = "" + + self.pmap = {} + self.fmap = {} + self.smap = {} + + self.callback = callback + self.entries = 0 + self.count = 0 + self.increment = 100 + self.event = None + self.name = None + self.tempDefault = None + self.owner = Researcher() + self.func_list = [None]*50 + self.func_index = 0 + self.func = None + + def parse(self,file): + p = xml.parsers.expat.ParserCreate() + p.StartElementHandler = self.startElement + p.EndElementHandler = self.endElement + p.CharacterDataHandler = self.characters + p.ParseFile(file) + + self.db.setResearcher(self.owner) + if self.tempDefault != None: + id = self.tempDefault + if self.db.personMap.has_key(id): + person = self.db.personMap[id] + self.db.setDefaultPerson(person) + + def start_lds_ord(self,attrs): + type = attrs['type'] + self.ord = LdsOrd() + if self.person: + if type == "baptism": + self.person.setLdsBaptism(self.ord) + elif type == "endowment": + self.person.setLdsEndowment(self.ord) + elif type == "sealed_to_parents": + self.person.setLdsSeal(self.ord) + elif self.family: + if type == "sealed_to_spouse": + self.family.setLdsSeal(self.ord) + + def start_temple(self,attrs): + self.ord.setTemple(attrs['val']) + + def start_status(self,attrs): + self.ord.setStatus(int(attrs['val'])) + + def start_sealed_to(self,attrs): + id = attrs['ref'] + self.ord.setFamily(self.db.findFamilyNoMap(id)) + + def start_place(self,attrs): + if attrs.has_key('ref'): + self.placeobj = self.db.findPlaceNoMap(attrs['ref']) + else: + self.placeobj = None + + def start_placeobj(self,attrs): + self.placeobj = self.db.findPlaceNoMap(attrs['id']) + title = attrs['title'] + if title == "": + title = attrs['id'] + self.placeobj.set_title(title) + self.locations = 0 + if self.num_places > 0: + if self.callback != None and self.count % self.increment == 0: + self.callback(float(self.count)/float(self.entries)) + self.count = self.count + 1 + + def start_location(self,attrs): + """Bypass the function calls for this one, since it appears to + take up quite a bit of time""" + + loc = Location() + if attrs.has_key('city'): + loc.city = attrs['city'] + if attrs.has_key('parish'): + loc.parish = attrs['parish'] + if attrs.has_key('state'): + loc.state = attrs['state'] + if attrs.has_key('county'): + loc.county =attrs['county'] + if attrs.has_key('country'): + loc.country = attrs['country'] + if self.locations > 0: + self.placeobj.add_alternate_locations(loc) + else: + self.placeobj.set_main_location(loc) + self.locations = self.locations + 1 + + def start_coord(self,attrs): + if attrs.has_key('lat'): + self.placeobj.set_latitude(attrs['lat']) + if attrs.has_key('long'): + self.placeobj.set_longitude(attrs['long']) + + def start_event(self,attrs): + self.event = Event() + self.event_type = attrs["type"] + if attrs.has_key("conf"): + self.event.conf = int(attrs["conf"]) + else: + self.event.conf = 2 + if attrs.has_key("priv"): + self.event.private = int(attrs["priv"]) + + def start_attribute(self,attrs): + self.attribute = Attribute() + if attrs.has_key("conf"): + self.attribute.conf = int(attrs["conf"]) + else: + self.attribute.conf = 2 + if attrs.has_key("priv"): + self.attribute.private = int(attrs["priv"]) + if attrs.has_key('type'): + self.attribute.setType(attrs["type"]) + if attrs.has_key('value'): + self.attribute.setValue(attrs["value"]) + if self.photo: + self.photo.addAttribute(self.attribute) + elif self.object: + self.object.addAttribute(self.attribute) + elif self.objref: + self.objref.addAttribute(self.attribute) + elif self.person: + self.person.addAttribute(self.attribute) + elif self.family: + self.family.addAttribute(self.attribute) + + def start_address(self,attrs): + self.address = Address() + self.person.addAddress(self.address) + if attrs.has_key("conf"): + self.address.conf = int(attrs["conf"]) + else: + self.address.conf = 2 + if attrs.has_key("priv"): + self.address.private = int(attrs["priv"]) + + def start_bmark(self,attrs): + person = self.db.findPersonNoMap(attrs["ref"]) + self.db.bookmarks.append(person) + + def start_person(self,attrs): + if self.callback != None and self.count % self.increment == 0: + self.callback(float(self.count)/float(self.entries)) + self.count = self.count + 1 + self.person = self.db.findPersonNoMap(attrs["id"]) + + def start_people(self,attrs): + if attrs.has_key("default"): + self.tempDefault = attrs["default"] + + def start_father(self,attrs): + self.family.Father = self.db.findPersonNoMap(attrs["ref"]) + + def start_mother(self,attrs): + self.family.Mother = self.db.findPersonNoMap(attrs["ref"]) + + def start_child(self,attrs): + self.family.Children.append(self.db.findPersonNoMap(attrs["ref"])) + + def start_url(self,attrs): + + if not attrs.has_key("href"): + return + try: + desc = attrs["description"] + except KeyError: + desc = "" + + try: + url = Url() + url.set_path(attrs["href"]) + url.set_description(desc) + if attrs.has_key("priv"): + url.setPrivacy(int(attrs['priv'])) + if self.person: + self.person.addUrl(url) + elif self.placeobj: + self.placeobj.addUrl(url) + except KeyError: + return + + def start_family(self,attrs): + if self.callback != None and self.count % self.increment == 0: + self.callback(float(self.count)/float(self.entries)) + self.count = self.count + 1 + self.family = self.db.findFamilyNoMap(attrs["id"]) + if attrs.has_key("type"): + self.family.setRelationship(attrs["type"]) + else: + self.family.setRelationship("") + + def start_childof(self,attrs): + family = self.db.findFamilyNoMap(attrs["ref"]) + if attrs.has_key("mrel"): + mrel = attrs["mrel"] + else: + mrel = "Birth" + if attrs.has_key("frel"): + frel = attrs["frel"] + else: + frel = "Birth" + self.person.AltFamilyList.append((family,mrel,frel)) + + def start_parentin(self,attrs): + self.person.FamilyList.append(self.db.findFamilyNoMap(attrs["ref"])) + + def start_name(self,attrs): + self.name = Name() + if attrs.has_key("type"): + self.name.setType(attrs["type"]) + if attrs.has_key("conf"): + self.name.conf = int(attrs["conf"]) + else: + self.name.conf = 2 + if attrs.has_key("priv"): + self.name.private = int(attrs["priv"]) + + def start_note(self,attrs): + self.in_note = 1 + + def start_sourceref(self,attrs): + self.source_ref = SourceRef() + source = self.db.findSourceNoMap(attrs["ref"]) + if attrs.has_key("conf"): + self.source_ref.confidence = int(attrs["conf"]) + else: + self.source_ref.confidence = self.conf + self.source_ref.setBase(source) + if self.photo: + self.photo.addSourceRef(self.source_ref) + elif self.ord: + self.ord.addSourceRef(self.source_ref) + elif self.object: + self.object.addSourceRef(self.source_ref) + elif self.event: + self.event.addSourceRef(self.source_ref) + elif self.address: + self.address.addSourceRef(self.source_ref) + elif self.name: + self.name.addSourceRef(self.source_ref) + elif self.attribute: + self.attribute.addSourceRef(self.source_ref) + elif self.placeobj: + self.placeobj.addSourceRef(self.source_ref) + + def start_source(self,attrs): + if self.num_srcs > 0: + if self.callback != None and self.count % self.increment == 0: + self.callback(float(self.count)/float(self.entries)) + self.count = self.count + 1 + self.source = self.db.findSourceNoMap(attrs["id"]) + + def start_objref(self,attrs): + self.objref = ObjectRef() + self.objref.setReference(self.db.findObjectNoMap(attrs['ref'])) + if attrs.has_key('priv'): + self.objref.setPrivacy(int(attrs['priv'])) + if self.family: + self.family.addPhoto(self.objref) + elif self.source: + self.source.addPhoto(self.objref) + elif self.person: + self.person.addPhoto(self.objref) + elif self.placeobj: + self.placeobj.addPhoto(self.objref) + + def start_object(self,attrs): + self.object = self.db.findObjectNoMap(attrs['id']) + self.object.setMimeType(attrs['mime']) + self.object.setDescription(attrs['description']) + src = attrs["src"] + if src: + if src[0] != '/': + self.object.setPath("%s/%s" % (self.base,src)) + self.object.setLocal(1) + else: + self.object.setPath(src) + self.object.setLocal(0) + + def stop_object(self,tag): + self.object = None + + def stop_objref(self,tag): + self.objref = None + + def start_photo(self,attrs): + self.photo = Photo() + self.pref = ObjectRef() + self.pref.setReference(self.photo) + + for key in attrs.keys(): + if key == "descrip" or key == "description": + self.photo.setDescription(attrs[key]) + elif key == "priv": + self.pref.setPrivacy(int(attrs[key])) + elif key == "src": + src = attrs["src"] + if src[0] != '/': + self.photo.setPath("%s/%s" % (self.base,src)) + self.photo.setLocal(1) + else: + self.photo.setPath(src) + self.photo.setLocal(0) + else: + a = Attribute() + a.setType(key) + a.setValue(attrs[key]) + self.photo.addAttribute(a) + self.photo.setMimeType(Utils.get_mime_type(self.photo.getPath())) + self.db.addObject(self.photo) + if self.family: + self.family.addPhoto(self.pref) + elif self.source: + self.source.addPhoto(self.pref) + elif self.person: + self.person.addPhoto(self.pref) + elif self.placeobj: + self.placeobj.addPhoto(self.pref) + + def start_daterange(self,attrs): + if self.source_ref: + d = self.source_ref.getDate() + elif self.ord: + d = self.ord.getDateObj() + elif self.address: + d = self.address.getDateObj() + else: + d = self.event.getDateObj() + + if attrs.has_key("calendar"): + d.set_calendar(int(attrs['calendar'])) + + d.get_start_date().setIsoDate(attrs['start']) + d.get_stop_date().setIsoDate(attrs['stop']) + d.range = 1 + + def start_dateval(self,attrs): + if self.source_ref: + d = self.source_ref.getDate() + elif self.ord: + d = self.ord.getDateObj() + elif self.address: + d = self.address.getDateObj() + else: + d = self.event.getDateObj() + + if attrs.has_key("calendar"): + d.set_calendar(int(attrs['calendar'])) + + d.get_start_date().setIsoDate(attrs['val']) + + if attrs.has_key("type"): + d.get_start_date().setMode(attrs['type']) + else: + d.get_start_date().setMode(None) + + def start_datestr(self,attrs): + if self.source_ref: + d = self.source_ref.getDate() + elif self.ord: + d = self.ord.getDateObj() + elif self.address: + d = self.address.getDateObj() + else: + d = self.event.getDateObj() + + d.set(attrs['val']) + + def start_created(self,attrs): + if attrs.has_key('sources'): + self.num_srcs = int(attrs['sources']) + else: + self.num_srcs = 0 + if attrs.has_key('places'): + self.num_places = int(attrs['places']) + else: + self.num_places = 0 + self.entries = int(attrs["people"]) + int(attrs["families"]) + \ + self.num_places + self.num_srcs + + def start_pos(self,attrs): + self.person.position = (int(attrs["x"]), int(attrs["y"])) + + def stop_attribute(self,tag): + self.attribute = None + + def stop_attr_type(self,tag): + self.attribute.setType(tag) + + def stop_attr_value(self,tag): + self.attribute.setValue(tag) + + def stop_address(self,tag): + self.address = None + + def stop_places(self,tag): + self.placeobj = None + + def stop_photo(self,tag): + self.photo = None + + def stop_placeobj(self,tag): + if self.placeobj.get_title() == "": + loc = self.placeobj.get_main_location() + self.placeobj.set_title(build_place_title(loc)) + self.db.buildPlaceDisplay(self.placeobj.getId()) + self.palceobj = None + + def stop_event(self,tag): + self.event.name = self.event_type + + if self.family: + self.family.EventList.append(self.event) + else: + if self.event_type == "Birth": + self.person.setBirth(self.event) + elif self.event_type == "Death": + self.person.setDeath(self.event) + else: + self.person.EventList.append(self.event) + self.event = None + + def stop_name(self,tag): + self.person.PrimaryName = self.name + if self.name.getType() == "": + self.name.setType("Birth Name") + self.name = None + + def stop_place(self,tag): + if self.placeobj == None: + if self.place_map.has_key(tag): + self.placeobj = self.place_map[tag] + else: + self.placeobj = Place() + self.placeobj.set_title(tag) + self.db.addPlace(self.placeobj) + self.place_map[tag] = self.placeobj + if self.ord: + self.ord.setPlace(self.placeobj) + else: + self.event.place = self.placeobj + + def stop_uid(self,tag): + self.person.setPafUid(tag) + + def stop_date(self,tag): + if tag: + if self.address: + self.address.setDate(tag) + else: + self.event.setDate(tag) + + def stop_first(self,tag): + self.name.FirstName = tag + + def stop_families(self,tag): + self.family = None + + def stop_people(self,tag): + self.person = None + + def stop_person(self,tag): + self.db.buildPersonDisplay(self.person.getId()) + + def stop_description(self,tag): + self.event.setDescription(tag) + + def stop_cause(self,tag): + self.event.setCause(tag) + + def stop_gender(self,tag): + t = tag + if t == "M": + self.person.gender = Person.male + elif t == "F": + self.person.gender = Person.female + else: + self.person.gender = Person.unknown + + def stop_stitle(self,tag): + self.source.setTitle(tag) + + def stop_sourceref(self,tag): + self.source_ref = None + + def stop_source(self,tag): + self.db.buildSourceDisplay(self.source.getId()) + self.source = None + + def stop_sauthor(self,tag): + self.source.setAuthor(tag) + + def stop_sdate(self,tag): + date = Date() + date.set(tag) + self.source_ref.setDate(date) + + def stop_street(self,tag): + self.address.setStreet(tag) + + def stop_city(self,tag): + self.address.setCity(tag) + + def stop_state(self,tag): + self.address.setState(tag) + + def stop_country(self,tag): + self.address.setCountry(tag) + + def stop_postal(self,tag): + self.address.setPostal(tag) + + def stop_spage(self,tag): + self.source_ref.setPage(tag) + + def stop_lds_ord(self,tag): + self.ord = None + + def stop_spubinfo(self,tag): + self.source.setPubInfo(tag) + + def stop_scallno(self,tag): + self.source.setCallNumber(tag) + + def stop_stext(self,tag): + if self.use_p: + self.use_p = 0 + note = fix_spaces(self.stext_list) + else: + note = tag + self.source_ref.setText(note) + + def stop_scomments(self,tag): + if self.use_p: + self.use_p = 0 + note = fix_spaces(self.scomments_list) + else: + note = tag + self.source_ref.setComments(note) + + def stop_last(self,tag): + if self.name: + self.name.Surname = tag + + def stop_suffix(self,tag): + if self.name: + self.name.Suffix = tag + + def stop_title(self,tag): + if self.name: + self.name.Title = tag + + def stop_nick(self,tag): + if self.person: + self.person.setNickName(tag) + + def stop_note(self,tag): + self.in_note = 0 + if self.use_p: + self.use_p = 0 + note = fix_spaces(self.note_list) + else: + note = tag + + if self.address: + self.address.setNote(note) + elif self.ord: + self.ord.setNote(note) + elif self.attribute: + self.attribute.setNote(note) + elif self.object: + self.object.setNote(note) + elif self.objref: + self.objref.setNote(note) + elif self.photo: + self.photo.setNote(note) + elif self.name: + self.name.setNote(note) + elif self.source: + self.source.setNote(note) + elif self.event: + self.event.setNote(note) + elif self.person: + self.person.setNote(note) + elif self.family: + self.family.setNote(note) + elif self.placeobj: + self.placeobj.setNote(note) + self.note_list = [] + + def stop_research(self,tag): + self.owner.set(self.resname, self.resaddr, self.rescity, self.resstate, + self.rescon, self.respos, self.resphone, self.resemail) + + def stop_resname(self,tag): + self.resname = tag + + def stop_resaddr(self,tag): + self.resaddr = tag + + def stop_rescity(self,tag): + self.rescity = tag + + def stop_resstate(self,tag): + self.resstate = tag + + def stop_rescountry(self,tag): + self.rescon = tag + + def stop_respostal(self,tag): + self.respos = tag + + def stop_resphone(self,tag): + self.resphone = tag + + def stop_resemail(self,tag): + self.resemail = tag + + def stop_ptag(self,tag): + self.use_p = 1 + if self.in_note: + self.note_list.append(tag) + elif self.in_stext: + self.stext_list.append(tag) + elif self.in_scomments: + self.scomments_list.append(tag) + + def stop_aka(self,tag): + self.person.addAlternateName(self.name) + if self.name.getType() == "": + self.name.setType("Also Known As") + self.name = None + + func_map = { + "address" : (start_address, stop_address), + "addresses" : (None,None), + "childlist" : (None,None), + "aka" : (start_name, stop_aka), + "attribute" : (start_attribute, stop_attribute), + "attr_type" : (None,stop_attr_type), + "attr_value" : (None,stop_attr_value), + "bookmark" : (start_bmark, None), + "bookmarks" : (None, None), + "child" : (start_child,None), + "childof" : (start_childof,None), + "city" : (None, stop_city), + "country" : (None, stop_country), + "created" : (start_created, None), + "database" : (None, None), + "date" : (None, stop_date), + "cause" : (None, stop_cause), + "description": (None, stop_description), + "event" : (start_event, stop_event), + "families" : (None, stop_families), + "family" : (start_family, None), + "father" : (start_father, None), + "first" : (None, stop_first), + "gender" : (None, stop_gender), + "header" : (None, None), + "last" : (None, stop_last), + "mother" : (start_mother,None), + "name" : (start_name, stop_name), + "nick" : (None, stop_nick), + "note" : (start_note, stop_note), + "p" : (None, stop_ptag), + "parentin" : (start_parentin,None), + "people" : (start_people, stop_people), + "person" : (start_person, stop_person), + "img" : (start_photo, stop_photo), + "objref" : (start_objref, stop_objref), + "object" : (start_object, stop_object), + "place" : (start_place, stop_place), + "dateval" : (start_dateval, None), + "daterange" : (start_daterange, None), + "datestr" : (start_datestr, None), + "places" : (None, stop_places), + "placeobj" : (start_placeobj,stop_placeobj), + "location" : (start_location,None), + "lds_ord" : (start_lds_ord, stop_lds_ord), + "temple" : (start_temple, None), + "status" : (start_status, None), + "sealed_to" : (start_sealed_to, None), + "coord" : (start_coord,None), + "pos" : (start_pos, None), + "postal" : (None, stop_postal), + "researcher" : (None, stop_research), + "resname" : (None, stop_resname ), + "resaddr" : (None, stop_resaddr ), + "rescity" : (None, stop_rescity ), + "resstate" : (None, stop_resstate ), + "rescountry" : (None, stop_rescountry), + "respostal" : (None, stop_respostal), + "resphone" : (None, stop_resphone), + "resemail" : (None, stop_resemail), + "sauthor" : (None, stop_sauthor), + "scallno" : (None, stop_scallno), + "scomments" : (None, stop_scomments), + "sdate" : (None,stop_sdate), + "source" : (start_source, stop_source), + "sourceref" : (start_sourceref, stop_sourceref), + "sources" : (None, None), + "spage" : (None, stop_spage), + "spubinfo" : (None, stop_spubinfo), + "state" : (None, stop_state), + "stext" : (None, stop_stext), + "stitle" : (None, stop_stitle), + "street" : (None, stop_street), + "suffix" : (None, stop_suffix), + "title" : (None, stop_title), + "uid" : (None, stop_uid), + "url" : (start_url, None) + } + + def startElement(self,tag,attrs): + + self.func_list[self.func_index] = (self.func,self.tlist) + self.func_index = self.func_index + 1 + self.tlist = [] + + try: + f,self.func = GrampsParser.func_map[tag] + if f: + f(self,attrs) + except KeyError: + GrampsParser.func_map[tag] = (None,None) + self.func = None + + def endElement(self,tag): + + if self.func: + self.func(self,string.join(self.tlist,'')) + self.func_index = self.func_index - 1 + self.func,self.tlist = self.func_list[self.func_index] + + def characters(self, data): + if self.func: + self.tlist.append(data) + +#------------------------------------------------------------------------- +# +# Gramps database parsing class. Derived from SAX XML parser +# +#------------------------------------------------------------------------- +class GrampsImportParser(GrampsParser): + + def start_bmark(self,attrs): + person = self.db.findPerson("x%s" % attrs["ref"],self.pmap) + self.db.bookmarks.append(person) + + def start_person(self,attrs): + if self.callback != None and self.count % self.increment == 0: + self.callback(float(self.count)/float(self.entries)) + self.count = self.count + 1 + self.person = self.db.findPerson("x%s" % attrs["id"],self.pmap) + + def start_father(self,attrs): + father = self.db.findPerson("x%s" % attrs["ref"],self.pmap) + self.family.setFather(father) + + def start_mother(self,attrs): + mother = self.db.findPerson("x%s" % attrs["ref"],self.pmap) + self.family.setMother(mother) + + def start_child(self,attrs): + child = self.db.findPerson("x%s" % attrs["ref"],self.pmap) + self.family.addChild(child) + + def start_family(self,attrs): + if self.callback != None and self.count % self.increment == 0: + self.callback(float(self.count)/float(self.entries)) + self.count = self.count + 1 + self.family = self.db.findFamily(attrs["id"],self.fmap) + if attrs.has_key("type"): + self.family.setRelationship(attrs["type"]) + + def start_sourceref(self,attrs): + self.source_ref = SourceRef() + self.source = self.db.findSource(attrs["ref"],self.smap) + self.source_ref.setBase(self.source) + if self.address: + self.address.addSourceRef(self.source_ref) + elif self.name: + self.name.addSourceRef(self.source_ref) + elif self.event: + self.event.addSourceRef(self.source_ref) + elif self.attribute: + self.attribute.addSourceRef(self.source_ref) + elif self.placeobj: + self.placeobj.addSourceRef(self.source_ref) + else: + print "Sorry, I'm lost" + + def start_source(self,attrs): + self.source = self.db.findSource(attrs["id"],self.smap) + + +def append_value(orig,val): + if orig: + return "%s, %s" % (orig,val) + else: + return val + +def build_place_title(loc): + "Builds a title from a location" + city = loc.get_city() + state = loc.get_state() + country = loc.get_country() + county = loc.get_county() + parish = loc.get_parish() + + value = "" + + if parish: + value = parish + if city: + value = append_value(value,city) + if county: + value = append_value(value,county) + if state: + value = append_value(value,state) + if country: + value = append_value(value,country) + return value + diff --git a/gramps2/src/GrampsXML.py b/gramps2/src/GrampsXML.py new file mode 100644 index 000000000..60ff140c6 --- /dev/null +++ b/gramps2/src/GrampsXML.py @@ -0,0 +1,58 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +from RelLib import GrampsDB +import WriteXML +import ReadXML +import const + +class GrampsXML(GrampsDB): + + def get_base(self): + return const.xmlFile + + def get_type(self): + return 'GrampsXML' + + def new(self): + GrampsDB.new(self) + + def save(self,name,callback): + WriteXML.exportData(self,name,callback) + + def load(self,name,callback): + ReadXML.importData(self,name,callback) + self.personTable = {} + for key in self.personMap.keys(): + person = self.personMap[key] + self.personTable[key] = person.getDisplayInfo() + self.addSurname(person.getPrimaryName().getSurname()) + + self.placeTable = {} + for key in self.placeMap.keys(): + place = self.placeMap[key] + self.placeTable[key] = place.getDisplayInfo() + + self.sourceTable = {} + for key in self.sourceMap.keys(): + src = self.sourceMap[key] + self.sourceTable[key] = src.getDisplayInfo() + + return 1 diff --git a/gramps2/src/GrampsZODB.py b/gramps2/src/GrampsZODB.py new file mode 100644 index 000000000..82a51ee4c --- /dev/null +++ b/gramps2/src/GrampsZODB.py @@ -0,0 +1,259 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2002 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +from ZODB import Persistent +from ZODB.PersistentList import PersistentList +from ZODB.dbmStorage import gdbmStorage +from ZODB.DB import DB +from BTrees.OOBTree import OOBTree +from UserDict import UserDict +from RelLib import GrampsDB, Person +import const + +class PersistentReference(Persistent): + + def __init__(self, ob): + self.ob = ob + + def getOb(self): + return self.ob + +class PersonWrapper: + + _real = None + + def __init__(self, real, map): + self._real = real + self._map_ref = PersistentReference(map) + self.id = real.getId() + self.PrimaryName = real.getPrimaryName() + self.gender = real.getGender() + self.birth = real.getBirth() + self.death = real.getDeath() + + def _notifyChange(self): + # Trigger a change to the PersonMap. + self._map_ref.getOb()[self.id] = self + + def getId(self): + return self.id + + def setId(self, id): + self._real.setId(id) + self.id = self._real.getId() + self._notifyChange() + + def getPrimaryName(self): + return self.PrimaryName + + def setPrimaryName(self, name): + self._real.setPrimaryName(name) + self.PrimaryName = self._real.getPrimaryName() + self._notifyChange() + + def getGender(self): + return self.gender + + def setGender(self, gender): + self._real.setGender(gender) + self.gender = self._real.getGender() + self._notifyChange() + + def getBirth(self): + return self.birth + + def setBirth(self, birth): + self._real.setBirth(birth) + self.birth = self._real.getBirth() + self._notifyChange() + + def getDeath(self): + return self.death + + def setDeath(self, death): + self._real.setDeath(death) + self.death = self._real.getDeath() + self._notifyChange() + + +for key, value in Person.__dict__.items(): + if not key.startswith('_'): + code = ("def %s(self, *args, **kw): " + "return apply(self._real.%s, args, kw)") % (key, key) + d = {} + exec code in d + PersonWrapper.__dict__[key] = d[key] + + +class PersonMap(Persistent, UserDict): + + def __init__(self): + self.data = OOBTree() + + def __setitem__(self, key, value): + if not isinstance(value, PersonWrapper): + # Create the PersonWrapper. + assert isinstance(value, Person) + value = PersonWrapper(value, self) + self.data[key] = value + + def update(self): + # This probably shouldn't be called anyway. + raise NotImplementedError + + def copy(self): + # This probably shouldn't be called anyway. + raise NotImplementedError + +class GrampsZODB(GrampsDB): + + def __init__(self): + self.conn = None + GrampsDB.__init__(self) + + def get_type(self): + return 'GrampsZODB' + + def close(self): + self.db.close() + + def get_base(self): + return const.zodbFile + + def need_autosave(self): + return 0 + + def new(self): + GrampsDB.new(self) + self.familyMap = OOBTree() + self.personMap = PersonMap() + self.sourceMap = OOBTree() + self.placeMap = OOBTree() + self.personTable = OOBTree() + self.placeTable = OOBTree() + self.sourceTable = OOBTree() + self.need_commit = 0 + + if self.conn: + self.db.close() + self.conn.close() + self.conn = None + + def save(self,name,callback): + get_transaction().commit() + if self.conn == None: + self.load(name,callback) + + def get_object(self,tag): + if self.root.has_key(tag): + item = self.root[tag] + else: + item = OOBTree() + self.root[tag] = item + self.need_commit = 1 + return item + + def get_display_table(self,src,tag): + if self.root.has_key(tag): + table = self.root[tag] + else: + table = OOBTree() + for key in src.keys(): + obj = src[key] + table[key] = obj.getDisplayInfo() + self.root[tag] = table + self.need_commit = 1 + return table + + def load(self,name,callback): + self.db = DB(gdbmStorage(name,'w')) + self.conn = self.db.open() + self.root = self.conn.root() + self.need_commit = 0 + + self.familyMap = self.get_object('familyMap') + + if self.root.has_key('personMap'): + self.personMap = self.root['personMap'] + else: + self.personMap = PersonMap() + self.root['personMap'] = self.personMap + self.need_commit = 1 + + self.personTable = self.get_display_table(self.personMap,'personTable') + + if self.root.has_key('surnames'): + self.surnames = self.root['surnames'] + else: + self.surnames = PersistentList() + for key in self.personMap.keys(): + person = self.personMap[key] + self.addSurname(person.getPrimaryName().getSurname()) + self.root['surnames'] = self.surnames + self.need_commit = 1 + + self.sourceMap = self.get_object('sourceMap') + self.sourceTable = self.get_display_table(self.sourceMap,'sourceTable') + + self.placeMap = self.get_object('placeMap') + self.placeTable = self.get_display_table(self.placeMap,'placeTable') + + if self.root.has_key('default'): + self.default = self.root['default'] + else: + self.default = None + self.root['default'] = self.default + self.need_commit = 1 + + if self.root.has_key('bookmarks'): + self.bookmarks = self.root['bookmarks'] + else: + self.bookmarks = [] + self.root['bookmarks'] = self.bookmarks + self.need_commit = 1 + if self.need_commit: + get_transaction().commit() + return 1 + + def setDefaultPerson(self,person): + """sets the default Person to the passed instance""" + GrampsDB.setDefaultPerson(self,person) + self.root['default'] = person + + + + + + + + + + + + + + + + + + + + + diff --git a/gramps2/src/GraphLayout.py b/gramps2/src/GraphLayout.py new file mode 100644 index 000000000..5b15db81e --- /dev/null +++ b/gramps2/src/GraphLayout.py @@ -0,0 +1,69 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +class GraphLayout: + + def __init__(self,plist,person): + self.plist = plist + self.person = person + self.v = [] + self.e = [] + self.maxx = 0 + self.maxy = 0 + + def max_size(self): + return (self.maxx,self.maxy) + + def layout(self): + return ([],[]) + +class DescendLine(GraphLayout): + + def layout(self): + self.elist = [(0,0)] + self.space_for(self.person) + return (self.v,self.e[1:]) + + def space_for(self,person,level=1.0,pos=1.0): + last = self.elist[-1] + self.elist.append((level,pos)) + self.e.append((last[0],last[1],level,pos)) + self.v.append((person,level,pos)) + if level > self.maxx: + self.maxx = level + if pos > self.maxy: + self.maxy = pos + + for family in person.getFamilyList(): + for child in family.getChildList(): + self.space_for(child,level+1.0,pos) + pos = pos + max(self.depth(child),1) + if pos > self.maxy: + self.maxy = pos + self.elist.pop() + + def depth(self,person,val=1.0): + for family in person.getFamilyList(): + clist = family.getChildList() + val = val + len(clist) + for child in clist: + val = self.depth(child,val) + val = val - 1.0 + return val diff --git a/gramps2/src/ImageSelect.py b/gramps2/src/ImageSelect.py new file mode 100644 index 000000000..dae6317a7 --- /dev/null +++ b/gramps2/src/ImageSelect.py @@ -0,0 +1,838 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +#------------------------------------------------------------------------- +# +# Standard python modules +# +#------------------------------------------------------------------------- +import os +import string + +#------------------------------------------------------------------------- +# +# GTK/Gnome modules +# +#------------------------------------------------------------------------- +import gtk +import gnome.ui +import gnome.canvas +import gtk.glade + +#------------------------------------------------------------------------- +# +# gramps modules +# +#------------------------------------------------------------------------- +import const +import Utils +import GrampsCfg +import Plugins +from RelLib import * +import RelImage + +import EditPerson +import Marriage +import EditPlace +import EditSource + +from intl import gettext as _ + +_IMAGEX = 140 +_IMAGEY = 150 + +#------------------------------------------------------------------------- +# +# ImageSelect class +# +#------------------------------------------------------------------------- +class ImageSelect: + + last_path = "" + + def __init__(self, path, db, parent): + """Creates an edit window. Associates a person with the window.""" + self.path = path; + self.db = db + self.dataobj = None + self.parent = parent + self.canvas_list = [] + + def add_thumbnail(self, photo): + "should be overrridden" + pass + + def load_images(self): + "should be overrridden" + pass + + def create_add_dialog(self): + """Create the gnome dialog for selecting a new photo and entering + its description.""" + + if self.path == '': + return + + self.glade = gtk.glade.XML(const.imageselFile,"imageSelect") + window = self.glade.get_widget("imageSelect") + self.fname = self.glade.get_widget("fname") + self.image = self.glade.get_widget("image") + self.description = self.glade.get_widget("photoDescription") + self.external = self.glade.get_widget("private") + self.temp_name = "" + + self.glade.signal_autoconnect({ + "on_savephoto_clicked" : self.on_savephoto_clicked, + "on_name_changed" : self.on_name_changed, + "destroy_passed_object" : Utils.destroy_passed_object + }) + + if ImageSelect.last_path != "": + self.glade.get_widget("photosel").set_default_path(ImageSelect.last_path) + window.show() + + def on_name_changed(self, obj): + """The filename has changed. Verify it and load the picture.""" + filename = self.fname.get_text() + + basename = os.path.basename(filename) + (root,ext) = os.path.splitext(basename) + old_title = self.description.get_text() + + if old_title == "" or old_title == self.temp_name: + self.description.set_text(root) + self.temp_name = root + + if os.path.isfile(filename): + type = Utils.get_mime_type(filename) + if type[0:5] == "image": + image = RelImage.scale_image(filename,const.thumbScale) + self.image.set_from_pixbuf(image) + else: + i = gtk.gdk.pixbuf_new_from_file(Utils.find_icon(type)) + self.image.set_from_pixbuf(i) + + def on_savephoto_clicked(self, obj): + """Save the photo in the dataobj object. (Required function)""" + filename = self.glade.get_widget("photosel").get_full_path(0) + ImageSelect.last_path = os.path.dirname(filename) + + description = self.description.get_text() + + if os.path.exists(filename) == 0: + gnome.ui.GnomeErrorDialog(_("That is not a valid file name.")); + return + + already_imported = None + for o in self.db.getObjectMap().values(): + if o.getPath() == filename: + already_imported = o + break + + if (already_imported): + oref = ObjectRef() + oref.setReference(already_imported) + self.dataobj.addPhoto(oref) + self.add_thumbnail(oref) + else: + type = Utils.get_mime_type(filename) + mobj = Photo() + if description == "": + description = os.path.basename(filename) + mobj.setDescription(description) + mobj.setMimeType(type) + self.savephoto(mobj) + + if type[0:5] == "image": + if self.external.get_active() == 0: + name = RelImage.import_media_object(filename,self.path, + mobj.getId()) + mobj.setLocal(1) + else: + name = filename + else: + if self.external.get_active() == 1: + name = filename + else: + name = RelImage.import_media_object(filename,self.path, + mobj.getId()) + mobj.setLocal(1) + mobj.setPath(name) + + self.parent.lists_changed = 1 + Utils.destroy_passed_object(obj) + self.load_images() + + def savephoto(self, photo): + """Save the photo in the dataobj object - must be overridden""" + pass + +#------------------------------------------------------------------------- +# +# Gallery class - This class handles all the logic underlying a +# picture gallery. This class does not load or contain the widget +# data structure to actually display the gallery. +# +#------------------------------------------------------------------------- +class Gallery(ImageSelect): + def __init__(self, dataobj, path, icon_list, db, parent): + ImageSelect.__init__(self, path, db, parent) + + t = [ + ('STRING', 0, 0), + ('text/plain',0,0), + ('text/uri-list',0,2), + ('application/x-rootwin-drop',0,1)] + + if path: + icon_list.drag_dest_set(gtk.DEST_DEFAULT_ALL, t, + gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_MOVE) + icon_list.connect("drag_data_received", + self.on_photolist_drag_data_received) + icon_list.drag_source_set(gtk.gdk.BUTTON1_MASK|gtk.gdk.BUTTON3_MASK,t, + gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_MOVE) + icon_list.connect("drag_data_get", + self.on_photolist_drag_data_get) + + # Remember arguments + self.path = path; + self.dataobj = dataobj; + self.iconlist = icon_list; + self.root = self.iconlist.root() + + # Local object variables + self.selectedIcon = -1 + self.x = 0 + self.y = 0 + + def savephoto(self, photo): + """Save the photo in the dataobj object. (Required function)""" + self.db.addObject(photo) + oref = ObjectRef() + oref.setReference(photo) + self.dataobj.addPhoto(oref) + self.add_thumbnail(oref) + + def add_thumbnail(self, photo): + """Scale the image and add it to the IconList.""" + object = photo.getReference() + name = Utils.thumb_path(self.db.getSavePath(),object) + description = object.getDescription() + if len(description) > 20: + description = "%s..." % description[0:20] + + image = gtk.gdk.pixbuf_new_from_file(name) + x = image.get_width() + y = image.get_height() + + grp = self.root.add(gnome.canvas.CanvasGroup,x=self.cx,y=self.cy) + + xloc = (_IMAGEX-x)/2 + yloc = (_IMAGEX-y)/2 + + item = grp.add(gnome.canvas.CanvasPixbuf, + pixbuf=image, + x=xloc, + y=yloc) + text = grp.add(gnome.canvas.CanvasText, + x=_IMAGEX/2, + y=_IMAGEX, + anchor=gtk.ANCHOR_CENTER, + text=description) + + self.cx = 10 + _IMAGEX + self.cx + if self.cx + 10 + _IMAGEX > self.x: + self.cx = 10 + self.cy = self.cy + 10 + _IMAGEY + + item.show() + text.show() + self.canvas_list.append(grp) + self.canvas_list.append(item) + self.canvas_list.append(text) + + def load_images(self): + """clears the currentImages list to free up any cached + Imlibs. Then add each photo in the place's list of photos to the + photolist window.""" + + for item in self.canvas_list: + item.destroy() + + self.pos = 0 + self.cx = 10 + self.cy = 10 + + for photo in self.dataobj.getPhotoList(): + self.add_thumbnail(photo) + + (self.x,self.y) = self.iconlist.get_size() + if self.cy > self.y: + self.iconlist.set_scroll_region(0,0,self.x,self.cy) + else: + self.iconlist.set_scroll_region(0,0,self.x,self.y) + + def on_photo_select_icon(self, obj,iconNumber,event): + """User clicked on a photo. Remember which one.""" + self.selectedIcon = iconNumber + + def on_photolist_drag_data_received(self,w, context, x, y, data, info, time): + import urlparse + if data and data.format == 8: + icon_index = w.get_icon_at(x,y) + d = string.strip(string.replace(data.data,'\0',' ')) + protocol,site,file, j,k,l = urlparse.urlparse(d) + if protocol == "file": + name = file + mime = Utils.get_mime_type(name) + photo = Photo() + photo.setPath(name) + photo.setMimeType(mime) + basename = os.path.basename(name) + (root,ext) = os.path.splitext(basename) + photo.setDescription(root) + self.savephoto(photo) + if GrampsCfg.mediaref == 0: + name = RelImage.import_media_object(name,self.path,photo.getId()) + photo.setPath(name) + photo.setLocal(1) + self.parent.lists_changed = 1 + if GrampsCfg.globalprop: + Utils.modified() + GlobalMediaProperties(self.db,photo,None) + elif protocol != "": + import urllib + u = urllib.URLopener() + try: + tfile,headers = u.retrieve(d) + except IOError, msg: + t = _("Could not import %s") % d + + gnome.ui.GnomeErrorDialog("%s\n%s %d" % (t,msg[0],msg[1])) + return + mime = Utils.get_mime_type(tfile) + photo = Photo() + photo.setMimeType(mime) + photo.setDescription(d) + photo.setLocal(1) + photo.setPath(tfile) + self.db.addObject(photo) + oref = ObjectRef() + oref.setReference(photo) + self.dataobj.addPhoto(oref) + try: + id = photo.getId() + name = RelImage.import_media_object(tfile,self.path,id) + photo.setLocal(1) + photo.setPath(name) + except: + photo.setPath(tfile) + w.drag_finish(context, 1, 0, time) + return + self.add_thumbnail(oref) + self.parent.lists_changed = 1 + if GrampsCfg.globalprop: + Utils.modified() + GlobalMediaProperties(self.db,photo,None) + else: + if self.db.getObjectMap().has_key(data.data): + index = 0 + for p in self.dataobj.getPhotoList(): + if data.data == p.getReference().getId(): + if index == icon_index or icon_index == -1: + w.drag_finish(context, 0, 0, time) + return + else: + w.drag_finish(context, 1, 0, time) + nl = self.dataobj.getPhotoList() + item = nl[index] + if icon_index == 0: + del nl[index] + nl = [item] + nl + else: + del nl[index] + nl = nl[0:icon_index] + [item] + nl[icon_index:] + self.dataobj.setPhotoList(nl) + Utils.modified() + self.parent.lists_changed = 1 + self.load_images() + return + index = index + 1 + oref = ObjectRef() + oref.setReference(self.db.findObjectNoMap(data.data)) + self.dataobj.addPhoto(oref) + self.add_thumbnail(oref) + self.parent.lists_changed = 1 + if GrampsCfg.globalprop: + LocalMediaProperties(oref,self.path,self) + Utils.modified() + w.drag_finish(context, 1, 0, time) + else: + w.drag_finish(context, 0, 0, time) + + def on_photolist_drag_data_get(self,w, context, selection_data, info, time): + if info == 1: + return + if self.selectedIcon != -1: + ref = self.dataobj.getPhotoList()[self.selectedIcon] + id = ref.getReference().getId() + selection_data.set(selection_data.target, 8, id) + + def on_add_photo_clicked(self, obj): + """User wants to add a new photo. Create a dialog to find out + which photo they want.""" + self.create_add_dialog() + + def on_delete_photo_clicked(self, obj): + """User wants to delete a new photo. Remove it from the displayed + thumbnails, and remove it from the dataobj photo list.""" + icon = self.selectedIcon + if icon != -1: + self.icon_list.remove(icon) + list = self.dataobj.getPhotoList() + del list[icon] + self.dataobj.setPhotoList(list) + self.parent.lists_changed = 1 + if len(self.dataobj.getPhotoList()) == 0: + self.selectedIcon = -1 + else: + self.selectedIcon = 0 + self.icon_list.select_icon(0) + + def on_button_press_event(self, obj, event): + """Look for right-clicks on a picture and create a popup + menu of the available actions.""" + icon = self.selectedIcon + if icon == -1: + return + + if event.button == 3: + photo = self.dataobj.getPhotoList()[icon] + menu = gtk.GtkMenu() + item = gtk.GtkTearoffMenuItem() + item.show() + menu.append(item) + Utils.add_menuitem(menu,_("View in the default viewer"), + None,self.popup_view_photo) + object = photo.getReference() + if object.getMimeType()[0:5] == "image": + Utils.add_menuitem(menu,_("Edit with the GIMP"), + None,self.popup_edit_photo) + Utils.add_menuitem(menu,_("Edit Object Properties"),None, + self.popup_change_description) + if object.getLocal() == 0: + Utils.add_menuitem(menu,_("Convert to local copy"),None, + self.popup_convert_to_private) + menu.popup(None,None,None,0,0) + + def popup_view_photo(self, obj): + """Open this picture in a picture viewer""" + photo = self.dataobj.getPhotoList()[self.selectedIcon] + Utils.view_photo(photo.getReference()) + + def popup_edit_photo(self, obj): + """Open this picture in a picture editor""" + photo = self.dataobj.getPhotoList()[self.selectedIcon] + if os.fork() == 0: + os.execvp(const.editor,[const.editor, + photo.getReference().getPath()]) + + def popup_convert_to_private(self, obj): + """Copy this picture into gramps private database instead of + leaving it as an external data object.""" + photo = self.dataobj.getPhotoList()[self.selectedIcon] + object = photo.getReference() + name = RelImage.import_media_object(object.getPath(),self.path, + object.getId()) + object.setPath(name) + object.setLocal(1) + + def popup_change_description(self, obj): + """Bring up a window allowing the user to edit the description + of a picture.""" + if self.selectedIcon >=0: + photo = self.dataobj.getPhotoList()[self.selectedIcon] + LocalMediaProperties(photo,self.path,self) + +#------------------------------------------------------------------------- +# +# LocalMediaProperties +# +#------------------------------------------------------------------------- +class LocalMediaProperties: + + def __init__(self,photo,path,parent): + self.photo = photo + self.object = photo.getReference() + self.alist = photo.getAttributeList()[:] + self.lists_changed = 0 + self.parent = parent + + fname = self.object.getPath() + self.change_dialog = gtk.glade.XML(const.imageselFile, + "change_description") + descr_window = self.change_dialog.get_widget("description") + pixmap = self.change_dialog.get_widget("pixmap") + self.attr_type = self.change_dialog.get_widget("attr_type") + self.attr_value = self.change_dialog.get_widget("attr_value") + self.attr_details = self.change_dialog.get_widget("attr_details") + self.attr_list = self.change_dialog.get_widget("attr_list") + + descr_window.set_text(self.object.getDescription()) + mtype = self.object.getMimeType() + + self.pix = gtk.gdk.pixbuf_new_from_file(path) + pixmap.set_from_pixbuf(self.pix) + + self.change_dialog.get_widget("private").set_active(photo.getPrivacy()) + self.change_dialog.get_widget("gid").set_text(self.object.getId()) + + if self.object.getLocal(): + self.change_dialog.get_widget("path").set_text("") + else: + self.change_dialog.get_widget("path").set_text(fname) + + mt = Utils.get_mime_description(mtype) + self.change_dialog.get_widget("type").set_text(mt) + print self.photo + self.change_dialog.get_widget("notes").get_buffer().set_text(self.photo.getNote()) + self.change_dialog.signal_autoconnect({ + "on_cancel_clicked" : Utils.destroy_passed_object, + "on_up_clicked" : self.on_up_clicked, + "on_down_clicked" : self.on_down_clicked, + "on_ok_clicked" : self.on_ok_clicked, + "on_apply_clicked" : self.on_apply_clicked, + "on_attr_list_select_row" : self.on_attr_list_select_row, + "on_add_attr_clicked": self.on_add_attr_clicked, + "on_delete_attr_clicked" : self.on_delete_attr_clicked, + "on_update_attr_clicked" : self.on_update_attr_clicked, + }) + self.redraw_attr_list() + + def on_up_clicked(self,obj): + if len(obj.selection) == 0: + return + row = obj.selection[0] + if row != 0: + obj.select_row(row-1,0) + + def on_down_clicked(self,obj): + if len(obj.selection) == 0: + return + row = obj.selection[0] + if row != obj.rows-1: + obj.select_row(row+1,0) + + def redraw_attr_list(self): + Utils.redraw_list(self.alist,self.attr_list,disp_attr) + + def on_apply_clicked(self, obj): + priv = self.change_dialog.get_widget("private").get_active() + text = self.change_dialog.get_widget("notes").get_chars(0,-1) + note = self.photo.getNote() + if text != note or priv != self.photo.getPrivacy(): + self.photo.setNote(text) + self.photo.setPrivacy(priv) + self.parent.lists_changed = 1 + Utils.modified() + if self.lists_changed: + self.photo.setAttributeList(self.alist) + self.parent.lists_changed = 1 + Utils.modified() + + def on_ok_clicked(self, obj): + self.on_apply_clicked(obj) + Utils.destroy_passed_object(obj) + + def on_attr_list_select_row(self,obj,row,b,c): + attr = obj.get_row_data(row) + + self.attr_type.set_label(attr.getType()) + self.attr_value.set_text(attr.getValue()) + self.attr_details.set_text(Utils.get_detail_text(attr)) + + def on_update_attr_clicked(self,obj): + import AttrEdit + if len(obj.selection) > 0: + row = obj.selection[0] + attr = obj.get_row_data(row) + AttrEdit.AttributeEditor(self,attr,"Media Object", + Plugins.get_image_attributes()) + + def on_delete_attr_clicked(self,obj): + if Utils.delete_selected(obj,self.alist): + self.lists_changed = 1 + self.redraw_attr_list() + + def on_add_attr_clicked(self,obj): + import AttrEdit + AttrEdit.AttributeEditor(self,None,"Media Object", + Plugins.get_image_attributes()) + +#------------------------------------------------------------------------- +# +# GlobalMediaProperties +# +#------------------------------------------------------------------------- +class GlobalMediaProperties: + + def __init__(self,db,object,update): + self.object = object + self.alist = self.object.getAttributeList()[:] + self.lists_changed = 0 + self.db = db + self.update = update + self.refs = 0 + + self.path = self.db.getSavePath() + self.change_dialog = gtk.glade.XML(const.imageselFile,"change_global") + self.descr_window = self.change_dialog.get_widget("description") + self.notes = self.change_dialog.get_widget("notes") + pixmap = self.change_dialog.get_widget("pixmap") + self.attr_type = self.change_dialog.get_widget("attr_type") + self.attr_value = self.change_dialog.get_widget("attr_value") + self.attr_details = self.change_dialog.get_widget("attr_details") + self.attr_list = self.change_dialog.get_widget("attr_list") + + self.descr_window.set_text(self.object.getDescription()) + mtype = self.object.getMimeType() + pb = gtk.gdk.pixbuf_new_from_file(Utils.thumb_path(self.path,self.object)) + pixmap.set_from_pixbuf(pb) + + self.change_dialog.get_widget("gid").set_text(self.object.getId()) + self.makelocal = self.change_dialog.get_widget("makelocal") + + self.update_info() + + self.change_dialog.get_widget("type").set_text(Utils.get_mime_description(mtype)) + self.notes.get_buffer().set_text(self.object.getNote()) + self.change_dialog.signal_autoconnect({ + "on_cancel_clicked" : Utils.destroy_passed_object, + "on_up_clicked" : self.on_up_clicked, + "on_down_clicked" : self.on_down_clicked, + "on_ok_clicked" : self.on_ok_clicked, + "on_apply_clicked" : self.on_apply_clicked, + "on_attr_list_select_row": self.on_attr_list_select_row, + "on_add_attr_clicked" : self.on_add_attr_clicked, + "on_notebook_switch_page": self.on_notebook_switch_page, + "on_make_local_clicked" : self.on_make_local_clicked, + "on_delete_attr_clicked" : self.on_delete_attr_clicked, + "on_update_attr_clicked" : self.on_update_attr_clicked, + }) + self.redraw_attr_list() + + def on_up_clicked(self,obj): + if len(obj.selection) == 0: + return + row = obj.selection[0] + if row != 0: + obj.select_row(row-1,0) + + def on_down_clicked(self,obj): + if len(obj.selection) == 0: + return + row = obj.selection[0] + if row != obj.rows-1: + obj.select_row(row+1,0) + + def update_info(self): + fname = self.object.getPath() + if self.object.getLocal(): + self.change_dialog.get_widget("path").set_text("") + self.makelocal.set_sensitive(0) + else: + self.change_dialog.get_widget("path").set_text(fname) + self.makelocal.set_sensitive(1) + + def on_make_local_clicked(self, obj): + name = RelImage.import_media_object(self.object.getPath(), + self.path, + self.object.getId()) + self.object.setPath(name) + self.object.setLocal(1) + self.update_info() + if self.update != None: + self.update() + + def redraw_attr_list(self): + Utils.redraw_list(self.alist,self.attr_list,disp_attr) + + def button_press(self,obj,event): + if len(obj.selection) <= 0: + return + if event.button == 1 and event.type == GDK._2BUTTON_PRESS: + data = obj.get_row_data(obj.selection[0]) + if data != None: + data[0](data[1],data[2]) + + def display_refs(self): + if self.refs == 1: + return + self.refs = 1 + index = 0 + ref = self.change_dialog.get_widget("refinfo") + ref.connect('button-press-event',self.button_press) + for key in self.db.getPersonKeys(): + p = self.db.getPerson(key) + for o in p.getPhotoList(): + if o.getReference() == self.object: + ref.append([_("Person"),p.getId(),GrampsCfg.nameof(p)]) + ref.set_row_data(index,(EditPerson.EditPerson,p,self.db)) + index = index + 1 + for p in self.db.getFamilyMap().values(): + for o in p.getPhotoList(): + if o.getReference() == self.object: + ref.append([_("Family"),p.getId(),Utils.family_name(p)]) + ref.set_row_data(index,(Marriage.Marriage,p,self.db)) + index = index + 1 + for key in self.db.getSourceKeys(): + p = self.db.getSource(key) + for o in p.getPhotoList(): + if o.getReference() == self.object: + ref.append([_("Source"),p.getId(),p.getTitle()]) + ref.set_row_data(index,(EditSource.EditSource,p,self.db)) + index = index + 1 + for key in self.db.getPlaceKeys(): + p = self.db.getPlace(key) + for o in p.getPhotoList(): + if o.getReference() == self.object: + ref.append([_("Place"),p.getId(),p.get_title()]) + ref.set_row_data(index,(EditPlace.EditPlace,p,self.db)) + index = index + 1 + + def on_notebook_switch_page(self,obj,junk,page): + if page == 3: + self.display_refs() + + def on_apply_clicked(self, obj): + text = self.notes.get_chars(0,-1) + desc = self.descr_window.get_text() + note = self.object.getNote() + if text != note or desc != self.object.getDescription(): + self.object.setNote(text) + self.object.setDescription(desc) + Utils.modified() + if self.lists_changed: + self.object.setAttributeList(self.alist) + Utils.modified() + if self.update != None: + self.update() + + def on_ok_clicked(self, obj): + self.on_apply_clicked(obj) + Utils.destroy_passed_object(obj) + + def on_attr_list_select_row(self,obj,row,b,c): + attr = obj.get_row_data(row) + + self.attr_type.set_label(attr.getType()) + self.attr_value.set_text(attr.getValue()) + self.attr_details.set_text(Utils.get_detail_text(attr)) + + def on_update_attr_clicked(self,obj): + import AttrEdit + if len(obj.selection) > 0: + row = obj.selection[0] + attr = obj.get_row_data(row) + AttrEdit.AttributeEditor(self,attr,"Media Object", + Plugins.get_image_attributes()) + + def on_delete_attr_clicked(self,obj): + if Utils.delete_selected(obj,self.alist): + self.lists_changed = 1 + self.redraw_attr_list() + + def on_add_attr_clicked(self,obj): + import AttrEdit + AttrEdit.AttributeEditor(self,None,"Media Object", + Plugins.get_image_attributes()) + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +def disp_attr(attr): + detail = Utils.get_detail_flags(attr) + return [const.display_pattr(attr.getType()),attr.getValue(),detail] + + +class DeleteMediaQuery: + + def __init__(self,media,db,update): + self.db = db + self.media = media + self.update = update + + def query_response(self): + del self.db.getObjectMap()[self.media.getId()] + Utils.modified() + + for key in self.db.getPersonKeys(): + p = self.db.getPerson(key) + nl = [] + change = 0 + for photo in p.getPhotoList(): + if photo.getReference() != self.media: + nl.append(photo) + else: + change = 1 + if change: + p.setPhotoList(nl) + + for p in self.db.getFamilyMap().values(): + nl = [] + change = 0 + for photo in p.getPhotoList(): + if photo.getReference() != self.media: + nl.append(photo) + else: + change = 1 + if change: + p.setPhotoList(nl) + + for key in self.db.getSourceKeys(): + p = self.db.getSource(key) + nl = [] + change = 0 + for photo in p.getPhotoList(): + if photo.getReference() != self.media: + nl.append(photo) + else: + change = 1 + if change: + p.setPhotoList(nl) + + for key in self.db.getPlaceKeys(): + p = self.db.getPlace(key) + nl = [] + change = 0 + for photo in p.getPhotoList(): + if photo.getReference() != self.media: + nl.append(photo) + else: + change = 1 + if change: + p.setPhotoList(nl) + + if self.update: + self.update(0) + + diff --git a/gramps2/src/ImgManip.py b/gramps2/src/ImgManip.py new file mode 100644 index 000000000..31d852ff7 --- /dev/null +++ b/gramps2/src/ImgManip.py @@ -0,0 +1,168 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +import os +import const +import string + +#------------------------------------------------------------------------- +# +# Check for the python imaging library +# +#------------------------------------------------------------------------- +try: + import PIL.Image + import StringIO + PIL.Image.init() + no_pil = 0 +except: + import popen2 +# import GDK +# import GTK + import gtk +# import GdkImlib + no_pil = 1 + +class ImgManip: + def __init__(self,source): + self.src = source + + if no_pil: + + def size(self): + img = GdkImlib.Image(self.src) + return (img.rgb_width,img.rgb_height) + + def fmt_thumbnail(self,dest,width,height,cnv): + w = int(width) + h = int(height) + cmd = "%s -geometry %dx%d '%s' '%s:%s'" % (const.convert,w,h,self.src,cnv,dest) + os.system(cmd) + + def fmt_convert(self,dest,cnv): + cmd = "%s '%s' '%s:%s'" % (const.convert,self.src,cnv,dest) + os.system(cmd) + + def fmt_data(self,cnv): + cmd = "%s '%s' '%s:-'" % (const.convert,self.src,cnv) + r,w = popen2.popen2(cmd) + buf = r.read() + r.close() + w.close() + return buf + + def fmt_scale_data(self,x,y,cnv): + cmd = "%s -geometry %dx%d '%s' '%s:-'" % (const.convert,x,y,self.src,cnv) + r,w = popen2.popen2(cmd) + buf = r.read() + r.close() + w.close() + return buf + + else: + + def size(self): + return PIL.Image.open(self.src).size + + def fmt_thumbnail(self,dest,width,height,pil): + im = PIL.Image.open(self.src) + im.thumbnail((width,height)) + if im.mode != 'RGB': + im.draft('RGB',im.size) + im = im.convert("RGB") + im.save(dest,string.upper(pil)) + + def fmt_convert(self,dest,pil): + im = PIL.Image.open(self.src) + if im.mode != 'RGB': + im.draft('RGB',im.size) + im = im.convert("RGB") + im.save(dest,string.upper(pil)) + + def fmt_data(self,pil): + g = StringIO.StringIO() + im = PIL.Image.open(self.src) + if im.mode != 'RGB': + im.draft('RGB',im.size) + im = im.convert("RGB") + im.save(g,string.upper(pil)) + g.seek(0) + buf = g.read() + g.close() + return buf + + def fmt_scale_data(self,x,y,pil): + im = PIL.Image.open(self.src) + im.thumbnail((x,y)) + if im.mode != 'RGB': + im.draft('RGB',im.size) + im = im.convert("RGB") + return im.tostring(string.upper(pil),"RGB") + + def jpg_thumbnail(self,dest,width,height): + self.fmt_thumbnail(dest,width,height,"jpeg") + + def png_thumbnail(self,dest,width,height): + self.fmt_thumbnail(dest,width,height,"png") + + def eps_thumbnail(self,dest,width,height): + self.fmt_thumbnail(dest,width,height,"eps") + + def jpg_convert(self,dest): + self.fmt_convert(dest,"jpeg") + + def png_convert(self,dest): + self.fmt_convert(dest,"png") + + def eps_convert(self,dest): + self.fmt_convert(dest,"eps") + + def jpg_data(self): + return self.fmt_data("jpeg") + + def png_data(self): + return self.fmt_data("png") + + def eps_data(self): + return self.fmt_data("eps") + + def jpg_scale_data(self,x,y): + return self.fmt_scale_data(x,y,"jpeg") + + def png_scale_data(self,x,y): + return self.fmt_scale_data(x,y,"png") + + def eps_scale_data(self,x,y): + return self.fmt_scale_data(x,y,"eps") + + +if __name__ == "__main__": + + import sys + + img = ImgManip(sys.argv[1]) + img.jpg_thumbnail("foo.jpg",50,50) + img.png_thumbnail("foo.png",50,50) + img.eps_thumbnail("foo.eps",50,50) + + + + + diff --git a/gramps2/src/LocEdit.py b/gramps2/src/LocEdit.py new file mode 100644 index 000000000..72de8afc8 --- /dev/null +++ b/gramps2/src/LocEdit.py @@ -0,0 +1,116 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +#------------------------------------------------------------------------- +# +# GTK/Gnome modules +# +#------------------------------------------------------------------------- +import gtk.glade + +#------------------------------------------------------------------------- +# +# gramps modules +# +#------------------------------------------------------------------------- +import const +import Utils +from RelLib import * + +from intl import gettext +_ = gettext + +#------------------------------------------------------------------------- +# +# LocationEditor class +# +#------------------------------------------------------------------------- +class LocationEditor: + + def __init__(self,parent,location): + self.parent = parent + self.location = location + self.top = gtk.glade.XML(const.dialogFile, "loc_edit") + self.window = self.top.get_widget("loc_edit") + self.city = self.top.get_widget("city") + self.state = self.top.get_widget("state") + self.parish = self.top.get_widget("parish") + self.county = self.top.get_widget("county") + self.country = self.top.get_widget("country") + + if parent.place: + name = _("Location Editor for %s") % parent.place.get_title() + else: + name = _("Location Editor") + + self.top.get_widget("locationTitle").set_text(name) + + if location != None: + self.city.set_text(location.get_city()) + self.county.set_text(location.get_county()) + self.country.set_text(location.get_country()) + self.state.set_text(location.get_state()) + self.parish.set_text(location.get_parish()) + + self.window.set_data("o",self) + self.top.signal_autoconnect({ + "destroy_passed_object" : Utils.destroy_passed_object, + "on_loc_edit_ok_clicked" : self.on_location_edit_ok_clicked + }) + + def on_location_edit_ok_clicked(self,obj): + self.location = self.location + + city = self.city.get_text() + county = self.county.get_text() + country = self.country.get_text() + state = self.state.get_text() + parish = self.parish.get_text() + + if self.location == None: + self.location = Location() + self.parent.llist.append(self.location) + + self.update_location(city,parish,county,state,country) + + self.parent.redraw_location_list() + Utils.destroy_passed_object(obj) + + def update_location(self,city,parish,county,state,country): + if self.location.get_city() != city: + self.location.set_city(city) + self.parent.lists_changed = 1 + + if self.location.get_parish() != parish: + self.location.set_parish(parish) + self.parent.lists_changed = 1 + + if self.location.get_county() != county: + self.location.set_county(county) + self.parent.lists_changed = 1 + + if self.location.get_state() != state: + self.location.set_state(state) + self.parent.lists_changed = 1 + + if self.location.get_country() != country: + self.location.set_country(country) + self.parent.lists_changed = 1 + diff --git a/gramps2/src/Makefile.am b/gramps2/src/Makefile.am new file mode 100644 index 000000000..451a73044 --- /dev/null +++ b/gramps2/src/Makefile.am @@ -0,0 +1,59 @@ +# This is the src level Makefile for Gramps +SUBDIRS = docgen filters plugins data po + +# For intl. support, how do we compile? +CFLAGS = -fPIC -shared -O @CFLAGS@ @CPPFLAGS@ -I@includedir@ +LDFLAGS = @LDFLAGS@ -L@libdir@ @LIBS@ +CLEANFILES = ${INTLLIBS} +MOSTLYCLEANFILES = + +# What are the PYTHON scripts for this package that need to be handled? +# +# We only want optimized byte-compiled (.pyo) versions, no .pyc +# In principle, this is handled by PYCFILES and PYOFILES, but +# they don't seem to work so we edited the py-compile script instead +pkgpython_PYTHON = ${wildcard *.py} + +# Use GNU make's ':=' syntax for nice wildcard use. +# If not using GNU make, then list all files individually +GLADEFILES := ${wildcard *.glade} +GRAPHICS := ${wildcard *.xpm} ${wildcard *.png} ${wildcard *.jpg} + +# Other stuff that we need to install +pkgdata_DATA = ${INTLLIBS} ${GLADEFILES} ${GRAPHICS} gramps.desktop + +#EXTRA_DIST =@DISTLANGS@ + + +all: ${INTLLIBS} + +DIST_SOURCES = intl.c +dist_pkgdata_DATA = ${pkgdata_DATA} + +# These can prbably be done in a better or more elegant/generic way +# eventually (libtool?), but this works. +intl15.so: intl.c + $(CC) $(CFLAGS) $(LDFLAGS) @P15_INCLUDES@ -DVER15 -o $@ intl.c +intl20.so: intl.c + $(CC) $(CFLAGS) $(LDFLAGS) @P20_INCLUDES@ -DVER20 -o $@ intl.c +intl21.so: intl.c + $(CC) $(CFLAGS) $(LDFLAGS) @P21_INCLUDES@ -DVER21 -o $@ intl.c +intl22.so: intl.c + $(CC) $(CFLAGS) $(LDFLAGS) @P22_INCLUDES@ -DVER22 -o $@ intl.c + +# In principle the following rule slightly violates the automake/autoconf +# spirit of keeping each subdirectory as a separate entity unto itself. +# But, since the template depends on everything from here, we allow this +# one exception. +trans: po/template.po + ./build_po + +install-data-local: + ${INSTALL} -d ${prefix}/share/pixmaps + ${INSTALL_DATA} gramps.png ${prefix}/share/pixmaps + ${INSTALL} -d ${prefix}/share/gnome/apps/Applications + ${INSTALL_DATA} gramps.desktop ${prefix}/share/gnome/apps/Applications + +uninstall-local: + -rm ${prefix}/share/pixmaps/gramps.png + -rm ${prefix}/share/gnome/apps/Applications/gramps.desktop diff --git a/gramps2/src/Makefile.in b/gramps2/src/Makefile.in new file mode 100644 index 000000000..2ea043f3a --- /dev/null +++ b/gramps2/src/Makefile.in @@ -0,0 +1,478 @@ +# Makefile.in generated by automake 1.6.3 from Makefile.am. +# @configure_input@ + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : + +EXEEXT = @EXEEXT@ +OBJEXT = @OBJEXT@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +AMTAR = @AMTAR@ +AWK = @AWK@ +BINSH = @BINSH@ +CC = @CC@ +DEPDIR = @DEPDIR@ +DISABLE_SCROLLKEEPER = @DISABLE_SCROLLKEEPER@ +GNOMEHELP = @GNOMEHELP@ +HAVE_GNOME_CONFIG = @HAVE_GNOME_CONFIG@ +HAVE_JW = @HAVE_JW@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +JW = @JW@ +LANGUAGES = @LANGUAGES@ +LIBS = @LIBS@ +MOFILES = @MOFILES@ +MSGFMT = @MSGFMT@ +P15_INCLUDES = @P15_INCLUDES@ +P20_INCLUDES = @P20_INCLUDES@ +P21_INCLUDES = @P21_INCLUDES@ +P22_INCLUDES = @P22_INCLUDES@ +PACKAGE = @PACKAGE@ +POFILES = @POFILES@ +PYTHON = @PYTHON@ +PYTHON22 = @PYTHON22@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RELEASE = @RELEASE@ +SCROLLKEEPER_CONFIG = @SCROLLKEEPER_CONFIG@ +SCROLLKEEPER_REQUIRED = @SCROLLKEEPER_REQUIRED@ +STRIP = @STRIP@ +VERSION = @VERSION@ +VERSIONSTRING = @VERSIONSTRING@ +ZIP = @ZIP@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ + +# This is the src level Makefile for Gramps +SUBDIRS = docgen filters plugins data po + +# For intl. support, how do we compile? +CFLAGS = -fPIC -shared -O @CFLAGS@ @CPPFLAGS@ -I@includedir@ +LDFLAGS = @LDFLAGS@ -L@libdir@ @LIBS@ +CLEANFILES = ${INTLLIBS} +MOSTLYCLEANFILES = + +# What are the PYTHON scripts for this package that need to be handled? +# +# We only want optimized byte-compiled (.pyo) versions, no .pyc +# In principle, this is handled by PYCFILES and PYOFILES, but +# they don't seem to work so we edited the py-compile script instead +pkgpython_PYTHON = ${wildcard *.py} + +# Use GNU make's ':=' syntax for nice wildcard use. +# If not using GNU make, then list all files individually +GLADEFILES := ${wildcard *.glade} +GRAPHICS := ${wildcard *.xpm} ${wildcard *.png} ${wildcard *.jpg} + +# Other stuff that we need to install +pkgdata_DATA = ${INTLLIBS} ${GLADEFILES} ${GRAPHICS} gramps.desktop + +DIST_SOURCES = intl.c +dist_pkgdata_DATA = ${pkgdata_DATA} +subdir = src +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_CLEAN_FILES = const.py +py_compile = $(top_srcdir)/py-compile +DATA = $(dist_pkgdata_DATA) $(pkgdata_DATA) + + +RECURSIVE_TARGETS = info-recursive dvi-recursive install-info-recursive \ + uninstall-info-recursive all-recursive install-data-recursive \ + install-exec-recursive installdirs-recursive install-recursive \ + uninstall-recursive check-recursive installcheck-recursive +DIST_COMMON = README $(dist_pkgdata_DATA) $(pkgpython_PYTHON) AUTHORS \ + ChangeLog Makefile.am Makefile.in NEWS configure.in const.py.in +DIST_SUBDIRS = $(SUBDIRS) +all: all-recursive + +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe) +const.py: $(top_builddir)/config.status const.py.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +uninstall-info-am: +pkgpythonPYTHON_INSTALL = $(INSTALL_DATA) +install-pkgpythonPYTHON: $(pkgpython_PYTHON) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(pkgpythondir) + @list='$(pkgpython_PYTHON)'; dlist=''; for p in $$list; do\ + if test -f $(srcdir)/$$p; then \ + d=`echo "$$p" | sed -e 's,^.*/,,'`; \ + dlist="$$dlist $$d"; \ + echo " $(pkgpythonPYTHON_INSTALL) $(srcdir)/$$p $(DESTDIR)$(pkgpythondir)/$$d"; \ + $(pkgpythonPYTHON_INSTALL) $(srcdir)/$$p $(DESTDIR)$(pkgpythondir)/$$d; \ + else :; fi; \ + done; \ + PYTHON=$(PYTHON) $(py_compile) --basedir $(DESTDIR)$(pkgpythondir) $$dlist + +uninstall-pkgpythonPYTHON: + @$(NORMAL_UNINSTALL) + list='$(pkgpython_PYTHON)'; for p in $$list; do \ + d=`echo "$$p" | sed -e 's,^.*/,,'`; \ + rm -f $(DESTDIR)$(pkgpythondir)/$$d; \ + rm -f $(DESTDIR)$(pkgpythondir)/$${d}c; \ + rm -f $(DESTDIR)$(pkgpythondir)/$${d}o; \ + done +dist_pkgdataDATA_INSTALL = $(INSTALL_DATA) +install-dist_pkgdataDATA: $(dist_pkgdata_DATA) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(pkgdatadir) + @list='$(dist_pkgdata_DATA)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + f="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " $(dist_pkgdataDATA_INSTALL) $$d$$p $(DESTDIR)$(pkgdatadir)/$$f"; \ + $(dist_pkgdataDATA_INSTALL) $$d$$p $(DESTDIR)$(pkgdatadir)/$$f; \ + done + +uninstall-dist_pkgdataDATA: + @$(NORMAL_UNINSTALL) + @list='$(dist_pkgdata_DATA)'; for p in $$list; do \ + f="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " rm -f $(DESTDIR)$(pkgdatadir)/$$f"; \ + rm -f $(DESTDIR)$(pkgdatadir)/$$f; \ + done +pkgdataDATA_INSTALL = $(INSTALL_DATA) +install-pkgdataDATA: $(pkgdata_DATA) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(pkgdatadir) + @list='$(pkgdata_DATA)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + f="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " $(pkgdataDATA_INSTALL) $$d$$p $(DESTDIR)$(pkgdatadir)/$$f"; \ + $(pkgdataDATA_INSTALL) $$d$$p $(DESTDIR)$(pkgdatadir)/$$f; \ + done + +uninstall-pkgdataDATA: + @$(NORMAL_UNINSTALL) + @list='$(pkgdata_DATA)'; for p in $$list; do \ + f="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " rm -f $(DESTDIR)$(pkgdatadir)/$$f"; \ + rm -f $(DESTDIR)$(pkgdatadir)/$$f; \ + done + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @set fnord $$MAKEFLAGS; amf=$$2; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @set fnord $$MAKEFLAGS; amf=$$2; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done + +ETAGS = etags +ETAGSFLAGS = + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -f $$subdir/TAGS && tags="$$tags -i $$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$tags$$unique" \ + || $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = .. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @list='$(DISTFILES)'; for file in $$list; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkinstalldirs) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d $(distdir)/$$subdir \ + || mkdir $(distdir)/$$subdir \ + || exit 1; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" \ + distdir=../$(distdir)/$$subdir \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(DATA) +installdirs: installdirs-recursive +installdirs-am: + $(mkinstalldirs) $(DESTDIR)$(pkgpythondir) $(DESTDIR)$(pkgdatadir) $(DESTDIR)$(pkgdatadir) + +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + -test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES) + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-recursive + +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-recursive + +dvi-am: + +info: info-recursive + +info-am: + +install-data-am: install-data-local install-dist_pkgdataDATA \ + install-pkgdataDATA install-pkgpythonPYTHON + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic + +uninstall-am: uninstall-dist_pkgdataDATA uninstall-info-am \ + uninstall-local uninstall-pkgdataDATA uninstall-pkgpythonPYTHON + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) GTAGS all all-am check check-am clean \ + clean-generic clean-recursive distclean distclean-generic \ + distclean-recursive distclean-tags distdir dvi dvi-am \ + dvi-recursive info info-am info-recursive install install-am \ + install-data install-data-am install-data-local \ + install-data-recursive install-dist_pkgdataDATA install-exec \ + install-exec-am install-exec-recursive install-info \ + install-info-am install-info-recursive install-man \ + install-pkgdataDATA install-pkgpythonPYTHON install-recursive \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am installdirs-recursive maintainer-clean \ + maintainer-clean-generic maintainer-clean-recursive mostlyclean \ + mostlyclean-generic mostlyclean-recursive tags tags-recursive \ + uninstall uninstall-am uninstall-dist_pkgdataDATA \ + uninstall-info-am uninstall-info-recursive uninstall-local \ + uninstall-pkgdataDATA uninstall-pkgpythonPYTHON \ + uninstall-recursive + + +#EXTRA_DIST =@DISTLANGS@ + +all: ${INTLLIBS} + +# These can prbably be done in a better or more elegant/generic way +# eventually (libtool?), but this works. +intl15.so: intl.c + $(CC) $(CFLAGS) $(LDFLAGS) @P15_INCLUDES@ -DVER15 -o $@ intl.c +intl20.so: intl.c + $(CC) $(CFLAGS) $(LDFLAGS) @P20_INCLUDES@ -DVER20 -o $@ intl.c +intl21.so: intl.c + $(CC) $(CFLAGS) $(LDFLAGS) @P21_INCLUDES@ -DVER21 -o $@ intl.c +intl22.so: intl.c + $(CC) $(CFLAGS) $(LDFLAGS) @P22_INCLUDES@ -DVER22 -o $@ intl.c + +# In principle the following rule slightly violates the automake/autoconf +# spirit of keeping each subdirectory as a separate entity unto itself. +# But, since the template depends on everything from here, we allow this +# one exception. +trans: po/template.po + ./build_po + +install-data-local: + ${INSTALL} -d ${prefix}/share/pixmaps + ${INSTALL_DATA} gramps.png ${prefix}/share/pixmaps + ${INSTALL} -d ${prefix}/share/gnome/apps/Applications + ${INSTALL_DATA} gramps.desktop ${prefix}/share/gnome/apps/Applications + +uninstall-local: + -rm ${prefix}/share/pixmaps/gramps.png + -rm ${prefix}/share/gnome/apps/Applications/gramps.desktop +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/gramps2/src/Marriage.py b/gramps2/src/Marriage.py new file mode 100644 index 000000000..4401bcf25 --- /dev/null +++ b/gramps2/src/Marriage.py @@ -0,0 +1,567 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# This program is free software; you can redistribute it and/or modiy +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +import pickle + +#------------------------------------------------------------------------- +# +# GTK/Gnome modules +# +#------------------------------------------------------------------------- +import gobject +import gtk +import gtk.glade +import gnome.ui + +#------------------------------------------------------------------------- +# +# gramps modules +# +#------------------------------------------------------------------------- + +import const +import GrampsCfg +import Utils +import AutoComp +from QuestionDialog import QuestionDialog +from RelLib import * +import ImageSelect +from intl import gettext +_ = gettext + + +_temple_names = const.lds_temple_codes.keys() +_temple_names.sort() +_temple_names = [""] + _temple_names + +pycode_tgts = [('fevent', 0, 0), + ('fattr', 0, 1)] + +#------------------------------------------------------------------------- +# +# Marriage class +# +#------------------------------------------------------------------------- +class Marriage: + + def __init__(self,family,db,callback): + """Initializes the Marriage class, and displays the window""" + self.family = family + self.db = db + self.path = db.getSavePath() + self.cb = callback + self.pmap = {} + + for key in db.getPlaceKeys(): + p = db.getPlaceDisplay(key) + self.pmap[p[0]] = key + + self.top = gtk.glade.XML(const.marriageFile,"marriageEditor") + top_window = self.get_widget("marriageEditor") + self.icon_list = self.get_widget('iconlist') + self.gallery = ImageSelect.Gallery(family, self.path, self.icon_list, db, self) + + self.top.signal_autoconnect({ + "destroy_passed_object" : self.on_cancel_edit, + "on_up_clicked" : self.on_up_clicked, + "on_down_clicked" : self.on_down_clicked, + "on_add_attr_clicked" : self.on_add_attr_clicked, + "on_addphoto_clicked" : self.gallery.on_add_photo_clicked, + "on_attr_list_select_row" : self.on_attr_list_select_row, +# "on_combo_insert_text" : Utils.combo_insert_text, + "on_close_marriage_editor" : self.on_close_marriage_editor, + "on_delete_attr_clicked" : self.on_delete_attr_clicked, + "on_delete_event" : self.on_delete_event, + "on_lds_src_clicked" : self.lds_src_clicked, + "on_lds_note_clicked" : self.lds_note_clicked, + "on_deletephoto_clicked" : self.gallery.on_delete_photo_clicked, + "on_edit_properties_clicked": self.gallery.popup_change_description, + "on_marriageAddBtn_clicked" : self.on_add_clicked, + "on_marriageDeleteBtn_clicked" : self.on_delete_clicked, + "on_marriageEventList_select_row" : self.on_select_row, + "on_marriageUpdateBtn_clicked" : self.on_update_clicked, + "on_photolist_button_press_event" : self.gallery.on_button_press_event, + "on_photolist_select_icon" : self.gallery.on_photo_select_icon, + "on_update_attr_clicked" : self.on_update_attr_clicked, + }) + + text_win = self.get_widget("marriageTitle") + title = _("%s and %s") % (GrampsCfg.nameof(family.getFather()), + GrampsCfg.nameof(family.getMother())) + text_win.set_text(title) + + self.event_list = self.get_widget("marriageEventList") + + # widgets + self.date_field = self.get_widget("marriageDate") + self.place_field = self.get_widget("marriagePlace") + self.cause_field = self.get_widget("marriageCause") + self.name_field = self.get_widget("marriageEventName") + self.descr_field = self.get_widget("marriageDescription") + self.type_field = self.get_widget("marriage_type") + self.notes_field = self.get_widget("marriageNotes") + self.gid = self.get_widget("gid") + self.attr_list = self.get_widget("attr_list") + self.attr_type = self.get_widget("attr_type") + self.attr_value = self.get_widget("attr_value") + self.event_src_field = self.get_widget("event_srcinfo") + self.event_conf_field = self.get_widget("event_conf") + self.attr_src_field = self.get_widget("attr_srcinfo") + self.attr_conf_field = self.get_widget("attr_conf") + self.lds_date = self.get_widget("lds_date") + self.lds_temple = self.get_widget("lds_temple") + self.lds_status = self.get_widget("lds_status") + self.lds_place = self.get_widget("lds_place") + + self.elist = family.getEventList()[:] + self.alist = family.getAttributeList()[:] + self.lists_changed = 0 + + # set initial data + self.gallery.load_images() + + self.event_model = gtk.ListStore(gobject.TYPE_STRING,gobject.TYPE_STRING, + gobject.TYPE_STRING) + cnum = 0 + for name in [_('Event'),_('Date'),_('Place')]: + renderer = gtk.CellRendererText() + column = gtk.TreeViewColumn(name,renderer,text=cnum) + cnum = cnum + 1 + self.event_list.append_column(column) + self.event_list.set_model(self.event_model) + + self.attr_model = gtk.ListStore(gobject.TYPE_STRING,gobject.TYPE_STRING) + cnum = 0 + for name in [_('Attribute'),_('Value')]: + renderer = gtk.CellRendererText() + column = gtk.TreeViewColumn(name,renderer,text=cnum) + cnum = cnum + 1 + self.attr_list.append_column(column) + self.attr_list.set_model(self.attr_model) + + self.addr_model = gtk.ListStore(gobject.TYPE_STRING,gobject.TYPE_STRING) + + self.type_field.set_popdown_strings(const.familyRelations) + frel = const.display_frel(family.getRelationship()) + self.type_field.entry.set_text(frel) + self.gid.set_text(family.getId()) + self.gid.set_editable(GrampsCfg.id_edit) + + self.lds_temple.set_popdown_strings(_temple_names) + + ord = self.family.getLdsSeal() + if ord: + if ord.getPlace(): + self.lds_place.entry.set_text(ord.getPlace().get_title()) + self.lds_date.set_text(ord.getDate()) + if ord.getTemple() != "": + name = const.lds_temple_to_abrev[ord.getTemple()] + else: + name = "" + self.lds_temple.entry.set_text(name) + self.seal_stat = ord.getStatus() + else: + self.lds_temple.entry.set_text("") + self.seal_stat = 0 + self.autoplace = AutoComp.AutoCombo(self.lds_place,self.pmap.keys()) + + self.build_seal_menu() + + self.event_list.drag_dest_set(gtk.DEST_DEFAULT_ALL, + pycode_tgts,gtk.gdk.ACTION_COPY) + self.event_list.drag_source_set(gtk.gdk.BUTTON1_MASK, + pycode_tgts, gtk.gdk.ACTION_COPY) + self.event_list.connect('drag_data_get', self.ev_source_drag_data_get) + self.event_list.connect('drag_data_received', + self.ev_dest_drag_data_received) + + self.attr_list.drag_dest_set(gtk.DEST_DEFAULT_ALL,pycode_tgts, + gtk.gdk.ACTION_COPY) + self.attr_list.drag_source_set(gtk.gdk.BUTTON1_MASK, pycode_tgts, + gtk.gdk.ACTION_COPY) + self.attr_list.connect('drag_data_get', self.at_source_drag_data_get) + self.attr_list.connect('drag_data_received', + self.at_dest_drag_data_received) + + # set notes data + self.notes_buffer = self.notes_field.get_buffer() + self.notes_buffer.set_text(family.getNote()) + + self.redraw_event_list() + self.redraw_attr_list() + top_window.show() + + def build_seal_menu(self): + menu = gtk.Menu() + index = 0 + for val in const.lds_ssealing: + menuitem = gtk.MenuItem(val) + menuitem.set_data("val",index) + menuitem.connect('activate',self.set_lds_seal) + menuitem.show() + menu.append(menuitem) + index = index + 1 + self.lds_status.set_menu(menu) + self.lds_status.set_history(self.seal_stat) + + def set_lds_seal(self,obj): + self.seal_stat = obj.get_data("val") + + def lds_src_clicked(self,obj): + import Sources + ord = self.family.getLdsSeal() + if ord == None: + ord = LdsOrd() + self.family.setLdsSeal(ord) + Sources.SourceSelector(ord.getSourceRefList(),self) + + def lds_note_clicked(self,obj): + import NoteEdit + ord = self.family.getLdsSeal() + if ord == None: + ord = LdsOrd() + self.family.setLdsSeal(ord) + NoteEdit.NoteEditor(ord) + + def on_up_clicked(self,obj): + if len(obj.selection) == 0: + return + row = obj.selection[0] + if row != 0: + obj.select_row(row-1,0) + + def on_down_clicked(self,obj): + if len(obj.selection) == 0: + return + row = obj.selection[0] + if row != obj.rows-1: + obj.select_row(row+1,0) + + def ev_dest_drag_data_received(self,widget,context,x,y,selection_data,info,time): + if selection_data and selection_data.data: + exec 'data = %s' % selection_data.data + exec 'mytype = "%s"' % data[0] + exec 'family = "%s"' % data[1] + if family == self.family.getId() or mytype != 'fevent': + return + foo = pickle.loads(data[2]); + for src in foo.getSourceRefList(): + base = src.getBase() + newbase = self.db.findSourceNoMap(base.getId()) + src.setBase(newbase) + place = foo.getPlace() + if place: + foo.setPlace(self.db.findPlaceNoMap(place.getId())) + self.elist.append(foo) + self.lists_changed = 1 + self.redraw_event_list() + + def ev_source_drag_data_get(self,widget, context, selection_data, info, time): + ev = widget.get_row_data(widget.focus_row) + + bits_per = 8; # we're going to pass a string + pickled = pickle.dumps(ev); + data = str(('fevent',self.family.getId(),pickled)); + selection_data.set(selection_data.target, bits_per, data) + + def at_dest_drag_data_received(self,widget,context,x,y,selection_data,info,time): + if selection_data and selection_data.data: + exec 'data = %s' % selection_data.data + exec 'mytype = "%s"' % data[0] + exec 'family = "%s"' % data[1] + if family == self.family.getId() or mytype != 'fattr': + return + foo = pickle.loads(data[2]); + for src in foo.getSourceRefList(): + base = src.getBase() + newbase = self.db.findSourceNoMap(base.getId()) + src.setBase(newbase) + self.alist.append(foo) + self.lists_changed = 1 + self.redraw_attr_list() + + def at_source_drag_data_get(self,widget, context, selection_data, info, time): + ev = widget.get_row_data(widget.focus_row) + bits_per = 8; # we're going to pass a string + pickled = pickle.dumps(ev); + data = str(('fattr',self.family.getId(),pickled)); + selection_data.set(selection_data.target, bits_per, data) + + #------------------------------------------------------------------------- + # + # + # + #------------------------------------------------------------------------- + def update_lists(self): + self.family.setEventList(self.elist) + self.family.setAttributeList(self.alist) + + def redraw_attr_list(self): + Utils.redraw_list(self.alist,self.attr_model,disp_attr) + + def redraw_event_list(self): + Utils.redraw_list(self.elist,self.event_model,disp_event) + + def get_widget(self,name): + return self.top.get_widget(name) + + def did_data_change(self): + changed = 0 + relation = self.type_field.entry.get_text() + if const.save_frel(relation) != self.family.getRelationship(): + changed = 1 + + text = self.notes_buffer.get_text(self.notes_buffer.get_start_iter(), + self.notes_buffer.get_end_iter(),gtk.FALSE) + if text != self.family.getNote(): + changed = 1 + + if self.lists_changed: + changed = 1 + + idval = self.gid.get_text() + if self.family.getId() != idval: + changed = 1 + + date = self.lds_date.get_text() + temple = self.lds_temple.entry.get_text() + if const.lds_temple_codes.has_key(temple): + temple = const.lds_temple_codes[temple] + else: + temple = "" + + place = Utils.get_place_from_list(self.lds_place) + + ord = self.family.getLdsSeal() + if not ord: + if date or temple or place or self.seal_stat: + changed = 1 + else: + d = Date() + d.set(date) + if compare_dates(d,ord.getDateObj()) != 0 or \ + ord.getTemple() != temple or \ + ord.getPlace() != place or \ + ord.getStatus() != self.seal_stat: + changed = 1 + + return changed + + def cancel_callback(self): + Utils.destroy_passed_object(self.quit) + + def on_cancel_edit(self,obj): + + if self.did_data_change(): + global quit + self.quit = obj + QuestionDialog(_('Abandon Changes'), + _("Data was modified. Are you sure you " + "want to abandon your changes?"), + _('Abandon Changes'),self.cancel_callback, + _('Continue Editing')) + else: + Utils.destroy_passed_object(obj) + + def on_delete_event(self,obj,b): + self.on_cancel_edit(obj) + + def on_close_marriage_editor(self,obj): + idval = self.gid.get_text() + family = self.family + if idval != family.getId(): + m = self.db.getFamilyMap() + if not m.has_key(idval): + if m.has_key(family.getId()): + del m[family.getId()] + m[idval] = family + family.setId(idval) + Utils.modified() + else: + msg1 = _("GRAMPS ID value was not changed.") + GnomeWarningDialog("%s" % msg1) + + relation = self.type_field.entry.get_text() + father = self.family.getFather() + mother = self.family.getMother() + if father and mother: + if const.save_frel(relation) != self.family.getRelationship(): + if father.getGender() == mother.getGender(): + self.family.setRelationship("Partners") + else: + val = const.save_frel(relation) + if val == "Partners": + val = "Unknown" + if father.getGender() == Person.female or \ + mother.getGender() == Person.male: + self.family.setFather(mother) + self.family.setMother(father) + self.family.setRelationship(val) + Utils.modified() + + text = self.notes_buffer.get_text(self.notes_buffer.get_start_iter(), + self.notes_buffer.get_end_iter(),gtk.FALSE) + if text != self.family.getNote(): + self.family.setNote(text) + Utils.modified() + + date = self.lds_date.get_text() + temple = self.lds_temple.entry.get_text() + if const.lds_temple_codes.has_key(temple): + temple = const.lds_temple_codes[temple] + else: + temple = "" + place = Utils.get_place_from_list(self.lds_place) + + ord = self.family.getLdsSeal() + if not ord: + if date or temple or place or self.seal_stat: + ord = LdsOrd() + ord.setDate(date) + ord.setTemple(temple) + ord.setStatus(self.seal_stat) + ord.setPlace(place) + self.family.setLdsSeal(ord) + Utils.modified() + else: + d = Date() + d.set(date) + if compare_dates(d,ord.getDateObj()) != 0: + ord.setDateObj(d) + Utils.modified() + if ord.getTemple() != temple: + ord.setTemple(temple) + Utils.modified() + if ord.getStatus() != self.seal_stat: + ord.setStatus(self.seal_stat) + Utils.modified() + if ord.getPlace() != place: + ord.setPlace(place) + Utils.modified() + + Utils.destroy_passed_object(self.get_widget("marriageEditor")) + + self.update_lists() + if self.lists_changed: + Utils.modified() + + def on_add_clicked(self,obj): + import EventEdit + name = Utils.family_name(self.family) + EventEdit.EventEditor(self,name,const.marriageEvents, + const.save_pevent,None,None,0,self.cb) + + def on_update_clicked(self,obj): + import EventEdit + if len(obj.selection) <= 0: + return + + event = obj.get_row_data(obj.selection[0]) + name = Utils.family_name(self.family) + EventEdit.EventEditor(self,name,const.marriageEvents, + const.save_pevent,event,None,0,self.cb) + + def on_delete_clicked(self,obj): + if Utils.delete_selected(obj,self.elist): + self.lists_changed = 1 + self.redraw_event_list() + + def on_select_row(self,obj,row,b,c): + event = obj.get_row_data(row) + + self.date_field.set_text(event.getDate()) + self.place_field.set_text(event.getPlaceName()) + self.cause_field.set_text(event.getCause()) + self.name_field.set_label(const.display_fevent(event.getName())) + if len(event.getSourceRefList()) > 0: + psrc = event.getSourceRefList()[0] + self.event_src_field.set_text(psrc.getBase().getTitle()) + self.event_conf_field.set_text(const.confidence[psrc.getConfidence()]) + else: + self.event_src_field.set_text('') + self.event_conf_field.set_text('') + self.descr_field.set_text(event.getDescription()) + + def on_attr_list_select_row(self,obj,row,b,c): + attr = obj.get_row_data(row) + + self.attr_type.set_label(const.display_fattr(attr.getType())) + self.attr_value.set_text(attr.getValue()) + if len(attr.getSourceRefList()) > 0: + psrc = attr.getSourceRefList()[0] + self.attr_src_field.set_text(psrc.getBase().getTitle()) + self.attr_conf_field.set_text(const.confidence[psrc.getConfidence()]) + else: + self.attr_src_field.set_text('') + self.attr_conf_field.set_text('') + + def on_update_attr_clicked(self,obj): + import AttrEdit + if len(obj.selection) > 0: + row = obj.selection[0] + attr = obj.get_row_data(row) + father = self.family.getFather() + mother = self.family.getMother() + if father and mother: + name = _("%s and %s") % (father.getPrimaryName().getName(), + mother.getPrimaryName().getName()) + elif father: + name = father.getPrimaryName().getName() + else: + name = mother.getPrimaryName().getName() + AttrEdit.AttributeEditor(self,attr,name,const.familyAttributes) + + def on_delete_attr_clicked(self,obj): + if Utils.delete_selected(obj,self.alist): + self.lists_changed = 1 + self.redraw_attr_list() + + def on_add_attr_clicked(self,obj): + import AttrEdit + father = self.family.getFather() + mother = self.family.getMother() + if father and mother: + name = _("%s and %s") % (father.getPrimaryName().getName(), + mother.getPrimaryName().getName()) + elif father: + name = father.getPrimaryName().getName() + else: + name = mother.getPrimaryName().getName() + AttrEdit.AttributeEditor(self,None,name,const.familyAttributes) + + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +def disp_attr(attr): + return [const.display_fattr(attr.getType()),attr.getValue()] + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +def disp_event(event): + return [const.display_fevent(event.getName()), + event.getQuoteDate(), + event.getPlaceName()] + diff --git a/gramps2/src/MediaView.py b/gramps2/src/MediaView.py new file mode 100644 index 000000000..ba6421860 --- /dev/null +++ b/gramps2/src/MediaView.py @@ -0,0 +1,356 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2001 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +#------------------------------------------------------------------------- +# +# standard python modules +# +#------------------------------------------------------------------------- +import string + +#------------------------------------------------------------------------- +# +# GTK/Gnome modules +# +#------------------------------------------------------------------------- +import gobject +import gtk +import gtk.gdk + +import ImageSelect + +#------------------------------------------------------------------------- +# +# gramps modules +# +#------------------------------------------------------------------------- +from RelLib import * +from QuestionDialog import QuestionDialog +import Utils +import os +import GrampsCfg +import const +import RelImage + +#------------------------------------------------------------------------- +# +# internationalization +# +#------------------------------------------------------------------------- +from intl import gettext as _ + +_column_headers = [(_('Title'),4,350), (_('ID'),1,50), (_('Type'),2,70), + ('Path',3,150), ('',4,0) ] + +#------------------------------------------------------------------------- +# +# MediaView +# +#------------------------------------------------------------------------- +class MediaView: + def __init__(self,db,glade,update): + self.db = db + self.list = glade.get_widget("media_list") + self.mid = glade.get_widget("mid") + self.mtype = glade.get_widget("mtype") + self.mdesc = glade.get_widget("mdesc") + self.mpath = glade.get_widget("mpath") + self.mdetails = glade.get_widget("mdetails") + self.preview = glade.get_widget("preview") + + self.id2col = {} + self.selection = self.list.get_selection() + + colno = 0 + for title in _column_headers: + renderer = gtk.CellRendererText () + column = gtk.TreeViewColumn (title[0], renderer, text=colno) + colno = colno + 1 + column.set_clickable (gtk.TRUE) + if title[0] == '': + column.set_visible(gtk.FALSE) + else: + column.set_resizable(gtk.TRUE) + column.set_visible(gtk.TRUE) + column.set_sort_column_id(title[1]) + column.set_min_width(title[2]) + self.list.append_column(column) + + self.list.set_search_column(0) + self.model = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, + gobject.TYPE_STRING, gobject.TYPE_STRING, + gobject.TYPE_STRING) + self.list.set_model(self.model) + self.list.get_column(0).clicked() + + t = [ ('STRING', 0, 0), + ('text/plain',0,0), + ('text/uri-list',0,2), + ('application/x-rootwin-drop',0,1)] + + self.list.drag_source_set(gtk.gdk.BUTTON1_MASK|gtk.gdk.BUTTON3_MASK, + t,gtk.gdk.ACTION_COPY) + self.list.drag_dest_set(gtk.DEST_DEFAULT_ALL, + t,gtk.gdk.ACTION_COPY|gtk.gdk.ACTION_MOVE) + + self.update = update + self.list.connect('button-press-event',self.on_button_press_event) + self.selection.connect('changed',self.on_select_row) + + def change_db(self,db): + self.db = db + + def on_select_row(self,obj): + fexists = 1 + + store,iter = self.selection.get_selected() + id = store.get_value(iter,1) + + mobj = self.db.findObjectNoMap(id) + type = mobj.getMimeType() + type_name = Utils.get_mime_description(type) + path = mobj.getPath() + thumb_path = Utils.thumb_path(self.db.getSavePath(),mobj) + pexists = os.path.exists(path) + if pexists and os.path.exists(thumb_path): + self.preview.set_from_pixbuf(gtk.gdk.pixbuf_new_from_file(thumb_path)) + else: + self.preview.set_from_pixbuf(Utils.find_icon(type)) + if not pexists: + fexists = 0 + + self.mid.set_text(mobj.getId()) + self.mtype.set_text(type_name) + self.mdesc.set_text(mobj.getDescription()) + if len(path) == 0 or fexists == 0: + self.mpath.set_text(_("The file no longer exists")) + elif path[0] == "/": + self.mpath.set_text(path) + else: + self.mpath.set_text("") + self.mdetails.set_text(Utils.get_detail_text(mobj,0)) + + def on_button_press_event(self,obj,event): + store,iter = self.selection.get_selected() + if not iter: + return + id = store.get_value(iter,1) + + object = self.db.findObjectNoMap(id) + if event.type == gtk.gdk._2BUTTON_PRESS and event.button == 1: + ImageSelect.GlobalMediaProperties(self.db,object,self.load_media) + elif event.button == 3: + menu = gtk.Menu() + item = gtk.TearoffMenuItem() + item.show() + menu.append(item) + self.obj = object + Utils.add_menuitem(menu,_("View in the default viewer"),None,self.popup_view_photo) + if object.getMimeType()[0:5] == "image": + Utils.add_menuitem(menu,_("Edit with the GIMP"),\ + None,self.popup_edit_photo) + Utils.add_menuitem(menu,_("Edit Object Properties"),None, + self.popup_change_description) + if object.getLocal() == 0: + Utils.add_menuitem(menu,_("Convert to local copy"),None, + self.popup_convert_to_private) + menu.popup(None,None,None,0,0) + + def popup_view_photo(self, obj): + Utils.view_photo(self.obj) + + def popup_edit_photo(self, obj): + if os.fork() == 0: + os.execvp(const.editor,[const.editor, self.obj.getPath()]) + + def popup_convert_to_private(self, obj): + path = self.db.getSavePath() + id = self.obj.getId() + name = RelImage.import_object(self.obj.getPath(),path,id) + if name: + self.obj.setPath(name) + self.obj.setLocal(1) + + def popup_change_description(self, obj): + ImageSelect.GlobalMediaProperties(self.db,self.obj,self.load_media) + + def load_media(self): + self.model.clear() + self.id2col = {} + + objects = self.db.getObjectMap().values() + + for src in objects: + title = src.getDescription() + id = src.getId() + type = Utils.get_mime_description(src.getMimeType()) + if src.getLocal(): + path = "" + else: + path = src.getPath() + details = Utils.get_detail_flags(src,0) + stitle = string.upper(title) + + iter = self.model.append() + self.id2col[id] = iter + self.model.set(iter, 0, title, 1, id, 2, type, 3, path, 4, stitle) + +# if index > 0: +# self.list.select_row(current_row,0) +# self.list.moveto(current_row) +# self.preview.show() +# else: +# self.mid.set_text("") +# self.mtype.set_text("") +# self.mdesc.set_text("") +# self.mpath.set_text("") +# self.mdetails.set_text("") +# self.preview.hide() + +# if current_row < self.list.rows: +# self.list.moveto(current_row) +# else: +# self.list.moveto(0) +# self.list.thaw() + + def create_add_dialog(self,obj): + """Add a new media object to the media list""" + import AddMedia + AddMedia.AddMediaObject(self.db,self.load_media) + + def on_edit_media_clicked(self,obj): + """Edit the properties of an existing media object in the media list""" + if len(self.list.selection) <= 0: + return + object = self.list.get_row_data(self.list.selection[0]) + ImageSelect.GlobalMediaProperties(self.db,object,self.load_media) + + def on_delete_clicked(self,obj): + if len(self.list.selection) <= 0: + return + else: + index = self.list.selection[0] + mobj = self.list.get_row_data(index) + if self.is_object_used(mobj): + ans = ImageSelect.DeleteMediaQuery(mobj,self.db,self.update) + QuestionDialog(_('Delete Object'), + _("This media object is currently being used. " + "Delete anyway?"), + _('Delete Object'),ans.query_response, + _('Keep Object')) + else: + map = self.db.getObjectMap() + del map[mobj.getId()] + Utils.modified() + self.update(0) + + def is_media_object_used(self,mobj): + for p in self.db.getFamilyMap().values(): + for o in p.getPhotoList(): + if o.getReference() == mobj: + return 1 + for key in self.db.getPersonKeys(): + p = self.db.getPerson(key) + for o in p.getPhotoList(): + if o.getReference() == mobj: + return 1 + for key in self.db.getSourceKeys(): + p = self.db.getSource(key) + for o in p.getPhotoList(): + if o.getReference() == mobj: + return 1 + for key in self.db.getPlaceKeys(): + p = self.db.getPlace(key) + for o in p.getPhotoList(): + if o.getReference() == mobj: + return 1 + return 0 + + def on_drag_data_get(self,w, context, selection_data, info, time): + if info == 1: + return + d = w.get_row_data(w.focus_row) + id = d.getId() + selection_data.set(selection_data.target, 8, id) + + def on_drag_data_received(self,w, context, x, y, data, info, time): + import urlparse + if data and data.format == 8: + d = string.strip(string.replace(data.data,'\0',' ')) + protocol,site,file, j,k,l = urlparse.urlparse(d) + if protocol == "file": + name = file + mime = Utils.get_mime_type(name) + photo = Photo() + photo.setPath(name) + photo.setMimeType(mime) + description = os.path.basename(name) + photo.setDescription(description) + self.db.addObject(photo) + Utils.modified() + w.drag_finish(context, 1, 0, time) + self.load_media() + if GrampsCfg.mediaref == 0: + name = RelImage.import_media_object(name, + self.db.getSavePath(), + photo.getId()) + if name: + photo.setPath(name) + photo.setLocal(1) + Utils.modified() + if GrampsCfg.globalprop: + ImageSelect.GlobalMediaProperties(self.db,photo,self.load_media) + elif protocol != "": + import urllib + u = urllib.URLopener() + try: + tfile,headers = u.retrieve(d) + except IOError, msg: + t = _("Could not import %s") % d + + gnome.ui.GnomeErrorDialog("%s\n%s %d" % (t,msg[0],msg[1])) + return + mime = Utils.get_mime_type(tfile) + photo = Photo() + photo.setMimeType(mime) + photo.setDescription(d) + photo.setLocal(1) + photo.setPath(tfile) + self.db.addObject(photo) + oref = ObjectRef() + oref.setReference(photo) + try: + id = photo.getId() + path = self.db.getSavePath() + name = RelImage.import_media_object(tfile,path,id) + if name: + photo.setLocal(1) + photo.setPath(name) + except: + photo.setPath(tfile) + w.drag_finish(context, 1, 0, time) + return + Utils.modified() + if GrampsCfg.globalprop: + ImageSelect.GlobalMediaProperties(self.db,photo,None) + else: + w.drag_finish(context, 0, 0, time) + + diff --git a/gramps2/src/MergeData.py b/gramps2/src/MergeData.py new file mode 100644 index 000000000..8fa2b43db --- /dev/null +++ b/gramps2/src/MergeData.py @@ -0,0 +1,881 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +import RelLib +import Utils +import GrampsCfg +import const + +from intl import gettext +_ = gettext + +import string +import gtk.glade + +#------------------------------------------------------------------------- +# +# Merge People +# +#------------------------------------------------------------------------- +class MergePeople: + + def __init__(self,db,person1,person2,update,ep_update=None): + self.db = db + self.p1 = person1 + self.p2 = person2 + self.update = update + self.ep_update = ep_update + + self.glade = gtk.glade.XML(const.mergeFile,"merge") + self.top = self.glade.get_widget("merge") + self.altname = self.glade.get_widget("altname") + self.altbirth = self.glade.get_widget("altbirth") + self.altdeath = self.glade.get_widget("altdeath") + self.family_list = db.getFamilyMap().values() + + self.glade.signal_autoconnect({ + "on_merge_clicked" : self.on_merge_clicked, + "on_next_clicked" : self.on_merge_edit_clicked, + "destroy_passed_object" : Utils.destroy_passed_object, + }) + + fname = GrampsCfg.nameof(person1) + mname = GrampsCfg.nameof(person2) + label_text = "Merge %s and %s" % (fname,mname) + self.glade.get_widget("progress").set_text(label_text) + f1 = person1.getMainParents() + f2 = person2.getMainParents() + + name1 = GrampsCfg.nameof(person1) + death1 = person1.getDeath().getDate() + dplace1 = self.place_name(person1.getDeath()) + birth1 = person1.getBirth().getDate() + bplace1 = self.place_name(person1.getBirth()) + + name2 = GrampsCfg.nameof(person2) + death2 = person2.getDeath().getDate() + dplace2 = self.place_name(person2.getDeath()) + birth2 = person2.getBirth().getDate() + bplace2 = self.place_name(person2.getBirth()) + + if f2 and not f1: + self.glade.get_widget("bfather2").set_active(1) + else: + self.glade.get_widget("bfather1").set_active(1) + + if f1: + father1 = name_of(f1.getFather()) + mother1 = name_of(f1.getMother()) + else: + father1 = "" + mother1 = "" + + if f2: + father2 = name_of(f2.getFather()) + mother2 = name_of(f2.getMother()) + else: + father2 = "" + mother2 = "" + + self.set_field(self.glade.get_widget("id1_text"),person1.getId()) + self.set_field(self.glade.get_widget("id2_text"),person2.getId()) + self.set_field(self.glade.get_widget("name1_text"),name1) + self.set_field(self.glade.get_widget("name2_text"),name2) + + self.bname1 = self.glade.get_widget("bname1") + self.bname1.set_active(1) + + self.set_field(self.glade.get_widget("birth1_text"),birth1) + self.set_field(self.glade.get_widget("birth2_text"),birth2) + self.set_field(self.glade.get_widget("bplace1_text"),bplace1) + self.set_field(self.glade.get_widget("bplace2_text"),bplace2) + + if ((not birth1 and not bplace1) and (birth2 or bplace2) or + (not birth1 or not bplace1) and (birth2 and bplace2)): + self.glade.get_widget('bbirth2').set_active(1) + else: + self.glade.get_widget('bbirth1').set_active(1) + + if ((not death1 and not dplace1) and (death2 or dplace2) or + (not death1 or not dplace1) and (death2 and dplace2)): + self.glade.get_widget('death2').set_active(1) + else: + self.glade.get_widget('death1').set_active(1) + + self.set_field(self.glade.get_widget("death1_text"),death1) + self.set_field(self.glade.get_widget("dplace1_text"),dplace1) + self.set_field(self.glade.get_widget("death2_text"),death2) + self.set_field(self.glade.get_widget("dplace2_text"),dplace2) + + self.set_field(self.glade.get_widget("father1"),father1) + self.set_field(self.glade.get_widget("father2"),father2) + self.set_field(self.glade.get_widget("mother1"),mother1) + self.set_field(self.glade.get_widget("mother2"),mother2) + + self.build_spouse_list(person1,self.glade.get_widget('spouse1')) + self.build_spouse_list(person2,self.glade.get_widget('spouse2')) + + if name1 != name2: + self.altname.set_sensitive(1) + self.altname.set_active(1) + else: + self.altname.set_sensitive(0) + self.altname.set_active(0) + + if birth1 and birth2 and birth1 != birth2: + self.altbirth.set_active(1) + if bplace1 and bplace2 or bplace1 != bplace2: + self.altbirth.set_active(1) + else: + self.altbirth.set_active(0) + + if death1 and death2 and death1 != death2: + self.altdeath.set_active(1) + if dplace1 and dplace2 or dplace1 != dplace2: + self.altdeath.set_active(1) + else: + self.altdeath.set_active(0) + + def build_spouse_list(self,person,widget): + plist = person.getFamilyList() + + length = min(len(plist),3) + widget.clear() + for index in range(0,3): + if index < length and plist[index]: + if person.getGender() == RelLib.Person.male: + spouse = plist[index].getMother() + else: + spouse = plist[index].getFather() + + if spouse == None: + name = "unknown" + else: + sname = GrampsCfg.nameof(spouse) + name = "%s (%s)" % (sname,spouse.getId()) + widget.append([name]) + + def set_field(self,widget,value): + """Sets the string of the entry field at positions it a space 0""" + widget.set_text(value) + widget.set_position(0) + + def place_name(self,event): + place = event.getPlace() + if place: + return "%s (%s)" % (place.get_title(),place.getId()) + else: + return "" + + def empty(self,junk): + pass + + def on_merge_edit_clicked(self,obj): + import EditPerson + self.on_merge_clicked(obj) + # This needs to be fixed to provide an update call + EditPerson.EditPerson(self.p1,self.db,self.ep_update) + + def copy_note(self,one,two): + if one.getNote() != two.getNote(): + one.setNote("%s\n\n%s" % (one.getNote(),two.getNote())) + + def copy_sources(self,one,two): + slist = one.getSourceRefList()[:] + for xsrc in two.getSourceRefList(): + for src in slist: + if src.are_equal(xsrc): + break + else: + one.addSourceRef(xsrc) + + def on_merge_clicked(self,obj): + Utils.modified() + + list = self.p1.getAlternateNames()[:] + for xdata in self.p2.getAlternateNames(): + for data in list: + if data.are_equal(xdata): + self.copy_note(xdata,data) + self.copy_sources(xdata,data) + break + else: + self.p1.addAlternateName(xdata) + + list = self.p1.getAttributeList()[:] + for xdata in self.p2.getAttributeList(): + for data in list: + if data.getType() == xdata.getType() and \ + data.getValue() == xdata.getValue(): + self.copy_note(xdata,data) + self.copy_sources(xdata,data) + break + else: + self.p1.addAttribute(xdata) + + list = self.p1.getEventList()[:] + for xdata in self.p2.getEventList(): + for data in list: + if data.are_equal(xdata): + self.copy_note(xdata,data) + self.copy_sources(xdata,data) + break + else: + self.p1.addEvent(xdata) + + list = self.p1.getUrlList()[:] + for xdata in self.p2.getUrlList(): + for data in list: + if data.are_equal(xdata): + break + else: + self.p1.addUrl(xdata) + + self.id2 = self.glade.get_widget("id2") + old_id = self.p1.getId() + if self.id2.get_active(): + self.p1.setId(self.p2.getId()) + + if self.bname1.get_active(): + if self.altname.get_active(): + self.p1.addAlternateName(self.p2.getPrimaryName()) + else: + if self.altname.get_active(): + self.p1.addAlternateName(self.p1.getPrimaryName()) + self.p1.setPrimaryName(self.p2.getPrimaryName()) + + alt = self.glade.get_widget("altbirth").get_active() + if self.glade.get_widget("bbirth2").get_active(): + if alt: + event = self.p1.getBirth() + event.setName("Alternate Birth") + self.p1.addEvent(event) + self.p1.setBirth(self.p2.getBirth()) + else: + if alt: + event = self.p2.getBirth() + event.setName("Alternate Birth") + self.p1.addEvent(event) + + alt = self.glade.get_widget("altdeath").get_active() + if self.glade.get_widget("bbirth2").get_active(): + if alt: + event = self.p1.getDeath() + event.setName("Alternate Death") + self.p1.addEvent(event) + self.p1.setDeath(self.p2.getDeath()) + else: + if alt: + event = self.p2.getDeath() + event.setName("Alternate Death") + self.p1.addEvent(event) + + if self.glade.get_widget("bfather2").get_active(): + orig_family = self.p1.getMainParents() + if orig_family: + orig_family.removeChild(self.p1) + + source_family = self.p2.getMainParents() + self.p1.setMainParents(source_family) + + if source_family: + if self.p2 in source_family.getChildList(): + source_family.removeChild(self.p2) + if self.p1 not in source_family.getChildList(): + source_family.addChild(self.p1) + else: + source_family = self.p2.getMainParents() + if source_family: + source_family.removeChild(self.p2) + self.p2.setMainParents(None) + + self.merge_families() + + for photo in self.p2.getPhotoList(): + self.p1.addPhoto(photo) + + if self.p1.getNickName() == "": + self.p1.setNickName(self.p2.getNickName()) + + if self.p2.getNote() != "": + old_note = self.p1.getNote() + if old_note: + old_note = old_note + "\n\n" + self.p1.setNote(old_note + self.p2.getNote()) + + try: + self.db.removePerson(self.p2.getId()) + self.db.buildPersonDisplay(self.p1.getId(),old_id) + except: + print "%s is not in the person map!" % (GrampsCfg.nameof(self.p2)) + self.update(self.p1,self.p2,old_id) + Utils.destroy_passed_object(self.top) + + def find_family(self,family): + if self.p1.getGender() == RelLib.Person.male: + mother = family.getMother() + father = self.p1 + else: + father = family.getFather() + mother = self.p1 + + for myfamily in self.family_list: + if myfamily.getFather() == father and myfamily.getMother() == mother: + return myfamily + return None + + def merge_families(self): + + family_num = 0 + mylist = self.p2.getFamilyList()[:] + for src_family in mylist: + + family_num = family_num + 1 + + if not self.db.getFamilyMap().has_key(src_family.getId()): + continue + if src_family in self.p1.getFamilyList(): + continue + + tgt_family = self.find_family(src_family) + + # + # This is the case where a new family to be added to the + # p1 as a result of the merge already exists as a + # family. In this case, we need to remove the old source + # family (with the pre-merge identity of the p1) from + # both the parents + # + if tgt_family in self.p1.getFamilyList(): + if tgt_family.getFather() != None and \ + src_family in tgt_family.getFather().getFamilyList(): + tgt_family.getFather().removeFamily(src_family) + if tgt_family.getMother() != None and \ + src_family in tgt_family.getMother().getFamilyList(): + tgt_family.getMother().removeFamily(src_family) + + # copy children from source to target + + for child in src_family.getChildList(): + if child not in tgt_family.getChildList(): + parents = child.getParentList() + tgt_family.addChild(child) + if child.getMainParents() == src_family: + child.setMainParents(tgt_family) + i = 0 + for fam in parents[:]: + if fam[0] == src_family: + parents[i] = (tgt_family,fam[1],fam[2]) + i = i + 1 + + # delete the old source family + del self.db.getFamilyMap()[src_family.getId()] + + continue + + # This is the case where a new family to be added + # and it is not already in the list. + + if tgt_family: + + # tgt_family a duplicate family, transfer children from + # the p2 family, and delete the family. Not sure + # what to do about marriage/divorce date/place yet. + + # transfer child to new family, alter children to + # point to the correct family + + for child in src_family.getChildList(): + if child not in tgt_family.getChildList(): + parents = child.getParentList() + tgt_family.addChild(child) + if child.getMainParents() == src_family: + child.setMainParents(tgt_family) + i = 0 + for fam in parents[:]: + if fam[0] == src_family: + parents[i] = (tgt_family,fam[1],fam[2]) + i = i + 1 + + # add family events from the old to the new + for event in src_family.getEventList(): + tgt_family.addEvent(event) + + # change parents of the family to point to the new + # family + + if src_family.getFather(): + src_family.getFather().removeFamily(src_family) + src_family.getFather().addFamily(tgt_family) + + if src_family.getMother(): + src_family.getMother().removeFamily(src_family) + src_family.getMother().addFamily(tgt_family) + + del self.db.getFamilyMap()[src_family.getId()] + else: + if src_family not in self.p1.getFamilyList(): + self.p1.addFamily(src_family) + if self.p1.getGender() == RelLib.Person.male: + src_family.setFather(self.p1) + else: + src_family.setMother(self.p1) + self.remove_marriage(src_family,self.p2) + + # a little debugging here + + for fam in self.db.getFamilyMap().values(): + if self.p2 in fam.getChildList(): + fam.removeChild(self.p2) + fam.addChild(self.p1) + if self.p2 == fam.getFather(): + fam.setFather(self.p1) + if self.p2 == fam.getMother(): + fam.setMother(self.p1) + if fam.getFather() == None and fam.getMother() == None: + self.delete_empty_family(fam) + + def remove_marriage(self,family,person): + if person: + person.removeFamily(family) + if family.getFather() == None and family.getMother() == None: + self.delete_empty_family(family) + + def delete_empty_family(self,family): + for child in family.getChildList(): + if child.getMainParents() == family: + child.setMainParents(None) + else: + child.removeAltFamily(family) + self.db.deleteFamily(family) + +def compare_people(p1,p2): + + name1 = p1.getPrimaryName() + name2 = p2.getPrimaryName() + + chance = name_match(name1,name2) + if chance == -1.0 : + return -1.0 + + birth1 = p1.getBirth() + death1 = p1.getDeath() + birth2 = p2.getBirth() + death2 = p2.getDeath() + + value = date_match(birth1.getDateObj(),birth2.getDateObj()) + if value == -1.0 : + return -1.0 + chance = chance + value + + value = date_match(death1.getDateObj(),death2.getDateObj()) + if value == -1.0 : + return -1.0 + chance = chance + value + + value = place_match(birth1.getPlace(),birth2.getPlace()) + if value == -1.0 : + return -1.0 + chance = chance + value + + value = place_match(death1.getPlace(),death2.getPlace()) + if value == -1.0 : + return -1.0 + chance = chance + value + + ancestors = [] + ancestors_of(p1,ancestors) + if p2 in ancestors: + return -1.0 + + ancestors = [] + ancestors_of(p2,ancestors) + if p1 in ancestors: + return -1.0 + + f1 = p1.getMainParents() + f2 = p2.getMainParents() + + if f1 and f1.getFather(): + dad1 = f1.getFather().getPrimaryName() + else: + dad1 = None + + if f2 and f2.getFather(): + dad2 = f2.getFather().getPrimaryName() + else: + dad2 = None + + value = name_match(dad1,dad2) + + if value == -1.0: + return -1.0 + + chance = chance + value + + if f1 and f1.getMother(): + mom1 = f1.getMother().getPrimaryName() + else: + mom1 = None + + if f2 and f2.getMother(): + mom2 = f2.getMother().getPrimaryName() + else: + mom2 = None + + value = name_match(mom1,mom2) + if value == -1.0: + return -1.0 + + chance = chance + value + + for f1 in p1.getFamilyList(): + for f2 in p2.getFamilyList(): + if p1.getGender() == RelLib.Person.female: + father1 = f1.getFather() + father2 = f2.getFather() + if father1 and father2: + if father1 == father2: + chance = chance + 1.0 + else: + fname1 = GrampsCfg.nameof(father1) + fname2 = GrampsCfg.nameof(father2) + value = name_match(fname1,fname2) + if value != -1.0: + chance = chance + value + else: + mother1 = f1.getMother() + mother2 = f2.getMother() + if mother1 and mother2: + if mother1 == mother2: + chance = chance + 1.0 + else: + mname1 = GrampsCfg.nameof(mother1) + mname2 = GrampsCfg.nameof(mother2) + value = name_match(mname1,mname2) + if value != -1.0: + chance = chance + value + + return chance + +#----------------------------------------------------------------- +# +# +# +#----------------------------------------------------------------- +def name_compare(s1,s2): + return s1 == s2 + +#----------------------------------------------------------------- +# +# +# +#----------------------------------------------------------------- +def date_match(date1,date2): + if date1.getDate() == "" or date2.getDate() == "": + return 0.0 + if date1.getDate() == date2.getDate(): + return 1.0 + + if date1.isRange() or date2.isRange(): + return range_compare(date1,date2) + + date1 = date1.get_start_date() + date2 = date2.get_start_date() + + if date1.getYear() == date2.getYear(): + if date1.getMonth() == date2.getMonth(): + return 0.75 + if not date1.getMonthValid() or not date2.getMonthValid(): + return 0.75 + else: + return -1.0 + else: + return -1.0 + +#----------------------------------------------------------------- +# +# +# +#----------------------------------------------------------------- +def range_compare(date1,date2): + d1_start = date1.get_start_date() + d2_start = date2.get_start_date() + d1_stop = date1.get_stop_date() + d2_stop = date2.get_stop_date() + + if date1.isRange() and date2.isRange(): + if d1_start >= d2_start and d1_start <= d2_stop or \ + d2_start >= d1_start and d2_start <= d1_stop or \ + d1_stop >= d2_start and d1_stop <= d2_stop or \ + d2_stop >= d1_start and d2_stop <= d1_stop: + return 0.5 + else: + return -1.0 + elif date2.isRange(): + if d1_start >= d2_start and d1_start <= d2_stop: + return 0.5 + else: + return -1.0 + else: + if d2_start >= d1_start and d2_start <= d1_stop: + return 0.5 + else: + return -1.0 + +#--------------------------------------------------------------------- +# +# +# +#--------------------------------------------------------------------- +def name_match(name,name1): + + if not name1 or not name: + return 0 + + srn1 = name.getSurname() + sfx1 = name.getSuffix() + srn2 = name1.getSurname() + sfx2 = name1.getSuffix() + + if not name_compare(srn1,srn2): + return -1 + if sfx1 != sfx2: + if sfx1 != "" and sfx2 != "": + return -1 + + if name.getFirstName() == name1.getFirstName(): + return 1 + else: + list1 = string.split(name.getFirstName()) + list2 = string.split(name1.getFirstName()) + + if len(list1) < len(list2): + return list_reduce(list1,list2) + else: + return list_reduce(list2,list1) + +#--------------------------------------------------------------------- +# +# +# +#--------------------------------------------------------------------- +def list_reduce(list1,list2): + value = 0 + for name in list1: + for name2 in list2: + if is_initial(name) and name[0] == name2[0]: + value = value + 0.25 + break + if is_initial(name2) and name2[0] == name[0]: + value = value + 0.25 + break + if name == name2: + value = value + 0.5 + break + if name[0] == name2[0] and name_compare(name,name2): + value = value + 0.25 + break + if value == 0: + return -1 + else: + return min(value,1) + +#--------------------------------------------------------------------- +# +# +# +#--------------------------------------------------------------------- +def place_match(p1,p2): + if p1 == p2: + return 1 + + if p1 == None: + name1 = "" + else: + name1 = p1.get_title() + + if p2 == None: + name2 = "" + else: + name2 = p2.get_title() + + if name1 == "" or name2 == "": + return 0 + if name1 == name2: + return 1 + + list1 = string.split(string.replace(name1,","," ")) + list2 = string.split(string.replace(name2,","," ")) + + value = 0 + for name in list1: + for name2 in list2: + if name == name2: + value = value + 0.5 + break + if name[0] == name2[0] and name_compare(name,name2): + value = value + 0.25 + break + if value == 0: + return -1 + else: + return min(value,1) + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +def is_initial(name): + if len(name) > 2: + return 0 + elif len(name) == 2: + if name[0] in string.uppercase and name[1] == '.': + return 1 + else: + return name[0] in string.uppercase + +#--------------------------------------------------------------------- +# +# +# +#--------------------------------------------------------------------- +def ancestors_of(p1,list): + if p1 == None: + return + list.append(p1) + f1 = p1.getMainParents() + if f1 != None: + ancestors_of(f1.getFather(),list) + ancestors_of(f1.getMother(),list) + +#--------------------------------------------------------------------- +# +# +# +#--------------------------------------------------------------------- +def name_of(p): + if not p: + return "" + return "%s (%s)" % (GrampsCfg.nameof(p),p.getId()) + +#------------------------------------------------------------------------- +# +# Merge Places +# +#------------------------------------------------------------------------- +class MergePlaces: + """ + Merges to places into a single place. Displays a dialog box that + allows the places to be combined into one. + """ + def __init__(self,database,place1,place2,update): + self.db = database + self.p1 = place1 + self.p2 = place2 + self.update = update + + self.glade = gtk.glade.XML(const.mergeFile,"merge_places") + self.top = self.glade.get_widget("merge_places") + self.glade.get_widget("title1_text").set_text(place1.get_title()) + self.glade.get_widget("title2_text").set_text(place2.get_title()) + self.t3 = self.glade.get_widget("title3_text") + self.t3.set_text(place1.get_title()) + + self.glade.signal_autoconnect({ + "destroy_passed_object" : Utils.destroy_passed_object, + "on_merge_places_clicked" : self.on_merge_places_clicked, + }) + self.top.show() + + def on_merge_places_clicked(self,obj): + """ + Performs the merge of the places when the merge button is clicked. + """ + t2active = self.glade.get_widget("title2").get_active() + + old_id = self.p1.getId() + + if t2active: + self.p1.set_title(self.p2.get_title()) + elif self.glade.get_widget("title3").get_active(): + self.p1.set_title(self.t3.get_text()) + + # Set longitude + if self.p1.get_longitude() == "" and self.p2.get_longitude() != "": + self.p1.set_longitude(self.p2.get_longitude()) + + # Set latitude + if self.p1.get_latitude() == "" and self.p2.get_latitude() != "": + self.p1.set_latitude(self.p2.get_latitude()) + + # Add URLs from P2 to P1 + for url in self.p2.getUrlList(): + self.p1.addUrl(url) + + # Copy photos from P2 to P1 + for photo in self.p2.getPhotoList(): + self.p1.addPhoto(photo) + + # Copy sources from P2 to P1 + for source in self.p2.getSourceRefList(): + self.p1.addSource(source) + + # Add notes from P2 to P1 + note = self.p2.getNote() + if note != "": + if self.p1.getNote() == "": + self.p1.setNote(note) + elif self.p1.getNote() != note: + self.p1.setNote("%s\n\n%s" % (self.p1.getNote(),note)) + + if t2active: + list = [self.p1.get_main_location()] + self.p1.get_alternate_locations() + self.p1.set_main_location(self.p2.get_main_location()) + for l in list: + if not l.is_empty(): + self.p1.add_alternate_locations(l) + else: + list = [self.p2.get_main_location()] + self.p2.get_alternate_locations() + for l in list: + if not l.is_empty(): + self.p1.add_alternate_locations(l) + + # loop through people, changing event references to P2 to P1 + for key in self.db.getPersonKeys(): + p = self.db.getPerson(key) + for event in [p.getBirth(), p.getDeath()] + p.getEventList(): + if event.getPlace() == self.p2: + event.setPlace(self.p1) + + # loop through families, changing event references to P2 to P1 + for f in self.db.getFamilyMap().values(): + for event in f.getEventList(): + if event.getPlace() == self.p2: + event.setPlace(self.p1) + + self.db.removePlace(self.p2.getId()) + self.db.buildPlaceDisplay(self.p1.getId(),old_id) + + self.update(self.p1.getId()) + Utils.modified() + Utils.destroy_passed_object(obj) + diff --git a/gramps2/src/NEWS b/gramps2/src/NEWS new file mode 100644 index 000000000..e69de29bb diff --git a/gramps2/src/NameEdit.py b/gramps2/src/NameEdit.py new file mode 100644 index 000000000..edb5b2baf --- /dev/null +++ b/gramps2/src/NameEdit.py @@ -0,0 +1,160 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +#------------------------------------------------------------------------- +# +# GTK/Gnome modules +# +#------------------------------------------------------------------------- +import gtk.glade + +#------------------------------------------------------------------------- +# +# gramps modules +# +#------------------------------------------------------------------------- +import const +import Utils +import AutoComp +import Sources +from RelLib import * + +from intl import gettext +_ = gettext + +#------------------------------------------------------------------------- +# +# NameEditor class +# +#------------------------------------------------------------------------- +class NameEditor: + + def __init__(self,parent,name): + self.parent = parent + self.name = name + self.top = gtk.glade.XML(const.dialogFile, "name_edit") + self.window = self.top.get_widget("name_edit") + self.given_field = self.top.get_widget("alt_given") + self.title_field = self.top.get_widget("alt_title") + self.surname_field = self.top.get_widget("alt_last") + self.suffix_field = self.top.get_widget("alt_suffix") + self.type_field = self.top.get_widget("name_type") + self.note_field = self.top.get_widget("alt_note") + self.slist = self.top.get_widget('slist') + slist = self.top.get_widget("alt_surname_list") + self.combo = AutoComp.AutoCombo(slist,self.parent.db.getSurnames()) + self.priv = self.top.get_widget("priv") + + types = const.NameTypesMap.keys() + types.sort() + self.type_field.set_popdown_strings(types) + self.typecomp = AutoComp.AutoEntry(self.type_field.entry,types) + + if self.name: + self.srcreflist = self.name.getSourceRefList() + else: + self.srcreflist = [] + + full_name = parent.person.getPrimaryName().getName() + + self.top.get_widget("altTitle").set_text( + _("Alternate Name Editor for %s") % full_name) + + self.sourcetab = Sources.SourceTab(self.srcreflist, self.parent, + self.top, self.slist, + self.top.get_widget('add_src'), + self.top.get_widget('del_src')) + + self.note_buffer = self.note_field.get_buffer() + + if name != None: + self.given_field.set_text(name.getFirstName()) + self.surname_field.set_text(name.getSurname()) + self.title_field.set_text(name.getTitle()) + self.suffix_field.set_text(name.getSuffix()) + self.type_field.entry.set_text(_(name.getType())) + self.priv.set_active(name.getPrivacy()) + self.note_buffer.set_text(name.getNote()) + + self.top.signal_autoconnect({ + "destroy_passed_object" : Utils.destroy_passed_object, + "on_name_edit_ok_clicked" : self.on_name_edit_ok_clicked, + }) + + def on_name_edit_ok_clicked(self,obj): + first = self.given_field.get_text() + last = self.surname_field.get_text() + title = self.title_field.get_text() + suffix = self.suffix_field.get_text() + note = self.note_buffer.get_text(self.note_buffer.get_start_iter(), + self.note_buffer.get_end_iter(),gtk.FALSE) + priv = self.priv.get_active() + + type = self.type_field.entry.get_text() + + if const.NameTypesMap.has_key(type): + type = const.NameTypesMap[type] + else: + type = "Also Known As" + + if self.name == None: + self.name = Name() + self.parent.nlist.append(self.name) + + self.name.setSourceRefList(self.srcreflist) + + self.update_name(first,last,suffix,title,type,note,priv) + self.parent.lists_changed = 1 + + self.parent.redraw_name_list() + Utils.destroy_passed_object(obj) + + def update_name(self,first,last,suffix,title,type,note,priv): + + if self.name.getFirstName() != first: + self.name.setFirstName(first) + self.parent.lists_changed = 1 + + if self.name.getSurname() != last: + self.name.setSurname(last) + self.parent.db.addSurname(last) + self.parent.lists_changed = 1 + + if self.name.getSuffix() != suffix: + self.name.setSuffix(suffix) + self.parent.lists_changed = 1 + + if self.name.getTitle() != title: + self.name.setTitle(title) + self.parent.lists_changed = 1 + + if self.name.getType() != type: + self.name.setType(type) + self.parent.lists_changed = 1 + + if self.name.getNote() != note: + self.name.setNote(note) + self.parent.lists_changed = 1 + + if self.name.getPrivacy() != priv: + self.name.setPrivacy(priv) + self.parent.lists_changed = 1 + + diff --git a/gramps2/src/NoteEdit.py b/gramps2/src/NoteEdit.py new file mode 100644 index 000000000..b77537e89 --- /dev/null +++ b/gramps2/src/NoteEdit.py @@ -0,0 +1,96 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +#------------------------------------------------------------------------- +# +# GTK/Gnome modules +# +#------------------------------------------------------------------------- +import gtk +import gnome.ui + +#------------------------------------------------------------------------- +# +# gramps modules +# +#------------------------------------------------------------------------- +import Utils +from intl import gettext +_ = gettext + +#------------------------------------------------------------------------- +# +# NoteEditor +# +#------------------------------------------------------------------------- +class NoteEditor: + """Displays a simple text editor that allows a person to edit a note""" + def __init__(self,data): + + self.data = data + self.draw() + + def draw(self): + """Displays the NoteEditor window""" + title = "%s - GRAMPS" % _("Edit Note") + + self.top = gtk.Dialog(title) + self.top.set_default_size(450,300) + + vbox = gtk.VBox() + self.top.vbox.pack_start(vbox,gtk.TRUE,gtk.TRUE,0) + vbox.pack_start(gtk.Label(_("Edit Note")), gtk.FALSE, gtk.FALSE, 10) + + vbox.pack_start(gtk.HSeparator(), gtk.FALSE, gtk.TRUE, 5) + self.entry = gtk.TextView() + self.entry.set_editable(gtk.TRUE) + self.entry.show() + + scroll = gtk.ScrolledWindow() + scroll.add(self.entry) + scroll.set_policy (gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) + scroll.show() + vbox.pack_start(scroll, gtk.TRUE, gtk.TRUE, 0) + + self.entry.get_buffer().set_text(self.data.getNote()) + + self.top.add_button(gtk.STOCK_OK,0) + self.top.add_button(gtk.STOCK_CANCEL,1) + self.top.show_all() + + if self.top.run() == 0: + self.on_save_note_clicked() + else: + self.cancel() + + def cancel(self): + """Closes the window without saving the note""" + self.top.destroy() + + def on_save_note_clicked(self): + """Saves the note and closes the window""" + buffer = self.entry.get_buffer() + text = buffer.get_text(buffer.get_start_iter(), + buffer.get_end_iter(),gtk.FALSE) + if text != self.data.getNote(): + self.data.setNote(text) + Utils.modified() + self.top.destroy() + diff --git a/gramps2/src/PaperMenu.py b/gramps2/src/PaperMenu.py new file mode 100644 index 000000000..d2bcf2a3c --- /dev/null +++ b/gramps2/src/PaperMenu.py @@ -0,0 +1,70 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +import TextDoc +import gtk +import GrampsCfg + +from intl import gettext +_ = gettext + +paper_sizes = [ + TextDoc.PaperStyle("Letter",27.94,21.59), + TextDoc.PaperStyle("Legal",35.56,21.59), + TextDoc.PaperStyle("A3",42.0,29.7), + TextDoc.PaperStyle("A4",29.7,21.0), + TextDoc.PaperStyle("A5",21.0,14.8), + TextDoc.PaperStyle("B4",35.3,25.0), + TextDoc.PaperStyle("B6",17.6,12.5), + TextDoc.PaperStyle("C4",32.4,22.9), + TextDoc.PaperStyle("C5",22.9,16.2), + TextDoc.PaperStyle("C6",16.2,11.4) + ] + +def make_paper_menu(main_menu): + + index = 0 + myMenu = gtk.Menu() + for paper in paper_sizes: + name = paper.get_name() + menuitem = gtk.MenuItem(name) + menuitem.set_data("i",paper) + menuitem.show() + myMenu.append(menuitem) + if name == GrampsCfg.paper_preference: + myMenu.set_active(index) + index = index + 1 + main_menu.set_menu(myMenu) + +def make_orientation_menu(main_menu): + + myMenu = gtk.Menu() + menuitem = gtk.MenuItem(_("Portrait")) + menuitem.set_data("i",TextDoc.PAPER_PORTRAIT) + menuitem.show() + myMenu.append(menuitem) + + menuitem = gtk.MenuItem(_("Landscape")) + menuitem.set_data("i",TextDoc.PAPER_LANDSCAPE) + menuitem.show() + myMenu.append(menuitem) + + main_menu.set_menu(myMenu) + diff --git a/gramps2/src/PedView.py b/gramps2/src/PedView.py new file mode 100644 index 000000000..1909268b4 --- /dev/null +++ b/gramps2/src/PedView.py @@ -0,0 +1,486 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2001 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +_PAD = 3 +_CANVASPAD = 3 +_PERSON = "p" + +import gtk +import gtk.gdk +import gnome.canvas +import pango + +import GrampsCfg + +from intl import gettext as _ + +class DispBox: + + def __init__(self,root,style,x,y,w,h,person,change): + shadow = _PAD + xpad = _PAD + + self.change = change + self.x = x + self.y = y + self.w = w + self.h = h + self.person = person + self.root = root + + self.name = person.getPrimaryName().getName() + self.exp = "%s\nb. %s\nd. %s" % (self.name,person.getBirth().getDate(), + person.getDeath().getDate()) + + self.group = self.root.add(gnome.canvas.CanvasGroup,x=x,y=y) + self.shadow = self.group.add(gnome.canvas.CanvasRect, + x1=shadow, + y1=shadow, + x2=w+shadow, + y2=h+shadow, + outline_color_gdk=style.dark[gtk.STATE_NORMAL], + fill_color_gdk=style.dark[gtk.STATE_NORMAL]) + + # draw the real box + self.bkgnd = self.group.add(gnome.canvas.CanvasRect, + x1=0, + y1=0, + x2=w, + y2=h, + outline_color_gdk=style.bg[gtk.STATE_NORMAL], + fill_color_gdk=style.white) + + font = gtk.gdk.font_from_description(style.font_desc) + self.textbox = self.group.add(gnome.canvas.CanvasText, + x=xpad, + y=h/2.0, + text=self.name, + fill_color_gdk=style.text[gtk.STATE_NORMAL], + font=font, anchor=gtk.ANCHOR_WEST) + self.group.connect('event',self.group_event) + self.group.set_data('p',person) + + def cleanup(self): + self.shadow.destroy() + self.bkgnd.destroy() + self.textbox.destroy() + self.group.destroy() + + def group_event(self,obj,event): + """Handle events over a drawn box. Doubleclick would edit, + shift doubleclick would change the active person, entering + the box expands it to display more information, leaving a + box returns it to the original size and information""" + + if event.type == gtk.gdk._2BUTTON_PRESS: + return 1 + elif event.type == gtk.gdk.ENTER_NOTIFY: + self.expand() + return 0 + elif event.type == gtk.gdk.LEAVE_NOTIFY: + self.shrink() + return 0 + return 0 + + def expand(self): + """Expand a box to include additional information""" + self.group.raise_to_top() + self.textbox.set(text=self.exp) + self.bkgnd.set(y1=-self.h,y2=self.h*2) + self.shadow.set(y1=-self.h+_PAD,y2=self.h*2+_PAD) + + def shrink(self): + """Expand a box to include additional information""" + self.group.raise_to_top() + self.textbox.set(text=self.name) + self.bkgnd.set(y1=0,y2=self.h) + self.shadow.set(y1=_PAD,y2=self.h+_PAD) + +#------------------------------------------------------------------------- +# +# PedigreeView +# +#------------------------------------------------------------------------- +class PedigreeView: + def __init__(self,canvas,update,status_bar,change_active,lp): + self.canvas = canvas + self.canvas_items = [] + self.boxes = [] + self.root = self.canvas.root() + self.active_person = None + self.x1 = 0 + self.x2 = 0 + self.y1 = 0 + self.y2 = 0 + self.update = update + self.sb = status_bar + self.change_active_person = change_active + self.load_person = lp + self.presel_descendants = [] + + def load_canvas(self, person): + """Redraws the pedigree view window, using the passed person + as the root person of the tree.""" + + for i in self.canvas_items: + i.destroy() + for i in self.boxes: + i.cleanup() + + if person is not self.active_person: + del self.presel_descendants[:] + self.active_person = person + if person == None: + return + + h = 0 + w = 0 + (x2,y2) = self.canvas.get_size() + + self.canvas.set_scroll_region(0,0,x2,y2) + + style = self.canvas.get_style() + font = gtk.gdk.font_from_description(style.font_desc) + + list = [None]*31 + self.find_tree(self.active_person,0,1,list) + + # determine the largest string width and height for calcuation + # of box sizes. + + a = pango.Layout(self.canvas.get_pango_context()) + + for t in list: + if t: + for n in [t[0].getPrimaryName().getName(), + "b. %s" % t[0].getBirth().getDate(), + "d. %s" % t[0].getDeath().getDate()]: + a.set_text(n,len(n)) + (w1,h1) = a.get_pixel_size() + h = max(h,h1) + w = max(w,w1) + cpad = 10 + w = w+_PAD + + cw = x2-(2*cpad)-10-h + ch = y2-(2*cpad) + + if 5*w < cw and 24*h < ch: + gen = 31 + xdiv = 5.0 + elif 4*w < cw and 12*h < ch: + gen = 15 + xdiv = 4.0 + else: + gen = 7 + xdiv = 3.0 + + xpts = self.build_x_coords(cw/xdiv,_CANVASPAD+h) + ypts = self.build_y_coords((ch-h)/32.0, h) + + self.canvas_items = [] + for family in self.active_person.getFamilyList(): + if len(family.getChildList()) > 0: + button,arrow = self.make_arrow_button(gtk.ARROW_LEFT, + self.on_show_child_menu) + item = self.root.add(gnome.canvas.CanvasWidget, widget=button, + x=_CANVASPAD, y=ypts[0]+(h/2.0), + height=h, width=h, + size_pixels=1, anchor=gtk.ANCHOR_WEST) + self.canvas_items = [item, button, arrow] + break + else: + self.canvas_items = [] + + if list[1]: + p = list[1] + self.add_parent_button(p[0],x2-_PAD,ypts[1],h) + + if list[2]: + p = list[2] + self.add_parent_button(p[0],x2-_PAD,ypts[2],h) + + gen_no = len(self.presel_descendants) + 1 + for i in range(int(xdiv)): + item = self.root.add(gnome.canvas.CanvasText, x=(cw*i/xdiv + cpad), y=h, + text=str(gen_no), + font=font, + anchor=gtk.ANCHOR_WEST) + self.canvas_items.append(item) + gen_no = gen_no + 1 + + for i in range(gen): + if list[i]: + if i < int(gen/2.0): + findex = (2*i)+1 + mindex = findex+1 + if list[findex]: + p = list[findex] + self.draw_canvas_line(xpts[i], ypts[i], xpts[findex], + ypts[findex], h, w, p[0], style, p[1]) + if list[mindex]: + p = list[mindex] + self.draw_canvas_line(xpts[i],ypts[i], xpts[mindex], + ypts[mindex], h, w, p[0], style, p[1]) + p = list[i] + box = DispBox(self.root,style,xpts[i],ypts[i],w,h,p[0], + self.change_active_person) + self.boxes.append(box) + self.change_active_person(person) + + def make_arrow_button(self,direction,function): + """Make a button containing an arrow with the attached callback""" + + arrow = gtk.Arrow(direction,gtk.SHADOW_NONE) + button = gtk.Button() + button.add(arrow) + button.connect("clicked",function) + arrow.show() + button.show() + return (button, arrow) + + def on_show_child_menu(self,obj): + """User clicked button to move to child of active person""" + + if self.presel_descendants: + # Go to a previously selected child. + person = self.presel_descendants.pop(-1) + self.active_person = person + self.load_canvas(person) + elif self.active_person: + # Build and display the menu attached to the left pointing arrow + # button. The menu consists of the children of the current root + # person of the tree. Attach a child to each menu item. + myMenu = gtk.Menu() + for family in self.active_person.getFamilyList(): + for child in family.getChildList(): + menuitem = gtk.MenuItem(GrampsCfg.nameof(child)) + myMenu.append(menuitem) + menuitem.set_data(_PERSON,child) + menuitem.connect("activate",self.on_childmenu_changed) + menuitem.show() + myMenu.popup(None,None,None,0,0) + return 1 + + def on_childmenu_changed(self,obj): + """Callback for the pulldown menu selection, changing to the person + attached with menu item.""" + + person = obj.get_data(_PERSON) + if person: + self.load_canvas(person) + return 1 + + def add_parent_button(self,parent,x,y,h): + """Add a button with a right pointing button on the main group at + the specified location. Attach the passed parent and the callback + to the button.""" + + button,arrow = self.make_arrow_button(gtk.ARROW_RIGHT,self.change_to_parent) + button.set_data(_PERSON,parent) + + item = self.root.add(gnome.canvas.CanvasWidget, widget=button, x=x, y=y+(h/2), + height=h, width=h, size_pixels=1, + anchor=gtk.ANCHOR_EAST) + self.canvas_items.append(arrow) + self.canvas_items.append(item) + self.canvas_items.append(button) + + def change_to_parent(self,obj): + """Callback to right pointing arrow button. Gets the person + attached to the button and change the root person to that + person, redrawing the view.""" + person = obj.get_data(_PERSON) + if self.active_person: + self.presel_descendants.append(self.active_person) + self.active_person = person + self.load_canvas(person) + + def draw_canvas_line(self,x1,y1,x2,y2,h,w,data,style,ls): + """Draw an two segment line between the x,y point pairs. Attach + a event callback and data to the line.""" + + startx = x1+(w/2.0) + pts = [startx,y1, startx,y2+(h/2.0), x2,y2+(h/2.0)] + item = self.root.add(gnome.canvas.CanvasLine, width_pixels=2, + points=pts, line_style=ls, + fill_color_gdk=style.black) + item.set_data(_PERSON,data) + item.connect("event",self.line_event) + self.canvas_items.append(item) + + def build_x_coords(self,x,cpad): + """Build the array of x coordinates for the possible positions + on the pedegree view.""" + return [cpad] + [x+cpad]*2 + [x*2+cpad]*4 + [x*3+cpad]*8 + [x*4+cpad]*16 + + def build_y_coords(self, y, top_pad): + """Build the array of y coordinates for the possible positions + on the pedegree view.""" + res = [ y*16.0, y*8.0, y*24.0, y*4.0, y*12.0, y*20.0, y*28.0, y*2.0, + y*6.0, y*10.0, y*14.0, y*18.0, y*22.0, y*26.0, y*30.0, y, + y*3.0, y*5.0, y*7.0, y*9.0, y*11.0, y*13.0, y*15.0, y*17.0, + y*19.0, y*21.0, y*23.0, y*25.0, y*27.0, y*29.0, y*31.0 ] + return map(lambda coord, top_pad=top_pad: coord + top_pad, res) + + def add_box(self, x, y, bwidth, bheight, person, style): + """Draw a box of the specified size at the specified location. + The box consists of a shadow box for effect, the real box + that contains the information, and the basic text + information. For convience, the all the subelements are + grouped into a GNOME canvas group.""" + + shadow = _PAD + xpad = _PAD + + name = person.getPrimaryName().getName() + group = self.root.add(gnome.canvas.CanvasGroup,x=x,y=y) + self.canvas_items.append(group) + + # draw the shadow box + item = group.add(gnome.canvas.CanvasRect, x1=shadow, y1=shadow, + x2=bwidth+shadow, y2=bheight+shadow, + outline_color_gdk=style.dark[gtk.STATE_NORMAL], + fill_color_gdk=style.dark[gtk.STATE_NORMAL]) + self.canvas_items.append(item) + + # draw the real box + item = group.add(gnome.canvas.CanvasRect, x1=0, y1=0, x2=bwidth, y2=bheight, + outline_color_gdk=style.bg[gtk.STATE_NORMAL], + fill_color_gdk=style.white) + self.canvas_items.append(item) + + # Write the text + + font = gtk.gdk.font_from_description(style.font_desc) + + item = group.add(gnome.canvas.CanvasText, x=xpad, y=bheight/2.0, text=name, + fill_color_gdk=style.text[gtk.STATE_NORMAL], + font=font, anchor=gtk.ANCHOR_WEST) + self.canvas_items.append(item) + group.connect('event',self.box_event) + group.set_data('p',person) + + def box_event(self,obj,event): + """Handle events over a drawn box. Doubleclick would edit, + shift doubleclick would change the active person, entering + the box expands it to display more information, leaving a + box returns it to the original size and information""" + + if event.type == gtk.gdk._2BUTTON_PRESS: +# if event.button == 1: +# person = obj.get_data(_PERSON) +# if (event.state & gtk.gdk.SHIFT_MASK) or (event.state & gtk.gdk.CONTROL_MASK): +# self.change_active_person(person) +# del self.presel_descendants[:] +# self.load_canvas(person) +# else: +# self.load_person(person) + return 1 + elif event.type == gtk.gdk.ENTER_NOTIFY: + self.expand_box(obj) + return 0 + elif event.type == gtk.gdk.LEAVE_NOTIFY: + self.shrink_box(obj) + return 0 + return 0 + + def shrink_box(self,obj): + """Shrink an exanded box back down to normal size""" + box = self.group_map[obj][1] + x,y,w,h = box.get_bounds() + box.set(x1=x,y1=y,x2=w,y2=h/3) + box2 = self.group_map[obj][2] + x,y,w,h1 = box2.get_bounds() + person = obj.get_data('p') + n = person.getPrimaryName().getName() + box2.set(text=n) + self.update() + self.canvas.update_now() + + def expand_box(self,obj): + """Expand a box to include additional information""" + obj.raise_to_top() + x,y,w,h = box.get_bounds() + box.set(x1=x,y1=y,x2=w,y2=h*3) + box2 = self.group_map[obj][0] + x,y,w,h1 = box2.get_bounds() + box2.set(x1=x,y1=y,x2=w,y2=(3*h)+_PAD) + person = obj.get_data('p') + color = self.canvas.get_style().text[gtk.STATE_NORMAL] + n = "%s\nb. %s.\nd. %s" % (person.getPrimaryName().getName(), + person.getBirth().getDate(), + person.getDeath().getDate()) + box2 = self.group_map[obj][2] + box2.set(text=n) + box2.show() + msg = _("Doubleclick to edit, Shift-Doubleclick to make the active person") + self.sb.set_status(msg) + + def line_event(self,obj,event): + """Catch X events over a line and respond to the ones we care about""" + + person = obj.get_data(_PERSON) + style = self.canvas.get_style() + + if event.type == gtk.gdk._2BUTTON_PRESS: + if event.button == 1 and event.type == gtk.gdk._2BUTTON_PRESS: + self.load_canvas(person) + elif event.type == gtk.gdk.ENTER_NOTIFY: + obj.set(fill_color_gdk=style.bg[gtk.STATE_SELECTED], + width_pixels=4) + name = GrampsCfg.nameof(person) + msg = _("Double clicking will make %s the active person") % name + self.sb.set_status(msg) + elif event.type == gtk.gdk.LEAVE_NOTIFY: + obj.set(fill_color_gdk=style.black, width_pixels=2) + self.update() + + def on_canvas1_event(self,obj,event): + """Handle resize events over the canvas, redrawing if the size changes""" + + if event.type == gtk.gdk.EXPOSE: + x1,y1,x2,y2 = self.canvas.get_allocation() + if self.x1 != x1 or self.x2 != x2 or \ + self.y1 != y1 or self.y2 != y2: + self.x1 = x1; self.x2 = x2 + self.y1 = y1; self.y2 = y2 + self.load_canvas(self.active_person) + return 0 + + def find_tree(self,person,index,depth,list,val=0): + """Recursively build a list of ancestors""" + + if depth > 5 or person == None: + return + (family,m,f) = person.getMainParentsRel() + if family: + mrel = (m != "Birth") + frel = (f != "Birth") + + list[index] = (person,val) + if family != None: + father = family.getFather() + if father != None: + self.find_tree(father,(2*index)+1,depth+1,list,frel) + mother = family.getMother() + if mother != None: + self.find_tree(mother,(2*index)+2,depth+1,list,mrel) + diff --git a/gramps2/src/PlaceView.py b/gramps2/src/PlaceView.py new file mode 100644 index 000000000..30c165f9f --- /dev/null +++ b/gramps2/src/PlaceView.py @@ -0,0 +1,225 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2001 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +""" +Handles the place view for GRAMPS. +""" + +#------------------------------------------------------------------------- +# +# GTK modules +# +#------------------------------------------------------------------------- +import gobject +import gtk +import gtk.gdk + +#------------------------------------------------------------------------- +# +# Gramps modules +# +#------------------------------------------------------------------------- +from RelLib import * +from QuestionDialog import QuestionDialog + +import EditPlace +import Utils +import GrampsCfg + +from intl import gettext as _ + +_column_headers = [ + (_('Place Name'),7,200), + (_('ID'),1,50), + (_('Church Parish'),8,75), + (_('City'),9,75), + (_('County'),10,75), + (_('State'),11,75), + (_('Country'),12,75), + (_(''),7,0), + (_(''),8,0), + (_(''),9,0), + (_(''),10,0), + (_(''),11,0), + (_(''),12,0)] + +#------------------------------------------------------------------------- +# +# PlaceView class +# +#------------------------------------------------------------------------- +class PlaceView: + + def __init__(self,db,glade,update): + self.db = db + self.glade = glade + self.list = glade.get_widget("place_list") + self.update_display= update + + self.active = None + + self.id2col = {} + self.selection = self.list.get_selection() + colno = 0 + for title in _column_headers: + renderer = gtk.CellRendererText () + column = gtk.TreeViewColumn (title[0], renderer, text=colno) + colno = colno + 1 + column.set_clickable (gtk.TRUE) + if title[0] == '': + column.set_visible(gtk.FALSE) + else: + column.set_resizable(gtk.TRUE) + column.set_visible(gtk.TRUE) + column.set_sort_column_id(title[1]) + column.set_min_width(title[2]) + self.list.append_column(column) + + self.list.set_search_column(0) + self.model = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, + gobject.TYPE_STRING, gobject.TYPE_STRING, + gobject.TYPE_STRING, gobject.TYPE_STRING, + gobject.TYPE_STRING, gobject.TYPE_STRING, + gobject.TYPE_STRING, gobject.TYPE_STRING, + gobject.TYPE_STRING, gobject.TYPE_STRING, + gobject.TYPE_STRING) + self.list.set_model(self.model) + self.list.get_column(0).clicked() + + def change_db(self,db): + self.db = db + + def load_places(self,id=None): + """Rebuilds the entire place view. This can be very time consuming + on large databases, and should only be called when absolutely + necessary""" + + self.model.clear() + self.id2col = {} + + for key in self.db.getPlaceKeys(): + val = self.db.getPlaceDisplay(key) + + iter = self.model.append() + self.id2col[key] = iter + self.model.set(iter, + 0, val[0], 1, val[1], 2, val[2], 3, val[3], + 4, val[4], 5, val[5], 6, val[6], 7, val[7], + 8, val[8], 9, val[9], 10, val[10], 11, val[11], + 12, val[12] + ) + self.list.connect('button-press-event',self.button_press) + + def merge(self): + if len(self.place_list.selection) != 2: + msg = _("Exactly two places must be selected to perform a merge") + gnome.ui.GnomeErrorDialog(msg) + else: + import MergeData + p1 = self.place_list.get_row_data(self.place_list.selection[0]) + p2 = self.place_list.get_row_data(self.place_list.selection[1]) + p1 = self.db.getPlace(p1) + p2 = self.db.getPlace(p2) + MergeData.MergePlaces(self.db,p1,p2,self.load_places) + + def button_press(self,obj,event): + if event.type == gtk.gdk._2BUTTON_PRESS and event.button == 1: + store,iter = self.selection.get_selected() + id = store.get_value(iter,1) + + place = self.db.getPlace(id) + EditPlace.EditPlace(self,place,self.update_display) + return 1 + + def insert_place(self,place): + self.place_list.append(place.getDisplayInfo()) + self.place_list.set_row_data(self.place_list.rows-1,place.getId()) + + def new_place_after_edit(self,place): + self.db.addPlace(place) + self.update(0) + + def update_display(self,place): + self.db.buildPlaceDisplay(place.getId()) + self.update(0) + + def on_add_place_clicked(self,obj): + EditPlace.EditPlace(self,Place(),self.new_place_after_edit) + + def moveto(self,row): + self.place_list.unselect_all() + self.place_list.select_row(row,0) + self.place_list.moveto(row) + + def on_delete_clicked(self,obj): + if len(obj.selection) == 0: + return + elif len(obj.selection) > 1: + msg = _("Currently, you can only delete one place at a time") + gnome.ui.GnomeErrorDialog(msg) + return + else: + index = obj.selection[0] + + used = 0 + place = self.db.getPlace(obj.get_row_data(index)) + for key in self.db.getPersonKeys(): + p = self.db.getPerson(key) + event_list = [p.getBirth(), p.getDeath()] + p.getEventList() + if p.getLdsBaptism(): + event_list.append(p.getLdsBaptism()) + if p.getLdsEndowment(): + event_list.append(p.getLdsEndowment()) + if p.getLdsSeal(): + event_list.append(p.getLdsSeal()) + for event in event_list: + if event.getPlace() == place: + used = 1 + + for f in self.db.getFamilyMap().values(): + event_list = f.getEventList() + if f.getLdsSeal(): + event_list.append(f.getLdsSeal()) + for event in event_list: + if event.getPlace() == place: + used = 1 + + if used == 1: + ans = EditPlace.DeletePlaceQuery(place,self.db,self.update_display) + QuestionDialog(_('Delete Place'), + _("This place is currently being used. Delete anyway?"), + _('Delete Place'),ans.query_response, + _('Keep Place')) + else: + obj.remove(index) + self.db.removePlace(place.getId()) + Utils.modified() + + def on_edit_clicked(self,obj): + """Display the selected places in the EditPlace display""" + list_store, iter = self.selection.get_selected() + if iter: + id = list_store.get_value(iter,1) + place = self.db.getPlace(id) + EditPlace.EditPlace(self, place, self.update_display) + + + + diff --git a/gramps2/src/Plugins.py b/gramps2/src/Plugins.py new file mode 100644 index 000000000..e86f3df56 --- /dev/null +++ b/gramps2/src/Plugins.py @@ -0,0 +1,648 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +""" +The core of the GRAMPS plugin system. This module provides tasks to load +plugins from specfied directories, build menus for the different categories, +and provide dialog to select and execute plugins. + +Plugins are divided into several categories. This are: reports, tools, +filters, importer, exporters, and document generators. +""" + +#------------------------------------------------------------------------- +# +# GTK libraries +# +#------------------------------------------------------------------------- +#import GdkImlib +import gtk +import gtk.glade + +#------------------------------------------------------------------------- +# +# Standard Python modules +# +#------------------------------------------------------------------------- +import traceback +import os +import sys +from re import compile + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +import const +import Utils +import GrampsCfg +from intl import gettext +_ = gettext + +#------------------------------------------------------------------------- +# +# Global lists +# +#------------------------------------------------------------------------- +_reports = [] +_tools = [] +_imports = [] +_exports = [] +_success = [] +_failed = [] +_expect = [] +_attempt = [] +_loaddir = [] +_textdoc = [] +_drawdoc = [] +_failmsg = [] + +_unavailable = _("No description was provided"), +#------------------------------------------------------------------------- +# +# Exception Strings +# +#------------------------------------------------------------------------- +MissingLibraries = _("Missing Libraries") + +#------------------------------------------------------------------------- +# +# Constants +# +#------------------------------------------------------------------------- +DOCSTRING = "d" +IMAGE = "i" +TASK = "f" +TITLE = "t" +STATUS = "s" + +#------------------------------------------------------------------------- +# +# PluginDialog interface class +# +#------------------------------------------------------------------------- +class PluginDialog: + """Displays the dialog box that allows the user to select the + report that is desired.""" + + def __init__(self,db,active,list,msg): + """Display the dialog box, and build up the list of available + reports. This is used to build the selection tree on the left + hand side of the dailog box.""" + + self.db = db + self.active = active + self.update = None + + self.dialog = gtk.glade.XML(const.pluginsFile,"report") + self.dialog.signal_autoconnect({ + "on_report_apply_clicked" : self.on_apply_clicked, + "on_report_ok_clicked" : self.on_apply_clicked, + "on_tree_select_row" : self.on_node_selected, + "destroy_passed_object" : Utils.destroy_passed_object + }) + + self.tree = self.dialog.get_widget("tree") + self.top = self.dialog.get_widget("report") + self.img = self.dialog.get_widget("image") + self.description = self.dialog.get_widget("description") + self.status = self.dialog.get_widget("report_status") + self.label = self.dialog.get_widget("report_label") + self.title = self.dialog.get_widget("title") + + self.run_tool = None + self.build_tree(list) + self.title.set_text(msg) + self.top.set_title("%s - GRAMPS" % msg) + + def on_apply_clicked(self,obj): + """Execute the selected report""" + + Utils.destroy_passed_object(obj) + if self.run_tool: + if self.update: + self.run_tool(self.db,self.active,self.update) + else: + self.run_tool(self.db,self.active) + + def on_node_selected(self,obj,node,other): + """Updates the informational display on the right hand side of + the dialog box with the description of the selected report""" + + data = self.tree.node_get_row_data(node) + if not data: + return + task = data[1] + title = data[0] + doc = data[2] + xpm = data[3] + status = data[4] + + #image = GdkImlib.create_image_from_xpm(xpm) + self.description.set_text(doc) + self.status.set_text(": %s" % status) + self.label.show() + #self.img.load_imlib(image) + self.title.set_text(title) + + self.dialog.get_widget("title").set_text(title) + self.run_tool = task + + def build_tree(self,list): + """Populates a GtkTree with each menu item assocated with a entry + in the lists. The list must consist of a tuples with the following + format: + + (task_to_call, category, report name, description, image, status) + + Items in the same category are grouped under the same submen. The + task_to_call is bound to the 'select' callback of the menu entry.""" + + # build the tree items and group together based on the category name + item_hash = {} + for report in list: + t = (report[2],report[0],report[3],report[4],report[5]) + if item_hash.has_key(report[1]): + item_hash[report[1]].append(t) + else: + item_hash[report[1]] = [t] + + # add a submenu for each category, and populate it with the + # GtkTreeItems that are associated with it. + key_list = item_hash.keys() + key_list.sort() + prev = None + for key in key_list: + data = item_hash[key] + node = self.tree.insert_node(None,prev,[key],is_leaf=0,expanded=1) + self.tree.node_set_row_data(node,0) + next = None + data.sort() + data.reverse() + for item in data: + next = self.tree.insert_node(node,next,[item[0]],is_leaf=1,expanded=1) + self.tree.node_set_row_data(next,item) + +#------------------------------------------------------------------------- +# +# ReportPlugins interface class +# +#------------------------------------------------------------------------- +class ReportPlugins(PluginDialog): + """Displays the dialog box that allows the user to select the + report that is desired.""" + + def __init__(self,db,active): + """Display the dialog box, and build up the list of available + reports. This is used to build the selection tree on the left + hand side of the dailog box.""" + PluginDialog.__init__(self,db,active,_reports,_("Report Selection")) + +#------------------------------------------------------------------------- +# +# ToolPlugins interface class +# +#------------------------------------------------------------------------- +class ToolPlugins(PluginDialog): + """Displays the dialog box that allows the user to select the tool + that is desired.""" + + def __init__(self,db,active,update): + """Display the dialog box, and build up the list of available + reports. This is used to build the selection tree on the left + hand side of the dailog box.""" + + PluginDialog.__init__(self,db,active,_tools,_("Tool Selection")) + self.update = update + +#------------------------------------------------------------------------- +# +# PluginStatus +# +#------------------------------------------------------------------------- +class PluginStatus: + """Displays a dialog showing the status of loaded plugins""" + + def __init__(self): + import cStringIO + + self.glade = gtk.glade.XML(const.pluginsFile,"plugstat") + self.top = self.glade.get_widget("plugstat") + window = self.glade.get_widget("text") + self.glade.signal_autoconnect({ + 'on_close_clicked' : self.close + }) + + info = cStringIO.StringIO() + info.write(_("The following modules could not be loaded:")) + info.write("\n\n") + + for (file,msg) in _expect: + info.write("%s: %s\n\n" % (file,msg)) + + for (file,msgs) in _failmsg: + error = str(msgs[0]) + if error[0:11] == "exceptions.": + error = error[11:] + info.write("%s: %s\n" % (file,error) ) + traceback.print_exception(msgs[0],msgs[1],msgs[2],None,info) + info.write('\n') + info.seek(0) + window.get_buffer().set_text(info.read()) + + def close(self,obj): + self.top.destroy() + +#------------------------------------------------------------------------- +# +# load_plugins +# +#------------------------------------------------------------------------- +def load_plugins(direct): + """Searches the specified directory, and attempts to load any python + modules that it finds, adding name to the _attempts list. If the module + successfully loads, it is added to the _success list. Each plugin is + responsible for registering itself in the correct manner. No attempt + is done in this routine to register the tasks.""" + + global _success,_failed,_attempt,_loaddir + + # if the directory does not exist, do nothing + if not os.path.isdir(direct): + return + + # if the path has not already been loaded, save it in the _loaddir + # list for use on reloading + + if direct not in _loaddir: + _loaddir.append(direct) + + # add the directory to the python search path + sys.path.append(direct) + + pymod = compile(r"^(.*)\.py$") + + # loop through each file in the directory, looking for files that + # have a .py extention, and attempt to load the file. If it succeeds, + # add it to the _success list. If it fails, add it to the _failure + # list + + for file in os.listdir(direct): + name = os.path.split(file) + match = pymod.match(name[1]) + if not match: + continue + _attempt.append(file) + plugin = match.groups()[0] + try: + a = __import__(plugin) + _success.append(a) + except MissingLibraries,msg: + _expect.append((file,msg)) + except: + _failmsg.append((file,sys.exc_info())) + +#------------------------------------------------------------------------- +# +# reload_plugins +# +#------------------------------------------------------------------------- +def reload_plugins(obj): + """Treated as a callback, causes all plugins to get reloaded. This is + useful when writing and debugging a plugin""" + + pymod = compile(r"^(.*)\.py$") + + # attempt to reload all plugins that have succeeded + # in the past + for plugin in _success: + try: + reload(plugin) + except: + _failmsg.append((plugin,sys.exc_info())) + + # attempt to load the plugins that have failed in the past + + for plugin in _failed: + try: + __import__(plugin) + except: + _failmsg.append((plugin,sys.exc_info())) + + # attempt to load any new files found + for dir in _loaddir: + for file in os.listdir(dir): + name = os.path.split(file) + match = pymod.match(name[1]) + if not match: + continue + if file in _attempt: + return + _attempt.append(file) + plugin = match.groups()[0] + try: + a = __import__(plugin) + _success.append(a) + except: + _failmsg.append((file,sys.exc_info())) + +#------------------------------------------------------------------------- +# +# Plugin registering +# +#------------------------------------------------------------------------- +def register_export(task, name): + """Register an export filter, taking the task and name""" + _exports.append((task, name)) + +def register_import(task, name): + """Register an import filter, taking the task and name""" + _imports.append((task, name)) + +def register_report(task, name, + category=_("Uncategorized"), + description=_unavailable, + xpm=None, + status=_("Unknown")): + """Register a report with the plugin system""" + + if xpm == None: + xpm = no_image() + _reports.append((task, category, name, description, xpm, status)) + +def register_tool(task, name, + category=_("Uncategorized"), + description=_unavailable, + xpm=None, + status=_("Unknown")): + """Register a tool with the plugin system""" + if xpm == None: + xpm = no_image() + _tools.append((task, category, name, description, xpm, status)) + + +def register_text_doc(name,classref, table, paper, style): + """Register a text document generator""" + for n in _textdoc: + if n[0] == name: + return + _textdoc.append((name,classref,table,paper,style)) + +def register_draw_doc(name,classref): + """Register a drawing document generator""" + for n in _drawdoc: + if n[0] == name: + return + _drawdoc.append((name,classref)) + +#------------------------------------------------------------------------- +# +# Image attributes +# +#------------------------------------------------------------------------- +_image_attributes = [] +def register_image_attribute(name): + if name not in _image_attributes: + _image_attributes.append(name) + +def get_image_attributes(): + return _image_attributes + +#------------------------------------------------------------------------- +# +# Building pulldown menus +# +#------------------------------------------------------------------------- +def build_menu(top_menu,list,callback): + report_menu = gtk.Menu() + report_menu.show() + + hash = {} + for report in list: + if hash.has_key(report[1]): + hash[report[1]].append((report[2],report[0])) + else: + hash[report[1]] = [(report[2],report[0])] + + catlist = hash.keys() + catlist.sort() + for key in catlist: + entry = gtk.MenuItem(key) + entry.show() + report_menu.append(entry) + submenu = gtk.Menu() + submenu.show() + entry.set_submenu(submenu) + list = hash[key] + list.sort() + for name in list: + subentry = gtk.MenuItem(name[0]) + subentry.show() + subentry.connect("activate",callback,name[1]) + submenu.append(subentry) + top_menu.set_submenu(report_menu) + +#------------------------------------------------------------------------- +# +# build_report_menu +# +#------------------------------------------------------------------------- +def build_report_menu(top_menu,callback): + build_menu(top_menu,_reports,callback) + +#------------------------------------------------------------------------- +# +# build_tools_menu +# +#------------------------------------------------------------------------- +def build_tools_menu(top_menu,callback): + build_menu(top_menu,_tools,callback) + +#------------------------------------------------------------------------- +# +# build_export_menu +# +#------------------------------------------------------------------------- +def build_export_menu(top_menu,callback): + myMenu = gtk.Menu() + + for report in _exports: + item = gtk.MenuItem(report[1]) + item.connect("activate", callback ,report[0]) + item.show() + myMenu.append(item) + top_menu.set_submenu(myMenu) + +#------------------------------------------------------------------------- +# +# build_import_menu +# +#------------------------------------------------------------------------- +def build_import_menu(top_menu,callback): + myMenu = gtk.Menu() + + for report in _imports: + item = gtk.MenuItem(report[1]) + item.connect("activate", callback ,report[0]) + item.show() + myMenu.append(item) + top_menu.set_submenu(myMenu) + +#------------------------------------------------------------------------- +# +# get_text_doc_menu +# +#------------------------------------------------------------------------- +def get_text_doc_menu(main_menu,tables,callback,obj=None): + + index = 0 + myMenu = gtk.Menu() + _textdoc.sort() + for item in _textdoc: + if tables and item[2] == 0: + continue + name = item[0] + menuitem = gtk.MenuItem(name) + menuitem.set_data("name",item[1]) + menuitem.set_data("styles",item[4]) + menuitem.set_data("paper",item[3]) + menuitem.set_data("obj",obj) + if callback: + menuitem.connect("activate",callback) + menuitem.show() + myMenu.append(menuitem) + if name == GrampsCfg.output_preference: + myMenu.set_active(index) + callback(menuitem) + index = index + 1 + main_menu.set_menu(myMenu) + +#------------------------------------------------------------------------- +# +# get_text_doc_list +# +#------------------------------------------------------------------------- +def get_text_doc_list(): + l = [] + _textdoc.sort() + for item in _textdoc: + l.append(item[0]) + return l + +#------------------------------------------------------------------------- +# +# get_draw_doc_list +# +#------------------------------------------------------------------------- +def get_draw_doc_list(): + + l = [] + _drawdoc.sort() + for item in _drawdoc: + l.append(item[0]) + return l + +#------------------------------------------------------------------------- +# +# get_draw_doc_menu +# +#------------------------------------------------------------------------- +def get_draw_doc_menu(main_menu,callback=None,obj=None): + + index = 0 + myMenu = gtk.Menu() + for (name,classref) in _drawdoc: + menuitem = gtk.MenuItem(name) + menuitem.set_data("name",classref) + menuitem.set_data("obj",obj) + if callback: + menuitem.connect("activate",callback) + menuitem.show() + myMenu.append(menuitem) + if name == GrampsCfg.goutput_preference: + myMenu.set_active(index) + if callback: + callback(menuitem) + index = index + 1 + main_menu.set_menu(myMenu) + +#------------------------------------------------------------------------- +# +# no_image +# +#------------------------------------------------------------------------- +def no_image(): + """Returns XPM data for basic 48x48 icon""" + return [ + "48 48 5 1", + " c None", + ". c #999999", + "+ c #FFFFCC", + "@ c #000000", + "# c #CCCCCC", + " ", + " ", + " ", + " ", + " ", + " ", + " .......... ", + " .++++++++. ", + " .++++++++. ", + " @@@.++++++++. ", + " @##.++++++++. ", + " @# .++++++++. ", + " .......... @# .......... ", + " .++++++++. @# ", + " .++++++++. @# ", + " @@@.++++++++.@@@@# ", + " @##.++++++++.###@# .......... ", + " @# .++++++++. @# .++++++++. ", + " @# .......... @# .++++++++. ", + " @# @@@.++++++++. ", + " @# ##.++++++++. ", + " @# .++++++++. ", + " .......... @# .......... ", + " .++++++++. @# ", + " .++++++++. @# ", + " .++++++++.@@@@# ", + " .++++++++.###@# ", + " .++++++++. @# .......... ", + " .......... @# .++++++++. ", + " @# .++++++++. ", + " @# @@@.++++++++. ", + " @# @##.++++++++. ", + " @# .......... @# .++++++++. ", + " @# .++++++++. @# .......... ", + " @# .++++++++. @# ", + " @@@.++++++++.@@@@# ", + " ##.++++++++.###@# ", + " .++++++++. @# .......... ", + " .......... @# .++++++++. ", + " @# .++++++++. ", + " @@@.++++++++. ", + " ##.++++++++. ", + " .++++++++. ", + " .......... ", + " ", + " ", + " ", + " "] diff --git a/gramps2/src/QuestionDialog.py b/gramps2/src/QuestionDialog.py new file mode 100644 index 000000000..3e9a408dc --- /dev/null +++ b/gramps2/src/QuestionDialog.py @@ -0,0 +1,61 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +import gnome.ui +import gtk + +class QuestionDialog: + def __init__(self,title,msg,blabel1,task1,blabel2,task2=None): + title = '%s - GRAMPS' % title + + self.top = gtk.Dialog() + self.top.set_title(title) + label = gtk.Label(msg) + label.show() + hbox = gtk.HBox() + image = gtk.Image() + image.set_from_stock(gtk.STOCK_DIALOG_QUESTION,gtk.ICON_SIZE_DIALOG) + hbox.set_spacing(10) + hbox.pack_start(image) + hbox.add(label) + self.top.vbox.pack_start(hbox) + self.top.set_default_size(300,150) + self.task2 = task2 + self.task1 = task1 + self.top.add_button(gtk.STOCK_YES,0) + self.top.add_button(gtk.STOCK_NO,1) + self.top.set_response_sensitive(1,gtk.TRUE) + self.top.set_response_sensitive(0,gtk.TRUE) + self.top.show_all() + if self.top.run(): + self.my_task2() + else: + self.my_task1() + + def my_task1(self): + if self.task1: + self.task1() + self.top.destroy() + + def my_task2(self): + if self.task2: + self.task2() + self.top.destroy() + diff --git a/gramps2/src/QuickAdd.py b/gramps2/src/QuickAdd.py new file mode 100644 index 000000000..9a5fa18e5 --- /dev/null +++ b/gramps2/src/QuickAdd.py @@ -0,0 +1,60 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +import gtk.glade + +import Utils +import AutoComp +import const +import RelLib + +class QuickAdd: + def __init__(self,db,sex,callback): + self.db = db + self.callback = callback + + self.xml = gtk.glade.XML(const.gladeFile,"addperson") + self.xml.get_widget(sex).set_active(1) + self.xml.signal_autoconnect({ + "on_addfather_close": self.close, + "destroy_passed_object" : Utils.destroy_passed_object + }) + + self.window = self.xml.get_widget("addperson") + self.c = AutoComp.AutoCombo(self.xml.get_widget("surnameCombo"), + self.db.getSurnames()) + + def close(self,obj): + surname = self.xml.get_widget("surname").get_text() + given = self.xml.get_widget("given").get_text() + person = RelLib.Person() + name = person.getPrimaryName() + name.setSurname(surname) + name.setFirstName(given) + self.db.addPerson(person) + if self.xml.get_widget("male").get_active(): + person.setGender(RelLib.Person.male) + self.father = person + else: + person.setGender(RelLib.Person.female) + self.mother = person + Utils.modified() + Utils.destroy_passed_object(self.window) + self.callback(person) diff --git a/gramps2/src/README b/gramps2/src/README new file mode 100644 index 000000000..e69de29bb diff --git a/gramps2/src/ReadXML.py b/gramps2/src/ReadXML.py new file mode 100644 index 000000000..bc6932f73 --- /dev/null +++ b/gramps2/src/ReadXML.py @@ -0,0 +1,199 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +#------------------------------------------------------------------------- +# +# Standard Python Modules +# +#------------------------------------------------------------------------- +import string +import os + +#------------------------------------------------------------------------- +# +# Gnome/GTK +# +#------------------------------------------------------------------------- +import gnome.ui #import GnomeErrorDialog + +#------------------------------------------------------------------------- +# +# Gramps Modules +# +#------------------------------------------------------------------------- +from RelLib import * +from GrampsParser import GrampsParser, GrampsImportParser +from intl import gettext +_ = gettext + +#------------------------------------------------------------------------- +# +# Try to detect the presence of gzip +# +#------------------------------------------------------------------------- +try: + import gzip + gzip_ok = 1 +except: + gzip_ok = 0 + +#------------------------------------------------------------------------- +# +# Initialization function for the module. Called to start the reading +# of data. +# +#------------------------------------------------------------------------- +def importData(database, filename, callback): + + basefile = os.path.dirname(filename) + database.smap = {} + database.pmap = {} + database.fmap = {} + + parser = GrampsImportParser(database,callback,basefile) + + if gzip_ok: + use_gzip = 1 + try: + f = gzip.open(filename,"r") + f.read(1) + f.close() + except IOError,msg: + use_gzip = 0 + else: + use_gzip = 0 + + try: + if use_gzip: + xml_file = gzip.open(filename,"rb") + else: + xml_file = open(filename,"r") + except IOError,msg: + GnomeErrorDialog(_("%s could not be opened\n") % filename + str(msg)) + return 0 + except: + GnomeErrorDialog(_("%s could not be opened\n") % filename) + return 0 + + try: + parser.parse(xml_file) + except IOError,msg: + GnomeErrorDialog(_("Error reading %s") % filename + "\n" + str(msg)) + import traceback + traceback.print_exc() + return 0 + except: + import DisplayTrace + DisplayTrace.DisplayTrace() + return 0 + + xml_file.close() + return 1 + +#------------------------------------------------------------------------- +# +# Initialization function for the module. Called to start the reading +# of data. +# +#------------------------------------------------------------------------- +def loadData(database, filename, callback=None): + + basefile = os.path.dirname(filename) + database.smap = {} + database.pmap = {} + database.fmap = {} + + filename = os.path.normpath(filename) + + parser = GrampsParser(database,callback,basefile) + + if gzip_ok: + use_gzip = 1 + try: + f = gzip.open(filename,"r") + f.read(1) + f.close() + except IOError,msg: + use_gzip = 0 + else: + use_gzip = 0 + + try: + if use_gzip: + xml_file = gzip.open(filename,"rb") + else: + xml_file = open(filename,"r") + except IOError,msg: + filemsg = _("%s could not be opened\n") % filename + GnomeErrorDialog(filemsg + str(msg)) + return 0 + except: + GnomeErrorDialog(_("%s could not be opened\n") % filename) + return 0 + + try: + parser.parse(xml_file) + except IOError,msg: + errmsg = "%s\n%s" % (_("Error reading %s") % filename,str(msg)) + GnomeErrorDialog(errmsg) + import traceback + traceback.print_exc() + return 0 + except: + GnomeErrorDialog(_("Error reading %s") % filename) + import traceback + traceback.print_exc() + return 0 + + xml_file.close() + return 1 + +#------------------------------------------------------------------------- +# +# Initialization function for the module. Called to start the reading +# of data. +# +#------------------------------------------------------------------------- +def loadRevision(database, file, filename, revision, callback=None): + + basefile = os.path.dirname(filename) + database.smap = {} + database.pmap = {} + database.fmap = {} + + parser = GrampsParser(database,callback,basefile) + + filename = _("%s (revision %s)") % (filename,revision) + + try: + parser.parse(file) + except IOError,msg: + errmsg = "%s\n%s" % (_("Error reading %s") % filename, str(msg)) + GnomeErrorDialog(errmsg) + import traceback + traceback.print_exc() + return 0 + except: + import DisplayTrace + DisplayTrace.DisplayTrace() + return 0 + + file.close() + return 1 diff --git a/gramps2/src/RelImage.py b/gramps2/src/RelImage.py new file mode 100644 index 000000000..924b33360 --- /dev/null +++ b/gramps2/src/RelImage.py @@ -0,0 +1,179 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +#------------------------------------------------------------------------- +# +# Standard python modules +# +#------------------------------------------------------------------------- +import os +import string + +#------------------------------------------------------------------------- +# +# GTK/Gnome modules +# +#------------------------------------------------------------------------- +import gtk +#from gnome.ui import GnomeErrorDialog, GnomeWarningDialog + +#------------------------------------------------------------------------- +# +# Gramps modules +# +#------------------------------------------------------------------------- +import const +import Utils +import ImgManip +from intl import gettext +_ = gettext + + +#------------------------------------------------------------------------- +# +# import_media_object +# +#------------------------------------------------------------------------- +def import_media_object(filename,path,base): + import shutil + + if not os.path.exists(filename): + GnomeErrorDialog(_("Could not import %s\nThe file has been moved or deleted") % filename) + return "" + + ext = os.path.splitext(filename)[1] + + type = Utils.get_mime_type(filename) + if type[0:5] == "image": + name = "%s/%s%s" % (path,base,ext) + #base = "%s%s" % (base,ext) + + thumb = "%s/.thumb" % (path) + + try: + if not os.path.exists(thumb): + os.mkdir(thumb) + except IOError,msg: + GnomeErrorDialog(_("Could not create %s") % thumb + "\n" + str(msg)) + return "" + except: + GnomeErrorDialog(_("Could not create %s") % thumb) + return "" + + try: + path = "%s/%s.jpg" % (thumb,base) + mk_thumb(filename,path,const.thumbScale) + except: + GnomeErrorDialog(_("Error creating the thumbnail : %s")) + return "" + + try: + shutil.copy(filename,name) + except IOError,msg: + GnomeErrorDialog(_("Error copying %s") % filename + "\n" + msg) + return "" + + else: + bname = os.path.basename(filename) + l = string.split(bname,'.') + name = "%s/%s.%s" % (path,base,l[-1]) + shutil.copy(filename,name) + + return name + +#------------------------------------------------------------------------- +# +# scale_image +# +#------------------------------------------------------------------------- +def scale_image(path,size): + try: + image1 = gtk.gdk.pixbuf_new_from_file(path) + except: + GnomeWarningDialog(_("Could not load image file %s") % path) + return gtk.gdk.pixbuf_new_from_file(const.icon) + + width = image1.get_width() + height = image1.get_height() + + scale = size / float(max(width,height)) + try: + image1.scale_simple(int(scale*width), int(scale*height), gtk.gdk.INTERP_BILINEAR) + return image1 + except: + GnomeWarningDialog(_("Could not load image file %s") % path) + return gtk.gdk.pixbuf_new_from_file(const.icon) + +#------------------------------------------------------------------------- +# +# scale_image +# +#------------------------------------------------------------------------- +def mk_thumb(source,dest,size): + dir = os.path.dirname(dest) + + source = os.path.normpath(source) + dest = os.path.normpath(dest) + + try: + if not os.path.exists(dir): + os.mkdir(dir) + except IOError,msg: + GnomeErrorDialog(_("Could not create %s") % dir + "\n" + str(msg)) + return + except: + GnomeErrorDialog(_("Could not create %s") % dir) + return + + if os.path.exists(dest): + try: + os.remove(dest) + except IOError,msg: + errmsg = _("Could not replace %s") % dir + GnomeErrorDialog(errmsg + "\n" + msg) + return + + if not os.path.exists(source): + GnomeErrorDialog(_("Could not create a thumbnail for %s\nThe file has been moved or deleted") % source) + + try: + img = ImgManip.ImgManip(source) + img.jpg_thumbnail(dest,size,size) + except: + import sys + msg = "%s\n%s %s" % (source,sys.exc_type,sys.exc_value) + GnomeErrorDialog(_("Could not create a thumbnail for %s") % msg) + return + +#------------------------------------------------------------------------- +# +# scale_image +# +#------------------------------------------------------------------------- +def check_thumb(source,dest,size): + if not os.path.isfile(source): + return 0 + if not os.path.isfile(dest): + mk_thumb(source,dest,size) + elif os.path.getmtime(source) > os.path.getmtime(dest): + mk_thumb(source,dest,size) + return 1 + + diff --git a/gramps2/src/RelLib.py b/gramps2/src/RelLib.py new file mode 100644 index 000000000..29c953498 --- /dev/null +++ b/gramps2/src/RelLib.py @@ -0,0 +1,2404 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000 Donald N. Allingham +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"""The core library of the GRAMPS database""" + +__author__ = "Donald N. Allingham" +__version__ = "$Revision$" + +#------------------------------------------------------------------------- +# +# standard python modules +# +#------------------------------------------------------------------------- +from re import compile +from string import strip, upper +import os + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +from Date import Date, compare_dates, not_too_old +import sort +import const + +#------------------------------------------------------------------------- +# +# Attempt to load the ZODB libraries, otherwise provide alternates +# +#------------------------------------------------------------------------- +try: + from ZODB import Persistent +except ImportError: + class Persistent: + """Dummy class used if ZODB is not installed on the system""" + pass + +#------------------------------------------------------------------------- +# +# Confidence levels +# +#------------------------------------------------------------------------- + +CONF_VERY_HIGH = 4 +CONF_HIGH = 3 +CONF_NORMAL = 2 +CONF_LOW = 1 +CONF_VERY_LOW = 0 + +#------------------------------------------------------------------------- +# +# ID regular expression +# +#------------------------------------------------------------------------- +_id_reg = compile("%\d+d") + + +def extlist(lst): + """Returns a copy of the passed list""" + return lst[:] # Make a copy. + +def extmap(map): + """Returns a map""" + return map + + +class SourceNote(Persistent): + """Base class for storing source references and notes""" + + def __init__(self,source=None): + """Create a new SourceNote, copying from source if not None""" + + self.source_list = [] + + if source: + if len(source.source_list) > 0: + for sref in source.source_list: + self.source_list.append(SourceRef(sref)) + if source.note: + self.note = Note(source.note.get()) + else: + self.note = None + else: + self.note = None + + def addSourceRef(self,id) : + """Set the source reference""" + self.source_list.append(id) + self._p_changed = 1 + + def getSourceRefList(self) : + """Return the source reference""" + return extlist(self.source_list) + + def setSourceRefList(self,list) : + """Replaces the source reference""" + self.source_list = list + + def setNote(self,text): + """Set the note to the given text""" + if self.note == None: + self.note = Note() + self.note.set(text) + + def getNote(self): + """Return the current note""" + if self.note == None: + return "" + else: + return self.note.get() + + def setNoteObj(self,obj): + """Change the note object instance to obj""" + self.note = obj + + def getNoteObj(self): + """Return in note instance, not just the text""" + return self.note + + def unique_note(self): + """Creates a unique instance of the current note""" + self.note = Note(self.note.get()) + +class LdsOrd(SourceNote): + """LDS Ordinance support""" + def __init__(self,source=None): + """Creates a LDS Ordinance instance""" + SourceNote.__init__(self,source) + if source: + self.famc = source.famc + self.date = Date(source.date) + self.temple = source.temple + self.status = source.status + self.place = source.place + else: + self.famc = None + self.date = None + self.temple = "" + self.status = 0 + self.place = None + + def getPlaceName(self): + """returns the title of the Place associated with the Ordinance""" + if self.place: + return self.place.get_title() + else: + return "" + + def setPlace(self,place): + """sets the Place instance of the Event""" + self.place = place + + def getPlace(self): + """returns the Place instance of the Event""" + return self.place + + def setFamily(self,family): + """Sets the family associated with the LDS ordinance""" + self.famc = family + + def getFamily(self): + """Gets the family associated with the LDS ordinance""" + return self.famc + + def setStatus(self,val): + """Sets the status of the LDS ordinance""" + self.status = val + + def getStatus(self): + """Gets the status of the LDS ordinance""" + return self.status + + def setDate(self, date) : + """attempts to sets the date of the LdsOrd instance""" + if not self.date: + self.date = Date() + self.date.set(date) + + def getDate(self) : + """returns a string representation of the date of the LdsOrd instance""" + if self.date: + return self.date.getDate() + return "" + + def getDateObj(self): + """returns the Date object associated with the LdsOrd""" + if not self.date: + self.date = Date() + return self.date + + def setDateObj(self,date): + """sets the Date object associated with the LdsOrd""" + self.date = date + + def setTemple(self,temple): + """Sets the temple assocated with the LDS ordinance""" + self.temple = temple + + def getTemple(self): + """Gets the temple assocated with the LDS ordinance""" + return self.temple + + def are_equal(self,other): + """returns 1 if the spdcified ordinance is the same as the instance""" + if other == None: + return 0 + if (self.famc != other.famc or + self.place != other.place or + self.temple != other.temple or + compare_dates(self.getDateObj(),other.getDateObj()) or + len(self.getSourceRefList()) != len(other.getSourceRefList())): + return 0 + + index = 0 + olist = other.getSourceRefList() + for a in self.getSourceRefList(): + if not a.are_equal(olist[index]): + return 0 + index = index + 1 + return 1 + +class DataObj(SourceNote): + """Base class for data elements, providing source, note, and privacy data""" + + def __init__(self,source=None): + """Create a new DataObj, copying data from a source object if provided""" + SourceNote.__init__(self,source) + + if source: + self.private = source.private + else: + self.private = 0 + + def setPrivacy(self,val): + """Sets or clears the privacy flag of the data""" + self.private = val + + def getPrivacy(self): + """Returns the privacy level of the data""" + return self.private + +class Place(SourceNote): + """Contains information related to a place, including multiple address + information (since place names can change with time), longitude, latitude, + a collection of images and URLs, a note and a source""" + + def __init__(self,source=None): + """Creates a new Place object. + + source - Object to copy. If none supplied, create an empty place object""" + + SourceNote.__init__(self,source) + if source: + self.long = source.log + self.lat = source.lat + self.title = source.title + self.main_loc = Location(source.main_loc) + self.alt_loc = [] + for loc in source.alt_loc: + self.alt_loc = Location(loc) + self.id = source.id + self.urls = [] + for u in source.urls: + self.urls.append(Url(u)) + self.photoList = [] + for photo in source.photoList: + self.photoList.append(ObjectRef(photo)) + else: + self.long = "" + self.lat = "" + self.title = "" + self.main_loc = None + self.alt_loc = [] + self.id = "" + self.urls = [] + self.photoList = [] + + def getUrlList(self): + """Return the list of URLs""" + return extlist(self.urls) + + def setUrlList(self,list): + """Replace the current URL list with the new one""" + self.urls = list + + def addUrl(self,url): + """Add a URL to the URL list""" + self.urls.append(url) + self._p_changed = 1 + + def setId(self,id): + """Sets the gramps ID for the place object""" + self.id = id + + def getId(self): + """Returns the gramps ID for the place object""" + return self.id + + def set_title(self,name): + """Sets the title of the place object""" + self.title = name + + def get_title(self): + """Returns the title of the place object""" + return self.title + + def set_longitude(self,long): + """Sets the longitude of the place""" + self.long = long + + def get_longitude(self): + """Returns the longitude of the place""" + return self.long + + def set_latitude(self,long): + """Sets the latitude of the place""" + self.lat = long + + def get_latitude(self): + """Returns the latitude of the place""" + return self.lat + + def get_main_location(self): + """Returns the Location object representing the primary information""" + if not self.main_loc: + self.main_loc = Location() + return self.main_loc + + def set_main_location(self,loc): + """Assigns the main location to the Location object passed""" + self.main_loc = loc + + def get_alternate_locations(self): + """Returns a list of alternate location information objects""" + return extlist(self.alt_loc) + + def set_alternate_locations(self,list): + """Replaces the current alternate location list with the new one""" + self.alt_loc = list + + def add_alternate_locations(self,loc): + """Adds a Location to the alternate location list""" + if loc not in self.alt_loc: + self.alt_loc.append(loc) + self._p_changed = 1 + + def addPhoto(self,photo): + """Adds a Photo object to the place object's image list""" + self.photoList.append(photo) + self._p_changed = 1 + + def getPhotoList(self): + """Returns the list of Photo objects""" + return extlist(self.photoList) + + def setPhotoList(self,list): + """Sets the list of Photo objects""" + self.photoList = list + + def getDisplayInfo(self): + """Gets the display information associated with the object. This includes + the information that is used for display and for sorting. Returns a list + consisting of 13 strings. These are: Place Title, Place ID, Main Location + Parish, Main Location County, Main Location City, Main Location State/Province, + Main Location Country, upper case Place Title, upper case Parish, upper + case city, upper case county, upper case state, upper case country""" + + if self.main_loc: + return [self.title,self.id,self.main_loc.parish,self.main_loc.city, + self.main_loc.county,self.main_loc.state,self.main_loc.country, + upper(self.title), upper(self.main_loc.parish), + upper(self.main_loc.city), upper(self.main_loc.county), + upper(self.main_loc.state), upper(self.main_loc.country)] + else: + return [self.title,self.id,'','','','','',upper(self.title), '','','','',''] + +class Researcher(Persistent): + """Contains the information about the owner of the database""" + + def __init__(self): + """Initializes the Researcher object""" + self.name = "" + self.addr = "" + self.city = "" + self.state = "" + self.country = "" + self.postal = "" + self.phone = "" + self.email = "" + + def getName(self): + """returns the database owner's name""" + return self.name + + def getAddress(self): + """returns the database owner's address""" + return self.addr + + def getCity(self): + """returns the database owner's city""" + return self.city + + def getState(self): + """returns the database owner's state""" + return self.state + + def getCountry(self): + """returns the database owner's country""" + return self.country + + def getPostalCode(self): + """returns the database owner's postal code""" + return self.postal + + def getPhone(self): + """returns the database owner's phone number""" + return self.phone + + def getEmail(self): + """returns the database owner's email""" + return self.email + + def set(self,name,addr,city,state,country,postal,phone,email): + """sets the information about the database owner""" + if name: + self.name = strip(name) + if addr: + self.addr = strip(addr) + if city: + self.city = strip(city) + if state: + self.state = strip(state) + if country: + self.country = strip(country) + if postal: + self.postal = strip(postal) + if phone: + self.phone = strip(phone) + if email: + self.email = strip(email) + +class Location(Persistent): + """Provides information about a place, including city, county, state, + and country. Multiple Location objects can represent the same place, + since names of citys, countys, states, and even countries can change + with time""" + + def __init__(self,source=None): + """creates a Location object, copying from the source object if it exists""" + if source: + self.city = source.city + self.parish = source.parish + self.county = source.county + self.state = source.state + self.country = source.country + else: + self.city = "" + self.parish = "" + self.county = "" + self.state = "" + self.country = "" + + def is_empty(self): + return self.city=="" and self.county=="" and self.state=="" and self.country=="" + + def set_city(self,data): + """sets the city name of the Location object""" + self.city = data + + def get_city(self): + """returns the city name of the Location object""" + return self.city + + def set_parish(self,data): + """sets the religious parish name""" + self.parish = data + + def get_parish(self): + """gets the religious parish name""" + return self.parish + + def set_county(self,data): + """sets the county name of the Location object""" + self.county = data + + def get_county(self): + """returns the county name of the Location object""" + return self.county + + def set_state(self,data): + """sets the state name of the Location object""" + self.state = data + + def get_state(self): + """returns the state name of the Location object""" + return self.state + + def set_country(self,data): + """sets the country name of the Location object""" + self.country = data + + def get_country(self): + """returns the country name of the Location object""" + return self.country + +class Note(Persistent): + """Provides general text information""" + + def __init__(self,text = ""): + """create a new Note object from the passed string""" + self.text = text + + def set(self,text): + """set the note contents to the passed string""" + self.text = text + + def get(self): + """return the note contents""" + return self.text + + def append(self,text): + """adds the text to the note's contents""" + self.text = self.text + text + +class Photo(SourceNote): + """Containter for information about an image file, including location, + description and privacy""" + + def __init__(self,source=None): + """Create a new Photo object, copying from the source if provided""" + + SourceNote.__init__(self,source) + + self.attrlist = [] + if source: + self.path = source.path + self.mime = source.mime + self.local = source.local + self.desc = source.desc + self.id = source.id + for attr in source.attrlist: + self.attrlist.append(Attribute(attr)) + else: + self.id = "" + self.local = 0 + self.path = "" + self.mime = "" + self.desc = "" + + def setLocal(self,val): + """set or clear the local flag""" + self.local = val + + def getLocal(self): + """return the local flag""" + return self.local + + def setId(self,id): + """Sets the gramps ID for the place object""" + self.id = id + + def getId(self): + """Returns the gramps ID for the place object""" + return self.id + + def setMimeType(self,type): + self.mime = type + + def getMimeType(self): + return self.mime + + def setPath(self,path): + """set the file path to the passed path""" + self.path = os.path.normpath(path) + + def getPath(self): + """return the file path""" + return self.path + + def setDescription(self,text): + """sets the description of the image""" + self.desc = text + + def getDescription(self): + """returns the description of the image""" + return self.desc + + def addAttribute(self,attr): + """Adds a propery to the Photo object. This is not used by gramps, + but provides a means for XML users to attach other properties to + the image""" + self.attrlist.append(attr) + self._p_changed = 1 + + def getAttributeList(self): + """returns the property list associated with the image""" + return extlist(self.attrlist) + + def setAttributeList(self,list): + self.attrlist = list + + +class ObjectRef(Persistent): + """Object reference class""" + def __init__(self,source=None): + self.attrlist = [] + if source: + self.private = source.private + self.ref = source.ref + self.note = Note(source.note) + for attr in source.attrlist: + self.attrlist.append(Attribute(attr)) + else: + self.private = 0 + self.ref = None + self.note = None + + def setPrivacy(self,val): + """Sets or clears the privacy flag of the data""" + self.private = val + + def getPrivacy(self): + """Returns the privacy level of the data""" + return self.private + + def setReference(self,obj): + self.ref = obj + + def getReference(self): + return self.ref + + def setNote(self,text): + """Set the note to the given text""" + if self.note == None: + self.note = Note() + self.note.set(text) + + def getNote(self): + """Return the current note""" + if self.note == None: + return "" + else: + return self.note.get() + + def setNoteObj(self,obj): + """Change the note object instance to obj""" + self.note = obj + + def getNoteObj(self): + """Return in note instance, not just the text""" + return self.note + + def unique_note(self): + """Creates a unique instance of the current note""" + self.note = Note(self.note.get()) + + def addAttribute(self,attr): + """Adds a propery to the Photo object. This is not used by gramps, + but provides a means for XML users to attach other properties to + the image""" + self.attrlist.append(attr) + self._p_changed = 1 + + def getAttributeList(self): + """returns the property list associated with the image""" + return extlist(self.attrlist) + + def setAttributeList(self,list): + """sets the property list associated with the image""" + self.attrlist = list + + +class Attribute(DataObj): + """Provides a simple key/value pair for describing properties. Used + by the Person and Family objects to store descriptive information.""" + + def __init__(self,source=None): + """creates a new Attribute object, copying from the source if provided""" + DataObj.__init__(self,source) + + if source: + self.type = source.type + self.value = source.value + else: + self.type = "" + self.value = "" + + def setType(self,val): + """sets the type (or key) of the Attribute instance""" + self.type = val + + def getType(self): + """returns the type (or key) or the Attribute instance""" + return self.type + + def setValue(self,val): + """sets the value of the Attribute instance""" + self.value = val + + def getValue(self): + """returns the value of the Attribute instance""" + return self.value + + +class Address(DataObj): + """Provides address information for a person""" + + def __init__(self,source=None): + """Creates a new Address instance, copying from the source + if provided""" + DataObj.__init__(self,source) + + if source: + self.street = source.street + self.city = source.city + self.state = source.state + self.country = source.country + self.postal = source.postal + self.date = Date(source.date) + else: + self.street = "" + self.city = "" + self.state = "" + self.country = "" + self.postal = "" + self.date = Date() + + def setDate(self,text): + """attempts to sets the date that the person lived at the address + from the passed string""" + self.date.set(text) + + def getDate(self): + """returns a string representation of the date that the person + lived at the address""" + return self.date.getDate() + + def getPrefDate(self): + """returns a string representation of the date that the person + lived at the address""" + return self.date.getPrefDate() + + def getDateObj(self): + """returns the Date object associated with the Address""" + return self.date + + def setDateObj(self,obj): + """sets the Date object associated with the Address""" + self.date = obj + + def setStreet(self,val): + """sets the street portion of the Address""" + self.street = val + + def getStreet(self): + """returns the street portion of the Address""" + return self.street + + def setCity(self,val): + """sets the city portion of the Address""" + self.city = val + + def getCity(self): + """returns the city portion of the Address""" + return self.city + + def setState(self,val): + """sets the state portion of the Address""" + self.state = val + + def getState(self): + """returns the state portion of the Address""" + return self.state + + def setCountry(self,val): + """sets the country portion of the Address""" + self.country = val + + def getCountry(self): + """returns the country portion of the Address""" + return self.country + + def setPostal(self,val): + """sets the postal code of the Address""" + self.postal = val + + def getPostal(self): + """returns the postal code of the Address""" + return self.postal + + +class Name(DataObj): + """Provides name information about a person. A person may have more + that one name throughout his or her life.""" + + def __init__(self,source=None): + """creates a new Name instance, copying from the source if provided""" + DataObj.__init__(self,source) + + if source: + self.FirstName = source.FirstName + self.Surname = source.Surname + self.Suffix = source.Suffix + self.Title = source.Title + self.type = source.type + else: + self.FirstName = "" + self.Surname = "" + self.Suffix = "" + self.Title = "" + self.type = "" + + def setType(self,type): + """sets the type of the Name instance""" + self.type = type + + def getType(self): + """returns the type of the Name instance""" + return self.type + + def setFirstName(self,name): + """sets the given name for the Name instance""" + self.FirstName = name + + def setSurname(self,name): + """sets the surname (or last name) for the Name instance""" + self.Surname = name + + def setSuffix(self,name): + """sets the suffix (such as Jr., III, etc.) for the Name instance""" + self.Suffix = name + + def getFirstName(self): + """returns the given name for the Name instance""" + return self.FirstName + + def getSurname(self): + """returns the surname (or last name) for the Name instance""" + return self.Surname + + def getSuffix(self): + """returns the suffix for the Name instance""" + return self.Suffix + + def setTitle(self,title): + """sets the title (Dr., Reverand, Captain) for the Name instance""" + self.Title = title + + def getTitle(self): + """returns the title for the Name instance""" + return self.Title + + def getName(self): + """returns a name string built from the components of the Name + instance, in the form of Surname, Firstname""" + + if (self.Suffix == ""): + return "%s, %s" % (self.Surname, self.FirstName) + else: + return "%s, %s %s" % (self.Surname, self.FirstName, self.Suffix) + + def getRegularName(self): + """returns a name string built from the components of the Name + instance, in the form of Firstname Surname""" + if (self.Suffix == ""): + return "%s %s" % (self.FirstName, self.Surname) + else: + return "%s %s, %s" % (self.FirstName, self.Surname, self.Suffix) + + def are_equal(self,other): + """compares to names to see if they are equal, return 0 if they + are not""" + if self.FirstName != other.FirstName: + return 0 + if self.Surname != other.Surname: + return 0 + if self.Suffix != other.Suffix: + return 0 + if self.Title != other.Title: + return 0 + if self.type != other.type: + return 0 + if self.private != other.private: + return 0 + if self.getNote() != other.getNote(): + return 0 + if len(self.getSourceRefList()) != len(other.getSourceRefList()): + return 0 + index = 0 + olist = other.getSourceRefList() + for a in self.getSourceRefList(): + if not a.are_equal(olist[index]): + return 0 + index = index + 1 + return 1 + +class Url(Persistent): + """Contains information related to internet Uniform Resource Locators, + allowing gramps to store information about internet resources""" + + def __init__(self,source=None): + """creates a new URL instance, copying from the source if present""" + if source: + self.path = source.path + self.desc = source.desc + self.private = source.private + else: + self.path = "" + self.desc = "" + self.private = 0 + + def setPrivacy(self,val): + """sets the privacy flag for the URL instance""" + self.private = val + + def getPrivacy(self): + """returns the privacy flag for the URL instance""" + return self.private + + def set_path(self,path): + """sets the URL path""" + self.path = path + + def get_path(self): + """returns the URL path""" + return self.path + + def set_description(self,description): + """sets the description of the URL""" + self.desc = description + + def get_description(self): + """returns the description of the URL""" + return self.desc + + def are_equal(self,other): + """returns 1 if the specified URL is the same as the instance""" + if other == None: + return 0 + if self.path != other.path: + return 0 + if self.desc != other.desc: + return 0 + return 1 + + +class Person(Persistent): + """Represents an individual person in the gramps database""" + + unknown = 2 + male = 1 + female = 0 + + def __init__(self): + """creates a new Person instance""" + + self.id = "" + self.PrimaryName = None + self.EventList = [] + self.FamilyList = [] + self.AltFamilyList = [] + self.photoList = [] + self.nickname = "" + self.alternateNames = [] + self.gender = 2 + self.death = None + self.birth = None + self.addressList = [] + self.attributeList = [] + self.urls = [] + self.note = None + self.paf_uid = "" + self.position = None + self.ancestor = None + self.lds_bapt = None + self.lds_endow = None + self.lds_seal = None + + def getDisplayInfo(self): + if self.gender == Person.male: + gender = const.male + elif self.gender == Person.female: + gender = const.female + else: + gender = const.unknown + bday = self.getBirth().getDateObj() + dday = self.getDeath().getDateObj() + return [self.getPrimaryName().getName(),self.id,gender, + bday.getQuoteDate(), dday.getQuoteDate(), + sort.build_sort_name(self.getPrimaryName()), + sort.build_sort_date(bday),sort.build_sort_date(dday)] + + + def setPrimaryName(self,name): + """sets the primary name of the Person to the specified + Name instance""" + self.PrimaryName = name + + def getPrimaryName(self): + """returns the Name instance marked as the Person's primary name""" + if not self.PrimaryName: + self.PrimaryName = Name() + return self.PrimaryName + + def setPafUid(self,val): + """sets Personal Ancestral File UID value""" + self.paf_uid = val + + def getPafUid(self) : + """returns the Personal Ancestral File UID value""" + return self.paf_uid + + def getAlternateNames(self): + """returns the list of alternate Names""" + return extlist(self.alternateNames) + + def setAlternateNames(self,list): + """changes the list of alternate names to the passed list""" + self.alternateNames = list + + def addAlternateName(self,name): + """adds an alternate Name instance to the list""" + self.alternateNames.append(name) + self._p_changed = 1 + + def getUrlList(self): + """returns the list of URL instances""" + return extlist(self.urls) + + def setUrlList(self,list): + """sets the list of URL instances to list""" + self.urls = list + + def addUrl(self,url): + """adds a URL instance to the list""" + self.urls.append(url) + self._p_changed = 1 + + def setId(self,id): + """sets the gramps ID for the Person""" + self.id = str(id) + + def getId(self): + """returns the gramps ID for the Person""" + return self.id + + def setNickName(self,name): + """sets the nickname for the Person""" + self.nickname = name + + def getNickName(self) : + """returns the nickname for the Person""" + return self.nickname + + def setGender(self,val) : + """sets the gender of the Person""" + self.gender = val + + def getGender(self) : + """returns the gender of the Person""" + return self.gender + + def setBirth(self,event) : + """sets the birth event to the passed event""" + self.birth = event + + def setDeath(self,event) : + """sets the death event to the passed event""" + self.death = event + + def getBirth(self) : + """returns the birth event""" + if self.birth == None: + self.birth = Event() + self.birth.name = "Birth" + return self.birth + + def getDeath(self) : + """returns the death event""" + if self.death == None: + self.death = Event() + self.death.name = "Death" + return self.death + + def addPhoto(self,photo): + """adds a Photo instance to the image list""" + self.photoList.append(photo) + self._p_changed = 1 + + def getPhotoList(self): + """returns the list of Photos""" + return extlist(self.photoList) + + def setPhotoList(self,list): + """Sets the list of Photo objects""" + self.photoList = list + + def addEvent(self,event): + """adds an Event to the event list""" + self.EventList.append(event) + self._p_changed = 1 + + def getEventList(self): + """returns the list of Event instances""" + return extlist(self.EventList) + + def setEventList(self,list): + """sets the event list to the passed list""" + self.EventList = list + + def addFamily(self,family): + """adds the specified Family instance to the list of + families/marriages/partnerships in which the person is a + parent or spouse""" + self.FamilyList.append(family) + self._p_changed = 1 + + def setPreferred(self,family): + if family in self.FamilyList: + self.FamilyList.remove(family) + self.FamilyList = [family] + self.FamilyList + + def getFamilyList(self) : + """returns the list of Family instances in which the + person is a parent or spouse""" + return extlist(self.FamilyList) + + def clearFamilyList(self) : + self.FamilyList = [] + + def removeFamily(self,family): + """removes the specified Family instance from the list + of marriages/partnerships""" + if family in self.FamilyList: + self.FamilyList.remove(family) + self._p_changed = 1 + + def addAddress(self,address): + """adds the Address instance to the list of addresses""" + self.addressList.append(address) + self._p_changed = 1 + + def removeAddress(self,address): + """removes the Address instance from the list of addresses""" + if address in self.addressList: + self.addressList.remove(address) + self._p_changed = 1 + + def getAddressList(self): + """returns the list of addresses""" + return extlist(self.addressList) + + def setAddressList(self,list): + """sets the address list to the specified list""" + self.addressList = list + + def addAttribute(self,attribute): + """adds an Attribute instance to the attribute list""" + self.attributeList.append(attribute) + self._p_changed = 1 + + def removeAttribute(self,attribute): + """removes the specified Attribute instance from the attribute list""" + if attribute in self.attributeList: + self.attributeList.remove(attribute) + self._p_changed = 1 + + def getAttributeList(self): + """returns the attribute list""" + return extlist(self.attributeList) + + def setAttributeList(self,list): + """sets the attribute list to the specified list""" + self.attributeList = list + + def getParentList(self): + """returns the list of alternate Family instances, in which the Person + is a child of the family, but not a natural child of both parents""" + return extlist(self.AltFamilyList) + + def addAltFamily(self,family,mrel,frel): + """adds a Family to the alternate family list, indicating the + relationship to the mother (mrel) and the father (frel)""" + self.AltFamilyList.append((family,mrel,frel)) + self._p_changed = 1 + + def clearAltFamilyList(self): + self.AltFamilyList = [] + + def removeAltFamily(self,family): + """removes a Family instance from the alternate family list""" + for f in self.AltFamilyList[:]: + if f[0] == family: + self.AltFamilyList.remove(f) + self._p_changed = 1 + return f + else: + return None + + def has_family(self,family): + for f in self.AltFamilyList: + if f[0] == family: + return f + else: + return None + + def setMainParents(self,family): + """sets the main Family of the Person, the Family in which the + Person is a natural born child""" + f = self.removeAltFamily(family) + if f: + self.AltFamilyList = [f] + self.AltFamilyList + + def getMainParents(self): + """returns the main Family of the Person, the Family in which the + Person is a natural born child""" + if len(self.AltFamilyList) == 0: + return None + else: + return self.AltFamilyList[0][0] + + def getMainParentsRel(self): + """returns the main Family of the Person, the Family in which the + Person is a natural born child""" + if len(self.AltFamilyList) == 0: + return (None,None,None) + else: + return self.AltFamilyList[0] + + def setNote(self,text): + """sets the note attached to the Person to the passed text""" + if self.note == None: + self.note = Note() + self.note.set(text) + + def getNote(self) : + """returns the text of the note attached to the Person""" + if self.note == None: + return "" + else: + return self.note.get() + + def setNoteObj(self,note): + """sets the Note instance attached to the Person""" + self.note = note + + def getNoteObj(self): + """returns the Note instance attached to the Person""" + if self.note == None: + self.note = Note() + return self.note + + def unique_note(self): + """Creates a unique instance of the current note""" + self.note = Note(self.note.get()) + + def setPosition(self,pos): + """sets a graphical location pointer for graphic display (x,y)""" + self.position = pos + + def getPosition(self): + """returns a graphical location pointer for graphic display (x,y)""" + return self.position + + def setAncestor(self, value): + """set ancestor flag and recurse""" + self.ancestor = value + for (family,m,f) in self.AltFamilyList: + if family.Father: + # Don't waste time if the ancestor is already flagged. + # This will happen when cousins marry. + if not family.Father.getAncestor(): + family.Father.setAncestor(value) + if family.getMother(): + if not family.Mother.getAncestor(): + family.Mother.setAncestor(value) + + def getAncestor(self): + return self.ancestor + + def setLdsBaptism(self,ord): + self.lds_bapt = ord + + def getLdsBaptism(self): + return self.lds_bapt + + def setLdsEndowment(self,ord): + self.lds_endow = ord + + def getLdsEndowment(self): + return self.lds_endow + + def setLdsSeal(self,ord): + self.lds_seal = ord + + def getLdsSeal(self): + return self.lds_seal + + def probablyAlive(self): + if self.death.getDate() != "": + return 0 + if self.birth.getDate() != "": + return not_too_old(self.birth.getDateObj().get_start_date()) + return 1 + + +class Event(DataObj): + """Event record, recording the event type, description, place, and date + of a particular event""" + + def __init__(self,source=None): + """creates a new Event instance, copying from the source if present""" + + DataObj.__init__(self,source) + + if source: + self.place = source.place + self.date = Date(source.date) + self.description = source.description + self.name = source.name + self.cause = source.cause + else: + self.place = None + self.date = None + self.description = "" + self.name = "" + self.cause = "" + + def is_empty(self): + date = self.getDateObj() + place = self.getPlace() + description = self.description + name = self.name + if (not name or name == "Birth" or name == "Death") and \ + date.isEmpty() and not place and not description: + return 1 + else: + return 0 + + def set(self,name,date,place,description): + """sets the name, date, place, and description of an Event instance""" + self.name = name + self.place = place + self.description = description + self.setDate(date) + + def are_equal(self,other): + """returns 1 if the specified event is the same as the instance""" + if other == None: + return 0 + if (self.name != other.name or self.place != other.place or + self.description != other.description or self.cause != other.cause or + self.private != other.private or + compare_dates(self.getDateObj(),other.getDateObj()) or + len(self.getSourceRefList()) != len(other.getSourceRefList())): + return 0 + + index = 0 + olist = other.getSourceRefList() + for a in self.getSourceRefList(): + if not a.are_equal(olist[index]): + return 0 + index = index + 1 + + return 1 + + def setName(self,name): + """sets the name of the Event""" + self.name = name + + def getName(self): + """returns the name of the Event""" + return self.name + + def setPlace(self,place): + """sets the Place instance of the Event""" + self.place = place + + def getPlace(self): + """returns the Place instance of the Event""" + return self.place + + def setCause(self,cause): + """sets the cause of the Event""" + self.cause = cause + + def getCause(self): + """returns the cause of the Event""" + return self.cause + + def getPlaceName(self): + """returns the title of the Place associated with the Event""" + if self.place: + return self.place.get_title() + else: + return "" + + def setDescription(self,description): + """sets the description of the Event instance""" + self.description = description + + def getDescription(self) : + """returns the description of the Event instance""" + return self.description + + def setDate(self, date) : + """attempts to sets the date of the Event instance""" + if not self.date: + self.date = Date() + self.date.set(date) + + def getDate(self) : + """returns a string representation of the date of the Event instance""" + if self.date: + return self.date.getDate() + return "" + + def getPrefDate(self) : + """returns a string representation of the date of the Event instance""" + if self.date: + return self.date.getDate() + return "" + + def getQuoteDate(self) : + """returns a string representation of the date of the Event instance, + enclosing the results in quotes if it is not a valid date""" + if self.date: + return self.date.getQuoteDate() + return "" + + def getDateObj(self): + """returns the Date object associated with the Event""" + if not self.date: + self.date = Date() + return self.date + + def setDateObj(self,date): + """sets the Date object associated with the Event""" + self.date = date + + +class Family(Persistent): + """Represents a family unit in the gramps database""" + + def __init__(self): + """creates a new Family instance""" + self.Father = None + self.Mother = None + self.Children = [] + self.Marriage = None + self.Divorce = None + self.type = "Married" + self.EventList = [] + self.id = "" + self.photoList = [] + self.note = Note() + self.attributeList = [] + self.position = None + self.lds_seal = None + + def setLdsSeal(self,ord): + self.lds_seal = ord + + def getLdsSeal(self): + return self.lds_seal + + def setPosition(self,pos): + """sets a graphical location pointer for graphic display (x,y)""" + self.position = pos + + def getPosition(self): + """returns a graphical location pointer for graphic display (x,y)""" + return self.position + + def addAttribute(self,attribute) : + """adds an Attribute instance to the attribute list""" + self.attributeList.append(attribute) + self._p_changed = 1 + + def removeAttribute(self,attribute): + """removes the specified Attribute instance from the attribute list""" + if attribute in self.attributeList: + self.attributeList.remove(attribute) + self._p_changed = 1 + + def getAttributeList(self) : + """returns the attribute list""" + return extlist(self.attributeList) + + def setAttributeList(self,list) : + """sets the attribute list to the specified list""" + self.attributeList = list + + def getNote(self): + """returns the text of the note attached to the Family""" + return self.note.get() + + def setNote(self,text): + """sets the note attached to the Family to the passed text""" + self.note.set(text) + + def getNoteObj(self): + """returns the Note instance attached to the Family""" + return self.note + + def unique_note(self): + """Creates a unique instance of the current note""" + self.note = Note(self.note.get()) + + def setNoteObj(self,obj): + """sets the Note instance attached to the Family""" + self.note = obj + + def setId(self,id) : + """sets the gramps ID for the Family""" + self.id = str(id) + + def getId(self) : + """returns the gramps ID for the Family""" + return self.id + + def setRelationship(self,type): + """assigns a string indicating the relationship between the + father and the mother""" + self.type = type + + def getRelationship(self): + """returns a string indicating the relationship between the + father and the mother""" + return self.type + + def setFather(self,person): + """sets the father of the Family to the specfied Person""" + update = self.someChildIsAncestor() + if update and self.Father: + self.Father.setAncestor(0) + self.Father = person + if update and self.Father: + self.Father.setAncestor(1) + + def getFather(self): + """returns the father of the Family""" + return self.Father + + def setMother(self,person): + """sets the mother of the Family to the specfied Person""" + update = self.someChildIsAncestor() + if self.Mother and update: + self.Mother.setAncestor(0) + self.Mother = person + if update and self.Mother: + self.Mother.setAncestor(1) + + def getMother(self): + """returns the mother of the Family""" + return self.Mother + + def addChild(self,person): + """adds the specfied Person as a child of the Family, adding it + to the child list""" + if person not in self.Children: + self.Children.append(person) + self._p_changed = 1 + if person.getAncestor(): + if self.Father: + self.Father.setAncestor(1) + if self.Mother: + self.Mother.setAncestor(1) + + def removeChild(self,person): + """removes the specified Person from the child list""" + if person in self.Children: + self.Children.remove(person) + self._p_changed = 1 + if person.getAncestor(): + if self.Father: + self.Father.setAncestor(0) + if self.Mother: + self.Mother.setAncestor(0) + + def getChildList(self): + """returns the list of children""" + return extlist(self.Children) + + def setChildList(self, list): + """sets the list of children""" + self.Children = list + + def getMarriage(self): + """returns the marriage event of the Family. Obsolete""" + for e in self.EventList: + if e.getName() == "Marriage": + return e + return None + + def getDivorce(self): + """returns the divorce event of the Family. Obsolete""" + for e in self.EventList: + if e.getName() == "Divorce": + return e + return None + + def addEvent(self,event): + """adds an Event to the event list""" + self.EventList.append(event) + self._p_changed = 1 + + def getEventList(self) : + """returns the list of Event instances""" + return extlist(self.EventList) + + def setEventList(self,list) : + """sets the event list to the passed list""" + self.EventList = list + + def addPhoto(self,photo): + """Adds a Photo object to the Family instance's image list""" + self.photoList.append(photo) + self._p_changed = 1 + + def getPhotoList(self): + """Returns the list of Photo objects""" + return extlist(self.photoList) + + def setPhotoList(self,list): + """Sets the list of Photo objects""" + self.photoList = list + + def someChildIsAncestor(self): + for child in self.Children: + if (child.getAncestor()): + return 1 + return None + +class Source(Persistent): + """A record of a source of information""" + + def __init__(self): + """creates a new Source instance""" + self.title = "" + self.author = "" + self.pubinfo = "" + self.callno = "" + self.note = Note() + self.photoList = [] + self.id = "" + + def getDisplayInfo(self): + return [self.title,self.id,self.author,upper(self.title), + upper(self.author)] + + def setId(self,newId): + """sets the gramps' ID for the Source instance""" + self.id = str(newId) + + def getId(self): + """returns the gramps' ID of the Source instance""" + return self.id + + def addPhoto(self,photo): + """Adds a Photo object to the Source instance's image list""" + self.photoList.append(photo) + self._p_changed = 1 + + def getPhotoList(self): + """Returns the list of Photo objects""" + return extlist(self.photoList) + + def setPhotoList(self,list): + """Sets the list of Photo objects""" + self.photoList = list + + def setTitle(self,title): + """sets the title of the Source""" + self.title = title + + def getTitle(self): + """returns the title of the Source""" + return self.title + + def setNote(self,text): + """sets the text of the note attached to the Source""" + self.note.set(text) + + def getNote(self): + """returns the text of the note attached to the Source""" + return self.note.get() + + def setNoteObj(self,obj): + """sets the Note instance attached to the Source""" + self.note = obj + + def getNoteObj(self): + """returns the Note instance attached to the Source""" + return self.note + + def unique_note(self): + """Creates a unique instance of the current note""" + self.note = Note(self.note.get()) + + def setAuthor(self,author): + """sets the author of the Source""" + self.author = author + + def getAuthor(self): + """returns the author of the Source""" + return self.author + + def setPubInfo(self,text): + """sets the publication information of the Source""" + self.pubinfo = text + + def getPubInfo(self): + """returns the publication information of the Source""" + return self.pubinfo + + def setCallNumber(self,val): + """sets the call number (or some identification indicator) + of the Source""" + self.callno = val + + def getCallNumber(self): + """returns the call number (or some identification indicator) + of the Source""" + return self.callno + +class SourceRef(Persistent): + """Source reference, containing detailed information about how a + referenced source relates to it""" + + def __init__(self,source=None): + """creates a new SourceRef, copying from the source if present""" + if source: + self.confidence = source.confidence + self.ref = source.ref + self.page = source.page + self.date = Date(source.date) + self.comments = Note(source.comments.get()) + self.text = source.text + else: + self.confidence = CONF_NORMAL + self.ref = None + self.page = "" + self.date = Date() + self.comments = Note() + self.text = "" + + def setConfidence(self,val): + """Sets the confidence level""" + self.confidence = val + + def getConfidence(self): + """Returns the confidence level""" + return self.confidence + + def setBase(self,ref): + """sets the Source instance to which the SourceRef refers""" + self.ref = ref + + def getBase(self): + """returns the Source instance to which the SourceRef refers""" + return self.ref + + def setDate(self,date): + """sets the Date instance of the SourceRef""" + self.date = date + + def getDate(self): + """returns the Date instance of the SourceRef""" + return self.date + + def setPage(self,page): + """sets the page indicator of the SourceRef""" + self.page = page + + def getPage(self): + """gets the page indicator of the SourceRef""" + return self.page + + def setText(self,text): + """sets the text related to the SourceRef""" + self.text = text + + def getText(self): + """returns the text related to the SourceRef""" + return self.text + + def setNoteObj(self,note): + """Change the Note instance to obj""" + self.comments = note + + def setComments(self,comments): + """sets the comments about the SourceRef""" + self.comments.set(comments) + + def getComments(self): + """returns the comments about the SourceRef""" + return self.comments.get() + + def are_equal(self,other): + """returns 1 if the passed SourceRef is equal to the current""" + if self.ref and other.ref: + if self.page != other.page: + return 0 + if compare_dates(self.date,other.date) != 0: + return 0 + if self.getText() != other.getText(): + return 0 + if self.getComments() != other.getComments(): + return 0 + if self.confidence != other.confidence: + return 0 + return 1 + elif not self.ref and not other.ref: + return 1 + else: + return 0 + + def unique_note(self): + """Creates a unique instance of the current note""" + self.comments = Note(self.comments.get()) + +class GrampsDB(Persistent): + """GRAMPS database object. This object is a base class for other + objects.""" + + def __init__(self): + """creates a new GrampsDB""" + self.surnames = [] + self.personTable = {} + self.placeTable = {} + self.sourceTable = {} + self.iprefix = "I%d" + self.sprefix = "S%d" + self.oprefix = "O%d" + self.pprefix = "P%d" + self.fprefix = "F%d" + self.familyMap = {} + self.personMap = {} + self.sourceMap = {} + self.placeMap = {} + self.new() + + def get_type(self): + return 'GrampsDB' + + def close(self): + pass + + def get_base(self): + return "" + + def need_autosave(self): + return 1 + + def getPersonKeys(self): + return self.personTable.keys() + + def getPersonDisplay(self,key): + return self.personTable[key] + + def buildPersonDisplay(self,nkey,okey=None): + if nkey != okey and okey != None: + del self.personTable[okey] + person = self.personMap[nkey] + self.personTable[nkey] = person.getDisplayInfo() + self.addSurname(person.getPrimaryName().getSurname()) + + def buildPlaceDisplay(self,nkey,okey=None): + if nkey != okey and okey != None: + del self.placeTable[okey] + self.placeTable[nkey] = self.placeMap[nkey].getDisplayInfo() + + def set_iprefix(self,val): + if _id_reg.search(val): + self.iprefix = val + else: + self.iprefix = val + "%d" + + def set_sprefix(self,val): + if _id_reg.search(val): + self.sprefix = val + else: + self.sprefix = val + "%d" + + def set_oprefix(self,val): + if _id_reg.search(val): + self.oprefix = val + else: + self.oprefix = val + "%d" + + def set_pprefix(self,val): + if _id_reg.search(val): + self.pprefix = val + else: + self.pprefix = val + "%d" + + def set_fprefix(self,val): + if _id_reg.search(val): + self.fprefix = val + else: + self.fprefix = val + "%d" + + def new(self): + """initializes the GrampsDB to empty values""" + + # eliminate memory reference cycles for 1.5.2 garbage collection + for f in self.familyMap.values(): + f.Father = None + f.Mother = None + f.Children = [] + self.familyMap = {} + + for p in self.personMap.values(): + p.clearAltFamilyList() + p.clearFamilyList() + + self.surnames = [] + self.personMap = {} + self.sourceMap = {} + self.placeMap = {} + self.objectMap = {} + self.personTable = {} + self.placeTable = {} + self.sourceTable = {} + self.smapIndex = 0 + self.pmapIndex = 0 + self.fmapIndex = 0 + self.lmapIndex = 0 + self.omapIndex = 0 + self.default = None + self.owner = Researcher() + self.bookmarks = [] + self.path = "" + self.place2title = {} + + def getSurnames(self): + return self.surnames + + def addSurname(self,name): + if name and name not in self.surnames: + self.surnames.append(name) + self.surnames.sort() + + def getBookmarks(self): + """returns the list of Person instances in the bookmarks""" + return extlist(self.bookmarks) + + def clean_bookmarks(self): + """cleans up the bookmark list, removing empty slots""" + new_bookmarks = [] + for person in self.bookmarks: + new_bookmarks.append(person) + self.bookmarks = new_bookmarks + + def setResearcher(self,owner): + """sets the information about the owner of the database""" + self.owner.set(owner.getName(),owner.getAddress(),owner.getCity(),\ + owner.getState(),owner.getCountry(),\ + owner.getPostalCode(),owner.getPhone(),owner.getEmail()) + + def getResearcher(self): + """returns the Researcher instance, providing information about + the owner of the database""" + return self.owner + + def setDefaultPerson(self,person): + """sets the default Person to the passed instance""" + if (self.default): + self.default.setAncestor(0) + self.default = person + self.default.setAncestor(1) + + def getDefaultPerson(self): + """returns the default Person of the database""" + return self.default + + def getPerson(self,id): + """returns a map of gramps's IDs to Person instances""" + return self.personMap[id] + + def getPersonMap(self): + """returns a map of gramps's IDs to Person instances""" + return extmap(self.personMap) + + def setPersonMap(self,map): + """sets the map of gramps's IDs to Person instances""" + self.personMap = map + + def getPlaceMap(self): + """returns a map of gramps's IDs to Place instances""" + return extmap(self.placeMap) + + def setPlaceMap(self,map): + """sets the map of gramps's IDs to Place instances""" + self.placeMap = map + + def getFamilyMap(self): + """returns a map of gramps's IDs to Family instances""" + return extmap(self.familyMap) + + def getFamily(self,id): + """returns a map of gramps's IDs to Family instances""" + return self.familyMap[id] + + def setFamilyMap(self,map): + """sets the map of gramps's IDs to Family instances""" + self.familyMap = map + + def getSourceMap(self): + """returns a map of gramps's IDs to Source instances""" + return extmap(self.sourceMap) + + + def getObjectMap(self): + """returns a map of gramps's IDs to Object instances""" + return self.objectMap + + def getSavePath(self): + """returns the save path of the file, or "" if one does not exist""" + return self.path + + def setSavePath(self,path): + """sets the save path for the database""" + self.path = path + + def getPersonEventTypes(self): + """returns a list of all Event types assocated with Person + instances in the database""" + map = {} + for person in self.personMap.values(): + for event in person.getEventList(): + map[event.getName()] = 1 + return map.keys() + + def getPersonAttributeTypes(self): + """returns a list of all Attribute types assocated with Person + instances in the database""" + map = {} + for key in self.personTable.keys(): + person = self.personMap[key] + for attr in person.getAttributeList(): + map[attr.getType()] = 1 + return map.keys() + + def getFamilyAttributeTypes(self): + """returns a list of all Attribute types assocated with Family + instances in the database""" + map = {} + for family in self.familyMap.values(): + for attr in family.getAttributeList(): + map[attr.getType()] = 1 + return map.keys() + + def getFamilyEventTypes(self): + """returns a list of all Event types assocated with Family + instances in the database""" + map = {} + for family in self.familyMap.values(): + for attr in family.getEventList(): + map[attr.getName()] = 1 + return map.keys() + + def getPlaces(self): + """returns a list of Place instances""" + return self.placeMap.values() + + def getFamilyRelationTypes(self): + """returns a list of all relationship types assocated with Family + instances in the database""" + map = {} + for family in self.familyMap.values(): + map[family.getRelationship()] = 1 + return map.keys() + + def removePerson(self,id): + del self.personMap[id] + del self.personTable[id] + + def removeSource(self,id): + del self.sourceMap[id] + del self.sourceTable[id] + + def addPersonAs(self,person): + self.personMap[person.getId()] = person + self.personTable[person.getId()] = person.getDisplayInfo() + + def addPerson(self,person): + """adds a Person to the database, assigning a gramps' ID""" + index = self.iprefix % self.pmapIndex + while self.personMap.has_key(index): + self.pmapIndex = self.pmapIndex + 1 + index = self.iprefix % self.pmapIndex + person.setId(index) + self.personMap[index] = person + self.personTable[index] = person.getDisplayInfo() + self.pmapIndex = self.pmapIndex + 1 + return index + + def findPerson(self,idVal,map): + """finds a Person in the database using the idVal and map + variables to translate between the external ID and gramps' + internal ID. If no such Person exists, a new Person instance + is created. + + idVal - external ID number + map - map build by findPerson of external to gramp's IDs""" + + idVal = str(idVal) + if map.has_key(idVal): + person = self.personMap[map[idVal]] + else: + person = Person() + map[idVal] = self.addPerson(person) + self.personTable[map[idVal]] = person.getDisplayInfo() + return person + + def addPersonNoMap(self,person,id): + """adds a Person to the database if the gramps' ID is known""" + + id = str(id) + person.setId(id) + self.personMap[id] = person + self.pmapIndex = self.pmapIndex+1 + self.personTable[id] = person.getDisplayInfo() + return id + + def findPersonNoMap(self,val): + """finds a Person in the database from the passed gramps' ID. + If no such Person exists, a new Person is added to the database.""" + + person = self.personMap.get(val) + if not person: + person = Person() + person.id = val + self.personMap[val] = person + self.pmapIndex = self.pmapIndex+1 + self.personTable[val] = person.getDisplayInfo() + return person + + def addSource(self,source): + """adds a Source instance to the database, assigning it a gramps' + ID number""" + + index = self.sprefix % self.smapIndex + while self.sourceMap.has_key(index): + self.smapIndex = self.smapIndex + 1 + index = self.sprefix % self.smapIndex + source.setId(index) + self.sourceMap[index] = source + self.sourceTable[index] = source.getDisplayInfo() + self.smapIndex = self.smapIndex + 1 + return index + + def findSource(self,idVal,map): + """finds a Source in the database using the idVal and map + variables to translate between the external ID and gramps' + internal ID. If no such Source exists, a new Source instance + is created. + + idVal - external ID number + map - map build by findSource of external to gramp's IDs""" + + if map.has_key(idVal): + source = self.sourceMap[map[idVal]] + else: + source = Source() + map[idVal] = self.addSource(source) + self.sourceTable[map[idVal]] = source.getDisplayInfo() + return source + + def addSourceNoMap(self,source,index): + """adds a Source to the database if the gramps' ID is known""" + source.setId(index) + self.sourceMap[index] = source + self.smapIndex = self.smapIndex + 1 + self.sourceTable[index] = source.getDisplayInfo() + return index + + def findSourceNoMap(self,val): + """finds a Source in the database from the passed gramps' ID. + If no such Source exists, a new Source is added to the database.""" + + if self.sourceMap.has_key(val): + source = self.sourceMap[val] + else: + source = Source() + self.addSourceNoMap(source,val) + self.sourceTable[val] = source.getDisplayInfo() + return source + + def addObject(self,object): + """adds an Object instance to the database, assigning it a gramps' + ID number""" + + index = self.oprefix % self.omapIndex + while self.objectMap.has_key(index): + self.omapIndex = self.omapIndex + 1 + index = self.oprefix % self.omapIndex + object.setId(index) + self.objectMap[index] = object + self.omapIndex = self.omapIndex + 1 + return index + + def findObject(self,idVal,map): + """finds an Object in the database using the idVal and map + variables to translate between the external ID and gramps' + internal ID. If no such Object exists, a new Object instance + is created. + + idVal - external ID number + map - map build by findObject of external to gramp's IDs""" + + idVal = str(idVal) + if map.has_key(idVal): + object = self.objectMap[map[idVal]] + else: + object = Photo() + map[idVal] = self.addObject(object) + return object + + def addObjectNoMap(self,object,index): + """adds an Object to the database if the gramps' ID is known""" + index = str(index) + object.setId(index) + self.objectMap[index] = object + self.omapIndex = self.omapIndex + 1 + return index + + def findObjectNoMap(self,idVal): + """finds an Object in the database from the passed gramps' ID. + If no such Source exists, a new Source is added to the database.""" + + val = str(idVal) + if self.objectMap.has_key(val): + object = self.objectMap[val] + else: + object = Photo() + self.addObjectNoMap(object,val) + return object + + def addPlace(self,place): + """adds a Place instance to the database, assigning it a gramps' + ID number""" + + index = self.pprefix % self.lmapIndex + while self.placeMap.has_key(index): + self.lmapIndex = self.lmapIndex + 1 + index = self.pprefix % self.lmapIndex + place.setId(index) + self.placeMap[index] = place + self.lmapIndex = self.lmapIndex + 1 + self.placeTable[index] = place.getDisplayInfo() + return index + + def removePlace(self,id): + del self.placeMap[id] + del self.placeTable[id] + + def addPlaceAs(self,place): + self.placeMap[place.getId()] = place + self.placeTable[place.getId()] = place.getDisplayInfo() + + def findPlace(self,idVal,map): + """finds a Place in the database using the idVal and map + variables to translate between the external ID and gramps' + internal ID. If no such Place exists, a new Place instance + is created. + + idVal - external ID number + map - map build by findPlace of external to gramp's IDs""" + + idVal = str(idVal) + if map.has_key(idVal): + place = self.placeMap[map[idVal]] + else: + place = Place() + map[idVal] = self.addPlace(place) + return place + + def addPlaceNoMap(self,place,index): + """adds a Place to the database if the gramps' ID is known""" + + index = str(index) + place.setId(index) + self.placeMap[index] = place + self.lmapIndex = self.lmapIndex + 1 + self.placeTable[index] = place.getDisplayInfo() + return index + + def findPlaceNoMap(self,val): + """finds a Place in the database from the passed gramps' ID. + If no such Place exists, a new Place is added to the database.""" + + place = self.placeMap.get(val) + if not place: + place = Place() + place.id = val + self.placeMap[val] = place + self.lmapIndex = self.lmapIndex + 1 + self.placeTable[val] = place.getDisplayInfo() + return place + + def getPlaceKeys(self): + return self.placeTable.keys() + + def getPlace(self,key): + return self.placeMap[key] + + def getPlaceDisplay(self,key): + return self.placeTable[key] + + def getSourceKeys(self): + return self.sourceTable.keys() + + def getSourceDisplay(self,key): + return self.sourceTable[key] + + def getSource(self,key): + return self.sourceMap[key] + + def buildSourceDisplay(self,nkey,okey=None): + if nkey != okey and okey != None: + del self.sourceTable[okey] + if self.sourceTable.has_key(nkey): + del self.sourceTable[nkey] + self.sourceTable[nkey] = self.sourceMap[nkey].getDisplayInfo() + + def newFamily(self): + """adds a Family to the database, assigning a gramps' ID""" + index = self.fprefix % self.fmapIndex + while self.familyMap.has_key(index): + self.fmapIndex = self.fmapIndex + 1 + index = self.fprefix % self.fmapIndex + self.fmapIndex = self.fmapIndex + 1 + family = Family() + family.setId(index) + self.familyMap[index] = family + return family + + def newFamilyNoMap(self,id): + """finds a Family in the database from the passed gramps' ID. + If no such Family exists, a new Family is added to the database.""" + + family = Family() + id = str(id) + family.setId(id) + self.familyMap[id] = family + self.fmapIndex = self.fmapIndex + 1 + return family + + def findFamily(self,idVal,map): + """finds a Family in the database using the idVal and map + variables to translate between the external ID and gramps' + internal ID. If no such Family exists, a new Family instance + is created. + + idVal - external ID number + map - map build by findFamily of external to gramp's IDs""" + + if map.has_key(idVal): + family = self.familyMap[map[idVal]] + else: + family = self.newFamily() + map[idVal] = family.getId() + return family + + def findFamilyNoMap(self,val): + """finds a Family in the database from the passed gramps' ID. + If no such Family exists, a new Family is added to the database.""" + + family = self.familyMap.get(val) + if not family: + family = Family() + family.id = val + self.familyMap[val] = family + self.fmapIndex = self.fmapIndex + 1 + return family + + def deleteFamily(self,family): + """deletes the Family instance from the database""" + if self.familyMap.has_key(family.getId()): + del self.familyMap[family.getId()] + + + diff --git a/gramps2/src/Report.py b/gramps2/src/Report.py new file mode 100644 index 000000000..ffc713277 --- /dev/null +++ b/gramps2/src/Report.py @@ -0,0 +1,1092 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2001 David R. Hampton +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +"Report Generation Framework" + +__author__ = "David R. Hampton, Donald N. Allingham" +__version__ = "$Revision$" + +#------------------------------------------------------------------------- +# +# standard python modules +# +#------------------------------------------------------------------------- +import os +import string + +#------------------------------------------------------------------------- +# +# GNOME/GTK modules +# +#------------------------------------------------------------------------- +import gtk +import gnome.ui + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +import const +import Utils +import Plugins +import GenericFilter +import TextDoc +import StyleEditor +import GrampsCfg +import PaperMenu +import intl +import latin_utf8 + +u2l = latin_utf8.utf8_to_latin +_ = intl.gettext + +#------------------------------------------------------------------------- +# +# Import XML libraries +# +#------------------------------------------------------------------------- +try: + from xml.sax import make_parser,handler,SAXParseException +except: + from _xmlplus.sax import make_parser,handler,SAXParseException + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +_default_template = _("Default Template") +_user_template = _("User Defined Template") + +_template_map = { + _user_template : None + } + +class Report: + """ + The Report base class. This is a base class for generating + customized reports. It cannot be used as is, but it can be easily + sub-classed to create a functional report generator. + """ + + # Ordinal generation names. Used by multiple reports. + gen = { + 1 : _("First"), + 2 : _("Second"), + 3 : _("Third"), + 4 : _("Fourth"), + 5 : _("Fifth"), + 6 : _("Sixth"), + 7 : _("Seventh"), + 8 : _("Eighth"), + 9 : _("Ninth"), + 10: _("Tenth"), + 11: _("Eleventh"), + 12: _("Twelfth"), + 13: _("Thirteenth"), + 14: _("Fourteenth"), + 15: _("Fifteenth"), + 16: _("Sixteenth"), + 17: _("Seventeenth"), + 18: _("Eighteenth"), + 19: _("Nineteenth"), + 20: _("Twentieth"), + 21: _("Twenty-first"), + 22: _("Twenty-second"), + 23: _("Twenty-third"), + 24: _("Twenty-fourth"), + 25: _("Twenty-fifth"), + 26: _("Twenty-sixth"), + 27: _("Twenty-seventh"), + 28: _("Twenty-eighth"), + 29: _("Twenty-ninth") + } + + def get_progressbar_data(self): + """The window title for this dialog, and the header line to + put at the top of the contents of the dialog box.""" + return (_("Progress Report - GRAMPS"), _("Working")) + + def progress_bar_setup(self,total): + """Create a progress dialog. This routine calls a + customization function to find out how to fill out the dialog. + The argument to this function is the maximum number of items + that the report will progress; i.e. what's considered 100%, + i.e. the maximum number of times this routine will be + called.""" + + # Customize the dialog for this report + (title, header) = self.get_progressbar_data() + self.ptop = gtk.Dialog() + self.ptop.set_title(title) + self.ptop.vbox.add(gtk.Label(header)) + self.ptop.vbox.add(gtk.HSeparator()) + self.ptop.vbox.set_spacing(10) + self.pbar = gtk.ProgressBar() + self.pbar.set_format_string(_("%v of %u (%P%%)")) + self.pbar.configure(0.0,0.0,total) + self.pbar.set_show_text(1) + self.pbar.set_usize(350,20) + self.pbar_max = total + self.pbar_index = 0.0 + + self.ptop.vbox.add(self.pbar) + self.ptop.show_all() + + def progress_bar_step(self): + """Click the progress bar over to the next value. Be paranoid + and insure that it doesn't go over 100%.""" + self.pbar_index = self.pbar_index + 1.0 + if (self.pbar_index > self.pbar_max): + self.pbar_index = self.pbar_max + self.pbar.set_value(self.pbar_index) + + def progress_bar_done(self): + """Done with the progress bar. It can be destroyed now.""" + Utils.destroy_passed_object(self.ptop) + + +class ReportDialog: + """ + The ReportDialog base class. This is a base class for generating + customized dialogs to solicit options for a report. It cannot be + used as is, but it can be easily sub-classed to create a functional + dialog. + """ + + frame_pad = 5 + border_pad = 2 + + def __init__(self,database,person): + """Initialize a dialog to request that the user select options + for a basic report.""" + + # Save info about who the report is about. + self.db = database + self.person = person + self.output_notebook = None + self.notebook_page = 1 + self.pagecount_menu = None + self.filter_combo = None + self.extra_menu = None + self.extra_textbox = None + self.pagebreak_checkbox = None + self.generations_spinbox = None + self.widgets = [] + self.frame_names = [] + self.frames = {} + + self.window = gtk.Dialog('GRAMPS') + # self.window.set_default(0) + self.window.add_button(gtk.STOCK_OK,0) + self.window.set_response_sensitive(0,gtk.TRUE) + self.window.add_button(gtk.STOCK_CANCEL,1) + self.window.set_response_sensitive(1,gtk.TRUE) +# self.window.button_connect(1,self.on_cancel) + self.window.set_resize_mode(0) + + # Build the list of widgets that are used to extend the Options + # frame and to create other frames + + self.add_user_options() + + # Set up and run the dialog. These calls are not in top down + # order when looking at the dialog box as there is some + # interaction between the various frames. + self.setup_title() + self.setup_header() + self.setup_target_frame() + self.setup_format_frame() + self.setup_style_frame() + self.setup_output_notebook() + self.setup_paper_frame() + self.setup_html_frame() + self.setup_report_options_frame() + self.setup_other_frames() + self.window.show_all() + self.setup_post_process() + + result = self.window.run() + self.on_ok_clicked(result) + + # Allow for post processing of the format frame, since the + # show_all task calls events that may reset values + + def setup_post_process(self): + pass + + #------------------------------------------------------------------------ + # + # Customization hooks for subclasses + # + #------------------------------------------------------------------------ + def get_title(self): + """The window title for this dialog.""" + return(_("Base Report - GRAMPS")) + + def get_header(self, name): + """The header line to put at the top of the contents of the + dialog box. By default this will just be the name of the + selected person. Most subclasses will customize this to give + some indication of what the report will be, i.e. 'Descendant + Report for %s'.""" + return(name) + + def get_target_browser_title(self): + """The title of the window that will be created when the user + clicks the 'Browse' button in the 'Save As' File Entry + widget.""" + return(_("Save Report As - GRAMPS")) + + def get_target_is_directory(self): + """Is the user being asked to input the name of a file or a + directory in the 'Save As' File Entry widget. This item + currently only selects the Filename/Directory prompt, and + whether or not the browser accepts filenames. In the future it + may also control checking of the selected filename.""" + return None + + def get_stylesheet_savefile(self): + """Where should new styles for this report be saved? This is + the name of an XML file that will be located in the ~/.gramps + directory. This file does not have to exist; it will be + created when needed. All subclasses should probably override + this function.""" + return "basic_report.xml" + + def get_print_pagecount_map(self): + """Return the data used to fill out the 'pagecount' option + menu in the print options box. The first value is a mapping + of string:value pairs. The strings will be used to label + individual menu items, and the values are what will be + returned if a given menu item is selected. The second value + is the name of menu item to pre-select.""" + return (None, None) + + def get_report_filters(self): + """Return the data used to fill out the 'filter' combo box in + the report options box. The return value is the list of + strings to be inserted into the pulldown.""" + return [] + + def get_report_generations(self): + """Return the default number of generations to start the + spinbox (zero to disable) and whether or not to include the + 'page break between generations' check box""" + return (10, 1) + + def get_report_extra_menu_info(self): + """Return the data used to fill out the 'extra' option menu in + the report options box. The first value is the string to be + used as the label to the left of the menu. The second value + is a mapping of string:value pairs. The strings will be used + to label individual menu items, and the values are what will + be returned if a given menu item is selected. The third value + is the name of menu item to pre-select, and the final value is + a string to use as the tooltip for the textbox.""" + return (None, None, None, None) + + def get_report_extra_textbox_info(self): + """Return the data used to fill out the 'extra' textbox in the + report options dialog. The first value is the string to be + used as the label to the left of the textbox. The second + value is the string to use as the default contents of the + textbox. If None, then the text box will be hidden. The + final value is a string to use as the tooltip for the + textbox.""" + return (None, None, None) + + #------------------------------------------------------------------------ + # + # Functions related getting/setting the default directory for a dialog. + # + #------------------------------------------------------------------------ + def get_default_directory(self): + """Get the name of the directory to which the target dialog + box should default. This value can be set in the preferences + panel.""" + return GrampsCfg.report_dir + + def set_default_directory(self, value): + """Save the name of the current directory, so that any future + reports will default to the most recently used directory. + This also changes the directory name that will appear in the + preferences panel, but does not change the preference in disk. + This means that the last directory used will only be + remembered for this session of gramps unless the user saves + his/her preferences.""" + GrampsCfg.report_dir = value + + #------------------------------------------------------------------------ + # + # Functions related to extending the options + # + #------------------------------------------------------------------------ + def add_user_options(self): + """Called to allow subclasses add widgets to the dialog form. + It is called immediately before the window is displayed. All + calls to add_option or add_frame_option should be called in + this task.""" + pass + + def add_option(self,label_text,widget,tooltip=None): + """Takes a text string and a Gtk Widget, and stores them to be + appended to the Options section of the dialog. The text string + is used to create a label for the passed widget. This allows the + subclass to extend the Options section with its own widgets. The + subclass is reponsible for all managing of the widgets, including + extracting the final value before the report executes. This task + should only be called in the add_user_options task.""" + self.widgets.append((label_text,widget)) + if tooltip: + self.add_tooltip(widget,tooltip) + + def add_frame_option(self,frame_name,label_text,widget,tooltip=None): + """Similar to add_option this method takes a frame_name, a + text string and a Gtk Widget. When the interface is built, + all widgets with the same frame_name are grouped into a + GtkFrame. This allows the subclass to create its own sections, + filling them with its own widgets. The subclass is reponsible for + all managing of the widgets, including extracting the final value + before the report executes. This task should only be called in + the add_user_options task.""" + + if self.frames.has_key(frame_name): + self.frames[frame_name].append((label_text,widget)) + else: + self.frames[frame_name] = [(label_text,widget)] + self.frame_names.append(frame_name) + if tooltip: + self.add_tooltip(widget,tooltip) + + #------------------------------------------------------------------------ + # + # Functions to create a default output style. + # + #------------------------------------------------------------------------ + def make_default_style(self): + """Create the default style to be used by the associated report. This + routine is a default implementation and should be overridden.""" + font = TextDoc.FontStyle() + font.set(face=TextDoc.FONT_SANS_SERIF,size=16,bold=1) + para = TextDoc.ParagraphStyle() + para.set_font(font) + para.set_header_level(1) + para.set(pad=0.5) + self.default_style.add_style("Title",para) + + def build_style_menu(self): + """Build a menu of style sets that are available for use in + this report. This menu will always have a default style + available, and will have any other style set name that the + user has previously created for this report. This menu is + created here instead of inline with the rest of the style + frame, because it must be recreated to reflect any changes + whenever the user closes the style editor dialog.""" + style_sheet_map = self.style_sheet_list.get_style_sheet_map() + myMenu = Utils.build_string_optmenu(style_sheet_map, "default") + self.style_menu.set_menu(myMenu) + + #------------------------------------------------------------------------ + # + # Functions related to selecting/changing the current file format. + # + #------------------------------------------------------------------------ + def make_doc_menu(self): + """Build a menu of document types that are appropriate for + this report. This menu will be generated based upon the type + of document (text, draw, graph, etc. - a subclass), whether or + not the document requires table support, etc.""" + pass + + def make_document(self): + """Create a document of the type selected by the user.""" + pass + + def doc_type_changed(self, obj): + """This routine is called when the user selects a new file + formats for the report. It adjust the various dialog sections + to reflect the appropriate values for the currently selected + file format. For example, a HTML document doesn't need any + paper size/orientation options, but it does need a template + file. Those chances are made here.""" + + # Is this to be a printed report or an electronic report + # (i.e. a set of web pages) + + if obj.get_data("paper") == 1: + self.notebook_page = 0 + else: + self.notebook_page = 1 + + if self.output_notebook == None: + return + + self.output_notebook.set_current_page(self.notebook_page) + + # Does this report format use styles? + self.style_frame.set_sensitive(obj.get_data("styles")) + + #------------------------------------------------------------------------ + # + # Functions related to setting up the dialog window. + # + #------------------------------------------------------------------------ + def setup_title(self): + """Set up the title bar of the dialog. This function relies + on the get_title() customization function for what the title + should be.""" + self.name = self.person.getPrimaryName().getRegularName() + self.window.set_title(self.get_title()) + + def setup_header(self): + """Set up the header line bar of the dialog. This function + relies on the get_header() customization function for what the + header line should read. If no customization function is + supplied by the subclass, the default is to use the full name + of the currently selected person.""" + + title = self.get_header(self.name) + label = gtk.Label(title) + label.set_size_request(450,10) + self.window.vbox.pack_start(label,gtk.TRUE,gtk.TRUE,ReportDialog.border_pad) + self.window.vbox.add(gtk.HSeparator()) + + def setup_target_frame(self): + """Set up the target frame of the dialog. This function + relies on several target_xxx() customization functions to + determine whether the target is a directory or file, what the + title of any browser window should be, and what default + directory should be used.""" + + # Save Frame + frame = gtk.Frame(_("Save As")) + frame.set_border_width(ReportDialog.frame_pad) + hid = self.get_stylesheet_savefile() + if hid[-4:]==".xml": + hid = hid[0:-4] + self.target_fileentry = gnome.ui.FileEntry(hid,_("Save As")) + + hbox = gtk.HBox() + hbox.set_border_width(ReportDialog.border_pad) + if self.get_target_is_directory(): + self.target_fileentry.set_directory_entry(1) + label = gtk.Label(_("Directory")) + else: + label = gtk.Label(_("Filename")) + hbox.pack_start(label,0,0,5) + + hbox.add(self.target_fileentry) + frame.add(hbox) + self.window.vbox.add(frame) + + self.target_fileentry.set_default_path(self.get_default_directory()) + if (self.get_target_is_directory()): + self.target_fileentry.set_directory_entry(1) + + self.target_fileentry.set_filename(self.get_default_directory()) + + # Faugh! The following line of code would allow the 'Enter' + # key in the file name box to close the dialog. However there + # is a bug (or is it?) in the closing of the Gnome FileEntry + # browser that sends the same signal that is sent when the + # 'Enter' key is pressed. This causes the report to be run + # when the browser window is closed instead of waiting for the + # dialog window OK button to be clicked. The user does not + # have a chance to set any other options. + # + # self.window.editable_enters(self.target_filename) + + def setup_format_frame(self): + """Set up the format frame of the dialog. This function + relies on the make_doc_menu() function to do all the hard + work.""" + + self.format_menu = gtk.OptionMenu() + self.make_doc_menu() + frame = gtk.Frame(_("Output Format")) + frame.add(self.format_menu) + frame.set_border_width(ReportDialog.frame_pad) + self.window.vbox.add(frame) + + def setup_style_frame(self): + """Set up the style frame of the dialog. This function relies + on other routines create the default style for this report, + and to read in any user defined styles for this report. It + the builds a menu of all the available styles for the user to + choose from.""" + + # Styles Frame + self.style_frame = gtk.Frame(_("Styles")) + hbox = gtk.HBox() + hbox.set_border_width(ReportDialog.border_pad) + self.style_menu = gtk.OptionMenu() + hbox.pack_start(self.style_menu,gtk.TRUE,gtk.TRUE,2) + style_button = gtk.Button(_("Style Editor")) + style_button.connect('clicked',self.on_style_edit_clicked) + hbox.pack_end(style_button,0,0,2) + self.style_frame.add(hbox) + self.style_frame.set_border_width(ReportDialog.frame_pad) + self.window.vbox.add(self.style_frame) + + # Build the default style set for this report. + self.default_style = TextDoc.StyleSheet() + self.make_default_style() + + # Build the initial list of available styles sets. This + # includes the default style set and any style sets saved from + # previous invocations of gramps. + self.style_sheet_list = TextDoc.StyleSheetList(self.get_stylesheet_savefile(), + self.default_style) + + # Now build the actual menu. + self.build_style_menu() + + def setup_output_notebook(self): + """Set up the output notebook of the dialog. This sole + purpose of this function is to grab a pointer for later use in + the callback from when the file format is changed.""" + + self.output_notebook = gtk.Notebook() + self.paper_frame = gtk.Frame(_("Paper Options")) + self.paper_frame.set_border_width(ReportDialog.frame_pad) + self.output_notebook.append_page(self.paper_frame,gtk.Label(_("Paper Options"))) + self.html_frame = gtk.Frame(_("HTML Options")) + self.html_frame.set_border_width(ReportDialog.frame_pad) + self.output_notebook.append_page(self.html_frame,gtk.Label(_("HTML Options"))) + self.output_notebook.set_show_tabs(0) + self.output_notebook.set_show_border(0) + self.output_notebook.set_current_page(self.notebook_page) + self.window.vbox.add(self.output_notebook) + + def setup_paper_frame(self): + """Set up the paper selection frame of the dialog. This + function relies on a paper_xxx() customization functions to + determine whether the pagecount menu should appear and what + its strings should be.""" + + (pagecount_map, start_text) = self.get_print_pagecount_map() + table = gtk.Table(2,4) + self.paper_frame.add(table) + self.papersize_menu = gtk.OptionMenu() + self.orientation_menu = gtk.OptionMenu() + l = gtk.Label(_("Size")) + pad = ReportDialog.border_pad + l.set_alignment(1.0,0.5) + table.attach(l,0,1,0,1,gtk.FILL,gtk.FILL,pad,pad) + table.attach(self.papersize_menu,1,2,0,1,xpadding=pad,ypadding=pad) + l = gtk.Label(_("Orientation")) + l.set_alignment(1.0,0.5) + table.attach(l,2,3,0,1,gtk.FILL,gtk.FILL,pad,pad) + table.attach(self.orientation_menu,3,4,0,1,xpadding=pad,ypadding=pad) + PaperMenu.make_paper_menu(self.papersize_menu) + PaperMenu.make_orientation_menu(self.orientation_menu) + + # The optional pagecount stuff. + if pagecount_map: + self.pagecount_menu = gtk.OptionMenu() + myMenu = Utils.build_string_optmenu(pagecount_map, start_text) + self.pagecount_menu.set_menu(myMenu) + table.attach(gtk.Label(_("Page Count")),0,1,1,2,gtk.FILL,gtk.FILL,pad,pad) + table.attach(self.pagecount_menu,1,2,1,2,xpadding=pad,ypadding=pad) + + def html_file_enable(self,obj): + text = obj.get_text() + if _template_map.has_key(text): + if _template_map[text]: + self.html_fileentry.set_sensitive(0) + else: + self.html_fileentry.set_sensitive(1) + else: + self.html_fileentry.set_sensitive(0) + + def setup_html_frame(self): + """Set up the html frame of the dialog. This sole purpose of + this function is to grab a pointer for later use in the parse + html frame function.""" + + table = gtk.Table(2,2) + self.html_frame.add(table) + l = gtk.Label(_("Template")) + pad = ReportDialog.border_pad + l.set_alignment(1.0,0.5) + table.attach(l,0,1,0,1,gtk.FILL,gtk.FILL,pad,pad) + self.template_combo = gtk.Combo() + + template_list = [ _default_template ] + tlist = _template_map.keys() + tlist.sort() + + for template in tlist: + if template != _user_template: + template_list.append(template) + template_list.append(_user_template) + + self.template_combo.set_popdown_strings(template_list) + self.template_combo.entry.set_editable(0) + self.template_combo.entry.connect('changed',self.html_file_enable) + + table.attach(self.template_combo,1,2,0,1, + gtk.FILL|gtk.EXPAND,gtk.FILL|gtk.EXPAND,pad,pad) + table.attach(gtk.Label(_("User Template")),0,1,1,2, + gtk.FILL,gtk.FILL,pad,pad) + self.html_fileentry = gnome.ui.FileEntry(_("HTML_Template"), + _("Choose File")) + self.html_fileentry.set_sensitive(0) + table.attach(self.html_fileentry,1,2,1,2, + gtk.FILL|gtk.EXPAND,gtk.FILL|gtk.EXPAND,pad,pad) + + def setup_report_options_frame(self): + """Set up the report options frame of the dialog. This + function relies on several report_xxx() customization + functions to determine which of the items should be present in + this box. *All* of these items are optional, although the + generations fields and the filter combo box are used in most + (but not all) dialog boxes.""" + + (use_gen, use_break) = self.get_report_generations() + local_filters = self.get_report_filters() + (em_label, extra_map, preset, em_tip) = self.get_report_extra_menu_info() + (et_label, string, et_tip) = self.get_report_extra_textbox_info() + + row = 0 + max_rows = 0 + if use_gen: + max_rows = max_rows + 1 + if use_break: + max_rows = max_rows + 1 + if len(local_filters): + max_rows = max_rows + 1 + if extra_map: + max_rows = max_rows + 1 + if string: + max_rows = max_rows + 1 + + max_rows = max_rows + len(self.widgets) + + if max_rows == 0: + return + + table = gtk.Table(2,max_rows) + + if len(self.frame_names) == 0: + frame = gtk.Frame(_("Report Options")) + frame.set_border_width(ReportDialog.frame_pad) + self.window.vbox.add(frame) + frame.add(table) + else: + self.notebook = gtk.Notebook() + self.window.vbox.pack_start(self.notebook,padding=ReportDialog.frame_pad) + self.notebook.append_page(table,gtk.Label(_("Report Options"))) + self.notebook.set_border_width(ReportDialog.frame_pad) + + pad = ReportDialog.border_pad + if len(local_filters): + self.filter_combo = gtk.OptionMenu() + l = gtk.Label(_("Filter")) + l.set_alignment(1.0,0.5) + table.attach(l,0,1,row,row+1,xoptions=gtk.FILL,yoptions=0, + xpadding=pad,ypadding=pad) + table.attach(self.filter_combo,1,2,row,row+1,yoptions=0, + xpadding=pad,ypadding=pad) + + menu = GenericFilter.build_filter_menu(local_filters) + + self.filter_combo.set_menu(menu) + self.filter_menu = menu + row = row + 1 + + # Set up the generations spin and page break checkbox + if use_gen: + self.generations_spinbox = gtk.SpinButton(digits=0) + self.generations_spinbox.set_numeric(1) + adjustment = gtk.Adjustment(use_gen,1,31,1,0) + self.generations_spinbox.set_adjustment(adjustment) + adjustment.value_changed() + l = gtk.Label(_("Generations")) + l.set_alignment(1.0,0.5) + table.attach(l,0,1,row,row+1,xoptions=gtk.FILL,yoptions=0, + xpadding=pad,ypadding=pad) + table.attach(self.generations_spinbox,1,2,row,row+1, + yoptions=0, xpadding=pad,ypadding=pad) + row = row + 1 + + if use_break: + msg = _("Page break between generations") + self.pagebreak_checkbox = gtk.CheckButton(msg) + table.attach(self.pagebreak_checkbox,1,2,row,row+1, + xpadding=pad,ypadding=pad) + row = row + 1 + + # Now the "extra" option menu + if extra_map: + self.extra_menu_label = gtk.Label(em_label) + self.extra_menu_label.set_alignment(1.0,0.5) + self.extra_menu = gtk.OptionMenu() + myMenu = Utils.build_string_optmenu(extra_map, preset) + self.extra_menu.set_menu(myMenu) + self.extra_menu.set_sensitive(len(extra_map) > 1) + self.add_tooltip(self.extra_menu,em_tip) + table.attach(self.extra_menu_label,0,1,row,row+1, + xoptions=gtk.FILL,yoptions=0,xpadding=pad,ypadding=pad) + table.attach(self.extra_menu,1,2,row,row+1,yoptions=0, + xpadding=pad,ypadding=pad) + row = row + 1 + + # Now the "extra" text box + if string: + self.extra_textbox_label = gtk.Label(et_label) + self.extra_textbox_label.set_alignment(1.0,0) + self.extra_textbox = gtk.TextView() + self.extra_textbox.get_buffer().set_text(string,len(string)) + self.extra_textbox.set_editable(1) + self.add_tooltip(self.extra_textbox,et_tip) + table.attach(self.extra_textbox_label,0,1,row,row+1,xoptions=gtk.FILL, + yoptions=0,xpadding=pad,ypadding=pad) + table.attach(self.extra_textbox,1,2,row,row+1, + yoptions=0,xpadding=pad,ypadding=pad) + row = row + 1 + + # Setup requested widgets + for (text,widget) in self.widgets: + if text == None: + table.attach(widget,0,2,row,row+1,yoptions=0,xpadding=pad,ypadding=pad) + else: + text_widget = gtk.Label(text) + text_widget.set_alignment(1.0,0) + table.attach(text_widget,0,1,row,row+1,yoptions=0,xpadding=pad,ypadding=pad) + table.attach(widget,1,2,row,row+1,yoptions=0,xpadding=pad,ypadding=pad) + row = row + 1 + + + def setup_other_frames(self): + pad = ReportDialog.border_pad + for key in self.frame_names: + list = self.frames[key] + table = gtk.Table(2,len(list)) + self.notebook.append_page(table,gtk.Label(_(key))) + + row = 0 + for (text,widget) in list: + if text == None: + table.attach(widget,0,2,row,row+1, + xpadding=pad,ypadding=pad) + else: + text_widget = gtk.Label(text) + text_widget.set_alignment(1.0,0) + table.attach(text_widget,0,1,row,row+1,gtk.FILL,gtk.FILL,pad,pad) + table.attach(widget,1,2,row,row+1,yoptions=0, + xpadding=pad,ypadding=pad) + row = row + 1 + + #------------------------------------------------------------------------ + # + # Functions related to retrieving data from the dialog window + # + #------------------------------------------------------------------------ + def parse_target_frame(self): + """Parse the target frame of the dialog. If the target + filename is empty this routine returns a special value of None + to tell the calling routine to give up. This function also + saves the current directory so that any future reports will + default to the most recently used directory.""" + self.target_path = self.target_fileentry.get_full_path(0) + if not self.target_path: + return None + + if not self.get_target_is_directory() and os.path.isdir(self.target_path): + gnome.ui.GnomeErrorDialog(_("The filename that you gave is a directory.\n" + "You need to provide a valid filename.")) + return None + + self.set_default_directory(os.path.dirname(self.target_path) + os.sep) + return 1 + + def parse_format_frame(self): + """Parse the format frame of the dialog. Save the user + selected output format for later use.""" + self.format = self.format_menu.get_menu().get_active().get_data("name") + + def parse_style_frame(self): + """Parse the style frame of the dialog. Save the user + selected output style for later use. Note that this routine + retrieves a value whether or not the menu is displayed on the + screen. The subclass will know whether this menu was enabled. + This is for simplicity of programming.""" + self.selected_style = self.style_menu.get_menu().get_active().get_data("d") + + def parse_paper_frame(self): + """Parse the paper frame of the dialog. Save the user + selected choices for later use. Note that this routine + retrieves a value from the pagecount menu, whether or not it + is displayed on the screen. The subclass will know which ones + it has enabled. This is for simplicity of programming.""" + self.paper = self.papersize_menu.get_menu().get_active().get_data("i") + self.orien = self.orientation_menu.get_menu().get_active().get_data("i") + if self.pagecount_menu == None: + self.pagecount = 0 + else: + self.pagecount = self.pagecount_menu.get_menu().get_active().get_data("d") + + def parse_html_frame(self): + """Parse the html frame of the dialog. Save the user selected + html template name for later use. Note that this routine + retrieves a value whether or not the file entry box is + displayed on the screen. The subclass will know whether this + entry was enabled. This is for simplicity of programming.""" + + text = self.template_combo.entry.get_text() + if _template_map.has_key(text): + if text == _user_template: + self.template_name = self.html_fileentry.get_full_path(0) + else: + self.template_name = "%s/%s" % (const.template_dir,_template_map[text]) + else: + self.template_name = None + + def parse_report_options_frame(self): + """Parse the report options frame of the dialog. Save the + user selected choices for later use. Note that this routine + retrieves a value from all fields in the frame, regardless of + whether or not they are displayed on the screen. The subclass + will know which ones it has enabled. This is for simplicity + of programming.""" + + if self.generations_spinbox: + self.max_gen = self.generations_spinbox.get_value_as_int() + else: + self.max_gen = 0 + + if self.pagebreak_checkbox: + self.pg_brk = self.pagebreak_checkbox.get_active() + else: + self.pg_brk = 0 + + if self.filter_combo: + self.filter = self.filter_menu.get_active().get_data("filter") + else: + self.filter = None + + if self.extra_menu: + self.report_menu = self.extra_menu.get_menu().get_active().get_data("d") + else: + self.report_menu = None + + if self.extra_textbox: + self.report_text = string.split(self.extra_textbox.get_chars(0,-1),'\n') + else: + self.report_text = "" + + def parse_other_frames(self): + """Do nothing. This sole purpose of this function is to give + subclass a place to hang a routine to parser any other frames + that are unique to that specific report.""" + pass + + #------------------------------------------------------------------------ + # + # Callback functions from the dialog + # + #------------------------------------------------------------------------ + def on_style_edit_clicked(self, obj): + """The user has clicked on the 'Edit Styles' button. Create a + style sheet editor object and let them play. When they are + done, the previous routine will be called to update the dialog + menu for selecting a style.""" + StyleEditor.StyleListDisplay(self.style_sheet_list,self.build_style_menu) + + + def on_ok_clicked(self, val): + """The user is satisfied with the dialog choices. Validate + the output file name before doing anything else. If there is + a file name, gather the options and create the report.""" + + if val == 1: + self.window.destroy() + else: + + # Is there a filename? This should also test file permissions, etc. + if not self.parse_target_frame(): + return + + # Preparation + self.parse_format_frame() + self.parse_style_frame() + self.parse_paper_frame() + self.parse_html_frame() + self.parse_report_options_frame() + self.parse_other_frames() + + # Create the output document. + self.make_document() + + # Create the report object and product the report. + try: + self.make_report() + except (IOError,OSError),msg: + gnome.ui.GnomeErrorDialog(str(msg)) + + # Clean up the dialog object + self.window.destroy() + + #------------------------------------------------------------------------ + # + # Functions related to creating the actual report document. + # + #------------------------------------------------------------------------ + def make_report(self): + """Create the contents of the report. This is the meat and + potatoes of reports. The whole purpose of the dialog is to + get to this routine so that data is written to a file. This + routine should either write the data directly to the file, or + better yet, should create a subclass of a Report that will + write the data to a file.""" + pass + + #------------------------------------------------------------------------ + # + # Miscellaneous functions. + # + #------------------------------------------------------------------------ + def add_tooltip(self,widget,string): + """Adds a tooltip to the specified widget""" + if not widget or not string: + return + tip = gtk.Tooltips() + tip.set_tip(widget,string) + + +class TextReportDialog(ReportDialog): + """A class of ReportDialog customized for text based reports.""" + + def __init__(self,database,person): + """Initialize a dialog to request that the user select options + for a basic text report. See the ReportDialog class for more + information.""" + ReportDialog.__init__(self,database,person) + + #------------------------------------------------------------------------ + # + # Customization hooks for subclasses + # + #------------------------------------------------------------------------ + def doc_uses_tables(self): + """Does this report require the ability to generate tables in + the file format. Override this for documents that do need + table support.""" + return 0 + + #------------------------------------------------------------------------ + # + # Functions related to selecting/changing the current file format. + # + #------------------------------------------------------------------------ + def make_doc_menu(self): + """Build a menu of document types that are appropriate for + this text report. This menu will be generated based upon + whether the document requires table support, etc.""" + Plugins.get_text_doc_menu(self.format_menu, self.doc_uses_tables(), + self.doc_type_changed) + + def make_document(self): + """Create a document of the type requested by the user.""" + self.doc = self.format(self.selected_style,self.paper, + self.template_name,self.orien) + + #------------------------------------------------------------------------ + # + # Functions related to creating the actual report document. + # + #------------------------------------------------------------------------ + def make_report(self): + """Create the contents of the report. This is a simple + default implementation suitable for testing. Is should be + overridden to produce a real report.""" + self.doc.start_paragraph("Title") + title = "Basic Report for %s" % self.name + self.doc.write_text(title) + self.doc.end_paragraph() + +class DrawReportDialog(ReportDialog): + """A class of ReportDialog customized for drawing based reports.""" + def __init__(self,database,person): + """Initialize a dialog to request that the user select options + for a basic drawing report. See the ReportDialog class for + more information.""" + ReportDialog.__init__(self,database,person) + + #------------------------------------------------------------------------ + # + # Functions related to selecting/changing the current file format. + # + #------------------------------------------------------------------------ + def make_doc_menu(self): + """Build a menu of document types that are appropriate for + this drawing report.""" + Plugins.get_draw_doc_menu(self.format_menu) + + def make_document(self): + """Create a document of the type requested by the user.""" + self.doc = self.format(self.selected_style,self.paper,self.orien) + + +class TemplateParser(handler.ContentHandler): + """ + Interface to the document template file + """ + def __init__(self,data,fpath): + """ + Creates a template parser. The parser loads map of tempate names + to the file containing the tempate. + + data - dictionary that holds the name to path mappings + fpath - filename of the XML file + """ + handler.ContentHandler.__init__(self) + self.data = data + self.path = fpath + + def setDocumentLocator(self,locator): + """Sets the XML document locator""" + self.locator = locator + + def startElement(self,tag,attrs): + """ + Loads the dictionary when an XML tag of 'template' is found. The format + XML tag is