/* FileOpInfo.m * * Copyright (C) 2004 Free Software Foundation, Inc. * * Author: Enrico Sersale * Date: March 2004 * * This file is part of the GNUstep GWorkspace application * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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 * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA. */ #include #include #include "FileOpInfo.h" #include "Operation.h" #include "Functions.h" #include "GNUstep.h" #define PROGR_STEPS (100.0) static BOOL stopped = NO; static BOOL paused = NO; static NSString *nibName = @"FileOperationWin"; @implementation FileOpInfo - (void)dealloc { RELEASE (operationDict); RELEASE (type); TEST_RELEASE (source); TEST_RELEASE (destination); TEST_RELEASE (files); TEST_RELEASE (dupfiles); TEST_RELEASE (notifNames); TEST_RELEASE (win); TEST_RELEASE (progInd); TEST_RELEASE (progView); DESTROY (executor); DESTROY (execconn); [super dealloc]; } + (id)operationOfType:(NSString *)tp ref:(int)rf source:(NSString *)src destination:(NSString *)dst files:(NSArray *)fls confirmation:(BOOL)conf usewindow:(BOOL)uwnd winrect:(NSRect)wrect controller:(id)cntrl { return AUTORELEASE ([[self alloc] initWithOperationType: tp ref: rf source: src destination: dst files: fls confirmation: conf usewindow: uwnd winrect: wrect controller: cntrl]); } - (id)initWithOperationType:(NSString *)tp ref:(int)rf source:(NSString *)src destination:(NSString *)dst files:(NSArray *)fls confirmation:(BOOL)conf usewindow:(BOOL)uwnd winrect:(NSRect)wrect controller:(id)cntrl { self = [super init]; if (self) { win = nil; showwin = uwnd; if (showwin) { NSRect r; if ([NSBundle loadNibNamed: nibName owner: self] == NO) { NSLog(@"failed to load %@!", nibName); DESTROY (self); return self; } if (NSEqualRects(wrect, NSZeroRect) == NO) { [win setFrame: wrect display: NO]; } else if ([win setFrameUsingName: @"fopinfo"] == NO) { [win setFrame: NSMakeRect(300, 300, 282, 102) display: NO]; } RETAIN (progInd); r = [[progBox contentView] bounds]; progView = [[OpProgressView alloc] initWithFrame: r refreshInterval: 0.05]; [fromLabel setStringValue: NSLocalizedString(@"From:", @"")]; [toLabel setStringValue: NSLocalizedString(@"To:", @"")]; [pauseButt setTitle: NSLocalizedString(@"Pause", @"")]; [stopButt setTitle: NSLocalizedString(@"Stop", @"")]; } ref = rf; controller = cntrl; fm = [NSFileManager defaultManager]; nc = [NSNotificationCenter defaultCenter]; dnc = [NSDistributedNotificationCenter defaultCenter]; ASSIGN (type, tp); ASSIGN (source, src); ASSIGN (destination, dst); ASSIGN (files, fls); dupfiles = [NSMutableArray new]; if ([type isEqual: @"NSWorkspaceDuplicateOperation"]) { NSString *copystr = NSLocalizedString(@"_copy", @""); unsigned i; for (i = 0; i < [files count]; i++) { NSDictionary *fdict = [files objectAtIndex: i]; NSString *fname = [fdict objectForKey: @"name"]; NSString *newname = [NSString stringWithString: fname]; NSString *ext = [newname pathExtension]; NSString *base = [newname stringByDeletingPathExtension]; NSString *ntmp; NSString *destpath; int count = 1; while (1) { if (count == 1) { ntmp = [NSString stringWithFormat: @"%@%@", base, copystr]; if ([ext length]) { ntmp = [ntmp stringByAppendingPathExtension: ext]; } } else { ntmp = [NSString stringWithFormat: @"%@%@%i", base, copystr, count]; if ([ext length]) { ntmp = [ntmp stringByAppendingPathExtension: ext]; } } destpath = [destination stringByAppendingPathComponent: ntmp]; if ([fm fileExistsAtPath: destpath] == NO) { newname = ntmp; break; } else { count++; } } [dupfiles addObject: newname]; } } operationDict = [NSMutableDictionary new]; [operationDict setObject: type forKey: @"operation"]; [operationDict setObject: [NSNumber numberWithInt: ref] forKey: @"ref"]; [operationDict setObject: source forKey: @"source"]; [operationDict setObject: destination forKey: @"destination"]; [operationDict setObject: files forKey: @"files"]; confirm = conf; executor = nil; opdone = NO; } return self; } - (void)startOperation { NSPort *port[2]; NSArray *ports; if (confirm) { NSString *title; NSString *msg, *msg1, *msg2; NSString *items; if ([files count] > 1) { items = [NSString stringWithFormat: @"%i %@", [files count], NSLocalizedString(@"items", @"")]; } else { items = NSLocalizedString(@"one item", @""); } if ([type isEqual: @"NSWorkspaceMoveOperation"]) { title = NSLocalizedString(@"Move", @""); msg1 = [NSString stringWithFormat: @"%@ %@ %@: ", NSLocalizedString(@"Move", @""), items, NSLocalizedString(@"from", @"")]; msg2 = NSLocalizedString(@"\nto: ", @""); msg = [NSString stringWithFormat: @"%@%@%@%@?", msg1, source, msg2, destination]; } else if ([type isEqual: @"NSWorkspaceCopyOperation"]) { title = NSLocalizedString(@"Copy", @""); msg1 = [NSString stringWithFormat: @"%@ %@ %@: ", NSLocalizedString(@"Copy", @""), items, NSLocalizedString(@"from", @"")]; msg2 = NSLocalizedString(@"\nto: ", @""); msg = [NSString stringWithFormat: @"%@%@%@%@?", msg1, source, msg2, destination]; } else if ([type isEqual: @"NSWorkspaceLinkOperation"]) { title = NSLocalizedString(@"Link", @""); msg1 = [NSString stringWithFormat: @"%@ %@ %@: ", NSLocalizedString(@"Link", @""), items, NSLocalizedString(@"from", @"")]; msg2 = NSLocalizedString(@"\nto: ", @""); msg = [NSString stringWithFormat: @"%@%@%@%@?", msg1, source, msg2, destination]; } else if ([type isEqual: @"NSWorkspaceRecycleOperation"]) { title = NSLocalizedString(@"Recycler", @""); msg1 = [NSString stringWithFormat: @"%@ %@ %@: ", NSLocalizedString(@"Move", @""), items, NSLocalizedString(@"from", @"")]; msg2 = NSLocalizedString(@"\nto the Recycler", @""); msg = [NSString stringWithFormat: @"%@%@%@?", msg1, source, msg2]; } else if ([type isEqual: @"GWorkspaceRecycleOutOperation"]) { title = NSLocalizedString(@"Recycler", @""); msg1 = [NSString stringWithFormat: @"%@ %@ %@ ", NSLocalizedString(@"Move", @""), items, NSLocalizedString(@"from the Recycler", @"")]; msg2 = NSLocalizedString(@"\nto: ", @""); msg = [NSString stringWithFormat: @"%@%@%@?", msg1, msg2, destination]; } else if ([type isEqual: @"GWorkspaceEmptyRecyclerOperation"]) { title = NSLocalizedString(@"Recycler", @""); msg = NSLocalizedString(@"Empty the Recycler?", @""); } else if ([type isEqual: @"NSWorkspaceDestroyOperation"]) { title = NSLocalizedString(@"Delete", @""); msg = NSLocalizedString(@"Delete the selected objects?", @""); } else if ([type isEqual: @"NSWorkspaceDuplicateOperation"]) { title = NSLocalizedString(@"Duplicate", @""); msg = NSLocalizedString(@"Duplicate the selected objects?", @""); } if (NSRunAlertPanel(title, msg, NSLocalizedString(@"OK", @""), NSLocalizedString(@"Cancel", @""), nil) != NSAlertDefaultReturn) { [self endOperation]; return; } } port[0] = (NSPort *)[NSPort port]; port[1] = (NSPort *)[NSPort port]; ports = [NSArray arrayWithObjects: port[1], port[0], nil]; execconn = [[NSConnection alloc] initWithReceivePort: port[0] sendPort: port[1]]; [execconn setRootObject: self]; [execconn setDelegate: self]; [nc addObserver: self selector: @selector(connectionDidDie:) name: NSConnectionDidDieNotification object: execconn]; NS_DURING { [NSThread detachNewThreadSelector: @selector(setPorts:) toTarget: [FileOpExecutor class] withObject: ports]; } NS_HANDLER { NSRunAlertPanel(nil, NSLocalizedString(@"A fatal error occured while detaching the thread!", @""), NSLocalizedString(@"Continue", @""), nil, nil); [self endOperation]; } NS_ENDHANDLER } - (int)requestUserConfirmationWithMessage:(NSString *)message title:(NSString *)title { return NSRunAlertPanel(NSLocalizedString(title, @""), NSLocalizedString(message, @""), NSLocalizedString(@"Ok", @""), NSLocalizedString(@"Cancel", @""), nil); } - (int)showErrorAlertWithMessage:(NSString *)message { return NSRunAlertPanel(nil, NSLocalizedString(message, @""), NSLocalizedString(@"Ok", @""), nil, nil); } - (IBAction)pause:(id)sender { if (paused == NO) { [pauseButt setTitle: NSLocalizedString(@"Continue", @"")]; [stopButt setEnabled: NO]; paused = YES; } else { [pauseButt setTitle: NSLocalizedString(@"Pause", @"")]; [stopButt setEnabled: YES]; paused = NO; [executor performOperation]; } } - (IBAction)stop:(id)sender { stopped = YES; } - (void)showProgressWin { if ([win isVisible] == NO) { if ([type isEqual: @"NSWorkspaceMoveOperation"]) { [win setTitle: NSLocalizedString(@"Move", @"")]; [fromLabel setStringValue: NSLocalizedString(@"From:", @"")]; [fromField setStringValue: relativePathFittingInField(fromField, source)]; [toLabel setStringValue: NSLocalizedString(@"To:", @"")]; [toField setStringValue: relativePathFittingInField(fromField, destination)]; } else if ([type isEqual: @"NSWorkspaceCopyOperation"]) { [win setTitle: NSLocalizedString(@"Copy", @"")]; [fromLabel setStringValue: NSLocalizedString(@"From:", @"")]; [fromField setStringValue: relativePathFittingInField(fromField, source)]; [toLabel setStringValue: NSLocalizedString(@"To:", @"")]; [toField setStringValue: relativePathFittingInField(fromField, destination)]; } else if ([type isEqual: @"NSWorkspaceLinkOperation"]) { [win setTitle: NSLocalizedString(@"Link", @"")]; [fromLabel setStringValue: NSLocalizedString(@"From:", @"")]; [fromField setStringValue: relativePathFittingInField(fromField, source)]; [toLabel setStringValue: NSLocalizedString(@"To:", @"")]; [toField setStringValue: relativePathFittingInField(fromField, destination)]; } else if ([type isEqual: @"NSWorkspaceDuplicateOperation"]) { [win setTitle: NSLocalizedString(@"Duplicate", @"")]; [fromLabel setStringValue: NSLocalizedString(@"In:", @"")]; [fromField setStringValue: relativePathFittingInField(fromField, destination)]; [toLabel setStringValue: @""]; [toField setStringValue: @""]; } else if ([type isEqual: @"NSWorkspaceDestroyOperation"]) { [win setTitle: NSLocalizedString(@"Destroy", @"")]; [fromLabel setStringValue: NSLocalizedString(@"In:", @"")]; [fromField setStringValue: relativePathFittingInField(fromField, destination)]; [toLabel setStringValue: @""]; [toField setStringValue: @""]; } else if ([type isEqual: @"NSWorkspaceRecycleOperation"]) { [win setTitle: NSLocalizedString(@"Move", @"")]; [fromLabel setStringValue: NSLocalizedString(@"From:", @"")]; [fromField setStringValue: relativePathFittingInField(fromField, source)]; [toLabel setStringValue: NSLocalizedString(@"To:", @"")]; [toField setStringValue: NSLocalizedString(@"the Recycler", @"")]; } else if ([type isEqual: @"GWorkspaceRecycleOutOperation"]) { [win setTitle: NSLocalizedString(@"Move", @"")]; [fromLabel setStringValue: NSLocalizedString(@"From:", @"")]; [fromField setStringValue: NSLocalizedString(@"the Recycler", @"")]; [toLabel setStringValue: NSLocalizedString(@"To:", @"")]; [toField setStringValue: relativePathFittingInField(fromField, destination)]; } else if ([type isEqual: @"GWorkspaceEmptyRecyclerOperation"]) { [win setTitle: NSLocalizedString(@"Destroy", @"")]; [fromLabel setStringValue: NSLocalizedString(@"In:", @"")]; [fromField setStringValue: NSLocalizedString(@"the Recycler", @"")]; [toLabel setStringValue: @""]; [toField setStringValue: @""]; } [progBox setContentView: progView]; [progView start]; } [win orderFront: nil]; showwin = YES; } - (void)setNumFiles:(int)n { [progView stop]; [progBox setContentView: progInd]; [progInd setMinValue: 0.0]; [progInd setMaxValue: n]; [progInd setDoubleValue: 0.0]; [executor performOperation]; } - (void)setProgIndicatorValue:(int)n { [progInd setDoubleValue: n]; } - (void)endOperation { if (showwin) { if ([progBox contentView] == progView) { [progView stop]; } [win saveFrameUsingName: @"fopinfo"]; [win close]; } [nc removeObserver: self]; if (executor) { [nc removeObserver: self name: NSConnectionDidDieNotification object: execconn]; DESTROY (executor); DESTROY (execconn); } [controller endOfFileOperation: self]; } - (void)sendWillChangeNotification { CREATE_AUTORELEASE_POOL(arp); NSMutableDictionary *dict = [NSMutableDictionary dictionary]; int i; notifNames = [NSMutableArray new]; for (i = 0; i < [files count]; i++) { NSDictionary *fdict = [files objectAtIndex: i]; NSString *name = [fdict objectForKey: @"name"]; [notifNames addObject: name]; } [dict setObject: type forKey: @"operation"]; [dict setObject: source forKey: @"source"]; [dict setObject: destination forKey: @"destination"]; [dict setObject: notifNames forKey: @"files"]; [nc postNotificationName: @"GWFileSystemWillChangeNotification" object: dict]; [dnc postNotificationName: @"GWFileSystemWillChangeNotification" object: nil userInfo: dict]; RELEASE (arp); } - (void)sendDidChangeNotification { CREATE_AUTORELEASE_POOL(arp); NSMutableDictionary *notifObj = [NSMutableDictionary dictionary]; [notifObj setObject: type forKey: @"operation"]; [notifObj setObject: source forKey: @"source"]; [notifObj setObject: destination forKey: @"destination"]; if (executor) { NSData *data = [executor processedFiles]; NSArray *procFiles = [NSUnarchiver unarchiveObjectWithData: data]; [notifObj setObject: procFiles forKey: @"files"]; [notifObj setObject: notifNames forKey: @"origfiles"]; } else { [notifObj setObject: notifNames forKey: @"files"]; [notifObj setObject: notifNames forKey: @"origfiles"]; } opdone = YES; [nc postNotificationName: @"GWFileSystemDidChangeNotification" object: notifObj]; [dnc postNotificationName: @"GWFileSystemDidChangeNotification" object: nil userInfo: notifObj]; RELEASE (arp); } - (void)registerExecutor:(id)anObject { NSData *opinfo = [NSArchiver archivedDataWithRootObject: operationDict]; BOOL samename; [anObject setProtocolForProxy: @protocol(FileOpExecutorProtocol)]; executor = (id )[anObject retain]; [executor setOperation: opinfo]; samename = [executor checkSameName]; if (samename) { NSString *msg, *title; int result; if ([type isEqual: @"NSWorkspaceMoveOperation"]) { msg = @"Some items have the same name;\ndo you want to replace them?"; title = @"Move"; } else if ([type isEqual: @"NSWorkspaceCopyOperation"]) { msg = @"Some items have the same name;\ndo you want to replace them?"; title = @"Copy"; } else if ([type isEqual: @"NSWorkspaceLinkOperation"]) { msg = @"Some items have the same name;\ndo you want to replace them?"; title = @"Link"; } else if ([type isEqual: @"NSWorkspaceRecycleOperation"]) { msg = @"Some items have the same name;\ndo you want to replace them?"; title = @"Recycle"; } else if ([type isEqual: @"GWorkspaceRecycleOutOperation"]) { msg = @"Some items have the same name;\ndo you want to replace them?"; title = @"Recycle"; } result = NSRunAlertPanel(NSLocalizedString(title, @""), NSLocalizedString(msg, @""), NSLocalizedString(@"OK", @""), NSLocalizedString(@"Cancel", @""), NSLocalizedString(@"Only older", @"")); if (result == NSAlertAlternateReturn) { [controller endOfFileOperation: self]; return; } else if (result == NSAlertOtherReturn) { [executor setOnlyOlder]; } } if (showwin) { [self showProgressWin]; } [self sendWillChangeNotification]; stopped = NO; paused = NO; [executor calculateNumFiles]; } - (BOOL)connection:(NSConnection*)ancestor shouldMakeNewConnection:(NSConnection*)newConn { if (ancestor == execconn) { [newConn setDelegate: self]; [nc addObserver: self selector: @selector(connectionDidDie:) name: NSConnectionDidDieNotification object: newConn]; return YES; } return NO; } - (void)connectionDidDie:(NSNotification *)notification { [nc removeObserver: self name: NSConnectionDidDieNotification object: [notification object]]; if (opdone == NO) { NSRunAlertPanel(nil, NSLocalizedString(@"executor connection died!", @""), NSLocalizedString(@"Continue", @""), nil, nil); [self sendDidChangeNotification]; [self endOperation]; } } - (NSString *)type { return type; } - (NSString *)source { return source; } - (NSString *)destination { return destination; } - (NSArray *)files { return files; } - (NSArray *)dupfiles { return dupfiles; } - (int)ref { return ref; } - (BOOL)showsWindow { return showwin; } - (NSWindow *)win { return win; } - (NSRect)winRect { if (win && [win isVisible]) { return [win frame]; } return NSZeroRect; } @end @implementation FileOpExecutor + (void)setPorts:(NSArray *)thePorts { CREATE_AUTORELEASE_POOL(pool); NSPort *port[2]; NSConnection *conn; FileOpExecutor *executor; port[0] = [thePorts objectAtIndex: 0]; port[1] = [thePorts objectAtIndex: 1]; conn = [NSConnection connectionWithReceivePort: (NSPort *)port[0] sendPort: (NSPort *)port[1]]; executor = [[self alloc] init]; [executor setFileop: thePorts]; [(id)[conn rootProxy] registerExecutor: executor]; RELEASE (executor); RELEASE (pool); } - (void)dealloc { TEST_RELEASE (operation); TEST_RELEASE (source); TEST_RELEASE (destination); TEST_RELEASE (files); TEST_RELEASE (procfiles); [super dealloc]; } - (id)init { self = [super init]; if (self) { fm = [NSFileManager defaultManager]; samename = NO; onlyolder = NO; } return self; } - (void)setFileop:(NSArray *)thePorts { NSPort *port[2]; NSConnection *conn; id anObject; port[0] = [thePorts objectAtIndex: 0]; port[1] = [thePorts objectAtIndex: 1]; conn = [NSConnection connectionWithReceivePort: (NSPort *)port[0] sendPort: (NSPort *)port[1]]; anObject = (id)[conn rootProxy]; [anObject setProtocolForProxy: @protocol(FileOpInfoProtocol)]; fileOp = (id )anObject; } - (BOOL)setOperation:(NSData *)opinfo { NSDictionary *opDict = [NSUnarchiver unarchiveObjectWithData: opinfo]; id dictEntry; dictEntry = [opDict objectForKey: @"operation"]; if (dictEntry) { ASSIGN (operation, dictEntry); } dictEntry = [opDict objectForKey: @"source"]; if (dictEntry) { ASSIGN (source, dictEntry); } dictEntry = [opDict objectForKey: @"destination"]; if (dictEntry) { ASSIGN (destination, dictEntry); } files = [NSMutableArray new]; dictEntry = [opDict objectForKey: @"files"]; if (dictEntry) { [files addObjectsFromArray: dictEntry]; } procfiles = [NSMutableArray new]; return YES; } - (BOOL)checkSameName { NSArray *dirContents; int i; samename = NO; if (([operation isEqual: @"GWorkspaceRenameOperation"]) || ([operation isEqual: @"GWorkspaceCreateDirOperation"]) || ([operation isEqual: @"GWorkspaceCreateFileOperation"])) { /* already checked by GWorkspace */ return NO; } if (destination && [files count]) { dirContents = [fm directoryContentsAtPath: destination]; for (i = 0; i < [files count]; i++) { NSDictionary *dict = [files objectAtIndex: i]; NSString *name = [dict objectForKey: @"name"]; if ([dirContents containsObject: name]) { samename = YES; break; } } } if (samename) { if (([operation isEqual: @"NSWorkspaceMoveOperation"]) || ([operation isEqual: @"NSWorkspaceCopyOperation"]) || ([operation isEqual: @"NSWorkspaceLinkOperation"]) || ([operation isEqual: @"GWorkspaceRecycleOutOperation"])) { return YES; } else if (([operation isEqual: @"NSWorkspaceDestroyOperation"]) || ([operation isEqual: @"NSWorkspaceDuplicateOperation"]) || ([operation isEqual: @"NSWorkspaceRecycleOperation"]) || ([operation isEqual: @"GWorkspaceEmptyRecyclerOperation"])) { return NO; } } return NO; } - (void)setOnlyOlder { onlyolder = YES; } - (oneway void)calculateNumFiles { int i, fnum = 0; for (i = 0; i < [files count]; i++) { CREATE_AUTORELEASE_POOL (arp); NSDictionary *dict = [files objectAtIndex: i]; NSString *name = [dict objectForKey: @"name"]; NSString *path = [source stringByAppendingPathComponent: name]; BOOL isDir = NO; [fm fileExistsAtPath: path isDirectory: &isDir]; if (isDir) { NSDirectoryEnumerator *enumerator = [fm enumeratorAtPath: path]; while (1) { CREATE_AUTORELEASE_POOL (arp2); NSString *dirEntry = [enumerator nextObject]; if (dirEntry) { if (stopped) { break; } fnum++; } else { RELEASE (arp2); break; } RELEASE (arp2); } } else { fnum++; } if (stopped) { RELEASE (arp); break; } RELEASE (arp); } if (stopped) { [self done]; } fcount = 0; stepcount = 0; if (fnum < PROGR_STEPS) { progstep = 1.0; } else { progstep = fnum / PROGR_STEPS; } [fileOp setNumFiles: fnum]; } - (oneway void)performOperation { canupdate = YES; if ([operation isEqual: @"NSWorkspaceMoveOperation"] || [operation isEqual: @"GWorkspaceRecycleOutOperation"]) { [self doMove]; } else if ([operation isEqual: @"NSWorkspaceCopyOperation"]) { [self doCopy]; } else if ([operation isEqual: @"NSWorkspaceLinkOperation"]) { [self doLink]; } else if ([operation isEqual: @"NSWorkspaceDestroyOperation"] || [operation isEqual: @"GWorkspaceEmptyRecyclerOperation"]) { [self doRemove]; } else if ([operation isEqual: @"NSWorkspaceDuplicateOperation"]) { [self doDuplicate]; } else if ([operation isEqual: @"NSWorkspaceRecycleOperation"]) { [self doTrash]; } else if ([operation isEqual: @"GWorkspaceRenameOperation"]) { [self doRename]; } else if ([operation isEqual: @"GWorkspaceCreateDirOperation"]) { [self doNewFolder]; } else if ([operation isEqual: @"GWorkspaceCreateFileOperation"]) { [self doNewFile]; } } - (NSData *)processedFiles { return [NSArchiver archivedDataWithRootObject: procfiles]; } #define CHECK_DONE \ if (([files count] == 0) || stopped || paused) break #define GET_FILENAME \ fileinfo = [files objectAtIndex: 0]; \ RETAIN (fileinfo); \ filename = [fileinfo objectForKey: @"name"]; - (void)doMove { while (1) { CHECK_DONE; GET_FILENAME; if ((samename == NO) || (samename && [self removeExisting: fileinfo])) { NSString *src = [source stringByAppendingPathComponent: filename]; NSString *dst = [destination stringByAppendingPathComponent: filename]; if ([fm movePath: src toPath: dst handler: self]) { [procfiles addObject: filename]; } else { /* check for broken symlink */ NSDictionary *attributes = [fm fileAttributesAtPath: src traverseLink: NO]; if (attributes && ([attributes fileType] == NSFileTypeSymbolicLink) && ([fm fileExistsAtPath: src] == NO)) { if ([fm copyPath: src toPath: dst handler: self] && [fm removeFileAtPath: src handler: self]) { [procfiles addObject: filename]; } } } } [files removeObject: fileinfo]; RELEASE (fileinfo); } if (([files count] == 0) || stopped) { [self done]; } } - (void)doCopy { while (1) { CHECK_DONE; GET_FILENAME; if ((samename == NO) || (samename && [self removeExisting: fileinfo])) { if ([fm copyPath: [source stringByAppendingPathComponent: filename] toPath: [destination stringByAppendingPathComponent: filename] handler: self]) { [procfiles addObject: filename]; } } [files removeObject: fileinfo]; RELEASE (fileinfo); } if (([files count] == 0) || stopped) { [self done]; } } - (void)doLink { while (1) { CHECK_DONE; GET_FILENAME; if ((samename == NO) || (samename && [self removeExisting: fileinfo])) { NSString *dst = [destination stringByAppendingPathComponent: filename]; NSString *src = [source stringByAppendingPathComponent: filename]; if ([fm createSymbolicLinkAtPath: dst pathContent: src]) { [procfiles addObject: filename]; } } [files removeObject: fileinfo]; RELEASE (fileinfo); } if (([files count] == 0) || stopped) { [self done]; } } - (void)doRemove { while (1) { CHECK_DONE; GET_FILENAME; if ([fm removeFileAtPath: [destination stringByAppendingPathComponent: filename] handler: self]) { [procfiles addObject: filename]; } [files removeObject: fileinfo]; RELEASE (fileinfo); } if (([files count] == 0) || stopped) { [self done]; } } - (void)doDuplicate { NSString *copystr = NSLocalizedString(@"_copy", @""); NSString *base; NSString *ext; NSString *destpath; NSString *newname; NSString *ntmp; while (1) { int count = 1; CHECK_DONE; GET_FILENAME; newname = [NSString stringWithString: filename]; ext = [newname pathExtension]; base = [newname stringByDeletingPathExtension]; while (1) { if (count == 1) { ntmp = [NSString stringWithFormat: @"%@%@", base, copystr]; if ([ext length]) { ntmp = [ntmp stringByAppendingPathExtension: ext]; } } else { ntmp = [NSString stringWithFormat: @"%@%@%i", base, copystr, count]; if ([ext length]) { ntmp = [ntmp stringByAppendingPathExtension: ext]; } } destpath = [destination stringByAppendingPathComponent: ntmp]; if ([fm fileExistsAtPath: destpath] == NO) { newname = ntmp; break; } else { count++; } } if ([fm copyPath: [destination stringByAppendingPathComponent: filename] toPath: destpath handler: self]) { [procfiles addObject: newname]; } [files removeObject: fileinfo]; RELEASE (fileinfo); } if (([files count] == 0) || stopped) { [self done]; } } - (void)doRename { GET_FILENAME; if ([fm movePath: source toPath: destination handler: self]) { [procfiles addObject: filename]; } else { /* check for broken symlink */ NSDictionary *attributes = [fm fileAttributesAtPath: source traverseLink: NO]; if (attributes && ([attributes fileType] == NSFileTypeSymbolicLink) && ([fm fileExistsAtPath: source] == NO)) { if ([fm copyPath: source toPath: destination handler: self] && [fm removeFileAtPath: source handler: self]) { [procfiles addObject: filename]; } } } [files removeObject: fileinfo]; RELEASE (fileinfo); [self done]; } - (void)doNewFolder { GET_FILENAME; if ([fm createDirectoryAtPath: [destination stringByAppendingPathComponent: filename] attributes: nil]) { [procfiles addObject: filename]; } [files removeObject: fileinfo]; RELEASE (fileinfo); [self done]; } - (void)doNewFile { GET_FILENAME; if ([fm createFileAtPath: [destination stringByAppendingPathComponent: filename] contents: nil attributes: nil]) { [procfiles addObject: filename]; } [files removeObject: fileinfo]; RELEASE (fileinfo); [self done]; } - (void)doTrash { NSString *copystr = NSLocalizedString(@"_copy", @""); NSString *srcpath; NSString *destpath; NSString *newname; NSString *ntmp; while (1) { CHECK_DONE; GET_FILENAME; newname = [NSString stringWithString: filename]; srcpath = [source stringByAppendingPathComponent: filename]; destpath = [destination stringByAppendingPathComponent: newname]; if ([fm fileExistsAtPath: destpath]) { NSString *ext = [filename pathExtension]; NSString *base = [filename stringByDeletingPathExtension]; int count = 1; newname = [NSString stringWithString: filename]; while (1) { if (count == 1) { ntmp = [NSString stringWithFormat: @"%@%@", base, copystr]; if ([ext length]) { ntmp = [ntmp stringByAppendingPathExtension: ext]; } } else { ntmp = [NSString stringWithFormat: @"%@%@%i", base, copystr, count]; if ([ext length]) { ntmp = [ntmp stringByAppendingPathExtension: ext]; } } destpath = [destination stringByAppendingPathComponent: ntmp]; if ([fm fileExistsAtPath: destpath] == NO) { newname = ntmp; break; } else { count++; } } } if ([fm movePath: srcpath toPath: destpath handler: self]) { [procfiles addObject: newname]; } else { /* check for broken symlink */ NSDictionary *attributes = [fm fileAttributesAtPath: srcpath traverseLink: NO]; if (attributes && ([attributes fileType] == NSFileTypeSymbolicLink) && ([fm fileExistsAtPath: srcpath] == NO)) { if ([fm copyPath: srcpath toPath: destpath handler: self] && [fm removeFileAtPath: srcpath handler: self]) { [procfiles addObject: newname]; } } } [files removeObject: fileinfo]; RELEASE (fileinfo); } if (([files count] == 0) || stopped) { [self done]; } } - (BOOL)removeExisting:(NSDictionary *)info { NSString *fname = [info objectForKey: @"name"]; NSString *destpath = [destination stringByAppendingPathComponent: fname]; BOOL isdir; canupdate = NO; if ([fm fileExistsAtPath: destpath isDirectory: &isdir]) { if (onlyolder) { NSDictionary *attributes = [fm fileAttributesAtPath: destpath traverseLink: NO]; NSDate *dstdate = [attributes objectForKey: NSFileModificationDate]; NSDate *srcdate = [info objectForKey: @"date"]; if ([srcdate isEqual: dstdate] == NO) { if ([[srcdate earlierDate: dstdate] isEqual: srcdate]) { canupdate = YES; return NO; } } else { canupdate = YES; return NO; } } [fm removeFileAtPath: destpath handler: self]; } canupdate = YES; return YES; } - (NSDictionary *)infoForFilename:(NSString *)name { int i; for (i = 0; i < [files count]; i++) { NSDictionary *info = [files objectAtIndex: i]; if ([[info objectForKey: @"name"] isEqual: name]) { return info; } } return nil; } - (void)done { [fileOp sendDidChangeNotification]; [fileOp endOperation]; } - (BOOL)fileManager:(NSFileManager *)manager shouldProceedAfterError:(NSDictionary *)errorDict { NSString *path; NSString *error; NSString *msg; int result; error = [errorDict objectForKey: @"Error"]; if ([error hasPrefix: @"Unable to change NSFileOwnerAccountID to to"] || [error hasPrefix: @"Unable to change NSFileOwnerAccountName to"] || [error hasPrefix: @"Unable to change NSFileGroupOwnerAccountID to"] || [error hasPrefix: @"Unable to change NSFileGroupOwnerAccountName to"] || [error hasPrefix: @"Unable to change NSFilePosixPermissions to"] || [error hasPrefix: @"Unable to change NSFileModificationDate to"]) { return YES; } path = [NSString stringWithString: [errorDict objectForKey: @"Path"]]; msg = [NSString stringWithFormat: @"%@ %@\n%@ %@\n", NSLocalizedString(@"File operation error:", @""), error, NSLocalizedString(@"with file:", @""), path]; result = [fileOp requestUserConfirmationWithMessage: msg title: @"Error"]; if (result != NSAlertDefaultReturn) { [self done]; } else { BOOL found = NO; while (1) { NSDictionary *info = [self infoForFilename: [path lastPathComponent]]; if ([path isEqual: source]) { break; } if (info) { [files removeObject: info]; found = YES; break; } path = [path stringByDeletingLastPathComponent]; } if ([files count]) { if (found) { [self performOperation]; } else { result = [fileOp showErrorAlertWithMessage: @"File Operation Error!"]; [self done]; } } else { [self done]; } } return YES; } - (void)fileManager:(NSFileManager *)manager willProcessPath:(NSString *)path { if (canupdate) { fcount++; stepcount++; if (stepcount >= progstep) { stepcount = 0; [fileOp setProgIndicatorValue: fcount]; } } if (stopped) { [self done]; } } @end @implementation OpProgressView #define PROG_IND_MAX (-28) - (void)dealloc { RELEASE (image); [super dealloc]; } - (id)initWithFrame:(NSRect)frameRect refreshInterval:(float)refresh { self = [super initWithFrame: frameRect]; if (self) { NSBundle *bundle = [NSBundle bundleForClass: [Operation class]]; NSString *path = [bundle pathForResource: @"progind" ofType: @"tiff"]; image = [[NSImage alloc] initWithContentsOfFile: path]; rfsh = refresh; orx = PROG_IND_MAX; } return self; } - (void)start { progTimer = [NSTimer scheduledTimerWithTimeInterval: rfsh target: self selector: @selector(animate:) userInfo: nil repeats: YES]; } - (void)stop { if (progTimer && [progTimer isValid]) { [progTimer invalidate]; } } - (void)animate:(id)sender { orx++; [self setNeedsDisplay: YES]; if (orx == 0) { orx = PROG_IND_MAX; } } - (void)drawRect:(NSRect)rect { [image compositeToPoint: NSMakePoint(orx, 2) operation: NSCompositeSourceOver]; } @end