/* App.m
 * Application class of Cenon
 *
 * Copyright (C) 1995-2006 by vhf interservice GmbH
 * Author:   Georg Fleischmann
 *
 * created:  1995-08-10
 * modified: 2006-01-06
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the vhf Public License as
 * published by vhf interservice GmbH. Among other things, the
 * License requires that the copyright notices and this notice
 * be preserved on all copies.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the vhf Public License for more details.
 *
 * You should have received a copy of the vhf Public License along
 * with this program; see the file LICENSE. If not, write to vhf.
 *
 * vhf interservice GmbH, Im Marxle 3, 72119 Altingen, Germany
 * eMail: info@vhf.de
 * http://www.vhf.de
 */

#include <AppKit/AppKit.h>
#include <VHFShared/vhfCommonFunctions.h>
#include <VHFShared/VHFSystemAdditions.h>
#include "messages.h"
#include "functions.h"
#include "locations.h"
#include "App.h"
#include "CenonModuleMethods.h"
#include "MyPageLayout.h"
#include "TilePanel.h"
#include "GridPanel.h"
#include "WorkingAreaPanel.h"
#include "InspectorPanel.subproj/InspectorPanel.h"
#include "Document.h"
#include "DocView.h"
#include "DocWindow.h"
#include "PreferencesMacros.h"

#include "DINImportSub.h"
#include "DXFImportSub.h"
#include "GerberImportSub.h"
#include "HPGLImportSub.h"
#include "PSImportSub.h"
#include "Type1ImportSub.h"

@implementation App

/*
 * modified: 2005-07-22
 * Initializes the defaults.
 */
+ (void)initialize
{   NSMutableDictionary	*registrationDict = [NSMutableDictionary dictionary];

    [registrationDict setObject:@"." forKey:@"NSDecimalSeparator"];

    /* General Preferences defaults */
#ifdef __APPLE__
    [registrationDict setObject:@"NO"  forKey:@"doCaching"];
#else
    [registrationDict setObject:@"YES" forKey:@"doCaching"];
#endif
    [registrationDict setObject:@"0"   forKey:@"unit"];
    [registrationDict setObject:@"NO"  forKey:@"removeBackups"];
    [registrationDict setObject:@"NO"  forKey:@"expertMode"];
    [registrationDict setObject:@"2"   forKey:@"snap"];
    [registrationDict setObject:@"0"   forKey:@"lineWidth"];
    [registrationDict setObject:@"YES"  forKey:@"selectByBorder"];
    [registrationDict setObject:@"20"  forKey:@"cacheLimit"];

    /* Import preferences defaults */
    [registrationDict setObject:@"hpgl_8Pen" forKey:@"hpglParmsFileName"];
    [registrationDict setObject:@"gerber"    forKey:@"gerberParmsFileName"];
    [registrationDict setObject:@""          forKey:@"dinParmsFileName"];
    [registrationDict setObject:@"25.4"      forKey:@"dxfRes"];
    [registrationDict setObject:@"NO"        forKey:@"psFlattenText"];
    [registrationDict setObject:@"NO"        forKey:@"psPreferArcs"];
    [registrationDict setObject:@"NO"        forKey:@"colorToLayer"];
    [registrationDict setObject:@"NO"        forKey:@"fillObjects"];

    /* Export preferences defaults */
    [registrationDict setObject:@"NO"        forKey:@"exportFlattenText"];

    [[NSUserDefaults standardUserDefaults] registerDefaults:registrationDict];
}

/* modified: 2004-02-13
 */
- init
{
    if ( (self = [super init]) )
        [self setDelegate:self]; // so that we get NSApp delegation methods

    modules = [NSMutableArray new];

    return self;
}

/*
 * Directory where we are currently "working."
 */
- (NSString*)currentDirectory
{   NSString	*cdir = [[self currentDocument] directory], *path;

    if (cdir && [cdir length])
        path = cdir;
    else
        path = (haveOpenedDocument ? [((NSOpenPanel*)[NSOpenPanel openPanel]) directory]
                                   : NSHomeDirectory());
    if (!path)
        return NSHomeDirectory();
    return path;
}

/* set the current document without regard to the active window
 * we need this in the moment of opening a new document.
 */
- (void)setCurrentDocument:(Document*)docu
{
    fixedDocument = docu;
}

- (Document*)currentDocument
{
    if (fixedDocument)
        return fixedDocument;
    else if ( [[self mainWindow] isMemberOfClass:[DocWindow class]] )
    {	id	docu = [(DocWindow*)[self mainWindow] document];

        if ([docu isMemberOfClass:[Document class]])
            return docu;
    }
    return nil;
}
- (Document*)openedDocument
{
    return document;
}

- (Document*)documentInWindow:(NSWindow*)window
{
    if ([window isMemberOfClass:[DocWindow class]])
    {	id	docu = [(DocWindow*)window document];

        if ([docu isMemberOfClass:[Document class]])
            return docu;
    }

    return nil;
}

/*
 * Returns the application-wide FontPageLayout panel.
 */
- (NSPageLayout *)pageLayout
{   static NSPageLayout *dpl = nil;

    if (!dpl)
    {
        dpl = [NSPageLayout pageLayout];
        if (![NSBundle loadModelNamed:@"PageLayoutAccessory" owner:self])
            NSLog(@"Cannot load PageLayoutAccessory interface file");
    }
    return dpl;
}

/* created:  2002-07-18
 * modified: 2005-09-05 (load module only once)
 */
