2020-09-11 02:06:41 +00:00
|
|
|
//
|
2021-05-30 22:19:51 +00:00
|
|
|
// SlotViewController.m
|
2020-09-11 02:06:41 +00:00
|
|
|
// Ample
|
|
|
|
//
|
|
|
|
// Created by Kelvin Sherlock on 9/9/2020.
|
|
|
|
// Copyright © 2020 Kelvin Sherlock. All rights reserved.
|
|
|
|
//
|
|
|
|
|
2021-03-13 00:58:12 +00:00
|
|
|
|
2021-03-08 23:59:02 +00:00
|
|
|
#import "Ample.h"
|
2021-05-30 22:19:51 +00:00
|
|
|
#import "SlotViewController.h"
|
2020-10-03 18:33:39 +00:00
|
|
|
#import "Menu.h"
|
2021-03-08 23:59:02 +00:00
|
|
|
#import "Slot.h"
|
|
|
|
#import "Media.h"
|
2020-09-11 02:06:41 +00:00
|
|
|
|
2021-03-13 00:58:12 +00:00
|
|
|
|
|
|
|
#import <objc/runtime.h>
|
|
|
|
|
2021-01-17 06:05:02 +00:00
|
|
|
/* number of slot types. bitmask used so should be < sizeof(unsigned *8) */
|
2021-03-14 19:58:10 +00:00
|
|
|
#define SLOT_COUNT 22
|
2021-01-18 20:46:07 +00:00
|
|
|
static_assert(SLOT_COUNT <= sizeof(unsigned) * 8, "too many slot types");
|
2020-09-11 02:06:41 +00:00
|
|
|
|
2021-01-17 06:19:11 +00:00
|
|
|
#define SIZEOF(x) (sizeof(x) / sizeof(x[0]))
|
|
|
|
|
2020-09-11 02:06:41 +00:00
|
|
|
|
2021-03-13 00:58:12 +00:00
|
|
|
static unsigned RootKey = 0;
|
2020-09-11 02:06:41 +00:00
|
|
|
|
|
|
|
|
2021-05-30 22:19:51 +00:00
|
|
|
@interface SlotViewController ()
|
2020-09-11 02:06:41 +00:00
|
|
|
@property (weak) IBOutlet NSOutlineView *outlineView;
|
2021-03-13 00:58:12 +00:00
|
|
|
@property (weak) IBOutlet NSOutlineView *childOutlineView;
|
2020-09-11 02:06:41 +00:00
|
|
|
|
|
|
|
@end
|
|
|
|
|
2021-05-30 22:19:51 +00:00
|
|
|
@implementation SlotViewController {
|
2021-03-08 23:59:02 +00:00
|
|
|
NSArray *_root;
|
2020-09-11 02:06:41 +00:00
|
|
|
|
|
|
|
unsigned _slots_explicit;
|
|
|
|
unsigned _slots_valid;
|
|
|
|
unsigned _slots_default;
|
|
|
|
|
2021-03-08 23:59:02 +00:00
|
|
|
Slot *_slot_object[SLOT_COUNT];
|
2021-01-18 20:46:07 +00:00
|
|
|
NSString *_slot_value[SLOT_COUNT]; // when explicitely set.
|
2021-03-08 23:59:02 +00:00
|
|
|
|
|
|
|
Media _slot_media[SLOT_COUNT];
|
|
|
|
Media _machine_media;
|
2020-09-11 02:06:41 +00:00
|
|
|
|
|
|
|
NSDictionary *_machine_data;
|
2021-03-08 23:59:02 +00:00
|
|
|
|
2021-03-10 23:37:59 +00:00
|
|
|
IBOutlet NSPopover *_popover;
|
2021-03-08 23:59:02 +00:00
|
|
|
|
2021-06-07 04:34:26 +00:00
|
|
|
BOOL _loadingBookmark;
|
2020-09-11 02:06:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)viewDidLoad {
|
|
|
|
[super viewDidLoad];
|
|
|
|
// Do view setup here.
|
2020-09-12 22:00:09 +00:00
|
|
|
|
2021-03-08 23:59:02 +00:00
|
|
|
_root = @[];
|
2021-03-13 00:58:12 +00:00
|
|
|
objc_setAssociatedObject(_outlineView, &RootKey, _root, OBJC_ASSOCIATION_RETAIN);
|
|
|
|
|
|
|
|
|
2021-03-10 23:37:59 +00:00
|
|
|
//[_outlineView setIndentationPerLevel: 2.0];
|
2020-09-11 02:06:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
-(void)resetMachine {
|
|
|
|
|
2021-03-08 23:59:02 +00:00
|
|
|
_root = @[];
|
2021-03-13 00:58:12 +00:00
|
|
|
objc_setAssociatedObject(_outlineView, &RootKey, _root, OBJC_ASSOCIATION_RETAIN);
|
|
|
|
|
2020-09-11 02:06:41 +00:00
|
|
|
[_outlineView reloadData];
|
|
|
|
|
|
|
|
_slots_valid = 0;
|
|
|
|
_slots_explicit = 0;
|
|
|
|
_slots_default = 0;
|
2021-03-08 23:59:02 +00:00
|
|
|
_machine_media = EmptyMedia;
|
2020-09-11 02:06:41 +00:00
|
|
|
_machine_data = nil;
|
|
|
|
|
2021-01-18 20:46:07 +00:00
|
|
|
for (unsigned i = 0; i < SLOT_COUNT; ++i) {
|
2021-03-08 23:59:02 +00:00
|
|
|
_slot_media[i] = EmptyMedia;
|
2020-09-11 02:06:41 +00:00
|
|
|
_slot_object[i] = nil;
|
|
|
|
_slot_value[i] = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
[self setResolution: NSMakeSize(0, 0)];
|
|
|
|
[self setArgs: @[]];
|
2021-03-08 23:59:02 +00:00
|
|
|
[self setMedia: EmptyMedia];
|
2020-09-11 02:06:41 +00:00
|
|
|
}
|
|
|
|
|
2021-03-10 03:22:24 +00:00
|
|
|
|
2020-09-11 02:06:41 +00:00
|
|
|
-(void)loadMachine {
|
|
|
|
|
|
|
|
|
2021-03-08 23:59:02 +00:00
|
|
|
NSDictionary *d = MameMachine(_machine);
|
2020-09-11 02:06:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
if (!d) {
|
|
|
|
[self resetMachine];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
NSArray *r = [d objectForKey: @"resolution"];
|
|
|
|
NSSize res = NSMakeSize(0, 0);
|
|
|
|
if (r) {
|
|
|
|
res.width = [(NSNumber *)[r objectAtIndex: 0 /*@"width"*/] doubleValue];
|
|
|
|
res.height = [(NSNumber *)[r objectAtIndex: 1 /*@"height"*/] doubleValue];
|
|
|
|
}
|
|
|
|
[self setResolution: res];
|
|
|
|
|
|
|
|
_slots_valid = 0;
|
|
|
|
//_slots_explicit = 0;
|
|
|
|
_slots_default = 0;
|
|
|
|
|
2021-03-08 23:59:02 +00:00
|
|
|
_machine_media = MediaFromDictionary([d objectForKey: @"media"]);
|
2020-09-11 02:06:41 +00:00
|
|
|
|
|
|
|
_machine_data = d;
|
|
|
|
|
2021-03-08 23:59:02 +00:00
|
|
|
for (unsigned i = 0; i < SLOT_COUNT; ++i) {
|
|
|
|
_slot_media[i] = EmptyMedia;
|
2020-09-11 02:06:41 +00:00
|
|
|
_slot_object[i] = nil;
|
2021-03-08 23:59:02 +00:00
|
|
|
}
|
|
|
|
|
2020-09-11 02:06:41 +00:00
|
|
|
|
2021-03-08 23:59:02 +00:00
|
|
|
extern NSArray *BuildSlots(NSString *name, NSDictionary *data);
|
|
|
|
_root = BuildSlots(_machine, d);
|
2021-03-13 00:58:12 +00:00
|
|
|
objc_setAssociatedObject(_outlineView, &RootKey, _root, OBJC_ASSOCIATION_RETAIN);
|
2021-03-08 23:59:02 +00:00
|
|
|
|
|
|
|
for (Slot *item in _root) {
|
|
|
|
NSInteger index = [item index];
|
|
|
|
if (index < 0) continue;
|
|
|
|
unsigned mask = 1 << index;
|
2020-09-11 02:06:41 +00:00
|
|
|
|
2021-03-08 23:59:02 +00:00
|
|
|
_slots_valid |= mask;
|
|
|
|
if ([item defaultIndex] >= 0)
|
2020-09-11 02:06:41 +00:00
|
|
|
_slots_default |= mask;
|
2021-03-08 23:59:02 +00:00
|
|
|
|
|
|
|
if (_slot_value[index])
|
|
|
|
[item selectValue: _slot_value[index]];
|
|
|
|
|
|
|
|
_slot_media[index] = [item selectedMedia];
|
|
|
|
_slot_object[index] = item;
|
2020-09-11 02:06:41 +00:00
|
|
|
}
|
2021-03-08 23:59:02 +00:00
|
|
|
|
|
|
|
|
2020-09-11 02:06:41 +00:00
|
|
|
[_outlineView reloadData];
|
2021-06-07 04:34:26 +00:00
|
|
|
if (!_loadingBookmark) {
|
|
|
|
[self rebuildMedia];
|
|
|
|
[self rebuildArgs];
|
|
|
|
}
|
2020-09-11 02:06:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
-(void)setMachine: (NSString *)machine {
|
|
|
|
if (_machine == machine) return;
|
|
|
|
if (_machine && machine && [machine compare: _machine] == NSOrderedSame) return;
|
|
|
|
_machine = machine;
|
|
|
|
|
|
|
|
if (!machine) {
|
|
|
|
[self resetMachine];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
[self loadMachine];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-(void)rebuildMedia {
|
|
|
|
|
2021-03-08 23:59:02 +00:00
|
|
|
|
|
|
|
Media media = _machine_media;
|
|
|
|
|
2020-09-11 02:06:41 +00:00
|
|
|
unsigned mask = 1;
|
2021-01-18 20:46:07 +00:00
|
|
|
for (unsigned i = 0; i < SLOT_COUNT; ++i, mask <<= 1) {
|
2020-09-11 02:06:41 +00:00
|
|
|
|
|
|
|
if (_slots_valid & mask) {
|
2021-03-08 23:59:02 +00:00
|
|
|
MediaAdd(&media, &_slot_media[i]);
|
2020-09-11 02:06:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-08 23:59:02 +00:00
|
|
|
[self setMedia: media];
|
2020-09-11 02:06:41 +00:00
|
|
|
}
|
|
|
|
|
2021-03-08 23:59:02 +00:00
|
|
|
|
2020-09-11 02:06:41 +00:00
|
|
|
-(void)rebuildArgs {
|
|
|
|
|
|
|
|
NSMutableArray *args = [NSMutableArray new];
|
|
|
|
|
2021-03-08 23:59:02 +00:00
|
|
|
for (Slot *item in _root) {
|
2020-09-11 02:06:41 +00:00
|
|
|
|
2021-03-08 23:59:02 +00:00
|
|
|
NSArray *x = [item args];
|
|
|
|
if (x) [args addObjectsFromArray: x];
|
2020-09-11 02:06:41 +00:00
|
|
|
}
|
2021-03-08 23:59:02 +00:00
|
|
|
|
2020-09-11 02:06:41 +00:00
|
|
|
[self setArgs: args];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (IBAction)menuChanged:(NSPopUpButton *)sender {
|
|
|
|
|
2021-03-10 03:58:01 +00:00
|
|
|
BOOL direct = YES;
|
2021-03-08 23:59:02 +00:00
|
|
|
NSInteger index = [sender tag];
|
|
|
|
if (index < 0) return; //
|
2021-03-10 03:58:01 +00:00
|
|
|
|
|
|
|
if (index >= 0 && index < SLOT_COUNT) {
|
|
|
|
direct = YES;
|
|
|
|
} else {
|
|
|
|
direct = NO;
|
|
|
|
index &= ~0x10000;
|
|
|
|
}
|
2021-03-10 03:22:24 +00:00
|
|
|
if (index >= SLOT_COUNT) return; //
|
2020-09-11 02:06:41 +00:00
|
|
|
unsigned mask = 1 << index;
|
|
|
|
|
|
|
|
|
|
|
|
// index 0 = ram = special case...
|
|
|
|
|
2021-03-08 23:59:02 +00:00
|
|
|
SlotOption *o = [[sender selectedItem] representedObject];
|
|
|
|
Slot *item = _slot_object[index];
|
2020-09-11 02:06:41 +00:00
|
|
|
|
2021-03-10 03:58:01 +00:00
|
|
|
if (direct) {
|
|
|
|
_slots_explicit |= mask;
|
|
|
|
_slot_value[index] = [o value];
|
|
|
|
//_slots_default &= ~mask;
|
|
|
|
}
|
2020-09-11 02:06:41 +00:00
|
|
|
|
2021-03-08 23:59:02 +00:00
|
|
|
Media media = [item selectedMedia];
|
|
|
|
if (!MediaEqual(&media, &_slot_media[index])) {
|
|
|
|
_slot_media[index] = media;
|
2020-09-11 02:06:41 +00:00
|
|
|
[self rebuildMedia];
|
2021-03-08 23:59:02 +00:00
|
|
|
|
2020-09-11 02:06:41 +00:00
|
|
|
}
|
2021-03-08 23:59:02 +00:00
|
|
|
|
2021-03-10 03:58:01 +00:00
|
|
|
// needs to reload children if expanded.
|
2021-03-13 00:58:12 +00:00
|
|
|
#ifdef SLOT_TREE
|
2021-03-10 03:58:01 +00:00
|
|
|
if (direct) {
|
|
|
|
BOOL rc = ([_outlineView isItemExpanded: item]);
|
|
|
|
[_outlineView reloadItem: item reloadChildren: rc];
|
|
|
|
}
|
2021-03-13 00:58:12 +00:00
|
|
|
#endif
|
2020-09-11 02:06:41 +00:00
|
|
|
[self rebuildArgs];
|
|
|
|
}
|
2021-03-10 23:37:59 +00:00
|
|
|
- (IBAction)hamburger:(id)sender {
|
|
|
|
|
2021-03-13 00:58:12 +00:00
|
|
|
#if 0
|
2021-03-10 23:37:59 +00:00
|
|
|
if ([_popover isShown]) {
|
|
|
|
[_popover close];
|
|
|
|
}
|
|
|
|
#endif
|
2021-03-13 00:58:12 +00:00
|
|
|
|
|
|
|
NSInteger index = [sender tag];
|
|
|
|
if (index < 0 || index >= SLOT_COUNT) return;
|
|
|
|
|
|
|
|
Slot *item = _slot_object[index];
|
|
|
|
|
|
|
|
NSArray *children = [item selectedChildren];
|
|
|
|
objc_setAssociatedObject(_childOutlineView, &RootKey, children, OBJC_ASSOCIATION_RETAIN);
|
|
|
|
if (!children) return;
|
|
|
|
|
|
|
|
[_childOutlineView reloadData];
|
|
|
|
NSSize size = [_popover contentSize];
|
|
|
|
if (size.width < 200) size.width = 250;
|
|
|
|
size = [_childOutlineView sizeThatFits: size];
|
|
|
|
size.height += 40;
|
|
|
|
[_popover setContentSize: size];
|
|
|
|
|
2021-03-10 23:37:59 +00:00
|
|
|
[_popover showRelativeToRect: [sender bounds]
|
|
|
|
ofView: sender
|
2021-03-13 00:58:12 +00:00
|
|
|
preferredEdge: NSRectEdgeMaxY];
|
2021-03-10 23:37:59 +00:00
|
|
|
}
|
|
|
|
|
2020-09-11 02:06:41 +00:00
|
|
|
-(IBAction)resetSlots:(id)sender {
|
|
|
|
|
|
|
|
_slots_explicit = 0;
|
2021-01-18 20:46:07 +00:00
|
|
|
for (unsigned i = 0; i < SLOT_COUNT; ++i) {
|
2021-03-08 23:59:02 +00:00
|
|
|
_slot_media[i] = EmptyMedia;
|
2020-09-11 02:06:41 +00:00
|
|
|
_slot_value[i] = nil;
|
|
|
|
}
|
2021-03-08 23:59:02 +00:00
|
|
|
for (Slot *item in _root) {
|
2020-09-11 02:06:41 +00:00
|
|
|
[item reset];
|
|
|
|
// if children, reset them too...
|
2021-03-08 23:59:02 +00:00
|
|
|
NSInteger index = [item index];
|
|
|
|
if (index < 0) continue;
|
2020-09-11 02:06:41 +00:00
|
|
|
_slot_media[index] = [item selectedMedia];
|
|
|
|
}
|
|
|
|
|
2021-03-13 00:58:12 +00:00
|
|
|
#ifdef SLOT_TREE
|
2021-03-10 03:58:01 +00:00
|
|
|
[_outlineView reloadData];
|
2021-03-13 00:58:12 +00:00
|
|
|
#endif
|
2021-06-07 04:34:26 +00:00
|
|
|
if (!_loadingBookmark) {
|
|
|
|
[self rebuildMedia];
|
|
|
|
[self rebuildArgs];
|
|
|
|
}
|
2020-09-11 02:06:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
2021-05-30 22:19:51 +00:00
|
|
|
@implementation SlotViewController (OutlineView)
|
2020-09-11 02:06:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
|
|
|
|
|
2021-03-13 00:58:12 +00:00
|
|
|
NSArray *root = objc_getAssociatedObject(outlineView, &RootKey);
|
|
|
|
if (!item) return [root count];
|
2020-09-11 02:06:41 +00:00
|
|
|
|
2021-03-13 00:58:12 +00:00
|
|
|
#ifdef SLOT_TREE
|
2021-03-10 03:58:01 +00:00
|
|
|
NSArray *tmp = [(Slot *)item selectedChildren];
|
|
|
|
return [tmp count];
|
2021-03-13 00:58:12 +00:00
|
|
|
#endif
|
|
|
|
return 0;
|
2020-09-11 02:06:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item {
|
2021-03-13 00:58:12 +00:00
|
|
|
NSArray *root = objc_getAssociatedObject(outlineView, &RootKey);
|
|
|
|
|
|
|
|
if (!item) return [root objectAtIndex: index];
|
|
|
|
#ifdef SLOT_TREE
|
2021-03-10 03:58:01 +00:00
|
|
|
NSArray *tmp = [(Slot *)item selectedChildren];
|
|
|
|
return [tmp objectAtIndex: index];
|
2021-03-13 00:58:12 +00:00
|
|
|
#endif
|
2020-09-11 02:06:41 +00:00
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
|
2021-03-13 00:58:12 +00:00
|
|
|
|
|
|
|
#ifdef SLOT_TREE
|
2021-03-10 03:58:01 +00:00
|
|
|
if (!item) return NO;
|
|
|
|
NSArray *tmp = [(Slot *)item selectedChildren];
|
|
|
|
return [tmp count] > 0;
|
2021-03-13 00:58:12 +00:00
|
|
|
#else
|
|
|
|
return NO;
|
|
|
|
#endif
|
2020-09-11 02:06:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-03-08 23:59:02 +00:00
|
|
|
- (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(Slot *)item {
|
2020-09-11 02:06:41 +00:00
|
|
|
|
|
|
|
SlotTableCellView *v = [outlineView makeViewWithIdentifier: @"MenuCell" owner: self];
|
|
|
|
|
|
|
|
[item prepareView: v];
|
|
|
|
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-06-07 04:34:26 +00:00
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
@implementation SlotViewController (Bookmark)
|
|
|
|
|
|
|
|
|
|
|
|
-(void)willLoadBookmark:(NSDictionary *)bookmark {
|
|
|
|
_loadingBookmark = YES;
|
|
|
|
[self setMachine: nil];
|
|
|
|
}
|
|
|
|
-(void)didLoadBookmark:(NSDictionary *)bookmark {
|
|
|
|
_loadingBookmark = NO;
|
|
|
|
|
|
|
|
[self rebuildArgs];
|
|
|
|
}
|
|
|
|
|
|
|
|
-(BOOL)loadBookmark: (NSDictionary *)bookmark {
|
|
|
|
|
|
|
|
NSDictionary *dict = [bookmark objectForKey: @"slots"];
|
|
|
|
|
|
|
|
[self setMachine: [bookmark objectForKey: @"machine"]];
|
|
|
|
[self resetSlots: nil];
|
|
|
|
|
|
|
|
for (Slot *item in _root) {
|
|
|
|
[item reserialize: dict];
|
|
|
|
|
|
|
|
NSInteger index = [item index];
|
|
|
|
if (index >= 0 && index < SLOT_COUNT) {
|
|
|
|
unsigned mask = 1 << index;
|
|
|
|
|
2021-06-12 18:43:58 +00:00
|
|
|
if ([item defaultIndex] != [item selectedIndex]) {
|
2021-06-07 04:34:26 +00:00
|
|
|
_slots_explicit |= mask; // grrr.
|
2021-06-12 18:43:58 +00:00
|
|
|
_slot_value[index] = [[item selectedItem] value];
|
|
|
|
}
|
2021-06-07 04:34:26 +00:00
|
|
|
|
|
|
|
_slot_media[index] = [item selectedMedia];
|
|
|
|
}
|
|
|
|
|
|
|
|
++index;
|
|
|
|
}
|
|
|
|
|
|
|
|
// need to do it here so it propogate to media view.
|
|
|
|
[self rebuildMedia];
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
-(BOOL)saveBookmark: (NSMutableDictionary *)bookmark {
|
|
|
|
|
|
|
|
NSMutableDictionary *slots = [NSMutableDictionary new];
|
|
|
|
for (Slot *item in _root) {
|
|
|
|
NSDictionary *d = [item serialize];
|
|
|
|
[slots addEntriesFromDictionary: d];
|
|
|
|
}
|
|
|
|
|
|
|
|
[bookmark setObject: slots forKey: @"slots"];
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-09-11 02:06:41 +00:00
|
|
|
|
|
|
|
@end
|