From c0f14c9070dc6c659252a9134d50e3568510f6e6 Mon Sep 17 00:00:00 2001 From: Benny Malengier Date: Sat, 18 Aug 2007 21:13:30 +0000 Subject: [PATCH] start work on new export manager svn: r8834 --- ChangeLog | 7 + po/POTFILES.in | 1 + src/ExportAssistant.py | 376 +++++++++++++++++++++++++++++++++++++++++ src/Makefile.am | 1 + src/ManagedWindow.py | 57 +++++-- src/ViewManager.py | 12 ++ 6 files changed, 444 insertions(+), 10 deletions(-) create mode 100644 src/ExportAssistant.py diff --git a/ChangeLog b/ChangeLog index 06a77fc19..936d11d03 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2007-08-18 Benny Malengier + * src/ExportAssistant.py: begin of new export manager based on gtk.Assistant... + * src/ManagedWindow.py : allow a gtk.Window to be a ManagedWindow itself + * src/ViewManager.py : temporarily hook calling new export assistant + * src/Makefile.am : Add ExportAssistant.py + * po/POTFILES.in : Add ExportAssistant.py + 2007-08-18 Brian Matherly * src/plugins/KinshipReport.py: Add kinship report * src/plugins/Makefile.am: Add KinshipReport.py diff --git a/po/POTFILES.in b/po/POTFILES.in index 9dc2f9559..a0bb2cdb5 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -19,6 +19,7 @@ src/DbManager.py src/DdTargets.py src/DisplayState.py src/Errors.py +src/ExportAssistant.py src/Exporter.py src/FontScale.py src/GrampsCfg.py diff --git a/src/ExportAssistant.py b/src/ExportAssistant.py new file mode 100644 index 000000000..7516fbdac --- /dev/null +++ b/src/ExportAssistant.py @@ -0,0 +1,376 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2004-2007 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 +# + +# Written by B.Malengier + +#------------------------------------------------------------------------- +# +# Python modules +# +#------------------------------------------------------------------------- +import os +import sys +from gettext import gettext as _ + +#------------------------------------------------------------------------- +# +# set up logging +# +#------------------------------------------------------------------------- +import logging +log = logging.getLogger(".ExportAssistant") + +#------------------------------------------------------------------------- +# +# Gnome modules +# +#------------------------------------------------------------------------- +import gtk + +#------------------------------------------------------------------------- +# +# gramps modules +# +#------------------------------------------------------------------------- + +import const +import Config +from PluginUtils import export_list +import Utils +import ManagedWindow + +#------------------------------------------------------------------------- +# +# Constants +# +#------------------------------------------------------------------------- +_gramps_png = os.path.join(const.image_dir,"gramps.png") +_splash_jpg = os.path.join(const.image_dir,"splash.jpg") + +#------------------------------------------------------------------------- +# +# ExportAssistant +# +#------------------------------------------------------------------------- + +class ExportAssistant(gtk.Assistant, ManagedWindow.ManagedWindow) : + """ + This class creates a GTK assistant to guide the user through the various + Save as/Export options. The overall goal is to keep things simple by + presenting few choice options on each assistant page. + + The export formats and options are obtained from the plugins + """ + + __gsignals__ = {"apply": "override", "cancel": "override", + "close": "override", "prepare": "override"} + + def __init__(self,dbstate,uistate): + """ + Set up the assistant, and build all the possible assistant pages. + Some page elements are left empty, since their contents depends + on the user choices and on the success of the attempted save. + + """ + self.dbstate = dbstate + self.uistate = uistate + + #set up Assisant + gtk.Assistant.__init__(self) + self.set_title('test') + + #set up ManagedWindow + self.top_title = _("Export Assistant") + ManagedWindow.ManagedWindow.__init__(self,uistate,[], + self.__class__) + self.set_window(self, None, self.top_title, isWindow=True) + + if self.dbstate.active: + self.person = self.dbstate.get_active_person() + else: + self.person = self.dbstate.db.find_initial_person() + + self.logo = gtk.gdk.pixbuf_new_from_file(_gramps_png) + self.splash = gtk.gdk.pixbuf_new_from_file(_splash_jpg) + + self.obtain_export_formats() + + self.previous_page = -1 + + #create the assistant pages + self.create_page_intro() + self.create_page_exporttypes() + self.create_page_options() + self.create_page_fileselect() + self.create_page_confirm() + self.create_page_progress() + self.create_page_summary() + + #ManagedWindow show method + ManagedWindow.ManagedWindow.show(self) + + def build_menu_names(self,obj): + ''' Override ManagedWindow method + ''' + return (self.top_title, None) + + def create_page_intro(self): + ''' Create the introduction page + ''' + label = gtk.Label(self.get_intro_text()) + label.set_line_wrap(True) + label.set_use_markup(True) + + page = label + page.show_all() + + self.append_page(page) + self.set_page_header_image(page, self.logo) + self.set_page_side_image(page, self.splash) + self.set_page_title(page, _('Saving your data')) + self.set_page_complete(page, True) + self.set_page_type(page, gtk.ASSISTANT_PAGE_INTRO) + + def create_page_exporttypes(self): + ''' Create the export type page + A Title label + A table of format radio buttons and their descriptions. + ''' + self.format_buttons = [] + + box = gtk.VBox() + box.set_border_width(12) + box.set_spacing(12) + + table = gtk.Table(2*len(self.exportformats),2) + table.set_row_spacings(6) + table.set_col_spacings(6) + + tip = gtk.Tooltips() + + group = None + for ix in range(len(self.exportformats)): + title = self.exportformats[ix][1] + description= self.exportformats[ix][2] + + button = gtk.RadioButton(group,title) + if not group: + group = button + self.format_buttons.append(button) + table.attach(button,0,2,2*ix,2*ix+1) + tip.set_tip(button,description) + + box.add(table) + + page = box + + page.show_all() + + self.append_page(page) + self.set_page_header_image(page, self.logo) + self.set_page_title(page, _('Choose the format you want to export to')) + #there is always one radio button selected + self.set_page_complete(page, True) + self.set_page_type(page, gtk.ASSISTANT_PAGE_CONTENT) + + def create_page_options(self): + pass + + def create_page_fileselect(self): + self.chooser = gtk.FileChooserWidget(gtk.FILE_CHOOSER_ACTION_SAVE) + #add border + self.chooser.set_border_width(12) + #global files, ask before overwrite + self.chooser.set_local_only(False) + self.chooser.set_do_overwrite_confirmation(True) + + folder, name = self.suggest_filename() + self.chooser.set_current_folder(folder) + self.chooser.set_current_name(name) + + #connect changes in filechooser with check to mark page complete + self.chooser.connect("selection-changed", self.check_fileselect) + self.chooser.connect("key-release-event", self.check_fileselect) + #first selection does not give a selection-changed event, grab the button + self.chooser.connect("button-release-event", self.check_fileselect) + #Note, we can induce an exotic error, delete filename, + # do not release button, click forward. We expect user not to do this + # In case he does, recheck on confirmation page! + + + self.chooser.show_all() + page = self.chooser + + self.append_page(page) + self.set_page_header_image(page, self.logo) + self.set_page_title(page, _('Select Save File')) + #see if page can be set as complete : + self.check_fileselect(page) + self.set_page_type(page, gtk.ASSISTANT_PAGE_CONTENT) + + def check_fileselect(self, filechooser, event=None): + ''' Given a filechooser, determine if it can be marked complete + in the Assistant + Used as normal callback and event callback. + ''' + filename = filechooser.get_filename() + folder = filechooser.get_current_folder() + #the file must be valid, not a folder, and folder must be valid + if filename and filename.strip and Utils.find_folder(filename) == '' \ + and folder and Utils.find_folder(folder): + #this page of the assistant is complete + self.set_page_complete(filechooser, True) + + else : + self.set_page_complete(filechooser, False) + + def create_page_confirm(self): + pass + + def create_page_progress(self): + pass + + def create_page_summary(self): + # Construct summary page + # As this is the last page needs to be of page_type + # gtk.ASSISTANT_PAGE_CONFIRM or gtk.ASSISTANT_PAGE_SUMMARY + label = gtk.Label('Thanks for using our Export Assistant!') + label.set_line_wrap(True) + label.set_use_markup(True) + label.show() + + page = label + self.append_page(page) + self.set_page_header_image(page, self.logo) + self.set_page_title(page, _('Summary')) + self.set_page_side_image(page, self.splash) + self.set_page_type(page, gtk.ASSISTANT_PAGE_SUMMARY) + + + def do_apply(self): + pass + + def do_cancel(self): + self.close() + + def do_close(self): + self.close() + + def do_prepare(self, page): + # prepare the next page, page_number is page we are on now + page_number = self.get_current_page() + page = self.get_nth_page(page_number) + + if page_number == 1 : + # store the selected export type: + self.exporttype = self.get_selected_format_index() + # create the correct option page: + + elif self.get_page_type(page) == gtk.ASSISTANT_PAGE_SUMMARY : + # The summary page, update the label and title + #determine success + success = True + if success: + conclusion_title = _('Your data has been saved') + conclusion_text = _( + 'The copy of your data has been ' + 'successfully saved. You may press OK button ' + 'now to continue.\n\n' + 'Note: the database currently opened in your GRAMPS ' + 'window is NOT the file you have just saved. ' + 'Future editing of the currently opened database will ' + 'not alter the copy you have just made. ') + #add test, what is dir + conclusion_text += '\n\n' + self.chooser.get_filename() + else: + conclusion_title = _('Saving failed'), + conclusion_text = _( + 'There was an error while saving your data. ' + 'You may try starting the export again.\n\n' + 'Note: your currently opened database is safe. ' + 'It was only ' + 'a copy of your data that failed to save.') + page.set_label(conclusion_text) + self.set_page_title(page, conclusion_title) + + #remember the previous page + self.__previous_page = page_number + + def close(self, *obj) : + #clean up ManagedWindow menu, then destroy window, bring forward parent + ManagedWindow.ManagedWindow.close(self,*obj) + + def obtain_export_formats(self): + """ + This method builds its own list of available exports. + The list is built from the PluginMgr.export_list list + and from the locally defined exports (i.e. native export defined here). + """ + self.exportformats = [item for item in export_list] + + def get_intro_text(self): + return _('Under normal circumstances, GRAMPS does not require you ' + 'to directly save your changes. All changes you make are ' + 'immediately saved to the database.\n\n' + 'This process will help you save a copy of your data ' + '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 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.') + + def get_selected_format_index(self): + """ + Query the format radiobuttons and return the index number + of the selected one. + """ + for ix in range(len(self.format_buttons)): + button = self.format_buttons[ix] + if button.get_active(): + return ix + else: + return 0 + + def suggest_filename(self): + """ + Prepare suggested filename and set it in the file chooser. + """ + ix = self.get_selected_format_index() + ext = self.exportformats[ix][4] + + # Suggested folder: try last export, then last import, then home. + default_dir = Config.get(Config.RECENT_EXPORT_DIR) + if len(default_dir)<=1: + default_dir = Config.get(Config.RECENT_IMPORT_DIR) + if len(default_dir)<=1: + default_dir = const.user_home + + if ext == 'gramps': + new_filename = os.path.join(default_dir,'data.gramps') + elif ext == 'burn': + new_filename = os.path.basename(self.dbstate.db.get_save_path()) + else: + new_filename = Utils.get_new_filename(ext,default_dir) + return (default_dir, os.path.split(new_filename)[1]) + + + diff --git a/src/Makefile.am b/src/Makefile.am index 4c35072d5..b0477881b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -48,6 +48,7 @@ gdir_PYTHON = \ DdTargets.py\ DisplayState.py\ Errors.py\ + ExportAssistant.py\ Exporter.py\ FontScale.py\ GrampsCfg.py\ diff --git a/src/ManagedWindow.py b/src/ManagedWindow.py index 7470419ae..6a41b45e4 100644 --- a/src/ManagedWindow.py +++ b/src/ManagedWindow.py @@ -310,6 +310,14 @@ class ManagedWindow: # Proceed with the class. ... + @param uistate gramps uistate + @param track {list of parent windows, [] if the main GRAMPS window + is the parent} + @param obj The object that is used to id the managed window, + The inheriting object needs a method build_menu_names(self,obj) + which works on this obj and creates menu labels + + """ window_key = self.build_window_key(obj) menu_label, submenu_label = self.build_menu_names(obj) @@ -345,10 +353,30 @@ class ManagedWindow: # On the top level: we use gramps top window self.parent_window = self.uistate.window - def set_window(self, window, title, text, msg=None): - set_titles(window, title, text, msg) - self.window = window - self.window.connect('delete-event', self.close) + def set_window(self, window, title, text, msg=None, isWindow=False): + ''' + Set the window that is managed. + + @param window if isWindow=False window must be a gtk.Window() object, otherwise None + @param title a label widget in which to write the title, None if not needed + @param text text to use as title of window and in title param + @param msg if not None, use msg as title of window instead of text + @param isWindow {if isWindow than self is the window + (so self inherits from gtk.Window and + from ManagedWindow.ManagedWindow) + if not isWindow, than window is the Window to manage, + and after this method self.window stores it. + } + + ''' + self.isWindow = isWindow + if self.isWindow : + set_titles(self, title, text, msg) + else : + set_titles(window, title, text, msg) + #closing the gtk.Window must also close ManagedWindow + self.window = window + self.window.connect('delete-event', self.close) def build_menu_names(self, obj): return ('Undefined Menu','Undefined Submenu') @@ -371,10 +399,16 @@ class ManagedWindow: self.get_widget(button_name).connect('clicked', function) def show(self): - assert self.window, "ManagedWindow: self.window does not exist!" - self.window.set_transient_for(self.parent_window) - self.opened = True - self.window.show_all() + if self.isWindow : + self.set_transient_for(self.parent_window) + self.opened = True + self.show_all() + + else : + assert self.window, "ManagedWindow: self.window does not exist!" + self.window.set_transient_for(self.parent_window) + self.opened = True + self.window.show_all() def close(self, *obj): """ @@ -390,8 +424,11 @@ class ManagedWindow: """ Present window (unroll/unminimize/bring to top). """ - assert self.window, "ManagedWindow: self.window does not exist!" - self.window.present() + if self.isWindow : + self.present(self) + else : + assert self.window, "ManagedWindow: self.window does not exist!" + self.window.present() #------------------------------------------------------------------------- # diff --git a/src/ViewManager.py b/src/ViewManager.py index 4a4b2a5f0..df6b460a9 100644 --- a/src/ViewManager.py +++ b/src/ViewManager.py @@ -110,6 +110,7 @@ UIDEFAULT = ''' + @@ -362,6 +363,8 @@ class ViewManager: None, self.save_as_activate), ('Export', 'gramps-export', _('_Export'), "e", None, self.export_data), + ('ExportNew', 'gramps-export', _('_ExportNew'), None, None, + self.exportnew_data), ('Abandon', gtk.STOCK_REVERT_TO_SAVED, _('_Abandon changes and quit'), None, None, self.abort), ('Reports', 'gramps-reports', _('_Reports'), None, @@ -1186,6 +1189,15 @@ class ViewManager: if self.state.db.db_is_open: import Exporter Exporter.Exporter(self.state, self.uistate) + + def exportnew_data(self, obj): + if self.state.db.db_is_open: + import ExportAssistant + try: + ExportAssistant.ExportAssistant(self.state, self.uistate) + except Errors.WindowActiveError: + return + def rebuild_report_and_tool_menus(self, tool_list, report_list): self.build_tools_menu(tool_list)