switch to NSDocument architecture.

This commit is contained in:
Kelvin Sherlock 2016-08-18 09:02:20 -04:00
parent b4caa4d811
commit 0aeca5de81
7 changed files with 574 additions and 1466 deletions

44
Image Opener/IODocument.h Normal file
View File

@ -0,0 +1,44 @@
//
// IODocument.h
// Image Opener
//
// Created by Kelvin Sherlock on 8/17/2016.
//
//
#import <Cocoa/Cocoa.h>
@interface IODocument : NSDocument {
IBOutlet NSMatrix *_fsMatrix;
IBOutlet NSMatrix *_ifMatrix;
IBOutlet NSTextView *_textView;
IBOutlet NSTextField *_nameView;
IBOutlet NSTextField *_sizeView;
IBOutlet NSButton *_mountButton;
NSTask *_task;
NSFileHandle *_handle;
NSString *_filePath;
NSDictionary *_fileInfo;
}
@property (nonatomic, retain) NSString *filePath;
@property (nonatomic, retain) NSDictionary *fileInfo;
-(void)runTask;
-(IBAction)mountButton: (id)sender;
#pragma mark -
#pragma mark Notifications
-(void)readComplete:(NSNotification *)notification;
-(void)taskComplete: (NSNotification *)notification;
@end

290
Image Opener/IODocument.m Normal file
View File

