From efb007259fdf455dfc97a1df0a8d662c78a128b0 Mon Sep 17 00:00:00 2001 From: Martin Hawlisch Date: Thu, 17 Mar 2005 12:51:14 +0000 Subject: [PATCH] * src/Exporter.py (build_info_page): proofreading; (build_format_page): shrink the size of the window by using tooltips to display the format description. * src/plugins/ExportVCard.py: New (unfinished) plugin to export addresses to pim applications. * src/plugins/vcardexport.glade: GUI for the Exporter Druid of vCard export * src/plugins/ExportVCalendar.py: New (unfinished) plugin to export events for calendaring applications. * src/plugins/vcalendarexport.glade: GUI for the Exporter Druid of vCalendar export svn: r4197 --- ChangeLog | 13 ++ src/Exporter.py | 9 +- src/plugins/ExportVCalendar.py | 318 ++++++++++++++++++++++++++++++ src/plugins/ExportVCard.py | 240 ++++++++++++++++++++++ src/plugins/vcalendarexport.glade | 189 ++++++++++++++++++ src/plugins/vcardexport.glade | 189 ++++++++++++++++++ 6 files changed, 953 insertions(+), 5 deletions(-) create mode 100644 src/plugins/ExportVCalendar.py create mode 100644 src/plugins/ExportVCard.py create mode 100644 src/plugins/vcalendarexport.glade create mode 100644 src/plugins/vcardexport.glade diff --git a/ChangeLog b/ChangeLog index e596678de..10c3a2991 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2005-03-17 Martin Hawlisch + * src/Exporter.py (build_info_page): proofreading; (build_format_page): + shrink the size of the window by using tooltips to display the + format description. + * src/plugins/ExportVCard.py: New (unfinished) plugin to export + addresses to pim applications. + * src/plugins/vcardexport.glade: GUI for the Exporter Druid of + vCard export + * src/plugins/ExportVCalendar.py: New (unfinished) plugin to export + events for calendaring applications. + * src/plugins/vcalendarexport.glade: GUI for the Exporter Druid + of vCalendar export + 2005-03-16 Alex Roitman * src/GenericFilter.py (HasTextMatchingSubstringOf): Minor. * src/plugins/FilterEditor.py (EditRule:__init__): Typo. diff --git a/src/Exporter.py b/src/Exporter.py index 3c4b71523..64c7f32d5 100644 --- a/src/Exporter.py +++ b/src/Exporter.py @@ -140,7 +140,7 @@ class Exporter: 'in any of the several formats supported by GRAMPS. ' 'This can be used to make a copy of your data, backup ' 'your data, or convert it to a format that will allow ' - 'you to trasnfer it to a different program.\n\n' + 'you to transfer it to a different program.\n\n' 'If you change your mind during this process, you ' 'can safely press the Cancel button at any time and your ' 'present database will still be intact.')) @@ -247,6 +247,8 @@ class Exporter: table.set_row_spacings(6) table.set_col_spacings(6) + tip = gtk.Tooltips() + group = None for ix in range(len(self.exports)): title = self.exports[ix][1] @@ -257,10 +259,7 @@ class Exporter: group = button self.format_buttons.append(button) table.attach(button,0,2,2*ix,2*ix+1) - label = gtk.Label(description) - label.set_line_wrap(True) - label.set_alignment(0,0.5) - table.attach(label,1,2,2*ix+1,2*ix+2,xpadding=24) + tip.set_tip(button,description) box.add(table) box.show_all() diff --git a/src/plugins/ExportVCalendar.py b/src/plugins/ExportVCalendar.py new file mode 100644 index 000000000..19f8f88be --- /dev/null +++ b/src/plugins/ExportVCalendar.py @@ -0,0 +1,318 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2004 Martin Hawlisch +# +# 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$ + +"Export Events to vCalendar" + +#------------------------------------------------------------------------- +# +# Standard Python Modules +# +#------------------------------------------------------------------------- +import os +import string +import time +import re + +#------------------------------------------------------------------------- +# +# GNOME/GTK modules +# +#------------------------------------------------------------------------- +import gtk +import gtk.glade +import gnome + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +import RelLib +import GenericFilter +import const +import Utils +import Date +import Errors +from gettext import gettext as _ +from QuestionDialog import ErrorDialog + +_title_string = _("Export to vCalendar") + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +class CalendarWriterOptionBox: + """ + Create a VBox with the option widgets and define methods to retrieve + the options. + """ + def __init__(self,person): + self.person = person + + def get_option_box(self): + + glade_file = "%s/vcalendarexport.glade" % os.path.dirname(__file__) + if not os.path.isfile(glade_file): + glade_file = "plugins/vcalendarexport.glade" + + self.topDialog = gtk.glade.XML(glade_file,"calendarExport","gramps") + + filter_obj = self.topDialog.get_widget("filter") + self.copy = 0 + + all = GenericFilter.GenericFilter() + all.set_name(_("Entire Database")) + all.add_rule(GenericFilter.Everyone([])) + + des = GenericFilter.GenericFilter() + des.set_name(_("Descendants of %s") % self.person.get_primary_name().get_name()) + des.add_rule(GenericFilter.IsDescendantOf([self.person.get_handle(),1])) + + ans = GenericFilter.GenericFilter() + ans.set_name(_("Ancestors of %s") % self.person.get_primary_name().get_name()) + ans.add_rule(GenericFilter.IsAncestorOf([self.person.get_handle(),1])) + + com = GenericFilter.GenericFilter() + com.set_name(_("People with common ancestor with %s") % + self.person.get_primary_name().get_name()) + com.add_rule(GenericFilter.HasCommonAncestorWith([self.person.get_handle()])) + + self.filter_menu = GenericFilter.build_filter_menu([all,des,ans,com]) + filter_obj.set_menu(self.filter_menu) + + the_box = self.topDialog.get_widget('vbox1') + the_parent = self.topDialog.get_widget('dialog-vbox1') + the_parent.remove(the_box) + self.topDialog.get_widget("calendarExport").destroy() + return the_box + + def parse_options(self): + self.cfilter = self.filter_menu.get_active().get_data("filter") + + +class CalendarWriter: + def __init__(self,database,person,cl=0,filename="",option_box=None): + self.db = database + self.person = person + self.option_box = option_box + self.cl = cl + self.filename = filename + + self.plist = {} + self.flist = {} + + self.persons_details_done = [] + self.persons_notes_done = [] + self.person_ids = {} + + if not option_box: + self.cl_setup() + else: + self.option_box.parse_options() + + if self.option_box.cfilter == None: + for p in self.db.get_person_handles(sort_handles=False): + self.plist[p] = 1 + else: + try: + for p in self.option_box.cfilter.apply(self.db, self.db.get_person_handles(sort_handles=False)): + self.plist[p] = 1 + except Errors.FilterError, msg: + (m1,m2) = msg.messages() + ErrorDialog(m1,m2) + return + + self.flist = {} + for key in self.plist: + p = self.db.get_person_from_handle(key) + for family_handle in p.get_family_handle_list(): + self.flist[family_handle] = 1 + + def cl_setup(self): + for p in self.db.get_person_handles(sort_handles=False): + self.plist[p] = 1 + + self.flist = {} + + for key in self.plist: + p = self.db.get_person_from_handle(key) + for family_handle in p.get_family_handle_list(): + self.flist[family_handle] = 1 + + def writeln(self,text): + self.g.write('%s\n' % (text.encode('iso-8859-1'))) + + def export_data(self,filename): + + self.dirname = os.path.dirname (filename) + try: + self.g = open(filename,"w") + except IOError,msg: + msg2 = _("Could not create %s") % filename + ErrorDialog(msg2,str(msg)) + return 0 + except: + ErrorDialog(_("Could not create %s") % filename) + return 0 + + self.writeln("BEGIN:VCALENDAR"); + self.writeln("PRODID:-//GNU//Gramps//EN"); + self.writeln("Version:1.0"); + for key in self.plist: + self.write_person(key) + + for key in self.flist: + self.write_family(key) + + self.writeln(""); + self.writeln("END:VCALENDAR"); + + self.g.close() + return 1 + + def write_family(self,family_handle): + family = self.db.get_family_from_handle(family_handle) + if family: + event_list = family.get_event_list() + for event_handle in event_list: + event = self.db.get_event_from_handle(event_handle) + if event.get_name() == "Marriage": + m_date = event.get_date_object() + place_handle = event.get_place_handle() + if place_handle: + place = self.db.get_place_from_handle(place_handle) + self.write_vevent("Marriage of x and y", m_date, place.get_title()) + else: + self.write_vevent("Marriage of x and y", m_date) + + def write_person(self, person_handle): + person = self.db.get_person_from_handle(person_handle) + if person: + birth_handle = person.get_birth_handle() + if birth_handle: + birth = self.db.get_event_from_handle(birth_handle) + if birth: + b_date = birth.get_date_object() + place_handle = birth.get_place_handle() + if place_handle: + place = self.db.get_place_from_handle(place_handle) + self.write_vevent("Birth of %s" % person.get_primary_name().get_name(), b_date, place.get_title()) + else: + self.write_vevent("Birth of %s" % person.get_primary_name().get_name(), b_date) + death_handle = person.get_death_handle() + if death_handle: + death = self.db.get_event_from_handle(death_handle) + if death: + d_date = death.get_date_object() + place_handle = death.get_place_handle() + if place_handle: + place = self.db.get_place_from_handle(place_handle) + self.write_vevent("Death of %s" % person.get_primary_name().get_name(), d_date, place.get_title()) + else: + self.write_vevent("Death of %s" % person.get_primary_name().get_name(), d_date) + + + def format_single_date(self,subdate,thisyear,cal): + retval = "" + (day,month,year,sl) = subdate + + if thisyear: + year = 2004 + + if not cal == Date.CAL_GREGORIAN: + return "NGREG" + + if year > 0: + if month > 0: + if day > 0: + retval = "%s%02d%02d" % (year, month, day) + return retval + + + def format_date(self,date,thisyear=0): + retval = "" + strval = date.get_text() + if strval: + return "" + elif not date.is_empty(): + mod = date.get_modifier() + cal = cal = date.get_calendar() + if mod == Date.MOD_SPAN or mod == Date.MOD_RANGE: + start = self.format_single_date(date.get_start_date(),thisyear,cal) + end = self.format_single_date(date.get_stop_date(),thisyear,cal) + if start and end: + retval = "DTSTART:%sT000001\nDTEND:%sT235959" % (start,end) + elif mod == Date.MOD_NONE: + start = self.format_single_date(date.get_start_date(),thisyear,cal) + if start: + retval = "DTSTART:%sT000001\nDTEND:%sT235959" % (start,start) + return retval + + def write_vevent(self, event_text, date, location=""): + date_string = self.format_date(date) + if date_string is not "": + self.writeln(""); + self.writeln("BEGIN:VEVENT"); + self.writeln("SUMMARY:%s" % event_text); + if location: + self.writeln("LOCATION:%s" % location); + self.writeln(date_string) + self.writeln("END:VEVENT"); + + date_string = self.format_date(date,1) + self.writeln(""); + self.writeln("BEGIN:VEVENT"); + self.writeln("SUMMARY:Anniversary: %s" % event_text); + if location: + self.writeln("LOCATION:%s" % location); + self.writeln("RRULE:YD1 #0") + self.writeln(date_string) + self.writeln("END:VEVENT"); + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +def exportData(database,filename,person,option_box): + ret = 0 + try: + cw = CalendarWriter(database,person,0,filename,option_box) + ret = cw.export_data(filename) + except: + import DisplayTrace + DisplayTrace.DisplayTrace() + return ret + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +_title = _('vCalendar') +_description = _('vCalendar is used in many calendaring and pim applications.') +_config = (_('vCalendar export options'),CalendarWriterOptionBox) +_filename = 'vcs' + +from PluginMgr import register_export +register_export(exportData,_title,_description,_config,_filename) diff --git a/src/plugins/ExportVCard.py b/src/plugins/ExportVCard.py new file mode 100644 index 000000000..b9085fca6 --- /dev/null +++ b/src/plugins/ExportVCard.py @@ -0,0 +1,240 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2004 Martin Hawlisch +# +# 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$ + +"Export Persons to vCard" + +#------------------------------------------------------------------------- +# +# Standard Python Modules +# +#------------------------------------------------------------------------- +import os +import string +import time +import re + +#------------------------------------------------------------------------- +# +# GNOME/GTK modules +# +#------------------------------------------------------------------------- +import gtk +import gtk.glade +import gnome + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +import RelLib +import GenericFilter +import const +import Utils +import Date +import Errors +from gettext import gettext as _ +from QuestionDialog import ErrorDialog + +_title_string = _("Export to vCard") + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +class CardWriterOptionBox: + """ + Create a VBox with the option widgets and define methods to retrieve + the options. + """ + def __init__(self,person): + self.person = person + + def get_option_box(self): + + glade_file = "%s/vcardexport.glade" % os.path.dirname(__file__) + if not os.path.isfile(glade_file): + glade_file = "plugins/vcardexport.glade" + + self.topDialog = gtk.glade.XML(glade_file,"vcardExport","gramps") + + filter_obj = self.topDialog.get_widget("filter") + self.copy = 0 + + all = GenericFilter.GenericFilter() + all.set_name(_("Entire Database")) + all.add_rule(GenericFilter.Everyone([])) + + des = GenericFilter.GenericFilter() + des.set_name(_("Descendants of %s") % self.person.get_primary_name().get_name()) + des.add_rule(GenericFilter.IsDescendantOf([self.person.get_handle(),1])) + + ans = GenericFilter.GenericFilter() + ans.set_name(_("Ancestors of %s") % self.person.get_primary_name().get_name()) + ans.add_rule(GenericFilter.IsAncestorOf([self.person.get_handle(),1])) + + com = GenericFilter.GenericFilter() + com.set_name(_("People with common ancestor with %s") % + self.person.get_primary_name().get_name()) + com.add_rule(GenericFilter.HasCommonAncestorWith([self.person.get_handle()])) + + self.filter_menu = GenericFilter.build_filter_menu([all,des,ans,com]) + filter_obj.set_menu(self.filter_menu) + + the_box = self.topDialog.get_widget('vbox1') + the_parent = self.topDialog.get_widget('dialog-vbox1') + the_parent.remove(the_box) + self.topDialog.get_widget("vcardExport").destroy() + return the_box + + def parse_options(self): + self.cfilter = self.filter_menu.get_active().get_data("filter") + +class CardWriter: + def __init__(self,database,person,cl=0,filename="",option_box=None): + self.db = database + self.person = person + self.option_box = option_box + self.cl = cl + self.filename = filename + + self.plist = {} + + if not option_box: + self.cl_setup() + else: + self.option_box.parse_options() + + if self.option_box.cfilter == None: + for p in self.db.get_person_handles(sort_handles=False): + self.plist[p] = 1 + else: + try: + for p in self.option_box.cfilter.apply(self.db, self.db.get_person_handles(sort_handles=False)): + self.plist[p] = 1 + except Errors.FilterError, msg: + (m1,m2) = msg.messages() + ErrorDialog(m1,m2) + return + + + def cl_setup(self): + for p in self.db.get_person_handles(sort_handles=False): + self.plist[p] = 1 + + def writeln(self,text): + self.g.write('%s\n' % (text.encode('iso-8859-1'))) + + def export_data(self,filename): + + self.dirname = os.path.dirname (filename) + try: + self.g = open(filename,"w") + except IOError,msg: + msg2 = _("Could not create %s") % filename + ErrorDialog(msg2,str(msg)) + return 0 + except: + ErrorDialog(_("Could not create %s") % filename) + return 0 + + for key in self.plist: + self.write_person(key) + + self.g.close() + return 1 + + + def write_person(self, person_handle): + person = self.db.get_person_from_handle(person_handle) + if person: + self.writeln("BEGIN:VCARD"); + prname = person.get_primary_name() + + self.writeln("FN:%s" % prname.get_regular_name()) + self.writeln("N:%s;%s;%s;%s;%s" % (prname.get_surname(), prname.get_first_name(), person.get_nick_name(), prname.get_surname_prefix(), prname.get_suffix())) + if prname.get_title(): + self.writeln("TITLE:%s" % prname.get_title()) + + birth_handle = person.get_birth_handle() + if birth_handle: + birth = self.db.get_event_from_handle(birth_handle) + if birth: + b_date = birth.get_date_object() + mod = b_date.get_modifier() + if not b_date.get_text() and not b_date.is_empty() and not mod == Date.MOD_SPAN and not mod == Date.MOD_RANGE: + (day,month,year,sl) = b_date.get_start_date() + if day > 0 and month > 0 and year > 0: + self.writeln("BDAY:%s-%02d-%02d" % (year,month,day)) + + address_list = person.get_address_list() + for address in address_list: + postbox = "" + ext = "" + street = address.get_street() + city = address.get_city() + state = address.get_state() + zip = address.get_postal_code() + country = address.get_country() + if street or city or state or zip or coutry: + self.writeln("ADR:%s;%s;%s;%s;%s;%s;%s" % (postbox,ext,street,city,state,zip,country)) + + phone = address.get_phone() + if phone: + self.writeln("TEL:%s" % phone) + + url_list = person.get_url_list() + for url in url_list: + href = url.get_path() + if href: + self.writeln("URL:%s" % href) + + self.writeln("END:VCARD"); + self.writeln(""); + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +def exportData(database,filename,person,option_box): + ret = 0 + try: + cw = CardWriter(database,person,0,filename,option_box) + ret = cw.export_data(filename) + except: + import DisplayTrace + DisplayTrace.DisplayTrace() + return ret + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +_title = _('vCard') +_description = _('vCard is used in many addressbook and pim applications.') +_config = (_('vCard export options'),CardWriterOptionBox) +_filename = 'vcf' + +from PluginMgr import register_export +register_export(exportData,_title,_description,_config,_filename) diff --git a/src/plugins/vcalendarexport.glade b/src/plugins/vcalendarexport.glade new file mode 100644 index 000000000..a10015d11 --- /dev/null +++ b/src/plugins/vcalendarexport.glade @@ -0,0 +1,189 @@ + + + + + + + + True + + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + True + 400 + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_DIALOG + GDK_GRAVITY_NORTH_WEST + False + + + + True + False + 8 + + + + True + GTK_BUTTONBOX_END + + + + True + True + True + gtk-cancel + True + GTK_RELIEF_NORMAL + True + 0 + + + + + + + True + True + True + gtk-ok + True + GTK_RELIEF_NORMAL + True + 0 + + + + + + + True + True + True + gtk-help + True + GTK_RELIEF_NORMAL + True + -11 + + + + + + 0 + False + True + GTK_PACK_END + + + + + + True + False + 0 + + + + 12 + True + 2 + 3 + False + 6 + 12 + + + + True + <b>Options</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + 0 + 3 + 0 + 1 + fill + + + + + + + True + _Filter: + True + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + filter + + + 1 + 2 + 1 + 2 + fill + + + + + + + True + True + -1 + + + + True + + + + + 2 + 3 + 1 + 2 + fill + + + + + + 0 + False + False + GTK_PACK_END + + + + + 0 + True + True + + + + + + + diff --git a/src/plugins/vcardexport.glade b/src/plugins/vcardexport.glade new file mode 100644 index 000000000..81d1fb082 --- /dev/null +++ b/src/plugins/vcardexport.glade @@ -0,0 +1,189 @@ + + + + + + + + True + + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + True + 400 + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_DIALOG + GDK_GRAVITY_NORTH_WEST + False + + + + True + False + 8 + + + + True + GTK_BUTTONBOX_END + + + + True + True + True + gtk-cancel + True + GTK_RELIEF_NORMAL + True + 0 + + + + + + + True + True + True + gtk-ok + True + GTK_RELIEF_NORMAL + True + 0 + + + + + + + True + True + True + gtk-help + True + GTK_RELIEF_NORMAL + True + -11 + + + + + + 0 + False + True + GTK_PACK_END + + + + + + True + False + 0 + + + + 12 + True + 2 + 3 + False + 6 + 12 + + + + True + <b>Options</b> + False + True + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + 0 + 3 + 0 + 1 + fill + + + + + + + True + _Filter: + True + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + filter + + + 1 + 2 + 1 + 2 + fill + + + + + + + True + True + -1 + + + + True + + + + + 2 + 3 + 1 + 2 + fill + + + + + + 0 + False + False + GTK_PACK_END + + + + + 0 + True + True + + + + + + +