ftp.nice.ch/pub/next/connectivity/news/NewsBase.3.02.s.tar.gz#/NewsBase302.source/NNTP/INewsBase.m

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

/*$Copyright:
 * Copyright (C) 1992.5.22. Recruit Co.,Ltd. 
 * Institute for Supercomputing Research
 * All rights reserved.
 * NewsBase  by ISR, Kazuto MIYAI, Gary ARAKAKI, Katsunori SUZUKI, Kok-meng Lue
 *
 * You may freely copy, distribute and reuse the code in this program under 
 * following conditions.
 * - to include this notice in the source code, if it is to be distributed 
 *   with source code.
 * - to add the file named "COPYING" within the code, which shall include 
 *   GNU GENERAL PUBLIC LICENSE(*).
 * - to display an acknowledgement in binary code as follows: "This product
 *   includes software developed by Recruit Co.,Ltd., ISR."
 * - to display a notice which shall state that the users may freely copy,
 *   distribute and reuse the code in this program under GNU GENERAL PUBLIC
 *   LICENSE(*)
 * - to indicate the way to access the copy of GNU GENERAL PUBLIC LICENSE(*)
 *
 *   (*)GNU GENERAL PUBLIC LICENSE is stored in the file named COPYING
 * 
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
$*/
#import "INewsBase.h"
#import "INntpIO.h"
#import "IDataGroupBrowser.h"
#import "IItemHeaderBrowser.h"
#import "INXBrowserCellWithLinkedObject.h"
#import "INewsgroupInfoD.h"
#import "IUifNode.h"
#import "SideSplitView.h"
#import "IBaseControl.h"
#import "IFolderDockMatrix.h"
#import "data_types.h"
#import <appkit/appkit.h>
#import <stdio.h>
#import <string.h>
#import <mach/mach.h>
#import <defaults/defaults.h>
#import "data_types.h"
#import "errdebug.h"
#import "Localization.h"

#define LoStr(key)      doLocalString(NULL,key,NULL)


//#define         NNTPHOST        "isrgwy"


@implementation INewsBase

+ (char *)label
{
    return ("NewsBase...");
}

- init
{
    BrowserMode		newsgroupMode, articleMode;
    id 			loadingAlertPanel;
    NXModalSession 	loadingModalSession;

    [super init];
    iNntpIO = nil;

    /* make zone for NewsBase */
    newsbaseZone = NXCreateZone (vm_page_size, vm_page_size, YES);
    /* load window into screen */
    LoadLocalNib("NewsBase.nib",self,NO,newsbaseZone);
/*
    [NXApp loadNibSection:"NewsBase.nib" owner:self 
    				withNames:NO fromZone:(NXZone *)newsbaseZone];
*/
    /* open openNewsBasePanel */
    if ([self openNewsBasePanel] == 0) {
	/* click cancel */
        [NXApp delayedFree:self];
	return(nil);
    } 
    /* click OK */
    /* read default database for newsgroupMode & articleMode */
    if (strcmp(NXGetDefaultValue(OWNER, NEWSGROUPBROWSERMODE),"List")==0) {
	newsgroupMode = Flat;
    } else {
	newsgroupMode = Tree;
    }
    if (strcmp(NXGetDefaultValue(OWNER, ARTICLEBROWSERMODE),"ByReference")
        == 0) {
	articleMode = Tree;
    } else if (strcmp(NXGetDefaultValue(OWNER, ARTICLEBROWSERMODE),"BySubject")
        == 0) {
	articleMode = BySubject;
    } else if (strcmp(NXGetDefaultValue(OWNER, ARTICLEBROWSERMODE),"ByKeyword")
        == 0) {
	articleMode = ByKeyword;
    } else {
	articleMode = Flat;
    }
    [iNntpIO setArticleMode:articleMode];

    /* open connection to nntp server */
    loadingAlertPanel = NXGetAlertPanel(LoStr("NewsBase"), 
	LoStr("Connecting to nntp server..., please wait"),
	NULL, NULL, NULL);
    [NXApp beginModalSession:&loadingModalSession for:loadingAlertPanel];

    iNntpIO = [[INntpIO allocFromZone:newsbaseZone] 
	     			initServer:iNntpHost allNews:iAllNewsFlag
				newsGroupMode:(BrowserMode)newsgroupMode
				articleMode:(BrowserMode)articleMode];
    if (iNntpIO==nil) {
	/* failed to connect to server */
	/* stop modal loop */
	[NXApp endModalSession:&loadingModalSession];
	[loadingAlertPanel orderOut:self];
	NXFreeAlertPanel(loadingAlertPanel);
	/* free objects */
	[oNewsReader free];
//	[oViewControlPanel free];
	[iNntpIO free];
	[super free];
	return nil;
    }

    [NXApp endModalSession:&loadingModalSession];
    [loadingAlertPanel orderOut:self];
    NXFreeAlertPanel(loadingAlertPanel);

    // set hierarchy of view and size from user's default data base
    [self _readWindowInfoFromDefaultDBandSetupViews];
    
    /* make NewsBase window */
    [self _setUpInterfaceWindow];

    return self;
}

