/******************************************************************************
 JXFileDocument.cc

	Document class that represents data stored in any type of file.  This can
	be used for a multi-file document if there is a unique file that the
	user selects to open the document.  This particular file can then be
	the file stored by JXFileDocument.

	Derived classes should not implement OKToClose().  Instead, they must
	implement either WriteFile() or WriteTextFile().  For documents stored
	in text files (the easiest, safest, and most portable method), simply
	override WriteTextFile().  For other types of files, override WriteFile().

	The safetySave flag passed to WriteFile() and WriteTextFile() should
	obviously not affect what is written, but it can affect other actions.
	As an example, see TestTextEditDocument::WriteFile() in testjx.

	CheckForSafetySaveFiles() is provided to look for safety saved versions
	of files that the user opens.  We cannot provide the equivalent feature
	for untitled documents because, while we could easily check for #untitled#_
	files in the user's home directory, we do not know which application created
	them.

	BASE CLASS = JXDocument

	Copyright © 1996-98 by John Lindal. All rights reserved.

 ******************************************************************************/

#include <JXFileDocument.h>
#include <JXWindow.h>
#include <jXGlobals.h>
#include <jXCSFIcons.h>
#include <JString.h>
#include <jFStreamUtil.h>
#include <jStreamUtil.h>
#include <jFileUtil.h>
#include <jDirUtil.h>
#include <jAssert.h>

JBoolean JXFileDocument::itsAskOKToCloseFlag = kTrue;

static const JCharacter* kBackupFileSuffix = "~";

static const JCharacter* kFileNameIndicator = "%f";
const JSize kFileNameIndicatorLength        = 2;

static const JCharacter* kSaveBeforeClosePrompt = "Save \"%f\" before closing?";
static const JCharacter* kSaveNewFilePrompt     = "Save file as:";
static const JCharacter* kOKToRevertPrompt      = "Discard all changes to \"%f\"?";

static const JCharacter* kSafetySavePrefix  = "#";
static const JCharacter* kSafetySaveSuffix  = "#";
static const JCharacter* kUnsavedFilePrefix = "#untitled#_";

static const JCharacter* kAssertSavePrefix        = "##";
static const JCharacter* kAssertSaveSuffix        = "##";
static const JCharacter* kAssertUnsavedFilePrefix = "##untitled##_";

// setup information

const JFileVersion kCurrentSetupVersion = 1;
const JCharacter kSetupDataEndDelimiter = '\1';

	// version 1 stores itsWantNewBackupEveryOpenFlag

// JBroadcaster message types

const JCharacter* JXFileDocument::kNameChanged = "NameChanged::JXFileDocument";

// JError messages

const JCharacter* JXFileDocument::kWriteFailed = "WriteFailed::JXFileDocument";

const JCharacter* JXFileDocument::kWriteFailedMsg =
	"An error occurred while trying to save the data.  "
	"Please check that there is enough space available "
	"on the disk.";

/******************************************************************************
 Constructor

	Derived class should call AdjustWindowTitle() after creating a window.

	defaultFileNameSuffix can be the empty string.  If it isn't,
	SaveInNewFile() appends it to the file name before opening the
	"Save file" dialog.

 ******************************************************************************/

JXFileDocument::JXFileDocument
	(
	JXDirector*			supervisor,
	const JCharacter*	fileName,
	const JBoolean		onDisk,
	const JBoolean		wantBackupFile,
	const JCharacter*	defaultFileNameSuffix
	)
	:
	JXDocument(supervisor)
{
	itsAllocateTitleSpaceFlag     = kFalse;
	itsWantBackupFileFlag         = wantBackupFile;
	itsWantNewBackupEveryOpenFlag = kTrue;
	itsAutosaveBeforeCloseFlag    = kFalse;
	itsCSF                        = JGetChooseSaveFile();
	itsNeedSafetySaveFlag         = kFalse;
	itsSafetySaveFileName         = NULL;

	itsFilePath = new JString;
	assert( itsFilePath != NULL );

	itsFileName = new JString;
	assert( itsFileName != NULL );

	itsFileSuffix = new JString(defaultFileNameSuffix);
	assert( itsFileSuffix != NULL );

	itsSaveBeforeClosePrompt = new JString(kSaveBeforeClosePrompt);
	assert( itsSaveBeforeClosePrompt != NULL );

	itsSaveNewFilePrompt = new JString(kSaveNewFilePrompt);
	assert( itsSaveNewFilePrompt != NULL );

	itsOKToRevertPrompt = new JString(kOKToRevertPrompt);
	assert( itsOKToRevertPrompt != NULL );

	FileChanged(fileName, onDisk);
}

