This is Shelf.m in view mode; [Download] [Up]
// Copyright H. Giesen, University of Koblenz-Landau 1996
#import "Shelf.h"
#import "Controller.h"
#import "DragView.h"
#import "BibliographicFile.h"
#import "Preferences.h"
#define DRAGVIEWTAG 7
#define W_SPACE 10.0
#define H_SPACE 10.0
#define L_MARGIN 5.0
@implementation Shelf
- initFrame:(const NXRect *)frameRect
/*
%initFrame% initializes the view and registers the Pasteboard types that the shelf will accept in an image-dragging session. The shelf accepts only the NXFilenamePboardType.
Returns self.
*/
{
[self registerForDraggedTypes:&NXFilenamePboardType count:1];
MyListPboardType = NXUniqueString( "My List Pboard Type" );
[super initFrame:frameRect];
[self setFlipped:YES];
preferences = [Preferences new];
isSelected = NO;
theImage = [NXImage findImageNamed:"Images/SmallBooks"];
boxList = [[List alloc] init];
return self;
}
- (void) setBoxPrototype:(Box *)aBox
/* New boxes for the shelf are generated from %aStream% by calling
the function NXReadObjectFromBuffer() using the buffer of %aStream%.
See also readBoxFromStream.
*/
{
NXTypedStream *masterTypedStr;
int streamLength;
int streamMaxLength;
char *streamBuffer;
[aBox getFrame:&boxRect];
protypeStream = NXOpenMemory( NULL, 0, NX_WRITEONLY);
masterTypedStr = NXOpenTypedStream(protypeStream, NX_WRITEONLY);
NXWriteRootObject( masterTypedStr, aBox );
NXCloseTypedStream( masterTypedStr );
NXGetMemoryBuffer(protypeStream,
&streamBuffer, &streamLength, &streamMaxLength);
NXCloseMemory(protypeStream, NX_TRUNCATEBUFFER);
protypeStream = NXOpenMemory( streamBuffer, streamLength, NX_READONLY);
}
- (BOOL)loadShelfFromFile:(const char *)file
{
int newInx;
int okFlag;
Box *newBox;
char buffer[1024];
char *first, *last, *comment;
FILE *fp;
fp = fopen( file, "r" );
if( fp==NULL ) return NO;
if( protypeStream==NULL ) return NO;
while( fgets( buffer, 512, fp ) ){
first = strchr( buffer, '"'); // first occurence of '"'
if( first==NULL ) continue;
last = strrchr( buffer, '"'); // last occurence of '"'
if( last==NULL ) continue;
*last = '\0';
// commentline ?
comment = strchr( buffer, '%'); // first occurence of '%'
if( comment && (comment<first) ) continue;
// filename is ok
newBox = [self readBoxFromStream:protypeStream];
newInx = [self insertBox:newBox];
[[newBox findViewWithTag:DRAGVIEWTAG] setFilename:first+1];
last++; //first character behind '\0', was '"'
while( *last ){
int rtn;
if( *last=='+' ){ // open the file
shelfBox[newInx].state = 1;
rtn = [NXApp openFile:first+1 ok:&okFlag];
break;
}
if( *last=='-' ) break;
last++;
}
}
fclose( fp );
return YES;
}
- setBackgroundColor:(NXColor)color
{
[[self superview] setBackgroundColor:color];
[[self superview] display];
return self;
}
- setBackgroundGray:(float)gray
{
[[self superview] setBackgroundGray:gray];
//[[self superview] display];
[self display];
return self;
}
/*********** find box or empty slot ************/
- (int)indexOfBox:(Box *)box
{
int j;
for( j=0; j<count; j++ ){
if( shelfBox[j].theBox == box ) return j;
}
return -1;
}
- (int)indexOfBoxWithIcon:(DragView *)icon
{
return [boxList indexOf:icon];
}
- (int)indexOfBoxForFile:(BibliographicFile *)aFile
{
//DragView *theView;
int j;
for( j=0; j<count; j++ ){
//theView = [boxList objectAt:j];
if( shelfBox[j].theFile == aFile ) return j;
}
return -1;
}
- (int)indexOfBoxNamed:(const char *)fileName
{
DragView *theView;
int j;
for( j=0; j<count; j++ ){
theView = [boxList objectAt:j];
if( [theView fileName]==NULL ) continue;
if( strcmp([theView fileName], fileName)==0 ) return j;
}
return -1;
}
- (int)indexOfEmptySlot
{
int j;
for( j=0; j<count; j++ ){
if( shelfBox[j].theBox==nil ) return j;
}
return -1;
}
/***** properties of an icon ****/
- (const char *)filenameOfIconAt:(int)inx
{
if( (inx<0) || (inx>=count) ) return NULL;
if( shelfBox[inx].theBox==nil ) return NULL;
return [[boxList objectAt:inx] fileName];
}
- (int)getstateOfIconAt:(int)inx
{
if( (inx<0) || (inx>=count) ) return -1;
return shelfBox[inx].state;
}
- setState:(int)state forIconAt:(int)inx
{
if( (inx<0) || (inx>=count) ) return nil;
shelfBox[inx].state = state;
return self;
}
/******** notification methods *******/
static const char * dragTypes[3];
- attachFile:(const char *)fName toObject:(BibliographicFile *)obj
{
DragView *theView;
int j = [self indexOfBoxNamed:fName];
if( j<0 ) return nil;
theView = [boxList objectAt:j];
[theView setFileObject:obj];
dragTypes[0] = MyListPboardType;
dragTypes[1] = NXAsciiPboardType;
[theView registerForDraggedTypes:dragTypes count:2];
shelfBox[j].theFile = obj;
[theView display];
return self;
}
- attachObject:(BibliographicFile *)obj toFile:(const char *)fName
{
DragView *theView;
int j = [self indexOfBoxForFile:obj];
if( j<0 ) return nil;
theView = [boxList objectAt:j];
[theView setFileObject:obj];
[theView display];
[preferences shelfDidChange];
return self;
}
- detachObject:(BibliographicFile *)obj
{
int j = [self indexOfBoxForFile:obj];
if( j<0 ) return nil;
[[boxList objectAt:j] unregister];
shelfBox[j].theFile = nil;
return self;
}
/*********** remove box identified by ... ************/
- (void) removeBoxAt:(int)inx
{
DragView *theView;
if( inx<0 ) return;
theView = [boxList objectAt:inx];
[theView clear];
[theView unregisterDraggedTypes];
[shelfBox[inx].theBox removeFromSuperview];
[NXApp delayedFree:shelfBox[inx].theBox];
shelfBox[inx].theBox = nil;
shelfBox[inx].theFile = nil;
[boxList removeObjectAt:inx];
[self redrawShelf];
return;
if( inx==count-1 ){ // determine last box and compact
while( count && (shelfBox[count-1].theBox==nil) ) count--;
[self redrawShelf];
}
else [self redrawShelf]; // compact
//else [self display]; // does not compact
}
- (void) removeBoxWithIcon:(DragView *)icon
{
[self removeBoxAt:[self indexOfBoxWithIcon:icon]];
[self display];
[preferences shelfDidChange];
}
- (Box *) readBoxFromStream:(NXStream *)theStream
{
char *buffer;
int len, maxlen;
Box *box;
NXGetMemoryBuffer(theStream, &buffer, &len, &maxlen);
box = NXReadObjectFromBuffer( buffer, len);
[box getFrame:&boxRect];
[box setBorderType:NX_NOBORDER];
return box;
}
- (void) sizeShelf:(int)cnt
{
NXRect r;
if( scrollView==nil ){ // first call
needsHorizontalScroller = YES; //1.2 select type of scroller
scrollView = [[self superview] superview];
[scrollView setVertScrollerRequired:!needsHorizontalScroller];
[scrollView setHorizScrollerRequired:needsHorizontalScroller];
if( needsHorizontalScroller )
[[scrollView setVertScroller:nil] free];
else [[scrollView setHorizScroller:nil] free];
// set size (this is the first call)
[[self superview] getFrame:&r];
[self sizeTo:r.W :r.H];
}
[[self superview] getFrame:&r];
if( needsHorizontalScroller ){ // horizontal
NXCoord width, height;
iconsPerRow = cnt;
width = iconsPerRow*(boxRect.W + W_SPACE);
height = boxRect.H + H_SPACE;
[self sizeTo:( (width<r.W)?r.W:width )
:( (height<r.H)?r.H:(boxRect.H + H_SPACE) )];
[scrollView setLineScroll:(boxRect.W + W_SPACE)];
}
else{
iconsPerRow = (int)((r.W - L_MARGIN + W_SPACE)/(boxRect.W + W_SPACE));
[self sizeTo:r.W
:(((cnt-1)/iconsPerRow)+1)*(boxRect.H + H_SPACE)];
[scrollView setLineScroll:(boxRect.H + H_SPACE)];
}
}
- (void) setCoordinates:(NXRect *)r at:(int)inx
{
if( inx<0 ) { r->X = W_SPACE; r->Y = H_SPACE; return; }
if( needsHorizontalScroller ){ // horizontal
r->X = (inx)*(r->W + W_SPACE) + L_MARGIN;
r->Y = 0;
}
else{
r->X = (inx%iconsPerRow)*(r->W + W_SPACE) + L_MARGIN;
r->Y = (inx/iconsPerRow)*(r->H + H_SPACE);
}
}
- (void) redrawShelf
{
Box *box;
int in, out;
[self sizeShelf:count];
for( in=0, out=0; in<count; in++){
box = shelfBox[in].theBox;
if( box==nil ) continue;
[self setCoordinates:&boxRect at:out];
[box setFrame:&boxRect];
shelfBox[out] = shelfBox[in];
out++;
}
if( count==out ){
[[self superview] display];
return;
}
count = out;
[self sizeShelf:count];
[[self superview] display];
[preferences shelfDidChange];
}
- (int)insertBox:(Box *)box at:(int)inx
{
DragView *newView;
if( (inx<0) || (inx>=MAXCOUNT) ){
fprintf( stderr, "internal error, invalid shelf index %d\n", inx );
return -1;
}
[self setCoordinates:&boxRect at:inx];
[box setFrame:&boxRect];
newView = [box findViewWithTag:DRAGVIEWTAG];
[boxList addObject:newView];
[newView setShelf:self];
shelfBox[inx].theBox = box;
shelfBox[inx].theFile = nil;
shelfBox[inx].state = 0;
[self addSubview:box];
[self scrollRectToVisible:&boxRect];
[preferences shelfDidChange];
return inx;
}
- (int)insertBox:(Box *)box
{
int inx = [self indexOfEmptySlot];
if( inx>=0 ){
[self insertBox:box at:inx];
}
else {
if( count>=MAXCOUNT ){
fprintf( stderr, "too many icons\n" ); // <---------
return -1;
}
count++;
[self sizeShelf:count];
inx = [self insertBox:box at:count-1]; // last entry in table
}
[self display];
return inx;
}
- (int)count
{
return count;
}
- windowDidResize:sender
{
[self redrawShelf];
return self;
}
/************ methods used while dragging ************/
- drawSelf:(const NXRect *)rects :(int)rectCount
{
NXRect shadowBoxRect = boxRect;
NXPoint zero = {0.0, 0.0 };
if( isSelected && (theImage!=nil) ){
NXImage *tmpImage = [[NXImage alloc] initSize:&boxRect.size];
if( [tmpImage lockFocus] ){
PSsetgray( NX_LTGRAY );
PSrectfill(0.0, 0.0, boxRect.size.width, boxRect.size.height);
shadowBoxRect.origin.x += 14;
shadowBoxRect.origin.y +=shadowBoxRect.size.height - 16;
[theImage dissolve:0.6 toPoint:&zero];
[tmpImage unlockFocus];
[tmpImage composite:NX_SOVER toPoint:&shadowBoxRect.origin];
[tmpImage free];
}
else{
fprintf( stderr, "cannot lockFocus\n" );
}
}
return self;
}
/************ dragging ************/
// includesType() returns YES if %type% is included within %types%.
static BOOL includesType (const NXAtom *types, NXAtom type)
{
if (types)
while (*types){ if (*types++ == type) return YES; }
return NO;
}
- (NXDragOperation)draggingEntered:(id <NXDraggingInfo>)sender
{
int inx;
NXDragOperation rtn = [self draggingUpdated:sender];
if( rtn!=NX_DragOperationGeneric ) return rtn;
inx = [self indexOfEmptySlot];
if( inx<0 ){
[self sizeShelf:count+1];
inx = count;
}
[self setCoordinates:&boxRect at:inx];
isSelected = YES;
theImage = [sender draggedImage];
[self scrollRectToVisible:&boxRect];
[self display];
return rtn;
}
- (NXDragOperation)draggingUpdated:(id <NXDraggingInfo>)sender
{
Pasteboard *pboard = [sender draggingPasteboard];
char *data, *lastDot, *file, *tab;
int length;
if (includesType([pboard types], MyListPboardType)) {
return NX_DragOperationNone;
}
if( ([sender draggingSourceOperationMask] & NX_DragOperationGeneric)
||([sender draggingSourceOperationMask] & NX_DragOperationCopy) ){
if( includesType([pboard types], NXFilenamePboardType) ){
[pboard readType:NXFilenamePboardType data:&data length:&length];
// is it a list of files ?
file = data;
while (file) {
if( tab = strchr(file, '\t') ) { *tab = '\0'; }
lastDot = rindex( file, '.' );
if( lastDot ){
if( strcmp( lastDot, ".bib" ) ) { // only .bib allowed
return NX_DragOperationNone;
}
}
else return NX_DragOperationNone; // no suffix -> invalid
// is the file already on the shelf ?
if( [self indexOfBoxNamed:file]>=0 ){ // yes
return NX_DragOperationNone;
}
file = (tab) ? ++tab : NULL;
}
return NX_DragOperationGeneric;
}
}
return NX_DragOperationNone;
}
- (BOOL)performDragOperation:(id <NXDraggingInfo>)sender
{
char *data, *file, *tab;
int length;
Pasteboard *pboard = [sender draggingPasteboard];
isSelected = NO;
if( includesType([pboard types], NXFilenamePboardType) ){
[pboard readType:NXFilenamePboardType data:&data length:&length];
file = data;
while (file) {
Box *newBox;
DragView *newView;
BibliographicFile *fileObj;
if (tab = strchr(file, '\t')) *tab = '\0';
newBox = [self readBoxFromStream:protypeStream];
newView = [newBox findViewWithTag:DRAGVIEWTAG];
[newView setFilename:file];
[self insertBox:newBox];
// ask if this file is open in this application
fileObj = [[NXApp delegate] objectForFileNamed:file];
if( fileObj )[self attachFile:file toObject:fileObj];
file = (tab) ? ++tab : NULL;
}
return YES;
}
return NO;
}
- draggingExited:sender
{
isSelected = NO;
[self redrawShelf];
return self;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.