/*+++*
 *  title:	InputlinePanel.m
 *  abstract:	An Inputline Panel in the style of NeXTSTEPs Alert Panel.
 *  author:	T.R.Hageman <Tom_Hageman@RnA.nl>
 *  created:	
 *  modified:	(see RCS Log at end)
 *  copyright:
 *
 *		Copyright (C) 1996 by Tom R. Hageman.
 *
 *	This software may be redistributed, used and modified without
 *	restriction, as long as this copyright notice is left intact,
 *	and modified versions of this software are clearly marked as such.
 *
 *	This software 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.
 *
 *  description:
 *	(see corresponding *.h file)
 *---*/

#import "InputlinePanel.h"


@implementation InputlinePanel

+ (const char *const *)resourcePaths
{
	static const char *const paths[] = {
		"~/Library/Resources",
		"/LocalLibrary/Resources",
		NULL
	};
	return paths;
}

static id theInputlinePanel;

+ new
{
	if (theInputlinePanel == nil)
	{
		const char *nibName = "InputlinePanel";
		NXBundle *bundle = [NXBundle mainBundle];
		char nibPath[MAXPATHLEN+1];

		if ([bundle getPath:nibPath forResource:nibName ofType:"nib"]) {
			[NXApp loadNibFile:nibPath owner:self];
			// This had better invoke -awakeFromNib, see below.
		}
		else {
			// Not found in ``main bundle'';
			// Try some semi-standard locations instead.
			const char *const *searchPaths = [self resourcePaths];
			const char *searchPath;

			while ((searchPath = *searchPaths++) != NULL) {
				if (searchPath[0] == '~') {
					sprintf(nibPath, "%s%s", NXHomeDirectory(),
						&searchPath[1]);
					searchPath = nibPath;
				}
				bundle = [[NXBundle alloc]
					  initForDirectory:searchPath];
				if (bundle == nil) continue;
				if ([bundle getPath:nibPath
				     forResource:nibName ofType:"nib"]) {
					[bundle free];
					// Found one!
					[NXApp loadNibFile:nibPath owner:self];
					break;
				}
				[bundle free];
			}
		}
	}
	return theInputlinePanel;
}

- awakeFromNib
{
	// Set shared instance pointer when unarchived from nib.
	theInputlinePanel = self;

	return self;
}

- free
{
	// Allow shared instance to be freed (and eventually reloaded.)
	if (self == theInputlinePanel) theInputlinePanel = nil;
	return [super free];
}

// Button actions.

- cancel:sender
{
	[NXApp stopModal:NX_CANCELTAG];
	return self;
}

- ok:sender
{
	if ([self makeFirstResponder:self])
	{
		[self endEditingFor:nil];
	}
	[NXApp stopModal:NX_OKTAG];
	return self;
}


// Private support methods

- (void)_adjustSizeOfControl:(Control *)control vertically:(BOOL)vertically
{
	NXRect oldFrame, newFrame;
	NXSize diff;

	[control getFrame:&oldFrame];
	if (vertically) {
		Cell *cell = [control cell];
		NXRect bounds;
		NXSize cellSize;

		[control getBounds:&bounds];
		[cell calcCellSize:&cellSize inRect:&bounds];
		// XXX This assumes no scaling (scale factor = 1):
		[control sizeTo:cellSize.width :cellSize.height];
	}
	else {
		// XXX This can ge way too wide if string is long enough.
		[control sizeToFit];
	}
	[control getFrame:&newFrame];
	diff.width  = (newFrame.size.width  - oldFrame.size.width);
	diff.height = (newFrame.size.height - oldFrame.size.height);
	if (diff.width < 0) {
		[control sizeBy:-diff.width :0];
		diff.width = 0;
	}
	if (diff.width ||diff.height) {
		Window *window = [control window];
		NXRect windowFrame;

		[window getFrame:&windowFrame];
		windowFrame.size.width += diff.width;
		windowFrame.size.height += diff.height;
		windowFrame.origin.x -= diff.width / 2;
		windowFrame.origin.y -= diff.height;
		[window placeWindow:&windowFrame];
	}
}

- (void)_centerAtScreen
{
	// Adjust frame for actual screen size.
	const NXSize designScreenSize = { 1120, 832 };
	const NXScreen *s = [NXApp mainScreen];
	NXRect f;
	NXPoint mid;

	if (s->screenBounds.size.width == designScreenSize.width &&
	    s->screenBounds.size.height == designScreenSize.height) {
		return;
	}

	[self getFrame:&f];
	mid.x = f.origin.x + f.size.width / 2;
	mid.y = f.origin.y + f.size.height / 2;

	// scale
	mid.x = mid.x * s->screenBounds.size.width / designScreenSize.width;
	mid.y = mid.y * s->screenBounds.size.height / designScreenSize.height;

	f.origin.x = mid.x - f.size.width / 2;
	f.origin.y = mid.y - f.size.height / 2;
	[self placeWindow:&f];
}

// Access methods.

- (void)setTitle:(const char *)aTitle
{
	[titleField setStringValue:aTitle];
}

- (const char *)title	{ return [titleField stringValue]; }

- (void)setPrompt:(const char *)aPrompt
{
	[promptField setStringValue:aPrompt];
}

- (const char *)prompt	{ return [promptField stringValue]; }

- (void)setInputline:(const char *)anInputline
{
	[inputField setStringValue:anInputline];
	[inputField selectText:self];
}

- (const char *)inputline	{ return [inputField stringValue]; }


- (int)runModal
{
	return [self runModalWithInputline:"" invisible:NO];
}

- (int)runModalWithInputline:(const char *)anInputline
	invisible:(BOOL)isInvisible;
{
	NXRect orgFrame;
	int result;
	float savedTextGray = 0;

	if (isInvisible) {
		savedTextGray = [inputField textGray];
		[inputField setTextGray:[inputField backgroundGray]];
	}

	[self setInputline:anInputline];

	// Dynamically adjust control (and window) sizes.
	[self getFrame:&orgFrame];
	[self _centerAtScreen];
	[self _adjustSizeOfControl:titleField vertically:NO];
	[self _adjustSizeOfControl:promptField vertically:YES];

	[self makeKeyAndOrderFront:self];
	result = [NXApp runModalFor:self];
	[self orderOut:self];

	// Restore original panel frame.
	[self placeWindow:&orgFrame];

	if (isInvisible) {
		[inputField setTextGray:savedTextGray];
	}
	return result;
}

@end // InputlinePanel

/*======================================================================
 * $Log: InputlinePanel.m,v $
 * Revision 1.3  1996/03/31 20:34:48  tom
 * fixed mail address;
 * (+new): avoid using -loadNibSection: since that prints a distracting error
 *  message if the nib is not found; locate it explicitly in main bundle,
 *  then -loadNibFile: instead.
 *
 * Revision 1.2  1996/03/28 23:15:33  tom
 * (-_adjustSizeOfControl:vertically:,-_centerAtScreen:): new private support
 *  methods;
 * (-setInputline:): select its text;
 * (-runModalWithInputline:): center and adjust size of panel before displaying
 *  it on-screen; restore original panel size afterwards.
 *
 * Revision 1.1  1996/03/27 23:03:31  tom
 * Initial revision
 *
 *======================================================================*/
