This is AppWithDoc.m in view mode; [Download] [Up]
#import "AppWithDoc.h"
#import "Document.h"
#import <objc/List.h>
#import <appkit/Matrix.h>
#import <appkit/Menu.h>
#import <appkit/MenuCell.h>
#import <appkit/OpenPanel.h>
#import <appkit/defaults.h>
#import <appkit/nextstd.h>
#import <objc/hashtable.h> /* for NXCopyStringBuffer() */
#import <text/text.h>
#define VERSION_STRING "%.2f"
@implementation AppWithDoc:Application
/*
* This class is used primarily to handle the opening of new documents and
* other application-wide activity. It listens for requests from the
* Workspace Manager to open a Document file as well as target/action
* messages from the New and Open... menu items.
*/
/* Private C functions used to implement methods in this class. */
static id initMenu(id menu)
/*
* Returns the activate menu if is found in this menu.
*/
{
const char *s;
static id activateMenu;
id cell, cellTarget;
id matrix = [menu itemList];
int count = [matrix cellCount];
while (count--) {
cell = [matrix cellAt:count :0];
cellTarget = [cell target];
if ([cell hasSubmenu])
initMenu(cellTarget);
s = [cell title];
if (s && !strcmp(s, "Activate"))
activateMenu = cellTarget;
}
return activateMenu;
}
static id documentInWindow(id window)
/*
* Checks to see if the passed window's delegate is a Document. If it is, it
* returns that document, otherwise it returns nil.
*/
{
id document = [window delegate];
return [document isKindOf:[Document class]] ? document : nil;
}
static id findDocument(const char *name)
/*
* Searches the window list looking for a Document with the specified name.
* Returns the window containing the document if found. If name == NULL then
* the first document found is returned.
*/
{
id document, window;
id windowList = [NXApp windowList];
int count = [windowList count];
while (count--) {
window = [windowList objectAt:count];
document = documentInWindow(window);
if (document && !(name && strcmp([document filename], name)))
return window;
}
return nil;
}
static BOOL loadActivateMenu()
/*
* Adds all open documents to the activate menu. Returns whether any were
* actually loaded.
*/
{
BOOL foundOne = NO;
id document, window;
id windowList = [NXApp windowList];
int count = [windowList count];
while (count--) {
window = [windowList objectAt:count];
document = documentInWindow(window);
if (document) {
[NXApp addDocument:[document filename]];
foundOne = YES;
}
}
return foundOne;
}
static id openFile(STR directory, id newClass, char* name, BOOL display)
/*
* Opens a file with the given name in the specified directory. If we already
* have that file open, it is ordered front. Returns the document if
* successful, nil otherwise.
*/
{
id window;
char path[MAXPATHLEN + 1];
if (name && *name) {
if (!directory)
directory = pwd();
else if (*directory != '/')
directory = fullPath(pwd(), directory);
if (isDirectory(directory)) {
strcpy(path, fullPath(directory, name));
window = findDocument(path);
if (window) {
if (display)
[window makeKeyAndOrderFront:window];
return[window delegate];
} else {
return [newClass newFromFile:path];
}
} else {
NXRunAlertPanel("Open", "Invalid path: %s",
"OK", NULL, NULL, directory);
}
}
return nil;
}
/* Public methods */
+ initialize
/*
* Initializes the defaults.
*/
{
const NXDefaultsVector AppDefaults = {
{"WinLocX", "50"}, {"WinLocY", "100"},
{"WinLocDx", "15"}, {"WinLocDy", "15"},
{"Quit", NULL}, {NULL, NULL}};
NXRegisterDefaults(appName, AppDefaults);
return self;
}
+ new
/*
* Defaults to Document; 0, 'document'
*/
{
return[self newClass:[Document class] extension:"document" version:0];
}
+ newClass:newDoc extension:(STR)newExt version:(int)newVersion
{
self = [super new];
docClass = newDoc;
extension = newExt;
version = newVersion / 10.0;
[[self class] setVersion:newVersion];
return self;
}
/* General application status and information querying/modifying methods. */
- readFromFile:(const char *)filename
{
NXTypedStream *tstream;
if (! (tstream=NXOpenTypedStreamForFile(filename, NX_READONLY)) ) {
NotifyArg("Read","Error opening file %s", filename);
return nil;
}
self = NXReadObject(tstream);
NXCloseTypedStream(tstream);
return self;
}
- write:obj toFile:(const char *)filename
{
NXTypedStream *tstream;
if (! (tstream=NXOpenTypedStreamForFile(filename, NX_WRITEONLY)) ) {
NotifyArg("Write", "Error opening file %s", filename);
return nil;
}
NXWriteObject(tstream, obj);
NXCloseTypedStream(tstream);
return obj;
}
- locateWindow:window
{
static NXCoord x, dx, y, dy;
if (!x) {
x = atof(NXGetDefaultValue(appName, "WinLocX"));
dx = atof(NXGetDefaultValue(appName, "WinLocDx"));
y = atof(NXGetDefaultValue(appName, "WinLocY"));
dy = atof(NXGetDefaultValue(appName, "WinLocDy"));
}
[window moveTo:x :y];
x += dx;
y += dy;
return window;
}
/* Application-wide shared info */
- currentDocument
{
return documentInWindow(mainWindow);
}
- (STR)extension
{
return extension;
}
- saveAsPanel
/*
* Returns a regular SavePanel with extension of the required file type.
*/
{
id savepanel = [SavePanel new];
[savepanel setAccessoryView:nil];
[savepanel setRequiredFileType:extension];
return savepanel;
}
/* Target/Action methods */
- info:sender
/*
* Brings up the information panel.
*/
{
char buf[6];
if (!infoPanel) {
infoPanel =
[self loadNibSection:"InfoPanel.nib" owner:self withNames:YES];
sprintf(buf, VERSION_STRING, version);
[versionField setStringValue:buf];
}
[infoPanel orderFront:self];
return self;
}
- new:sender
/*
* Called by pressing New in the Window menu.
*/
{
[docClass new];
return self;
}
- open:sender
/*
* Called by pressing Open... in the Window menu.
*/
{
const char *directory;
const char* const* files;
const char *const docType[] = {extension, NULL};
id openpanel = [[OpenPanel new] allowMultipleFiles:YES];
directory = [[self currentDocument] directory];
if (directory && (*directory == '/')) {
[openpanel setDirectory:directory];
}
if ([openpanel runModalForTypes:docType]) {
files = [openpanel filenames];
directory = [openpanel directory];
while (files && *files) {
openFile(directory, docClass, *files, YES);
files++;
}
}
return self;
}
- print:sender
/*
* Print the View
*/
{
[[[self currentDocument] view] printPSCode:sender];
}
- terminate:sender
/*
* Overridden to be sure all documents get an opportunity to be saved before
* exiting the program.
*/
{
id window, document;
int count = [windowList count];
while (count--) {
window = [windowList objectAt:count];
document = [window delegate];
if ([document respondsTo:@selector(windowWillClose:)]) {
if ([document windowWillClose:window])
[window close];
else
return self;
}
}
return[super terminate:sender];
}
/*
* Application object delegate methods. Since we don't have an application
* delegate, messages that would normally be sent there are sent to the
* Application object itself instead.
*/
- appDidInit:sender
/*
* Check for files to open specified on the command line. Initialize the
* menus and get a handle on the activate menu. Add all opened documents to
* the activate menu. If there are no open documents, then open a blank one.
*/
{
int i;
char buffer[MAXPATHLEN + 1];
char type[MAXPATHLEN + 1];
char *directory, *name, *ext;
if (NXArgc > 1) {
for (i = 1; i < NXArgc; i++) {
strcpy(buffer, NXArgv[i]);
strcpy(type, ".");
strcat(type, extension);
ext = strrchr(buffer, '.');
if (!ext || strcmp(ext, type))
strcat(buffer, type);
directory = parentname(buffer);
name = basename(buffer);
openFile(directory, docClass, name, YES);
}
}
activateMenu = initMenu([NXApp mainMenu]);
if (!loadActivateMenu())
[self new:self]; /* if none opened, open one */
if (NXGetDefaultValue(appName, "Quit")) {
[self activateSelf:YES];
NXPing();
[self terminate:self];
}
return self;
}
- (int)appOpenFile:(char* )path type:(char* )type
/*
* This method is performed whenever a user double-clicks on an icon in the
* Workspace Manager representing the document.
*/
{
char *name;
char *directory;
if (type && !strcmp(type, extension)) {
directory = parentname(path);
name = basename(path);
return (openFile(directory, docClass, name, YES) ? YES : NO);
}
return NO;
}
- (BOOL) appAcceptsAnotherFile:sender
/*
* We accept any number of appOpenFile:type: messages.
*/
{
return YES;
}
/* Listener/Speaker remote methods */
- (int)msgVersion:(char* *)aString ok:(int *)flag
{
char buf[6];
sprintf(buf, VERSION_STRING, version);
*aString = NXCopyStringBuffer(buf);
*flag = YES;
return 0;
}
- (int)msgDirectory:(const char **)totalPath ok:(int *)flag
{
*totalPath = [[self currentDocument] directory];
if (!*totalPath)
*totalPath = [[OpenPanel new] directory];
if (*totalPath) {
*flag = YES;
} else {
*totalPath = "";
*flag = NO;
}
return 0;
}
- (int)msgFile:(const char **)totalPath ok:(int *)flag
{
const char *file;
file = [[self currentDocument] filename];
if (file) {
*totalPath = file;
*flag = YES;
} else {
*totalPath = "";
*flag = NO;
}
return 0;
}
- (int)msgPrint:(char* )totalPath ok:(int *)flag
{
BOOL close;
id document = nil;
char *directory, *name;
char *path;
directory = parentname(totalPath);
name = basename(totalPath);
if (isDirectory(directory)) {
path = NXCopyStringBuffer(fullPath(directory, name));
document = [findDocument(path) delegate];
}
if (document) {
close = NO;
} else {
document = openFile(directory, docClass, name, NO);
close = YES;
}
if (document && [document contents]) {
[document print:self];
if (close)
[[document window] close];
*flag = YES;
} else {
*flag = NO;
}
return 0;
}
/* Activate menu updating */
static int cellcmp(const void *first, const void *second)
{
const char *s1 = [*((id *) first) title];
const char *s2 = [*((id *) second) title];
return (s1 && s2) ? strcmp(s1, s2) : 0;
}
#define CellSortIf(COND) if (COND) {\
qsort(NX_ADDRESS(cells), [cells count], sizeof(id *), cellcmp);\
[activateMenu sizeToFit]; }
- addDocument:(char *)name
/*
* Adds the name passed in to the Activate menu (if it isn't already there).
*/
{
int i;
const char *s;
id matrix = [activateMenu itemList];
id cells = [matrix cellList];
if (!name)
return self;
for (i = [cells count] - 1; i >= 0; i--) {
s = [[cells objectAt:i] title];
if (s && !strcmp(s, name))
return self;
}
[activateMenu addItem:name action:@selector(activateDocument:)
keyEquivalent :0];
CellSortIf(cells);
return self;
}
- removeDocument:(char *)name
/*
* Removes the passed name from the Activate menu.
*/
{
int i;
const char *s;
id matrix = [activateMenu itemList];
id cells = [matrix cellList];
int count = [cells count];
if (!name)
return self;
for (i = 0; i < count; i++) {
s = [[cells objectAt:i] title];
if (s && !strcmp(s, name)) {
[matrix removeRowAt:i andFree:YES];
break;
}
}
CellSortIf(i != count);
return self;
}
- renameDocument:(char *)oldName to:(char *)newName
/*
* Finds the passed oldName in the Activate menu and changes the title of
* that menu entry to newName.
*/
{
int i;
const char *s;
id matrix = [activateMenu itemList];
id cell, cells = [matrix cellList];
int count = [cells count];
if (!oldName)
return self;
for (i = 0; i < count; i++) {
cell = [cells objectAt:i];
s = [cell title];
if (s && !strcmp(s, oldName)) {
[cell setTitle:newName];
break;
}
}
CellSortIf(i != count);
return self;
}
- activateDocument:sender
/*
* Sent by items in the Activate menu. Queries back to the sender to find
* out which document to activate, then activates it.
*/
{
[findDocument([[sender selectedCell] title]) makeKeyAndOrderFront :self];
return self;
}
- setVersionField:anObject
{
versionField = anObject;
return self;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.