diff --git a/autogen.sh b/autogen.sh
index a10fbbaa1..1c39530a7 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -5,7 +5,7 @@
PKG_NAME="gramps"
srcdir=`dirname $0`
test -z "$srcdir" && srcdir=.
-srcfile=$srcdir/src/gramps_main.py
+srcfile=$srcdir/src/gramps.py
REQUIRED_AUTOMAKE_VERSION=1.9
DIE=0
diff --git a/po/POTFILES.in b/po/POTFILES.in
index aef0705d0..f7a061a2f 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -4,7 +4,6 @@
# Python files
#
src/ansel_utf8.py
-src/ArgHandler.py
src/Assistant.py
src/AutoComp.py
src/Bookmarks.py
@@ -12,8 +11,6 @@ src/ColumnOrder.py
src/const.py
src/DateEdit.py
src/Date.py
-src/DbLoader.py
-src/DbManager.py
src/DdTargets.py
src/DisplayState.py
src/Errors.py
@@ -22,7 +19,6 @@ src/ExportOptions.py
src/GrampsAboutDialog.py
src/GrampsCfg.py
src/GrampsDisplay.py
-src/gramps_main.py
src/gramps.py
src/ImgManip.py
src/ListModel.py
@@ -47,10 +43,13 @@ src/TransUtils.py
src/TreeTips.py
src/Utils.py
src/UndoHistory.py
-src/ViewManager.py
# cli
src/cli/__init__.py
+src/cli/arghandler.py
+src/cli/argparser.py
+src/cli/clidbman.py
+src/cli/grampscli.py
# gen API
src/gen/__init__.py
@@ -182,6 +181,10 @@ src/gen/plug/docbackend/docbackend.py
# gui - GUI code
src/gui/__init__.py
+src/gui/dbloader.py
+src/gui/dbman.py
+src/gui/grampsgui.py
+src/gui/viewmanager.py
# gui/views - the GUI views package
src/gui/views/__init__.py
@@ -823,7 +826,7 @@ src/glade/grampscfg.glade
src/glade/dateedit.glade
src/glade/editsource.glade
src/glade/styleeditor.glade
-src/glade/dbmanager.glade
+src/glade/dbman.glade
src/glade/editurl.glade
src/glade/editrepository.glade
src/glade/editreporef.glade
diff --git a/src/Assistant.py b/src/Assistant.py
index 0dc35ae83..1bfb59ced 100644
--- a/src/Assistant.py
+++ b/src/Assistant.py
@@ -170,6 +170,7 @@ class Assistant(gtk.Object, ManagedWindow.ManagedWindow):
gtk.main_iteration()
def destroy(self, *obj):
+ self.window.emit('delete-event', None)
self.window.destroy()
def do_get_property(self, prop):
diff --git a/src/DbState.py b/src/DbState.py
index be24371cb..afe909027 100644
--- a/src/DbState.py
+++ b/src/DbState.py
@@ -28,7 +28,7 @@ import Config
class DbState(Callback):
"""
- Provide a class to encapsulate the state of the database..
+ Provide a class to encapsulate the state of the database.
"""
__signals__ = {
diff --git a/src/GrampsLogger/_ErrorReportAssistant.py b/src/GrampsLogger/_ErrorReportAssistant.py
index df1b90797..89441f6d0 100644
--- a/src/GrampsLogger/_ErrorReportAssistant.py
+++ b/src/GrampsLogger/_ErrorReportAssistant.py
@@ -8,7 +8,7 @@ import sys, os,bsddb
class ErrorReportAssistant(object):
- def __init__(self,error_detail,rotate_handler):
+ def __init__(self,error_detail,rotate_handler, ownthread=False):
self._error_detail = error_detail
self._rotate_handler = rotate_handler
@@ -18,6 +18,9 @@ class ErrorReportAssistant(object):
self._final_report_text_buffer = None
self.w = Assistant.Assistant(None,None,self.complete)
+ #connect our extra close to close by x, and close by cancel click
+ self.w.window.connect('delete-event', self.close)
+ self.w.cancel.connect('clicked', self.close)
self.w.add_text_page(
_('Report a bug'),
@@ -52,13 +55,23 @@ class ErrorReportAssistant(object):
self.w.connect('page-changed',self.on_page_changed)
self.w.show()
+
+ self.ownthread = ownthread
+ if self.ownthread:
+ gtk.main()
+ def close(self, *obj):
+ if self.ownthread:
+ gtk.main_quit()
+
def on_page_changed(self, obj,page,data=None):
if page in self.cb:
self.cb[page]()
def complete(self):
- pass
+ if self.ownthread:
+ #stop the thread we started
+ gtk.main_quit()
def _copy_to_clipboard(self, obj=None):
clipboard = gtk.Clipboard()
@@ -75,7 +88,8 @@ class ErrorReportAssistant(object):
def _start_email_client(self, obj=None):
import GrampsDisplay
- GrampsDisplay.url('mailto:gramps-bugs@lists.sourceforge.net?subject="bug report"&body="%s"' \
+ GrampsDisplay.url('mailto:gramps-bugs@lists.sourceforge.net?subject='
+ '"bug report"&body="%s"' \
% self._final_report_text_buffer.get_text(
self._final_report_text_buffer.get_start_iter(),
self._final_report_text_buffer.get_end_iter()))
diff --git a/src/GrampsLogger/_ErrorView.py b/src/GrampsLogger/_ErrorView.py
index cc9f0c1a1..3c9edfe4a 100644
--- a/src/GrampsLogger/_ErrorView.py
+++ b/src/GrampsLogger/_ErrorView.py
@@ -46,9 +46,12 @@ class ErrorView(object):
if response == gtk.RESPONSE_HELP:
self.help_clicked()
elif response == gtk.RESPONSE_YES:
+ self.top.destroy()
ErrorReportAssistant(error_detail = self._error_detail,
- rotate_handler = self._rotate_handler)
- self.top.destroy()
+ rotate_handler = self._rotate_handler,
+ ownthread=True)
+ elif response == gtk.RESPONSE_CANCEL:
+ self.top.destroy()
def help_clicked(self):
"""Display the relevant portion of GRAMPS manual"""
diff --git a/src/GrampsLogger/_GtkHandler.py b/src/GrampsLogger/_GtkHandler.py
index 81b2bb94e..ee269c835 100644
--- a/src/GrampsLogger/_GtkHandler.py
+++ b/src/GrampsLogger/_GtkHandler.py
@@ -21,9 +21,7 @@ class GtkHandler(logging.Handler):
def emit(self, record):
"""
Add the record to the rotating buffer.
-
"""
-
self._record = record
ErrorView(error_detail=self,rotate_handler=self._rotate_handler)
diff --git a/src/Makefile.am b/src/Makefile.am
index 98d3fa4a3..853fdd35b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -3,6 +3,7 @@
SUBDIRS = \
BasicUtils \
+ cli \
Config \
data \
DataViews \
@@ -18,6 +19,7 @@ SUBDIRS = \
GrampsDbUtils \
GrampsLocale \
GrampsLogger \
+ gui \
images \
Merge \
mapstraction \
@@ -33,7 +35,6 @@ gdirdir=$(prefix)/share/gramps
gdir_PYTHON = \
ansel_utf8.py\
- ArgHandler.py\
Assistant.py\
AutoComp.py\
Bookmarks.py\
@@ -41,8 +42,6 @@ gdir_PYTHON = \
const.py\
DateEdit.py\
Date.py\
- DbLoader.py\
- DbManager.py\
DbState.py\
DdTargets.py\
DisplayState.py\
@@ -52,7 +51,6 @@ gdir_PYTHON = \
GrampsAboutDialog.py\
GrampsCfg.py\
GrampsDisplay.py\
- gramps_main.py\
gramps.py\
ImgManip.py\
LdsUtils.py \
@@ -77,7 +75,6 @@ gdir_PYTHON = \
TransUtils.py\
TreeTips.py\
Utils.py\
- ViewManager.py\
UndoHistory.py\
PlaceUtils.py\
ProgressDialog.py
diff --git a/src/cli/Makefile.am b/src/cli/Makefile.am
index 70d6a4013..424da2228 100644
--- a/src/cli/Makefile.am
+++ b/src/cli/Makefile.am
@@ -6,7 +6,11 @@
pkgdatadir = $(datadir)/@PACKAGE@/cli
pkgdata_PYTHON = \
- __init__.py
+ __init__.py \
+ arghandler.py \
+ argparser.py \
+ clidbman.py \
+ grampscli.py
pkgpyexecdir = @pkgpyexecdir@/cli
pkgpythondir = @pkgpythondir@/cli
diff --git a/src/cli/__init__.py b/src/cli/__init__.py
index f4f4e672b..c719604bb 100644
--- a/src/cli/__init__.py
+++ b/src/cli/__init__.py
@@ -22,3 +22,8 @@
"""
Package init for the cli package.
"""
+
+from grampscli import startcli, CLIDbLoader, CLIManager
+from argparser import ArgParser
+from arghandler import ArgHandler
+from clidbman import CLIDbManager
diff --git a/src/ArgHandler.py b/src/cli/arghandler.py
similarity index 55%
rename from src/ArgHandler.py
rename to src/cli/arghandler.py
index d23b930d2..2d5edbd43 100644
--- a/src/ArgHandler.py
+++ b/src/cli/arghandler.py
@@ -2,7 +2,7 @@
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2006 Donald N. Allingham, A. Roitman
-# Copyright (C) 2007-2008 B. Malengier
+# Copyright (C) 2007-2009 B. Malengier
# Copyright (C) 2008 Lukasz Rymarczyk
# Copyright (C) 2008 Raphael Ackermann
# Copyright (C) 2008 Brian G. Matherly
@@ -22,7 +22,7 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-# $Id$
+# $Id: ArgHandler.py 12559 2009-05-21 17:19:50Z gbritton $
"""
Module responsible for handling the command line arguments for GRAMPS.
@@ -49,186 +49,83 @@ import Config
import RecentFiles
import Utils
import gen
-from DbManager import CLIDbManager, NAME_FILE, find_locker_name
+from clidbman import CLIDbManager, NAME_FILE, find_locker_name
from PluginUtils import Tool
from gen.plug import PluginManager
from ReportBase import CATEGORY_BOOK, CATEGORY_CODE, cl_report
-# Note: Make sure to edit const.py POPT_TABLE too!
-_help = """
-Usage: gramps.py [OPTION...]
- --load-modules=MODULE1,MODULE2,... Dynamic modules to load
-
-Help options
- -?, --help Show this help message
- --usage Display brief usage message
-
-Application options
- -O, --open=FAMILY_TREE Open family tree
- -i, --import=FILENAME Import file
- -e, --export=FILENAME Export file
- -f, --format=FORMAT Specify format
- -a, --action=ACTION Specify action
- -p, --options=OPTIONS_STRING Specify options
- -d, --debug=LOGGER_NAME Enable debug logs
- -l List Family Trees
- -L List Family Tree Details
- -u, --force-unlock Force unlock of family tree
-"""
-
#-------------------------------------------------------------------------
# ArgHandler
#-------------------------------------------------------------------------
class ArgHandler(object):
"""
- This class is responsible for handling command line arguments (if any)
- given to gramps. The valid arguments are:
-
- FAMTREE : family tree name or database dir to open.
- All following arguments will be ignored.
- -O, --open=FAMTREE : Family tree or family tree database dir to open.
- -i, --import=FILE : filename to import.
- -e, --export=FILE : filename to export.
- -f, --format=FORMAT : format of the file preceding this option.
-
- If the filename (no flags) is specified, the interactive session is
- launched using data from filename.
- In this mode (filename, no flags), the rest of the arguments is ignored.
- This is a mode suitable by default for GUI launchers, mime type handlers,
- and the like
-
- If no filename or -i option is given, a new interactive session (empty
- database) is launched, since no data is given anyway.
-
- If -O or -i option is given, but no -e or -a options are given, an
- interactive session is launched with the FILE (specified with -i).
-
- If both input (-O or -i) and processing (-e or -a) options are given,
- interactive session will not be launched.
+ This class is responsible for the non GUI handling of commands
+ The handler is passed a parser object, sanitizes it, and can execute the
+ actions requested working on a DbState
"""
- def __init__(self, state, vm, args):
- self.state = state
- self.vm = vm
- self.args = args
-
- self.open_gui = None
- self.open = None
+ def __init__(self, dbstate, parser, sessionmanager,
+ errorfunc=None, gui=False):
+ self.dbstate = dbstate
+ self.sm = sessionmanager
+ self.errorfunc = errorfunc
+ self.gui = gui
+ self.dbman = CLIDbManager(self.dbstate)
+ self.force_unlock = parser.force_unlock
+ self.open = self.__handle_open_option(parser.open)
self.cl = 0
- self.exports = []
- self.actions = []
self.imports = []
+ self.exports = []
+ self.sanitize_args(parser.imports, parser.exports)
+ self.open_gui = parser.open_gui
+ if self.gui:
+ self.actions = []
+ self.list = False
+ self.list_more = False
+ self.open_gui = None
+ else:
+ self.actions = parser.actions
+ self.list = parser.list
+ self.list_more = parser.list_more
self.imp_db_path = None
- self.list = False
- self.list_more = False
- self.help = False
- self.force_unlock = False
- self.dbman = CLIDbManager(self.state)
-
- self.parse_args()
+
+ def error(self, string):
+ if self.errorfunc:
+ self.errorfunc(string)
+ else:
+ print string
#-------------------------------------------------------------------------
# Argument parser: sorts out given arguments
#-------------------------------------------------------------------------
- def parse_args(self):
+ def sanitize_args(self, importlist, exportlist):
"""
- Fill in lists with open, exports, imports, and actions options.
-
- Any parsing errors lead to abort.
-
- Possible:
- 1/ Just the family tree (name or database dir)
- 2/ -O, Open of a family tree
- 3/ -i, Import of any format understood by an importer, optionally provide
- -f to indicate format
- 4/ -e, export a family tree in required format, optionally provide
- -f to indicate format
- 5/ -a, --action: An action (possible: 'check', 'summary', 'report',
- 'tool')
- 6/ -u, --force-unlock: A locked database can be unlocked by given this
- argument when opening it
-
+ check the lists with open, exports, imports, and actions options.
"""
- try:
- options, leftargs = getopt.getopt(self.args[1:],
- const.SHORTOPTS, const.LONGOPTS)
- except getopt.GetoptError, msg:
- print msg
- # return without filling anything if we could not parse the args
- print "Error parsing the arguments: %s " % self.args[1:]
- print "Type gramps --help for an overview of commands, or ",
- print "read manual pages."
- sys.exit(0)
-
- if leftargs:
- # if there were an argument without option,
- # use it as a file to open and return
- self.open_gui = leftargs[0]
- print "Trying to open: %s ..." % leftargs[0]
- #see if force open is on
- for opt_ix in range(len(options)):
- option, value = options[opt_ix]
- if option in ('-u', '--force-unlock'):
- self.force_unlock = True
- break
- return
-
- # Go over all given option and place them into appropriate lists
- for opt_ix in range(len(options)):
- option, value = options[opt_ix]
- if option in ( '-O', '--open'):
- self.__handle_open_option(value)
- elif option in ( '-i', '--import'):
- format = None
- if opt_ix < len(options) - 1 \
- and options[opt_ix + 1][0] in ( '-f', '--format'):
- format = options[opt_ix + 1][1]
- self.__handle_import_option(value, format)
- elif option in ( '-e', '--export' ):
- format = None
- if opt_ix < len(options) - 1 \
- and options[opt_ix + 1][0] in ( '-f', '--format'):
- format = options[opt_ix + 1][1]
- self.__handle_export_option(value, format)
- elif option in ( '-a', '--action' ):
- action = value
- if action not in ( 'check', 'summary', 'report', 'tool' ):
- print "Unknown action: %s. Ignoring." % action
- continue
- options_str = ""
- if opt_ix < len(options)-1 \
- and options[opt_ix+1][0] in ( '-p', '--options' ):
- options_str = options[opt_ix+1][1]
- self.actions.append((action, options_str))
- elif option in ('-d', '--debug'):
- logger = logging.getLogger(value)
- logger.setLevel(logging.DEBUG)
- elif option in ('-l',):
- self.list = True
- elif option in ('-L',):
- self.list_more = True
- elif option in ('-h', '-?', '--help'):
- self.help = True
- elif option in ('-u', '--force-unlock'):
- self.force_unlock = True
+ for (value, format) in importlist:
+ self.__handle_import_option(value, format)
+ for (value, format) in exportlist:
+ self.__handle_export_option(value, format)
def __handle_open_option(self, value):
"""
Handle the "-O" or "--open" option.
"""
+ if value is None:
+ return None
db_path = self.__deduce_db_path(value)
if db_path:
# We have a potential database path.
# Check if it is good.
- if not self.__check_db(db_path, self.force_unlock):
+ if not self.check_db(db_path, self.force_unlock):
sys.exit(0)
- self.open = db_path
+ return db_path
else:
- print _('Input family tree "%s" does not exist.') % value
- print _("If gedcom, gramps-xml or grdb, use the -i option to "
- "import into a family tree instead")
+ self.error( _('Error: Input family tree "%s" does not exist.\n'
+ "If gedcom, gramps-xml or grdb, use the -i option to "
+ "import into a family tree instead.") % value)
sys.exit(0)
def __handle_import_option(self, value, format):
@@ -238,9 +135,8 @@ class ArgHandler(object):
fname = value
fullpath = os.path.abspath(os.path.expanduser(fname))
if not os.path.exists(fullpath):
- print 'Import file not found.'
- print "Ignoring import file: %s" % fname
- return
+ self.error(_('Error: Import file %s not found.') % fname)
+ sys.exit(0)
if format is None:
# Guess the file format based on the file extension.
@@ -257,27 +153,33 @@ class ArgHandler(object):
if plugin_found:
self.imports.append((fname, format))
else:
- print 'Unrecognized type: "%s" for import file: %s' \
- % (format, fname)
- print "Ignoring import file: %s" % fname
+ self.error(_('Error: Unrecognized type: "%(format)s" for '
+ 'import file: %(filename)s') \
+ % {'format' : format,
+ 'filename' : fname})
+ sys.exit(0)
def __handle_export_option(self, value, format):
"""
- Handle the "-e" or "--export" option.
+ Handle the "-e" or "--export" option.
+ Note: only the CLI version has export
"""
+ if self.gui:
+ return
fname = value
fullpath = os.path.abspath(os.path.expanduser(fname))
if os.path.exists(fullpath):
- print "WARNING: Output file already exist!"
- print "WARNING: It will be overwritten:\n %s" % fullpath
+ self.error(_("WARNING: Output file already exist!\n"
+ "WARNING: It will be overwritten:\n %(name)s") % \
+ {'name' : fullpath})
answer = None
while not answer:
- answer = raw_input('OK to overwrite? (yes/no) ')
- if answer.upper() in ('Y','YES'):
- print "Will overwrite the existing file: %s" % fullpath
+ answer = raw_input(_('OK to overwrite? (yes/no) '))
+ if answer.upper() in ('Y','YES', _('YES')):
+ self.error( _("Will overwrite the existing file: %s")
+ % fullpath)
else:
- print "Will skip the output file: %s" % fullpath
- return
+ sys.exit(0)
if format is None:
# Guess the file format based on the file extension.
@@ -294,8 +196,9 @@ class ArgHandler(object):
if plugin_found:
self.exports.append((fullpath, format))
else:
- print "Unrecognized format for export file %s" % fname
- print "Ignoring export file: %s" % fname
+ self.error(_("ERROR: Unrecognized format for export file %s")
+ % fname)
+ sys.exit(0)
def __deduce_db_path(self, db_name_or_path):
"""
@@ -318,38 +221,60 @@ class ArgHandler(object):
db_path = fullpath
return db_path
-
- #-------------------------------------------------------------------------
- # Determine the need for GUI
- #-------------------------------------------------------------------------
- def need_gui(self):
- """
- Determine whether we need a GUI session for the given tasks.
- """
- if self.open_gui:
- # No-option argument, definitely GUI
- return True
- # If we have data to work with:
- if (self.open or self.imports):
- if (self.exports or self.actions):
- # have both data and what to do with it => no GUI
- return False
- else:
- # data given, but no action/export => GUI
- return True
-
- # No data, can only do GUI here
- return True
-
#-------------------------------------------------------------------------
# Overall argument handler:
# sorts out the sequence and details of operations
#-------------------------------------------------------------------------
- def handle_args(self):
+ def handle_args_gui(self, dbman):
+ """
+ method to handle the arguments that can be given for a GUI session.
+ Returns the filename of the family tree that should be openend
+ 1/no options: a family tree can be given, if so, this name is tested
+ and returned. If a filename, it is imported in a new db
+ and name of new db returned
+ 2/an open option can have been given
+
+ """
+ if self.open_gui:
+ # First check if a Gramps database was provided
+ # (either a database path or a database name)
+ db_path = self.__deduce_db_path(self.open_gui)
+
+ if not db_path:
+ # Apparently it is not a database. See if it is a file that
+ # can be imported.
+ db_path, title = self.dbman.import_new_db(self.open_gui, None)
+
+ if db_path:
+ # Test if not locked or problematic
+ if not self.check_db(db_path, self.force_unlock):
+ sys.exit(0)
+ # Add the file to the recent items
+ path = os.path.join(db_path, "name.txt")
+ try:
+ ifile = open(path)
+ title = ifile.readline().strip()
+ ifile.close()
+ except:
+ title = db_path
+ RecentFiles.recent_files(db_path, title)
+ else:
+ sys.exit(0)
+ return db_path
+
+ # if not open_gui, parse any command line args. We can only have one
+ # open argument, and perhaps some import arguments
+ self.__open_action()
+ self.__import_action()
+
+ def handle_args_cli(self, climan):
"""
Depending on the given arguments, import or open data, launch
session, write files, and/or perform actions.
+
+ @param: climan: the manager of a CLI session
+ @type: CLIManager object
"""
if self.list:
@@ -367,104 +292,70 @@ class ArgHandler(object):
if item != "Family tree":
print " %s: %s" % (item, summary[item])
sys.exit(0)
-
- if self.help:
- print _help
- sys.exit(0)
-
- if self.open_gui:
- # First check if a Gramps database was provided
- # (either a database path or a database name)
- db_path = self.__deduce_db_path(self.open_gui)
-
- if not db_path:
- # Apparently it is not a database. See if it is a file that
- # can be imported.
- db_path, title = self.dbman.import_new_db(self.open_gui, None)
-
- if db_path:
- # Test if not locked or problematic
- if not self.__check_db(db_path, self.force_unlock):
- sys.exit(0)
- # Add the file to the recent items
- path = os.path.join(db_path, "name.txt")
- try:
- ifile = open(path)
- title = ifile.readline().strip()
- ifile.close()
- except:
- title = db_path
- RecentFiles.recent_files(db_path, title)
- else:
- sys.exit(1)
- return db_path
- if self.open:
- # Family Tree to open was given. Open it
- # Then go on and process the rest of the command line arguments.
- self.cl = bool(self.exports or self.actions)
+ self.__open_action()
+ self.__import_action()
+
+ for (action, options_str) in self.actions:
+ print "Performing action: %s." % action
+ if options_str:
+ print "Using options string: %s" % options_str
+ self.cl_action(action, options_str)
- filename = self.open
+ for expt in self.exports:
+ print "Exporting: file %s, format %s." % expt
+ self.cl_export(expt[0], expt[1])
- try:
- self.vm.open_activate(filename)
- print "Opened successfully!"
- except:
- print "Error opening the file."
- print "Exiting..."
- sys.exit(1)
+ print "Cleaning up."
+ # remove files in import db subdir after use
+ self.dbstate.db.close()
+ if self.imp_db_path:
+ Utils.rm_tempdir(self.imp_db_path)
+ print "Exiting."
+ sys.exit(0)
+ def __import_action(self):
if self.imports:
self.cl = bool(self.exports or self.actions or self.cl)
if not self.open:
# Create empty dir for imported database(s)
- self.imp_db_path = Utils.get_empty_tempdir("import_dbdir")
+ if self.gui:
+ self.imp_db_path, title = self.dbman._create_new_db_cli()
+ else:
+ self.imp_db_path = Utils.get_empty_tempdir("import_dbdir")
- newdb = gen.db.GrampsDBDir()
- newdb.write_version(self.imp_db_path)
+ newdb = gen.db.GrampsDBDir()
+ newdb.write_version(self.imp_db_path)
- if not self.vm.db_loader.read_file(self.imp_db_path):
+ try:
+ self.sm.open_activate(self.imp_db_path)
+ print "Created empty fam tree successfully"
+ except:
+ print "Error opening the file."
+ print "Exiting..."
sys.exit(0)
for imp in self.imports:
print "Importing: file %s, format %s." % imp
self.cl_import(imp[0], imp[1])
- elif len(self.args) > 1 and not self.open:
- print "No data was given -- will launch interactive session."
- print "To use in the command-line mode,", \
- "supply at least one input file to process."
- print "Launching interactive session..."
+ def __open_action(self):
+ if self.open:
+ # Family Tree to open was given. Open it
+ # Then go on and process the rest of the command line arguments.
+ self.cl = bool(self.exports or self.actions)
- if self.cl:
- for (action, options_str) in self.actions:
- print "Performing action: %s." % action
- if options_str:
- print "Using options string: %s" % options_str
- self.cl_action(action, options_str)
+ # we load this file for use
+ try:
+ self.sm.open_activate(self.open)
+ print "Opened successfully!"
+ except:
+ print "Error opening the file."
+ print "Exiting..."
+ sys.exit(0)
- for expt in self.exports:
- print "Exporting: file %s, format %s." % expt
- self.cl_export(expt[0], expt[1])
-
- print "Cleaning up."
- # remove files in import db subdir after use
- self.state.db.close()
- if self.imp_db_path:
- Utils.rm_tempdir(self.imp_db_path)
- print "Exiting."
- sys.exit(0)
-
- elif Config.get(Config.RECENT_FILE) and Config.get(Config.AUTOLOAD):
- filename = Config.get(Config.RECENT_FILE)
- if os.path.isdir(filename) and \
- os.path.isfile(os.path.join(filename, "name.txt")) and \
- self.__check_db(filename):
- self.vm.db_loader.read_file(filename)
- return filename
-
- def __check_db(self, dbpath, force_unlock = False):
+ def check_db(self, dbpath, force_unlock = False):
# Test if not locked or problematic
if force_unlock:
self.dbman.break_lock(dbpath)
@@ -485,19 +376,18 @@ class ArgHandler(object):
def cl_import(self, filename, format):
"""
Command-line import routine. Try to import filename using the format.
- Any errors will cause the sys.exit(1) call.
"""
pmgr = PluginManager.get_instance()
for plugin in pmgr.get_import_plugins():
if format == plugin.get_extension():
import_function = plugin.get_import_function()
- import_function(self.state.db, filename, None)
+ import_function(self.dbstate.db, filename, None)
if not self.cl:
if self.imp_db_path:
- return self.vm.open_activate(self.imp_db_path)
+ return self.sm.open_activate(self.imp_db_path)
else:
- return self.vm.open_activate(self.open)
+ return self.sm.open_activate(self.open)
#-------------------------------------------------------------------------
#
@@ -508,13 +398,12 @@ class ArgHandler(object):
"""
Command-line export routine.
Try to write into filename using the format.
- Any errors will cause the sys.exit(1) call.
"""
pmgr = PluginManager.get_instance()
for plugin in pmgr.get_export_plugins():
if format == plugin.get_extension():
export_function = plugin.get_export_function()
- export_function(self.state.db, filename)
+ export_function(self.dbstate.db, filename)
#-------------------------------------------------------------------------
#
@@ -528,7 +417,7 @@ class ArgHandler(object):
pmgr = PluginManager.get_instance()
if action == 'check':
import Check
- checker = Check.CheckIntegrity(self.state.db, None, None)
+ checker = Check.CheckIntegrity(self.dbstate.db, None, None)
checker.check_for_broken_family_links()
checker.cleanup_missing_photos(1)
checker.check_parent_relationships()
@@ -538,7 +427,7 @@ class ArgHandler(object):
checker.report(1)
elif action == 'summary':
import Summary
- text = Summary.build_report(self.state.db, None)
+ text = Summary.build_report(self.dbstate.db, None)
print text
elif action == "report":
try:
@@ -557,10 +446,10 @@ class ArgHandler(object):
report_class = item[2]
options_class = item[3]
if category in (CATEGORY_BOOK, CATEGORY_CODE):
- options_class(self.state.db, name, category,
+ options_class(self.dbstate.db, name, category,
options_str_dict)
else:
- cl_report(self.state.db, name, category, report_class,
+ cl_report(self.dbstate.db, name, category, report_class,
options_class, options_str_dict)
return
# name exists, but is not in the list of valid report names
@@ -593,7 +482,7 @@ class ArgHandler(object):
category = item[1]
tool_class = item[2]
options_class = item[3]
- Tool.cli_tool(self.state, name, category, tool_class,
+ Tool.cli_tool(self.dbstate, name, category, tool_class,
options_class, options_str_dict)
return
msg = "Unknown tool name."
@@ -605,5 +494,4 @@ class ArgHandler(object):
print " %s" % item[0]
else:
print "Unknown action: %s." % action
- sys.exit(1)
-
+ sys.exit(0)
diff --git a/src/cli/argparser.py b/src/cli/argparser.py
new file mode 100644
index 000000000..a7c5fd5f8
--- /dev/null
+++ b/src/cli/argparser.py
@@ -0,0 +1,256 @@
+#
+# Gramps - a GTK+/GNOME based genealogy program
+#
+# Copyright (C) 2000-2006 Donald N. Allingham, A. Roitman
+# Copyright (C) 2007-2009 B. Malengier
+# Copyright (C) 2008 Lukasz Rymarczyk
+# Copyright (C) 2008 Raphael Ackermann
+# Copyright (C) 2008 Brian G. Matherly
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# 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,
+# 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.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+# $Id: ArgHandler.py 12559 2009-05-21 17:19:50Z gbritton $
+
+"""
+Module responsible for handling the command line arguments for GRAMPS.
+"""
+
+#-------------------------------------------------------------------------
+#
+# Standard python modules
+#
+#-------------------------------------------------------------------------
+import os
+import sys
+import getopt
+from gettext import gettext as _
+import logging
+
+#-------------------------------------------------------------------------
+#
+# gramps modules
+#
+#-------------------------------------------------------------------------
+import const
+
+
+# Note: Make sure to edit const.py POPT_TABLE too!
+_HELP = _("""
+Usage: gramps.py [OPTION...]
+ --load-modules=MODULE1,MODULE2,... Dynamic modules to load
+
+Help options
+ -?, --help Show this help message
+ --usage Display brief usage message
+
+Application options
+ -O, --open=FAMILY_TREE Open family tree
+ -i, --import=FILENAME Import file
+ -e, --export=FILENAME Export file
+ -f, --format=FORMAT Specify format
+ -a, --action=ACTION Specify action
+ -p, --options=OPTIONS_STRING Specify options
+ -d, --debug=LOGGER_NAME Enable debug logs
+ -l List Family Trees
+ -L List Family Trees in Detail
+ -u, --force-unlock Force unlock of family tree
+""")
+
+#-------------------------------------------------------------------------
+# ArgParser
+#-------------------------------------------------------------------------
+class ArgParser(object):
+ """
+ This class is responsible for parsing the command line arguments (if any)
+ given to gramps, and determining if a GUI or a CLI session must be started.
+ The valid arguments are:
+
+ Possible:
+ 1/ FAMTREE : Just the family tree (name or database dir)
+ 2/ -O, --open=FAMTREE, Open of a family tree
+ 3/ -i, --import=FILE, Import of any format understood by an importer, optionally
+ provide- f to indicate format
+ 4/ -e, --export=FILE, export a family tree in required format, optionally provide
+ -f to indicate format
+ 5/ -f, --format=FORMAT : format after a -i or -e option
+ 5/ -a, --action: An action (possible: 'check', 'summary', 'report',
+ 'tool')
+ 6/ -u, --force-unlock: A locked database can be unlocked by giving this
+ argument when opening it
+
+ If the filename (no flags) is specified, the interactive session is
+ launched using data from filename.
+ In this mode (filename, no flags), the rest of the arguments is ignored.
+ This is a mode suitable by default for GUI launchers, mime type handlers,
+ and the like
+
+ If no filename or -i option is given, a new interactive session (empty
+ database) is launched, since no data is given anyway.
+
+ If -O or -i option is given, but no -e or -a options are given, an
+ interactive session is launched with the FILE (specified with -i).
+
+ If both input (-O or -i) and processing (-e or -a) options are given,
+ interactive session will not be launched.
+ """
+
+ def __init__(self, args):
+ """
+ pass the command line arguments on creation
+ """
+ self.args = args
+
+ self.open_gui = None
+ self.open = None
+ self.exports = []
+ self.actions = []
+ self.imports = []
+ self.imp_db_path = None
+ self.list = False
+ self.list_more = False
+ self.help = False
+ self.force_unlock = False
+
+ self.errors = []
+ self.parse_args()
+
+ #-------------------------------------------------------------------------
+ # Argument parser: sorts out given arguments
+ #-------------------------------------------------------------------------
+ def parse_args(self):
+ """
+ Fill in lists with open, exports, imports, and actions options.
+
+ Any errors are added to self.errors
+
+ Possible:
+ 1/ Just the family tree (name or database dir)
+ 2/ -O, Open of a family tree
+ 3/ -i, Import of any format understood by an importer, optionally
+ provide-f to indicate format
+ 4/ -e, export a family tree in required format, optionally provide
+ -f to indicate format
+ 5/ -a, --action: An action (possible: 'check', 'summary', 'report',
+ 'tool')
+ 6/ -u, --force-unlock: A locked database can be unlocked by giving this
+ argument when opening it
+
+ """
+ try:
+ options, leftargs = getopt.getopt(self.args[1:],
+ const.SHORTOPTS, const.LONGOPTS)
+ except getopt.GetoptError, msg:
+ self.errors += [(_('Error parsing the arguments'),
+ str(msg) + '\n' +
+ _("Error parsing the arguments: %s \n"
+ "Type gramps --help for an overview of commands, or "
+ "read the manual pages.") % self.args[1:])]
+ return
+
+ if leftargs:
+ # if there were an argument without option,
+ # use it as a file to open and return
+ self.open_gui = leftargs[0]
+ print "Trying to open: %s ..." % leftargs[0]
+ #see if force open is on
+ for opt_ix in range(len(options)):
+ option, value = options[opt_ix]
+ if option in ('-u', '--force-unlock'):
+ self.force_unlock = True
+ break
+ return
+
+ # Go over all given option and place them into appropriate lists
+ for opt_ix in range(len(options)):
+ option, value = options[opt_ix]
+ if option in ( '-O', '--open'):
+ self.open = value
+ elif option in ( '-i', '--import'):
+ format = None
+ if opt_ix < len(options) - 1 \
+ and options[opt_ix + 1][0] in ( '-f', '--format'):
+ format = options[opt_ix + 1][1]
+ self.imports.append((value, format))
+ elif option in ( '-e', '--export' ):
+ format = None
+ if opt_ix < len(options) - 1 \
+ and options[opt_ix + 1][0] in ( '-f', '--format'):
+ format = options[opt_ix + 1][1]
+ self.exports.append((value, format))
+ elif option in ( '-a', '--action' ):
+ action = value
+ if action not in ( 'check', 'summary', 'report', 'tool' ):
+ print "Unknown action: %s. Ignoring." % action
+ continue
+ options_str = ""
+ if opt_ix < len(options)-1 \
+ and options[opt_ix+1][0] in ( '-p', '--options' ):
+ options_str = options[opt_ix+1][1]
+ self.actions.append((action, options_str))
+ elif option in ('-d', '--debug'):
+ logger = logging.getLogger(value)
+ logger.setLevel(logging.DEBUG)
+ elif option in ('-l',):
+ self.list = True
+ elif option in ('-L',):
+ self.list_more = True
+ elif option in ('-h', '-?', '--help'):
+ self.help = True
+ elif option in ('-u', '--force-unlock'):
+ self.force_unlock = True
+
+ if len(options) > 0 and self.open is None and self.imports == [] \
+ and not (self.list or self.list_more or self.help):
+ self.errors += [(_('Error parsing the arguments'),
+ _("Error parsing the arguments: %s \n"
+ "To use in the command-line mode," \
+ "supply at least one input file to process.") % self.args[1:])]
+
+ #-------------------------------------------------------------------------
+ # Determine the need for GUI
+ #-------------------------------------------------------------------------
+ def need_gui(self):
+ """
+ Determine whether we need a GUI session for the given tasks.
+ """
+ if self.errors:
+ #errors in argument parsing ==> give cli error, no gui needed
+ return False
+
+ if self.list or self.list_more or self.help:
+ return False
+
+ if self.open_gui:
+ # No-option argument, definitely GUI
+ return True
+
+ # If we have data to work with:
+ if (self.open or self.imports):
+ if (self.exports or self.actions):
+ # have both data and what to do with it => no GUI
+ return False
+ else:
+ # data given, but no action/export => GUI
+ return True
+
+ # No data, can only do GUI here
+ return True
+
+ def print_help(self):
+ if self.help:
+ print _HELP
+ sys.exit(0)
+
diff --git a/src/cli/clidbman.py b/src/cli/clidbman.py
new file mode 100644
index 000000000..42102a96d
--- /dev/null
+++ b/src/cli/clidbman.py
@@ -0,0 +1,380 @@
+#
+# Gramps - a GTK+/GNOME based genealogy program
+#
+# Copyright (C) 2000-2007 Donald N. Allingham
+# Copyright (C) 2009 Brian G. Matherly
+# Copyright (C) 2009 Gary Burton
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# 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,
+# 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.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+# $Id: DbManager.py 12621 2009-06-03 18:39:24Z ldnp $
+
+"""
+Provide the management of databases from CLI. This includes opening, renaming,
+creating, and deleting of databases.
+"""
+
+#-------------------------------------------------------------------------
+#
+# Standard python modules
+#
+#-------------------------------------------------------------------------
+import os
+import time
+from gettext import gettext as _
+#-------------------------------------------------------------------------
+#
+# set up logging
+#
+#-------------------------------------------------------------------------
+import logging
+LOG = logging.getLogger(".clidbman")
+
+from gtk import STOCK_DIALOG_ERROR, STOCK_OPEN
+
+#-------------------------------------------------------------------------
+#
+# gramps modules
+#
+#-------------------------------------------------------------------------
+import gen.db
+from gen.plug import PluginManager
+import Config
+
+#-------------------------------------------------------------------------
+#
+# constants
+#
+#-------------------------------------------------------------------------
+
+DEFAULT_TITLE = _("Family Tree")
+NAME_FILE = "name.txt"
+META_NAME = "meta_data.db"
+
+#-------------------------------------------------------------------------
+#
+# CLIDbManager
+#
+#-------------------------------------------------------------------------
+class CLIDbManager(object):
+ """
+ Database manager without GTK functionality, allows users to create and
+ open databases
+ """
+ def __init__(self, dbstate):
+ self.dbstate = dbstate
+ self.msg = None
+
+ if dbstate:
+ self.active = dbstate.db.get_save_path()
+ else:
+ self.active = None
+
+ self.current_names = []
+ self._populate_cli()
+
+ def empty(self, val):
+ """Callback that does nothing
+ """
+ pass
+
+ def get_dbdir_summary(self, file_name):
+ """
+ Returns (people_count, version_number) of current DB.
+ Returns ("Unknown", "Unknown") if invalid DB or other error.
+ """
+ from bsddb import dbshelve, db
+ from gen.db import META, PERSON_TBL
+ env = db.DBEnv()
+ flags = db.DB_CREATE | db.DB_PRIVATE |\
+ db.DB_INIT_MPOOL | db.DB_INIT_LOCK |\
+ db.DB_INIT_LOG | db.DB_INIT_TXN | db.DB_THREAD
+ try:
+ env.open(file_name, flags)
+ except:
+ return "Unknown", "Unknown"
+ dbmap1 = dbshelve.DBShelf(env)
+ fname = os.path.join(file_name, META + ".db")
+ try:
+ dbmap1.open(fname, META, db.DB_HASH, db.DB_RDONLY)
+ except:
+ return "Unknown", "Unknown"
+ version = dbmap1.get('version', default=None)
+ dbmap1.close()
+ dbmap2 = dbshelve.DBShelf(env)
+ fname = os.path.join(file_name, PERSON_TBL + ".db")
+ try:
+ dbmap2.open(fname, PERSON_TBL, db.DB_HASH, db.DB_RDONLY)
+ except:
+ env.close()
+ return "Unknown", "Unknown"
+ count = len(dbmap2)
+ dbmap2.close()
+ env.close()
+ return (count, version)
+
+ def family_tree_summary(self):
+ """
+ Return a list of dictionaries of the known family trees.
+ """
+ # make the default directory if it does not exist
+ list = []
+ for item in self.current_names:
+ (name, dirpath, path_name, last,
+ tval, enable, stock_id) = item
+ count, version = self.get_dbdir_summary(dirpath)
+ retval = {}
+ retval["Number of people"] = count
+ if enable:
+ retval["Locked?"] = "yes"
+ else:
+ retval["Locked?"] = "no"
+ retval["DB version"] = version
+ retval["Family tree"] = name
+ retval["Path"] = dirpath
+ retval["Last accessed"] = time.strftime('%x %X',
+ time.localtime(tval))
+ list.append( retval )
+ return list
+
+ def _populate_cli(self):
+ """ Get the list of current names in the database dir
+ """
+ # make the default directory if it does not exist
+ dbdir = os.path.expanduser(Config.get(Config.DATABASE_PATH))
+ make_dbdir(dbdir)
+
+ self.current_names = []
+
+ for dpath in os.listdir(dbdir):
+ dirpath = os.path.join(dbdir, dpath)
+ path_name = os.path.join(dirpath, NAME_FILE)
+ 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.dbstate.db.is_open())
+
+ if (stock_id == 'gramps-lock'):
+ last = find_locker_name(dirpath)
+
+ self.current_names.append(
+ (name, os.path.join(dbdir, dpath), path_name,
+ last, tval, enable, stock_id))
+
+ self.current_names.sort()
+
+ def get_family_tree_path(self, name):
+ """
+ Given a name, return None if name not existing or the path to the
+ database if it is a known database name.
+ """
+ for data in self.current_names:
+ if data[0] == name:
+ return data[1]
+ return None
+
+ def family_tree_list(self):
+ """Return a list of name, dirname of the known family trees
+ """
+ lst = [(x[0], x[1]) for x in self.current_names]
+ return lst
+
+ def __start_cursor(self, msg):
+ """
+ Do needed things to start import visually, eg busy cursor
+ """
+ print _('Starting Import, %s') % msg
+
+ def __end_cursor(self):
+ """
+ Set end of a busy cursor
+ """
+ print _('Import finished...')
+
+ def _create_new_db_cli(self, title=None):
+ """
+ Create a new database.
+ """
+ new_path = find_next_db_dir()
+
+ os.mkdir(new_path)
+ path_name = os.path.join(new_path, NAME_FILE)
+
+ if title is None:
+ 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()
+
+ # write the version number into metadata
+ newdb = gen.db.GrampsDBDir()
+ newdb.write_version(new_path)
+
+ (tval, last) = time_val(new_path)
+
+ self.current_names.append((title, new_path, path_name,
+ last, tval, False, ""))
+ return new_path, title
+
+ def _create_new_db(self, title=None):
+ """
+ Create a new database, do extra stuff needed
+ """
+ return self._create_new_db_cli(title)
+
+ def import_new_db(self, filename, callback):
+ """
+ Attempt to import the provided file into a new database.
+ A new database will only be created if an appropriate importer was
+ found.
+
+ @return: A tuple of (new_path, name) for the new database
+ or (None, None) if no import was performed.
+ """
+ pmgr = PluginManager.get_instance()
+ (name, ext) = os.path.splitext(os.path.basename(filename))
+ format = ext[1:].lower()
+
+ for plugin in pmgr.get_import_plugins():
+ if format == plugin.get_extension():
+
+ new_path, name = self._create_new_db(name)
+
+ # Create a new database
+ self.__start_cursor(_("Importing data..."))
+ dbclass = gen.db.GrampsDBDir
+ dbase = dbclass()
+ dbase.load(new_path, callback)
+
+ import_function = plugin.get_import_function()
+ import_function(dbase, filename, callback)
+
+ # finish up
+ self.__end_cursor()
+ dbase.close()
+
+ return new_path, name
+ return None, None
+
+ def is_locked(self, dbpath):
+ """
+ returns True if there is a lock file in the dirpath
+ """
+ if os.path.isfile(os.path.join(dbpath,"lock")):
+ return True
+ return False
+
+ def needs_recovery(self, dbpath):
+ """
+ returns True if the database in dirpath needs recovery
+ """
+ if os.path.isfile(os.path.join(dbpath,"need_recover")):
+ return True
+ return False
+
+ def break_lock(self, dbpath):
+ """
+ Breaks the lock on a database
+ """
+ if os.path.exists(os.path.join(dbpath, "lock")):
+ os.unlink(os.path.join(dbpath, "lock"))
+
+def make_dbdir(dbdir):
+ """
+ Create the default database directory, as defined by dbdir
+ """
+ try:
+ if not os.path.isdir(dbdir):
+ os.makedirs(dbdir)
+ except (IOError, OSError), msg:
+ LOG.error(_("Could not make database directory: ") + str(msg))
+
+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())
+ dbdir = os.path.expanduser(Config.get(Config.DATABASE_PATH))
+ new_path = os.path.join(dbdir, 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.strftime('%x %X', time.localtime(tval))
+ else:
+ tval = 0
+ last = _("Never")
+ return (tval, last)
+
+def icon_values(dirpath, active, is_open):
+ """
+ If the directory path is the active path, then return values
+ that indicate to use the icon, and which icon to use.
+ """
+ if os.path.isfile(os.path.join(dirpath,"need_recover")):
+ return (True, STOCK_DIALOG_ERROR)
+ elif dirpath == active and is_open:
+ return (True, STOCK_OPEN)
+ elif os.path.isfile(os.path.join(dirpath,"lock")):
+ return (True, 'gramps-lock')
+ else:
+ return (False, "")
+
+def find_locker_name(dirpath):
+ """
+ Opens the lock file if it exists, reads the contexts which is "USERNAME"
+ and returns the contents, with correct string before "USERNAME",
+ so the message can be printed with correct locale.
+ If a file is encountered with errors, we return 'Unknown'
+ This data can eg be displayed in the time column of the manager
+ """
+ try:
+ fname = os.path.join(dirpath, "lock")
+ ifile = open(fname)
+ username = ifile.read().strip()
+ last = _("Locked by %s") % username
+ ifile.close()
+ except (OSError, IOError):
+ last = _("Unknown")
+ return last
diff --git a/src/cli/grampscli.py b/src/cli/grampscli.py
new file mode 100644
index 000000000..75b3a1c5e
--- /dev/null
+++ b/src/cli/grampscli.py
@@ -0,0 +1,284 @@
+#
+# Gramps - a GTK+/GNOME based genealogy program
+#
+# Copyright (C) 2001-2006 Donald N. Allingham
+# Copyright (C) 2009 Benny Malengier
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# 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,
+# 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.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+# $Id:gramps_main.py 9912 2008-01-22 09:17:46Z acraphae $
+
+#-------------------------------------------------------------------------
+#
+# Python modules
+#
+#-------------------------------------------------------------------------
+from gettext import gettext as _
+import os
+import sys
+
+#-------------------------------------------------------------------------
+#
+# GRAMPS modules
+#
+#-------------------------------------------------------------------------
+from BasicUtils import name_displayer
+import Config
+import const
+import Errors
+import DbState
+from gen.db import GrampsDBDir
+from gen.plug import PluginManager
+import GrampsCfg
+import RecentFiles
+
+#-------------------------------------------------------------------------
+#
+# CLI DbLoader class
+#
+#-------------------------------------------------------------------------
+class CLIDbLoader(object):
+ def __init__(self, dbstate):
+ self.dbstate = dbstate
+
+ def _warn(title, warnmessage):
+ print _('WARNING: %s') %warnmessage
+
+ def _errordialog(title, errormessage):
+ """
+ Show the error. A title for the error and an errormessage
+ """
+ print _('ERROR: %s') % errormessage
+ sys.exit(1)
+
+ def _dberrordialog(self, msg):
+ self._errordialog( '', _("Low level database corruption detected")
+ + '\n' +
+ _("GRAMPS has detected a problem in the underlying "
+ "Berkeley database. This can be repaired by from "
+ "the Family Tree Manager. Select the database and "
+ 'click on the Repair button') + '\n\n' + str(msg))
+
+ def _begin_progress(self):
+ pass
+
+ def _pulse_progress(self, value):
+ pass
+
+ def read_file(self, filename):
+ """
+ This method takes care of changing database, and loading the data.
+ In 3.0 we only allow reading of real databases of filetype
+ 'x-directory/normal'
+
+ This method should only return on success.
+ Returning on failure makes no sense, because we cannot recover,
+ since database has already beeen changed.
+ Therefore, any errors should raise exceptions.
+
+ On success, return with the disabled signals. The post-load routine
+ should enable signals, as well as finish up with other UI goodies.
+ """
+
+ if os.path.exists(filename):
+ if not os.access(filename, os.W_OK):
+ mode = "r"
+ self._warn(_('Read only database'),
+ _('You do not have write access '
+ 'to the selected file.'))
+ else:
+ mode = "w"
+ else:
+ mode = 'w'
+
+ dbclass = GrampsDBDir
+
+ self.dbstate.change_database(dbclass())
+ self.dbstate.db.disable_signals()
+
+ self._begin_progress()
+
+ try:
+ self.dbstate.db.load(filename, self._pulse_progress, mode)
+ self.dbstate.db.set_save_path(filename)
+ except gen.db.FileVersionDeclineToUpgrade:
+ self.dbstate.no_database()
+ except gen.db.exceptions.FileVersionError, msg:
+ self.dbstate.no_database()
+ self._errordialog( _("Cannot open database"), str(msg))
+ except OSError, msg:
+ self.dbstate.no_database()
+ self._errordialog(
+ _("Could not open file: %s") % filename, str(msg))
+ except Errors.DbError, msg:
+ self.dbstate.no_database()
+ self._dberrordialog(msg)
+ except Exception:
+ self.dbstate.no_database()
+ _LOG.error("Failed to open database.", exc_info=True)
+ return True
+
+#-------------------------------------------------------------------------
+#
+# CLIManager class
+#
+#-------------------------------------------------------------------------
+
+class CLIManager(object):
+ """
+ A reduced viewmanager suitable for cli actions.
+ Aim is to manage a database on which to work
+ """
+ def __init__(self, dbstate, setloader):
+ self.dbstate = dbstate
+ if setloader:
+ self.db_loader = CLIDbLoader(self.dbstate)
+ else:
+ self.db_loader = None
+ self.file_loaded = False
+ self._pmgr = PluginManager.get_instance()
+
+ def open_activate(self, path):
+ """
+ Open and make a family tree active
+ """
+ self._read_recent_file(path)
+
+ def _errordialog(title, errormessage):
+ """
+ Show the error. A title for the error and an errormessage
+ """
+ print _('ERROR: %s') % errormessage
+ sys.exit(1)
+
+ def _read_recent_file(self, filename):
+ """
+ Called when a file needs to be loaded
+ """
+ # A recent database should already have a directory
+ # If not, do nothing, just return. This can be handled better if family tree
+ # delete/rename also updated the recent file menu info in DisplayState.py
+ if not os.path.isdir(filename):
+ self.errordialog(
+ _("Could not load a recent Family Tree."),
+ _("Family Tree does not exist, as it has been deleted."))
+ return
+
+ if self.db_loader.read_file(filename):
+ # Attempt to figure out the database title
+ path = os.path.join(filename, "name.txt")
+ try:
+ ifile = open(path)
+ title = ifile.readline().strip()
+ ifile.close()
+ except:
+ title = filename
+
+ self._post_load_newdb(filename, 'x-directory/normal', title)
+
+ def _post_load_newdb(self, filename, filetype, title=None):
+ """
+ The method called after load of a new database.
+ Here only CLI stuff is done, inherit this method to add extra stuff
+ """
+ self._post_load_newdb_nongui(filename, filetype, title)
+
+ def _post_load_newdb_nongui(self, filename, filetype, title=None):
+ """
+ Called after a new database is loaded.
+ """
+ if not filename:
+ return
+
+ if filename[-1] == os.path.sep:
+ filename = filename[:-1]
+ name = os.path.basename(filename)
+ if title:
+ name = title
+
+ # This method is for UI stuff when the database has changed.
+ # Window title, recent files, etc related to new file.
+
+ self.dbstate.db.set_save_path(filename)
+
+ # apply preferred researcher if loaded file has none
+ res = self.dbstate.db.get_researcher()
+ owner = GrampsCfg.get_researcher()
+ if res.get_name() == "" and owner.get_name() != "":
+ self.dbstate.db.set_researcher(owner)
+
+ name_displayer.set_name_format(self.dbstate.db.name_formats)
+ fmt_default = Config.get(Config.NAME_FORMAT)
+ if fmt_default < 0:
+ name_displayer.set_default_format(fmt_default)
+
+ self.dbstate.db.enable_signals()
+ self.dbstate.signal_change()
+
+ Config.set(Config.RECENT_FILE, filename)
+
+ try:
+ self.dbstate.change_active_person(self.dbstate.db.find_initial_person())
+ except:
+ pass
+
+ RecentFiles.recent_files(filename, name)
+ self.file_loaded = True
+
+ def do_load_plugins(self):
+ """
+ Loads the plugins at initialization time. The plugin status window is
+ opened on an error if the user has requested.
+ """
+ # load plugins
+
+ error = self._pmgr.load_plugins(const.PLUGINS_DIR)
+ error |= self._pmgr.load_plugins(const.USER_PLUGINS)
+
+ return error
+
+def startcli(errors, argparser):
+ """
+ Starts a cli session of GRAMPS.
+ errors : errors already encountered
+ argparser : ArgParser instance
+ """
+ if errors:
+ #already errors encountered. Show first one on terminal and exit
+ print _('Error encountered: %s') % errors[0][0]
+ print _(' Details: %s') % errors[0][1]
+ sys.exit(1)
+
+ if argparser.errors:
+ print _('Error encountered in argument parsing: %s') \
+ % argparser.errors[0][0]
+ print _(' Details: %s') % argparser.errors[0][1]
+ sys.exit(1)
+
+ #we need to keep track of the db state
+ dbstate = DbState.DbState()
+ #we need a manager for the CLI session
+ climanager = CLIManager(dbstate, True)
+ #load the plugins
+ climanager.do_load_plugins()
+ # handle the arguments
+ from arghandler import ArgHandler
+ handler = ArgHandler(dbstate, argparser, climanager)
+ # create a manager to manage the database
+
+ handler.handle_args_cli(climanager)
+
+ sys.exit(0)
diff --git a/src/glade/Makefile.am b/src/glade/Makefile.am
index 0e5e125a4..7d44bef31 100644
--- a/src/glade/Makefile.am
+++ b/src/glade/Makefile.am
@@ -23,7 +23,7 @@ dist_pkgdata_DATA = \
dateedit.glade \
editsource.glade \
styleeditor.glade \
- dbmanager.glade \
+ dbman.glade \
editurl.glade \
editrepository.glade \
editreporef.glade \
diff --git a/src/glade/dbmanager.glade b/src/glade/dbman.glade
similarity index 100%
rename from src/glade/dbmanager.glade
rename to src/glade/dbman.glade
diff --git a/src/gramps.py b/src/gramps.py
index 1f060ff52..58c1df5b6 100644
--- a/src/gramps.py
+++ b/src/gramps.py
@@ -2,6 +2,7 @@
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2006 Donald N. Allingham
+# Copyright (C) 2009 Benny Malengier
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -33,29 +34,14 @@ import signal
import gettext
import logging
-log = logging.getLogger(".")
+LOG = logging.getLogger(".")
#-------------------------------------------------------------------------
#
-# pygtk
+# GRAMPS modules
#
#-------------------------------------------------------------------------
-try:
- import pygtk
- pygtk.require('2.0')
-except ImportError:
- pass
-
-#-------------------------------------------------------------------------
-#
-# Miscellaneous initialization
-#
-#-------------------------------------------------------------------------
-import gtk
-from gtk import glade
-import gobject
-
-gobject.threads_init()
+from Mime import mime_type_is_defined
#-------------------------------------------------------------------------
#
@@ -78,13 +64,6 @@ except ValueError:
pass
gettext.bindtextdomain("gramps",loc)
-glade.bindtextdomain("gramps",loc)
-
-try:
- glade.textdomain("gramps")
-except:
- pass
-
gettext.textdomain("gramps")
gettext.install("gramps",loc,unicode=1)
@@ -117,24 +96,15 @@ except:
args = sys.argv
+MIN_PYTHON_VERSION = (2, 5, 0, '', 0)
+
def setup_logging():
"""Setup basic logging support."""
- from GrampsLogger import RotateHandler, GtkHandler
-
# Setup a formatter
form = logging.Formatter(fmt="%(relativeCreated)d: %(levelname)s: %(filename)s: line %(lineno)d: %(message)s")
# Create the log handlers
- rh = RotateHandler(capacity=20)
- rh.setFormatter(form)
-
- # Only error and critical log records should
- # trigger the GUI handler.
- gtkh = GtkHandler(rotate_handler=rh)
- gtkh.setFormatter(form)
- gtkh.setLevel(logging.ERROR)
-
stderrh = logging.StreamHandler(sys.stderr)
stderrh.setFormatter(form)
stderrh.setLevel(logging.DEBUG)
@@ -143,8 +113,6 @@ def setup_logging():
# everything.
l = logging.getLogger()
l.setLevel(logging.WARNING)
- l.addHandler(rh)
- l.addHandler(gtkh)
l.addHandler(stderrh)
# put a hook on to catch any completely unhandled exceptions.
@@ -156,69 +124,53 @@ def setup_logging():
# strange Windows logging error on close
return
import traceback
- log.error("Unhandled exception\n" +
+ LOG.error("Unhandled exception\n" +
"".join(traceback.format_exception(type, value, tb)))
sys.excepthook = exc_hook
+def build_user_paths():
+ """ check/make user-dirs on each Gramps session"""
+ for path in const.USER_DIRLIST:
+ if not os.path.isdir(path):
+ os.mkdir(path)
+
def run():
-
- setup_logging()
-
- try:
- #This is GNOME initialization code that is necessary for use
- # with the other GNOME libraries.
- #It only gets called if the user has gnome installed on his/her system.
- #There is *no* requirement for it.
- #If you don't call this, you are not guaranteed that the other GNOME
- #libraries will function properly. I learned this the hard way.
- import gnome
- program = gnome.program_init('gramps',const.VERSION,
- gnome.libgnome_module_info_get(),
- args, const.POPT_TABLE)
+ error = []
- program.set_property('app-libdir',
- '%s/lib' % const.PREFIXDIR)
- program.set_property('app-datadir',
- '%s/share' % const.PREFIXDIR)
- program.set_property('app-sysconfdir',const.SYSCONFDIR)
- program.set_property('app-prefix', const.PREFIXDIR)
+ setup_logging()
+
+ try:
+ build_user_paths()
+ except OSError, msg:
+ error += [(_("Configuration error"), str(msg))]
+ return False
except:
- pass
-
- try:
- quit_now = False
- exit_code = 0
- import gramps_main
- gramps_main.Gramps(args)
- # TODO: check for returns from gramps_main.Gramps.__init__()
- # that perhaps should have raised an exception to be caught here
-
- except SystemExit, e:
- quit_now = True
- if e.code:
- exit_code = e.code
- log.error("Gramps terminated with exit code: %d." \
- % e.code, exc_info=True)
- except OSError, e:
- quit_now = True
- exit_code = e[0] or 1
- try:
- fn = e.filename
- except AttributeError:
- fn = ""
- log.error("Gramps terminated because of OS Error\n" +
- "Error details: %s %s" % (repr(e), fn), exc_info=True)
- except:
- quit_now = True
- exit_code = 1
- log.error("Gramps failed to start.", exc_info=True)
-
- if quit_now:
- gtk.main_quit()
- sys.exit(exit_code)
+ LOG.error("Error reading configuration.", exc_info=True)
+ return False
- return False
+ if not mime_type_is_defined(const.APP_GRAMPS):
+ error += [(_("Configuration error"),
+ _("A definition for the MIME-type %s could not "
+ "be found \n\n Possibly the installation of GRAMPS "
+ "was incomplete. Make sure the MIME-types "
+ "of GRAMPS are properly installed.")
+ % const.APP_GRAMPS)]
+
+ #we start with parsing the arguments to determine if we have a cli or a
+ # gui session
+ from cli import ArgParser
+ argpars = ArgParser(sys.argv)
+
+ if argpars.need_gui():
+ #A GUI is needed, set it up
+ from gui import startgtkloop
+ startgtkloop(error, argpars)
+ else:
+ #CLI use of GRAMPS
+ argpars.print_help()
-gobject.timeout_add(100, run, priority=100)
-gtk.main()
+ from cli import startcli
+ startcli(error, argpars)
+
+run()
diff --git a/src/gramps_main.py b/src/gramps_main.py
deleted file mode 100644
index fe93218b0..000000000
--- a/src/gramps_main.py
+++ /dev/null
@@ -1,292 +0,0 @@
-#
-# Gramps - a GTK+/GNOME based genealogy program
-#
-# Copyright (C) 2001-2006 Donald N. Allingham
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# 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,
-# 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.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-#
-
-# $Id:gramps_main.py 9912 2008-01-22 09:17:46Z acraphae $
-
-#-------------------------------------------------------------------------
-#
-# Python modules
-#
-#-------------------------------------------------------------------------
-from gettext import gettext as _
-import os
-import platform
-
-import logging
-log = logging.getLogger(".")
-
-#-------------------------------------------------------------------------
-#
-# GTK+/GNOME modules
-#
-#-------------------------------------------------------------------------
-import gtk
-
-#-------------------------------------------------------------------------
-#
-# GRAMPS modules
-#
-#-------------------------------------------------------------------------
-import ViewManager
-import ArgHandler
-import Config
-import const
-import Errors
-import TipOfDay
-import DataViews
-import DbState
-from Mime import mime_type_is_defined
-from QuestionDialog import ErrorDialog
-
-#-------------------------------------------------------------------------
-#
-# helper functions
-#
-#-------------------------------------------------------------------------
-
-
-def register_stock_icons ():
- """
- Add the gramps names for its icons (eg gramps-person) to the GTK icon
- factory. This allows all gramps modules to call up the icons by their name
- """
-
- #iconpath to the base image. The front of the list has highest priority
- if platform.system() == "Windows":
- iconpaths = [
- (os.path.join(const.IMAGE_DIR, '48x48'), '.png'),
- (const.IMAGE_DIR, '.png'),
- ]
- else :
- iconpaths = [
- (os.path.join(const.IMAGE_DIR, 'scalable'), '.svg'),
- (const.IMAGE_DIR, '.svg'), (const.IMAGE_DIR, '.png'),
- ]
-
- #sizes: menu=16, small_toolbar=18, large_toolbar=24,
- # button=20, dnd=32, dialog=48
- #add to the back of this list to overrule images set at beginning of list
- extraiconsize = [
- (os.path.join(const.IMAGE_DIR, '22x22'),
- gtk.ICON_SIZE_LARGE_TOOLBAR),
- (os.path.join(const.IMAGE_DIR, '16x16'),
- gtk.ICON_SIZE_MENU),
- (os.path.join(const.IMAGE_DIR, '22x22'),
- gtk.ICON_SIZE_BUTTON),
- ]
-
- items = [
- ('gramps-db', _('Family Trees'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-address', _('Address'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-attribute', _('Attribute'), gtk.gdk.CONTROL_MASK, 0, ''),
- #('gramps-bookmark', _('Bookmarks'), gtk.gdk.CONTROL_MASK, 0, ''),
- #('gramps-bookmark-delete', _('Delete bookmark'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-bookmark-edit', _('Organize Bookmarks'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-bookmark-new', _('Add Bookmark'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-date', _('Date'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-date-edit', _('Edit Date'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-event', _('Events'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-family', _('Family'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-font', _('Font'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-font-color', _('Font Color'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-font-bgcolor', _('Font Background Color'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-gramplet', _('Gramplets'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-geo', _('GeoView'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-lock', _('Public'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-media', _('Media'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-notes', _('Notes'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-parents', _('Parents'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-parents-add', _('Add Parents'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-parents-open', _('Select Parents'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-pedigree', _('Pedigree'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-person', _('Person'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-place', _('Places'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-relation', _('Relationships'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-reports', _('Reports'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-repository', _('Repositories'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-source', _('Sources'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-spouse', _('Add Spouse'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-tools', _('Tools'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-unlock', _('Private'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-viewmedia', _('View'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-zoom-in', _('Zoom In'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-zoom-out', _('Zoom Out'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-zoom-fit-width', _('Fit Width'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-zoom-best-fit', _('Fit Page'), gtk.gdk.CONTROL_MASK, 0, ''),
- ]
- # the following icons are not yet in new directory structure
- # they should be ported in the near future
- items_legacy = [
- ('gramps-export', _('Export'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-import', _('Import'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-undo-history', _('Undo History'), gtk.gdk.CONTROL_MASK, 0, ''),
- ('gramps-url', _('URL'), gtk.gdk.CONTROL_MASK, 0, ''),
- ]
-
- # Register our stock items
- gtk.stock_add (items+items_legacy)
-
- # Add our custom icon factory to the list of defaults
- factory = gtk.IconFactory ()
- factory.add_default ()
-
- for data in items+items_legacy:
- pixbuf = 0
- for (dirname, ext) in iconpaths:
- icon_file = os.path.expanduser(os.path.join(dirname, data[0]+ext))
- if os.path.isfile(icon_file):
- try:
- pixbuf = gtk.gdk.pixbuf_new_from_file (icon_file)
- break
- except:
- pass
-
- if not pixbuf :
- icon_file = os.path.join(const.IMAGE_DIR, 'gramps.png')
- pixbuf = gtk.gdk.pixbuf_new_from_file (icon_file)
-
- pixbuf = pixbuf.add_alpha(True, chr(0xff), chr(0xff), chr(0xff))
- icon_set = gtk.IconSet (pixbuf)
- #add different sized icons, always png type!
- for size in extraiconsize :
- pixbuf = 0
- icon_file = os.path.expanduser(
- os.path.join(size[0], data[0]+'.png'))
- if os.path.isfile(icon_file):
- try:
- pixbuf = gtk.gdk.pixbuf_new_from_file (icon_file)
- except:
- pass
-
- if pixbuf :
- source = gtk.IconSource()
- source.set_size_wildcarded(False)
- source.set_size(size[1])
- source.set_pixbuf(pixbuf)
- icon_set.add_source(source)
-
- factory.add (data[0], icon_set)
-
-
-def build_user_paths():
- """ check/make user-dirs on each Gramps session"""
- for path in const.USER_DIRLIST:
- if not os.path.isdir(path):
- os.mkdir(path)
-
-def _display_welcome_message():
- """
- Display a welcome message to the user.
- """
- if not Config.get(Config.BETAWARN):
- from QuestionDialog import WarningDialog
- WarningDialog(
- _('Danger: This is unstable code!'),
- _("This GRAMPS 3.x-trunk is a development release. "
- "This version is not meant for normal usage. Use "
- "at your own risk.\n"
- "\n"
- "This version may:\n"
- "1) Work differently than you expect.\n"
- "2) Fail to run at all.\n"
- "3) Crash often.\n"
- "4) Corrupt your data.\n"
- "5) Save data in a format that is incompatible with the "
- "official release.\n"
- "\n"
- "BACKUP your existing databases before opening "
- "them with this version, and make sure to export your "
- "data to XML every now and then."))
- Config.set(Config.AUTOLOAD, False)
-# Config.set(Config.BETAWARN, True)
- Config.set(Config.BETAWARN, Config.get(Config.BETAWARN))
-
-#-------------------------------------------------------------------------
-#
-# Main Gramps class
-#
-#-------------------------------------------------------------------------
-class Gramps(object):
- """
- Main class corresponding to a running gramps process.
-
- There can be only one instance of this class per gramps application
- process. It may spawn several windows and control several databases.
- """
-
- def __init__(self, args):
- stopload = False
- try:
- build_user_paths()
- _display_welcome_message()
- except OSError, msg:
- ErrorDialog(_("Configuration error"), str(msg))
- except Errors.GConfSchemaError, val:
- ErrorDialog(_("Configuration error"), str(val) +
- _("\n\nPossibly the installation of GRAMPS "
- "was incomplete. Make sure the GConf schema "
- "of GRAMPS is properly installed."))
- gtk.main_quit()
- stopload = True
- except:
- log.error("Error reading configuration.", exc_info=True)
-
- if not mime_type_is_defined(const.APP_GRAMPS):
- ErrorDialog(_("Configuration error"),
- _("A definition for the MIME-type %s could not "
- "be found \n\nPossibly the installation of GRAMPS "
- "was incomplete. Make sure the MIME-types "
- "of GRAMPS are properly installed.")
- % const.APP_GRAMPS)
- gtk.main_quit()
- stopload = True
-
- register_stock_icons()
-
- state = DbState.DbState()
- self.vm = ViewManager.ViewManager(state)
- for view in DataViews.get_views():
- self.vm.register_view(view)
-
- if stopload:
- # We stop further loading so family tree manager is not shown
- # before the exit of GRAMPS
- return
-
- self.vm.init_interface()
-
- # Depending on the nature of this session,
- # we may need to change the order of operation
- ah = ArgHandler.ArgHandler(state, self.vm, args)
- if ah.need_gui():
- filename = ah.handle_args()
- if filename:
- self.vm.post_init_interface(show_manager=False)
- self.vm.open_activate(filename)
- else:
- self.vm.post_init_interface()
- else:
- ah.handle_args()
- self.vm.post_init_interface()
-
- if Config.get(Config.USE_TIPS):
- TipOfDay.TipOfDay(self.vm.uistate)
-
-
diff --git a/src/gui/Makefile.am b/src/gui/Makefile.am
index 5b899f28c..bc17f7e6f 100644
--- a/src/gui/Makefile.am
+++ b/src/gui/Makefile.am
@@ -9,7 +9,11 @@ SUBDIRS = \
pkgdatadir = $(datadir)/@PACKAGE@/gui
pkgdata_PYTHON = \
- __init__.py
+ __init__.py \
+ dbloader.py \
+ dbman.py \
+ grampsgui.py \
+ viewmanager.py
pkgpyexecdir = @pkgpyexecdir@/gui
pkgpythondir = @pkgpythondir@/gui
diff --git a/src/gui/__init__.py b/src/gui/__init__.py
index 59361d5ab..9431b8141 100644
--- a/src/gui/__init__.py
+++ b/src/gui/__init__.py
@@ -24,4 +24,9 @@
Package init for the gui package.
"""
+from grampsgui import startgtkloop
+from viewmanager import ViewManager
+from dbman import DbManager
+from dbloader import DbLoader
+
__all__ = [ "views" ]
diff --git a/src/DbLoader.py b/src/gui/dbloader.py
similarity index 85%
rename from src/DbLoader.py
rename to src/gui/dbloader.py
index 0c018a1a1..225fa9330 100644
--- a/src/DbLoader.py
+++ b/src/gui/dbloader.py
@@ -54,6 +54,7 @@ import gobject
# GRAMPS modules
#
#-------------------------------------------------------------------------
+from cli.grampscli import CLIDbLoader
import const
import Config
import gen.db
@@ -68,11 +69,32 @@ import Errors
# DbLoader class
#
#-------------------------------------------------------------------------
-class DbLoader(object):
+class DbLoader(CLIDbLoader):
def __init__(self, dbstate, uistate):
- self.dbstate = dbstate
+ CLIDbLoader.__init__(self, dbstate)
self.uistate = uistate
self.import_info = None
+
+ def _warn(title, warnmessage):
+ WarningDialog(title, warnmessage)
+
+ def _errordialog(title, errormessage):
+ """
+ Show the error.
+ In the GUI, the error is shown, and a return happens
+ """
+ ErrorDialog(title, errormessage)
+ return 1
+
+ def _dberrordialog(self, msg):
+ DBErrorDialog(str(msg.value))
+
+ def _begin_progress(self):
+ self.uistate.window.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
+ self.uistate.progress.show()
+
+ def _pulse_progress(self, value):
+ self.uistate.pulse_progressbar(value)
def import_file(self):
# First thing first: import is a batch transaction
@@ -198,58 +220,6 @@ class DbLoader(object):
return False
- def read_file(self, filename):
- """
- This method takes care of changing database, and loading the data.
- In 3.0 we only allow reading of real databases of filetype
- 'x-directory/normal'
-
- This method should only return on success.
- Returning on failure makes no sense, because we cannot recover,
- since database has already beeen changed.
- Therefore, any errors should raise exceptions.
-
- On success, return with the disabled signals. The post-load routine
- should enable signals, as well as finish up with other UI goodies.
- """
-
- if os.path.exists(filename):
- if not os.access(filename, os.W_OK):
- mode = "r"
- WarningDialog(_('Read only database'),
- _('You do not have write access '
- 'to the selected file.'))
- else:
- mode = "w"
- else:
- mode = 'w'
-
- dbclass = gen.db.GrampsDBDir
-
- self.dbstate.change_database(dbclass())
- self.dbstate.db.disable_signals()
-
- self.uistate.window.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
- self.uistate.progress.show()
-
- try:
- self.dbstate.db.load(filename, self.uistate.pulse_progressbar, mode)
- self.dbstate.db.set_save_path(filename)
- except gen.db.FileVersionDeclineToUpgrade:
- self.dbstate.no_database()
- except gen.db.exceptions.FileVersionError, msg:
- ErrorDialog( _("Cannot open database"), str(msg))
- self.dbstate.no_database()
- except OSError, msg:
- ErrorDialog(
- _("Could not open file: %s") % filename, str(msg))
- except Errors.DbError, msg:
- DBErrorDialog(str(msg.value))
- self.dbstate.no_database()
- except Exception:
- _LOG.error("Failed to open database.", exc_info=True)
- return True
-
def do_import(self, dialog, importer, filename):
self.import_info = None
dialog.destroy()
diff --git a/src/DbManager.py b/src/gui/dbman.py
similarity index 74%
rename from src/DbManager.py
rename to src/gui/dbman.py
index 282f2f24b..b2f0a2dc3 100644
--- a/src/DbManager.py
+++ b/src/gui/dbman.py
@@ -71,6 +71,7 @@ import const
from QuestionDialog import ErrorDialog, QuestionDialog
import gen.db
from gen.plug import PluginManager
+from cli.clidbman import *
import GrampsDbUtils
import Config
import Mime
@@ -87,9 +88,7 @@ _KP_ENTER = gtk.gdk.keyval_from_name("KP_Enter")
# constants
#
#-------------------------------------------------------------------------
-DEFAULT_TITLE = _("Family Tree")
-NAME_FILE = "name.txt"
-META_NAME = "meta_data.db"
+
ARCHIVE = "rev.gramps"
ARCHIVE_V = "rev.gramps,v"
@@ -103,233 +102,6 @@ STOCK_COL = 6
RCS_BUTTON = { True : _('_Extract'), False : _('_Archive') }
-class CLIDbManager(object):
- """
- Database manager without GTK functionality, allows users to create and
- open databases
- """
- def __init__(self, dbstate):
- self.dbstate = dbstate
- self.msg = None
-
- if dbstate:
- self.active = dbstate.db.get_save_path()
- else:
- self.active = None
-
- self.current_names = []
- self._populate_cli()
-
- def empty(self, val):
- """Callback that does nothing
- """
- pass
-
- def get_dbdir_summary(self, file_name):
- """
- Returns (people_count, version_number) of current DB.
- Returns ("Unknown", "Unknown") if invalid DB or other error.
- """
- from bsddb import dbshelve, db
- from gen.db import META, PERSON_TBL
- env = db.DBEnv()
- flags = db.DB_CREATE | db.DB_PRIVATE |\
- db.DB_INIT_MPOOL | db.DB_INIT_LOCK |\
- db.DB_INIT_LOG | db.DB_INIT_TXN | db.DB_THREAD
- try:
- env.open(file_name, flags)
- except:
- return "Unknown", "Unknown"
- dbmap1 = dbshelve.DBShelf(env)
- fname = os.path.join(file_name, META + ".db")
- try:
- dbmap1.open(fname, META, db.DB_HASH, db.DB_RDONLY)
- except:
- return "Unknown", "Unknown"
- version = dbmap1.get('version', default=None)
- dbmap1.close()
- dbmap2 = dbshelve.DBShelf(env)
- fname = os.path.join(file_name, PERSON_TBL + ".db")
- try:
- dbmap2.open(fname, PERSON_TBL, db.DB_HASH, db.DB_RDONLY)
- except:
- env.close()
- return "Unknown", "Unknown"
- count = len(dbmap2)
- dbmap2.close()
- env.close()
- return (count, version)
-
- def family_tree_summary(self):
- """
- Return a list of dictionaries of the known family trees.
- """
- # make the default directory if it does not exist
- list = []
- for item in self.current_names:
- (name, dirpath, path_name, last,
- tval, enable, stock_id) = item
- count, version = self.get_dbdir_summary(dirpath)
- retval = {}
- retval["Number of people"] = count
- if enable:
- retval["Locked?"] = "yes"
- else:
- retval["Locked?"] = "no"
- retval["DB version"] = version
- retval["Family tree"] = name
- retval["Path"] = dirpath
- retval["Last accessed"] = time.strftime('%x %X',
- time.localtime(tval))
- list.append( retval )
- return list
-
- def _populate_cli(self):
- """ Get the list of current names in the database dir
- """
- # make the default directory if it does not exist
- dbdir = os.path.expanduser(Config.get(Config.DATABASE_PATH))
- make_dbdir(dbdir)
-
- self.current_names = []
-
- for dpath in os.listdir(dbdir):
- dirpath = os.path.join(dbdir, dpath)
- path_name = os.path.join(dirpath, NAME_FILE)
- 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.dbstate.db.is_open())
-
- if (stock_id == 'gramps-lock'):
- last = find_locker_name(dirpath)
-
- self.current_names.append(
- (name, os.path.join(dbdir, dpath), path_name,
- last, tval, enable, stock_id))
-
- self.current_names.sort()
-
- def get_family_tree_path(self, name):
- """
- Given a name, return None if name not existing or the path to the
- database if it is a known database name.
- """
- for data in self.current_names:
- if data[0] == name:
- return data[1]
- return None
-
- def family_tree_list(self):
- """Return a list of name, dirname of the known family trees
- """
- lst = [(x[0], x[1]) for x in self.current_names]
- return lst
-
- def __start_cursor(self, msg):
- """
- Do needed things to start import visually, eg busy cursor
- """
- print _('Starting Import, %s') % msg
-
- def __end_cursor(self):
- """
- Set end of a busy cursor
- """
- print _('Import finished...')
-
- def _create_new_db_cli(self, title=None):
- """
- Create a new database.
- """
- new_path = find_next_db_dir()
-
- os.mkdir(new_path)
- path_name = os.path.join(new_path, NAME_FILE)
-
- if title is None:
- 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()
-
- # write the version number into metadata
- newdb = gen.db.GrampsDBDir()
- newdb.write_version(new_path)
-
- (tval, last) = time_val(new_path)
-
- self.current_names.append((title, new_path, path_name,
- last, tval, False, ""))
- return new_path, title
-
- def _create_new_db(self, title=None):
- """
- Create a new database, do extra stuff needed
- """
- return self._create_new_db_cli(title)
-
- def import_new_db(self, filename, callback):
- """
- Attempt to import the provided file into a new database.
- A new database will only be created if an appropriate importer was
- found.
-
- @return: A tuple of (new_path, name) for the new database
- or (None, None) if no import was performed.
- """
- pmgr = PluginManager.get_instance()
- (name, ext) = os.path.splitext(os.path.basename(filename))
- format = ext[1:].lower()
-
- for plugin in pmgr.get_import_plugins():
- if format == plugin.get_extension():
-
- new_path, name = self._create_new_db(name)
-
- # Create a new database
- self.__start_cursor(_("Importing data..."))
- dbclass = gen.db.GrampsDBDir
- dbase = dbclass()
- dbase.load(new_path, callback)
-
- import_function = plugin.get_import_function()
- import_function(dbase, filename, callback)
-
- # finish up
- self.__end_cursor()
- dbase.close()
-
- return new_path, name
- return None, None
-
- def is_locked(self, dbpath):
- """
- returns True if there is a lock file in the dirpath
- """
- if os.path.isfile(os.path.join(dbpath,"lock")):
- return True
- return False
-
- def needs_recovery(self, dbpath):
- """
- returns True if the database in dirpath needs recovery
- """
- if os.path.isfile(os.path.join(dbpath,"need_recover")):
- return True
- return False
-
- def break_lock(self, dbpath):
- """
- Breaks the lock on a database
- """
- if os.path.exists(os.path.join(dbpath, "lock")):
- os.unlink(os.path.join(dbpath, "lock"))
-
class DbManager(CLIDbManager):
"""
Database Manager. Opens a database manager window that allows users to
@@ -952,72 +724,6 @@ def drop_cb(wid, context, xpos, ypos, time_stamp):
context.finish(True, False, time_stamp)
return 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())
- dbdir = os.path.expanduser(Config.get(Config.DATABASE_PATH))
- new_path = os.path.join(dbdir, base)
- if not os.path.isdir(new_path):
- break
- return new_path
-
-def make_dbdir(dbdir):
- """
- Create the default database directory, as defined by dbdir
- """
- try:
- if not os.path.isdir(dbdir):
- os.makedirs(dbdir)
- except (IOError, OSError), msg:
- LOG.error(_("Could not make database directory: ") + str(msg))
-
-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.strftime('%x %X', time.localtime(tval))
- else:
- tval = 0
- last = _("Never")
- return (tval, last)
-
-def icon_values(dirpath, active, is_open):
- """
- If the directory path is the active path, then return values
- that indicate to use the icon, and which icon to use.
- """
- if os.path.isfile(os.path.join(dirpath,"need_recover")):
- return (True, gtk.STOCK_DIALOG_ERROR)
- elif dirpath == active and is_open:
- return (True, gtk.STOCK_OPEN)
- elif os.path.isfile(os.path.join(dirpath,"lock")):
- return (True, 'gramps-lock')
- else:
- return (False, "")
-
def find_revisions(name):
"""
Finds all the revisions of the specfied RCS archive.
@@ -1062,23 +768,7 @@ def find_revisions(name):
del proc
return revlist
-def find_locker_name(dirpath):
- """
- Opens the lock file if it exists, reads the contexts which is "USERNAME"
- and returns the contents, with correct string before "USERNAME",
- so the message can be printed with correct locale.
- If a file is encountered with errors, we return 'Unknown'
- This data is displayed in the time column of the manager
- """
- try:
- fname = os.path.join(dirpath, "lock")
- ifile = open(fname)
- username = ifile.read().strip()
- last = _("Locked by %s") % username
- ifile.close()
- except (OSError, IOError):
- last = _("Unknown")
- return last
+
def check_out(dbase, rev, path, callback):
"""
diff --git a/src/ViewManager.py b/src/gui/viewmanager.py
similarity index 89%
rename from src/ViewManager.py
rename to src/gui/viewmanager.py
index aeed0024a..918f0ab97 100644
--- a/src/ViewManager.py
+++ b/src/gui/viewmanager.py
@@ -55,9 +55,9 @@ import gtk
# GRAMPS modules
#
#-------------------------------------------------------------------------
+from cli import CLIManager
from PluginUtils import Tool, PluginWindows, \
ReportPluginDialog, ToolPluginDialog
-from gen.plug import PluginManager
import ReportBase
import DisplayState
import const
@@ -72,7 +72,7 @@ import RecentFiles
from BasicUtils import name_displayer
import widgets
import UndoHistory
-from DbLoader import DbLoader
+from gui.dbloader import DbLoader
import GrampsDisplay
from gen.utils import ProgressMonitor
from GrampsAboutDialog import GrampsAboutDialog
@@ -178,17 +178,20 @@ UIDEFAULT = '''
WIKI_HELP_PAGE_FAQ = '%s_-_FAQ' % const.URL_MANUAL_PAGE
WIKI_HELP_PAGE_KEY = '%s_-_Keybindings' % const.URL_MANUAL_PAGE
WIKI_HELP_PAGE_MAN = '%s' % const.URL_MANUAL_PAGE
+
#-------------------------------------------------------------------------
#
# ViewManager
#
#-------------------------------------------------------------------------
-class ViewManager(object):
+
+class ViewManager(CLIManager):
"""
Overview
========
- The ViewManager is the main window of the program. It is closely tied
+ The ViewManager is the session manager of the program.
+ Specifically, it manages 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.
@@ -204,14 +207,13 @@ class ViewManager(object):
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
+ method of containing each view, and switching between the views.
"""
- def __init__(self, state):
-
+ def __init__(self, dbstate):
+ CLIManager.__init__(self, dbstate, False)
self.page_is_changing = False
- self.state = state
self.active_page = None
self.views = []
self.pages = []
@@ -220,19 +222,24 @@ class ViewManager(object):
self.merge_ids = []
self.tips = gtk.Tooltips()
self._key = None
- self.file_loaded = False
self.show_sidebar = Config.get(Config.VIEW)
self.show_toolbar = Config.get(Config.TOOLBAR_ON)
self.show_filter = Config.get(Config.FILTER)
self.fullscreen = Config.get(Config.FULLSCREEN)
-
- self.__pmgr = PluginManager.get_instance()
self.__build_main_window()
self.__connect_signals()
- self.__do_load_plugins()
+ self.do_load_plugins()
+ def _errordialog(title, errormessage):
+ """
+ Show the error.
+ In the GUI, the error is shown, and a return happens
+ """
+ ErrorDialog(title, errormessage)
+ return 1
+
def __build_main_window(self):
"""
Builds the GTK interface
@@ -244,7 +251,7 @@ class ViewManager(object):
self.window.set_icon_from_file(const.ICON)
self.window.set_default_size(width, height)
- self.rel_class = self.__pmgr.get_relationship_calculator()
+ self.rel_class = self._pmgr.get_relationship_calculator()
vbox = gtk.VBox()
self.window.add(vbox)
@@ -278,7 +285,7 @@ class ViewManager(object):
self.window, self.statusbar, self.progress, self.warnbtn,
self.uimanager, self.progress_monitor, self)
- self.state.connect('database-changed', self.uistate.db_changed)
+ self.dbstate.connect('database-changed', self.uistate.db_changed)
self.filter_menu = self.uimanager.get_widget(
'/MenuBar/ViewMenu/Filter/')
@@ -290,14 +297,14 @@ class ViewManager(object):
self.uistate.set_open_widget(openbtn)
self.toolbar.insert(openbtn, 0)
- self.person_nav = Navigation.PersonNavigation(self.state, self.uistate)
+ self.person_nav = Navigation.PersonNavigation(self.dbstate, self.uistate)
self._navigation_type[PageView.NAVIGATION_PERSON] = (self.person_nav,
None)
self.recent_manager = DisplayState.RecentDocsMenu(
- self.uistate, self.state, self.__read_recent_file)
+ self.uistate, self.dbstate, self._read_recent_file)
self.recent_manager.build()
- self.db_loader = DbLoader(self.state, self.uistate)
+ self.db_loader = DbLoader(self.dbstate, self.uistate)
self.__setup_sidebar()
@@ -501,7 +508,7 @@ class ViewManager(object):
try:
self.active_page.call_function(name)
except Exception:
- self.uistate.push_message(self.state,
+ self.uistate.push_message(self.dbstate,
_("Key %s is not bound") % name)
def __next_view(self, action):
@@ -549,10 +556,10 @@ class ViewManager(object):
self.actiongroup.set_visible(False)
self.readonlygroup.set_visible(False)
self.fileactions.set_sensitive(False)
- self.__build_tools_menu(self.__pmgr.get_tool_list())
- self.__build_report_menu(self.__pmgr.get_report_list())
+ self.__build_tools_menu(self._pmgr.get_tool_list())
+ self.__build_report_menu(self._pmgr.get_report_list())
self.uistate.set_relationship_class()
- self.__pmgr.connect('plugins-reloaded',
+ self._pmgr.connect('plugins-reloaded',
self.__rebuild_report_and_tool_menus)
self.fileactions.set_sensitive(True)
self.uistate.widget.set_sensitive(True)
@@ -565,7 +572,7 @@ class ViewManager(object):
"""
Callback function for statusbar key update
"""
- self.uistate.modify_statusbar(self.state)
+ self.uistate.modify_statusbar(self.dbstate)
def __filter_signal(self, client, cnxn_id, entry, data):
"""
@@ -580,7 +587,7 @@ class ViewManager(object):
ArgHandler can work without it always shown
"""
self.window.show()
- if not self.state.db.is_open() and show_manager:
+ if not self.dbstate.db.is_open() and show_manager:
self.__open_activate(None)
def post_load_newdb(self, filename, filetype):
@@ -592,23 +599,22 @@ class ViewManager(object):
ifile.close()
except:
title = filename
- self.__post_load_newdb(filename, filetype, title)
+ self._post_load_newdb(filename, filetype, title)
- def __do_load_plugins(self):
+ def do_load_plugins(self):
"""
Loads the plugins at initialization time. The plugin status window is
opened on an error if the user has requested.
"""
# load plugins
self.uistate.status_text(_('Loading plugins...'))
- error = self.__pmgr.load_plugins(const.PLUGINS_DIR)
- error |= self.__pmgr.load_plugins(const.USER_PLUGINS)
+ error = CLIManager.do_load_plugins(self)
# get to see if we need to open the plugin status window
if error and Config.get(Config.POP_PLUGIN_STATUS):
self.__plugin_status()
- self.uistate.push_message(self.state, _('Ready'))
+ self.uistate.push_message(self.dbstate, _('Ready'))
def quit(self, *obj):
"""
@@ -619,7 +625,7 @@ class ViewManager(object):
# backup data, and close the database
self.__backup()
- self.state.db.close()
+ self.dbstate.db.close()
# have each page save anything, if they need to:
self.__delete_pages()
@@ -637,11 +643,11 @@ class ViewManager(object):
"""
import GrampsDbUtils
- if self.state.db.undoindex >= 0:
+ if self.dbstate.db.undoindex >= 0:
self.uistate.set_busy_cursor(1)
self.uistate.progress.show()
- self.uistate.push_message(self.state, _("Autobackup..."))
- GrampsDbUtils.Backup.backup(self.state.db)
+ self.uistate.push_message(self.dbstate, _("Autobackup..."))
+ GrampsDbUtils.Backup.backup(self.dbstate.db)
self.uistate.set_busy_cursor(0)
self.uistate.progress.hide()
@@ -649,7 +655,7 @@ class ViewManager(object):
"""
Abandon changes and quit.
"""
- if self.state.db.abort_possible:
+ if self.dbstate.db.abort_possible:
dialog = QuestionDialog2(
_("Abort changes?"),
@@ -659,8 +665,8 @@ class ViewManager(object):
_("Cancel"))
if dialog.run():
- self.state.db.disable_signals()
- while self.state.db.undo():
+ self.dbstate.db.disable_signals()
+ while self.dbstate.db.undo():
pass
self.quit()
else:
@@ -713,7 +719,7 @@ class ViewManager(object):
Open the preferences dialog.
"""
try:
- GrampsCfg.GrampsPreferences(self.uistate, self.state)
+ GrampsCfg.GrampsPreferences(self.uistate, self.dbstate)
self._key = self.uistate.connect('nameformat-changed',
self.active_page.build_tree)
except Errors.WindowActiveError:
@@ -824,7 +830,7 @@ class ViewManager(object):
index = 0
for page_def in self.views:
- page = page_def(self.state, self.uistate)
+ page = page_def(self.dbstate, self.uistate)
page_title = page.get_title()
page_stock = page.get_stock()
@@ -1013,7 +1019,7 @@ class ViewManager(object):
# set button of current page active
self.__set_active_button(num)
- if self.state.open:
+ if self.dbstate.open:
self.__disconnect_previous_page()
@@ -1041,89 +1047,60 @@ class ViewManager(object):
"""
Imports a file
"""
- if self.state.db.is_open():
+ if self.dbstate.db.is_open():
self.db_loader.import_file()
infotxt = self.db_loader.import_info_text()
if infotxt:
InfoDialog(_('Import Statistics'), infotxt, self.window)
self.__post_load()
- def open_activate(self, path):
- """
- Open and make a family tree active
- """
- self.__read_recent_file(path)
def __open_activate(self, obj):
"""
Called when the Open button is clicked, opens the DbManager
"""
- import DbManager
- dialog = DbManager.DbManager(self.state, self.window)
+ from dbman import DbManager
+ dialog = DbManager(self.dbstate, self.window)
value = dialog.run()
if value:
(filename, title) = value
self.db_loader.read_file(filename)
- self.__post_load_newdb(filename, 'x-directory/normal', title)
-
- def __read_recent_file(self, filename):
- """
- Called when the recent file is loaded
- """
- # A recent database should already have a directory
- # If not, do nothing, just return. This can be handled better if family tree
- # delete/rename also updated the recent file menu info in DisplayState.py
- if not os.path.isdir(filename):
- ErrorDialog(
- _("Could not load a recent Family Tree."),
- _("Family Tree does not exist, as it has been deleted."))
- return
-
- if self.db_loader.read_file(filename):
- # Attempt to figure out the database title
- path = os.path.join(filename, "name.txt")
- try:
- ifile = open(path)
- title = ifile.readline().strip()
- ifile.close()
- 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):
"""
This method is for the common UI post_load, both new files
and added data like imports.
"""
- if self.state.active :
+ if self.dbstate.active :
# clear history and fill history with first entry, active person
- self.uistate.clear_history(self.state.active.handle)
+ self.uistate.clear_history(self.dbstate.active.handle)
else :
self.uistate.clear_history(None)
self.uistate.progress.hide()
- self.state.db.undo_callback = self.__change_undo_label
- self.state.db.redo_callback = self.__change_redo_label
+ self.dbstate.db.undo_callback = self.__change_undo_label
+ self.dbstate.db.redo_callback = self.__change_redo_label
self.__change_undo_label(None)
self.__change_redo_label(None)
- self.state.db.undo_history_callback = self.undo_history_update
+ self.dbstate.db.undo_history_callback = self.undo_history_update
self.undo_history_close()
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):
"""
- Called after a new database is loaded.
+ The method called after load of a new database.
+ Inherit CLI method to add GUI part
"""
- if not filename:
- return
-
- # This method is for UI stuff when the database has changed.
- # Window title, recent files, etc related to new file.
-
- self.state.db.set_save_path(filename)
-
+ self._post_load_newdb_nongui(filename, filetype, title)
+ self._post_load_newdb_gui(filename, filetype, title)
+
+ def _post_load_newdb_gui(self, filename, filetype, title=None):
+ """
+ Called after a new database is loaded to do GUI stuff
+ """
+ # GUI related post load db stuff
# Update window title
if filename[-1] == os.path.sep:
filename = filename[:-1]
@@ -1131,7 +1108,7 @@ class ViewManager(object):
if title:
name = title
- if self.state.db.readonly:
+ if self.dbstate.db.readonly:
msg = "%s (%s) - GRAMPS" % (name, _('Read Only'))
self.uistate.window.set_title(msg)
self.actiongroup.set_sensitive(False)
@@ -1140,39 +1117,15 @@ class ViewManager(object):
self.uistate.window.set_title(msg)
self.actiongroup.set_sensitive(True)
- # apply preferred researcher if loaded file has none
- res = self.state.db.get_researcher()
- owner = GrampsCfg.get_researcher()
- if res.get_name() == "" and owner.get_name() != "":
- self.state.db.set_researcher(owner)
-
self.setup_bookmarks()
- name_displayer.set_name_format(self.state.db.name_formats)
- fmt_default = Config.get(Config.NAME_FORMAT)
- if fmt_default < 0:
- name_displayer.set_default_format(fmt_default)
-
- self.state.db.enable_signals()
- self.state.signal_change()
-
- Config.set(Config.RECENT_FILE, filename)
-
- try:
- self.state.change_active_person(self.state.db.find_initial_person())
- except:
- pass
-
self.change_page(None, None)
self.actiongroup.set_visible(True)
self.readonlygroup.set_visible(True)
-
- self.file_loaded = True
-
- RecentFiles.recent_files(filename, name)
+
self.recent_manager.build()
- # Call common __post_load
+ # Call common __post_load method for GUI update after a change
self.__post_load()
def __change_undo_label(self, label):
@@ -1240,16 +1193,16 @@ class ViewManager(object):
"""
import Bookmarks
self.bookmarks = Bookmarks.Bookmarks(
- self.state, self.uistate, self.state.db.get_bookmarks())
+ self.dbstate, self.uistate, self.dbstate.db.get_bookmarks())
def add_bookmark(self, obj):
"""
Add a bookmark to the bookmark list
"""
- 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,
+ if self.dbstate.active:
+ self.bookmarks.add(self.dbstate.active.get_handle())
+ name = name_displayer.display(self.dbstate.active)
+ self.uistate.push_message(self.dbstate,
_("%s has been bookmarked") % name)
else:
WarningDialog(
@@ -1268,7 +1221,7 @@ class ViewManager(object):
Displays the Reports dialog
"""
try:
- ReportPluginDialog(self.state, self.uistate, [])
+ ReportPluginDialog(self.dbstate, self.uistate, [])
except Errors.WindowActiveError:
return
@@ -1277,7 +1230,7 @@ class ViewManager(object):
Displays the Tools dialog
"""
try:
- ToolPluginDialog(self.state, self.uistate, [])
+ ToolPluginDialog(self.dbstate, self.uistate, [])
except Errors.WindowActiveError:
return
@@ -1287,7 +1240,7 @@ class ViewManager(object):
"""
import ScratchPad
try:
- ScratchPad.ScratchPadWindow(self.state, self.uistate)
+ ScratchPad.ScratchPadWindow(self.dbstate, self.uistate)
except Errors.WindowActiveError:
return
@@ -1296,7 +1249,7 @@ class ViewManager(object):
Calls the undo function on the database
"""
self.uistate.set_busy_cursor(1)
- self.state.db.undo()
+ self.dbstate.db.undo()
self.uistate.set_busy_cursor(0)
def redo(self, obj):
@@ -1304,7 +1257,7 @@ class ViewManager(object):
Calls the redo function on the database
"""
self.uistate.set_busy_cursor(1)
- self.state.db.redo()
+ self.dbstate.db.redo()
self.uistate.set_busy_cursor(0)
def undo_history(self, obj):
@@ -1312,7 +1265,7 @@ class ViewManager(object):
Displays the Undo history window
"""
try:
- self.undo_history_window = UndoHistory.UndoHistory(self.state,
+ self.undo_history_window = UndoHistory.UndoHistory(self.dbstate,
self.uistate)
except Errors.WindowActiveError:
return
@@ -1321,10 +1274,10 @@ class ViewManager(object):
"""
Calls the ExportAssistant to export data
"""
- if self.state.db.db_is_open:
+ if self.dbstate.db.db_is_open:
import ExportAssistant
try:
- ExportAssistant.ExportAssistant(self.state, self.uistate)
+ ExportAssistant.ExportAssistant(self.dbstate, self.uistate)
except Errors.WindowActiveError:
return
@@ -1332,8 +1285,8 @@ class ViewManager(object):
"""
Callback that rebuilds the tools and reports menu
"""
- tool_menu_list = self.__pmgr.get_tool_list()
- report_menu_list = self.__pmgr.get_report_list()
+ tool_menu_list = self._pmgr.get_tool_list()
+ report_menu_list = self._pmgr.get_report_list()
self.__build_tools_menu(tool_menu_list)
self.__build_report_menu(report_menu_list)
self.uistate.set_relationship_class()
@@ -1403,7 +1356,7 @@ class ViewManager(object):
menu_name = ("%s...") % name[2]
ofile.write('' % new_key)
actions.append((new_key, None, menu_name, None, None,
- func(name, self.state, self.uistate)))
+ func(name, self.dbstate, self.uistate)))
ofile.write('')
# If there are any unsupported items we add separator
@@ -1418,7 +1371,7 @@ class ViewManager(object):
new_key = name[3].replace(' ', '-')
ofile.write('' % new_key)
actions.append((new_key, None, name[2], None, None,
- func(name, self.state, self.uistate)))
+ func(name, self.dbstate, self.uistate)))
ofile.write('')
ofile.write('')