
#import <time.h>
#import <unistd.h>
#import <fcntl.h>

#import "Options.h"
#import "Preview.h"

#import "Controller.h"


@implementation Controller

- init
{
    imageTypes =
        [NSArray arrayWithObjects:@"gif", @"GIF",
                 @"png", @"PNG",
                 @"tiff", @"TIFF",
                 @"jpg", @"JPG",
                 @"jpeg", @"JPEG",
                 nil];
    [imageTypes retain];

    psTypes = 
        [NSArray arrayWithObjects:@"ps", @"PS",
                 @"pdf", @"PDF",
                 nil];
    [psTypes retain];

    openers = [NSMutableArray arrayWithCapacity:4];
    [openers retain];
    
    initIsDone = NO;

    imgFmt = nil;

    return [super init];
}

- (BOOL)application:(NSApplication *)theApp openFile:(NSString *)path
{
    NSFileManager *fm = [NSFileManager defaultManager];
    
    if([fm isReadableFileAtPath:path]==NO){
        return NO;
    }

    if(initIsDone==NO){
        [openers addObject:path];
    }
    else{
        NSString *ext = [path pathExtension];
        
        Class pclass = 
            ([psTypes containsObject:ext]==YES ?
             [PostScriptOrPDFPreview class] :
             ([imageTypes containsObject:ext]==YES ?
              [ImagePreview class] :
              [TextPreview class]));
        id previewer = 
            [[pclass alloc] 
                initWithController:self andPath:path
                resolution:-1];
        [previewer orderFrontRegardless];
    }
    
    return YES;
}


- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    NSArray *procArgs = [[NSProcessInfo processInfo] arguments];
    int openCount = [openers count];
    NSEnumerator *argEn;

    NSString *path;

    NSMutableSet *pset = [NSMutableSet setWithCapacity:1];

    [openers addObjectsFromArray:procArgs];
    [openers removeObjectAtIndex:openCount]; // skip program name
    argEn = [openers objectEnumerator];

    currentOption = nil;

    [self makePreferences];
    [self makeProgPanel];
    [self makeOptions];

    [[NSApplication sharedApplication] updateWindows];

    initIsDone = YES;

    while((path = [argEn nextObject])!=nil){
        if([pset containsObject:path]==YES){
            continue;
        }
        [pset addObject:path];

        [[NSNotificationQueue defaultQueue]
            enqueueNotification:
                [NSNotification
                    notificationWithName:OpenCmdLineFile
                    object:path]
            postingStyle:NSPostWhenIdle];
    }

    [[NSNotificationCenter defaultCenter] 
        addObserver:self
        selector:@selector(openCmdLineFile:)
        name:OpenCmdLineFile object:nil];
}

- (void)openCmdLineFile:(NSNotification *)aNotification
{
    NSString *path = [aNotification object],
        *ext = [path pathExtension];

    if([[NSApplication sharedApplication] modalWindow]!=nil){
        [[NSNotificationQueue defaultQueue]
            enqueueNotification:
                [NSNotification
                    notificationWithName:OpenCmdLineFile
                    object:path]
            postingStyle:NSPostWhenIdle];
        return;
    }

    NSFileManager *fm = [NSFileManager defaultManager];

    if([fm isReadableFileAtPath:path]==NO){
        NSLog(@"can't read %@, skipping", path);
        return;
    }

    Class pclass = 
        ([psTypes containsObject:ext]==YES ?
         [PostScriptOrPDFPreview class] :
         ([imageTypes containsObject:ext]==YES ?
          [ImagePreview class] :
          [TextPreview class]));

    [[pclass alloc] 
        initWithController:self andPath:path
        resolution:-1];
}

- (void)applicationWillTerminate:(NSNotification *)aNotification
{
    NSEnumerator *itemEn =
        [[optChooser itemArray] objectEnumerator];
    NSMenuItem *item;

    while((item=[itemEn nextObject])!=nil){
        [[item representedObject] writeUserDefaults];
    }
}

- previewAll:(id)sender
{
    NSEnumerator *winEnum =
        [[[NSApplication sharedApplication] windows]
            objectEnumerator];

    id win;
    while((win=[winEnum nextObject])!=nil){
        if([win isKindOfClass:[Preview class]]==YES){
            [win performSelector:@selector(a2psPreview:)
                withObject:self];
        }
    }

    return self;
}

