[comp.sys.next] columns in a Cell

simsong@daily-bugle.media.mit.edu (Simson L. Garfinkel) (01/11/91)

Over the past few months, I've been trying to develop a Cell subclass that lets me display data in columns. 

Now, there is an internal next object called ColumnCell, but I didn't want to use it because it's undocumented.  So I ended up developing a class called NewsCell.  I've included the source-code for NewsCell in this message.

Basically, NewsCell includes a private data structure that defines the number of columns, the format for each column, and data pointers to the format for each column.  NewsCell can also display icons.  And, best of all, it is a subclass of NXBrowserCell, so you can use it inside a browser (as I do).

NewsCell works by repeatedly calling [super drawSelf] and swapping around self's internal variables, so each call results with different text being drawn in different locations.  Although this is all very elegant and works kind of nicely, it is rather slow.  One of my reasons for wanting to post this is to see if somebody else has a better idea of how to do this.

Anyway, here is NewsCell.h, NewsCell.m, and an example of its use, ArticleCell.[hm]:

/*
 * NewsBrowserCell.h:
 *
 * NewsBrowserCell is a subclass of the TextFieldCell that can display
 * several textfields with user-specified widths and alignmnets.
 *
 * It also has the ability to save the id of the object that it represents,
 * called the "thing."
 *
 * Lastly, it responds to the "visible/setVisible" messages, for use
 * with ConditionalMatrix.
 */

#import <appkit/NXImage.h>
#import <appkit/TextFieldCell.h>
#import <appkit/NXBrowserCell.h>

struct nsformat {
	int	type;
	int	alignment;
	float	width;
	NXImage	*image;			/* for icon cells; display if data */
};

@interface NewsBrowserCell:NXBrowserCell
{
	int		fields;		/* number of format fields*/
	struct nsformat	*nsf;		/* format of fields 	*/
	char		**data;		/* contents 		*/
	NXSize		*image_sizes;	/* sizes of images, or 	*/
}

@end



/* NewsBrowserCell.m
 *
 * A Cell that displays the icon on the left...
 *
 * Based on an idea by William J. Shipley.
 */

#import "NewsBrowserCell.h"

#import <dpsclient/dpsclient.h>
#import <dpsclient/psops.h>
#import <appkit/Text.h>
#import <math.h>
#import <stdlib.h>


@implementation NewsBrowserCell

- drawInside:(const NXRect *)cellFrame inView:controlView
{
	int		field;
	NXRect		fieldFrame 		= *cellFrame;
	char		*original_contents 	= contents;
	int		image=0;

	/* This is fairly gross.
	 * To get multiple fields, we diddle the internal state variables
	 * in the cell and repeatedly call drawInside to draw each set.
	 *
	 * But it works and it seems to be fairly fast.
	 */

	for(field=0;field<fields;field++){
		NX_WIDTH(&fieldFrame)	= nsf[field].width;
		cFlags1.alignment	= nsf[field].alignment;
		contents		=
		  nsf[field].type==NX_TEXTCELL ? data[field] : "";
		[super drawInside:&fieldFrame inView:controlView];

		/* Draw icon if necessary */
		if(nsf[field].type==NX_ICONCELL && data[field]){
			NXPoint	point;

			point.x	= NX_X(&fieldFrame);
			point.y = NX_Y(&fieldFrame);

			if(image_sizes){
				point.x += (NX_WIDTH(&fieldFrame)
					    -image_sizes[image].width)/2;
				point.y += NX_HEIGHT(&fieldFrame)
				  - (NX_HEIGHT(&fieldFrame)
				     -image_sizes[image].height)/2;
			}
			
			[nsf[field].image composite:NX_SOVER toPoint:&point];
			image++;
		}
		NX_X(&fieldFrame)	+= nsf[field].width;
	}
	contents	= original_contents; /* put back for freeing */
	return self;
}

@end



Example of using this cell:

/* 
 * ArticleCell.h:
 *
 * Cell that displays a Article in a matrix.
 */

#import "NewsBrowserCell.h"

@interface ArticleCell:NewsBrowserCell
{
	id	article;
}
 
- setArticle:aArticle;
- article;

@end
  


/*
 * ArticleCell.m:
 *
 * Like iconCell, but asks the Article whether it should be visible
 * or not, and decides which icon to use based on the Article status...
 */

#import "ArticleCell.h"
#import "Article.h"
#import <appkit/Text.h>
#import <stdlib.h>

#define FIELDS 4
#define FIELD_ICON     0
#define FIELD_NUMBER   1
#define	FIELD_AUTHOR   2
#define FIELD_HEADLINE 3

struct nsformat article_format[FIELDS] = {
	NX_ICONCELL, NX_CENTERED,      14.0, nil, 	/* unseen icon    */
	NX_TEXTCELL, NX_RIGHTALIGNED,  55.0, nil, 	/* article number */
	NX_TEXTCELL, NX_LEFTALIGNED,   65.0, nil, 	/* author	  */
	NX_TEXTCELL, NX_LEFTALIGNED,  600.0, nil	/* headline	  */
};


NXSize	unseen_size;

@implementation ArticleCell

/* Create a new cell for a specified Article. */

+ initialize
{
	article_format[0].image	= [NXImage findImageNamed:"unseen"];
	[article_format[0].image getSize:&unseen_size];
	return [super initialize];
}

+ alloc
{
	self			= [super alloc];
	
	data			= calloc(FIELDS,sizeof(char *));
	data[FIELD_NUMBER]	= malloc(16); 		/* space for number */
	return  self;
}

- init
{
	[super init];

	nsf		= article_format;
	fields		= FIELDS;
	image_sizes	= &unseen_size;

	return 	self;
}	

- copy
{
	/* Just copy for cloning; allocates more memory for numeric field. */

	self 	= [super copy];
	data	= calloc(FIELDS,sizeof(char *));
	data[FIELD_NUMBER]	= malloc(16);

	return self;
}

- free
{
	free(data[FIELD_NUMBER]);
	free(data);
	return [super free];
}

- setArticle:aArticle
{
	article	= aArticle;

	sprintf(data[FIELD_NUMBER],"%d",[article number]);
	data[FIELD_ICON]	= [article articleRead] ? 0 : "unseen";
	data[FIELD_AUTHOR]	= (char *)[article sauthor];
	data[FIELD_HEADLINE] 	= (char *)[article headline];

	return self;
}

- article
{
	return article;
}

@end