//
// Time-stamp: <95/12/10 17:27:02 stephan>
//
// RulerPreferencesControl.m
// Project: Rulers
//
// Stephan Wacker
// 93-01-31


#import "RulerPreferencesControl.h"
#import "RulerControl.h"
#import "RulerManager.h"



#define APP_NAME	"RulersBySW"	// name in defaults database

#define DPI_MIN		50
#define DPI_MAX		300

//
// Defaults vector for initializing defaults database
//
static NXDefaultsVector RulersDefaults = {
    { UNIT_DEFAULT_NAME,		UNIT_DEFAULT_VALUE },
#if 0
    { SCREEN_DEFAULT_NAME,		SCREEN_DEFAULT_VALUE },
#else
    { DPI_DEFAULT_NAME,			DPI_DEFAULT_VALUE },
#endif
    { INCH_DEFAULT_NAME,		INCH_DEFAULT_VALUE },
    { HMARKS_DEFAULT_NAME,		HMARKS_DEFAULT_VALUE },
    { VMARKS_DEFAULT_NAME,		VMARKS_DEFAULT_VALUE },
    { LABELS_DEFAULT_NAME,		LABELS_DEFAULT_VALUE },
    { FLOAT_DEFAULT_NAME,		FLOAT_DEFAULT_VALUE },
    { HIDE_DEFAULT_NAME,		HIDE_DEFAULT_VALUE },
    { HPOSNUM_DEFAULT_NAME,		HPOSNUM_DEFAULT_VALUE },
    { VPOSNUM_DEFAULT_NAME,		VPOSNUM_DEFAULT_VALUE },
    { COLOR_DEFAULT_NAME,		COLOR_DEFAULT_VALUE },
    { NULL, NULL }
};
    

//
// Backup of settings for revert: method.
//
static	int	savedUnitsMode;
#if 0
static	int	savedScreenMode;
#else
static	float	savedScreenDpi;
#endif
static	int	savedInchMode;
static	int	savedHMarksMode;
static	int	savedVMarksMode;
static	BOOL	savedLabelsMode;
static	BOOL	savedFloatMode;
static	BOOL	savedHideMode;
static	int	savedHPosNumMode;
static	int	savedVPosNumMode;
static	NXColor	savedColor;



// Static functions.

static
NXColor		parseColor( const char* str )
{
    NXColor	result;
    float	r, g, b;
    
    if( sscanf( str, COLOR_FORMAT, &r, &g, &b ) < 3 ) {
	result = COLOR_DEFAULT;
    } else {
	result = NXConvertRGBToColor( r, g, b );
    }
	
    return result;
}


static
const char*	unparseColor( NXColor color )
{
    char	result[30];
    float	r, g, b;

    NXConvertColorToRGB( color, &r, &g, &b );
    sprintf( result, COLOR_FORMAT, r, g, b );
    
    return result;
}


@interface RulerPreferencesControl ( Private )

- setUnitsMode: (int) mode;
#if 0
- setScreenMode: (int) mode;
#else
- setScreenDpi: (float) value;
#endif
- setInchMode: (int) mode;
- setHMarksMode: (int) mode;
- setVMarksMode: (int) mode;
- setLabelsMode: (BOOL) mode;
- setFloatMode: (BOOL) mode;
- setHideMode: (BOOL) mode;
- setHPosNumMode: (int) mode;
- setVPosNumMode: (int) mode;
- setColor: (NXColor) color;


@end	// RulerPreferencesControl ( Private )


@implementation RulerPreferencesControl


+ initialize
// Register default values after class has been created.
{
    NXRegisterDefaults( APP_NAME, RulersDefaults );
    
    return self;
}



- (int) unitsMode
// Answer current units mode.
{
    return [unitsMatrix selectedTag];
}

- setUnitsMode: (int) mode
// Set new units mode.
{
    [unitsMatrix selectCellWithTag: mode];
    return self;
}