@ -0,0 +1,290 @@
//
// IODocument.m
// Image Opener
//
// Created by Kelvin Sherlock on 8/17/2016.
//
//
#import "IODocument.h"
enum {
kTagLucky = 1,
kTag2MG,
kTagDC42,
kTagSDK,
kTagDavex,
kTagPO,
kTagDO
};
static const char *TagToFormat(NSInteger tag)
{
switch (tag)
{
case kTagPO:
default:
return "po";
case kTagDO:
return "do";
case kTag2MG:
return "2img";
case kTagDC42:
return "dc42";
case kTagDavex:
return "davex";
case kTagSDK:
return "sdk";
}
}
@implementation IODocument
@synthesize filePath = _filePath;
- (NSString *)windowNibName {
return @"IODocument";
}
- (void)windowControllerDidLoadNib:(NSWindowController *)aController {
[super windowControllerDidLoadNib:aController];
[_nameView setStringValue: [_filePath lastPathComponent]];
NSString *ss = @"";
off_t size = [(NSNumber *)[_fileInfo objectForKey: NSFileSize] unsignedLongLongValue];
if (size < 1024)
ss = [NSString stringWithFormat: @"%u B", (unsigned)size];
else if (size < 1024 * 1024)
ss = [NSString stringWithFormat: @"%.1f KB", (double) size / 1024.0];
else ss = [NSString stringWithFormat: @"%.1f MB", (double) size / (1024.0 * 1024.0)];
[_sizeView setStringValue: ss];
[_ifMatrix selectCellWithTag: kTagLucky];
[_fsMatrix selectCellWithTag: 1]; // assume prodos.
}
-(BOOL)readFromURL:(NSURL *)url ofType:(NSString *)typeName error:(NSError * _Nullable *)outError {
NSString *path = [url isFileURL] ? [url path] : nil;
//NSLog(@"%d %@", (int)result, path);
[self setFileInfo: nil];
[self setFilePath: nil];
if (path) {
NSFileManager *manager;
NSDictionary *dict;
NSError *error = nil;
_filePath = [path retain];
manager = [NSFileManager defaultManager];
dict = [manager attributesOfItemAtPath: path error: &error];
if (error) {
*outError = error;
return NO;
}
_fileInfo = [dict retain];
return YES;
} else {
*outError = [NSError errorWithDomain: NSURLErrorDomain code: 1 userInfo: nil];
return NO;
}
}
+(BOOL)autosavesInPlace {
// YES adds the rename drop down panel in the title bar!
return NO;
}
- (void)dealloc
{
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc removeObserver: self];
[_task release];
[_handle release];
[_filePath release];
[super dealloc];
}
-(IBAction)mountButton: (id)sender
{
[_mountButton setEnabled: NO];
[self runTask];
}
-(void)appendString: (NSString *)string
{
if ([string length])
{
[[[_textView textStorage] mutableString] appendString: string];
}
}
-(void)runTask
{
NSPipe *pipe = [NSPipe pipe];
NSString *launchPath;
NSMutableArray *argv;
NSNotificationCenter *nc;
NSString *exe;
NSInteger tag;
_task = [[NSTask alloc] init];
[_task setStandardError: pipe];
[_task setStandardOutput: pipe];
[_task setStandardInput: [NSFileHandle fileHandleWithNullDevice]];
_handle = [[pipe fileHandleForReading] retain];
switch ([_fsMatrix selectedTag])
{
case 1:
default:
exe = @"profuse";
break;
case 2:
exe = @"fuse_pascal";
break;
}
launchPath = [[NSBundle mainBundle] pathForAuxiliaryExecutable: exe];
argv = [NSMutableArray arrayWithCapacity: 4];
[argv addObject: @"-r"]; // read-only.
tag = [_ifMatrix selectedTag];
if (tag != kTagLucky)
{
[argv addObject: [NSString stringWithFormat: @"--format=%s", TagToFormat(tag)]];
}
[argv addObject: _filePath];
[_task setLaunchPath: launchPath];
[_task setArguments: argv];
[self appendString: launchPath];
for (NSString *string in argv)
{
[self appendString: @" "];
[self appendString: string];
}
[self appendString: @"\n\n"];
nc = [NSNotificationCenter defaultCenter];
[nc addObserver: self
selector: @selector(taskComplete:)
name: NSTaskDidTerminateNotification
object: _task];
[nc addObserver: self
selector: @selector(readComplete:)
name: NSFileHandleReadCompletionNotification
object: _handle];
[_task launch];
[_handle readInBackgroundAndNotify];
}
#pragma mark -
#pragma mark Notifications
-(void)readComplete:(NSNotification *)notification
{
// read complete, queue up another.
NSDictionary *dict = [notification userInfo];
NSData *data = [dict objectForKey: NSFileHandleNotificationDataItem];
if ([data length])
{
NSString *string = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
[self appendString: string];
[string release];
[_handle readInBackgroundAndNotify];
}
}
-(void)taskComplete: (NSNotification *)notification
{
BOOL ok = NO;
NSTaskTerminationReason reason;
int status;
NSString *string = nil;
reason = [_task terminationReason];
status = [_task terminationStatus];
if (reason == NSTaskTerminationReasonExit)
{
if (status == 0)
{
string = @"\n\n[Success]\n\n";
ok = YES;
}
else string = @"\n\n[An error occurred]\n\n";
}
else
{
string = @"\n\n[Caught signal]\n\n";
}
[self appendString: string];
[_handle release];
_handle = nil;
[_task release];
_task = nil;
if (!ok) [_mountButton setEnabled: YES];
}
@end

View File

@ -16,7 +16,7 @@
@property (assign) IBOutlet NSWindow *window;
-(IBAction)openDocument:(id)sender;
//-(IBAction)openDocument:(id)sender;
@end

View File