- closeAll:(id)sender
{
    NSEnumerator *winEnum =
        [[[NSApplication sharedApplication] windows]
            objectEnumerator];

    id win;
    while((win=[winEnum nextObject])!=nil){
        if([win isKindOfClass:[Preview class]]==YES){
            [win performSelector:@selector(close)];
        }
    }

    return self;
}


- (NSString *)imgFmt
{
    return imgFmt;
}

- setImgFmt:(NSString *)fmt
{
    if(imgFmt!=nil){
        [imgFmt release];
    }

    imgFmt = fmt;
    [imgFmt retain];

    return self;
}


#define OPTWIDTH       250
#define OPTHEIGHT      300


int cmpOptions(id opt1, id opt2, void *context)
{
    return
        [[opt1 title] compare:[opt2 title]];
}

- changeOption:(id)sender
{
    NSRect cframe = [[optPanel contentView] frame], bcontent, sframe;
    id sopt, scontrol;
    NSBox *box;
    unsigned int mask = NSViewMinXMargin | NSViewMaxXMargin | 
        NSViewMinYMargin | NSViewMaxYMargin;

    if((sopt=[[sender selectedItem] representedObject])==
       currentOption){
        return self;
    }

    if(currentOption!=nil){
        id pcontrol = [currentOption control];
        [[pcontrol superview] removeFromSuperview];
        [pcontrol removeFromSuperview];
    }

    scontrol = [sopt control];
    [scontrol setFrameOrigin:NSMakePoint(0, 0)];
    sframe = [scontrol frame];

    box = [[NSBox alloc] 
              initWithFrame:NSMakeRect(0, 0, 
                                       sframe.size.width+50,
                                       sframe.size.height+50)];
    [box setContentView:scontrol];
    [box setTitle:[sopt title]];
    [box setBorderType:NSGrooveBorder];

    // [box sizeToFit];
    bcontent = NSMakeRect((cframe.size.width-sframe.size.width)/2, 
                          (cframe.size.height-sframe.size.height)/2,
                          sframe.size.width, sframe.size.height);
    [box setAutoresizingMask:mask];
    [box setFrameFromContentFrame:bcontent];
    
    [[optPanel contentView] addSubview:box];

    [optCheckBox 
        setState:([sopt optEnabled]==YES ?
                  NSOnState : NSOffState)];

    currentOption = sopt;

    return self;
}

- optSetEnabled:(id)sender
{
    BOOL enable = ([sender state]==NSOnState ? YES : NO);
    [currentOption setOptEnabled:enable];
    return self;
}


- makeOptions
{
    unsigned int style = NSTitledWindowMask | NSClosableWindowMask |
        NSMiniaturizableWindowMask | NSResizableWindowMask;
    NSView *contentView;
    NSRect cframe, chframe;

    optPanel = [[NSPanel alloc] 
                   initWithContentRect:
                       NSMakeRect(0, 0, OPTWIDTH, OPTHEIGHT)
                   styleMask:style
                   backing:NSBackingStoreRetained
                   defer:NO];
    [optPanel setMinSize:[optPanel frame].size];
    [optPanel setReleasedWhenClosed:NO];
    [optPanel setTitle:@"Options"];

    contentView = [optPanel contentView];
    cframe = [contentView frame];

    optChooser = [[NSPopUpButton alloc] 
                     initWithFrame:NSMakeRect(0, 0, 20, 10)
                     pullsDown:NO];
    [optChooser setTarget:self];
    [optChooser setAction:@selector(changeOption:)];
    [optChooser 
        setAutoresizingMask:
            NSViewMinXMargin | NSViewMaxXMargin | NSViewMinYMargin];        
    [contentView addSubview:optChooser];

    optCheckBox = [[NSButton alloc]
                   initWithFrame:NSMakeRect(0, 0, 20, 10)];
    [optCheckBox setButtonType:NSSwitchButton];
    [optCheckBox setBordered:NO];
    [optCheckBox setTitle:@"Enabled"];
    [optCheckBox sizeToFit];
    [optCheckBox setTarget:self];
    [optCheckBox setAction:@selector(optSetEnabled:)];
    [optCheckBox
        setAutoresizingMask:
            NSViewMinXMargin | NSViewMaxXMargin | NSViewMaxYMargin];        

    chframe = [optCheckBox frame];
    [optCheckBox 
        setFrame:
            NSMakeRect((cframe.size.width-chframe.size.width)/2, 5,
                       chframe.size.width, chframe.size.height)];
    [contentView addSubview:optCheckBox];

    [self rescanOptions];

    return self;
}

