/***********************************************************************\
controller class for Convert RTF which converts between Mac and NeXT rtf formats.
Copyright (C) 1993 David John Burrowes

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 1, 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.

The author, David John Burrowes, can be reached at:
	davidjohn@kira.net.netcom.com
	David John Burrowes
	1926 Ivy #10
	San Mateo, CA 94403-1367
\***********************************************************************/

/*
====================================================================
	This is $Revision: 1.8 $ of this file
	It was last modified by $Author: death $ on $Date: 93/04/04 23:28:27 $
Note that this file was created while using the New Century Schoolbook Roman typeface.  You may find that some things line up strangely if you don't use that family.
$Log:	rtfController.m,v $
Revision 1.8  93/04/04  23:28:27  death
Sun Apr  4 23:28:27 PDT 1993

Revision 1.7  93/02/21  12:00:05  death
Sun Feb 21 12:00:05 PST 1993

Revision 1.6  93/01/10  08:27:28  death
Sun Jan 10 08:27:28 PST 1993

Revision 1.5  93/01/02  23:41:36  death
Sat Jan  2 23:41:36 PST 1993

Revision 1.4  93/01/02  13:39:06  death
Sat Jan  2 13:39:06 PST 1993

Revision 1.3  92/12/25  16:26:51  death
Fri Dec 25 16:26:51 PST 1992

Revision 1.2  92/12/21  07:01:18  death
Mon Dec 21 07:01:18 PST 1992

Revision 1.1  92/12/19  08:19:11  death
Sat Dec 19 08:19:10 PST 1992

 ====================================================================
 */
#import "rtfController.h"
#import "rtfFile.h"
#import "rtfConverter.h"
#import <appkit/Matrix.h>	// For matrix stuff for preferences setting
#import <libc.h>			// for mkdir  and rmdir
#import	<string.h>		// for strrchr
#import <appkit/Listener.h>		// for the drag-and-drop of files facility
#import <appkit/Speaker.h>		// ibid
#import <appkit/Application.h>		// To get NXApp (I thought that is what defaults was...)
#import <errno.h>				// for errno.

#import	"djbflag.h"