@ -8,6 +8,7 @@
#import "Image_OpenerAppDelegate.h"
#import "WindowController.h"
#import "IODocumentController.h"
@implementation Image_OpenerAppDelegate
@ -16,15 +17,33 @@
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
}
-(void)applicationWillFinishLaunching:(NSNotification *)notification {
// initialize the shared document controller.
//[[IODocumentController alloc] init];
}
#if 0
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
{
WindowController *controller;
controller = [WindowController controllerWithFilePath: filename];
[WindowController controllerWithFilePath: filename];
return YES;
}
#endif
-(BOOL)applicationShouldOpenUntitledFile:(NSApplication *)sender {
return NO;
}
-(BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender {
return NO;
}
#if 0
-(IBAction)openDocument:(id)sender
{
NSOpenPanel *panel;
@ -49,7 +68,8 @@
if (path)
{
[self application: nil openFile: path];
[WindowController controllerWithFilePath: path];
[[NSDocumentController sharedDocumentController] noteNewRecentDocumentURL: url];
}
@ -59,5 +79,6 @@
}
#endif
@end

View File

@ -11,8 +11,19 @@
<array>
<string>*</string>
</array>
<key>CFBundleTypeName</key>
<string>DiskImage</string>
<key>CFBundleTypeOSTypes</key>
<array>
<string>****</string>
<string></string>
</array>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSTypeIsPackage</key>
<integer>0</integer>
<key>NSDocumentClass</key>
<string>IODocument</string>
</dict>
</array>
<key>CFBundleExecutable</key>
@ -20,7 +31,7 @@
<key>CFBundleIconFile</key>
<string>Icon.icns</string>
<key>CFBundleIdentifier</key>
<string>com.ksherlock.${PRODUCT_NAME:rfc1034identifier}</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
@ -31,8 +42,6 @@
<string>1.0.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array/>
<key>CFBundleVersion</key>
<string>1.0.1</string>
<key>LSApplicationCategoryType</key>
@ -43,11 +52,5 @@
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSServices</key>
<array/>
<key>UTExportedTypeDeclarations</key>
<array/>
<key>UTImportedTypeDeclarations</key>
<array/>
</dict>
</plist>

View File

@ -0,0 +1,203 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11173.2" systemVersion="15G31" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11173.2"/>
<capability name="box content view" minToolsVersion="7.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="IODocument">
<connections>
<outlet property="_fsMatrix" destination="17" id="40"/>
<outlet property="_ifMatrix" destination="21" id="41"/>
<outlet property="_mountButton" destination="11" id="43"/>
<outlet property="_nameView" destination="33" id="38"/>
<outlet property="_sizeView" destination="35" id="39"/>
<outlet property="_textView" destination="8" id="42"/>
<outlet property="window" destination="1" id="37"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application"/>
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" visibleAtLaunch="NO" animationBehavior="default" id="1">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" unifiedTitleAndToolbar="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="196" y="240" width="480" height="420"/>
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1178"/>
<view key="contentView" id="2">
<rect key="frame" x="0.0" y="0.0" width="480" height="420"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<scrollView autohidesScrollers="YES" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" id="5">
<rect key="frame" x="-1" y="-1" width="482" height="168"/>
<autoresizingMask key="autoresizingMask"/>
<clipView key="contentView" id="wG1-Of-dzm">
<rect key="frame" x="1" y="1" width="480" height="166"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textView editable="NO" importsGraphics="NO" richText="NO" allowsNonContiguousLayout="YES" spellingCorrection="YES" id="8">
<rect key="frame" x="0.0" y="22" width="480" height="166"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<size key="minSize" width="480" height="166"/>
<size key="maxSize" width="480" height="10000000"/>
<color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
</textView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</clipView>
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="YES" id="7">
<rect key="frame" x="-100" y="-100" width="87" height="18"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="6">
<rect key="frame" x="466" y="1" width="15" height="133"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
<button verticalHuggingPriority="750" id="11">
<rect key="frame" x="360" y="175" width="100" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<buttonCell key="cell" type="roundTextured" title="Mount" bezelStyle="texturedRounded" alignment="center" controlSize="small" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="12">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
DQ
</string>
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</buttonCell>
<connections>
<action selector="mountButton:" target="-2" id="45"/>
</connections>
</button>
<box autoresizesSubviews="NO" borderType="line" title="File System" id="28">
<rect key="frame" x="17" y="197" width="100" height="178"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<view key="contentView" id="0YI-bn-yk4">
<rect key="frame" x="1" y="1" width="98" height="162"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<matrix verticalHuggingPriority="750" allowsEmptySelection="NO" id="17">
<rect key="frame" x="18" y="114" width="62" height="38"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
<size key="cellSize" width="62" height="18"/>
<size key="intercellSpacing" width="4" height="2"/>
<buttonCell key="prototype" type="radio" title="Radio" imagePosition="left" alignment="left" controlSize="small" inset="2" id="18">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="smallSystem"/>
</buttonCell>
<cells>
<column>
<buttonCell type="radio" title="ProDOS" imagePosition="left" alignment="left" controlSize="small" state="on" tag="1" inset="2" id="19">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="smallSystem"/>
</buttonCell>
<buttonCell type="radio" title="Pascal" imagePosition="left" alignment="left" controlSize="small" tag="2" inset="2" id="20">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="smallSystem"/>
</buttonCell>
</column>
</cells>
</matrix>
</subviews>
</view>
</box>
<box autoresizesSubviews="NO" borderType="line" title="Disk Image Format" id="29">
<rect key="frame" x="119" y="197" width="172" height="178"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<view key="contentView" id="aZY-DD-HW7">
<rect key="frame" x="1" y="1" width="170" height="162"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<matrix verticalHuggingPriority="750" allowsEmptySelection="NO" id="21">
<rect key="frame" x="18" y="14" width="134" height="138"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
<size key="cellSize" width="134" height="18"/>
<size key="intercellSpacing" width="4" height="2"/>
<buttonCell key="prototype" type="radio" title="Radio" imagePosition="left" alignment="left" controlSize="small" inset="2" id="22">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="smallSystem"/>
</buttonCell>
<cells>
<column>
<buttonCell type="radio" title="Im Feeling Lucky" imagePosition="left" alignment="left" controlSize="small" state="on" tag="1" inset="2" id="23">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="smallSystem"/>
</buttonCell>
<buttonCell type="radio" title="Universal Disk Image" imagePosition="left" alignment="left" controlSize="small" tag="2" inset="2" id="24">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="smallSystem"/>
</buttonCell>
<buttonCell type="radio" title="DiskCopy 4.2" imagePosition="left" alignment="left" controlSize="small" tag="3" inset="2" id="25">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="smallSystem"/>
</buttonCell>
<buttonCell type="radio" title="ShrinkIt Disk Image" imagePosition="left" alignment="left" controlSize="small" tag="4" inset="2" id="26">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="smallSystem"/>
</buttonCell>
<buttonCell type="radio" title="Davex Disk Image" imagePosition="left" alignment="left" controlSize="small" tag="5" inset="2" id="27">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="smallSystem"/>
</buttonCell>
<buttonCell type="radio" title="ProDOS Order" imagePosition="left" alignment="left" controlSize="small" tag="6" inset="2" id="46">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="smallSystem"/>
</buttonCell>
<buttonCell type="radio" title="DOS Order" imagePosition="left" alignment="left" controlSize="small" tag="7" inset="2" id="47">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="smallSystem"/>
</buttonCell>
</column>
</cells>
</matrix>
</subviews>
</view>
</box>
<box autoresizesSubviews="NO" borderType="line" title="Other Options" id="30">
<rect key="frame" x="293" y="197" width="170" height="178"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<view key="contentView" id="eax-o3-0PV">
<rect key="frame" x="1" y="1" width="168" height="162"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button id="31">
<rect key="frame" x="16" y="136" width="86" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="Read Only" bezelStyle="regularSquare" imagePosition="left" alignment="left" enabled="NO" state="on" inset="2" id="32">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="smallSystem"/>
</buttonCell>
</button>
</subviews>
</view>
</box>
<textField verticalHuggingPriority="750" id="33">
<rect key="frame" x="17" y="383" width="274" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" controlSize="small" lineBreakMode="truncatingMiddle" sendsActionOnEndEditing="YES" title="My Disk Image.2mg" id="34">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" id="35">
<rect key="frame" x="293" y="383" width="170" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="800K" id="36">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
</view>
<contentBorderThickness minY="22"/>
<connections>
<outlet property="delegate" destination="-2" id="44"/>
</connections>
</window>
</objects>
</document>

File diff suppressed because it is too large Load Diff