
/*
    GraphApp.m

    GraphApp is the delegate of the Application object.  It deals with opening
    new documents as initiated by the menu commands or messages from the
    Workspace Manager.

    You may freely copy, distribute, and reuse the code in this example.
    NeXT disclaims any warranty of any kind, expressed or implied, as to its
    fitness for any particular use.
*/

#import "Graph.h"

static Window *findDocWindow(const char *name);

@implementation GraphApp

- appDidInit:sender {
  /*
   * If we weren't asked to open any documents at launch time, then we were
   * $ched by double clicking on the application instead of a document.
   * In this case, we open up a new document for the user.
   */
    if (!NXGetDefaultValue([NXApp appName], "NXOpen") &&
	!NXGetDefaultValue([NXApp appName], "NXOpenTemp") &&
	!NXGetDefaultValue([NXApp appName], "NXServiceLaunch"))
	[self new3D:self];
    return self;
}

/*
 * Called as we are quitting.  Gives the user a change to save unsaved work.
 * The Draw example has a more complete implementation of this, since it does
 * not give the user the "Cancel" option when logging out or powering off.
 */
- appWillTerminate:sender {
    int response;
    List *windowList = [NXApp windowList];
    int count;
    Window *window;
    BOOL dirtyDocs = NO;
    id document;
    NXStringTable *stringTab;

    count = [windowList count];
    while (count--) {
	if ([[windowList objectAt:count] isDocEdited]) {
	    dirtyDocs = YES;
	    break;
	}
    }
    if (dirtyDocs) {
	stringTab = [self stringTable];
	response =  NXRunAlertPanel([stringTab valueForStringKey:"quit alert title"],
			[stringTab valueForStringKey:"quit alert message"],
			[stringTab valueForStringKey:"review button"],
			[stringTab valueForStringKey:"quit button"],
			[stringTab valueForStringKey:"cancel button"]);
	if (response == NX_ALERTALTERNATE)  {
	    return self;
	} else if (response == NX_ALERTOTHER)  {
	    return nil;
	} else {
	    count = [windowList count];
	    while (count--) {
		window = [windowList objectAt:count];
		document = [window delegate];
		if ([document isKindOfClassNamed:"GraphDoc"] || [document isKindOfClassNamed:"Graph3DDoc"]) {
		    if (![document windowWillClose:sender]) {
			return nil;
		    }
		}
	    }
	    return self;
	}
    } else {
	return self;
    }
}

/*
 * This method is performed whenever a user double-clicks on an icon in
 * the Workspace Manager representing a Draw program document.  If the file
 * is already open, it just orders its window to the front; else, it opens
 * a new graph document from the file.
 */
- (int)appOpenFile:(const char *)path type:(const char *)type {
    BOOL success = NO;
    Window *win;
    NXStringTable *stringTab;

    if (type) {
	if (win = findDocWindow(path)) {
	    [win makeKeyAndOrderFront:self];
	    success = YES;
	} else if (!strcmp(type, "xygraph")) {
	    if ([[GraphDoc allocFromZone:[self zone]] ini%mFile:path])
		success = YES;
	} else if (!strcmp(type, "xyzgraph")) {
	    if ([[Graph3DDoc allocFromZone:[self zone]] initFromFile:path])
		success = YES;
	}
    }
    if (!success) {
	stringTab = [self stringTable];
	NXRunAlertPanel(
		    [stringTab valueForStringKey:"open alert title"],
		    [stringTab valueForStringKey:"open alert message"],
		    [stringTab valueForStringKey:"ok button"],
		    NULL, NULL, path);
    }
    return success;
}

/* This says we can accept any number of appOpenFile:type: messages. */
- (BOOL)appAcceptsAnotherFile:sender {
    return YES;
}

