* src/GrampsDb/_GrampsDbBase.py: handle close/delete of active database

* src/DbManager.py: clean up
	* src/DbState.py: issue database-changed signal on db close
	* src/GrampsDbUtils/_GedcomParse.py: fix adding of notes
	* src/DbLoader.py: don't give undo warning if importing into empty db


svn: r8347
This commit is contained in:
Don Allingham 2007-04-02 03:56:30 +00:00
parent 30ce0c5291
commit 5580ad12d3
9 changed files with 251 additions and 114 deletions

View File

@ -1,4 +1,9 @@
2007-04-01 Don Allingham <don@gramps-project.org>
* src/GrampsDb/_GrampsDbBase.py: handle close/delete of active database
* src/DbManager.py: clean up
* src/DbState.py: issue database-changed signal on db close
* src/GrampsDbUtils/_GedcomParse.py: fix adding of notes
* src/DbLoader.py: don't give undo warning if importing into empty db
* src/DataViews/_PedigreeView.py: display matches in statusbar
* src/DataViews/_PersonView.py: display matches in statusbar
* src/DataViews/_RelationView.py: display matches in statusbar

View File

@ -260,17 +260,19 @@ class DbLoader:
def import_file(self):
# First thing first: import is a batch transaction
# so we will lose the undo history. Warn the user.
warn_dialog = QuestionDialog.QuestionDialog2(
_('Undo history warning'),
_('Proceeding with import will erase the undo history '
'for this session. In particular, you will not be able '
'to revert the import or any changes made prior to it.\n\n'
'If you think you may want to revert the import, '
'please stop here and backup your database.'),
_('_Proceed with import'), _('_Stop'),
self.uistate.window)
if not warn_dialog.run():
return False
if self.dbstate.db.get_number_of_people() > 0:
warn_dialog = QuestionDialog.QuestionDialog2(
_('Undo history warning'),
_('Proceeding with import will erase the undo history '
'for this session. In particular, you will not be able '
'to revert the import or any changes made prior to it.\n\n'
'If you think you may want to revert the import, '
'please stop here and backup your database.'),
_('_Proceed with import'), _('_Stop'),
self.uistate.window)
if not warn_dialog.run():
return False
choose = gtk.FileChooserDialog(
_('GRAMPS: Import database'),
@ -336,7 +338,7 @@ class DbLoader:
const.app_gramps_xml,
const.app_gedcom):
importer = GrampsDbUtils.gramps_db_reader_factory(filetype)
self.do_import(choose,importer,filename)
self.do_import(choose, importer, filename)
return True
# Then we try all the known plugins
@ -345,7 +347,7 @@ class DbLoader:
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)
self.do_import(choose, importData, filename)
return True
# Finally, we give up and declare this an unknown format

View File

