
/*
	Copyright 1993  Jeremy Slade.

	You are free to use all or any parts of the Locus project
	however you wish, just give credit where credit is due.
	The author (Jeremy Slade) shall not be held responsible
	for any damages that result out of use or misuse of any
	part of this project.

*/

/*
	Project: Locus
	
	Class: GroupInsPane
	
	Description: See GroupInsPane.h
		
	Original Author: Jeremy Slade
	
	Revision History:
		Created
			V.101	JGS Mon Feb  8 22:07:12 GMT-0700 1993
			
*/

#import "GroupInsPane.h"

#import "DynamicItemSpec.h"
#import "DynamicItems.h"
#import "Folder.h"
#import "Globals.h"
#import "Group.h"
#import "SendAction.h"
#import "SwapView.h"


// Group Inspector sub-modes ---------------------------------
#define DISPLAYOPTS_MODE	0
#define ALLOWEDTYPES_MODE	1
#define DYNAMICITEMS_MODE	2
char *subModeTitles[] = {
	"Display Options", "Allowed Types", "Dynamic Items",
	NULL
 };


@implementation GroupInsPane


// -------------------------------------------------------------------------
//   Creating, Initializing
// -------------------------------------------------------------------------


+ initialize
{
	[self setVersion:GroupInsPane_VERSION];
	return ( self );
}



- initContent:(const NXRect *)contentRect
	style:(int)aStyle
	backing:(int)bufferingType
	buttonMask:(int)mask
	defer:(BOOL)flag
{
	[super initContent:contentRect
		style:aStyle
		backing:bufferingType
		buttonMask:mask
		defer:flag];
	
	[self setDisplayOptsMode:self];
		
	return ( self );
}



- awakeFromNib
/*
	Perform final initializations
*/
{
	[dynamicBrowser setDoubleAction:@selector(modifyDynamicItemSpec:)];
	return (self );
}



- free
{
	displayOptsPane = [displayOptsPane free];
	allowedTypesPane = [allowedTypesPane free];
	dynamicItemsPane = [dynamicItemsPane free];
	
	return ( [super free] );
}



// -------------------------------------------------------------------------
//   General group stuff
// -------------------------------------------------------------------------


- (BOOL)canInspect:anObject
{
	return ( [anObject isKindOf:[Group class]] );
}