- _readWindowInfoFromDefaultDBandSetupViews
{
    const char *buffer;
    NXRect 	newsbaseRect;
    NXSize folderDockSize, browserSplitViewSize, leftSideSize, rightSideSize;
    NXRect	frameRect;

    /* setup for side spilited view, newsgroup browser and article browser */
    [oNewsgroupView setAutoresizeSubviews:YES];
    [oNewsgroupView addSubview:oNewsfolderScroll];
    [oNewsgroupView addSubview:oNewsGroupNXBrowser];
    
    if ((buffer = NXGetDefaultValue(OWNER, NNTPBROWSER_FRAME)) != 0 &&
        sscanf(buffer, "(%f,%f):(%f,%f):(%f,%f):(%f,%f):(%f,%f):(%f,%f)", 
		&newsbaseRect.origin.x,&newsbaseRect.origin.y,
		&newsbaseRect.size.width, &newsbaseRect.size.height,
		&folderDockSize.width, &folderDockSize.height,
		&browserSplitViewSize.width, &browserSplitViewSize.height,
		&leftSideSize.width, &leftSideSize.height,
		&rightSideSize.width, &rightSideSize.height
	) == 12) {
        [oNewsReader placeWindow:&newsbaseRect];
    	[oNewsgroupView sizeTo:leftSideSize.width :leftSideSize.height];
	[oArticleNXBrowser sizeTo:rightSideSize.width :rightSideSize.height];
	[oNewsfolderDock sizeTo:folderDockSize.width :folderDockSize.height];
	[oNewsfolderDockMatrix sizeTo:folderDockSize.width :folderDockSize.height];
	[oBrowserSplitView sizeTo:browserSplitViewSize.width
				 :browserSplitViewSize.height];
    }
    
    /* setup side split view */
    [oBrowserSplitView setRightView:oArticleNXBrowser];
    [oBrowserSplitView setLeftView:oNewsgroupView];

    /* setup for newsgroup folder dock and bowser */
    [oSplitView addSubview:oNewsfolderDock];
    [oSplitView addSubview:oBrowserSplitView];
    [oBrowserSplitView initViews];

    [oNewsfolderScroll setAutoresizeSubviews:YES];
    

//    [oNewsfolderScrollMatrix notifyAncestorWhenFrameChanged:YES];
    
    [oNewsfolderScrollMatrix getFrame:&frameRect];
    frameRect.origin.x = frameRect.origin.y = 0.0;
    [oNewsfolderScrollMatrix setFrame:&frameRect];

    [oNewsfolderScrollDocView getFrame:&frameRect];
    frameRect.origin.x = frameRect.origin.y = 0.0;
    [oNewsfolderScrollDocView setFrame:&frameRect];

    [oNewsfolderScrollDocView addSubview:oNewsfolderScrollMatrix];
    [oNewsfolderScrollDocView setAutoresizeSubviews:NO];
    [oNewsfolderScroll setDocView:oNewsfolderScrollDocView];
    
    // set delegate for resizing behavior when sliding divider bar
//    [oSplitView setDelegate:oNewsfolderDockMatrix];
    [oSplitView setDelegate:self];

    // set root cell (next station icon) for newsgroup folder view
    // oNewsfolderScroll is scroll view, 
    // which document view is IFolderScrollMatrix
    [oNewsfolderScrollMatrix setRootName:[iNntpIO rootName]];
    [oNewsfolderDockMatrix setRootName:[iNntpIO rootName]];

    [[oNewsReader contentView] displayFromOpaqueAncestor:NULL :0 :YES];
    
    [oNewsfolderDockMatrix registerCells];
    
    return self;
}

