// InformerWindow.m
//
// Purpose :
//		Handles the "document" windows, one instance per window.
//
// Structure :
//		InformerWindow is a subclass of Object. It is the delegate of
//		its "document" window. It is also the owner of InformerWindow.nib.
//		It is the delegate of the Browser.
//		
//
// History :
//		9.20.1992 : Created by Max Tardiveau


#import "InformerWindow.h"


@implementation InformerWindow

/////////////////////////////////////////////////
// 

- init
{
NXRect	tmpRect;
//int		tmpInt;

[super init];

[NXApp loadNibSection:"InformerWindow.nib" owner:self];

[myInspectorView setVertScrollerRequired:NO];
[myInspectorView getBounds:&tmpRect];
[myInspectorView setDocCursor:NXArrow];
myEmptyView = [[EmptyView new] initFrame:&tmpRect];
[myInspectorView setDocView:myEmptyView];

[myBrowser setCellClass:[InformerCell class]];
[myBrowser useScrollButtons:YES];

[myBrowser loadColumnZero];

[myWindow makeKeyAndOrderFront:self];

updatePeriod = 0;
[updateText setEnabled:NO];

return self;
}

/////////////////////////////////////////////////
// Sets our dispatcher

- setDispatcher:sender
{
myDispatcher = sender;
return self;
}

/////////////////////////////////////////////////
// Target method for the Update TextItem

static void updateFunc(DPSTimedEntry teNumber, double now, char *userData)
{
id		theCell;
id		theObject;

theCell = (id)userData;
if ((theObject = [theCell updater]) != NIL)
	[theObject doUpdateForCell:theCell];
}

/////////////////////////////////////////////////
// Target method for the Update TextItem and Scroller

- setUpdate:sender
{
id		CurrentCell;

updatePeriod = [sender intValue];
[updateScroller setIntValue:updatePeriod];
[updateText setIntValue:updatePeriod];
if (updatePeriod <= 1)
	[updateTextSeconds setStringValue:"second"];
else
	[updateTextSeconds setStringValue:"seconds"];

if (myTimedEntry)
	{
	DPSRemoveTimedEntry(myTimedEntry);
	myTimedEntry = 0;
	}

CurrentCell = [[myBrowser matrixInColumn:[myBrowser selectedColumn]] 
															selectedCell];

if (updatePeriod && CurrentCell)
	myTimedEntry = DPSAddTimedEntry(updatePeriod,
		(DPSTimedEntryProc)updateFunc, CurrentCell, NX_BASETHRESHOLD);

return self;
}

////////////////////////////////////////////////////////////////////////
// Target method for the Help button
// This can only be called if a Help file exists for
// the current cell.

- helpClicked:sender
{
id 			CurrentCell;
char		tmpchar[256];
NXStream	*textStream;

if ([[myHelpText docView] textLength] == 0)	// Not yet loaded
	{
	CurrentCell = [[myBrowser matrixInColumn:
									[myBrowser selectedColumn]] selectedCell];
	sprintf(tmpchar, "%s/%s", [CurrentCell getDir], "HELP.rtf");
	textStream = NXMapFile(tmpchar, NX_READONLY);
	if (textStream == NIL)
		{
		[myDispatcher showError:"Error (2) opening HELP.rtf file."];
		return self;
		}
	[[myHelpText docView] readRichText:textStream];
	[[myHelpText docView] setSel:0 :0];
	[[myHelpText docView] scrollSelToVisible];
	NXCloseMemory(textStream, NX_FREEBUFFER);
	}

[myHelpPanel makeKeyAndOrderFront:self];

return self;
}

////////////////////////////////////////////////////////////////////////
// Called by modules to put up messages.

- addMessage:(char *)theMessage
{
[[messageScrollView docView] setSel:1000000 :1000000];
[[[messageScrollView docView] replaceSel:theMessage] scrollSelToVisible];
return self;
}

////////////////////////////////////////////////////////////////////////
// Target method for the Clear button. Clears out all messages.

- clearMessages:sender
{
[[messageScrollView docView] setText:""];
return self;
}

///////////////////////////////////////////////////////////////////////////
// This method is used by modules to run functions that do not work when
// called from a dynamically loaded module. Functions having this problem :
// PScountscreenlist(), PScountwindowlist(), PSostype() and probably others.

- (int *) runFunction:(int * (*)())theFunction
{
return theFunction();
}

