Cocoa/Obj-C - Open file when dragging it to application icon - objective-c

Currently there is a button on my app inteface which allow to open a file, here is my open code:
In my app.h:
- (IBAction)selectFile:(id)sender;
In my app.m:
#synthesize window;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
}
- (IBAction)selectFile:(id)sender {
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
NSArray *fileTypes = [NSArray arrayWithObjects:#"xml",nil];
NSInteger result = [openPanel runModalForDirectory:NSHomeDirectory() file:nil types:fileTypes ];
if(result == NSOKButton){
NSString * input = [openPanel filename];
How can I edit my code to allow opening with the application-icon drag & drop?
Note: I edited the .plist file and added a line for "xml" but it change anything, got an error when my file is dropped on the icon.
Note 2: I linked the "File -> Open..." to the selectFile: wich refer to my code
Note 3: My app isn't a document-based application
Thanks for your help!
Miskia

First add the proper extensions to CFBundleDocumentTypes inside the .plist file.
Next implement the following delegates:
- application:openFile: (one file dropped)
- application:openFiles: (multiple files dropped)
Reference:
NSApplicationDelegate Protocol Reference
Response to comment:
Step by step example, hopefully it makes everything clear :)
Add to the .plist file:
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>xml</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>application.icns</string>
<key>CFBundleTypeMIMETypes</key>
<array>
<string>text/xml</string>
</array>
<key>CFBundleTypeName</key>
<string>XML File</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSIsAppleDefaultForType</key>
<true/>
</dict>
</array>
Add to ...AppDelegate.h
- (BOOL)processFile:(NSString *)file;
- (IBAction)openFileManually:(id)sender;
Add to ...AppDelegate.m
- (IBAction)openFileManually:(id)sender;
{
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
NSArray *fileTypes = [NSArray arrayWithObjects:#"xml",nil];
NSInteger result = [openPanel runModalForDirectory:NSHomeDirectory() file:nil types:fileTypes ];
if(result == NSOKButton){
[self processFile:[openPanel filename]];
}
}
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
{
return [self processFile:filename];
}
- (BOOL)processFile:(NSString *)file
{
NSLog(#"The following file has been dropped or selected: %#",file);
// Process file here
return YES; // Return YES when file processed succesfull, else return NO.
}

for a quick&dirty solution:
Cocoa: Drag and Drop any file type
Tested under Xcode Version 11.4 (11E146)
catalina 10.15.4 (19E266)

Related

Macos Screen capture with LaunchAgent

I made a screen capture command line tool LaunchAgent. When launched, the screen capture policy alert did not pop up, so I couldn’t capture the right screenshots. I have no idea how to correct this. Could you help me with it? Thank you.
the command line tool path: /bin/capture
code follows:
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
int main(int argc, const char * argv[]) {
#autoreleasepool {
// insert code here...
NSLog(#"capture screen...");
CGRect mainRect = CGDisplayBounds(CGMainDisplayID());
CGImageRef desktopImage = CGWindowListCreateImage(mainRect, kCGWindowListOptionOnScreenOnly, kCGNullWindowID, kCGWindowImageBestResolution | kCGWindowImageShouldBeOpaque);
NSBitmapImageRep *bmpImgRef = [[NSBitmapImageRep alloc] initWithCGImage:desktopImage];
NSData *data = [bmpImgRef representationUsingType:NSBitmapImageFileTypeJPEG properties:#{NSImageCompressionFactor: #(1)}];
NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
[fmt setDateFormat:#"yyyyMMdd_hh:mm:ss"];
[data writeToURL:[NSURL fileURLWithPath:[NSString stringWithFormat:#"/tmp/%#.jpg", [fmt stringFromDate:[NSDate date]]]] atomically:true];
}
return 0;
}
the LaunchAgent plist file path:
/Library/LaunchAgent/com.test.launchagent.screencapture.plist
and the LaunchAgent plist follows:
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.test.launchagent.screencapture</string>
<key>ProgramArguments</key>
<array>
<string>/bin/capture</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
You do need several things to get this work.
First of all, you need to have an Info.plist for your binary. You can use embedded Info.plist (can be set in Xcode build settings) if you care about keeping it just single unix file, not creating a bundle.
Second, you have to change launch plist to run your program in Aqua session.
With such steps you should be able to run this and see policy popup.

Obj-C: application:openFiles: (multiple files dropped) not receiving all files. Bug?

Closest one I found is: https://stackoverflow.com/questions/4778932/cocoa-app-not-receiving-all-dropped-files-in-applicationopenfiles
But it hasn't been answered.
My Problem is this,
So let's say I am dropping 4 files on my dock application:
test.xls
test.rtf
test.jpg
test.pdf
If I go debug my program it shows this:
Debug log
As you can see the .jpg file is not in there.
This also happens for: .py, other image types, .txt files
I have found out that if I drop files only from that group (.py .jpg .txt) then it recognizes all of them.
My Info.plist looks like this:
<?xmlversion="1.0"encoding="UTF-8"?>
<!DOCTYPEplistPUBLIC"-//Apple//DTDPLIST1.0//EN""http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plistversion="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeName</key>
<string>AllFiles</string>
<key>CFBundleTypeIconFile</key>
<string>application.icns</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSHandlerRank</key>
<string>Alternate</string>
<key>LSItemContentTypes</key>
<array>
<string>public.item</string>
</array>
</dict>
</array>
I don't know where the error is in here, maybe it's a bug? Or did I configure my info.plist wrong?
I hope someone can help me and thank you all in advance!
I encountered the same problem and here is how I solved it:
#property (strong) NSMutableArray *openFilesList;
#property (strong) NSTimer *openFilesTimer;
...
#synthesize openFilesList;
#synthesize openFilesTimer;
...
-(void)init {
openFilesList = [[NSMutableArray alloc] init];
openFilesTimer = nil;
}
-(void)application:(NSApplication *)sender openFiles:(NSArray *)filenames {
if (openFilesTimer) {
[openFilesTimer invalidate];
}
[openFilesList addObjectsFromArray:filenames];
openFilesTimer = [NSTimer scheduledTimerWithTimeInterval:0.2f
target:self
selector:#selector(processOpenFiles)
userInfo:nil
repeats:NO];
}
-(void)processOpenFiles {
NSArray *filenames = [openFilesList copy];
openFilesTimer = nil;
[openFilesList removeAllObjects];
// Do your processing with filenames
}
set in info.plist like this
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>*</string>
</array>
<key>CFBundleTypeName</key>
<string>Files</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSItemContentTypes</key>
<array>
<string>public.data</string>
</array>
</dict>
</array>
and use this method in AppDelegate.m
-(void)application:(NSApplication *)sender openFiles:(NSArray *)fileList{
// use fileList array
}
-(BOOL)application:(NSApplication *)sender openFile:(NSString *)filename{
return YES;
}

NSTableView with source, leads to EXC_BAD_ACCESS

Although I find myself familiar with a bunch of programming languages, I fail for some time now to get used to Cocoa/Objective-C.
I wanted to fill a NSTableView with some content, with the following code:
- (int) numberOfRowsInTableView:(NSTableView *)aTableView {
return (int)[settingsPlist count];}
- (id) tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex {
NSString *title = [[NSString stringWithString:[[aTableColumn headerCell] stringValue]] autorelease];
NSLog(title); // debug
NSLog(#"%i",rowIndex); // debug
if([title isEqual: #"Plugin Name"]) {
return [[settingsPlist objectForKey:[NSString stringWithFormat:#"%i",rowIndex]] objectForKey:#"name"];
} else {
return #"(n/a)";
}
}
settingsPlist is filled with the contents of a plist, looks like this:
<plist version="1.0">
<dict>
<key>0</key>
<dict>
<key>name</key>
<string>Test-Plugin</string>
<key>version</key>
<string>0.1</string>
<key>type</key>
<string>Car</string>
<key>creator</key>
<string>Icke</string>
<key>checksum</key>
<string>0x32</string>
<key>link</key>
<string>http://</string>
</dict>
</dict>
The problem: It works fine for one row (you can tell by looking at the console output, thrown up by NSLog(title); and NSLog(rowIndex);), however, after that, the debugger halts the execution, throwing up EXC_BAD_ACCESS. I know what that means, but I don't know why I'm getting this and how to fix this.
Thanks for any help. :)
you did over release here,
NSString *title = [[NSString stringWithString:[[aTableColumn headerCell] stringValue]] autorelease];
-[NSString stringWithString:] already autorelease the returned value, so you didn't need to call it again.

Open image file only when dragging to application icon

I'm working on a small app that works with image (it is not a document based app).
I want to be able to open image files by dragging them to the app icon.
I have looked at this very clear explanation
Cocoa/Obj-C - Open file when dragging it to application icon
and have added the ability to open .png files by dragging to the app icon.
Do I have to add ALL the file types I require separately , or is there a way to specify ALL image types (for instance all image types that an NSImageView will accept)?
In your application’s Info.plist’s, you can specify the Uniform Type Identifier public.image in LSItemContentTypes for a document type even if your application is not document-based:
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSItemContentTypes</key>
<array>
<string>public.image</string>
</array>
</dict>
</array>
or
If you’re opening other types of files as well, you may want to check whether the dropped file is an image:
- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename {
NSError *error;
NSString *fileUTI = [[NSWorkspace sharedWorkspace] typeOfFile:filename error:&error];
if (!fileUTI)
NSLog(#"Error when trying to detect the type of file %#: %#", filename, error);
else if (UTTypeConformsTo((__bridge CFStringRef)fileUTI, kUTTypeImage))
NSLog(#"%# is an image", filename);
else
NSLog(#"%# is not an image", filename);
return YES;
}

opening files with different extensions with Cocoa

I have an NSDocument based app that already handles *.txt files. I can drag and drop those files in the dock and my app will launch correctly.
Now I want to be able to open *.text files too. So I have added:
<key>CFBundleTypeExtensions</key>
<array>
<string>.txt</string>
<string>.text</string>
</array>
to my *plist
However I can't open *.text files by drag&dropping them in the dock. (I can do MyApp> File>Open> myFile.text )
I can open *.txt as usual, and drag and drop them in the dock.
I have tried implementing these NSApp delegate methods but now I get an error and I can't open any kind of file.
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename{
NSURL *url = [NSURL URLWithString:filename];
NSError *error = nil;
[[NSDocumentController sharedDocumentController] openDocumentWithContentsOfURL:url display:YES error:&error];
if (error) {
NSLog(#"error: %#", [error localizedDescription]);
error = nil;
return NO;
}
return YES;
}
- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames{
for (NSString *filename in filenames) {
[self application:sender openFile:filename];
}
}
Error:
typeForContentsOfURL:error: must be
overridden for your application to
support non-'file:' URLs.
I am not subclassing NSDocumentController, so do I really need to? or is there a better/easier way of doing this?
Thanks in advance
The file extensions in the Info.plist file should not contain a period character (.). What happens if you do this?:
<key>CFBundleTypeExtensions</key>
<array>
<string>txt</string>
<string>text</string>
</array>