ftp.nice.ch/Attic/openStep/implementation/gnustep/sources/objcX-0.87.tgz#/objcX-0.87/appkit/NXBrowser.m

This is NXBrowser.m in view mode; [Download] [Up]

/* Implementation of NXBrowser class
 *
 * Copyright (C)  1994  The Board of Trustees of  
 * The Leland Stanford Junior University.  All Rights Reserved.
 *
 * Authors: Mike Gravina, Fred Harris, Paul Kunz, 
 *          Imran Qureshi, and Libing Wang
 *	    Mike L. Kienenberger (Alaska)
 *
 * This file is part of an Objective-C class library for a window system
 *
 * NXBrowser.m,v 1.78 1995/12/13 22:33:09 fedor Exp
 */

/* Implementation notes:
 *
 * Temporarily using MList object for the vertical scrolling columns.
 * This class uses Motif List widget.   Will convert to using Matrix
 * when Matrix class can handle the job
 *
 * shadowList contains a list of matrices corresponding to the columns
 * so that OpenStep API is correct even tho MList objects are created.
 */


#include "NXBrowser.h"

#include "Box.h"
#include "NXBrowserCell.h"
#include "Matrix.h"
#include "Scroller.h"
#include "ScrollView.h"
#include "TextField.h"
#include "MList.h"
#include <objc/List.h>
#include "Text.h"
#include <objc/hashtable.h>
#include "stdmacros.h"

#define MLIST_WIDTH_DIFF 26

/*** Private Methods ***/

@interface NXBrowser(PrivateMethods)
- _calcColumnFrame:(NXRect *)aRect at:(int) column;
- _loadColumn:(int)column;
- (MList *)_createMList;
- _emptyColumnsFrom:(int)column;
- _tileContents;
- _initFrame:(const NXRect *)frameRect;
@end

@implementation NXBrowser(PrivateMethods)

- _calcColumnFrame:(NXRect *)aRect at:(int) column
{
    aRect->origin.x   = column * colWidth;
    aRect->origin.y   = 0;
    aRect->size.width = colWidth - 4;
    if ( isTitled ) {
	aRect->size.height = frame.size.height - titleHeight;
    } else {
	aRect->size.height = frame.size.height;
    }
    if ( hasHorizontalScroller ) {
	aRect->origin.y    += (NX_SCROLLERWIDTH + 4);
        aRect->size.height -= (NX_SCROLLERWIDTH + 4);
    }
    return self;
}

- _loadColumn:(int)column
{
    ScrollView		*sview;
    Matrix		*matrix;
    MList		*mlist;
    NXBrowserCell	*proto;
    NXRect		rect;
    int			rows, visblecol;
    
	matrix = [shadowList objectAt:column];
	if ( !matrix ) {
		proto = [cellPrototype copy];
		matrix = [[matrixClass alloc] initFrame:&rect mode:NX_LISTMODE
				    prototype:proto numRows:0 numCols:1];
		[shadowList addObject:matrix];
	} else {
	    if ([[matrix prototype] class] != [cellPrototype class])
	    {
		id oldMatrix = matrix;
		proto = [cellPrototype copy];
		matrix = [[matrixClass alloc] initFrame:&rect mode:NX_LISTMODE
				    prototype:proto numRows:0 numCols:1];
		[shadowList replaceObjectAt:column with:matrix];
		[oldMatrix free];
	    }
	    else [matrix renewRows:0 cols:1];
	}
	rows = [delegate browser:self fillMatrix:matrix inColumn:column];
	if ([matrix cellCount] > 0) {
		[matrix selectCellAt:0 :0];
	}
	visblecol = column - columnOfViewZero;
	[self _calcColumnFrame:&rect at:visblecol];
	sview = [columnList objectAt:column];
	if (!sview) {
	    sview = [[ScrollView alloc] initFrame:&rect];
	    [sview setVertScrollerRequired:YES];
	    [sview setBorderType:NX_BEZEL];
	    mlist = [self _createMList];
	    [sview setDocView:mlist];
	    [columnList addObject:sview];
		if ((visblecol >= 0) && (visblecol < numVisibleCols)) {
			[self addSubview:sview];
		}
	} else {
		[sview setFrame:&rect];
		mlist = [sview docView];
		if ((visblecol >= 0) && (visblecol < numVisibleCols)
		&& (![sview superview])) {
			[self addSubview:sview];
		} else if ((visblecol < 0) || (visblecol >= numVisibleCols)) {
			[sview removeFromSuperview];
		}
	}
	[mlist fillListFrom:matrix];
	[self reflectScroll:[mlist superview]];
    return self;
}