- rescanOptions
{
    NSRect cframe = [[optPanel contentView] frame], pframe;
    NSEnumerator *optEn;
    NSMutableArray *unsorted;
    NSArray *options;
    Class optClass;
    id opt;
    int count, index;

    if(currentOption!=nil){
        NSEnumerator *itemEn =
            [[optChooser itemArray] objectEnumerator];
        NSMenuItem *item;
        while((item=[itemEn nextObject])!=nil){
            [[item representedObject] cleanupOption];
        }
    }

    [optCheckBox setState:NSOffState];

    optEn = [Option optEnumerator];
    unsorted = [NSMutableArray arrayWithCapacity:1];
    while((optClass=(Class)[optEn nextObject])!=nil){
        opt = [optClass alloc];
        if([opt initWithController:self]==nil){
            NSString *errMsg =
                [[[opt errMsg] componentsSeparatedByString:@"\n"]
                    componentsJoinedByString:@", "];

            NSLog(@"Couldn't initialize option \"%@\": %@",
                  [opt title], errMsg);
            [opt cleanupOption];
            AUTORELEASE(opt);
        }
        else{
            [unsorted addObject:opt];
            [opt readUserDefaults];
        }
    }

    options = [unsorted sortedArrayUsingFunction:cmpOptions
                        context:NULL];

    [optChooser removeAllItems];
    for(index=0, count = [options count]; 
        index<count; index++){
        opt = [options objectAtIndex:index];
        [optChooser addItemWithTitle:[opt title]];
        [[optChooser lastItem] setRepresentedObject:opt];
    }
    [optChooser sizeToFit];

    pframe = [optChooser frame];
    [optChooser
        setFrame:
            NSMakeRect((cframe.size.width-pframe.size.width)/2, 
                       cframe.size.height-pframe.size.height-10,
                       pframe.size.width, pframe.size.height)];

    [optChooser selectItemAtIndex:0];
    [self changeOption:optChooser];

    return self;
}

- (NSSize)mediumSizeFor:(id)sender
{
    NSSize current;
    NSEnumerator *itemEn = [[optChooser itemArray] objectEnumerator];
    id item;

    if([sender isKindOfClass:[TextPreview class]]==YES){
        // a2ps default is a4 (carry over to gs)
        current = NSMakeSize(8.26389, 11.6944);
    }
    else{
        // gs default is letter
        current = NSMakeSize(8.5, 11);
    }
    current.width  *= 72.0;
    current.height *= 72.0;

    while((item=[itemEn nextObject])!=nil){
        id opt = [(NSCell *)item representedObject];
        if([opt isKindOfClass:[MediaOpt class]] &&
           [opt optEnabled]==YES){
            current = [opt mediumSize];
            break;
        }
    }
    
    NSLog(@"medium: %f x %f", 
          current.width, current.height);
    return current;
}


- a2psDisplayOptions:(id)sender
{
    if([optPanel isVisible]==NO){
        [optPanel center];
    }
    [optPanel makeKeyAndOrderFront:self];
    
    return self;
}

- a2psResetOptions:(id)sender
{
    if([self checkFields]==nil){
        [self runPreferencesPanel:nil];
        return self;
    }
    
    [self rescanOptions];
 
    if([optPanel isVisible]==NO){
        [optPanel center];
    }
    [optPanel makeKeyAndOrderFront:self];
    
    return self;
}

#define PREFWIDTH       300
#define PREFHEIGHT       50

#define PAD @"------------------------------------------------"

typedef enum {
    TAG_A2PS = 0,
    TAG_GHOSTSCRIPT,
    TAG_LPR,
    TAG_BIN_ALL
} BINTAGS;