@ -18,12 +18,13 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# $Id: Bookmarks.py 8197 2007-02-20 20:56:48Z hippy $
"""
Provides the management of databases. This includes opening, renaming,
creating, and deleting of databases.
"""
"Handle bookmarks for the gramps interface"
__author__ = "Donald N. Allingham"
__version__ = "$Revision: 8197 $"
__author__ = "Donald N. Allingham"
__revision__ = "$Revision: 8197 $"
#-------------------------------------------------------------------------
#
@ -41,7 +42,7 @@ from gettext import gettext as _
#
#-------------------------------------------------------------------------
import logging
log = logging.getLogger(".DbManager")
LOG = logging.getLogger(".DbManager")
#-------------------------------------------------------------------------
#
@ -58,7 +59,6 @@ import gtk.glade
#-------------------------------------------------------------------------
import QuestionDialog
#-------------------------------------------------------------------------
#
# constants
@ -74,6 +74,7 @@ PATH_COL = 1
FILE_COL = 2
DATE_COL = 3
OPEN_COL = 5
STOCK_COL = 6
class DbManager:
"""
@ -98,6 +99,10 @@ class DbManager:
self.dblist = self.glade.get_widget('dblist')
self.rename = self.glade.get_widget('rename')
self.model = None
self.dbstate = dbstate
self.column = None
self.data_to_delete = None
if dbstate:
self.active = dbstate.db.get_save_path()
else:
@ -122,47 +127,93 @@ class DbManager:
self.dblist.connect('button-press-event', self.button_press)
def button_press(self, obj, event):
"""
Checks for a double click event. In the tree view, we want to
treat a double click as if it was OK button press. However, we have
to make sure that an item was selected first.
"""
if event.type == gtk.gdk._2BUTTON_PRESS and event.button == 1:
self.top.response(gtk.RESPONSE_OK)
data = self.selection.get_selected()
if data[1]:
self.top.response(gtk.RESPONSE_OK)
return True
return False
def selection_changed(self, selection):
store, iter = selection.get_selected()
if not iter or store.get_value(iter, OPEN_COL):
self.remove.set_sensitive(False)
"""
Called with the selection is changed in the TreeView. What we
are trying to detect is the selection or unselection of a row.
When a row is unselected, the Open, Rename, and Remove buttons
are set insensitive. If a row is selected, the rename and remove
buttons are disabled, and the Open button is disabled if the
row represents a open database.
"""
# Get the current selection
store, node = selection.get_selected()
if not node:
self.connect.set_sensitive(False)
self.rename.set_sensitive(False)
self.remove.set_sensitive(False)
else:
self.remove.set_sensitive(True)
self.connect.set_sensitive(True)
if store.get_value(node, OPEN_COL):
self.connect.set_sensitive(False)
else:
self.connect.set_sensitive(True)
self.rename.set_sensitive(True)
self.remove.set_sensitive(True)
def build_interface(self):
render = gtk.CellRendererPixbuf()
column = gtk.TreeViewColumn('', render, stock_id=6)
self.dblist.append_column(column)
"""
Builds the columns for the TreeView. The columns are:
Icon, Database Name, Last Modified
The Icon column gets its data from column 6 of the database model.
It is expecting either None, or a GTK stock icon name
The Database Name column is an editable column. We connect to the
'edited' signal, so that we can change the name when the user changes
the column.
The last modified column simply displays the last modification time.
"""
# build the icon column
render = gtk.CellRendererPixbuf()
icon_column = gtk.TreeViewColumn('', render, stock_id=STOCK_COL)
self.dblist.append_column(icon_column)
# build the database name column
render = gtk.CellRendererText()
render.set_property('editable',True)
render.set_property('editable', True)
render.connect('edited', self.change_name)
self.column = gtk.TreeViewColumn(_('Family tree name'), render,
text=NAME_COL)
self.dblist.append_column(self.column)
# build the last modified cocolumn
render = gtk.CellRendererText()
column = gtk.TreeViewColumn(_('Last modified'), render, text=DATE_COL)
self.dblist.append_column(column)
# set the rules hit
self.dblist.set_rules_hint(True)
def populate(self):
"""
Builds the display model.
"""
self.model = gtk.ListStore(str, str, str, str, int, bool, str)
try:
if not os.path.isdir(DEFAULT_DIR):
os.mkdir(DEFAULT_DIR)
except:
print "did not make default dir"
# make the default directory if it does not exist
try:
if not os.path.isdir(DEFAULT_DIR):
os.mkdir(DEFAULT_DIR)
except (IOError, OSError), msg:
LOG.error(_("Could not make database directory: ") + str(msg))
self.current_names = []
for dpath in os.listdir(DEFAULT_DIR):
@ -171,115 +222,190 @@ class DbManager:
if os.path.isfile(path_name):
name = file(path_name).readline().strip()
meta = os.path.join(dirpath, META_NAME)
if os.path.isfile(meta):
tval = os.stat(meta)[9]
last = time.asctime(time.localtime(tval))
else:
tval = 0
last = _("Never")
(tval, last) = time_val(dirpath)
(enable, stock_id) = icon_values(dirpath, self.active)
if dirpath == self.active:
enable = True
stock_id = gtk.STOCK_OPEN
else:
enable = False
stock_id = ""
self.current_names.append((name,
os.path.join(DEFAULT_DIR, dpath),
path_name,
last,
tval,
enable,
stock_id))
self.current_names.append(
(name, os.path.join(DEFAULT_DIR, dpath), path_name,
last, tval, enable, stock_id))
self.current_names.sort()
for items in self.current_names:
data = [items[0], items[1], items[2], items[3], items[4], items[5], items[6]]
data = [items[0], items[1], items[2], items[3],
items[4], items[5], items[6]]
self.model.append(data)
self.dblist.set_model(self.model)
def run(self):
"""
Runs the dialog, returning None if nothing has been chosen,
or the path and name if something has been selected
"""
value = self.top.run()
if value == gtk.RESPONSE_OK:
(model, node) = self.selection.get_selected()
if node:
self.top.destroy()
return (self.model.get_value(node, PATH_COL),
self.model.get_value(node, NAME_COL))
else:
self.top.destroy()
return None
else:
self.top.destroy()
return None
return (model.get_value(node, PATH_COL),
model.get_value(node, NAME_COL))
self.top.destroy()
return None
def change_name(self, text, path, new_text):
"""
Changes the name of the database. This is a callback from the
column, which has been marked as editable.
If the new string is empty, do nothing. Otherwise, renaming the
database is simply changing the contents of the name file.
"""
if len(new_text) > 0:
iter = self.model.get_iter(path)
filename = self.model.get_value(iter, FILE_COL)
node = self.model.get_iter(path)
filename = self.model.get_value(node, FILE_COL)
try:
f = open(filename, "w")
f.write(new_text)
f.close()
self.model.set_value(iter, NAME_COL, new_text)
except:
pass
name_file = open(filename, "w")
name_file.write(new_text)
name_file.close()
self.model.set_value(node, NAME_COL, new_text)
except (OSError, IOError), msg:
QuestionDialog.ErrorDialog(
_("Could not rename family tree"),
str(msg))
def remove_db(self, obj):
store, iter = self.selection.get_selected()
path = store.get_path(iter)
row = store[path]
if row[OPEN_COL]:
return
self.data_to_delete = (row[0], row[1], row[2])
"""
Callback associated with the Remove button. Get the selected
row and data, then call the verification dialog.
"""
store, node = self.selection.get_selected()
self.data_to_delete = store[store.get_path(node)]
QuestionDialog.QuestionDialog(
_("Remove the '%s' database?") % self.data_to_delete[0],
_("Removing this database will permanently destroy "
"the data."),
_("Removing this database will permanently destroy the data."),
_("Remove database"),
self.really_delete_db)
# rebuild the display
self.populate()
def really_delete_db(self):
for (top, dirs, files) in os.walk(self.data_to_delete[1]):
for f in files:
os.unlink(os.path.join(top,f))
os.rmdir(top)
"""
Delete the selected database. If the databse is open, close it first.
Then scan the database directory, deleting the files, and finally
removing the directory.
"""
# close the database if the user has requested to delete the
# active database
if self.data_to_delete[OPEN_COL]:
self.dbstate.no_database()
try:
for (top, dirs, files) in os.walk(self.data_to_delete[1]):
for filename in files:
os.unlink(os.path.join(top, filename))
os.rmdir(self.data_to_delete[1])
except (IOError, OSError), msg:
QuestionDialog.ErrorDialog(_("Could not delete family tree"),
str(msg))
def rename_db(self, obj):
"""
Start the rename process by calling the start_editing option on
the line with the cursor.
"""
store, node = self.selection.get_selected()
path = self.model.get_path(node)
self.dblist.set_cursor(path, focus_column=self.column, start_editing=True)
self.dblist.set_cursor(path, focus_column=self.column,
start_editing=True)
def new_db(self, obj):
while True:
base = "%x" % int(time.time())
new_path = os.path.join(DEFAULT_DIR, base)
if not os.path.isdir(new_path):
break
"""
Callback wrapper around the actual routine that creates the
new database. Catch OSError and IOError and display a warning
message.
"""
try:
self.mk_db()
except (OSError, IOError), msg:
QuestionDialog.ErrorDialog(_("Could not create family tree"),
str(msg))
def mk_db(self):
"""
Create a new database.
"""
new_path = find_next_db_dir()
os.mkdir(new_path)
path_name = os.path.join(new_path, NAME_FILE)
name_list = [ name[0] for name in self.current_names ]
i = 1
while True:
title = "%s %d" % (DEFAULT_TITLE, i)
if title not in name_list:
break
i += 1
title = find_next_db_name(name_list)
f = open(path_name, "w")
f.write(title)
f.close()
name_file = open(path_name, "w")
name_file.write(title)
name_file.close()
self.current_names.append(title)
node = self.model.append([title, new_path, path_name, _("Never"), 0, False, ''])
node = self.model.append([title, new_path, path_name,
_("Never"), 0, False, ''])
self.selection.select_iter(node)
path = self.model.get_path(node)
self.dblist.set_cursor(path, focus_column=self.column, start_editing=True)
self.dblist.set_cursor(path, focus_column=self.column,
start_editing=True)
def find_next_db_name(name_list):
"""
Scan the name list, looking for names that do not yet exist.
Use the DEFAULT_TITLE as the basis for the database name.
"""
i = 1
while True:
title = "%s %d" % (DEFAULT_TITLE, i)
if title not in name_list:
return title
i += 1
def find_next_db_dir():
"""
Searches the default directory for the first available default
database name. Base the name off the current time. In all actuality,
the first should be valid.
"""
while True:
base = "%x" % int(time.time())
new_path = os.path.join(DEFAULT_DIR, base)
if not os.path.isdir(new_path):
break
return new_path
def time_val(dirpath):
"""
Return the last modified time of the database. We do this by looking
at the modification time of the meta db file. If this file does not
exist, we indicate that database as never modified.
"""
meta = os.path.join(dirpath, META_NAME)
if os.path.isfile(meta):
tval = os.stat(meta)[9]
last = time.asctime(time.localtime(tval))
else:
tval = 0
last = _("Never")
return (tval, last)
def icon_values(dirpath, active):
"""
If the directory path is the active path, then return values
that indicate to use the icon, and which icon to use.
"""
if dirpath == active:
return (True, gtk.STOCK_OPEN)
else:
return (False, "")

View File

@ -86,4 +86,4 @@ class DbState(GrampsDBCallback):
self.db = GrampsDbBase()
self.active = None
self.open = False
self.emit('no-database')
self.emit('database-changed', (self.db, ))

View File

@ -691,6 +691,9 @@ class GrampsDbBase(GrampsDBCallback):
Commits the specified Note to the database, storing the changes
as part of the transaction.
"""
if not note.gramps_id:
import traceback
traceback.print_stack()
self._commit_base(note, self.note_map, NOTE_KEY,
transaction.note_update,
@ -1487,7 +1490,7 @@ class GrampsDbBase(GrampsDBCallback):
"""
self.nprefix = self._validated_id_prefix(val, "N")
def transaction_begin(self, msg="",batch=False,no_magic=False):
def transaction_begin(self, msg="", batch=False, no_magic=False):
"""
Creates a new Transaction tied to the current UNDO database. The
transaction has no effect until it is committed using the

View File

@ -1072,7 +1072,7 @@ class GedcomParser(UpdateCallback):
note.set_handle(intid)
note.set_gramps_id(gramps_id)
if need_commit:
self.dbase.commit_note(note, self.trans)
self.dbase.add_note(note, self.trans)
return note
def __find_or_create_place(self, title):
@ -4258,7 +4258,7 @@ class GedcomParser(UpdateCallback):
else:
new_note = RelLib.Note(line.data)
new_note.set_handle(Utils.create_id())
self.dbase.commit_note(new_note, self.trans)
self.dbase.add_note(new_note, self.trans)
self.__skip_subordinate_levels(level+1)
def __parse_inline_note(self, line, level):
@ -4267,7 +4267,7 @@ class GedcomParser(UpdateCallback):
handle = self.nid2id.get(gid)
new_note.set_handle(handle)
new_note.set_gramps_id(gid)
self.dbase.commit_note(new_note,self.trans)
self.dbase.add_note(new_note,self.trans)
self.nid2id[new_note.gramps_id] = new_note.handle
self.__skip_subordinate_levels(level+1)

View File

@ -560,7 +560,7 @@
<property name="visible">True</property>
<property name="title" translatable="yes">GRAMPS - GEDCOM Encoding</property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_NONE</property>
<property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
<property name="modal">False</property>
<property name="resizable">True</property>
<property name="destroy_with_parent">False</property>

View File

@ -735,11 +735,11 @@ class ListView(BookMarkView):
self.tooltips = TreeTips.TreeTips(
self.list, self.model.tooltip_column, True)
self.dirty = False
self.uistate.show_filter_results(self.dbstate,
self.model.displayed,
self.model.total)
else:
self.dirty = True
self.uistate.show_filter_results(self.dbstate,
self.model.displayed,
self.model.total)
def filter_toggle_action(self,obj):
if obj.get_active():
@ -853,6 +853,10 @@ class ListView(BookMarkView):
return False
def change_page(self):
if self.model:
self.uistate.show_filter_results(self.dbstate,
self.model.displayed,
self.model.total)
self.edit_action.set_sensitive(not self.dbstate.db.readonly)
def key_delete(self):

View File

@ -244,9 +244,6 @@ class Gramps:
ah.handle_args()
self.vm.post_init_interface()
# state.db.request_rebuild()
# state.change_active_person(state.db.get_default_person())
if Config.get(Config.USE_TIPS):
TipOfDay.TipOfDay(self.vm.uistate)