2020-08-21 22:38:02 +00:00
|
|
|
//
|
|
|
|
// MediaViewController.m
|
2020-08-30 03:24:49 +00:00
|
|
|
// Ample
|
2020-08-21 22:38:02 +00:00
|
|
|
//
|
|
|
|
// Created by Kelvin Sherlock on 8/20/2020.
|
|
|
|
// Copyright © 2020 Kelvin Sherlock. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
#import "MediaViewController.h"
|
2020-09-14 02:48:55 +00:00
|
|
|
#import "TableCellView.h"
|
2020-08-22 04:13:23 +00:00
|
|
|
|
2021-06-13 20:29:16 +00:00
|
|
|
#define SIZEOF(x) (sizeof(x) / sizeof(x[0]))
|
|
|
|
|
2020-08-22 04:13:23 +00:00
|
|
|
|
|
|
|
@protocol MediaNode
|
|
|
|
-(BOOL)isGroupItem;
|
|
|
|
-(BOOL)isExpandable;
|
2021-06-14 01:31:56 +00:00
|
|
|
-(NSInteger)count;
|
2021-06-13 20:29:16 +00:00
|
|
|
-(NSInteger)category;
|
2020-08-22 04:13:23 +00:00
|
|
|
|
|
|
|
-(NSString *)viewIdentifier;
|
|
|
|
-(void)prepareView: (NSTableCellView *)view;
|
|
|
|
-(CGFloat)height;
|
2020-09-06 03:29:30 +00:00
|
|
|
-(NSInteger)index;
|
2020-08-22 04:13:23 +00:00
|
|
|
@end
|
|
|
|
|
|
|
|
@interface MediaCategory : NSObject <MediaNode> {
|
2020-08-21 22:38:02 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
@property NSInteger validCount;
|
2020-09-06 04:31:09 +00:00
|
|
|
@property NSMutableArray *children; // URLs?
|
2020-08-21 22:38:02 +00:00
|
|
|
@property NSString *title;
|
2020-09-06 03:29:30 +00:00
|
|
|
@property NSInteger index;
|
2021-06-13 20:29:16 +00:00
|
|
|
@property NSInteger category;
|
2021-07-10 05:15:34 +00:00
|
|
|
@property (weak)NSOutlineView *view;
|
2020-08-21 22:38:02 +00:00
|
|
|
|
|
|
|
-(NSInteger)count;
|
|
|
|
-(id)objectAtIndex:(NSInteger)index;
|
|
|
|
-(BOOL)isGroupItem;
|
|
|
|
@end
|
|
|
|
|
2020-08-25 00:23:56 +00:00
|
|
|
@interface MediaItem : NSObject <MediaNode>
|
|
|
|
|
2021-06-13 20:29:16 +00:00
|
|
|
@property NSString *string;
|
2020-08-25 00:23:56 +00:00
|
|
|
@property NSURL *url;
|
|
|
|
@property BOOL valid;
|
2020-09-06 03:29:30 +00:00
|
|
|
@property NSInteger index;
|
2021-06-13 20:29:16 +00:00
|
|
|
@property NSInteger category;
|
|
|
|
|
|
|
|
@property (readonly) BOOL occupied;
|
2020-08-25 00:23:56 +00:00
|
|
|
|
|
|
|
-(NSInteger)count;
|
|
|
|
-(id)objectAtIndex:(NSInteger)index;
|
|
|
|
-(BOOL)isGroupItem;
|
|
|
|
|
|
|
|
-(void)invalidate;
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-08-21 22:38:02 +00:00
|
|
|
@implementation MediaCategory
|
|
|
|
|
2021-03-08 23:59:02 +00:00
|
|
|
+(instancetype)categoryWithTitle: (NSString *)title {
|
|
|
|
return [[self alloc] initWithTitle: title];
|
|
|
|
}
|
|
|
|
|
2020-08-21 22:38:02 +00:00
|
|
|
-(instancetype)initWithTitle: (NSString *)title {
|
|
|
|
[self setTitle: title];
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
-(NSInteger) count {
|
|
|
|
return [_children count];
|
|
|
|
}
|
|
|
|
|
|
|
|
-(id)objectAtIndex:(NSInteger)index {
|
|
|
|
return [_children objectAtIndex: index];
|
|
|
|
}
|
|
|
|
|
|
|
|
-(BOOL)isGroupItem {
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
-(BOOL)isExpandable {
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
2020-08-22 04:13:23 +00:00
|
|
|
-(NSString *)viewIdentifier {
|
|
|
|
return @"CategoryView";
|
|
|
|
}
|
|
|
|
|
|
|
|
-(void)prepareView: (NSTableCellView *)view {
|
2021-03-13 00:58:12 +00:00
|
|
|
|
2020-08-22 04:13:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
-(CGFloat)height {
|
|
|
|
return 17;
|
|
|
|
}
|
2020-08-21 22:38:02 +00:00
|
|
|
|
|
|
|
|
2020-08-25 00:23:56 +00:00
|
|
|
-(BOOL)setItemCount: (unsigned)newCount {
|
|
|
|
|
2020-08-25 04:35:40 +00:00
|
|
|
if (newCount == _validCount) {
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned count = (unsigned)[_children count];
|
2020-08-25 00:23:56 +00:00
|
|
|
|
|
|
|
_validCount = newCount;
|
2020-09-06 04:31:09 +00:00
|
|
|
if (!_children) _children = [NSMutableArray new];
|
2020-08-25 00:23:56 +00:00
|
|
|
|
2020-08-25 04:35:40 +00:00
|
|
|
for (unsigned i = count; i < newCount; ++i) {
|
2020-09-06 03:29:30 +00:00
|
|
|
MediaItem *item = [MediaItem new];
|
|
|
|
[item setIndex: i];
|
2021-06-13 20:29:16 +00:00
|
|
|
[item setCategory: _category];
|
2020-09-06 04:31:09 +00:00
|
|
|
[_children addObject: item];
|
2020-08-25 00:23:56 +00:00
|
|
|
}
|
2020-09-06 03:29:30 +00:00
|
|
|
|
2020-08-25 00:23:56 +00:00
|
|
|
// delete excess items, if blank. otherwise, mark invalid.
|
|
|
|
unsigned ix = 0;
|
2020-09-06 04:31:09 +00:00
|
|
|
for(MediaItem *item in _children) {
|
2020-08-25 04:35:40 +00:00
|
|
|
[item setValid: ix++ < newCount];
|
2020-08-25 00:23:56 +00:00
|
|
|
}
|
|
|
|
|
2020-08-25 04:35:40 +00:00
|
|
|
for (unsigned i = newCount; i < count; ++i) {
|
2020-09-06 04:31:09 +00:00
|
|
|
MediaItem *item = [_children lastObject];
|
2021-06-13 20:29:16 +00:00
|
|
|
if ([item occupied]) break;
|
2020-08-25 00:23:56 +00:00
|
|
|
|
2020-09-06 04:31:09 +00:00
|
|
|
[_children removeLastObject];
|
2020-08-25 00:23:56 +00:00
|
|
|
}
|
2020-08-21 22:38:02 +00:00
|
|
|
|
2020-08-25 00:23:56 +00:00
|
|
|
return YES;
|
2020-08-21 22:38:02 +00:00
|
|
|
}
|
|
|
|
|
2021-07-10 05:15:34 +00:00
|
|
|
-(BOOL)addURL: (NSURL *)url {
|
|
|
|
|
|
|
|
for (MediaItem *item in _children) {
|
|
|
|
if (![item occupied]) {
|
|
|
|
[item setUrl: url];
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// add an extra item...
|
|
|
|
|
|
|
|
if (!_children) _children = [NSMutableArray new];
|
|
|
|
NSUInteger ix = [_children count];
|
|
|
|
|
|
|
|
MediaItem *item = [MediaItem new];
|
|
|
|
[item setIndex: ix];
|
|
|
|
[item setCategory: _category];
|
|
|
|
[item setUrl: url];
|
|
|
|
[item setValid: ix < _validCount];
|
|
|
|
[_children addObject: item];
|
|
|
|
if (_view) {
|
|
|
|
NSIndexSet *set = [NSIndexSet indexSetWithIndex: ix];
|
|
|
|
[_view insertItemsAtIndexes: set
|
|
|
|
inParent: self
|
|
|
|
withAnimation: NSTableViewAnimationEffectFade];
|
|
|
|
}
|
|
|
|
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
-(BOOL)pruneChildren {
|
2020-08-25 04:35:40 +00:00
|
|
|
NSUInteger count = [_children count];
|
|
|
|
BOOL delta = NO;
|
|
|
|
if (_validCount == count) return NO;
|
2020-09-12 23:24:32 +00:00
|
|
|
NSMutableIndexSet *set = [NSMutableIndexSet new];
|
2020-08-25 04:35:40 +00:00
|
|
|
|
|
|
|
for (NSInteger i = _validCount; i < count; ++i) {
|
2020-09-06 04:31:09 +00:00
|
|
|
MediaItem *item = [_children lastObject];
|
2021-06-13 20:29:16 +00:00
|
|
|
if ([item occupied]) break;
|
2020-08-25 04:35:40 +00:00
|
|
|
|
2020-09-06 04:31:09 +00:00
|
|
|
[_children removeLastObject];
|
2020-09-12 23:24:32 +00:00
|
|
|
[set addIndex: [_children count]];
|
|
|
|
|
2020-08-25 04:35:40 +00:00
|
|
|
delta = YES;
|
|
|
|
}
|
|
|
|
if (delta) {
|
2020-09-12 23:24:32 +00:00
|
|
|
|
2021-07-10 05:15:34 +00:00
|
|
|
if (_view)
|
|
|
|
[_view removeItemsAtIndexes: set inParent: self withAnimation: NSTableViewAnimationEffectFade];
|
2020-09-12 23:24:32 +00:00
|
|
|
|
2020-08-25 04:35:40 +00:00
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
return NO;
|
|
|
|
}
|
2020-08-21 22:38:02 +00:00
|
|
|
|
2021-07-10 05:15:34 +00:00
|
|
|
-(BOOL)moveItemFrom: (NSInteger)oldIndex to: (NSInteger)newIndex {
|
2020-09-06 04:31:09 +00:00
|
|
|
if (newIndex == oldIndex) return NO;
|
|
|
|
NSUInteger count = [_children count];
|
|
|
|
if (oldIndex >= count) return NO;
|
2020-08-25 00:23:56 +00:00
|
|
|
|
2020-09-06 04:31:09 +00:00
|
|
|
MediaItem *item = [_children objectAtIndex: oldIndex];
|
|
|
|
[_children removeObjectAtIndex: oldIndex];
|
|
|
|
if (newIndex > oldIndex) newIndex--;
|
|
|
|
if (newIndex >= count) {
|
|
|
|
[_children addObject: item];
|
|
|
|
} else {
|
|
|
|
[_children insertObject: item atIndex: newIndex];
|
|
|
|
}
|
2021-07-10 05:15:34 +00:00
|
|
|
if (_view) [_view moveItemAtIndex: oldIndex inParent: self toIndex: newIndex inParent: self];
|
2020-09-12 23:24:32 +00:00
|
|
|
|
2020-09-06 04:31:09 +00:00
|
|
|
// re-index and re-validate.
|
|
|
|
unsigned ix = 0;
|
|
|
|
for (MediaItem *item in _children) {
|
|
|
|
[item setIndex: ix];
|
|
|
|
[item setValid: ix < _validCount];
|
2020-09-12 23:24:32 +00:00
|
|
|
|
2020-09-28 02:06:03 +00:00
|
|
|
// [view reloadItem: item];
|
2020-09-12 23:24:32 +00:00
|
|
|
|
2020-09-06 04:31:09 +00:00
|
|
|
++ix;
|
|
|
|
}
|
2021-07-10 05:15:34 +00:00
|
|
|
[self pruneChildren];
|
2020-09-12 23:24:32 +00:00
|
|
|
//[view reloadItem: self reloadChildren: YES];
|
2020-09-06 04:31:09 +00:00
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
@end
|
2020-08-25 00:23:56 +00:00
|
|
|
|
2020-08-21 22:38:02 +00:00
|
|
|
@implementation MediaItem
|
|
|
|
|
2021-06-13 16:29:23 +00:00
|
|
|
|
|
|
|
|
2020-08-21 22:38:02 +00:00
|
|
|
-(instancetype)initWithURL: (NSURL *)url {
|
2021-06-13 20:29:16 +00:00
|
|
|
_url = url;
|
2020-08-21 22:38:02 +00:00
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2021-06-13 20:29:16 +00:00
|
|
|
-(instancetype)initWithString: (NSString *)string {
|
|
|
|
_string = string;
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
-(NSString *)argument {
|
|
|
|
if (_string)
|
|
|
|
return _string;
|
|
|
|
|
|
|
|
// todo -- have setURL also update _string?
|
|
|
|
if (_url)
|
|
|
|
return [NSString stringWithCString: [_url fileSystemRepresentation] encoding: NSUTF8StringEncoding];
|
|
|
|
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
+(NSSet *)keyPathsForValuesAffectingOccupied {
|
|
|
|
return [NSSet setWithObjects: @"url", @"string", nil];
|
|
|
|
}
|
|
|
|
|
|
|
|
-(BOOL)occupied {
|
|
|
|
return _url || _string;
|
|
|
|
}
|
|
|
|
|
2020-08-21 22:38:02 +00:00
|
|
|
-(NSInteger) count {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
-(id)objectAtIndex:(NSInteger)index {
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
-(BOOL)isGroupItem {
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
-(BOOL)isExpandable {
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2020-08-22 04:13:23 +00:00
|
|
|
-(NSString *)viewIdentifier {
|
2021-06-13 20:29:16 +00:00
|
|
|
if (_category == kIndexBitBanger) return @"BBItemView";
|
2021-08-07 19:12:20 +00:00
|
|
|
if (_category == kIndexMidiOut) return @"MidiItemView";
|
|
|
|
if (_category == kIndexMidiIn) return @"MidiItemView";
|
2020-08-22 04:13:23 +00:00
|
|
|
return @"ItemView";
|
|
|
|
}
|
|
|
|
|
2021-08-07 19:12:20 +00:00
|
|
|
-(void)prepareView: (MediaTableCellView *)view {
|
2021-07-10 16:07:01 +00:00
|
|
|
/* set the path tag = category. */
|
2021-08-07 19:12:20 +00:00
|
|
|
|
|
|
|
[view prepareView: _category];
|
|
|
|
#if 0
|
|
|
|
if (_category == kIndexMidiIn || _category == kIndexMidiOut || _category == kIndexBitBanger) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-07-10 16:07:01 +00:00
|
|
|
NSPathControl *pc = [view pathControl];
|
|
|
|
[pc setTag: _category + 1]; // to differentiate 0 / no path control.
|
2021-08-07 19:12:20 +00:00
|
|
|
#endif
|
2020-08-21 22:38:02 +00:00
|
|
|
}
|
|
|
|
|
2020-08-22 04:13:23 +00:00
|
|
|
-(CGFloat)height {
|
|
|
|
return 27;
|
|
|
|
}
|
2020-08-21 22:38:02 +00:00
|
|
|
|
2020-08-25 00:23:56 +00:00
|
|
|
-(void)invalidate {
|
2020-09-28 02:06:03 +00:00
|
|
|
if (!_valid) return;
|
|
|
|
[self setValid: NO];
|
2020-08-25 00:23:56 +00:00
|
|
|
}
|
2020-08-21 22:38:02 +00:00
|
|
|
@end
|
|
|
|
|
2021-01-18 20:32:22 +00:00
|
|
|
|
2021-06-13 20:29:16 +00:00
|
|
|
|
2021-01-18 20:32:22 +00:00
|
|
|
|
2020-08-21 22:38:02 +00:00
|
|
|
@interface MediaViewController () {
|
|
|
|
|
2021-01-18 20:32:22 +00:00
|
|
|
MediaCategory *_data[CATEGORY_COUNT];
|
2021-07-10 05:45:03 +00:00
|
|
|
NSMutableArray *_root;
|
2021-03-08 23:59:02 +00:00
|
|
|
Media _media;
|
2021-06-07 04:34:26 +00:00
|
|
|
|
|
|
|
BOOL _loadingBookmark;
|
2020-08-21 22:38:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation MediaViewController
|
|
|
|
|
2021-03-08 23:59:02 +00:00
|
|
|
|
|
|
|
|
2020-08-21 22:38:02 +00:00
|
|
|
-(void)awakeFromNib {
|
|
|
|
|
2020-08-22 04:13:23 +00:00
|
|
|
static unsigned first = 0;
|
|
|
|
|
|
|
|
if (first) return;
|
|
|
|
first++;
|
|
|
|
|
2021-03-08 23:59:02 +00:00
|
|
|
_data[kIndexFloppy525] = [MediaCategory categoryWithTitle: @"5.25\" Floppies"];
|
|
|
|
_data[kIndexFloppy35] = [MediaCategory categoryWithTitle: @"3.5\" Floppies"];
|
|
|
|
_data[kIndexHardDrive] = [MediaCategory categoryWithTitle: @"Hard Drives"];
|
|
|
|
_data[kIndexCDROM] = [MediaCategory categoryWithTitle: @"CD-ROMs"];
|
|
|
|
_data[kIndexCassette] = [MediaCategory categoryWithTitle: @"Cassettes"];
|
|
|
|
_data[kIndexDiskImage] = [MediaCategory categoryWithTitle: @"Hard Disk Images"]; // Mac Nubus psuedo image device
|
2021-06-13 16:29:23 +00:00
|
|
|
_data[kIndexBitBanger] = [MediaCategory categoryWithTitle: @"Serial Bit Banger"]; // null_modem
|
2020-08-25 00:23:56 +00:00
|
|
|
|
2021-07-11 03:33:20 +00:00
|
|
|
_data[kIndexMidiIn] = [MediaCategory categoryWithTitle: @"MIDI (In)"];
|
|
|
|
_data[kIndexMidiOut] = [MediaCategory categoryWithTitle: @"MIDI (Out)"];
|
|
|
|
_data[kIndexPicture] = [MediaCategory categoryWithTitle: @"Picture"];
|
|
|
|
|
2021-07-14 23:40:40 +00:00
|
|
|
for (unsigned i = 0; i < CATEGORY_COUNT; ++i) {
|
2021-06-13 20:29:16 +00:00
|
|
|
[_data[i] setCategory: i];
|
2021-07-14 23:40:40 +00:00
|
|
|
[_data[i] setIndex: -1];
|
|
|
|
}
|
2021-06-13 20:29:16 +00:00
|
|
|
|
2021-07-10 05:45:03 +00:00
|
|
|
_root = [NSMutableArray new];
|
2020-08-21 22:38:02 +00:00
|
|
|
|
2020-08-25 00:23:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-08-25 04:35:40 +00:00
|
|
|
|
2020-08-26 00:13:37 +00:00
|
|
|
-(void)rebuildArgs {
|
|
|
|
|
|
|
|
static char* prefix[] = {
|
2021-07-11 03:33:20 +00:00
|
|
|
"flop", "flop", "hard", "cdrm", "cass", "disk", "bitb", "min", "mout", "pic"
|
2020-08-26 00:13:37 +00:00
|
|
|
};
|
2021-01-18 20:32:22 +00:00
|
|
|
static_assert(SIZEOF(prefix) == CATEGORY_COUNT, "Missing item");
|
2020-08-26 00:13:37 +00:00
|
|
|
NSMutableArray *args = [NSMutableArray new];
|
|
|
|
|
2021-01-18 20:32:22 +00:00
|
|
|
unsigned counts[CATEGORY_COUNT] = { 0 };
|
2020-08-26 00:13:37 +00:00
|
|
|
|
2021-01-18 20:32:22 +00:00
|
|
|
for (unsigned j = 0; j < CATEGORY_COUNT; ++j) {
|
2020-08-26 00:13:37 +00:00
|
|
|
|
|
|
|
MediaCategory *cat = _data[j];
|
|
|
|
NSInteger valid = [cat validCount];
|
|
|
|
for (NSInteger i = 0; i < valid; ++i) {
|
|
|
|
counts[j]++;
|
|
|
|
|
|
|
|
MediaItem *item = [cat objectAtIndex: i];
|
2021-06-13 20:29:16 +00:00
|
|
|
NSString *arg = [item argument];
|
|
|
|
|
|
|
|
if (arg) {
|
|
|
|
[args addObject: [NSString stringWithFormat: @"-%s%u", prefix[j], counts[j]]];
|
|
|
|
[args addObject: arg];
|
|
|
|
}
|
2020-08-26 00:13:37 +00:00
|
|
|
}
|
|
|
|
if (j == 0) counts[1] = counts[0]; // 3.5/5.25
|
|
|
|
}
|
|
|
|
|
|
|
|
[self setArgs: args];
|
|
|
|
}
|
|
|
|
|
2020-08-25 04:35:40 +00:00
|
|
|
-(void)rebuildRoot {
|
2021-07-14 23:40:40 +00:00
|
|
|
|
2020-08-25 04:35:40 +00:00
|
|
|
NSMutableArray *tmp = [NSMutableArray new];
|
2020-09-06 03:29:30 +00:00
|
|
|
int ix = 0;
|
2021-01-18 20:32:22 +00:00
|
|
|
for (unsigned j = 0 ; j < CATEGORY_COUNT; ++j) {
|
2020-08-25 04:35:40 +00:00
|
|
|
MediaCategory *cat = _data[j];
|
2020-09-06 03:29:30 +00:00
|
|
|
[cat setIndex: -1];
|
|
|
|
if ([cat count]) {
|
|
|
|
[cat setIndex: ix++];
|
|
|
|
[tmp addObject: cat];
|
|
|
|
}
|
2020-08-25 04:35:40 +00:00
|
|
|
}
|
|
|
|
_root = tmp;
|
|
|
|
|
2020-09-12 23:24:32 +00:00
|
|
|
// todo - switch this to use removeItemsAtIndexes:inParent:withAnimation:
|
|
|
|
// and insertItemsAtIndexes:inParent:withAnimation:
|
|
|
|
|
2021-06-07 04:34:26 +00:00
|
|
|
if (!_loadingBookmark) {
|
|
|
|
[_outlineView reloadData];
|
|
|
|
[_outlineView expandItem: nil expandChildren: YES];
|
|
|
|
}
|
2020-08-25 04:35:40 +00:00
|
|
|
}
|
|
|
|
|
2021-03-08 23:59:02 +00:00
|
|
|
-(void)setMedia: (Media)media {
|
2020-08-25 00:23:56 +00:00
|
|
|
|
2021-07-11 03:33:20 +00:00
|
|
|
// todo -- fancy diff algorithm to animate changes.
|
|
|
|
|
2020-08-25 00:23:56 +00:00
|
|
|
MediaCategory *cat;
|
|
|
|
BOOL delta = NO;
|
2021-03-08 23:59:02 +00:00
|
|
|
unsigned x;
|
|
|
|
|
|
|
|
if (MediaEqual(&_media, &media)) return;
|
2020-08-25 04:35:40 +00:00
|
|
|
_media = media;
|
|
|
|
|
2021-07-14 23:40:40 +00:00
|
|
|
[_outlineView beginUpdates];
|
|
|
|
|
2020-08-25 00:23:56 +00:00
|
|
|
|
2021-03-08 23:59:02 +00:00
|
|
|
#undef _
|
|
|
|
#define _(name, index) \
|
|
|
|
x = media.name; cat = _data[index]; delta |= [cat setItemCount: x]
|
|
|
|
_(cass, kIndexCassette);
|
|
|
|
_(cdrom, kIndexCDROM);
|
|
|
|
_(hard, kIndexHardDrive);
|
|
|
|
_(floppy_3_5, kIndexFloppy35);
|
|
|
|
_(floppy_5_25, kIndexFloppy525);
|
|
|
|
_(pseudo_disk, kIndexDiskImage);
|
2021-06-13 16:29:23 +00:00
|
|
|
_(bitbanger, kIndexBitBanger);
|
2021-07-24 01:30:16 +00:00
|
|
|
// disable midi for now - it's either a midi file (which auto-plays too soon to be useful)
|
|
|
|
// or a midi device ("default" for first one).
|
|
|
|
// So we should build a device list (and pre-populate the default one)
|
|
|
|
// another approach is a separate utility to act as a midi/serial input converter
|
|
|
|
// and midi file / serial converter so the modem/serial port could be used.
|
2021-08-07 19:12:20 +00:00
|
|
|
#if 1
|
2021-07-11 03:33:20 +00:00
|
|
|
_(midiin, kIndexMidiIn);
|
|
|
|
_(midiout, kIndexMidiOut);
|
2021-07-24 01:30:16 +00:00
|
|
|
#endif
|
2021-07-11 03:33:20 +00:00
|
|
|
_(picture, kIndexPicture);
|
2021-03-08 23:59:02 +00:00
|
|
|
|
2020-08-25 00:23:56 +00:00
|
|
|
|
2020-08-26 00:13:37 +00:00
|
|
|
if (delta) {
|
|
|
|
[self rebuildRoot];
|
2021-06-07 04:34:26 +00:00
|
|
|
if (!_loadingBookmark) [self rebuildArgs];
|
|
|
|
}
|
2021-07-14 23:40:40 +00:00
|
|
|
|
|
|
|
[_outlineView endUpdates];
|
2021-06-07 04:34:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
-(void)resetDiskImages {
|
|
|
|
|
|
|
|
BOOL delta = NO;
|
|
|
|
for (unsigned j = 0; j < CATEGORY_COUNT; ++j) {
|
|
|
|
|
|
|
|
MediaCategory *cat = _data[j];
|
|
|
|
NSInteger count = [cat count];
|
|
|
|
for (NSInteger i = 0; i < count; ++i) {
|
|
|
|
|
|
|
|
MediaItem *item = [cat objectAtIndex: i];
|
2021-06-13 20:29:16 +00:00
|
|
|
if (![item occupied]) continue;
|
2021-06-07 04:34:26 +00:00
|
|
|
[item setUrl: nil];
|
2021-06-13 20:29:16 +00:00
|
|
|
[item setString: nil];
|
2021-06-07 04:34:26 +00:00
|
|
|
delta = YES;
|
|
|
|
}
|
2021-07-10 05:15:34 +00:00
|
|
|
if ([cat pruneChildren]) delta = YES;
|
2021-06-07 04:34:26 +00:00
|
|
|
}
|
|
|
|
if (delta) {
|
|
|
|
[self rebuildRoot];
|
|
|
|
if (!_loadingBookmark) [self rebuildArgs];
|
2020-08-26 00:13:37 +00:00
|
|
|
}
|
2020-08-21 22:38:02 +00:00
|
|
|
}
|
|
|
|
|
2020-09-06 03:29:30 +00:00
|
|
|
static NSString *kDragType = @"private.ample.media";
|
2020-08-21 22:38:02 +00:00
|
|
|
- (void)viewDidLoad {
|
2021-07-10 05:15:34 +00:00
|
|
|
|
|
|
|
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
|
|
|
|
|
2020-08-21 22:38:02 +00:00
|
|
|
[super viewDidLoad];
|
|
|
|
|
|
|
|
//NSOutlineView *view = [self view];
|
|
|
|
//[view expandItem: nil expandChildren: YES];
|
|
|
|
// Do view setup here.
|
2020-09-04 23:22:18 +00:00
|
|
|
|
2020-08-25 04:35:40 +00:00
|
|
|
[_outlineView reloadData];
|
2020-08-22 04:13:23 +00:00
|
|
|
[_outlineView expandItem: nil expandChildren: YES];
|
2020-09-06 03:29:30 +00:00
|
|
|
|
|
|
|
[_outlineView registerForDraggedTypes: @[kDragType]];
|
2021-07-10 05:15:34 +00:00
|
|
|
|
|
|
|
for (unsigned i = 0; i < CATEGORY_COUNT; ++i)
|
|
|
|
[_data[i] setView: _outlineView];
|
|
|
|
|
|
|
|
|
|
|
|
[nc addObserver: self selector: @selector(magicRouteNotification:) name: kNotificationDiskImageMagicRoute object: nil];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
-(void)viewWillDisappear {
|
|
|
|
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
|
|
|
|
[nc removeObserver: self];
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < CATEGORY_COUNT; ++i)
|
|
|
|
[_data[i] setView: nil];
|
2020-08-21 22:38:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark - NSOutlineViewDelegate
|
|
|
|
|
2020-08-22 04:13:23 +00:00
|
|
|
|
2020-08-21 22:38:02 +00:00
|
|
|
- (void)outlineView:(NSOutlineView *)outlineView didAddRowView:(NSTableRowView *)rowView forRow:(NSInteger)row {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)outlineView:(NSOutlineView *)outlineView didRemoveRowView:(NSTableRowView *)rowView forRow:(NSInteger)row {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-08-22 04:13:23 +00:00
|
|
|
- (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id<MediaNode>)item {
|
2020-08-21 22:38:02 +00:00
|
|
|
|
2020-08-22 04:13:23 +00:00
|
|
|
NSString *ident = [item viewIdentifier];
|
|
|
|
if (!ident) return nil;
|
|
|
|
NSTableCellView *v = [outlineView makeViewWithIdentifier: ident owner: self];
|
2020-09-28 02:06:03 +00:00
|
|
|
[v setObjectValue: item];
|
2021-06-13 16:26:56 +00:00
|
|
|
|
|
|
|
[(id<MediaNode>)item prepareView: v];
|
2020-08-21 22:38:02 +00:00
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
2020-08-22 04:13:23 +00:00
|
|
|
|
|
|
|
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id<MediaNode>)item {
|
2020-08-21 22:38:02 +00:00
|
|
|
return [item isExpandable];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)outlineView:(NSOutlineView *)outlineView isGroupItem:(id)item {
|
2020-08-22 19:55:16 +00:00
|
|
|
return NO; //[item isGroupItem];
|
2020-08-21 22:38:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- (BOOL)outlineView:(NSOutlineView *)outlineView shouldShowOutlineCellForItem:(id)item {
|
2020-08-22 04:13:23 +00:00
|
|
|
return NO;
|
2020-08-21 22:38:02 +00:00
|
|
|
}
|
|
|
|
|
2020-08-22 04:13:23 +00:00
|
|
|
/*
|
2020-08-21 22:38:02 +00:00
|
|
|
- (BOOL)outlineView:(NSOutlineView *)outlineView shouldShowCellExpansionForTableColumn:(NSTableColumn *)tableColumn item:(id)item {
|
2020-08-22 04:13:23 +00:00
|
|
|
return NO;
|
2020-08-21 22:38:02 +00:00
|
|
|
}
|
2020-08-22 04:13:23 +00:00
|
|
|
*/
|
|
|
|
|
2020-08-21 22:38:02 +00:00
|
|
|
|
2020-08-22 04:13:23 +00:00
|
|
|
-(BOOL)outlineView:(NSOutlineView *)outlineView shouldCollapseItem:(id)item {
|
|
|
|
return NO;
|
|
|
|
}
|
2020-08-21 22:38:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark - NSOutlineViewDataSource
|
|
|
|
|
|
|
|
// nil item indicates the root.
|
|
|
|
|
|
|
|
|
|
|
|
- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
|
|
|
|
if (item == nil)
|
|
|
|
return [_root count];
|
|
|
|
return [item count];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item {
|
|
|
|
|
|
|
|
if (item == nil) {
|
|
|
|
return [_root objectAtIndex: index];
|
|
|
|
}
|
|
|
|
return [item objectAtIndex: index];
|
|
|
|
}
|
|
|
|
|
2020-08-22 04:13:23 +00:00
|
|
|
-(CGFloat)outlineView:(NSOutlineView *)outlineView heightOfRowByItem:(id<MediaNode>)item {
|
|
|
|
return [item height];
|
|
|
|
}
|
|
|
|
|
2020-09-06 03:29:30 +00:00
|
|
|
#if 0
|
|
|
|
- (id<NSPasteboardWriting>)outlineView:(NSOutlineView *)outlineView pasteboardWriterForItem:(id<MediaNode>)item {
|
|
|
|
|
|
|
|
if ([item isGroupItem]) return nil;
|
|
|
|
|
|
|
|
NSPasteboardItem *pb = [NSPasteboardItem new];
|
|
|
|
|
|
|
|
return pb;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
- (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pasteboard {
|
|
|
|
if ([items count] > 1) return NO;
|
|
|
|
|
2020-09-06 03:58:03 +00:00
|
|
|
//NSLog(@"%s", sel_getName(_cmd));
|
2020-09-06 03:29:30 +00:00
|
|
|
|
|
|
|
MediaItem *item = [items firstObject];
|
|
|
|
|
|
|
|
if (![item isKindOfClass: [MediaItem class]]) return NO;
|
|
|
|
|
|
|
|
// find the category. only allow if more than 1 item in the category.
|
|
|
|
|
|
|
|
MediaCategory *cat = nil;
|
|
|
|
|
|
|
|
|
|
|
|
for (MediaCategory *c in _root) {
|
|
|
|
NSUInteger ix = [[c children] indexOfObject: item];
|
|
|
|
if (ix != NSNotFound){
|
|
|
|
cat = c;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!cat) return NO;
|
|
|
|
if ([cat count] < 2) return NO;
|
|
|
|
|
|
|
|
NSInteger indexes[2] = { 0, 0 };
|
|
|
|
indexes[0] = [cat index];
|
|
|
|
indexes[1] = [item index];
|
2020-09-14 02:48:55 +00:00
|
|
|
NSData *data = [NSData dataWithBytes: indexes length: sizeof(indexes)];
|
2020-09-06 03:29:30 +00:00
|
|
|
|
|
|
|
[pasteboard setData: data forType: kDragType];
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* IF item is present, it's a MediaCategory and index is the index of the MediaItem it would be inserted as.
|
|
|
|
* IF item is nil, index is the MediaCategory index, which should be converted to moving to the end.
|
|
|
|
* IF index < 0, dragging far beyond the category list, so NOPE it.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
- (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id<NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index {
|
|
|
|
|
|
|
|
if (index < 0) return NSDragOperationNone;
|
|
|
|
|
|
|
|
|
|
|
|
NSPasteboard *pb = [info draggingPasteboard];
|
|
|
|
NSData *data = [pb dataForType: kDragType];
|
|
|
|
|
|
|
|
if (!data) return NSDragOperationNone;
|
|
|
|
|
|
|
|
NSInteger indexes[2];
|
|
|
|
if ([data length] != sizeof(indexes)) return NSDragOperationNone;
|
|
|
|
[data getBytes: &indexes length: sizeof(indexes)];
|
|
|
|
|
2020-09-06 03:58:03 +00:00
|
|
|
//NSLog(@"%d - %d", (int)indexes[0], (int)indexes[1]);
|
2020-09-06 03:29:30 +00:00
|
|
|
|
2020-09-06 03:58:03 +00:00
|
|
|
MediaCategory *cat = item;
|
2020-09-06 03:29:30 +00:00
|
|
|
if (!item) {
|
|
|
|
// move to the END of the previous category.
|
|
|
|
if (index == 0) return NSDragOperationNone;
|
2020-09-06 03:58:03 +00:00
|
|
|
cat = [_root objectAtIndex: index - 1];
|
|
|
|
index = [cat count]; // -1; - interferes w/ -1 logic below.
|
2020-09-06 03:29:30 +00:00
|
|
|
}
|
|
|
|
|
2020-09-06 03:58:03 +00:00
|
|
|
//NSLog(@"%d - %d", (int)[(MediaCategory *)item index], (int)index);
|
2020-09-06 03:29:30 +00:00
|
|
|
|
|
|
|
|
2020-09-06 03:58:03 +00:00
|
|
|
if ([cat index] != indexes[0]) return NSDragOperationNone;
|
2020-09-06 03:29:30 +00:00
|
|
|
if (indexes[1] == index) return NSDragOperationNone;
|
|
|
|
if (indexes[1] == index-1) return NSDragOperationNone;
|
|
|
|
return NSDragOperationMove;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id<NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index {
|
2021-07-11 03:33:20 +00:00
|
|
|
|
2020-09-06 03:58:03 +00:00
|
|
|
if (index < 0) return NO;
|
|
|
|
|
|
|
|
|
|
|
|
NSPasteboard *pb = [info draggingPasteboard];
|
|
|
|
NSData *data = [pb dataForType: kDragType];
|
|
|
|
|
|
|
|
if (!data) return NSDragOperationNone;
|
|
|
|
|
|
|
|
NSInteger indexes[2];
|
|
|
|
if ([data length] != sizeof(indexes)) return NO;
|
|
|
|
[data getBytes: &indexes length: sizeof(indexes)];
|
|
|
|
|
|
|
|
//NSLog(@"%d - %d", (int)indexes[0], (int)indexes[1]);
|
|
|
|
|
|
|
|
MediaCategory *cat = item;
|
|
|
|
|
|
|
|
if (!item) {
|
|
|
|
// move to the END of the previous category.
|
|
|
|
if (index == 0) return NO;
|
|
|
|
cat = [_root objectAtIndex: index - 1];
|
|
|
|
index = [cat count]; // -1; - interferes w/ -1 logic below.
|
|
|
|
}
|
|
|
|
|
|
|
|
//NSLog(@"%d - %d", (int)[(MediaCategory *)item index], (int)index);
|
|
|
|
|
|
|
|
|
|
|
|
if ([cat index] != indexes[0]) return NO;
|
|
|
|
if (indexes[1] == index) return NO;
|
|
|
|
if (indexes[1] == index-1) return NO;
|
|
|
|
|
|
|
|
NSInteger oldIndex = indexes[1];
|
|
|
|
|
2020-09-12 23:24:32 +00:00
|
|
|
[_outlineView beginUpdates];
|
2021-07-10 05:15:34 +00:00
|
|
|
[cat moveItemFrom: oldIndex to: index];
|
2020-09-12 23:24:32 +00:00
|
|
|
[_outlineView endUpdates];
|
2020-09-06 04:31:09 +00:00
|
|
|
[self rebuildArgs];
|
2020-09-06 03:58:03 +00:00
|
|
|
|
2020-09-12 23:24:32 +00:00
|
|
|
//[_outlineView reloadItem: cat reloadChildren: YES];
|
2020-09-06 03:58:03 +00:00
|
|
|
return YES;
|
|
|
|
|
2020-09-06 03:29:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-08-21 22:38:02 +00:00
|
|
|
|
2020-08-22 04:13:23 +00:00
|
|
|
#pragma mark - IBActions
|
2020-09-05 03:39:10 +00:00
|
|
|
- (IBAction)ejectAction:(id)sender {
|
2020-08-22 04:13:23 +00:00
|
|
|
|
|
|
|
NSInteger row = [_outlineView rowForView: sender];
|
|
|
|
if (row < 0) return;
|
2020-08-21 22:38:02 +00:00
|
|
|
|
2020-08-22 04:13:23 +00:00
|
|
|
MediaItem *item = [_outlineView itemAtRow: row];
|
|
|
|
[item setUrl: nil];
|
2021-06-13 20:29:16 +00:00
|
|
|
[item setString: nil];
|
2020-08-25 04:35:40 +00:00
|
|
|
|
|
|
|
// if item is invalid, should attempt to remove...
|
|
|
|
if (![item valid]) {
|
|
|
|
MediaCategory *cat = [_outlineView parentForItem: item];
|
2020-09-12 23:24:32 +00:00
|
|
|
[_outlineView beginUpdates];
|
2021-07-10 05:15:34 +00:00
|
|
|
[cat pruneChildren];
|
2021-07-10 05:45:03 +00:00
|
|
|
|
|
|
|
// remove the entire category??
|
|
|
|
if (![cat validCount] && ![cat count]) {
|
|
|
|
NSUInteger ix = [_root indexOfObject: cat];
|
|
|
|
if (ix != NSNotFound) {
|
|
|
|
NSIndexSet *set = [NSIndexSet indexSetWithIndex: ix];
|
|
|
|
[_outlineView removeItemsAtIndexes: set
|
|
|
|
inParent: nil
|
|
|
|
withAnimation: NSTableViewAnimationEffectFade];
|
|
|
|
|
|
|
|
[_root removeObjectAtIndex: ix];
|
2021-07-14 23:40:40 +00:00
|
|
|
[cat setIndex: -1];
|
2021-07-10 05:45:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-12 23:24:32 +00:00
|
|
|
[_outlineView endUpdates];
|
2020-08-25 04:35:40 +00:00
|
|
|
}
|
2020-09-12 23:24:32 +00:00
|
|
|
|
2020-08-26 00:13:37 +00:00
|
|
|
[self rebuildArgs];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (IBAction)pathAction:(id)sender {
|
2020-09-16 01:39:14 +00:00
|
|
|
|
|
|
|
NSURL *url = [(NSPathControl *)sender URL];
|
2021-07-10 16:07:01 +00:00
|
|
|
NSInteger tag = [sender tag] - 1;
|
|
|
|
|
|
|
|
switch(tag) {
|
|
|
|
|
|
|
|
case kIndexFloppy525:
|
|
|
|
case kIndexFloppy35:
|
|
|
|
case kIndexHardDrive:
|
|
|
|
case kIndexCDROM:
|
|
|
|
case kIndexCassette:
|
|
|
|
case kIndexDiskImage:
|
|
|
|
if (url) {
|
|
|
|
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
|
|
|
|
[nc postNotificationName: kNotificationDiskImageAdded object: url];
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2021-07-24 01:30:16 +00:00
|
|
|
// not disk images or don't use a path control.
|
2021-07-11 03:33:20 +00:00
|
|
|
case kIndexPicture:
|
|
|
|
case kIndexMidiIn:
|
|
|
|
case kIndexMidiOut:
|
|
|
|
case kIndexBitBanger:
|
2021-07-10 16:07:01 +00:00
|
|
|
default: break;
|
2020-09-16 01:39:14 +00:00
|
|
|
}
|
|
|
|
|
2020-08-26 00:13:37 +00:00
|
|
|
[self rebuildArgs];
|
2020-08-22 04:13:23 +00:00
|
|
|
}
|
2021-06-07 04:34:26 +00:00
|
|
|
|
2021-06-13 20:29:16 +00:00
|
|
|
-(IBAction)textAction: (id)sender {
|
|
|
|
[self rebuildArgs];
|
|
|
|
}
|
2021-08-07 19:12:20 +00:00
|
|
|
- (IBAction)midiAction:(id)sender {
|
|
|
|
[self rebuildArgs];
|
|
|
|
}
|
2021-06-13 20:29:16 +00:00
|
|
|
|
2021-06-13 02:59:19 +00:00
|
|
|
-(IBAction)resetMedia:(id)sender {
|
2021-06-07 04:34:26 +00:00
|
|
|
[self resetDiskImages];
|
|
|
|
}
|
|
|
|
|
2021-07-10 05:15:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
-(void)magicRouteNotification: (NSNotification *)notification {
|
|
|
|
NSDictionary *userInfo = [notification userInfo];
|
|
|
|
id path = [userInfo objectForKey: @"path"];
|
|
|
|
|
|
|
|
if ([path isKindOfClass: [NSURL class]]) {
|
|
|
|
[self smartRouteURL: path];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ([path isKindOfClass: [NSString class]]) {
|
|
|
|
NSURL *url = [NSURL fileURLWithPath: path];
|
|
|
|
[self smartRouteURL: url];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* given a file, add it to the media list.
|
|
|
|
* TODO - how to handle if full or media type missing?
|
|
|
|
*/
|
|
|
|
-(BOOL)smartRouteURL: (NSURL *)url {
|
|
|
|
|
|
|
|
if (!url) return NO;
|
|
|
|
|
|
|
|
MediaType mt = ClassifyMediaFile(url);
|
|
|
|
if (mt < 1) return NO; // unknown / error.
|
|
|
|
|
|
|
|
unsigned ix = 0;
|
|
|
|
switch(mt) {
|
|
|
|
case MediaType_3_5: ix = kIndexFloppy35; break;
|
|
|
|
case MediaType_5_25: ix = kIndexFloppy525; break;
|
|
|
|
case MediaType_Cassette: ix = kIndexCassette; break;
|
|
|
|
case MediaType_HardDisk: ix = kIndexHardDrive; break;
|
|
|
|
case MediaType_CDROM: ix = kIndexCDROM; break;
|
|
|
|
|
2021-07-11 03:33:20 +00:00
|
|
|
case MediaType_Picture: ix = kIndexPicture; break;
|
2021-07-24 01:30:16 +00:00
|
|
|
case MediaType_MIDI: // ix = kIndexMidiIn; break;
|
2021-07-10 05:15:34 +00:00
|
|
|
case MediaTypeError:
|
|
|
|
case MediaTypeUnknown:
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2021-07-10 15:25:25 +00:00
|
|
|
[_outlineView beginUpdates];
|
|
|
|
// todo -- check root, insert if necessary?
|
2021-07-10 05:15:34 +00:00
|
|
|
MediaCategory *cat = _data[ix];
|
|
|
|
[cat addURL: url];
|
2021-07-10 15:25:25 +00:00
|
|
|
[_outlineView endUpdates];
|
2021-07-10 05:15:34 +00:00
|
|
|
|
|
|
|
[self rebuildArgs];
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
-(BOOL)smartRouteFile: (NSString *)file {
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2021-06-07 04:34:26 +00:00
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation MediaViewController (Bookmark)
|
|
|
|
|
|
|
|
-(void)willLoadBookmark:(NSDictionary *)bookmark {
|
|
|
|
_loadingBookmark = YES;
|
|
|
|
[self resetDiskImages];
|
|
|
|
}
|
|
|
|
-(void)didLoadBookmark:(NSDictionary *)bookmark {
|
|
|
|
_loadingBookmark = NO;
|
|
|
|
|
|
|
|
|
|
|
|
[self rebuildRoot];
|
|
|
|
[self rebuildArgs];
|
|
|
|
}
|
|
|
|
|
2021-06-14 01:31:56 +00:00
|
|
|
static NSString * BookmarkStrings[] = {
|
2021-07-11 03:33:20 +00:00
|
|
|
@"flop_525", @"flop_35", @"hard", @"cdrm", @"cass", @"disk", @"bitb", @"midiin", @"midiout", @"pic"
|
2021-06-14 01:31:56 +00:00
|
|
|
};
|
|
|
|
static_assert(SIZEOF(BookmarkStrings) == CATEGORY_COUNT, "Missing item");
|
|
|
|
|
|
|
|
static int BookmarkIndex(NSString *str) {
|
|
|
|
if (!str) return -1;
|
|
|
|
for (int i = 0; i < SIZEOF(BookmarkStrings); ++i) {
|
|
|
|
if ([str isEqualToString: BookmarkStrings[i]]) return i;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2021-06-07 04:34:26 +00:00
|
|
|
|
|
|
|
-(BOOL)loadBookmark: (NSDictionary *)bookmark {
|
|
|
|
|
2021-06-14 01:31:56 +00:00
|
|
|
// fragile - depends on order
|
|
|
|
id media = [bookmark objectForKey: @"media"];
|
2021-06-07 04:34:26 +00:00
|
|
|
|
2021-06-14 01:31:56 +00:00
|
|
|
if ([media isKindOfClass: [NSArray class]]) {
|
|
|
|
unsigned ix = 0;
|
|
|
|
for (NSArray *a in (NSArray *)media) {
|
|
|
|
if (ix >= CATEGORY_COUNT) {
|
|
|
|
NSLog(@"MediaViewController: too many categories.");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
MediaCategory *cat = _data[ix];
|
|
|
|
NSInteger count = [cat count];
|
|
|
|
unsigned i = 0;
|
|
|
|
for (NSString *path in a) {
|
|
|
|
if (i >= count) {
|
|
|
|
NSLog(@"MediaViewController: too many files.");
|
|
|
|
break; //
|
|
|
|
}
|
|
|
|
MediaItem *item = [cat objectAtIndex: i++];
|
|
|
|
if (![path length]) continue;
|
|
|
|
|
2021-07-24 01:30:16 +00:00
|
|
|
if (ix == kIndexBitBanger || ix == kIndexMidiOut || ix == kIndexMidiIn) {
|
2021-06-14 01:31:56 +00:00
|
|
|
[item setString: path];
|
|
|
|
} else {
|
|
|
|
NSURL *url = [NSURL fileURLWithPath: path];
|
|
|
|
[item setUrl: url];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
++ix;
|
2021-06-07 04:34:26 +00:00
|
|
|
}
|
2021-06-14 01:31:56 +00:00
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
if ([media isKindOfClass: [NSDictionary class]]) {
|
|
|
|
|
|
|
|
for (NSString *key in (NSDictionary *)media) {
|
|
|
|
NSInteger ix = BookmarkIndex(key);
|
|
|
|
if (ix < 0) {
|
|
|
|
NSLog(@"MediaViewController: unrecognized category: %@", key);
|
|
|
|
continue;
|
2021-06-07 04:34:26 +00:00
|
|
|
}
|
2021-06-14 01:31:56 +00:00
|
|
|
MediaCategory *cat = _data[ix];
|
|
|
|
NSInteger count = [cat count];
|
|
|
|
NSArray *a = [(NSDictionary *)media objectForKey: key];
|
|
|
|
unsigned i = 0;
|
|
|
|
|
|
|
|
for (NSString *path in a) {
|
|
|
|
if (i >= count) {
|
|
|
|
NSLog(@"MediaViewController: too many files.");
|
|
|
|
break; //
|
|
|
|
}
|
|
|
|
MediaItem *item = [cat objectAtIndex: i++];
|
|
|
|
if (![path length]) continue;
|
|
|
|
|
2021-07-24 01:30:16 +00:00
|
|
|
if (ix == kIndexBitBanger || ix == kIndexMidiOut || ix == kIndexMidiIn) {
|
2021-06-14 01:31:56 +00:00
|
|
|
[item setString: path];
|
|
|
|
} else {
|
|
|
|
NSURL *url = [NSURL fileURLWithPath: path];
|
|
|
|
[item setUrl: url];
|
|
|
|
}
|
2021-06-13 20:29:16 +00:00
|
|
|
}
|
2021-06-07 04:34:26 +00:00
|
|
|
}
|
2021-06-14 01:31:56 +00:00
|
|
|
|
|
|
|
return YES;
|
2021-06-07 04:34:26 +00:00
|
|
|
}
|
2021-06-14 01:31:56 +00:00
|
|
|
return NO;
|
|
|
|
}
|
2021-06-07 04:34:26 +00:00
|
|
|
|
2021-06-14 01:31:56 +00:00
|
|
|
static void CompressArray(NSMutableArray *array) {
|
|
|
|
|
|
|
|
for(;;) {
|
|
|
|
NSString *s = [array lastObject];
|
|
|
|
if (!s) return;
|
|
|
|
if ([s length]) return;
|
|
|
|
[array removeLastObject];
|
|
|
|
}
|
2021-06-07 04:34:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
-(BOOL)saveBookmark: (NSMutableDictionary *)bookmark {
|
|
|
|
|
2021-06-14 01:31:56 +00:00
|
|
|
|
|
|
|
NSMutableDictionary *media = [NSMutableDictionary new];
|
2021-06-07 04:34:26 +00:00
|
|
|
|
|
|
|
for (unsigned ix = 0; ix < CATEGORY_COUNT; ++ix) {
|
|
|
|
|
|
|
|
MediaCategory *cat = _data[ix];
|
|
|
|
NSInteger count = [cat validCount];
|
2021-06-14 01:31:56 +00:00
|
|
|
if (!count) continue;
|
2021-06-07 04:34:26 +00:00
|
|
|
|
|
|
|
NSMutableArray *array = [NSMutableArray new];
|
|
|
|
for (NSInteger i = 0; i < count; ++i) {
|
|
|
|
|
|
|
|
MediaItem *item = [cat objectAtIndex: i];
|
2021-06-13 20:29:16 +00:00
|
|
|
NSString *s = [item argument];
|
|
|
|
if (!s) s = @"";
|
2021-06-07 04:34:26 +00:00
|
|
|
[array addObject: s];
|
|
|
|
}
|
2021-06-14 01:31:56 +00:00
|
|
|
|
|
|
|
CompressArray(array);
|
|
|
|
|
|
|
|
if ([array count])
|
|
|
|
[media setObject: array forKey: BookmarkStrings[ix]];
|
2021-06-07 04:34:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
[bookmark setObject: media forKey: @"media"];
|
|
|
|
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
2020-08-21 22:38:02 +00:00
|
|
|
@end
|