This is eTImageComponent.m in view mode; [Download] [Up]
///////////////////////////////////////////////////////////////////////////////
// FILENAME: eTImageComponent.m
// SUMMARY: Implementation for a container of imageable data.
// SUPERCLASS: Object
// INTERFACE: None, but it "exports" eTImageComponentIcon.
// PROTOCOLS: <ComponentData,
// ETFDSupport,HTMDSupport,LaTeXSupport>
// AUTHOR: Rohit Khare
// COPYRIGHT: (c) 1994 California Institure of Technology, eText Project
///////////////////////////////////////////////////////////////////////////////
// IMPLEMENTATION COMMENTS
// The concept is that eText systems only access NXImages through this
// component, which offers default services for image coercion and encoding.
//
// The UI has the responsibility for passing in the desired filespec.
// Thus to keep a .pcx file as .pcx, pass in a complete path to the .pcx
// on disk and eText will either reference it or copy it as-is into the
// future (without filter-services, you will get eTImageComponentIcon.tiff)
// If the file cannot be found (or isDirty), eText will generate a .tiff
// and reset the names correctly.
//
// Since this component maps onto an IMG tag, the only format with
// wide acceptance for IMG is GIF; so any non-gif file will be coerced.
// Similarly, LaTeX only has provisions for eps, so it will, too.
//
// LaTeX and HTML representations will force the creation of local copies of
// images (this can/will change in the future, cf HTML OODB ideas)
//
// shared images are currently envisioned for sysBitmaps like NXLinkButton
//
// Note on HTML calling convention: this object maps exclusively onto the
// IMG tag, which can occur in the body of another anchor-like element.
///////////////////////////////////////////////////////////////////////////////
// HISTORY
// 01/22/95: Added call to handle caption strings.
// 12/23/94: Patched to always reset etDoc on read/write
// 07/19/94: Rearchitected
// 07/14/94: Created. Aliases the NeXTSTEP NXImage API.
///////////////////////////////////////////////////////////////////////////////
#import "eTImageComponent.h"
#import "Kludges.subproj/EPSWriting.h"
#import "Kludges.subproj/GIFWriting.h"
#define _eTImageComponentVERSION 10
@implementation eTImageComponent
// NXImage *theImage;
// BOOL isShared;
+ newImageNamed:(const char *)newName
{
static id _imageComponentTable=nil;
id newObj;
id newImg;
NXAtom theName;
theName = NXUniqueString(newName);
if (!_imageComponentTable) {
_imageComponentTable = [[HashTable alloc] initKeyDesc:"%"];
}
newObj = [_imageComponentTable valueForKey:theName];
if (!newObj) {
if (newImg = [NXImage findImageNamed:theName]){ // system bitmap?
[newImg setScalable:NO]; // this is a shared image!
[newImg setDataRetained:YES]; // write TIFFs of sysBitmaps
[newImg setEPSUsedOnResolutionMismatch:YES];
// Here's a hack for dealing with recalcitrant alpha channels:
[newImg setBackgroundColor:NX_COLORWHITE];
} else {
newImg = [[NXImage alloc] init];
if (![newImg useFromFile:theName]){
[newImg free];
newImg = [NXImage findImageNamed:"eTImageComponentIcon"];
} else {
// WHAT Happens to naming if theName is an absolute path?
[newImg setName:theName];
}
}
newObj = [[[eTImageComponent alloc] initInDoc:nil linked:NO]
useImage:newImg name:theName path:"" shared:YES];
[_imageComponentTable insertKey:theName value:newObj];
}
return newObj;
}
- theImage {return theImage;}
- setComponentName:(const char*)newComponentName
{return (isShared ? nil : [super setComponentName:newComponentName]);}
- (BOOL)isShared {return isShared;}
- (BOOL)isMutable {return (!isShared && [super isMutable]);}
//////// begin semi-private API section ///////
- initInDoc:newDoc linked: (BOOL) linked
{
[super initInDoc:newDoc linked:linked];
isShared = NO; shouldEdit = YES; theImage = nil;
return self;
}
- free
{
if (isShared || [theImage name]) return self; // stop the free chain
theImage = [theImage free];
return self = [super free];
}
- useImage:(NXImage *)newImage
name:(const char *) newComponentName
path:(const char *) newCurrentPath
shared:(BOOL) newShared;
{
theImage = newImage;
componentName = NXUniqueString(newComponentName);
currentPath = NXUniqueString(newCurrentPath);
isShared = newShared;
return self;
}
- readComponentFromPath:(const char *)newPath
{
if (newPath != currentPath)
[super readComponentFromPath:newPath];
// now we have filled in componentName/currentPath
// go out to disk and find the image
theImage = [[NXImage alloc] init];
[theImage setScalable:YES];
[theImage setDataRetained:YES];
[theImage setEPSUsedOnResolutionMismatch:YES];
// Here's a hack for dealing with recalcitrant alpha channels:
[theImage setBackgroundColor:NX_COLORWHITE];
if (![theImage loadFromFile:currentPath]){
char temp[MAXPATHLEN];
strcpy(temp, currentPath);
strcat(temp, ".eps");
if (![theImage loadFromFile:temp]){
strcpy(temp, currentPath);
strcat(temp, ".tiff");
if (![theImage loadFromFile:temp]){
strcpy(temp, currentPath);
[theImage free];
// use eTImageComponentIcon.tiff to indicate an error
theImage = [NXImage findImageNamed:"eTImageComponentIcon"];
isShared = YES;
}
}
currentPath = NXUniqueString(temp);
}
return self;
}
- writeComponentToPath:(NXAtom)path inFormat:(int) theFormat
{
const char *tmp;
char target[MAXPATHLEN];
if ((theFormat == ETFD_FMT) && (isShared || [theImage name]))
return self;
switch(theFormat) {
case ETFD_FMT:
// if it's not (a link) or (clean and the target is found),
// if the (source is found and it's clean), copy
// else write.
if (isShared) break; // special case for etfds.
sprintf(target,"%s/%s",path,componentName);
if (!((isLinked) || ((!isDirty) && (!access(target,F_OK|R_OK))))){
if ((!isDirty) && *currentPath &&
(!access(currentPath, F_OK|R_OK))) {
// copy
char cmd[2*MAXPATHLEN];
sprintf(cmd,"cp -rp \"%s\" \"%s\"", currentPath, target);
system(cmd);
currentPath = NXUniqueString(target);
} else {
id repList;
int i,j;
BOOL foundEPS=NO;
NXStream *stream;
// choose format
repList = [theImage representationList];
i = [repList count];
for(j=0; ((j<i) && !foundEPS); j++){
if ([[repList objectAt:j] isKindOf:[NXEPSImageRep class]])
foundEPS = YES;
}
stream = NXOpenMemory(NULL, 0, NX_READWRITE);
if (foundEPS) {
[theImage writeEPS:stream];
if (!rindex(target, '.') ||
strcasecmp(".eps", rindex(target, '.')))
strcat(target, ".eps");
} else {
[theImage writeTIFF:stream allRepresentations:YES];
if (!rindex(target, '.') ||
strncasecmp(".tif", rindex(target, '.'),3))
strcat(target, ".tiff");
}
NXSaveToFile(stream, target);
NXCloseMemory(stream, NX_FREEBUFFER);
currentPath = NXUniqueString(target);
componentName = NXUniqueString(rindex(target,'/')+1);
}
isDirty=NO;
}
[etDoc registerComponent:componentName];
break;
case HTMD_FMT:
// Always is a gif. If it doesn't exist, copy a .gif or create one.
if (componentName) tmp = componentName;
else if ([theImage name]) tmp = [theImage name];
else tmp = [[self class] name];
sprintf(target,"%s/%s",path,tmp);
if(!rindex(target, '.') || strcasecmp(".gif", rindex(target, '.')))
strcat(target, ".gif");
if (access(target,F_OK|R_OK) || isDirty) {
// if currentPath is a gif, copy.
if (rindex(currentPath, '.') &&
(!strcasecmp(".gif", rindex(currentPath, '.'))) &&
(!access(currentPath,F_OK|R_OK))) {
char cmd[2*MAXPATHLEN];
sprintf(cmd,"cp -rp \"%s\" \"%s\"", currentPath, target);
system(cmd);
} else {
[theImage writeGIFtoPath:target];
}
}
[etDoc registerComponent:(rindex(target,'/')+1)];
break;
case TeXD_FMT:
// Always is an eps. If it doesn't exist, copy a .eps or create one.
if (componentName) tmp = componentName;
else if ([theImage name]) tmp = [theImage name];
else tmp = [[self class] name];
sprintf(target,"%s/%s",path,tmp);
if (!rindex(target, '.') || strcasecmp(".eps", rindex(target, '.')))
strcat(target, ".eps");
if (access(target,F_OK|R_OK) || isDirty) {
// if currentPath is an eps, copy.
if ((rindex(currentPath, '.')) &&
(!strcasecmp(".eps", rindex(currentPath, '.'))) &&
(!access(currentPath,F_OK|R_OK))) {
char cmd[2*MAXPATHLEN];
sprintf(cmd,"cp -rp \"%s\" \"%s\"", currentPath, target);
system(cmd);
} else {
NXStream *stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
[theImage writeEPS:stream];
NXSaveToFile(stream, target);
NXCloseMemory(stream, NX_FREEBUFFER);
}
}
[etDoc registerComponent:(rindex(target,'/')+1)];
break;
}
return self;
}
- writeComponentToPboard:thePboard
{
// I don't know why yet, but I want to defeat dragging of shared images
return (isShared ? nil : [super writeComponentToPboard:thePboard]);
}
- addToPboard:pboard
{
id repList;
int i,j;
BOOL foundEPS=NO;
NXStream *stream;
// we can let people copy them, though.???
if (!theImage) return nil;
// choose format
repList = [theImage representationList];
i = [repList count];
for(j=0; ((j<i) && !foundEPS); j++){
if ([[repList objectAt:j] isKindOf:[NXEPSImageRep class]])
foundEPS = YES;
}
stream = NXOpenMemory(NULL, 0, NX_READWRITE);
if (foundEPS) {
[theImage writeEPS:stream];
[pboard addTypes:&NXPostScriptPboardType num:1 owner:nil];
[pboard writeType:NXPostScriptPboardType fromStream:stream];
} else {
[theImage writeTIFF:stream allRepresentations:YES];
[pboard addTypes:&NXTIFFPboardType num:1 owner:nil];
[pboard writeType:NXTIFFPboardType fromStream:stream];
}
NXCloseMemory(stream, NX_FREEBUFFER);
return self;
}
- readComponentFromPboard:thePboard
{
[super readComponentFromPboard:thePboard];
// ok, so now we have a quasi-valid entry in currentPath
// If the image can init from thePboard, go right ahead.
theImage = [[NXImage alloc] init];
[theImage setScalable:YES];
[theImage setDataRetained:YES];
[theImage setEPSUsedOnResolutionMismatch:YES];
// Here's a hack for dealing with recalcitrant alpha channels:
[theImage setBackgroundColor:NX_COLORWHITE];
if ([NXImage canInitFromPasteboard:thePboard]) {
// Patch for recalcitrant f*king appkit
if([NXEPSImageRep canInitFromPasteboard:thePboard]) {
id epsRep = [[NXEPSImageRep alloc] initFromPasteboard:thePboard];
[theImage useRepresentation:epsRep];
} else
theImage = [theImage initFromPasteboard:thePboard];
if (!theImage){
theImage = [NXImage findImageNamed:"eTImageComponentIcon"];
isShared = YES;
}
// special case: If we paste raw image data and there's no path,
// componentName is currently "Untitled-%d"; we need to cat an ext.
// we should look at how this whole schmeer works/fails with RIBs.
if (!(currentPath && *currentPath)) {
char tmp[MAXPATHLEN];
strcpy(tmp, componentName);
if([thePboard findAvailableTypeFrom:&NXPostScriptPboardType num:1])
strcat(tmp, ".eps");
else if([thePboard findAvailableTypeFrom:&N3DRIBPboardType num:1])
strcat(tmp, ".rib");
else strcat(tmp, ".tiff");
componentName = NXUniqueString(tmp);
}
} else if (currentPath && *currentPath) {
if (![theImage loadFromFile:currentPath]){
[theImage free];
theImage = [NXImage findImageNamed:"eTImageComponentIcon"];
isShared = YES;
}
} else {
theImage = [NXImage findImageNamed:"eTImageComponentIcon"];
isShared = YES;
}
return self;
}
- readRichText:(NXStream *)stream forView:view
{
int i;
id temp;
temp = [view etDoc];
if (temp && [temp respondsTo:@selector(registerComponent:)])
etDoc = temp; // Always reset if possible; bugfix for shared images, 12/23
NXScanf(stream, "%d ", &i);
if (i != _eTImageComponentVERSION) {
// bad version block.
NXLogError("eTImageComponent found unparseable version %d at position %d",
i, NXTell(stream));
return nil;
}
NXScanf(stream, "%c ", &isShared); isShared -= 'A';
[super readRichText:stream forView:view];
if (isShared){
// we have a real problem here: we need to return the "uniq"d instance!
id retval = [eTImageComponent newImageNamed:componentName];
[self free];
return retval;
}
if (!isLinked) {
// derive a fresh currentPath. try to load.
char buf[MAXPATHLEN];
sprintf(buf, "%s/%s",[[etDoc docInfo] docPath], componentName);
currentPath = NXUniqueString(buf);
}
[self readComponentFromPath:currentPath];
return self;
}
- writeRichText:(NXStream *)stream forView:view
{
BOOL fakeShared;
id temp;
temp = [view etDoc];
if (temp && [temp respondsTo:@selector(registerComponent:)])
etDoc = temp; // Always reset if possible; bugfix for shared images, 12/23
// this fixes a subtle bug related to images we can't locate at
// read-time and are assigned the eTImageComponentIcon by default
// (and thus set isShared) -- on reopen, it calls +newImageNamed with
// the old componentName
fakeShared = isShared;
if (([theImage name] == NXUniqueString("eTImageComponentIcon")) &&
strcmp([theImage name], componentName))
fakeShared = NO;
NXPrintf(stream, "%d %c ", _eTImageComponentVERSION, fakeShared+'A');
[super writeRichText:stream forView:view];
return self;
}
- writeHTML:(NXStream *)stream forView:view {
return [self writeHTML:(NXStream *)stream forView:view withCaption:NULL];}
- writeHTML:(NXStream *)stream forView:view withCaption:(NXAtom)caption
{
const char *tmp;
id temp;
temp = [view etDoc];
if (temp && [temp respondsTo:@selector(registerComponent:)])
etDoc = temp; // Always reset if possible; bugfix for shared images, 12/23
if (componentName) tmp = componentName;
else if ([theImage name]) tmp = [theImage name];
else tmp = [[self class] name];
NXPrintf(stream, "<IMG SRC=\"%s%s\" ALT=\"%v\">", tmp,
((!rindex(tmp, '.') || strcasecmp(".gif", rindex(tmp, '.')))?".gif":""),
(caption && *caption) ? caption : tmp);
return self;
}
- writeLaTeX:(NXStream *)stream forView:view
{
const char *tmp;
id temp;
temp = [view etDoc];
if (temp && [temp respondsTo:@selector(registerComponent:)])
etDoc = temp; // Always reset if possible; bugfix for shared images, 12/23
if (componentName) tmp = componentName;
else if ([theImage name]) tmp = [theImage name];
else tmp = [[self class] name];
NXPrintf(stream, "\n\\centerline{\\epsffile{%s%s}}\n",tmp,
((!rindex(tmp, '.') || strcasecmp(".eps", rindex(tmp, '.')))?".eps":""));
return self;
}
// VERY PRIVATE API
- setImage:newImg {theImage = newImg; return self;}
- setShared:(BOOL)newState {isShared = newState; return self;}
- registerError
{
theImage = [NXImage findImageNamed:"eTImageComponentIcon"];
isShared = YES;
return self;
}
@endThese are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.