- (MList *)_createMList
{
    MList	*mlist;
    NXRect	rect = { {0,0}, {100,100} };
    
    rect.size.width = colWidth - MLIST_WIDTH_DIFF;
    rect.size.height = 10;	/* will re-adjust when filled */
    mlist = [[MList alloc] initFrame:&rect];
    [mlist setTarget: self];
    [mlist setAction:@selector(doClick:)];
    [mlist setDoubleAction:@selector(doDoubleClick:)];
    return mlist;
}

- _emptyColumnsFrom:(int)column
{
    Matrix             *matrix;
    ScrollView		*sview;
    int                 i, count;

//	[matrix renewRows:0 cols:1];

	count = [shadowList count];
	for ( i = count -1; i >= column; i-- ) {
		matrix = [shadowList removeObjectAt:i];
		[matrix free];
		if ((sview = [columnList removeObjectAt:i])) {
			[sview removeFromSuperview];
			[sview free];
		}
	}
	return self;
}

- _tileContents
{
 /* BUG: only handling titleFromPrev state */
 
    NXBrowserCell	*aCell;
    ScrollView		*sview;
    Matrix		*matrix;
    const char		*title;
    float		d, pos, percent;
    int			i, visblecol, numSviews, selrow;
	NXRect		rect;
        
    if ( isTitled ) {
		if (columnOfViewZero == 0 ) {
			if ( colOneTitle ) {
				title = colOneTitle;
			} else {
				title = "/";
			}
		} else {
			matrix = [shadowList objectAt:columnOfViewZero - 1];
			aCell = [matrix selectedCell];
			title = [aCell stringValue];
		}
	}
	numSviews = [columnList count];
	for (i = 0; i < numSviews; i++) {
		visblecol = i - columnOfViewZero;
		sview = [columnList objectAt:i];
		if ((visblecol >= 0) && (visblecol < numVisibleCols)) {
			[self _calcColumnFrame:&rect at:visblecol];
			sview = [columnList objectAt:i];
			[sview setFrame:&rect];
			if (![sview superview]) {
				[self addSubview:sview];
			}
			selrow = [[shadowList objectAt:i] selectedRow];
			if (selrow >= 0) {
				[[sview docView] selectCellAt:selrow];
			}
			if (isTitled) {
				[self setTitle:title ofColumn:visblecol];
				if (i < numSviews - 1) {
					matrix = [shadowList objectAt:i];
					aCell  = [matrix selectedCell];
					title = [aCell stringValue];
				}
			}
		} else {
			[sview removeFromSuperview];
		}
	}
	if ( horizScroller ) {
		d = [shadowList count];
		percent = numVisibleCols / d;
		percent = MIN(1.0, percent);
		d -= numVisibleCols;
		if ( d > 0.0 ) {
			pos = columnOfViewZero / d;
			pos = MIN(1.0, pos);
		} else {
			pos = 0.0;
		}
		[horizScroller setFloatValue:pos :percent];
	}
	return self;
}

- _initFrame:(const NXRect *)frameRect
{
  /* invoked by both -initFrame: and -awake */
  
    instancename = "NXBrowser";
    colWidth = frame.size.width / numVisibleCols;
    shadowList = [[List alloc] initCount:numVisibleCols];
    [self setMatrixClass:[Matrix class]];
    [self setCellClass:[NXBrowserCell class]];
    pathSeparator = '/';
    return self;
}

