You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gnucash/src/FileDialog.c

510 lines
15 KiB

/********************************************************************\
* FileDialog.c -- file-handling utility dialogs for gnucash. *
* *
* Copyright (C) 1997 Robin D. Clark *
* Copyright (C) 1998, 1999 Linas Vepstas *
* *
* 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., 675 Mass Ave, Cambridge, MA 02139, USA. *
\********************************************************************/
/*
* hack alert -- the contents of this file are 99.997% motif independent.
* this file should be moved to a neutral directory, and recycled for use
* with the gtk/gnome code.
*/
#include <errno.h>
#include "config.h"
#include "FileBox.h"
#include "FileDialog.h"
#include "FileIO.h"
#include "Group.h"
#include "messages.h"
#include "Session.h"
#include "TransLog.h"
#include "Destroy.h"
#include "util.h"
#include "ui-callbacks.h"
/* This static indicates the debugging module that this .o belongs to. */
// static short module = MOD_GUI;
/** GLOBALS *********************************************************/
Session *current_session = NULL;
AccountGroup *topgroup = NULL; /* the current top of the heriarchy */
/********************************************************************\
* fileMenubarCB -- handles file menubar choices *
\********************************************************************/
#define SHOW_IO_ERR_MSG(io_error) { \
switch (io_error) { \
case ERR_FILEIO_NO_ERROR: \
break; \
case ERR_FILEIO_FILE_NOT_FOUND: \
sprintf (buf, FILE_NOT_FOUND_MSG, newfile); \
errorBox (buf); \
uh_oh = 1; \
break; \
case ERR_FILEIO_FILE_EMPTY: \
sprintf (buf, FILE_EMPTY_MSG, newfile); \
errorBox (buf); \
uh_oh = 1; \
break; \
case ERR_FILEIO_FILE_TOO_NEW: \
errorBox ( FILE_TOO_NEW_MSG); \
uh_oh = 1; \
break; \
case ERR_FILEIO_FILE_TOO_OLD: \
if (!verifyBox( FILE_TOO_OLD_MSG )) { \
xaccFreeAccountGroup (newgrp); \
newgrp = NULL; \
uh_oh = 1; \
} \
break; \
case ERR_FILEIO_FILE_BAD_READ: \
if (!verifyBox( FILE_BAD_READ_MSG )) { \
xaccFreeAccountGroup (newgrp); \
newgrp = NULL; \
uh_oh = 1; \
} \
break; \
default: \
break; \
} \
}
#define SHOW_LOCK_ERR_MSG(session) \
{ \
int norr = xaccSessionGetError (session); \
if (ETXTBSY == norr) \
{ \
sprintf (buf, FMB_LOCKED_MSG, newfile); \
errorBox (buf); \
uh_oh = 1; \
} \
else \
if (norr) \
{ \
sprintf (buf, FMB_INVALID_MSG, newfile); \
errorBox (buf); \
uh_oh = 1; \
} \
} \
/* ======================================================== */
void
gncFileNew (void)
{
Session *sess;
AccountGroup *grp;
/* If user attempts to start a new session before saving
* results of the last one, prompt them to clean up thier
* act. */
gncFileQuerySave ();
sess = current_session;
grp = xaccSessionGetGroup (sess);
/* if session not yet started ... */
if (!grp) grp = topgroup;
/* when starting new everything, destroy old stuff first */
/* destroy open windows first, before destroying the group itself */
xaccGroupWindowDestroy (grp);
/* close any ongoing file sessions, if any */
xaccSessionEnd (sess);
xaccSessionDestroy (sess);
current_session = NULL;
topgroup = NULL;
/* disable logging while we move over to the new set of accounts to
* edit; the mass deletetion of accounts and transactions during
* switchover is not something we want to keep in a journal. */
xaccLogDisable();
xaccFreeAccountGroup (grp);
xaccLogEnable();
grp = xaccMallocAccountGroup();
topgroup = grp;
}
/* ======================================================== */
void
gncFileQuerySave (void)
{
Session *sess;
AccountGroup *grp;
sess = current_session;
grp = xaccSessionGetGroup (sess);
/* if session not yet started ... */
if (!grp) grp = topgroup;
/* If user wants to mess around before finishing business with
* the old file, give em a chance to figure out what's up.
* Pose the question as a "while" loop, so that if user screws
* up the file-selection dialog, we don't blow em out of the water;
* instead, give them another chance to say "no" to the verify box.
*/
while ( xaccAccountGroupNotSaved (grp) )
{
if( verifyBox( FMB_SAVE_MSG) )
{
gncFileSave ();
}
else
return;
}
}
/* ======================================================== */
/* private utilities for file open; done in two stages */
static void
gncPostFileOpen (const char * filename)
{
Session *newsess;
AccountGroup *oldgrp;
int io_error, uh_oh=0;
char buf[BUFSIZE];
AccountGroup *newgrp;
char * newfile;
if (!filename) return;
newfile = xaccResolveFilePath (filename);
if (!newfile) return;
/* -------------- BEGIN CORE SESSION CODE ------------- */
/* -- this code is almost identical in FileOpen and FileSaveAs -- */
oldgrp = xaccSessionGetGroup (current_session);
/* if session not yet started ... */
if (!oldgrp) oldgrp = topgroup;
/* load the accounts from the users datafile */
/* but first, check to make sure we've got a session going ... */
newsess = xaccMallocSession ();
/* disable logging while we move over to the new set of accounts to
* edit; the mass deletetion of accounts and transactions during
* switchover is not something we want to keep in a journal. */
xaccLogDisable();
newgrp = xaccSessionBeginFile (newsess, newfile);
xaccLogEnable();
/* check for session errors, put up appropriate dialog */
SHOW_LOCK_ERR_MSG (newsess);
/* check for i/o error, put up appropriate error message */
io_error = xaccGetFileIOError();
SHOW_IO_ERR_MSG(io_error);
/* Umm, came up empty-handed, i.e. the file was not found. */
/* This is almost certainly not what the user wanted. */
if (!newgrp && !io_error)
{
sprintf (buf, FILE_NOT_FOUND_MSG, newfile);
errorBox ( buf);
uh_oh = 1;
}
/* going down -- abandon ship */
if (uh_oh)
{
xaccSessionEnd (newsess);
xaccSessionDestroy (newsess);
/* well, no matter what, I think its a good idea to have
* a topgroup around. For example, early in the gnucash startup
* sequence, the user opens a file ... if this open fails for any
* reason, we don't want to leave them high & dry without a topgroup,
* because if user continues, then bad things will happen ...
*/
if (NULL == topgroup)
{
topgroup = xaccMallocAccountGroup();
}
free (newfile);
return;
}
/* if we got to here, then we've successfully gotten a new session */
/* close up the old file session (if any) */
xaccSessionEnd (current_session);
xaccSessionDestroy (current_session);
current_session = newsess;
/* --------------- END CORE SESSION CODE -------------- */
/* clean up old stuff, and then we're outta here. */
xaccLogDisable();
xaccLogSetBaseName (newfile);
/* destroy open windows first, before destroying the group itself */
xaccGroupWindowDestroy (oldgrp);
xaccFreeAccountGroup (oldgrp);
topgroup = newgrp;
xaccLogEnable();
free (newfile);
}
/* ======================================================== */
void
gncFileOpen (void)
{
char * newfile;
gncFileQuerySave ();
newfile = fileBox( OPEN_STR, "*.xac");
gncPostFileOpen (newfile);
/* This dialogue can show up early in the startup process.
* If the user fails to pick a file (by e.g. hitting the cancel
* button), we might be left with a null topgroup, which leads
* to nastiness when user goes to create thier very first account.
* Don't leave thier ass in a sling, give them what they need.
*/
if (NULL == topgroup)
{
topgroup = xaccMallocAccountGroup();
}
}
void
gncFileOpenFile (const char * newfile)
{
if (!newfile) return;
gncFileQuerySave ();
gncPostFileOpen (newfile);
}
/* ======================================================== */
void
gncFileQIFImport (void)
{
char * newfile;
char buf[BUFSIZE];
int io_error, uh_oh = 0;
AccountGroup *newgrp;
newfile = fileBox( OPEN_STR, "*.qif");
if (!newfile) return;
/* load the accounts from the file the user specified */
newgrp = xaccReadQIFAccountGroup (newfile);
/* check for i/o error, put up appropriate error message */
io_error = xaccGetQIFIOError();
SHOW_IO_ERR_MSG(io_error);
if (uh_oh) return;
if( NULL == topgroup ) {
/* no topgroup exists */
topgroup = xaccMallocAccountGroup();
}
/* since quicken will not export all accounts
* into one file, we must merge them in one by one */
xaccConcatGroups (topgroup, newgrp);
xaccMergeAccounts (topgroup);
xaccConsolidateGrpTransactions (topgroup);
}
/* ======================================================== */
static int been_here_before = 0;
void
gncFileSave (void)
{
AccountGroup *newgrp = NULL;
char * newfile;
char buf[BUFSIZE];
int io_error, norr, uh_oh = 0;
/* hack alert -- Somehow make sure all in-progress edits get committed! */
/* if no session exists, then we don't have a filename/path
* to save to. Get one now. */
if ((NULL == current_session) ||
(NULL == xaccSessionGetGroup (current_session)))
{
gncFileSaveAs();
return;
}
/* use the current session to save to file */
xaccSessionSave (current_session);
/* in theory, no error should have occured, but just in case,
* we're gonna check and handle ... */
norr = xaccSessionGetError (current_session);
if (norr)
{
if (been_here_before) return;
been_here_before = 1;
gncFileSaveAs(); /* been_here prevents infinite recuirsion */
been_here_before = 0;
return;
}
/* check for i/o error, put up appropriate error message */
io_error = xaccGetFileIOError();
newfile = xaccSessionGetFilePath(current_session);
SHOW_IO_ERR_MSG(io_error);
/* going down -- abandon ship */
if (uh_oh) return;
xaccAccountGroupMarkSaved (topgroup);
}
/* ======================================================== */
void
gncFileSaveAs (void)
{
Session *newsess;
AccountGroup *oldgrp;
char * newfile;
AccountGroup *newgrp;
char * oldfile;
char buf[BUFSIZE];
int io_error, uh_oh = 0;
newfile = fileBox( SAVE_STR, "*.xac");
if (!newfile) return;
/* check to see if the user did something silly,
* like specifying the same file as the current file ...
* if so, then just do that, instead of the below,
* which assumes a tuly new name was given.
*/
newfile = xaccResolveFilePath (newfile);
assert (newfile); /* deep doodoo if resolve failed */
oldfile = xaccSessionGetFilePath (current_session);
if (oldfile && !strcmp (oldfile, newfile))
{
free (newfile);
gncFileSave ();
return;
}
/* -------------- BEGIN CORE SESSION CODE ------------- */
/* -- this code identical in FileOpen and FileSaveAs -- */
oldgrp = xaccSessionGetGroup (current_session);
/* if session not yet started ... */
if (!oldgrp) oldgrp = topgroup;
/* create a new session ... */
newsess = xaccMallocSession ();
/* disable logging while we move over to the new set of accounts to
* edit; the mass deletetion of accounts and transactions during
* switchover is not something we want to keep in a journal. */
xaccLogDisable();
newgrp = xaccSessionBeginFile (newsess, newfile);
xaccLogEnable();
/* check for session errors (e.g. file locked by another user) */
SHOW_LOCK_ERR_MSG (newsess);
/* check for i/o error, put up appropriate error message */
io_error = xaccGetFileIOError();
SHOW_IO_ERR_MSG(io_error);
/* going down -- abandon ship */
if (uh_oh)
{
xaccSessionEnd (newsess);
xaccSessionDestroy (newsess);
/* well, no matter what, I think its a good idea to have
* a topgroup around. For example, early in the gnucash startup
* sequence, the user opens a file ... if this open fails for any
* reason, we don't want to leave them high & dry without a topgroup,
* because if user continues, then bad things will happen ...
*/
if (NULL == topgroup)
{
topgroup = xaccMallocAccountGroup();
}
free (newfile);
return;
}
/* if we got to here, then we've successfully gotten a new session */
/* close up the old file session (if any) */
xaccSessionEnd (current_session);
xaccSessionDestroy (current_session);
current_session = newsess;
/* --------------- END CORE SESSION CODE -------------- */
/* oops ... file already exists ... ask user what to do... */
if (newgrp)
{
char *tmpmsg;
tmpmsg = alloca (strlen (FMB_EEXIST_MSG) + strlen (newfile));
sprintf (tmpmsg, FMB_EEXIST_MSG, newfile);
/* if user says cancel, we should break out */
if (! verifyBox ( tmpmsg)) return;
/* Whoa-ok. Blow away the previous file.
* Do not disable logging ... we want to capture the
* old file in the log, just in case the user later
* decides it was all a big mistake. */
xaccSessionSetGroup (newsess, NULL);
/* xaccLogDisable(); no don't disable, keep logging on */
xaccFreeAccountGroup (newgrp);
}
/* OK, save the data to the file ... */
xaccLogSetBaseName (newfile);
xaccSessionSetGroup (newsess, oldgrp);
gncFileSave ();
free (newfile);
}
/* ======================================================== */
void
gncFileQuit (void)
{
AccountGroup *grp;
grp = xaccSessionGetGroup (current_session);
/* if session not yet started ... */
if (!grp) grp = topgroup;
/* disable logging; the mass deletetion of accounts and transactions
* during shutdown is not something we want to keep in a journal. */
xaccLogDisable();
xaccSessionEnd (current_session);
xaccSessionDestroy (current_session);
current_session = NULL;
// xaccGroupWindowDestroy (grp);
xaccFreeAccountGroup (grp);
topgroup = NULL;
}
/********************* END OF FILE **********************************/