diff --git a/data/tests/imp_MediaTest.ged b/data/tests/imp_MediaTest.ged index c2ce7029d..1bde200f5 100644 --- a/data/tests/imp_MediaTest.ged +++ b/data/tests/imp_MediaTest.ged @@ -43,6 +43,15 @@ 2 NOTE @N0018@ 1 OBJE @M7@ 1 OBJE @M8@ +1 OBJE +2 FORM URL +2 FILE http:\\obje.form.on_person.org +1 BIRT +2 TYPE Birth of the Tester +2 DATE 2 OCT 1864 +2 OBJE +3 FORM URL +3 FILE http:\\obje.form.on_event.org 0 @M1@ OBJE 1 FORM jpeg 1 TITL Multimedia link to linked form v5.5 blob diff --git a/data/tests/imp_MediaTest.gramps b/data/tests/imp_MediaTest.gramps index 2674b4e01..22b6f1083 100644 --- a/data/tests/imp_MediaTest.gramps +++ b/data/tests/imp_MediaTest.gramps @@ -3,17 +3,26 @@ "http://gramps-project.org/xml/1.7.1/grampsxml.dtd">
- +
+ + + Birth + + Birth of the Tester + + + - + U The Tester + @@ -25,106 +34,107 @@ - + + - + 0 - - + + - + 77, 78 discussion of multimedia link with two files 4 - - + + - + A Great Photographer - + The Testers personal files The Tester Name: Tester Publishing Operations, Inc.; Location: OSF world - + - + - - + + - - + + - + - - - + + + - + - + - - - + + + - + - - - + + + - + - + - + - + - + - + - + Media note test: Multimedia link embedded form v5.5 n OBJE {1:1} p.55 +1 FORM <MULTIMEDIA_FORMAT> {1:1} p.48 @@ -132,7 +142,7 @@ n OBJE {1:1} p.55 +1 FILE <MULTIMEDIA_FILE_REFERENCE> {1:1} p.47 +1 <<NOTE_STRUCTURE>> {0:M} p.33 - + Media note test: Multimedia link embedded form v5.5.1 This note is not in the 5.5.1 spec, but is an obvious extrapolation from 5.5. n OBJE @@ -141,23 +151,27 @@ n OBJE +3 MEDI <SOURCE_MEDIA_TYPE> {0:1} p.62 +1 TITL <DESCRIPTIVE_TITLE> {0:1} p.48 - + Multimedia embedded 2nd copy v5.5 - + + http:\\obje.form.on_event.org + + Records not imported into INDI (individual) Gramps ID I0001: Could not import test_emb_55.jpg Line 18: 1 OBJE Could not import test_emb_551.jpg Line 26: 1 OBJE -Could not import test_emb_55.jpg Line 34: 1 OBJE +Could not import test_emb_55.jpg Line 34: 1 OBJE + - + SOMETEXT - + Media note test: Multimedia link to linked form v5.5 blob n @XREF:OBJE@ OBJE {1:1} +1 FORM <MULTIMEDIA_FORMAT> {1:1} p.48 @@ -171,10 +185,10 @@ n @XREF:OBJE@ OBJE {1:1} +1 RIN <AUTOMATED_RECORD_ID> {0:1} p.38 +1 <<CHANGE_DATE>> {0:1} p.29 - + Records not imported into OBJE (multi-media object) Gramps ID M1: -Tag recognized but not supported Line 49: 1 BLOB +Tag recognized but not supported Line 58: 1 BLOB .HM.......k.1..F.jwA.Dzzzzw............A....1.........0U.66..E.8 .......A..k.a6.A.......A..k.........../6....G.......0../..U..... .w1/m........HC0..../...zzzzzzzz..5zzk..AnA..U..W6U....2rRrRrRrR @@ -182,23 +196,25 @@ Tag recognized but not supported Line 49: /Dw/.Tvz.E5zzUE9/kHz.Tw2/DzzzEEA.kE2zk5yzk2/zzs21.U2/Dw/.Tw/.Tzy /.fy/.HzzkHzzzo21Ds00.E2.UE2.U62/.k./Ds0.UE0/Do0..E8/UE2.U62.U9w /.Tx/.20.jg2/jo2..9u/.0U.6A.zk -Line ignored as not understood Line 57: 1 OBJE @M2@ -Filename omitted Line 46: 0 M1 OBJE +Line ignored as not understood Line 66: 1 OBJE @M2@ +Filename omitted Line 55: 0 M1 OBJE + - + Records not imported into OBJE (multi-media object) Gramps ID M2: -Tag recognized but not supported Line 68: 1 BLOB +Tag recognized but not supported Line 77: 1 BLOB 67890gramps doesn't do this anyway, so don't bother doing it right. -Filename omitted Line 65: 0 M2 OBJE +Filename omitted Line 74: 0 M2 OBJE + - + Media note test: Multimedia link to linked form Gramps style v5.5ish file n @XREF:OBJE@ OBJE {1:1} +1 FORM <MULTIMEDIA_FORMAT> {1:1} p.48 @@ -206,18 +222,19 @@ n @XREF:OBJE@ OBJE {1:1} +1 FILE <MULTIMEDIA_FILE_REFERENCE> {1:1} p.47 +1 <<NOTE_STRUCTURE>> {0:M} p.33 - + Records not imported into OBJE (multi-media object) Gramps ID M3: -Could not import test.jpg Line 73: 1 FILE test.jpg +Could not import test.jpg Line 82: 1 FILE test.jpg + - + SOMETEXT - + Media note test: Multimedia link to linked form v5.5.1 file n @XREF:OBJE@ OBJE {1:1} +1 FILE <MULTIMEDIA_FILE_REFN> {1:M} p.54 @@ -231,25 +248,26 @@ n @XREF:OBJE@ OBJE {1:1} +1 <<SOURCE_CITATION>> {0:M} p.39 +1 <<CHANGE_DATE>> {0:1} p.31 - + who shall remain un-named - + Records not imported into OBJE (multi-media object) Gramps ID M4: -Could not import test.jpg Line 79: 1 FILE test.jpg +Could not import test.jpg Line 88: 1 FILE test.jpg + - + A fine gentelman was he, upstanding in his community and a great believer in the testing of open source software. - + A note on the FTM media, to see how this integrates... The DATE line is bad; it doesnt follow Gedcom standard at all, and includes the time. The TEXT line comes from the FTM media description. This is the media Note. - + Multimedia link to linked form FTM style n @XREF:OBJE@ OBJE {1:1} +1 FILE <MULTIMEDIA_FILE_REFN> {1:M} p.54 @@ -258,39 +276,42 @@ n @XREF:OBJE@ OBJE {1:1} +2 TEXT text string from FTM media description sometimes populated from exif comments +1 <<NOTE_STRUCTURE>> {0:M} p.33 - + Records not imported into OBJE (multi-media object) Gramps ID M5: -Could not import test.jpg Line 94: 1 FILE test.jpg +Could not import test.jpg Line 103: 1 FILE test.jpg + - + SOMETEXT - + Multimedia link to linked form v5.5.1 with two files - + A source who shall remain un-named - + Records not imported into OBJE (multi-media object) Gramps ID M6: -Could not import test.jpg Line 102: 1 FILE test.jpg -Multiple FILE in a single OBJE ignored Line 106: 1 FILE test1.jpg -Skipped subordinate line Line 107: 2 FORM jpeg -Skipped subordinate line Line 108: 3 TYPE photo -Skipped subordinate line Line 109: 2 TITL Multimedia link to linked form v5.5.1 with two files(2) +Could not import test.jpg Line 111: 1 FILE test.jpg +Multiple FILE in a single OBJE ignored Line 115: 1 FILE test1.jpg +Skipped subordinate line Line 116: 2 FORM jpeg +Skipped subordinate line Line 117: 3 TYPE photo +Skipped subordinate line Line 118: 2 TITL Multimedia link to linked form v5.5.1 with two files(2) + - + Records not imported into OBJE (multi-media object) Gramps ID M8: -Could not import No_path_No_Title_NoForm.jpg Line 129: 1 FILE No_path_No_Title_NoForm.jpg +Could not import No_path_No_Title_NoForm.jpg Line 138: 1 FILE No_path_No_Title_NoForm.jpg + diff --git a/gramps/gen/const.py b/gramps/gen/const.py index db0123d38..8770529a5 100644 --- a/gramps/gen/const.py +++ b/gramps/gen/const.py @@ -59,8 +59,8 @@ PROGRAM_NAME = "Gramps" #------------------------------------------------------------------------- URL_HOMEPAGE = "http://gramps-project.org/" URL_MAILINGLIST = "http://sourceforge.net/mail/?group_id=25770" -URL_BUGHOME = "http://bugs.gramps-project.org" -URL_BUGTRACKER = "http://bugs.gramps-project.org/bug_report_page.php" +URL_BUGHOME = "http://gramps-project.org/bugs" +URL_BUGTRACKER = "http://gramps-project.org/bugs/bug_report_page.php" URL_WIKISTRING = "http://gramps-project.org/wiki/index.php?title=" URL_MANUAL_PAGE = "Gramps_%s_Wiki_Manual" % major_version URL_MANUAL_DATA = '%s_-_Entering_and_editing_data:_detailed' % URL_MANUAL_PAGE diff --git a/gramps/gen/db/generic.py b/gramps/gen/db/generic.py index 1d5332d28..9f958a0c9 100644 --- a/gramps/gen/db/generic.py +++ b/gramps/gen/db/generic.py @@ -44,7 +44,7 @@ import glob from . import (DbReadBase, DbWriteBase, DbUndo, DBLOGNAME, DBUNDOFN, KEY_TO_CLASS_MAP, REFERENCE_KEY, PERSON_KEY, FAMILY_KEY, CITATION_KEY, SOURCE_KEY, EVENT_KEY, MEDIA_KEY, PLACE_KEY, - REPOSITORY_KEY, NOTE_KEY, TAG_KEY) + REPOSITORY_KEY, NOTE_KEY, TAG_KEY, TXNADD, TXNDEL) from ..errors import HandleError from ..utils.callback import Callback from ..updatecallback import UpdateCallback @@ -147,11 +147,11 @@ class DbGenericUndo(DbUndo): # now emit the signals for record_id in subitems: (key, trans_type, handle, old_data, new_data) = \ - pickle.loads(self.undodb[record_id]) + pickle.loads(self.undodb[record_id]) if key != REFERENCE_KEY: - self.undo_signals(new_data, handle, key, - db.emit, SIGBASE[key]) + self.undo_signals(trans_type, handle, + db.emit, SIGBASE[key], False) self.db._txn_commit() except: self.db._txn_abort() @@ -201,8 +201,8 @@ class DbGenericUndo(DbUndo): pickle.loads(self.undodb[record_id]) if key != REFERENCE_KEY: - self.undo_signals(old_data, handle, key, - db.emit, SIGBASE[key]) + self.undo_signals(trans_type, handle, + db.emit, SIGBASE[key], True) self.db._txn_commit() except: self.db._txn_abort() @@ -257,20 +257,19 @@ class DbGenericUndo(DbUndo): obj = self.db._get_table_func(cls)["class_func"].create(data) self.db._update_secondary_values(obj) - def undo_signals(self, data, handle, obj_key, emit, signal_root): + def undo_signals(self, trans_type, handle, emit, signal_root, reverse): """ Helper method to undo/redo the changes made """ - cls = KEY_TO_CLASS_MAP[obj_key] - table = cls.lower() - if data is None: - emit(signal_root + '-delete', ([handle],)) - else: - if self.db.has_handle(obj_key, handle): - signal = signal_root + '-update' - else: - signal = signal_root + '-add' - emit(signal, ([handle],)) + if ((not reverse) and trans_type == TXNADD) \ + or (reverse and trans_type == TXNDEL): + typ = '-add' + elif not reverse and trans_type == TXNDEL \ + or reverse and trans_type == TXNADD: + typ = '-delete' + else: # TXNUPD + typ = '-update' + emit(signal_root + typ, ([handle],)) class Cursor: def __init__(self, iterator): diff --git a/gramps/gen/filters/rules/person/_islessthannthgenerationancestorof.py b/gramps/gen/filters/rules/person/_islessthannthgenerationancestorof.py index 48139b1cd..b95e330d1 100644 --- a/gramps/gen/filters/rules/person/_islessthannthgenerationancestorof.py +++ b/gramps/gen/filters/rules/person/_islessthannthgenerationancestorof.py @@ -51,36 +51,34 @@ class IsLessThanNthGenerationAncestorOf(Rule): def prepare(self, db, user): self.db = db self.map = set() - try: - root_handle = db.get_person_from_gramps_id(self.list[0]).get_handle() - self.init_ancestor_list(root_handle,0) - except: - pass + person = db.get_person_from_gramps_id(self.list[0]) + if person: + root_handle = person.get_handle() + if root_handle: + self.init_ancestor_list(root_handle) + + def init_ancestor_list(self, root_handle): + queue = [(root_handle, 1)] # generation 1 is root + while queue: + handle, gen = queue.pop(0) # pop off front of queue + self.map.add(handle) + gen += 1 + if gen <= int(self.list[1]): + p = self.db.get_person_from_handle(handle) + fam_id = p.get_main_parents_family_handle() + if fam_id: + fam = self.db.get_family_from_handle(fam_id) + if fam: + f_id = fam.get_father_handle() + m_id = fam.get_mother_handle() + # append to back of queue: + if f_id: + queue.append((f_id, gen)) + if m_id: + queue.append((m_id, gen)) def reset(self): self.map.clear() def apply(self,db,person): return person.handle in self.map - - def init_ancestor_list(self, handle,gen): -# if p.get_handle() in self.map: -# loop_error(self.orig,p) - if not handle: - return - if gen: - self.map.add(handle) - if gen >= int(self.list[1]): - return - - p = self.db.get_person_from_handle(handle) - fam_id = p.get_main_parents_family_handle() - fam = self.db.get_family_from_handle(fam_id) - if fam: - f_id = fam.get_father_handle() - m_id = fam.get_mother_handle() - - if f_id: - self.init_ancestor_list(f_id,gen+1) - if m_id: - self.init_ancestor_list(m_id,gen+1) diff --git a/gramps/gen/filters/rules/person/_ismorethannthgenerationancestorof.py b/gramps/gen/filters/rules/person/_ismorethannthgenerationancestorof.py index 11e9f6158..9ee74c59f 100644 --- a/gramps/gen/filters/rules/person/_ismorethannthgenerationancestorof.py +++ b/gramps/gen/filters/rules/person/_ismorethannthgenerationancestorof.py @@ -51,34 +51,34 @@ class IsMoreThanNthGenerationAncestorOf(Rule): def prepare(self, db, user): self.db = db self.map = set() - try: - root_handle = db.get_person_from_gramps_id(self.list[0]).get_handle() - self.init_ancestor_list(root_handle,0) - except: - pass + person = db.get_person_from_gramps_id(self.list[0]) + if person: + root_handle = person.get_handle() + if root_handle: + self.init_ancestor_list(root_handle) + + def init_ancestor_list(self, root_handle): + queue = [(root_handle, 1)] # generation 1 is root + while queue: + handle, gen = queue.pop(0) # pop off front of queue + if gen > int(self.list[1]): + self.map.add(handle) + gen += 1 + p = self.db.get_person_from_handle(handle) + fam_id = p.get_main_parents_family_handle() + if fam_id: + fam = self.db.get_family_from_handle(fam_id) + if fam: + f_id = fam.get_father_handle() + m_id = fam.get_mother_handle() + # append to back of queue: + if f_id: + queue.append((f_id, gen)) + if m_id: + queue.append((m_id, gen)) def reset(self): self.map.clear() def apply(self,db,person): return person.handle in self.map - - def init_ancestor_list(self, handle, gen): -# if p.get_handle() in self.map: -# loop_error(self.orig,p) - if not handle: - return - if gen >= int(self.list[1]): - self.map.add(handle) - - p = self.db.get_person_from_handle(handle) - fam_id = p.get_main_parents_family_handle() - fam = self.db.get_family_from_handle(fam_id) - if fam: - f_id = fam.get_father_handle() - m_id = fam.get_mother_handle() - - if f_id: - self.init_ancestor_list(f_id, gen+1) - if m_id: - self.init_ancestor_list(m_id, gen+1) diff --git a/gramps/gen/utils/alive.py b/gramps/gen/utils/alive.py index 8fcd5d0f9..99626eabc 100644 --- a/gramps/gen/utils/alive.py +++ b/gramps/gen/utils/alive.py @@ -108,11 +108,7 @@ class ProbablyAlive: if death_ref: death = self.db.get_event_from_handle(death_ref.ref) if death: - if death.get_date_object().is_valid(): - death_date = death.get_date_object() - else: # has a death event, but it is not valid: - death_date = Today() # before today - death_date.set_modifier(Date.MOD_BEFORE) + death_date = death.get_date_object() # Look for Cause Of Death, Burial or Cremation events. # These are fairly good indications that someone's not alive. @@ -144,7 +140,8 @@ class ProbablyAlive: if not birth_date and death_date: # person died more than MAX after current year - birth_date = death_date.copy_offset_ymd(year=-self.MAX_AGE_PROB_ALIVE) + if death_date.is_valid(): + birth_date = death_date.copy_offset_ymd(year=-self.MAX_AGE_PROB_ALIVE) explain = _("death date") if not death_date and birth_date: diff --git a/gramps/gui/dbman.py b/gramps/gui/dbman.py index 37a1ab85f..150314289 100644 --- a/gramps/gui/dbman.py +++ b/gramps/gui/dbman.py @@ -716,7 +716,7 @@ class DbManager(CLIDbManager, ManagedWindow): node = self.model.get_iter(path) filename = self.model.get_value(node, FILE_COL) try: - with open(filename, "r") as name_file: + with open(filename, "r", encoding='utf-8') as name_file: file_name_to_delete = name_file.read() remove_filename(file_name_to_delete) directory = self.data_to_delete[1] diff --git a/gramps/gui/editors/editmediaref.py b/gramps/gui/editors/editmediaref.py index 25f7970c4..e746fa0bd 100644 --- a/gramps/gui/editors/editmediaref.py +++ b/gramps/gui/editors/editmediaref.py @@ -100,6 +100,7 @@ class EditMediaRef(EditReference): tblref = self.top.get_object('table50') self.notebook_ref = self.top.get_object('notebook_ref') self.track_ref_for_deletion("notebook_ref") + self.expander = self.top.get_object('expander1') #recreate start page as GrampsTab self.notebook_ref.remove_page(0) self.reftab = RefTab(self.dbstate, self.uistate, self.track, @@ -187,6 +188,7 @@ class EditMediaRef(EditReference): self.selection.set_multiple_selection(False) self.selection.connect("region-modified", self.region_modified) self.selection.connect("region-created", self.region_modified) + self.expander.connect("activate", self.selection.expander) frame = self.top.get_object("frame9") frame.add(self.selection) self.track_ref_for_deletion("selection") @@ -384,6 +386,7 @@ class EditMediaRef(EditReference): real = self.selection.proportional_to_real_rect(self.rectangle) region = Region(real[0], real[1], real[2], real[3]) self.selection.set_regions([region]) + self.selection.select(region) # update the selection box shown self.selection.refresh() def region_modified(self, widget): diff --git a/gramps/gui/filters/sidebar/_citationsidebarfilter.py b/gramps/gui/filters/sidebar/_citationsidebarfilter.py index 000e50315..24d9067f0 100644 --- a/gramps/gui/filters/sidebar/_citationsidebarfilter.py +++ b/gramps/gui/filters/sidebar/_citationsidebarfilter.py @@ -142,15 +142,7 @@ class CitationSidebarFilter(SidebarFilter): gid = str(self.filter_id.get_text()).strip() page = str(self.filter_page.get_text()).strip() date = str(self.filter_date.get_text()).strip() - model = self.filter_conf.get_model() - node = self.filter_conf.get_active_iter() - conf_name = model.get_value(node, 0) # The value is actually the text - conf = Citation.CONF_NORMAL - for i in list(conf_strings.keys()): - if _(conf_strings[i]) == conf_name: - conf = i - break -# conf = self.citn.get_confidence_level() + conf = str(self.filter_conf.get_active()) note = str(self.filter_note.get_text()).strip() regex = self.filter_regex.get_active() tag = self.tag.get_active() > 0 diff --git a/gramps/gui/logger/_errorreportassistant.py b/gramps/gui/logger/_errorreportassistant.py index 97ccc8ebe..74c28c5a2 100644 --- a/gramps/gui/logger/_errorreportassistant.py +++ b/gramps/gui/logger/_errorreportassistant.py @@ -119,7 +119,7 @@ class ErrorReportAssistant(ManagedWindow, Gtk.Assistant): if self.parent_window is not None: self._save_position(save_config=False) # the next line saves it self._save_size() - self.hide() + self.destroy() if self.ownthread: Gtk.main_quit() @@ -174,26 +174,31 @@ class ErrorReportAssistant(ManagedWindow, Gtk.Assistant): operatingsystem = sys.platform distribution = " " - return "Python version: %s \n"\ + sqlite = '' + if __debug__: + sqlite = "sqlite version: %s (%s) \n" % (sqlite3_version_str, + sqlite3_py_version_str) + + return "Gramps version: %s \n"\ + "Python version: %s \n"\ "BSDDB version: %s \n"\ - "sqlite version: %s (%s) \n"\ - "Gramps version: %s \n"\ + "%s"\ "LANG: %s\n"\ "OS: %s\n"\ "Distribution: %s\n\n"\ "GTK version : %s\n"\ "gobject version: %s\n"\ "cairo version : %s"\ - % (str(sys.version).replace('\n',''), + % (str(VERSION), + str(sys.version).replace('\n',''), BSDDB_STR, - sqlite3_version_str, - sqlite3_py_version_str, - str(VERSION), + sqlite, get_env_var('LANG',''), operatingsystem, distribution, '%d.%d.%d' % (Gtk.get_major_version(), - Gtk.get_minor_version(), Gtk.get_micro_version()), + Gtk.get_minor_version(), + Gtk.get_micro_version()), '%d.%d.%d' % GObject.pygobject_version, cairo.version_info) diff --git a/gramps/gui/logger/_errorview.py b/gramps/gui/logger/_errorview.py index ca878299d..84e07ab9c 100644 --- a/gramps/gui/logger/_errorview.py +++ b/gramps/gui/logger/_errorview.py @@ -106,8 +106,13 @@ class ErrorView(ManagedWindow): def draw_window(self): title = "%s - Gramps" % _("Error Report") self.top = Gtk.Dialog(title) + # look over the top level windows, it seems the oldest come first, so + # the most recent still visible window appears to be a good choice for + # a transient parent for win in self.top.list_toplevels(): - if win.is_active(): + if win == self.top: # not interested if this is us... + continue + if win.is_toplevel() and win.is_visible(): self.parent_window = win # for ManagedWindow if self.parent_window is None: # but it is on some screen self.parent_window = self.top.get_screen() diff --git a/gramps/gui/viewmanager.py b/gramps/gui/viewmanager.py index 494bae522..ab1177087 100644 --- a/gramps/gui/viewmanager.py +++ b/gramps/gui/viewmanager.py @@ -1178,7 +1178,8 @@ class ViewManager(CLIManager): self.dbstate.db.close(user=self.user) (filename, title) = value self.db_loader.read_file(filename) - self._post_load_newdb(filename, 'x-directory/normal', title) + if self.dbstate.db.is_open(): + self._post_load_newdb(filename, 'x-directory/normal', title) else: if dialog.after_change != "": # We change the title of the main window. diff --git a/gramps/gui/widgets/selectionwidget.py b/gramps/gui/widgets/selectionwidget.py index 9335e21e2..26e3e989d 100644 --- a/gramps/gui/widgets/selectionwidget.py +++ b/gramps/gui/widgets/selectionwidget.py @@ -197,6 +197,7 @@ class SelectionWidget(Gtk.ScrolledWindow): self.pixbuf = None self.scaled_pixbuf = None self.scale = 1.0 + self.old_viewport_size = None Gtk.ScrolledWindow.__init__(self) self.add(self._build_gui()) @@ -227,6 +228,7 @@ class SelectionWidget(Gtk.ScrolledWindow): self.event_box.add(self.image) self.viewport = Gtk.Viewport() + self.connect("size-allocate", self._resize) self.viewport.add(self.event_box) return self.viewport @@ -296,6 +298,7 @@ class SelectionWidget(Gtk.ScrolledWindow): self.pixbuf.get_height()) viewport_size = self.viewport.get_allocation() + self.old_viewport_size = viewport_size self.scale = scale_to_fit(self.pixbuf.get_width(), self.pixbuf.get_height(), viewport_size.width, @@ -313,6 +316,32 @@ class SelectionWidget(Gtk.ScrolledWindow): self.image.set_from_icon_name('image-missing', Gtk.IconSize.DIALOG) self.image.queue_draw() + def _resize(self, *dummy): + """ + Handles size-allocate' events from Gtk. + """ + if self.pixbuf: + viewport_size = self.viewport.get_allocation() + if viewport_size.height != self.old_viewport_size.height or \ + viewport_size.width != self.old_viewport_size.width or \ + not self.image.get_pixbuf(): + self.scale = scale_to_fit(self.pixbuf.get_width(), + self.pixbuf.get_height(), + viewport_size.width, + viewport_size.height) + self._rescale() + self.old_viewport_size = viewport_size + return False + + def expander(self, *dummy): + """ Handler for expander in caller; needed because Gtk doesn't handle + verticle expansion right + """ + self.image.clear() + self.image.set_size_request(2, 2) + self.event_box.set_size_request(2, 2) + return False + # ====================================================== # coordinate transformations (public methods) # ====================================================== @@ -531,14 +560,14 @@ class SelectionWidget(Gtk.ScrolledWindow): # drawing and scaling the image # ====================================================== - def _expose_handler(self, widget, event): + def _expose_handler(self, widget, cr): """ Handles the expose-event signal of the underlying widget. """ if self.pixbuf: - self._draw_selection() + self._draw_selection(widget, cr) - def _draw_selection(self): + def _draw_selection(self, widget, cr): """ Draws the image, the selection boxes and does the necessary shading. @@ -551,8 +580,6 @@ class SelectionWidget(Gtk.ScrolledWindow): offset_x -= 1 offset_y -= 1 - cr = self.image.get_window().cairo_create() - if self.selection: x1, y1, x2, y2 = self._rect_image_to_screen(self.selection) @@ -667,7 +694,8 @@ class SelectionWidget(Gtk.ScrolledWindow): return if event.button == 1: # left button self.start_point_screen = (event.x, event.y) - if self.current is not None and self.grabber is None: + if self.current is not None and self.grabber is None and \ + self.multiple_selection: self.current = None self.selection = None self.refresh() @@ -697,18 +725,23 @@ class SelectionWidget(Gtk.ScrolledWindow): if self.start_point_screen: if self.current is not None: # a box is currently selected - if self.grabber is None: - # clicked outside of the grabbing area - self.current = None - self.selection = None - self.emit("selection-cleared") - elif self.grabber != INSIDE: + if self.grabber and self.grabber != INSIDE: # clicked on one of the grabbers dx, dy = (event.x - self.start_point_screen[0], event.y - self.start_point_screen[1]) self.grabber_to_draw = self._modify_selection(dx, dy) self.current.set_coords(*self.selection) self.emit("region-modified") + elif self.grabber is None and self.multiple_selection: + # clicked outside of the grabbing area + self.current = None + self.selection = None + self.emit("selection-cleared") + else: + # update current selection + self.current.set_coords(*self.selection) + self.region = self.current + self.emit("region-modified") else: # nothing is currently selected if (minimum_region(self.start_point_screen, @@ -747,10 +780,11 @@ class SelectionWidget(Gtk.ScrolledWindow): dx, dy = (event.x - self.start_point_screen[0], event.y - self.start_point_screen[1]) self.grabber_to_draw = self._modify_selection(dx, dy) - elif self._can_select(): + else: # making new selection start_point = self._screen_to_truncated(self.start_point_screen) self.selection = order_coordinates(start_point, end_point) + else: # motion (mouse button is not pressed) self.in_region = self._find_region(*end_point_orig) diff --git a/gramps/plugins/db/bsddb/undoredo.py b/gramps/plugins/db/bsddb/undoredo.py index 21e1951e3..b69ac56a8 100644 --- a/gramps/plugins/db/bsddb/undoredo.py +++ b/gramps/plugins/db/bsddb/undoredo.py @@ -244,8 +244,9 @@ class DbUndo: pickle.loads(self.undodb[record_id]) if key != REFERENCE_KEY: - self.undo_signals(old_data, handle, self.mapbase[key], - db.emit, _SIGBASE[key]) + self.undo_signals(trans_type, handle, + db.emit, _SIGBASE[key], True) + # Notify listeners if db.undo_callback: if self.undo_count > 0: @@ -289,8 +290,8 @@ class DbUndo: pickle.loads(self.undodb[record_id]) if key != REFERENCE_KEY: - self.undo_signals(new_data, handle, self.mapbase[key], - db.emit, _SIGBASE[key]) + self.undo_signals(trans_type, handle, + db.emit, _SIGBASE[key], False) # Notify listeners if db.undo_callback: db.undo_callback(_("_Undo %s") @@ -336,24 +337,19 @@ class DbUndo: self.db._log_error() raise DbError(msg) - def undo_signals(self, data, handle, db_map, emit, signal_root): + def undo_signals(self, trans_type, handle, emit, signal_root, reverse): """ Helper method to undo/redo the changes made """ - try: - if data is None: - emit(signal_root + '-delete', ([handle.decode('utf-8')],)) - else: - ex_data = db_map.get(handle, txn=self.txn) - if ex_data: - signal = signal_root + '-update' - else: - signal = signal_root + '-add' - emit(signal, ([handle.decode('utf-8')],)) - - except DBERRS as msg: - self.db._log_error() - raise DbError(msg) + if ((not reverse) and trans_type == TXNADD) \ + or (reverse and trans_type == TXNDEL): + typ = '-add' + elif not reverse and trans_type == TXNDEL \ + or reverse and trans_type == TXNADD: + typ = '-delete' + else: # TXNUPD + typ = '-update' + emit(signal_root + typ, ([handle.decode('utf-8')],)) undo_count = property(lambda self:len(self.undoq)) redo_count = property(lambda self:len(self.redoq)) diff --git a/gramps/plugins/db/dbapi/postgresql.py b/gramps/plugins/db/dbapi/postgresql.py index eca488584..95246e9eb 100644 --- a/gramps/plugins/db/dbapi/postgresql.py +++ b/gramps/plugins/db/dbapi/postgresql.py @@ -65,6 +65,7 @@ class Postgresql: query = query.replace("?", "%s") query = query.replace("REGEXP", "~") query = query.replace("desc", "desc_") + query = query.replace("BLOB", "bytea") ## LIMIT offset, count ## count can be -1, for all ## LIMIT -1 @@ -114,7 +115,7 @@ class Postgresql: def table_exists(self, table): self.__cursor.execute("SELECT COUNT(*) " "FROM information_schema.tables " - "WHERE table_name=?;", [table]) + "WHERE table_name=%s;", [table]) return self.fetchone()[0] != 0 def close(self): diff --git a/gramps/plugins/gramplet/citations.py b/gramps/plugins/gramplet/citations.py index 5c5d168ed..b31e365d4 100644 --- a/gramps/plugins/gramplet/citations.py +++ b/gramps/plugins/gramplet/citations.py @@ -57,8 +57,14 @@ class Citations(Gramplet, DbGUIElement): """ called on init of DbGUIElement, connect to db as required. """ - self.callman.register_callbacks({'citation-update': self.changed}) - self.callman.connect_all(keys=['citation']) + self.callman.register_callbacks({'citation-update': self.changed, + 'person-update': self.changed, + 'family-update': self.changed, + 'event-update': self.changed, + 'media-update': self.changed, + 'place-update': self.changed}) + self.callman.connect_all(keys=['citation', 'family', 'person', 'event', + 'media', 'place']) def changed(self, handle): """ @@ -102,6 +108,7 @@ class Citations(Gramplet, DbGUIElement): self.add_media_citations(media) def add_media_citations(self, media): + self.callman.register_handles({'media': [media.handle]}) self.add_citations(media) self.add_attribute_citations(media) @@ -112,6 +119,7 @@ class Citations(Gramplet, DbGUIElement): self.add_event_citations(event) def add_event_citations(self, event): + self.callman.register_handles({'event': [event.handle]}) self.add_citations(event) self.add_attribute_citations(event) self.add_mediaref_citations(event) @@ -122,6 +130,7 @@ class Citations(Gramplet, DbGUIElement): self.add_place_citations(place) def add_place_citations(self, place): + self.callman.register_handles({'place': [place.handle]}) self.add_citations(place) self.add_mediaref_citations(place) @@ -133,9 +142,10 @@ class Citations(Gramplet, DbGUIElement): for lds in obj.get_lds_ord_list(): self.add_citations(lds) place_handle = lds.get_place_handle() - place = self.dbstate.db.get_place_from_handle(place_handle) - if place: - self.add_place_citations(place) + if place_handle: + place = self.dbstate.db.get_place_from_handle(place_handle) + if place: + self.add_place_citations(place) def add_association_citations(self, obj): for assoc in obj.get_person_ref_list(): @@ -310,7 +320,6 @@ class PersonCitations(Citations): if active_handle: active = self.dbstate.db.get_person_from_handle(active_handle) if active: - self.callman.register_obj(active) self.display_citations(active) else: self.set_has_data(False) @@ -322,6 +331,7 @@ class PersonCitations(Citations): Display the citations for the active person. """ self.source_nodes = {} + self.callman.register_handles({'person': [person.handle]}) self.add_citations(person) self.add_eventref_citations(person) for handle in person.get_family_handle_list(): @@ -387,7 +397,6 @@ class EventCitations(Citations): if active_handle: active = self.dbstate.db.get_event_from_handle(active_handle) if active: - self.callman.register_obj(active) self.display_citations(active) else: self.set_has_data(False) @@ -435,7 +444,6 @@ class FamilyCitations(Citations): if active_handle: active = self.dbstate.db.get_family_from_handle(active_handle) if active: - self.callman.register_obj(active) self.display_citations(active) else: self.set_has_data(False) @@ -447,6 +455,7 @@ class FamilyCitations(Citations): Display the citations for the active family. """ self.source_nodes = {} + self.callman.register_handles({'family': [family.handle]}) self.add_citations(family) self.add_eventref_citations(family) self.add_attribute_citations(family) @@ -496,7 +505,6 @@ class PlaceCitations(Citations): if active_handle: active = self.dbstate.db.get_place_from_handle(active_handle) if active: - self.callman.register_obj(active) self.display_citations(active) else: self.set_has_data(False) @@ -544,7 +552,6 @@ class MediaCitations(Citations): if active_handle: active = self.dbstate.db.get_media_from_handle(active_handle) if active: - self.callman.register_obj(active) self.display_citations(active) else: self.set_has_data(False) diff --git a/gramps/plugins/gramplet/topsurnamesgramplet.py b/gramps/plugins/gramplet/topsurnamesgramplet.py index 591b6746a..e3bf54c97 100644 --- a/gramps/plugins/gramplet/topsurnamesgramplet.py +++ b/gramps/plugins/gramplet/topsurnamesgramplet.py @@ -57,6 +57,7 @@ class TopSurnamesGramplet(Gramplet): self.dbstate.db.connect('person-update', self.update) self.dbstate.db.connect('person-rebuild', self.update) self.dbstate.db.connect('family-rebuild', self.update) + self.set_text(_("No Family Tree loaded.")) def on_load(self): if len(self.gui.data) > 0: diff --git a/gramps/plugins/graph/gvfamilylines.py b/gramps/plugins/graph/gvfamilylines.py index 4740b2e90..be07c94ad 100644 --- a/gramps/plugins/graph/gvfamilylines.py +++ b/gramps/plugins/graph/gvfamilylines.py @@ -895,6 +895,9 @@ class FamilyLinesReport(Report): # see if we have a table that needs to be terminated if image_path: label += '' + else: + # non html label is enclosed by "" so escape other " + label = label.replace('"', '\\\"') shape = "box" style = "solid" diff --git a/gramps/plugins/lib/libgedcom.py b/gramps/plugins/lib/libgedcom.py index d488f90ba..fa84585e4 100644 --- a/gramps/plugins/lib/libgedcom.py +++ b/gramps/plugins/lib/libgedcom.py @@ -133,6 +133,7 @@ from gramps.gen.db.dbconst import EVENT_KEY from gramps.gui.dialog import WarningDialog from gramps.gen.lib.const import IDENTICAL, DIFFERENT from gramps.gen.lib import (StyledText, StyledTextTag, StyledTextTagType) +from gramps.gen.lib.urlbase import UrlBase from gramps.plugins.lib.libplaceimport import PlaceImport from gramps.gen.display.place import displayer as _pd from gramps.gen.utils.grampslocale import GrampsLocale @@ -5181,11 +5182,21 @@ class GedcomParser(UpdateCallback): # The following code that detects URL is an older v5.5 usage; the # modern option is to use the EMAIL tag. if isinstance(sub_state.form, str) and sub_state.form == "url": - url = Url() - url.set_path(sub_state.filename) - url.set_description(sub_state.title) - url.set_type(UrlType.WEB_HOME) - pri_obj.add_url(url) + if isinstance(pri_obj, UrlBase): + url = Url() + url.set_path(sub_state.filename) + url.set_description(sub_state.title) + url.set_type(UrlType.WEB_HOME) + pri_obj.add_url(url) + else: # some primary objects (Event) son't have spot for URL + new_note = Note(sub_state.filename) + new_note.set_gramps_id(self.nid_map[""]) + new_note.set_handle(create_id()) + new_note.set_type(OBJ_NOTETYPE.get(type(pri_obj).__name__, + NoteType.GENERAL)) + self.dbase.commit_note(new_note, self.trans, new_note.change) + pri_obj.add_note(new_note.get_handle()) + else: # to allow import of references to URLs (especially for import from # geni.com), do not try to find the file if it is blatently a URL diff --git a/gramps/plugins/view/geoperson.py b/gramps/plugins/view/geoperson.py index c9f4a8914..67f5fbae9 100644 --- a/gramps/plugins/view/geoperson.py +++ b/gramps/plugins/view/geoperson.py @@ -201,12 +201,7 @@ class GeoPerson(GeoGraphyView): Rebuild the tree with the given person handle as the root. """ active = self.get_active() - #if handle: - # self._createmap(handle) - #elif active: - # p1 = self.dbstate.db.get_person_from_handle(active) - # self._createmap(p1) - self._createmap() + self._createmap(None) self.uistate.modify_statusbar(self.dbstate) def build_tree(self): @@ -216,8 +211,7 @@ class GeoPerson(GeoGraphyView): information. """ active = self.get_active() - #self._createmap(active) - self._createmap() + self._createmap(None) self.uistate.modify_statusbar(self.dbstate) def animate(self, menu, marks, index, stepyear): @@ -288,10 +282,11 @@ class GeoPerson(GeoGraphyView): menu, marks, i, stepyear) return False - def _createmap(self): + def _createmap(self, active): """ Create all markers for each people's event in the database which has a lat/lon. + @param: active is mandatory but unused in this view. Fix : 10088 """ dbstate = self.dbstate self.cal = config.get('preferences.calendar-format-report') diff --git a/gramps/plugins/view/relview.py b/gramps/plugins/view/relview.py index fcd9d3423..6c3c365fe 100644 --- a/gramps/plugins/view/relview.py +++ b/gramps/plugins/view/relview.py @@ -1541,10 +1541,11 @@ class RelationshipView(NavigationView): name.add_surname(Surname()) name.set_primary_surname(0) family = self.dbstate.db.get_family_from_handle(handle) - father = self.dbstate.db.get_person_from_handle( - family.get_father_handle()) - if father: - preset_name(father, name) + father_h = family.get_father_handle() + if father_h: + father = self.dbstate.db.get_person_from_handle(father_h) + if father: + preset_name(father, name) person.set_primary_name(name) try: EditPerson(self.dbstate, self.uistate, [], person, diff --git a/po/cs.po b/po/cs.po index df24c80a6..e35ee9c18 100644 --- a/po/cs.po +++ b/po/cs.po @@ -9712,7 +9712,7 @@ msgstr "" #: ../gramps/gen/relationship.py:1406 msgid "Relationship loop detected:" -msgstr "Detekována smyčka ve vztazích" +msgstr "Detekována smyčka ve vztazích:" #: ../gramps/gen/relationship.py:1407 #, python-format @@ -10040,7 +10040,7 @@ msgstr "Makedonština" #. Windows has no translation for Macedonian #: ../gramps/gen/utils/grampslocale.py:93 msgid "Norwegian Bokmal" -msgstr "Norština Bokmål" +msgstr "Norský Bokmål" #: ../gramps/gen/utils/grampslocale.py:94 msgid "Dutch" @@ -10048,7 +10048,7 @@ msgstr "Holandština" #: ../gramps/gen/utils/grampslocale.py:95 msgid "Norwegian Nynorsk" -msgstr "Norština (Nynorsk)" +msgstr "Norský Nynorsk" #: ../gramps/gen/utils/grampslocale.py:96 msgid "Polish" @@ -15559,8 +15559,7 @@ msgstr "" msgid "" "Field used to paste info from a web page like google, openstreetmap, ... " msgstr "" -"Pole použité pro vložení informace z webové stránky jako google, " -"openstreetmap, ..." +"Pole použité pro vložení informace z webové stránky jako google, openstreetmap, ... " #: ../gramps/gui/glade/editplace.glade:264 #: ../gramps/gui/glade/editplaceref.glade:495 @@ -30103,12 +30102,12 @@ msgstr "Zda použít rozvité, nebo holé věty." #: ../gramps/plugins/textreport/detancestralreport.py:869 #: ../gramps/plugins/textreport/detdescendantreport.py:1060 msgid "Use full dates instead of only the year" -msgstr "Používat plná data místo pouze let(roků)" +msgstr "Místo pouze roku používat plný datum" #: ../gramps/plugins/textreport/detancestralreport.py:871 #: ../gramps/plugins/textreport/detdescendantreport.py:1062 msgid "Whether to use full dates instead of just year." -msgstr "Zda používat plná data místo pouze let(roků)." +msgstr "Zda používat plný datum místo pouze roku." #: ../gramps/plugins/textreport/detancestralreport.py:874 #: ../gramps/plugins/textreport/detdescendantreport.py:1065 @@ -32603,7 +32602,7 @@ msgstr "Množství použitých ID." #: ../gramps/plugins/tool/reorderids.glade:1404 msgid " Quantity" -msgstr " Množství " +msgstr " Množství" #: ../gramps/plugins/tool/reorderids.glade:1419 msgid "Actual / Upcoming ID format."