ample/Ample/SlotViewController.m
Kelvin Sherlock 3d5a2951bb Squashed commit of the following:
commit 78c81626670fdf41fa6bdd71a4243a89a0746615
Author: Kelvin Sherlock <ksherlock@gmail.com>
Date:   Mon Jun 7 00:33:48 2021 -0400

    check if software set has a particular entry.

commit ef5ab6b6948dc3bbbe2947ea099fcacd08435e86
Author: Kelvin Sherlock <ksherlock@gmail.com>
Date:   Sun Jun 6 22:20:34 2021 -0400

    fix scroller background on recent disk images window.

commit dee56fa50e87299b396b48361bd0a780aaaaa768
Author: Kelvin Sherlock <ksherlock@gmail.com>
Date:   Sun Jun 6 21:26:23 2021 -0400

    update cheat sheet javascript to work with 10.11
    * => functions not supported
    * NodeList.prototype.forEach not supported.

commit b00cc05413f4ebd6d6d58f96e24303008608f3a6
Author: Kelvin Sherlock <ksherlock@gmail.com>
Date:   Sun Jun 6 17:10:41 2021 -0400

    default full machine name for bookmark entry.

commit a671cafdc98051b56b12cdd3ccd13c22f54f605a
Author: Kelvin Sherlock <ksherlock@gmail.com>
Date:   Sun Jun 6 15:39:32 2021 -0400

    loading a bookmark wasn't updating the media.

commit 3000e0eb1b10bede3345aaab8478e9ec209f328c
Author: Kelvin Sherlock <ksherlock@gmail.com>
Date:   Sun Jun 6 15:38:53 2021 -0400

    bump copyright year.

commit 45222dacd4aa0047fae63a9112509de57139df63
Author: Kelvin Sherlock <ksherlock@gmail.com>
Date:   Sun Jun 6 13:38:23 2021 -0400

    add reset w/ value for setting the item explicitely.

commit cc7fde1253b71c4d8655eb4c010bbf4e61333a15
Author: Kelvin Sherlock <ksherlock@gmail.com>
Date:   Sun Jun 6 13:37:48 2021 -0400

    add checkboxes for bitbanger/share directory.

    The general idea is it's easier to toggle a checkbox than to type/retype a path.

commit 5674b2d7f6b0e2f0b973197bf3493ad61bf46428
Author: Kelvin Sherlock <ksherlock@gmail.com>
Date:   Sat Jun 5 19:11:43 2021 -0400

    commentary on searches with diacritics.

commit ec60634dcd9c573130dc34673b4d3fe597ea2b42
Author: Kelvin Sherlock <ksherlock@gmail.com>
Date:   Sat Jun 5 19:11:22 2021 -0400

    clean up auto-complete a little bit when setting a value directly.

commit 1a182bbdab237c89d355d8294b5a4a64b785783a
Author: Kelvin Sherlock <ksherlock@gmail.com>
Date:   Sat Jun 5 13:08:29 2021 -0400

    fix text color when value is set.

    There are still some bugs relating to multiple copies of the value being stored.

commit 49c0bc15c73446259d8cc151cf52d6058644db76
Author: Kelvin Sherlock <ksherlock@gmail.com>
Date:   Sat Jun 5 12:09:44 2021 -0400

    reset all controls first.

commit 059797ad85b057e296cc707b4645f839bfccac13
Author: Kelvin Sherlock <ksherlock@gmail.com>
Date:   Sat Jun 5 10:52:06 2021 -0400

    more bookmark loading.

commit e5a612d9f8e7414dd15c66dbaa540b637765eeec
Author: Kelvin Sherlock <ksherlock@gmail.com>
Date:   Fri Jun 4 23:52:38 2021 -0400

    bookmark - restore the software

commit f9411a1e84df7bd46e352cc5ca995b585c2a0523
Author: Kelvin Sherlock <ksherlock@gmail.com>
Date:   Fri Jun 4 23:52:25 2021 -0400

    clean up software / name logic.

commit f628d99e4a
Author: Kelvin Sherlock <ksherlock@gmail.com>
Date:   Fri Jun 4 00:21:08 2021 -0400

    load bookmark...

commit 0b248e6aad
Author: Kelvin Sherlock <ksherlock@gmail.com>
Date:   Fri Jun 4 00:20:42 2021 -0400

    stringValue can't be nil.

