/*+++*
 *  RCS CNewsConfigPanel.m,v 1.5 1995/07/13 11:47:31 tom Exp
 *  title:	CNewsConfigPanel.m
 *  abstract:	implementation of  CNewsConfigPanel class, for NewsConfig.app
 *  author:	T.R.Hageman, Groningen, The Netherlands
 *  created:	December 1994
 *  modified:	(see RCS Log at end)
 *  copyright:
 *
 *		Copyright (C) 1994,1995  Tom R. Hageman.
 *
 *	This is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation; either version 2 of the License, or
 *	(at your option) any later version.
 *
 *	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.  See the
 *	GNU General Public License for more details.
 *
 *	You should have received a copy of the GNU General Public License
 *	along with this software; if not, write to the Free Software
 *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  description:
 *
 *---*/

#import "CNewsConfigPanel.h"
#import "CheckedBrowserCell.h"

#import "configkeys.h"
#include <string.h>

@interface CNewsConfigPanel (Private)
- (void)_configure;
- (const char *)_activegroups;
- (void)_setActiveGroups;
- (void)_selectionToActive:(BOOL)value;
// Browser delegate methods.
- (int)browser:sender fillMatrix:matrix inColumn:(int)column;
@end


@implementation CNewsConfigPanel

- awakeFromNib
{
	// Configure the activegroups browser some more.
	[[[[[activegroups setCellClass:[CheckedBrowserCell class]]
	    /* {{ ???Arrow keys still don't work???
		  Browser refuses to become first responder.
		  Maybe because it's in a box?}} */
	    acceptArrowKeys:YES andSendActionMessages:NO]
	   setTarget:self]
	  setAction:@selector(activeSelected:)]
	 setDoubleAction:@selector(toggleActive:)];
	return self;
}

// Action methods
- revert:sender
{
	[self _configure];
	return self;
}

- activeSelected:sender	// single-click (or just select) browser item
{
	List *selection = [[List alloc] init];

	[activegroups getSelectedCells:selection];

	[activeButtons setEnabled:([selection count] > 0)];
	[selection free];

	return self;
}

- toggleActive:sender	// double-click browser item
{
	CheckedBrowserCell *cell = [activegroups selectedCell];

	[cell setChecked:![cell checked]];

	[activegroups display];	// unkh!

	return self;
}

- addSelectionToActive:sender
{
	[self _selectionToActive:YES];
	return self;
}

- removeSelectionFromActive:sender
{
	[self _selectionToActive:NO];
	return self;
}

- (void)setConfiguration:(HashTable *)aTable
{
	if (table) [table free];
	table = aTable;
	[self _configure];
}

- (HashTable *)configuration
{
	HashTable *result = [[HashTable alloc] initKeyDesc:"*" valueDesc:"*"];
	const char *v;
#define SETRESULT(field, key)	[result insertKey:key value:\
	(char *)(((v = [field stringValue]) && *(v += strspn(v, " \t"))) ? v :\
		 [table valueForKey:key])]
	
	SETRESULT(organization, ORGANIZATION);
	SETRESULT(hostname, HOSTNAME);
	SETRESULT(uucpname, UUCPNAME);
	SETRESULT(newsfeed, NEWSFEED);
	SETRESULT(newsfeedSubscriptions, NEWSFEED_SUBSCRIPTIONS);
	SETRESULT(newsfeedDistribution, NEWSFEED_DISTRIBUTION);
	SETRESULT(newsfeedFlags, NEWSFEED_FLAGS);
#undef SETRESULT
	if ([immednews intValue]) [result insertKey:IMMEDNEWS value:"yes"];
	if ([expirermdir intValue]) [result insertKey:EXPIRERMDIR value:"yes"];
	if ([allowNewHierarchies intValue]) [result insertKey:ALLGROUPS value:"yes"];
	if ([addsignature intValue]) {
		if (!((v = [addsigMaxlines stringValue]) && 
		      *(v += strspn(v, " \t")))) {
			v = "all";
		}		
		[result insertKey:ADDSIGNATURE value:(char *)v];
	}
	[result insertKey:ACTIVEGROUPS value:(char *)[self _activegroups]];

#define PROPAGATE(key) \
	((void) ([table isKey:key] && \
		 [result insertKey:key value:[table valueForKey:key]]))

	PROPAGATE(AVAILABLEGROUPS);
	PROPAGATE(EXPIRERMDIR_PRESENT);
	PROPAGATE(ADDSIGNATURE_PRESENT);
	PROPAGATE(IMMEDNEWS_PRESENT);
#undef PROPAGATE

	return result;
}

- setEnabled:(BOOL)yn	{ [okButton setEnabled:yn]; return self; }

- (BOOL)isEnabled	{ return [okButton isEnabled]; }

@end // CNewsConfigPanel


@implementation CNewsConfigPanel (Private)

