<!DOCTYPE article PUBLIC "-//GNOME//DTD DocBook PNG Variant V1.1//EN"[
<!ENTITY version "0.5.0"> <!-- replace with application version -->
]>

<!-- =============Document Header ============================= -->

<article id="index"> <!-- please do not change the id -->

  <artheader>
    <title>Writing Extentions for gramps</title>
    <copyright>
      <year>2001</year>
      <holder>Donald N. Allingham</holder>
    </copyright>

  <!-- translators: uncomment this:

  <copyright>
   <year>2001</year>
   <holder>ME-THE-TRANSLATOR (Latin translation)</holder>
  </copyright>

   -->

  <!-- do not put authorname in the header except in copyright - use
  section "authors" below -->

  <!-- Use this legal notice for online documents which depend on -->
  <!-- core GNOME packages.                                       -->
  <legalnotice id="legalnotice">
    <para>
    Permission is granted to copy, distribute and/or modify this
    document under the terms of the <ulink type="help"
    url="gnome-help:fdl"><citetitle>GNU Free Documentation
    License</citetitle></ulink>, 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 <ulink type="help"
    url="gnome-help:fdl">here</ulink>.
    </para>
    <para>
    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.
    </para>
  </legalnotice>

  <!-- Use this legal notice for documents which are placed on  -->
  <!-- the web, shipped in any way other than online documents  -->
  <!-- (eg. PS, PDF, or RTF), or which do not depend on the     -->
  <!-- core GNOME distribution.                                 -->
  <!--                                                          -->
  <!-- If you use this version, you must place the following    -->
  <!-- line in the document declaration at the top of your      -->
  <!-- document:                                                -->
  <!--   <!ENTITY FDL SYSTEM "fdl.sgml">                        -->
  <!-- and the following line at the bottom of your document    -->
  <!-- after the last </sect1>.                                 -->
  <!--   &FDL;                                                  -->
  <!--
  <legalnotice id="legalnotice">
    <para>
    Permission is granted to copy, distribute and/or modify this
    document under the terms of the <link linkend="fdl"><citetitle>GNU
    Free Documentation License</citetitle></link>, 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 in <xref linkend="fdl">.
    </para>
    <para>
    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.
    </para>
  </legalnotice>
  -->

  <!-- This is the manual version, not application version. --> 
    <releaseinfo>
       This is version 1.0 of the Writing Extentions for gramps manual.
    </releaseinfo>

  </artheader>

<!-- ============= Introduction ============================== -->
  <sect1 id="intro">
    <title>Introduction</title>
    <para>
    <application>gramps</application> 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.
    </para>
    <para>
    All plugins are written in the <application>python</application>
    language.
    </para>
    <sect2 id="intro-filter">
      <title>Filters</title>
      <para>
      A filter is a plugin that be used to temporarily display or hide
      individuals in the <interface>People View</interface>. 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.
      </para>
      <para>
      Filters should never alter a database.
      </para>
    </sect2>
    <sect2 id="intro-report">
      <title>Reports</title>
      <para>
      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.
      </para>
      <para>
      Plugins that conform to the reportplugin interface appear in the
        <menuchoice>
          <guimenu>Reports</guimenu>
        </menuchoice>
      menu and in the <interface>Report Selection</interface> dialog
      box.
      </para>
      <para>
      A report should never alter the database.
      </para>
    </sect2>
    <sect2 id="intro-tool">
      <title>Tools</title>
      <para>
      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.
      </para>
      <para>
      Plugins that conform to the tool plugin interface appear in the
        <menuchoice>
          <guimenu>Tools</guimenu>
        </menuchoice>
      menu and in the <interface>Tool Selection</interface> dialog
      box.
      </para>
      <para>
      A tool is allowed (and usually expected) to alter the database.
      </para>
    </sect2>
    <sect2 id="intro-import">
      <title>Import Filters</title>
      <para>
      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.
      </para>
      <para>
      Plugins that conform to the import filter calling syntax appear
      in the
        <menuchoice>
          <guimenu>File</guimenu>
          <guisubmenu>Import</guisubmenu>
        </menuchoice>
      menu.
      </para>
      <para>
      An import filter is allowed to modify the database.
      </para>
    </sect2>
    <sect2 id="intro-export">
      <title>Export Filters</title>
      <para>
      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.
      </para>
      <para>
      Plugins that conform to the export filter calling syntax appear
      in the
        <menuchoice>
          <guimenu>File</guimenu>
          <guisubmenu>Export</guisubmenu>
        </menuchoice>
      menu.
      </para>
      <para>
      An export filter should not alter the database.
      </para>
    </sect2>
  </sect1>