commit 94aac38af4
Author: Kelvin Sherlock <ksherlock@gmail.com>
Date:   Thu Jun 3 23:04:37 2021 -0400

    add bookmark menu

commit 6215a0df12
Author: Kelvin Sherlock <ksherlock@gmail.com>
Date:   Thu Jun 3 23:03:29 2021 -0400

    slot view needs to know the machine.

commit d348c15dc5
Author: Kelvin Sherlock <ksherlock@gmail.com>
Date:   Thu Jun 3 23:02:58 2021 -0400

    transformer to enable/disable control based on string length.

commit e14336a009
Author: Kelvin Sherlock <ksherlock@gmail.com>
Date:   Thu Jun 3 23:02:14 2021 -0400

    shut up compiler warning.

commit 4baf545245
Author: Kelvin Sherlock <ksherlock@gmail.com>
Date:   Thu Jun 3 23:01:15 2021 -0400

    bookmark manager

commit 0f3e6c8307
Author: Kelvin Sherlock <ksherlock@gmail.com>
Date:   Mon May 31 23:54:29 2021 -0400

    more (untested) bookmark code

commit 8fdb149eb3
Author: Kelvin Sherlock <ksherlock@gmail.com>
Date:   Mon May 31 16:13:43 2021 -0400

    start of bookmarking support. Untested.

commit 787eac87f6
Author: Kelvin Sherlock <ksherlock@gmail.com>
Date:   Mon May 31 16:12:45 2021 -0400

    shut up warnings about content clipping.
    maybe it's a 10.11 thing.  The size was chosen by interface builder.
2021-06-07 00:34:26 -04:00

405 lines
9.0 KiB
Objective-C