- _setUpInterfaceWindow
{
    /*
     * The objects in this module are:
     *     INewsBase initializes the module.
     *     NewsGroupTree holds the newsgroups in a tree structure.
     *     NewsGroupTreeControl initializes and maintains the NewsGroupTree.
     */
     
    /* set up news group browser */
    [oNewsGroupNXBrowser setIOmodule:iNntpIO];
    if ([iNntpIO newsGroupMode]==Flat) {
	[oNewsGroupNXBrowser setMaxVisibleColumns:1];
    } else {
	[oNewsGroupNXBrowser setMaxVisibleColumns:4];
    }

    /* set up article browser */
    [oArticleNXBrowser setIOmodule:iNntpIO];
    if ([iNntpIO articleMode]==Flat) {
	[oArticleNXBrowser setMaxVisibleColumns:1];
    } else {
	[oArticleNXBrowser setMaxVisibleColumns:2];
    }

    /* load cell into NewsGroupBrowser */
    [oNewsGroupNXBrowser setRootListnode:[iNntpIO subDirectoryOf:nil]];
    [oNewsGroupNXBrowser loadColumnZero];

    /* set up interaction of DataGroupBrowser,ItemRepBrowser,ItemBrowser */
    [oNewsGroupNXBrowser setItemRepUIF:oArticleNXBrowser];
    [oArticleNXBrowser setDataGroupUIF:oNewsGroupNXBrowser];

    /* set control panel */
    [oNewsReader setDelegate:self];
    
    [self setArticleBrowserMode];

    /* set up window for getting article number */
    [iNntpIO setOGetArticleNumWindow:oGetArticleNumWindow];
    [iNntpIO setOGetArticleNumField:oGetArticleNumField];
    [iNntpIO setIPercentageView:oPercentageView];
    
    [oArticleNumOKButton setTarget:iNntpIO];
    [oArticleNumCancelButton setTarget:iNntpIO];
    [oArticleNumOKButton setAction:@selector(okArticleNumWindow:)];
    [oArticleNumCancelButton setAction:@selector(cancelArticleNumWindow:)];

    DBG(10, fprintf(stderr," NewsBase is made\n"));
       
    return self;
}

- (char *)label
{
    static char	labelString[64];
    
    sprintf (labelString, "NewsBase: %.53s", iNntpHost);
    return labelString;
}

- show:sender
{
    unsigned int windowNum;
    NXRect	oldRect;

    [oNewsReader display];
    [oNewsReader makeKeyAndOrderFront:self];
    // register window for accepting file icons
    NXConvertWinNumToGlobal([oNewsReader windowNum], &windowNum);
    [[NXApp appSpeaker] setSendPort:NXPortFromName(NX_WORKSPACEREQUEST, NULL)];
    [[NXApp appSpeaker] registerWindow:windowNum
        toPort:[[NXApp appListener] listenPort]];

    // these statements should not be needed here. but
    // I could not register rectangle of cells during setting up views
    // in "_readWindowInfoFromDefaultDBandSetupViews" above.
    [oNewsfolderDockMatrix registerCells];
    [oNewsfolderScrollMatrix registerCells];
    [oSplitView getFrame:&oldRect];
    [self splitView:oSplitView 
    			resizeSubviews:(const NXSize *)&(oldRect.size)];
    
    return(self);
}

- terminateProc
{
    [self _saveWindowInfoToDefaultDB];
    return self;
}

- (BOOL)postArticle:(const char *)article length:(int)length
{
    return([iNntpIO postArticle:article length:length]);
}

- (BOOL)sendArticle:(const char *)messageId
{
    return([iNntpIO sendArticle:messageId]);
}

- newsGroupTreeControl
{
    return iNntpIO;
}

- toggleNewsGroupBrowser:sender
{
    if ([iNntpIO toggleNewsGroupMode:sender]==Flat) {
	[oNewsGroupNXBrowser setMaxVisibleColumns:1];
        NXWriteDefault(OWNER, NEWSGROUPBROWSERMODE, "List");
    } else {
	[oNewsGroupNXBrowser setMaxVisibleColumns:4];
        NXWriteDefault(OWNER, NEWSGROUPBROWSERMODE, "Browser");
    }
    /* reset newsgroup browser and redraw new browser */
    [oNewsGroupNXBrowser setRootListnode:[iNntpIO subDirectoryOf:nil]];
    [oNewsGroupNXBrowser loadColumnZero];
    return self;
}

