diff --git a/gramps/TODO b/gramps/TODO
index 7acf1a3bc..f966bd505 100644
--- a/gramps/TODO
+++ b/gramps/TODO
@@ -1,8 +1,44 @@
+* 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 inside a gallery to allow for reordering of media objects.
+ This would allow for the elimination of the rather lame "Make Primary"
+ option.
+* 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.
+* Allow an image to be dropped onto the image box on the first tab of
+ the EditPerson dialog. This would make that image the first in the
+ photo list as well.
+* 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.
+* Progress report on the WebPage report generation. I've been told that
+ for large databases, this can take up to about 10 minutes. A progress
+ bar would give people an indication of where they are in the process,
+ and an indication that gramps has not locked up on them.
+* GEDCOM import should use the GEDCOM ID values as the GRAMPS ids if the
+ current database is empty. This would help us in the future if we want
+ to do an incremental update. Having the GEDCOM ID and the gramps ID
+ match up would be a good indication that these are the same people.
+ For example, @F001@ would become F001.
+* Completely revamp the merge utility.
+* 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.
* Dates do not understand alternate calendars
-* There are no sophisticated functions at all yet, such as merging of
- databases or finding duplicate people.
-* There are no logic rules yet, such as warning if a parent's birthday
- is after a child's.
* 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
diff --git a/gramps/src/EditPerson.py b/gramps/src/EditPerson.py
index 077c886de..071e540d7 100644
--- a/gramps/src/EditPerson.py
+++ b/gramps/src/EditPerson.py
@@ -48,6 +48,7 @@ import Config
from RelLib import *
import RelImage
import ImageSelect
+import sort
_ = intl.gettext
@@ -618,6 +619,16 @@ class EditPerson:
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.getMainFamily()
+ if (family):
+ new_order = reorder_child_list(self.person,family.getChildList())
+ family.setChildList(new_order)
+ for (family, rel1, rel2) in self.person.getAltFamilyList():
+ new_order = reorder_child_list(self.person,family.getChildList())
+ family.setChildList(new_order)
self.death.setDate(self.ddate.get_text())
dplace_obj = utils.get_place_from_list(self.dpcombo)
@@ -795,3 +806,69 @@ def cancel_callback(a):
def src_changed(parent):
parent.lists_changed = 1
+
+#-------------------------------------------------------------------------
+#
+# 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_birth(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 orde. 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_birth(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_birth(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_birth(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/gramps/src/RelLib.py b/gramps/src/RelLib.py
index 0bc110cd3..d5af514cc 100644
--- a/gramps/src/RelLib.py
+++ b/gramps/src/RelLib.py
@@ -1204,6 +1204,10 @@ class Family:
"""returns the list of children"""
return 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:
diff --git a/gramps/src/gramps.glade b/gramps/src/gramps.glade
index 8e84453b6..0bbc5a4f9 100644
--- a/gramps/src/gramps.glade
+++ b/gramps/src/gramps.glade
@@ -1882,6 +1882,7 @@
GtkCListchild_list80
+ Click column headers to sort. When sorted by birth date, drag and drop to reorder children.Trueselect_row
@@ -1893,23 +1894,77 @@
on_child_list_button_press_eventThu, 21 Dec 2000 20:47:47 GMT
- 6
- 210,60,85,150,75,50
+
+ click_column
+ on_child_list_click_column
+ Mon, 08 Oct 2001 20:29:49 GMT
+
+
+ row_move
+ on_child_list_row_move
+ Mon, 08 Oct 2001 20:31:41 GMT
+
+ 7
+ 210,60,85,150,75,50,25GTK_SELECTION_SINGLETrueGTK_SHADOW_IN
- GtkLabel
+ GtkHBoxCList:title
- label13
-
- GTK_JUSTIFY_CENTER
- False
- 0.5
- 0.5
- 0
- 0
+ hbox34
+ True
+ 0
+
+
+ GtkHBox
+ hbox35
+ False
+ 0
+
+ 0
+ False
+ False
+
+
+
+ GtkLabel
+ CList:title
+ label256
+
+ GTK_JUSTIFY_CENTER
+ False
+ 0.5
+ 0.5
+ 0
+ 0
+
+ 0
+ False
+ False
+
+
+
+
+ GtkArrow
+ cNameSort
+ 10
+ 10
+ False
+ GTK_ARROW_DOWN
+ GTK_SHADOW_OUT
+ 0.5
+ 0.5
+ 0
+ 0
+
+ 5
+ False
+ True
+
+
+
@@ -1939,16 +1994,59 @@
- GtkLabel
+ GtkHBoxCList:title
- label15
-
- GTK_JUSTIFY_CENTER
- False
- 0.5
- 0.5
- 0
- 0
+ hbox36
+ True
+ 0
+
+
+ GtkHBox
+ hbox37
+ False
+ 0
+
+ 0
+ False
+ False
+
+
+
+ GtkLabel
+ CList:title
+ label257
+
+ GTK_JUSTIFY_CENTER
+ False
+ 0.5
+ 0.5
+ 0
+ 0
+
+ 0
+ False
+ False
+
+
+
+
+ GtkArrow
+ cDateSort
+ 10
+ 10
+ GTK_ARROW_DOWN
+ GTK_SHADOW_OUT
+ 0.5
+ 0.5
+ 0
+ 0
+
+ 0
+ False
+ False
+
+
+
@@ -1976,6 +2074,19 @@
00
+
+
+ GtkLabel
+ CList:title
+ label15
+
+ GTK_JUSTIFY_CENTER
+ False
+ 0.5
+ 0.5
+ 0
+ 0
+
diff --git a/gramps/src/gramps_main.py b/gramps/src/gramps_main.py
index f1f8c3a89..aece425ae 100755
--- a/gramps/src/gramps_main.py
+++ b/gramps/src/gramps_main.py
@@ -123,6 +123,9 @@ canvas = None
sort_column = 5
sort_direct = SORT_ASCENDING
DataFilter = Filter.Filter("")
+c_birth_order = 6
+c_sort_column = c_birth_order
+c_sort_direct = SORT_ASCENDING
#-------------------------------------------------------------------------
#
@@ -1930,9 +1933,129 @@ def on_spouse_list_select_row(obj,row,b,c):
#-------------------------------------------------------------------------
#
-#
+# on_child_list_click_column
+#
+# Called when the user selects a column header on the person_list window.
+# Change the sort function (column 0 is the name column, and column 2 is
+# the birthdate column), set the arrows on the labels to the correct
+# orientation, and then call apply_filter to redraw the list
#
#-------------------------------------------------------------------------
+def on_child_list_click_column(clist,column):
+ if column == 0:
+ child_change_sort(clist,0,gtop.get_widget("cNameSort"))
+ elif (column == 3) or (column == 6):
+ child_change_sort(clist,6,gtop.get_widget("cDateSort"))
+ else:
+ return
+
+ sort_child_list(clist)
+ if id2col.has_key(active_child):
+ row = clist.find_row_from_data(id2col[active_child])
+ clist.moveto(row)
+
+#-------------------------------------------------------------------------
+#
+#
+#
+#-------------------------------------------------------------------------
+def child_change_sort(clist,column,arrow):
+ global c_sort_direct
+ global c_sort_column
+
+ cNameArrow.hide()
+ cDateArrow.hide()
+ arrow.show()
+
+ if c_sort_column == column:
+ if c_sort_direct == SORT_DESCENDING:
+ c_sort_direct = SORT_ASCENDING
+ arrow.set(GTK.ARROW_DOWN,2)
+ else:
+ c_sort_direct = SORT_DESCENDING
+ arrow.set(GTK.ARROW_UP,2)
+ else:
+ c_sort_direct = SORT_ASCENDING
+ c_sort_column = column
+ clist.set_sort_type(c_sort_direct)
+ clist.set_sort_column(c_sort_column)
+ clist.set_reorderable(c_sort_column == c_birth_order)
+
+
+def sort_child_list(clist):
+ clist.freeze()
+ clist.sort()
+ clist.thaw()
+
+#-------------------------------------------------------------------------
+#
+# on_child_list_row_move
+#
+# Validate whether or not this child can be moved within the clist.
+# This routine is called in the middle of the clist's callbacks, so
+# the state can be confusing. If the code is being debugged, the
+# display at this point shows that the list has been reordered when in
+# actuality it hasn't. All accesses to the clist data structure
+# reference the state just prior to the "move".
+#
+# This routine must keep/compute its own list indices as the functions
+# list.remove(), list.insert(), list.reverse() etc. do not affect the
+# values returned from the list.index() routine.
+#
+#-------------------------------------------------------------------------
+def on_child_list_row_move(clist,fm,to):
+ family = clist.get_data("f")
+
+ # Create a list based upon the current order of the clist
+ clist_order = []
+ for i in range(clist.rows):
+ clist_order = clist_order + [clist.get_row_data(i)]
+ child = clist_order[fm]
+
+ # This function deals with ascending order lists. Convert if
+ # necessary.
+ if (c_sort_direct == SORT_DESCENDING):
+ clist_order.reverse()
+ max_index = len(clist_order) - 1
+ fm = max_index - fm
+ to = max_index - to
+
+ # Create a new list to match the requested order
+ desired_order = clist_order[:fm] + clist_order[fm+1:]
+ desired_order = desired_order[:to] + [child] + desired_order[to:]
+
+ # Check birth date order in the new list
+ if (EditPerson.birth_dates_in_order(desired_order) == 0):
+ clist.emit_stop_by_name("row_move")
+ GnomeWarningDialog(_("Invalid move. Children must be ordered by birth date."))
+ return
+
+ # OK, this birth order works too. Update the family data structures.
+ family.setChildList(desired_order)
+
+ # Build a mapping of child item to list position. This would not
+ # be necessary if indices worked properly
+ i = 0
+ new_order = {}
+ for tmp in desired_order:
+ new_order[tmp] = i
+ i = i + 1
+
+ # Convert the original list back to whatever ordering is being
+ # used by the clist itself.
+ if (c_sort_direct == SORT_DESCENDING):
+ clist_order.reverse()
+
+ # Update the clist indices so any change of sorting works
+ i = 0
+ for tmp in clist_order:
+ clist.set_text(i, c_birth_order, "%2d"%new_order[tmp])
+ i = i + 1
+
+ # Need to save the changed order
+ utils.modified()
+
+
def on_open_activate(obj):
wFs = libglade.GladeXML(const.gladeFile, "dbopen")
wFs.signal_autoconnect({
@@ -2875,6 +2998,10 @@ def display_marriage(family):
active_child = None
i = 0
+ clist.set_sort_type(c_sort_direct)
+ clist.set_sort_column(c_sort_column)
+ clist.set_reorderable(c_sort_column == c_birth_order)
+
if family != None:
if active_person.getGender() == Person.male:
active_spouse = family.getMother()
@@ -2882,7 +3009,7 @@ def display_marriage(family):
active_spouse = family.getFather()
child_list = family.getChildList()
- child_list.sort(sort.by_birthdate)
+ # List is already sorted by birth date
attr = ""
for child in child_list:
status = _("Unknown")
@@ -2917,7 +3044,7 @@ def display_marriage(family):
attr = attr + "P"
clist.append([Config.nameof(child),child.getId(),\
- gender,birthday(child),status,attr])
+ gender,birthday(child),status,attr,"%2d"%i])
clist.set_row_data(i,child)
i=i+1
if i != 0:
@@ -2925,6 +3052,8 @@ def display_marriage(family):
clist.select_row(0,0)
else:
fv_prev.set_sensitive(0)
+ clist.set_data("f",family)
+ clist.sort()
else:
fv_prev.set_sensitive(0)
@@ -3304,6 +3433,7 @@ def main(arg):
global person_list, source_list, place_list, canvas, media_list
global topWindow, preview
global nameArrow, dateArrow, deathArrow
+ global cNameArrow, cDateArrow
global mid, mtype, mdesc, mpath, mdetails
rc_parse(const.gtkrcFile)
@@ -3345,6 +3475,8 @@ def main(arg):
('application/x-rootwin-drop',0,1)]
media_list.drag_source_set(GDK.BUTTON1_MASK|GDK.BUTTON3_MASK,t,GDK.ACTION_COPY)
media_list.drag_dest_set(DEST_DEFAULT_ALL,t,GDK.ACTION_COPY|GDK.ACTION_MOVE)
+ cNameArrow = gtop.get_widget("cNameSort")
+ cDateArrow = gtop.get_widget("cDateSort")
person_list.set_column_visibility(5,0)
person_list.set_column_visibility(6,0)
person_list.set_column_visibility(7,0)
@@ -3377,6 +3509,8 @@ def main(arg):
"on_canvas1_event" : on_canvas1_event,
"on_child_list_button_press_event" : on_child_list_button_press_event,
"on_child_list_select_row" : on_child_list_select_row,
+ "on_child_list_click_column" : on_child_list_click_column,
+ "on_child_list_row_move" : on_child_list_row_move,
"on_choose_parents_clicked" : on_choose_parents_clicked,
"on_contents_activate" : on_contents_activate,
"on_default_person_activate" : on_default_person_activate,
@@ -3436,7 +3570,10 @@ def main(arg):
person_list.set_column_visibility(1,Config.id_visible)
notebook.set_show_tabs(Config.usetabs)
- gtop.get_widget("child_list").set_column_visibility(4,Config.show_detail)
+ child_list = gtop.get_widget("child_list")
+ child_list.set_column_visibility(4,Config.show_detail)
+ child_list.set_column_visibility(6,0)
+ child_list.set_column_visibility(7,0)
if arg != None:
read_file(arg)
diff --git a/gramps/src/sort.py b/gramps/src/sort.py
index 64701b107..324df648a 100644
--- a/gramps/src/sort.py
+++ b/gramps/src/sort.py
@@ -46,7 +46,7 @@ def build_sort_birth(n):
d = n.start.day
if d == -1:
d = 99
- return "%04d%2d%2d" % (y,m,d)
+ return "%04d%02d%02d" % (y,m,d)
#-------------------------------------------------------------------------
#
@@ -63,7 +63,7 @@ def build_sort_death(n):
d = n.start.day
if d == -1:
d = 99
- return "%04d%2d%2d" % (y,m,d)
+ return "%04d%02d%02d" % (y,m,d)
#-------------------------------------------------------------------------
#
@@ -158,19 +158,3 @@ def by_last_name(first, second) :
#-------------------------------------------------------------------------
def by_last_name_backwards(first, second) :
return by_last_name(second,first)
-
-#-------------------------------------------------------------------------
-#
-#
-#
-#-------------------------------------------------------------------------
-def by_birthdate(first, second) :
-
- date1 = first.getBirth().getDateObj()
- date2 = second.getBirth().getDateObj()
- val = compare_dates(date1,date2)
- if val == 0:
- return by_last_name(first,second)
- return val
-
-