#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2001-2005  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
#

# $Id$

#-------------------------------------------------------------------------
#
# Python modules
#
#-------------------------------------------------------------------------
from gettext import gettext as _

#-------------------------------------------------------------------------
#
# GTK/Gnome modules
#
#-------------------------------------------------------------------------
import gobject
import gtk
import gtk.gdk

#-------------------------------------------------------------------------
#
# Gramps Modules
#
#-------------------------------------------------------------------------
import RelLib
import PageView
import EditPerson
import NameDisplay
import Utils
import DateHandler

#-------------------------------------------------------------------------
#
# Constants
#
#-------------------------------------------------------------------------
_PERSON = "p"
_BORN = _('b.')
_DIED = _('d.')
_BAPT = _('bap.')
_CHRI = _('chr.')
_BURI = _('bur.')
_CREM = _('crem.')


#-------------------------------------------------------------------------
#
# PedigreeView
#
#-------------------------------------------------------------------------
class PedView(PageView.PersonNavView):

    def __init__(self,dbstate,uistate):
        print "PedView.__init__"
        PageView.PersonNavView.__init__(self,'Pedigree View',dbstate,uistate)
        dbstate.connect('database-changed',self.change_db)
        dbstate.connect('active-changed',self.goto_active_person)
        self.force_size = 0 # Automatic resize
        self.db = dbstate.db

    def init_parent_signals_cb(self, widget, event):
        print "PedView.init_parent_signals_cb"
        self.notebook.disconnect(self.bootstrap_handler)
        self.notebook.parent.connect("size-allocate", self.size_request_cb)
        self.size_request_cb(widget.parent,event)
        
    def add_table_to_notebook( self, table):
        frame = gtk.ScrolledWindow(None,None)
        frame.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)
        frame.add_with_viewport(table)
        try:
            self.notebook.append_page(frame,None)
        except:
            # for PyGtk < 2.4
            self.notebook.append_page(frame,gtk.Label(""))

    def get_stock(self):
        """
        Returns the name of the stock icon to use for the display.
        This assumes that this icon has already been registered with
        GNOME as a stock icon.
        """
        return 'gramps-pedigree'

    def build_widget(self):
        print "PedView.build_widget"
        """
        Builds the interface and returns a gtk.Container type that
        contains the interface. This containter will be inserted into
        a gtk.Notebook page.
        """
        self.notebook = gtk.Notebook()
        self.notebook.connect("button-press-event", self.on_show_option_menu_cb)
        self.bootstrap_handler = self.notebook.connect("expose-event", self.init_parent_signals_cb)
        self.notebook.set_show_border(False)
        self.notebook.set_show_tabs(False)
            
        self.table_2 = gtk.Table(1,1,False)
        self.table_2.connect("button-press-event", self.on_show_option_menu_cb)
        self.add_table_to_notebook( self.table_2)

        self.table_3 = gtk.Table(1,1,False)
        self.table_3.connect("button-press-event", self.on_show_option_menu_cb)
        self.add_table_to_notebook( self.table_3)

        self.table_4 = gtk.Table(1,1,False)
        self.table_4.connect("button-press-event", self.on_show_option_menu_cb)
        self.add_table_to_notebook( self.table_4)

        self.table_5 = gtk.Table(1,1,False)
        self.table_5.connect("button-press-event", self.on_show_option_menu_cb)
        self.add_table_to_notebook( self.table_5)

        self.rebuild_trees(None)

        return self.notebook

    def ui_definition(self):
        """
        Specifies the UIManager XML code that defines the menus and buttons
        associated with the interface.
        """
        return '''<ui>
          <menubar name="MenuBar">
            <menu action="GoMenu">
              <placeholder name="CommonGo">
                <menuitem action="Back"/>
                <menuitem action="Forward"/>
                <separator/>
                <menuitem action="HomePerson"/>
                <separator/>
              </placeholder>
            </menu>
          </menubar>
          <toolbar name="ToolBar">
            <placeholder name="CommonNavigation">
              <toolitem action="Back"/>  
              <toolitem action="Forward"/>  
              <toolitem action="HomePerson"/>
            </placeholder>
          </toolbar>
        </ui>'''

    def define_actions(self):
        """
        Required define_actions function for PageView. Builds the action
        group information required. We extend beyond the normal here,
        since we want to have more than one action group for the PersonView.
        Most PageViews really won't care about this.

        Special action groups for Forward and Back are created to allow the
        handling of navigation buttons. Forward and Back allow the user to
        advance or retreat throughout the history, and we want to have these
        be able to toggle these when you are at the end of the history or
        at the beginning of the history.
        """

        PageView.PersonNavView.define_actions(self)
        self.add_action('HomePerson',gtk.STOCK_HOME,  "_Home",  callback=self.home)

    def change_db(self,db):
        print "PedView.change_db"
        """
        Callback associated with DbState. Whenenver the database
        changes, this task is called. In this case, we rebuild the
        columns, and connect signals to the connected database. Tere
        is no need to store the database, since we will get the value
        from self.state.db
        """
        self.db = db
        db.connect('person-add', self.person_updated_cb)
        db.connect('person-update', self.person_updated_cb)
        db.connect('person-delete', self.person_updated_cb)
        db.connect('person-rebuild', self.person_rebuild)
        self.rebuild_trees(None)
 
    def goto_active_person(self,handle=None):
        print "PedView.goto_active_person"
        if handle:
            person = self.db.get_person_from_handle(handle)
            self.rebuild_trees(person)
            self.handle_history(person.handle)
        else:
            self.rebuild_trees(None)
    
    def person_updated_cb(self,handle_list):
        print "PedView.person_updated_cb"
        self.rebuild_trees(self.dbstate.active)

    def person_rebuild(self):
        print "PedView.person_rebuild"
        self.rebuild_trees(self.dbstate.active)

    def person_edited_cb(self, p1=None, p2=None):
        print "PedView.person_edited_cb"

    def request_resize(self):
        print "PedView.request_resize"
        self.size_request_cb(self.notebook.parent,None,None)
        
    def size_request_cb(self, widget, event, data=None):
        print "PedView.size_request_cb"
        if self.force_size == 0:
            v = widget.get_allocation()
            page_list = range(0,self.notebook.get_n_pages())
            page_list.reverse()
            for n in page_list:
                p = self.notebook.get_nth_page(n).get_child().get_child().get_allocation()
                if v.width >= p.width and v.height > p.height:
                    self.notebook.set_current_page(n)
                    break;
        else:
            self.notebook.set_current_page(self.force_size-2)

    def rebuild_trees(self,person):
        pos_2 =((0,3,3,(3,4,5)),
                (2,0,3,None),
                (2,10,3,None))
        pos_3 =((0,4,5,(3,6,3)),
                (2,1,3,(5,2,3)),
                (2,9,3,(5,10,3)),
                (4,0,1,None),
                (4,4,1,None),
                (4,8,1,None),
                (4,12,1,None))
        pos_4 =((0, 5,5,(3, 7,5)),
                (2, 2,3,(5, 3,3)),
                (2,10,3,(5,11,3)),
                (4, 1,1,(7,1,1)),
                (4, 5,1,(7,5,1)),
                (4, 9,1,(7,9,1)),
                (4,13,1,(7,13,1)),
                (6, 0,1,None),
                (6, 2,1,None),
                (6, 4,1,None),
                (6, 6,1,None),
                (6, 8,1,None),
                (6,10,1,None),
                (6,12,1,None),
                (6,14,1,None),)
        pos_5 =((0,10,11,(3,15,3)),
                (2, 5,5,(5, 7,1)),
                (2,21,5,(5,23,1)),
                (4, 2,3,(7,3,1)),
                (4,10,3,(7,11,1)),
                (4,18,3,(7,19,1)),
                (4,26,3,(7,27,1)),
                (6, 1,1,(9,1,1)),
                (6, 5,1,(9,5,1)),
                (6, 9,1,(9,9,1)),
                (6,13,1,(9,13,1)),
                (6,17,1,(9,17,1)),
                (6,21,1,(9,21,1)),
                (6,25,1,(9,25,1)),
                (6,29,1,(9,29,1)),
                (8, 0,1,None),
                (8, 2,1,None),
                (8, 4,1,None),
                (8, 6,1,None),
                (8, 8,1,None),
                (8,10,1,None),
                (8,12,1,None),
                (8,14,1,None),
                (8,16,1,None),
                (8,18,1,None),
                (8,20,1,None),
                (8,22,1,None),
                (8,24,1,None),
                (8,26,1,None),
                (8,28,1,None),
                (8,30,1,None),)
        self.rebuild( self.table_2, pos_2, person)
        self.rebuild( self.table_3, pos_3, person)
        self.rebuild( self.table_4, pos_4, person)
        self.rebuild( self.table_5, pos_5, person)
        
        #gobject.idle_add(self.request_resize)


    def rebuild( self, table_widget, positions, active_person):
        print "PedView.rebuild"
        # Build ancestor tree
        lst = [None]*31
        self.find_tree(active_person,0,1,lst)

        # Purge current table content
        for child in table_widget.get_children():
            child.destroy()
        table_widget.resize(1,1)
        
        tooltip = gtk.Tooltips()
        
        debug = False
        if debug:
            xmax = 0
            ymax = 0
            for field in positions:
                x = field[0]+3
                if x > xmax:
                    xmax = x
                y = field[1]+field[2]
                if y > ymax:
                    ymax = y
            for x in range(0,xmax):
                for y in range(0,ymax):
                    label=gtk.Label("%d,%d"%(x,y))
                    frame = gtk.ScrolledWindow(None,None)
                    frame.set_shadow_type(gtk.SHADOW_ETCHED_OUT)
                    frame.set_policy(gtk.POLICY_NEVER,gtk.POLICY_NEVER)
                    frame.add(label)
                    table_widget.attach(frame,x,x+1,y,y+1,0,0,0,0)          
        
        for i in range(0,31):
            try:
                # Table placement for person data
                x = positions[i][0]
                y = positions[i][1]
                w = 3
                h = positions[i][2]
                
                if not lst[i]:
                    # No person -> show empty box
                    label = gtk.Label(" ")
                    frame = gtk.ScrolledWindow(None,None)
                    frame.set_shadow_type(gtk.SHADOW_OUT)
                    frame.set_policy(gtk.POLICY_NEVER,gtk.POLICY_NEVER)
                    frame.add_with_viewport(label)
                    if positions[i][2] > 1:
                        table_widget.attach(frame,x,x+w,y,y+h,gtk.EXPAND|gtk.FILL,gtk.EXPAND|gtk.FILL,0,0)
                    else:
                        table_widget.attach(frame,x,x+w,y,y+h,gtk.EXPAND|gtk.FILL,gtk.FILL,0,0)
                else:
                    text = gtk.Button(self.format_person(lst[i][0], positions[i][2]))
                    if i > 0 and positions[i][2] < 3:
                        tooltip.set_tip(text, self.format_person(lst[i][0], 11))
                    text.set_alignment(0.0,0.0)
                    gender = lst[i][0].get_gender()
                    if gender == RelLib.Person.MALE:
                        text.modify_bg( gtk.STATE_NORMAL, text.get_colormap().alloc_color("#F5FFFF"))
                    elif gender == RelLib.Person.FEMALE:
                        text.modify_bg( gtk.STATE_NORMAL, text.get_colormap().alloc_color("#FFF5FF"))
                    else:
                        text.modify_bg( gtk.STATE_NORMAL, text.get_colormap().alloc_color("#FFFFF5"))
                    white = text.get_colormap().alloc_color("white")
                    text.modify_bg( gtk.STATE_ACTIVE, white)
                    text.modify_bg( gtk.STATE_PRELIGHT, white)
                    text.modify_bg( gtk.STATE_SELECTED, white)
                    text.set_data(_PERSON,lst[i][0].get_handle())
                    text.connect("button-press-event", self.build_full_nav_menu_cb)
                    if positions[i][2] > 1:
                        table_widget.attach(text,x,x+w,y,y+h,gtk.EXPAND|gtk.FILL,gtk.EXPAND|gtk.FILL,0,0)
                    else:
                        table_widget.attach(text,x,x+w,y,y+h,gtk.EXPAND|gtk.FILL,gtk.FILL,0,0)
                    
                    # Connection lines
                    if i > 0:
                        x = positions[i][0]-1
                        y = positions[i][1]
                        w = 1
                        h = positions[i][2]
                        line = gtk.DrawingArea()
                        line.connect("expose-event", self.line_expose_cb)
                        line.set_data("idx", i)
                        line.set_data("rela", lst[i][1])
                        table_widget.attach(line,x,x+w,y,y+h,gtk.FILL,gtk.FILL,0,0)
                    
                # Marriage data
                if positions[i][3]:
                    # An empty label is used as fallback, to allow it to EXPAND.
                    # This gives a nicer layout
                    text = " "
                    try:
                        if lst[i] and lst[i][2]:
                            text = self.format_relation( lst[i][2], positions[i][3][2])
                    except IndexError:
                        pass
                    label = gtk.Label(text)
                    label.set_justify(gtk.JUSTIFY_LEFT)
                    label.set_line_wrap(True)
                    label.set_alignment(0.1,0.0)
                    x = positions[i][3][0]
                    y = positions[i][3][1]
                    w = 2
                    h = 1
                    if positions[i][3][2] > 1:
                        table_widget.attach(label,x,x+w,y,y+h,gtk.EXPAND|gtk.FILL,gtk.EXPAND|gtk.FILL,0,0)
                    else:
                        table_widget.attach(label,x,x+w,y,y+h,gtk.EXPAND|gtk.FILL,gtk.FILL,0,0)
            except IndexError:
                pass
        table_widget.show_all()

    def line_expose_cb(self, area, event):
        style = area.get_style()
        gc = style.fg_gc[gtk.STATE_NORMAL]
        alloc = area.get_allocation()
        idx = area.get_data("idx")
        rela = area.get_data("rela")
        if rela:
            gc.line_style = gtk.gdk.LINE_ON_OFF_DASH
        else:
            gc.line_style = gtk.gdk.LINE_SOLID
        gc.line_width = 3
        if idx %2 == 0:
            area.window.draw_line(gc, alloc.width, alloc.height/2, alloc.width/2, 0)
        else:
            area.window.draw_line(gc, alloc.width, alloc.height/2, alloc.width/2, alloc.height)
    
    def home(self,obj):
        print "PedView.home"
        defperson = self.dbstate.db.get_default_person()
        if defperson:
            self.dbstate.change_active_person(defperson)

    def edit_person_cb(self,obj):
        person_handle = obj.get_data(_PERSON)
        person = self.db.get_person_from_handle(person_handle)
        if person:
            EditPerson.EditPerson(self.dbstate, person, self.person_edited_cb)
            return True
        return False

    def on_show_option_menu_cb(self,obj,data=None):
        myMenu = gtk.Menu()
        self.add_settings_to_menu(myMenu)
        myMenu.popup(None,None,None,0,0)
        return(True);
    
    def on_show_child_menu(self,obj):
        """User clicked button to move to child of active person"""

        if self.dbstate.active:
            # 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.

            childlist = find_children(self.db,self.dbstate.active)
            if len(childlist) == 1:
                child = self.db.get_person_from_handle(childlist[0])
                if child:
                    self.dbstate.change_active_person(child)
            elif len(childlist) > 1:
                myMenu = gtk.Menu()
                for child_handle in childlist:
                    child = self.db.get_person_from_handle(child_handle)
                    cname = NameDisplay.displayer.display(child)
                    menuitem = gtk.MenuItem(None)
                    if find_children(self.db,child):
                        label = gtk.Label('<b><i>%s</i></b>' % cname)
                    else:
                        label = gtk.Label(cname)
                    label.set_use_markup(True)
                    label.show()
                    label.set_alignment(0,0)
                    menuitem.add(label)
                    myMenu.append(menuitem)
                    menuitem.set_data(_PERSON,child_handle)
                    menuitem.connect("activate",self.on_childmenu_changed)
                    menuitem.show()
                myMenu.popup(None,None,None,0,0)
            return 1
        return 0

    def on_childmenu_changed(self,obj):
        """Callback for the pulldown menu selection, changing to the person
           attached with menu item."""

        person_handle = obj.get_data(_PERSON)
        if person_handle:
            self.dbstate.change_active_handle(person_handle)
            return True
        return False
    
    def change_force_size_cb(self,event,data):
        if data in [0,2,3,4,5]:
            self.force_size = data
            self.size_request_cb(self.notebook.parent,None) # switch to matching size
    
    def find_tree(self,person,index,depth,lst,val=0):
        """Recursively build a list of ancestors"""

        if depth > 5 or person == None:
            return
        lst[index] = (person,val,None)

        parent_families = person.get_parent_family_handle_list()
        if parent_families:
            (family_handle,m,f) = parent_families[0]
        else:
            return
        if family_handle:
            mrel = m != RelLib.Person.CHILD_BIRTH
            frel = f != RelLib.Person.CHILD_BIRTH

        family = self.db.get_family_from_handle(family_handle)
        if family != None:
            lst[index] = (person,val,family)
            father_handle = family.get_father_handle()
            if father_handle != None:
                father = self.db.get_person_from_handle(father_handle)
                self.find_tree(father,(2*index)+1,depth+1,lst,frel)
            mother_handle = family.get_mother_handle()
            if mother_handle != None:
                mother = self.db.get_person_from_handle(mother_handle)
                self.find_tree(mother,(2*index)+2,depth+1,lst,mrel)

    def add_nav_portion_to_menu(self,menu):
        """
        This function adds a common history-navigation portion 
        to the context menu. Used by both build_nav_menu() and 
        build_full_nav_menu() methods. 
        """
        #back_sensitivity = self.parent.hindex > 0 
        #fwd_sensitivity = self.parent.hindex + 1 < len(self.parent.history)
        entries = [
            #(gtk.STOCK_GO_BACK,self.parent.back_clicked,back_sensitivity),
            #(gtk.STOCK_GO_FORWARD,self.parent.fwd_clicked,fwd_sensitivity),
            #FIXME: revert to stock item when German gtk translation is fixed
            #(gtk.STOCK_HOME,self.parent.on_home_clicked,1),
            (_("Home"),self.home,1),
            (None,None,0),
            #(_("Set anchor"),self.on_anchor_set,1),
            #(_("Remove anchor"),self.on_anchor_removed,1),
        ]

        for stock_id,callback,sensitivity in entries:
            item = gtk.ImageMenuItem(stock_id)
            #FIXME: remove when German gtk translation is fixed
            if stock_id == _("Home"):
                im = gtk.image_new_from_stock(gtk.STOCK_HOME,gtk.ICON_SIZE_MENU)
                im.show()
                item.set_image(im)
            if callback:
                item.connect("activate",callback)
            item.set_sensitive(sensitivity)
            item.show()
            menu.append(item)

    def add_settings_to_menu(self,menu):
        item = gtk.MenuItem(_("Tree size"))
        item.set_submenu(gtk.Menu())
        size_menu = item.get_submenu()
        
        current_image = gtk.image_new_from_stock(gtk.STOCK_APPLY,gtk.ICON_SIZE_MENU)
        current_image.show()
        
        entry = gtk.ImageMenuItem(_("Automatic"))
        entry.connect("activate", self.change_force_size_cb,0)
        if self.force_size == 0:
            entry.set_image(current_image)
        entry.show()
        size_menu.append(entry)

        for n in range(2,6):
            entry = gtk.ImageMenuItem(_("%d generations") % n)
            if self.force_size == n:
                entry.set_image(current_image)
            entry.connect("activate", self.change_force_size_cb,n)
            entry.show()
            size_menu.append(entry)
        
        size_menu.show()
        item.show()
        menu.append(item)
        

    def build_full_nav_menu_cb(self,obj,event):
        """
        Builds the full menu (including Siblings, Spouses, Children, 
        and Parents) with navigation.
        """
        
        menu = gtk.Menu()
        menu.set_title(_('People Menu'))

        person_handle = obj.get_data(_PERSON)
        person = self.db.get_person_from_handle(person_handle)
        if not person:
            return 0

        go_image = gtk.image_new_from_stock(gtk.STOCK_JUMP_TO,gtk.ICON_SIZE_MENU)
        go_image.show()
        go_item = gtk.ImageMenuItem(NameDisplay.displayer.display(person))
        go_item.set_image(go_image)
        go_item.set_data(_PERSON,person_handle)
        go_item.connect("activate",self.on_childmenu_changed)
        go_item.show()
        menu.append(go_item)

        #edit_image = gtk.image_new_from_stock(gtk.STOCK_JUMP_TO,gtk.ICON_SIZE_MENU)
        #edit_image.show()
        edit_item = gtk.ImageMenuItem(gtk.STOCK_EDIT)
        #edit_item.set_image(edit_image)
        edit_item.set_data(_PERSON,person_handle)
        edit_item.connect("activate",self.edit_person_cb)
        edit_item.show()
        menu.append(edit_item)

        # Go over spouses and build their menu
        item = gtk.MenuItem(_("Spouses"))
        fam_list = person.get_family_handle_list()
        no_spouses = 1
        for fam_id in fam_list:
            family = self.db.get_family_from_handle(fam_id)
            if family.get_father_handle() == person.get_handle():
                sp_id = family.get_mother_handle()
            else:
                sp_id = family.get_father_handle()
            spouse = self.db.get_person_from_handle(sp_id)
            if not spouse:
                continue

            if no_spouses:
                no_spouses = 0
                item.set_submenu(gtk.Menu())
                sp_menu = item.get_submenu()

            go_image = gtk.image_new_from_stock(gtk.STOCK_JUMP_TO,gtk.ICON_SIZE_MENU)
            go_image.show()
            sp_item = gtk.ImageMenuItem(NameDisplay.displayer.display(spouse))
            sp_item.set_image(go_image)
            sp_item.set_data(_PERSON,sp_id)
            sp_item.connect("activate",self.on_childmenu_changed)
            sp_item.show()
            sp_menu.append(sp_item)

        if no_spouses:
            item.set_sensitive(0)

        item.show()
        menu.append(item)
        
        # Go over siblings and build their menu
        item = gtk.MenuItem(_("Siblings"))
        pfam_list = person.get_parent_family_handle_list()
        no_siblings = 1
        for (f,mrel,frel) in pfam_list:
            fam = self.db.get_family_from_handle(f)
            sib_list = fam.get_child_handle_list()
            for sib_id in sib_list:
                if sib_id == person.get_handle():
                    continue
                sib = self.db.get_person_from_handle(sib_id)
                if not sib:
                    continue

                if no_siblings:
                    no_siblings = 0
                    item.set_submenu(gtk.Menu())
                    sib_menu = item.get_submenu()

                go_image = gtk.image_new_from_stock(gtk.STOCK_JUMP_TO,gtk.ICON_SIZE_MENU)
                go_image.show()
                sib_item = gtk.ImageMenuItem(NameDisplay.displayer.display(sib))
                sib_item.set_image(go_image)
                sib_item.set_data(_PERSON,sib_id)
                sib_item.connect("activate",self.on_childmenu_changed)
                sib_item.show()
                sib_menu.append(sib_item)

        if no_siblings:
            item.set_sensitive(0)
        item.show()
        menu.append(item)
        
        # Go over children and build their menu
        item = gtk.MenuItem(_("Children"))
        no_children = 1
        childlist = find_children(self.db,person)
        for child_handle in childlist:
            child = self.db.get_person_from_handle(child_handle)
            if not child:
                continue
        
            if no_children:
                no_children = 0
                item.set_submenu(gtk.Menu())
                child_menu = item.get_submenu()

            if find_children(self.db,child):
                label = gtk.Label('<b><i>%s</i></b>' % NameDisplay.displayer.display(child))
            else:
                label = gtk.Label(NameDisplay.displayer.display(child))

            go_image = gtk.image_new_from_stock(gtk.STOCK_JUMP_TO,gtk.ICON_SIZE_MENU)
            go_image.show()
            child_item = gtk.ImageMenuItem(None)
            child_item.set_image(go_image)
            label.set_use_markup(True)
            label.show()
            label.set_alignment(0,0)
            child_item.add(label)
            child_item.set_data(_PERSON,child_handle)
            child_item.connect("activate",self.on_childmenu_changed)
            child_item.show()
            child_menu.append(child_item)

        if no_children:
            item.set_sensitive(0)
        item.show()
        menu.append(item)

        # Go over parents and build their menu
        item = gtk.MenuItem(_("Parents"))
        no_parents = 1
        par_list = find_parents(self.db,person)
        for par_id in par_list:
            par = self.db.get_person_from_handle(par_id)
            if not par:
                continue

            if no_parents:
                no_parents = 0
                item.set_submenu(gtk.Menu())
                par_menu = item.get_submenu()

            if find_parents(self.db,par):
                label = gtk.Label('<b><i>%s</i></b>' % NameDisplay.displayer.display(par))
            else:
                label = gtk.Label(NameDisplay.displayer.display(par))

            go_image = gtk.image_new_from_stock(gtk.STOCK_JUMP_TO,gtk.ICON_SIZE_MENU)
            go_image.show()
            par_item = gtk.ImageMenuItem(None)
            par_item.set_image(go_image)
            label.set_use_markup(True)
            label.show()
            label.set_alignment(0,0)
            par_item.add(label)
            par_item.set_data(_PERSON,par_id)
            par_item.connect("activate",self.on_childmenu_changed)
            par_item.show()
            par_menu.append(par_item)

        if no_parents:
            item.set_sensitive(0)
        item.show()
        menu.append(item)
    
        # Add separator
        item = gtk.MenuItem(None)
        item.show()
        menu.append(item)

        # Add history-based navigation
        self.add_nav_portion_to_menu(menu)
        self.add_settings_to_menu(menu)
        menu.popup(None,None,None,event.button,event.time)
        return 1

    def format_relation( self, family, line_count):
        text = ""
        for event_ref in family.get_event_ref_list():
            event = self.db.get_event_from_handle(event_ref.ref)
            if event:
                if line_count < 3:
                    return DateHandler.get_date(event)
                i,s = event.get_type()
                name = Utils.family_relations[i]
                text += name
                text += "\n"
                text += DateHandler.get_date(event)
                text += "\n"
                text += self.get_place_name(event.get_place_handle())
                if line_count < 5:
                   return text;
                break
        return text

    def get_place_name( self, place_handle):
        text = ""
        place = self.db.get_place_from_handle(place_handle)
        if place:
            place_title = self.db.get_place_from_handle(place_handle).get_title()
            if place_title != "":
                if len(place_title) > 25:
                    text = place_title[:24]+"..."
                else:
                    text = place_title
        return text
        
    def format_person( self, person, line_count):
        if not person:
            return ""
        name = NameDisplay.displayer.display(person)
        if line_count < 3:
            return name
        birth_ref = person.get_birth_ref()
        bd=""
        bp=""
        if birth_ref:
            birth = self.db.get_event_from_handle(birth_ref.ref)
            bd = DateHandler.get_date(birth)
            bp = self.get_place_name(birth.get_place_handle())
        death_ref = person.get_death_ref()
        dd=""
        dp=""
        if death_ref:
            death = self.db.get_event_from_handle(death_ref.ref)
            dd = DateHandler.get_date(death)
            dp = self.get_place_name(death.get_place_handle())
        if line_count < 5:
            return "%s\n* %s\n+ %s" % (name,bd,dd)
        else:
            return "%s\n* %s\n  %s\n+ %s\n  %s" % (name,bd,bp,dd,dp)
            
            