///////////////////////////////////////////////////////////////////////////
// Target method for the browser.
//
// When the user clicks on a cell in the browser, it can either be a
// leaf or a branch cell. If it is a branch, then we put back the Text
// in the ScrollView.
// If it is a leaf, then we load the Main.o file in the .inform directory,
// first alone, and then with /lib/libsys_s.a if it fails. We then create
// an instance of the class, which returns its view. We finally put that
// view as the DocView of our ScrollView.

- cellClicked:sender
{
id 			CurrentCell;
id			theObject;
id			theView;
id			theClass;
char		tmpchar[256];
struct stat myStat;

CurrentCell = [[sender matrixInColumn:[sender selectedColumn]] selectedCell];

// If there is a help file, enable Help button, else hide help window
sprintf(tmpchar, "%s/%s", [CurrentCell getDir], "HELP.rtf");
if (stat(tmpchar, &myStat) == 0)
	[helpButton setEnabled:YES];
else
	[helpButton setEnabled:NO];

[myHelpPanel performClose:self];
[[myHelpText docView] setText:""];

if (myTimedEntry)
	{
	DPSRemoveTimedEntry(myTimedEntry);
	myTimedEntry = 0;
	}
[updateTextSeconds setStringValue:"second"];
[updateScroller setIntValue:0];
[updateText setIntValue:0];
[updateScroller setEnabled:NO];
[updateText setEnabled:NO];
updatePeriod = 0;

if ((theObject = [CurrentCell updater]) != NIL)
	{
	[myWindow disableFlushWindow]; 
	if ([theObject doUpdateForCell:CurrentCell])
		{
		[myInspectorView setDocView:[theObject mainView]];
		updatePeriod = [theObject updateInterval];
		[updateScroller setIntValue:updatePeriod];
		[updateText setIntValue:updatePeriod];
		if (updatePeriod <= 1)
			[updateTextSeconds setStringValue:"second"];
		else
			[updateTextSeconds setStringValue:"seconds"];
		if (updatePeriod)
			{
			[updateScroller setEnabled:YES];
			[updateText setEnabled:YES];
			}
		else
			{
			[updateScroller setEnabled:NO];
			[updateText setEnabled:NO];
			}
		if (updatePeriod)
			myTimedEntry = DPSAddTimedEntry(updatePeriod, 
				(DPSTimedEntryProc)updateFunc, CurrentCell, NX_BASETHRESHOLD);
		}
	else
		[myInspectorView setDocView:myEmptyView];
		
	[[myWindow reenableFlushWindow] flushWindow];
	return self;
	}

if ( ! [CurrentCell isLeaf])		// it's only a directory
	{
	[myInspectorView setDocView:myEmptyView];		
	return self;
	}

if ( ! [CurrentCell getMasterInstance])
	{
	theClass = [myDispatcher loadClassFromCell:CurrentCell];

	if (theClass)		// load was successful
		theObject = [theClass new];
	else
		{
		[self addMessage:"InformerWindow : could not create instance.\n"];
		return self;
		}
	
	theView = [theObject loadEverythingFrom:[CurrentCell getDir]];
	[theObject setMaster:self];
	updatePeriod = [theObject updateInterval];
	[updateScroller setIntValue:updatePeriod];
	[updateText setIntValue:updatePeriod];
	if (updatePeriod <= 1)
		[updateTextSeconds setStringValue:"second"];
	else
		[updateTextSeconds setStringValue:"seconds"];
	if (updatePeriod)
		{
		[updateScroller setEnabled:YES];
		[updateText setEnabled:YES];
		}
	else
		{
		[updateScroller setEnabled:NO];
		[updateText setEnabled:NO];
		}

	[theView removeFromSuperview];
	[myInspectorView setDocView:theView];
	}
else
	theObject = [CurrentCell getMasterInstance];

[myWindow disableFlushWindow];
if ([theObject doUpdateForCell:CurrentCell])
	{
	if (updatePeriod)
		myTimedEntry = DPSAddTimedEntry(updatePeriod,
			(DPSTimedEntryProc)updateFunc, CurrentCell, NX_BASETHRESHOLD);
	}
else
	[myInspectorView setDocView:myEmptyView];

[[myWindow reenableFlushWindow] flushWindow];
return self;
}

//////////////////////////////////////////////////////////////////////////////
//