@end

/*** Public Methods ***/

@implementation NXBrowser

- initFrame:(const NXRect *)frameRect
{
    maxVisibleCols = 1;
    numVisibleCols = 1;
    titleHeight = 20;
    isTitled = YES;
    [self getTitleFromPreviousColumn:YES];
    minColWidth = 100;
    [super initFrame:frameRect];
    [self _initFrame:frameRect];
    [self setHorizontalScrollerEnabled:YES];
    return self;
}

- setFrame:(const NXRect *)frameRect
{
	[super setFrame:frameRect];
	numVisibleCols = MIN(frame.size.width / minColWidth, maxVisibleCols);
	if (numVisibleCols) {
		colWidth = frame.size.width / numVisibleCols;
	}
	[self tile];
	return self;
}

- free
{
    [shadowList freeObjects];
    [shadowList free];
    [columnList free]; /* objects in it will be freed as subviews */
    [titleList free];
    [horizScroller free];
    return [super free];
}

- (SEL)action 
{
    return  action;
}

- setAction:(SEL)aSelector
{
    action = aSelector;
    return self;
}

- target
{
    return target;
}

- setTarget:anObject
{
    target = anObject;
    return self;
}

- (SEL)doubleAction
{
    if (doubleAction)
        return doubleAction;
    else
        return action;
}

- setDoubleAction:(SEL)aSelector
{
    doubleAction = aSelector;
    return self;
}

- setMatrixClass:classId
{
    matrixClass = classId;
    return self;
}

- setCellClass:classId
{
    id		proto;
    
    proto = [[classId alloc] init];
  /* give it a controlView to stop warning messages upon -copy */
    [proto _setFrame:(NXRect *)0 inView:self];
    if ( ![proto isKindOf:[NXBrowserCell class]] ) {
        [proto free];
        return self;
    }
    if ( cellPrototype ) {
    	[cellPrototype free];
    }
    cellPrototype = proto;
    return self;
}

- cellPrototype
{
    return cellPrototype;
}

- setCellPrototype:aCell
{
    NXBrowserCell	*oldCell;
    
    if ( ![aCell isKindOf:[NXBrowserCell class]] ) {
    	return nil;
    }
    oldCell = cellPrototype;
    cellPrototype = aCell;
    [cellPrototype _setFrame:(NXRect *)0 inView:self];
    return oldCell;
}

- delegate
{
    return delegate;
}

- setDelegate:aDelegate
{
    delegate = aDelegate;
    return  self;
}

- setEnabled:(BOOL)flag
{
  return [self notImplemented:_cmd];
}

- setMultipleSelectionEnabled:(BOOL)flag
{
  return [self notImplemented:_cmd];
}

- (BOOL)isMultipleSelectionEnabled
{
  [self notImplemented:_cmd];
  return 0;
}

- setBranchSelectionEnabled:(BOOL)flag
{
  return [self notImplemented:_cmd];
}

- (BOOL)isBranchSelectionEnabled
{
  [self notImplemented:_cmd];
  return 0;
}

- setEmptySelectionEnabled:(BOOL)flag
{
  return [self notImplemented:_cmd];
}

- (BOOL)isEmptySelectionEnabled
{
  [self notImplemented:_cmd];
  return 0;
}

- setHorizontalScrollerEnabled:(BOOL)flag
{    
    if ( flag == hasHorizontalScroller ) {
        return self;
    }
    hasHorizontalScroller = flag;
    [self tile];
    return self;
}

- (BOOL)isHorizontalScrollerEnabled
{
    return hasHorizontalScroller;
}

- setHorizontalScrollButtonsEnabled:(BOOL)flag
{
  return [self notImplemented:_cmd];
}

- (BOOL)areHorizontalScrollButtonsEnabled
{
  [self notImplemented:_cmd];
  return 0;
}

- reuseColumns:(BOOL)flag
{
  return [self notImplemented:_cmd];
}

