mirror of
https://github.com/ksherlock/ample.git
synced 2024-11-19 02:11:08 +00:00
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. commitf628d99e4a
Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Fri Jun 4 00:21:08 2021 -0400 load bookmark... commit0b248e6aad
Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Fri Jun 4 00:20:42 2021 -0400 stringValue can't be nil. commit94aac38af4
Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Thu Jun 3 23:04:37 2021 -0400 add bookmark menu commit6215a0df12
Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Thu Jun 3 23:03:29 2021 -0400 slot view needs to know the machine. commitd348c15dc5
Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Thu Jun 3 23:02:58 2021 -0400 transformer to enable/disable control based on string length. commite14336a009
Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Thu Jun 3 23:02:14 2021 -0400 shut up compiler warning. commit4baf545245
Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Thu Jun 3 23:01:15 2021 -0400 bookmark manager commit0f3e6c8307
Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Mon May 31 23:54:29 2021 -0400 more (untested) bookmark code commit8fdb149eb3
Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Mon May 31 16:13:43 2021 -0400 start of bookmarking support. Untested. commit787eac87f6
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.
This commit is contained in:
parent
789d61d9f1
commit
3d5a2951bb
@ -64,6 +64,8 @@
|
||||
B6152B5B25F5B57E00605E6E /* Media.m in Sources */ = {isa = PBXBuildFile; fileRef = B6152B5925F5B57E00605E6E /* Media.m */; };
|
||||
B615A99F26640940001FBF99 /* SlotView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B6E9A18125088B36005E7525 /* SlotView.xib */; };
|
||||
B615A9A026640A70001FBF99 /* SlotViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B6E9A17F25088B1B005E7525 /* SlotViewController.m */; };
|
||||
B63005332666D6940014C381 /* BookmarkManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B63005322666D6940014C381 /* BookmarkManager.m */; };
|
||||
B63005342666D6940014C381 /* BookmarkManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B63005322666D6940014C381 /* BookmarkManager.m */; };
|
||||
B6374AC4260EBBCF0045CA16 /* pty_shell.c in Sources */ = {isa = PBXBuildFile; fileRef = B6374AB6260EBB970045CA16 /* pty_shell.c */; };
|
||||
B6374AC5260EBC5A0045CA16 /* pty_shell in CopyFiles */ = {isa = PBXBuildFile; fileRef = B6374ABD260EBBC90045CA16 /* pty_shell */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
B6374AD1260ECB400045CA16 /* macclas2.plist in Resources */ = {isa = PBXBuildFile; fileRef = B6374AC9260ECB3F0045CA16 /* macclas2.plist */; };
|
||||
@ -375,6 +377,8 @@
|
||||
B6152B5525F4549F00605E6E /* Slot.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Slot.m; sourceTree = "<group>"; };
|
||||
B6152B5825F5B4F100605E6E /* Media.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Media.h; sourceTree = "<group>"; };
|
||||
B6152B5925F5B57E00605E6E /* Media.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Media.m; sourceTree = "<group>"; };
|
||||
B63005312666D6940014C381 /* BookmarkManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BookmarkManager.h; sourceTree = "<group>"; };
|
||||
B63005322666D6940014C381 /* BookmarkManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BookmarkManager.m; sourceTree = "<group>"; };
|
||||
B6374AB6260EBB970045CA16 /* pty_shell.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = pty_shell.c; sourceTree = "<group>"; };
|
||||
B6374ABD260EBBC90045CA16 /* pty_shell */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = pty_shell; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
B6374AC9260ECB3F0045CA16 /* macclas2.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = macclas2.plist; sourceTree = "<group>"; };
|
||||
@ -690,6 +694,8 @@
|
||||
B608E17E2502FE0C00D53465 /* TransparentScroller.m */,
|
||||
B66D0FE62611386B000902F1 /* SoftwareList.m */,
|
||||
B66D0FE926113AA8000902F1 /* SoftwareList.h */,
|
||||
B63005312666D6940014C381 /* BookmarkManager.h */,
|
||||
B63005322666D6940014C381 /* BookmarkManager.m */,
|
||||
B6BA563A251685DA00B0C47D /* Window Controllers */,
|
||||
B6B9EA652506A5550080E70D /* EjectButton.h */,
|
||||
B6B9EA642506A5550080E70D /* EjectButton.m */,
|
||||
@ -1154,6 +1160,7 @@
|
||||
B60A6E1424EE0AE2004B7EEF /* FlippedView.m in Sources */,
|
||||
B6BA258024E99BE9005FB8FF /* AppDelegate.m in Sources */,
|
||||
B6004DF024FB05D600D38596 /* LogWindowController.m in Sources */,
|
||||
B63005332666D6940014C381 /* BookmarkManager.m in Sources */,
|
||||
B66236A924FD9A34006CABD7 /* PreferencesWindowController.m in Sources */,
|
||||
B63C1F0F25B1447C0016A611 /* CheatSheetWindowController.m in Sources */,
|
||||
B64AF1F2250ECB2E00A09B9B /* DiskImagesWindowController.m in Sources */,
|
||||
@ -1183,6 +1190,7 @@
|
||||
B6E4B5B324FDE2670094A35C /* MediaViewController.m in Sources */,
|
||||
B64AF1F7250ED5E400A09B9B /* TableCellView.m in Sources */,
|
||||
B6E4B5B424FDE2670094A35C /* FlippedView.m in Sources */,
|
||||
B63005342666D6940014C381 /* BookmarkManager.m in Sources */,
|
||||
B615A9A026640A70001FBF99 /* SlotViewController.m in Sources */,
|
||||
B6665C15265A0E3E00254939 /* AutocompleteControl.m in Sources */,
|
||||
B6E4B5B524FDE2670094A35C /* AppDelegate.m in Sources */,
|
||||
|
@ -40,4 +40,13 @@ extern NSString *kDownloadExtension;
|
||||
extern NSString *kDefaultDownloadURL;
|
||||
extern NSString *kDefaultDownloadExtension;
|
||||
|
||||
|
||||
@protocol Bookmark <NSObject>
|
||||
-(BOOL)loadBookmark: (NSDictionary *)bookmark;
|
||||
-(BOOL)saveBookmark: (NSMutableDictionary *)bookmark;
|
||||
|
||||
-(void)willLoadBookmark: (NSDictionary *)bookmark;
|
||||
-(void)didLoadBookmark: (NSDictionary *)bookmark;
|
||||
@end
|
||||
|
||||
#endif /* Ample_h */
|
||||
|
@ -13,6 +13,7 @@
|
||||
#import "DiskImagesWindowController.h"
|
||||
#import "CheatSheetWindowController.h"
|
||||
#import "Transformers.h"
|
||||
#import "BookmarkManager.h"
|
||||
|
||||
#import "LogWindowController.h"
|
||||
|
||||
@ -40,6 +41,10 @@
|
||||
|
||||
RegisterTransformers();
|
||||
|
||||
BookmarkManager *bm = [BookmarkManager sharedManager];
|
||||
[bm loadBookmarks];
|
||||
[bm updateMenu];
|
||||
|
||||
path = [bundle pathForResource: @"Defaults" ofType: @"plist"];
|
||||
dict = [NSDictionary dictionaryWithContentsOfFile: path];
|
||||
|
||||
|
@ -32,6 +32,10 @@ Todo --
|
||||
@property (weak) AutocompleteControl *parent;
|
||||
|
||||
-(void)reset;
|
||||
-(void)reset: (id<AutocompleteItem>)value;
|
||||
-(void)setItems:(NSArray<id<AutocompleteItem>> *)items;
|
||||
-(void)setItems:(NSArray<id<AutocompleteItem>> *)items value: (id<AutocompleteItem>)value;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@ -96,27 +100,37 @@ Todo --
|
||||
}
|
||||
|
||||
-(void)setStringValue:(NSString *)stringValue {
|
||||
[super setStringValue: stringValue];
|
||||
if (_value && [[_value menuTitle] isEqualToString: stringValue] == NO)
|
||||
[super setStringValue: stringValue ? stringValue : @""];
|
||||
if (_value && [[_value menuTitle] isEqualToString: stringValue] == NO) {
|
||||
// post change notification?
|
||||
_value = nil;
|
||||
[_menuView reset];
|
||||
}
|
||||
|
||||
[self fixTextColor: _editing];
|
||||
// todo -- search for a matching item, update text color.
|
||||
}
|
||||
|
||||
// todo -- _menuView has second copy of value, need to update that.
|
||||
-(void)setObjectValue:(id)objectValue {
|
||||
if (_value == objectValue) return;
|
||||
if (![objectValue conformsToProtocol: @protocol(AutocompleteItem)]) {
|
||||
_value = nil;
|
||||
[_menuView reset];
|
||||
[super setStringValue: @""];
|
||||
[self fixTextColor: _editing];
|
||||
return;
|
||||
}
|
||||
_value = objectValue;
|
||||
if (!_value) [super setStringValue: @""]; //
|
||||
else {
|
||||
[super setStringValue: [_value menuTitle]];
|
||||
NSArray *array = [_autocompleteDelegate autocomplete: self completionsForItem: _value];
|
||||
[_menuView setItems: array];
|
||||
[_menuView reset: _value];
|
||||
// TODO -- menu view currently uses text search.
|
||||
//NSArray *array = [_autocompleteDelegate autocomplete: self completionsForItem: _value];
|
||||
//[_menuView setItems: array value: _value];
|
||||
}
|
||||
[self fixTextColor: NO];
|
||||
}
|
||||
|
||||
-(BOOL)valid {
|
||||
@ -377,6 +391,23 @@ Todo --
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (commandSelector == @selector(scrollPageDown:)) {
|
||||
if ([_panel isVisible]) {
|
||||
[_menuView scrollPageDown: textView];
|
||||
} else {
|
||||
[self updateSuggestions];
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
if (commandSelector == @selector(scrollPageUp:)) {
|
||||
if ([_panel isVisible]) {
|
||||
[_menuView scrollPageUp: textView];
|
||||
} else {
|
||||
[self updateSuggestions];
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
//NSLog(@"%@", NSStringFromSelector(commandSelector));
|
||||
return NO;
|
||||
}
|
||||
@ -468,14 +499,24 @@ static CGFloat HeightForItems(NSUInteger count) {
|
||||
_items = nil;
|
||||
}
|
||||
|
||||
-(void)setItems:(NSArray *)items {
|
||||
if (_items == items) return;
|
||||
-(void)reset: (id<AutocompleteItem>)value {
|
||||
[self invalidateRow: _index];
|
||||
_index = -1;
|
||||
_items = nil;
|
||||
_value = value;
|
||||
}
|
||||
|
||||
-(void)setItems:(NSArray *)items value: (id<AutocompleteItem> )value {
|
||||
if (_items == items && _value == value) return;
|
||||
_items = [items copy];
|
||||
_index = -1;
|
||||
_count = [items count];
|
||||
|
||||
_value = value;
|
||||
|
||||
if (!_items) return;
|
||||
if (!_items) {
|
||||
_value = nil;
|
||||
return;
|
||||
}
|
||||
|
||||
// also check enabled status....
|
||||
if (_value) {
|
||||
@ -502,8 +543,6 @@ static CGFloat HeightForItems(NSUInteger count) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
NSInteger displayCount = MIN(_count, MAX_DISPLAY_ITEMS);
|
||||
CGFloat newHeight = HeightForItems(displayCount) + 8 ; // 4px top/bottom
|
||||
NSWindow *window = [self window];
|
||||
@ -539,6 +578,14 @@ static CGFloat HeightForItems(NSUInteger count) {
|
||||
//NSLog(@"%@", NSStringFromRect(wFrame));
|
||||
}
|
||||
|
||||
-(void)setItems:(NSArray<id<AutocompleteItem>> *)items {
|
||||
|
||||
if (_items == items) return;
|
||||
[self setItems: items value: _value];
|
||||
}
|
||||
|
||||
|
||||
|
||||
-(id<AutocompleteItem>)itemAtPoint: (NSPoint)point indexPtr: (NSInteger *)indexPtr {
|
||||
|
||||
NSInteger index = floor(point.y / MENU_HEIGHT);
|
||||
@ -636,6 +683,18 @@ enum {
|
||||
[_parent selectItem: _value withSelector: _cmd];
|
||||
}
|
||||
|
||||
-(void)scrollPageUp:(id)sender {
|
||||
if (_count == 0 || _index <= 0) return;
|
||||
|
||||
|
||||
}
|
||||
|
||||
-(void)scrollPageDown:(id)sender {
|
||||
if (_count == 0 || _index == _count - 1) return;
|
||||
|
||||
}
|
||||
|
||||
|
||||
-(void)insertNewline:(id)sender {
|
||||
if (_value) {
|
||||
[_parent selectItem: _value withSelector: _cmd];
|
||||
|
@ -27,7 +27,7 @@
|
||||
<scrollView focusRingType="none" fixedFrame="YES" borderType="none" autohidesScrollers="YES" horizontalLineScroll="24" horizontalPageScroll="10" verticalLineScroll="24" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" findBarPosition="belowContent" translatesAutoresizingMaskIntoConstraints="NO" id="ivr-XT-BPS">
|
||||
<rect key="frame" x="-1" y="-1" width="302" height="402"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<clipView key="contentView" ambiguous="YES" copiesOnScroll="NO" id="1jn-uc-06s">
|
||||
<clipView key="contentView" ambiguous="YES" drawsBackground="NO" copiesOnScroll="NO" id="1jn-uc-06s">
|
||||
<rect key="frame" x="0.0" y="0.0" width="302" height="402"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
@ -35,7 +35,7 @@
|
||||
<rect key="frame" x="0.0" y="0.0" width="302" height="402"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<size key="intercellSpacing" width="3" height="2"/>
|
||||
<color key="backgroundColor" name="windowBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" white="1" alpha="0.0" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
|
||||
<tableColumns>
|
||||
<tableColumn width="282" minWidth="40" maxWidth="1000" id="1rF-M3-pYV">
|
||||
|
@ -8,6 +8,8 @@
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="LaunchWindowController">
|
||||
<connections>
|
||||
<outlet property="addBookmarkWindow" destination="yIt-hP-HBq" id="Qhx-WV-cg4"/>
|
||||
<outlet property="bookmarkTextField" destination="fAl-Vn-x1w" id="GNr-j6-BiQ"/>
|
||||
<outlet property="machineView" destination="oVt-eD-aaj" id="Q9V-Kr-6GN"/>
|
||||
<outlet property="machineViewController" destination="RgH-d9-xl8" id="DIa-h0-6y2"/>
|
||||
<outlet property="mediaController" destination="t7c-zy-czN" id="a7d-HC-TWx"/>
|
||||
@ -123,7 +125,7 @@ DQ
|
||||
</connections>
|
||||
</button>
|
||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="hM8-FM-Agh">
|
||||
<rect key="frame" x="382" y="87" width="172" height="25"/>
|
||||
<rect key="frame" x="382" y="87" width="174" height="25"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Default" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="Dsm-bi-Txy" id="mBS-h4-BWC">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
@ -239,7 +241,7 @@ DQ
|
||||
</button>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="PGK-yK-2ZK">
|
||||
<rect key="frame" x="15" y="91" width="43" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="Speed" id="D9w-Mz-PXs">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -247,7 +249,7 @@ DQ
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="FE4-gG-fPb">
|
||||
<rect key="frame" x="62" y="86" width="107" height="25"/>
|
||||
<rect key="frame" x="62" y="86" width="109" height="25"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<popUpButtonCell key="cell" type="push" title="100%" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="1" imageScaling="proportionallyDown" inset="2" selectedItem="yoI-Ra-evu" id="M40-f0-awc">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
@ -300,7 +302,7 @@ DQ
|
||||
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="5Yf-ZJ-8kx">
|
||||
<rect key="frame" x="132" y="59" width="523" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="/path/to/file.wav" drawsBackground="YES" id="Xqc-zw-28b">
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="/path/to/file.wav" drawsBackground="YES" usesSingleLineMode="YES" id="Xqc-zw-28b">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -317,7 +319,7 @@ DQ
|
||||
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ne8-5I-D1H">
|
||||
<rect key="frame" x="132" y="28" width="523" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="/path/to/file.vgm" drawsBackground="YES" id="xGs-Mi-wzj">
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="/path/to/file.vgm" drawsBackground="YES" usesSingleLineMode="YES" id="xGs-Mi-wzj">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -342,7 +344,7 @@ DQ
|
||||
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Xht-PS-Hw8">
|
||||
<rect key="frame" x="132" y="90" width="523" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="/path/to/file.avi" drawsBackground="YES" id="iY4-FF-x2e">
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="/path/to/file.avi" drawsBackground="YES" usesSingleLineMode="YES" id="iY4-FF-x2e">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -378,13 +380,14 @@ DQ
|
||||
<textField toolTip="path or socket.address:port" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Wga-1O-bDR">
|
||||
<rect key="frame" x="132" y="90" width="523" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="socket.127.0.0.1:23 or /path/to/file" drawsBackground="YES" id="6d8-LZ-wh1">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="socket.127.0.0.1:23 or /path/to/file" drawsBackground="YES" usesSingleLineMode="YES" id="6d8-LZ-wh1">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="value" keyPath="self.mameBitBanger" id="O76-B6-FhP">
|
||||
<binding destination="-2" name="enabled" keyPath="self.mameBitBanger" id="tcz-38-yaz"/>
|
||||
<binding destination="-2" name="value" keyPath="self.mameBitBangerPath" id="jLz-NS-hFB">
|
||||
<dictionary key="options">
|
||||
<string key="NSNullPlaceholder">socket.127.0.0.1:23 or /path/to/file</string>
|
||||
</dictionary>
|
||||
@ -394,37 +397,42 @@ DQ
|
||||
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="VDW-FX-9az">
|
||||
<rect key="frame" x="132" y="59" width="523" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="/path/to/directory/" drawsBackground="YES" id="ykl-aj-n1L">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="/path/to/directory/" drawsBackground="YES" usesSingleLineMode="YES" id="ykl-aj-n1L">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="value" keyPath="self.mameShareDirectory" id="fqw-G0-t5P">
|
||||
<binding destination="-2" name="enabled" keyPath="self.mameShareDirectory" id="bPv-hU-XgK"/>
|
||||
<binding destination="-2" name="value" keyPath="self.mameShareDirectoryPath" id="ddj-BN-tsY">
|
||||
<dictionary key="options">
|
||||
<string key="NSNullPlaceholder">/path/to/directory/</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="uPk-SB-lIF">
|
||||
<rect key="frame" x="15" y="92" width="71" height="16"/>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="qNn-Lq-1lv">
|
||||
<rect key="frame" x="15" y="91" width="81" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="Bit Banger:" id="9jA-4x-Abr">
|
||||
<buttonCell key="cell" type="check" title="Bitbanger" bezelStyle="regularSquare" imagePosition="left" inset="2" id="EEl-hT-bOK">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="upA-x5-LVd">
|
||||
<rect key="frame" x="15" y="61" width="103" height="16"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="value" keyPath="self.mameBitBanger" id="fTq-ke-gcc"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="1fA-nr-wns">
|
||||
<rect key="frame" x="15" y="60" width="117" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="Share Directory:" id="RmQ-pY-o5K">
|
||||
<buttonCell key="cell" type="check" title="Share Directory" bezelStyle="regularSquare" imagePosition="left" inset="2" id="xmA-1v-wU7">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="value" keyPath="self.mameShareDirectory" id="x3D-ho-u4v"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
</view>
|
||||
</tabViewItem>
|
||||
@ -487,6 +495,78 @@ DQ
|
||||
<viewController title="Media View" nibName="MediaView" id="t7c-zy-czN" customClass="MediaViewController"/>
|
||||
<viewController title="Slot View" nibName="SlotView" id="lyS-mc-3Tf" customClass="SlotViewController"/>
|
||||
<customObject id="RgH-d9-xl8" customClass="MachineViewController"/>
|
||||
<window title="Add Bookmark" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="yIt-hP-HBq">
|
||||
<windowStyleMask key="styleMask" titled="YES"/>
|
||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||
<rect key="contentRect" x="196" y="240" width="462" height="116"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
|
||||
<view key="contentView" wantsLayer="YES" id="WVd-g4-Tbl">
|
||||
<rect key="frame" x="0.0" y="0.0" width="462" height="116"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Gho-Xy-rn4">
|
||||
<rect key="frame" x="358" y="13" width="90" height="32"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="push" title="Add" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Vxo-rD-aMe">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<string key="keyEquivalent" base64-UTF8="YES">
|
||||
DQ
|
||||
</string>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="bookmarkSave:" target="-2" id="wLF-LE-5RO"/>
|
||||
<binding destination="-2" name="enabled" keyPath="bookmarkName" id="U5R-gU-1bE">
|
||||
<dictionary key="options">
|
||||
<string key="NSValueTransformerName">StringNotEmptyTransformer</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="3sv-Kd-Ep5">
|
||||
<rect key="frame" x="268" y="13" width="90" height="32"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="jaa-RY-PAg">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<string key="keyEquivalent" base64-UTF8="YES">
|
||||
Gw
|
||||
</string>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="bookmarkCancel:" target="-2" id="uWM-vs-hz7"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="1fO-x7-Gft">
|
||||
<rect key="frame" x="18" y="80" width="103" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="Add Bookmark…" id="AZS-JR-SNt">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fAl-Vn-x1w">
|
||||
<rect key="frame" x="20" y="49" width="422" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="bookmark name" drawsBackground="YES" id="Enq-2z-cle">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="value" keyPath="bookmarkName" id="84v-el-4Uk">
|
||||
<dictionary key="options">
|
||||
<bool key="NSContinuouslyUpdatesValue" value="YES"/>
|
||||
<string key="NSNullPlaceholder">bookmark name</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
</connections>
|
||||
</textField>
|
||||
</subviews>
|
||||
</view>
|
||||
<point key="canvasLocation" x="130" y="70"/>
|
||||
</window>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="NSAppleMenuImage" width="11" height="14"/>
|
||||
|
@ -19,6 +19,11 @@
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
|
||||
<customObject id="sJn-ug-xF3" customClass="BookmarkManager">
|
||||
<connections>
|
||||
<outlet property="menu" destination="ha0-nx-PIl" id="vTa-TU-DS1"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
|
||||
<items>
|
||||
<menuItem title="Ample" id="1Xt-HY-uBw">
|
||||
@ -389,6 +394,22 @@
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Bookmarks" id="vHO-2e-qJc">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Bookmarks" id="ha0-nx-PIl">
|
||||
<items>
|
||||
<menuItem title="Add Bookmark…" keyEquivalent="d" id="1JF-xV-zmG">
|
||||
<connections>
|
||||
<action selector="addBookmark:" target="-1" id="cqp-ko-BfQ"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Manage Bookmarks…" id="isI-q6-b1V">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="gWR-Yl-mg6"/>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Window" id="aUF-d1-5bR">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
|
||||
|
34
Ample/BookmarkManager.h
Normal file
34
Ample/BookmarkManager.h
Normal file
@ -0,0 +1,34 @@
|
||||
//
|
||||
// BookmarkManager.h
|
||||
// Ample
|
||||
//
|
||||
// Created by Kelvin Sherlock on 6/1/2021.
|
||||
// Copyright © 2021 Kelvin Sherlock. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class NSMenu;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface BookmarkManager : NSObject
|
||||
|
||||
@property (weak) IBOutlet NSMenu *menu;
|
||||
|
||||
+(instancetype)sharedManager;
|
||||
|
||||
-(void)loadBookmarks;
|
||||
-(void)updateMenu;
|
||||
|
||||
-(BOOL)validateName: (NSString *)name;
|
||||
|
||||
-(BOOL)saveBookmark: (NSDictionary *)bookmark name: (NSString *)name;
|
||||
-(NSDictionary *)loadBookmarkFromURL: (NSURL *)url;
|
||||
|
||||
-(BOOL)saveDefault: (NSDictionary *)bookmark;
|
||||
-(NSDictionary *)loadDefault;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
214
Ample/BookmarkManager.m
Normal file
214
Ample/BookmarkManager.m
Normal file
@ -0,0 +1,214 @@
|
||||
//
|
||||
// BookmarkManager.m
|
||||
// Ample
|
||||
//
|
||||
// Created by Kelvin Sherlock on 6/1/2021.
|
||||
// Copyright © 2021 Kelvin Sherlock. All rights reserved.
|
||||
//
|
||||
|
||||
#import "BookmarkManager.h"
|
||||
#import "Ample.h"
|
||||
|
||||
@interface BookmarkManager () {
|
||||
NSArray<NSURL *> *_urls;
|
||||
NSURL *_bookmarkDirectory;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation BookmarkManager
|
||||
|
||||
static BookmarkManager *singleton = nil;
|
||||
|
||||
-(void)awakeFromNib {
|
||||
if (!singleton) singleton = self;
|
||||
}
|
||||
|
||||
+(instancetype)sharedManager {
|
||||
if (!singleton) singleton = [BookmarkManager new];
|
||||
return singleton;
|
||||
}
|
||||
|
||||
-(instancetype)init {
|
||||
if (singleton) return singleton;
|
||||
return [super init];
|
||||
}
|
||||
|
||||
-(NSURL *)bookmarkDirectory {
|
||||
|
||||
if (_bookmarkDirectory) return _bookmarkDirectory;
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
|
||||
NSURL *url = SupportDirectory();
|
||||
url = [url URLByAppendingPathComponent: @"Bookmarks"];
|
||||
NSError *error = nil;
|
||||
[fm createDirectoryAtURL: url withIntermediateDirectories: YES attributes: nil error: &error];
|
||||
if (error) NSLog(@"%@", error);
|
||||
_bookmarkDirectory = url;
|
||||
return url;
|
||||
}
|
||||
|
||||
/* disallow leading .
|
||||
* disallow : or / characters.
|
||||
*/
|
||||
-(BOOL)validateName: (NSString *)name {
|
||||
|
||||
enum { kMaxLength = 128 };
|
||||
unichar buffer[kMaxLength];
|
||||
NSUInteger length = [name length];
|
||||
if (length == 0 || length > kMaxLength) return NO;
|
||||
[name getCharacters: buffer range: NSMakeRange(0, length)];
|
||||
if (buffer[0] == '.') return NO;
|
||||
for (unsigned i = 0; i < length; ++i) {
|
||||
unichar c = buffer[i];
|
||||
if (c == ':' || c == '/') return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
-(NSDictionary *)loadDefault {
|
||||
NSURL *url = [self bookmarkDirectory];
|
||||
url = [url URLByAppendingPathComponent: @".Default"];
|
||||
|
||||
NSDictionary *d;
|
||||
|
||||
if (@available(macOS 10.13, *)) {
|
||||
NSError *error = nil;
|
||||
d = [NSDictionary dictionaryWithContentsOfURL: url error: &error];
|
||||
if (!d) NSLog(@"Error loading %@: %@", url, error);
|
||||
} else {
|
||||
d = [NSDictionary dictionaryWithContentsOfURL: url];
|
||||
if (!d) NSLog(@"Error loading %@", url);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
/* save as .Default */
|
||||
-(BOOL)saveDefault: (NSDictionary *)bookmark {
|
||||
|
||||
NSURL *url = [self bookmarkDirectory];
|
||||
url = [url URLByAppendingPathComponent: @".Default"];
|
||||
|
||||
NSError *error = nil;
|
||||
BOOL ok = NO;
|
||||
if (@available(macOS 10.13, *)) {
|
||||
ok = [bookmark writeToURL: url error: &error];
|
||||
if (!ok) NSLog(@"%@", error);
|
||||
} else {
|
||||
ok = [bookmark writeToURL: url atomically: YES];
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
-(BOOL)saveBookmark: (NSDictionary *)bookmark name: (NSString *)name {
|
||||
|
||||
NSError *error;
|
||||
NSData *data = [NSPropertyListSerialization dataWithPropertyList: bookmark
|
||||
format: NSPropertyListXMLFormat_v1_0
|
||||
options: 0
|
||||
error: &error];
|
||||
|
||||
|
||||
|
||||
NSURL *base = [self bookmarkDirectory];
|
||||
|
||||
NSURL *url = [base URLByAppendingPathComponent: name];
|
||||
|
||||
BOOL ok = [data writeToURL: url options: NSDataWritingWithoutOverwriting error: &error];
|
||||
|
||||
if (!ok) {
|
||||
for (unsigned i = 1 ; i < 100; ++i) {
|
||||
NSString *tmp = [name stringByAppendingFormat: @"(%d)", i];
|
||||
[base URLByAppendingPathComponent: tmp];
|
||||
|
||||
ok = [data writeToURL: url options: NSDataWritingWithoutOverwriting error: &error];
|
||||
if (ok) {
|
||||
name = tmp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ok) return NO;
|
||||
|
||||
if (!_menu) return YES; // ?
|
||||
|
||||
NSUInteger ix = [_urls indexOfObjectPassingTest: ^BOOL(NSURL *object, NSUInteger index, BOOL *stop){
|
||||
NSString *path = [object lastPathComponent];
|
||||
return [name caseInsensitiveCompare: path] == NSOrderedAscending;
|
||||
}];
|
||||
|
||||
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle: name action: @selector(bookmarkMenu:) keyEquivalent: @""];
|
||||
[item setRepresentedObject: url];
|
||||
|
||||
if (ix == NSNotFound) {
|
||||
_urls = [_urls arrayByAddingObject: url];
|
||||
[_menu addItem: item];
|
||||
} else {
|
||||
|
||||
NSInteger n = [_menu numberOfItems];
|
||||
[_menu insertItem: item atIndex: n - [_urls count] + ix];
|
||||
NSMutableArray *tmp = [_urls mutableCopy];
|
||||
|
||||
[tmp insertObject: url atIndex: ix];
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
-(NSDictionary *)loadBookmarkFromURL: (NSURL *)url {
|
||||
|
||||
NSDictionary *d;
|
||||
|
||||
if (@available(macOS 10.13, *)) {
|
||||
NSError *error = nil;
|
||||
d = [NSDictionary dictionaryWithContentsOfURL: url error: &error];
|
||||
if (!d) NSLog(@"Error loading %@: %@", url, error);
|
||||
} else {
|
||||
d = [NSDictionary dictionaryWithContentsOfURL: url];
|
||||
if (!d) NSLog(@"Error loading %@", url);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
-(void)loadBookmarks {
|
||||
|
||||
NSURL *url = [self bookmarkDirectory];
|
||||
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
|
||||
NSError *error = nil;
|
||||
|
||||
NSArray *files = [fm contentsOfDirectoryAtURL: url
|
||||
includingPropertiesForKeys: nil
|
||||
options: NSDirectoryEnumerationSkipsHiddenFiles
|
||||
error: &error];
|
||||
|
||||
// bleh, has to create 2 new NSStrings for every comparison
|
||||
files = [files sortedArrayUsingComparator: ^(NSURL *a, NSURL *b){
|
||||
NSString *aa = [a lastPathComponent];
|
||||
NSString *bb = [b lastPathComponent];
|
||||
return [aa caseInsensitiveCompare: bb];
|
||||
}];
|
||||
|
||||
|
||||
_urls = files;
|
||||
}
|
||||
|
||||
-(void)updateMenu {
|
||||
|
||||
NSArray *menus = [_menu itemArray];
|
||||
for (NSMenuItem *item in [menus reverseObjectEnumerator]) {
|
||||
if ([item tag] == 0xdeadbeef) [_menu removeItem: item];
|
||||
}
|
||||
for (NSURL *url in _urls) {
|
||||
NSString *title = [url lastPathComponent]; // [[url lastPathComponent] stringByDeletingPathExtension];
|
||||
|
||||
NSMenuItem *item = [_menu addItemWithTitle: title action: @selector(bookmarkMenu:) keyEquivalent: @""];
|
||||
[item setRepresentedObject: url];
|
||||
[item setTag: 0xdeadbeef];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
@ -50,7 +50,7 @@
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2020 Kelvin Sherlock. All rights reserved.</string>
|
||||
<string>Copyright © 2020-2021 Kelvin Sherlock. All rights reserved.</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#import "AutocompleteControl.h"
|
||||
#import "SoftwareList.h"
|
||||
#import "BookmarkManager.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <wctype.h>
|
||||
@ -23,7 +24,9 @@ static NSString *kMyContext = @"kMyContext";
|
||||
static NSString *kContextMachine = @"kContextMachine";
|
||||
|
||||
|
||||
@interface LaunchWindowController ()
|
||||
@interface LaunchWindowController () {
|
||||
BOOL _loadingBookmark;
|
||||
}
|
||||
@property (strong) IBOutlet MediaViewController *mediaController;
|
||||
@property (strong) IBOutlet SlotViewController *slotController;
|
||||
@property (strong) IBOutlet MachineViewController *machineViewController;
|
||||
@ -41,20 +44,22 @@ static NSString *kContextMachine = @"kContextMachine";
|
||||
@property BOOL mameSquarePixels;
|
||||
@property BOOL mameMouse;
|
||||
@property BOOL mameSamples;
|
||||
@property BOOL mameBGFX;
|
||||
|
||||
@property BOOL mameAVI;
|
||||
@property BOOL mameWAV;
|
||||
@property BOOL mameVGM;
|
||||
@property BOOL mameBitBanger;
|
||||
@property BOOL mameShareDirectory;
|
||||
|
||||
@property NSString *mameAVIPath;
|
||||
@property NSString *mameWAVPath;
|
||||
@property NSString *mameVGMPath;
|
||||
@property NSString *mameShareDirectory;
|
||||
@property NSString *mameBitBanger;
|
||||
@property NSString *mameShareDirectoryPath;
|
||||
@property NSString *mameBitBangerPath;
|
||||
|
||||
@property NSInteger mameSpeed;
|
||||
|
||||
@property BOOL mameBGFX;
|
||||
@property NSInteger mameBackend;
|
||||
@property NSInteger mameEffects;
|
||||
|
||||
@ -64,6 +69,12 @@ static NSString *kContextMachine = @"kContextMachine";
|
||||
@property (weak) IBOutlet AutocompleteControl *softwareListControl;
|
||||
@property SoftwareSet *softwareSet;
|
||||
@property Software *software;
|
||||
|
||||
|
||||
|
||||
@property (strong) IBOutlet NSWindow *addBookmarkWindow;
|
||||
@property (strong) NSString *bookmarkName;
|
||||
@property (weak) IBOutlet NSTextField *bookmarkTextField;
|
||||
@end
|
||||
|
||||
@interface LaunchWindowController (SoftwareList)
|
||||
@ -72,17 +83,84 @@ static NSString *kContextMachine = @"kContextMachine";
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface LaunchWindowController (Bookmark)
|
||||
|
||||
-(IBAction)addBookmark:(id)sender;
|
||||
|
||||
@end
|
||||
|
||||
#define SIZEOF(x) (sizeof(x) / sizeof(x[0]))
|
||||
static NSString *BackendStrings[] = {
|
||||
@"",
|
||||
@"metal",
|
||||
@"opengl",
|
||||
};
|
||||
|
||||
static NSString *EffectsStrings[] = {
|
||||
@"-",
|
||||
@"unfiltered",
|
||||
@"hlsl",
|
||||
@"crt-geom",
|
||||
@"crt-geom-deluxe",
|
||||
@"lcd-grid",
|
||||
};
|
||||
|
||||
|
||||
static int BackEndIndex(NSString *str) {
|
||||
if (!str) return -1;
|
||||
for (int i = 1; i < SIZEOF(BackendStrings); ++i) {
|
||||
if ([str isEqualToString: BackendStrings[i]]) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int EffectsIndex(NSString *str) {
|
||||
if (!str) return -1;
|
||||
for (int i = 1; i < SIZEOF(EffectsStrings); ++i) {
|
||||
if ([str isEqualToString: EffectsStrings[i]]) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@implementation LaunchWindowController
|
||||
|
||||
-(NSString *)windowNibName {
|
||||
return @"LaunchWindow";
|
||||
}
|
||||
|
||||
-(void)windowWillLoad {
|
||||
-(void)reset {
|
||||
[self setMameMachine: nil];
|
||||
|
||||
[self setMameSpeed: 1];
|
||||
[self setMameBGFX: YES];
|
||||
[self setMameMouse: NO];
|
||||
[self setMameSamples: YES];
|
||||
[self setMameSquarePixels: NO];
|
||||
[self setMameDebug: NO];
|
||||
|
||||
[self setMameBackend: 0];
|
||||
[self setMameEffects: 0];
|
||||
|
||||
[self setMameBitBangerPath: nil];
|
||||
[self setMameShareDirectoryPath: nil];
|
||||
[self setMameAVIPath: nil];
|
||||
[self setMameWAVPath: nil];
|
||||
[self setMameVGMPath: nil];
|
||||
|
||||
[self setMameAVI: NO];
|
||||
[self setMameWAV: NO];
|
||||
[self setMameVGM: NO];
|
||||
[self setMameBitBanger: NO];
|
||||
[self setMameShareDirectory: NO];
|
||||
|
||||
[self setSoftware: nil];
|
||||
}
|
||||
|
||||
-(void)windowWillLoad {
|
||||
|
||||
[self reset];
|
||||
}
|
||||
|
||||
- (void)windowDidLoad {
|
||||
@ -97,14 +175,16 @@ static NSString *kContextMachine = @"kContextMachine";
|
||||
|
||||
|
||||
NSArray *keys = @[
|
||||
@"mameMachine", @"mameSquarePixels", @"mameWindowMode",
|
||||
//@"mameMachine", // - handled
|
||||
@"mameSquarePixels", @"mameWindowMode",
|
||||
@"mameMouse", @"mameSamples",
|
||||
@"mameDebug",
|
||||
@"mameSpeed",
|
||||
@"mameAVI", @"mameAVIPath",
|
||||
@"mameWAV", @"mameWAVPath",
|
||||
@"mameVGM", @"mameVGMPath",
|
||||
@"mameShareDirectory", @"mameBitBanger",
|
||||
@"mameShareDirectory", @"mameShareDirectoryPath",
|
||||
@"mameBitBanger", @"mameBitBangerPath",
|
||||
@"mameBGFX", @"mameBackend", @"mameEffects",
|
||||
@"software",
|
||||
];
|
||||
@ -132,8 +212,11 @@ static NSString *kContextMachine = @"kContextMachine";
|
||||
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
|
||||
|
||||
if (context == (__bridge void *)kMyContext) {
|
||||
if (_loadingBookmark) return;
|
||||
[self buildCommandLine];
|
||||
} else if (context == (__bridge void *)kContextMachine) {
|
||||
if (_loadingBookmark) return;
|
||||
|
||||
NSString *machine = [_machineViewController machine];
|
||||
[self setMameMachine: machine];
|
||||
[_slotController setMachine: machine];
|
||||
@ -298,11 +381,8 @@ static NSString *ShellQuote(NSString *s) {
|
||||
[argv addObject: _mameMachine];
|
||||
|
||||
if (_software) {
|
||||
// todo -- need to include source as well.
|
||||
NSString *name = [_software name];
|
||||
if (![_softwareSet nameIsUnique: name])
|
||||
name = [_software fullName];
|
||||
[argv addObject: name];
|
||||
NSString *name = [_softwareSet nameForSoftware: _software];
|
||||
if (name) [argv addObject: name];
|
||||
}
|
||||
|
||||
// -confirm_quit?
|
||||
@ -365,25 +445,12 @@ static NSString *ShellQuote(NSString *s) {
|
||||
|
||||
if (_mameBGFX) {
|
||||
if (_mameBackend) {
|
||||
static NSString *Names[] = {
|
||||
@"-",
|
||||
@"metal",
|
||||
@"opengl",
|
||||
};
|
||||
[argv addObject: @"-bgfx_backend"];
|
||||
[argv addObject: Names[_mameBackend]];
|
||||
[argv addObject: BackendStrings[_mameBackend]];
|
||||
}
|
||||
if (_mameEffects) {
|
||||
static NSString *Names[] = {
|
||||
@"-",
|
||||
@"unfiltered",
|
||||
@"hlsl",
|
||||
@"crt-geom",
|
||||
@"crt-geom-deluxe",
|
||||
@"lcd-grid",
|
||||
};
|
||||
[argv addObject: @"-bgfx_screen_chains"];
|
||||
[argv addObject: Names[_mameEffects]];
|
||||
[argv addObject: EffectsStrings[_mameEffects]];
|
||||
}
|
||||
|
||||
} else {
|
||||
@ -433,14 +500,14 @@ static NSString *ShellQuote(NSString *s) {
|
||||
}
|
||||
}
|
||||
|
||||
if (_mameShareDirectory && [_mameShareDirectory length]) {
|
||||
if (_mameShareDirectory && [_mameShareDirectoryPath length]) {
|
||||
[argv addObject: @"-share_directory"];
|
||||
[argv addObject: _mameShareDirectory];
|
||||
[argv addObject: _mameShareDirectoryPath];
|
||||
}
|
||||
|
||||
if (_mameBitBanger && [_mameBitBanger length]) {
|
||||
if (_mameBitBanger && [_mameBitBangerPath length]) {
|
||||
[argv addObject: @"-bitbanger"];
|
||||
[argv addObject: _mameBitBanger];
|
||||
[argv addObject: _mameBitBangerPath];
|
||||
}
|
||||
|
||||
[self setCommandLine: JoinArguments(argv, nil)];
|
||||
@ -453,7 +520,12 @@ static NSString *ShellQuote(NSString *s) {
|
||||
if (cmd == @selector(exportShellScript:)) {
|
||||
return [_args count] ? YES : NO;
|
||||
}
|
||||
return [super validateMenuItem: menuItem];
|
||||
if (cmd == @selector(addBookmark:)) {
|
||||
return _mameMachine ? YES : NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
//return [super validateMenuItem: menuItem]; // not implemented?
|
||||
}
|
||||
|
||||
# pragma mark - IBActions
|
||||
@ -512,6 +584,14 @@ static NSString *ShellQuote(NSString *s) {
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
-(IBAction)reset:(id)sender {
|
||||
|
||||
[self reset];
|
||||
[_slotController resetSlots: sender];
|
||||
[_mediaController reset: sender];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@ -541,3 +621,207 @@ static NSString *ShellQuote(NSString *s) {
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation LaunchWindowController (Bookmark)
|
||||
|
||||
-(IBAction)addBookmark:(id)sender {
|
||||
|
||||
if (!_mameMachine) return;
|
||||
|
||||
NSString *name = nil;
|
||||
NSDictionary *d = MameMachine(_mameMachine);
|
||||
if (d) name = [d objectForKey:@"description"];
|
||||
if (!name) name = _mameMachine;
|
||||
|
||||
|
||||
if (_software) {
|
||||
name = [name stringByAppendingFormat: @" - %@", [_software title]];
|
||||
}
|
||||
[self setBookmarkName: name];
|
||||
[_bookmarkTextField selectText: nil];
|
||||
[[self window] beginSheet: _addBookmarkWindow completionHandler: nil];
|
||||
}
|
||||
|
||||
-(IBAction)bookmarkCancel:(id)sender {
|
||||
[[self window] endSheet: _addBookmarkWindow];
|
||||
[_addBookmarkWindow orderOut: nil];
|
||||
}
|
||||
|
||||
-(IBAction)bookmarkSave:(id)sender {
|
||||
|
||||
|
||||
BookmarkManager *bm = [BookmarkManager sharedManager];
|
||||
|
||||
if (![bm validateName: _bookmarkName]) {
|
||||
[_bookmarkTextField selectText: nil];
|
||||
NSBeep();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//NSLog(@"%@", _bookmarkName);
|
||||
NSDictionary *d = [self makeBookmark];
|
||||
//NSLog(@"%@", d);
|
||||
|
||||
[bm saveBookmark: d name: _bookmarkName];
|
||||
|
||||
[[self window] endSheet: _addBookmarkWindow];
|
||||
[_addBookmarkWindow orderOut: nil];
|
||||
[self setBookmarkName: nil];
|
||||
}
|
||||
|
||||
|
||||
-(IBAction)bookmarkMenu:(id)sender {
|
||||
|
||||
Class StringClass = [NSString class];
|
||||
Class NumberClass = [NSNumber class];
|
||||
|
||||
NSURL *url = [sender representedObject];
|
||||
if (!url) return;
|
||||
|
||||
NSDictionary *d = [NSDictionary dictionaryWithContentsOfURL: url];
|
||||
if (!d) return; // oops...
|
||||
|
||||
NSString *machine = [d objectForKey: @"machine"];
|
||||
if (!machine) return;
|
||||
|
||||
_loadingBookmark = YES;
|
||||
[_machineViewController willLoadBookmark: d];
|
||||
[_slotController willLoadBookmark: d];
|
||||
[_mediaController willLoadBookmark: d];
|
||||
|
||||
[self reset];
|
||||
|
||||
[self setMameMachine: machine];
|
||||
[self updateSoftwareList];
|
||||
[_softwareListControl setObjectValue: nil]; // will reload the completion list.
|
||||
|
||||
NSString *str;
|
||||
|
||||
str = [d objectForKey: @"software"];
|
||||
if ([str isKindOfClass: StringClass]) {
|
||||
Software *s = [_softwareSet softwareForName: str];
|
||||
if (s) {
|
||||
[_softwareListControl setObjectValue: s];
|
||||
[self setSoftware: s];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Boolean values.
|
||||
NSNumber *n;
|
||||
#define _(a,b) n = [d objectForKey: a]; if ([n isKindOfClass: NumberClass]) [self b : [n boolValue]]
|
||||
|
||||
_(@"debug", setMameDebug);
|
||||
_(@"squarePixels", setMameSquarePixels);
|
||||
_(@"mouse", setMameMouse);
|
||||
_(@"samples", setMameSamples);
|
||||
_(@"bgfx", setMameBGFX);
|
||||
|
||||
// numeric values
|
||||
// check if in range?
|
||||
#undef _
|
||||
#define _(a,b) n = [d objectForKey: a]; if ([n isKindOfClass: NumberClass]) [self b : [n intValue]]
|
||||
|
||||
_(@"windowMode", setMameWindowMode);
|
||||
_(@"speed", setMameSpeed);
|
||||
|
||||
|
||||
// string values
|
||||
#undef _
|
||||
#define _(a,b) str = [d objectForKey: a]; if ([str isKindOfClass: StringClass]) [self b : str]
|
||||
|
||||
_(@"shareDirectory", setMameShareDirectoryPath);
|
||||
_(@"bitBanger", setMameBitBangerPath);
|
||||
if ([_mameShareDirectoryPath length]) [self setMameShareDirectory: YES];
|
||||
if ([_mameBitBangerPath length]) [self setMameBitBanger: YES];
|
||||
|
||||
_(@"AVIPath", setMameAVIPath);
|
||||
_(@"WAVPath", setMameWAVPath);
|
||||
_(@"VGMPath", setMameVGMPath);
|
||||
if ([_mameAVIPath length]) [self setMameAVI: YES];
|
||||
if ([_mameVGMPath length]) [self setMameVGM: YES];
|
||||
if ([_mameWAVPath length]) [self setMameWAV: YES];
|
||||
|
||||
|
||||
str = [d objectForKey: @"backend"];
|
||||
if ([str isKindOfClass: [NSString class]]) {
|
||||
int ix = BackEndIndex(str);
|
||||
if (ix >= 0) [self setMameBackend: ix];
|
||||
}
|
||||
|
||||
str = [d objectForKey: @"effects"];
|
||||
if ([str isKindOfClass: [NSString class]]) {
|
||||
int ix = EffectsIndex(str);
|
||||
if (ix >= 0) [self setMameEffects: ix];
|
||||
}
|
||||
|
||||
[_machineViewController loadBookmark: d];
|
||||
[_slotController loadBookmark: d];
|
||||
[_mediaController loadBookmark: d];
|
||||
|
||||
[_machineViewController didLoadBookmark: d];
|
||||
[_slotController didLoadBookmark: d];
|
||||
[_mediaController didLoadBookmark: d];
|
||||
|
||||
_loadingBookmark = NO;
|
||||
|
||||
[self buildCommandLine];
|
||||
}
|
||||
|
||||
-(NSDictionary *)makeBookmark {
|
||||
|
||||
[[self window] makeFirstResponder: nil];
|
||||
|
||||
NSMutableDictionary *dict = [NSMutableDictionary new];
|
||||
|
||||
[dict setObject: _mameMachine forKey: @"machine"];
|
||||
[dict setObject: @232 forKey: @"version"];
|
||||
[_machineViewController saveBookmark: dict];
|
||||
[_slotController saveBookmark: dict];
|
||||
[_mediaController saveBookmark: dict];
|
||||
|
||||
|
||||
// Boolean values
|
||||
#undef _
|
||||
#define _(v,k) [dict setObject: v ? (NSObject *)kCFBooleanTrue : (NSObject *)kCFBooleanFalse forKey: k]
|
||||
|
||||
_(_mameDebug, @"debug");
|
||||
_(_mameSquarePixels, @"squarePixels");
|
||||
_(_mameMouse, @"mouse");
|
||||
_(_mameSamples, @"samples");
|
||||
_(_mameBGFX, @"bgfx");
|
||||
|
||||
// numeric values
|
||||
#undef _
|
||||
#define _(v,k) [dict setObject: @(v) forKey: k]
|
||||
_(_mameWindowMode, @"windowMode");
|
||||
_(_mameSpeed, @"speed");
|
||||
|
||||
// String values
|
||||
#undef _
|
||||
#define _(v,k) [dict setObject: v forKey: k]
|
||||
|
||||
if (_mameAVI && [_mameAVIPath length]) _(_mameAVIPath, @"AVIPath");
|
||||
if (_mameWAV && [_mameWAVPath length]) _(_mameWAVPath, @"WAVPath");
|
||||
if (_mameVGM && [_mameVGMPath length]) _(_mameVGMPath, @"VGMPath");
|
||||
|
||||
if (_mameShareDirectory && [_mameShareDirectoryPath length]) _(_mameShareDirectoryPath, @"shareDirectory");
|
||||
if (_mameBitBanger && [_mameBitBangerPath length]) _(_mameBitBangerPath, @"bitBanger");
|
||||
|
||||
|
||||
if (_software) _([_software fullName], @"software");
|
||||
|
||||
|
||||
if (_mameBackend) _(BackendStrings[_mameBackend], @"backend");
|
||||
if (_mameEffects) _(EffectsStrings[_mameEffects], @"effects");
|
||||
|
||||
|
||||
return dict;
|
||||
|
||||
#undef _
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "Ample.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@ -17,4 +18,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@end
|
||||
|
||||
@interface MachineViewController (Bookmark) <Bookmark>
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
@ -110,5 +110,67 @@
|
||||
return [data count];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MachineViewController (Bookmark)
|
||||
|
||||
-(BOOL)loadBookmark: (NSDictionary *)bookmark {
|
||||
|
||||
NSBrowser *browser = (NSBrowser *)[self view];
|
||||
NSString *machine = [bookmark objectForKey: @"machine"];
|
||||
|
||||
NSIndexPath *path = nil;
|
||||
NSUInteger ix[2] = {0, 0 };
|
||||
for (NSDictionary *d in _data) {
|
||||
|
||||
NSArray *children = [d objectForKey: @"children"];
|
||||
|
||||
for (NSDictionary *dd in children) {
|
||||
NSString *value = [dd objectForKey: @"value"];
|
||||
|
||||
if ([machine isEqualToString: value]) {
|
||||
path = [NSIndexPath indexPathWithIndexes: ix length: 2];
|
||||
[browser selectRow: ix[0] inColumn: 0];
|
||||
[browser selectRow: ix[1] inColumn: 1];
|
||||
|
||||
//[browser setSelectionIndexPath: path];
|
||||
return YES;
|
||||
}
|
||||
++ix[1];
|
||||
}
|
||||
ix[1] = 0;
|
||||
|
||||
|
||||
// check parent after.
|
||||
NSString *value = [d objectForKey: @"value"];
|
||||
if ([machine isEqualToString: value]) {
|
||||
path = [NSIndexPath indexPathWithIndexes: ix length: 1];
|
||||
[browser selectRow: ix[0] inColumn: 0];
|
||||
// "setSelectionIndexPath: is not supported for browsers with matrix delegates."
|
||||
//[browser setSelectionIndexPath: path];
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
++ix[0];
|
||||
|
||||
}
|
||||
NSLog(@"MachineViewController: Unable to find %@", machine);
|
||||
return NO;
|
||||
}
|
||||
|
||||
-(BOOL)saveBookmark: (NSMutableDictionary *)bookmark {
|
||||
// machine saved in parent.
|
||||
return YES;
|
||||
}
|
||||
|
||||
-(void)willLoadBookmark:(NSDictionary *)bookmark {
|
||||
}
|
||||
|
||||
-(void)didLoadBookmark:(NSDictionary *)bookmark {
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "Media.h"
|
||||
#import "Ample.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@ -20,8 +21,11 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
- (IBAction)ejectAction:(id)sender;
|
||||
- (IBAction)pathAction:(id)sender;
|
||||
|
||||
-(IBAction)reset:(id)sender;
|
||||
|
||||
//-(void)setMedia: (NSDictionary *)media;
|
||||
@end
|
||||
|
||||
@interface MediaViewController (Bookmark) <Bookmark>
|
||||
|
||||
@end
|
||||
|
||||
|
@ -262,6 +262,8 @@
|
||||
MediaCategory *_data[CATEGORY_COUNT];
|
||||
NSArray *_root;
|
||||
Media _media;
|
||||
|
||||
BOOL _loadingBookmark;
|
||||
}
|
||||
|
||||
@end
|
||||
@ -345,8 +347,10 @@ enum {
|
||||
// todo - switch this to use removeItemsAtIndexes:inParent:withAnimation:
|
||||
// and insertItemsAtIndexes:inParent:withAnimation:
|
||||
|
||||
[_outlineView reloadData];
|
||||
[_outlineView expandItem: nil expandChildren: YES];
|
||||
if (!_loadingBookmark) {
|
||||
[_outlineView reloadData];
|
||||
[_outlineView expandItem: nil expandChildren: YES];
|
||||
}
|
||||
}
|
||||
|
||||
-(void)setMedia: (Media)media {
|
||||
@ -372,7 +376,29 @@ x = media.name; cat = _data[index]; delta |= [cat setItemCount: x]
|
||||
|
||||
if (delta) {
|
||||
[self rebuildRoot];
|
||||
[self rebuildArgs];
|
||||
if (!_loadingBookmark) [self rebuildArgs];
|
||||
}
|
||||
}
|
||||
|
||||
-(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];
|
||||
NSURL *url = [item url];
|
||||
if (!url) continue;
|
||||
[item setUrl: nil];
|
||||
delta = YES;
|
||||
}
|
||||
}
|
||||
if (delta) {
|
||||
[self rebuildRoot];
|
||||
if (!_loadingBookmark) [self rebuildArgs];
|
||||
}
|
||||
}
|
||||
|
||||
@ -641,4 +667,87 @@ static NSString *kDragType = @"private.ample.media";
|
||||
|
||||
[self rebuildArgs];
|
||||
}
|
||||
|
||||
-(IBAction)reset:(id)sender {
|
||||
[self resetDiskImages];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MediaViewController (Bookmark)
|
||||
|
||||
-(void)willLoadBookmark:(NSDictionary *)bookmark {
|
||||
_loadingBookmark = YES;
|
||||
[self resetDiskImages];
|
||||
}
|
||||
-(void)didLoadBookmark:(NSDictionary *)bookmark {
|
||||
_loadingBookmark = NO;
|
||||
|
||||
|
||||
[self rebuildRoot];
|
||||
[self rebuildArgs];
|
||||
}
|
||||
|
||||
|
||||
-(BOOL)loadBookmark: (NSDictionary *)bookmark {
|
||||
|
||||
|
||||
// if order of indexes change, would need to do a version check.
|
||||
|
||||
NSArray *media = [bookmark objectForKey: @"media"];
|
||||
unsigned ix = 0;
|
||||
for (NSArray *a in 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++];
|
||||
NSURL *url = nil;
|
||||
if ([path length])
|
||||
url = [NSURL fileURLWithPath: path];
|
||||
|
||||
[item setUrl: url];
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
|
||||
}
|
||||
|
||||
-(BOOL)saveBookmark: (NSMutableDictionary *)bookmark {
|
||||
|
||||
NSMutableArray *media = [NSMutableArray arrayWithCapacity: CATEGORY_COUNT];
|
||||
|
||||
for (unsigned ix = 0; ix < CATEGORY_COUNT; ++ix) {
|
||||
|
||||
MediaCategory *cat = _data[ix];
|
||||
NSInteger count = [cat validCount];
|
||||
|
||||
NSMutableArray *array = [NSMutableArray new];
|
||||
for (NSInteger i = 0; i < count; ++i) {
|
||||
|
||||
MediaItem *item = [cat objectAtIndex: i];
|
||||
NSURL *url = [item url];
|
||||
NSString *s = @"";
|
||||
if (url)
|
||||
s = [NSString stringWithCString: [url fileSystemRepresentation] encoding: NSUTF8StringEncoding];
|
||||
|
||||
[array addObject: s];
|
||||
}
|
||||
[media addObject: array];
|
||||
}
|
||||
|
||||
[bookmark setObject: media forKey: @"media"];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
@ -56,20 +56,25 @@
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
function macbook(onoff) {
|
||||
/* os x 10.11 */
|
||||
if (window.NodeList && !NodeList.prototype.forEach) {
|
||||
NodeList.prototype.forEach = Array.prototype.forEach;
|
||||
}
|
||||
|
||||
function macbook(onoff) {
|
||||
var nodes = document.querySelectorAll("[data-macbook]");
|
||||
nodes.forEach( (e) => {
|
||||
nodes.forEach( function(e) {
|
||||
var m = (e.dataset.macbook == "true");
|
||||
e.hidden = m ^ onoff;
|
||||
});
|
||||
}
|
||||
window.addEventListener('load', (event)=> {
|
||||
window.addEventListener('load', function(event) {
|
||||
macbook(false);
|
||||
|
||||
var e = document.getElementById('macbook');
|
||||
if (e) {
|
||||
e.checked = false;
|
||||
e.addEventListener('click', (event) => {
|
||||
e.addEventListener('click', function (event) {
|
||||
macbook(e.checked);
|
||||
});
|
||||
}
|
||||
|
@ -27,7 +27,8 @@
|
||||
@property (readonly) SlotOption *selectedItem;
|
||||
|
||||
-(NSArray *)args;
|
||||
-(NSArray *)serialize;
|
||||
-(NSDictionary *)serialize;
|
||||
-(void)reserialize: (NSDictionary *)dict;
|
||||
|
||||
-(void)reset;
|
||||
-(void)prepareView: (SlotTableCellView *)view;
|
||||
|
63
Ample/Slot.m
63
Ample/Slot.m
@ -60,7 +60,10 @@ static NSArray *DeepCopyArray(NSArray *src) {
|
||||
-(void)setKeyPath: (NSString *)path;
|
||||
-(void)buildArgs: (NSMutableArray *)args;
|
||||
-(void)buildMedia: (Media *)media;
|
||||
-(void)buildSerial: (NSMutableArray *)array;
|
||||
-(void)buildSerial: (NSMutableDictionary *)array;
|
||||
|
||||
|
||||
-(void)reserialize: (NSDictionary *)dict;
|
||||
|
||||
//-(BOOL)loadDeviceSlots: (NSDictionary *)devices;
|
||||
|
||||
@ -138,15 +141,47 @@ static NSDictionary *IndexMap = nil;
|
||||
return rv;
|
||||
}
|
||||
|
||||
-(NSArray *)serialize {
|
||||
-(NSDictionary *)serialize {
|
||||
if (_selectedIndex < 0) return nil;
|
||||
|
||||
NSMutableArray *array = [NSMutableArray new];
|
||||
NSMutableDictionary *d = [NSMutableDictionary new];
|
||||
SlotOption *option = [_options objectAtIndex: _selectedIndex];
|
||||
[option buildSerial: array];
|
||||
return array;
|
||||
[option buildSerial: d];
|
||||
//if (![d count]) return nil; //?
|
||||
return d;
|
||||
}
|
||||
|
||||
-(void)reserialize: (NSDictionary *)dict {
|
||||
// { 'sl3' : 'uthernet' }
|
||||
|
||||
// special case for smartport since the name isn't used.
|
||||
if (_index == kSMARTPORT) {
|
||||
SlotOption *option = [_options objectAtIndex: _selectedIndex];
|
||||
[option reserialize: dict];
|
||||
return;
|
||||
}
|
||||
NSString *value = [dict objectForKey: _name];
|
||||
if (!value) {
|
||||
//[self reset];
|
||||
return;
|
||||
}
|
||||
// find it...
|
||||
BOOL found = NO;
|
||||
unsigned ix = 0;
|
||||
for (SlotOption *option in _options) {
|
||||
if ([value isEqualToString: [option value]]) {
|
||||
|
||||
[self setSelectedIndex: ix];
|
||||
[option reserialize: dict];
|
||||
found = YES;
|
||||
break;
|
||||
}
|
||||
++ix;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
-(Media)selectedMedia {
|
||||
|
||||
if (_selectedIndex < 0) return EmptyMedia;
|
||||
@ -457,14 +492,26 @@ https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/KeyVa
|
||||
}
|
||||
}
|
||||
|
||||
-(void)reserialize: (NSDictionary *)dict {
|
||||
|
||||
#if 0
|
||||
NSString *value = [dict objectForKey: _keyPath];
|
||||
if (value) {
|
||||
// don't need to do anything since set by slot.
|
||||
}
|
||||
#endif
|
||||
for (Slot *s in _children) {
|
||||
[s reserialize: dict];
|
||||
}
|
||||
}
|
||||
|
||||
-(void)buildSerial: (NSMutableArray *)array {
|
||||
-(void)buildSerial: (NSMutableDictionary *)dict {
|
||||
|
||||
if (!_default)
|
||||
[array addObject: _keyPath];
|
||||
[dict setObject: _value forKey: _keyPath];
|
||||
|
||||
for (Slot *s in _children)
|
||||
[[s selectedItem] buildSerial: array];
|
||||
[[s selectedItem] buildSerial: dict];
|
||||
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "Media.h"
|
||||
#import "Ample.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@ -16,14 +17,19 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@property NSArray *args;
|
||||
@property Media media;
|
||||
@property NSSize resolution;
|
||||
@property (nonatomic) NSString *machine;
|
||||
@property (nonatomic, nullable) NSString *machine;
|
||||
|
||||
-(IBAction)resetSlots:(nullable id)sender;
|
||||
|
||||
@end
|
||||
|
||||
@interface SlotViewController (OutlineView) <NSOutlineViewDelegate, NSOutlineViewDataSource>
|
||||
|
||||
@end
|
||||
|
||||
@interface SlotViewController (Bookmark) <Bookmark>
|
||||
|
||||
@end
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
@ -49,6 +49,7 @@ static unsigned RootKey = 0;
|
||||
|
||||
IBOutlet NSPopover *_popover;
|
||||
|
||||
BOOL _loadingBookmark;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
@ -142,8 +143,10 @@ static unsigned RootKey = 0;
|
||||
|
||||
|
||||
[_outlineView reloadData];
|
||||
[self rebuildMedia];
|
||||
[self rebuildArgs];
|
||||
if (!_loadingBookmark) {
|
||||
[self rebuildMedia];
|
||||
[self rebuildArgs];
|
||||
}
|
||||
}
|
||||
|
||||
-(void)setMachine: (NSString *)machine {
|
||||
@ -281,8 +284,10 @@ static unsigned RootKey = 0;
|
||||
#ifdef SLOT_TREE
|
||||
[_outlineView reloadData];
|
||||
#endif
|
||||
[self rebuildMedia];
|
||||
[self rebuildArgs];
|
||||
if (!_loadingBookmark) {
|
||||
[self rebuildMedia];
|
||||
[self rebuildArgs];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@ -338,5 +343,62 @@ static unsigned RootKey = 0;
|
||||
}
|
||||
|
||||
|
||||
@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
|
||||
|
@ -37,6 +37,10 @@
|
||||
+(instancetype)softwareSetForMachine: (NSString *)machine;
|
||||
-(BOOL)nameIsUnique: (NSString *)name;
|
||||
|
||||
-(NSString *)nameForSoftware: (Software *)software;
|
||||
-(Software *)softwareForName: (NSString *)name;
|
||||
|
||||
-(BOOL)hasSoftware: (Software *)software;
|
||||
@end
|
||||
|
||||
|
||||
|
@ -425,6 +425,44 @@ NSArray<SoftwareList *> *SoftwareListForMachine(NSString *machine) {
|
||||
return [_set countForObject: name] <= 1;
|
||||
}
|
||||
|
||||
-(NSString *)nameForSoftware: (Software *)software {
|
||||
|
||||
if (!software) return nil;
|
||||
if (!_set) [self buildSet];
|
||||
|
||||
NSString *name = [software name];
|
||||
if ([_set countForObject: name] > 1) {
|
||||
return [software fullName];
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
-(Software *)softwareForName: (NSString *)name {
|
||||
/* name will be name or set:name */
|
||||
NSString *set = nil;
|
||||
NSArray *tmp = [name componentsSeparatedByString: @":"];
|
||||
switch([tmp count]) {
|
||||
case 1: break;
|
||||
case 2:
|
||||
set = [tmp objectAtIndex: 0];
|
||||
name = [tmp objectAtIndex: 1];
|
||||
break;
|
||||
default: return nil;
|
||||
}
|
||||
|
||||
if (_set && ![_set containsObject: name]) return nil;
|
||||
|
||||
for (SoftwareList *list in _items) {
|
||||
if (set && ![set isEqualToString: [list name]]) continue;
|
||||
|
||||
for (Software *s in [list items]) {
|
||||
if ([name isEqualToString: [s name]]) return s;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
|
||||
}
|
||||
|
||||
+(instancetype)softwareSetForMachine:(NSString *)machine {
|
||||
|
||||
static NSCache *cache;
|
||||
@ -497,6 +535,24 @@ NSArray<SoftwareList *> *SoftwareListForMachine(NSString *machine) {
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
-(BOOL)hasSoftware: (Software *)software {
|
||||
|
||||
if (_set) {
|
||||
NSString *name = [software name];
|
||||
if (![_set containsObject: name]) return NO;
|
||||
}
|
||||
|
||||
NSString *slist = [software list];
|
||||
for (SoftwareList *list in _items) {
|
||||
if (![slist isEqualToString: [list name]]) continue;
|
||||
|
||||
return [[list items] containsObject: software];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
- (nonnull NSArray<id<AutocompleteItem>> *)autocomplete:(AutocompleteControl *)control completionsForItem:(id<AutocompleteItem>)item {
|
||||
|
||||
for (SoftwareList *list in _items) {
|
||||
@ -508,6 +564,15 @@ NSArray<SoftwareList *> *SoftwareListForMachine(NSString *machine) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
// NSStringTransformStripDiacritics
|
||||
// pre-process all entries to lowercase and remove diacritics (second string for search text?)
|
||||
#if 0
|
||||
static unichar diacritics[][2] = {
|
||||
{ 0xd8, 'O' }, // Ø
|
||||
{ 0xf8, 'o' }, // ø
|
||||
};
|
||||
#endif
|
||||
- (nonnull NSArray<id<AutocompleteItem>> *)autocomplete:(nonnull AutocompleteControl *)control completionsForString:(nonnull NSString *)string {
|
||||
|
||||
if (!_cache) {
|
||||
@ -515,7 +580,8 @@ NSArray<SoftwareList *> *SoftwareListForMachine(NSString *machine) {
|
||||
[_cache setCountLimit: 10];
|
||||
}
|
||||
|
||||
|
||||
// todo -- diacritic normalization.
|
||||
// déjá vu -> deja vu
|
||||
|
||||
enum { max_haystack_length = 256, max_needle_length = 256 };
|
||||
|
||||
@ -523,6 +589,8 @@ NSArray<SoftwareList *> *SoftwareListForMachine(NSString *machine) {
|
||||
|
||||
if (!_items) return @[];
|
||||
|
||||
//string = [string stringByApplyingTransform: NSStringTransformStripDiacritics reverse: NO];
|
||||
|
||||
NSUInteger needle_length = [string length];
|
||||
needle_length = MIN(needle_length, max_needle_length);
|
||||
|
||||
|
@ -28,11 +28,13 @@
|
||||
|
||||
-(void)viewDidMoveToSuperview {
|
||||
return;
|
||||
#if 0
|
||||
if (_trackingRect) {
|
||||
[self removeTrackingRect: _trackingRect];
|
||||
}
|
||||
NSRect rect = [_dragHandle frame];
|
||||
_trackingRect = [self addTrackingRect: rect owner: self userData: NULL assumeInside:NO];
|
||||
#endif
|
||||
}
|
||||
|
||||
-(void)mouseEntered:(NSEvent *)event {
|
||||
|
@ -21,5 +21,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@interface ValidColorTransformer : NSValueTransformer
|
||||
@end
|
||||
|
||||
@interface StringNotEmptyTransformer : NSValueTransformer
|
||||
@end
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
@ -73,6 +73,21 @@
|
||||
@end
|
||||
|
||||
|
||||
@implementation StringNotEmptyTransformer
|
||||
+ (BOOL)allowsReverseTransformation {
|
||||
return NO;
|
||||
}
|
||||
+ (Class)transformedValueClass {
|
||||
return [NSNumber class];
|
||||
}
|
||||
|
||||
- (id)transformedValue:(id)value {
|
||||
NSUInteger length = [(NSString *)value length];
|
||||
return [NSNumber numberWithBool: length ? YES : NO];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
void RegisterTransformers(void) {
|
||||
|
||||
@ -85,4 +100,8 @@ void RegisterTransformers(void) {
|
||||
|
||||
t = [ValidColorTransformer new];
|
||||
[NSValueTransformer setValueTransformer: t forName: @"ValidColorTransformer"];
|
||||
|
||||
t = [StringNotEmptyTransformer new];
|
||||
[NSValueTransformer setValueTransformer: t forName: @"StringNotEmptyTransformer"];
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user