1/* FileOpInfo.m 2 * 3 * Copyright (C) 2004-2015 Free Software Foundation, Inc. 4 * 5 * Author: Enrico Sersale <enrico@imago.ro> 6 * Riccardo Mottola <rm@gnu.org> 7 * Date: March 2004 8 * 9 * This file is part of the GNUstep GWorkspace application 10 * 11 * This program is free software; you can redistribute it and/or modify 12 * it under the terms of the GNU General Public License as published by 13 * the Free Software Foundation; either version 2 of the License, or 14 * (at your option) any later version. 15 * 16 * This program is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 * GNU General Public License for more details. 20 * 21 * You should have received a copy of the GNU General Public License 22 * along with this program; if not, write to the Free Software 23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA. 24 */ 25 26#import <Foundation/Foundation.h> 27#import <AppKit/AppKit.h> 28#import <GNUstepBase/GNUstep.h> 29 30#import "FileOpInfo.h" 31#import "Operation.h" 32#import "Functions.h" 33 34 35#define PROGR_STEPS (100.0) 36static BOOL stopped = NO; 37static BOOL paused = NO; 38 39static NSString *nibName = @"FileOperationWin"; 40 41@implementation FileOpInfo 42 43- (NSString *)description 44{ 45 return [NSString stringWithFormat: @"%@ from: %@ to: %@", type, source, destination]; 46} 47 48- (void)dealloc 49{ 50 [nc removeObserver: self]; 51 52 RELEASE (operationDict); 53 RELEASE (type); 54 RELEASE (source); 55 RELEASE (destination); 56 RELEASE (files); 57 RELEASE (procFiles); 58 RELEASE (dupfiles); 59 RELEASE (notifNames); 60 RELEASE (win); 61 62 DESTROY (executor); 63 DESTROY (execconn); 64 65 [super dealloc]; 66} 67 68+ (id)operationOfType:(NSString *)tp 69 ref:(int)rf 70 source:(NSString *)src 71 destination:(NSString *)dst 72 files:(NSArray *)fls 73 confirmation:(BOOL)conf 74 usewindow:(BOOL)uwnd 75 winrect:(NSRect)wrect 76 controller:(id)cntrl 77{ 78 return AUTORELEASE ([[self alloc] initWithOperationType: tp ref: rf 79 source: src destination: dst files: fls 80 confirmation: conf usewindow: uwnd 81 winrect: wrect controller: cntrl]); 82} 83 84- (id)initWithOperationType:(NSString *)tp 85 ref:(int)rf 86 source:(NSString *)src 87 destination:(NSString *)dst 88 files:(NSArray *)fls 89 confirmation:(BOOL)conf 90 usewindow:(BOOL)uwnd 91 winrect:(NSRect)wrect 92 controller:(id)cntrl 93{ 94 self = [super init]; 95 96 if (self) 97 { 98 win = nil; 99 showwin = uwnd; 100 101 if (showwin) { 102 if ([NSBundle loadNibNamed: nibName owner: self] == NO) 103 { 104 NSLog(@"failed to load %@!", nibName); 105 DESTROY (self); 106 return self; 107 } 108 109 if (NSEqualRects(wrect, NSZeroRect) == NO) { 110 [win setFrame: wrect display: NO]; 111 } else if ([win setFrameUsingName: @"fopinfo"] == NO) { 112 [win setFrame: NSMakeRect(300, 300, 282, 102) display: NO]; 113 } 114 115 [fromLabel setStringValue: NSLocalizedString(@"From:", @"")]; 116 [toLabel setStringValue: NSLocalizedString(@"To:", @"")]; 117 [pauseButt setTitle: NSLocalizedString(@"Pause", @"")]; 118 [stopButt setTitle: NSLocalizedString(@"Stop", @"")]; 119 } 120 121 ref = rf; 122 controller = cntrl; 123 fm = [NSFileManager defaultManager]; 124 nc = [NSNotificationCenter defaultCenter]; 125 dnc = [NSDistributedNotificationCenter defaultCenter]; 126 127 ASSIGN (type, tp); 128 ASSIGN (source, src); 129 ASSIGN (destination, dst); 130 files = [[NSMutableArray arrayWithArray:fls] retain]; 131 procFiles = [[NSMutableArray alloc] init]; 132 133 dupfiles = [NSMutableArray new]; 134 135 if ([type isEqual: NSWorkspaceDuplicateOperation]) { 136 NSString *copystr = NSLocalizedString(@"_copy", @""); 137 unsigned i; 138 139 for (i = 0; i < [files count]; i++) 140 { 141 NSDictionary *fdict = [files objectAtIndex: i]; 142 NSString *fname = [fdict objectForKey: @"name"]; 143 NSString *newname = [NSString stringWithString: fname]; 144 NSString *ext = [newname pathExtension]; 145 NSString *base = [newname stringByDeletingPathExtension]; 146 NSString *ntmp; 147 NSString *destpath; 148 NSUInteger count = 1; 149 150 while (1) 151 { 152 if (count == 1) 153 { 154 ntmp = [NSString stringWithFormat: @"%@%@", base, copystr]; 155 if ([ext length]) { 156 ntmp = [ntmp stringByAppendingPathExtension: ext]; 157 } 158 } else 159 { 160 ntmp = [NSString stringWithFormat: @"%@%@%i", base, copystr, count]; 161 if ([ext length]) { 162 ntmp = [ntmp stringByAppendingPathExtension: ext]; 163 } 164 } 165 destpath = [destination stringByAppendingPathComponent: ntmp]; 166 167 if ([fm fileExistsAtPath: destpath] == NO) { 168 newname = ntmp; 169 break; 170 } else 171 { 172 count++; 173 } 174 } 175 176 [dupfiles addObject: newname]; 177 } 178 } 179 180 operationDict = [NSMutableDictionary new]; 181 [operationDict setObject: type forKey: @"operation"]; 182 [operationDict setObject: [NSNumber numberWithInt: ref] forKey: @"ref"]; 183 [operationDict setObject: source forKey: @"source"]; 184 if (destination != nil) 185 [operationDict setObject: destination forKey: @"destination"]; 186 [operationDict setObject: files forKey: @"files"]; 187 188 confirm = conf; 189 executor = nil; 190 opdone = NO; 191 } 192 193 return self; 194} 195 196- (void)startOperation 197{ 198 if (confirm) 199 { 200 NSString *title = nil; 201 NSString *msg = nil; 202 NSString *msg1 = nil; 203 NSString *msg2 = nil; 204 NSString *items; 205 206 if ([files count] > 1) 207 { 208 items = [NSString stringWithFormat: @"%lu %@", (unsigned long)[files count], NSLocalizedString(@"items", @"")]; 209 } 210 else 211 { 212 items = NSLocalizedString(@"one item", @""); 213 } 214 215 if ([type isEqual: NSWorkspaceMoveOperation]) 216 { 217 title = NSLocalizedString(@"Move", @""); 218 msg1 = [NSString stringWithFormat: @"%@ %@ %@: ", 219 NSLocalizedString(@"Move", @""), 220 items, 221 NSLocalizedString(@"from", @"")]; 222 msg2 = NSLocalizedString(@"\nto: ", @""); 223 msg = [NSString stringWithFormat: @"%@%@%@%@?", msg1, source, msg2, destination]; 224 } 225 else if ([type isEqual: NSWorkspaceCopyOperation]) 226 { 227 title = NSLocalizedString(@"Copy", @""); 228 msg1 = [NSString stringWithFormat: @"%@ %@ %@: ", 229 NSLocalizedString(@"Copy", @""), 230 items, 231 NSLocalizedString(@"from", @"")]; 232 msg2 = NSLocalizedString(@"\nto: ", @""); 233 msg = [NSString stringWithFormat: @"%@%@%@%@?", msg1, source, msg2, destination]; 234 } 235 else if ([type isEqual: NSWorkspaceLinkOperation]) 236 { 237 title = NSLocalizedString(@"Link", @""); 238 msg1 = [NSString stringWithFormat: @"%@ %@ %@: ", 239 NSLocalizedString(@"Link", @""), 240 items, 241 NSLocalizedString(@"from", @"")]; 242 msg2 = NSLocalizedString(@"\nto: ", @""); 243 msg = [NSString stringWithFormat: @"%@%@%@%@?", msg1, source, msg2, destination]; 244 } 245 else if ([type isEqual: NSWorkspaceRecycleOperation]) 246 { 247 title = NSLocalizedString(@"Recycler", @""); 248 msg1 = [NSString stringWithFormat: @"%@ %@ %@: ", 249 NSLocalizedString(@"Move", @""), 250 items, 251 NSLocalizedString(@"from", @"")]; 252 msg2 = NSLocalizedString(@"\nto the Recycler", @""); 253 msg = [NSString stringWithFormat: @"%@%@%@?", msg1, source, msg2]; 254 } 255 else if ([type isEqual: @"GWorkspaceRecycleOutOperation"]) 256 { 257 title = NSLocalizedString(@"Recycler", @""); 258 msg1 = [NSString stringWithFormat: @"%@ %@ %@ ", 259 NSLocalizedString(@"Move", @""), 260 items, 261 NSLocalizedString(@"from the Recycler", @"")]; 262 msg2 = NSLocalizedString(@"\nto: ", @""); 263 msg = [NSString stringWithFormat: @"%@%@%@?", msg1, msg2, destination]; 264 } 265 else if ([type isEqual: @"GWorkspaceEmptyRecyclerOperation"]) 266 { 267 title = NSLocalizedString(@"Recycler", @""); 268 msg = NSLocalizedString(@"Empty the Recycler?", @""); 269 } 270 else if ([type isEqual: NSWorkspaceDestroyOperation]) 271 { 272 title = NSLocalizedString(@"Delete", @""); 273 msg = NSLocalizedString(@"Delete the selected objects?", @""); 274 } 275 else if ([type isEqual: NSWorkspaceDuplicateOperation]) 276 { 277 title = NSLocalizedString(@"Duplicate", @""); 278 msg = NSLocalizedString(@"Duplicate the selected objects?", @""); 279 } 280 281 if (NSRunAlertPanel(title, msg, 282 NSLocalizedString(@"OK", @""), 283 NSLocalizedString(@"Cancel", @""), 284 nil) != NSAlertDefaultReturn) { 285 [self endOperation]; 286 return; 287 } 288 } 289 [self detachOperationThread]; 290} 291 292- (void) threadWillExit: (NSNotification *)notification 293{ 294 [nc removeObserver:self 295 name:NSThreadWillExitNotification 296 object:nil]; 297 298 [nc removeObserver: self 299 name: NSConnectionDidDieNotification 300 object: execconn]; 301 302 executor = nil; 303} 304 305-(void)detachOperationThread 306{ 307 NSPort *port[2]; 308 NSArray *ports; 309 310 port[0] = (NSPort *)[NSPort port]; 311 port[1] = (NSPort *)[NSPort port]; 312 313 ports = [NSArray arrayWithObjects: port[1], port[0], nil]; 314 315 execconn = [[NSConnection alloc] initWithReceivePort: port[0] 316 sendPort: port[1]]; 317 [execconn setRootObject: self]; 318 [execconn setDelegate: self]; 319 320 [nc addObserver: self 321 selector: @selector(connectionDidDie:) 322 name: NSConnectionDidDieNotification 323 object: execconn]; 324 325 [nc addObserver: self 326 selector: @selector(threadWillExit:) 327 name: NSThreadWillExitNotification 328 object: nil]; 329 330 NS_DURING 331 { 332 [NSThread detachNewThreadSelector: @selector(setPorts:) 333 toTarget: [FileOpExecutor class] 334 withObject: ports]; 335 } 336 NS_HANDLER 337 { 338 NSRunAlertPanel(nil, 339 NSLocalizedString(@"A fatal error occured while detaching the thread!", @""), 340 NSLocalizedString(@"Continue", @""), 341 nil, 342 nil); 343 [self endOperation]; 344 } 345 NS_ENDHANDLER 346} 347 348- (NSInteger)requestUserConfirmationWithMessage:(NSString *)message 349 title:(NSString *)title 350{ 351 return NSRunAlertPanel(NSLocalizedString(title, @""), 352 NSLocalizedString(message, @""), 353 NSLocalizedString(@"Ok", @""), 354 NSLocalizedString(@"Cancel", @""), 355 nil); 356} 357 358- (NSInteger)showErrorAlertWithMessage:(NSString *)message 359{ 360 return NSRunAlertPanel(nil, 361 NSLocalizedString(message, @""), 362 NSLocalizedString(@"Ok", @""), 363 nil, 364 nil); 365} 366 367- (IBAction)pause:(id)sender 368{ 369 if (paused == NO) 370 { 371 [pauseButt setTitle: NSLocalizedString(@"Continue", @"")]; 372 paused = YES; 373 } 374 else 375 { 376 [self detachOperationThread]; 377 [pauseButt setTitle: NSLocalizedString(@"Pause", @"")]; 378 paused = NO; 379 } 380} 381 382- (IBAction)stop:(id)sender 383{ 384 if (paused) 385 { 386 [self endOperation]; 387 } 388 stopped = YES; 389} 390 391- (void)removeProcessedFiles 392{ 393 NSData *pFData; 394 NSArray *pFiles; 395 NSUInteger i; 396 397 pFData = [executor processedFiles]; 398 pFiles = [NSUnarchiver unarchiveObjectWithData: pFData]; 399 400 for (i = 0; i < [pFiles count]; i++) 401 { 402 NSDictionary *fi; 403 NSUInteger j; 404 BOOL found; 405 406 j = 0; 407 found = NO; 408 while (j < [files count] && !found) 409 { 410 fi = [files objectAtIndex:j]; 411 412 if ([[pFiles objectAtIndex:i] isEqualTo:[fi objectForKey:@"name"]]) 413 found = YES; 414 else 415 i++; 416 } 417 if (found) 418 { 419 [procFiles addObject:[files objectAtIndex:j]]; 420 [files removeObjectAtIndex:j]; 421 } 422 } 423} 424 425- (void)showProgressWin 426{ 427 if ([win isVisible] == NO) { 428 if ([type isEqual: NSWorkspaceMoveOperation]) { 429 [win setTitle: NSLocalizedString(@"Move", @"")]; 430 [fromLabel setStringValue: NSLocalizedString(@"From:", @"")]; 431 [fromField setStringValue: relativePathFittingInField(fromField, source)]; 432 [toLabel setStringValue: NSLocalizedString(@"To:", @"")]; 433 [toField setStringValue: relativePathFittingInField(fromField, destination)]; 434 435 } else if ([type isEqual: NSWorkspaceCopyOperation]) { 436 [win setTitle: NSLocalizedString(@"Copy", @"")]; 437 [fromLabel setStringValue: NSLocalizedString(@"From:", @"")]; 438 [fromField setStringValue: relativePathFittingInField(fromField, source)]; 439 [toLabel setStringValue: NSLocalizedString(@"To:", @"")]; 440 [toField setStringValue: relativePathFittingInField(fromField, destination)]; 441 442 } else if ([type isEqual: NSWorkspaceLinkOperation]) { 443 [win setTitle: NSLocalizedString(@"Link", @"")]; 444 [fromLabel setStringValue: NSLocalizedString(@"From:", @"")]; 445 [fromField setStringValue: relativePathFittingInField(fromField, source)]; 446 [toLabel setStringValue: NSLocalizedString(@"To:", @"")]; 447 [toField setStringValue: relativePathFittingInField(fromField, destination)]; 448 449 } else if ([type isEqual: NSWorkspaceDuplicateOperation]) { 450 [win setTitle: NSLocalizedString(@"Duplicate", @"")]; 451 [fromLabel setStringValue: NSLocalizedString(@"In:", @"")]; 452 [fromField setStringValue: relativePathFittingInField(fromField, destination)]; 453 [toLabel setStringValue: @""]; 454 [toField setStringValue: @""]; 455 456 } else if ([type isEqual: NSWorkspaceDestroyOperation]) { 457 [win setTitle: NSLocalizedString(@"Destroy", @"")]; 458 [fromLabel setStringValue: NSLocalizedString(@"In:", @"")]; 459 [fromField setStringValue: relativePathFittingInField(fromField, destination)]; 460 [toLabel setStringValue: @""]; 461 [toField setStringValue: @""]; 462 463 } else if ([type isEqual: NSWorkspaceRecycleOperation]) { 464 [win setTitle: NSLocalizedString(@"Move", @"")]; 465 [fromLabel setStringValue: NSLocalizedString(@"From:", @"")]; 466 [fromField setStringValue: relativePathFittingInField(fromField, source)]; 467 [toLabel setStringValue: NSLocalizedString(@"To:", @"")]; 468 [toField setStringValue: NSLocalizedString(@"the Recycler", @"")]; 469 470 } else if ([type isEqual: @"GWorkspaceRecycleOutOperation"]) { 471 [win setTitle: NSLocalizedString(@"Move", @"")]; 472 [fromLabel setStringValue: NSLocalizedString(@"From:", @"")]; 473 [fromField setStringValue: NSLocalizedString(@"the Recycler", @"")]; 474 [toLabel setStringValue: NSLocalizedString(@"To:", @"")]; 475 [toField setStringValue: relativePathFittingInField(fromField, destination)]; 476 477 } else if ([type isEqual: @"GWorkspaceEmptyRecyclerOperation"]) { 478 [win setTitle: NSLocalizedString(@"Destroy", @"")]; 479 [fromLabel setStringValue: NSLocalizedString(@"In:", @"")]; 480 [fromField setStringValue: NSLocalizedString(@"the Recycler", @"")]; 481 [toLabel setStringValue: @""]; 482 [toField setStringValue: @""]; 483 } 484 485 [progInd setIndeterminate: YES]; 486 [progInd startAnimation: self]; 487 } 488 489 [win orderFront: nil]; 490 showwin = YES; 491} 492 493- (void)setNumFiles:(int)n 494{ 495 [progInd stopAnimation: self]; 496 [progInd setIndeterminate: NO]; 497 [progInd setMinValue: 0.0]; 498 [progInd setMaxValue: n]; 499 [progInd setDoubleValue: 0.0]; 500} 501 502- (void)setProgIndicatorValue:(int)n 503{ 504 [progInd setDoubleValue: n]; 505} 506 507- (void)cleanUpExecutor 508{ 509 if (executor) 510 { 511 [nc removeObserver: self 512 name: NSConnectionDidDieNotification 513 object: execconn]; 514 [execconn setRootObject:nil]; 515 DESTROY (executor); 516 DESTROY (execconn); 517 } 518} 519 520- (void)endOperation 521{ 522 if (showwin) 523 { 524 if ([progInd isIndeterminate]) 525 [progInd stopAnimation:self]; 526 527 [win saveFrameUsingName: @"fopinfo"]; 528 [win close]; 529 } 530 531 [controller endOfFileOperation: self]; 532 [execconn setRootObject:nil]; 533} 534 535- (void)sendWillChangeNotification 536{ 537 CREATE_AUTORELEASE_POOL(arp); 538 NSMutableDictionary *dict = [NSMutableDictionary dictionary]; 539 NSUInteger i; 540 541 notifNames = [NSMutableArray new]; 542 543 for (i = 0; i < [files count]; i++) { 544 NSDictionary *fdict = [files objectAtIndex: i]; 545 NSString *name = [fdict objectForKey: @"name"]; 546 [notifNames addObject: name]; 547 } 548 549 [dict setObject: type forKey: @"operation"]; 550 [dict setObject: source forKey: @"source"]; 551 if (destination != nil) 552 [dict setObject: destination forKey: @"destination"]; 553 [dict setObject: notifNames forKey: @"files"]; 554 555 [nc postNotificationName: @"GWFileSystemWillChangeNotification" object: dict]; 556 557 [dnc postNotificationName: @"GWFileSystemWillChangeNotification" object: nil userInfo: dict]; 558 RELEASE (arp); 559} 560 561- (void)sendDidChangeNotification 562{ 563 CREATE_AUTORELEASE_POOL(arp); 564 NSMutableDictionary *notifObj = [NSMutableDictionary dictionary]; 565 566 [notifObj setObject: type forKey: @"operation"]; 567 [notifObj setObject: source forKey: @"source"]; 568 if (destination != nil) 569 [notifObj setObject: destination forKey: @"destination"]; 570 571 if (executor) { 572 NSData *data = [executor processedFiles]; 573 NSArray *processedFiles = [NSUnarchiver unarchiveObjectWithData: data]; 574 575 [notifObj setObject: processedFiles forKey: @"files"]; 576 [notifObj setObject: notifNames forKey: @"origfiles"]; 577 } else { 578 [notifObj setObject: notifNames forKey: @"files"]; 579 [notifObj setObject: notifNames forKey: @"origfiles"]; 580 } 581 582 opdone = YES; 583 584 [nc postNotificationName: @"GWFileSystemDidChangeNotification" object: notifObj]; 585 586 [dnc postNotificationName: @"GWFileSystemDidChangeNotification" object: nil userInfo: notifObj]; 587 RELEASE (arp); 588} 589 590- (void)registerExecutor:(id)anObject 591{ 592 NSData *opinfo = [NSArchiver archivedDataWithRootObject: operationDict]; 593 BOOL samename; 594 595 [anObject setProtocolForProxy: @protocol(FileOpExecutorProtocol)]; 596 executor = (id <FileOpExecutorProtocol>)[anObject retain]; 597 598 [executor setOperation: opinfo]; 599 600 if ([procFiles count] == 0) 601 { 602 samename = [executor checkSameName]; 603 604 if (samename) 605 { 606 NSString *msg = nil; 607 NSString *title = nil; 608 int result; 609 610 onlyOlder = NO; 611 if ([type isEqual: NSWorkspaceMoveOperation]) 612 { 613 msg = @"Some items have the same name;\ndo you want to replace them?"; 614 title = @"Move"; 615 } 616 else if ([type isEqual: NSWorkspaceCopyOperation]) 617 { 618 msg = @"Some items have the same name;\ndo you want to replace them?"; 619 title = @"Copy"; 620 } 621 else if ([type isEqual: NSWorkspaceLinkOperation]) 622 { 623 msg = @"Some items have the same name;\ndo you want to replace them?"; 624 title = @"Link"; 625 } 626 else if ([type isEqual: NSWorkspaceRecycleOperation]) 627 { 628 msg = @"Some items have the same name;\ndo you want to replace them?"; 629 title = @"Recycle"; 630 } 631 else if ([type isEqual: @"GWorkspaceRecycleOutOperation"]) 632 { 633 msg = @"Some items have the same name;\ndo you want to replace them?"; 634 title = @"Recycle"; 635 } 636 637 result = NSRunAlertPanel(NSLocalizedString(title, @""), NSLocalizedString(msg, @""), 638 NSLocalizedString(@"OK", @""), 639 NSLocalizedString(@"Cancel", @""), 640 NSLocalizedString(@"Only older", @"")); 641 642 if (result == NSAlertAlternateReturn) 643 { 644 [controller endOfFileOperation: self]; 645 return; 646 } 647 else if (result == NSAlertOtherReturn) 648 { 649 onlyOlder = YES; 650 } 651 } 652 } 653 654 [executor setOnlyOlder:onlyOlder]; 655 656 if (showwin) 657 [self showProgressWin]; 658 659 [self sendWillChangeNotification]; 660 661 stopped = NO; 662 paused = NO; 663 [executor calculateNumFiles:[procFiles count]]; 664} 665 666- (BOOL)connection:(NSConnection*)ancestor 667shouldMakeNewConnection:(NSConnection*)newConn 668{ 669 if (ancestor == execconn) 670 { 671 [newConn setDelegate: self]; 672 [nc addObserver: self 673 selector: @selector(connectionDidDie:) 674 name: NSConnectionDidDieNotification 675 object: newConn]; 676 return YES; 677 } 678 679 return NO; 680} 681 682- (void)connectionDidDie:(NSNotification *)notification 683{ 684 [nc removeObserver: self 685 name: NSConnectionDidDieNotification 686 object: [notification object]]; 687 688 if (opdone == NO) { 689 NSRunAlertPanel(nil, 690 NSLocalizedString(@"executor connection died!", @""), 691 NSLocalizedString(@"Continue", @""), 692 nil, 693 nil); 694 [self sendDidChangeNotification]; 695 [self endOperation]; 696 } 697} 698 699- (NSString *)type 700{ 701 return type; 702} 703 704- (NSString *)source 705{ 706 return source; 707} 708 709- (NSString *)destination 710{ 711 return destination; 712} 713 714- (NSArray *)files 715{ 716 return files; 717} 718 719- (NSArray *)dupfiles 720{ 721 return dupfiles; 722} 723 724- (int)ref 725{ 726 return ref; 727} 728 729- (BOOL)showsWindow 730{ 731 return showwin; 732} 733 734- (NSWindow *)win 735{ 736 return win; 737} 738 739- (void) getWinRect: (NSRect*)rptr 740{ 741 *rptr = NSZeroRect; 742 if (win && [win isVisible]) { 743 *rptr = [win frame]; 744 } 745} 746 747@end 748 749 750@implementation FileOpExecutor 751 752+ (void)setPorts:(NSArray *)thePorts 753{ 754 CREATE_AUTORELEASE_POOL(pool); 755 NSPort *port[2]; 756 NSConnection *conn; 757 FileOpExecutor *executor; 758 759 port[0] = [thePorts objectAtIndex: 0]; 760 port[1] = [thePorts objectAtIndex: 1]; 761 762 conn = [NSConnection connectionWithReceivePort: (NSPort *)port[0] 763 sendPort: (NSPort *)port[1]]; 764 765 executor = [[self alloc] init]; 766 [executor setFileop: thePorts]; 767 [(id)[conn rootProxy] registerExecutor: executor]; 768 RELEASE (executor); 769 770 RELEASE (pool); 771} 772 773- (void)dealloc 774{ 775 RELEASE (operation); 776 RELEASE (source); 777 RELEASE (destination); 778 RELEASE (files); 779 RELEASE (procfiles); 780 [super dealloc]; 781} 782 783- (id)init 784{ 785 self = [super init]; 786 787 if (self) { 788 fm = [NSFileManager defaultManager]; 789 samename = NO; 790 onlyolder = NO; 791 } 792 793 return self; 794} 795 796- (void)setFileop:(NSArray *)thePorts 797{ 798 NSPort *port[2]; 799 NSConnection *conn; 800 id anObject; 801 802 port[0] = [thePorts objectAtIndex: 0]; 803 port[1] = [thePorts objectAtIndex: 1]; 804 805 conn = [NSConnection connectionWithReceivePort: (NSPort *)port[0] 806 sendPort: (NSPort *)port[1]]; 807 808 anObject = (id)[conn rootProxy]; 809 [anObject setProtocolForProxy: @protocol(FileOpInfoProtocol)]; 810 fileOp = (id <FileOpInfoProtocol>)anObject; 811} 812 813- (BOOL)setOperation:(NSData *)opinfo 814{ 815 NSDictionary *opDict = [NSUnarchiver unarchiveObjectWithData: opinfo]; 816 id dictEntry; 817 818 dictEntry = [opDict objectForKey: @"operation"]; 819 if (dictEntry) { 820 ASSIGN (operation, dictEntry); 821 } 822 823 dictEntry = [opDict objectForKey: @"source"]; 824 if (dictEntry) { 825 ASSIGN (source, dictEntry); 826 } 827 828 dictEntry = [opDict objectForKey: @"destination"]; 829 if (dictEntry) { 830 ASSIGN (destination, dictEntry); 831 } 832 833 files = [NSMutableArray new]; 834 dictEntry = [opDict objectForKey: @"files"]; 835 if (dictEntry) { 836 [files addObjectsFromArray: dictEntry]; 837 } 838 839 procfiles = [NSMutableArray new]; 840 841 return YES; 842} 843 844- (BOOL)checkSameName 845{ 846 NSArray *dirContents; 847 NSUInteger i; 848 849 samename = NO; 850 851 if (([operation isEqual: @"GWorkspaceRenameOperation"]) 852 || ([operation isEqual: @"GWorkspaceCreateDirOperation"]) 853 || ([operation isEqual: @"GWorkspaceCreateFileOperation"])) { 854 /* already checked by GWorkspace */ 855 return NO; 856 } 857 858 if (destination && [files count]) 859 { 860 dirContents = [fm directoryContentsAtPath: destination]; 861 for (i = 0; i < [files count]; i++) 862 { 863 NSDictionary *dict = [files objectAtIndex: i]; 864 NSString *name = [dict objectForKey: @"name"]; 865 866 if ([dirContents containsObject: name]) 867 { 868 samename = YES; 869 break; 870 } 871 } 872 } 873 874 if (samename) 875 { 876 if (([operation isEqual: NSWorkspaceMoveOperation]) 877 || ([operation isEqual: NSWorkspaceCopyOperation]) 878 || ([operation isEqual: NSWorkspaceLinkOperation]) 879 || ([operation isEqual: @"GWorkspaceRecycleOutOperation"])) 880 { 881 return YES; 882 883 } 884 else if (([operation isEqual: NSWorkspaceDestroyOperation]) 885 || ([operation isEqual: NSWorkspaceDuplicateOperation]) 886 || ([operation isEqual: NSWorkspaceRecycleOperation]) 887 || ([operation isEqual: @"GWorkspaceEmptyRecyclerOperation"])) 888 { 889 return NO; 890 } 891 } 892 893 return NO; 894} 895 896- (void)setOnlyOlder:(BOOL)flag 897{ 898 onlyolder = flag; 899} 900 901- (oneway void)calculateNumFiles:(NSUInteger)continueFrom 902{ 903 NSUInteger i; 904 NSUInteger fnum = 0; 905 906 if (continueFrom == 0) 907 { 908 for (i = 0; i < [files count]; i++) 909 { 910 CREATE_AUTORELEASE_POOL (arp); 911 NSDictionary *dict = [files objectAtIndex: i]; 912 NSString *name = [dict objectForKey: @"name"]; 913 NSString *path = [source stringByAppendingPathComponent: name]; 914 BOOL isDir = NO; 915 916 [fm fileExistsAtPath: path isDirectory: &isDir]; 917 918 if (isDir) 919 { 920 NSDirectoryEnumerator *enumerator = [fm enumeratorAtPath: path]; 921 922 while (1) 923 { 924 CREATE_AUTORELEASE_POOL (arp2); 925 NSString *dirEntry = [enumerator nextObject]; 926 927 if (dirEntry) 928 { 929 if (stopped) 930 { 931 RELEASE (arp2); 932 break; 933 } 934 fnum++; 935 } 936 else 937 { 938 RELEASE (arp2); 939 break; 940 } 941 RELEASE (arp2); 942 } 943 } 944 else 945 { 946 fnum++; 947 } 948 949 if (stopped) 950 { 951 RELEASE (arp); 952 break; 953 } 954 RELEASE (arp); 955 } 956 957 if (stopped) 958 { 959 [fileOp endOperation]; 960 [fileOp cleanUpExecutor]; 961 } 962 963 fcount = 0; 964 stepcount = 0; 965 966 if (fnum < PROGR_STEPS) 967 { 968 progstep = 1.0; 969 } 970 else 971 { 972 progstep = fnum / PROGR_STEPS; 973 } 974 [fileOp setNumFiles: fnum]; 975 } 976 else 977 { 978 fcount = continueFrom; 979 stepcount = continueFrom; 980 } 981 [self performOperation]; 982} 983 984- (oneway void)performOperation 985{ 986 canupdate = YES; 987 988 if ([operation isEqual: NSWorkspaceMoveOperation] 989 || [operation isEqual: @"GWorkspaceRecycleOutOperation"]) 990 { 991 [self doMove]; 992 } 993 else if ([operation isEqual: NSWorkspaceCopyOperation]) 994 { 995 [self doCopy]; 996 } 997 else if ([operation isEqual: NSWorkspaceLinkOperation]) 998 { 999 [self doLink]; 1000 } 1001 else if ([operation isEqual: NSWorkspaceDestroyOperation] 1002 || [operation isEqual: @"GWorkspaceEmptyRecyclerOperation"]) 1003 { 1004 [self doRemove]; 1005 } 1006 else if ([operation isEqual: NSWorkspaceDuplicateOperation]) 1007 { 1008 [self doDuplicate]; 1009 } 1010 else if ([operation isEqual: NSWorkspaceRecycleOperation]) 1011 { 1012 [self doTrash]; 1013 } 1014 else if ([operation isEqual: @"GWorkspaceRenameOperation"]) 1015 { 1016 [self doRename]; 1017 } 1018 else if ([operation isEqual: @"GWorkspaceCreateDirOperation"]) 1019 { 1020 [self doNewFolder]; 1021 } 1022 else if ([operation isEqual: @"GWorkspaceCreateFileOperation"]) 1023 { 1024 [self doNewFile]; 1025 } 1026} 1027 1028- (NSData *)processedFiles 1029{ 1030 return [NSArchiver archivedDataWithRootObject: procfiles]; 1031} 1032 1033#define CHECK_DONE \ 1034if (([files count] == 0) || stopped || paused) break 1035 1036#define GET_FILENAME \ 1037fileinfo = [files objectAtIndex: 0]; \ 1038RETAIN (fileinfo); \ 1039filename = [fileinfo objectForKey: @"name"]; 1040 1041- (void)doMove 1042{ 1043 while (1) 1044 { 1045 CHECK_DONE; 1046 GET_FILENAME; 1047 1048 if ((samename == NO) || (samename && [self removeExisting: fileinfo])) 1049 { 1050 NSString *src = [source stringByAppendingPathComponent: filename]; 1051 NSString *dst = [destination stringByAppendingPathComponent: filename]; 1052 1053 if ([fm movePath: src toPath: dst handler: self]) 1054 { 1055 [procfiles addObject: filename]; 1056 } 1057 else 1058 { 1059 /* check for broken symlink */ 1060 NSDictionary *attributes = [fm fileAttributesAtPath: src traverseLink: NO]; 1061 1062 if (attributes && ([attributes fileType] == NSFileTypeSymbolicLink) && ([fm fileExistsAtPath: src] == NO)) 1063 { 1064 if ([fm copyPath: src toPath: dst handler: self] && [fm removeFileAtPath: src handler: self]) 1065 { 1066 [procfiles addObject: filename]; 1067 } 1068 } 1069 } 1070 } 1071 1072 [files removeObject: fileinfo]; 1073 RELEASE (fileinfo); 1074 } 1075 1076 [fileOp sendDidChangeNotification]; 1077 if (([files count] == 0) || stopped) 1078 { 1079 [fileOp endOperation]; 1080 } 1081 else if (paused) 1082 { 1083 [fileOp removeProcessedFiles]; 1084 } 1085 [fileOp cleanUpExecutor]; 1086} 1087 1088- (void)doCopy 1089{ 1090 while (1) 1091 { 1092 CHECK_DONE; 1093 GET_FILENAME; 1094 1095 if ((samename == NO) || (samename && [self removeExisting: fileinfo])) 1096 { 1097 if ([fm copyPath: [source stringByAppendingPathComponent: filename] 1098 toPath: [destination stringByAppendingPathComponent: filename] 1099 handler: self]) 1100 { 1101 [procfiles addObject: filename]; 1102 } 1103 } 1104 [files removeObject: fileinfo]; 1105 RELEASE (fileinfo); 1106 } 1107 1108 [fileOp sendDidChangeNotification]; 1109 if (([files count] == 0) || stopped) 1110 { 1111 [fileOp endOperation]; 1112 } 1113 else if (paused) 1114 { 1115 [fileOp removeProcessedFiles]; 1116 } 1117 [fileOp cleanUpExecutor]; 1118} 1119 1120- (void)doLink 1121{ 1122 while (1) 1123 { 1124 CHECK_DONE; 1125 GET_FILENAME; 1126 1127 if ((samename == NO) || (samename && [self removeExisting: fileinfo])) 1128 { 1129 NSString *dst = [destination stringByAppendingPathComponent: filename]; 1130 NSString *src = [source stringByAppendingPathComponent: filename]; 1131 1132 if ([fm createSymbolicLinkAtPath: dst pathContent: src]) 1133 { 1134 [procfiles addObject: filename]; 1135 } 1136 } 1137 [files removeObject: fileinfo]; 1138 RELEASE (fileinfo); 1139 } 1140 1141 [fileOp sendDidChangeNotification]; 1142 if (([files count] == 0) || stopped) 1143 { 1144 [fileOp endOperation]; 1145 } 1146 else if (paused) 1147 { 1148 [fileOp removeProcessedFiles]; 1149 } 1150 [fileOp cleanUpExecutor]; 1151} 1152 1153- (void)doRemove 1154{ 1155 while (1) 1156 { 1157 CHECK_DONE; 1158 GET_FILENAME; 1159 1160 if ([fm removeFileAtPath: [source stringByAppendingPathComponent: filename] 1161 handler: self]) 1162 { 1163 [procfiles addObject: filename]; 1164 } 1165 [files removeObject: fileinfo]; 1166 RELEASE (fileinfo); 1167 } 1168 1169 [fileOp sendDidChangeNotification]; 1170 if (([files count] == 0) || stopped) 1171 { 1172 [fileOp endOperation]; 1173 } 1174 else if (paused) 1175 { 1176 [fileOp removeProcessedFiles]; 1177 } 1178 [fileOp cleanUpExecutor]; 1179} 1180 1181- (void)doDuplicate 1182{ 1183 NSString *copystr = NSLocalizedString(@"_copy", @""); 1184 NSString *base; 1185 NSString *ext; 1186 NSString *destpath; 1187 NSString *newname; 1188 NSString *ntmp; 1189 1190 while (1) { 1191 int count = 1; 1192 1193 CHECK_DONE; 1194 GET_FILENAME; 1195 1196 newname = [NSString stringWithString: filename]; 1197 ext = [newname pathExtension]; 1198 base = [newname stringByDeletingPathExtension]; 1199 1200 while (1) { 1201 if (count == 1) { 1202 ntmp = [NSString stringWithFormat: @"%@%@", base, copystr]; 1203 if ([ext length]) { 1204 ntmp = [ntmp stringByAppendingPathExtension: ext]; 1205 } 1206 } else { 1207 ntmp = [NSString stringWithFormat: @"%@%@%i", base, copystr, count]; 1208 if ([ext length]) { 1209 ntmp = [ntmp stringByAppendingPathExtension: ext]; 1210 } 1211 } 1212 1213 destpath = [destination stringByAppendingPathComponent: ntmp]; 1214 1215 if ([fm fileExistsAtPath: destpath] == NO) { 1216 newname = ntmp; 1217 break; 1218 } else { 1219 count++; 1220 } 1221 } 1222 1223 if ([fm copyPath: [destination stringByAppendingPathComponent: filename] 1224 toPath: destpath 1225 handler: self]) { 1226 [procfiles addObject: newname]; 1227 } 1228 [files removeObject: fileinfo]; 1229 RELEASE (fileinfo); 1230 } 1231 1232 [fileOp sendDidChangeNotification]; 1233 if (([files count] == 0) || stopped) 1234 { 1235 [fileOp endOperation]; 1236 } 1237 else if (paused) 1238 { 1239 [fileOp removeProcessedFiles]; 1240 } 1241 [fileOp cleanUpExecutor]; 1242} 1243 1244- (void)doRename 1245{ 1246 GET_FILENAME; 1247 1248 if ([fm movePath: source toPath: destination handler: self]) 1249 { 1250 [procfiles addObject: filename]; 1251 1252 } 1253 else 1254 { 1255 /* check for broken symlink */ 1256 NSDictionary *attributes = [fm fileAttributesAtPath: source traverseLink: NO]; 1257 1258 if (attributes && ([attributes fileType] == NSFileTypeSymbolicLink) 1259 && ([fm fileExistsAtPath: source] == NO)) { 1260 if ([fm copyPath: source toPath: destination handler: self] 1261 && [fm removeFileAtPath: source handler: self]) { 1262 [procfiles addObject: filename]; 1263 } 1264 } 1265 } 1266 1267 [files removeObject: fileinfo]; 1268 RELEASE (fileinfo); 1269 1270 [fileOp sendDidChangeNotification]; 1271 [fileOp endOperation]; 1272 [fileOp cleanUpExecutor]; 1273} 1274 1275- (void)doNewFolder 1276{ 1277 GET_FILENAME; 1278 1279 if ([fm createDirectoryAtPath: [destination stringByAppendingPathComponent: filename] 1280 attributes: nil]) { 1281 [procfiles addObject: filename]; 1282 } 1283 [files removeObject: fileinfo]; 1284 RELEASE (fileinfo); 1285 1286 [fileOp sendDidChangeNotification]; 1287 [fileOp endOperation]; 1288 [fileOp cleanUpExecutor]; 1289} 1290 1291- (void)doNewFile 1292{ 1293 GET_FILENAME; 1294 1295 if ([fm createFileAtPath: [destination stringByAppendingPathComponent: filename] 1296 contents: nil 1297 attributes: nil]) { 1298 [procfiles addObject: filename]; 1299 } 1300 [files removeObject: fileinfo]; 1301 RELEASE (fileinfo); 1302 1303 [fileOp sendDidChangeNotification]; 1304 [fileOp endOperation]; 1305 [fileOp cleanUpExecutor]; 1306} 1307 1308- (void)doTrash 1309{ 1310 NSString *copystr = NSLocalizedString(@"_copy", @""); 1311 NSString *srcpath; 1312 NSString *destpath; 1313 NSString *newname; 1314 NSString *ntmp; 1315 1316 while (1) 1317 { 1318 CHECK_DONE; 1319 GET_FILENAME; 1320 1321 newname = [NSString stringWithString: filename]; 1322 srcpath = [source stringByAppendingPathComponent: filename]; 1323 destpath = [destination stringByAppendingPathComponent: newname]; 1324 1325 if ([fm fileExistsAtPath: destpath]) { 1326 NSString *ext = [filename pathExtension]; 1327 NSString *base = [filename stringByDeletingPathExtension]; 1328 NSUInteger count = 1; 1329 1330 while (1) { 1331 if (count == 1) { 1332 ntmp = [NSString stringWithFormat: @"%@%@", base, copystr]; 1333 if ([ext length]) { 1334 ntmp = [ntmp stringByAppendingPathExtension: ext]; 1335 } 1336 } else { 1337 ntmp = [NSString stringWithFormat: @"%@%@%lu", base, copystr, (unsigned long)count]; 1338 if ([ext length]) { 1339 ntmp = [ntmp stringByAppendingPathExtension: ext]; 1340 } 1341 } 1342 1343 destpath = [destination stringByAppendingPathComponent: ntmp]; 1344 1345 if ([fm fileExistsAtPath: destpath] == NO) { 1346 newname = ntmp; 1347 break; 1348 } else { 1349 count++; 1350 } 1351 } 1352 } 1353 1354 if ([fm movePath: srcpath toPath: destpath handler: self]) { 1355 [procfiles addObject: newname]; 1356 1357 } else { 1358 /* check for broken symlink */ 1359 NSDictionary *attributes = [fm fileAttributesAtPath: srcpath traverseLink: NO]; 1360 1361 if (attributes && ([attributes fileType] == NSFileTypeSymbolicLink) 1362 && ([fm fileExistsAtPath: srcpath] == NO)) { 1363 if ([fm copyPath: srcpath toPath: destpath handler: self] 1364 && [fm removeFileAtPath: srcpath handler: self]) { 1365 [procfiles addObject: newname]; 1366 } 1367 } 1368 } 1369 1370 [files removeObject: fileinfo]; 1371 RELEASE (fileinfo); 1372 } 1373 1374 [fileOp sendDidChangeNotification]; 1375 if (([files count] == 0) || stopped) 1376 { 1377 [fileOp endOperation]; 1378 } 1379 else if (paused) 1380 { 1381 [fileOp removeProcessedFiles]; 1382 } 1383 [fileOp cleanUpExecutor]; 1384} 1385 1386- (BOOL)removeExisting:(NSDictionary *)info 1387{ 1388 NSString *fname = [info objectForKey: @"name"]; 1389 NSString *destpath = [destination stringByAppendingPathComponent: fname]; 1390 BOOL isdir; 1391 1392 canupdate = NO; 1393 1394 if ([fm fileExistsAtPath: destpath isDirectory: &isdir]) 1395 { 1396 if (onlyolder) 1397 { 1398 NSDictionary *attributes = [fm fileAttributesAtPath: destpath traverseLink: NO]; 1399 NSDate *dstdate = [attributes objectForKey: NSFileModificationDate]; 1400 NSDate *srcdate = [info objectForKey: @"date"]; 1401 1402 if ([srcdate isEqual: dstdate] == NO) 1403 { 1404 if ([[srcdate earlierDate: dstdate] isEqual: srcdate]) 1405 { 1406 canupdate = YES; 1407 return NO; 1408 } 1409 } 1410 else 1411 { 1412 canupdate = YES; 1413 return NO; 1414 } 1415 } 1416 1417 [fm removeFileAtPath: destpath handler: self]; 1418 } 1419 1420 canupdate = YES; 1421 1422 return YES; 1423} 1424 1425- (NSDictionary *)infoForFilename:(NSString *)name 1426{ 1427 int i; 1428 1429 for (i = 0; i < [files count]; i++) { 1430 NSDictionary *info = [files objectAtIndex: i]; 1431 1432 if ([[info objectForKey: @"name"] isEqual: name]) { 1433 return info; 1434 } 1435 } 1436 1437 return nil; 1438} 1439 1440 1441- (BOOL)fileManager:(NSFileManager *)manager 1442 shouldProceedAfterError:(NSDictionary *)errorDict 1443{ 1444 NSString *path; 1445 NSString *error; 1446 NSString *msg; 1447 int result; 1448 1449 error = [errorDict objectForKey: @"Error"]; 1450 1451 if ([error hasPrefix: @"Unable to change NSFileOwnerAccountID to to"] 1452 || [error hasPrefix: @"Unable to change NSFileOwnerAccountName to"] 1453 || [error hasPrefix: @"Unable to change NSFileGroupOwnerAccountID to"] 1454 || [error hasPrefix: @"Unable to change NSFileGroupOwnerAccountName to"] 1455 || [error hasPrefix: @"Unable to change NSFilePosixPermissions to"] 1456 || [error hasPrefix: @"Unable to change NSFileModificationDate to"]) { 1457 return YES; 1458 } 1459 1460 path = [NSString stringWithString: [errorDict objectForKey: @"NSFilePath"]]; 1461 1462 msg = [NSString stringWithFormat: @"%@ %@\n%@ %@\n", 1463 NSLocalizedString(@"File operation error:", @""), 1464 error, 1465 NSLocalizedString(@"with file:", @""), 1466 path]; 1467 1468 result = [fileOp requestUserConfirmationWithMessage: msg title: @"Error"]; 1469 1470 if (result != NSAlertDefaultReturn) 1471 { 1472 [fileOp endOperation]; 1473 [fileOp cleanUpExecutor]; 1474 } 1475 else 1476 { 1477 BOOL found = NO; 1478 1479 while (1) 1480 { 1481 NSDictionary *info = [self infoForFilename: [path lastPathComponent]]; 1482 1483 if ([path isEqual: source]) 1484 break; 1485 1486 if (info) 1487 { 1488 [files removeObject: info]; 1489 found = YES; 1490 break; 1491 } 1492 1493 path = [path stringByDeletingLastPathComponent]; 1494 } 1495 1496 if ([files count]) 1497 { 1498 if (found) 1499 { 1500 [self performOperation]; 1501 } 1502 else 1503 { 1504 [fileOp showErrorAlertWithMessage: @"File Operation Error!"]; 1505 [fileOp endOperation]; 1506 [fileOp cleanUpExecutor]; 1507 } 1508 } 1509 else 1510 { 1511 [fileOp endOperation]; 1512 [fileOp cleanUpExecutor]; 1513 } 1514 } 1515 1516 return YES; 1517} 1518 1519- (void)fileManager:(NSFileManager *)manager willProcessPath:(NSString *)path 1520{ 1521 if (canupdate) { 1522 fcount++; 1523 stepcount++; 1524 1525 if (stepcount >= progstep) { 1526 stepcount = 0; 1527 [fileOp setProgIndicatorValue: fcount]; 1528 } 1529 } 1530 1531 if (stopped) 1532 { 1533 [fileOp endOperation]; 1534 [fileOp cleanUpExecutor]; 1535 } 1536} 1537 1538@end 1539 1540