- showCurrent:sender
/*
	Show the current attributes/settings of the target in the inspector.
	
	NOTE: When setting the contents of a TextField, it is important to set it's text delegate to nil before setting, that reset its TextDelegate to self afterwards.  This prevents -textDidChange: messages from being sent when we are setting the contents of the field.
*/
{
	DrawInfo drawInfo;
	char s[2] = " ";
	
	[[swapView window] disableFlushWindow];
	
	[nameField setTextDelegate:nil];
	[nameField setStringValue:[target groupName]];
	[nameField setTextDelegate:self];
	
	if ( [target keyEquivalent] ) s[0] = [target keyEquivalent];
		else s[0] = '\0';
	[keyField setTextDelegate:nil];
	[keyField setStringValue:s];
	[keyField setTextDelegate:self];
	
	[sortSwitch setState:[target doesSortItems]];
	
	[pathField setTextDelegate:nil];
	[pathField setStringValue:[target defaultPath]];
	[pathField setTextDelegate:self];
	
	[target getDrawInfo:&drawInfo];
	[drawModeCover setTitle:ICModeTitles[drawInfo.mode]];

	[subModeCover setTitle:subModeTitles[subMode]];
	[swapView swap];
	switch ( subMode ) {
	
		case DISPLAYOPTS_MODE:
			[dispOptActualImage setState:drawInfo.actualImage];
			switch ( drawInfo.mode ) {
				case IC_LARGE_BROWSE:
					 [[dispOptFormatCover setEnabled:YES]
					 	setTitle: ICFormatTitles[drawInfo.lb_nameMode]];
					[[dispOptShowPath setState:0] setEnabled:NO];
					[[dispOptTriDots setEnabled:YES]
						setState:drawInfo.lb_tridots];
					[[dispOptShowInfo1 setEnabled:YES]
						setState:drawInfo.lb_info1];
					//[[dispOptShowInfo2 setEnabled:YES]
					//	setState:drawInfo.lb_info2];
					[[dispOptSmallIcons setEnabled:NO] setState:0];
					break;
				case IC_SMALL_BROWSE:
					 [[dispOptFormatCover setEnabled:YES]
					 	setTitle: ICFormatTitles[drawInfo.sb_nameMode]];
					[[dispOptShowPath setState:0] setEnabled:NO];
					[[dispOptTriDots setState:0] setEnabled:NO];
					[[dispOptShowInfo1 setState:0] setEnabled:NO];
					//[[dispOptShowInfo2 setState:0] setEnabled:NO];
					[[dispOptSmallIcons setEnabled:YES]
						setState:drawInfo.sb_icon];
					break;
				case IC_LARGE_ICON:
					[[dispOptFormatCover setEnabled:NO]
						setTitle:ICFormatTitles[IC_FILEONLY]];
					[[dispOptShowPath setEnabled:NO]
						setState:drawInfo.li_showName];
					[[dispOptTriDots setState:0] setEnabled:NO];
					[[dispOptShowInfo1 setState:0] setEnabled:NO];
					//[[dispOptShowInfo2 setState:0] setEnabled:NO];
					[[dispOptSmallIcons setEnabled:NO] setState:0];
					break;
				case IC_SMALL_ICON:
					[[dispOptFormatCover setEnabled:NO]
						setTitle:ICFormatTitles[IC_FILEONLY]];
					[[dispOptShowPath setEnabled:NO]
						setState:drawInfo.si_showName];
					[[dispOptTriDots setState:0] setEnabled:NO];
					[[dispOptShowInfo1 setState:0] setEnabled:NO];
					//[[dispOptShowInfo2 setState:0] setEnabled:NO];
					[[dispOptSmallIcons setEnabled:NO] setState:0];
					break;
				case IC_DRAW_UNKNOWN:
				default:
					[[dispOptFormatCover setEnabled:NO]
						setTitle:ICFormatTitles[IC_NAME_UNKNOWN]];
					[[dispOptTriDots setState:0] setEnabled:NO];
					[[dispOptShowInfo1 setState:0] setEnabled:NO];
					//[[dispOptShowInfo2 setState:0] setEnabled:NO];
					[[dispOptSmallIcons setEnabled:NO] setState:0];
					break;
			}
			break;
			
		case ALLOWEDTYPES_MODE:
			[self loadAllowedTypes:self];
			[allowedRestrictSwitch setState:![target doesRestrictTypes]];
			[allowedRemoveButton setEnabled:NO];
			[allowedField setTextDelegate:nil];
			[allowedField setStringValue:""];
			[allowedField setTextDelegate:self];
			[allowedAddButton setEnabled:NO];
			break;
			
		case DYNAMICITEMS_MODE:
			[self loadDynamicItemSpecs:self];
			[dynamicField setTextDelegate:nil];
			[dynamicField setStringValue:""];
			[dynamicField setTextDelegate:self];
			[dynamicDisableButton setEnabled:NO];
			[dynamicEnableButton setEnabled:NO];
			[dynamicRemoveButton setEnabled:NO];
			[dynamicAddButton setEnabled:NO];
			[dynamicIntervalField setIntValue:[target updateInterval] / 60];
			[dynamicIntervalSlider setIntValue:[target updateInterval] / 60];
			break;
			
	}

	[[[swapView window] reenableFlushWindow] flushWindow];
	return ( [super showCurrent:sender] );
}



- selectNameField:sender
/*
	Start editing in the nameField, to immediately be able to set the name of a group.
*/
{
	[nameField selectText:self];
	return ( self );
}



- changeGroupName:sender
/*
	Called by nameField when editing on the field finishes
*/
{
	if ( !strlen ( [nameField stringValue] ) ) {
		[nameField selectText:self];
		return ( self );
	}
	
	if ( ![[target folder] renameGroup:target to:[nameField stringValue]] ) {
		[nameField selectText:self];
		return ( self );
	}
	
	return ( self );
}



- changeGroupKey:sender
/*
	Called by keyField when editing on the field finishes
*/
{
	char str[2];
	int i, count;
	BOOL isInUse = NO;
	id	folder, group, menu;
	
	strncpy ( str, [keyField stringValue], 1 );
	str[1] = '\0';
	
	if ( !strlen ( str ) ) {	
		// Set to no key equivalent
		isInUse = NO;
	} else {
	
		if ( !isGoodKeyEq ( str[0] ) ) { // Not a valid key equivalent
			[keyField selectText:self];
			return ( self );
		}
		
		// Make sure the key is not in use my another group in the same folder
		folder = [target folder];
		count = [folder count];
		for ( i=0; i<count; i++ ) {
			if ( (group = [folder objectAt:i]) != target &&
				[group keyEquivalent] == (unsigned short)str[0] ) {
					// Key already in use
					isInUse = YES;
					break;
			}
		}
	}
	
	if ( !isInUse ) {
		[target setKeyEquivalent:str[0]];
		i = [(menu = [[target folder] groupMenu])
			indexOfItem:[target groupName]];
		[[[[menu itemList] cellList] objectAt:i] setKeyEquivalent:str[0]];
	}
	
	return ( self );
}