- (void)loadModules
{   NSBundle		*mainBundle = [NSBundle mainBundle], *bundle;
    Class		bundleClass;
    NSString		*path;
    int			i, j;
    NSFileManager	*fileManager = [NSFileManager defaultManager];
    BOOL		camModuleLoaded = NO;
    NSMutableArray	*loadedFiles = [NSMutableArray array];

    /* load modules from application bundle
     * FIXME: The CAM module should be integrated below (case 0)
     *        Each module should give info about other modules needed before
     *        Each module should give info about modules to be in conflict
     *        Available modules should be selectable by switches in preferences
     */
    path = [mainBundle pathForResource:@"CAM" ofType:@"bundle"];
    if ( (bundle = [NSBundle bundleWithPath:path]) )
    {
        bundleClass = [bundle principalClass];	// controller
        [modules addObject:bundle];		// our loaded modules
        [bundleClass instance];			// create instance
        camModuleLoaded = YES;
    }

    /* load additional modules */
    for (i=0; i<5; i++)
    {   NSArray	*files;

        switch (i)
        {
            //case 0: path = [[mainBundle resourcePath] stringByAppendingPathComponent:BUNDLEFOLDER]; break;
            //case 0: path = [mainBundle resourcePath]; break;
            case 0: path = [userLibrary()  stringByAppendingPathComponent:@"Bundles"]; break;
            case 1: path = [localLibrary() stringByAppendingPathComponent:@"Bundles"]; break;
            case 2: path = userBundlePath(); break;
            case 3: path = localBundlePath(); break;
            case 4: path = systemBundlePath(); break;
        }

        /* we load the bundles in alphabetic order */
        files = [[fileManager directoryContentsAtPath:path] sortedArrayUsingSelector:@selector(compare:)];
        for (j=0; j<(int)[files count]; j++)
        {   NSString	*file = [files objectAtIndex:j];

            if (camModuleLoaded && [file hasPrefix:@"Cut."])	// either CAM or Cut, not both
                continue;
            if ( [file hasSuffix:@".bundle"] &&
                 ![loadedFiles containsObject:file] &&	// already loaded ?
                 (bundle = [NSBundle bundleWithPath:[path stringByAppendingPathComponent:file]]) )
            {
                bundleClass = [bundle principalClass];	// controller
                [modules addObject:bundle];		// our loaded modules
                [bundleClass instance];			// create instance
                [loadedFiles addObject:file];
            }
        }
    }
}
- (NSArray*)modules
{
    return modules;
}

/* created:  1993-01-??
 * modified: 2005-11-18
 */
- (void)applicationDidFinishLaunching:(NSNotification *)notification
{   NSApplication	*theApplication = [notification object];
    NSString		*path;
    NSFileManager	*fileManager = [NSFileManager defaultManager];

    [[NSUserDefaults standardUserDefaults] setObject:@"." forKey:@"NSDecimalSeparator"];

    /* create HOME-Library if it don't exist
     */
    path = userLibrary();
    if ( ![fileManager fileExistsAtPath:path] )
    {
        if ( ![fileManager fileExistsAtPath:localLibrary()] )
            NSRunAlertPanel(@"", CANTFINDLIB_STRING, OK_STRING, nil, nil, NULL);
        /* copy Cenon directory */
        else
        {   NSString	*from, *to;

            /* create HOME/Cenon */
            [fileManager createDirectoryAtPath:path attributes:nil];
            /* create HOME/Cenon/Projects */
            [fileManager createDirectoryAtPath:vhfPathWithPathComponents(path, @"Projects", nil)
                                    attributes:nil];
            /* copy Cenon/Projects/.dir.tiff */
            from = vhfPathWithPathComponents(localLibrary(), @"Projects", @".dir.tiff", nil);
            to = vhfPathWithPathComponents(path, @"Projects", @".dir.tiff", nil);
            [fileManager copyPath:from toPath:to handler:nil];
            /* copy Cenon/dir.tiff */
            from = vhfPathWithPathComponents(localLibrary(), @".dir.tiff", nil);
            to = vhfPathWithPathComponents(path, @".dir.tiff", nil);
            [fileManager copyPath:from toPath:to handler:nil];
        }
    }

    [theApplication setDelegate:self];

    /* to avoid another call of init when double clicking a file in workspace */
    appIsRunning = 1;

    /* load modules */
    [self loadModules];

    [self displayToolPanel:YES];

#if !defined(GNUSTEP_BASE_VERSION) && !defined(__APPLE__)	// OpenStep 4.2
    [self saveAsPanel];	// OS Bug: If 1st Panel is the open panel this function would return an open panel
#endif

    [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:500.0/1000000.0]];
}


- (NSString *)appDirectory
{
    return [[NSBundle mainBundle] bundlePath];
}


/* Creates a new document--called by pressing New in the Document menu.
 */
- (void)new:sender
{
    document = [Document new];
}