<!-- ============= Writing Filters ============================= -->

  <sect1 id="writingfilters">
    <title>Writing Filters</title>
    <para>
    Users can create their own filters and add them to
    <application>gramps</application>.  By adding the filter to the
    user's private filter directory (<filename
    class="directory">~/.gramps/filters</filename>), the filter will
    be automatically recognized the next time that the program is
    started.
    </para>
    <sect2 id="createfilter">
      <title>Creating a filter</title>
      <para>
      Each filter is a class derived from the
      <function>Filter.Filter</function> class.  The
      <function>__init__</function> task may be overridden, but if so,
      should call the <function>__init__</function> function on the
      <function>Filter.Filter</function> class.  The parent class
      provides the variable <function>self.text</function>, 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.
      </para>
      <para>
      All filter classes must define a <function>match</function>
      function. The function takes one argument (other than
      <function>self</function>), which is an object of type
      <function>Person</function> to compare against. The function
      should return a 1 if the person matches the filter, or a zero if
      the person does not.
      </para>
      <para>
      Each filter must be registered, so that
      <application>gramps</application> knows about it. This is
      accomplished by calling the
      <function>Filter.register_filter</function> 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 <application>gramps</application>, and helps
      the user choose the appropriate filter. The qualifier flag tells
      <application>gramps</application> whether or not the filter
      needs a qualifier string. If this flag is 0,
      <application>gramps</application> will disable the entry of a
      qualifier string.
      </para>
      <figure id="filtersrc">
        <title>Sample filter implementation</title>
        <programlisting>

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)

        </programlisting>
      </figure>
    </sect2>
  </sect1>

<!-- ============= Writing Reports ============================= -->

  <sect1 id="writingreports">
    <title>Writing Reports</title>
    <para>
    Users can create their own report generators and add them to
    <application>gramps</application>.  By adding the report generator
    to the user's private plugin directory (<filename
    class="directory">~/.gramps/plugins</filename>), the report
    generator will be automatically recognized the next time that the
    program is started.
    </para>
    <sect2 id="createreport">
      <title>Creating a report generator</title>
      <para>
      Fewer restrictions are made on report generators than on
      filters.  The report generator is passed the current
      <application>gramps</application> database and the active
      person.  The generator needs to take special care to make sure
      that it does not alter the database in anyway.
      </para>
      <para>
      A report generator is a function that takes two arguments
      &mdash; a database (of type <function>RelDataBase</function>)
      and the currently selected person (of type
      <function>Person</function>). When called, this task should
      generate the desired report.
      </para>
      <para>
      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.
      </para>
      <para>
      As with filters, the report generator must be registered before
      <application>gramps</application> will understand it. The report
      generator is registered using the
      <function>Plugins.register_report</function>. This function
      takes five arguments.
      </para>
      <itemizedlist>
        <listitem>
	  <para>
	  <guilabel>The report generation task</guilabel> This task
	  that generates the report.
	  </para>
	</listitem>
	<listitem>
	  <para>
	  <guilabel>The report category</guilabel> The category in
	  which the report is grouped in the
	  <menuchoice><guimenu>Reports</guimenu></menuchoice> menu and
	  in the <interface>Report Selection</interface> dialog.
	  </para>
	</listitem>
	<listitem>
	  <para>
	  <guilabel>The report name</guilabel>
	  The name of the report.
	  </para>
	</listitem>
	<listitem>
	  <para>
	  <guilabel>A text description of the report</guilabel> The
	  description appears in the report selection tool to provide
	  the user with a description of what the tools does.
	  </para>
	</listitem>
	<listitem>
	  <para>
	  <guilabel>A graphic logo in XPM format</guilabel> 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.
	  </para>
	</listitem>
      </itemizedlist> 
      <para>
      While only the task and report name are required, it is
      recommended to provide all five parameters.
      </para>
      <figure id="reportsrc">
        <title>Sample report implementation</title>
        <programlisting>

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__)
)
        </programlisting>
      </figure>
    </sect2>
    <sect2 id="alittlehelp">
      <title>A little help - Format Interfaces</title>
      <para>
      <application>gramps</application> 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, <application>gramps</application> derives interfaces to
      various document formats.  This means that by coding to the
      generic word processing class (<function>TextDoc</function>), a
      report generator can instant access to multiple file formats
      (such as HTML, OpenOffice, and AbiWord).
      </para>
      <para>
      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
      <application>KWord</application> or
      <application>LaTeX</application>) makes it easy for existing
      report generators to use the new formats.
      </para>
    </sect2>
  </sect1>

