Merged [15040]: Trying some magic: 5 seconds after the last unreachable host is repor...
[adiumx.git] / Source / ESFileTransferController.m
blobd3fe76ece4adce7b2d0be6ed980ba56611e0f6ea
1 /* 
2  * Adium is the legal property of its developers, whose names are listed in the copyright file included
3  * with this source distribution.
4  * 
5  * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
6  * General Public License as published by the Free Software Foundation; either version 2 of the License,
7  * or (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
10  * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
11  * Public License for more details.
12  * 
13  * You should have received a copy of the GNU General Public License along with this program; if not,
14  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
15  */
17 #import "AIAccountController.h"
18 #import "AIContactController.h"
19 #import "AIMenuController.h"
20 #import "AIToolbarController.h"
21 #import "ESContactAlertsController.h"
22 #import "ESFileTransferController.h"
23 #import "ESFileTransferPreferences.h"
24 #import "ESFileTransferProgressWindowController.h"
25 #import "ESFileTransferRequestPromptController.h"
26 #import <AIUtilities/AIDictionaryAdditions.h>
27 #import <AIUtilities/AIMenuAdditions.h>
28 #import <AIUtilities/AIToolbarUtilities.h>
29 #import <AIUtilities/CBObjectAdditions.h>
30 #import <AIUtilities/AIStringAdditions.h>
31 #import <AIUtilities/ESImageAdditions.h>
32 #import <AIUtilities/AIExceptionHandlingUtilities.h>
33 #import <Adium/AIAccount.h>
34 #import <Adium/AIListContact.h>
35 #import <Adium/AIListObject.h>
36 #import <Adium/ESFileTransfer.h>
38 #define SEND_FILE                                       AILocalizedString(@"Send File",nil)
39 #define SEND_FILE_WITH_ELLIPSIS         [SEND_FILE stringByAppendingEllipsis]
40 #define CONTACT                                         AILocalizedString(@"Contact",nil)
42 #define SEND_FILE_IDENTIFIER            @"SendFile"
44 #define FILE_TRANSFER_DEFAULT_PREFS     @"FileTransferPrefs"
46 #define SAFE_FILE_EXTENSIONS_SET        [NSSet setWithObjects:@"jpg",@"jpeg",@"gif",@"png",@"tif",@"tiff",@"psd",@"pdf",@"txt",@"rtf",@"html",@"htm",@"swf",@"mp3",@"wma",@"wmv",@"ogg",@"ogm",@"mov",@"mpg",@"mpeg",@"m1v",@"m2v",@"mp4",@"avi",@"vob",@"avi",@"asx",@"asf",@"pls",@"m3u",@"rmp",@"aif",@"aiff",@"aifc",@"wav",@"wave",@"m4a",@"m4p",@"m4b",@"dmg",@"udif",@"ndif",@"dart",@"sparseimage",@"cdr",@"dvdr",@"iso",@"img",@"toast",@"rar",@"sit",@"sitx",@"bin",@"hqx",@"zip",@"gz",@"tgz",@"tar",@"bz",@"bz2",@"tbz",@"z",@"taz",@"uu",@"uue",@"colloquytranscript",@"torrent",@"AdiumIcon",@"AdiumSoundset",@"AdiumEmoticon",@"AdiumMessageStyle",nil]
48 static ESFileTransferPreferences *preferences;
50 @interface ESFileTransferController (PRIVATE)
51 - (void)configureFileTransferProgressWindow;
52 - (void)showProgressWindow:(id)sender;
53 - (void)showProgressWindowIfNotOpen:(id)sender;
55 - (void)_finishReceiveRequestForFileTransfer:(ESFileTransfer *)fileTransfer localFilename:(NSString *)localFilename;
57 - (BOOL)shouldOpenCompleteFileTransfer:(ESFileTransfer *)fileTransfer;
58 - (void)_removeFileTransfer:(ESFileTransfer *)fileTransfer;
59 @end
61 @implementation ESFileTransferController
63 //init and close
64 - (void)initController
66         fileTransferArray = [[NSMutableArray alloc] init];
67         safeFileExtensions = nil;
69     //Add our get info contextual menu item
70     menuItem_sendFileContext = [[NSMenuItem alloc] initWithTitle:SEND_FILE
71                                                                                                                  target:self action:@selector(contextualMenuSendFile:)
72                                                                                                   keyEquivalent:@""];
73         [[adium menuController] addContextualMenuItem:menuItem_sendFileContext toLocation:Context_Contact_Action];
75         //Register the events we generate
76         ESContactAlertsController *contactAlertsController = [adium contactAlertsController];
77         [contactAlertsController registerEventID:FILE_TRANSFER_REQUEST withHandler:self inGroup:AIFileTransferEventHandlerGroup globalOnly:YES];
78         [contactAlertsController registerEventID:FILE_TRANSFER_BEGAN withHandler:self inGroup:AIFileTransferEventHandlerGroup globalOnly:YES];
79         [contactAlertsController registerEventID:FILE_TRANSFER_CANCELED withHandler:self inGroup:AIFileTransferEventHandlerGroup globalOnly:YES];
80         [contactAlertsController registerEventID:FILE_TRANSFER_COMPLETE withHandler:self inGroup:AIFileTransferEventHandlerGroup globalOnly:YES];
82     //Install the Send File menu item
83         menuItem_sendFile = [[NSMenuItem alloc] initWithTitle:SEND_FILE
84                                                                                                    target:self action:@selector(sendFileToSelectedContact:)
85                                                                                         keyEquivalent:@"F"];
86         [menuItem_sendFile setKeyEquivalentModifierMask:(NSCommandKeyMask | NSShiftKeyMask)];
87         [[adium menuController] addMenuItem:menuItem_sendFile toLocation:LOC_Contact_Action];
88         
89         //Add our "Send File" toolbar item
90         NSToolbarItem   *toolbarItem;
91     toolbarItem = [AIToolbarUtilities toolbarItemWithIdentifier:SEND_FILE_IDENTIFIER
92                                                                                                                   label:SEND_FILE
93                                                                                                    paletteLabel:SEND_FILE
94                                                                                                                 toolTip:AILocalizedString(@"Send a file",nil)
95                                                                                                                  target:self
96                                                                                                 settingSelector:@selector(setImage:)
97                                                                                                         itemContent:[NSImage imageNamed:@"sendfile" forClass:[self class]]
98                                                                                                                  action:@selector(sendFileToSelectedContact:)
99                                                                                                                    menu:nil];
100     [[adium toolbarController] registerToolbarItem:toolbarItem forToolbarType:@"ListObject"];
102         AIPreferenceController *preferenceController = [adium preferenceController];
103     //Register our default preferences
104     [preferenceController registerDefaults:[NSDictionary dictionaryNamed:FILE_TRANSFER_DEFAULT_PREFS
105                                                                                                                                                 forClass:[self class]] 
106                                                                                   forGroup:PREF_GROUP_FILE_TRANSFER];
107     
108     //Observe pref changes
109         [preferenceController registerPreferenceObserver:self forGroup:PREF_GROUP_FILE_TRANSFER];
110         preferences = [[ESFileTransferPreferences preferencePane] retain];
112         //Set up the file transfer progress window
113         [self configureFileTransferProgressWindow];
116 - (void)closeController
118     [[adium preferenceController] unregisterPreferenceObserver:self];
121 - (void)dealloc
123         [super dealloc];
124         
125         [safeFileExtensions release]; safeFileExtensions = nil;
126         [fileTransferArray release]; fileTransferArray = nil;
129 #pragma mark Access to file transfer objects
130 - (ESFileTransfer *)newFileTransferWithContact:(AIListContact *)inContact forAccount:(AIAccount *)inAccount
132         ESFileTransfer *fileTransfer;
133         
134         fileTransfer = [ESFileTransfer fileTransferWithContact:inContact
135                                                                                                 forAccount:inAccount];
136         [fileTransferArray addObject:fileTransfer];
137         [fileTransfer setStatus:Not_Started_FileTransfer];
139         //Wait until the next run loop to inform observers of the new file transfer object;
140         //this way the code which requested a new ESFileTransfer has time to configure it before we
141         //dispaly information to the user
142         [[adium notificationCenter] performSelector:@selector(postNotificationName:object:)
143                                                                          withObject:FileTransfer_NewFileTransfer 
144                                                                          withObject:fileTransfer
145                                                                          afterDelay:0.0001];
147         return(fileTransfer);
150 - (NSArray *)fileTransferArray
152         return(fileTransferArray);
155 //Remove a file transfer from our array.
156 - (void)_removeFileTransfer:(ESFileTransfer *)fileTransfer
158         [fileTransferArray removeObject:fileTransfer];
161 #pragma mark Sending and receiving
162 //Sent by an account when it gets a request for us to receive a file; prompt the user for a save location
163 - (NSWindowController *)receiveRequestForFileTransfer:(ESFileTransfer *)fileTransfer
165         AIListContact                           *listContact = [fileTransfer contact];
166         NSString                                        *localFilename = nil;
167         NSWindowController                      *promptController = nil;
169         [fileTransfer setType:Incoming_FileTransfer];
171         [[adium contactAlertsController] generateEvent:FILE_TRANSFER_REQUEST
172                                                                          forListObject:listContact
173                                                                                   userInfo:fileTransfer
174                                           previouslyPerformedActionIDs:nil];
176         if((autoAcceptType == AutoAccept_All) ||
177            ((autoAcceptType == AutoAccept_FromContactList) && (![listContact isStranger]))){
178                 NSString        *preferredDownloadFolder = [[adium preferenceController] userPreferredDownloadFolder];
179                 NSString        *remoteFilename = [fileTransfer remoteFilename];
180                 
181                 //If we should autoaccept, determine the local filename  and proceed to accept the request
182                 localFilename = [preferredDownloadFolder stringByAppendingPathComponent:remoteFilename];
183                 
184                 /* If the file does not exist, immediately accept the receive request.
185                  * If it does, display a Save As dialog.
186                  */
187                 if(![[NSFileManager defaultManager] fileExistsAtPath:localFilename]){
188                         [self _finishReceiveRequestForFileTransfer:fileTransfer localFilename:localFilename];
189                         
190                 }else{
191                         //Prompt for a location to save; savePanelDidEnd::: will release the retained fileTransfer and the savePanel
192                         [[[NSSavePanel savePanel] retain] beginSheetForDirectory:preferredDownloadFolder
193                                                                             file:remoteFilename
194                                                                   modalForWindow:nil
195                                                                    modalDelegate:self
196                                                                   didEndSelector:@selector(savePanelDidEnd:returnCode:contextInfo:)
197                                                                      contextInfo:[fileTransfer retain]];
198                 }
200         } else {
201                 //Prompt to accept/deny
202                 promptController = [ESFileTransferRequestPromptController displayPromptForFileTransfer:fileTransfer
203                                                                                                                                                            notifyingTarget:self
204                                                                                                                                                                           selector:@selector(_finishReceiveRequestForFileTransfer:localFilename:)];
205         }
206         
207         return promptController;
211 - (void)savePanelDidEnd:(NSSavePanel *)savePanel returnCode:(int)returnCode contextInfo:(ESFileTransfer *)fileTransfer
213         NSString        *localFilename = nil;
214         
215         if(returnCode == NSOKButton){
216                 localFilename = [savePanel filename];
217         }
219         //Pass nil to cancel, if the user didn't press OK
220         [self  _finishReceiveRequestForFileTransfer:fileTransfer
221                                                                   localFilename:localFilename];
222         
223         //Match the retain made when invoking the save panel above
224         [fileTransfer release];
226         //release the save panel too
227         [savePanel release];    
231  * @brief Finish the receive request process
233  * Called by either ESFileTransferRequestPromptController or self, this method is the last step in accepting or
234  * refusing a request to be sent a file.
236  * @param fileTransfer The file transfer in question
237  * @param localFilename Full path at which to save the file.  If anything exists at this path it will be overwritten without further confirmation.  Pass nil to deny the transfer.
238  */
239 - (void)_finishReceiveRequestForFileTransfer:(ESFileTransfer *)fileTransfer localFilename:(NSString *)localFilename
240 {       
241         if(localFilename){
242                 [fileTransfer setLocalFilename:localFilename];
243                 [fileTransfer setStatus:Accepted_FileTransfer];
245                 [(AIAccount<AIAccount_Files> *)[fileTransfer account] acceptFileTransferRequest:fileTransfer];
246                 
247                 if(showProgressWindow){
248                         [self showProgressWindowIfNotOpen:nil];
249                 }
250                 
251         }else{
252                 [(AIAccount<AIAccount_Files> *)[fileTransfer account] rejectFileReceiveRequest:fileTransfer];
253                 [fileTransfer setStatus:Canceled_Local_FileTransfer];
254         }       
257 //Prompt the user for the file to send via an Open File dialogue
258 - (void)requestForSendingFileToListContact:(AIListContact *)listContact
260         NSOpenPanel *openPanel = [NSOpenPanel openPanel];
261         [openPanel setTitle:[NSString stringWithFormat:AILocalizedString(@"Send File to %@",nil),[listContact displayName]]];
262         
263         if([openPanel runModalForDirectory:nil file:nil types:nil] == NSOKButton){
264                 [self sendFile:[openPanel filename] toListContact:listContact];
265         }
268 - (NSString *)pathToArchiveOfFolder:(NSString *)inPath
270         NSString                *pathToArchive = nil;
271         NSFileManager   *defaultManager = [NSFileManager defaultManager];
272         NSString                *tmpDir;
274         //Desired folder: /private/tmp/$UID/`uuidgen`
275         tmpDir = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]];
276         if (tmpDir) {
277                 NSString        *launchPath = [[[@"/" stringByAppendingPathComponent:@"usr"] stringByAppendingPathComponent:@"bin"] stringByAppendingPathComponent:@"zip"];
279                 //Proceed only if /usr/bin/zip exists
280                 if ([defaultManager fileExistsAtPath:launchPath]) {
281                         NSString        *folderName = [inPath lastPathComponent];
282                         NSString        *destinationName = folderName;
283                         NSArray         *arguments;
284                         NSTask          *zipTask;
285                         int                     uniqueNameCounter = 0;
286                         BOOL            success = NO;
288                         //Ensure our temporary directory exists [it never will the first time this method is called]
289                         [defaultManager createDirectoryAtPath:tmpDir attributes:nil];
291                         //Get a unique name if necessary. This could happen if we are sending this folder multiple times.
292                         while([defaultManager fileExistsAtPath:[tmpDir stringByAppendingPathComponent:destinationName]]){
293                                 destinationName = [NSString stringWithFormat:@"%@-%i",folderName,++uniqueNameCounter];
294                         }
296                         pathToArchive = [tmpDir stringByAppendingPathComponent:[destinationName stringByAppendingPathExtension:@"zip"]];
298                         arguments = [NSArray arrayWithObjects:
299                                 @"-r", //we'll want to store recursively
300                                 @"-1", //use the fastest level of compression that isn't storage; the user can compress manually to do better
301                                 @"-q", //shhh!
302                                 pathToArchive,   //output to our destination name
303                                 folderName, //store the folder
304                                 nil];
305                         
306                         zipTask = [[NSTask alloc] init];
307                         [zipTask setLaunchPath:launchPath];
308                         [zipTask setArguments:arguments];
309                         [zipTask setCurrentDirectoryPath:[inPath stringByDeletingLastPathComponent]];
310                         
311                         AI_DURING
312                                 [zipTask launch];
313                                 [zipTask waitUntilExit];
314                                 success = ([zipTask terminationStatus] == 0);
315                         AI_HANDLER
316                                 /* No exception handler needed */
317                         AI_ENDHANDLER
318                         [zipTask release];
319                                 
320                         if(!success) pathToArchive = nil;
321                 }
322         }
324         return pathToArchive;
327 //Initiate sending a file at a specified path to listContact
328 - (void)sendFile:(NSString *)inPath toListContact:(AIListContact *)listContact
330         AIAccount               *account;
331         ESFileTransfer  *fileTransfer;
332         
333         if (account = [[adium accountController] preferredAccountForSendingContentType:FILE_TRANSFER_TYPE
334                                                                                                                                                  toContact:listContact]){
335                 NSFileManager   *defaultManager = [NSFileManager defaultManager];
336                 BOOL                    isDir;
337                 
338                 //Set up a fileTransfer object
339                 fileTransfer = [self newFileTransferWithContact:listContact
340                                                                                          forAccount:account];
341                 [fileTransfer setType:Outgoing_FileTransfer];
343                 if([defaultManager fileExistsAtPath:inPath isDirectory:&isDir]){
344                         //If we get a directory, compress it first (this could be specified on a per-account basis later if we have services supporting folder transfer)
345                         if(isDir){
346                                 inPath = [self pathToArchiveOfFolder:inPath];
347                         }
348                         
349                         if(inPath){
350                                 [fileTransfer setLocalFilename:inPath];
351                                 [fileTransfer setSize:[[[defaultManager fileAttributesAtPath:inPath
352                                                                                                                                 traverseLink:YES] objectForKey:NSFileSize] longValue]];
353                                 
354                                 //The fileTransfer object should now have everything the account needs to begin transferring
355                                 [(AIAccount<AIAccount_Files> *)account beginSendOfFileTransfer:fileTransfer];
356                                 
357                                 if(showProgressWindow){
358                                         [self showProgressWindowIfNotOpen:nil];
359                                 }
360                         }
361                 }
362         }
365 //Menu or context menu item for sending a file was selected - possible only when a listContact is selected
366 - (IBAction)sendFileToSelectedContact:(id)sender
368         //Get the "selected" list object (contact list or message window)
369         AIListObject    *selectedObject;
370         AIListContact   *listContact = nil;
371         
372         selectedObject = [[adium contactController] selectedListObject];
373         if ([selectedObject isKindOfClass:[AIListContact class]]){
374                 listContact = [[adium contactController] preferredContactForContentType:FILE_TRANSFER_TYPE
375                                                                                                                                  forListContact:(AIListContact *)selectedObject];
376         }
377         
378         if(listContact){
379                 [self requestForSendingFileToListContact:listContact];
380         }
382 //Prompt for a new contact with the current tab's name
383 - (IBAction)contextualMenuSendFile:(id)sender
385         AIListObject    *selectedObject = [[adium menuController] currentContextMenuObject];
386         AIListContact   *listContact = [[adium contactController] preferredContactForContentType:FILE_TRANSFER_TYPE
387                                                                                                                                                           forListContact:(AIListContact *)selectedObject];
388         
389         [NSApp activateIgnoringOtherApps:YES];
390         [self requestForSendingFileToListContact:listContact];
393 #pragma mark Status updates
394 - (void)fileTransfer:(ESFileTransfer *)fileTransfer didSetStatus:(FileTransferStatus)status
396         switch(status){
397                 case Accepted_FileTransfer:
398                 {
399                         [[adium contactAlertsController] generateEvent:FILE_TRANSFER_BEGAN
400                                                                                          forListObject:[fileTransfer contact] 
401                                                                                                   userInfo:fileTransfer
402                                                           previouslyPerformedActionIDs:nil];
404                         if(showProgressWindow){
405                                 [self showProgressWindowIfNotOpen:nil];
406                         }
407                         
408                         break;
409                 }
410                 case Complete_FileTransfer:
411                 {               
412                         [[adium contactAlertsController] generateEvent:FILE_TRANSFER_COMPLETE
413                                                                                          forListObject:[fileTransfer contact] 
414                                                                                                   userInfo:fileTransfer
415                                                           previouslyPerformedActionIDs:nil];
416                         
417                         //The file is complete; if we are supposed to automatically open safe files and this is one, open it
418                         if([self shouldOpenCompleteFileTransfer:fileTransfer]){ 
419                                 [fileTransfer openFile];
420                         }
421                         
422                         if(autoClearCompletedTransfers){
423                                 [ESFileTransferProgressWindowController removeFileTransfer:fileTransfer];
424                                 [self _removeFileTransfer:fileTransfer];
425                         }
426                         break;
427                 }
428                 case Canceled_Remote_FileTransfer:
429                 {
430                         [[adium contactAlertsController] generateEvent:FILE_TRANSFER_CANCELED
431                                                                                          forListObject:[fileTransfer contact] 
432                                                                                                   userInfo:fileTransfer
433                                                           previouslyPerformedActionIDs:nil];
434                         break;
435                 }
436                 default:
437                         break;
438         }
441 - (BOOL)shouldOpenCompleteFileTransfer:(ESFileTransfer *)fileTransfer
443         BOOL    shouldOpen = NO;
444         
445         if(autoOpenSafe &&
446            ([fileTransfer type] == Incoming_FileTransfer)){
447                 
448                 if(!safeFileExtensions) safeFileExtensions = [SAFE_FILE_EXTENSIONS_SET retain];         
450                 shouldOpen = [safeFileExtensions containsObject:[[[fileTransfer localFilename] pathExtension] lowercaseString]];
451         }
453         return(shouldOpen);
456 - (BOOL)validateMenuItem:(id <NSMenuItem>)menuItem
458         AIListContact   *listContact = nil;
459         
460     if(menuItem == menuItem_sendFile){
461         AIListObject    *selectedObject = [[adium contactController] selectedListObject];
462                 if (selectedObject && [selectedObject isKindOfClass:[AIListContact class]]){
463                         listContact = [[adium contactController] preferredContactForContentType:FILE_TRANSFER_TYPE
464                                                                                                                                          forListContact:(AIListContact *)selectedObject];
465                 }
466                 
467                 return(listContact != nil);
468                 
469         }else if(menuItem == menuItem_sendFileContext){
470                 AIListObject    *selectedObject = [[adium menuController] currentContextMenuObject];
471                 if (selectedObject && [selectedObject isKindOfClass:[AIListContact class]]){
472                         listContact = [[adium contactController] preferredContactForContentType:FILE_TRANSFER_TYPE
473                                                                                                                                          forListContact:(AIListContact *)selectedObject];
474                 }
475                 
476                 return(listContact != nil);
477                 
478     }else if(menuItem == menuItem_showFileTransferProgress){
479                 return(YES);
480         }
482     return(YES);
486 - (BOOL)validateToolbarItem:(NSToolbarItem *)theItem
488         AIListContact   *listContact = nil;
489         
490         AIListObject    *selectedObject = [[adium contactController] selectedListObject];
491         if (selectedObject && [selectedObject isKindOfClass:[AIListContact class]]){
492                 listContact = [[adium contactController] preferredContactForContentType:FILE_TRANSFER_TYPE
493                                                                                                                                  forListContact:(AIListContact *)selectedObject];
494         }
496     return(listContact != nil);
499 #pragma mark File transfer progress window
500 - (void)configureFileTransferProgressWindow
502         //Add the File Transfer Progress window menuItem
503         menuItem_showFileTransferProgress = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"File Transfers",nil)
504                                                                                                                                    target:self 
505                                                                                                                                    action:@selector(showProgressWindow:)
506                                                                                                                         keyEquivalent:@"l"];
507         [menuItem_showFileTransferProgress setKeyEquivalentModifierMask:(NSCommandKeyMask | NSAlternateKeyMask)];
508         [[adium menuController] addMenuItem:menuItem_showFileTransferProgress toLocation:LOC_Window_Auxiliary];
511 //Show the file transfer progress window
512 - (void)showProgressWindow:(id)sender
514         [ESFileTransferProgressWindowController showFileTransferProgressWindow];
517 - (void)showProgressWindowIfNotOpen:(id)sender
519         [ESFileTransferProgressWindowController showFileTransferProgressWindowIfNotOpen];       
522 #pragma mark Preferences
523 - (void)preferencesChangedForGroup:(NSString *)group key:(NSString *)key
524                                                         object:(AIListObject *)object preferenceDict:(NSDictionary *)prefDict firstTime:(BOOL)firstTime
526         autoAcceptType = [[prefDict objectForKey:KEY_FT_AUTO_ACCEPT] intValue];
527         autoOpenSafe = [[prefDict objectForKey:KEY_FT_AUTO_OPEN_SAFE] boolValue];
528         autoClearCompletedTransfers = [[prefDict objectForKey:KEY_FT_AUTO_CLEAR_COMPLETED] boolValue];
529         
530         //If we created a safe file extensions set and no longer need it, desroy it
531         if(!autoOpenSafe && safeFileExtensions){
532                 [safeFileExtensions release]; safeFileExtensions = nil;
533         }
534         
535         showProgressWindow = [[prefDict objectForKey:KEY_FT_SHOW_PROGRESS_WINDOW] boolValue];
538 #pragma mark AIEventHandler
540 - (NSString *)shortDescriptionForEventID:(NSString *)eventID { return @""; }
542 - (NSString *)globalShortDescriptionForEventID:(NSString *)eventID
544         NSString        *description;
545         
546         if([eventID isEqualToString:FILE_TRANSFER_REQUEST]){
547                 description = AILocalizedString(@"File transfer requested",nil);
548         }else if([eventID isEqualToString:FILE_TRANSFER_BEGAN]){
549                 description = AILocalizedString(@"File transfer begins",nil);
550         }else if([eventID isEqualToString:FILE_TRANSFER_CANCELED]){
551                 description = AILocalizedString(@"File transfer canceled by the other side",nil);
552         }else if([eventID isEqualToString:FILE_TRANSFER_COMPLETE]){
553                 description = AILocalizedString(@"File transfer completed successfully",nil);
554         }else{          
555                 description = @"";      
556         }
557         
558         return(description);
561 //Evan: This exists because old X(tras) relied upon matching the description of event IDs, and I don't feel like making
562 //a converter for old packs.  If anyone wants to fix this situation, please feel free :)
563 - (NSString *)englishGlobalShortDescriptionForEventID:(NSString *)eventID
565         NSString        *description;
566         
567         if([eventID isEqualToString:FILE_TRANSFER_REQUEST]){
568                 description = @"File Transfer Request";
569         }else if([eventID isEqualToString:FILE_TRANSFER_BEGAN]){
570                 description = @"File Transfer Began";
571         }else if([eventID isEqualToString:FILE_TRANSFER_CANCELED]){
572                 description = @"File Transfer Canceled Remotely";
573         }else if([eventID isEqualToString:FILE_TRANSFER_COMPLETE]){
574                 description = @"File Transfer Complete";
575         }else{          
576                 description = @"";      
577         }
578         
579         return(description);
582 - (NSString *)longDescriptionForEventID:(NSString *)eventID forListObject:(AIListObject *)listObject
583 {       
584         NSString        *description;
585         
586         if([eventID isEqualToString:FILE_TRANSFER_REQUEST]){
587                 description = AILocalizedString(@"When a file transfer is requested",nil);
588         }else if([eventID isEqualToString:FILE_TRANSFER_BEGAN]){
589                 description = AILocalizedString(@"When a file transfer begins",nil);
590         }else if([eventID isEqualToString:FILE_TRANSFER_CANCELED]){
591                 description = AILocalizedString(@"When a file transfer is canceled remotely",nil);
592         }else if([eventID isEqualToString:FILE_TRANSFER_COMPLETE]){
593                 description = AILocalizedString(@"When a file transfer is completed successfully",nil);
594         }else{          
595                 description = @"";      
596         }
598         return(description);
601 - (NSString *)naturalLanguageDescriptionForEventID:(NSString *)eventID
602                                                                                 listObject:(AIListObject *)listObject
603                                                                                   userInfo:(id)userInfo
604                                                                         includeSubject:(BOOL)includeSubject
606         NSString                *description = nil;
607         NSString                *displayName, *displayFilename;
608         ESFileTransfer  *fileTransfer;
610         NSParameterAssert([userInfo isKindOfClass:[ESFileTransfer class]]);
611         fileTransfer = (ESFileTransfer *)userInfo;
612         
613         displayName = [listObject displayName];
614         displayFilename = [fileTransfer displayFilename];
615         
616         if(includeSubject){
617                 NSString        *format = nil;
618                 
619                 if([eventID isEqualToString:FILE_TRANSFER_REQUEST]){
620                         //Should only happen for an incoming transfer
621                         format = AILocalizedString(@"%@ requests to send you %@",nil);
622                         
623                 }else if([eventID isEqualToString:FILE_TRANSFER_BEGAN]){
624                         if([fileTransfer type] == Incoming_FileTransfer){
625                                 format = AILocalizedString(@"%@ began sending you %@",nil);
626                         }else{
627                                 format = AILocalizedString(@"%@ began receiving %@",nil);       
628                         }
629                 }else if([eventID isEqualToString:FILE_TRANSFER_CANCELED]){
630                         format = AILocalizedString(@"%@ canceled the transfer of %@",nil);
631                 }else if([eventID isEqualToString:FILE_TRANSFER_COMPLETE]){
632                         if([fileTransfer type] == Incoming_FileTransfer){
633                                 format = AILocalizedString(@"%@ sent you %@",nil);
634                         }else{
635                                 format = AILocalizedString(@"%@ received %@",nil);      
636                         }
637                 }
638                 
639                 if(format){
640                         description = [NSString stringWithFormat:format,displayName,displayFilename];
641                 }
642         }else{
643                 NSString        *format = nil;
644                 
645                 if([eventID isEqualToString:FILE_TRANSFER_REQUEST]){
646                         //Should only happen for an incoming transfer
647                         format = AILocalizedString(@"requests to send you %@",nil);
648                         
649                 }else if([eventID isEqualToString:FILE_TRANSFER_BEGAN]){
650                         if([fileTransfer type] == Incoming_FileTransfer){
651                                 format = AILocalizedString(@"began sending you %@",nil);
652                         }else{
653                                 format = AILocalizedString(@"began receiving %@",nil);  
654                         }
655                 }else if([eventID isEqualToString:FILE_TRANSFER_CANCELED]){
656                         format = AILocalizedString(@"canceled the transfer of %@",nil);
657                 }else if([eventID isEqualToString:FILE_TRANSFER_COMPLETE]){
658                         if([fileTransfer type] == Incoming_FileTransfer){
659                                 format = AILocalizedString(@"sent you %@",nil);
660                         }else{
661                                 format = AILocalizedString(@"received %@",nil); 
662                         }
663                 }
664                 
665                 if(format){
666                         description = [NSString stringWithFormat:format,displayFilename];
667                 }               
668         }
670         return(description);
673 - (NSImage *)imageForEventID:(NSString *)eventID
675         static NSImage  *eventImage = nil;
676         if(!eventImage) eventImage = [[NSImage imageNamed:@"pref-ft" forClass:[self class]] retain];
677         return eventImage;
680 #pragma mark Strings for sizes
682 #define ZERO_BYTES                      AILocalizedString(@"Zero bytes", "no file size")
684 - (NSString *)stringForSize:(unsigned long long)inSize
686         NSString *ret = nil;
687         
688         if( inSize == 0. ) ret = ZERO_BYTES;
689         else if( inSize > 0. && inSize < 1024. ) ret = [NSString stringWithFormat:AILocalizedString( @"%lu bytes", "file size measured in bytes" ), inSize];
690         else if( inSize >= 1024. && inSize < pow( 1024., 2. ) ) ret = [NSString stringWithFormat:AILocalizedString( @"%.1f KB", "file size measured in kilobytes" ), ( inSize / 1024. )];
691         else if( inSize >= pow( 1024., 2. ) && inSize < pow( 1024., 3. ) ) ret = [NSString stringWithFormat:AILocalizedString( @"%.2f MB", "file size measured in megabytes" ), ( inSize / pow( 1024., 2. ) )];
692         else if( inSize >= pow( 1024., 3. ) && inSize < pow( 1024., 4. ) ) ret = [NSString stringWithFormat:AILocalizedString( @"%.3f GB", "file size measured in gigabytes" ), ( inSize / pow( 1024., 3. ) )];
693         else if( inSize >= pow( 1024., 4. ) ) ret = [NSString stringWithFormat:AILocalizedString( @"%.4f TB", "file size measured in terabytes" ), ( inSize / pow( 1024., 4. ) )];
694         
695         if(!ret) ret = ZERO_BYTES;
696         
697         return(ret);
700 - (NSString *)stringForSize:(unsigned long long)inSize of:(unsigned long long)totalSize ofString:(NSString *)totalSizeString
702         NSString *ret = nil;
703         
704         if( inSize == 0. ){
705                 ret = ZERO_BYTES;
706         }else if( inSize > 0. && inSize < 1024. ){
707                 if( totalSize > 0. && totalSize < 1024. ){
708                         ret = [NSString stringWithFormat:AILocalizedString( @"%lu of %lu bytes", "file sizes both measured in bytes" ), inSize, totalSize];
709                         
710                 }else{
711                         ret = [NSString stringWithFormat:AILocalizedString( @"%lu bytes of %@", "file size measured in bytes out of some other measurement" ), inSize, totalSizeString];
712                         
713                 }
714         }else if( inSize >= 1024. && inSize < pow( 1024., 2. ) ){
715                 if( totalSize >= 1024. && totalSize < pow( 1024., 2. ) ){
716                         ret = [NSString stringWithFormat:AILocalizedString( @"%.1f of %.1f KB", "file sizes both measured in kilobytes" ), ( inSize / 1024. ), ( totalSize / 1024. )];
717                         
718                 }else{
719                         ret = [NSString stringWithFormat:AILocalizedString( @"%.1f KB of %@", "file size measured in kilobytes out of some other measurement" ), ( inSize / 1024. ), totalSizeString];
720                 }
721         }
722         else if( inSize >= pow( 1024., 2. ) && inSize < pow( 1024., 3. ) ){
723                 if( totalSize >= pow( 1024., 2. ) && totalSize < pow( 1024., 3. ) ){
724                         ret = [NSString stringWithFormat:AILocalizedString( @"%.2f of %.2f MB", "file sizes both measured in megabytes" ), ( inSize / pow( 1024., 2. ) ), ( totalSize / pow( 1024., 2. ) )];
725                 }else{
726                         ret = [NSString stringWithFormat:AILocalizedString( @"%.2f MB of %@", "file size measured in megabytes out of some other measurement" ), ( inSize / pow( 1024., 2. ) ), totalSizeString];       
727                 }
728         }
729         else if( inSize >= pow( 1024., 3. ) && inSize < pow( 1024., 4. ) ){
730                 if( totalSize >= pow( 1024., 3. ) && totalSize < pow( 1024., 4. ) ){
731                         ret = [NSString stringWithFormat:AILocalizedString( @"%.3f of %.3f GB", "file sizes both measured in gigabytes" ), ( inSize / pow( 1024., 3. ) ), ( totalSize / pow( 1024., 3. ) )];
732                 }else{
733                         ret = [NSString stringWithFormat:AILocalizedString( @"%.3f GB of %@", "file size measured in gigabytes out of some other measurement" ), ( inSize / pow( 1024., 3. ) ), totalSizeString];
734                         
735                 }
736         }
737         else if( inSize >= pow( 1024., 4. ) ){
738                 if( totalSize >= pow( 1024., 4. ) ){
739                         ret = [NSString stringWithFormat:AILocalizedString( @"%.4f of %.4f TB", "file sizes both measured in terabytes" ), ( inSize / pow( 1024., 4. ) ),  ( totalSize / pow( 1024., 4. ) )];
740                 }else{
741                         ret = [NSString stringWithFormat:AILocalizedString( @"%.4f TB of %@", "file size measured in terabytes out of some other measurement" ), ( inSize / pow( 1024., 4. ) ), totalSizeString];                       
742                 }
743         }
744         
745         if(!ret) ret = ZERO_BYTES;
746         
747         return(ret);
750 @end