/******************************************************************************
 Destructor

 ******************************************************************************/

JXFileDocument::~JXFileDocument()
{
	RemoveSafetySaveFile();

	delete itsFilePath;
	delete itsFileName;
	delete itsFileSuffix;
	delete itsSaveBeforeClosePrompt;
	delete itsSaveNewFilePrompt;
	delete itsOKToRevertPrompt;
}

/******************************************************************************
 ReadJXFDSetup

 ******************************************************************************/

void
JXFileDocument::ReadJXFDSetup
	(
	istream& input
	)
{
	JFileVersion vers;
	input >> vers;

	if (vers <= kCurrentSetupVersion)
		{
		JBoolean wantBackup, allocTitleSpace;
		input >> wantBackup >> allocTitleSpace;
		ShouldMakeBackupFile(wantBackup);
		ShouldAllocateTitleSpace(allocTitleSpace);

		if (vers >= 1)
			{
			JBoolean newBackupEveryOpen;
			input >> newBackupEveryOpen;
			ShouldMakeNewBackupEveryOpen(newBackupEveryOpen);
			}
		}

	JIgnoreUntil(input, kSetupDataEndDelimiter);
}

/******************************************************************************
 WriteJXFDSetup

 ******************************************************************************/

void
JXFileDocument::WriteJXFDSetup
	(
	ostream& output
	)
	const
{
	output << kCurrentSetupVersion;
	output << ' ' << itsWantBackupFileFlag;
	output << ' ' << itsAllocateTitleSpaceFlag;
	output << ' ' << itsWantNewBackupEveryOpenFlag;
	output << kSetupDataEndDelimiter;
}

/******************************************************************************
 ExistsOnDisk

 ******************************************************************************/

JBoolean
JXFileDocument::ExistsOnDisk()
	const
{
	const JString fullName = *itsFilePath + *itsFileName;
	return JConvertToBoolean( itsWasOnDiskFlag && JFileExists(fullName) );
}

/*****************************************************************************
 GetFullName

 ******************************************************************************/

JString
JXFileDocument::GetFullName
	(
	JBoolean* onDisk
	)
	const
{
	const JString fullName = *itsFilePath + *itsFileName;
	*onDisk = JConvertToBoolean( itsWasOnDiskFlag && JFileExists(fullName) );
	return fullName;
}

/******************************************************************************
 GetName (virtual)

	Override of JXDocument::GetName().

 ******************************************************************************/

JString
JXFileDocument::GetName()
	const
{
	return *itsFileName;
}

/******************************************************************************
 GetMenuIcon (virtual)

	Override of JXDocument::GetMenuIcon().

 ******************************************************************************/

JXPM
JXFileDocument::GetMenuIcon()
	const
{
	return JXGetPlainFileIcon();
}

/******************************************************************************
 NeedsSave (virtual)

	Implementation of JXDocument::NeedsSave().

 ******************************************************************************/

JBoolean
JXFileDocument::NeedsSave()
	const
{
	return JNegate(itsSavedFlag);
}

/******************************************************************************
 FileChanged (protected)

	Call this to notify us that the document is now displaying a different file.
	(e.g. after New or Open on the File menu)

 ******************************************************************************/

