diff --git a/Ample.xcodeproj/project.pbxproj b/Ample.xcodeproj/project.pbxproj index bea00b1..acb95cc 100644 --- a/Ample.xcodeproj/project.pbxproj +++ b/Ample.xcodeproj/project.pbxproj @@ -79,6 +79,14 @@ B63C1BA8250192E600511A71 /* zijini.plist in Resources */ = {isa = PBXBuildFile; fileRef = B63C1BA7250192E600511A71 /* zijini.plist */; }; B63C1BA9250192E600511A71 /* zijini.plist in Resources */ = {isa = PBXBuildFile; fileRef = B63C1BA7250192E600511A71 /* zijini.plist */; }; B64979C224EF6703008ABD20 /* MediaViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B64979C124EF6703008ABD20 /* MediaViewController.m */; }; + B64AF1EE250EC35B00A09B9B /* DiskImages.xib in Resources */ = {isa = PBXBuildFile; fileRef = B64AF1EC250EC35B00A09B9B /* DiskImages.xib */; }; + B64AF1EF250EC35B00A09B9B /* DiskImages.xib in Resources */ = {isa = PBXBuildFile; fileRef = B64AF1EC250EC35B00A09B9B /* DiskImages.xib */; }; + B64AF1F2250ECB2E00A09B9B /* DiskImagesWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = B64AF1F1250ECB2E00A09B9B /* DiskImagesWindowController.m */; }; + B64AF1F3250ECB2E00A09B9B /* DiskImagesWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = B64AF1F1250ECB2E00A09B9B /* DiskImagesWindowController.m */; }; + B64AF1F6250ED5E400A09B9B /* TableCellView.m in Sources */ = {isa = PBXBuildFile; fileRef = B64AF1F5250ED5E400A09B9B /* TableCellView.m */; }; + B64AF1F7250ED5E400A09B9B /* TableCellView.m in Sources */ = {isa = PBXBuildFile; fileRef = B64AF1F5250ED5E400A09B9B /* TableCellView.m */; }; + B64AF1FA250EF6A500A09B9B /* Transformers.m in Sources */ = {isa = PBXBuildFile; fileRef = B64AF1F9250EF6A500A09B9B /* Transformers.m */; }; + B64AF1FB250EF6A500A09B9B /* Transformers.m in Sources */ = {isa = PBXBuildFile; fileRef = B64AF1F9250EF6A500A09B9B /* Transformers.m */; }; B64E15A924EA1D5300E8AD3D /* MachineViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B64E15A824EA1D5300E8AD3D /* MachineViewController.m */; }; B66236A924FD9A34006CABD7 /* PreferencesWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = B66236A824FD9A34006CABD7 /* PreferencesWindowController.m */; }; B66236B524FDA527006CABD7 /* SDL2.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B66236B224FDA522006CABD7 /* SDL2.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -282,6 +290,13 @@ B63C1BA7250192E600511A71 /* zijini.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = zijini.plist; sourceTree = ""; }; B64979C024EF6703008ABD20 /* MediaViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MediaViewController.h; sourceTree = ""; }; B64979C124EF6703008ABD20 /* MediaViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MediaViewController.m; sourceTree = ""; }; + B64AF1ED250EC35B00A09B9B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/DiskImages.xib; sourceTree = ""; }; + B64AF1F0250ECB2E00A09B9B /* DiskImagesWindowController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DiskImagesWindowController.h; sourceTree = ""; }; + B64AF1F1250ECB2E00A09B9B /* DiskImagesWindowController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DiskImagesWindowController.m; sourceTree = ""; }; + B64AF1F4250ED5E400A09B9B /* TableCellView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TableCellView.h; sourceTree = ""; }; + B64AF1F5250ED5E400A09B9B /* TableCellView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TableCellView.m; sourceTree = ""; }; + B64AF1F8250EF6A500A09B9B /* Transformers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Transformers.h; sourceTree = ""; }; + B64AF1F9250EF6A500A09B9B /* Transformers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Transformers.m; sourceTree = ""; }; B64E15A724EA1D5300E8AD3D /* MachineViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MachineViewController.h; sourceTree = ""; }; B64E15A824EA1D5300E8AD3D /* MachineViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MachineViewController.m; sourceTree = ""; }; B65593AF24ECB61800722E0C /* SlotViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SlotViewController.h; sourceTree = ""; }; @@ -450,16 +465,14 @@ B6BA257D24E99BE9005FB8FF /* Ample */ = { isa = PBXGroup; children = ( - B6E9A17E25088B1B005E7525 /* NewSlotViewController.h */, - B6E9A17F25088B1B005E7525 /* NewSlotViewController.m */, - B63C1B9125008A2700511A71 /* DownloadWindowController.h */, - B63C1B9225008A2700511A71 /* DownloadWindowController.m */, B6BA257E24E99BE9005FB8FF /* AppDelegate.h */, B6BA257F24E99BE9005FB8FF /* AppDelegate.m */, B63C1B8924FF4B7100511A71 /* Ample.h */, B63C1B8A24FF4BF700511A71 /* Ample.m */, - B6D6DE3F24FADFAC00661A5F /* LaunchWindowController.h */, - B6D6DE4024FADFAC00661A5F /* LaunchWindowController.m */, + B64AF1F4250ED5E400A09B9B /* TableCellView.h */, + B64AF1F5250ED5E400A09B9B /* TableCellView.m */, + B6E9A17E25088B1B005E7525 /* NewSlotViewController.h */, + B6E9A17F25088B1B005E7525 /* NewSlotViewController.m */, B65593B024ECB61800722E0C /* SlotViewController.m */, B65593AF24ECB61800722E0C /* SlotViewController.h */, B64979C024EF6703008ABD20 /* MediaViewController.h */, @@ -470,12 +483,11 @@ B60A6E1224EE0AE2004B7EEF /* FlippedView.h */, B608E17D2502FE0C00D53465 /* TransparentScroller.h */, B608E17E2502FE0C00D53465 /* TransparentScroller.m */, - B6004DED24FB05D600D38596 /* LogWindowController.h */, - B6004DEE24FB05D600D38596 /* LogWindowController.m */, - B66236A724FD9A34006CABD7 /* PreferencesWindowController.h */, - B66236A824FD9A34006CABD7 /* PreferencesWindowController.m */, + B6BA563A251685DA00B0C47D /* Window Controllers */, B6B9EA652506A5550080E70D /* EjectButton.h */, B6B9EA642506A5550080E70D /* EjectButton.m */, + B64AF1F8250EF6A500A09B9B /* Transformers.h */, + B64AF1F9250EF6A500A09B9B /* Transformers.m */, B6BA258124E99BEB005FB8FF /* Assets.xcassets */, B64E15AF24EA365E00E8AD3D /* Resources */, B6BA258624E99BEB005FB8FF /* Info.plist */, @@ -488,6 +500,23 @@ path = Ample; sourceTree = ""; }; + B6BA563A251685DA00B0C47D /* Window Controllers */ = { + isa = PBXGroup; + children = ( + B64AF1F0250ECB2E00A09B9B /* DiskImagesWindowController.h */, + B64AF1F1250ECB2E00A09B9B /* DiskImagesWindowController.m */, + B63C1B9125008A2700511A71 /* DownloadWindowController.h */, + B63C1B9225008A2700511A71 /* DownloadWindowController.m */, + B6D6DE3F24FADFAC00661A5F /* LaunchWindowController.h */, + B6D6DE4024FADFAC00661A5F /* LaunchWindowController.m */, + B6004DED24FB05D600D38596 /* LogWindowController.h */, + B6004DEE24FB05D600D38596 /* LogWindowController.m */, + B66236A724FD9A34006CABD7 /* PreferencesWindowController.h */, + B66236A824FD9A34006CABD7 /* PreferencesWindowController.m */, + ); + name = "Window Controllers"; + sourceTree = ""; + }; B6D6DE4224FAEE8900661A5F /* Nibs */ = { isa = PBXGroup; children = ( @@ -500,6 +529,7 @@ B61099E524F5F230005CB652 /* MediaView.xib */, B6D6DE3724FAC8B500661A5F /* Preferences.xib */, B61099E324F5F230005CB652 /* SlotView.xib */, + B64AF1EC250EC35B00A09B9B /* DiskImages.xib */, ); name = Nibs; sourceTree = ""; @@ -648,6 +678,7 @@ B6109A2B24F5F377005CB652 /* elppa.plist in Resources */, B63C1B8E25004C6D00511A71 /* mame-data.tgz in Resources */, B6DDECCE2505A86E0093587A /* eject-16x16@3x.png in Resources */, + B64AF1EE250EC35B00A09B9B /* DiskImages.xib in Resources */, B6109A3B24F5F377005CB652 /* apple2gsr1.plist in Resources */, B63C1B9625008A2700511A71 /* DownloadWindow.xib in Resources */, B6D6DE3B24FACF4F00661A5F /* Defaults.plist in Resources */, @@ -691,6 +722,7 @@ B6E4B5C424FDE2670094A35C /* LogWindow.xib in Resources */, B6E4B5C524FDE2670094A35C /* Assets.xcassets in Resources */, B6E4B5C624FDE2670094A35C /* apple2e.plist in Resources */, + B64AF1EF250EC35B00A09B9B /* DiskImages.xib in Resources */, B63C1BA2250192D800511A71 /* cecg.plist in Resources */, B6E4B5C724FDE2670094A35C /* am100.plist in Resources */, B6E4B5C824FDE2670094A35C /* laser2c.plist in Resources */, @@ -768,7 +800,10 @@ B6BA258024E99BE9005FB8FF /* AppDelegate.m in Sources */, B6004DF024FB05D600D38596 /* LogWindowController.m in Sources */, B66236A924FD9A34006CABD7 /* PreferencesWindowController.m in Sources */, + B64AF1F2250ECB2E00A09B9B /* DiskImagesWindowController.m in Sources */, + B64AF1F6250ED5E400A09B9B /* TableCellView.m in Sources */, B63C1B9425008A2700511A71 /* DownloadWindowController.m in Sources */, + B64AF1FA250EF6A500A09B9B /* Transformers.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -777,12 +812,15 @@ buildActionMask = 2147483647; files = ( B608E1802502FE0C00D53465 /* TransparentScroller.m in Sources */, + B64AF1FB250EF6A500A09B9B /* Transformers.m in Sources */, B6E4B5B024FDE2670094A35C /* main.m in Sources */, B63C1B8C24FF4BF700511A71 /* Ample.m in Sources */, + B64AF1F3250ECB2E00A09B9B /* DiskImagesWindowController.m in Sources */, B6B9EA672506A5550080E70D /* EjectButton.m in Sources */, B6E4B5B124FDE2670094A35C /* LaunchWindowController.m in Sources */, B6E4B5B224FDE2670094A35C /* MachineViewController.m in Sources */, B6E4B5B324FDE2670094A35C /* MediaViewController.m in Sources */, + B64AF1F7250ED5E400A09B9B /* TableCellView.m in Sources */, B6E4B5B424FDE2670094A35C /* FlippedView.m in Sources */, B6E4B5B524FDE2670094A35C /* AppDelegate.m in Sources */, B6E4B5B624FDE2670094A35C /* LogWindowController.m in Sources */, @@ -810,6 +848,14 @@ name = MediaView.xib; sourceTree = ""; }; + B64AF1EC250EC35B00A09B9B /* DiskImages.xib */ = { + isa = PBXVariantGroup; + children = ( + B64AF1ED250EC35B00A09B9B /* Base */, + ); + name = DiskImages.xib; + sourceTree = ""; + }; B66236BF24FDB7A6006CABD7 /* Credits.rtf */ = { isa = PBXVariantGroup; children = ( diff --git a/Ample/AppDelegate.m b/Ample/AppDelegate.m index b12e677..0f502a8 100644 --- a/Ample/AppDelegate.m +++ b/Ample/AppDelegate.m @@ -10,6 +10,8 @@ #import "LaunchWindowController.h" #import "PreferencesWindowController.h" #import "DownloadWindowController.h" +#import "DiskImagesWindowController.h" +#import "Transformers.h" @interface AppDelegate () @property (weak) IBOutlet NSWindow *installWindow; @@ -20,6 +22,7 @@ NSWindowController *_prefs; NSWindowController *_launcher; NSWindowController *_downloader; + NSWindowController *_diskImages; } @@ -31,6 +34,8 @@ NSDictionary *dict; + RegisterTransformers(); + path = [bundle pathForResource: @"Defaults" ofType: @"plist"]; dict = [NSDictionary dictionaryWithContentsOfFile: path]; @@ -41,7 +46,10 @@ + _diskImages = [DiskImagesWindowController sharedInstance]; //[DiskImagesWindowController new]; + if ([self installMameComponents]) { + [self displayLaunchWindow]; } @@ -112,6 +120,7 @@ } [win close]; [self displayLaunchWindow]; + [self displayROMS: nil]; }); }]; @@ -143,12 +152,18 @@ } -- (IBAction)downloadROMS:(id)sender { - +- (IBAction)displayROMS:(id)sender { if (!_downloader) { - _downloader = [DownloadWindowController new]; + _downloader = [DownloadWindowController sharedInstance]; } [_downloader showWindow: sender]; } +- (IBAction)displayRecentDiskImages:(id)sender { + if (!_diskImages) { + _diskImages = [DiskImagesWindowController sharedInstance]; + } + [_diskImages showWindow: sender]; +} + @end diff --git a/Ample/Base.lproj/DiskImages.xib b/Ample/Base.lproj/DiskImages.xib new file mode 100644 index 0000000..9820552 --- /dev/null +++ b/Ample/Base.lproj/DiskImages.xib @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FilePathTransformer + + + + + + + + + + + + + + + + + FileSizeTransformer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Ample/Base.lproj/DownloadWindow.xib b/Ample/Base.lproj/DownloadWindow.xib index b86f454..fd3de9b 100644 --- a/Ample/Base.lproj/DownloadWindow.xib +++ b/Ample/Base.lproj/DownloadWindow.xib @@ -14,7 +14,7 @@ - + diff --git a/Ample/Base.lproj/LaunchWindow.xib b/Ample/Base.lproj/LaunchWindow.xib index af7c17a..e8d55ec 100644 --- a/Ample/Base.lproj/LaunchWindow.xib +++ b/Ample/Base.lproj/LaunchWindow.xib @@ -19,7 +19,7 @@ - + diff --git a/Ample/Base.lproj/MainMenu.xib b/Ample/Base.lproj/MainMenu.xib index b6f6835..286bd61 100644 --- a/Ample/Base.lproj/MainMenu.xib +++ b/Ample/Base.lproj/MainMenu.xib @@ -402,7 +402,13 @@ - + + + + + + + diff --git a/Ample/Defaults.plist b/Ample/Defaults.plist index 3a4e96d..4414f9d 100644 --- a/Ample/Defaults.plist +++ b/Ample/Defaults.plist @@ -10,5 +10,7 @@ MamePath /usr/local/bin/mame + NSQuitAlwaysKeepsWindows + diff --git a/Ample/DiskImagesWindowController.h b/Ample/DiskImagesWindowController.h new file mode 100644 index 0000000..37562cd --- /dev/null +++ b/Ample/DiskImagesWindowController.h @@ -0,0 +1,28 @@ +// +// DiskImagesWindowController.h +// Ample +// +// Created by Kelvin Sherlock on 9/13/2020. +// Copyright © 2020 Kelvin Sherlock. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface DiskImagesWindowController : NSWindowController + ++(instancetype)sharedInstance; + +@end + +@interface DiskImagesWindowController (TableView) + +@end + +@interface DiskImagesWindowController (Menu) + +@end + + +NS_ASSUME_NONNULL_END diff --git a/Ample/DiskImagesWindowController.m b/Ample/DiskImagesWindowController.m new file mode 100644 index 0000000..9ef7d98 --- /dev/null +++ b/Ample/DiskImagesWindowController.m @@ -0,0 +1,319 @@ +// +// DiskImagesWindowController.m +// Ample +// +// Created by Kelvin Sherlock on 9/13/2020. +// Copyright © 2020 Kelvin Sherlock. All rights reserved. +// + +#import "DiskImagesWindowController.h" +#import "TableCellView.h" +#import "Ample.h" + +@interface DiskImagesWindowController () +@property (weak) IBOutlet NSTableView *tableView; +@property (strong) IBOutlet NSArrayController *arrayController; +@property (strong) NSMutableArray *content; + +@end + +@implementation DiskImagesWindowController { + BOOL _dirty; + NSSet *_extensions; + NSTimer *_timer; + +} + ++(instancetype)sharedInstance { + static DiskImagesWindowController *me; + if (!me) { + me = [self new]; + } + return me; +} + ++ (void)restoreWindowWithIdentifier:(NSUserInterfaceItemIdentifier)identifier state:(NSCoder *)state completionHandler:(void (^)(NSWindow *, NSError *))completionHandler { + NSLog(@"restore disk images window"); + NSWindowController *controller = [self sharedInstance]; + NSWindow *w = [controller window]; + [w restoreStateWithCoder: state]; + completionHandler(w, nil); +} + +-(instancetype)init { + + if ((self = [super init])) { + + [self loadRecentDiskImages]; + + _extensions = [NSSet setWithObjects: + @"2img", @"2mg", @"chd", @"dc", @"do", @"dsk", @"hd", @"hdv", @"image", @"nib", @"po", @"wav", @"woz", @"iso", nil + ]; + } + return self; +} + +-(NSString *)windowNibName { + return @"DiskImages"; +} + +- (void)windowDidLoad { + + if (!_content) + [self setContent: [NSMutableArray new]]; + + [super windowDidLoad]; + NSWindow *window = [self window]; + [window setRestorable: YES]; + [window setRestorationClass: [self class]]; + + + [_tableView registerForDraggedTypes: @[NSPasteboardTypeFileURL]]; + [_tableView setDraggingSourceOperationMask: NSDragOperationCopy forLocal: NO]; // enable drag/drop to othr apps. + + // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. +} + +-(void)loadRecentDiskImages { +// NSError *error; + + NSURL *sd = SupportDirectory(); + NSURL *url = [sd URLByAppendingPathComponent: @"RecentDiskImages.plist"]; + + NSData *data = [NSData dataWithContentsOfURL: url]; + if (data) { + _content = [NSPropertyListSerialization propertyListWithData:data options:NSPropertyListMutableContainers format:nil error: nil]; + + } + if (!_content) + _content = [NSMutableArray new]; + + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + + [nc addObserver: self selector: @selector(diskImageAdded:) name: @"DiskImageAdded" object: nil]; + + [nc addObserver: self selector: @selector(willTerminate:) name: NSApplicationWillTerminateNotification object: nil]; +} + +-(void)diskImageAdded: (NSNotification *)notification { + + NSURL *url = [notification object]; + if (!url) return; + + [self addFile: url]; +} +-(void)markDirty { + _dirty = YES; + if (_timer) [_timer invalidate]; + _timer = [NSTimer scheduledTimerWithTimeInterval: 5 * 60 repeats: NO block: ^(NSTimer *t) { + + self->_timer = nil; + [self saveFile]; + }]; +} + +-(void)saveFile { + + [_timer invalidate]; + _timer = nil; + + NSURL *sd = SupportDirectory(); + NSURL *url = [sd URLByAppendingPathComponent: @"RecentDiskImages.plist"]; + + if (_content && url) { + [_content writeToURL: url atomically: YES]; + } + _dirty = NO; + +} + +-(void)willTerminate: (NSNotification *)notification { + // if dirty, write data.... + + if (!_dirty) return; + + [self saveFile]; + +} + + + + +-(BOOL)addFile: (NSObject *)pathOrURL { + + NSString *path = nil; + NSURL *url = nil; + if ([pathOrURL isKindOfClass: [NSString class]]) { + path = (NSString *)pathOrURL; + } else if ([pathOrURL isKindOfClass: [NSURL class]]){ + url = (NSURL *)pathOrURL; + + path = [NSString stringWithCString: [url fileSystemRepresentation] encoding: NSUTF8StringEncoding]; + } + if (!path) return NO; + + // todo -- check if file is in the list already... + + BOOL found = NO; + // should really compare the volume id / ino I suppose. + for (NSMutableDictionary *d in _content) { + NSString *s = [d objectForKey: @"path"]; + if ([path compare: s] == NSOrderedSame) { + found = YES; + [d setObject: [NSDate new] forKey: @"date"]; + [self markDirty]; + break; + } + } + if (found) return NO; + + + NSFileManager *fm = [NSFileManager defaultManager]; + NSError *error; + + NSDictionary *attr = [fm attributesOfItemAtPath: path error: &error]; + if (error) { + NSLog(@"%@ : %@", path, error); + return NO; + } + + + NSNumber *size = [attr objectForKey: NSFileSize]; + + NSMutableDictionary *d = [NSMutableDictionary dictionaryWithObjectsAndKeys: + path, @"path", + size, @"size", + [NSDate new], @"date", + nil]; + + @synchronized (self) { + if (_arrayController) + [_arrayController addObject: d]; + else + [_content addObject: d]; + } + [self markDirty]; + return YES; +} + + +-(NSMutableDictionary *)clickedItem { + + NSInteger row = [_tableView clickedRow]; + if (row < 0) return nil; + + NSTableCellView *v = [_tableView viewAtColumn: 0 row: row makeIfNecessary: NO]; + return [v objectValue]; +} +#pragma mark - IBActions + +- (IBAction)showInFinder:(id)sender { + + NSMutableDictionary *item = [self clickedItem]; + if (!item) return; + NSString *path = [item objectForKey: @"path"]; + + NSURL *url = [NSURL fileURLWithPath: path]; + if (!url) return; + + NSWorkspace *ws = [NSWorkspace sharedWorkspace]; + [ws activateFileViewerSelectingURLs: @[url]]; +} + +- (IBAction)eject:(id)sender { + + NSMutableDictionary *item = [self clickedItem]; + if (!item) return; + + @synchronized (self) { + + if (_arrayController) { + [_arrayController removeObject: item]; + } else { + [_content removeObject: item]; + } + [self markDirty]; + } + +} + +@end + +@implementation DiskImagesWindowController (TableView) + +#if 0 +- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView { + return 5; //[_data count]; +} + +- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row { + + NSTableCellView *v = [tableView makeViewWithIdentifier: @"PathCell" owner: self]; + + return v; +} +#endif + + +-(id)tableView:(NSTableView *)tableView pasteboardWriterForRow:(NSInteger)row { + + id objects = [_arrayController arrangedObjects]; + + NSDictionary *d = [objects objectAtIndex: row]; + + NSString *path = [d objectForKey: @"path"]; + + NSURL *url = [NSURL fileURLWithPath: path]; + return url; + +#if 0 + NSPasteboardItem *item = [NSPasteboardItem new]; + [item setString: [url absoluteString] forType: NSPasteboardTypeFileURL]; // FileURL + [item setString: path forType: NSPasteboardTypeString]; // for Terminal.app + + return item; +#endif +} + + + +-(NSDragOperation)tableView:(NSTableView *)tableView validateDrop:(id)info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)dropOperation { + + if ([info draggingSource] == _tableView) return NSDragOperationNone; + + // option key will ignore all filetype restrictions. + if ([NSEvent modifierFlags] & NSEventModifierFlagOption) return NSDragOperationCopy; + + // this only checks the first dragged item... + NSPasteboard * pb = [info draggingPasteboard]; + NSURL *url = [NSURL URLFromPasteboard: pb]; + + NSString *ext = [url pathExtension]; + ext = [ext lowercaseString]; + if ([_extensions containsObject: ext]) + return NSDragOperationCopy; + + return NSDragOperationNone; +} + +-(BOOL)tableView:(NSTableView *)tableView acceptDrop:(id)info row:(NSInteger)row dropOperation:(NSTableViewDropOperation)dropOperation { + + if ([info draggingSource] == _tableView) return NO; + + NSPasteboard * pb = [info draggingPasteboard]; + + BOOL ok = NO; + for (NSPasteboardItem *item in [pb pasteboardItems]) { + + // need to convert from a string to a url back to a file in case it's a file id url? + NSString *s = [item stringForType: NSPasteboardTypeFileURL]; + if (!s) continue; + NSURL *url = [NSURL URLWithString: s]; + if (!url) continue; + + ok |= [self addFile: url]; + } + return ok; +} + +@end diff --git a/Ample/DownloadWindowController.h b/Ample/DownloadWindowController.h index 80c1ec3..65103e1 100644 --- a/Ample/DownloadWindowController.h +++ b/Ample/DownloadWindowController.h @@ -10,7 +10,7 @@ NS_ASSUME_NONNULL_BEGIN -@interface DownloadWindowController : NSWindowController +@interface DownloadWindowController : NSWindowController @property NSString *currentROM; @property NSInteger currentCount; @@ -18,6 +18,9 @@ NS_ASSUME_NONNULL_BEGIN @property NSInteger errorCount; @property BOOL active; ++(instancetype)sharedInstance; + + @end @interface DownloadWindowController (URL) diff --git a/Ample/DownloadWindowController.m b/Ample/DownloadWindowController.m index 956e008..ecc6a63 100644 --- a/Ample/DownloadWindowController.m +++ b/Ample/DownloadWindowController.m @@ -52,12 +52,38 @@ enum { NSMutableDictionary *_taskIndex; } ++(instancetype)sharedInstance { + static DownloadWindowController *me = nil; + if (!me) { + me = [self new]; + } + return me; +} + ++ (void)restoreWindowWithIdentifier:(nonnull NSUserInterfaceItemIdentifier)identifier state:(nonnull NSCoder *)state completionHandler:(nonnull void (^)(NSWindow * _Nullable, NSError * _Nullable))completionHandler { + NSLog(@"restore rom manager window"); + + NSWindowController *controller = [DownloadWindowController sharedInstance]; + NSWindow *w = [controller window]; + [w restoreStateWithCoder: state]; + completionHandler(w, nil); +} + +#if 0 +- (void)encodeWithCoder:(nonnull NSCoder *)coder { + +} +#endif + -(NSString *)windowNibName { return @"DownloadWindow"; } - (void)windowDidLoad { [super windowDidLoad]; + NSWindow *window = [self window]; + [window setRestorable: YES]; + [window setRestorationClass: [self class]]; // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. @@ -303,6 +329,7 @@ enum { */ NSLog(@"%@", src); } + @end @implementation DownloadWindowController (Table) diff --git a/Ample/MediaViewController.h b/Ample/MediaViewController.h index 36103ce..df9b61f 100644 --- a/Ample/MediaViewController.h +++ b/Ample/MediaViewController.h @@ -26,10 +26,4 @@ NS_ASSUME_NONNULL_BEGIN -@interface TablePathView : NSTableCellView -@property (weak) IBOutlet NSPathControl *pathControl; -@property (weak) IBOutlet NSButton *ejectButton; -@property (weak) IBOutlet NSImageView *dragHandle; -@end - NS_ASSUME_NONNULL_END diff --git a/Ample/MediaViewController.m b/Ample/MediaViewController.m index e8c7360..5e1f2c1 100644 --- a/Ample/MediaViewController.m +++ b/Ample/MediaViewController.m @@ -7,42 +7,7 @@ // #import "MediaViewController.h" - - -@implementation TablePathView { - NSTrackingRectTag _trackingRect; -} - -#if 0 --(void)awakeFromNib { - - // this is apparently necessary for setTintColor to work. - NSImage *img; - img = [_ejectButton image]; - [img setTemplate: YES]; - img = [_ejectButton alternateImage]; - [img setTemplate: YES]; -} -#endif - --(void)viewDidMoveToSuperview { - return; - if (_trackingRect) { - [self removeTrackingRect: _trackingRect]; - } - NSRect rect = [_dragHandle frame]; - _trackingRect = [self addTrackingRect: rect owner: self userData: NULL assumeInside:NO]; -} - --(void)mouseEntered:(NSEvent *)event { - [_dragHandle setHidden: NO]; -} - --(void)mouseExited:(NSEvent *)event { - [_dragHandle setHidden: YES]; -} - -@end +#import "TableCellView.h" @protocol MediaNode @@ -286,7 +251,7 @@ b = [[MediaCategory alloc] initWithTitle: @"3.5\" Floppies"]; c = [[MediaCategory alloc] initWithTitle: @"Hard Drives"]; d = [[MediaCategory alloc] initWithTitle: @"CD-ROMs"]; - e = [[MediaCategory alloc] initWithTitle: @"Casettes"]; + e = [[MediaCategory alloc] initWithTitle: @"Cassettes"]; _data[0] = a; @@ -526,7 +491,7 @@ static NSString *kDragType = @"private.ample.media"; NSInteger indexes[2] = { 0, 0 }; indexes[0] = [cat index]; indexes[1] = [item index]; - NSData *data =[NSData dataWithBytes: indexes length: sizeof(indexes)]; + NSData *data = [NSData dataWithBytes: indexes length: sizeof(indexes)]; [pasteboard setData: data forType: kDragType]; return YES; @@ -646,6 +611,14 @@ static NSString *kDragType = @"private.ample.media"; - (IBAction)pathAction:(id)sender { // need to update the eject button... + + NSURL *url = [(NSPathControl *)sender URL]; + + if (url) { + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc postNotificationName: @"DiskImageAdded" object: url]; + } + [self rebuildArgs]; } @end diff --git a/Ample/TableCellView.h b/Ample/TableCellView.h new file mode 100644 index 0000000..d115eb2 --- /dev/null +++ b/Ample/TableCellView.h @@ -0,0 +1,22 @@ +// +// TableCellView.h +// Ample +// +// Created by Kelvin Sherlock on 9/13/2020. +// Copyright © 2020 Kelvin Sherlock. All rights reserved. +// + +#import + +//NS_ASSUME_NONNULL_BEGIN + + + +@interface TablePathView : NSTableCellView +@property (weak) IBOutlet NSPathControl *pathControl; +@property (weak) IBOutlet NSButton *ejectButton; +@property (weak) IBOutlet NSImageView *dragHandle; +@property BOOL movable; +@end + +//NS_ASSUME_NONNULL_END diff --git a/Ample/TableCellView.m b/Ample/TableCellView.m new file mode 100644 index 0000000..d90c7fa --- /dev/null +++ b/Ample/TableCellView.m @@ -0,0 +1,47 @@ +// +// TableCellView.m +// Ample +// +// Created by Kelvin Sherlock on 9/13/2020. +// Copyright © 2020 Kelvin Sherlock. All rights reserved. +// + +#import "TableCellView.h" + + + +@implementation TablePathView { + NSTrackingRectTag _trackingRect; +} + +#if 0 +-(void)awakeFromNib { + + // this is apparently necessary for setTintColor to work. + NSImage *img; + img = [_ejectButton image]; + [img setTemplate: YES]; + img = [_ejectButton alternateImage]; + [img setTemplate: YES]; +} +#endif + +-(void)viewDidMoveToSuperview { + return; + if (_trackingRect) { + [self removeTrackingRect: _trackingRect]; + } + NSRect rect = [_dragHandle frame]; + _trackingRect = [self addTrackingRect: rect owner: self userData: NULL assumeInside:NO]; +} + +-(void)mouseEntered:(NSEvent *)event { + [_dragHandle setHidden: NO]; +} + +-(void)mouseExited:(NSEvent *)event { + [_dragHandle setHidden: YES]; +} + +@end + diff --git a/Ample/Transformers.h b/Ample/Transformers.h new file mode 100644 index 0000000..5831fb1 --- /dev/null +++ b/Ample/Transformers.h @@ -0,0 +1,24 @@ +// +// Transformers.h +// Ample +// +// Created by Kelvin Sherlock on 9/13/2020. +// Copyright © 2020 Kelvin Sherlock. All rights reserved. +// + +#import + +void RegisterTransformers(void); + +NS_ASSUME_NONNULL_BEGIN + +@interface FilePathTransformer : NSValueTransformer + +@end + +@interface FileSizeTransformer : NSValueTransformer + +@end + + +NS_ASSUME_NONNULL_END diff --git a/Ample/Transformers.m b/Ample/Transformers.m new file mode 100644 index 0000000..db07232 --- /dev/null +++ b/Ample/Transformers.m @@ -0,0 +1,68 @@ +// +// Transformers.m +// Ample +// +// Created by Kelvin Sherlock on 9/13/2020. +// Copyright © 2020 Kelvin Sherlock. All rights reserved. +// + +#import "Transformers.h" + +@implementation FilePathTransformer + ++ (Class)transformedValueClass { + return [NSString class]; +} + ++ (BOOL)allowsReverseTransformation { + return NO; +} + +- (id)transformedValue:(id)value { + if (!value) return value; + + return [(NSString *)value lastPathComponent]; +} + +@end + +@implementation FileSizeTransformer + ++ (Class)transformedValueClass { + return [NSString class]; +} + ++ (BOOL)allowsReverseTransformation { + return NO; +} + +- (id)transformedValue:(id)value { + if (!value) return value; + if (![value respondsToSelector: @selector(integerValue)]) { + [NSException raise: NSInternalInconsistencyException + format: @"Value (%@) does not respond to -integerValue.", + [value class]]; + } + NSInteger size = [(NSNumber *)value integerValue]; + + if (size < 0) return nil; + if (size < 1024*1024) return [NSString stringWithFormat: @"%.1fKB", (float)size / 1024]; + if (size < 1024*1024*1024) return [NSString stringWithFormat: @"%.1fMB", (float)size / (1024*1024)]; + + return [NSString stringWithFormat: @"%.1fGB", (float)size / (1024*1024*1024)]; +} + +@end + + + +void RegisterTransformers(void) { + + NSValueTransformer *t; + t = [FileSizeTransformer new]; + [NSValueTransformer setValueTransformer: t forName: @"FileSizeTransformer"]; + + t = [FilePathTransformer new]; + [NSValueTransformer setValueTransformer: t forName: @"FilePathTransformer"]; + +}