#import #import #import #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=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