void
JXFileDocument::FileChanged
	(
	const JCharacter*	fileName,
	const JBoolean		onDisk
	)
{
	itsSavedFlag          = kTrue;
	itsWasOnDiskFlag      = onDisk;
	itsMakeBackupFileFlag = JConvertToBoolean( itsWasOnDiskFlag && itsWantBackupFileFlag );
	itsIsFirstSaveFlag    = kTrue;

	if (onDisk)
		{
		JString fullName;
		const JBoolean ok = JGetTrueName(fileName, &fullName);
		assert( ok );
		JSplitPathAndName(fullName, itsFilePath, itsFileName);
		const JError err = JGetModificationTime(fileName, &itsFileModTime);
		assert_ok( err );
		itsCheckModTimeFlag = kTrue;
		}
	else
		{
		itsFilePath->Clear();
		*itsFileName        = fileName;
		itsFileModTime      = 0;
		itsCheckModTimeFlag = kFalse;
		}

	RemoveSafetySaveFile();
	AdjustWindowTitle();

	const JString fullName = *itsFilePath + *itsFileName;
	Broadcast(NameChanged(fullName));
}

/******************************************************************************
 OKToClose (virtual protected)

 ******************************************************************************/

JBoolean
JXFileDocument::OKToClose()
{
	if (itsSavedFlag)
		{
		if (itsAutosaveBeforeCloseFlag && itsWasOnDiskFlag)
			{
			DataModified();
			SaveInCurrentFile();
			}
		return itsSavedFlag;
		}

	Activate();		// show ourselves so user knows what we are asking about

	JString msg = *itsSaveBeforeClosePrompt;
	JIndex index;
	while (msg.LocateSubstring(kFileNameIndicator, &index))
		{
		msg.ReplaceSubstring(index, index+kFileNameIndicatorLength-1, *itsFileName);
		}

	const JUserNotification::CloseAction action =
		itsAskOKToCloseFlag ? (JGetUserNotification())->OKToClose(msg) :
							  JUserNotification::kSaveData;
	if (action == JUserNotification::kSaveData)
		{
		SaveInCurrentFile();
		return itsSavedFlag;
		}
	else if (action == JUserNotification::kDiscardData)
		{
		return kTrue;
		}
	else
		{
		assert( action == JUserNotification::kDontClose );
		return kFalse;
		}
}

/******************************************************************************
 OKToRevert (virtual protected)

	If the data hasn't been saved or the file has been changed behind
	our back, asks the user if it is ok to revert to the data stored in
	the file.

 ******************************************************************************/

JBoolean
JXFileDocument::OKToRevert()
{
	const JString fullName = *itsFilePath + *itsFileName;
	time_t modTime;
	if (JGetModificationTime(fullName, &modTime) == kJNoError &&
		modTime != itsFileModTime)
		{
		JString msg = "The copy of \"";
		msg += *itsFileName;
		msg += "\" on disk has been modified by another program.  "
			   "Are you sure that you want to discard your changes?";
		return (JGetUserNotification())->AskUserNo(msg);
		}

	else if (!itsSavedFlag && ExistsOnDisk())
		{
		JString msg = *itsOKToRevertPrompt;
		JIndex index;
		while (msg.LocateSubstring(kFileNameIndicator, &index))
			{
			msg.ReplaceSubstring(index, index+kFileNameIndicatorLength-1, *itsFileName);
			}

		return (JGetUserNotification())->AskUserYes(msg);
		}

	else
		{
		return kFalse;
		}
}

/******************************************************************************
 CanRevert (virtual protected)

	Returns kTrue if the data has not been saved or the file has
	been modified behind our back.

 ******************************************************************************/

JBoolean
JXFileDocument::CanRevert()
{
	if (!ExistsOnDisk())
		{
		return kFalse;
		}
	else if (!itsSavedFlag)
		{
		return kTrue;
		}

	const JString fullName = *itsFilePath + *itsFileName;
	time_t modTime;
	return JConvertToBoolean(
			JGetModificationTime(fullName, &modTime) == kJNoError &&
			modTime != itsFileModTime );
}

/******************************************************************************
 DataReverted

	Call this after a "Revert to saved" operation

 ******************************************************************************/

