This is BibTexView.m in view mode; [Download] [Up]
// Copyright H. Giesen, University of Koblenz-Landau 1996
#import "Controller.h"
#import "BibTexView.h"
#import "BibTeXObject.h"
#import "BibliographicFile.h"
#import "Preferences.h"
#import "BufferClass.h"
#define IN &
#define KEY_FIELD_TAG 100
#define NOTE_TEXT_TAG 120
#define FORM_FIELD_TAG 123
#define BIG_LEFT NXPrintf( localStream, "%s",\
[leftBraceButton state] ?\
[leftBraceButton altTitle]: [leftBraceButton title] )
#define BIG_RIGHT NXPrintf( localStream, "%s\n\n",\
[rightBraceButton state] ?\
[rightBraceButton altTitle]: [rightBraceButton title] )
#define LEFTQUOTE if( cooked ) NXPutc(localStream, LBRACE )
#define RIGHTQUOTE if( cooked ) NXPutc(localStream, RBRACE )
#define THE_TYPE NXPrintf( localStream, "\n@%s",[popUpButton title] )
#define THE_KEY NXPrintf( localStream, " %s",\
[self stringOf:[keyField stringValue]] )
#define ASSIGN NXPrintf( localStream, "\t= " )
#define ACTUALBUFFER \
[self convert:[self listBufferFor:actualRow] toStream:localStream]
#define NEXTENTRY(title) NXPrintf( localStream, ",\n\t%s\t= ", title )
#define NEWLINE NXPrintf( localStream, "\n" )
#define ADD_HASH_MARK { NXPrintf( toStream, " # "); }
static id entryInspectorObject = nil;
static int STRING;
static int PREAMBLE;
static int COMMENT;
static NXStream *localStream = NULL;
static char *localBuffer;
static int locBufferLength, locBufferMaxLength;
static id viewBuffer = nil;
static id cookedBuffer = nil;
@implementation BibTexView
+ new
{
if( entryInspectorObject==nil ){
entryInspectorObject = [[super alloc] init];
viewBuffer = [[BufferClass alloc] initBufferSize:80];
cookedBuffer = [[BufferClass alloc] init];
}
return entryInspectorObject;
}
+ alloc
{
return [self notImplemented:_cmd];
}
- (void)initButtons
{
int i;
id button;
NXRect aFrame = { {0.0, 0.0}, {18.0, 18.0} };
toggleListType *toggleList = [preferences toggleList];
i = 0;
while( toggleList[i].entryIndex ){ // create a button
// create a new toggle button
button = [[Button alloc] initFrame:&aFrame];
[button setTitle:"t"];
[button setTag:i];
[button setTarget:self];
[button setAction:@selector(toggle:)];
[button setTransparent:YES];
toggleList[i].tglButton = button;
toggleList[i].isInWindow = NO;
i++;
}
}
- (void)clearToggleButtons
{
int i = 0;
toggleListType *toggleList = [preferences toggleList];
while( toggleList[i].entryIndex ){ // remove a button from superview
if( toggleList[i].isInWindow==YES ){
[toggleList[i].tglButton removeFromSuperview];
toggleList[i].isInWindow = NO;
}
i++;
}
}
- findCellWithTitle:(NXAtom)txt at:(int *)inx
{
int rows, cols;
int i, len = strlen(txt);
[entryForm getNumRows:&rows numCols:&cols];
for( i=0; i<rows; i++ ){
if( NXOrderStrings([entryForm titleAt:i], txt, NO, len, NULL)==0 ){
// fprintf( stderr, "found %s in %d\n", txt, i );
*inx = i;
return [entryForm cellAt:i :0];
}
}
//fprintf( stderr, "not found %s\n", txt ); // %%%% ??? %%%%%
*inx = -1;
return nil;
}
- setDelayedToggleButtonsFor:(int)eType
{
int i=0, j;
id tglButton;
id theCell;
NXRect cellFrame;
toggleListType *toggleList = [preferences toggleList];
[self clearToggleButtons];
while( toggleList[i].entryIndex ){
if( toggleList[i].entryIndex==eType ){
id fnt = [Font newFont:"Helvetica-BoldOblique" size:12.0 ];
theCell = [self findCellWithTitle:toggleList[i].txt0 at:&j];
if( theCell==nil )
theCell = [self findCellWithTitle:toggleList[i].txt1 at:&j];
if( theCell==nil ) break;
if( fnt ) [theCell setTitleFont:fnt];
[entryForm getCellFrame:&cellFrame at:j :0];
tglButton = toggleList[i].tglButton;
toggleList[i].formInx = j;
cellFrame.W = [theCell titleWidth];
[tglButton setFrame:&cellFrame];
[entryForm addSubview:tglButton];
[tglButton display]; // <---
toggleList[i].isInWindow = YES;
}
i++;
}
return self;
}
- setToggleButtonsFor:(int)eType
{
[self perform:@selector(setDelayedToggleButtonsFor:)
with:(id)eType
afterDelay:0 cancelPrevious:YES];
//[self setDelayedToggleButtonsFor:eType];
return self;
}
static char itemTextBuffer[1024]; // variable length ??????
- (const char *)titleOf:(configListType *)item
{
char *pnt = itemTextBuffer;
int mode = item->mode[entryType];
strcpy( itemTextBuffer, item->fieldName );
while( *pnt ) pnt++;
*pnt++ = ' ';
if( (3 IN mode )==REQUIRED ) *pnt++ = '·';
else *pnt++ = 0x80;
*pnt = '\0';
return itemTextBuffer;
}
- (BOOL)fillFormAt:(int)inx for:(locType)location
{
// copy into a formcell
// and test for linefeed
int i;
char c, *theString;
if( location.length==0 ){
[entryForm setStringValue:NULL at:inx];
return NO;
}
[viewBuffer setMinSizeTo:location.length];
[theObject convertRawField:location to:[viewBuffer buffer]];
[entryForm setStringValue:[viewBuffer buffer] at:inx];
i = 0;
theString = [viewBuffer buffer];
while( c=theString[i++] ){
if( c=='\n' ) return YES;
}
return NO;
}
- toggle:sender
{
toggleListType *toggleList = [preferences toggleList];
int tag = [sender tag];
int fieldInx;
int formInx = toggleList[tag].formInx;
configListType *item;
locType location;
if( toggleList[tag].state==0 ){
fieldInx = [preferences indexOfFieldname:toggleList[tag].txt1];
toggleList[tag].state = 1;
}
else{
fieldInx = [preferences indexOfFieldname:toggleList[tag].txt0];
toggleList[tag].state = 0;
}
item = ((configListType*)[configList elementAt:fieldInx]);
[entryForm setTitle:[self titleOf:item] at:formInx];
location.start = item->start; // set by the parser
location.length = item->length;
[self fillFormAt:formInx for:location];
return self;
}
// a key is a string of characters.
// all characters are allowed exept the comma
// BUT: if the entryType is STRING the following 10 characters are not allowed
// " # % ' ( ) , { }
#define isOneOfTheTen(x) (x=='"' || x=='#' || x=='%' || x=='\''\
|| x=='(' || x==')' || x==',' || x=='=' || x=='{' || x=='}')
- (BOOL) key:(const char *)key isValidFrom:(int*)first to:(int*)last
{
unsigned char c;
int i = 0;
if( (entryType==PREAMBLE) || (entryType==COMMENT) ) return YES;
if( key==NULL ) return NO;
if( (c = key[i])=='\0' ) return NO;
while( NXIsSpace(c) ) c = key[++i];
*first = i;
while( c ){
//if( isOneOfTheTen(c) || (c>127) ){ // not valid
if( c==',' ||
( (entryType==STRING) && isOneOfTheTen(c) ) ){ // not valid
*last = i; // here is the error
return NO;
}
if( NXIsSpace(c) ) break;
c = key[++i];
}
*last = i;
if( c=='\0' ) return YES;
// skip trailing blanks
while( NXIsSpace(c) ) c = key[++i];
if( c ) return NO;
return YES;
}
- enableReplaceButton
{
char buttonBuffer[128], *lastDot;
//if( [replaceButton isEnabled] ) return self;
sprintf( buttonBuffer, "update ." );
lastDot = rindex( buttonBuffer, '.' );
[theObject copyRange:[theObject key] toBuffer:lastDot];
[replaceButton setTitle:buttonBuffer];
[replaceButton setEnabled:YES];
return self;
}
- enableAddButton
{
char buttonBuffer[128], *lastDot;
if( [addButton isEnabled] ) return self;
sprintf( buttonBuffer, "add to file %s", [theFile fileName] );
lastDot = rindex( buttonBuffer, '.' );
*lastDot = '\0';
[addButton setTitle:buttonBuffer];
[addButton setEnabled:YES];
return self;
}
- (void) updateButtons
{
[clearButton setEnabled:YES];
if( theFile==nil ){ // no file selected
[revertButton setEnabled:NO];
[addButton setEnabled:NO];
[replaceButton setEnabled:NO];
return;
}
// file exists
if( theObject==nil ){
// a file but no object (maybe we create a new object)
[revertButton setEnabled:NO];
[replaceButton setEnabled:NO];
if( isDirty &&( (entryType==PREAMBLE)||(entryType==COMMENT) ) ){
[self enableAddButton];
return;
}
if( isDirty
&& keyIsValid
&& (objWithThisKey==nil) )
[self enableAddButton]; // key is unique
else [addButton setEnabled:NO];
return;
}
// both file and object exist
if( (isDirty==NO) && (objWithThisKey==theObject) ){
[revertButton setEnabled:NO];
[replaceButton setEnabled:NO];
[addButton setEnabled:NO];
return;
}
// both file and object exist and the text has been edited
[revertButton setEnabled:YES];
if( keyIsValid ){
if( objWithThisKey==nil ){ // key is valid and unique
[self enableReplaceButton];
[self enableAddButton];
return;
}
// key is valid but not unique
if( objWithThisKey==theObject ){ // it's me
[self enableReplaceButton];
[addButton setEnabled:NO];
return;
}
// key is valid but belongs to another entry
[replaceButton setEnabled:NO];
[addButton setEnabled:NO];
return;
}
// key is syntactically not valid
[replaceButton setEnabled:NO];
[addButton setEnabled:NO];
return;
}
- raw:(const char *)rawText toCooked:(char *)cookedText
{
return self;
}
- (const char *)convertText:(int)i
{
const char* txt = [entryForm stringValueAt:i];
if( cooked==NO ) return txt;
if( (txt[0]=='"') || (txt[0]==LBRACE) ){
[cookedBuffer setText:txt];
return [cookedBuffer buffer]+1;
}
return txt;
}
- (void) clearConfigList
{
int i;
configListType *item;
// clear location entries in configList
for( i=0; i<[configList count]; i++ ){
item = ((configListType*)[configList elementAt:i]);
item->start = 0;
item->length = 0;
}
}
// called from controller (newButton)
- newEntryFor:(BibliographicFile *)file
{
[self setEntry:nil inFile:file];
NXAttachPopUpList( popUpButton, popUpListNew );
[self drawTemplate:-1];
//[self clear:self];
[inspector makeKeyAndOrderFront:self];
if( [keyField isEnabled] ) [keyForm selectTextAt:0];
else [noteView setSel:0 :0];
[self clearConfigList];
return self;
}
- fillCharMatrix:sender
{
unsigned char str[8];
char **tbl = [BibTexParser NX2TeX];
//char *texString;
int i;
str[1] = '\0';
[[charMatrix window] disableDisplay];
for( i=0; i<128; i++ ){
id cell = [charMatrix cellAt:i/8 :i%8];
str[0] = i + 0x80;
[cell setTitle:str];
//texString = tbl[ i ];
if( tbl[i][1]=='?' ) [cell setEnabled:NO];
}
[[[charMatrix window] reenableDisplay] display];
[[charMatrix window]setBecomeKeyOnlyIfNeeded:YES];
return self;
}
- firstCall
{
int index;
NXRect entryFrame;
[NXApp loadNibSection:"EntryInspector.nib" owner:self];
if( BIBTEST==NO ) [[bufferedButton removeFromSuperview] free];
[inspector useOptimizedDrawing:YES];
[entryForm setInterline:0]; // ******
preferences = [Preferences new];
STRING = [preferences indexOfEntryname:"STRING"];
PREAMBLE = [preferences indexOfEntryname:"PREAMBLE"];
COMMENT = [preferences indexOfEntryname:"COMMENT"];
entryNameList = [preferences entryNameList];
configList = [preferences configList];
[self clearConfigList];
popUpList = [[PopUpList alloc] init];
popUpListNew = [[PopUpList alloc] init];
for( index=0; index<[entryNameList count]; index++ ){
const char *title = (const char *)[entryNameList objectAt:index];
[popUpList addItem:title];
[popUpListNew addItem:title];
}
// clean the popUpList
[[popUpList removeItem:"STRING"] free];
[[popUpList removeItem:"PREAMBLE"] free];
[[popUpList removeItem:"COMMENT"] free];
// size both lists to fit, setTarget
[popUpList sizeToFit];
[[popUpList setAction:@selector(bibPopup:)] setTarget:self];
[popUpListNew sizeToFit];
[[popUpListNew setAction:@selector(bibPopup:)] setTarget:self];
[popUpButton setFont:[Font newFont:"Courier" size:12.0 ]];
popUpListString = [[PopUpList alloc] init];
[popUpListString addItem:"STRING"];
popUpListPreamble = [[PopUpList alloc] init];
[popUpListPreamble addItem:"PREAMBLE"];
popUpListComment = [[PopUpList alloc] init];
[popUpListComment addItem:"COMMENT"];
// we start with the "normal" list
NXAttachPopUpList( popUpButton, popUpList );
[popUpList sizeButton:popUpButton];
// get the original windowFrame (without entryForm and scrollView)
[entryForm getFrame:&entryFrame];
[inspector getFrame:&windowFrame];
[noteView getFrame:&minTextFrame]; // as set in IB
windowFrame.Y += windowFrame.H; // northwest_corner
windowFrame.H -= entryFrame.H; // without entryForm
windowFrame.H -= minTextFrame.H; // without scrollView
// windowFrame is now the raw frame (only height is of interest)
// without entryform
entryFormMask = [entryForm autosizing]; // as made in IB
scrollViewMask = [scrollView autosizing]; // as made in IB
scrollViewMask = [marker autosizing]; // as made in IB
[noteView setDelegate:self];
[noteView setTag:120];
[noteView setFont:[Font userFontOfSize:0.0 matrix:NX_FLIPPEDMATRIX]];
theFont = [[entryForm cellAt:0 :0] titleFont];
isDirty = NO;
textBufferList = [[List alloc] init];
[marker renewRows:0 cols:1];
[marker setFont:[Font newFont:"Symbol" size:12.0 ]];
[self drawTemplate:-1];
[inspector orderFront:self];
[self fillCharMatrix:self];
[self initButtons];
[fieldEditor setMonoFont:YES];
[fieldEditor setCharFilter:NXFieldFilter];
cooked = YES;
return self;
}
- (void) clearBuffers
{
int i;
for( i=[textBufferList count]-1; i>=0; i-- )
[[textBufferList objectAt:i] clear];
}
- prefsDidChange
{
prefsDidChange = YES;
if( [inspector isDisplayEnabled]==NO ) return self;
[self drawTemplate:-1];
[self revert:self];
return self;
}
- (void) sizeBufferListTo:(int)index
{
while( [textBufferList count]<=index )
[textBufferList addObject:[[BufferClass alloc] init] ];
}
- (char *) listBufferFor:(int)inx
{
if( [textBufferList count] <= inx ){
[self sizeBufferListTo:inx];
}
return [[textBufferList objectAt:inx] buffer];
}
- (char *) listBufferFor:(int)inx size:(int)size
{
if( [textBufferList count] <= inx ){
[self sizeBufferListTo:inx];
}
[[textBufferList objectAt:inx] setMinSizeTo:size];
return [[textBufferList objectAt:inx] buffer];
}
- noteSelect:sender // sender is Matrix
{
int row = [sender selectedRow];
char *aTextBuffer; // source buffer
if( row<0 ) return self;
aTextBuffer = [self listBufferFor:row];
if( noteIsDirty ){ // write the text back to the buffer
char *actualTextBuffer =
[self listBufferFor:actualRow size:[noteView textLength]+1];
[noteView getSubstring:actualTextBuffer
start:0 length:[noteView textLength]+1];
}
[noteView setSel:0 :[noteView textLength]];
[noteView replaceSel:aTextBuffer];
noteIsDirty = NO;
actualRow = row;
return self;
}
- copyLoc:(locType)loc ofObject:obj to:(int)inx
{
char *aTextBuffer = [self listBufferFor:inx size:loc.length];
if( entryType==COMMENT ){
[obj copyRange:loc toBuffer:aTextBuffer];
}
else [obj convertRawField:loc to:aTextBuffer];
return self;
}
// the key-field is already filled
- showString:(int)k
{
configListType *item;
locType location;
int NOTE = [preferences indexOfFieldname:"NOTE"];
// STRING has no fieldname, it's content is stored in NOTE
// PREAMBLE has no fieldname, it's content is stored in NOTE
// COMMENT has no fieldname, it's content is stored in NOTE
item = ((configListType*)[configList elementAt:NOTE]);
location.start = item->start; // set by the parser
location.length = item->length;
// copy the content to a buffer at index [0];
[self copyLoc:location ofObject:theObject to:0];
[noteView setSel:0 :[noteView textLength]];
[noteView replaceSel:[self listBufferFor:0]];
[[entryForm renewRows:0 cols:1] sizeToCells];
[noteMatrix renewRows:0 cols:0];
return self;
}
- resizeInspector:(NXRect *)fr
{
NXRect entryFrame, textFrame, minFrame;
[entryForm getFrame:&entryFrame];
[scrollView getFrame:&textFrame];
newWindowFrame = windowFrame;
newWindowFrame.H = // new minimal frame
windowFrame.H
+ entryFrame.H
+ minTextFrame.H;
minFrame = newWindowFrame;
minFrame.W = 300.0;
[inspector setMinSize:&minFrame.size];
newWindowFrame.H = // new real frame
windowFrame.H
+ entryFrame.H
+ textFrame.H;
// northwest_corner --> southeast_corner
newWindowFrame.Y -= newWindowFrame.H;
*fr = newWindowFrame;
return self;
}
// skips leading blanks
// returns a pointer to a string from the first nonblanl
// character. The string ends before the first blank
// behind the string.
// used in printing key, digits and the title of the fields
- (const char *)stringOf:(const char *)txt
{
int i = 0;
char c;
while( (c=*txt++)==' ' ); // skip leading blanks
while( c && (c!=' ') ){itemTextBuffer[i++] = c; c = *txt++;}
itemTextBuffer[i] = '\0';
return itemTextBuffer;
}
- (void) addURLat:(int)inx
{
NXRect cellFrame;
Cell *theCell = [entryForm cellAt:inx :0];
Cell *markerCell = [marker cellAt:inx :0];
[entryForm getCellFrame:&cellFrame at:inx :0];
fprintf( stderr, "add type=%d\n", [markerCell type] );
//[markerCell setIcon:"Images/smallIcon"];
[theCell setIcon:"Images/smallIcon"];
fprintf( stderr, "add type=%d %s\n",
[theCell type], [theCell icon] );
}
- (void) addMarker:(BOOL)m at:(int)inx
{
id cell;
int row, col;
[marker addRow];
[marker getNumRows:&row numCols:&col];
cell = [marker cellAt:(row-1) :0];
if( m ){
[cell setStringValue:"\272"];
//[self addURLat:inx];
}
else [cell setStringValue:NULL];
}
- (void) setMarker:(BOOL)m at:(int)inx
{
id cell = [marker cellAt:inx :0];
const char *str = [cell stringValue];
if( m == (str!=NULL) ) return;
if( m ){
[cell setStringValue:"\272"];
//[self addURLat:inx];
}
else [cell setStringValue:NULL];
}
- (void) drawTemplate:(int)aType // empty inspector
{
int i, count, mode, rows, cols;
configListType *item;
NXRect winFrame;
if( aType<0 ){
entryType = [preferences indexOfEntryname:(char *)[popUpButton title]];
}
else entryType = aType;
[inspector disableDisplay];
[inspector endEditingFor:nil];
[keyForm setNextText:entryForm];
[keyField setEnabled:YES];
[keyForm selectTextAt:0];
[[entryForm renewRows:0 cols:1] sizeToCells]; // clear the matrix
[marker renewRows:0 cols:1];
if( (entryType==STRING)
|| (entryType==PREAMBLE)
|| (entryType==COMMENT) ){ // special handling
[[noteMatrix renewRows:0 cols:1] sizeToCells];
if( entryType==PREAMBLE )[keyField setEnabled:NO];
if( entryType==COMMENT )[keyField setEnabled:NO];
if( entryType==STRING ) [keyForm setNextText:noteView];
[self resizeInspector:&winFrame];
[inspector reenableDisplay];
[inspector placeWindowAndDisplay:&winFrame];
return;
}
[noteMatrix renewRows:0 cols:1];
// set the empty forms
for( i=0, count=0; i<[configList count]; i++ ){
item = ((configListType*)[configList elementAt:i]);
item->tag = -1; // clear
mode = item->mode[entryType];
if( SHOW IN mode ){
if( (LARGE IN mode) ){
id cell;
int row, col;
[noteMatrix addRow];
[noteMatrix getNumRows:&row numCols:&col];
cell = [noteMatrix cellAt:(row-1) :0];
[cell setTitle:item->fieldName];
}
else{ // normal form
[entryForm addRow];
[[entryForm cellAt:count :0] setTitleFont:theFont];
item->tag = count;
[entryForm setTitle:[self titleOf:item] at:count];
count++;
}
}
}
[noteMatrix sizeToCells];
[entryForm sizeToCells];
[entryForm getNumRows:&rows numCols:&cols];
[marker renewRows:rows cols:cols];
[marker sizeToCells];
for( i=0; i<rows; i++ ){
[[marker cellAt:i :0] setStringValue:NULL];
}
[self resizeInspector:&winFrame];
[self setToggleButtonsFor:entryType];
[self clearBuffers];
prefsDidChange = NO;
[inspector reenableDisplay];
[inspector placeWindowAndDisplay:&winFrame];
}
- (void) fillTemplate
{
int i, j, n, mode;
configListType *item;
locType location, range;
BOOL multiLine = NO;
[self showAllFieldsFor:entryType];
[[inspector reenableDisplay] displayIfNeeded];
return;
[theObject setKeyTo:keyField];
[marker renewRows:0 cols:1];
if( (entryType==STRING)||(entryType==PREAMBLE)||(entryType==COMMENT) ){
[self showString:entryType];
[[inspector reenableDisplay] displayIfNeeded];
[scrollView display];
return;
}
for( i=0, j=0, n=0; i<[configList count]; i++ ){
item = ((configListType*)[configList elementAt:i]);
mode = item->mode[entryType];
location.start = item->start; // set by the parser
location.length = item->length;
[viewBuffer setMinSizeTo:location.length];
if( SHOW IN mode ){
if( (LARGE IN mode) ){
[self copyLoc:location ofObject:theObject to:n];
n++;
continue;
}
multiLine = [self fillFormAt:j for:location];
[self addMarker:multiLine at:j];
j++;
}
}
noteIsDirty = NO;
range = [theObject range];
sprintf( [viewBuffer buffer], "line %d, charpos %d, length %d",
[theObject line], range.start, range.length );
[positionField setStringValue:[viewBuffer buffer]];
[self noteSelect:noteMatrix];
[marker sizeToCells];
[[inspector reenableDisplay] displayIfNeeded];
[scrollView display];
[marker display];
}
- (void) showAllFieldsFor:(int)aType
{
int i, j, count, mode;
configListType *item;
locType location;
BOOL multiLine;
BOOL mustEnable = NO;
if( (aType==STRING)||(aType==PREAMBLE)||(aType==COMMENT) ){
[self showString:aType];
return;
}
if( [inspector isDisplayEnabled] ){
[inspector disableDisplay];
mustEnable = YES;
}
// how many fields are needed ?
configList = [preferences configList]; // actual list
for( i=0, count=0; i<[configList count]; i++ ){
item = ((configListType*)[configList elementAt:i]);
mode = item->mode[aType];
item->tag = -1; // clear
if( LARGE IN mode ) continue;
if( SHOW IN mode ) count++;
}
[[entryForm renewRows:count cols:1] sizeToCells];
[noteMatrix renewRows:0 cols:1];
[marker renewRows:0 cols:1];
for( i=0, j=0; i<[configList count]; i++ ){
item = ((configListType*)[configList elementAt:i]);
mode = item->mode[aType];
location.start = item->start; // set by the parser
location.length = item->length;
if( SHOW IN mode ){
if( (LARGE IN mode) ){ // copy to the noteView - textObject
id cell;
int row, col;
[noteMatrix addRow];
[noteMatrix getNumRows:&row numCols:&col];
cell = [noteMatrix cellAt:(row-1) :0];
[cell setTitle:item->fieldName];
// copy the content to textObject[row-1];
[self copyLoc:location ofObject:theObject to:row-1];
}
else{ // copy into a formcell
[[entryForm cellAt:j :0] setTitleFont:theFont];
[entryForm setTitle:[self titleOf:item] at:j];
item->tag = j;
multiLine = [self fillFormAt:j for:location];
[self addMarker:multiLine at:j];
j++;
}
}
}
[noteMatrix sizeToCells];
[marker sizeToCells];
noteIsDirty = NO;
[self noteSelect:[noteMatrix selectCellAt:actualRow :0]];
if( mustEnable ) [inspector reenableDisplay];
}
// here is a method envoked from controller and finder
- setEntry:anObject inFile:(BibliographicFile *)file
{
locType range;
if( inspector==nil ){
if( file ) [self firstCall]; // else no inspector needed
else return self;
}
if(0)if( isDirty==YES ){ // doesn't work
int rtn;
//fprintf( stderr, "isdirty\n" );
rtn = NXRunAlertPanel("EntryInspector", // title
"entry did change", // message
" save ", // 1: default button
" don't save ", // 0: alternate "
" cancel " //-1: other "
);
if( rtn==NX_ALERTDEFAULT ){
//[self entryChangedIsNew:isNew];
//[self makeEntry:self];
}
if( rtn==NX_ALERTOTHER ) return self;
}
keyIsValid = (anObject==nil) ? NO : YES;
objWithThisKey = anObject;
if( (theObject==anObject) && (theFile==file) ){
if( isDirty==NO ) return self;
isDirty=NO; // restore
}
theObject = anObject;
theFile = file;
[entryForm setAutosizing:entryFormMask];
[scrollView setAutosizing:scrollViewMask];
if( (theFile == nil) || (theObject == nil) ){
[self clear:self];
return self;
}
[inspector disableDisplay];
[inspector endEditingFor:nil];
[self enableAddButton]; // set the filename as title
[self enableReplaceButton]; // sets key as title
[self updateButtons];
// theObject and theFile are both existent
[inspector setTitleAsFilename:[theFile fullPath]];
if( NO /*isDirty==YES*/ ){
//entryType = aType;
}
else {
[theObject parseSelf];
[leftBraceButton setState:( [theObject delimiter]==RBRACE )?0:1];
[rightBraceButton setState:( [theObject delimiter]==RBRACE )?0:1];
if(0)if( (entryType==[theObject entryType]) && (prefsDidChange==NO) ){
// the new entry is the same entrytype as the former entry
// we need no resizing, no new toggle buttons
[self fillTemplate];
[inspector orderFront:self];
return self;
}
prefsDidChange=NO;
entryType=[theObject entryType];
[keyField setEnabled:YES];
if( entryType==STRING ){
NXAttachPopUpList( popUpButton, popUpListString );
[keyForm setNextText:noteView];
}
else
if( entryType==PREAMBLE ){
NXAttachPopUpList( popUpButton, popUpListPreamble );
[keyField setEnabled:NO];
}
else
if( entryType==COMMENT ){
NXAttachPopUpList( popUpButton, popUpListComment );
[keyField setEnabled:NO];
}
else{
NXAttachPopUpList( popUpButton, popUpList );
[keyForm setNextText:entryForm];
}
isDirty = NO;
isNew = NO;
}
[theObject setKeyTo:keyField];
range = [theObject range];
sprintf( [viewBuffer buffer], "line %d, charpos %d, length %d",
[theObject line], range.start, range.length );
[positionField setStringValue:[viewBuffer buffer]];
[self showAllFieldsFor:entryType];
// and now resize the window
[self resizeInspector:&newWindowFrame];
[self setToggleButtonsFor:entryType];
[popUpButton setTitle:
(const char *)[entryNameList objectAt:entryType]];
[inspector reenableDisplay];
[self displayEntry:self];
return self;
}
- displayEntry:sender
{
[inspector placeWindowAndDisplay:&newWindowFrame];
[inspector orderFront:self];
return self;
}
// erases the form, nothing else is changed
- clear:sender
{
int rows, cols, i;
if( inspector==nil ) return self;
[inspector disableDisplay];
[inspector endEditingFor:nil];
if( theFile ){
[inspector setTitleAsFilename:[theFile fullPath]];
//[self callSomethingNotImplemented]; // Make me crash!!!
}
else{
[inspector setTitle:"Entry Panel"];
}
[entryForm getNumRows:&rows numCols:&cols];
for( i=0; i<rows; i++ ){
[entryForm setStringValue:NULL at:i];
[[marker cellAt:i :0] setStringValue:NULL];
}
[keyField setStringValue:NULL];
//[noteView setText:""]; // loescht nicht
isDirty = (theObject)? YES :NO; // --> revert is possible
keyIsValid = NO;
[self updateButtons];
[clearButton setEnabled:NO];
[self clearConfigList]; // !!!!
[[inspector reenableDisplay] displayIfNeeded];
[marker display];
[noteView setText:""];
[scrollView displayIfNeeded];
return self;
}
- bibPopup:sender // sender is Matrix
{
int originalEntryType = [theObject entryType];
entryType = [sender selectedRow];
if( theObject==nil ){
[self drawTemplate:entryType];
return self;
}
entryType = [sender selectedRow];
if( ((originalEntryType==STRING) && (entryType!=STRING)) ||
((originalEntryType!=STRING) && (entryType==STRING)) ){
NXRunAlertPanel("EntryInspector", // title
"you cannot change %s to %s", // message
" OK ", // 1: default button
NULL, // 0: alternate "
NULL, //-1: other "
(const char *)[entryNameList objectAt:originalEntryType],
(const char *)[entryNameList objectAt:entryType]
);
[sender selectCellAt:originalEntryType :0];
return self;
}
[inspector endEditingFor:nil];
isDirty = isDirty || (entryType!=[theObject entryType]);
[self drawTemplate:entryType]; // empty inspector
[self fillTemplate];
[self updateButtons];
return self;
}
- (BOOL)saveDigitsFrom:(int)inx :(const char *)txt
{
char *digitText;
int count = 0;
while( NXIsSpace(*txt) ) txt++;
if( *txt=='\0' ) return NO;
digitText = (char *)txt; // first digit
while( NXIsDigit(*txt) ){ txt++; count++; }
while( NXIsSpace(*txt) ) txt++; // trailing blanks
if( *txt ) return NO;
NXPrintf( localStream, ",\n\t%s\t= ",
[self stringOf:[entryForm titleAt:inx]] );
while( count-- ){ NXPutc( localStream, *digitText ); digitText++; }
return YES;
}
- convert:(const char *)txt toStream:(NXStream *)toStream
{
unsigned char c;
int i = 0;
int level = 0;
BOOL leftPartIsEmpty = YES;
// skip over blanks
c = txt[0];
while( NXIsSpace(c) ) c = txt[++i];
if( c=='\0' ) return self; // empty text
while( YES ){
if( c=='\0' ) return self;
if( c!='@' ){
if( leftPartIsEmpty==NO ) ADD_HASH_MARK;
NXPutc(toStream, LBRACE);
while( c && ( (c!='@')||(level>0) ) ){
if( c>=0x80 ){
unsigned char tc;
const char *texText = [BibTexParser NX2TeX][ c-0x80 ];
while( tc = *texText++ ) NXPutc(toStream, tc);
}
else NXPutc(toStream, c);
if( c==LBRACE ) level++;
if( c==RBRACE ) level--;
c = txt[++i];
}
NXPutc(toStream, RBRACE);
if( c=='\0' ) return self;
leftPartIsEmpty = NO;
}
// c == '@'
if( leftPartIsEmpty==NO ) ADD_HASH_MARK;
c = txt[++i];
//while( NXIsAlpha(c) ) // STRING allows more characters --> theTen
while( c && (c!=',') && !NXIsSpace(c) && (c!='@') ){
NXPutc(toStream, c);
c = txt[++i];
leftPartIsEmpty = NO;
}
while( NXIsSpace(c) ) c = txt[++i]; // skip trailing blanks
}
return self;
}
- parseKey
{
int first, last,i=0;
const char *key;
char keyBuffer[64];
if( (entryType==PREAMBLE)||(entryType==COMMENT) ){
keyIsValid = YES;
objWithThisKey=theObject;
return self;
}
keyIsValid = [self key:[keyField stringValue] isValidFrom:&first to:&last];
if( keyIsValid==NO ){
objWithThisKey = nil;
return self;
}
key = [keyField stringValue];
while( *key ){ keyBuffer[i++] = *key++; } // copy the key
keyBuffer[last] = '\0';
objWithThisKey = [theFile findKey:&keyBuffer[first]];
return self;
}
- entryChangedIsNew:(BOOL)n
{
isNew = n;
isDirty = NO;
[self makeEntry:self];
[theFile setDirty];
[self parseKey];
[self updateButtons];
return self;
}
- addNewEntry:sender
{
return [self entryChangedIsNew:YES];
}
- replaceEntry:sender
{
return [self entryChangedIsNew:NO];
}
- (BOOL) textIsEmpty:(const char *)txt
{
int i = 0;
unsigned char c = txt[0];
while( NXIsSpace(c) ) c = txt[++i];
return c=='\0';
}
- makeEntry:sender // from add-/replace button
{
int rows, cols, i, pos;
char *outString;
NXStream *theStream;
if(0)if( theObject==nil ){ // it's really new
theStream = [theFile stream];
return self;
}
if ( localStream==NULL ){
localStream = NXOpenMemory( NULL, 0, NX_READWRITE );
}
NXSeek(localStream, 0, NX_FROMSTART);
[entryForm getNumRows:&rows numCols:&cols];
// update changes (if any)
if( noteIsDirty ){ // write the text back to the buffer
char *actualTextBuffer =
[self listBufferFor:actualRow
size:[noteView textLength]+1];
[noteView getSubstring:actualTextBuffer
start:0 length:[noteView textLength]+1];
}
// print all fields from the view
if( entryType==PREAMBLE ){ // PREAMBLE has no keyfield
THE_TYPE; // @PREAMBLE
BIG_LEFT;
//LEFTQUOTE; ACTUALBUFFER; RIGHTQUOTE;
ACTUALBUFFER;
BIG_RIGHT;
}
else
if( entryType==COMMENT ){ // COMMENT has no keyfield
THE_TYPE; // @COMMENT
BIG_LEFT;
ACTUALBUFFER;
BIG_RIGHT;
}
else
if( entryType==STRING ){
THE_TYPE; // @STRING
BIG_LEFT;
//THE_KEY; ASSIGN; LEFTQUOTE; ACTUALBUFFER; RIGHTQUOTE;
THE_KEY; ASSIGN; ACTUALBUFFER;
BIG_RIGHT;
}
else{
THE_TYPE; // @BOOK or so
BIG_LEFT;
THE_KEY; // key value
for( i=0; i<[configList count]; i++ ){
locType location;
configListType *item =
((configListType*)[configList elementAt:i]);
int tag = item->tag;
int mode = item->mode[entryType];
//fprintf( stderr, "%3d %s\n", item->tag, item->fieldName );
if( (tag>=0)
&& [entryForm stringValueAt:tag]
&& (![self textIsEmpty:[entryForm stringValueAt:tag]]) ){
outString = (char *)[self convertText:tag];
// check for digits
if( [self saveDigitsFrom:tag :outString] ) continue;
NEXTENTRY([self stringOf:[entryForm titleAt:tag]]);
//LEFTQUOTE;
[self convert:[entryForm stringValueAt:tag]
toStream:localStream];
//RIGHTQUOTE;
}
else if( (tag<0) && !(LARGE IN mode) ) {
location.start = item->start; // set by the parser
location.length = item->length;
if( location.length > 0 ){
NXPrintf( localStream, ",\n\t%s\t= ", item->fieldName );
[theObject copyRange:location toStream:localStream];
}
}
}
// copy LARGE entry fields
{
int row, col, i;
[noteMatrix getNumRows:&row numCols:&col];
for( i=0; i<row; i++ ){
id cell = [noteMatrix cellAt:i :0];
char *buffer = [self listBufferFor:i];
if( buffer && (![self textIsEmpty:buffer]) ){
NEXTENTRY([cell title]);
//LEFTQUOTE;
[self convert:(const char *)buffer toStream:localStream];
//RIGHTQUOTE;
}
}
}
NEWLINE;
BIG_RIGHT;
}
NXFlush( localStream );
pos = NXTell( localStream );
// we need the buffer of this stream for NXWrite
NXGetMemoryBuffer( localStream,
&localBuffer, &locBufferLength, &locBufferMaxLength);
// we have now a bibtex - conforming text of the entryObject
// append the text
if( isNew==YES ){
theObject = [theFile appendNewText:pos :localBuffer];
[theObject parseSelf]; // @@@@@ 1.1
}
else{
//[theFile replace:pos :localBuffer forObject:theObject];
[theFile replaceTextOf:theObject with:localBuffer length:pos];
}
[[NXApp delegate] update:theObject for:theFile isNew:isNew shiftLit:NO];
return self;
}
- revert:sender
{
isDirty=YES;
[self setEntry:theObject inFile:theFile];
//isDirty=NO;
[self updateButtons];
return self;
}
// copy selected text to findPasteBoard
- enterSelection:sender
{
[[NXApp delegate] enterSelectionFor:[inspector firstResponder]];
return self;
}
- mainBrace:sender
{
[leftBraceButton setState:[sender state]];
[rightBraceButton setState:[sender state]];
if( theFile ){
isDirty = YES;
[self updateButtons];
}
return self;
}
- showCharPanel:sender
{
[[charMatrix window] orderFront:self];
return self;
}
- insertText:(char *)txt
{
id keyWindow = [NXApp keyWindow];
id delegate = [keyWindow delegate];
id editor = [keyWindow firstResponder];
NXSelPt start, end;
if( ![editor respondsTo:@selector(getSel::)] ) return self;
[editor getSel:&start :&end];
[editor replaceSel:txt];
if( [delegate respondsTo:@selector(textDidGetKeys:isEmpty:)] ){
[delegate textDidGetKeys:editor isEmpty:NO];
}
return self;
}
extern fnct_convertRawString();
- insertMacro:(char *)txt isRaw:(BOOL)isRaw
{
id editor = [inspector firstResponder];
NXSelPt start, end;
if( ![editor isKindOf:[Text class]] ){
return self;
}
else
[editor getSel:&start :&end];
if( start.cp<0 ){ // nothing selected (noteView?)
return self;
//[editor getSel:&start :&end];
}
if( isRaw ){
[viewBuffer setMinSizeTo:strlen(txt)+1];
fnct_convertRawString(txt, [viewBuffer buffer] );
[editor replaceSel:[viewBuffer buffer]];
}
else [editor replaceSel:txt];
//[editor showCaret];
[self textDidGetKeys:editor isEmpty:NO];
return self;
}
- specialMatrix:sender
{
[self insertText:(char *)[[sender selectedCell] title]];
return self;
}
// * * * * * * * delegate methods
- windowDidResignKey:sender
{
return self;
}
- windowWillReturnFieldEditor:sender toObject:client
{
return nil;
// we need something like 'endEditingfor:'
fprintf( stderr, "client is %s", [client name] );
if( [client class]==[Button class] ){
fprintf( stderr, " %s", [client title] );
}
if( [client class]==[TextField class] ){
fprintf( stderr, " %d", [client tag] );
}
fprintf( stderr, "\n" );
if( [client class]==[Form class] ){
if(0)[sender perform:@selector(endEditingFor:)
with:(id)nil afterDelay:0 cancelPrevious:YES];
if(1)
fprintf( stderr, "sender is %s firstResponder = %s index=%d tag=%d\n",
[sender name],
[[sender firstResponder] name],
[client selectedIndex],
[client tag] );
[sender endEditingFor:nil];
return fieldEditor;
}
return nil;
}
- windowWillResize:sender toSize:(NXSize *)frameSize
{
[entryForm setAutosizing: NX_WIDTHSIZABLE | NX_MINYMARGINSIZABLE];
[marker setAutosizing: NX_MINXMARGINSIZABLE | NX_MINYMARGINSIZABLE];
[scrollView setAutosizing: NX_WIDTHSIZABLE | NX_HEIGHTSIZABLE ];
return self;
}
- windowDidResize:sender
{
NXRect newFrame;
[inspector getFrame:&newFrame];
[sender endEditingFor:nil];
windowFrame.W = newFrame.W;
windowFrame.X = newFrame.X;
[entryForm setAutosizing:entryFormMask]; // @@@@ 1.1
[scrollView setAutosizing:scrollViewMask];
[marker setAutosizing:markerMask];
return self;
}
- windowDidMove:sender
{
NXRect newFrame;
[inspector getFrame:&newFrame];
windowFrame.X = newFrame.X;
windowFrame.Y =
newFrame.Y + newFrame.H; // northwest_corner
return self;
}
- textDidGetKeys:sender isEmpty:(BOOL)flag
{
if(0)fprintf( stderr, "sender is %s resp=%s, tag=%d\n",
[sender name], [[inspector firstResponder]name], [[sender delegate] tag] );
if( [[sender delegate] tag] == KEY_FIELD_TAG ){
[self parseKey];
if( theFile ) [self updateButtons];
return self;
}
isDirty = YES;
if( theFile ) [self updateButtons];
// set the marker
if( [[sender delegate] tag]==FORM_FIELD_TAG ){
int i = 0;
char c;
int row = [[sender delegate] selectedRow];
const char *theString = [[sender delegate] stringValueAt:row];
if( theString==NULL ) return self;
while( c=theString[i++] ){
if( c=='\n' )break;
}
[self setMarker:c=='\n' at:row];
}
noteIsDirty = YES;
return self;
}
- (BOOL)testSyntaxFor:(const char *)txt
// returns NO, if this method has no objections
{
char c;
int braceLevel = 0, paraLevel = 0;
if( (txt==NULL) || (*txt=='\0') ) return NO;
while( c=*txt++ ){
if( c==LBRACE ) braceLevel++;
else
if( c==RBRACE ) braceLevel--;
else
if( c==LPARA ) paraLevel++;
else
if( c==RPARA ) paraLevel--;
}
if( braceLevel || paraLevel ) return YES;
return NO;
}
- (BOOL)testSyntaxForStream:(NXStream *)str
// returns NO, if this method has no objections
{
char c;
int braceLevel = 0, paraLevel = 0;
NXSeek( str, 0, NX_FROMSTART );
while( (c = NXGetc( str ))!=EOF ){
if( c==LBRACE ) braceLevel++;
else
if( c==RBRACE ) braceLevel--;
else
if( c==LPARA ) paraLevel++;
else
if( c==RPARA ) paraLevel--;
}
if( braceLevel || paraLevel ){
NXBeep();
return YES;
}
return NO;
}
- (BOOL)textWillEnd:sender
{
//fprintf( stderr, "textWillEnd sender is %s\n", [sender name] );
//fprintf( stderr, "textWillEnd delagte is %s\n", [[sender delegate] name] );
//fprintf( stderr, "textWillEnd tag is %d\n", [[sender delegate] tag] );
if( [sender tag]==NOTE_TEXT_TAG ){
if( noteIsDirty==NO ) return NO;
// test text
return [self testSyntaxForStream:[noteView stream]];
}
if( [[sender delegate] tag]==KEY_FIELD_TAG ){
return NO; // key is ok
}
if( [[sender delegate] tag]==FORM_FIELD_TAG ){
int row = [[sender delegate] selectedRow];
// sender delegate is Form --> Matrix
return [self testSyntaxFor:
[[sender delegate] stringValueAt:row]];
}
return NO;
}
- XXXXtextDidEnd:sender endChar:(unsigned short)whyEnd
{
int i = 0;
char *theString, c;
if( [[sender delegate] tag]==FORM_FIELD_TAG ){
int row = [[sender delegate] selectedRow];
theString = (char *)[[sender delegate] stringValueAt:row];
if( theString==NULL ) return self;
while( c=theString[i++] ){
if( c=='\n' )break;
}
[self setMarker:c=='\n' at:row];
}
return self;
}
- (BOOL)shouldDelayWindowOrderingForEvent:(NXEvent *)theEvent
{
// This is the other portion of the new appkit functionality
// which allows you to drag a partially obscure object without
// making the source window become main, and without making the
// source application the current app.
fprintf( stderr, "shouldDelayWindow\n" );
return YES;
}
- (BOOL)acceptsFirstMouse
{
fprintf( stderr, "acceptsFirstMouse\n" );
return YES;
}
- (BOOL)updateMenuFor:menuCell
{
BOOL newState=NO; // .. and the compiler keeps silent
//BOOL isEnabled = [menuCell isEnabled];
switch( [menuCell tag] ){
case CUT : newState = YES; break;
case COPY : newState = YES; break;
case PASTE : newState = YES; break;
case SELECTALL : newState = YES; break;
case ENTERSELECTION : newState = YES; break;
default : newState =
[[[NXApp mainWindow] delegate]updateMenuFor:menuCell];
break;
}
return newState;
}
- setBacking:sender
{
[inspector setBackingType:
([sender state]==1) ? NX_BUFFERED :NX_RETAINED];
return self;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.