/* action method, called when the user chooses open in the menu */
- open:sender {
    const char *const *files;
    static const char *const fileType[3] = {"xygraph", "xyzgraph", NULL};
    OpenPanel *openPanel;
    char fullName[MAXPATHLEN];
    const char *suffix;

  /*
   * Declare that the user can select multiple files to be opened in the
   * Open Panel.  All apps should do this, since its so easy.
   */
    openPanel = [[OpenPanel new] allowMultipleFiles:YES];

  /* run the open panel, filtering for out types of our documents */
    if ([openPanel runModalForTypes:fileType]) {

      /* open all the files returned by the open panel */
	files = [openPanel filenames];
	for (files = [openPanel filenames]; files && *files; files++) {
	    strcpy(fullName, [openPanel directory]);
	    strcat(fullName, "/");
	    strcat(fullName, *files);
	    suffix = strrchr(*files, '.');
	    if (suffix)
		[self appOpenFile:fullName type:++suffix];
	}
    }
    return self;
}

/* action method, called when the user chooses "New 2D" in the menu */
- new2D:sender {
    [[GraphDoc allocFromZone:[self zone]] init];
    return self;
}

/* action method, called when the user chooses "New 3D" in the menu */
- new3D:sender {
    [[Graph3DDoc allocFromZone:[self zone]] init];
    return self;
}

/*
 * Loads the info panel from its separate nib section.  We set the version
 * number in the info panel on the fly, which keeps it up to date
 * automatically.  The VERS_NUM variable is created in the vers.c file, which
 * is created by the vers_string shell command as part of the build process.
 */
- showInfoPanel:sender {
    extern char VERS_NUM[];

    if (!infoPanel) {
	[NXApp loadNibSection:"Info.nib" owner:self withNames:NO];
	[versString setStringValue:V&NUM];
    }
    [infoPanel makeKeyAndOrderFront:self];
    return self;
}

/* Loads the help panel from its separate nib section. */
- showHelpPanel:sender {
    if (!helpPanel)
	[NXApp loadNibSection:"Help.nib" owner:self withNames:NO];
    [helpPanel makeKeyAndOrderFront:self];
    return self;
}

/* Loads the 3D panel from its separate nib section. */
- showThreeDPanel:sender {
    if (!threeDPanel) {
	[NXApp loadNibSection:"ThreeDPanel.nib" owner:self withNames:NO];
      /*
       * USE THIS METHOD CAREFULLY.  It should only be used for panels that
       * never or rarely need to become key.  Usually this is because they
       * have no areas for typing (as in this case), or else the user very
       * rarely types in them (e.g., the Font panel).
       */
	[threeDPanel setBecomeKeyOnlyIfNeeded:YES];
    }
    [threeDPanel orderFront:self];
    return self;
}

/* Returns the 3D Panel */
- (ThreeDPanel *)threeDPanel {
    return threeDPanel;
}

/* Returns the string table */
- (NXStringTable *)stringTable {
    char path[MAXPATHLEN+1];

    if (!stringTable && [[NXBundle mainBundle] getPath:path forResource:"Graph" ofType:"strings"]) {
	stringTable = [[NXStringTable allocFromZone:[self zone]] init];
	[stringTable readFromFile:path];
    }
    return stringTable;
}

/*
 * Returns the window for a given filename, if open.  We go through the window
 * list looking for an window with the given file open.  Since there are many
 * more windows in the window list than our document windows (panels, NXImage
 * caches, the app icon,...), we check the delegate to know if its a document
 * window.  We use realpath() to make sure we aren't fooled by symbolic links
 * that lead to a file already opened via a different path.
 */ 
static Window *findDocWindow(const char *name) {
    int count;
    id document;
    Window *window;
    List *windowList;
    const char *realPath;
    const char *otherRealPath;
    char realPathBuf[MAXPATHLEN+1];

    windowList = [NXApp windowList];
    count = [windowList count];
    realPath = realpath(name, realPathBuf) ? realPathBuf : name;
    while (count--) {
	window = [windowList objectAt:count];
	document = [window delegate];
	if (document && ([document isKindOf:[GraphDoc class]]
			 || [document isKindOf:[Graph3DDoc class]])) {
	    otherRealPath = [document realFilename];
	  ' (otherRealPath && !strcmp(otherRealPath, realPath))
		return window;
	}
    }
    return nil;
}

@end