- separateColumns:(BOOL)flag
{
  return [self notImplemented:_cmd];
}

- (BOOL)columnsAreSeparated
{
  [self notImplemented:_cmd];
  return 0;
}

- useScrollButtons:(BOOL)flag
{
  return [self notImplemented:_cmd];
}

- useScrollBars:(BOOL)flag
{
  return [self notImplemented:_cmd];
}

- getTitleFromPreviousColumn:(BOOL)flag
{
    Matrix		*matrix;
    NXBrowserCell	*aCell;
    int                 i;

	if ( titleFromPrev == flag ) 
		return self;
	titleFromPrev = flag;
	if (flag && [self isTitled] ) {
		if (colOneTitle ) {
			[self setTitle:colOneTitle ofColumn:0];
		} else {
			[self setTitle:"/" ofColumn:0];
		}
		for ( i = columnOfViewZero + 1; i < numVisibleCols; i++) {
			matrix = [shadowList objectAt:i-1];
			aCell = [matrix selectedCell];
			[self setTitle:[aCell stringValue] ofColumn:i];
		}
	}
	return self;
}

- (BOOL)isTitled
{
    return isTitled;
}

- setTitled:(BOOL)flag
{
    if ( flag == isTitled ) 
        return self;
	
    isTitled = flag;
    if (flag) {
	if (titleHeight <= 0) {
	    titleHeight = 20;
	}
    }
    [self tile];
    return self;
}


- (NXRect *)getTitleFrame:(NXRect *)theRect ofColumn:(int)column
{
  [self notImplemented:_cmd];
  return 0;
}

- setTitle:(const char *)aString ofColumn:(int)column
{
    [[titleList objectAt:column] setStringValue:aString];
    return self;
}

- (const char *)titleOfColumn:(int)column 
{
    if (column >= [columnList count])  {
        return NULL;
	}
    return [[titleList objectAt:column - columnOfViewZero] stringValue];
}

- drawTitle:(const char *)aTitle inRect:(const NXRect *)aRect
    ofColumn:(int)column
{
    [[titleList objectAt:column] setStringValue:aTitle];
    return self;
}

- clearTitleInRect:(const NXRect *)aRect ofColumn:(int)column
{
    [self drawTitle:"\0" inRect:aRect ofColumn:column];

    return self;
}

- (NXCoord)titleHeight
{
    return titleHeight;
}

- loadColumnZero
{
    [self _emptyColumnsFrom:1];
    columnOfViewZero = 0;
    [self _loadColumn:0];
    [self tile];
    return self;
}


- (BOOL)isLoaded
{
  [self notImplemented:_cmd];
  return 0;
}

- setPathSeparator:(unsigned short)charCode
{
   pathSeparator = charCode;
   return self;
}

- setPath:(const char *)aPath
{
	char *thePath, *pathPtr;
	int currentCol, rows, cols, j;
	int i = 0;
	Matrix *matrix;
	BOOL isMore = YES;
	const char *cellString;

	if (!aPath)
		return self;

	thePath = malloc((strlen(aPath) + 1) * sizeof(char));
	strcpy(thePath, aPath);
	pathPtr = thePath;

	if ((pathPtr[0] != (char)pathSeparator) && (selectedColumn > 0)) {
		currentCol = selectedColumn;
	} else {
		currentCol = 0;
		pathPtr++;
		[self loadColumnZero];
	}
	while (isMore) {
		for (i = 0;
                     ((pathPtr[i]!=(char)pathSeparator) && (pathPtr[i]!='\0'));
                     i++);
		if ((isMore = (pathPtr[i] == (char)pathSeparator))) {
			pathPtr[i] = '\0';
		}
		matrix = [shadowList objectAt:currentCol];
		[matrix getNumRows:&rows numCols:&cols];
		for (j = 0; j < rows; j++) {
			cellString = [[matrix cellAt:j :0] stringValue];
			if (!strcmp(cellString, pathPtr)) {
 				[matrix selectCellAt:j :0];
				selectedColumn = currentCol;
				[self _loadColumn:++currentCol];
				break;
			}
		}
		if (j == rows) {
			free(thePath);
			return self;
		}
		pathPtr += (i+1);
	}
	free(thePath);
	columnOfViewZero = MAX(currentCol - numVisibleCols + 1, 0);
//	[self _tileContents];
	[self tile];
	return self;
}

