//**************************************************************************
//	You may freely use this code
//
//	Project	: SampleGTool - A simple text editor
//	File	: SampleGTool
//	Author	: Gerd Knops
//	Date	: 940504
//
//**************************************************************************
//
//	History:
//	940504 Creation of file
//
//**************************************************************************
//	<CLASSDESCRIPTION>
//	SampleGTool is a simple text editor, that serves as an example in
//	how to program a GTool. In order to keep it simple, some methods
//	have been implemented in a quite 'simplistic' manner.
//
//**************************************************************************
#import <appkit/appkit.h>
#import <GKappkit/GKappkit.h>
#import "SampleGTool.h"
//**************************************************************************
@implementation SampleGTool
//**************************************************************************
// Class methods
//**************************************************************************
+ initialize
{
	//
	// During initialize, we 'connect' our menu cells to our code.
	//
	if(self==[SampleGTool class])
	{
		[[self module]setupMenuItem:0 target:self
						selector:@selector(sampleGToolNew:)];
		[[self module]setupMenuItem:1 target:self
						selector:@selector(sampleGToolOpen:)];
	}
	
	return self;
}
+ sampleGToolNew:sender
{
	//
	// This method will opens an empty sampleGTool window.
	//
	[[SampleGTool alloc]init];  
	
	return self;
}
+ sampleGToolOpen:sender
{
	//
	// This method shows some use of the GKIOCtrl class. It shows,
	// how to run the open panel, with multiple selections enabled.
	//
	// It should be checked, wether one of the files is already
	// opened, we don't do it in order to keep it simple. 
	//
	const char 	*extensions[]={"rtf",NULL};	// only rtf files allowed
	const char	*fName;

	if(fName=[GKIOCtrl getOpenPathFor:extensions
					title:GKLocalString("Open File")
					type:"SampleGTool"
					flags:GK_PACKAGES|GK_SHOWFILE|GK_MULTIPLE])
	{
		//
		// If we wouldn't allow multiple selections, we could
		// just go ahead with a [[SampleGTool alloc]initFromFile:fName];
		//
		//  
		// load, multiple files allowed...  
		//  
		id 					openPanel;  
		const char			*directory;  
		const char *const	*filenames;  
		int 				i;  
		char 				path[MAXPATHLEN+1];  
		
		openPanel=[OpenPanel new];  
		directory=[openPanel directory];  
		filenames=[openPanel filenames];  
		i=0;  
		while(filenames[i])  
		{  
			strcpy(path,directory);  
			strcat(path,"/");  
			strcat(path,filenames[i++]);  
			
			[[SampleGTool alloc]initFromFile:path];  
		}  
	}
	
	return self;
}
+ (int)uniqueNumber
{
	//
	// This method returns a unique number
	// for untitled documents.
	//
	static int	number=0;
	
	number++;
	
	return number;
}
//**************************************************************************
// Factory methods and related methods
//**************************************************************************
- init
{
	//
	// init simply calls initFromFile: (the designated initializer) 
	// with NULL as argument.
	//
	return [self initFromFile:NULL];
}
- initFromFile:(const char *)theFile
{
	//
	// This is the designated initializer for the SampleGTool class.
	//
	[super init];
	
	//
	// Preset all instance variables
	//
	myWindow=nil;
	myText=nil;
	myPath=NULL;
	pathIsValid=NO;
	
	//
	// load, localize and initialize the nib
	//
	[[self module]loadNib:"SampleGTool" owner:self];
	[self localize:myWindow];

	[myText setDelegate:self];
	
	//
	// just in case we forgot this in IB
	//
	[myWindow setDelegate:self];
	
	//
	// Set/store the position the user moves the window to;
	// this should be enhanced to make more sense, when multiple
	// instances are present.
	//
	[myWindow setFrameAutosaveName:"HexEditorWindowPos"];
	[myWindow setFrameUsingName:"HexEditorWindowPos"];
	
	if(theFile)
	{
		//
		// There is a valid file name
		//
		myPath=NXCopyStringBuffer(theFile);
		pathIsValid=YES;
		[self loadFromFile];
		//
		// We should care about what happens, if loadFromFile failed...
		//
	}
	else
	{
		//
		// No valid filename
		//
		char	tmp[MAXPATHLEN+1];
		
		sprintf(tmp,"%s%i",GKLocalString("UNTITLED"),
								[SampleGTool uniqueNumber]);
		myPath=NXCopyStringBuffer(tmp);
		pathIsValid=NO;
	}
	
	[myWindow setTitleAsFilename:[GKIOCtrl relativePathFor:myPath]];
	//[myWindow setNextResponder:self];
	[myWindow makeKeyAndOrderFront:self];
	
	return self;
}
- free
{
	//
	// We have to free myPath.
	//
	if(myPath)
	{
		free(myPath);
		myPath=NULL;
	}
	
	return [super free];
}
//**************************************************************************
// responder methods
//**************************************************************************
- save:sender
{
	//
	// Save the data to the file pointed to by myPath. Returns nil
	// on error; self otherwise. Note, that if we can respond to
	// save:, GTools will automatically ask the user about saving
	// the windows contents, when GTools will be quitted.
	//
	NXStream *stream;
	
	if(!pathIsValid)
	{
		return [self saveAs:self];
	}
	
	stream=NXOpenMemory(NULL,0,NX_WRITEONLY);
	[myText writeRichText:stream];
	if(NXSaveToFile(stream,myPath))
	{
		[GKIOCtrl saveErrorFor:myPath];
		
		return nil;
	}
	NXClose(stream);
	[myWindow setDocEdited:NO];
	
	return self;
}
- saveAs:sender
{
	//
	// This method runs a save panel, sets myPath to the new path,
	// and the calls save:.
	//
	const char	*newName;

	if(newName=[GKIOCtrl getSavePathFor:"rtf"
					title:GKLocalString("Save As")
					type:"SampleGTool"
					flags:GK_PACKAGES|GK_SHOWFILE])
	{
		free(myPath);
		myPath=malloc(strlen(newName+1));
		strcpy(myPath,newName);
		pathIsValid=YES;
		
		[self save:self];
		[myWindow setTitleAsFilename:[GKIOCtrl relativePathFor:myPath]];
	}
	
    return self;
}
- revert:sender
{
	//
	// Ask the user, wether this is, what he really wants; if so,
	// reload from file.
	//
	if(NXRunAlertPanel(GKLocalString("Revert"),
						GKLocalString("Do you want to revert changes to %s?"),
						NULL,
						GKLocalString("Cancel"),
						NULL,
						myPath)==NX_ALERTDEFAULT)
	{
		[self loadFromFile];
	}
	
	return self;
}
//**************************************************************************
// Text delegate methods
//**************************************************************************
- textDidGetKeys:sender isEmpty:(BOOL)flag
{
	//
	// This gets called when the contents of myText were changed;
	// we reflect this in the windows close button.
	//
	[myWindow setDocEdited:YES];
	
	return self;
}
//**************************************************************************
// Window delegate methods
//**************************************************************************
- windowWillClose:sender
{
	//
	// If a modification was not saved yet, warn the user and
	// ask, what to do.
	//
	if([myWindow isDocEdited])
	{
		switch(NXRunAlertPanel(GKLocalString("Close Window"),
				GKLocalString("Discard changes to %s ?"),
				GKLocalString("Discard"),
				GKLocalString("Save"),
				(sender==[NXApp delegate])?NULL:GKLocalString("Cancel"),
				myPath))
		{
			case NX_ALERTDEFAULT:
				break;
			case NX_ALERTALTERNATE:
				[self save:self];
				break;
			case NX_ALERTOTHER:
				return nil;
				break;
		}
	}
	[NXApp delayedFree:self];
	
	return self;
}
//****************************************************************************
// Menu cell validation
//****************************************************************************
- (BOOL)validateCommand:menuCell
{
	//
	// This method ensures, that the 'Revert to Saved' menu cell will
	// only be enabled, if the key window was edited.
	//
    SEL action = [menuCell action];

    if (action==@selector(revert:))
	{
		if(![myWindow isDocEdited])
		{
			return NO;
		}
    }

    return YES;
}
//****************************************************************************
// Internal methods
//****************************************************************************
- loadFromFile
{
	//
	// This method loads the data from the path held in the instance
	// variable myPath. Returns nil on error, self otherwise.
	//
	NXStream *stream;
	
	stream=NXMapFile(myPath,NX_READONLY);
	if(!stream)
	{
		//
		// Some error occured.
		//
		[GKIOCtrl loadErrorFor:myPath];
		
		return nil;
	}
	[myText readRichText:stream];
	NXClose(stream);
	[myWindow setDocEdited:NO];
	
	return self;
}
@end
//***********************************************************************EOF