+ (int) defaultUnitsMode
// Answer default setting of units mode.
{
    const char	*value = NXGetDefaultValue( APP_NAME, UNIT_DEFAULT_NAME );
    
    if( !strcmp( value, "pica"		) ) return UNIT_PC;
    if( !strcmp( value, "inch"		) ) return UNIT_IN;
    if( !strcmp( value, "cm"		) ) return UNIT_CM;
    if( !strcmp( value, "pt"		) ) return UNIT_PT;
    if( !strcmp( value, "true in"	) ) return UNIT_TRUE_IN;
    if( !strcmp( value, "true cm"	) ) return UNIT_TRUE_CM;
	
    return UNIT_DEFAULT;	// only if invalid default value
}



#if 0
#
- (int) screenMode
// Answer current screen mode.
{
    return [screenMatrix selectedTag];
}

// Set new screen mode.
{
    [screenMatrix selectCellWithTag: mode];
    return self;
}

+ (int) defaultScreenMode
// Answer default setting of screen mode.
{
    const char	*value = NXGetDefaultValue( APP_NAME, SCREEN_DEFAULT_NAME );
    
    if( !strcmp( value, "17"		) ) return XVII_INCH;
    if( !strcmp( value, "21"		) ) return XXI_INCH;
    
    return SCREEN_DEFAULT;	// only if invalid default value
}
#
#else
#
- (float) screenDpi
// Answer current dpi setting.
{
    float	result = [dpiField floatValue];

    if( result < DPI_MIN )
    {
	result = DPI_MIN;
    }
    if( result > DPI_MAX )
    {
	result = DPI_MAX;
    }

    return result;
}

- setScreenDpi: (float) value
// Set new dpi setting.
{
    char	buf[40];

    if( value < DPI_MIN )
    {
	value = DPI_MIN;
    }
    if( value > DPI_MAX )
    {
	value = DPI_MAX;
    }

    sprintf( buf, "%.1f", value );

    [dpiField setStringValue: buf];
    return self;
}

+ (float) defaultScreenDpi
// Answer default setting of dpi setting.
{
    const char	*value = NXGetDefaultValue( APP_NAME, DPI_DEFAULT_NAME );
    float	result;
    
    if( sscanf( value, "%f", &result ) == 1 )
    {
	if( result < DPI_MIN )
	{
	    result = DPI_MIN;
	}
	if( result > DPI_MAX )
	{
	    result = DPI_MAX;
	}

	return result;
    }
    
    return DPI_DEFAULT;	// only if invalid default value
}
#
#endif



- (int) inchMode
// Answer current inch mode.
{
    return [inchMatrix selectedTag];
}

- setInchMode: (int) mode
// Set new inch mode.
{
    [inchMatrix selectCellWithTag: mode];
    return self;
}

+ (int) defaultInchMode
// Answer default setting of inch mode.
{
    const char	*value = NXGetDefaultValue( APP_NAME, INCH_DEFAULT_NAME );
    
    if( !strcmp( value, "8"		) ) return INCH_DIV_8;
    if( !strcmp( value, "10"		) ) return INCH_DIV_10;
    
    return INCH_DEFAULT;	// only if invalid default value
}



- (int) hMarksMode
// Answer current horizontal marks mode.
{
    return [hMarksMatrix selectedTag];
}

- setHMarksMode: (int) mode
// Set new hMarks mode.
{
    [hMarksMatrix selectCellWithTag: mode];
    return self;
}

+ (int) defaultHMarksMode
// Answer default setting of hMarks mode.
{
    const char	*value = NXGetDefaultValue( APP_NAME, HMARKS_DEFAULT_NAME );
    
    if( !strcmp( value, "Top"		) ) return HMARKS_TOP;
    if( !strcmp( value, "Bottom"	) ) return HMARKS_BOTTOM;
    if( !strcmp( value, "Both"		) ) return HMARKS_BOTH;
    
    return HMARKS_DEFAULT;	// only if invalid default value
}