- (char *)getPath:(char *)thePath toColumn:(int)column;
{
    Cell	*aCell;
    Matrix	*matrix;
    int		i,lenPath;
    const char  *currentName;
    
    if ( !thePath ) {
        return NULL;
    }
    strcpy( thePath, "" );
    lenPath = 0;
    for ( i = 0; i <= column; i++ ) {
        matrix = [shadowList objectAt:i];
		aCell = [matrix selectedCell];
		if ( aCell ) {
/* don't add extra pathSeparators if they are included in the path already */
                    if (lenPath)
                        if (thePath[lenPath-1] == (char)pathSeparator)
                            goto addCurrentName ;
                    thePath[lenPath++] = (char)pathSeparator;
                  addCurrentName:
                    currentName = [aCell stringValue];
		    strcpy( &thePath[lenPath], currentName );
                    lenPath += strlen(currentName);
		}
    }
    return thePath;
}

- displayColumn:(int)column
{
  return [self notImplemented:_cmd];
}

- reloadColumn:(int)index
{
    [self _loadColumn:index];
    return self;
}

- validateVisibleColumns
{
  return [self notImplemented:_cmd];
}

- displayAllColumns
{
    return self;
}

- scrollColumnsRightBy:(int)shiftAmount
{
    int		i;
    
    i = columnOfViewZero - shiftAmount;
    columnOfViewZero = ( i<0 ) ? 0 : i;
//	[self _tileContents];
	[self tile];
    return self;
}

- scrollColumnsLeftBy:(int)shiftAmount
{
    int		i, last, cols = [shadowList count];
    
    i = columnOfViewZero + shiftAmount;
    last = columnOfViewZero + numVisibleCols - 1;
    if ( last >= cols ) {
        return [self setLastColumn:last-1];
    }
    columnOfViewZero = i;
//	[self _tileContents];
	[self tile];
	return self;
}

- scrollColumnToVisible:(int)column
{
  return [self notImplemented:_cmd];
}

- scrollUpOrDown:sender
{
  return [self notImplemented:_cmd];
}

- reflectScroll:clipView
{
    ScrollView	*scrollview;
    
    scrollview = [clipView superview];
    [scrollview reflectScroll:clipView];
    return self;
}

- scrollViaScroller:sender
{
    float	f;
    int		i, hit, count;
    
    hit = [sender hitPart];
    switch (hit) {
    case NX_KNOB :
        f = [sender floatValue];
	count = [shadowList count];
	if ( count > numVisibleCols ) {
	    i = floor( f * (count - numVisibleCols) + 0.5 );
	    if ( i == columnOfViewZero ) {
	        return self;
	    }
	    if ( i > columnOfViewZero ) {
	    	[self scrollColumnsLeftBy:1];
	    } else {
	    	[self scrollColumnsRightBy:1];
	    }
	} else {
	    return self;
	}
        break;
    case NX_INCLINE :
    case NX_INCPAGE :
        [self scrollColumnsLeftBy:1];
        break;
    case NX_DECLINE :
    case NX_DECPAGE :
	[self scrollColumnsRightBy:1];
        break;
    case NX_NOPART :
    default:
    	break;
    }
    return self;
}

- updateScroller
{
  return [self notImplemented:_cmd];
}

- setLastColumn:(int)column
{
    int		i, cols = [shadowList count];
        
    if ( column >= cols ) {
        return self;
    }
    i = column - numVisibleCols + 1;
    columnOfViewZero = (i < 0 ) ? 0 : i;
//	[self _tileContents];
	[self tile];
	return self;
}