- window
{
return myWindow;
}

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// Delegate method for the browser

- (int)browser:sender fillMatrix:matrix inColumn:(int)column
{
DIR				*myDirStream;
struct direct 	*myDir;
id 				CurrentCell;
id				tmpCell;
char			tmpChar[512];
char			tmpName[256];
char			*tmpcharptr;
char			*cellDir;
struct stat 	myStat;
//NXStream		*myStream;
int				isList;
id				updater;
id				theObject;
id				theClass;

if ( ! [[sender cellPrototype] isMemberOf:[InformerCell class]])
	return 0;

if (column == 0)		// First column
	{
	cellDir = baseDir;
	isList = NO;
	updater = NULL;
	[sender setTitle:"Informer" ofColumn:0];
	}
else		// Not the first column
	{
	CurrentCell=[[sender matrixInColumn:[sender selectedColumn]] selectedCell];
	cellDir = [CurrentCell getDir];
	isList = [CurrentCell isList];
	updater = [CurrentCell updater];
	}

if (updater)
	{
	[updater fillList:matrix fromCell:CurrentCell];
	}
else if (isList)			// Ask the new class to fill the matrix
	{
	if ( ! [CurrentCell getMasterInstance])
		{
		theClass = [myDispatcher loadClassFromCell:CurrentCell];
	
		if (theClass)	// load was successful
			{
			theObject = [theClass new];
			[theObject loadEverythingFrom:[CurrentCell getDir]];
			[theObject setMaster:self];
			[[theObject mainView] removeFromSuperview];
			}
		}
	else
		theObject = [CurrentCell getMasterInstance];
	
	[theObject fillList:matrix fromCell:CurrentCell];
	}
else		// for plain directories
	{
	myDirStream = opendir(cellDir);
	if (myDirStream == NIL)
		{
		[myDispatcher showError:"Could not find base directory."];
		[self addMessage:"InformerWindow : could not find base directory.\n"];
		return 0;
		}
	
	for (myDir = readdir(myDirStream); myDir != NULL;
									myDir = readdir(myDirStream))
		{
		if (myDir->d_name[0] == '.')		// Don't display "." and ".."
			continue;
		
		sprintf(tmpChar, "%s/%s", cellDir, myDir->d_name);
		stat(tmpChar, &myStat);
		if (myStat.st_mode & S_IFDIR)		// It is a directory
			{
			[matrix addRow];
			strncpy(tmpName, myDir->d_name, 511);	// The name for now
			tmpCell = [matrix cellAt:([matrix cellCount] -1) :0];
			if (strlen(myDir->d_name) > 5)			//  Check if .item dir
				{
				strncpy(tmpChar, myDir->d_name, 255);
				tmpcharptr = tmpChar;
				tmpcharptr += (strlen(tmpcharptr) - 5);
				if ( ! strncmp(tmpcharptr, ".item", 5))	// It is a .item
					{
					[tmpCell setLeaf:YES];
					*tmpcharptr = '\0';				// Remove .item ending
					strncpy(tmpName, tmpChar, 255);
					}
				else if ( ! strncmp(tmpcharptr, ".list", 5)) // It is a .list
					{
					[tmpCell setIsList:YES];
					*tmpcharptr = '\0';				// Remove .list ending
					strncpy(tmpName, tmpChar, 255);
					}
				}
			sprintf(tmpChar, "%s/%s", cellDir, myDir->d_name);
			[tmpCell setDir:tmpChar];
			[tmpCell setStringValue:tmpName];		// Give the cell its name
			}
		}
	}

return [matrix cellCount];
}

//////////////////////////////////////////////////////////////////////////////
// Delegate methods for the window
//
/////////////////////////////////////////////////
// Called when our window becomes the main window.

- windowDidBecomeMain:sender
{
[myDispatcher newFrontWindow:self];
return self;
}

/////////////////////////////////////////////////
// Called when our window becomes the key window.

- windowDidBecomeKey:sender
{
[myDispatcher newFrontWindow:self];
return self;
}

/////////////////////////////////////////////////
// Called when our window has stopped being the key window.

- windowDidResignKey:sender
{
[myDispatcher oldFrontWindow:self];
return self;
}

/////////////////////////////////////////////////
// Called when our window is going to close.

- windowWillClose:sender
{
[myDispatcher oldFrontWindow:self];
return self;
}

@end
