This is ToolLoader.m in view mode; [Download] [Up]
// ToolLoader.m
// By Charles G. Fleming, Educational Computing Services, Allegheny College.
// Copyright 1993, Allegheny College.
// You may freely copy, distribute and reuse this code.
// Allegheny College and the author disclaim any warranty of any kind,
// expressed or implied, as to its fitness for any particular use.
#import "ToolLoader.h"
#import "InspectorLoader.h"
#import "WBProtocols.h"
#import "AcceptDragScrollView.h"
#import "AcceptDragText.h"
#import "Output.h"
#import "Input.h"
#import <objc/NXBundle.h>
#import <objc/objc-runtime.h>
#include <sys/types.h>
#include <sys/dir.h>
@implementation ToolLoader
- appDidInit:sender
{
NXBundle *mainBundle, *loadableBundle;
char resourceFullPath[MAXPATHLEN+1];
id ClassObject, classObject;
const char *appDirectory;
DIR *directoryStream;
struct direct *directoryEntry;
char *lastPeriod, *className, *bundleDirectory, *errorMessage;
// Load any connectors that are in the app directory. Store them in a
// list.
[patiencePanel makeKeyAndOrderFront:self];
NXPing();
mainBundle = [NXBundle mainBundle];
appDirectory = [mainBundle directory];
directoryStream = opendir(appDirectory);
while(directoryEntry = readdir(directoryStream))
{
className = (char *)malloc(directoryEntry->d_namlen + 1);
strcpy(className, directoryEntry->d_name);
lastPeriod = rindex(className, '.');
if(lastPeriod)
if(strcmp(lastPeriod+1, "wbResource") == 0)
{
bundleDirectory = (char *)malloc(directoryEntry->d_namlen + 1);
strcpy(bundleDirectory, className);
*lastPeriod = '\0';
if(objc_lookUpClass(className) == nil)
{
sprintf(resourceFullPath, "%s/%s", appDirectory,
bundleDirectory);
loadableBundle = [[NXBundle allocFromZone:[self zone]]
initForDirectory:resourceFullPath];
// Get the class object from the bundle.
if(ClassObject = [loadableBundle principalClass])
{
// Allocate and initialize an object.
classObject = [[ClassObject allocFromZone:[self zone]]
init];
// Add the object to the list of connectors.
if(classObject)
[connectorList addObject:classObject];
else
{
errorMessage = (char *)malloc(strlen(className)+
21);
sprintf(errorMessage, "Unable to load %s.",
className);
NXRunAlertPanel("Error", errorMessage, NULL,
NULL, NULL);
free(errorMessage);
}
}
else
{
errorMessage = (char *)malloc(strlen(className) + 21);
sprintf(errorMessage, "Unable to load %s.", className);
NXRunAlertPanel("Error", errorMessage, NULL,
NULL, NULL);
free(errorMessage);
}
}
free(bundleDirectory);
}
free(className);
}
closedir(directoryStream);
// The input text should accept dragged files.
[inputText free];
inputText = acceptDragText;
// Set the input text's outputWindow and currentInspectorWindow variables.
[acceptDragText setOutputWindow:[outputText window]];
[acceptDragText setInspectorPanel:inspectorPanel];
// The input and output windows need to have their inputText variables set.
[outputWindow setInputText:acceptDragText];
[inputWindow setInputText:acceptDragText];
// Set up the menu so that we can print the input text.
[printInputMenuCell setTarget:acceptDragText];
[printInputMenuCell setAction:@selector(printPSCode:)];
[patiencePanel orderOut:self];
return self;
}
// Miscellaneous initializations - create a list to hold the tools,
// create a default icon for tools that don't provide their own, set up a
// prototype cell for the tool matrix, and set up the matrix to hold the button
// cells that will represent loaded tools.
- init
{
NXRect matrixRect = {0, 0, 636, 36};
const NXSize cellSize = {36, 36};
[super init];
openPanel = [OpenPanel new];
[openPanel allowMultipleFiles:YES];
//make self the delegate of the help panel
[[NXHelpPanel new] setDelegate:self];
// Create a list to hold the loaded connectors.
connectorList = [[List allocFromZone:[self zone]] initCount:6];
// Create a list to hold the loaded tools.
toolList = [[List allocFromZone:[self zone]] initCount:6];
// Create a storage object to hold the paths of the loaded tools.
toolPathsStorage = [[Storage allocFromZone:[self zone]] initCount:0
elementSize:sizeof(char *) description:"*"];
// This icon will be used if a tool to be loaded doesn't provide one.
defaultToolIcon = [[NXImage allocFromZone:[self zone]]
initFromSection:"defaultToolIcon.tiff"];
// Set up a button cell that will be used to represent a loaded tool.
prototypeCell = [[ButtonCell allocFromZone:[self zone]]
initIconCell:"defaultToolIcon.tiff"];
[prototypeCell setType:NX_ONOFF];
[prototypeCell setTarget:self];
[prototypeCell setAction:@selector(selectTool:)];
// Set up the matrix which will hold button cells that represent the
// loaded tools.
toolMatrix = [[Matrix allocFromZone:[self zone]] initFrame:&matrixRect
mode:NX_RADIOMODE prototype:prototypeCell numRows:1
numCols:0];
[toolMatrix setCellSize:&cellSize];
return self;
}
// Insert into the input scroll view a text object that can accept dragged
// files.
- setInputScrollView:anObject
{
NXRect contentRect = {0, 0, 0, 0};
NXSize textSize;
inputScrollView = anObject;
[inputScrollView setHorizScrollerRequired:YES];
[inputScrollView getContentSize:&contentRect.size];
acceptDragText = [[AcceptDragText allocFromZone:[self zone]]
initFrame:&contentRect text:NULL alignment:NX_LEFTALIGNED];
[acceptDragText notifyAncestorWhenFrameChanged:YES];
[acceptDragText setVertResizable:YES];
[acceptDragText setHorizResizable:YES];
[acceptDragText setOpaque:YES];
textSize.width = contentRect.size.width;
textSize.height = contentRect.size.height;
[acceptDragText setMinSize:&textSize];
textSize.width = 1E38;
textSize.height = 1E38;
[acceptDragText setMaxSize:&textSize];
[acceptDragText setFont:[Font newFont:"Courier" size:14]];
[inputScrollView setDocView:acceptDragText];
[[inputScrollView superview] setAutoresizeSubviews:YES];
[[inputScrollView superview] setAutosizing:NX_HEIGHTSIZABLE |
NX_WIDTHSIZABLE];
return self;
}
// Add the tool matrix to the tool box.
- setToolBox:anObject
{
NXRect scrollViewRect;
toolBox = anObject;
// Set up the scrollView that will hold the tools.
[[toolBox contentView] getBounds:&scrollViewRect];
toolScrollView = [[AcceptDragScrollView allocFromZone:[self zone]]
initFrame:&scrollViewRect];
[toolScrollView setToolLoader:self];
[toolScrollView setHorizScrollerRequired:YES];
[toolScrollView setDocView:toolMatrix];
[toolScrollView setAutosizing:NX_WIDTHSIZABLE];
[toolBox addSubview:toolScrollView];
[[toolBox contentView] setAutoresizeSubviews:YES];
return self;
}
// Load a tool if it hasn't already been loaded. If we do load a tool,
// add a new button cell to the tool matrix to represent the new tool.
- loadTool
{
id ClassObject, classObject;
NXBundle *loadableBundle;
const char *fileTypes[2] = {"wbTool", NULL};
char className[MAXPATHLEN+1], tiffFilePath[MAXPATHLEN+1];
char *period, bundleDirectory[MAXPATHLEN+1], *savePath;
int rows, cols, toolCount;
BOOL tiffFound;
NXImage *toolIcon;
for(; *filenames; filenames++)
{
sprintf(bundleDirectory, "%s/%s", directory, *filenames);
strcpy(className, *filenames);
period = rindex(className, '.');
*period = '\0';
if(![self toolLoaded:className])
{
loadableBundle = [[NXBundle allocFromZone:[self zone]]
initForDirectory:bundleDirectory];
// Get the class object from the bundle.
if(ClassObject = [loadableBundle principalClass])
{
// Allocate and initialize an object.
classObject = [[ClassObject allocFromZone:[self zone]] init];
// Set the object's connectors.
if(![self setToolConnectors:classObject])
[classObject free];
else
{
// Set the object's instance variables.
if([classObject respondsTo:@selector(setInputText:)])
[classObject setInputText:inputText];
if([classObject respondsTo:@selector(setOutputText:)])
[classObject setOutputText:outputText];
if([classObject
respondsTo:@selector(setOutputViewBox:)])
[classObject setOutputViewBox:outputViewBox];
// Add the object to the list of tools and save
// its path.
[toolList addObject:classObject];
savePath = (char *)malloc(strlen(bundleDirectory) + 1);
strcpy(savePath, bundleDirectory);
[toolPathsStorage addElement:&savePath];
// Add a new button cell to the tool matrix.
tiffFound = [loadableBundle getPath:tiffFilePath
forResource:className ofType:"tiff"];
if(tiffFound)
{
toolIcon = [[NXImage allocFromZone:[self zone]]
initFromFile:tiffFilePath];
[prototypeCell setImage:toolIcon];
}
else
[prototypeCell setImage:defaultToolIcon];
[toolMatrix addCol];
[toolMatrix sizeToCells];
[toolMatrix display];
[toolMatrix getNumRows:&rows numCols:&cols];
[toolMatrix selectCellAt:0 :cols-1];
[toolMatrix scrollCellToVisible:0 :cols-1];
// Load the tool's inspector, if it has one.
currentInspector = [classObject inspector];
if(currentInspector)
{
[self setTextFieldsTextDelegates:
[[currentInspector window] contentView]];
[currentInspector revert:self];
[inspectorLoader setCurrentInspector:
[currentInspector window]];
[inspectorPanel setTitle:
[currentInspector inspectorTitle]];
}
// Load the tool's help TOC.
[[NXHelpPanel new] addSupplement:"Help"
inPath:bundleDirectory];
}
}
}
else
NXRunAlertPanel("Error", "The tool selected has already "
"been loaded.", NULL, NULL, NULL);
}
// If any tools were successfully loaded, set up the Apply Tool
// button's target to be the last object loaded. Show the last
// tool's inspector if it has one.
toolCount = [toolList count];
if(toolCount)
{
// Show the inspector of the last tool loaded.
if(currentInspector)
[inspectorPanel makeKeyAndOrderFront:self];
else
[inspectorLoader setCurrentInspector:nil];
// The target of the Apply Tool button will be the last
// object loaded. The action will be "evaluate:".
[evaluateButton setTarget:[toolList objectAt:toolCount-1]];
[evaluateButton setAction:@selector(evaluate:)];
}
// The application will not be active if tools were dragged into the
// tool box from the Workspace.
if(![NXApp isActive])
{
[NXApp activateSelf:YES];
[[outputText window] orderFront:self];
[[inputText window] orderFront:self];
}
return self;
}
- loadTool:sender
{
const char *fileTypes[2] = {"wbTool", NULL};
// Get the paths to the bundles selected and load the tools.
if([openPanel runModalForTypes:fileTypes])
{
filenames = [openPanel filenames];
directory = [openPanel directory];
[self loadTool];
}
return self;
}
- setToolDirectory:(const char *)aDirectory
{
directory = aDirectory;
return self;
}
- setToolFiles:(const char *const *)fileList
{
filenames = fileList;
return self;
}
// This is sent when one of the button cells (representing tools) is clicked.
// Make sure the correct inspector is displayed and set the target of the
// evaluate button to be the correct tool.
- selectTool:sender
{
int column;
id tool;
column = [sender selectedCol];
tool = [toolList objectAt:column];
[evaluateButton setTarget:tool];
currentInspector = [tool inspector];
if(currentInspector)
{
[inspectorPanel setTitle:[currentInspector inspectorTitle]];
[currentInspector revert:self];
[inspectorLoader setCurrentInspector:[currentInspector window]];
[inspectorPanel setDocEdited:NO];
[inspectorPanel makeKeyAndOrderFront:self];
}
else
{
currentInspector = nil;
[inspectorLoader setCurrentInspector:nil];
}
return self;
}
// Make sure the inspector is marked dirty if some editing has been done
// in one of the inspector's text fields.
- textDidChange:sender
{
[inspectorPanel setDocEdited:YES];
return self;
}
// Make sure the inspector is marked clean after editing is completed.
- textDidEnd:textObject endChar:(unsigned short)whyEnd
{
if(whyEnd == NX_RETURN)
[inspectorPanel setDocEdited:NO];
return self;
}
// Find all text fields that are subviews of aView and assign ourself
// as their textDelegate.
- setTextFieldsTextDelegates:aView
{
int index, count;
List *subviewList;
id view;
subviewList = [aView subviews];
if(subviewList)
{
count = [subviewList count];
for(index = 0; index < count; index++)
{
view = [subviewList objectAt:index];
if([view respondsTo:@selector(setTextDelegate:)])
[view setTextDelegate:self];
else
[self setTextFieldsTextDelegates:view];
}
}
return self;
}
// For each connector that a tool needs, see if it has been loaded. If it
// hasn't, run an alert panel. If the connectors have been loaded, call
// the tools set methods to set the connectors.
- (BOOL)setToolConnectors:tool
{
id connector;
SEL setMethod;
char *setMethodString, *errorMessage;
char **connectors;
int connectorCount, count, toolNameLength;
BOOL connectorFound = YES, match;
const char *className = NULL, *toolName;
// Ask the tool what connectors it needs.
connectors = [tool requiredConnectors];
toolName = [tool name];
toolNameLength = strlen(toolName)+1;
connectorCount = [connectorList count];
for(; *connectors && connectorFound; connectors++)
{
match = NO;
for(count = 0; count < connectorCount && !match; count++)
{
connector = [connectorList objectAt:count];
className = [connector name];
if(strcmp(*connectors, className) == 0)
{
setMethodString = (char *)malloc(5 + strlen(className));
sprintf(setMethodString, "set%s:", className);
setMethod = sel_getUid(setMethodString);
[tool perform:setMethod with:connector];
match = YES;
free(setMethodString);
}
}
if(!match && connectorCount)
{
connectorFound = NO;
errorMessage = (char *)malloc(strlen(className) + toolNameLength +
60);
sprintf(errorMessage, "%s cannot be loaded because the "
"resource %s is not loaded.", toolName, *connectors);
NXRunAlertPanel("Error", errorMessage, NULL, NULL, NULL);
free(errorMessage);
}
}
return connectorFound;
}
// See if tool has already been loaded.
- (BOOL)toolLoaded:(char *)tool
{
int count, entry;
BOOL found = NO;
id loadedTool;
count = [toolList count];
for(entry=0; entry < count && !found; entry++)
{
loadedTool = [toolList objectAt:entry];
if(strcmp([loadedTool name], tool) == 0)
found = YES;
}
return found;
}
// These delegate messages may be sent from the help panel, the input window,
// or the output window.
- windowWillClose:sender
{
char helpFilePath[MAXPATHLEN+1];
if ((sender != inputWindow) && (sender != outputWindow))
{
sprintf(helpFilePath, "%s/English.lproj/Help/LoadingTools.rtf",
[[NXBundle mainBundle] directory]);
[sender showFile:helpFilePath atMarker:NULL];
}
return self;
}
- windowDidMove:sender
{
id reply;
if ((sender != inputWindow) && (sender != outputWindow))
reply = [[NXHelpPanel new] windowDidMove:sender];
else
reply = self;
return reply;
}
- windowWillResize:sender toSize:(NXSize *)frameSize;
{
id reply;
int numberOfTools;
if ((sender != inputWindow) && (sender != outputWindow))
reply = [[NXHelpPanel new] windowWillResize:sender toSize:frameSize];
else
{
reply = self;
if (sender == inputWindow)
{
// do the usual size restraints so it can't go too small
if (frameSize -> width < 600)
frameSize -> width = 600;
if (frameSize -> height < 231)
frameSize -> height = 231;
// make sure the number of tools visible is always a whole number
numberOfTools = (int)(frameSize->width - 21) / 37;
frameSize -> width = numberOfTools * 37 + 21;
}
else // sender is outputWindow
{
if (frameSize -> width < 687)
frameSize -> width = 687;
if (frameSize -> height < 141)
frameSize -> height = 141;
}
}
return reply;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.