- toggleArticleBrowser:sender
{
    [iNntpIO toggleArticleMode:sender];
    [self setArticleBrowserMode];
    return(self);
}

- setArticleBrowserMode
{
    id		node;
    id		itemHeaderslist;
    
    switch ([iNntpIO articleMode]) {
    case Flat:
    	[oArticleNXBrowser setMaxVisibleColumns:1];
	[oArticleNXBrowser setTitle:LISTCOL0TITLE ofColumn:0];
        NXWriteDefault(OWNER, ARTICLEBROWSERMODE, "List");
        break;
    case Tree:
	[oArticleNXBrowser setMaxVisibleColumns:2];
	[oArticleNXBrowser setTitle:BROWSERCOL0TITLE ofColumn:0];
        NXWriteDefault(OWNER, ARTICLEBROWSERMODE, "ByReference");
        break;
    case BySubject:
    	[oArticleNXBrowser setMaxVisibleColumns:2];
	[oArticleNXBrowser setTitle:SUBJECTCOL0TITLE ofColumn:0];
        NXWriteDefault(OWNER, ARTICLEBROWSERMODE, "BySubject");
        break;
    case ByKeyword:
	[oArticleNXBrowser setTitle:KEYWORDCOL0TITLE ofColumn:0];
    	[oArticleNXBrowser setMaxVisibleColumns:2];
        NXWriteDefault(OWNER, ARTICLEBROWSERMODE, "ByKeyword");
        break;
    }
    /* reset article browser */
    node = [self _nodeForSelectedCellInBrowser:oNewsGroupNXBrowser];
    itemHeaderslist = [iNntpIO itemHeadersOf:node];

    /* update article browser */
    [oArticleNXBrowser setRootListnode:itemHeaderslist];
    [oArticleNXBrowser loadColumnZero];
    return(self);
}

- setReadFlagToAll:sender
{
    id		node;
    id		itemHeaderslist;

    [iNntpIO setReadFlag:(ReadFlag)ALL];
    node = [self _nodeForSelectedCellInBrowser:oNewsGroupNXBrowser];
    itemHeaderslist = [iNntpIO itemHeadersOf:node];

    /* update article browser */
    [oArticleNXBrowser setRootListnode:itemHeaderslist];
    [oArticleNXBrowser loadColumnZero];

    /* set read flag to default */
    [iNntpIO setReadFlag:(ReadFlag)UNMARKEDONLY];    
    return self;
}

- markAllArticleAsRead:sender
{
    id		lastColumnMatrix;
    
    switch(NXRunAlertPanel(LoStr("NewsBase"),
	LoStr("Cannot Undo! all article will be marked as read."),
	LoStr("OK"),LoStr("Cancel"),NULL)) {
        case NX_ALERTDEFAULT:
	    lastColumnMatrix = [oNewsGroupNXBrowser matrixInColumn:
    					[oNewsGroupNXBrowser lastColumn]];
	    [lastColumnMatrix sendAction:@selector(markAllArticleOfCell:)
	    					     to:self forAllCells:NO];
            break;
        case NX_ALERTALTERNATE:
            return(self);
    }
    /* reset article browser */
    [oArticleNXBrowser setRootListnode:nil];
    [oArticleNXBrowser loadColumnZero];
    return self;
}

- markAllArticleOfCell:sender
{
    /* this method will be called from oNewsGroupNXBrowser's cell */
    /* "markAllArticleAsRead:" will triger it */
    id		newsGroup;
    id		groupInfo;
    
    newsGroup = [[sender node] linkedData];
    groupInfo = [newsGroup dataForKey:GROUPINFO];
    
    [groupInfo markAllArticle];
    return self;
}

- getNewArticles:sender
{
    if ([iNntpIO reconnectServer:sender] == nil) {
	NXRunAlertPanel(LoStr("NewsBase"),
	    LoStr("Can not recconect to nntp server."), LoStr("OK"),NULL,NULL);
	[NXApp terminate:self];
    }
    [oArticleNXBrowser setRootListnode:nil];
    [oArticleNXBrowser loadColumnZero];

    return self;
}

