2007-06-20 Don Allingham <don@gramps-project.org>

* src/DbManager.py: Handle more advanced RCS commands, such as
	rename revisions, checking out a new revision, and breaking locks
	* src/glade/gramps.glade: fix window positioning



svn: r8614
This commit is contained in:
Don Allingham 2007-06-20 23:36:09 +00:00
parent f5c87ae46a
commit dc360924e6
3 changed files with 236 additions and 52 deletions

View File

@ -1,3 +1,8 @@
2007-06-20 Don Allingham <don@gramps-project.org>
* src/DbManager.py: Handle more advanced RCS commands, such as
rename revisions, checking out a new revision, and breaking locks
* src/glade/gramps.glade: fix window positioning
2007-06-20 Alex Roitman <shura@gramps-project.org> 2007-06-20 Alex Roitman <shura@gramps-project.org>
* src/Filters/Rules/Person/_RelationshipPathBetween.py * src/Filters/Rules/Person/_RelationshipPathBetween.py
(apply_filter): Object/handle mixup, #1090. (apply_filter): Object/handle mixup, #1090.

View File

@ -76,6 +76,8 @@ import Config
DEFAULT_TITLE = _("Family Tree") DEFAULT_TITLE = _("Family Tree")
NAME_FILE = "name.txt" NAME_FILE = "name.txt"
META_NAME = "meta_data.db" META_NAME = "meta_data.db"
ARCHIVE = "rev.gramps"
ARCHIVE_V = "rev.gramps,v"
NAME_COL = 0 NAME_COL = 0
PATH_COL = 1 PATH_COL = 1
@ -149,7 +151,12 @@ class DbManager:
""" """
if event.type == gtk.gdk._2BUTTON_PRESS and event.button == 1: if event.type == gtk.gdk._2BUTTON_PRESS and event.button == 1:
store, node = self.selection.get_selected() store, node = self.selection.get_selected()
# don't open a locked file
if store.get_value(node,STOCK_COL) == 'gramps-lock': if store.get_value(node,STOCK_COL) == 'gramps-lock':
self.__ask_to_break_lock(store, node)
return
# don't open a version
if len(store.get_path(node)) > 1:
return return
if store.get_value(node,PATH_COL): if store.get_value(node,PATH_COL):
self.top.response(gtk.RESPONSE_OK) self.top.response(gtk.RESPONSE_OK)
@ -181,10 +188,9 @@ class DbManager:
if is_rev: if is_rev:
self.rcs.set_label(_("Restore")) self.rcs.set_label(_("Restore"))
self.rename.set_sensitive(False)
else: else:
self.rcs.set_label(_("Archive")) self.rcs.set_label(_("Archive"))
self.rename.set_sensitive(True) self.rename.set_sensitive(True)
if store.get_value(node, OPEN_COL): if store.get_value(node, OPEN_COL):
self.connect.set_sensitive(False) self.connect.set_sensitive(False)
@ -292,10 +298,10 @@ class DbManager:
for items in self.current_names: for items in self.current_names:
data = [items[0], items[1], items[2], items[3], data = [items[0], items[1], items[2], items[3],
items[4], items[5], items[6]] items[4], items[5], items[6]]
iter = self.model.append(None, data) node = self.model.append(None, data)
for rdata in find_revisions(os.path.join(items[1],"rev.gramps,v")): for rdata in find_revisions(os.path.join(items[1], ARCHIVE_V)):
data = [ rdata[2], "", "", rdata[1], 0, False, "" ] data = [ rdata[2], rdata[0], items[1], rdata[1], 0, False, "" ]
self.model.append(iter, data) self.model.append(node, data)
self.dblist.set_model(self.model) self.dblist.set_model(self.model)
def run(self): def run(self):
@ -313,6 +319,34 @@ class DbManager:
self.top.destroy() self.top.destroy()
return None return None
def __ask_to_break_lock(self, store, node):
path = store.get_path(node)
self.lock_file = store[path][PATH_COL]
QuestionDialog.QuestionDialog(
_("Break the lock on the '%s' database?") % store[path][0],
_("GRAMPS believes that someone else is actively editing "
"this database. You cannot edit this database while it "
"is locked. If no one is editing the database you may "
"safely break the lock. However, if someone else is editing "
"the database and you break the lock, you may corrupt the "
"database."),
_("Break lock"),
self.__really_break_lock)
def __really_break_lock(self):
try:
os.unlink(os.path.join(self.lock_file, "lock"))
store, node = self.selection.get_selected()
dbpath = store.get_value(node, PATH_COL)
(tval, last) = time_val(dbpath)
store.set_value(node, OPEN_COL, 0)
store.set_value(node, STOCK_COL, "")
store.set_value(node, DATE_COL, last)
store.set_value(node, DSORT_COL, tval)
except:
pass
def __change_name(self, text, path, new_text): def __change_name(self, text, path, new_text):
""" """
Changes the name of the database. This is a callback from the Changes the name of the database. This is a callback from the
@ -321,25 +355,79 @@ class DbManager:
If the new string is empty, do nothing. Otherwise, renaming the If the new string is empty, do nothing. Otherwise, renaming the
database is simply changing the contents of the name file. database is simply changing the contents of the name file.
""" """
if len(new_text) > 0: if len(new_text) > 0 and text != new_text:
node = self.model.get_iter(path) if len(path) > 1 :
filename = self.model.get_value(node, FILE_COL) self.__rename_revision(path, new_text)
try: else:
name_file = open(filename, "w") self.__rename_database(path, new_text)
name_file.write(new_text)
name_file.close() def __rename_revision(self, path, new_text):
self.model.set_value(node, NAME_COL, new_text) node = self.model.get_iter(path)
except (OSError, IOError), msg: db_dir = self.model.get_value(node, FILE_COL)
QuestionDialog.ErrorDialog( rev = self.model.get_value(node, PATH_COL)
_("Could not rename family tree"), archive = os.path.join(db_dir, ARCHIVE_V)
str(msg))
cmd = [ "rcs", "-m%s:%s" % (rev, new_text), archive ]
proc = subprocess.Popen(cmd, stderr = subprocess.PIPE)
status = proc.wait()
message = "\n".join(proc.stderr.readlines())
proc.stderr.close()
del proc
if status != 0:
from QuestionDialog import ErrorDialog
ErrorDialog(
_("Rename failed"),
_("An attempt to rename a version failed "
"with the following message:\n\n%s") % message
)
else:
self.model.set_value(node, NAME_COL, new_text)
def __rename_database(self, path, new_text):
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 __rcs(self, obj): def __rcs(self, obj):
store, node = self.selection.get_selected() store, node = self.selection.get_selected()
check_in(self.dbstate.db, tree_path = store.get_path(node)
os.path.join(self.dbstate.db.get_save_path(), "rev.gramps"), if len(tree_path) > 1:
None) parent_node = store.get_iter((tree_path[0],))
self.__populate() parent_name = store.get_value(parent_node, NAME_COL)
name = store.get_value(node, NAME_COL)
revision = store.get_value(node, PATH_COL)
db_path = store.get_value(node, FILE_COL)
new_path = self.__create_new_db("%s : %s" % (parent_name, name))
trans = Config.TRANSACTIONS
dbtype = 'x-directory/normal'
self.__start_cursor(_("Extracting archive..."))
db = GrampsDb.gramps_db_factory(dbtype)(trans)
db.load(new_path, None)
self.__start_cursor(_("Importing archive..."))
check_out(db, revision, db_path, name, None)
self.__end_cursor()
db.close()
self.__populate()
else:
base_path = self.dbstate.db.get_save_path()
archive = os.path.join(base_path, ARCHIVE)
check_in(self.dbstate.db, ARCHIVE, None, self.__start_cursor)
self.__end_cursor()
self.__populate()
def __remove_db(self, obj): def __remove_db(self, obj):
""" """
@ -347,16 +435,24 @@ class DbManager:
row and data, then call the verification dialog. row and data, then call the verification dialog.
""" """
store, node = self.selection.get_selected() store, node = self.selection.get_selected()
self.data_to_delete = store[store.get_path(node)] path = store.get_path(node)
self.data_to_delete = store[path]
QuestionDialog.QuestionDialog( if len(path) == 1:
_("Remove the '%s' database?") % self.data_to_delete[0], QuestionDialog.QuestionDialog(
_("Removing this database will permanently destroy the data."), _("Remove the '%s' database?") % self.data_to_delete[0],
_("Remove database"), _("Removing this database will permanently destroy the data."),
self.__really_delete_db) _("Remove database"),
self.__really_delete_db)
# rebuild the display else:
self.__populate() rev = self.data_to_delete[0]
parent = store[(path[0],)][0]
QuestionDialog.QuestionDialog(
_("Remove the '%s' version of %s") % (rev, parent),
_("Removing this version will prevent you from "
"restoring it in the future."),
_("Remove version"),
self.__really_delete_version)
def __really_delete_db(self): def __really_delete_db(self):
""" """
@ -378,6 +474,38 @@ class DbManager:
except (IOError, OSError), msg: except (IOError, OSError), msg:
QuestionDialog.ErrorDialog(_("Could not delete family tree"), QuestionDialog.ErrorDialog(_("Could not delete family tree"),
str(msg)) str(msg))
# rebuild the display
self.__populate()
def __really_delete_version(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.
"""
db_dir = self.data_to_delete[FILE_COL]
rev = self.data_to_delete[PATH_COL]
archive = os.path.join(db_dir, ARCHIVE_V)
cmd = [ "rcs", "-o%s" % rev, archive ]
proc = subprocess.Popen(cmd, stderr = subprocess.PIPE)
status = proc.wait()
message = "\n".join(proc.stderr.readlines())
proc.stderr.close()
del proc
if status != 0:
from QuestionDialog import ErrorDialog
ErrorDialog(
_("Deletion failed"),
_("An attempt to delete a version failed "
"with the following message:\n\n%s") % message
)
# rebuild the display
self.__populate()
def __rename_db(self, obj): def __rename_db(self, obj):
""" """
@ -410,19 +538,32 @@ class DbManager:
db = dbclass(Config.get(Config.TRANSACTIONS)) db = dbclass(Config.get(Config.TRANSACTIONS))
db.set_save_path(dirname) db.set_save_path(dirname)
db.load(dirname, None) db.load(dirname, None)
self.msg.set_label(_("Rebuilding database from backup files"))
self.top.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
while (gtk.events_pending()):
gtk.main_iteration()
GrampsDbUtils.Backup.restore(db)
self.top.window.set_cursor(None)
self.msg.set_label("") self.__start_cursor(_("Rebuilding database from backup files"))
GrampsDbUtils.Backup.restore(db)
self.__end_cursor()
db.close() db.close()
self.dbstate.no_database() self.dbstate.no_database()
self.__populate() self.__populate()
def __start_cursor(self, msg):
"""
Sets the cursor to the busy state, and displays the associated
message
"""
self.msg.set_label(msg)
self.top.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
while (gtk.events_pending()):
gtk.main_iteration()
def __end_cursor(self):
"""
Sets the cursor back to normal and clears the message
"""
self.top.window.set_cursor(None)
self.msg.set_label("")
def __new_db(self, obj): def __new_db(self, obj):
""" """
Callback wrapper around the actual routine that creates the Callback wrapper around the actual routine that creates the
@ -435,7 +576,7 @@ class DbManager:
QuestionDialog.ErrorDialog(_("Could not create family tree"), QuestionDialog.ErrorDialog(_("Could not create family tree"),
str(msg)) str(msg))
def __create_new_db(self): def __create_new_db(self, title=None):
""" """
Create a new database. Create a new database.
""" """
@ -445,9 +586,9 @@ class DbManager:
os.mkdir(new_path) os.mkdir(new_path)
path_name = os.path.join(new_path, NAME_FILE) path_name = os.path.join(new_path, NAME_FILE)
name_list = [ name[0] for name in self.current_names ] if title == None:
name_list = [ name[0] for name in self.current_names ]
title = find_next_db_name(name_list) title = find_next_db_name(name_list)
name_file = open(path_name, "w") name_file = open(path_name, "w")
name_file.write(title) name_file.write(title)
@ -461,6 +602,7 @@ class DbManager:
path = self.model.get_path(node) path = self.model.get_path(node)
self.dblist.set_cursor(path, focus_column=self.column, self.dblist.set_cursor(path, focus_column=self.column,
start_editing=True) start_editing=True)
return new_path
def find_next_db_name(name_list): def find_next_db_name(name_list):
""" """
@ -518,6 +660,9 @@ def icon_values(dirpath, active, open):
return (False, "") return (False, "")
def find_revisions(name): def find_revisions(name):
"""
Finds all the revisions of the specfied RCS archive.
"""
import re import re
rev = re.compile("\s*revision\s+([\d\.]+)") rev = re.compile("\s*revision\s+([\d\.]+)")
@ -556,7 +701,32 @@ def find_revisions(name):
revlist.append((rev_str, date_str, com_str)) revlist.append((rev_str, date_str, com_str))
return revlist return revlist
def check_in(db, filename, callback): def check_out(db, rev, path, name, callback):
co = [ "co", "-q%s" % rev] + [ os.path.join(path, ARCHIVE),
os.path.join(path, ARCHIVE_V)]
proc = subprocess.Popen(co, stderr = subprocess.PIPE)
status = proc.wait()
message = "\n".join(proc.stderr.readlines())
proc.stderr.close()
del proc
if status != 0:
from QuestionDialog import ErrorDialog
ErrorDialog(
_("Retrieve failed"),
_("An attempt to retrieve the data failed "
"with the following message:\n\n%s") % message
)
return
rdr = GrampsDbUtils.gramps_db_reader_factory(const.app_gramps_xml)
xml_file = os.path.join(path, ARCHIVE)
rdr(db, xml_file, callback)
os.unlink(xml_file)
def check_in(db, filename, callback, cursor_func = None):
init = [ "rcs", '-i', '-U', '-q', '-t-"GRAMPS database"', ] init = [ "rcs", '-i', '-U', '-q', '-t-"GRAMPS database"', ]
ci = [ "ci", "-q", "-f" ] ci = [ "ci", "-q", "-f" ]
@ -576,22 +746,31 @@ def check_in(db, filename, callback):
proc.stderr.close() proc.stderr.close()
del proc del proc
if cursor_func:
cursor_func(_("Creating data to be archived..."))
xmlwrite = GrampsDbUtils.XmlWriter(db, callback, False, 0) xmlwrite = GrampsDbUtils.XmlWriter(db, callback, False, 0)
xmlwrite.write(filename) xmlwrite.write(filename)
cmd = ci + ['-m%s' % comment, filename, filename + ",v" ] cmd = ci + ['-m%s' % comment, filename, filename + ",v" ]
if cursor_func:
cursor_func(_("Saving archive..."))
proc = subprocess.Popen( proc = subprocess.Popen(
cmd, cmd,
stdin = subprocess.PIPE, stdin = subprocess.PIPE,
stderr = subprocess.PIPE ) stderr = subprocess.PIPE )
proc.stdin.close() proc.stdin.close()
message = "\n".join(proc.stderr.readlines()) message = "\n".join(proc.stderr.readlines())
print message
proc.stderr.close() proc.stderr.close()
status = proc.wait() status = proc.wait()
del proc del proc
if __name__ == "__main__": if status != 0:
import sys from QuestionDialog import ErrorDialog
print find_revisions(sys.argv[1])
ErrorDialog(
_("Archiving failed"),
_("An attempt to archive the data failed "
"with the following message:\n\n%s") % message
)

View File

@ -15425,7 +15425,7 @@ Very High</property>
<property name="type">GTK_WINDOW_TOPLEVEL</property> <property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property> <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
<property name="modal">False</property> <property name="modal">False</property>
<property name="default_width">500</property> <property name="default_width">550</property>
<property name="default_height">300</property> <property name="default_height">300</property>
<property name="resizable">True</property> <property name="resizable">True</property>
<property name="destroy_with_parent">False</property> <property name="destroy_with_parent">False</property>
@ -15807,8 +15807,8 @@ Very High</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="title" translatable="yes">Revision comment - GRAMPS</property> <property name="title" translatable="yes">Revision comment - GRAMPS</property>
<property name="type">GTK_WINDOW_TOPLEVEL</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="modal">True</property>
<property name="default_width">450</property> <property name="default_width">450</property>
<property name="resizable">True</property> <property name="resizable">True</property>
<property name="destroy_with_parent">False</property> <property name="destroy_with_parent">False</property>
@ -15901,7 +15901,7 @@ Very High</property>
<property name="text" translatable="yes"></property> <property name="text" translatable="yes"></property>
<property name="has_frame">True</property> <property name="has_frame">True</property>
<property name="invisible_char">●</property> <property name="invisible_char">●</property>
<property name="activates_default">False</property> <property name="activates_default">True</property>
</widget> </widget>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>