|
TOC | PREV | NEXT
//
// GifController.m (c) 1998 Andrew C Stone
// andrew@stone.com (505) 345-4800 http://www.stone.com - Create(TM)
//
// THE BRAINS BEHIND GIFfun!
// Permission is granted to use this code, but please retain credit
// And as always, CAVEAT EMPTOR!
// formatted with tabs for instant web page making, make your window wide!
#import "GifController.h"
#import "GIFfun.h"
#import "GifInfo.h"
#import "GifWell.h"
#import "Preferences.subproj/GIFPrefs.h"
#import "Info.subproj/InfoAndHelp.h"
#import "TIFFtoGIF.subproj/GimmeGIF.h"
#import "SDTask.subproj/SDLogTask.h"
//
// If you code PASCAL style (top down), then you'll
// need to prototype methods which are defined
// later in the source file after a previous usage
//
@interface GifController(ShushMyLittlePrettyCompiler)
- (void)loadDefaults;
- (void)tryAndDoEverything:(NSString *)path;
- (void)setLookAndFeelOfTableView;
- (void)readSettingsFromDict:(NSDictionary *)dict;
@end
@implementation GifController
//
// Application Delegate methods
//
- (void)applicationWillFinishLaunching:(NSNotification *)notification
{
// we are the recipient of the service calls outlined in CustomInfo.plist:
[[notification object] setServicesProvider:self];
// force an +initialize and thus registry...
[GIFPrefs sharedDefaults];
// find the executable in our Resouces directory:
{
NSString *path;
#ifdef WIN32
NSString *ext = @"exe";
#else
NSString *ext = @"";
#endif
if ((path = [[NSBundle mainBundle] pathForResource:@"WhirlGif" ofType:ext]))
whirlgif = [[NSString alloc]initWithString:path];
else NSLog(@"Cannot find WhirlGif executable...");
}
}
//
// We make sure that we are initialized, even if launched from services or double-click
//
- (void)doInitThingsOnce
{
static BOOL first = YES;
if (first) {
first = NO;
// let's restore the user's previous state:
[self loadDefaults];
[window setFrameUsingName:NSStringFromClass([self class])];
[window setFrameAutosaveName:NSStringFromClass([self class])];
[self setLookAndFeelOfTableView];
}
}
//
// This method is all we need to implement to open files by double-clicking:
//
- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename;
{
// in case we don't finish launching!
[self doInitThingsOnce];
[self tryAndDoEverything:filename];
return YES;
}
- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
[self doInitThingsOnce];
}
//
// User Preferences
// deal with users preferences by remembering whatever they had before
// At "Create A GIF time" we save the current state
//
- (void)loadDefaults
{
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
[colorIndexField setStringValue:[ud stringForKey:DEF_TRANS]];
[disposalMatrix selectCellWithTag:[ud integerForKey:DEF_DISPOSAL]];
[loopField setIntValue:[ud integerForKey:DEF_LOOPS]];
[loopMatrix selectCellWithTag:[ud integerForKey:DEF_LOOP_METHOD]];
}
//
// Factoring the code into small chunks which all methods
// pass through the same call stack:
//
//
// Is a file possible to be read as an image?
//
- (BOOL)isImageFile:(NSString *)path
{
NSArray *types = [NSImage imageFileTypes];
int i = [types count];
while (i--) if ([[path pathExtension] caseInsensitiveCompare:[types objectAtIndex:i]] == NSOrderedSame) return YES;
return NO;
}
- (BOOL)isGifFile:(NSString *)path
{
BOOL isDir;
return ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir] && !isDir && (
([[path pathExtension] caseInsensitiveCompare:@"gif"] == NSOrderedSame)
#if !defined(OS_API)
|| [self isImageFile:path]
#endif
));
}
- (int)tryAndLoadPath:(NSString *)path
{
if ([self isGifFile:path]) return [self addGifFile:path];
else return [self loadGifsInDirectory:path];
}
- (void)tryAndDoEverything:(NSString *)path
{
if ([self tryAndLoadPath:path]) [self mergeGifs:self];
}
// Service methods: (see CustomInfo.plist)
- (void)loadAFileOrDirectory:(NSPasteboard *)pboard userData:(NSString *)ud
error:(NSString **)msg
{
NSArray *fileNames;
int i, count;
fileNames = [pboard propertyListForType:NSFilenamesPboardType];
count = [fileNames count];
// if just one FILE, we'll add it to the list:
if (count == 1) {
[self tryAndDoEverything:[fileNames objectAtIndex:0]];
} else {
for (i = 0; i < [fileNames count]; i++) {
NSString *n = [fileNames objectAtIndex:i];
if ([self isGifFile:n]) [self addGifFile:n];
}
[self mergeGifs:self];
}
}
//
// Temporary Directory stuff: useful code.
//
BOOL directoryOK(NSString *path)
{
BOOL isDirectory;
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:path isDirectory:&isDirectory] || !isDirectory) {
NSDictionary *dict = [NSDictionary dictionaryWithObject:
[NSNumber numberWithUnsignedLong:0777]
forKey:NSFilePosixPermissions];
if (![fileManager createDirectoryAtPath:path attributes:dict])
return NO;
}
return YES;
}
NSString * existingPath(NSString *path)
{
while (path && ![path isEqualToString:@""]
&& ![[NSFileManager defaultManager] fileExistsAtPath:path])
path = [path stringByDeletingLastPathComponent];
return path;
}
NSArray *directoriesToAdd(NSString *path, NSString *existing)
{
NSMutableArray *a = [NSMutableArray arrayWithCapacity:4];
if (path != nil && existing != nil) {
while (![path isEqualToString:existing]) {
[a insertObject:[path lastPathComponent] atIndex:0];
path = [path stringByDeletingLastPathComponent];
}
}
return a;
}
// this will go up the path until it finds an existing directory
// and will add each subpath and return YES if succeeds, NO if fails:
- (BOOL)createWritableDirectory:(NSString *)path
{
BOOL isDirectory;
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:path isDirectory:&isDirectory]
&& isDirectory && [fileManager isWritableFileAtPath:path])
return YES; // no work to do
else {
NSString *existing = existingPath(path);
NSArray *dirsToAdd = directoriesToAdd(path,existing);
int i;
BOOL good = YES;
for (i = 0; i < [dirsToAdd count]; i++) {
existing = [existing stringByAppendingPathComponent:
[dirsToAdd objectAtIndex:i]];
if (!directoryOK(existing)) {
good = NO;
break;
}
}
return good;
}
}
#define GIF_FILE_NAME @"GF.gif"
- (NSString *)nextUniqueNameUsing:(NSString *)templatier
{
static int unique = 1;
NSString *tempName = nil;
do {
tempName =[NSString stringWithFormat:@"%@_%d.%@",
[templatier stringByDeletingPathExtension],unique++,
[templatier pathExtension]];
} while ([[NSFileManager defaultManager] fileExistsAtPath:tempName]);
return tempName;
}
- (NSString *)temporaryDirectory
{
NSString *tempDir =[[NSTemporaryDirectory()
stringByAppendingPathComponent:
[[NSProcessInfo processInfo] processName]]
stringByAppendingPathComponent:NSUserName()];
if (! [self createWritableDirectory:tempDir]) {
NSLog(@"Couldn't create %@, using %@",tempDir,
NSTemporaryDirectory());
tempDir = NSTemporaryDirectory();
}
return tempDir;
}
- (NSString *)scratchFolder
{
NSString *s = [[NSUserDefaults standardUserDefaults]stringForKey:DEF_DIRECTORY];
if (!s || [s isEqualToString:@""] ||
![self createWritableDirectory:s])
s = [self temporaryDirectory];
return s;
}
// if the file package has 00, 01, 02 endings, then we should sort accordingly:
- (NSArray *)sortFiles:(NSArray *)dp
{
return [dp sortedArrayUsingSelector:@selector(compare:)];
}
- (int)loadGifsInDirectory:(NSString *)directory;
{
int i;
int count;
NSArray *dp;
NSString *shortname;
NSString *path;
NSDictionary *dict;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *settingsPath = [directory
stringByAppendingPathComponent:GIFFUN_SETTINGS_FILE];
// start from scratch:
[self clearGifs:self];
dp = [fileManager directoryContentsAtPath:directory];
dp = [self sortFiles:dp];
if ((count = [dp count])>0) {
for (i = 0; i < count; i++) {
shortname = [dp objectAtIndex:i];
path = [directory stringByAppendingPathComponent:shortname];
if ([self isGifFile:path]) {
[self addGifFile:path];
}
}
}
//
// Now, if we were the ones who saved these GIFS
// then we'll find a file which has our settings in dictionary form
// We'll try and load it, but we don't care if the dict is not there..
//
if ([[NSFileManager defaultManager] isReadableFileAtPath:settingsPath]
&& ((dict = [NSDictionary dictionaryWithContentsOfFile:settingsPath]) != nil)) {
[self readSettingsFromDict:dict];
}
return [gifFileArray count];
}
- (int)addGifFile:(NSString *)aFile
{
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
// On OpenStep (ie OS_API defined) this will never be the case:
BOOL needsConversion = ([[aFile pathExtension] caseInsensitiveCompare:@"gif"] != NSOrderedSame);
if (needsConversion) {
// /somewhere/something.tiff -> /tmp/GIFfun/andrew/something.gif
NSString *gifFile = [[self temporaryDirectory]
stringByAppendingPathComponent:
[[[aFile lastPathComponent]stringByDeletingPathExtension]
stringByAppendingPathExtension:@"gif"]];
if ([[GimmeGIF sharedInstance]
convertImageFile:aFile toGIFFile:gifFile])
aFile = [[gifFile copy]autorelease];
else return 0;
}
if (!gifFileArray)
gifFileArray = [[NSMutableArray alloc]initWithCapacity:1];
[gifFileArray addObject:
[[GifInfo alloc]initWithPath:aFile
andDelay:[ud integerForKey:DEF_DELAY]]];
[tableView reloadData];
[tableView scrollRowToVisible:[tableView numberOfRows]-1];
// status field info:
[theDirectory release];
theDirectory = [[[aFile stringByDeletingLastPathComponent]stringByDeletingPathExtension] copy];
[statusField setStringValue:
[NSString stringWithFormat:@"%@: %@",DIRECTORY_KEY, theDirectory]];
return 1;
}
#define HTML_CODE @"<HTML><HEAD><title>%@</title></HEAD><BODY BGCOLOR=\"#eeeeff\"><h2>%@</h2><HR><IMG SRC=\"%@\"></BODY></HTML>"
- (void)createAndOpenHTML:(NSString *)outFile
{
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
NSString *htmlFile = [[outFile stringByDeletingPathExtension]stringByAppendingPathExtension:@"html"];
NSString *s = [NSString stringWithFormat:HTML_CODE,outFile,outFile,[outFile lastPathComponent]];
[[NSFileManager defaultManager] removeFileAtPath:htmlFile handler:nil]; // get rid of old
if ([s writeToFile:htmlFile atomically:NO] && [ud boolForKey:DEF_USE_BROWSER])
[[NSWorkspace sharedWorkspace] openFile:htmlFile];
}
//
// A more complicated, interrelated set of UI items: a Text field & a radio matrix
// Therefore, we must talk to these through a higher level abstraction:
//
- (int)loopKeyValue
{
switch ( [loopMatrix selectedTag]) {
case LOOP_NONE: return 0;
case LOOP_SPECIFIED: return [loopField intValue];
case LOOP_FOREVER:
default:
return -1;
}
}
- (void)setLoopFromKeyValue:(int)value
{
switch ( value) {
case 0: [loopMatrix selectCellWithTag:LOOP_NONE]; break;
case -1: [loopMatrix selectCellWithTag:LOOP_FOREVER]; break;
default:
[loopMatrix selectCellWithTag:LOOP_SPECIFIED];
[loopField setIntValue:value];
}
}
// *************
- (NSArray *)getLoopArray
{
switch ( [loopMatrix selectedTag]) {
case LOOP_NONE:
return nil;
case LOOP_SPECIFIED:
return [NSArray arrayWithObjects:@"-loop",
[NSString stringWithFormat:@"%d",[loopField intValue]],nil];
case LOOP_FOREVER:
default:
return [NSArray arrayWithObject:@"-loop"];
}
return nil;
}
//
// IB METHODS:
//
- (NSArray *)getFileNames
{
int count = [gifFileArray count];
NSMutableArray *a = [NSMutableArray arrayWithCapacity:count*2];
int i;
[a addObject:[[gifFileArray objectAtIndex:0]path]];
for (i = 1; i<[gifFileArray count]; i++) {
GifInfo *gi = [gifFileArray objectAtIndex:i];
[a addObject:[NSString stringWithFormat:@"-time %d", [gi delay]]];
[a addObject:[gi path]];
}
return a;
}
- (void)mergeGifs:(id)sender
{
int count = [gifFileArray count];
if (count > 0 ) {
NSString *baseName = (theDirectory && ![theDirectory isEqualToString:@""])?
[theDirectory lastPathComponent] : @"GIFfun";
NSString *disp = [[disposalMatrix selectedCell]title];
NSArray *loop = [self getLoopArray];
NSString *colorIndexString = [colorIndexField stringValue];
NSArray *fileNames = [self getFileNames];
NSMutableArray *args = [NSMutableArray arrayWithCapacity:count*2];
NSString *outFile;
// get our outputFile:
[outputFile autorelease];
outputFile = [[self nextUniqueNameUsing:[[self scratchFolder] stringByAppendingPathComponent:[baseName stringByAppendingPathExtension:@"gif"]]]retain];
outFile = [outputFile lastPathComponent];
// get rid of old file if it exists:
[[NSFileManager defaultManager] removeFileAtPath:outputFile handler:nil];
/// grab the options and arguments:
if (colorIndexString && ![colorIndexString isEqualToString:@""])
[args addObject:[NSString stringWithFormat:@"-trans %d",[colorIndexField intValue]]];
[args addObject:[NSString stringWithFormat:@"-time %d",[[gifFileArray objectAtIndex:0]delay]]];
[args addObject:@"-o"];
[args addObject:outFile];
if (loop) [args addObjectsFromArray:loop];
[args addObject:@"-disp"];
[args addObject:disp];
[args addObjectsFromArray:fileNames];
// Here's my groovy Task wrapper which logs errors:
[[SDLogTask alloc]initAndLaunchWithArgs:args executable:whirlgif
directory:[outputFile stringByDeletingLastPathComponent]
logToText:logText includeStandardOutput:NO owner:self];
} else {
NSBeep();
[statusField setStringValue:DROP_ON_KEY];
}
}
//
// Here is the callback from SDLogText
//
- (void)taskTerminated:(BOOL)success
{
if(success) {
[ouputDragWell setFile:outputFile];
[statusField setStringValue:[NSString stringWithFormat:@"%@: %@",FINISHED_MSG,outputFile]];
[self createAndOpenHTML:outputFile];
} else [statusField setStringValue:JOB_FAILED];
}
///*********
- (void)openGifFolder:(id)sender
{
// run open panel to allow selection of a directory
id openpanel = [NSOpenPanel openPanel];
[openpanel setTitle:@"Open Directory of GIFS"];
[openpanel setAllowsMultipleSelection:NO];
[openpanel setCanChooseDirectories:YES];
if ([openpanel runModal] == NSOKButton) {
[self loadGifsInDirectory:[openpanel filename]];
}
}
- (void)addAnotherFile:(id)sender
{
NSArray *fileTypes;
id openpanel = [NSOpenPanel openPanel];
#if defined(OS_API)
fileTypes = [NSArray arrayWithObjects:@"gif",@"GIF",nil];
[openpanel setTitle:@"Add GIF Files..."];
#else
fileTypes = [NSImage imageFileTypes];
[openpanel setTitle:@"Add Image Files..."];
#endif
[openpanel setAllowsMultipleSelection:YES];
if ([openpanel runModalForTypes:fileTypes]) {
int index = 0;
NSString *directory = (NSString *)[openpanel directory] ;
NSArray *files = [openpanel filenames];
int numFiles = [files count];
while (index < numFiles) {
/* add file to menu */
NSString *fullName = [directory stringByAppendingPathComponent:[files objectAtIndex:index]];
if ([self isGifFile:fullName]) [self addGifFile:fullName];
index++;
}
}
}
- (void)clearGifs:(id)sender;
{
[gifFileArray autorelease];
gifFileArray = [[NSMutableArray alloc]initWithCapacity:1];
[statusField setStringValue:@""];
[ouputDragWell setFile:nil];
[tableView reloadData];
}
- (void)changeLoop:(id)sender;
{
if (sender == loopField) {
[loopMatrix selectCellWithTag:LOOP_SPECIFIED];
} else if (sender == loopMatrix) { // it's the matrix
[loopField setEnabled: ([sender selectedTag] == LOOP_SPECIFIED)];
if ([sender selectedTag] == LOOP_SPECIFIED) [loopField selectText:self];
}
}
//
// saving
//
// these are the NSFileManger callbacks:
+ (BOOL)fileManager:(NSFileManager *)manager
shouldProceedAfterError:(NSDictionary *)errorDict
{
int result;
result = NSRunAlertPanel(@"GIFfun",
@"File operation error: %@ with file: %@",@"Proceed", @"Stop", NULL,
[errorDict objectForKey:@"Error"],
[errorDict objectForKey:@"Path"]);
if (result == NSAlertDefaultReturn)
return YES;
else
return NO;
}
+ (void)fileManager:(NSFileManager *)fm willProcessPath:(NSString *)path
{
}
//
// Save the animated GIF somewhere other than in /tmp!
//
- (void)saveAs:(id)sender;
{
if (outputFile && ![outputFile isEqual:@""]) {
static NSSavePanel *sp = nil;
if (!sp) {
sp = [[NSSavePanel savePanel]retain];
[sp setTitle:SAVE_AS];
[sp setDirectory:NSHomeDirectory()];
}
if ([sp runModalForDirectory:nil file:[outputFile lastPathComponent]]) {
[[NSFileManager defaultManager] copyPath:outputFile
toPath:[sp filename] handler:[self class]];
}
} else NSBeep();
}
//
// Save the collected GIFs and settings in a easy-to-read way
//
// define some keys which could be defined in headers for others...
#define NUM_OTHER_DICT_ITEMS 6
#define DICT_SIZE (2* [gifFileArray count] + NUM_OTHER_DICT_ITEMS)
#define KEY_LOOP @"Number_Of_Loops"
#define KEY_DISPOSAL @"Disposal_Method"
#define KEY_TRANSPARENT @"Transparent_Index"
- (NSString *)pathKeyForGifInfo:(GifInfo *)gi
{
// this is too strict and not very general
// for example if we do a similar animation, but different root
// name, then are settings won't work!
// instead, we'll optimize for Create's numbering system
// and basically, write the numeric identifier of the frame
// and we'll overlook the actual path
// return [[gi path] lastPathComponent];
NSString *path = [[gi path] lastPathComponent];
// get rid of "gif":
NSString *justNum = [path stringByDeletingPathExtension];
// now grab number: ie: 00 or 01
NSString *numIdentifier = [justNum pathExtension];
if (!numIdentifier || [numIdentifier isEqualToString:@""]) return path;
return numIdentifier;
}
- (NSString *)ordinalKeyForGifInfo:(GifInfo *)gi
{
return [NSString stringWithFormat:@"%@-order",[self pathKeyForGifInfo:gi]];
}
- (void)reorderGifArrayWithDict:(NSDictionary *)dict
{
int i,j , cnt = [gifFileArray count];
NSMutableArray *newList = [[NSMutableArray allocWithZone:[self zone]] initWithCapacity:cnt];
for (i = 0; i < cnt; i++) {
for (j = 0; j < cnt; j++) {
GifInfo *gi = [gifFileArray objectAtIndex:j];
id number;
if ((number = [dict objectForKey:
[self ordinalKeyForGifInfo:gi]]) != nil) {
if ([number intValue] == i) {
[newList addObject:gi];
break;
}
}
}
}
if ([newList count] == cnt) {
[gifFileArray autorelease];
gifFileArray = newList;
[tableView reloadData];
} else [newList autorelease];
}
- (void)writeSettingsToDict:(NSMutableDictionary *)dict
{
int i;
// write the delays between frames:
// and the position of the GIF in the animation
// Note that we only write out strings, arrays, data or dicts in order that
// the data can be serialized...
for (i = 0; i < [gifFileArray count]; i++) {
GifInfo *gi = [gifFileArray objectAtIndex:i];
[dict setObject:[NSString stringWithFormat:@"%d",
[gi delay]] forKey:[self pathKeyForGifInfo:gi]];
[dict setObject:[NSString stringWithFormat:@"%d", i]
forKey:[self ordinalKeyForGifInfo:gi]];
}
[dict setObject:[NSString stringWithFormat:@"%d",[self loopKeyValue]]
forKey:KEY_LOOP];
[dict setObject:[NSString stringWithFormat:@"%d",
[disposalMatrix selectedTag]] forKey:KEY_DISPOSAL];
[dict setObject:[colorIndexField stringValue]
forKey:KEY_TRANSPARENT];
}
- (void)readSettingsFromDict:(NSDictionary *)dict
{
int i;
BOOL needToReorder = NO;
// Are the GIFS in the correct order?
// Each GIFs ordinality key should match its position...
// but we are lazy, and we'll do this only if necessary
// read the delays between frames:
for (i = 0; i < [gifFileArray count]; i++) {
GifInfo *gi = [gifFileArray objectAtIndex:i];
id number;
if ((number = [dict objectForKey:
[self pathKeyForGifInfo:gi]]) != nil)
[gi setDelay:[number intValue]];
if ((number = [dict objectForKey:
[self ordinalKeyForGifInfo:gi]]) != nil)
if ([number intValue] != i) needToReorder = YES;
}
[self setLoopFromKeyValue:[[dict objectForKey:KEY_LOOP]intValue]];
[disposalMatrix selectCellWithTag:[[dict objectForKey:KEY_DISPOSAL]intValue]];
[colorIndexField setStringValue:[dict objectForKey:KEY_TRANSPARENT]];
// OK - do the reordering work if you have to...
if (needToReorder) [self reorderGifArrayWithDict:dict];
}
//
// save settings
//
- (BOOL)saveDictToFile:(NSString *)file
{
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:DICT_SIZE];
// write a dictionary containing our settings to this directory
[self writeSettingsToDict:dict];
return [dict writeToFile:file atomically:NO];
}
- (void)saveSettings:(id)sender;
{
if ([gifFileArray count]) {
static NSSavePanel *sp = nil;
NSString *dir = [[[gifFileArray objectAtIndex:0] path]
stringByDeletingLastPathComponent];
if (!sp) {
// retain it, or you'll crash on the second invoke!
sp = [[NSSavePanel savePanel]retain];
[sp setTitle:SAVE_GIF_SETTINGS];
[sp setDirectory:NSHomeDirectory()];
[sp setRequiredFileType:GIFFUN_SETTINGS_FILETYPE];
}
// we'll use the last directory selected
// but we'll use the basis of the file name to be
// the original name that was loaded:
if ([sp runModalForDirectory:dir file:GIFFUN_SETTINGS_FILE]) {
[self saveDictToFile:[sp filename]];
}
}
}
- (BOOL)writeGIFSToPath:(NSString *)destPath
{
// Create the destination directory
if (directoryOK(destPath)) {
int i;
// for each gif info:
// copy to the destination directory
// but only if it doesn't exist already!
for (i = 0; i < [gifFileArray count]; i++) {
GifInfo *gi = [gifFileArray objectAtIndex:i];
NSString *gifName = [[gi path] lastPathComponent];
NSFileManager *fm = [NSFileManager defaultManager];
NSString *destFile = [destPath stringByAppendingPathComponent:gifName];
if (![fm fileExistsAtPath:destFile])
[fm copyPath:[gi path]
toPath:destFile
handler:[self class]];
}
return [self saveDictToFile:
[destPath stringByAppendingPathComponent:GIFFUN_SETTINGS_FILE]];
}
return NO;
}
- (void)saveGifAnimDirectory:(id)sender;
{
if ([gifFileArray count]) {
static NSSavePanel *sp = nil;
NSString *filename = [[[[gifFileArray objectAtIndex:0] path]
stringByDeletingLastPathComponent] lastPathComponent];
if (!sp) {
sp = [[NSSavePanel savePanel]retain];
[sp setTitle:SAVE_GIF_ANIMS];
[sp setDirectory:NSHomeDirectory()];
[sp setRequiredFileType:GIFFUN_FILE_TYPE];
}
// we'll use the last directory selected
// but we'll use the basis of the file name to be
// the original name that was loaded:
if ([sp runModalForDirectory:nil file:filename]) {
[self writeGIFSToPath:[sp filename]];
}
}
}
//
// Covers for other nib files actions:
//
- (void)runDefaultsPanel:(id)sender;
{
[[GIFPrefs sharedDefaults] orderFront:self];
}
- (void)loadHelp:(id)sender
{
[[InfoAndHelp sharedHelp] orderFrontHelp:self];
}
- (void)loadWhirlGifHelp:(id)sender;
{
[[InfoAndHelp sharedHelp] orderFrontWhirlHelp:self];
}
- (void)orderFrontInfo:(id)sender;
{
[[InfoAndHelp sharedHelp] orderFrontInfo:self];
}
- (void)loadSample:(id)sender;
{
NSString *path = [[NSBundle mainBundle] pathForResource:SAMPLE_GIFS ofType:GIFFUN_FILE_TYPE];
[self loadGifsInDirectory:path];
}
//
// Setting up the TableView the way we want it:
//
#define ROW_HEIGHT 48
- (void)setLookAndFeelOfTableView
{
NSImageCell *ic = [[NSImageCell allocWithZone:[self zone]] initImageCell:nil];
NSTableColumn *tc = [tableView tableColumnWithIdentifier:I_IMAGE];
// Make the rows taller so we can see our GIFS:
[tableView setRowHeight:ROW_HEIGHT];
// We want the GIF not to be stretched, and we set the TableColumn's cell to our new ImageCell:
[ic setImageScaling:NSScaleProportionally]; // or NSScaleToFit
[tc setDataCell:ic];
// the Delay field is the only editable one
// We'll show that with a bigger font and a bezeled field:
tc = [tableView tableColumnWithIdentifier:I_DELAY];
[[tc dataCell] setFont:[NSFont userFontOfSize:18.]];
[[tc dataCell] setBezeled:YES];
}
//
// TableView delegate methods:
//
- (int)numberOfRowsInTableView:(NSTableView *)tv
{
return [gifFileArray count];
}
- (NSString *)uiPath:(GifInfo *)gi
{
return [[[gi path]lastPathComponent]stringByDeletingPathExtension];
}
- tableView:(NSTableView *)tv
objectValueForTableColumn:(NSTableColumn *)tc
row:(int)row
{
NSString *ident = [tc identifier];
if (tv == tableView) {
if ([ident isEqual:I_NUMBER]) return [NSNumber numberWithInt:row + 1];
else if ([ident isEqual:I_IMAGE])
return [[gifFileArray objectAtIndex:row]image];
else if ([ident isEqual:I_PATH])
return [self uiPath:[gifFileArray objectAtIndex:row]];
else if ([ident isEqual:I_DELAY])
return [NSNumber numberWithInt:[[gifFileArray objectAtIndex:row]delay]];
}
return @"";
}
- (void)tableView:(NSTableView *)tv setObjectValue:(id)object forTableColumn:(NSTableColumn *)tc row:(int)row;
{
NSString *ident = [tc identifier];
if (tv == tableView) {
if ([ident isEqual:I_DELAY])
[[gifFileArray objectAtIndex:row]setDelay:[object intValue]];
}
}
//
// SDMovingRowsProtocol for reordering of rows:
//
- (BOOL)rowOK:(int)row
{
return (row > -1 && row < [gifFileArray count]);
}
- (unsigned int)dragReorderingMask:(int)col;
{
// DRAG_ALWAYS means do it with no modifier needed at all
if (col == IMAGE_COLUMN_NUMBER) return DRAG_ALWAYS;
else return NSCommandKeyMask;
}
- (void)willMoveRow:(int)row;
{
[statusField setStringValue:@""];
}
- (NSImage *)imageForRow:(int)row;
{
if ([self rowOK:row]) {
GifInfo *gi = [gifFileArray objectAtIndex:row];
[statusField setStringValue:
[NSString stringWithFormat:@"%@: %@", NOW_MOVING_MSG,
[self uiPath:gi]]];
return [gi image];
}
return nil;
}
- (BOOL)tableView:(NSTableView *)tv didDepositRow:(int)rowToMove at:(int)newPosition
{
if (rowToMove != -1 && newPosition != -1) {
id object = [gifFileArray objectAtIndex:rowToMove];
if (newPosition < [gifFileArray count] - 1) {
[gifFileArray removeObjectAtIndex:rowToMove];
[gifFileArray insertObject:object atIndex:newPosition];
} else {
[gifFileArray removeObjectAtIndex:rowToMove];
[gifFileArray addObject:object];
}
[statusField setStringValue:[NSString stringWithFormat:
@"%@ %@ %@ %d %@ %d", MOVED_MSG,[self uiPath:object],
FROM_MSG,rowToMove + 1,DEST_MSG,newPosition + 1]];
return YES; // ie reload
}
return NO;
}
// cut copy paste
- (void)copy:sender
{
int toCopy = [tableView selectedRow];
if ([self rowOK:toCopy])
copiedRow = [[gifFileArray objectAtIndex:toCopy]copy];
else {
NSBeep();
[statusField setStringValue:
[gifFileArray count]>0?SELECT_ROW_KEY:DROP_ON_KEY];
}
}
- (void)cut:sender
{
int toCut = [tableView selectedRow];
if ([self rowOK:toCut]) {
copiedRow = [[gifFileArray objectAtIndex:toCut]copy];
[gifFileArray removeObjectAtIndex:toCut];
[tableView reloadData];
}else {
NSBeep();
[statusField setStringValue:[gifFileArray count]>0?SELECT_ROW_KEY:DROP_ON_KEY];
}
}
- (void)paste:sender
{
int location = [tableView selectedRow];
int count = [gifFileArray count];
if (copiedRow != nil) {
if (location == -1 || location >= count - 1)
[gifFileArray addObject:[copiedRow copy]];
else
[gifFileArray insertObject:[copiedRow copy] atIndex:location+1];
[tableView reloadData];
} else {
NSBeep();
[statusField setStringValue:
[gifFileArray count]>0?SELECT_ROW_KEY:DROP_ON_KEY];
}
}
@end
TOC | PREV | NEXT
Created by Stone Design's Create on 4/30/1998
|
|