// (re)configure the window with the values in table.
- (void)_configure
{
	const char *v;
	
	[self disableFlushWindow];
	
	[organization setStringValue:[table valueForKey:ORGANIZATION]];
	[hostname setStringValue:[table valueForKey:HOSTNAME]];
	[uucpname setStringValue:[table valueForKey:UUCPNAME]];
	[newsfeed setStringValue:[table valueForKey:NEWSFEED]];
	[newsfeedSubscriptions setStringValue:[table valueForKey:NEWSFEED_SUBSCRIPTIONS]];
	[newsfeedDistribution setStringValue:[table valueForKey:NEWSFEED_DISTRIBUTION]];
	[newsfeedFlags setStringValue:[table valueForKey:NEWSFEED_FLAGS]];
	[immednews setIntValue:[table isKey:IMMEDNEWS]];
	[expirermdir setIntValue:[table isKey:EXPIRERMDIR]];
	[allowNewHierarchies setIntValue:[table isKey:ALLGROUPS]];
	if (!(v = [table valueForKey:ADDSIGNATURE]) || 
	    *v == '\0' || *v == 'n' || *v == 'N' || strcmp(v, "0") == 0) {
		[addsignature setIntValue:NO];
		[addsigMaxlines setStringValue:""];
	}
	else {
		[addsignature setIntValue:YES];
		[addsigMaxlines setStringValue:(NXIsDigit(*v) ? v : "")];
	}
	// Initialize `activeGroups'.
	[self _setActiveGroups];

	// Disable some fields is news config does not support them.
	[expirermdir setEnabled:[table isKey:EXPIRERMDIR_PRESENT]];
	[addsignature setEnabled:[table isKey:ADDSIGNATURE_PRESENT]];
	[immednews setEnabled:[table isKey:IMMEDNEWS_PRESENT]];

	// Remove the blurb from the window.
	if ([expirermdir isEnabled] && [addsignature isEnabled] &&
	    [immednews isEnabled]) {
		[negenExtrasBlurb removeFromSuperview];
	}

	[self reenableFlushWindow];
	[self flushWindowIfNeeded];
}

// Extract the active groups from the browser.
- (const char *)_activegroups
{
	NXStream *stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
	const char *result;
	int dummy;
	int row = 0, active = 0;
	CheckedBrowserCell *cell;

	for (; cell = [activegroups getLoadedCellAtRow:row inColumn:0]; row++) {
		if (![cell checked]) continue;
		if (active) {
			NXPutc(stream, ' ');
		}
		NXPrintf(stream, "%s", [cell stringValue]);
		++active;
	}
	NXPutc(stream, '\0');

	NXGetMemoryBuffer(stream, &result, &dummy, &dummy);
	NXClose(stream);

	return result;
}

// Initialize the browser with the group list, and mark active groups.
- (void)_setActiveGroups
{
	// Load the browser (we are its delegate).
	[activegroups loadColumnZero];
	// Selection is zapped in the process...
	[activeButtons setEnabled:NO];
}

- (void)_selectionToActive:(BOOL)value
{
	List *selection = [[List alloc] init];

	[activegroups getSelectedCells:selection];
	[selection makeObjectsPerform:@selector(setChecked:) with:(id)(int)value];
	// XXX {{{Should we deselect the selection?}}}
	[selection free];

	[activegroups display];	// unkh!
}

// browser delegate method: fill in the browser.
- (int)browser:sender fillMatrix:matrix inColumn:(int)column
{
	int row = 0, nrows;
	CheckedBrowserCell *cell = nil;
	const char *group;
	char *groups = [table valueForKey:AVAILABLEGROUPS];

	// This happens at nib instantiation time.
	if (groups == NULL) return 0;

	groups = NXCopyStringBuffer(groups);

	// Add all availablegroups in browser.
	// ASSUMPTION: both group lists are sorted.

	if (group = strtok(groups, " ")) do {
		[matrix addRow];
		cell = [matrix cellAt:row :0];
		[[[cell setStringValue:group] setLoaded:YES] setLeaf:YES];
		++row;
	} while (group = strtok(NULL, " "));
	free(groups);

	nrows = row;
	row = 0;

	// Now set state (and image) for all active groups 
	groups = NXCopyStringBuffer([table valueForKey:ACTIVEGROUPS]);

	if (group = strtok(groups, " ")) do {
		int c = -1;

		while (row < nrows &&
		       (c = strcmp([(cell = [matrix cellAt:row:0]) stringValue],
				   group)) < 0) {
			row++;
		}
		if (c != 0) {
			[matrix insertRowAt:row];
			cell = [matrix cellAt:row :0];
			[[[cell setStringValue:group] setLoaded:YES] setLeaf:YES];
			++nrows;
		}
		[cell setChecked:YES];
		++row;
	} while (group = strtok(NULL, " "));
	free(groups);

	return nrows;
}

@end // CNewsConfigPanel (Private)

//======================================================================
// CNewsConfigPanel.m,v
// Revision 1.5  1995/07/13 11:47:31  tom
// (-setEnabled:,isEnabled): new methods;
// (-configuration): add ALLGROUPS support, propagate IMMEDNEWS_PRESENT;
// (-configure:): handle ALLGROUPS and IMMEDNEWS_PRESENT.
//
// Revision 1.4  1995/01/15  22:50:47  tom
// (awakeFromNib): comment fix; (setConfiguration): free old table;
// (_configuration): propagate non-GUI key/value pairs;
// (_setActiveGroups): disable activeButtons.
//
// Revision 1.3  1995/01/11  13:48:40  tom
// (#import): CheckedBrowserCell.h; (Private): declare new support methods;
// (awakeFromNib): new, extra browserconfiguration;
// (activeSelected:,addSelectionToActive:,removeSelectionFromActive:): new;
// (setConfiguration:): use _setActiveGroups;
// (configuration): use [CheckedBrowserCell checked] to test active state;
// (_setActiveGroups,_selectionToActive:): new private support methods;
// (browser:fillMatrix:inColumn:): use [CheckedBrowserCell setChecked:] to store
//  active state.
//
// Revision 1.2  1994/12/24  02:10:34  tom
// (configuration): use "yes" for BOOL keys; enable extended options and
// remove blurb if extensions are supported by installed CNews version.
//
// Revision 1.2  1994/12/24  02:10:34  tom
// (configuration): use "yes" for BOOL keys; enable extended options and
// remove blurb if extensions are supported by installed CNews version.
//
// Revision 1.1  1994/12/20  14:41:15  tom
// Initial revision
//
//======================================================================
