I try to assign a file type to my application.
In Info.plist I add:
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>type</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>icon</string>
<key>CFBundleTypeName</key>
<string>My Project</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSTypeIsPackage</key>
<false/>
</dict>
</array>
In Main.mm:
....
-(BOOL) application:(NSApplication *)sender openFile:(NSString *)filename {
NSLog(@"Opened by file");
return YES;
}
@end
int main(int argc, char* argv[]) {
[NSApplication sharedApplication];
[[[[Window alloc] init] autorelease] makeMainWindow];
[NSApp run];
return 0;
}
But when I try double click on the my file type, The app only open with the warn: could not be opened, MyApp cannot open file in format. Also the message from NSLog is not called at all.
CodePudding user response:
There are several issues with the code you posted but I was able to get the desired behavior with a few modifications.
I assume this is your window interface and implementation:
@interface Window : NSWindow <NSApplicationDelegate>
- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename;
@end
@implementation Window
- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename {
NSLog(@"Opened by file");
return YES;
}
@end
This is extremely odd to be using a window object as an application delegate. Normally, you have a controller object that owns and manages the window and also acts as the application's delegate.
In any case, it's still possible to get... well, functional behavior by changing the main()
function to the following:
int main(int argc, const char * argv[]) {
[NSApplication sharedApplication];
Window *window = [[[Window alloc] init] autorelease];
NSApp.delegate = window;
[window makeKeyAndOrderFront:nil];
[NSApp run];
return 0;
}
There are two notable changes. First, your problem was that you didn't set the window instance to be the application delegate. Second, IIRC, you should never call -makeMainWindow
directly; rather, that method exists so that you can override it if you wish. If you want to display the window on screen, you call -makeKeyAndOrderFront:
.
Opening a file should display the logged line in console (if you're using Xcode 12.5.1, resize the log window if needed to workaround the display bug).
Under manual reference counting, I believe this would leak memory, since no autorelease pool is created, but I didn't see any of the usual warnings in console. Anyway, while this code works, it results in a fairly undesirable scenario. There is no main menu in the app so to quit it you have to use the Dock. The window created is also tiny and has no resizing capabilities, etc.
CodePudding user response:
The following subclass of NSWindow should allow you to save a file with a unique ‘.jaf’ extension and then reopen the file into the app by double clicking on it. The info.plist is not as critical as I initially thought; I did not alter the one created by Xcode. Most important for this non-Document based app seems to be the calling of NSApplicationDelegate method -(BOOL) application: openFile. The NSApplicationDelegate was added to the NSWindow subclass instead of having a separate AppDelegate as is usually the case. When working correctly you should hear a beep when this method is called after a .jaf file is double-clicked; I couldn’t find the NSLog output. To run the demo in Xcode first create an objc project and delete everything in the ‘main.m’ file and copy/paste the following source code into it. Delete the pre-supplied AppDelegate class to avoid duplicate symbols. In the entitlements set the App Sandbox to NO and set read-only to zero. After the JAF app has been made, use it to save a file to your desktop with the ‘.jaf’ extension. Then make a copy of the app (shown in the Finder) and copy/paste it into the Applications folder. The next step is critical; right click on the file that you just made and use either Get Info or Open With to set the file to always open with your newly made app. At this point you should be able to double click on the xxxx.jaf file and have it open into your app with an audible beep.
#import <Cocoa/Cocoa.h>
@interface Window : NSWindow <NSApplicationDelegate> {
NSTextView *txtView;
}
- (instancetype)initWithContentRect:(NSRect)contentRect styleMask:(NSWindowStyleMask)style backing:(NSBackingStoreType)backingStoreType defer:(BOOL)flag;
-(void) buildMenu;
-(void) openAction;
-(void) saveAction;
@end
@implementation Window
#define _wndW 700
#define _wndH 550
- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename {
NSLog(@"This comes from JAF : filename = %@.",filename);
NSBeep(); // Listen for this.
NSError *error;
NSURL *url = [NSURL fileURLWithPath:filename];
NSString *fileStr = [[NSString alloc] initWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
if (!fileStr) {
NSLog(@"Unable to open file %@", error);
} else {
[txtView setString:fileStr];
}
return YES;
}
-(void) buildMenu {
// **** Menu Bar **** //
NSMenu *menubar = [NSMenu new];
[NSApp setMainMenu:menubar];
// **** App Menu **** //
NSMenuItem *appMenuItem = [NSMenuItem new];
NSMenu *appMenu = [NSMenu new];
[appMenu addItemWithTitle: @"Quit" action:@selector(terminate:) keyEquivalent:@"q"];
[appMenuItem setSubmenu:appMenu];
[menubar addItem:appMenuItem];
}
-(void) openAction {
NSOpenPanel *op = [NSOpenPanel openPanel];
[op setAllowedFileTypes:[NSArray arrayWithObjects: @"jaf", @"txt", nil]];
[op beginSheetModalForWindow: self completionHandler: ^(NSInteger returnCode) {
if (returnCode == NSModalResponseOK) {
NSURL *url = [op URL];
NSError *error;
NSString *fileStr = [[NSString alloc] initWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
if (!fileStr) {
NSLog(@"Unable to open file %@", error);
} else {
[self->txtView setString:fileStr];
}
}
}];
}
-(void) saveAction {
NSSavePanel *sp = [NSSavePanel savePanel];
[sp setTitle:@"Save contents to file"];
[sp setAllowedFileTypes:[NSArray arrayWithObjects: @"jaf", nil]];
[sp setNameFieldStringValue: @".jaf"];
[sp beginSheetModalForWindow: self completionHandler: ^(NSInteger returnCode) {
if (returnCode == NSModalResponseOK) {
NSURL *url = [sp URL];
NSString *viewStr = [[self->txtView textStorage] string];
NSError *err;
BOOL fileSaved = [viewStr writeToURL:url atomically:YES encoding:NSUTF8StringEncoding error:&err];
if (!fileSaved) { NSLog(@"Unable to save file due to error: %@", err);}
}
}];
}
- (instancetype)initWithContentRect:(NSRect)contentRect styleMask:(NSWindowStyleMask)style backing:(NSBackingStoreType)backingStoreType defer:(BOOL)flag {
self = [super initWithContentRect:NSMakeRect(0, 0, _wndW, _wndH) styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable backing:NSBackingStoreBuffered defer:NO];
[self setTitle: @"Test window"];
[self center];
[self makeKeyAndOrderFront: nil];
// ****** NSTextView with Scroll ****** //
NSScrollView *scrlView = [[NSScrollView alloc] initWithFrame:NSMakeRect( 10, 10, _wndW - 20, _wndH - 80 )];
[[self contentView] addSubview:scrlView];
[scrlView setHasVerticalScroller: YES];
[scrlView setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ];
txtView = [[NSTextView alloc] initWithFrame:NSMakeRect( 0, 0, _wndW - 20, _wndH - 80 )];
[scrlView setDocumentView: txtView];
// **** Open Button **** //
NSButton *openBtn =[[NSButton alloc]initWithFrame:NSMakeRect( 30, _wndH - 50, 95, 30 )];
[openBtn setBezelStyle:NSBezelStyleRounded ];
[openBtn setTitle: @"Open"];
[openBtn setAutoresizingMask: NSViewMinYMargin];
[openBtn setAction: @selector (openAction)];
[[self contentView] addSubview: openBtn];
// **** Save Button **** //
NSButton *saveBtn =[[NSButton alloc]initWithFrame:NSMakeRect( 130, _wndH - 50, 95, 30 )];
[saveBtn setBezelStyle:NSBezelStyleRounded ];
[saveBtn setTitle: @"Save"];
[saveBtn setAutoresizingMask: NSViewMinYMargin];
[saveBtn setAction: @selector (saveAction)];
[[self contentView] addSubview: saveBtn];
return self;
}
- (BOOL)windowShouldClose:(id)sender {
[NSApp terminate:sender];
return YES;
}
@end
int main() {
NSApplication *application = [NSApplication sharedApplication];
Window *window = [[Window alloc]init];
[window buildMenu];
[application setDelegate:window];
[application activateIgnoringOtherApps:YES];
[NSApp run];
return 0;
}