This is IconWell.m in view mode; [Download] [Up]
// IconWell.m
//
// Free software created 1 Feb 1992
// by Paul Burchard <burchard@math.utah.edu>.
#import "IconWell.h"
#import "IconWellControl.h"
#import <appkit/appkit.h>
#import <objc/Storage.h>
@implementation IconWell
static id windowList;
static id wellsByWindow;
+ initialize
{
if(self == [IconWell class])
{
windowList = [[List alloc] initCount:0];
wellsByWindow = [[List alloc] initCount:0];
}
return self;
}
+ wellListFor:aWindow
{
int index;
index = [windowList indexOf:aWindow];
if(index == NX_NOT_IN_LIST) return nil;
return [wellsByWindow objectAt:index];
}
- initFrame:(const NXRect *)frameRect
{
id theCell;
[super initFrame:frameRect];
theCell = [[ActionCell alloc] initIconCell:blankIconName];
[theCell setBezeled:YES];
[self setCell:theCell];
iconPath = [[Storage alloc] initCount:0 elementSize:sizeof(char) description:"c"];
sprintf((char *)iconName, "IconWell-%ld", (long)[self self]);
isHoldOnDrag = YES;
[self setDraggable:YES droppable:YES];
return self;
}
- free
{
int index;
id list;
[iconPath free];
if(list = [IconWell wellListFor:window])
{
[list removeObject:self];
if([list count] <= 0)
{
index = [wellsByWindow indexOf:list];
[wellsByWindow removeObjectAt:index];
[windowList removeObjectAt:index];
[list free];
}
}
return [super free];
}
- windowChanged:newWindow
{
id list;
// Enter new well into global list.
if(window || !newWindow) return nil;//!!!
if(!(list = [IconWell wellListFor:newWindow]))
{
list = [[List alloc] initCount:0];
[windowList addObject:newWindow];
[wellsByWindow addObject:list];
}
[list addObjectIfAbsent:self];
return self;
}
- setBezeled:(BOOL)flag
{
return [cell setBezeled:flag];
}
- (BOOL)isBezeled
{
return [cell isBezeled];
}
- setBordered:(BOOL)flag
{
return [cell setBordered:flag];
}
- (BOOL)isBordered
{
return [cell isBordered];
}
- setDraggable:(BOOL)dragFlag droppable:(BOOL)dropFlag
{
isDraggable = dragFlag;
isDroppable = dropFlag;
return self;
}
- (BOOL)isDraggable
{
return isDraggable;
}
- (BOOL)isDroppable
{
return isDroppable;
}
- setHoldOnDrag:(BOOL)flag
{
if(isDragging) return nil;
isHoldOnDrag = flag;
return self;
}
- (BOOL)isHoldOnDrag
{
return isHoldOnDrag;
}
- clear:sender
{
// This does not de-alloc Blank.tiff since it was not renamed.
// This only affects this IconWell because of the unique iconName.
[cell setIcon:blankIconName];
[self update]; //!!! make sure image really replaced before freeing
[[NXImage findImageNamed:(const char *)iconName] free];
[iconPath setNumSlots:0];
return self;
}
- (const char *)stringValue
{
if([iconPath count] <= 0) return 0;
return (const char *)[iconPath elementAt:0];
}
- getTiffForPath:(const char *)pathString
{
int ok, length;
char *tiff, *fakePath, *q;
const char *p;
NXStream *imageStream;
id iconImage;
// Ask WorkSpace for correct TIFF corresponding to path list pathString.
// Since ``full paths'' are required, prepend '/' to each name if missing.
if(!pathString) return nil;
if(!(fakePath = (char *)malloc((strlen(pathString)+1)*sizeof(char))))
return nil;
for(p=pathString, q=fakePath; *p; p++)
{
if(p==pathString && *p!='/') *q++ = '/';
*q++ = *p;
if(*p=='\t' && *(p+1)!='/') *q++ = '/';
}
*q = 0;
[[NXApp appSpeaker] setSendPort:NXPortFromName(NX_WORKSPACEREQUEST, NULL)];
[[NXApp appSpeaker] getFileIconFor:fakePath
TIFF:&tiff TIFFLength:&length ok:&ok];
free(fakePath);
// If no icon, use generic ".txt" icon.
if(!ok)
{
[[NXApp appSpeaker]
setSendPort:NXPortFromName(NX_WORKSPACEREQUEST, NULL)];
[[NXApp appSpeaker] getFileIconFor:"/file.txt"
TIFF:&tiff TIFFLength:&length ok:&ok];
if(!ok) return nil;
}
// Create NXImage from TIFF data.
imageStream = NXOpenMemory(tiff, length, NX_READONLY);
if(!imageStream) return nil;
iconImage = [[NXImage alloc] initFromStream:imageStream];
NXClose(imageStream);
return iconImage;
}
- setStringValue:(const char *)aString
{
id iconImage;
// If path is NULL, clear well.
if(!aString) [self clear:self];
// Ask WorkSpace for correct TIFF corresponding to path aString.
if(!(iconImage = [self getTiffForPath:aString])) return nil;
// Enter NXImage into cell, freeing previous image.
// (Note: Common blank image is not freed as it was never renamed.)
[cell setIcon:blankIconName];
[self update]; //!!! make sure image really replaced before freeing
[[NXImage findImageNamed:(const char *)iconName] free];
[iconImage setName:(const char *)iconName];
[cell setIcon:(const char *)iconName];
// Enter new path into iconPath, notify target.
[iconPath setNumSlots:(strlen(aString)+1)];
strcpy((char *)[iconPath elementAt:0], aString);
[self sendAction:[cell action] to:[cell target]];
return self;
}
- takeStringValueFrom:sender
{
id oldTarget = nil;
id rtn;
// If sender is target, don't send action (to avoid circularity).
if(sender == [self target])
{ oldTarget = [self target]; [self setTarget:nil]; }
rtn = [self setStringValue:[sender stringValue]];
if(oldTarget) [self setTarget:oldTarget];
return rtn;
}
- (BOOL)acceptsFirstMouse
{
return YES;
}
- (int)openFile:(const char *)fullPath ok:(int *)flag
{
int rtn;
// Ask WorkSpace to open the file. No fudging paths now.
[[NXApp appSpeaker] setSendPort:NXPortFromName(NX_WORKSPACEREQUEST, NULL)];
rtn = [[NXApp appSpeaker] openFile:fullPath ok:flag];
if(rtn != 0) return(rtn);
return 0;
}
- (int)prepFile:(const char *)fullPath ok:(int *)flag
{
// Default is no prep needed on drag-out.
return 0;
}
- setDelegate:anObject
{
delegate = anObject;
return self;
}
- delegate
{
return delegate;
}
- mouseDown:(NXEvent *)theEvent
{
int ok;
char *fileName, *nxt;
NXPoint mousePoint;
NXRect iconRect;
id success, handler;
if(theEvent->data.mouse.click < 2)
{
// Single click means drag icon out.
// Check if mouse actually clicked on icon.
mousePoint = theEvent->location;
[super convertPoint:&mousePoint fromView:nil];
[self getBounds:&iconRect];
[cell getIconRect:&iconRect];
NX_WIDTH(&iconRect) = NX_HEIGHT(&iconRect) = 48.0;
if(!NXMouseInRect(&mousePoint, &iconRect, [self isFlipped]))
return self;
// Don't actually start the drag until mouse gets dragged a bit.
if(isDraggable)
{
isDragging = YES;
[window addToEventMask:NX_LMOUSEDRAGGEDMASK];
dragFromEvent = *theEvent;
}
return self;
}
else if(theEvent->data.mouse.click == 2)
{
// Double-click means open file(s).
// Delegate this task if possible.
if([delegate respondsTo:@selector(openFile:ok:)]) handler = delegate;
else handler = self;
// Open files one by one.
if([iconPath count] <= 0) return nil;
fileName = (char *)[iconPath elementAt:0];
nxt = strchr(fileName, '\t');
success = self;
while(fileName)
{
if(nxt) *nxt = 0;
[handler openFile:fileName ok:&ok];
if(!ok) success = nil;
if(nxt)
{
*nxt = '\t';
fileName = nxt+1;
nxt = strchr(fileName, '\t');
}
else fileName = nxt = 0;
}
return success;
}
else
{
// Ignore higher multi-clicks.
return self;
}
}
- mouseDragged:(NXEvent *)theEvent
{
NXRect iconRect;
int ok;
char *fileName, *nxt;
id success, handler;
// Check if valid drag.
if(!(isDragging && isDraggable && [cell icon] && [iconPath count]>0))
{ isDragging = NO; return self; }
// Prep file(s) for drag; delegate this task if possible.
if([delegate respondsTo:@selector(prepFile:ok:)]) handler = delegate;
else handler = self;
if([iconPath count] <= 0) return nil;
fileName = (char *)[iconPath elementAt:0];
nxt = strchr(fileName, '\t');
success = self;
while(fileName)
{
if(nxt) *nxt = 0;
[handler prepFile:fileName ok:&ok];
if(!ok) success = nil;
if(nxt)
{
*nxt = '\t';
fileName = nxt+1;
nxt = strchr(fileName, '\t');
}
else fileName = nxt = 0;
}
if(!success) { isDragging = NO; return success; }
// Try to drag icon out.
[self getBounds:&iconRect];
[cell getIconRect:&iconRect];
NX_WIDTH(&iconRect) = NX_HEIGHT(&iconRect) = 48.0;
success = [super dragFile:(const char *)[iconPath elementAt:0]
fromRect:&iconRect slideBack:YES event:&dragFromEvent];
isDragging = NO;
// Take care of no-hold-on-drag.
if(!isHoldOnDrag)
{
// Successful drag: clear path and icon to initial state.
if(success) [self clear:self];
// Unsuccessful drag: redisplay old icon in case it left.
else [cell setIcon:(const char *)iconName];
}
return success;
}
- mouseUp:(NXEvent *)theEvent
{
isDragging = NO;
return self;
}
- (BOOL)isScreenPointInView:(double)x :(double)y
{
NXPoint point;
NXRect rect;
// If (x,y) is not in this IconWell, try next one in chain.
point.x = x; point.y = y;
[window convertScreenToBase:&point];
[self convertPoint:&point fromView:nil];
[self getBounds:&rect];
if(NXMouseInRect(&point, &rect, [self isFlipped])) return YES;
else return NO;
}
- (int)iconEntered:(int)windowNum at:(double)x :(double)y
iconWindow:(int)iconWindowNum iconX:(double)iconX iconY:(double)iconY
iconWidth:(double)iconWidth iconHeight:(double)iconHeight
pathList:(char *)pathList
{
// Temporarily display new image if (x,y) is in this IconWell.
if(!isDroppable || ![self isScreenPointInView:x :y])
{ isDropping = NO; return 0; }
[cell setIcon:blankIconName];
[cell setIcon:newIconName];
isDropping = YES; return 0;
}
- (int)iconMovedTo:(double)x :(double)y
{
// Check if (x,y) moved in or out of this IconWell.
if(!isDroppable) return 0;
else if(![self isScreenPointInView:x :y])
{
if(!isDropping) return 0;
// Mouse left; restore old image (unless no-hold-on-drag thing).
[cell setIcon:blankIconName];
if(!isDragging || isHoldOnDrag) [cell setIcon:(const char *)iconName];
isDropping = NO; return 0;
}
else if(!isDropping)
{
// Mouse entered; display new image.
[cell setIcon:blankIconName];
[cell setIcon:newIconName];
isDropping = YES; return 0;
}
return 0;
}
- (int)iconExitedAt:(double)x :(double)y
{
if(!isDroppable || !isDropping) return 0;
// Mouse left; restore old image (unless no-hold-on-drag thing).
[cell setIcon:blankIconName];
if(!isDragging || isHoldOnDrag) [cell setIcon:(const char *)iconName];
isDropping = NO; return 0;
}
- (int)iconReleasedAt:(double)x :(double)y ok:(int *)flag
{
const char *path;
// Check if (x,y) moved in or out of this IconWell.
*flag = 0;
if(!isDroppable) return 0;
else if(![self isScreenPointInView:x :y]
|| !(path = [[IconWellControl controlFor:window] newIconPath]))
{
if(!isDropping) return 0;
// Mouse left; restore old image (unless no-hold-on-drag thing).
[cell setIcon:blankIconName];
if(!isDragging || isHoldOnDrag) [cell setIcon:(const char *)iconName];
isDropping = NO; return 0;
}
else if(!isDropping)
{
// Mouse entered; display new image.
[cell setIcon:blankIconName];
[cell setIcon:newIconName];
}
// Replace path and icon with new ones; notify target.
[iconPath setNumSlots:(strlen(path)+1)];
strcpy((char *)[iconPath elementAt:0], path);
[cell setIcon:blankIconName];
[self update]; //!!! make sure image really replaced before freeing
[[NXImage findImageNamed:(const char *)iconName] free];
[[NXImage findImageNamed:newIconName] setName:(const char *)iconName];
[cell setIcon:(const char *)iconName];
[self sendAction:[cell action] to:[cell target]];
// Accept icon and end drop.
isDropping = NO;
*flag = 1;
return 0;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.