diff --git a/ChangeLog b/ChangeLog index d52d55d0f..3d3c8c154 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2007-09-08 Don Allingham + * src/ViewManager.py: code cleanup + * src/FontScale.py: code cleanup + * src/GrampsWidgets.py: code cleanup + * src/ExportOptions.py: code cleanup + * src/GrampsDisplay.py: code cleanup + * src/DateEdit.py: code cleanup + * src/DbLoader.py: code cleanup + 2007-09-08 Benny Malengier * src/DataViews/_RelationView.py : expand/collapse, edit button on/off bug fix * src/GrampsWidgets.py : Expand widget, changes link labels diff --git a/src/DateEdit.py b/src/DateEdit.py index b83a4e785..7ee0c11cf 100644 --- a/src/DateEdit.py +++ b/src/DateEdit.py @@ -264,7 +264,7 @@ class DateEditorDialog(ManagedWindow.ManagedWindow): if response == gtk.RESPONSE_HELP: GrampsDisplay.help('adv-dates') elif response == gtk.RESPONSE_DELETE_EVENT: - return + break else: if response == gtk.RESPONSE_OK: (the_quality, the_modifier, the_calendar, @@ -277,9 +277,11 @@ class DateEditorDialog(ManagedWindow.ManagedWindow): value=the_value, text=the_text) self.close() - return def build_menu_names(self, obj): + """ + Define the menu entry for the ManagedWindows + """ return (_("Date selection"), None) def build_date_from_ui(self): diff --git a/src/DbLoader.py b/src/DbLoader.py index 4a8852969..3e66a5953 100644 --- a/src/DbLoader.py +++ b/src/DbLoader.py @@ -87,6 +87,9 @@ class DbLoader: self.uistate = uistate def open_file(self): + """ + Presents a file open dialog and opens the corresponding exsting file + """ choose = gtk.FileChooserDialog( _('GRAMPS: Open database'), self.uistate.window, @@ -140,63 +143,6 @@ class DbLoader: choose.destroy() return ('', '') - def new_file(self): - choose = gtk.FileChooserDialog( - _('GRAMPS: Create GRAMPS database'), - self.uistate.window, - gtk.FILE_CHOOSER_ACTION_SAVE, - (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, - gtk.STOCK_NEW, gtk.RESPONSE_OK)) - - # Always add automatic (macth all files) filter - add_all_files_filter(choose) - add_grdb_filter(choose) - - default_dir = get_default_dir() - new_filename = Utils.get_new_filename('grdb', default_dir) - - choose.set_current_folder(default_dir) - choose.set_current_name(os.path.split(new_filename)[1]) - - while (True): - response = choose.run() - if response == gtk.RESPONSE_OK: - filename = unicode(choose.get_filename(), - sys.getfilesystemencoding()) - if self.check_errors(filename): - return ('','') - - ext = os.path.splitext(filename)[1].lower() - if ext == ".ged": - filetype = const.APP_GEDCOM - elif ext == ".gramps": - filetype = const.APP_GRAMPS_XML - elif ext == ".grdb": - filetype = const.APP_GRAMPS - else: - filename = filename + ".grdb" - filetype = const.APP_GRAMPS - - choose.destroy() - try: - self.dbstate.db.close() - except: - pass - - self.read_file(filename, filetype) - - try: - os.chdir(os.path.dirname(filename)) - except: - return ('', '') - self.dbstate.db.db_is_open = True - return (filename, filetype) - else: - choose.destroy() - return ('', '') - choose.destroy() - return ('', '') - def save_as(self): choose = gtk.FileChooserDialog( _('GRAMPS: Create GRAMPS database'), @@ -343,8 +289,8 @@ class DbLoader: # Then we try all the known plugins (the_path, the_file) = os.path.split(filename) Config.set(Config.RECENT_IMPORT_DIR, the_path) - for (importData, mime_filter, mime_type, native_format, format_name) \ - in import_list: + for (importData, mime_filter, mime_type, native_format, + format_name) in import_list: if filetype == mime_type or the_file == mime_type: self.do_import(choose, importData, filename) return True diff --git a/src/ExportOptions.py b/src/ExportOptions.py index dbe83fc18..5313d33ab 100644 --- a/src/ExportOptions.py +++ b/src/ExportOptions.py @@ -17,10 +17,24 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # + +""" +Provides the common export options for Exporters +""" + +#------------------------------------------------------------------------- +# +# python modules +# +#------------------------------------------------------------------------- import gtk from gettext import gettext as _ -import RelLib +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- import Config from BasicUtils import name_displayer @@ -28,7 +42,7 @@ from Filters import GenericFilter, Rules #------------------------------------------------------------------------- # -# +# WriterOptionBox # #------------------------------------------------------------------------- class WriterOptionBox: @@ -38,13 +52,24 @@ class WriterOptionBox: """ def __init__(self, person): self.person = person + self.private = 0 + self.restrict = 0 + self.cfilter = None + self.restrict_check = None + self.private_check = None + self.filter_obj = None def get_option_box(self): + """ + Builds up a gtk.Table that contains the standard options + """ table = gtk.Table(3, 2) label = gtk.Label('Filter') self.filter_obj = gtk.ComboBox() - self.private_check = gtk.CheckButton(_('Do not include records marked private')) - self.restrict_check = gtk.CheckButton(_('Restrict data on living people')) + self.private_check = gtk.CheckButton( + _('Do not include records marked private')) + self.restrict_check = gtk.CheckButton( + _('Restrict data on living people')) self.private_check.set_active(Config.get(Config.EXPORT_NO_PRIVATE)) self.restrict_check.set_active(Config.get(Config.EXPORT_RESTRICT)) @@ -57,40 +82,19 @@ class WriterOptionBox: table.attach(self.private_check, 1, 2, 1, 2, yoptions=0) table.attach(self.restrict_check, 1, 2, 2, 3, yoptions=0) - #filter_obj = self.topDialog.get_widget("filter") - - all = GenericFilter() - all.set_name(_("Entire Database")) - - the_filters = [all] + entire_db = GenericFilter() + entire_db.set_name(_("Entire Database")) + the_filters = [entire_db] if self.person: - des = GenericFilter() - des.set_name(_("Descendants of %s") % - name_displayer.display(self.person)) - des.add_rule(Rules.Person.IsDescendantOf( - [self.person.get_gramps_id(), 1])) - - ans = GenericFilter() - ans.set_name(_("Ancestors of %s") - % name_displayer.display(self.person)) - ans.add_rule(Rules.Person.IsAncestorOf( - [self.person.get_gramps_id(), 1])) - - com = GenericFilter() - com.set_name(_("People with common ancestor with %s") % - name_displayer.display(self.person)) - com.add_rule(Rules.Person.HasCommonAncestorWith( - [self.person.get_gramps_id()])) - - the_filters += [des, ans, com] + the_filters += self.__define_person_filters() from Filters import CustomFilters the_filters.extend(CustomFilters.get_filters('Person')) model = gtk.ListStore(str, object) - for f in the_filters: - model.append(row=[f.get_name(), f]) + for item in the_filters: + model.append(row=[item.get_name(), item]) cell = gtk.CellRendererText() self.filter_obj.pack_start(cell, True) @@ -101,8 +105,40 @@ class WriterOptionBox: table.show() return table - def parse_options(self): + def __define_person_filters(self): + """ + Add person filters if the active person is defined + """ + des = GenericFilter() + des.set_name(_("Descendants of %s") % + name_displayer.display(self.person)) + des.add_rule(Rules.Person.IsDescendantOf( + [self.person.get_gramps_id(), 1])) + + ans = GenericFilter() + ans.set_name(_("Ancestors of %s") + % name_displayer.display(self.person)) + ans.add_rule(Rules.Person.IsAncestorOf( + [self.person.get_gramps_id(), 1])) + + com = GenericFilter() + com.set_name(_("People with common ancestor with %s") % + name_displayer.display(self.person)) + com.add_rule(Rules.Person.HasCommonAncestorWith( + [self.person.get_gramps_id()])) + return [des, ans, com] + + def parse_options(self): + """ + Extract the common values from the GTK widgets. After this function + is called, the following variables are defined: + + private = privacy requested + restrict = restrict information on living peoplel + cfitler = return the GenericFilter selected + + """ self.restrict = self.restrict_check.get_active() self.private = self.private_check.get_active() diff --git a/src/FontScale.py b/src/FontScale.py index 3233dad26..381cae9f8 100644 --- a/src/FontScale.py +++ b/src/FontScale.py @@ -17,7 +17,12 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # -_swiss = [ + +""" +Provides a rough estimate of the width of a text string. +""" + +SWISS = [ 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, @@ -45,7 +50,7 @@ _swiss = [ 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.584, 0.611, 0.556, 0.556, 0.556, 0.556, 0.500, 0.556, 0.500] -_swiss_b = [ +SWISS_B = [ 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, @@ -73,7 +78,7 @@ _swiss_b = [ 0.611, 0.611, 0.611, 0.611, 0.611, 0.611, 0.611, 0.584, 0.611, 0.611, 0.611, 0.611, 0.611, 0.556, 0.611, 0.556] -_swiss_i = [ +SWISS_I = [ 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, @@ -101,7 +106,7 @@ _swiss_i = [ 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.584, 0.611, 0.556, 0.556, 0.556, 0.556, 0.500, 0.556, 0.500] -_swiss_bi = [ +SWISS_BI = [ 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, @@ -129,7 +134,7 @@ _swiss_bi = [ 0.611, 0.611, 0.611, 0.611, 0.611, 0.611, 0.611, 0.584, 0.611, 0.611, 0.611, 0.611, 0.611, 0.556, 0.611, 0.556] -_roman = [ +ROMAN = [ 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, @@ -157,7 +162,7 @@ _roman = [ 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.564, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500] -_roman_b = [ +ROMAN_B = [ 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, @@ -185,7 +190,7 @@ _roman_b = [ 0.500, 0.556, 0.500, 0.500, 0.500, 0.500, 0.500, 0.570, 0.500, 0.556, 0.556, 0.556, 0.556, 0.500, 0.556, 0.500] -_roman_i = [ +ROMAN_I = [ 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, @@ -213,7 +218,7 @@ _roman_i = [ 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.500, 0.675, 0.500, 0.500, 0.500, 0.500, 0.500, 0.444, 0.500, 0.444] -_roman_bi = [ +ROMAN_BI = [ 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, @@ -241,19 +246,22 @@ _roman_bi = [ 0.500, 0.556, 0.500, 0.500, 0.500, 0.500, 0.500, 0.570, 0.500, 0.556, 0.556, 0.556, 0.556, 0.444, 0.500, 0.444] -_font_array = [ [_swiss, _swiss_b, _swiss_i, _swiss_bi ], - [_roman, _roman_b, _roman_i, _roman_bi ] ] +FONT_ARRAY = [ [SWISS, SWISS_B, SWISS_I, SWISS_BI ], + [ROMAN, ROMAN_B, ROMAN_I, ROMAN_BI ] ] #------------------------------------------------------------------------- # -# +# string_width # #------------------------------------------------------------------------- -def string_width(font,text): +def string_width(font, text): + """ + returns with width of a string in the specified font + """ i = font.get_type_face() j = font.get_bold() + font.get_italic()*2 s = font.get_size() - l = _font_array[i][j] + l = FONT_ARRAY[i][j] r = 0 for c in text: try: @@ -270,7 +278,7 @@ def string_trim(font, text, width, ellipses = "..."): i = font.get_type_face() j = font.get_bold() + font.get_italic()*2 s = font.get_size() - l = _font_array[i][j] + l = FONT_ARRAY[i][j] ellipses_length = 0 # get length of each letter for c in ellipses: diff --git a/src/GrampsDisplay.py b/src/GrampsDisplay.py index 8e80f82b0..6a24e1fb9 100644 --- a/src/GrampsDisplay.py +++ b/src/GrampsDisplay.py @@ -23,15 +23,21 @@ import const def help(target): + """ + Display the specified target in a help window. If this fails, + open the manual on the web site. + """ try: import gnome gnome.help_display('gramps',target) except: - # FIXME: as manual translations appear online, this needs to - # become more complex to directo to the correct language url(const.url_manual+'en/') def url(target): + """ + Open the specified URL in a browser. Attempt using the GNOME system if + available, if not, try to find a browser. + """ try: import gnome gnome.url_show(target) @@ -40,6 +46,10 @@ def url(target): def run_browser(url): + """ + Attempt of find a browswer, and launch with the browser with the + specified URL + """ import os search = os.environ['PATH'].split(':') diff --git a/src/GrampsWidgets.py b/src/GrampsWidgets.py index d871115f0..335e6a9a7 100644 --- a/src/GrampsWidgets.py +++ b/src/GrampsWidgets.py @@ -8,7 +8,7 @@ # 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, +# 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. @@ -75,7 +75,7 @@ except: INFO_ICON = gtk.STOCK_DIALOG_INFO # Enabling custom widgets to be included in Glade -def get_custom_handler(glade, function_name, widget_name, +def get_custom_handler(glade, function_name, widget_name, str1, str2, int1, int2): if function_name == 'ValidatableMaskedEntry': return ValidatableMaskedEntry() @@ -164,7 +164,7 @@ class LinkLabel(gtk.EventBox): class IconButton(gtk.Button): - def __init__(self, func, handle, icon=gtk.STOCK_EDIT, + def __init__(self, func, handle, icon=gtk.STOCK_EDIT, size=gtk.ICON_SIZE_MENU): gtk.Button.__init__(self) image = gtk.Image() @@ -366,7 +366,7 @@ class MonitoredCheckbox: class MonitoredEntry: - def __init__(self, obj, set_val, get_val, read_only=False, + def __init__(self, obj, set_val, get_val, read_only=False, autolist=None, changed=None): self.obj = obj self.set_val = set_val @@ -379,7 +379,7 @@ class MonitoredEntry: self.obj.set_editable(not read_only) if autolist: - AutoComp.fill_entry(obj,autolist) + AutoComp.fill_entry(obj, autolist) def reinit(self, set_val, get_val): self.set_val = set_val @@ -432,7 +432,7 @@ class MonitoredText: class MonitoredType: - def __init__(self, obj, set_val, get_val, mapping, custom, readonly=False, + def __init__(self, obj, set_val, get_val, mapping, custom, readonly=False, custom_values=None): self.set_val = set_val self.get_val = get_val @@ -467,7 +467,7 @@ class MonitoredType: class MonitoredDataType: - def __init__(self, obj, set_val, get_val, readonly=False, + def __init__(self, obj, set_val, get_val, readonly=False, custom_values=None, ignore_values=None): """ Constructor for the MonitoredDataType class. @@ -509,13 +509,13 @@ class MonitoredDataType: del map[key] self.sel = AutoComp.StandardCustomSelector( - map, - obj, - get_val().get_custom(), - default, + map, + obj, + get_val().get_custom(), + default, additional=custom_values) - self.sel.set_values((int(get_val()),str(get_val()))) + self.sel.set_values((int(get_val()), str(get_val()))) self.obj.set_sensitive(not readonly) self.obj.connect('changed', self.on_change) @@ -528,14 +528,14 @@ class MonitoredDataType: if value[0] == self.get_val().get_custom(): return value else: - return (value[0],'') + return (value[0], '') def update(self): val = self.get_val() if type(val) == tuple : self.sel.set_values(val) else: - self.sel.set_values((int(val),str(val))) + self.sel.set_values((int(val), str(val))) def on_change(self, obj): value = self.fix_value(self.sel.get_values()) @@ -543,7 +543,7 @@ class MonitoredDataType: class MonitoredMenu: - def __init__(self, obj, set_val, get_val, mapping, + def __init__(self, obj, set_val, get_val, mapping, readonly=False, changed=None): self.set_val = set_val self.get_val = get_val @@ -567,7 +567,7 @@ class MonitoredMenu: self.data[v] = index index += 1 self.obj.set_model(self.model) - self.obj.set_active(self.data.get(self.get_val(),0)) + self.obj.set_active(self.data.get(self.get_val(), 0)) def on_change(self, obj): self.set_val(self.model.get_value(obj.get_active_iter(), 1)) @@ -622,7 +622,7 @@ class PlaceEntry: Handles the selection of a existing or new Place. Supports Drag and Drop to select a place. """ - def __init__(self, dbstate, uistate, track, obj, set_val, + def __init__(self, dbstate, uistate, track, obj, set_val, get_val, add_del, share): self.obj = obj @@ -643,7 +643,7 @@ class PlaceEntry: if get_val(): self.set_button(True) p = self.db.get_place_from_handle(self.get_val()) - name = "%s [%s]" % (p.get_title(),p.gramps_id) + name = "%s [%s]" % (p.get_title(), p.gramps_id) else: name = u"" self.set_button(False) @@ -665,7 +665,7 @@ class PlaceEntry: obj.set_text(name) def after_edit(self, place): - name = "%s [%s]" % (place.get_title(),place.gramps_id) + name = "%s [%s]" % (place.get_title(), place.gramps_id) self.obj.set_text(name) def add_del_clicked(self, obj): @@ -679,7 +679,7 @@ class PlaceEntry: place = Place() try: - EditPlace(self.dbstate, self.uistate, self.track, + EditPlace(self.dbstate, self.uistate, self.track, place, self.place_added) except WindowActiveError: pass @@ -692,7 +692,7 @@ class PlaceEntry: def place_added(self, data): self.set_val(data.handle) - self.obj.set_text("%s [%s]" % (data.get_title(),data.gramps_id)) + self.obj.set_text("%s [%s]" % (data.get_title(), data.gramps_id)) self.set_button(True) def share_clicked(self, obj): @@ -701,7 +701,7 @@ class PlaceEntry: place = self.db.get_place_from_handle(self.get_val()) try: - EditPlace(self.dbstate, self.uistate, self.track, place, + EditPlace(self.dbstate, self.uistate, self.track, place, self.after_edit) except WindowActiveError: pass @@ -721,22 +721,22 @@ class PlaceEntry: if use_add: image = gtk.Image() - image.set_from_stock(gtk.STOCK_REMOVE,gtk.ICON_SIZE_BUTTON) + image.set_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_BUTTON) image.show() self.add_del.add(image) image = gtk.Image() - image.set_from_stock(gtk.STOCK_EDIT,gtk.ICON_SIZE_BUTTON) + image.set_from_stock(gtk.STOCK_EDIT, gtk.ICON_SIZE_BUTTON) image.show() self.share.add(image) self.tooltips.set_tip(self.share, _('Edit place')) self.tooltips.set_tip(self.add_del, _('Remove place')) else: image = gtk.Image() - image.set_from_stock(gtk.STOCK_ADD,gtk.ICON_SIZE_BUTTON) + image.set_from_stock(gtk.STOCK_ADD, gtk.ICON_SIZE_BUTTON) image.show() self.add_del.add(image) image = gtk.Image() - image.set_from_stock(gtk.STOCK_INDEX,gtk.ICON_SIZE_BUTTON) + image.set_from_stock(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON) image.show() self.share.add(image) self.tooltips.set_tip(self.share, _('Select an existing place')) @@ -747,7 +747,7 @@ class Statusbar(gtk.HBox): Statusbar can have any number of fields included, each identified by it's own bar id. It has by default one field with id = 0. This - defult field is used when no bar id is given in the relevant (push, pop, + defult field is used when no bar id is given in the relevant (push, pop, etc.) methods, thus Statusbar behaves as a single gtk.Statusbar. To add a new field use the "insert" method. Using the received bar id @@ -757,16 +757,16 @@ class Statusbar(gtk.HBox): __gtype_name__ = 'Statusbar' ##__gsignals__ = { - ##'text-popped': , - ##'text-pushed': , + ##'text-popped': , + ##'text-pushed': , ##} __gproperties__ = { - 'has-resize-grip': (gobject.TYPE_BOOLEAN, - 'Resize grip', - 'Whether resize grip is visible', - True, - gobject.PARAM_READWRITE), + 'has-resize-grip': (gobject.TYPE_BOOLEAN, + 'Resize grip', + 'Whether resize grip is visible', + True, + gobject.PARAM_READWRITE), } def __init__(self, min_width=30): @@ -812,7 +812,7 @@ class Statusbar(gtk.HBox): def _set_resize_grip(self): """Set the resize grip for the statusbar. - Resize grip is disabled for all statusbars except the last one, + Resize grip is disabled for all statusbars except the last one, which is set according to the "has-resize-grip" propery. """ @@ -824,7 +824,7 @@ class Statusbar(gtk.HBox): def _set_packing(self): """Set packing style of the statusbars. - All bars are packed with "expand"=True, "fill"=True parameters, + All bars are packed with "expand"=True, "fill"=True parameters, except the last one, which is packed with "expand"=False, "fill"=False. """ @@ -937,12 +937,12 @@ class FadeOut(gobject.GObject): Call my methods start() and stop() to control the fading. """ __gsignals__ = { - 'done': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, - ()), - 'color-changed': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, - (gtk.gdk.Color,)), + 'done': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, + ()), + 'color-changed': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, + (gtk.gdk.Color, )), } # How long time it'll take before we start (in ms) @@ -975,8 +975,8 @@ class FadeOut(gobject.GObject): rs += rinc gs += ginc bs += binc - col = gtk.gdk.color_parse("#%02X%02X%02X" % (int(rs) >> 8, - int(gs) >> 8, + col = gtk.gdk.color_parse("#%02X%02X%02X" % (int(rs) >> 8, + int(gs) >> 8, int(bs) >> 8)) self.emit('color-changed', col) yield True @@ -993,7 +993,7 @@ class FadeOut(gobject.GObject): return ##log.debug('_start_merging: Starting') - func = self._merge_colors(self._start_color, + func = self._merge_colors(self._start_color, gtk.gdk.color_parse(self.ERROR_COLOR)).next self._background_timeout_id = ( gobject.timeout_add(FadeOut.MERGE_COLORS_DELAY, func)) @@ -1034,7 +1034,7 @@ class FadeOut(gobject.GObject): self._widget.update_background(self._start_color) self._done = False -if gtk.pygtk_version < (2,8,0): +if gtk.pygtk_version < (2, 8, 0): gobject.type_register(FadeOut) class Tooltip(gtk.Window): @@ -1095,9 +1095,9 @@ class Tooltip(gtk.Window): # from gtktooltips.c:gtk_tooltips_paint_window def _on__expose_event(self, window, event): w, h = window.size_request() - window.style.paint_flat_box(window.window, - gtk.STATE_NORMAL, gtk.SHADOW_OUT, - None, window, "tooltip", + window.style.paint_flat_box(window.window, + gtk.STATE_NORMAL, gtk.SHADOW_OUT, + None, window, "tooltip", 0, 0, w, h) return False @@ -1124,17 +1124,17 @@ class Tooltip(gtk.Window): if self._show_timeout_id != -1: return - self._show_timeout_id = gobject.timeout_add(Tooltip.DEFAULT_DELAY, - self._real_display, + self._show_timeout_id = gobject.timeout_add(Tooltip.DEFAULT_DELAY, + self._real_display, widget) # This is tricky and contains quite a few hacks: # An entry contains 2 GdkWindows, one for the background and one for # the text area. The normal one, on which the (normally white) background # is drawn can be accessed through entry.window (after realization) -# The other window is the one where the cursor and the text is drawn upon, +# The other window is the one where the cursor and the text is drawn upon, # it's refered to as "text area" inside the GtkEntry code and it is called -# the same here. It can only be accessed through window.get_children()[0], +# the same here. It can only be accessed through window.get_children()[0], # since it's considered private to the entry. # # +-------------------------------------+ @@ -1149,7 +1149,7 @@ class Tooltip(gtk.Window): # +-------------------------------------| # # So, now we want to put an icon in the edge: -# An earlier approached by Lorzeno drew the icon directly on the text area, +# An earlier approached by Lorzeno drew the icon directly on the text area, # which is not desired since if the text is using the whole width of the # entry the icon will be drawn on top of the text. # Now what we want to do is to resize the text area and create a @@ -1168,7 +1168,7 @@ class Tooltip(gtk.Window): # # When resizing the text area the cursor and text is not moved into the # correct position, it'll still be off by the width of the icon window -# To fix this we need to call a private function, gtk_entry_recompute, +# To fix this we need to call a private function, gtk_entry_recompute, # a workaround is to call set_visiblity() which calls recompute() # internally. # @@ -1191,11 +1191,11 @@ class IconEntry(object): self._entry = entry self._tooltip = Tooltip(self) self._locked = False - entry.connect('enter-notify-event', + entry.connect('enter-notify-event', self._on_entry__enter_notify_event) - entry.connect('leave-notify-event', + entry.connect('leave-notify-event', self._on_entry__leave_notify_event) - entry.connect('notify::xalign', + entry.connect('notify::xalign', self._on_entry__notify_xalign) self._update_position() @@ -1278,17 +1278,17 @@ class IconEntry(object): self._text_area_pos = self._text_area.get_position() # PyGTK should allow default values for most of the values here. - win = gtk.gdk.Window(entry.window, - self._pixw, self._pixh, - gtk.gdk.WINDOW_CHILD, + win = gtk.gdk.Window(entry.window, + self._pixw, self._pixh, + gtk.gdk.WINDOW_CHILD, (gtk.gdk.ENTER_NOTIFY_MASK | - gtk.gdk.LEAVE_NOTIFY_MASK), - gtk.gdk.INPUT_OUTPUT, - 'icon window', - 0, 0, - entry.get_visual(), - entry.get_colormap(), - gtk.gdk.Cursor(entry.get_display(), gtk.gdk.LEFT_PTR), + gtk.gdk.LEAVE_NOTIFY_MASK), + gtk.gdk.INPUT_OUTPUT, + 'icon window', + 0, 0, + entry.get_visual(), + entry.get_colormap(), + gtk.gdk.Cursor(entry.get_display(), gtk.gdk.LEFT_PTR), '', '', True) self._icon_win = win win.set_user_data(entry) @@ -1365,13 +1365,13 @@ class IconEntry(object): # Draw background first color = self._entry.style.base_gc[self._entry.state] - win.draw_rectangle(color, True, + win.draw_rectangle(color, True, 0, 0, self._pixw, self._pixh) # If sensitive draw the icon, regardless of the window emitting the # event since makes it a bit smoother on resize if self._entry.flags() & gtk.SENSITIVE: - win.draw_pixbuf(None, self._pixbuf, 0, 0, 0, 0, + win.draw_pixbuf(None, self._pixbuf, 0, 0, 0, 0, self._pixw, self._pixh) def _update_position(self): @@ -1403,17 +1403,17 @@ HAVE_2_6 = gtk.pygtk_version[:2] == (2, 6) (DIRECTION_LEFT, DIRECTION_RIGHT) = (1, -1) -(INPUT_ASCII_LETTER, - INPUT_ALPHA, - INPUT_ALPHANUMERIC, +(INPUT_ASCII_LETTER, + INPUT_ALPHA, + INPUT_ALPHANUMERIC, INPUT_DIGIT) = range(4) INPUT_FORMATS = { - '0': INPUT_DIGIT, - 'L': INPUT_ASCII_LETTER, - 'A': INPUT_ALPHANUMERIC, - 'a': INPUT_ALPHANUMERIC, - '&': INPUT_ALPHA, + '0': INPUT_DIGIT, + 'L': INPUT_ASCII_LETTER, + 'A': INPUT_ALPHANUMERIC, + 'a': INPUT_ALPHANUMERIC, + '&': INPUT_ALPHA, } # Todo list: Other usefull Masks @@ -1422,13 +1422,13 @@ INPUT_FORMATS = { # C - Alpha, optional INPUT_CHAR_MAP = { - INPUT_ASCII_LETTER: lambda text: text in string.ascii_letters, - INPUT_ALPHA: unicode.isalpha, - INPUT_ALPHANUMERIC: unicode.isalnum, - INPUT_DIGIT: unicode.isdigit, + INPUT_ASCII_LETTER: lambda text: text in string.ascii_letters, + INPUT_ALPHA: unicode.isalpha, + INPUT_ALPHANUMERIC: unicode.isalnum, + INPUT_DIGIT: unicode.isdigit, } -(COL_TEXT, +(COL_TEXT, COL_OBJECT) = range(2) class MaskedEntry(gtk.Entry): @@ -1455,7 +1455,7 @@ class MaskedEntry(gtk.Entry): self.connect('focus-out-event', self._on_focus_out_event) self.connect('move-cursor', self._on_move_cursor) self.connect('button-press-event', self._on_button_press_event) - self.connect('notify::cursor-position', + self.connect('notify::cursor-position', self._on_notify_cursor_position) self._completion = None @@ -1482,7 +1482,7 @@ class MaskedEntry(gtk.Entry): # Virtual methods # PyGTK 2.6 does not support the virtual method do_size_allocate so # we have to use the signal instead - # PyGTK 2.9.0 and later (bug #327715) does not work using the old code, + # PyGTK 2.9.0 and later (bug #327715) does not work using the old code, # so we have to make this conditionally if HAVE_2_6: gsignal('size-allocate', 'override') @@ -1675,7 +1675,7 @@ class MaskedEntry(gtk.Entry): start, end = self._mask_fields[field] return end - start - def _shift_text(self, start, end, direction=DIRECTION_LEFT, + def _shift_text(self, start, end, direction=DIRECTION_LEFT, positions=1): """ Shift the text, to the right or left, n positions. Note that this @@ -1705,11 +1705,11 @@ class MaskedEntry(gtk.Entry): # Non-static char shoud be here. Get the next one (depending # on the direction, and the number of positions to skip.) # - # When shifting left, the next char will be on the right, + # When shifting left, the next char will be on the right, # so, it will be appended, to the new text. # Otherwise, when shifting right, the char will be # prepended. - next_pos = self._get_next_non_static_char_pos(i, direction, + next_pos = self._get_next_non_static_char_pos(i, direction, positions-1) # If its outside the bounds of the region, ignore it. @@ -1737,7 +1737,7 @@ class MaskedEntry(gtk.Entry): return new_text - def _get_next_non_static_char_pos(self, pos, direction=DIRECTION_LEFT, + def _get_next_non_static_char_pos(self, pos, direction=DIRECTION_LEFT, skip=0): """ Get next non-static char position, skiping some chars, if necessary. @@ -1827,7 +1827,7 @@ class MaskedEntry(gtk.Entry): def _get_completion(self): # Check so we have completion enabled, not this does not - # depend on the property, the user can manually override it, + # depend on the property, the user can manually override it, # as long as there is a completion object set completion = self.get_completion() if completion: @@ -1846,7 +1846,7 @@ class MaskedEntry(gtk.Entry): #completion.set_model(gtk.ListStore(str, object)) completion.set_model(gtk.ListStore(str)) completion.set_text_column(0) - #completion.connect("match-selected", + #completion.connect("match-selected", #self._on_completion__match_selected) self._completion = gtk.Entry.get_completion(self) @@ -1920,7 +1920,7 @@ class MaskedEntry(gtk.Entry): @param new: The char that wants to be inserted. @param pos: The position where it wants to be inserted. - @return: Returns None if it can be inserted. If it cannot be, + @return: Returns None if it can be inserted. If it cannot be, return the next position where it can be successfuly inserted. """ @@ -1954,7 +1954,7 @@ class MaskedEntry(gtk.Entry): return None -# When inserting new text, supose, the entry, at some time is like this, +# When inserting new text, supose, the entry, at some time is like this, # ahd the user presses '0', for instance: # -------------------------------- # | ( 1 2 ) 3 4 5 - 6 7 8 9 | @@ -2105,7 +2105,7 @@ class MaskedEntry(gtk.Entry): # Shift Left new_text = (text[:start] + - self._shift_text(start, _end, DIRECTION_LEFT, + self._shift_text(start, _end, DIRECTION_LEFT, end-start) + text[_end:]) @@ -2271,9 +2271,9 @@ class MaskedEntry(gtk.Entry): else: values[item] = None - model.append((item,)) + model.append((item, )) -if gtk.pygtk_version < (2,8,0): +if gtk.pygtk_version < (2, 8, 0): gobject.type_register(MaskedEntry) #number = (int, float, long) @@ -2293,32 +2293,32 @@ class ValidatableMaskedEntry(MaskedEntry): __gtype_name__ = 'ValidatableMaskedEntry' __gsignals__ = { - 'content-changed': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, - ()), - 'validation-changed': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, - (gobject.TYPE_BOOLEAN,)), - 'validate': (gobject.SIGNAL_RUN_LAST, - gobject.TYPE_PYOBJECT, - (gobject.TYPE_PYOBJECT,)), - 'changed': 'override', + 'content-changed': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, + ()), + 'validation-changed': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, + (gobject.TYPE_BOOLEAN, )), + 'validate': (gobject.SIGNAL_RUN_LAST, + gobject.TYPE_PYOBJECT, + (gobject.TYPE_PYOBJECT, )), + 'changed': 'override', } __gproperties__ = { - 'data-type': (gobject.TYPE_PYOBJECT, - 'Data Type of the widget', - 'Type object', - gobject.PARAM_READWRITE), - 'mandatory': (gobject.TYPE_BOOLEAN, - 'Mandatory', - 'Mandatory', - False, - gobject.PARAM_READWRITE), + 'data-type': (gobject.TYPE_PYOBJECT, + 'Data Type of the widget', + 'Type object', + gobject.PARAM_READWRITE), + 'mandatory': (gobject.TYPE_BOOLEAN, + 'Mandatory', + 'Mandatory', + False, + gobject.PARAM_READWRITE), } # FIXME put the data type support back - #allowed_data_types = (basestring, datetime.date, datetime.time, + #allowed_data_types = (basestring, datetime.date, datetime.time, #datetime.datetime, object) + number def __init__(self, data_type=None, err_color = "#ffd5d5", error_icon=ERROR_ICON): @@ -2367,8 +2367,8 @@ class ValidatableMaskedEntry(MaskedEntry): #if not issubclass(value, self.allowed_data_types): #raise TypeError( #"%s only accept %s types, not %r" - #% (self, - #' or '.join([t.__name__ for t in self.allowed_data_types]), + #% (self, + #' or '.join([t.__name__ for t in self.allowed_data_types]), #value)) self.data_type = value elif prop.name == 'mandatory': @@ -2565,10 +2565,9 @@ class ValidatableMaskedEntry(MaskedEntry): def _on_fadeout__color_changed(self, fadeout, color): self.update_background(color) -if gtk.pygtk_version < (2,8,0): +if gtk.pygtk_version < (2, 8, 0): gobject.type_register(ValidatableMaskedEntry) - def main(args): from DateHandler import parser @@ -2621,7 +2620,7 @@ def main(args): statusbar.push(1, "A short one", yet_another_statusbar) last_statusbar = statusbar.insert(min_width=41, ralign=True) - statusbar.push(1, "The last statusbar has always fixed width", + statusbar.push(1, "The last statusbar has always fixed width", last_statusbar) # ========================================================================= diff --git a/src/ViewManager.py b/src/ViewManager.py index 50e0835a2..12f80f93c 100644 --- a/src/ViewManager.py +++ b/src/ViewManager.py @@ -51,7 +51,6 @@ LOG = logging.getLogger(".") # #------------------------------------------------------------------------- import gtk -import gobject #------------------------------------------------------------------------- # @@ -61,7 +60,8 @@ import gobject from PluginUtils import Plugins, Tool, PluginStatus, \ relationship_class, load_plugins, \ tool_list, report_list -from ReportBase import standalone_categories, report + +import ReportBase import DisplayState import const import Config @@ -70,8 +70,6 @@ import Errors import QuestionDialog import PageView import Navigation -import TipOfDay -import Bookmarks import RecentFiles from BasicUtils import name_displayer import GrampsWidgets @@ -189,12 +187,37 @@ UIDEFAULT = ''' ''' +#------------------------------------------------------------------------- +# +# ViewManager +# +#------------------------------------------------------------------------- class ViewManager: + """ + Overview + ======== + + The ViewManager is the main window of the program. It is closely tied + into the gtk.UIManager to control all menus and actions. + + The ViewManager controls the various Views within the GRAMPS programs. + A View is a particular way of looking a information in the GRAMPS main + window. Each view is separate from the others, and has no knowledge of + the others. All Views are held in the DisplayViews module. Examples of + current views include: + + - Person View + - Relationship View + - Family View + - Source View + + The View Manager does not have to know the number of views, the type of + views, or any other details about the views. It simply provides the + method of containing each view, and switching between the views.s + + """ def __init__(self, state): - """ - Initialize the ViewManager - """ self.page_is_changing = False self.state = state self.active_page = None @@ -203,11 +226,11 @@ class ViewManager: self.tips = gtk.Tooltips() self._key = None self.file_loaded = False - self._build_main_window() - self._connect_signals() - self.do_load_plugins() + self.__build_main_window() + self.__connect_signals() + self.__do_load_plugins() - def _build_main_window(self): + def __build_main_window(self): """ Builds the GTK interface """ @@ -220,7 +243,7 @@ class ViewManager: self.statusbar = GrampsWidgets.Statusbar() - self.RelClass = relationship_class + self.rel_class = relationship_class vbox = gtk.VBox() self.window.add(vbox) @@ -240,8 +263,8 @@ class ViewManager: self.notebook = gtk.Notebook() self.notebook.set_show_tabs(False) self.notebook.show() - self._init_lists() - self._build_ui_manager() + self.__init_lists() + self.__build_ui_manager() hbox.pack_start(self.notebook, True) self.menubar = self.uimanager.get_widget('/MenuBar') @@ -250,7 +273,7 @@ class ViewManager: vbox.pack_start(self.toolbar, False) vbox.add(hbox) self.progress_monitor = ProgressMonitor( - ProgressDialog.GtkProgressDialog, ("",self.window)) + ProgressDialog.GtkProgressDialog, ("", self.window)) self.progress = gtk.ProgressBar() self.progress.set_size_request(100, -1) self.progress.hide() @@ -269,32 +292,22 @@ class ViewManager: self.uistate = DisplayState.DisplayState( self.window, self.statusbar, self.progress, self.warnbtn, self.uimanager, self.progress_monitor) + self.state.connect('database-changed', self.uistate.db_changed) - toolbar = self.uimanager.get_widget('/ToolBar') self.filter_menu = self.uimanager.get_widget( '/MenuBar/ViewMenu/Filter/') - openbtn = gtk.MenuToolButton('gramps-db') - openbtn.connect('clicked', self.open_activate) - openbtn.set_sensitive(False) + # handle OPEN button, insert it into the toolbar. Unfortunately, + # UIManager has no built in support for and Open Recent button + openbtn = self.__build_open_button() self.uistate.set_open_widget(openbtn) - toolbar.insert(openbtn, 0) + self.toolbar.insert(openbtn, 0) - self.open_tips = gtk.Tooltips() - openbtn.set_arrow_tooltip(self.open_tips, - _("Connect to a recent database"), - _("Connect to a recent database")) - - openbtn.set_tooltip(self.open_tips, - _("Manage databases"), - _("Manage databases") - ) - openbtn.show() self.person_nav = Navigation.PersonNavigation(self.state, self.uistate) - self._navigation_type[PageView.NAVIGATION_PERSON] = (self.person_nav, + self._navigation_type[PageView.NAVIGATION_PERSON] = (self.person_nav, None) self.recent_manager = DisplayState.RecentDocsMenu( self.uistate, self.state, self.read_recent_file) @@ -319,85 +332,108 @@ class ViewManager: # But we need to realize it here to have gtk.gdk.window handy self.window.realize() - def _connect_signals(self): + def __build_open_button(self): + """ + Build the OPEN button. Since GTK's UIManager does not have support for + the Open Recent button, we must build in on our own. + """ + openbtn = gtk.MenuToolButton('gramps-db') + openbtn.connect('clicked', self.__open_activate) + openbtn.set_sensitive(False) + open_tips = gtk.Tooltips() + openbtn.set_arrow_tooltip( + open_tips, + _("Connect to a recent database"), + _("Connect to a recent database")) + + openbtn.set_tooltip( + open_tips, + _("Manage databases"), + _("Manage databases") + ) + openbtn.show() + return openbtn + + def __connect_signals(self): """ connects the signals needed """ self.window.connect('delete-event', self.quit) self.notebook.connect('switch-page', self.change_page) - def _init_lists(self): + def __init_lists(self): self._file_action_list = [ ('FileMenu', None, _('_Family Trees')), - ('Open', 'gramps-db', _('_Manage Family Trees'), "o", - _("Manage databases"), self.open_activate), - ('OpenRecent', None, _('Open _Recent'), None, + ('Open', 'gramps-db', _('_Manage Family Trees'), "o", + _("Manage databases"), self.__open_activate), + ('OpenRecent', None, _('Open _Recent'), None, _("Open an existing database")), - ('Quit', gtk.STOCK_QUIT, _('_Quit'), "q",None,self.quit), + ('Quit', gtk.STOCK_QUIT, _('_Quit'), "q", None, + self.quit), ('ViewMenu', None, _('_View')), ('EditMenu', None, _('_Edit')), - ('Preferences', gtk.STOCK_PREFERENCES,_('_Preferences'),None, None, - self.preferences_activate), + ('Preferences', gtk.STOCK_PREFERENCES, _('_Preferences'), None, + None, self.preferences_activate), ('HelpMenu', None, _('_Help')), - ('HomePage', None, _('GRAMPS _home page'), None, None, - self.home_page_activate), - ('MailingLists', None, _('GRAMPS _mailing lists'), None, None, - self.mailing_lists_activate), - ('ReportBug', None, _('_Report a bug'), None, None, - self.report_bug_activate), - ('About', gtk.STOCK_ABOUT, _('_About'), None, None, self.about), - ('PluginStatus', None,_('_Plugin status'), None, None, + ('HomePage', None, _('GRAMPS _home page'), None, None, + home_page_activate), + ('MailingLists', None, _('GRAMPS _mailing lists'), None, None, + mailing_lists_activate), + ('ReportBug', None, _('_Report a bug'), None, None, + report_bug_activate), + ('About', gtk.STOCK_ABOUT, _('_About'), None, None, + display_about_box), + ('PluginStatus', None, _('_Plugin status'), None, None, self.plugin_status), - ('FAQ', None, _('_FAQ'), None, None, self.faq_activate), - ('KeyBindings', None, _('_Key Bindings'), None, None, - self.key_bindings), - ('UserManual', gtk.STOCK_HELP, _('_User Manual'), 'F1', None, - self.manual_activate), - ('TipOfDay', None, _('Tip of the day'), None, None, + ('FAQ', None, _('_FAQ'), None, None, faq_activate), + ('KeyBindings', None, _('_Key Bindings'), None, None, key_bindings), + ('UserManual', gtk.STOCK_HELP, _('_User Manual'), 'F1', None, + manual_activate), + ('TipOfDay', None, _('Tip of the day'), None, None, self.tip_of_day_activate), ] self._readonly_action_list = [ - ('SaveAs', gtk.STOCK_SAVE_AS, _('_Save As'), "s", + ('SaveAs', gtk.STOCK_SAVE_AS, _('_Save As'), "s", None, self.save_as_activate), - ('Export', 'gramps-export', _('_Export'), "e", None, + ('Export', 'gramps-export', _('_Export'), "e", None, self.export_data), - ('Abandon', gtk.STOCK_REVERT_TO_SAVED, + ('Abandon', gtk.STOCK_REVERT_TO_SAVED, _('_Abandon changes and quit'), None, None, self.abort), - ('Reports', 'gramps-reports', _('_Reports'), None, + ('Reports', 'gramps-reports', _('_Reports'), None, _("Open the reports dialog"), self.reports_clicked), ('GoMenu', None, _('_Go')), ('ReportsMenu', None, _('_Reports')), - ('WindowsMenu', None, _('_Windows')), - ('F2', None, 'F2', "F2", None, self.keypress), - ('F3', None, 'F3', "F3", None, self.keypress), - ('F4', None, 'F4', "F4", None, self.keypress), - ('F5', None, 'F5', "F5", None, self.keypress), - ('F6', None, 'F6', "F6", None, self.keypress), - ('F7', None, 'F7', "F7", None, self.keypress), - ('F8', None, 'F9', "F8", None, self.keypress), - ('F9', None, 'F9', "F9", None, self.keypress), - ('F11', None, 'F11', "F11", None, self.keypress), + ('WindowsMenu', None, _('_Windows')), + ('F2', None, 'F2', "F2", None, self.__keypress), + ('F3', None, 'F3', "F3", None, self.__keypress), + ('F4', None, 'F4', "F4", None, self.__keypress), + ('F5', None, 'F5', "F5", None, self.__keypress), + ('F6', None, 'F6', "F6", None, self.__keypress), + ('F7', None, 'F7', "F7", None, self.__keypress), + ('F8', None, 'F9', "F8", None, self.__keypress), + ('F9', None, 'F9', "F9", None, self.__keypress), + ('F11', None, 'F11', "F11", None, self.__keypress), ('BackSpace', None, 'BackSpace', - "BackSpace", None, self.keypress), + "BackSpace", None, self.__keypress), ('Delete', None, 'Delete', - "Delete", None, self.keypress), + "Delete", None, self.__keypress), ('Insert', None, 'Insert', - "Insert", None, self.keypress), - ('F12', None, 'F12', "F12", None, self.keypress), + "Insert", None, self.__keypress), + ('F12', None, 'F12', "F12", None, self.__keypress), ('J', None, 'J', - "J", None, self.keypress), - ('N', None, 'N', "N", None, self.next_view), - ('P', None, 'P', "P", None, self.prev_view), + "J", None, self.__keypress), + ('N', None, 'N', "N", None, self.__next_view), + ('P', None, 'P', "P", None, self.__prev_view), ] self._action_action_list = [ - ('ScratchPad', gtk.STOCK_PASTE, _('_ScratchPad'), "s", + ('ScratchPad', gtk.STOCK_PASTE, _('_ScratchPad'), "s", _("Open the ScratchPad dialog"), self.scratchpad), - ('Import', gtk.STOCK_CONVERT, _('_Import'), "i", None, + ('Import', gtk.STOCK_CONVERT, _('_Import'), "i", None, self.import_data), - ('Tools', 'gramps-tools', _('_Tools'), None, - _("Open the tools dialog"), self.tools_clicked), + ('Tools', 'gramps-tools', _('_Tools'), None, + _("Open the tools dialog"), self.tools_clicked), ('EditMenu', None, _('_Edit')), ('ColumnEdit', gtk.STOCK_PROPERTIES, _('_Column Editor')), ('BookMenu', None, _('_Bookmarks')), @@ -405,26 +441,27 @@ class ViewManager: ] self._file_toggle_action_list = [ - ('Sidebar', None, _('_Sidebar'), None, None, self.sidebar_toggle, + ('Sidebar', None, _('_Sidebar'), None, None, self.sidebar_toggle, self.show_sidebar ), - ('Toolbar', None, _('_Toolbar'), None, None, self.toolbar_toggle, + ('Toolbar', None, _('_Toolbar'), None, None, self.toolbar_toggle, self.show_toolbar ), ('Filter', None, _('_Filter sidebar'), None, None, - self.filter_toggle, self.show_filter), + filter_toggle, self.show_filter), ] self._undo_action_list = [ - ('Undo', gtk.STOCK_UNDO, _('_Undo'),'z', None, self.undo), + ('Undo', gtk.STOCK_UNDO, _('_Undo'), 'z', None, + self.undo), ] self._redo_action_list = [ - ('Redo', gtk.STOCK_REDO,_('_Redo'), 'z', None, - self.redo), + ('Redo', gtk.STOCK_REDO, _('_Redo'), 'z', None, + self.redo), ] self._undo_history_action_list = [ - ('UndoHistory', 'gramps-undo-history', - _('Undo History'), "H", None, self.undo_history), + ('UndoHistory', 'gramps-undo-history', + _('Undo History'), "H", None, self.undo_history), ] self._navigation_type = { @@ -432,15 +469,25 @@ class ViewManager: PageView.NAVIGATION_PERSON: (None, None), } - def keypress(self, action): + def __keypress(self, action): + """ + Callback that is called on a keypress. It works by extracting the + name of the associated action, and passes that to the active page + (current view) so that it can take the associated action. + """ name = action.get_name() try: self.active_page.call_function(name) except: - self.uistate.push_message(self.state, + self.uistate.push_message(self.state, _("Key %s is not bound") % name) - def next_view(self, action): + def __next_view(self, action): + """ + Callback that is called when the next view action is selected. + It selects the next view as the active view. If we reach the end of + the list of views, we wrap around to the first view. + """ current_page = self.notebook.get_current_page() if current_page == len(self.pages)-1: new_page = 0 @@ -448,7 +495,12 @@ class ViewManager: new_page = current_page + 1 self.buttons[new_page].set_active(True) - def prev_view(self, action): + def __prev_view(self, action): + """ + Callback that is called when the previous view action is selected. + It selects the previous view as the active view. If we reach the beginning + of the list of views, we wrap around to the last view. + """ current_page = self.notebook.get_current_page() if current_page == 0: new_page = len(self.pages)-1 @@ -457,31 +509,31 @@ class ViewManager: self.buttons[new_page].set_active(True) def init_interface(self): - self._init_lists() + self.__init_lists() + self.__create_pages() - self.create_pages() if not self.file_loaded: self.actiongroup.set_visible(False) self.readonlygroup.set_visible(False) self.fileactions.set_sensitive(False) self.build_tools_menu(tool_list) self.build_report_menu(report_list) - self.uistate.connect('plugins-reloaded', - self.rebuild_report_and_tool_menus) + self.uistate.connect('plugins-reloaded', + self.__rebuild_report_and_tool_menus) self.fileactions.set_sensitive(True) self.uistate.widget.set_sensitive(True) - Config.client.notify_add("/apps/gramps/interface/statusbar", - self.statusbar_key_update) - Config.client.notify_add("/apps/gramps/interface/filter", - self.filter_signal) + Config.client.notify_add("/apps/gramps/interface/statusbar", + self.__statusbar_key_update) + Config.client.notify_add("/apps/gramps/interface/filter", + self.__filter_signal) - def statusbar_key_update(self, client, cnxn_id, entry, data): + def __statusbar_key_update(self, client, cnxn_id, entry, data): """ Callback function for statusbar key update """ self.uistate.modify_statusbar(self.state) - def filter_signal(self, client, cnxn_id, entry, data): + def __filter_signal(self, client, cnxn_id, entry, data): """ Callback function for statusbar key update """ @@ -493,15 +545,26 @@ class ViewManager: # ArgHandler can work without it always shown self.window.show() if not self.state.db.is_open(): - self.open_activate(None) + self.__open_activate(None) - def do_load_plugins(self): + def __do_load_plugins(self): + """ + Loads the plugins at initialization time. We load the document + generators and the plugins. The plugin status window is opened + on an error if the user has requested. + """ + + # load document generators self.uistate.status_text(_('Loading document formats...')) error = load_plugins(const.DOCGEN_DIR) error |= load_plugins(os.path.join(const.HOME_DIR, "docgen")) + + # load plugins self.uistate.status_text(_('Loading plugins...')) error |= load_plugins(const.PLUGINS_DIR) error |= load_plugins(os.path.join(const.HOME_DIR, "plugins")) + + # get to ssee if we need to open the plugin status window if Config.get(Config.POP_PLUGIN_STATUS) and error: try: PluginStatus.PluginStatus(self.state, self.uistate, []) @@ -514,16 +577,24 @@ class ViewManager: self.uistate.push_message(self.state, _('Ready')) def quit(self, *obj): + """ + Closes out the program, backing up data + """ + # mark interface insenstitive to prevent unexpected events self.uistate.set_sensitive(False) - self.backup() + + # backup data, and close the database + self.__backup() self.state.db.close() + + # save the current window size (width, height) = self.window.get_size() Config.set(Config.WIDTH, width) Config.set(Config.HEIGHT, height) Config.sync() gtk.main_quit() - def backup(self): + def __backup(self): """ Backup the current file as a backup file. """ @@ -546,10 +617,10 @@ class ViewManager: if self.state.db.abort_possible: dialog = QuestionDialog2( - _("Abort changes?"), + _("Abort changes?"), _("Aborting changes will return the database to the state " - "is was before you started this editing session."), - _("Abort changes"), + "is was before you started this editing session."), + _("Abort changes"), _("Cancel")) if dialog.run(): @@ -559,12 +630,12 @@ class ViewManager: self.quit() else: WarningDialog( - _("Cannot abandon session's changes"), + _("Cannot abandon session's changes"), _('Changes cannot be completely abandoned because the ' 'number of changes made in the session exceeded the ' 'limit.')) - def _build_ui_manager(self): + def __build_ui_manager(self): self.merge_ids = [] self.uimanager = gtk.UIManager() @@ -601,46 +672,17 @@ class ViewManager: self.uimanager.insert_action_group(self.undohistoryactions, 1) self.uimanager.ensure_update() - def home_page_activate(self, obj): - GrampsDisplay.url(const.URL_HOMEPAGE) - - def mailing_lists_activate(self, obj): - GrampsDisplay.url( const.URL_MAILINGLIST) - def preferences_activate(self, obj): try: GrampsCfg.GrampsPreferences(self.uistate, self.state) - self._key = self.uistate.connect('nameformat-changed', + self._key = self.uistate.connect('nameformat-changed', self.active_page.build_tree) except Errors.WindowActiveError: pass - def report_bug_activate(self, obj): - GrampsDisplay.url( const.URL_BUGTRACKER) - - def manual_activate(self, obj): - """Display the GRAMPS manual""" - try: - GrampsDisplay.help('index') - except gobject.GError, msg: - QuestionDialog.ErrorDialog(_("Could not open help"), str(msg)) - - def faq_activate(self, obj): - """Display FAQ""" - try: - GrampsDisplay.help('faq') - except gobject.GError, msg: - QuestionDialog.ErrorDialog(_("Could not open help"), str(msg)) - - def key_bindings(self, obj): - """Display FAQ""" - try: - GrampsDisplay.help('keybind-lists') - except gobject.GError, msg: - QuestionDialog.ErrorDialog(_("Could not open help"), str(msg)) - def tip_of_day_activate(self, obj): """Display Tip of the day""" + import TipOfDay TipOfDay.TipOfDay(self.uistate) def plugin_status(self, obj): @@ -653,41 +695,6 @@ class ViewManager: old_win.close() PluginStatus.PluginStatus(self.state, self.uistate, []) - def about(self, obj): - about = gtk.AboutDialog() - about.set_name(const.PROGRAM_NAME) - about.set_version(const.VERSION) - about.set_copyright(const.COPYRIGHT_MSG) - about.set_artists([ - _("Much of GRAMPS' artwork is either from\n" - "the Tango Project or derived from the Tango\n" - "Project. This artwork is released under the\n" - "Create Commons Attribution-ShareAlike 2.5\n" - "license.") - ]) - try: - ifile = open(const.LICENSE_FILE, "r") - about.set_license(ifile.read().replace('\x0c', '')) - ifile.close() - except: - about.set_license("License file is missing") - about.set_comments(_(const.COMMENTS)) - about.set_website_label(_('GRAMPS Homepage')) - about.set_website(const.URL_HOMEPAGE) - about.set_authors(const.AUTHORS) - - # Only set translation credits if they are translated - trans_credits = _(const.TRANSLATORS) - if trans_credits != const.TRANSLATORS: - about.set_translator_credits(trans_credits) - - about.set_documenters(const.DOCUMENTERS) - about.set_logo(gtk.gdk.pixbuf_new_from_file(const.SPLASH)) - about.set_modal(True) - about.show() - about.run() - about.destroy() - def sidebar_toggle(self, obj): if obj.get_active(): self.ebox.show() @@ -708,28 +715,33 @@ class ViewManager: Config.set(Config.TOOLBAR_ON, False) Config.sync() - def filter_toggle(self, obj): - Config.set(Config.FILTER, obj.get_active()) - Config.sync() - def register_view(self, view): self.views.append(view) - def switch_page_on_dnd(self, widget, context, x, y, time, page_no): + def __switch_page_on_dnd(self, widget, context, x, y, time, page_no): self.vb_handlers_block() if self.notebook.get_current_page() != page_no: self.notebook.set_current_page(page_no) self.vb_handlers_unblock() - - def create_pages(self): - self.pages = [] - self.prev_nav = PageView.NAVIGATION_NONE - use_text = Config.get(Config.SIDEBAR_TEXT) + def __setup_text_tips(self, use_text): + """ + Enable/disable the text tips in the sidebar + """ if use_text: self.tips.disable() else: self.tips.enable() + + def __create_pages(self): + """ + Creates the Views + """ + self.pages = [] + self.prev_nav = PageView.NAVIGATION_NONE + + use_text = Config.get(Config.SIDEBAR_TEXT) + self.__setup_text_tips(use_text) index = 0 for page_def in self.views: @@ -755,41 +767,19 @@ class ViewManager: # Enable view switching during DnD hbox.drag_dest_set(0, [], 0) - hbox.connect('drag_motion', self.switch_page_on_dnd, page_no) + hbox.connect('drag_motion', self.__switch_page_on_dnd, page_no) # create the button and add it to the sidebar - button = gtk.ToggleButton() - self.tips.set_tip(button, page_title) - hbox = gtk.HBox() - hbox.show() - image = gtk.Image() - if use_text: - image.set_from_stock(page_stock, gtk.ICON_SIZE_BUTTON) - else: - image.set_from_stock(page_stock, gtk.ICON_SIZE_DND) - image.show() - hbox.pack_start(image, False, False) - hbox.set_spacing(4) + button = self.__make_sidebar_button(use_text, index, + page_title, page_stock) - if use_text: - label = gtk.Label(page_title) - label.show() - hbox.pack_start(label, False, True) - - button.add(hbox) - button.set_relief(gtk.RELIEF_NONE) - button.set_alignment(0, 0.5) - handler_id = button.connect('clicked', self.vb_clicked, index) - button.show() index += 1 self.bbox.pack_start(button, False) self.buttons.append(button) - self.button_handlers.append(handler_id) # Enable view switching during DnD button.drag_dest_set(0, [], 0) - button.connect('drag_motion', self.switch_page_on_dnd, page_no) - + button.connect('drag_motion', self.__switch_page_on_dnd, page_no) use_current = Config.get(Config.USE_LAST_VIEW) if use_current: @@ -804,14 +794,55 @@ class ViewManager: self.active_page.set_active() self.notebook.set_current_page(current_page) - def vb_clicked(self, button, index): + def __make_sidebar_button(self, use_text, index, page_title, page_stock): + """ + Creates the sidebar button. The page_title is the text associated with + the button. + """ + + # create the button + button = gtk.ToggleButton() + button.set_relief(gtk.RELIEF_NONE) + button.set_alignment(0, 0.5) + + # add the tooltip + self.tips.set_tip(button, page_title) + + # connect the signal, along with the index as user data + handler_id = button.connect('clicked', self.__vb_clicked, index) + self.button_handlers.append(handler_id) + button.show() + + # add the image. If we are using text, use the BUTTON (larger) size. + # otherwise, use the smaller size + hbox = gtk.HBox() + hbox.show() + image = gtk.Image() + if use_text: + image.set_from_stock(page_stock, gtk.ICON_SIZE_BUTTON) + else: + image.set_from_stock(page_stock, gtk.ICON_SIZE_DND) + image.show() + hbox.pack_start(image, False, False) + hbox.set_spacing(4) + + # add text if requested + if use_text: + label = gtk.Label(page_title) + label.show() + hbox.pack_start(label, False, True) + + button.add(hbox) + return button + + def __vb_clicked(self, button, index): if Config.get(Config.VIEW): self.vb_handlers_block() self.notebook.set_current_page(index) # FIXME: This used to work, but now DnD switches views # and messes this up - # If the click is on the same view we're in, + # If the click is on the same view we're in, # restore the button state to active if not button.get_active(): button.set_active(True) @@ -894,14 +925,14 @@ class ViewManager: def import_pkg(self, filename): import ReadPkg ReadPkg.impData(self.state.db, filename, self.uistate.pulse_progressbar) - self.post_load() + self.__post_load() def import_data(self, obj): if self.state.db.is_open(): self.db_loader.import_file() - self.post_load() + self.__post_load() - def open_activate(self, obj): + def __open_activate(self, obj): import DbManager dialog = DbManager.DbManager(self.state, self.window) value = dialog.run() @@ -912,14 +943,14 @@ class ViewManager: os.chdir(os.path.dirname(filename)) except: pass - self.post_load_newdb(filename, 'x-directory/normal', title) + self.__post_load_newdb(filename, 'x-directory/normal', title) def read_file(self, filename, filetype): """ This method takes care of changing database, and loading the data. This method should only return on success. - Returning on failure makes no sense, because we cannot recover, + Returning on failure makes no sense, because we cannot recover, since database has already beeen changed. Therefore, any errors should raise exceptions. @@ -939,7 +970,7 @@ class ViewManager: mode = "w" elif filetype == 'unknown': QuestionDialog.WarningDialog( - _('Missing or Invalid database'), + _('Missing or Invalid database'), _('%s could not be found.\n' 'It is possible that this file no longer exists ' 'or has been moved.') % filename) @@ -980,14 +1011,10 @@ class ViewManager: def save_as_activate(self, obj): if self.state.db.is_open(): (filename, filetype) = self.db_loader.save_as() - self.post_load_newdb(filename, filetype) - - def new_activate(self, obj): - (filename, filetype) = self.db_loader.new_file() - self.post_load_newdb(filename, filetype) + self.__post_load_newdb(filename, filetype) def read_recent_file(self, filename, filetype): - if self.db_loader.read_file(filename,'x-directory/normal'): + if self.db_loader.read_file(filename, 'x-directory/normal'): # Attempt to figure out the database title path = os.path.join(filename, "name.txt") @@ -998,9 +1025,9 @@ class ViewManager: except: title = filename - self.post_load_newdb(filename, 'x-directory/normal', title) + self.__post_load_newdb(filename, 'x-directory/normal', title) - def post_load(self): + def __post_load(self): # This method is for the common UI post_load, both new files # and added data like imports. if self.state.active : @@ -1019,7 +1046,7 @@ class ViewManager: self.uistate.window.window.set_cursor(None) - def post_load_newdb(self, filename, filetype, title=None): + def __post_load_newdb(self, filename, filetype, title=None): if not filename: return @@ -1027,8 +1054,6 @@ class ViewManager: # This method is for UI stuff when the database has changed. # Window title, recent files, etc related to new file. - check_for_portability_problems(filetype) - self.state.db.set_save_path(filename) # Update window title @@ -1079,15 +1104,15 @@ class ViewManager: RecentFiles.recent_files(filename, name) self.recent_manager.build() - # Call common post_load - self.post_load() + # Call common __post_load + self.__post_load() def change_undo_label(self, label): self.uimanager.remove_action_group(self.undoactions) self.undoactions = gtk.ActionGroup('Undo') if label: self.undoactions.add_actions([ - ('Undo', gtk.STOCK_UNDO,label, 'z', None, self.undo)]) + ('Undo', gtk.STOCK_UNDO, label, 'z', None, self.undo)]) else: self.undoactions.add_actions([ ('Undo', gtk.STOCK_UNDO, _('_Undo'), @@ -1104,7 +1129,7 @@ class ViewManager: None, self.redo)]) else: self.redoactions.add_actions([ - ('Redo', gtk.STOCK_UNDO, _('_Redo'), + ('Redo', gtk.STOCK_UNDO, _('_Redo'), 'z', None, self.redo)]) self.redoactions.set_sensitive(False) self.uimanager.insert_action_group(self.redoactions, 1) @@ -1132,14 +1157,19 @@ class ViewManager: pass def setup_bookmarks(self): - self.bookmarks = Bookmarks.Bookmarks(self.state, self.uistate, - self.state.db.get_bookmarks()) + """ + Initializes the bookmarks based of the database. This needs to + be called anytime the database changes. + """ + import Bookmarks + self.bookmarks = Bookmarks.Bookmarks( + self.state, self.uistate, self.state.db.get_bookmarks()) def add_bookmark(self, obj): if self.state.active: self.bookmarks.add(self.state.active.get_handle()) name = name_displayer.display(self.state.active) - self.uistate.push_message(self.state, + self.uistate.push_message(self.state, _("%s has been bookmarked") % name) else: QuestionDialog.WarningDialog( @@ -1181,10 +1211,10 @@ class ViewManager: def undo_history(self, obj): try: - self.undo_history_window = UndoHistory.UndoHistory(self.state, + self.undo_history_window = UndoHistory.UndoHistory(self.state, self.uistate) except Errors.WindowActiveError: - pass + return def export_data(self, obj): if self.state.db.db_is_open: @@ -1193,9 +1223,8 @@ class ViewManager: ExportAssistant.ExportAssistant(self.state, self.uistate) except Errors.WindowActiveError: return - - def rebuild_report_and_tool_menus(self, tool_list, report_list): + def __rebuild_report_and_tool_menus(self, tool_list, report_list): self.build_tools_menu(tool_list) self.build_report_menu(report_list) @@ -1211,7 +1240,7 @@ class ViewManager: def build_report_menu(self, report_list): self.reportactions = gtk.ActionGroup('ReportWindow') (ui, actions) = self.build_plugin_menu( - 'ReportsMenu', report_list, standalone_categories, + 'ReportsMenu', report_list, ReportBase.standalone_categories, make_report_callback) self.reportactions.add_actions(actions) self.uistate.uimanager.add_ui_from_string(ui) @@ -1274,41 +1303,94 @@ class ViewManager: ofile.write('') return (ofile.getvalue(), actions) +def display_about_box(obj): + """ + Displays the About box. + """ + about = gtk.AboutDialog() + about.set_name(const.PROGRAM_NAME) + about.set_version(const.VERSION) + about.set_copyright(const.COPYRIGHT_MSG) + about.set_artists([ + _("Much of GRAMPS' artwork is either from\n" + "the Tango Project or derived from the Tango\n" + "Project. This artwork is released under the\n" + "Create Commons Attribution-ShareAlike 2.5\n" + "license.") + ]) + try: + ifile = open(const.LICENSE_FILE, "r") + about.set_license(ifile.read().replace('\x0c', '')) + ifile.close() + except: + about.set_license("License file is missing") + about.set_comments(_(const.COMMENTS)) + about.set_website_label(_('GRAMPS Homepage')) + about.set_website(const.URL_HOMEPAGE) + about.set_authors(const.AUTHORS) + + # Only set translation credits if they are translated + trans_credits = _(const.TRANSLATORS) + if trans_credits != const.TRANSLATORS: + about.set_translator_credits(trans_credits) + + about.set_documenters(const.DOCUMENTERS) + about.set_logo(gtk.gdk.pixbuf_new_from_file(const.SPLASH)) + about.set_modal(True) + about.show() + about.run() + about.destroy() + +def filter_toggle(obj): + """ + Save the filter state to the config settings on change + """ + Config.set(Config.FILTER, obj.get_active()) + Config.sync() + +def key_bindings(obj): + """ + Display key bindings + """ + GrampsDisplay.help('keybind-lists') + +def manual_activate(self, obj): + """ + Display the GRAMPS manual + """ + GrampsDisplay.help('index') + +def report_bug_activate(obj): + """ + Display the bug tracker web site + """ + GrampsDisplay.url(const.URL_BUGTRACKER) + +def home_page_activate(obj): + """ + Display the GRAMPS home page + """ + GrampsDisplay.url(const.URL_HOMEPAGE) + +def mailing_lists_activate(obj): + """ + Display the mailing list web page + """ + GrampsDisplay.url( const.URL_MAILINGLIST) + +def faq_activate(obj): + """Display FAQ""" + GrampsDisplay.help('faq') + def by_menu_name(first, second): return cmp(first[2], second[2]) - def make_report_callback(lst, dbstate, uistate): - return lambda x: report(dbstate, uistate, dbstate.get_active_person(), - lst[0], lst[1], lst[2], lst[3], lst[4], lst[5]) + return lambda x: ReportBase.report( + dbstate, uistate, dbstate.get_active_person(), + lst[0], lst[1], lst[2], lst[3], lst[4], lst[5]) def make_tool_callback(lst, dbstate, uistate): return lambda x: Tool.gui_tool(dbstate, uistate, lst[0], lst[1], lst[2], lst[3], lst[4], dbstate.db.request_rebuild) - -def check_for_portability_problems(filetype): - """ - Checks for the portability problem caused by the combination of - python 2.4 and bsddb. If the problem exists, issue a warning message - that the user can disable. - """ - - # check for a GRDB type and if transactions are enabled. If not, - # then we do not have any issues - - if filetype == const.APP_GRAMPS and Config.get(Config.TRANSACTIONS): - - import sys - - # Check for a version less than python 2.5. This is held in the - # sys.version_info variable - - version = (sys.version_info[0], sys.version_info[1]) - if version < (2, 5) and not Config.get(Config.PORT_WARN): - QuestionDialog.MessageHideDialog( - _('Family Tree is not portable'), - _('If you need to transfer the database to another machine, ' - 'export to a GRAMPS Package, and import the GRAMPS Package ' - 'on the other machine.'), - Config.PORT_WARN)