- changeGroupPath:sender
/*
	Called by pathField when editing in the field finishes
*/
{
	if ( ![target setDefaultPath:[pathField stringValue]] ) {
		[pathField selectText:self];
		return ( self );
	}
	
	return ( self );
}



- chooseGroupPath:sender
/*
	This method is the action of the choosePathButton. Pops up an OpenPanel to choose the group's default path.  The OpenPanel forces them to choose a directory by setting the required type to something that (most likely) will not ever be a valid file type.
*/
{
	id	openPanel = [OpenPanel new];
	
	[[openPanel setTitle:"Choose Default Path"] setPrompt:"Path:"];
	[[openPanel allowMultipleFiles:NO] chooseDirectories:YES];

	if ( [openPanel runModalForDirectory:[target defaultPath] file:NULL] ) {
		// Ask for filename rather than directory, because -directory doesn't
		// return last selected column
		[target setDefaultPath:[openPanel filename]];
		[self showCurrent:self];
	}
	
	return ( self );
}



- changeGroupSort:sender
{
	[target setSortItems:[sortSwitch state]];
	return ( self );
}



- changeDrawToLargeBrowse:sender
{
	[target setDrawMode:IC_LARGE_BROWSE];
	[self showCurrent:self];
	return ( self );
}



- changeDrawToSmallBrowser:sender
{
	[target setDrawMode:IC_SMALL_BROWSE];
	[self showCurrent:self];
	return ( self );
}



- changeDrawToLargeIcon:sender
{
	[target setDrawMode:IC_LARGE_ICON];
	[self showCurrent:self];
	return ( self );
}



- changeDrawToSmallIcon:sender
{
	[target setDrawMode:IC_SMALL_ICON];
	[self showCurrent:self];
	return ( self );
}



// -------------------------------------------------------------------------
//   Setting the sub-mode
// -------------------------------------------------------------------------


- setDisplayOptsMode:sender
{
	subMode = DISPLAYOPTS_MODE;
	[self showCurrent:self];
	return ( self );
}



- setAllowedTypesMode:sender
{
	subMode = ALLOWEDTYPES_MODE;
	[self showCurrent:self];
	return ( self );
}



- setDynamicItemsMode:sender
{
	subMode = DYNAMICITEMS_MODE;
	[self showCurrent:self];
	return ( self );
}



- swapPaneFor:sender
/*
T	his is SwapView's delegate method to determine which panel to show...
*/
{
	switch ( subMode ) {
		case DISPLAYOPTS_MODE: return ( displayOptsPane );
		case ALLOWEDTYPES_MODE: return ( allowedTypesPane );
		case DYNAMICITEMS_MODE: return ( dynamicItemsPane );
	}

	return ( nil );
}



// -------------------------------------------------------------------------
//   Display Options sub-mode
// -------------------------------------------------------------------------


- setFormatFullPath:sender
{
	switch ( [target drawMode] ) {
		case IC_LARGE_BROWSE:
			[target setLBNameMode:IC_FULLPATH]; break;
		case IC_SMALL_BROWSE:
			[target setSBNameMode:IC_FULLPATH]; break;
	}
	return ( self );
}



- setFormatFileAndPath:sender
{
	switch ( [target drawMode] ) {
		case IC_LARGE_BROWSE:
			[target setLBNameMode:IC_FILEANDPATH]; break;
		case IC_SMALL_BROWSE:
			[target setSBNameMode:IC_FILEANDPATH]; break;
	}
	return ( self );
}



- setFormatFileOnly:sender
{
	switch ( [target drawMode] ) {
		case IC_LARGE_BROWSE:
			[target setLBNameMode:IC_FILEONLY]; break;
		case IC_SMALL_BROWSE:
			[target setSBNameMode:IC_FILEONLY]; break;
	}
	return ( self );
}