- (int) vMarksMode
// Answer current vertical marks mode.
{
    return [vMarksMatrix selectedTag];
}

- setVMarksMode: (int) mode
// Set new vMarks mode.
{
    [vMarksMatrix selectCellWithTag: mode];
    return self;
}

+ (int) defaultVMarksMode
// Answer default setting of vMarks mode.
{
    const char	*value = NXGetDefaultValue( APP_NAME, VMARKS_DEFAULT_NAME );
    
    if( !strcmp( value, "Left"		) ) return VMARKS_LEFT;
    if( !strcmp( value, "Right"		) ) return VMARKS_RIGHT;
    if( !strcmp( value, "Both"		) ) return VMARKS_BOTH;
    
    return VMARKS_DEFAULT;	// only if invalid default value
}



- (BOOL) labelsMode
// Answer current labels mode.
{
    return [labelsButton state];
}

- setLabelsMode: (BOOL) mode
// Set new labels mode.
{
    [labelsButton setState: mode];
    return self;
}

+ (BOOL) defaultLabelsMode
// Answer default setting of labels mode.
{
    const char	*value = NXGetDefaultValue( APP_NAME, LABELS_DEFAULT_NAME );
    
    if( !strcmp( value, "YES"		) ) return YES;
    if( !strcmp( value, "NO"		) ) return NO;
    
    return LABELS_DEFAULT;	// only if invalid default value
}



- (BOOL) floatMode
// Answer current float mode.
{
    return [floatButtonCell intValue];
}

- setFloatMode: (BOOL) mode
// Set new float mode.
{
    [floatButtonCell setIntValue: (int) mode];
    
    return self;
}

+ (BOOL) defaultFloatMode
// Answer default setting of float mode.
{
    const char	*value = NXGetDefaultValue( APP_NAME, FLOAT_DEFAULT_NAME );
    
    if( !strcmp( value, "YES"		) ) return YES;
    if( !strcmp( value, "NO"		) ) return NO;
    
    return FLOAT_DEFAULT;	// only if invalid default value
}



- (BOOL) hideMode
// Answer current hide mode.
{
    return [hideButtonCell intValue];
}

- setHideMode: (BOOL) mode
// Set new hide mode.
{
    [hideButtonCell setIntValue: (int) mode];
    
    return self;
}

+ (BOOL) defaultHideMode
// Answer default setting of hide mode.
{
    const char	*value = NXGetDefaultValue( APP_NAME, HIDE_DEFAULT_NAME );
    
    if( !strcmp( value, "YES"		) ) return YES;
    if( !strcmp( value, "NO"		) ) return NO;
    
    return HIDE_DEFAULT;	// only if invalid default value
}



- (int) hPosNumMode
// Answer current horizontal positive numbers mode.
{
    return [hNumberMatrix selectedTag];
}

- setHPosNumMode: (int) mode
// Set new horizontal positive numbers mode.
{
    [hNumberMatrix selectCellWithTag: mode];
    return self;
}

+ (int) defaultHPosNumMode
// Answer default setting of horizontal positive numbers mode.
{
    const char	*value = NXGetDefaultValue( APP_NAME, HPOSNUM_DEFAULT_NAME );
    
    if( !strcmp( value, "Left"		) ) return POSNUM_LEFT;
    if( !strcmp( value, "Right"		) ) return POSNUM_RIGHT;
    if( !strcmp( value, "Both"		) ) return POSNUM_BOTH;
    
    return HPOSNUM_DEFAULT;	// only if invalid default value
}



- (int) vPosNumMode
// Answer current vertical positive numbers mode.
{
    return [vNumberMatrix selectedTag];
}

- setVPosNumMode: (int) mode
// Set new vertical positive numbers mode.
{
    [vNumberMatrix selectCellWithTag: mode];
    return self;
}