void
JXFileDocument::DataReverted()
{
	if (itsWasOnDiskFlag)
		{
		const JString fullName = *itsFilePath + *itsFileName;
		time_t modTime;
		if (JGetModificationTime(fullName, &modTime) == kJNoError)
			{
			itsFileModTime = modTime;
			}
		}

	itsSavedFlag = kTrue;
	RemoveSafetySaveFile();
	AdjustWindowTitle();
}

/******************************************************************************
 SaveCopyInNewFile (protected)

	Save a copy of the data in a new file.  Our file is not changed.
	Returns kTrue if a file is successfully written.

	If fullName != NULL and the save is successful, *fullName is the
	name of the file that was created.

 ******************************************************************************/

JBoolean
JXFileDocument::SaveCopyInNewFile
	(
	JString* fullName
	)
{
	const JString origName = GetFileNameForSave();
	JString newName;
	if (itsCSF->SaveFile(*itsSaveNewFilePrompt, NULL, origName, &newName))
		{
		// We set safetySave because it's as if it were a temp file.

		const JError err = WriteFile(newName, kTrue);
		if (err.OK())
			{
			if (fullName != NULL)
				{
				*fullName = newName;
				}
			return kTrue;
			}
		else
			{
			err.ReportError();
			}
		}

	if (fullName != NULL)
		{
		fullName->Clear();
		}
	return kFalse;
}

/******************************************************************************
 SaveInNewFile (protected)

	Save the data in a new file and update ourselves to refer to this new file.

 ******************************************************************************/

void
JXFileDocument::SaveInNewFile()
{
	const JString origName = GetFileNameForSave();
	JString fullName;
	if (itsCSF->SaveFile(*itsSaveNewFilePrompt, NULL, origName, &fullName))
		{
		const JString savePath     = *itsFilePath;
		const JString saveName     = *itsFileName;
		const JBoolean saveFlag[4] =
			{ itsWasOnDiskFlag, itsMakeBackupFileFlag, itsCheckModTimeFlag, itsSavedFlag };

		JSplitPathAndName(fullName, itsFilePath, itsFileName);
		itsWasOnDiskFlag      = kTrue;
		itsMakeBackupFileFlag = kFalse;
		itsCheckModTimeFlag   = kFalse;
		itsSavedFlag          = kFalse;
		SaveInCurrentFile();
		if (itsSavedFlag)
			{
			itsMakeBackupFileFlag = itsWantBackupFileFlag;	// make backup when save next time
			Broadcast(NameChanged(fullName));
			}
		else
			{
			*itsFilePath          = savePath;
			*itsFileName          = saveName;
			itsWasOnDiskFlag      = saveFlag[0];
			itsMakeBackupFileFlag = saveFlag[1];
			itsCheckModTimeFlag   = saveFlag[2];
			itsSavedFlag          = saveFlag[3];
			}
		}
}

/******************************************************************************
 GetFileNameForSave (private)

 ******************************************************************************/

JString
JXFileDocument::GetFileNameForSave()
	const
{
	JString fileName = *itsFileName;
	if (!itsWasOnDiskFlag && !fileName.EndsWith(*itsFileSuffix))
		{
		fileName += *itsFileSuffix;
		}
	if (itsWasOnDiskFlag)
		{
		fileName.Prepend(*itsFilePath);
		}
	return fileName;
}

/******************************************************************************
 SaveInCurrentFile (protected)

 ******************************************************************************/