- addColumn
{
    Matrix		*matrix;
    NXBrowserCell	*proto;
    NXRect		rect = { {0,0}, {0,0} };	/* dummy for now */
    int			cols = [shadowList count];

    proto = [cellPrototype copy];
    matrix = [[matrixClass alloc] initFrame:&rect mode:NX_LISTMODE
    			     prototype:proto numRows:0 numCols:1];
    [matrix setCellClass:[cellPrototype class]];
    [shadowList addObject:matrix];
    [self _loadColumn:cols];
    if ( cols >= numVisibleCols ) {
	[self setLastColumn:cols];
    }
    return self;
}

- setMinColumnWidth:(int)columnWidth
{
	if (columnWidth < 1)
		return self;
    minColWidth = columnWidth;
	numVisibleCols = MIN(frame.size.width / minColWidth, maxVisibleCols);
	if (numVisibleCols) {
		colWidth = frame.size.width / numVisibleCols;
	}
    [self tile];
    return self;
}

- (int)minColumnWidth
{
  [self notImplemented:_cmd];
  return 0;
}

- setMaxVisibleColumns:(int)columnCount
{
	if (columnCount < 1)
		return self;
	maxVisibleCols = columnCount;
	numVisibleCols = MIN(frame.size.width / minColWidth, maxVisibleCols);
	if (numVisibleCols) {
		colWidth = frame.size.width / numVisibleCols;
	}
    [self tile];
    return self;
}

- (int)maxVisibleColumns
{
  [self notImplemented:_cmd];
  return 0;
}

- (int)numVisibleColumns
{
  [self notImplemented:_cmd];
  return 0;
}

- (int)firstVisibleColumn
{
  [self notImplemented:_cmd];
  return 0;
}

- (int)lastVisibleColumn
{
  [self notImplemented:_cmd];
  return 0;
}

- (int)lastColumn
{
  [self notImplemented:_cmd];
  return 0;
}

- (int)selectedColumn
{
   return  selectedColumn;
}

- selectedCell
{
    return [[shadowList objectAt:selectedColumn] selectedCell];
}

- getSelectedCells:(List *)aList
{
    List		*selList = nil;
    Matrix		*matrix;

    matrix = [shadowList objectAt:selectedColumn];
    selList = [matrix getSelectedCells:aList];
    return selList;
}

- (int)columnOf:matrix
{
    return [shadowList indexOf:matrix];
}

- matrixInColumn:(int) index
{
    return [shadowList objectAt:index];
}

- getLoadedCellAtRow:(int)row inColumn:(int)col
{
  [self notImplemented:_cmd];
  return 0;
}

- selectAll:sender
{
  [self notImplemented:_cmd];
  return 0;
}

- (NXRect *)getFrame:(NXRect *)theRect ofColumn:(int)column
{
  [self notImplemented:_cmd];
  return 0;
}

- (NXRect *)getFrame:(NXRect *)theRect ofInsideOfColumn:(int)column
{
  [self notImplemented:_cmd];
  return 0;
}

