mirror of
https://github.com/ksherlock/ample.git
synced 2024-06-17 15:29:38 +00:00
add midi input/output menu.
This commit is contained in:
parent
026a9e7704
commit
70f4738264
|
@ -149,6 +149,10 @@
|
|||
B6841BD7251EC926006A5C39 /* vmnet_helper.c in Sources */ = {isa = PBXBuildFile; fileRef = B6841BCA251EC88E006A5C39 /* vmnet_helper.c */; };
|
||||
B6841BDA251ECB1C006A5C39 /* mame64 in CopyFiles */ = {isa = PBXBuildFile; fileRef = B66236B824FDA698006CABD7 /* mame64 */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
B6841BDE251ECC29006A5C39 /* vmnet.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B6841BDD251ECC29006A5C39 /* vmnet.framework */; };
|
||||
B68A899026BE18E000B2C8C6 /* MidiManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B68A898F26BE18E000B2C8C6 /* MidiManager.m */; };
|
||||
B68A899126BE18E000B2C8C6 /* MidiManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B68A898F26BE18E000B2C8C6 /* MidiManager.m */; };
|
||||
B68A899426BF124B00B2C8C6 /* CoreMIDI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B68A899326BF124B00B2C8C6 /* CoreMIDI.framework */; };
|
||||
B68A899526BF12A600B2C8C6 /* CoreMIDI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B68A899326BF124B00B2C8C6 /* CoreMIDI.framework */; };
|
||||
B6A1A1942528EB1700DB0FD7 /* Menu.m in Sources */ = {isa = PBXBuildFile; fileRef = B6A1A1932528EB1700DB0FD7 /* Menu.m */; };
|
||||
B6A1A1952528EB1700DB0FD7 /* Menu.m in Sources */ = {isa = PBXBuildFile; fileRef = B6A1A1932528EB1700DB0FD7 /* Menu.m */; };
|
||||
B6B9EA662506A5550080E70D /* EjectButton.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B9EA642506A5550080E70D /* EjectButton.m */; };
|
||||
|
@ -449,6 +453,9 @@
|
|||
B6841BCA251EC88E006A5C39 /* vmnet_helper.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = vmnet_helper.c; sourceTree = "<group>"; };
|
||||
B6841BD0251EC913006A5C39 /* vmnet_helper */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = vmnet_helper; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
B6841BDD251ECC29006A5C39 /* vmnet.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = vmnet.framework; path = System/Library/Frameworks/vmnet.framework; sourceTree = SDKROOT; };
|
||||
B68A898F26BE18E000B2C8C6 /* MidiManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MidiManager.m; sourceTree = "<group>"; };
|
||||
B68A899226BE320200B2C8C6 /* MidiManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MidiManager.h; sourceTree = "<group>"; };
|
||||
B68A899326BF124B00B2C8C6 /* CoreMIDI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMIDI.framework; path = System/Library/Frameworks/CoreMIDI.framework; sourceTree = SDKROOT; };
|
||||
B6A1A1932528EB1700DB0FD7 /* Menu.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Menu.m; sourceTree = "<group>"; };
|
||||
B6A1A1962528EB4600DB0FD7 /* Menu.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Menu.h; sourceTree = "<group>"; };
|
||||
B6B9EA642506A5550080E70D /* EjectButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EjectButton.m; sourceTree = "<group>"; };
|
||||
|
@ -518,6 +525,7 @@
|
|||
files = (
|
||||
B65D718625E70BD5008C5F87 /* WebKit.framework in Frameworks */,
|
||||
B635C09D26784A4800B23BFD /* Sparkle.framework in Frameworks */,
|
||||
B68A899426BF124B00B2C8C6 /* CoreMIDI.framework in Frameworks */,
|
||||
B635C09A26784A1200B23BFD /* Sparkle.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -526,6 +534,7 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B68A899526BF12A600B2C8C6 /* CoreMIDI.framework in Frameworks */,
|
||||
B65D718725E70BE5008C5F87 /* WebKit.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -635,6 +644,7 @@
|
|||
B66236B624FDA686006CABD7 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B68A899326BF124B00B2C8C6 /* CoreMIDI.framework */,
|
||||
B635C09926784A1200B23BFD /* Sparkle.framework */,
|
||||
B65D718525E70BD5008C5F87 /* WebKit.framework */,
|
||||
B6841BDD251ECC29006A5C39 /* vmnet.framework */,
|
||||
|
@ -725,6 +735,8 @@
|
|||
B6B9EA642506A5550080E70D /* EjectButton.m */,
|
||||
B64AF1F8250EF6A500A09B9B /* Transformers.h */,
|
||||
B64AF1F9250EF6A500A09B9B /* Transformers.m */,
|
||||
B68A898F26BE18E000B2C8C6 /* MidiManager.m */,
|
||||
B68A899226BE320200B2C8C6 /* MidiManager.h */,
|
||||
B6BA258124E99BEB005FB8FF /* Assets.xcassets */,
|
||||
B64E15AF24EA365E00E8AD3D /* Resources */,
|
||||
B6BA258624E99BEB005FB8FF /* Info.plist */,
|
||||
|
@ -1205,6 +1217,7 @@
|
|||
B6665C14265A0E3E00254939 /* AutocompleteControl.m in Sources */,
|
||||
B64979C224EF6703008ABD20 /* MediaViewController.m in Sources */,
|
||||
B60A6E1424EE0AE2004B7EEF /* FlippedView.m in Sources */,
|
||||
B68A899026BE18E000B2C8C6 /* MidiManager.m in Sources */,
|
||||
B6BA258024E99BE9005FB8FF /* AppDelegate.m in Sources */,
|
||||
B6004DF024FB05D600D38596 /* LogWindowController.m in Sources */,
|
||||
B63005332666D6940014C381 /* BookmarkManager.m in Sources */,
|
||||
|
@ -1223,6 +1236,7 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B68A899126BE18E000B2C8C6 /* MidiManager.m in Sources */,
|
||||
B608E1802502FE0C00D53465 /* TransparentScroller.m in Sources */,
|
||||
B64AF1FB250EF6A500A09B9B /* Transformers.m in Sources */,
|
||||
B6E4B5B024FDE2670094A35C /* main.m in Sources */,
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
<outlet property="textField" destination="Pep-mX-LHY" id="hUe-cf-6vq"/>
|
||||
</connections>
|
||||
</tableCellView>
|
||||
<tableCellView identifier="ItemView" translatesAutoresizingMaskIntoConstraints="NO" id="yGq-lc-RCM" customClass="TablePathView">
|
||||
<tableCellView identifier="ItemView" translatesAutoresizingMaskIntoConstraints="NO" id="yGq-lc-RCM" customClass="PathTableCellView">
|
||||
<rect key="frame" x="1" y="21" width="296" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
|
@ -101,7 +101,7 @@
|
|||
<outlet property="pathControl" destination="f7R-TO-fmF" id="oH7-N3-JC7"/>
|
||||
</connections>
|
||||
</tableCellView>
|
||||
<tableCellView identifier="BBItemView" id="2Nq-Xz-tkV" customClass="TablePathView">
|
||||
<tableCellView identifier="BBItemView" id="2Nq-Xz-tkV" customClass="PathTableCellView">
|
||||
<rect key="frame" x="1" y="46" width="296" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
|
@ -146,7 +146,7 @@
|
|||
<outlet property="ejectButton" destination="zxn-1E-o34" id="eC6-Mf-tN6"/>
|
||||
</connections>
|
||||
</tableCellView>
|
||||
<tableCellView identifier="OutputItemView" id="jQd-Ar-5uf" customClass="TablePathView">
|
||||
<tableCellView identifier="OutputItemView" id="jQd-Ar-5uf" customClass="PathTableCellView">
|
||||
<rect key="frame" x="1" y="71" width="296" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
|
@ -177,12 +177,12 @@
|
|||
</textFieldCell>
|
||||
<connections>
|
||||
<action selector="textAction:" target="-2" id="N7h-6y-D8W"/>
|
||||
<binding destination="jQd-Ar-5uf" name="enabled" keyPath="objectValue.valid" id="1wE-NY-Vb2"/>
|
||||
<binding destination="jQd-Ar-5uf" name="value" keyPath="objectValue.string" id="kXU-qJ-HKa">
|
||||
<dictionary key="options">
|
||||
<string key="NSNullPlaceholder">/path/to/file</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
<binding destination="jQd-Ar-5uf" name="enabled" keyPath="objectValue.valid" id="1wE-NY-Vb2"/>
|
||||
</connections>
|
||||
</textField>
|
||||
</subviews>
|
||||
|
@ -191,6 +191,57 @@
|
|||
<outlet property="ejectButton" destination="vG4-PP-efF" id="X2I-2N-t5r"/>
|
||||
</connections>
|
||||
</tableCellView>
|
||||
<tableCellView identifier="MidiItemView" id="HDi-YM-iKn" customClass="MidiTableCellView">
|
||||
<rect key="frame" x="1" y="96" width="296" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" id="XcO-0S-W4c">
|
||||
<rect key="frame" x="0.0" y="1" width="20" height="20"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageAlignment="left" image="drag-handle-4x10" id="4a2-So-rxD"/>
|
||||
</imageView>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="F3v-8Z-LaF" customClass="EjectButton">
|
||||
<rect key="frame" x="277" y="3" width="16" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="bevel" bezelStyle="regularSquare" image="eject-16x16" imagePosition="only" alignment="center" alternateImage="eject-hover-16x16" imageScaling="proportionallyDown" inset="2" id="b3t-Zp-2si">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="ejectAction:" target="-2" id="AvU-Sz-QKJ"/>
|
||||
<binding destination="HDi-YM-iKn" name="enabled" keyPath="objectValue.occupied" id="ERy-Iz-T9Q"/>
|
||||
</connections>
|
||||
</button>
|
||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="L2F-mj-lkr">
|
||||
<rect key="frame" x="20" y="-3" width="244" height="25"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<popUpButtonCell key="cell" type="push" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="CaJ-NG-vbR" id="Rac-za-bEQ">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<menu key="menu" id="I55-5S-oM2">
|
||||
<items>
|
||||
<menuItem state="on" id="CaJ-NG-vbR">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
<connections>
|
||||
<action selector="midiAction:" target="-2" id="LDL-4h-xUR"/>
|
||||
<binding destination="HDi-YM-iKn" name="selectedValue" keyPath="objectValue.string" id="xjL-hx-VG3">
|
||||
<dictionary key="options">
|
||||
<string key="NSValueTransformerName">EmptyStringTransformer</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
</connections>
|
||||
</popUpButton>
|
||||
</subviews>
|
||||
<connections>
|
||||
<outlet property="dragHandle" destination="XcO-0S-W4c" id="oKY-UF-LkW"/>
|
||||
<outlet property="ejectButton" destination="F3v-8Z-LaF" id="ql8-4u-qCA"/>
|
||||
<outlet property="popUpButton" destination="L2F-mj-lkr" id="Ktx-Yc-DIp"/>
|
||||
</connections>
|
||||
</tableCellView>
|
||||
</prototypeCellViews>
|
||||
</tableColumn>
|
||||
</tableColumns>
|
||||
|
|
|
@ -9,26 +9,8 @@
|
|||
#import "MediaViewController.h"
|
||||
#import "TableCellView.h"
|
||||
|
||||
enum {
|
||||
kIndexFloppy525 = 0,
|
||||
kIndexFloppy35,
|
||||
kIndexHardDrive,
|
||||
kIndexCDROM,
|
||||
kIndexCassette,
|
||||
kIndexDiskImage,
|
||||
kIndexBitBanger,
|
||||
kIndexMidiIn,
|
||||
kIndexMidiOut,
|
||||
kIndexPicture, // computer eyes -pic, .png only.
|
||||
// kIndexPrintout // -prin, .prn extension only?
|
||||
|
||||
kIndexLast
|
||||
};
|
||||
|
||||
#define CATEGORY_COUNT 10
|
||||
#define SIZEOF(x) (sizeof(x) / sizeof(x[0]))
|
||||
|
||||
static_assert(kIndexLast == CATEGORY_COUNT, "Invalid Category Count");
|
||||
|
||||
@protocol MediaNode
|
||||
-(BOOL)isGroupItem;
|
||||
|
@ -285,15 +267,23 @@ static_assert(kIndexLast == CATEGORY_COUNT, "Invalid Category Count");
|
|||
|
||||
-(NSString *)viewIdentifier {
|
||||
if (_category == kIndexBitBanger) return @"BBItemView";
|
||||
if (_category == kIndexMidiOut) return @"OutputItemView";
|
||||
if (_category == kIndexMidiIn) return @"OutputItemView";
|
||||
if (_category == kIndexMidiOut) return @"MidiItemView";
|
||||
if (_category == kIndexMidiIn) return @"MidiItemView";
|
||||
return @"ItemView";
|
||||
}
|
||||
|
||||
-(void)prepareView: (TablePathView *)view {
|
||||
-(void)prepareView: (MediaTableCellView *)view {
|
||||
/* set the path tag = category. */
|
||||
|
||||
[view prepareView: _category];
|
||||
#if 0
|
||||
if (_category == kIndexMidiIn || _category == kIndexMidiOut || _category == kIndexBitBanger) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSPathControl *pc = [view pathControl];
|
||||
[pc setTag: _category + 1]; // to differentiate 0 / no path control.
|
||||
#endif
|
||||
}
|
||||
|
||||
-(CGFloat)height {
|
||||
|
@ -437,7 +427,7 @@ x = media.name; cat = _data[index]; delta |= [cat setItemCount: x]
|
|||
// 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.
|
||||
#if 0
|
||||
#if 1
|
||||
_(midiin, kIndexMidiIn);
|
||||
_(midiout, kIndexMidiOut);
|
||||
#endif
|
||||
|
@ -782,6 +772,9 @@ static NSString *kDragType = @"private.ample.media";
|
|||
-(IBAction)textAction: (id)sender {
|
||||
[self rebuildArgs];
|
||||
}
|
||||
- (IBAction)midiAction:(id)sender {
|
||||
[self rebuildArgs];
|
||||
}
|
||||
|
||||
-(IBAction)resetMedia:(id)sender {
|
||||
[self resetDiskImages];
|
||||
|
|
25
Ample/MidiManager.h
Normal file
25
Ample/MidiManager.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// MidiManager.h
|
||||
// Ample
|
||||
//
|
||||
// Created by Kelvin Sherlock on 8/6/2021.
|
||||
// Copyright © 2021 Kelvin Sherlock. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef MidiManager_h
|
||||
#define MidiManager_h
|
||||
|
||||
|
||||
extern NSString *kMidiSourcesChangedNotification;
|
||||
extern NSString *kMidiDestinationsChangedNotification;
|
||||
|
||||
@interface MidiManager : NSObject
|
||||
|
||||
@property NSArray *sources;
|
||||
@property NSArray *destinations;
|
||||
|
||||
+(instancetype)sharedManager;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* MidiManager_h */
|
153
Ample/MidiManager.m
Normal file
153
Ample/MidiManager.m
Normal file
|
@ -0,0 +1,153 @@
|
|||
//
|
||||
// Midi.m
|
||||
// Ample
|
||||
//
|
||||
// Created by Kelvin Sherlock on 8/6/2021.
|
||||
// Copyright © 2021 Kelvin Sherlock. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreMIDI/CoreMIDI.h>
|
||||
|
||||
#import "MidiManager.h"
|
||||
|
||||
static NSArray *MidiSources(void) {
|
||||
|
||||
ItemCount count = MIDIGetNumberOfSources();
|
||||
if (count <= 0) return @[];
|
||||
|
||||
NSMutableArray *rv = [NSMutableArray arrayWithCapacity: count + 1];
|
||||
|
||||
MIDIEndpointRef ep;
|
||||
for(int i = 0; i < count; ++i) {
|
||||
ep = MIDIGetSource(i);
|
||||
if (!ep) continue;
|
||||
|
||||
// https://developer.apple.com/library/archive/qa/qa1374/_index.html
|
||||
CFStringRef str = NULL;
|
||||
MIDIObjectGetStringProperty(ep, kMIDIPropertyDisplayName, &str);
|
||||
|
||||
if (str) {
|
||||
[rv addObject: (__bridge id _Nonnull)(str)];
|
||||
CFRelease(str);
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
static NSArray *MidiDestinations(void) {
|
||||
|
||||
ItemCount count = MIDIGetNumberOfDestinations();
|
||||
if (count <= 0) return @[];
|
||||
|
||||
NSMutableArray *rv = [NSMutableArray arrayWithCapacity: count + 1];
|
||||
|
||||
MIDIEndpointRef ep;
|
||||
for(int i = 0; i < count; ++i) {
|
||||
ep = MIDIGetDestination(i);
|
||||
if (!ep) continue;
|
||||
|
||||
// https://developer.apple.com/library/archive/qa/qa1374/_index.html
|
||||
CFStringRef str = NULL;
|
||||
MIDIObjectGetStringProperty(ep, kMIDIPropertyDisplayName, &str);
|
||||
|
||||
if (str) {
|
||||
[rv addObject: (__bridge id _Nonnull)(str)];
|
||||
CFRelease(str);
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
NSString *kMidiSourcesChangedNotification = @"Midi Sources Changed";
|
||||
NSString *kMidiDestinationsChangedNotification = @"Midi Destinations Changed";
|
||||
|
||||
|
||||
@interface MidiManager () {
|
||||
MIDIClientRef _client;
|
||||
}
|
||||
|
||||
-(void)objectAddRemove: (const MIDIObjectAddRemoveNotification *)message;
|
||||
-(void)objectPropertyChanged: (const MIDIObjectPropertyChangeNotification *)message;
|
||||
@end
|
||||
|
||||
|
||||
static MidiManager *singleton = nil;
|
||||
@implementation MidiManager
|
||||
|
||||
-(void)awakeFromNib {
|
||||
if (!singleton) singleton = self;
|
||||
}
|
||||
|
||||
+(instancetype)sharedManager {
|
||||
if (!singleton) singleton = [MidiManager new];
|
||||
return singleton;
|
||||
}
|
||||
|
||||
-(instancetype)init {
|
||||
|
||||
if (singleton) return singleton;
|
||||
|
||||
OSStatus status;
|
||||
|
||||
|
||||
status = MIDIClientCreateWithBlock(
|
||||
CFSTR("serial_midi"),
|
||||
&_client,
|
||||
^(const MIDINotification *message){
|
||||
switch(message->messageID) {
|
||||
case kMIDIMsgObjectAdded:
|
||||
case kMIDIMsgObjectRemoved:
|
||||
[self objectAddRemove: (const MIDIObjectAddRemoveNotification *)message];
|
||||
break;
|
||||
case kMIDIMsgPropertyChanged:
|
||||
[self objectPropertyChanged: (const MIDIObjectPropertyChangeNotification *)message];
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
_sources = MidiSources();
|
||||
_destinations = MidiDestinations();
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void)objectAddRemove: (const MIDIObjectAddRemoveNotification *)message {
|
||||
|
||||
const MIDIObjectAddRemoveNotification *m = (const MIDIObjectAddRemoveNotification *)message;
|
||||
|
||||
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
|
||||
|
||||
if (m->childType == kMIDIObjectType_Source) {
|
||||
[self setSources: MidiSources()];
|
||||
[nc postNotificationName: kMidiSourcesChangedNotification object: self];
|
||||
}
|
||||
|
||||
if (m->childType == kMIDIObjectType_Destination) {
|
||||
[self setDestinations: MidiDestinations()];
|
||||
[nc postNotificationName: kMidiDestinationsChangedNotification object: self];
|
||||
}
|
||||
|
||||
}
|
||||
-(void)objectPropertyChanged: (const MIDIObjectPropertyChangeNotification *)message {
|
||||
|
||||
const MIDIObjectPropertyChangeNotification *m = (const MIDIObjectPropertyChangeNotification *)message;
|
||||
if (m->propertyName == kMIDIPropertyDisplayName) {
|
||||
[self setSources: MidiSources()];
|
||||
[self setDestinations: MidiDestinations()];
|
||||
|
||||
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
|
||||
[nc postNotificationName: kMidiSourcesChangedNotification object: self];
|
||||
[nc postNotificationName: kMidiDestinationsChangedNotification object: self];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-(void)dealloc {
|
||||
|
||||
if (_client)
|
||||
MIDIClientDispose(_client);
|
||||
}
|
||||
@end
|
||||
|
|
@ -11,12 +11,40 @@
|
|||
//NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
enum {
|
||||
kIndexFloppy525 = 0,
|
||||
kIndexFloppy35,
|
||||
kIndexHardDrive,
|
||||
kIndexCDROM,
|
||||
kIndexCassette,
|
||||
kIndexDiskImage,
|
||||
kIndexBitBanger,
|
||||
kIndexMidiIn,
|
||||
kIndexMidiOut,
|
||||
kIndexPicture, // computer eyes -pic, .png only.
|
||||
// kIndexPrintout // -prin, .prn extension only?
|
||||
|
||||
@interface TablePathView : NSTableCellView
|
||||
@property (weak) IBOutlet NSPathControl *pathControl;
|
||||
kIndexLast
|
||||
};
|
||||
#define CATEGORY_COUNT 10
|
||||
static_assert(kIndexLast == CATEGORY_COUNT, "Invalid Category Count");
|
||||
|
||||
|
||||
@interface MediaTableCellView : NSTableCellView
|
||||
@property (weak) IBOutlet NSButton *ejectButton;
|
||||
@property (weak) IBOutlet NSImageView *dragHandle;
|
||||
@property BOOL movable;
|
||||
|
||||
-(void)prepareView: (NSInteger)category;
|
||||
@end
|
||||
|
||||
@interface PathTableCellView : MediaTableCellView <NSPathControlDelegate>
|
||||
@property (weak) IBOutlet NSPathControl *pathControl;
|
||||
@end
|
||||
|
||||
|
||||
@interface MidiTableCellView : MediaTableCellView
|
||||
@property (weak) IBOutlet NSPopUpButton *popUpButton;
|
||||
@end
|
||||
|
||||
//NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -7,13 +7,16 @@
|
|||
//
|
||||
|
||||
#import "TableCellView.h"
|
||||
#import "MidiManager.h"
|
||||
#import "Menu.h"
|
||||
|
||||
|
||||
|
||||
@implementation TablePathView {
|
||||
@implementation MediaTableCellView
|
||||
#if 0
|
||||
{
|
||||
NSTrackingRectTag _trackingRect;
|
||||
}
|
||||
|
||||
#endif
|
||||
-(void)awakeFromNib {
|
||||
|
||||
// need to do it here for 10.11 compatibility.
|
||||
|
@ -31,6 +34,8 @@
|
|||
|
||||
}
|
||||
|
||||
-(void)prepareView: (NSInteger)category {
|
||||
}
|
||||
|
||||
#if 0
|
||||
-(void)awakeFromNib {
|
||||
|
@ -65,3 +70,143 @@
|
|||
|
||||
@end
|
||||
|
||||
@implementation PathTableCellView
|
||||
|
||||
-(void)prepareView: (NSInteger)category {
|
||||
[_pathControl setTag: category + 1];
|
||||
}
|
||||
|
||||
- (void)pathControl:(NSPathControl *)pathControl willPopUpMenu:(NSMenu *)menu {
|
||||
// if this is an output path, replace the "choose..." button with a save panel.
|
||||
NSMenuItem *item = [menu itemAtIndex: 0];
|
||||
if (item) {
|
||||
[item setTarget: self];
|
||||
[item setAction: @selector(choosePath:)];
|
||||
}
|
||||
}
|
||||
|
||||
-(IBAction)choosePath:(id)sender {
|
||||
NSPathControl *pc = _pathControl;
|
||||
NSURL *url = [pc URL];
|
||||
|
||||
NSSavePanel *p = [NSSavePanel savePanel];
|
||||
|
||||
if (url) {
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
BOOL dir = NO;
|
||||
NSString *str = [NSString stringWithCString: [url fileSystemRepresentation] encoding: NSUTF8StringEncoding];
|
||||
[fm fileExistsAtPath: str isDirectory: &dir];
|
||||
|
||||
if (!dir) {
|
||||
[p setNameFieldStringValue: [str lastPathComponent]];
|
||||
url = [url URLByDeletingLastPathComponent];
|
||||
}
|
||||
[p setDirectoryURL: url];
|
||||
}
|
||||
[p setExtensionHidden: NO];
|
||||
|
||||
[p beginWithCompletionHandler: ^(NSModalResponse response){
|
||||
if (response != NSModalResponseOK) return;
|
||||
NSURL *url = [p URL];
|
||||
[pc setURL: url];
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface EmptyStringTransformer : NSValueTransformer
|
||||
|
||||
@end
|
||||
|
||||
static NSString *kNone = @"—None—";
|
||||
|
||||
@implementation EmptyStringTransformer
|
||||
|
||||
+(void)load {
|
||||
[self setValueTransformer: [self new] forName: @"EmptyStringTransformer"];
|
||||
}
|
||||
|
||||
+ (Class)transformedValueClass {
|
||||
return [NSString class];
|
||||
}
|
||||
+ (BOOL)allowsReverseTransformation {
|
||||
return YES;
|
||||
}
|
||||
- (id)transformedValue:(id)value {
|
||||
if (value == nil) return kNone;
|
||||
if ([kNone isEqualToString: value]) return nil;
|
||||
return value;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MidiTableCellView {
|
||||
NSInteger _category;
|
||||
}
|
||||
|
||||
/* binding should be able to handle the menu but i couldn't make it work. */
|
||||
|
||||
|
||||
-(void)prepareView: (NSInteger)category {
|
||||
_category = category;
|
||||
|
||||
// 10.11 + doesn't need to remove the observer in the -dealloc
|
||||
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
|
||||
[nc addObserver: self selector: @selector(midiChanged:) name: category == kIndexMidiIn ? kMidiSourcesChangedNotification : kMidiDestinationsChangedNotification object: nil];
|
||||
|
||||
[self updateMenus: NO];
|
||||
}
|
||||
|
||||
-(void)updateMenus: (BOOL)notification {
|
||||
NSMenu *menu = [_popUpButton menu];
|
||||
MidiManager *mgr = [MidiManager sharedManager];
|
||||
|
||||
NSArray *array = _category == kIndexMidiIn ? [mgr sources] : [mgr destinations];
|
||||
|
||||
[menu removeAllItems];
|
||||
NSString *selected = [[_popUpButton selectedItem] representedObject];
|
||||
int selectedIndex = -1;
|
||||
NSMenuItem *item;
|
||||
|
||||
item = [[NSMenuItem alloc] initWithTitle: kNone action: NULL keyEquivalent: @""];
|
||||
[item setAttributedTitle: ItalicMenuString(kNone)];
|
||||
[menu addItem: item];
|
||||
selectedIndex = 0;
|
||||
#if 0
|
||||
if (!selected || [@"" isEqualToString: selected]) {
|
||||
selectedIndex = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int ix = 1;
|
||||
for (NSString *s in array) {
|
||||
item = [[NSMenuItem alloc] initWithTitle: s action: NULL keyEquivalent: @""];
|
||||
[item setRepresentedObject: s];
|
||||
[menu addItem: item];
|
||||
if ([s isEqualToString: selected]) {
|
||||
selectedIndex = ix;
|
||||
}
|
||||
++ix;
|
||||
}
|
||||
|
||||
// does this propogate?
|
||||
[_popUpButton selectItemAtIndex: selectedIndex];
|
||||
if (notification) [_popUpButton sendAction: [_popUpButton action] to: [_popUpButton target]];
|
||||
}
|
||||
|
||||
-(void)midiChanged: (NSNotification *)notification {
|
||||
|
||||
[self updateMenus: YES];
|
||||
}
|
||||
|
||||
|
||||
-(void)prepareForReuse {
|
||||
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
|
||||
[nc removeObserver: self];
|
||||
_category = 0;
|
||||
[super prepareForReuse];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
Loading…
Reference in New Issue
Block a user