<!-- ============= Writing Tools ============================= -->

  <sect1 id="writingtools">
    <title>Writing Tools</title>
    <para>
    Users can create their own tools and add them to
    <application>gramps</application>.  By adding the tool to the
    user's private plugin directory (<filename
    class="directory">~/.gramps/plugins</filename>), the tool will be
    automatically recognized the next time that
    <application>gramps</application> is started.
    </para>
    <para>
    Unlike a report generator, a tool is allowed to modify the
    database.  The tool is passed the current
    <application>gramps</application> 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.
    </para>
    <para>
    As with filters and report generators, tools must be registered
    before <application>gramps</application> will understand it. The
    tool is registered using the
    <function>Plugins.register_tool</function>. This function takes
    four arguments.
    </para>
    <itemizedlist>
      <listitem>
        <para>
	<guilabel>The tool task</guilabel> This task
	that executes the tool.
	</para>
      </listitem>
      <listitem>
	<para>
	<guilabel>The tool category</guilabel> The category in which
	the tool is grouped in the
	<menuchoice><guimenu>Tools</guimenu></menuchoice> menu and in
	the <interface>Tool Selection</interface> dialog.
	</para>
      </listitem>
      <listitem>
	<para>
	<guilabel>The tool name</guilabel>
	The name of the tool.
	</para>
      </listitem>
      <listitem>
	<para>
	<guilabel>A text description of the tool</guilabel> The
	description appears in the Tool Selection dialog to provide
	the user with a description of what the tool does.
	</para>
      </listitem>
    </itemizedlist> 
    <para>
    While only the task and report name are required, it is
    recommended to provide all five parameters.
    </para>
    <figure id="toolsrc">
      <title>Sample tool implementation</title>
      <programlisting>

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"
)
      </programlisting>
    </figure>
  </sect1>

<!-- ============= Import Filters ============================= -->

  <sect1 id="writingimportfilters">
    <title>Writing Import Filters</title>
    <para>
    Import filters are similar to tools, since they are allowed to
    modify the databases. An import filter is a task that accepts
    three arguments &mdash; a database, the filename of the file that
    is to be imported, and a callback function.
    </para>
    <para>
    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.
    </para>
    <para>
    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.
    </para>
    <para>
    As with the other plugin types, an import filter must be
    registered with <application>gramps</application>. This is
    accomplished by calling the
    <function>Plugins.register_import</function> task. The
    <function>Plugins.register_import</function> accepts two arguments
    &mdash; the function the performs the import and a string
    providing a brief description. This description is used as the
    menu entry under the
    <menuchoice>
      <guimenu>File</guimenu>
      <guisubmenu>Import</guisubmenu>
    </menuchoice>
    menu.
    </para>
    <figure id="importexample">
      <title>Sample Import Implementation</title>
      <programlisting>

import Plugins

def gedcom_import(database,filename,callback):
    ... actual code ...

Plugins.register_import(gedcom_import,"GEDCOM import")

      </programlisting>
    </figure>
  </sect1>

<!-- ============= Export Filters ============================= -->

  <sect1 id="writingexportfilters">
    <title>Writing Export Filters</title>
    <para>
    Export filters are similar to report generators. They are not
    allowed to modify the database. An export filter accepts three
    arguments &mdash; a database, the filename of the file that is to
    be written, and a callback function.
    </para>
    <para>
    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.
    </para>
    <para>
    As with the other plugin types, an export filter must be
    registered with <application>gramps</application>. This is
    accomplished by calling the
    <function>Plugins.register_export</function> task. The
    <function>Plugins.register_export</function> accepts two arguments
    &mdash; the function the performs the import and a string
    providing a brief description. This description is used as the
    menu entry under the
    <menuchoice>
      <guimenu>File</guimenu>
      <guisubmenu>Export</guisubmenu>
    </menuchoice>
    menu.
    </para>
    <figure id="exportexample">
      <title>Sample Export Implementation</title>
      <programlisting>

import Plugins

def gedcom_export(database,filename,callback):
    ... actual code ...