- tile
{
	NXBrowserCell *aCell;
	Matrix *matrix;
    MList	*mlist;
    ScrollView	*sview;
    TextField	*textfield;
    NXRect	rect;
    float	d, pos, percent;
    int		i, j, colCount, visblecol, selrow;
 
 /* First get the right number of components */      
    if ( !columnList ) {
        columnList = [[List alloc] initCount:numVisibleCols];
    }
	if ( isTitled ) {
		if ( !titleList ) {
			titleList = [[List alloc] initCount:numVisibleCols];
		}
	} else if ( titleList ) {
		[titleList freeObjects];
		[titleList free];
    }
    if ( hasHorizontalScroller ) {
		rect.origin.x = 0;
		rect.origin.y = 0;
		rect.size.width = frame.size.width - CLIPVIEW_MARGIN;
		rect.size.height = NX_SCROLLERWIDTH;
		if ( !horizScroller ) {
	    	horizScroller = [[Scroller alloc] initFrame:&rect];
			[horizScroller setTarget:self];
			[horizScroller setAction:@selector(scrollViaScroller:)];
	    	[self addSubview:horizScroller];
		} else {
			[horizScroller setFrame:&rect];
		}
		d = [columnList count];
		if ( d > 0.0 ) {
			percent = numVisibleCols / d;
			percent = MIN(1.0, percent);
			d -= numVisibleCols;
			if (d > 0.0) {
				pos = columnOfViewZero / d;
				pos = MIN(1.0, pos);
			} else {
				pos = 0.0;
			}
		} else {
			percent = 1.0;
			pos = 0.0;
		}
		[horizScroller setFloatValue:pos :percent];
    }
	if (isTitled) {
		i = [titleList count] - numVisibleCols;
		while (i > 0) {
			[[titleList removeLastObject] free];
			i--;
		}
	}
	
	/* Remove extra empty columns and matrices to the right.  They
		get removed if they are farther right than the last visible
		column and if they do not represent an empty branch
		(thus we check to see if the previous column is empty and
		if it is not then we check to see if the selected cell is
		a leaf). */
	i = [columnList count] - columnOfViewZero - numVisibleCols;
	j = [columnList count] - 1;
	while (i > 0) {
		if ([[shadowList objectAt:j-1] cellCount] == 0) {
			[[shadowList removeLastObject] free];
			[[columnList removeLastObject] free];
			i--;
			j--;
		} else if ([[[shadowList objectAt:j-1] selectedCell] isLeaf]) {
			[[shadowList removeLastObject] free];
			[[columnList removeLastObject] free];
			i--;
			j--;
		} else {
			i = 0;
		}
	}

	while (i < 0) {
		[self _calcColumnFrame:&rect at:numVisibleCols + i];
		sview = [[ScrollView alloc] initFrame:&rect];
		[sview setVertScrollerRequired:YES];
		[sview setBorderType:NX_BEZEL];
		mlist = [self _createMList];
		[sview setDocView:mlist];
		[columnList addObject:sview];
		[self addSubview:sview];
		rect.origin.x = rect.origin.y = 0;
		rect.size.width = rect.size.height = 0;
		aCell = [cellPrototype copy];
		matrix = [[matrixClass alloc] initFrame:&rect mode:NX_LISTMODE
					prototype:aCell numRows:0 numCols:1];
		[matrix setCellClass:[cellPrototype class]];
		[shadowList addObject:matrix];
		i++;
	}
  /* Now do the tiling */
	colCount = [columnList count];
	for ( i = 0; i < colCount; i++ ) {
	if ((isTitled) && (i < numVisibleCols)) {
	    rect.origin.x = i * colWidth;
	    rect.origin.y = frame.size.height - titleHeight;
	    rect.size.width = colWidth;
	    rect.size.height = titleHeight;
	    if (![titleList objectAt:i]) {
	        textfield = [[TextField alloc] initFrame:&rect];
			[textfield _initAsLabel];
	        [textfield setAlignment:NX_CENTERED];
	        [titleList addObject:textfield];
	        [self addSubview:textfield];
	    } else {
			textfield = [titleList objectAt:i];
			[textfield setFrame:&rect];
	    }
	}
	visblecol = i - columnOfViewZero;
	if ((visblecol >= 0) && (visblecol < numVisibleCols)) {
		[self _calcColumnFrame:&rect at:visblecol];
		sview = [columnList objectAt:i];
		[sview setFrame:&rect];
		if (![sview superview]) {
			[self addSubview:sview];
			selrow = [[shadowList objectAt:i] selectedRow];
			if (selrow >= 0) {
				[[sview docView] selectCellAt:selrow];
			}

		}
		if (isTitled) {
	    	if (i == 0) {
	       		if (colOneTitle) {
					[self setTitle:colOneTitle ofColumn:0];
				} else {
					[self setTitle:"/" ofColumn:0];
				}
			} else {
				matrix = [shadowList objectAt:i - 1];
				if ([matrix selectedRow] == -1) {
					[self setTitle:"" ofColumn:visblecol];
				} else {
					aCell = [matrix selectedCell];
					if ([aCell isLeaf]) {
						[self setTitle:"" ofColumn:visblecol];
					} else {
						[self setTitle:[aCell stringValue] ofColumn:visblecol];
					}
				}
			}
		}
	} else {
		[[columnList objectAt:i] removeFromSuperview];
	}
	}
    return self;
}

