Reputation: 1416
I'm working on an Cocoa / Objective C++ utility application and want to handle multiple (instantaneous) file opens. To handle the file opening part, I have this function in my AppDelegate.mm
file:
- (BOOL)application:(NSApplication*)app openFile:(NSString *)filename
{
NSLog(@"Opening file %@", filename);
// more C++ code here
return YES;
}
The files are being sent to Quicksilver, which I believe will send the files one after another to the application (Finder's "Open With" I think sends all the files at once - and so possibly using handleOpenApplicationEvent:(NSAppleEventDescriptor *)event
might work better for that) - but regardless, the application will receive several files one after another - almost instantaneously - but not exactly together. Here is what Console.app shows:
console-screenshot.png http://img109.imageshack.us/img109/1205/consolescreenshot.png
Since the files are sent separately, but one right after another, my question is how can I gather all the files that are sent and do one thing with them? For instance, I'd like to gather all the file paths sent and then display them together in a dialog box.
I can think of one concept which might work: store each file path in an array as it is received. At the same time, when the first one is received, set a delay of 1-2 seconds in the background, and then have a dialog box display everything that is a particular variable. Is that concept right or is there a better way to do that? I'm new to Cocoa / Objective C++ - coming from languages like PHP / Perl, bash, etc.
I'd also like to have this helper application terminate when it is finished, but if I'm waiting on files how can I terminate the application when the last of the files are received?
If I add [NSApp terminate:nil];
to the applicationDidFinishLaunching
function the application actually terminates after only the first file is received.
Update - More Notes
This is the console after using the openFiles
(plural) function:
Picture 7.png http://img24.imageshack.us/img24/5715/picture7zc.png
And with Quicksilver I grab some files, like the ones in the temp directory, and choose "Open With" and then the App I'm creating (named darn.app)
But with Finder it seems to works just fine:
...results in consolea.app:
Picture 8.png http://img24.imageshack.us/img24/2812/picture8fe.png
I did ask the Quicksilver developers and they confirmed that QS does open files one at a time with the Open With action and they acknowledge that this is different than how Finder does it but they see this as the intended. So maybe Darren is right - a timer may be the best way to go...
Upvotes: 3
Views: 683
Reputation: 2099
I am starting an App executable with several command line arguments.
MyApp.app/Contents/MacOS/MyApp file1 file2 file3
Each argument generates a separate application:openFile(s) call as described above. However, in this case applicationDidFinishLaunching is not called until AFTER all of the openFile calls, therefore it is easy to do something like this:
NSMutableArray *_allFiles = nil;
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
[self handleFiles:_allFiles];
}
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename {
[self application:theApplication openFiles:[NSArray arrayWithObject:filename]];
return YES;
}
- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames {
if(!_allFiles) _allFiles = [NSMutableArray arrayWithCapacity:8];
[_allFiles addObjectsFromArray:filenames];
}
This method also works for opening multiple files with "Open With" from the Finder. I wouldn't be surprised if Quicksilver handles multiple files in the same way, and so this simple method could work for you.
Upvotes: 0
Reputation: 25619
Try implementing -application:openFiles:
instead of -application:openFile:
- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames
{
NSLog(@"Opening multiple files %@", filenames);
}
That handles receiving multiple files from Finder.
If that doesn't work with your Quicksilver setup then a timer is probably the best way to go.
Set up the timer in the AppDelegate's -init method.
@implementation MyAppDelegate {
NSMutableArray* _files;
}
- (id)init {
if (self = [super init]) {
_files = [[NSMutableArray alloc] initWithCapacity:10];
[self performSelector:@selector(processFiles)
withObject:nil
afterDelay:1.0]; // one second delay
}
return self;
}
In -application:openFile: collect the file in an array and reset the timer.
- (BOOL)application:(NSApplication*)app openFile:(NSString*)filename {
NSAssert(_files != nil, @"Timer already fired");
[_files addObject:filename];
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[self performSelector:@selector(processFiles)
withObject:nil
afterDelay:1.0];
}
When the timer fires, process the collected files and then quit the app.
-(void)processFiles
// Process the _files
[_files release];
_files = nil;
[NSApp terminate:nil];
}
Upvotes: 6
Reputation: 77400
The system has no way of knowing whether more file open requests are to come, so not only is there no provided method of signifying the last request, there's no possible method.
As an alternative to the timer for the first issue, you could start processing the files immediately and display each as they come in in the dialog, so the user would see the list grow. This doesn't address the second issue (terminating the application after all files have been received).
If you do use a timer, make sure to reset it after each application:openFile:
.
Upvotes: 1