NSString *bintitles[TAG_BIN_ALL] = {
    @"A2PS", @"GHOSTSCRIPT", @"LPR"
};

- makePreferences
{
    NSBox *box;
    NSButton *button;
    NSView *contentView, *boxContentView;
    NSRect winRect = {{250, 100}, {0, 50}}, boxFrame;
    unsigned int style = NSTitledWindowMask | NSClosableWindowMask |
        NSMiniaturizableWindowMask | NSResizableWindowMask;
    float bwidth;

    int f;
    NSTextField *bin, *bins[TAG_BIN_ALL];

    for(f=TAG_A2PS; f<TAG_BIN_ALL; f++){
         bin = bins[f] = [NSTextField new];
         [bin setStringValue:PAD];
         [bin setEditable:YES];
         [bin sizeToFit];
         [bin setAutoresizingMask:
                  (NSViewWidthSizable |
                   NSViewMinYMargin | NSViewMaxYMargin)];

         button = [NSButton new];
         [button setTitle:@"Browse ..."];
         [button setTarget:self];
         [button setTag:f];
         [button setAction:@selector(browseBinary:)];
         [button setFont:[NSFont labelFontOfSize:
                                     [[button font] pointSize]-2]];
         [button sizeToFit];
         [button setAutoresizingMask:NSViewMinXMargin];
         [button setFrameOrigin:
                     NSMakePoint([bin frame].size.width+5, 0)];

         boxFrame = NSMakeRect(0, winRect.size.height, 
                               PREFWIDTH, PREFHEIGHT);

         box = [[NSBox alloc] initWithFrame:boxFrame];
         [box setContentViewMargins:NSMakeSize(5, 5)];

         boxContentView = [box contentView];
         [boxContentView addSubview:bin];
         [boxContentView addSubview:button];

         [box setTitle:bintitles[f]];
         [box setBorderType:NSGrooveBorder];

         [box sizeToFit]; boxFrame = [box frame];

         [box setAutoresizingMask:
                  (NSViewWidthSizable | NSViewMinYMargin)];

         if(f==TAG_A2PS){
             winRect.size.width = boxFrame.size.width;
         }
         winRect.size.height += boxFrame.size.height;
    }

    a2psf = bins[TAG_A2PS];
    gsf   = bins[TAG_GHOSTSCRIPT];
    lprf  = bins[TAG_LPR];

    contentView = 
        [[NSView alloc]
            initWithFrame:NSMakeRect(0, 0,
                                     winRect.size.width,
                                     winRect.size.height)];

    [contentView addSubview:[[a2psf superview] superview]];
    [contentView addSubview:[[gsf superview] superview]];
    [contentView addSubview:[[lprf superview] superview]];

    [a2psf setNextKeyView:gsf];
    [gsf setNextKeyView:lprf];
    [lprf setNextKeyView:a2psf];

    button = [NSButton new];
    [button setTitle:@"Defaults"];
    [button setTarget:self];
    [button setAction:@selector(setDefaults:)];
    [button setFont:[NSFont labelFontOfSize:
                                [[button font] pointSize]+2]];
    [button sizeToFit]; bwidth = [button frame].size.width;
    [button setAutoresizingMask:NSViewMaxXMargin];
    [button setFrameOrigin:
                NSMakePoint(5, 5)];

    [contentView addSubview:button];

    NSSize buttonsize = [button frame].size;

    button = [NSButton new];
    [button setTitle:@"Reread"];
    [button setTarget:self];
    [button setAction:@selector(readDefaults:)];
    [button setFont:[NSFont labelFontOfSize:
                                [[button font] pointSize]+2]];
    [button sizeToFit];
    [button setFrameSize:
                NSMakeSize(bwidth, [button frame].size.height)];
    [button setAutoresizingMask:NSViewMaxXMargin];
    [button setFrameOrigin:
                NSMakePoint(5+buttonsize.width+5, 5)];

    [contentView addSubview:button];

    [contentView addSubview:button];
    button = [NSButton new];
    [button setTitle:@"Set"];
    [button setTarget:self];
    [button setAction:@selector(setEntered:)];
    [button setFont:[NSFont labelFontOfSize:
                                [[button font] pointSize]+2]];
    [button sizeToFit];
    [button setFrameSize:
                NSMakeSize(bwidth, [button frame].size.height)];
    [button setAutoresizingMask:NSViewMinXMargin];
    [button setFrameOrigin:
                NSMakePoint(boxFrame.size.width-
                            [button frame].size.width-5, 5)];

    [contentView addSubview:button];

    buttonsize = [button frame].size;

    button = [NSButton new];
    [button setTitle:@"Cancel"];
    [button setTarget:self];
    [button setAction:@selector(cancelPreferences:)];
    [button setFont:[NSFont labelFontOfSize:
                                [[button font] pointSize]+2]];
    [button sizeToFit];
    [button setFrameSize:
                NSMakeSize(bwidth, [button frame].size.height)];
    [button setAutoresizingMask:NSViewMinXMargin];
    [button setFrameOrigin:
                NSMakePoint(boxFrame.size.width-buttonsize.width-
                            [button frame].size.width-10, 5)];


    [contentView addSubview:button];

    [self readDefaults:nil];

    prefs = [[NSPanel alloc] initWithContentRect:winRect
                             styleMask:style
                             backing:NSBackingStoreRetained
                             defer:NO];
    [prefs setMinSize:[contentView frame].size];
    [prefs setContentView:contentView];
    [prefs setReleasedWhenClosed:NO];
    [prefs setTitle:@"Preferences"];

    return self;
}