- drawSelf:(const NXRect *)rects :(int)rectCount
{
  return self;
}

- mouseDown:(NXEvent *)theEvent
{
  [self notImplemented:_cmd];
  return 0;
}

- sizeTo:(NXCoord)width :(NXCoord)height
{
  [self notImplemented:_cmd];
  return 0;
}

- sizeToFit
{
  [self notImplemented:_cmd];
  return 0;
}

- acceptArrowKeys:(BOOL)acceptFlag andSendActionMessages:(BOOL)sendFlag
{
  [self notImplemented:_cmd];
  return 0;
}

- keyDown:(NXEvent *)theEvent
{
  [self notImplemented:_cmd];
  return 0;
}

- (BOOL)acceptsFirstResponder
{
  [self notImplemented:_cmd];
  return 0;
}

- doClick:sender
{
 /* sender will be a MList managing the List widget */

    List		*selList = nil;
    Matrix		*matrix;
    MList		*mlist;
    NXBrowserCell	*aCell;
    ScrollView		*sview;
    int                 i, count, newColumn;

  /* find the column of sender */
	for ( i = columnOfViewZero; i < columnOfViewZero + numVisibleCols; i++ ) {
		sview = [columnList objectAt:i];
		mlist = [sview docView];
		if ( mlist == sender ) {
			selectedColumn = i;
			break;
		}
	}
	matrix = [shadowList objectAt:selectedColumn];
	selList = [sender getSelectedCells:selList from:matrix];
	count = [selList count];
	for (i = 0; i < count; i++) {
		aCell = [selList objectAt:i];
		[matrix selectCell:aCell];
	}
	if (count == 1) {
		newColumn = selectedColumn + 1;
		if ([aCell isLeaf]) {
			[self _emptyColumnsFrom:newColumn];
		} else {
			if ( isTitled ) {
				[self setTitle:[aCell stringValue] 
					ofColumn:selectedColumn + 1];
			}
			if (newColumn < [shadowList count]) {
				[self _emptyColumnsFrom:newColumn + 1];
				[self _loadColumn:newColumn];
			} else if ((newColumn < numVisibleCols)
			|| (hasHorizontalScroller)) {
				[self addColumn];
			}
		}
		[self tile];
	}
    if (action && target) {
		[target perform:action with:self];
	}
    return self;
}

- doDoubleClick:sender
{
    if (!target)
	return self;
    if (doubleAction) {
	[target perform:doubleAction with:self];
    } else {
	[target perform:action with:self];
    }
    return self;
}

-  sendAction
{
    [target perform:action with:self];
    return self;
}

// + newFrame:(const NXRect *)frameRect;

- read:(TypedStream *)stream
{
    short	_reserved4[4];
    
    [super read:stream];    
    objc_read_types(stream, "cc", &hasHorizontalScroller, &isTitled);
    objc_read_type(stream, "s", &_reserved4[2]); /* number of columns */
    numVisibleCols = _reserved4[2];
    if ( isTitled ) {
        objc_read_types(stream, "i*", &titleHeight, &colOneTitle);
    }

    return self;
}

- awake
{
    /* the following might need to be read in or computed from read: */
    minColWidth = 100;
    maxVisibleCols = 1;
    
    [super awake];
    [self _initFrame:&frame];
    if (isTitled) {
	[self setTitled:YES];
	[[titleList objectAt:0] setStringValue:colOneTitle];
    }
    [self tile];
    return self;
}

@end

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.