+ (int) defaultVPosNumMode
// Answer default setting of vertical positive numbers mode.
{
    const char	*value = NXGetDefaultValue( APP_NAME, VPOSNUM_DEFAULT_NAME );
    
    if( !strcmp( value, "Up"		) ) return POSNUM_UP;
    if( !strcmp( value, "Down"		) ) return POSNUM_DOWN;
    if( !strcmp( value, "Both"		) ) return POSNUM_BOTH;
    
    return VPOSNUM_DEFAULT;	// only if invalid default value
}



- (NXColor) color
// Answer current solor.
{
    return [colorCell color];
}

- setColor: (NXColor) color
// Set new color.
{
    [colorCell setColor: color];
    return self;
}

+ (NXColor) defaultColor
// Answer default color.
{
    const char	*value = NXGetDefaultValue( APP_NAME, COLOR_DEFAULT_NAME );
    
    return parseColor( value );
}



- awakeFromNib;
// Initialize yourself after nib file has been loaded.
{
    [self setUnitsMode:  [[self class] defaultUnitsMode]];
#if 0
    [self setScreenMode: [[self class] defaultScreenMode]];
#else
    [self setScreenDpi:  [[self class] defaultScreenDpi]];
#endif
    [self setInchMode:   [[self class] defaultInchMode]];
    [self setHMarksMode: [[self class] defaultHMarksMode]];
    [self setVMarksMode: [[self class] defaultVMarksMode]];
    [self setLabelsMode: [[self class] defaultLabelsMode]];
    [self setFloatMode:  [[self class] defaultFloatMode]];
    [self setHideMode:   [[self class] defaultHideMode]];
    [self setColor:      [[self class] defaultColor]];

    [dpiField setTextDelegate: self];
    
    return self;
}



- storeSettings
{
    savedUnitsMode	= [self unitsMode];
#if 0
    savedScreenMode	= [self screenMode];
#else
    savedScreenDpi	= [self screenDpi];
#endif
    savedInchMode	= [self inchMode];
    savedHMarksMode	= [self hMarksMode];
    savedVMarksMode	= [self vMarksMode];
    savedLabelsMode	= [self labelsMode];
    savedFloatMode	= [self floatMode];
    savedHideMode	= [self hideMode];
    savedHPosNumMode	= [self hPosNumMode];
    savedVPosNumMode	= [self vPosNumMode];
    savedColor		= [self color];
    
    return self;
}

- restoreSettings
{
    [self setUnitsMode	: savedUnitsMode];
#if 0
    [self setScreenMode	: savedScreenMode];
#else
    [self setScreenDpi	: savedScreenDpi];
#endif
    [self setInchMode	: savedInchMode];
    [self setHMarksMode	: savedHMarksMode];
    [self setVMarksMode	: savedVMarksMode];
    [self setLabelsMode	: savedLabelsMode];
    [self setFloatMode	: savedFloatMode];
    [self setHideMode	: savedHideMode];
    [self setHPosNumMode: savedHPosNumMode];
    [self setVPosNumMode: savedVPosNumMode];
    [self setColor	: savedColor];
    
    return self;
}



- takePreferencesFrom: sender
// Get all preferences settings from the sender object.
// Ask only for preferences that the sender knows.
{
    if( [sender respondsTo: @selector( unitsMode )] )
	[self setUnitsMode: [sender unitsMode]];
#if 0
    if( [sender respondsTo: @selector( screenMode )] )
	[self setScreenMode: [sender screenMode]];
#else
    if( [sender respondsTo: @selector( screenDpi )] )
	[self setScreenDpi: [sender screenDpi]];
#endif
    if( [sender respondsTo: @selector( inchMode )] )
	[self setInchMode: [sender inchMode]];
    if( [sender respondsTo: @selector( hMarksMode )] )
	[self setHMarksMode: [sender hMarksMode]];
    if( [sender respondsTo: @selector( vMarksMode )] )
	[self setVMarksMode: [sender vMarksMode]];
    if( [sender respondsTo: @selector( labelsMode )] )
	[self setLabelsMode: [sender labelsMode]];
    if( [sender respondsTo: @selector( floatMode )] )
	[self setFloatMode: [sender floatMode]];
    if( [sender respondsTo: @selector( hideMode )] )
	[self setHideMode: [sender hideMode]];
    if( [sender respondsTo: @selector( hPosNumMode )] )
	[self setHPosNumMode: [sender hPosNumMode]];
    if( [sender respondsTo: @selector( vPosNumMode )] )
	[self setVPosNumMode: [sender vPosNumMode]];
    if( [sender respondsTo: @selector( color )] )
	[self setColor: [sender color]];
    
    return self;
}