- runPreferencesPanel:(id)sender
{
    [prefs makeKeyAndOrderFront:self];
    [[NSApplication sharedApplication]
        runModalForWindow:prefs];
    return self;
}

- cancelPreferences:(id)sender
{
    // NSLog(@"preferences cancelled ...");

    [[NSApplication sharedApplication]
        stopModal];
    [prefs orderOut:self];

    [self readDefaults:nil];

    return self;
}

#define PROGWIDTH 300

- makeProgPanel
{
    NSRect contentFrame, stopperFrame, knobFrame;
    int style =  NSTitledWindowMask | NSClosableWindowMask;
    NSButton *progStopper;
    NSView *cview;

    progStopper = [NSButton new];
    [progStopper setTitle:@"Stop"];
    [progStopper setTarget:self];
    [progStopper setAction:@selector(progStop:)];
    [progStopper sizeToFit];
    stopperFrame = [progStopper frame];
    [progStopper 
        setFrameOrigin:
            NSMakePoint((PROGWIDTH-stopperFrame.size.width)/2, 5)];

    knobFrame.origin = NSMakePoint(0, 5+stopperFrame.size.height+5);
    knobFrame.size   = NSMakeSize(PROGWIDTH, 25);
    progInd = [[KnobView alloc] initWithFrame:knobFrame];
    [progInd setPercent:0.0];

    contentFrame = 
        NSMakeRect(0, 0, PROGWIDTH,
                   5+stopperFrame.size.height+5+knobFrame.size.height);
    progPanel = [[NSPanel alloc]
                    initWithContentRect:contentFrame
                    styleMask:style
                    backing:NSBackingStoreBuffered
                    defer:NO];
    [progPanel setReleasedWhenClosed:NO];

    cview = [progPanel contentView];
    [cview addSubview:progStopper];
    [cview addSubview:progInd];

    return self;
}

- progStop:(id)sender
{
    interrupted = YES;
    return self;
}

- browseBinary:(id)sender
{
  NSOpenPanel *openPanel;
  NSString    *file;
  int result, tag = [sender tag];
  id fields[TAG_BIN_ALL] = { 
      a2psf, gsf, lprf
  };

  openPanel = [NSOpenPanel openPanel];
  
  [openPanel 
      setTitle:[NSString 
                   stringWithFormat:
                       @"Choose Command for %@", bintitles[tag]]];
  [openPanel setAllowsMultipleSelection:NO];
  [openPanel setPrompt: 
                 [NSString stringWithFormat:@"%@ Command:", 
                           bintitles[tag]]];
  [openPanel setCanChooseDirectories:NO];

  file = [fields[tag] stringValue];

  result = 
      [openPanel 
          runModalForDirectory:[file stringByDeletingLastPathComponent]
          file:[file lastPathComponent] types:nil];

  if(result==NSOKButton){
      [fields[tag] setStringValue:[openPanel filename]];
  }

  return self;
}

