This is NewsGroupSetBrowser.m in view mode; [Download] [Up]
#import "NewsGroupSetBrowser.h"
#import "Newsgroup.h"
#import "NewsgroupBrowserCell.h"
#import <misckit/MiscAppDefaults.h>
#import "MatrixSet.h"
#import "MatrixScroller.h"
#import "instr.h"
#import "regexpr.h"
@implementation NewsGroupSetBrowser
#define IS_LEAF 1
#define IS_NO_LEAF 2
- awakeFromNib
{
buttonTitle=NXCopyStringBuffer([selectionButton title]);
[self setCellPrototype:[[[NewsgroupBrowserCell alloc] init] setNewsgroupCell:nil]];
return self;
}
- free
{
if(myVisibleCellList!=nil)
[myVisibleCellList free];
free(buttonTitle);
return [super free];
}
- initFrame:(const NXRect *)frameRect
{
int minWidth;
[super initFrame:frameRect];
minWidth=[NXApp defaultIntValue:"MinBrowserColumnWidth"];
if(minWidth==0)
minWidth=70;
[self setAction:@selector(selectNewsgroup:)];
[self setTarget:mySet];
[self setPathSeparator:'.'];
[self setMultipleSelectionEnabled:NO];
[self setDelegate:self];
[self setEmptySelectionEnabled:YES];
[self setHorizontalScrollButtonsEnabled:NO];
[self setHorizontalScrollerEnabled:YES];
[self setMinColumnWidth:minWidth];
[self setTitled:NO];
[self reuseColumns:YES];
[self separateColumns:YES];
[self useScrollBars:YES];
[self useScrollButtons:YES];
listList=[[List alloc] init];
return self;
}
- (int)browser:sender fillMatrix:matrix inColumn:(int)column
{
struct bcellDesc{
int leaf;
id newsgroup;
};
int i,y,z,from,ii;
List *aList,*bList,*cList;
Newsgroup *aNewsgroup;
const char *name;
char *end_of_name;
char *cell_name;
NXHashState hashState;
HashTable *aHashTable;
char *aStringKey;
char buf[255];
char *path;
char isnull[]="";
struct bcellDesc *c;
//compute column selection list
if(column>0){
//get selected path
path=[self getPath:buf toColumn:column];
NX_ASSERT(path!=NULL,"No path selected");
path++;
y=[listList count];
for(i=y-1;i>=column;i--)
[[listList removeObjectAt:i] free];
bList=[[List alloc] init];
aList=[listList objectAt:column-1];
z=[aList count];
for(i=0;i<z;i++){
const char *lname;
int plen=strlen(path);
aNewsgroup=[aList objectAt:i];
lname=[aNewsgroup stringValue];
if((strncmp(lname,path,plen)==0) && (lname[plen]=='.'))
[bList addObject:aNewsgroup];
}
[listList addObject:bList];
}
else
path=isnull;
//compute column entries
aList=[listList objectAt:column];
aHashTable=[[HashTable alloc] initKeyDesc:"*" valueDesc:"!"];
z=[aList count];
for(i=0;i<z;i++){
int isLeaf;
aNewsgroup=[aList objectAt:i];
name=[aNewsgroup stringValue];
name+=strlen(path);
if(path[0]!='\0')
name++;
end_of_name=strchr(name,'.');
if(end_of_name==NULL){
cell_name=NXCopyStringBuffer(name);
isLeaf=IS_LEAF;
}
else{
int size=strlen(name)-strlen(end_of_name);
cell_name=(char *)malloc((size+3)*sizeof(char));
strncpy(cell_name,name,size);
cell_name[size]='\0';
isLeaf=IS_NO_LEAF;
}
if([aHashTable isKey:(void *)cell_name]){
c=(struct bcellDesc *)[aHashTable valueForKey:(void *)cell_name];
free(cell_name);
}
else{
c=(struct bcellDesc *)calloc(1,sizeof(struct bcellDesc));
[aHashTable insertKey:(const void *)cell_name value:(void *)c];
}
c->leaf|=isLeaf;
if(isLeaf==IS_LEAF)
c->newsgroup=aNewsgroup;
}
//column enties are now in the hashtable
//now create column matrix
hashState=[aHashTable initState];
i=-1;
while([aHashTable nextState:&hashState key:(const void **)&aStringKey value:(void **)&c]){
id aCell;
[matrix addRow];
i++;
aCell=[matrix cellAt:i :0];
[aCell setLeaf:((c->leaf&IS_LEAF)==IS_LEAF)];
[aCell setStringValueNoCopy:aStringKey shouldFree:YES];
if(c->newsgroup!=nil)
[aCell setNewsgroupCell:c->newsgroup];
if(c->leaf==3){
[matrix addRow];
i++;
aCell=[matrix cellAt:i :0];
[aCell setLeaf:NO];
[aCell setStringValue:aStringKey];
}
free(c);
}
i++;
//simple sorting :-(
cList=[matrix cellList];
for(from=0;from<i-1;from++){
int min=from;
id minObject=[cList objectAt:from];
const char *minString=[minObject stringValue];
for(ii=from+1;ii<i;ii++){
int c;
id anObject=[cList objectAt:ii];
const char *aString=[anObject stringValue];
if( ((c=strcmp(aString,minString))<0) ||
(c==0 && [anObject isLeaf]) ){
minObject=anObject;
minString=aString;
min=ii;
}
}
if(min!=from){
id anObject=[cList replaceObjectAt:from with:[cList objectAt:min]];
[cList replaceObjectAt:min with:anObject];
}
}
[aHashTable free];
return i;
}
- loadMatrix
{
[self makeVisibleCellList:self];
[self loadColumnZero];
return self;
}
- reloadMatrix
{
char path_buf[255];
id curSel;
[[self window] disableFlushWindow];
[self setAutodisplay:NO];
curSel=[mySet currentSelection];
[self getPath:path_buf toColumn:[self lastColumn]];
[self loadMatrix];
//reselect path or path+cell
if(curSel==nil){
if(path_buf[0]!='\0'){
int i,j,z;
z=strlen(path_buf)-1;
j=[myVisibleCellList count];
//see if there's a visible group with its prefix==path_buf
for(i=0;i<j;i++)
if(strncmp([[myVisibleCellList objectAt:i] stringValue],path_buf+1,z)==0){
strcat(path_buf,".");
[self setPath:path_buf];
break;
}
}
}
else if([curSel isTaged]==TRUE){
const char *gname=[curSel stringValue];
char *newPath;
newPath=(char *)malloc((strlen(gname)+2)*sizeof(char));
sprintf(newPath,".%s",gname);
[self setPath:newPath];
free(newPath);
}
[[self setAutodisplay:YES] display];
[[[self window] reenableFlushWindow] flushWindow];
return self;
}
- setMatrixCellList:(List *)aList
{
myCellList=aList;
return self;
}
- (List *)cellList
{
return myVisibleCellList;
}
- removeInvalidCell:aCell;
{
return [self removeInvalidCell:aCell andUpdate:YES];
}
- removeInvalidCell:aCell andUpdate:(BOOL)update
{
int i,j;
for(i=0,j=[listList count];i<j;i++)
[[listList objectAt:i] removeObject:aCell];
[myVisibleCellList removeObject:aCell];
[[myCellList removeObject:aCell] free];
if(update==YES)
[self update];
return self;
}
- update
{
[self loadColumnZero];
[self display];
[mySet sync];
[NXApp updateWindows];
return self;
}
- (List *)getCurrSelections
{
List *aList=[[List alloc] init];
id selection;
selection=[self currSelection];
if(selection!=nil)
[aList addObject:selection];
else {
int j=[listList count];
NX_ASSERT(j>0,"Listlist empty");
if(j>1)
if([[listList objectAt:j-1] count]>1) //shit!
[aList appendList:[listList objectAt:j-1]];
}
return aList;
}
- currSelection
{
char buf[255];
char *newsg_name;
id aCell=[self selectedCell];
id selCell=nil;
List *aList;
int i,j;
if(aCell==nil)
return nil;
if([aCell isLeaf]==TRUE){
const char *cellName=[aCell stringValue];
int lcol=[self lastColumn];
[self getPath:buf toColumn:lcol];
newsg_name=(char *)malloc((strlen(buf)+strlen(cellName)+3)*sizeof(char));
newsg_name[0]='\0';
strcpy(newsg_name,buf);
strcat(newsg_name,".");
strcat(newsg_name,cellName);
aList=[listList objectAt:lcol];
j=[aList count];
for(i=0;i<j;i++){
if(strcmp([[aList objectAt:i] stringValue],newsg_name+1)==0){
selCell=[aList objectAt:i];
break;
}
}
free(newsg_name);
}
return selCell;
}
- makeVisibleCellList:sender
{
int i,j;
if(myVisibleCellList!=nil)
[myVisibleCellList free];
myVisibleCellList=[[List alloc] init];
j=[myCellList count];
for(i=0;i<j;i++){
id aCell=[myCellList objectAt:i];
if([aCell isTaged]==TRUE)
[myVisibleCellList addObject:aCell];
}
if([listList count]==0)
[listList addObject:myVisibleCellList];
else
[listList replaceObjectAt:0 with:myVisibleCellList];
return self;
}
- (id)selectionButton
{
return selectionButton;
}
- setButtonTitle:(const char *)title
{
free(buttonTitle);
[selectionButton setTitle:title];
buttonTitle=NXCopyStringBuffer(title);
return self;
}
- _delayedUpdate:sender
{
[selectionButton setTitle:buttonTitle];
return self;
}
- updateButtonTitle
{
[self perform:@selector(_delayedUpdate:) with:self afterDelay:0.0 cancelPrevious:TRUE];
return self;
}
- (BOOL)selectNextCell:(int)delta inColumn:(int)col leafSelected:(BOOL *)sel
{
id matrix=[self matrixInColumn:col];
id aCell=[matrix selectedCell];
id cellList=[matrix cellList];
int j,jj;
int i=0;
char buf[255];
id newCell;
[matrix getNumRows:&j numCols:&jj];
*sel=FALSE;
if(j==0)
return FALSE;
//find index of cell to select
if(aCell==nil){
if(delta>0)
i=0;
else
i=j-1;
}
else{
int pos=[cellList indexOf:aCell];
if(((delta>0)&&(pos==j-1))||((delta<0)&&(pos==0)))
return FALSE;
i=pos+delta;
}
//compose and set path
[self getPath:buf toColumn:col];
newCell=[cellList objectAt:i];
if(![newCell isLeaf]){
strcat(buf,".");
strcat(buf,[newCell stringValue]);
strcat(buf,".");
[self setPath:buf];
}
else{
strcat(buf,".");
strcat(buf,[newCell stringValue]);
[self setPath:buf];
*sel=TRUE;
}
return TRUE;
}
- (BOOL)selectNextCell:(int)delta
{
int i;
BOOL r,rr;
NX_ASSERT(((delta==1) || (delta==-1)),"Wrong parameter selectNextCell");
for(i=[self lastColumn];i>=0;i--){
r=[self selectNextCell:delta inColumn:i leafSelected:&rr];
if(rr==TRUE){
[self sendAction];
return TRUE;
}
if(r==TRUE)
i=[self lastColumn]+1;
}
return FALSE;
}
- setPath:(const char *)aPath
{
char *newPath,*c;
id bmatrix;
int i,j;
NX_ASSERT(*aPath==(char)pathSeparator,"setPath: wrong pathstring");
if(aPath[strlen(aPath)-1]==(char)pathSeparator){
if([self selectedCell]!=nil)
[[self matrixInColumn:[self lastColumn]] selectCellAt:-1 :-1];
return [super setPath:aPath];
}
newPath=NXCopyStringBuffer(aPath);
c=strrchr(newPath,pathSeparator);
NX_ASSERT(c!=NULL,"setPath: internal error");
*c='\0';
if(*newPath=='\0')
[super setPath:"."];
else if([super setPath:newPath]==nil)
return nil;
c++;
bmatrix=[self matrixInColumn:[self lastColumn]];
[bmatrix getNumRows:&j numCols:&i];
for(i=0;i<j;i++){
id aCell=[bmatrix cellAt:i :0];
if(!strcmp([aCell stringValue],c))
if([aCell isLeaf]){
[bmatrix selectCellAt:i :0];
break;
}
}
free(newPath);
[self scrollColumnToVisible:[self lastColumn]];
[self scrollSelectionToVisible];
if(i==j)
return nil;
return self;
}
- scrollSelectionToVisible
{
id aMatrix=[self matrixInColumn:[self lastVisibleColumn]];
id aCell=[aMatrix selectedCell];
if(aCell!=nil)
[aMatrix scrollCellToVisible:[[aMatrix cellList] indexOf:aCell]
upperOffset:1.5 lowerOffset:1.5];
return self;
}
//-------------------------
// searchable text protocol
//-------------------------
- (oneway void)makeSelectionVisible
{
}
- (int)replaceAll:(const char *)pattern with:(const char *)replacement mode:(SearchMode)mode regexpr:(BOOL)regexpr cases:(BOOL)cases
{
return SEARCH_CANNOT_WRITE;
}
- (oneway void)replaceSelection:(const char *)replacement
{
}
- (const char *)stringValueForCellAt:(int)index
{
return [[myVisibleCellList objectAt:index] stringValue];
}
- (int)searchFor:(const char *)pattern mode:(SearchMode)mode reverse:(BOOL)rev regexpr:(BOOL)regexpr cases:(BOOL)cases position:(out int *)pos size:(out int *)size
{
int ccount;
int s_pos;
int fStart,fEnd;
int delta;
List *selList;
unsigned char fm[256], tr[256];
struct re_pattern_buffer rpat;
s_pos=-2;
fStart=fEnd=-2;
ccount=[myVisibleCellList count];
if(ccount==0)
return 0;
// find first selected cell
selList=[self getCurrSelections];
if([selList count]>0)
s_pos=[myVisibleCellList indexOf:[selList objectAt:0]];
[selList free];
if(!rev){
if(s_pos<0){
fStart=0; fEnd=ccount;
}
else{
fStart=s_pos+1; fEnd=s_pos+ccount;
}
}
else{
if(s_pos<0){
fStart=ccount-1; fEnd=-1;
}
else{
fStart=s_pos-1+ccount; fEnd=s_pos+1;
}
}
delta= rev? -1:1;
if(regexpr){
char *str;
int i;
memset(&rpat, 0, sizeof(rpat));
for(i=256; i--;)
tr[i] = i;
if(!cases)
for(i='A'; i<='Z'; i++)
tr[i] = i-'A'+'a';
rpat.translate = tr;
rpat.fastmap = fm;
str = re_compile_pattern((char *)pattern,strlen(pattern), &rpat);
if (str!=NULL)
return (strcmp(str, "Out of memory")?SEARCH_INVALID_REGEXPR:SEARCH_INTERNAL_ERROR);
}
for(;fStart!=fEnd;fStart+=delta){
int index=fStart%ccount;
const char *result=NULL;
const char *cellString=[self stringValueForCellAt:index];
if(regexpr){
int l=strlen(cellString);
int p=re_search_pattern(&rpat,(char *)cellString,l,0,l,0);
if(p==-2)
return SEARCH_INTERNAL_ERROR;
result= (p==-1)? NULL : cellString;
}
else
result=instr(cellString,pattern,cases);
if(result!=NULL){
*pos=index;
*size=1;
return 1;
}
}
return 0;
}
- (oneway void)selectTextFrom:(int)start to:(int)end
{
if(start<=end){
if(start==end){
[super setPath:"."];
[self sendAction];
}
else{
if(start<[myVisibleCellList count]){
id aCell=[myVisibleCellList objectAt:start];
char *newPath=malloc((strlen([aCell stringValue])+2)*sizeof(char));
sprintf(newPath,".%s",[aCell stringValue]);
[self setPath:newPath];
free(newPath);
[self sendAction];
}
}
}
}
- (void)writeSelectionToPasteboard:(in Pasteboard *)pboard asType:(in NXAtom)type
{
id aCell=[self currSelection];
if(aCell!=nil){
const char *sval=[aCell stringValue];
[pboard declareTypes:&type num:1 owner:NULL];
[pboard writeType:type data:sval length:strlen(sval)];
}
return;
}
- mySet
{
return mySet;
}
- (BOOL)selectNewsgroupNamed:(const char *)gname
{
int i,j;
for(i=0,j=[myVisibleCellList count];i<j;i++){
const char *name=[[myVisibleCellList objectAt:i] stringValue];
if(name && !strcmp(gname,name)){
char *newPath=malloc((strlen(name)+2)*sizeof(char));
sprintf(newPath,".%s",name);
[self setPath:newPath];
[self scrollSelectionToVisible];
free(newPath);
[self sendAction];
return TRUE;
}
}
return FALSE;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.