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.