- (id)listFromFile:(NSString*)fileName
{   NSArray		*list = nil;
    NSString		*path, *name;
    NSFileManager	*defaultManager = [NSFileManager defaultManager];

    //if ( [fileName rangeOfString:@".dxf" options:NSCaseInsensitiveSearch].length )
    //if ( [fileName compare:@".dxf" options:NSAnchoredSearch|NSBackwardsSearch|NSCaseInsensitiveSearch] )
    if ( [fileName hasSuffix:@".dxf"] || [fileName hasSuffix:@".DXF"] )
    {	id	dxfImport = [[DXFImportSub allocWithZone:[self zone]] init];
        NSData	*data = [NSData dataWithContentsOfFile:fileName];	// get data object

        [dxfImport setRes:Prefs_DXFRes];
        list = [[dxfImport importDXF:data] retain];	// get list of graphic-objects from import
        [dxfImport release];
    }
    else if ( [fileName hasSuffix:@".hpgl"] || [fileName hasSuffix:@".hgl"] || [fileName hasSuffix:@".plt"] )
    {	id	hpglImport;
        NSData	*data;

        /* load parameter file
         * 1st try it in the users home library then try it in /LocalLibrary
         */
        name = [Prefs_HPGLParmsFileName stringByAppendingPathExtension:DEV_EXT];
        path = vhfPathWithPathComponents(userLibrary(), HPGLPATH, name, nil);
        if ( ![defaultManager fileExistsAtPath:path] )
        {   path = vhfPathWithPathComponents(localLibrary(), HPGLPATH, name, nil);
            if ( ![defaultManager fileExistsAtPath:path] )
            {	NSRunAlertPanel(@"", CANTLOADFILE_STRING, OK_STRING, nil, nil, path);
                return nil;
            }
        }
        hpglImport = [[HPGLImportSub allocWithZone:[self zone]] init];	// get new import-object
        if (![hpglImport loadParameter:path])
        {   NSRunAlertPanel(@"", CANTLOADFILE_STRING, OK_STRING, nil, nil, path);
      	    [hpglImport release];
            return nil;
        }
        data = [NSData dataWithContentsOfFile:fileName];	// get file-stream
        list = [[hpglImport importHPGL:data] retain];		// get list of graphic-objects from import
        [hpglImport release];
    }
    else if ( [fileName hasSuffix:@".ger"] || [fileName hasSuffix:@".gerber"] )
    {   id	gerberImport;
        NSData	*data;

        /* load parameter file
         * 1st try it in the users home library then try it in /LocalLibrary
         */
        name = [Prefs_GerberParmsFileName stringByAppendingPathExtension:DEV_EXT];
        path = vhfPathWithPathComponents(userLibrary(), GERBERPATH, name, nil);
        if ( ![defaultManager fileExistsAtPath:path] )
        {   path = vhfPathWithPathComponents(localLibrary(), GERBERPATH, name, nil);
            if ( ![defaultManager fileExistsAtPath:path] )
            {	NSRunAlertPanel(@"", CANTLOADFILE_STRING, OK_STRING, nil, nil, path);
                return nil;
            }
        }
        gerberImport = [[GerberImportSub allocWithZone:[self zone]] init];	// get new import-object
        [gerberImport setDefaultParameter];
        if (![gerberImport loadParameter:path])
        {   NSRunAlertPanel(@"", CANTLOADFILE_STRING, OK_STRING, nil, nil, path);
            [gerberImport release];
            return nil;
        }
        /* look if RS274X file -> we need no Apertures
         */
        data = [NSData dataWithContentsOfFile:fileName]; // get file-stream
        if ( ![gerberImport loadRS274XApertures:data] ) // + parameter
        {
            /* load aperture table (extension: .tab)
             * try to load a table with the same name and in the same path as the file.
             * if this fails try to load the default-table from the Home-Library or the LocalLibrary
             */
            path = [[fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"tab"];
            if ( ![gerberImport loadApertures:path] )
            {
                if ( NSRunAlertPanel(@"", CANTLOADFILEDEFAULT_STRING,
                                    OK_STRING, CANCEL_STRING, nil, path) == NSAlertAlternateReturn )
                {   [gerberImport release];
                    return nil;
                }
                name = [Prefs_GerberParmsFileName stringByAppendingPathExtension:@"tab"];
                path = vhfPathWithPathComponents(userLibrary(), GERBERPATH, name, nil);
                if ( ![gerberImport loadApertures:path] )
                {   path = vhfPathWithPathComponents(localLibrary(), GERBERPATH, name, nil);
                    if ( ![gerberImport loadApertures:path] )
                    {   NSRunAlertPanel(@"", CANTLOADFILE_STRING, OK_STRING, nil, nil, path);
                        [gerberImport release];
                        return nil;
                    }
                }
            }
        }
        list = [[gerberImport importGerber:data] retain];	// get list of graphic-objects from import
        [gerberImport release];
    }
    else if ( [fileName hasSuffix:@".drl"] || [fileName hasSuffix:@".din"] )
    {   id		dinImport;
        NSData		*data;
        NSString	*devFileName;

        /* look for parameter file
         * 1st try it in the users home library then try it in /LocalLibrary
         */
        if ([(devFileName = Prefs_DINParmsFileName) length])
        {
            name = [devFileName stringByAppendingPathExtension:DEV_EXT];
            path = vhfPathWithPathComponents(userLibrary(), DINPATH, name, nil);
            if ( ![defaultManager fileExistsAtPath:path] )
            {   path = vhfPathWithPathComponents(localLibrary(), DINPATH, name, nil);
                if ( ![defaultManager fileExistsAtPath:path] )
                {   NSRunAlertPanel(@"", CANTLOADFILE_STRING, OK_STRING, nil, nil, path);
                    return nil;
                }
            }
        }
        else
            path = nil;
        dinImport = [[DINImportSub allocWithZone:[self zone]] init];	// get new import-object
        data = [NSData dataWithContentsOfFile:fileName]; // get file-stream
        // load parameter
        if (path && ![dinImport loadParameter:path])
        {   NSRunAlertPanel(@"", CANTLOADFILE_STRING, OK_STRING, nil, nil, path);
            [DINImport release];
            return nil;
        }
        list = [dinImport importDIN:data]; // get list of graphic-objects from import
        list = [[DINImportSub layerListFromGraphicList:list] retain];
        [dinImport release];
    }
    else if ( [fileName hasSuffix:@".font"] || [fileName hasSuffix:@".pfa"] || [fileName hasSuffix:@".pfb"] )
    {   id		fontObject, type1Import;
        NSData		*data;
        NSString	*name;

	if ( [fileName hasSuffix:@".pfa"] || [fileName hasSuffix:@".pfb"] )	// pfa file
            name = [NSString stringWithFormat:@"%@", fileName];
	else
            name = [fileName stringByAppendingPathComponent:
                [[fileName lastPathComponent] stringByDeletingPathExtension]];
        type1Import = [[Type1ImportSub allocWithZone:[self zone]] init];	// get new import-object
        data = [NSData dataWithContentsOfFile:name];				// get file-data

        fontObject = [[type1Import importType1:data] retain];	// get list of graphic-objects from import
        [type1Import release];
        return [fontObject autorelease];
    }

    /* PostScript, AI */
    else if ( [fileName hasSuffix:@".eps"] || [fileName hasSuffix:@".ps"] ||
              [fileName hasSuffix:@".ai"] )
        list = [[self listFromPSFile:fileName] retain];

    /* PDF */
    else if ( [fileName hasSuffix:@".pdf"] || [fileName hasSuffix:@".PDF"])
    {   PSImportSub	*psImport = [[PSImportSub allocWithZone:[self zone]] init];

        [psImport preferArcs:Prefs_PSPreferArcs];
        [psImport flattenText:Prefs_PSFlattenText];
        list = [[psImport importPDFFromFile:fileName] retain];
        [psImport release];
    }

    /* raster images */
    else
    {   VImage	*g = [[[VImage allocWithZone:(NSZone *)[self zone]] initWithFile:fileName] autorelease];

        if ( g )
            list = [[NSMutableArray arrayWithObject:g] retain];
    }

    return [list autorelease];
}

- (NSArray*)listFromPSFile:(NSString*)fileName
{   PSImportSub		*psImport;
    NSData		*data = nil;
    NSString		*path;
    NSFileManager	*defaultManager = [NSFileManager defaultManager];

    /* Adobe Illustrator */
    if ( [fileName hasSuffix:@".ai"] )
    {   NSString	*string, *header;
        NSRange		range;

        string = [NSString stringWithContentsOfFile:fileName];	/* get file */
        range = [string rangeOfString:@"%%BeginResource"];
        if ( !range.length )	/* no ps-header */
        {
            path = [[[NSBundle bundleForClass:[PSImport class]] resourcePath]
                    stringByAppendingPathComponent:AI_HEADER];
            if ( ![defaultManager fileExistsAtPath:path] )
            {   NSRunAlertPanel(@"", CANTLOADFILE_STRING, OK_STRING, nil, nil, path);
                return nil;
            }
            header = [NSString stringWithContentsOfFile:path];	/* get file */

            string = [header stringByAppendingString:string];
        }
        data = [string dataUsingEncoding:NSASCIIStringEncoding];
    }
    else
        data = [NSData dataWithContentsOfFile:fileName];	/* get file */

    /* import file */
    psImport = [[[PSImportSub allocWithZone:[self zone]] init] autorelease];
    //[psImport moveToOrigin:NO];
    [psImport preferArcs:Prefs_PSPreferArcs];
    [psImport flattenText:Prefs_PSFlattenText];
    return [psImport importPS:data];
}

- (BOOL)openFile:(NSString *)fileName
{   id			list = 0;
    NSString		*path;
    NSFileManager	*fileManager = [NSFileManager defaultManager];

    if (!appIsRunning)	// give application a chance to load modules first
    {
        [self performSelector:@selector(openFile:) withObject:fileName afterDelay:0];
        return YES;
    }

    document = nil;
    if ( ![fileManager fileExistsAtPath:fileName] )
        return NO;
    if ( [fileName hasSuffix:DOCUMENT_EXT] )
    {	int	i;

        for (i=[[self windows] count]-1; i>=0; i--)
        {   Document	*docu = [self documentInWindow:[[self windows] objectAtIndex:i]];

            if (!docu)
                continue;
            path = vhfPathWithPathComponents([docu directory], [docu name], nil);
            if ( docu && [path isEqualToString:fileName] )
            {	[[[self windows] objectAtIndex:i] makeKeyAndOrderFront:self];
                return YES;
            }
        }
        document = [Document newFromFile:fileName];
        [self setCurrentDocument:nil];
        return YES;
    }
    else if ( (list = [self listFromFile:fileName]) )
    {
        /* special treatment for fonts */
        if ( [fileName hasSuffix:@".font"] || [fileName hasSuffix:@".pfa"] || [fileName hasSuffix:@".pfb"] )
        {
            document = [Document new];
            [self setCurrentDocument:nil];
            [document setName:UNTITLED_STRING andDirectory:[self currentDirectory]];
            [document setDirty:YES];
            [document setFontObject:list]; // font daten + font list hinzufögen
        }
        /* imports with complete layerList or a single list of objects */
        else
        {
            document = [Document newFromList:list];
            [self setCurrentDocument:nil];
        }
        return YES;
    }
    return NO;
}

/* Import files
 * modified: 20005-09-25
 */
typedef enum
{   IMPORTTO_SELECTEDLAYER  = 0,
    IMPORTTO_NEWLAYER       = 1,
    IMPORTTO_EXISTINGLAYERS = 2
} ImportToLayerSelection;
- (void)import:sender
{   NSArray		*fileTypes = [NSArray arrayWithObjects:@"hpgl", @"hgl", @"plt", @"ai",
                                              @"dxf", @"eps", @"ps", @"pdf", @"gerber", @"ger",
                                              @"cenon", @"tiff", @"jpg", @"gif",
                                              @"font", @"pfa", @"din", @"drl", nil];
    NSString		*fileName;
    id			openPanel = [NSOpenPanel openPanel];
    static NSString	*openDir = @"";

    [openPanel setAccessoryView:[importAccessory retain]];
    [openPanel setAllowsMultipleSelection:NO];

    if ( [openPanel runModalForDirectory:openDir file:@"" types:fileTypes] )
    {   ImportToLayerSelection	importTo = [iaPopup indexOfSelectedItem];
        id			list;
        DocView			*view = [[self currentDocument] documentView];

        [openDir release];
        openDir = [[[openPanel filename] stringByDeletingLastPathComponent] retain];

	fileName = [openPanel filename];
        list = [self listFromFile:fileName];

        switch (importTo)
        {
            case IMPORTTO_NEWLAYER:
                [view addList:list toLayerAtIndex:-1 /*replaceObjects:NO*/];
                break;
            case IMPORTTO_SELECTEDLAYER:
            {   id	layerList = [view layerList];
                int	i = [view indexOfSelectedLayer];

                if ([[layerList objectAtIndex:i] editable])
                    [view addList:list toLayerAtIndex:i /*replaceObjects:NO*/];
                else
                    NSRunAlertPanel(@"", LAYERNOTEDITABLE_STRING, OK_STRING, nil, nil, NULL);
                /*for (i=0; i<[layerList count]; i++)
                    if ([[layerList objectAtIndex:i] editable])
                {   [view addList:list toLayerAtIndex:i];
                        break;
                }
                if (i>=[layerList count])
                    [view addList:list toLayerAtIndex:-1];*/
                break;
            }
            case IMPORTTO_EXISTINGLAYERS:
                [view addList:list toLayerAtIndex:-2 /*replaceObjects:YES*/];
         }
    }
    [openPanel setAccessoryView:nil];
}

/* modified: 2005-11-14
 */
- (void)importASCII:sender
{   NSArray		*fileTypes = [NSArray arrayWithObjects:@"txt", @"asc", @"tab", nil];
    NSString		*fileName;
    id			openPanel = [NSOpenPanel openPanel];
    static NSString	*openDir = @"";

    fillPopup(iaaPopup, CHARCONV_FOLDER, DICT_EXT, 1);
    [openPanel setAccessoryView:[importASCIIAccessory retain]];

    [openPanel setAllowsMultipleSelection:NO];
    if ( [openPanel runModalForDirectory:openDir file:@"" types:fileTypes] )
    {   int		sort = [iaaRadio selectedColumn];
        NSString	*tabName, *string;
        NSDictionary	*conversionDict = nil;

        if ( [iaaPopup indexOfSelectedItem] >= 1 &&
             (tabName = [NSString stringWithFormat:@"%@%@", [iaaPopup title], DICT_EXT]) )
                conversionDict = dictionaryFromFolder(CHARCONV_FOLDER, tabName);
        [openDir release];
        openDir = [[[openPanel filename] stringByDeletingLastPathComponent] retain];

	fileName = [openPanel filename];
        string = [NSString stringWithContentsOfFile:fileName];
        [[[self currentDocument] documentView] importASCII:stringWithConvertedChars(string, conversionDict)
                                                      sort:sort];
    }
    [openPanel setAccessoryView:nil];
}

/*
 * openDocument gets a file name from the user, creates a new document window,
 * and loads the specified file into it.
 */
- (void)openDocument:sender
{   NSArray		*fileTypes = [NSArray arrayWithObjects:@"hpgl", @"hgl", @"plt", @"ai",
                                              @"dxf", @"eps", @"ps", @"pdf", @"gerber", @"ger",
                                              @"cenon", @"tiff", @"jpg", @"gif",
                                              @"font", @"pfa", @"din", @"drl", nil];
    NSArray		*fileNames;
    id			openPanel = [NSOpenPanel openPanel];
    int			i, cnt;
    static NSString	*openDir = @"";

    [openPanel setAllowsMultipleSelection:YES];
    if ( [openPanel runModalForDirectory:openDir file:@"" types:fileTypes] )
    {
        [openDir release];
        openDir = [[[openPanel filename] stringByDeletingLastPathComponent] retain];

	fileNames = [openPanel filenames];
        cnt = [fileNames count];
	for ( i=0; i<cnt; i++ )
	    haveOpenedDocument = [self openFile:[fileNames objectAtIndex:i]] || haveOpenedDocument;
    }
}

/*
 * Saves the file.  If this document has never been saved to disk,
 * then a SavePanel is put up to ask the user what file name she
 * wishes to use to save the document.
 */
- (void)save:sender
{
    [[self currentDocument] save:sender];
}

- (void)saveAs:sender
{
    [[self currentDocument] saveAs:sender];
}

- (void)changeSaveType:sender
{   id	savePanel = [sender window];

    switch ([sender indexOfSelectedItem])
    {
        case 0: [savePanel setRequiredFileType:DOCUMENT_EXT]; break;
        case 1: [savePanel setRequiredFileType:EPS_EXT]; break;
        case 2: [savePanel setRequiredFileType:GERBER_EXT]; break;
        case 3: [savePanel setRequiredFileType:DXF_EXT]; break;
        case 4: [savePanel setRequiredFileType:HPGL_EXT]; break;
        case 5: [savePanel setRequiredFileType:FONT_EXT]; break;
        case 6: [savePanel setRequiredFileType:DIN_EXT]; break;
        default: NSLog(@"App, changeFileType: Unknown file type");
    }
}

- (void)revertToSaved:sender
{   NSString	*fileName = [[self currentDocument] filename];

    if ( [[self currentDocument] dirty]
         &&  NSRunAlertPanel(@"", REVERT_STRING, OK_STRING, CANCEL_STRING, nil, fileName)
         == NSAlertAlternateReturn )
        return;

    [[self currentDocument] setDirty:NO];

    [[[self currentDocument] window] close];
    if (![self openFile:fileName])
        NSRunAlertPanel(@"", CANTOPENFILE_STRING, OK_STRING, nil, nil);
}

/*
 * Returns an OpenPanel with the accessory view
 */
- (NSOpenPanel*)openPanel
{   NSOpenPanel	*openpanel = [NSOpenPanel openPanel];

    [openpanel setAccessoryView:openPanelAccessory];
    return openpanel;
}

- (NSSavePanel*)saveAsPanelWithSaveType:(NSString*)ext
{   NSSavePanel		*savePanel = [NSSavePanel savePanel];
    NSDictionary	*dict = [NSDictionary dictionaryWithObjectsAndKeys:@"0", DOCUMENT_EXT, @"1", EPS_EXT, @"2", GERBER_EXT, @"3", DXF_EXT, @"4", HPGL_EXT, @"5", FONT_EXT, @"6", DIN_EXT, nil];

    [savePanel setAccessoryView:[savePanelAccessory retain]];
    [savePanel setRequiredFileType:ext];
    [spaFormatPopUp selectItemAtIndex:[dict intForKey:ext]];
    [[spaFormatPopUp selectedItem] setEnabled:YES];
    return savePanel;
}

/*
 * Returns a SavePanel with the accessory view which allows the user to
 * pick which type of file she wants to save.
 */
- (NSSavePanel*)saveAsPanel
{   NSSavePanel	*savePanel = [NSSavePanel savePanel];

    /* we have to set the file type and accessory on every call,
     * because it gets destroyed sometimes !
     */
    [savePanel setAccessoryView:[savePanelAccessory retain]];
    //[self changeSaveType:spaFormatPopUp];
    [savePanel setRequiredFileType:DOCUMENT_EXT];
    [spaFormatPopUp selectItemAtIndex:0];

    //[savepanel setTitle:@"Save As"];

    return savePanel;
}

- (NSView*)printPanelAccessory
{
    if (!printPanelAccessory && ![NSBundle loadModelNamed:@"PrintPanelAccessory" owner:self])
        NSLog(@"Cannot load PrintPanelAccessory interface file");
    return printPanelAccessory;
}
- (id)ppaRadio
{
    return ppaRadio; // 0 is composite 1 is separation
}

/*
 * app:openFile: is invoked by Workspace when the user double-clicks
 * on a file Cenon is prepared to accept.
 *
 * modified: 2002-07-01
 */
- (int)application:app openFile:(NSString *)path
{   BOOL	info = NO;

#if 0
    if (!appIsRunning)
    {
        //[self init];
        info = YES;
        [self displayInfo];
        [infoPanel center];
        [infoPanel orderFront:self];
    }
#endif

    if (![self openFile:path])
        NSRunAlertPanel(@"", CANTOPENFILE_STRING, OK_STRING, nil, nil);

    if (info)
        [infoPanel orderOut:self];

    return YES;
}

/*
 * Methods to load model files for the various panels.
 */
- (void)displayInfo
{
    if (!infoPanel)
    {
        if (![NSBundle loadModelNamed:@"Info" owner:self])
            NSLog(@"Cannot load Info interface file");
    }

    [serialNumber setStringValue:@""];

    [[NSNotificationCenter defaultCenter] postNotificationName:InfoPanelWillDisplay
                                                        object:nil userInfo:nil];

    /*if ([keyPanel respondsToSelector:@selector(setVersionOfKey:andSerialNumber:)])
        [keyPanel performSelector:@selector(setVersionOfKey:andSerialNumber:)
                       withObject:kindOfVersion withObject:serialNumber];*/
}
- (void)showInfo:sender
{
    [self displayInfo];
    [infoPanel makeKeyAndOrderFront:sender];
}
- (id)infoVersionText	{ return kindOfVersion; }
- (id)infoSerialText	{ return serialNumber; }

- (void)showPrefsPanel:sender
{
    if (!preferencesPanel)
    {
        if (![NSBundle loadModelNamed:@"PreferencesPanel" owner:self])
            NSLog(@"Cannot load PreferencesPanel interface file");
        [preferencesPanel init];
        [preferencesPanel setFrameUsingName:@"PreferencesPanel"];
        [preferencesPanel setFrameAutosaveName:@"PreferencesPanel"];
    }
    [preferencesPanel makeKeyAndOrderFront:sender];
}
- preferencesPanel
{
    return preferencesPanel;
}

#if 0
- (void)showHelp:sender
{
    if (!helpPanel)
    {
        if (![NSBundle loadModelNamed:@"Help" owner:self])
            NSLog(@"Cannot load Help interface file");	
    }
    [helpPanel setFrameAutosaveName:@"HelpPanel"];
    [helpPanel makeKeyAndOrderFront:sender];
}
#endif

- (void)showInspectorPanel:sender
{
    if (!inspectorPanel)
    {
        if (![NSBundle loadModelNamed:@"InspectorPanel" owner:self])
            NSLog(@"Cannot load InspectorPanel interface file");	
    }
    [inspectorPanel init];
    [inspectorPanel updateInspector];
    [inspectorPanel setFrameAutosaveName:@"InspectorPanel"];
    [inspectorPanel setBecomesKeyOnlyIfNeeded:YES];
    [inspectorPanel orderFront:sender];
}
- inspectorPanel;
{
    return inspectorPanel;
}

- (void)showTransformPanel:sender
{
    if (!transformPanel)
    {
        if (![NSBundle loadModelNamed:@"TransformPanel" owner:self])
            NSLog(@"Cannot load TransformPanel interface file");	
        [transformPanel init];
    }
    [transformPanel setFrameAutosaveName:@"TransformPanel"];
    [transformPanel makeKeyAndOrderFront:sender];
}
- transformPanel;
{
    return transformPanel;
}

- (void)showProjectSettingsPanel:sender
{
    if (!projectSettingsPanel)
    {
        if (![NSBundle loadModelNamed:@"ProjectSettingsPanel" owner:self])
            NSLog(@"Cannot load ProjectSettingsPanel interface file");	
        [projectSettingsPanel init];
    }
    //[projectSettingsPanel setFrameAutosaveName:@"ProjectSettingsPanel"];
    [projectSettingsPanel makeKeyAndOrderFront:sender];
}
- projectSettingsPanel;
{
    return projectSettingsPanel;
}

- (void)showTilePanel:sender
{
    if (!tilePanel)
    {
        if ( ![NSBundle loadModelNamed:@"TilePanel" owner:self] )
            NSLog(@"Cannot load TilePanel model");	
        [tilePanel updatePanel:sender];
    }
    [tilePanel setFrameAutosaveName:@"TilePanel"];
    [tilePanel setDelegate:self];
    [tilePanel makeKeyAndOrderFront:sender];
}
- tilePanel
{
    return tilePanel;
}

- (void)runGridPanel:sender
{
    if (!gridPanel)
    {
        if ( ![NSBundle loadModelNamed:@"GridPanel" owner:self] )
            NSLog(@"Cannot load GridPanel model");
    }

    [gridPanel update:sender];
    [gridPanel setDelegate:self];
    [gridPanel setFrameAutosaveName:@"GridPanel"];
    if (gridPanel)
        [self runModalForWindow:gridPanel];
}
- (id)gridPanel
{
    return gridPanel;
}

- (void)showWorkingAreaPanel:sender
{
    if (!workingAreaPanel)
    {
        if (![NSBundle loadModelNamed:@"WorkingAreaPanel" owner:self])
            NSLog(@"Cannot find WorkingAreaPanel model");	
    }
    [workingAreaPanel update:sender];
    [workingAreaPanel setFrameAutosaveName:@"WorkingAreaPanel"];
    [workingAreaPanel makeKeyAndOrderFront:sender];
}

- (void)showIntersectionPanel:sender
{
    if (!intersectionPanel)
    {
        if (![NSBundle loadModelNamed:@"IntersectionPanel" owner:self])
            NSLog(@"Cannot load IntersectionPanel interface file");	
    }
    [intersectionPanel setFrameAutosaveName:@"IntersectionPanel"];
    [intersectionPanel makeKeyAndOrderFront:sender];
}
- intersectionPanel;
{
    return intersectionPanel;
}

/*
 */

/* shows the tool panel
 */
- (void)showToolPanel:sender
{
    [self displayToolPanel:YES]; 
}

/* shows the tool panel
 */
- (void)displayToolPanel:(BOOL)flag
{
    if (!toolPanel)
    {	if (![NSBundle loadModelNamed:@"ToolPanel" owner:self])
            NSLog(@"Cannot load ToolPanel interface file");
        [toolPanel setFrameUsingName:@"ToolPanel"];
        [toolPanel setFrameAutosaveName:@"ToolPanel"];
    }

    [toolPanel setBecomesKeyOnlyIfNeeded:YES];
    [toolPanel setFloatingPanel:YES];
    [toolPanel orderFront:self];
}

/*
 * modified: 2004-12-03
 */
- (void)setCurrent2DTool:sender
{   id		cursor;
    id		matrix = [[[toolPanel contentView] subviews] objectAtIndex:0];
    static id	rotateCursor = nil, crossCursor = nil, scissorCursor = nil;	// the cursors

    if ( [self currentDocument] )
    {
        if (!sender && current2DTool)
        {   current2DTool = 0;
            [[[self currentDocument] scrollView] setDocumentCursor:[NSCursor arrowCursor]];
            return;
        }

        if (sender && sender!=self)	// command key pressed
            [[[self currentDocument] window] endEditingFor:nil];	// end editing of text
        current2DTool = [[matrix selectedCell] tag];
        switch (current2DTool)
        {
            case TOOL2D_ROTATE:		// rotate
                if (!rotateCursor)
                {
                    rotateCursor = [[NSCursor alloc] initWithImage:[NSImage imageNamed:@"cursorRotate.tiff"]
                                                           hotSpot:NSMakePoint(5, 2)]; // was 7, 7
                }
                cursor = rotateCursor;
                break;
            case TOOL2D_MARK:		// mark
            case TOOL2D_WEB:		// web
            case TOOL2D_LINE:		// line
            case TOOL2D_CURVE:		// curve
            case TOOL2D_ARC:		// arc
            case TOOL2D_THREAD:		// thread
            case TOOL2D_SINKING:	// sag
            case TOOL2D_RECT:		// rectangle
            case TOOL2D_PATH:		// path
            case TOOL2D_POLYLINE:	// polyline
                if (!crossCursor)
                {
                    crossCursor = [[NSCursor alloc] initWithImage:[NSImage imageNamed:@"cursorCross.tiff"]
                                                          hotSpot:NSMakePoint(7, 7)];
                }
                cursor = crossCursor;
                break;
            case TOOL2D_TEXT:		// text
                cursor = [NSCursor IBeamCursor];
                break;
            case TOOL2D_SCISSOR:	// scissor
                if (!scissorCursor)
                {
                    scissorCursor = [[NSCursor alloc] initWithImage:[NSImage imageNamed:@"cursorCutter.tiff"]
                                                            hotSpot:NSMakePoint(0, 14)];
                }
                cursor = scissorCursor;
                break;
            default:			// arrow
                cursor = [NSCursor arrowCursor];
        }
        [[[self currentDocument] scrollView] setDocumentCursor:cursor];
    }
}

/*
 * The current 2D tool used to create new Graphics.
 */
- (int)current2DTool
{
    return current2DTool;
}

/* terminating the app...
 *
 * modified: 2002-01-30
 */
- (void)terminate:(id)sender
{   int		count;

    for (count=[[self windows] count]-1; count>=0; count--)
    {	int	i;
        id	docu = [self documentInWindow:[[self windows] objectAtIndex:count]];

        if (docu && [docu dirty])
        {
            switch (NSRunAlertPanel(QUIT_STRING, UNSAVEDDOCS_STRING, REVIEW_STRING, QUITANYWAY_STRING, CANCEL_STRING))
            {
                case NSAlertDefaultReturn:	// review unsaved
                    count = -1;
                    break;
                case NSAlertAlternateReturn:	// quit
                    for (i=count; i>=0; i--)
                        [[self documentInWindow:[[self windows] objectAtIndex:i]] setDirty:NO];
                    break;
                default:			// cancel
                    return;
            }
        }
    }

    /* close doc windows, so the user has a chance to check dirty windows */
    for (count=[[self windows] count]-1; count>=0; count--)
        [[[self documentInWindow:[[self windows] objectAtIndex:count]] window] performClose:self];

    /* If window is not closed, then cancel */
    for (count=[[self windows] count]-1; count>=0; count--)
        if ([[self documentInWindow:[[self windows] objectAtIndex:count]] window])
            return;	// cancel termination

    /* quit sub process */
    if ([self respondsToSelector:@selector(quitXyzSubProcess)])
        [self performSelector:@selector(quitXyzSubProcess)];

    [super terminate:sender];
}

- (BOOL)command		{ return command; }
- (BOOL)control		{ return control; }
- (BOOL)alternate	{ return alternate; }

/*
 * modified: 05.11.95
 *
 * We override this because we need to find out when the command key is down
 */
- (void)sendEvent:(NSEvent *)event
{
    if (event && [event type] < NSAppKitDefined)
    {	BOOL	lastCommand = command;

        command = ([event modifierFlags] & NSCommandKeyMask) ? YES : NO;
        control = ([event modifierFlags] & NSControlKeyMask) ? YES : NO;
        alternate = ([event modifierFlags] & NSAlternateKeyMask) ? YES : NO;
        shift = ([event modifierFlags] & NSShiftKeyMask) ? YES : NO;

        /* temporary set arrow mode
         */
        if (command != lastCommand)
            [self setCurrent2DTool:(command) ? nil : self];

        if (command && [event type] == NSKeyDown &&
            [[self mainWindow] isMemberOfClass:[DocWindow class]])
        {   NSString	*string = [event charactersIgnoringModifiers];
            int		i = [string intValue];
            id		fe = [[self mainWindow] fieldEditor:NO forObject:nil];

            /* set inspector panel only, if no text ruler is activated,
             * to avoid setting the inspector when rulers are copied
             */
            if (![fe isRulerVisible])
            {
                if (i >= 1 && i<= 5)
                {
                    if (!inspectorPanel)
                        [self showInspectorPanel:self];
                    [inspectorPanel setLevelAt:i-1];
                }
            }
        }
    }
    [super sendEvent:event];
}

/*
 * Can be called to see if the specified action is valid now.
 * It returns NO if the action is not valid now,
 * otherwise it returns YES.
 */
- (BOOL)validateMenuItem:(id <NSMenuItem>)anItem
{   SEL	action = [anItem action];

    if ( (action == @selector(import:) ||
          action == @selector(importASCII:) ||
          action == @selector(revertToSaved:) ||
          action == @selector(save:) ||
          action == @selector(saveAs:)) &&
         ![self currentDocument] )
        return NO;

    return YES;
}


- (BOOL)windowShouldClose:(id)sender
{
    if ( sender == contourPanel || sender == gridPanel )
        [NSApp stopModalWithCode:YES];

    return YES;
}

@end