- setFormatRelative:sender
{
	switch ( [target drawMode] ) {
		case IC_LARGE_BROWSE:
			[target setLBNameMode:IC_RELATIVE]; break;
		case IC_SMALL_BROWSE:
			[target setSBNameMode:IC_RELATIVE]; break;
	}
	return ( self );
}



- setFormatNoPath:sender
{
	switch ( [target drawMode] ) {
		case IC_LARGE_BROWSE:
			[target setLBNameMode:IC_NOPATH]; break;
		case IC_SMALL_BROWSE:
			[target setSBNameMode:IC_NOPATH]; break;
	}
	return ( self );
}



- setShowPath:sender
{
	switch ( [target drawMode] ) {
		case IC_LARGE_ICON:
			[target setLIDrawName:[dispOptShowPath state]]; break;
		case IC_SMALL_ICON:
			[target setSIDrawName:[dispOptShowPath state]]; break;
	}
	return ( self );
}



- setShowActualImage:sender
{
	[target setDrawActualImage:[dispOptActualImage state]];
	return ( self );
}



- setShowTriDots:sender
{
	[target setDrawTriDots:[dispOptTriDots state]];
	return ( self );
}



- setShowInfoLine:sender
{
	[target setDrawInfoLine1:[dispOptShowInfo1 state]];
	return ( self );
}



- setShowSmallIcon:sender
{
	[target setDrawSmallIcons:[dispOptSmallIcons state]];
	return ( self );
}



// -------------------------------------------------------------------------
//   Allowed Types sub-mode
// -------------------------------------------------------------------------


- loadAllowedTypes:sender
{
	[allowedBrowser loadColumnZero];
	[allowedRemoveButton setEnabled:NO];
	return ( self );
}



- allowedTypeSelected:sender
/*
	Called when one of the items in the allowsBrowser is clicked
*/
{
	[allowedRemoveButton setEnabled:YES];
	return ( self );
}



- changeUseRestrictions:sender
/*
	Called when the User Restrictions switch is clicked
*/
{
	[target setRestrictTypes:([allowedRestrictSwitch state] ? NO : YES)];
	return ( self );
}



- newAllowedType:sender
/*
	Called when the Add button is clicked
*/
{
	[target addAllowedType:[allowedField stringValue]];
	[allowedField setStringValue:""]; // Blank the field
	[self loadAllowedTypes:self];
	return ( self );
}



- removeAllowedTypes:sender
/*
	Called when the remove button is clicked
*/
{
	int i, count;
	id list = [[allowedBrowser matrixInColumn:0] cellList];
	id cell;
	
	count = [list count];
	for ( i=0; i<count; i++ )
		if ( [(cell = [list objectAt:i]) state] )
			[target removeAllowedType:[cell stringValue]];
	
	[self loadAllowedTypes:self];
	return ( self );
}



// -------------------------------------------------------------------------
//   Dynamic Items sub-mode
// -------------------------------------------------------------------------


- loadDynamicItemSpecs:sender
{
	[dynamicBrowser loadColumnZero];
	[dynamicDisableButton setEnabled:NO];
	[dynamicEnableButton setEnabled:NO];
	[dynamicRemoveButton setEnabled:NO];

	return ( self );
}



- dynamicItemSpecSelected:sender
/*
	Called when one of the items in the dynamicBrowser is clicked
*/
{
	[dynamicDisableButton setEnabled:YES];
	[dynamicEnableButton setEnabled:YES];
	[dynamicRemoveButton setEnabled:YES];

	return ( self );
}



- newDynamicItemSpec:sender
/*
	Called when the dynamic Add button is clicked
*/
{
	[target addDynamicItemSpec:[dynamicField stringValue]];
	[self loadDynamicItemSpecs:self];
	return ( self );
}



- modifyDynamicItemSpec:sender
/*
	Called when an item in the dynamic browser is double-clicked
*/
{
	// Not implemented
	[self debug:10 :"Modify Dynamic Item: Not implemented\n"];
	return ( self );
}



- disableDynamicItemSpecs:sender
/*
	Called when the dynamic disable button is clicked
*/
{
	int i, count;
	id list = [[dynamicBrowser matrixInColumn:0] cellList];
	id cell;
	
	count = [list count];
	for ( i=0; i<count; i++ )
		if ( [(cell = [list objectAt:i]) state] )
			[target disableDynamicItemSpec:[cell stringValue]];
	
	[self loadDynamicItemSpecs:self];
	return ( self );
}