- (BOOL)checkBinary:(NSString *)name title:(NSString *)tstr
{
    NSFileManager *fm = [NSFileManager defaultManager];
    BOOL isDir;
    NSString *alert = nil;

    if([fm fileExistsAtPath:name isDirectory:&isDir]==YES){
        if(isDir==YES){
            alert = 
                [NSString stringWithFormat:
                              @"%@ binary %@ is a directory", tstr, name];
        }
        else if([fm isExecutableFileAtPath:name]==NO){
            alert = 
                [NSString stringWithFormat:
                              @"%@ binary %@ is not executable", tstr, name];
        }
    }
    else{
        alert = 
            [NSString stringWithFormat:
                          @"%@ binary %@ does not exist", tstr, name];
    }

    if(alert!=nil){
        NSRunAlertPanel(@"Error", alert, @"OK", NULL, NULL);
        return NO;
    }

    return YES;
}


- checkFields
{
    NSMutableArray *lprCmd = [self lprCommand];

    if([self checkBinary:[a2psf stringValue]
             title:@"a2ps"]==NO){
        return nil;
    }

    if([self checkBinary:[gsf stringValue]
             title:@"ghostscript"]==NO){
        return nil;
    }

    if([lprCmd count]>=1 &&
       [self checkBinary:[lprCmd objectAtIndex:0]
             title:@"lpr"]==NO){
        return nil;
    }

    return self;
}

- readDefaults:(id)sender
{
    NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];

    [a2psf setStringValue:
                 [ud stringForKey:@"a2ps"]];
    [gsf setStringValue:
             [ud stringForKey:@"gs"]];
    [lprf setStringValue:
              [ud stringForKey:@"lpr"]];

    return self;
}

- setDefaults:(id)sender
{
    [a2psf setStringValue:@"/usr/bin/a2ps"];
    [gsf setStringValue:@"/usr/bin/gs"];
    [lprf setStringValue:@"/usr/bin/lpr -P someprinter"];

    return self;
}



- setEntered:(id)sender
{
    NSString *lprCmd;
    NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];

    if([self checkFields]==nil){
        return self;
    }

    lprCmd = [[self lprCommand] componentsJoinedByString:@" "];
    [lprf setStringValue:lprCmd];

    [ud setObject:[a2psf stringValue] forKey:@"a2ps"];
    [ud setObject:[gsf stringValue] forKey:@"gs"];
    [ud setObject:lprCmd forKey:@"lpr"];

    [ud synchronize];

    [[NSApplication sharedApplication]
        stopModal];
    [prefs orderOut:self];

    return self;
}

- (NSString *)a2psStr
{
    return [a2psf stringValue];
}

- (NSString *)gsStr
{
    return [gsf stringValue];
}

- (NSString *)lprStr
{
    return [lprf stringValue];
}

- (NSMutableArray *)lprCommand
{
    NSMutableArray *parts = [NSMutableArray arrayWithCapacity:1];
    NSString *lprStr = [lprf stringValue];
    const char *prev = [lprStr cString], *current;
    
    if([lprStr length]){
        while(*prev && isspace(*prev)){
            prev++;
        }
        current = prev;
        while(*current){
            prev = current;
            while(*current && !isspace(*current)){
                current++;
            }
            [parts addObject:
                       [NSString stringWithCString:prev
                                 length:(current-prev)]];
            while(*current && isspace(*current)){
                current++;
            }
        }
    }

    return parts;
}

- (NSMutableArray *)getA2PSArgs
{
    NSEnumerator *itemEn =
        [[optChooser itemArray] objectEnumerator];
    NSMenuItem *item;
    NSMutableArray *args = [NSMutableArray arrayWithCapacity:1];

    while((item=[itemEn nextObject])!=nil){
        Option *opt = [item representedObject];
        if([opt optEnabled]==YES && [opt usedByA2PS]==YES){
            [args addObjectsFromArray:[opt optionsForA2PS]];
        }
    }

    return args;
}