- unSubscribeGroup:sender
{
    id		lastColumnMatrix;

    switch(NXRunAlertPanel(LoStr("NewsBase"),
	LoStr("Cannot Undo! Selected newsgroup in browser will be unsubscribed."),
	LoStr("OK"),LoStr("Cancel"),NULL)) {
        case NX_ALERTDEFAULT:
	    lastColumnMatrix = [oNewsGroupNXBrowser matrixInColumn:
    					[oNewsGroupNXBrowser lastColumn]];
	    [lastColumnMatrix sendAction:@selector(unSubscribeGroupOfCell:)
	    					     to:self forAllCells:NO];
	    /* reset oNewsGroupNXBrowser browser */
	    [lastColumnMatrix display];
            break;
        case NX_ALERTALTERNATE:
            break;
    }
    return (self);
}

- unSubscribeGroupOfCell:sender
{
    /* this method will be called from oNewsGroupNXBrowser's cell */
    /* "unSubscribeGroup:" will triger it */
    id		node;
    id		groupInfo;
    
    node = [sender node];
    groupInfo = [[node linkedData] dataForKey:GROUPINFO];
    
    [groupInfo addInfoString:"no" key:SUBSCRIBE];
    [node setActive:NO];
    [sender setActive:NO];
    
    return self;
}
   
- _nodeForSelectedCellInBrowser:kbrowser
{
    id		lastColumnMatrix;
    id		selectedCell;

    lastColumnMatrix = [kbrowser matrixInColumn:[kbrowser lastColumn]];
    selectedCell = [lastColumnMatrix selectedCell];
    
    return [selectedCell node];
}

- (int)openNewsBasePanel
{
    const char	*servername;
    int returnCode;
    
    servername = NXGetDefaultValue(OWNER, NNTPSERVER);

    DBG(10,fprintf(stderr,"default value nntpserver=%s",servername));
    [oOpenNewsBasePanel makeKeyAndOrderFront:self];
    [oNntpServerNameField setStringValue:(char *)servername];

    returnCode = [NXApp runModalFor:oOpenNewsBasePanel];
    [oOpenNewsBasePanel free];
    return(returnCode);
}

- cancelConnect:sender
{
    [NXApp stopModal:0];
    return self;
}

- okConnect:sender
{
    strncpy(iNntpHost, [oNntpServerNameField stringValue],
    						 (sizeof(iNntpHost)-1));
    if ([[oGroupSelectionButton selectedCell] tag]==0) {
	iAllNewsFlag = NO;
    } else {
	iAllNewsFlag = YES;
    }
    [NXApp stopModal:1];
    return self;
}
	
- windowDidBecomeKey:sender
{
    [[NXApp delegate] browserDidBecomeKey:self];
    return(self);
}

- windowDidResignKey:sender
{
    [[NXApp delegate] browserDidResignKey:self];
    return(self);
}

- windowWillClose:sender
{
    [self _saveWindowInfoToDefaultDB];
    [[NXApp delegate] browserDidResignKey:self];
    [NXApp delayedFree:self];
    return(self);
}

- _saveWindowInfoToDefaultDB
{
    NXRect newsbaseRect;
    NXRect folderDockRect, browserViewRect, leftSideRect, rightSideRect;
    char buffer[256];

    //   +----------------------+  -+
    //   |    folderDock        |  newsbaseRect
    //   +----------------------+   |
    //   |        |             |   |
    //   |leftSide|             |   |
    //   |        |             |   |
    //   +----------------------+  -+

    // save .newsrc file
    [iNntpIO saveNewsRcFile];
    // save .dir.newsbase file
    [oNewsfolderDockMatrix saveDirFile];
    
    // save NewsBase frameSize and location of divider
    [oNewsReader getFrame:&newsbaseRect];
    [oNewsfolderDock getFrame:&folderDockRect];
    [oBrowserSplitView getFrame:&browserViewRect];
    [oNewsgroupView getFrame:&leftSideRect];
    [oArticleNXBrowser getFrame:&rightSideRect];
    
    sprintf(buffer, "(%.0f,%.0f):(%.0f,%.0f):"
    		"(%.0f,%.0f):(%.0f,%.0f):(%.0f,%.0f):(%.0f,%.0f)", 
		newsbaseRect.origin.x, newsbaseRect.origin.y,
       		newsbaseRect.size.width, newsbaseRect.size.height,
        	folderDockRect.size.width, folderDockRect.size.height,
		browserViewRect.size.width, browserViewRect.size.height,
      		leftSideRect.size.width, leftSideRect.size.height,
      		rightSideRect.size.width, rightSideRect.size.height);
    
    NXWriteDefault(OWNER, NNTPBROWSER_FRAME, buffer);
    return self;
}