- run;
// Open the preferences panel and run it in a modal loop.
{
    BOOL	enabled;
    
    enabled = YES;
    [saveButton setEnabled: enabled];
    
    // We have to ask the RulerManager if there is a current main
    // RulerControl object.  Otherwise, the Apply button will be disabled
    // because there is no object to apply the settings to.
    // We also store that object in the rulerControl instance variable
    // for later use it in the apply: method.
    //
    rulerControl = [[NXApp delegate] mainRuler];
    enabled = (rulerControl != nil);
    [applyButton setEnabled: enabled];
    if( enabled ) {
	[self takePreferencesFrom: rulerControl];
    }
    
    enabled = ([[[NXApp delegate] rulerList] count] > 0);
    [applyAllButton setEnabled: enabled];
    
    [self storeSettings];			// prepare for revert:
    
    [window makeKeyAndOrderFront: nil];
    [NXApp runModalFor: window];
    [window close];
    
    return self;
}



- cancel: sender
// Discard changes.
{
    [self revert: sender];
    
    [NXApp stopModal];			// see run
    return self;
}



- ok: sender
// Keep changes but don't apply them.
{
    [NXApp stopModal];			// see run
    return self;
}



- apply: sender
// Apply selected preferences to rulerControl.
{
    [rulerControl applyPreferences: self];
    
    [NXApp stopModal];			// see run
    return self;
}



- applyAll: sender
// Apply selected preferences to all rulers.
{
    id		rulers = [[NXApp delegate] rulerList];
    
    [rulers makeObjectsPerform: @selector(applyPreferences:) with: self];
    
    [NXApp stopModal];			// see run
    return self;
}



