* 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,6 +260,8 @@ class DbLoader:
def import_file(self):
# First thing first: import is a batch transaction
# so we will lose the undo history. Warn the user.
if self.dbstate.db.get_number_of_people() > 0:
warn_dialog = QuestionDialog.QuestionDialog2(
_('Undo history warning'),
_('Proceeding with import will erase the undo history '

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 $
"Handle bookmarks for the gramps interface"
"""
Provides the management of databases. This includes opening, renaming,
creating, and deleting of databases.
"""
__author__ = "Donald N. Allingham"
__version__ = "$Revision: 8197 $"
__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,27 +127,65 @@ 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:
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:
if store.get_value(node, OPEN_COL):
self.connect.set_sensitive(False)
else:
self.remove.set_sensitive(True)
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.connect('edited', self.change_name)
@ -150,19 +193,27 @@ class DbManager:
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)
# make the default directory if it does not exist
try:
if not os.path.isdir(DEFAULT_DIR):
os.mkdir(DEFAULT_DIR)
except:
print "did not make 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,6 +222,174 @@ class DbManager:
if os.path.isfile(path_name):
name = file(path_name).readline().strip()
(tval, last) = time_val(dirpath)
(enable, stock_id) = icon_values(dirpath, self.active)
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]]
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 (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:
node = self.model.get_iter(path)
filename = self.model.get_value(node, FILE_COL)
try:
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):
"""
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."),
_("Remove database"),
self.really_delete_db)
# rebuild the display
self.populate()
def really_delete_db(self):
"""
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)
def new_db(self, obj):
"""
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 ]
title = find_next_db_name(name_list)
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, ''])
self.selection.select_iter(node)
path = self.model.get_path(node)
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]
@ -178,108 +397,15 @@ class DbManager:
else:
tval = 0
last = _("Never")
return (tval, last)
if dirpath == self.active:
enable = True
stock_id = gtk.STOCK_OPEN
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:
enable = False
stock_id = ""
return (False, "")
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]]
self.model.append(data)
self.dblist.set_model(self.model)
def run(self):
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
def change_name(self, text, path, new_text):
if len(new_text) > 0:
iter = self.model.get_iter(path)
filename = self.model.get_value(iter, FILE_COL)
try:
f = open(filename, "w")
f.write(new_text)
f.close()
self.model.set_value(iter, NAME_COL, new_text)
except:
pass
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])
QuestionDialog.QuestionDialog(
_("Remove the '%s' database?") % self.data_to_delete[0],
_("Removing this database will permanently destroy "
"the data."),
_("Remove database"),
self.really_delete_db)
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)
def rename_db(self, obj):
store, node = self.selection.get_selected()
path = self.model.get_path(node)
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
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
f = open(path_name, "w")
f.write(title)
f.close()
self.current_names.append(title)
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)

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,

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
else:
self.dirty = True
self.uistate.show_filter_results(self.dbstate,
self.model.displayed,
self.model.total)
else:
self.dirty = True
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)