/* SelectView.m - Copyright 1993  Steve Ludtke */
/* This object displays a list and allows you to select various lines. */
/* Multiple lines can be selected if <shift> is depressed. The order in */
/* which the items were selected is stored in 2 redundant structures */
/* This object works well in a ScrollView */

/* funtion is pretty simple, so there aren't many comments. To use it, */
/* just pass it an initialized SELDAT structure (see Mtypes.h) */

#import "Mtypes.h"
#import "SelectView.h"
#import "Inspector.h"

@implementation SelectView
-initFrame:(const NXRect *)rect
{
[super initFrame:rect];
[self setFlipped:YES];		/* make origin upper left */
nsel=0;
title[0]=gtitle[0]=0;
return self;
}

/* initialize everything from passed data structure */
-setData:(int)n :(struct SELDAT *)Data :Notify
{
int i;

data=Data;
ndat=n;
notify=Notify;
nsel=0;

for (i=0; i<n; i++) { data[i].sel=0; data[i].stab= -1; }

[self sizeTo:frame.size.width :12.0*(n+1)];
[self display];
return self;
}

-setHeader:(char *)Title
{
strcpy(title,Title);
return self;
}

-setGHeader:(char *)Title
{
strcpy(gtitle,Title);
return self;
}

-select:(int)n
{
if (n>=ndat||n<0) return self;
if (data[n].sel) return self;

data[nsel].stab=n;		/* store selected line in list */
nsel++;
data[n].sel=nsel;		/* tag this item as selected */

[self display];
if (notify) [notify selected:self :n :nsel];
return self;
}

-selectAry:(short)n :(short *)sel
{
int i;

[self deselectAll];
for (i=0; i<n; i++) {
	data[nsel].stab=sel[i];
	nsel++;
	data[sel[i]].sel=nsel;
}
[self display];
if (notify&&nsel) [notify selected:self :sel[0] :nsel];
else if (notify) [notify deselected:self :0 :0];
return self;
}

/* somewhat messy deselect routine, I should clean it up a bit sometime */
-deselect:(int)n
{
int i;

if (n>=ndat||n<0) return self;
if (!data[n].sel) return self;
for (i=data[n].sel-1; i<nsel; i++) data[i].stab=data[i+1].stab;
nsel--;
data[n].sel=0;
for (i=0; i<nsel; i++) data[data[i].stab].sel=i+1;

[self display];
if (notify) [notify deselected:self :n :nsel];
return self;
}

-notifyO
{
if (notify) [notify deselected:self :0 :nsel];
return self;
}

-mouseDown:(NXEvent *)oevent 
{
int n,f=0;
NXEvent evs;
float ix,iy;

evs=*oevent;
oevent=&evs;
[self convertPoint:&oevent->location fromView:nil];
ix=oevent->location.x;
iy=oevent->location.y;

n=floor(iy/12.0)-1;
if (n==-1) return self;
if (data[n].sel) f=1;
if (evs.flags&NX_SHIFTMASK) {
	if (f) [self deselect:n];
	else [self select:n];
}
else {
	if (nsel==1&&f) [self deselect:n];
	else {
		[self deselectAll];  
		[self select:n];
	}
}
return self;
}

-selectAll:sender
{
int i;

for (i=0; i<ndat; i++) data[i].sel=data[i].stab=i+1;
nsel=ndat;

[self display];
if (notify) [notify selected:self :0 :nsel];
return self;
}

-deselectAll
{
int i;

for (i=0; i<ndat; i++) { data[i].sel=0; data[i].stab= -1; }
nsel=0;
[self display];
return self;
}

-drawSelf:(const NXRect *)rects :(int)nr
{
int i,j,f,l;
NXRect fill;

PSsetohlfs();
for (i=0; i<nr; i++) {
	PSsetgray(1.0);
	NXRectFill(&rects[i]);
	f=floor(rects[i].origin.y/12.0)-1;
	l=f+1+floor(rects[i].size.height/12.0);
	if (l>=ndat) l=ndat-1;
	if (f>=ndat) f=l+1;
	if (f==-1) {
		PSsetgray(0.0);
		PSmoveto(1.0,10.0);
		PSshow(title);
		f++;
		/* this will overlay a title in the symbol font if available */
		if (strlen(gtitle)!=0) {
			PSsetsymb();
			PSmoveto(1.0,10.0);
			PSshow(gtitle);
			PSsetohlfs();
		}
		PSstroke();
	}
	for (j=f; j<=l; j++) {
		if (data[j].sel) {
			PSsetgray(.6666667);
			fill=frame;
			fill.origin.y=j*12.0+12.0;
			fill.size.height=12.0;
			NXRectFill(&fill);
		}
		PSsetgray(0.0);
		PSmoveto(1.0,(float)j*12.0+22.0);
		PSshow(data[j].text);
		PSstroke();
	}
}
return self;
}

-(BOOL)acceptsFirstMouse
{
return YES;
}
@end