#-------------------------------------------------------------------------
#
# Function to return children's list of a person
#
#-------------------------------------------------------------------------
def find_children(db,p):
    """
    Returns the list of all children's IDs for a person.
    """
    childlist = []
    for family_handle in p.get_family_handle_list():
        family = db.get_family_from_handle(family_handle)
        for child_handle in family.get_child_handle_list():
            childlist.append(child_handle)
    return childlist

#-------------------------------------------------------------------------
#
# Function to return parent's list of a person
#
#-------------------------------------------------------------------------
def find_parents(db,p):
    """
    Returns the unique list of all parents' IDs for a person.
    """
    parentlist = []
    for (f,mrel,frel) in p.get_parent_family_handle_list():
        family = db.get_family_from_handle(f)
        father_handle = family.get_father_handle()
        mother_handle = family.get_mother_handle()
        if father_handle not in parentlist:
            parentlist.append(father_handle)
        if mother_handle not in parentlist:
            parentlist.append(mother_handle)
    return parentlist

#-------------------------------------------------------------------------
#
# Functions to build the text displayed in the details view of a DispBox
# aditionally used by PedigreeView to get the largest area covered by a DispBox
#
#-------------------------------------------------------------------------
def build_detail_string(db,person):

    detail_text = NameDisplay.displayer.display(person)

    def format_event(db, label, event):
        if not event:
            return u""
        ed = DateHandler.get_date(event)
        ep = None
        place_handle = event.get_place_handle()
        if place_handle:
            place_title = db.get_place_from_handle(place_handle).get_title()
            if place_title != "":
                if len(place_title) > 15:
                    ep = place_title[:14]+"..."
                else:
                    ep = place_title
        if ep:
            return u"\n%s %s, %s" % (label,ed,ep)
        return u"\n%s %s" % (label,ed)

    
    birth_ref = person.get_birth_ref()
    if birth_ref:
        detail_text += format_event(db, _BORN,
                                    db.get_event_from_handle(birth_ref.ref))
    else:
        for event_ref in person.get_event_ref_list():
            event = db.get_event_from_handle(event_ref.ref)
            if event and event.get_type()[0] == RelLib.Event.BAPTISM:
                detail_text += format_event(db, _BAPT, event)
                break
            if event and event.get_type()[0] == RelLib.Event.CHRISTEN:
                detail_text += format_event(db, _CHRI, event)
                break

    death_ref = person.get_death_ref()
    if death_ref:
        detail_text += format_event(db, _DIED,
                                    db.get_event_from_handle(death_ref.ref))
    else:
        for event_ref in person.get_event_ref_list():
            event = db.get_event_from_handle(event_ref.ref)
            if event and event.get_type()[0] == RelLib.Event.BURIAL:
                detail_text += format_event(db, _BURI, event)
                break
            if event and event.get_type()[0] == RelLib.Event.CREMATION:
                detail_text += format_event(db, _CREM, event)
                break

    return detail_text