//
// SlotViewController.m
// Ample
//
// Created by Kelvin Sherlock on 9/9/2020.
// Copyright © 2020 Kelvin Sherlock. All rights reserved.
//
#import "Ample.h"
#import "SlotViewController.h"
#import "Menu.h"
#import "Slot.h"
#import "Media.h"
#import <objc/runtime.h>
/* number of slot types. bitmask used so should be < sizeof(unsigned *8) */
#define SLOT_COUNT 22
static_assert(SLOT_COUNT <= sizeof(unsigned) * 8, "too many slot types");
#define SIZEOF(x) (sizeof(x) / sizeof(x[0]))
static unsigned RootKey = 0;
@interface SlotViewController ()
@property (weak) IBOutlet NSOutlineView *outlineView;
@property (weak) IBOutlet NSOutlineView *childOutlineView;
@end
@implementation SlotViewController {
NSArray *_root;
unsigned _slots_explicit;
unsigned _slots_valid;
unsigned _slots_default;
Slot *_slot_object[SLOT_COUNT];
NSString *_slot_value[SLOT_COUNT]; // when explicitely set.
Media _slot_media[SLOT_COUNT];
Media _machine_media;
NSDictionary *_machine_data;
IBOutlet NSPopover *_popover;
BOOL _loadingBookmark;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do view setup here.
_root = @[];
objc_setAssociatedObject(_outlineView, &RootKey, _root, OBJC_ASSOCIATION_RETAIN);
//[_outlineView setIndentationPerLevel: 2.0];
}
-(void)resetMachine {
_root = @[];
objc_setAssociatedObject(_outlineView, &RootKey, _root, OBJC_ASSOCIATION_RETAIN);
[_outlineView reloadData];
_slots_valid = 0;
_slots_explicit = 0;
_slots_default = 0;
_machine_media = EmptyMedia;
_machine_data = nil;
for (unsigned i = 0; i < SLOT_COUNT; ++i) {
_slot_media[i] = EmptyMedia;
_slot_object[i] = nil;
_slot_value[i] = nil;
}
[self setResolution: NSMakeSize(0, 0)];
[self setArgs: @[]];
[self setMedia: EmptyMedia];
}
-(void)loadMachine {
NSDictionary *d = MameMachine(_machine);
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 = MediaFromDictionary([d objectForKey: @"media"]);
_machine_data = d;
for (unsigned i = 0; i < SLOT_COUNT; ++i) {
_slot_media[i] = EmptyMedia;
_slot_object[i] = nil;
}
extern NSArray *BuildSlots(NSString *name, NSDictionary *data);
_root = BuildSlots(_machine, d);
objc_setAssociatedObject(_outlineView, &RootKey, _root, OBJC_ASSOCIATION_RETAIN);
for (Slot *item in _root) {
NSInteger index = [item index];
if (index < 0) continue;
unsigned mask = 1 << index;
_slots_valid |= mask;
if ([item defaultIndex] >= 0)
_slots_default |= mask;
if (_slot_value[index])
[item selectValue: _slot_value[index]];
_slot_media[index] = [item selectedMedia];
_slot_object[index] = item;
}
[_outlineView reloadData];
if (!_loadingBookmark) {
[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 {
Media media = _machine_media;
unsigned mask = 1;
for (unsigned i = 0; i < SLOT_COUNT; ++i, mask <<= 1) {
if (_slots_valid & mask) {
MediaAdd(&media, &_slot_media[i]);
}
}
[self setMedia: media];
}
-(void)rebuildArgs {
NSMutableArray *args = [NSMutableArray new];
for (Slot *item in _root) {
NSArray *x = [item args];
if (x) [args addObjectsFromArray: x];
}
[self setArgs: args];
}
- (IBAction)menuChanged:(NSPopUpButton *)sender {
BOOL direct = YES;
NSInteger index = [sender tag];
if (index < 0) return; //
if (index >= 0 && index < SLOT_COUNT) {
direct = YES;
} else {
direct = NO;
index &= ~0x10000;
}
if (index >= SLOT_COUNT) return; //
unsigned mask = 1 << index;
// index 0 = ram = special case...
SlotOption *o = [[sender selectedItem] representedObject];
Slot *item = _slot_object[index];
if (direct) {
_slots_explicit |= mask;
_slot_value[index] = [o value];
//_slots_default &= ~mask;
}
Media media = [item selectedMedia];
if (!MediaEqual(&media, &_slot_media[index])) {
_slot_media[index] = media;
[self rebuildMedia];
}
// needs to reload children if expanded.
#ifdef SLOT_TREE
if (direct) {
BOOL rc = ([_outlineView isItemExpanded: item]);
[_outlineView reloadItem: item reloadChildren: rc];
}
#endif
[self rebuildArgs];
}
- (IBAction)hamburger:(id)sender {
#if 0
if ([_popover isShown]) {
[_popover close];
}
#endif
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];
[_popover showRelativeToRect: [sender bounds]
ofView: sender
preferredEdge: NSRectEdgeMaxY];
}
-(IBAction)resetSlots:(id)sender {
_slots_explicit = 0;
for (unsigned i = 0; i < SLOT_COUNT; ++i) {
_slot_media[i] = EmptyMedia;
_slot_value[i] = nil;
}
for (Slot *item in _root) {
[item reset];
// if children, reset them too...
NSInteger index = [item index];
if (index < 0) continue;
_slot_media[index] = [item selectedMedia];
}
#ifdef SLOT_TREE
[_outlineView reloadData];
#endif
if (!_loadingBookmark) {
[self rebuildMedia];
[self rebuildArgs];
}
}
@end
@implementation SlotViewController (OutlineView)
- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
NSArray *root = objc_getAssociatedObject(outlineView, &RootKey);
if (!item) return [root count];
#ifdef SLOT_TREE
NSArray *tmp = [(Slot *)item selectedChildren];
return [tmp count];
#endif
return 0;
}
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item {
NSArray *root = objc_getAssociatedObject(outlineView, &RootKey);
if (!item) return [root objectAtIndex: index];
#ifdef SLOT_TREE
NSArray *tmp = [(Slot *)item selectedChildren];
return [tmp objectAtIndex: index];
#endif
return nil;
}
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
#ifdef SLOT_TREE
if (!item) return NO;
NSArray *tmp = [(Slot *)item selectedChildren];
return [tmp count] > 0;
#else
return NO;
#endif
}
- (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(Slot *)item {
SlotTableCellView *v = [outlineView makeViewWithIdentifier: @"MenuCell" owner: self];
[item prepareView: v];
return v;
}
@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;
if ([item defaultIndex] != [item selectedIndex])
_slots_explicit |= mask; // grrr.
_slot_media[index] = [item selectedMedia];
_slot_value[index] = [[item selectedItem] value];
}
++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;
}
@end