- enableDynamicItemSpecs:sender
/*
	Called when the dynamic enable button is clicked
*/
{
	int i, count;
	id list = [[dynamicBrowser matrixInColumn:0] cellList];
	id cell;
	
	count = [list count];
	for ( i=0; i<count; i++ )
		if ( [(cell = [list objectAt:i]) state] )
			[target enableDynamicItemSpec:[cell stringValue]];
	
	[self loadDynamicItemSpecs:self];
	return ( self );
}



- removeDynamicItemSpecs:sender
/*
	Called when the dynamic remove button is clicked
*/
{
	int i, count;
	id list = [[dynamicBrowser matrixInColumn:0] cellList];
	id cell;
	
	count = [list count];
	for ( i=0; i<count; i++ )
		if ( [(cell = [list objectAt:i]) state] )
			[target removeDynamicItemSpec:[[list objectAt:i] stringValue]];
	
	[self loadDynamicItemSpecs:self];
	return ( self );
}



- setDynamicUpdateInterval:sender
/*
	Called by both the dynamicIntervalField and the dynamicIntervalSlider when they are changed.
*/
{
	float minutes;

	// Make sure the slider and field are showing the same value
	minutes = [sender intValue];
	[dynamicIntervalSlider setIntValue:minutes];
	[dynamicIntervalField setIntValue:minutes];
	
	// Set the dynamic update interval of the group in seconds
	[target setUpdateInterval:minutes * 60];
	
	return ( self );
}




// -------------------------------------------------------------------------
//   Text Delegate methods
// -------------------------------------------------------------------------


- textDidChange:sender
{
	if ( [sender isKindOf:[Text class]] ) sender = [sender delegate];

	if ( sender == nameField ) {
	} else
	if ( sender == keyField ) {
	} else
	if ( sender == pathField ) {
	} else
	if ( sender == allowedField ) {
		[allowedAddButton setEnabled:YES];
	} else
	if ( sender == dynamicField ) {
		[dynamicAddButton setEnabled:YES];
	}
	
	return ( self );
}



- textDidEnd:sender endChar:(unsigned short)endChar
/*
	Make sure changes made in field get saved.  If the field was exited with a return, then it will automatically send the action (which is where it gets saved).  If the field was exited with a tab, we need to send the action ourselves.
*/
{
	if ( [sender isKindOf:[Text class]] ) sender = [sender delegate];

	if ( sender == nameField ) {
		[[nameField target] perform:[nameField action] with:self];
		if ( endChar == NX_CR ) [[NXApp mainWindow] makeKeyWindow];
	} else
	if ( sender == keyField ) {
		[keyField sendAction];
		if ( endChar == NX_CR ) [[NXApp mainWindow] makeKeyWindow];
	} else
	if ( sender == pathField ) {
		[pathField sendAction];
		if ( endChar == NX_CR ) [[NXApp mainWindow] makeKeyWindow];
	} else
	if ( sender == allowedField ) {
	} else
	if ( sender == dynamicField ) {
	}
	
	return ( self );
}



// -------------------------------------------------------------------------
//   Browser delegate methods
// -------------------------------------------------------------------------


- (int)browser:sender fillMatrix:matrix inColumn:(int)column
/* All the browsers use normal delegate */
{
	int count = 0;
	id cell;
	
	if ( sender == allowedBrowser ) {
		const char **allowedTypes = [target allowedTypes];		

		while ( allowedTypes[count] ) {
			[matrix addRow];
			cell = [matrix cellAt:[matrix cellCount]-1 :0];
			[cell initTextCell:allowedTypes[count]];
			[[cell setLoaded:YES] setLeaf:YES];
			count++;
		}
	} else
	
	if ( sender == dynamicBrowser ) {
		id list = [target dynamicItemSpecs]; // Get list of DynamicItemSpecs
		id spec;
		id enabledFont = [Font newFont:"Helvetica-Bold" size:12];
		id disabledFont = [Font newFont:"Helvetica" size:12];
		int i;
		count = [list count];
		for ( i=0; i<count; i++ ) {
			spec = [list objectAt:i];
			[matrix addRow];
			cell = [matrix cellAt:[matrix cellCount]-1 :0];
			[cell initTextCell:[spec expression]];
			[cell setFont:([spec enabled] ? enabledFont : disabledFont)];
			[[cell setLoaded:YES] setLeaf:YES];
		}
	}
	
	return ( count );
}



@end