void
JXFileDocument::SaveInCurrentFile()
{
	if (!itsSavedFlag && !itsWasOnDiskFlag)
		{
		SaveInNewFile();
		return;
		}

	const JString fullName     = *itsFilePath + *itsFileName;
	const JBoolean exists      = JFileExists(fullName);
	const JBoolean dirWritable = JDirectoryWritable(*itsFilePath);
	if (!itsSavedFlag && exists && !JFileWritable(fullName))
		{
		if ((JGetUserNotification())->AskUserYes(
				"The data cannot be saved because the file is write protected.  "
				"Would you like to save it in a different file?"))
			{
			SaveInNewFile();
			}
		}
	else if (!itsSavedFlag && !exists && !JDirectoryExists(*itsFilePath))
		{
		(JGetUserNotification())->ReportError(
			"The data cannot be saved because the directory does not exist.");
		}
	else if (!itsSavedFlag && !exists && !dirWritable)
		{
		(JGetUserNotification())->ReportError(
			"The data cannot be saved because the directory is write protected.");
		}
	else if (!itsSavedFlag)
		{
		if (itsCheckModTimeFlag)
			{
			time_t modTime;
			if (JGetModificationTime(fullName, &modTime) == kJNoError &&
				modTime != itsFileModTime)
				{
				JString msg = "The copy of \"";
				msg += *itsFileName;
				msg += "\" on disk has been modified by another program.  "
					   "Do you want to overwrite the changes?";
				if (!(JGetUserNotification())->AskUserNo(msg))
					{
					return;
					}
				}
			}

		JBoolean madeBackupFile = kFalse;
		mode_t filePerms        = 0;
		if (itsMakeBackupFileFlag)
			{
			const JString backupName  = fullName + kBackupFileSuffix;
			const JBoolean makeBackup = JConvertToBoolean(
				!JFileExists(backupName) ||
				(itsIsFirstSaveFlag && itsWantNewBackupEveryOpenFlag) );
			if (makeBackup && dirWritable)
				{
				const JError err = JRenameFile(fullName, backupName);
				if (err.OK())
					{
					madeBackupFile = (JGetPermissions(backupName, &filePerms)).OK();
					}
				else
					{
					JString msg = "A backup file could not be created because:\n\n";
					msg += err.GetMessage();
					msg += "\n\nDo you still want to save the file?";
					if (!(JGetUserNotification())->AskUserNo(msg))
						{
						return;
						}
					}
				}
			else if (makeBackup &&
					 !(JGetUserNotification())->AskUserNo(
							"A backup file could not be created "
							"because the directory is not writable.\n\n"
							"Do you still want to save the file?"))
				{
				return;
				}
			}
		itsMakeBackupFileFlag = kFalse;

		const JError err = WriteFile(fullName, kFalse);
		if (err.OK())
			{
			itsSavedFlag       = kTrue;
			itsIsFirstSaveFlag = kFalse;
			RemoveSafetySaveFile();
			AdjustWindowTitle();

			if (madeBackupFile)
				{
				const JError err = JSetPermissions(fullName, filePerms);
				if (!err.OK())
					{
					JString msg = "The file permissons could not be preserved because:\n\n";
					msg += err.GetMessage();
					(JGetUserNotification())->ReportError(msg);
					}
				}

			const JError err = JGetModificationTime(fullName, &itsFileModTime);
			assert_ok( err );
			itsCheckModTimeFlag = kTrue;
			}
		else
			{
			err.ReportError();
			}
		}
}

/******************************************************************************
 WriteFile (virtual protected)

	This must be overridden if there is more than one file, or the file
	is not a plain text file.

 ******************************************************************************/

JError
JXFileDocument::WriteFile
	(
	const JCharacter*	fullName,
	const JBoolean		safetySave
	)
	const
{
	ofstream output(fullName);
	if (output.good())
		{
		WriteTextFile(output, safetySave);

		if (output.good())
			{
			return JNoError();
			}
		else
			{
			output.close();
			JRemoveFile(fullName);
			return WriteFailed();
			}
		}
	else
		{
		// use JFOpen() to get an error message

		FILE* file = NULL;
		JError err = JFOpen(fullName, "w", &file);
		if (file != NULL)
			{
			// This should never happen, but who knows??

			fclose(file);
			err = JAccessDenied();
			}
		return err;
		}
}

/******************************************************************************
 WriteTextFile (virtual protected)

	This must be overridden if WriteFile() is not overridden.

 ******************************************************************************/

void
JXFileDocument::WriteTextFile
	(
	ostream&		output,
	const JBoolean	safetySave
	)
	const
{
	assert( 0 /* The programmer forgot to override JXFileDocument::WriteTextFile() */ );
}