- (NSMutableArray *)getGSArgs
{
    NSEnumerator *argEn, *itemEn =
        [[optChooser itemArray] objectEnumerator];
    NSMenuItem *item;
    NSMutableArray *args = 
        [NSMutableArray arrayWithObjects:@"-dNOPAUSE", @"-q", nil];
    BOOL device = NO;
    id arg;

    while((item=[itemEn nextObject])!=nil){
        Option *opt = [item representedObject];
        if([opt optEnabled]==YES && [opt usedByGS]==YES){
            [args addObjectsFromArray:[opt optionsForGS]];
        }
    }

    argEn = [args objectEnumerator];
    while((arg=[argEn nextObject])!=nil){
        if([arg hasPrefix:@"-sDEVICE="]==YES){
            if([arg rangeOfString:@"tiff"].location!=NSNotFound){
                [self setImgFmt:[NSString stringWithString:@"tiff"]];
            }
            else{
                [self setImgFmt:[NSString stringWithString:@"png"]];
            }

            device = YES;
            break;
        }
    }

    if(device==NO){
        [args addObject:@"-sDEVICE=pngalpha"];
        [self setImgFmt:[NSString stringWithString:@"png"]];
    }

    return args;
}

- openFile:(id)sender
{
    NSOpenPanel *openPanel = [NSOpenPanel openPanel];
    OPEN_TYPE what = [sender tag];
    NSArray *types;
    NSString *title;
    Class pclass;

    if(what==OPEN_TEXT){
        types = nil;
        title = @"Process with A2PS";
        pclass = [TextPreview class];
    }
    else if(what==OPEN_IMAGE){
        types = [NSArray arrayWithArray:imageTypes];
        title = @"Preview Image";
        pclass = [ImagePreview class];	
    }
    else{
        types = [NSArray arrayWithArray:psTypes];
        title = @"Preview PostScript/PDF";
        pclass = [PostScriptOrPDFPreview class];
    }

    if([self checkFields]==nil){
        [self runPreferencesPanel:nil];
        return self;
    }
    
    [openPanel setTitle:title];
    [openPanel setAllowsMultipleSelection:NO];
    [openPanel setPrompt:@"File:"];
    [openPanel setCanChooseDirectories:NO];

    if([openPanel runModalForTypes:types]==NSOKButton){
	id preview =
	    [[pclass alloc] 
		initWithController:self 
		andPath:[openPanel filename]
		resolution:-1];
	if(what==OPEN_IMAGE){
	    [preview setImgFmt:[[preview path] pathExtension]];
	}
    }

    return self;
}

- saveTIFFImageFromPB:(id)sender
{
    NSPasteboard *pb = [NSPasteboard generalPasteboard];
    NSString *bestType = 
        [pb availableTypeFromArray:
                [NSArray arrayWithObject:NSTIFFPboardType]];

    if(bestType==nil){
        NSRunAlertPanel(@"Alert", 
                        @"Couldn't initialize image from pasteboard.",
                        @"Ok", nil, nil);
        return self;
    }

    NSImage *pbImage = [[NSImage alloc] initWithPasteboard:pb];
    AUTORELEASE(pbImage);

    NSSavePanel *savePanel = [NSSavePanel savePanel];
    [savePanel setRequiredFileType:@"tiff"];

    if([savePanel runModal] == NSOKButton){
        NSString *fname = [savePanel filename];
        NSData *data = [pbImage TIFFRepresentation];

        if([data writeToFile:fname atomically:YES]==NO){
            NSString *msg 
                = [NSString 
                      stringWithFormat:@"Couldn't write %@: %s",
                      fname, strerror(errno)];
            NSRunAlertPanel(@"Alert", msg, @"Ok", nil, nil);
        }
    }

    [[NSCursor arrowCursor] set];
    return self;
}


- (NSString *)tempNameExt:(NSString *)pathExt
{
    NSProcessInfo *pinfo = [NSProcessInfo processInfo];
    NSFileManager *fm = [NSFileManager defaultManager];
    NSString *name, *temp = NSTemporaryDirectory();

    do {
        NSString *uniq = [pinfo globallyUniqueString];
        if(pathExt==nil){
            name = [temp stringByAppendingPathComponent:uniq];
        }
        else{
            name = 
                [[temp stringByAppendingPathComponent:uniq]
                    stringByAppendingPathExtension:pathExt];
        }
    } while([fm fileExistsAtPath:name]==YES);

    return name;
}