@implementation	rtfController


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		init
//	Parameters:	none
//	Returns:		self
//	Stores:		none
//	Description:
//		This overrides the defalut init method.  and sets up some strings used during
//		some of the user interaction. time. We also create the pict converter instance
//		needed later.
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- init
{
	static NXDefaultsVector theDefaults =
	{
		{CONVERTTYPE, "YES"},
		{REMOVEUNDERLINE, "NO"},
		{CONVERTTEXT, "ALL"},
		{CONVERTQUOTES, "NO"},
		{CONVERTPICTS, "YES"},
		{DELETEPICTS, "YES"},
		{NULL, NULL}
	};
	CString	tempPref;

	[super init];

	DefaultsOwner = "RTFConverter";

	NXRegisterDefaults(DefaultsOwner, theDefaults);
	
	convertMacRTF = [self   GetBoolPref: CONVERTTYPE];
	StripFirstUL0 = [self   GetBoolPref: REMOVEUNDERLINE];
	//
	//	93.02.15	djb	Changed from a boolean(yes/no) to a string parameter to support
	//				doing no conversion whatsoever.  Checks for yes and no for
	//				backward compatability. =)
	//
	tempPref = [self   GetPref: CONVERTTEXT];
	if ((strcmp(tempPref, "ALL") == 0) || (strcmp(tempPref,"YES") == 0))
		ConvertAllFonts = ConvertAll;
	else if ((strcmp(tempPref, "STANDARD") == 0)|| (strcmp(tempPref,"NO") == 0))
		ConvertAllFonts = ConvertStandard;
	else if (strcmp(tempPref, "NONE") == 0)
		ConvertAllFonts = ConvertNone;
	FreeCString(tempPref);
	ConvertSingleQuotes = [self   GetBoolPref: CONVERTQUOTES];
	ConvertPictData = [self   GetBoolPref: CONVERTPICTS];
	DeletePictData = [self   GetBoolPref: DELETEPICTS];

	if (convertMacRTF == YES)
	{
		ConversionString = "Converting Mac rtf to NeXT rtf";
		SourcePrompt = "Mac file:";
		DestPrompt = "NeXT file:";
		if (ForDavidJohnBurrowesOnly == 1)
			DestExtension = ".mac.rtf";
		else
			DestExtension = ".next.rtf";
	}
	else
	{
		ConversionString = "Converting NeXT rtf to Mac rtf";
		SourcePrompt = "NeXT file:";
		DestPrompt = "Mac file:";
		if (ForDavidJohnBurrowesOnly == 1)
			DestExtension = ".next.rtf";
		else
			DestExtension = ".mac.rtf";
	}
	MenuOpen = NO;

	converterInst = [[rtfConverter   alloc] init];
	return self;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		free
//	Parameters:	none
//	Returns:		?
//	Stores:		none
//	Description:
//		This cleans up before being deallocated.  (derezed, in tron terms, I suppose).
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- free
{
	if (converterInst != NullInstance)
		[converterInst  free];
	
	return [super free];
}

//
//	Added this because the free method is apparently not being called, and we
//	need to kill the converter instance so it can kill the pict converter (so much for
//	modularity, eh?  thank you NeXT/Interface Builder  (or maybe my stupidity)
//
- appWillTerminate: sender
{
	if (converterInst != NullInstance)
		[converterInst  free];
	converterInst = NullInstance;

	return [super  appWillTerminate: sender];
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		displayPreferences: 
//	Parameters:	the object that called us
//	Returns:		self
//	Stores:		none
//		This is called whenever we need to bring the preferences panel onto the screen.
//		Using the value in our several preferences variables, we set the buttons in
//		the preferences panel to reflect the current values, and then shows the panel.
//	Bugs:
//		As pointed out elsewhere, we don't really need to do this after the first time,
//		since the buttons keep their own state.  So my frakework needs revising!!
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-displayPreferences: target
{
	//
	//	Get the default preference values, and compare judge what buttons to
	//	highlight for the user.
	//
	if (convertMacRTF == YES)
		[MacSourceButton  selectCellWithTag: 1];
	else
		[MacSourceButton  selectCellWithTag: 0];

	if (StripFirstUL0 == YES)
		[RemoveUnderlineButton  setIntValue: 1];
	else
		[RemoveUnderlineButton  setIntValue: 0];
	//
	//	93.02.15	djb	Added support for three types of conversion, rather than 2
	//
	if (ConvertAllFonts == ConvertAll)
		[ConvertAllFontsButton  selectCellWithTag: 0];
	else 	if (ConvertAllFonts == ConvertStandard)
		[ConvertAllFontsButton  selectCellWithTag: 1];
	else
		[ConvertAllFontsButton  selectCellWithTag: 2];

	if (ConvertSingleQuotes == YES)
		[ConvertSingleQuotesButton  selectCellWithTag: 1];
	else
		[ConvertSingleQuotesButton  selectCellWithTag: 0];

	if (ConvertPictData == YES)
		[ConvertPictButton  selectCellWithTag: 0];
	else
		[ConvertPictButton  selectCellWithTag: 1];

	if (DeletePictData == YES)
		[DeletePictDataButton  selectCellWithTag: 0];
	else
		[DeletePictDataButton  selectCellWithTag: 1];

	[prefPanel	makeKeyAndOrderFront:self];
	return self;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		ChangeConvertSource: 
//	Parameters:	the object that called us
//	Returns:		self;
//	Description:
//		Specifies whether the source is a Mac or a NeXT rtf file.  This is relevant when
//		dealing with drag-and-drop or double clicked files where it is otherwise ambiguous.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-ChangeConvertSource: sender
{
	if ( [[sender selectedCell] tag] == 1)
	{
		convertMacRTF = YES;
		ConversionString = "Converting Mac rtf to NeXT rtf";
		SourcePrompt = "Mac file:";
		DestPrompt = "NeXT file:";
		if (ForDavidJohnBurrowesOnly == 1)
			DestExtension = ".mac.rtf";
		else
			DestExtension = ".next.rtf";
	}
	else
	{
		convertMacRTF = NO;
		ConversionString = "Converting NeXT rtf to Mac rtf";
		SourcePrompt = "NeXT file:";
		DestPrompt = "Mac file:";
		if (ForDavidJohnBurrowesOnly == 1)
			DestExtension = ".next.rtf";
		else
			DestExtension = ".mac.rtf";
	}

	[self   SetBoolPref: CONVERTTYPE To: convertMacRTF];

	return self;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		SetUnderlineRemoval: 
//	Parameters:	the object that called us
//	Returns:		self
//	Description:
//		Sets the boolean preference that indicates whether or not we should remove
//		the first \ul0 in a NeXT rtf file.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- SetUnderlineRemoval: sender
{
	if (  [sender intValue] == 1)
		StripFirstUL0 = YES;
	else
		StripFirstUL0 = NO;

	[self   SetBoolPref: REMOVEUNDERLINE To: StripFirstUL0];

	return self;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		SetFontConversion: 
//	Parameters:	the object that called us
//	Returns:		self
//	Description:
//		Sets the preference that indicates whether or not we should convert
//		text in all fonts, or in the standard fonts, or in none at all.
//	History
//		93.02.15	djb	Added suport for three types of conversion, rather than two.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- SetFontConversion: sender
{
	if (  [[sender selectedCell] tag]  == 0)
	{
		ConvertAllFonts = ConvertAll;
		[self   SetPref: CONVERTTEXT To: "ALL"];
	}
	else if (  [[sender selectedCell] tag]  == 1)
	{
		ConvertAllFonts = ConvertStandard;
		[self   SetPref: CONVERTTEXT To: "STANDARD"];
	}
	else
	{
		ConvertAllFonts = ConvertNone;
		[self   SetPref: CONVERTTEXT To: "NONE"];
	}

	return self;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		SetSingleQuoteChange: 
//	Parameters:	the object that called us
//	Returns:		self
//	Description:
//		Sets the boolean preference that indicates whether or not we should produce
//		MacStyle single quotes
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- SetSingleQuoteChange: sender
{
	if (  [[sender selectedCell] tag]  == 1)
		ConvertSingleQuotes = YES;
	else
		ConvertSingleQuotes = NO;

	[self   SetBoolPref: CONVERTQUOTES To: ConvertSingleQuotes];
	return self;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		SetPictDataDeletion: 
//	Parameters:	the object that called us
//	Returns:		self
//	Description:
//		Sets the boolean preference that indicates what we should do with PICT data
//		found in the rtf file.  If the value is YES, then we wish to delete the data entirely
//		If it is NO, then we store the pict data in another file
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- SetPictDataDeletion: sender
{
	if (  [[sender selectedCell] tag]  == 0)
		DeletePictData = YES;
	else
		DeletePictData = NO;

	[self   SetBoolPref: DELETEPICTS To: DeletePictData];
	return self;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		SetPictConversion: 
//	Parameters:	the object that called us
//	Returns:		self
//	Description:
//		Sets the boolean preference that indicates whether we should try to do a
//		conversion of any pictures we find in the rtf file.  If the value is YES, then
//		we will convert the pictures, otherwise we won't.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- SetPictConversion: sender
{
	if ( [[sender selectedCell] tag]  == 0)
		ConvertPictData = YES;
	else
		ConvertPictData = NO;

	[self   SetBoolPref: CONVERTPICTS To: ConvertPictData];
	return self;
}



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		PrepareForNeXTConversion:
//	Parameters:	the object that called us
//	Returns:		self
//	Description:
//		If the user chooses to convert a NeXT file via the menus, this routine is
//		called first, so we can set up the right conversion options.
//		Note that we store ptrs to all the strings used by the user interface stuff,
//		and substitute ones for this routine, and then restore later.  This is because
//		the default strings are all about what happens when one drags and drops
//		files, wheras this is when someone chooses a menu item.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- PrepareForNeXTConversion: sender
{
	CString	tempString = ConversionString;
	CString	sourceString = SourcePrompt;
	CString	destString = DestPrompt;
	CString	extenString = DestExtension;

	ConversionString = "Converting NeXT rtf to Mac rtf";
	SourcePrompt = "NeXT file:";
	DestPrompt = "Mac file:";
	if (ForDavidJohnBurrowesOnly == 1)
		DestExtension = ".next.rtf";
	else
		DestExtension = ".mac.rtf";

	[converterInst	SetConversionDirection: NO];
	MenuOpen = YES;
	[self   PrepareForConversion: self];

	ConversionString = tempString;
	SourcePrompt = sourceString;
	DestPrompt = destString;
	DestExtension = extenString;
	return self;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		PrepareForMacConversion: 
//	Parameters:	the object that called us
//	Returns:		self
//	Description:
//		If the user chooses to convert a Mac file via the menus, this routine is
//		called first, so we can set up the right conversion options.
//		Note that we store ptrs to all the strings used by the user interface stuff,
//		and substitute ones for this routine, and then restore later.  This is because
//		the default strings are all about what happens when one drags and drops
//		files, wheras this is when someone chooses a menu item.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- PrepareForMacConversion: sender
{
	CString	tempString = ConversionString;
	CString	sourceString = SourcePrompt;
	CString	destString = DestPrompt;
	CString	extenString = DestExtension;

	ConversionString = "Converting Mac rtf to NeXT rtf";
	SourcePrompt = "Mac file:";
	DestPrompt = "NeXT file:";
	if (ForDavidJohnBurrowesOnly == 1)
		DestExtension = ".mac.rtf";
	else
		DestExtension = ".next.rtf";
	
	[converterInst	SetConversionDirection: YES];
	MenuOpen = YES;
	[self   PrepareForConversion: self];
	
	ConversionString = tempString;
	SourcePrompt = sourceString;
	DestPrompt = destString;
	DestExtension = extenString;
	return self;
}



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		openSourceFile: 
//	Parameters:	the name of the file we are to open
//	Returns:		the new file object
//	Stores:		error
//	Description:
//		This simply attempts to open the source file.  If we are successfull, we
//		return it to the caller, otherwise we return an error.
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- openSourceFile: (roCString) theFile
{
	Instance	fileInstance;
	[self   ResetResults];
	
	fileInstance = [[rtfFile alloc] initAndUse: theFile];
	if ([fileInstance   GetErrorCode] == ERR_OK)
		[fileInstance   OpenExistingFor: FILE_READ];
	if ([fileInstance   GetErrorCode] != ERR_OK)
	{
		[self   StoreErrorCode: ERR_CANTOPEN
			AndText: "Unable to open that file."];
		[fileInstance   free];
		fileInstance = NullInstance;
	}
	return fileInstance;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		openDestFile: 
//	Parameters:	the name of the file we are to open
//	Returns:		the new file object
//	Stores:		error
//	Description:
//		This simply attempts to open the destination file.  If we are successfull, we
//		return it to the caller, otherwise we return an error.
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- openDestFile: (roCString) theFile
{
	Instance	fileInstance;
	[self   ResetResults];
	
	fileInstance = [[rtfFile alloc] initAndUse: theFile];
	if ([fileInstance   GetErrorCode] == ERR_OK)
		[fileInstance   ClearAndOpenFor: FILE_WRITE];
	if ([fileInstance   GetErrorCode] != ERR_OK)
	{
		[self   StoreErrorCode: ERR_CREATEFAILED
			AndText: "We were not able to create and/or open that file"];
		[fileInstance   free];
		fileInstance = NullInstance;
	}
	return fileInstance;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		ConvertFrom:To: 
//	Parameters:
//		The file to be converted from
//		the file to be converted to
//	Returns:		self
//	Stores:		error
//	Description:
//		This asks the super class to do any initalizing it needs to, and then goes
//		ahead and asks the converter object to go at it.
//	Bugs
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ConvertFrom: sourceFile To: destinationFile
{
	[self   ResetResults];
	[super   ConvertFrom: sourceFile To: destinationFile];
	//
	//	If we were started via a menu selection, then the convert
	//	direction has already been set.  Otherwise, we need to set it based on
	//	the default stored in convertMacRTF
	//
	if (MenuOpen == NO)
		[converterInst	SetConversionDirection: convertMacRTF];
	[converterInst	RemoveFirstNeXTUnderline: StripFirstUL0];
	[converterInst	SetTextConversion: ConvertAllFonts];
	[converterInst	ConvertTheSingleQuotes: ConvertSingleQuotes];
	[converterInst	SetPictConversion: ConvertPictData
					AndDataRemoval: DeletePictData];

	[MacConvertCommand	setEnabled: NO];
	[NeXTConvertCommand	setEnabled: NO];

	destFileHolder = destinationFile;
	[converterInst	ConvertFrom: sourceFile To: destinationFile];

	[MacConvertCommand	setEnabled: YES];
	[NeXTConvertCommand	setEnabled: YES];
	MenuOpen = NO;

	if ( [converterInst   GetErrorCode] < ERR_OK)
		[self   StoreErrorCode:  [converterInst   GetErrorCode]
			AndText:  [converterInst   GetErrorText] ];
	else
		[self   StoreErrorCode: ERR_OK
			AndText: "Converted Successfully" ];
	return destFileHolder;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		MakeDestAnRTFD
//	Parameters:	none
//	Returns:		self
//	Stores:		error
//	Description:
//		This semi-hack routine allows the converter to call us and ask that the destination
//		file be converted into being an rtfd instead of an rtf file.  This involves closeing
//		the rtf file earlier allocated, and creating a directory and subfile for the rtfd.
//		It also involves using a definite hack by way of destFileHolder, which allows
//		us to implicitly change the value that the above routine will return.
//	Bugs
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- MakeDestAnRTFD
{
	id	newFile;
	CString	buffer	= NewCString(1024);
	CString			oldPathName;
	CString	rtfdName;
	CString	pathname;
	Integer	result;

	oldPathName = [destFileHolder   GetPathname];
	[destFileHolder   CloseAndDelete];
	//
	//	Create an RTFD folder and file by
	//		1) getting the name for the rtfd folder (the rtf name + d)
	//		2) Creating a directory with this name
	//		3) Create a TXT.rtf file in said directory
	//
	rtfdName = NewCString(strlen(oldPathName) + 1);
	strcat(rtfdName, oldPathName);
	strcat(rtfdName, "d");
	//
	//		2) Creating a directory with the name of the requested font
	//
	errno = 0;
	result = mkdir(rtfdName, 0700 | 0070 | 0007); // strange perms
	if (result != 0)
	{
		sprintf(buffer,  "The rtfd folder could not be created. (mkdir error: %d)", errno);
		[self   StoreErrorCode: ERR_CREATEFAILED  AndText:buffer];
		rmdir(rtfdName);
		newFile = NullInstance;
	}
	else
	{
		//
		//	Allocate space for path to font folder, plus font or font.afm name
		//
		pathname = NewCString(strlen(rtfdName) + 1 +7);
		strcpy(pathname, rtfdName);
		strcat(pathname, "/TXT.rtf");
		//
		//		3) Create a font file in said directory
		//
		newFile =  [[rtfFile alloc] initAndUse: pathname];
		if ([newFile GetErrorCode] == ERR_OK)
			[newFile CreateAndOpenFor: FILE_WRITE];
		if ([newFile GetErrorCode] != ERR_OK)
		{
			[self   StoreErrorCode: ERR_CREATEFAILED
				AndText: "We failed to create the rtf file"];
			newFile = NullInstance;
			rmdir(rtfdName);
		}
		FreeCString(pathname);
	}
	FreeCString(oldPathName);
	FreeCString(rtfdName);
	FreeCString(buffer);
	destFileHolder = newFile;
	destIsDead = YES;
	return newFile;
}




@end