- save: sender
// Save preferences in database.
{
    static NXDefaultsVector newDefaults = {
	{ UNIT_DEFAULT_NAME,		NULL },
#if 0
	{ SCREEN_DEFAULT_NAME,		NULL },
#else
	{ DPI_DEFAULT_NAME,		NULL },
#endif
	{ HMARKS_DEFAULT_NAME,		NULL },
	{ VMARKS_DEFAULT_NAME,		NULL },
	{ LABELS_DEFAULT_NAME,		NULL },
	{ FLOAT_DEFAULT_NAME,		NULL },
	{ HIDE_DEFAULT_NAME,		NULL },
	{ HPOSNUM_DEFAULT_NAME,		NULL },
	{ VPOSNUM_DEFAULT_NAME,		NULL },
	{ INCH_DEFAULT_NAME,		NULL },
	{ COLOR_DEFAULT_NAME,		NULL },
	{ NULL, NULL }
    };
    
    int		i;
    const char	*value;				// new defaults value
    char	buf[60];
    
    
    if( !newDefaults[0].value )			// allocate default values
	for( i = 0; newDefaults[i].name != NULL; i++ ) {
	    newDefaults[i].value = malloc( 60 );	// max length = 59
	}
    
    
    i = 0;
    
    switch( [self unitsMode] ) {
      case UNIT_PC	: value = "pica";		break;
      case UNIT_IN	: value = "inch";		break;
      case UNIT_CM	: value = "cm";			break;
      case UNIT_PT	: value = "pt";			break;
      case UNIT_TRUE_IN	: value = "true in";		break;
      case UNIT_TRUE_CM	: value = "true cm";		break;
      default		: value = UNIT_DEFAULT_VALUE;	break;
    }
    strcpy( newDefaults[i++].value, value );

#if 0
    switch( [self screenMode] ) {
      case XVII_INCH	: value = "17";			break;
      case XXI_INCH	: value = "21";			break;
      default		: value = SCREEN_DEFAULT_VALUE;	break;
    }
#else
    sprintf( buf, "%.1f", [self screenDpi] );
    value = buf;
#endif
    strcpy( newDefaults[i++].value, value );
    
    switch( [self hMarksMode] ) {
      case HMARKS_TOP	: value = "Top";		break;
      case HMARKS_BOTTOM: value = "Bottom";		break;
      case HMARKS_BOTH	: value = "Both";		break;
      default		: value = HMARKS_DEFAULT_VALUE;	break;
    }
    strcpy( newDefaults[i++].value, value );
    
    switch( [self vMarksMode] ) {
      case VMARKS_LEFT	: value = "Left";		break;
      case VMARKS_RIGHT	: value = "Right";		break;
      case VMARKS_BOTH	: value = "Both";		break;
      default		: value = VMARKS_DEFAULT_VALUE;	break;
    }
    strcpy( newDefaults[i++].value, value );
    
    switch( [self labelsMode] ) {
      case YES		: value = "YES";		break;
      case NO		: value = "NO";			break;
      default		: value = LABELS_DEFAULT_VALUE;	break;
    }
    strcpy( newDefaults[i++].value, value );
    
    switch( [self floatMode] ) {
      case YES		: value = "YES";		break;
      case NO		: value = "NO";			break;
      default		: value = FLOAT_DEFAULT_VALUE;	break;
    }
    strcpy( newDefaults[i++].value, value );
    
    switch( [self hideMode] ) {
      case YES		: value = "YES";		break;
      case NO		: value = "NO";			break;
      default		: value = HIDE_DEFAULT_VALUE;	break;
    }
    strcpy( newDefaults[i++].value, value );
    
    switch( [self hPosNumMode] ) {
      case POSNUM_LEFT	: value = "Left";		break;
      case POSNUM_RIGHT	: value = "Right";		break;
      case POSNUM_BOTH	: value = "Both";		break;
      default		: value = HPOSNUM_DEFAULT_VALUE;break;
    }
    strcpy( newDefaults[i++].value, value );
    
    switch( [self vPosNumMode] ) {
      case POSNUM_UP	: value = "Up";			break;
      case POSNUM_DOWN	: value = "Down";		break;
      case POSNUM_BOTH	: value = "Both";		break;
      default		: value = VPOSNUM_DEFAULT_VALUE;break;
    }
    strcpy( newDefaults[i++].value, value );
    
    switch( [self inchMode] ) {
      case INCH_DIV_8	: value = "8";			break;
      case INCH_DIV_10	: value = "10";			break;
      default		: value = INCH_DEFAULT_VALUE;	break;
    }
    strcpy( newDefaults[i++].value, value );
    
    strcpy( newDefaults[i++].value, unparseColor( [self color] ) );
    
    
    NXWriteDefaults( APP_NAME, newDefaults );
    
    [NXApp stopModal];			// see run
    return self;
}



- revert: sender
// Undo changes.
{
    return [self restoreSettings];
}


@end // RulerPreferencesControl




@implementation RulerPreferencesControl( WindowDelegate )


- windowWillClose: sender
// Clean up before preferences panel is closed.
// Will not be called when one of the buttons 'Cancel',
// 'Save' or 'Apply' is pressed.
{
    [self revert: nil];
    [NXApp abortModal];			// see run; will not return!
    return self;
}


@end // RulerPreferencesControl( WindowDelegate )


@implementation RulerPreferencesControl( TextDelegate )


- textDidEnd: sender endChar: (unsigned short) whyEnd
{
    // Check range and format of dpiField.
    //
    if( [sender isDescendantOf: dpiField] )
    {
	[self setScreenDpi: [dpiField floatValue]];
    }

    return self;
}


@end // RulerPreferencesControl( TextDelegate )