#define INTERVAL      3


- (BOOL)waitForTaskToFinish:(NSTask *)aTask
                description:(NSString *)desc
{
    int ticks = 0;

    NSApplication *app = [NSApplication sharedApplication];
    NSModalSession progSession;
    BOOL pflag = NO;
    NSDate *progDate;

    int mask = NSLeftMouseDownMask | NSLeftMouseUpMask |
        NSLeftMouseDraggedMask;
    NSDate *evDate;
    NSEvent *event;

    interrupted = NO;
    progDate = [NSDate dateWithTimeIntervalSinceNow:INTERVAL];

    while([aTask isRunning]==YES){
        if(pflag==NO && 
           [progDate timeIntervalSinceNow]<0.0){
            [progPanel setTitle:desc];

            progSession = [app beginModalSessionForWindow:progPanel];
            pflag = YES;
            [[NSCursor arrowCursor] push];
        }
        else if(pflag==YES){
            ticks = (ticks+1)%200;
            [progInd 
                setPercent:(float)(ticks<100 ? ticks : 199-ticks)];
            [progInd display];
            [progPanel flushWindow];

            evDate = [NSDate dateWithTimeIntervalSinceNow:0.025];
            event  = [app nextEventMatchingMask:mask 
                          untilDate:evDate
                          inMode:NSDefaultRunLoopMode
                          dequeue:YES];
            if([event window]==progPanel){
                [app sendEvent:event];
            }
            if(interrupted==YES){
                break;
            }
        }
        [NSThread 
            sleepUntilDate:
                [NSDate dateWithTimeIntervalSinceNow:0.025]];
    }

    if(pflag==YES){
        [app endModalSession:progSession];
        [progPanel close];
        [NSCursor pop];
    }

    return interrupted;
}


- (NSArray *)runCommand:(NSString *)cmd arguments:(NSArray *)args
{
    NSPipe *pipe;
    NSFileHandle *reader;
    NSMutableData *data = [NSMutableData dataWithCapacity:1024];
    NSTask *cmdTask = [NSTask new];
    
    NSArray *result;
    unsigned length;
    const char *bytes;

    NSMutableDictionary *edict
        = [NSMutableDictionary 
              dictionaryWithDictionary:
                  [[NSProcessInfo processInfo] environment]];
    [edict setObject:@"C" forKey:@"LANG"];

    NSLog(@"%@ %@", cmd, args);

    pipe = [NSPipe pipe];
    reader = [pipe fileHandleForReading];
    
    [cmdTask setLaunchPath:cmd];
    [cmdTask setArguments:args];
    [cmdTask setStandardOutput:pipe];
    [cmdTask setEnvironment:edict];

    NS_DURING
    [cmdTask launch];
    while([cmdTask isRunning]==YES){
        [data appendData:[reader availableData]];
    }

    [data appendData:[reader readDataToEndOfFile]];
    NS_HANDLER
    NSString *exMsg = 
        [NSString stringWithFormat:@"EXCEPTION: %@ %@",
                  [localException name],
                  [localException reason]];
    const char *exBytes = [exMsg cString];
    data = [NSData dataWithBytes:exBytes length:strlen(exBytes)];
    NS_ENDHANDLER
    
    length = [data length];
    bytes  = [data bytes];

    if(length && bytes[length-1]=='\n'){
        length--;
    }

    result = [[NSString stringWithCString:bytes length:length] 
                 componentsSeparatedByString:@"\n"];
    return result;
}


- (void)windowWillClose:(NSNotification *)aNotification
{
    id win = [aNotification object];

    if([win isKindOfClass:[Preview class]]==YES){
        [(Preview *)win cleanupPreview];
    }
}

- (void)windowDidResize:(NSNotification *)aNotification
{
    id win = [aNotification object];

    if([win isKindOfClass:[Preview class]]==YES){
        [win saveFrameUsingName:[(Preview *)win path]];
    }
}

- (void)windowDidMove:(NSNotification *)aNotification
{
    id win = [aNotification object];

    if([win isKindOfClass:[Preview class]]==YES){
        [win saveFrameUsingName:[(Preview *)win path]];
    }
}

@end