/******************************************************************************
 SafetySave (virtual)

	Save the data in a temporary file, in case the program crashes.
	No error reporting is allowed because one possibility is
	that the GUI itself has died.

 ******************************************************************************/

void
JXFileDocument::SafetySave
	(
	const JXDocumentManager::SafetySaveReason reason
	)
{
	const JBoolean quitting = JConvertToBoolean( reason != JXDocumentManager::kTimer );

	JString homeDir;
	if ((!itsSavedFlag || (quitting && itsAutosaveBeforeCloseFlag)) &&
		(quitting || itsNeedSafetySaveFlag) &&
		(itsWasOnDiskFlag || (quitting && JGetHomeDirectory(&homeDir))))
		{
		JString fullName;

		JBoolean copyPerms = kFalse;
		mode_t filePerms   = 0;
		if (itsWasOnDiskFlag)
			{
			fullName  = *itsFilePath + *itsFileName;
			copyPerms = (JGetPermissions(fullName, &filePerms)).OK();

			fullName = *itsFilePath;
			if (reason == JXDocumentManager::kAssertFired)
				{
				fullName += kAssertSavePrefix + *itsFileName + kAssertSaveSuffix;
				}
			else
				{
				fullName += kSafetySavePrefix + *itsFileName + kSafetySaveSuffix;
				}
			}
		else if (reason == JXDocumentManager::kAssertFired)
			{
			fullName = JGetUniqueDirEntryName(homeDir, kAssertUnsavedFilePrefix);
			}
		else
			{
			fullName = JGetUniqueDirEntryName(homeDir, kUnsavedFilePrefix);
			}

		const JError err = WriteFile(fullName, kTrue);
		if (err.OK())
			{
			if (copyPerms)
				{
				JSetPermissions(fullName, filePerms);
				}

			itsNeedSafetySaveFlag = kFalse;
			if (itsSafetySaveFileName == NULL)
				{
				itsSafetySaveFileName = new JString(fullName);
				assert( itsSafetySaveFileName != NULL );
				}
			else
				{
				*itsSafetySaveFileName = fullName;
				}
			}
		}
}

/******************************************************************************
 RemoveSafetySaveFile (private)

 ******************************************************************************/

void
JXFileDocument::RemoveSafetySaveFile()
{
	if (itsSafetySaveFileName != NULL)
		{
		JRemoveFile(*itsSafetySaveFileName);

		delete itsSafetySaveFileName;
		itsSafetySaveFileName = NULL;
		}

	itsNeedSafetySaveFlag = kFalse;
}

/******************************************************************************
 GetSafetySaveFileName

	Returns kTrue if the file has been safety saved.

 ******************************************************************************/

JBoolean
JXFileDocument::GetSafetySaveFileName
	(
	JString* fileName
	)
	const
{
	if (itsSafetySaveFileName != NULL)
		{
		*fileName = *itsSafetySaveFileName;
		return kTrue;
		}
	else
		{
		fileName->Clear();
		return kFalse;
		}
}

/******************************************************************************
 CheckForSafetySaveFiles (static)

	Checks for the existence of a safety save version with a later
	modification date.  If it exists, asks the user if it should be
	opened.  *filesToOpen contains the names of these files, if any.

 ******************************************************************************/