- free
{
    if (iNntpIO != nil) {
        [iNntpIO saveNewsRcFile];
        [iNntpIO free];
    }
    [oNewsReader free];
    [oGetArticleNumWindow free];
    [self perform:@selector(destroyZone:) with:nil afterDelay:1
        cancelPrevious:NO];
    return(nil);
}

- destroyZone:dummy
{
    NXDestroyZone(newsbaseZone);
    [super free];
    return(nil);
}

/* delegate method for NXSplitView */

#define		SCRLLMATRIX_HEIGHT	107  // height for oNewsfolderScroll

- splitView:sender resizeSubviews:(const NXSize *)oldSize
{
    id		upperView, lowerView;
    NXRect	viewFrame, upperFrame;
    int		ncols, nrows;
    int		i, j;
    NXSize	cellSize, intercell;
    int		numRows, numCols;
    
    DBG(1,fprintf(stderr,"-- splitView:\n"));
//    upperView = *(NX_ADDRESS([sender subviews]));
//    lowerView = *(NX_ADDRESS([sender subviews]) + 1);
    upperView = oNewsfolderDock;
    lowerView = oBrowserSplitView;

    [[sender window] disableDisplay];
    [sender adjustSubviews];

    // adjust upper(self) view size
    [sender getFrame:&viewFrame];
    [upperView getFrame:&upperFrame];	// upperView is ScrollView
		// and oNewsfolderDockMatrix is a docView of that ScrollView

    [oNewsfolderDockMatrix getCellSize:&cellSize];
    [oNewsfolderDockMatrix getIntercell:&intercell];
    [oNewsfolderDockMatrix getEnoughNumRows:&nrows numCols:&ncols 
	    				forSize:&(upperFrame.size)];
    upperFrame.size.height = cellSize.height * nrows +
    						 intercell.height * (nrows-1);
    [upperView setFrame:&upperFrame];
    
    // compute lower view size
    viewFrame.origin.y = [sender dividerHeight] + upperFrame.size.height;
    					// coord. is flipped in NXSplitView
    viewFrame.size.height -= viewFrame.origin.y;
    [lowerView setFrame:&viewFrame];    

    [[sender window] reenableDisplay];
    
    // unregister cell's frame to viewRectList in AcceptWindow
    // so that the clipped cell can not be used as dropped well
    [oNewsfolderDockMatrix getNumRows:&numRows numCols:&numCols];
    if (numCols > ncols) {
	// remove columns
	for (i = (numCols-1); i > (ncols-1); --i) {
	    // unregister rect of cell which will be clipped by ScrollView,
	    // which is a superview of this class
	    for (j = (numRows-1); j >= 0; --j) {
		[oNewsfolderDockMatrix unregisterCellAt:j :i window:nil];
	    }
	}
    }
    
    if (numRows > nrows) {
	// remove rows
	for (i = (numRows-1); i > (nrows-1); --i) {
	    // unregister rect of cell which will be clipped by ScrollView,
	    // which is a superview of this class
	    for (j = (numCols-1); j >= 0; --j) {
		[oNewsfolderDockMatrix unregisterCellAt:i :j window:nil];
	    }
	}
    }
    
    // reregister the cells in Scroll view
    [oNewsfolderScrollMatrix registerCells];
    
    return self;
}

- splitView:sender getMinY:(NXCoord *)minY maxY:(NXCoord *)maxY
						ofSubviewAt:(int)offset
{
    NXRect	viewFrame;
    int		nrows, ncols;
    id		upperView, lowerView;
    NXSize	cellSize, intercell;

    upperView = oNewsfolderDockMatrix;
    lowerView = oBrowserSplitView;
    
    [upperView getCellSize:&cellSize];
    [upperView getIntercell:&intercell];

    *minY = 0.0;
    [sender getFrame:&viewFrame];
    viewFrame.size.height -= SCRLLMATRIX_HEIGHT;
    [upperView getEnoughNumRows:&nrows numCols:&ncols 
	    				forSize:&(viewFrame.size)];
    nrows -= 1;			// avoid that the upperview size will be 
    				// overflow because of rounding nrows
    if (nrows >= 1) {
	*maxY = cellSize.height * nrows + intercell.height * (nrows -1);
    } else {
	*maxY = 0.0;
    }
    
    return self;
}


@end

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