Plugins.register_export(gedcom_export,"GEDCOM export")

      </programlisting>
    </figure>
  </sect1>
  <sect1 id="commontasks">
    <title>Common tasks</title> 
    <para>
    While this manual does not document the
    <application>gramps</application> database interface, this section
    shows a few common tasks.
    </para>
    <sect2 id="gettingnames">
      <title>Printing names of people</title>
      <para>
      This example shows how to display the name of people in the
      database. It assumes that the database is called
      <function>db</function>. To get a list of people, it calls the
      <function>getPersonMap</function> method, which returns a map of
      <application>gramps</application> ID to
      <function>Person</function> objects. Calling the
      <function>valus</function> method of the returned map returns a
      list of people. For each person, the primary name is extracted,
      and then the <function>Name</function> object's
      <function>getName</function> method is called to build a
      presentable name from the individual name components.
      </para>
      <figure id="displaynames">
        <title>Displaying names</title>
        <programlisting>

for person in db.getPersonMap().values():
    name = person.getPrimaryName()
    print name.getName()

	</programlisting>
      </figure>
    </sect2>
    <sect2 id="listingevents">
      <title>Displaying the events of person</title>
      <para>
      This example shows how to display the public events associated
      with a person. It assumes that the person is called
      <function>person</function>.
      </para>
      <figure id="eventexample">
        <title>Displaying Event Information</title>
        <programlisting>

for event in person.getEventList():
    if event.getPrivacy() == 0:
        print "Event:",event.getName()
	print "Date:",event.getDate()
	print "Place:",event.getPlaceName()

	</programlisting>
      </figure>
    </sect2>
    <sect2 id="printfamily">
      <title>Print the members of each family</title>
      <para>
      This example shows how to display the parents and children of
      each family in the database. It assumes that the database is called
      <function>db</function>.
      </para>
      <figure id="familyexample">
        <title>Displaying Family Information</title>
        <programlisting>

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()

	</programlisting>
      </figure>
    </sect2>
    <sect2 id="personsfamily">
      <title>Display the marriages/relationships of a person</title>
      <para>
      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 <function>person</function>.
      </para>
      <para>
      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 <function>getFather</function> and
      <function>getMother</function> to find the one that is not
      equal.  It should also be noted that the
      <function>getFather</function> and
      <function>getMother</function> methods will return None if noone
      has been associated with that role in the family.
      </para>
      <figure id="relexample">
        <title>Displaying Relationship Information</title>
        <programlisting>

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()

	</programlisting>
      </figure>
    </sect2>
  </sect1>

<!-- ============= Authors ================================ -->

  <sect1 id="authors">
    <title>Authors</title>
    <para>
    <application>gramps</application> was written by Don Allingham
    (<email>dallingham@users.sourceforge.net</email>). To find more
    information about <application>gramps</application>, please visit
    the <ulink url="http://gramps.sourceforge.net" type="http">gramps
    web page</ulink>.
    </para>
    <para>
    This manual was written by Don Allingham
    (<email>dallingham@users.sourceforge.net</email>).
    </para>

<!-- For translations: uncomment this:
    <para>
    Latin translation was done by ME
    (<email>MYNAME@MYADDRESS</email>). Please send all comments and
    suggestions regarding this translation to SOMEWHERE.
    </para>
-->
  </sect1>

<!-- ============= Application License ============================= -->

  <sect1 id="license">
    <title>License</title>
    <para>
    This program is free software; you can redistribute it and/or
    modify it under the terms of the <ulink type="help"
    url="gnome-help:gpl"> <citetitle>GNU General Public
    License</citetitle></ulink> as published by the Free Software
    Foundation; either version 2 of the License, or (at your option)
    any later version.
    </para>
    <para>
    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
    <citetitle>GNU General Public License</citetitle> for more
    details.
    </para>
    <para>
    A copy of the <citetitle>GNU General Public License</citetitle> is
    included as an appendix to the <citetitle>GNOME Users
    Guide</citetitle>.  You may also obtain a copy of the
    <citetitle>GNU General Public License</citetitle> from the Free
    Software Foundation by visiting <ulink type="http"
    url="http://www.fsf.org">their Web site</ulink> or by writing to
    <address>
    Free Software Foundation, Inc.  <street>59 Temple Place</street> -
    Suite 330 <city>Boston</city>, <state>MA</state>
    <postcode>02111-1307</postcode> <country>USA</country>
    </address>
    </para>
  </sect1>
</article>









