mirror of
https://github.com/ksherlock/ample.git
synced 2024-11-19 02:11:08 +00:00
511 lines
12 KiB
Objective-C
511 lines
12 KiB
Objective-C
//
|
|
// NewSlotViewController.m
|
|
// Ample
|
|
//
|
|
// Created by Kelvin Sherlock on 9/9/2020.
|
|
// Copyright © 2020 Kelvin Sherlock. All rights reserved.
|
|
//
|
|
|
|
#import "NewSlotViewController.h"
|
|
|
|
static NSFont *ItalicMenuFont(void) {
|
|
NSFont *font = [NSFont menuFontOfSize: 0];
|
|
NSFontDescriptor *fd = [font fontDescriptor];
|
|
NSFontDescriptor *fd2 = [fd fontDescriptorWithSymbolicTraits: NSFontDescriptorTraitItalic];
|
|
return [NSFont fontWithDescriptor: fd2 size: [font pointSize]];
|
|
}
|
|
|
|
static NSAttributedString *ItalicMenuString(NSString *s) {
|
|
static NSDictionary *attr = nil;
|
|
if (!attr) {
|
|
attr = @{
|
|
NSFontAttributeName: ItalicMenuFont()
|
|
};
|
|
}
|
|
return [[NSAttributedString alloc] initWithString: s attributes: attr];
|
|
}
|
|
|
|
@implementation SlotTableCellView
|
|
|
|
@end
|
|
|
|
@implementation SlotItem
|
|
|
|
-(id)init {
|
|
_defaultIndex = -1;
|
|
_selectedIndex = -1;
|
|
|
|
return self;
|
|
}
|
|
|
|
-(NSString *)label {
|
|
static NSString *Names[] = {
|
|
@"RAM:",
|
|
@"Slot 0:",
|
|
@"Slot 1:",
|
|
@"Slot 2:",
|
|
@"Slot 3:",
|
|
@"Slot 4:",
|
|
@"Slot 5:",
|
|
@"Slot 6:",
|
|
@"Slot 7:",
|
|
@"Expansion:",
|
|
@"Auxiliary:",
|
|
@"RS232:",
|
|
@"Game I/O:",
|
|
@"Modem:",
|
|
@"Printer:"
|
|
};
|
|
return Names[_index];
|
|
}
|
|
|
|
-(NSString *)flag {
|
|
|
|
static NSString *Names[] = {
|
|
@"-ramsize",
|
|
@"-sl0",
|
|
@"-sl1",
|
|
@"-sl2",
|
|
@"-sl3",
|
|
@"-sl4",
|
|
@"-sl5",
|
|
@"-sl6",
|
|
@"-sl7",
|
|
@"-exp",
|
|
@"-aux",
|
|
@"-rs232",
|
|
@"-gameio",
|
|
@"-modem",
|
|
@"-printer"
|
|
};
|
|
return Names[_index];
|
|
|
|
}
|
|
|
|
-(void)buildMenuWithSelectedValue: (NSString *)value {
|
|
|
|
NSMutableArray *tmp = [NSMutableArray arrayWithCapacity: [_children count]];
|
|
_defaultIndex = -1;
|
|
_selectedIndex = -1;
|
|
|
|
int ix = 0;
|
|
for (NSDictionary *d in _children) {
|
|
NSString *title = [d objectForKey: @"description"];
|
|
NSMenuItem *mi = [[NSMenuItem alloc] initWithTitle: title action: NULL keyEquivalent: @""];
|
|
|
|
// row 0 for slots is -- None -- which should be nil...
|
|
[mi setRepresentedObject: d];
|
|
|
|
|
|
BOOL disabled = [(NSNumber *)[d objectForKey: @"disabled"] boolValue];
|
|
if (disabled) {
|
|
[mi setEnabled: NO];
|
|
}
|
|
|
|
BOOL def = [(NSNumber *)[d objectForKey: @"default"] boolValue];
|
|
if (def) {
|
|
[mi setAttributedTitle: ItalicMenuString(title)];
|
|
_defaultIndex = ix;
|
|
}
|
|
|
|
if (value) {
|
|
NSString *v = [d objectForKey: @"value"];
|
|
if ([value compare: v] == NSOrderedSame) {
|
|
_selectedIndex = ix;
|
|
}
|
|
}
|
|
|
|
[tmp addObject: mi];
|
|
++ix;
|
|
}
|
|
|
|
|
|
[self setMenuItems: tmp];
|
|
if (_selectedIndex < 0) _selectedIndex = _defaultIndex;
|
|
if (_selectedIndex < 0) _selectedIndex = 0;
|
|
}
|
|
|
|
-(void)reset {
|
|
[self setSelectedIndex: _defaultIndex >= 0 ? _defaultIndex : 0];
|
|
}
|
|
|
|
-(NSDictionary *)selectedItem {
|
|
if (_selectedIndex < 0) return nil;
|
|
return [_children objectAtIndex: _selectedIndex];
|
|
}
|
|
|
|
-(NSDictionary *)selectedMedia {
|
|
if (_selectedIndex < 0) return nil;
|
|
NSDictionary *d = [_children objectAtIndex: _selectedIndex];
|
|
return [d objectForKey: @"media"];
|
|
}
|
|
|
|
-(BOOL)hasDefault {
|
|
return _defaultIndex >= 0;
|
|
}
|
|
|
|
-(void)prepareView: (SlotTableCellView *)view {
|
|
|
|
NSPopUpButton *button = [view menuButton];
|
|
NSTextField *text = [view textField];
|
|
|
|
[text setObjectValue: [self label]];
|
|
|
|
[button unbind: @"selectedIndex"];
|
|
[[button menu] setItemArray: _menuItems];
|
|
[button bind: @"selectedIndex" toObject: self withKeyPath: @"selectedIndex" options: nil];
|
|
[button setTag: _index];
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
@interface NewSlotViewController ()
|
|
@property (weak) IBOutlet NSOutlineView *outlineView;
|
|
|
|
@end
|
|
|
|
@implementation NewSlotViewController {
|
|
NSMutableArray *_root;
|
|
|
|
unsigned _slots_explicit;
|
|
unsigned _slots_valid;
|
|
unsigned _slots_default;
|
|
|
|
NSDictionary *_slot_object[16];
|
|
NSDictionary *_slot_media[16];
|
|
NSString *_slot_value[16]; // when explicitely set.
|
|
NSDictionary *_machine_media;
|
|
|
|
NSDictionary *_machine_data;
|
|
}
|
|
|
|
- (void)viewDidLoad {
|
|
[super viewDidLoad];
|
|
// Do view setup here.
|
|
|
|
_root = [NSMutableArray new];
|
|
}
|
|
|
|
-(void)resetMachine {
|
|
|
|
[_root removeAllObjects];
|
|
[_outlineView reloadData];
|
|
|
|
_slots_valid = 0;
|
|
_slots_explicit = 0;
|
|
_slots_default = 0;
|
|
_machine_media = nil;
|
|
_machine_data = nil;
|
|
|
|
for (unsigned i = 0; i < 16; ++i) {
|
|
_slot_media[i] = nil;
|
|
_slot_object[i] = nil;
|
|
_slot_value[i] = nil;
|
|
}
|
|
|
|
[self setResolution: NSMakeSize(0, 0)];
|
|
[self setArgs: @[]];
|
|
[self setMedia: @{}];
|
|
}
|
|
|
|
-(void)loadMachine {
|
|
|
|
static NSString *Keys[] = {
|
|
@"ram",
|
|
@"sl0", @"sl1", @"sl2", @"sl3",
|
|
@"sl4", @"sl5", @"sl6", @"sl7",
|
|
@"exp", @"aux", @"rs232",
|
|
@"gameio", @"printer", @"modem",
|
|
};
|
|
static unsigned SizeofKeys = sizeof(Keys)/sizeof(Keys[0]);
|
|
|
|
|
|
NSBundle *bundle = [NSBundle mainBundle];
|
|
NSURL *url= [bundle URLForResource: _machine withExtension: @"plist"];
|
|
|
|
NSDictionary *d = [NSDictionary dictionaryWithContentsOfURL: url];
|
|
|
|
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;
|
|
|
|
_machine_media = [d objectForKey: @"media"];
|
|
|
|
_machine_data = d;
|
|
|
|
[_root removeAllObjects];
|
|
|
|
unsigned mask = 1;
|
|
for (unsigned i = 0; i < SizeofKeys; ++i, mask <<= 1) {
|
|
|
|
NSString *v = [_slot_object[i] objectForKey: @"value"];
|
|
_slot_media[i] = nil;
|
|
_slot_object[i] = nil;
|
|
if (v) _slot_value[i] = v;
|
|
|
|
NSArray *options = [d objectForKey: Keys[i]];
|
|
if (!options) continue;
|
|
|
|
_slots_valid |= mask;
|
|
|
|
SlotItem *item = [SlotItem new];
|
|
[item setIndex: i];
|
|
[item setChildren: options];
|
|
[item buildMenuWithSelectedValue: _slot_value[i]];
|
|
|
|
if ([item defaultIndex] >= 0) {
|
|
_slots_default |= mask;
|
|
}
|
|
// default media...
|
|
_slot_media[i] = [item selectedMedia];
|
|
[_root addObject: item];
|
|
}
|
|
|
|
|
|
|
|
[_outlineView reloadData];
|
|
[self rebuildMedia];
|
|
[self rebuildArgs];
|
|
}
|
|
|
|
-(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 {
|
|
|
|
|
|
#define _(var, o) var += [[o objectForKey: @ # var ] unsignedIntValue]
|
|
|
|
unsigned cass = 0;
|
|
unsigned cdrm = 0;
|
|
unsigned hard = 0;
|
|
unsigned flop_5_25 = 0;
|
|
unsigned flop_3_5 = 0;
|
|
|
|
#if 0
|
|
for (SlotItem *item in _root) {
|
|
NSDictionary *tmp = [item selectedMedia];
|
|
if (tmp) {
|
|
_(cass, tmp);
|
|
_(cdrm, tmp);
|
|
_(hard, tmp);
|
|
_(flop_5_25, tmp);
|
|
_(flop_3_5, tmp);
|
|
}
|
|
}
|
|
#endif
|
|
#if 1
|
|
unsigned mask = 1;
|
|
for (unsigned i = 0; i < 16; ++i, mask <<= 1) {
|
|
|
|
if (_slots_valid & mask) {
|
|
NSDictionary *tmp = _slot_media[i];
|
|
if (tmp) {
|
|
_(cass, tmp);
|
|
_(cdrm, tmp);
|
|
_(hard, tmp);
|
|
_(flop_5_25, tmp);
|
|
_(flop_3_5, tmp);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
NSDictionary *tmp = _machine_media;
|
|
if (tmp) {
|
|
_(cass, tmp);
|
|
_(cdrm, tmp);
|
|
_(hard, tmp);
|
|
_(flop_5_25, tmp);
|
|
_(flop_3_5, tmp);
|
|
}
|
|
|
|
[self setMedia: @{
|
|
@"cass": @(cass),
|
|
@"cdrm": @(cdrm),
|
|
@"hard": @(hard),
|
|
@"flop_5_25": @(flop_5_25),
|
|
@"flop_3_5": @(flop_3_5),
|
|
}];
|
|
|
|
}
|
|
|
|
|
|
static NSString *SlotFlagForIndex(unsigned index){
|
|
static NSString *Names[] = {
|
|
@"-ramsize",
|
|
@"-sl0",
|
|
@"-sl1",
|
|
@"-sl2",
|
|
@"-sl3",
|
|
@"-sl4",
|
|
@"-sl5",
|
|
@"-sl6",
|
|
@"-sl7",
|
|
@"-exp",
|
|
@"-aux",
|
|
@"-rs232",
|
|
@"-gameio",
|
|
@"-modem",
|
|
@"-printer"
|
|
};
|
|
return Names[index];
|
|
}
|
|
|
|
-(void)rebuildArgs {
|
|
|
|
NSMutableArray *args = [NSMutableArray new];
|
|
|
|
for (SlotItem *item in _root) {
|
|
NSDictionary *d = [item selectedItem];
|
|
if ([(NSNumber *)[d objectForKey: @"default"] boolValue]) {
|
|
continue; // default, don't include it.
|
|
}
|
|
NSString *value = [d objectForKey: @"value"];
|
|
|
|
if (!value || ![value length]) {
|
|
if (![item hasDefault]) continue;
|
|
value = @"";
|
|
}
|
|
[args addObject: [item flag]];
|
|
[args addObject: value];
|
|
}
|
|
|
|
#if 0
|
|
unsigned mask = 1;
|
|
for (unsigned i = 0 ; i < 16; ++i, mask <<= 1) {
|
|
|
|
if (!(_slots_valid & mask)) continue;
|
|
NSDictionary *d = _slot_object[i];
|
|
|
|
if ([(NSNumber *)[d objectForKey: @"default"] boolValue]) {
|
|
continue; // default, don't include it.
|
|
}
|
|
NSString *value = [d objectForKey: @"value"];
|
|
|
|
if (!value) {
|
|
// if slot has a default, need to overwrite it.
|
|
if (!(_slots_default & mask)) continue;
|
|
value = @"";
|
|
}
|
|
|
|
[args addObject: SlotFlagForIndex(i)];
|
|
[args addObject: value];
|
|
}
|
|
#endif
|
|
[self setArgs: args];
|
|
}
|
|
|
|
- (IBAction)menuChanged:(NSPopUpButton *)sender {
|
|
|
|
unsigned index = (unsigned)[sender tag];
|
|
unsigned mask = 1 << index;
|
|
|
|
|
|
// index 0 = ram = special case...
|
|
|
|
NSDictionary *d = [[sender selectedItem] representedObject];
|
|
|
|
_slots_explicit |= mask;
|
|
_slot_value[index] = [d objectForKey: @"value"];
|
|
//_slots_default &= ~mask;
|
|
|
|
//_slot_object[index] = d;
|
|
|
|
// media...
|
|
NSDictionary *newMedia = [d objectForKey: @"media"];
|
|
NSDictionary *oldMedia = _slot_media[index];
|
|
|
|
if (newMedia != oldMedia) {
|
|
_slot_media[index] = newMedia;
|
|
[self rebuildMedia];
|
|
}
|
|
|
|
[self rebuildArgs];
|
|
}
|
|
-(IBAction)resetSlots:(id)sender {
|
|
|
|
_slots_explicit = 0;
|
|
for (unsigned i = 0; i < 16; ++i) {
|
|
_slot_media[i] = nil;
|
|
_slot_object[i] = nil;
|
|
_slot_value[i] = nil;
|
|
}
|
|
for (SlotItem *item in _root) {
|
|
[item reset];
|
|
// if children, reset them too...
|
|
unsigned index = [item index];
|
|
_slot_media[index] = [item selectedMedia];
|
|
}
|
|
//[_outlineView reloadData]; // will need to reload if changing the default makes children disappear.
|
|
|
|
[self rebuildMedia];
|
|
[self rebuildArgs];
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@implementation NewSlotViewController (OutlineView)
|
|
|
|
|
|
- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
|
|
|
|
if (!item) return [_root count];
|
|
|
|
return 0;
|
|
}
|
|
|
|
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item {
|
|
if (!item) return [_root objectAtIndex: index];
|
|
return nil;
|
|
}
|
|
|
|
|
|
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
|
|
return NO;
|
|
}
|
|
|
|
|
|
|
|
- (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(SlotItem *)item {
|
|
|
|
SlotTableCellView *v = [outlineView makeViewWithIdentifier: @"MenuCell" owner: self];
|
|
|
|
[item prepareView: v];
|
|
|
|
return v;
|
|
}
|
|
|
|
|
|
|
|
@end
|