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.