JBoolean
JXFileDocument::CheckForSafetySaveFiles
	(
	const JCharacter*	fullName,
	JPtrArray<JString>*	filesToOpen
	)
{
	filesToOpen->DeleteAll();

	JString path, name;
	JSplitPathAndName(fullName, &path, &name);

	time_t modTime, safetyTime, assertTime;
	if (!name.BeginsWith(kSafetySavePrefix) &&
		JGetModificationTime(fullName, &modTime) == kJNoError)
		{
		const JString safetyName = path + kSafetySavePrefix + name + kSafetySaveSuffix;
		const JBoolean safetyExists = JConvertToBoolean(
			JGetModificationTime(safetyName, &safetyTime) == kJNoError &&
			safetyTime > modTime);

		const JString assertName = path + kAssertSavePrefix + name + kAssertSaveSuffix;
		const JBoolean assertExists = JConvertToBoolean(
			JGetModificationTime(assertName, &assertTime) == kJNoError &&
			assertTime > modTime);

		JUserNotification* un = JGetUserNotification();
		JString* s;
		if (safetyExists && assertExists)
			{
			JString msg = "There are newer versions of \"";
			msg += name;
			msg += "\" that were safety saved when the X Server and this program "
					"crashed.\n\nDo you want to open these newer versions?";
			if (un->AskUserYes(msg))
				{
				s = new JString(safetyName);
				assert( s != NULL );
				filesToOpen->Append(s);

				s = new JString(assertName);
				assert( s != NULL );
				filesToOpen->Append(s);
				}
			}
		else if (safetyExists)
			{
			JString msg = "There is a newer version of \"";
			msg += name;
			msg += "\" that was safety saved when the X Server crashed.\n\n"
					"Do you want to open this newer version?";
			if (un->AskUserYes(msg))
				{
				s = new JString(safetyName);
				assert( s != NULL );
				filesToOpen->Append(s);
				}
			}
		else if (assertExists)
			{
			JString msg = "There is a newer version of \"";
			msg += name;
			msg += "\" that was safety saved when this program crashed.\n\n"
					"Do you want to open this newer version?";
			if (un->AskUserYes(msg))
				{
				s = new JString(assertName);
				assert( s != NULL );
				filesToOpen->Append(s);
				}
			}
		}

	return JNegate( filesToOpen->IsEmpty() );
}

/******************************************************************************
 DefaultCanReadASCIIFile (static protected)

	We provide this as a convenience for simple derived classes.
	It assumes that the front of the file is {signature, version} and that
	all older versions are readable.  It returns the actual file version
	so the caller can perform further checks if necessary.

 ******************************************************************************/

JXFileDocument::FileStatus
JXFileDocument::DefaultCanReadASCIIFile
	(
	istream&			input,
	const JCharacter*	fileSignature,
	const JFileVersion	latestFileVersion,
	JFileVersion*		actualFileVersion
	)
{
	const JSize offset = JTellg(input);

	const JString filePrefix = JRead(input, strlen(fileSignature));
	if (filePrefix != fileSignature)
		{
		JSeekg(input, offset);
		*actualFileVersion = 0;
		return kNotMyFile;
		}

	input >> *actualFileVersion;
	JSeekg(input, offset);

	if (*actualFileVersion <= latestFileVersion)
		{
		return kFileReadable;
		}
	else
		{
		return kNeedNewerVersion;
		}
}

/******************************************************************************
 AdjustWindowTitle (protected)

	We prepend spaces to the names of documents that don't need to be
	saved so fvwm will allocate enough width in the iconified state
	to have space for the asterisks when they appear later.

 ******************************************************************************/

void
JXFileDocument::AdjustWindowTitle()
{
	JXWindow* window = GetWindow();
	if (window != NULL)
		{
		JString title = *itsFileName;
		if (!itsSavedFlag)
			{
			title.Prepend("*** ");
			}
		else if (itsAllocateTitleSpaceFlag)
			{
			title.Prepend("     ");
			}
		window->SetTitle(title);
		}
}

/******************************************************************************
 Prompts

 ******************************************************************************/

void
JXFileDocument::SetSaveBeforeClosePrompt
	(
	const JCharacter* prompt
	)
{
	*itsSaveBeforeClosePrompt = prompt;
}

void
JXFileDocument::SetSaveNewFilePrompt
	(
	const JCharacter* prompt
	)
{
	*itsSaveNewFilePrompt = prompt;
}

void
JXFileDocument::SetOKToRevertPrompt
	(
	const JCharacter* prompt
	)
{
	*itsOKToRevertPrompt = prompt;
}

/******************************************************************************
 Cast to JXFileDocument*

	Not inline because they are virtual

 ******************************************************************************/

JXFileDocument*
JXFileDocument::CastToJXFileDocument()
{
	return this;
}

const JXFileDocument*
JXFileDocument::CastToJXFileDocument()
	const
{
	return this;
}
