mirror of https://github.com/ksherlock/ample.git
Squashed commit of the following:
commit 1dd1d6d3eee928d283dafbaf4432b053dff65ac2 Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Thu May 27 13:30:38 2021 -0400 0.232 rom update commit cfb7f9a4d1647c41fab3328bada0f37bdb041983 Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Wed May 26 20:31:16 2021 -0400 fix resizing to prevent horizontal scrolling commit 5c393ab5a95c9e53c4ef1e0b3f2b77db93e1c29b Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Wed May 26 20:01:48 2021 -0400 esc should close the menu if visible, otherwise clear the text. commit 7da142b64280c39e216584cedb7fb14243d573f0 Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Tue May 25 23:17:10 2021 -0400 show software short name unless it's ambiguous. commit 6f3b3bef5e18e78619959613e28064eb6251ce27 Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Tue May 25 23:16:47 2021 -0400 cleanup some auto complete rough edges commit 4988d8b266b8b11e519bcb5aab05ad2c3ffe5c23 Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Tue May 25 23:16:24 2021 -0400 fix commit 6d4bf94fd6b4850564b705ea15e91079b00346c2 Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Tue May 25 19:13:28 2021 -0400 clean up autocomplete a little... commit 3722271e51d93599903bccc721dc1de61f0aae72 Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Tue May 25 19:13:07 2021 -0400 move auto complete logic to SoftwareSet commit 04cc83ecbe9ee4c9100305712c47a11359327734 Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Mon May 24 20:46:39 2021 -0400 software - keep the list name to differentiate in case of name clashes. commit 8e5b5dc7e283f760a16011308298e999110fc052 Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Mon May 24 20:45:13 2021 -0400 Autocomplete - limit menu content width to the clip view content width. prevents horizontal scrolling. commit 356048d450447485bb33a4261018f85dac19a134 Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Mon May 24 18:45:42 2021 -0400 add auto complete software list to the launch window. commit 9dc72b702c9b36d490349f291c10b98b56178567 Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Mon May 24 18:44:28 2021 -0400 autocomplete control. commit 40eee83f56d957c64e0f8d927486f13165b9e120 Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Mon May 24 18:39:32 2021 -0400 add filters to software list. commit cebfbaa545441f061cffafe8ca93db2377240a06 Author: Kelvin Sherlock <ksherlock@gmail.com> Date: Sun May 23 11:59:00 2021 -0400 add compatibility field for software list filtering.
This commit is contained in:
parent
e48e2a9223
commit
3d4d0c92c7
|
@ -133,6 +133,10 @@
|
||||||
B66236A924FD9A34006CABD7 /* PreferencesWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = B66236A824FD9A34006CABD7 /* PreferencesWindowController.m */; };
|
B66236A924FD9A34006CABD7 /* PreferencesWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = B66236A824FD9A34006CABD7 /* PreferencesWindowController.m */; };
|
||||||
B66236B524FDA527006CABD7 /* SDL2.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B66236B224FDA522006CABD7 /* SDL2.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
B66236B524FDA527006CABD7 /* SDL2.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B66236B224FDA522006CABD7 /* SDL2.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
B66236C124FDB7A6006CABD7 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = B66236BF24FDB7A6006CABD7 /* Credits.rtf */; };
|
B66236C124FDB7A6006CABD7 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = B66236BF24FDB7A6006CABD7 /* Credits.rtf */; };
|
||||||
|
B6665C14265A0E3E00254939 /* AutocompleteControl.m in Sources */ = {isa = PBXBuildFile; fileRef = B6665C13265A0E3E00254939 /* AutocompleteControl.m */; };
|
||||||
|
B6665C15265A0E3E00254939 /* AutocompleteControl.m in Sources */ = {isa = PBXBuildFile; fileRef = B6665C13265A0E3E00254939 /* AutocompleteControl.m */; };
|
||||||
|
B6665C1C265C639A00254939 /* Autocomplete.xib in Resources */ = {isa = PBXBuildFile; fileRef = B6665C1A265C639900254939 /* Autocomplete.xib */; };
|
||||||
|
B6665C1D265C639A00254939 /* Autocomplete.xib in Resources */ = {isa = PBXBuildFile; fileRef = B6665C1A265C639900254939 /* Autocomplete.xib */; };
|
||||||
B66D0FE72611386C000902F1 /* SoftwareList.m in Sources */ = {isa = PBXBuildFile; fileRef = B66D0FE62611386B000902F1 /* SoftwareList.m */; };
|
B66D0FE72611386C000902F1 /* SoftwareList.m in Sources */ = {isa = PBXBuildFile; fileRef = B66D0FE62611386B000902F1 /* SoftwareList.m */; };
|
||||||
B66D0FE82611386C000902F1 /* SoftwareList.m in Sources */ = {isa = PBXBuildFile; fileRef = B66D0FE62611386B000902F1 /* SoftwareList.m */; };
|
B66D0FE82611386C000902F1 /* SoftwareList.m in Sources */ = {isa = PBXBuildFile; fileRef = B66D0FE62611386B000902F1 /* SoftwareList.m */; };
|
||||||
B6841BD7251EC926006A5C39 /* vmnet_helper.c in Sources */ = {isa = PBXBuildFile; fileRef = B6841BCA251EC88E006A5C39 /* vmnet_helper.c */; };
|
B6841BD7251EC926006A5C39 /* vmnet_helper.c in Sources */ = {isa = PBXBuildFile; fileRef = B6841BCA251EC88E006A5C39 /* vmnet_helper.c */; };
|
||||||
|
@ -423,6 +427,9 @@
|
||||||
B66236B224FDA522006CABD7 /* SDL2.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL2.framework; path = embedded/SDL2.framework; sourceTree = "<group>"; };
|
B66236B224FDA522006CABD7 /* SDL2.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL2.framework; path = embedded/SDL2.framework; sourceTree = "<group>"; };
|
||||||
B66236B824FDA698006CABD7 /* mame64 */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = mame64; path = embedded/mame64; sourceTree = "<group>"; };
|
B66236B824FDA698006CABD7 /* mame64 */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = mame64; path = embedded/mame64; sourceTree = "<group>"; };
|
||||||
B66236C024FDB7A6006CABD7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = Base; path = Base.lproj/Credits.rtf; sourceTree = "<group>"; };
|
B66236C024FDB7A6006CABD7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = Base; path = Base.lproj/Credits.rtf; sourceTree = "<group>"; };
|
||||||
|
B6665C12265A0E3E00254939 /* AutocompleteControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AutocompleteControl.h; sourceTree = "<group>"; };
|
||||||
|
B6665C13265A0E3E00254939 /* AutocompleteControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AutocompleteControl.m; sourceTree = "<group>"; };
|
||||||
|
B6665C1B265C639900254939 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/Autocomplete.xib; sourceTree = "<group>"; };
|
||||||
B66D0FE62611386B000902F1 /* SoftwareList.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SoftwareList.m; sourceTree = "<group>"; };
|
B66D0FE62611386B000902F1 /* SoftwareList.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SoftwareList.m; sourceTree = "<group>"; };
|
||||||
B66D0FE926113AA8000902F1 /* SoftwareList.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SoftwareList.h; sourceTree = "<group>"; };
|
B66D0FE926113AA8000902F1 /* SoftwareList.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SoftwareList.h; sourceTree = "<group>"; };
|
||||||
B67BD48424EE249D0073E334 /* apple1.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = apple1.plist; sourceTree = "<group>"; };
|
B67BD48424EE249D0073E334 /* apple1.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = apple1.plist; sourceTree = "<group>"; };
|
||||||
|
@ -662,6 +669,8 @@
|
||||||
B6BA257D24E99BE9005FB8FF /* Ample */ = {
|
B6BA257D24E99BE9005FB8FF /* Ample */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
B6665C12265A0E3E00254939 /* AutocompleteControl.h */,
|
||||||
|
B6665C13265A0E3E00254939 /* AutocompleteControl.m */,
|
||||||
B6BA257E24E99BE9005FB8FF /* AppDelegate.h */,
|
B6BA257E24E99BE9005FB8FF /* AppDelegate.h */,
|
||||||
B6BA257F24E99BE9005FB8FF /* AppDelegate.m */,
|
B6BA257F24E99BE9005FB8FF /* AppDelegate.m */,
|
||||||
B63C1B8924FF4B7100511A71 /* Ample.h */,
|
B63C1B8924FF4B7100511A71 /* Ample.h */,
|
||||||
|
@ -727,6 +736,7 @@
|
||||||
B6D6DE4224FAEE8900661A5F /* Nibs */ = {
|
B6D6DE4224FAEE8900661A5F /* Nibs */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
B6665C1A265C639900254939 /* Autocomplete.xib */,
|
||||||
B6E9A18125088B36005E7525 /* NewSlotView.xib */,
|
B6E9A18125088B36005E7525 /* NewSlotView.xib */,
|
||||||
B63C1B9325008A2700511A71 /* DownloadWindow.xib */,
|
B63C1B9325008A2700511A71 /* DownloadWindow.xib */,
|
||||||
B66236BF24FDB7A6006CABD7 /* Credits.rtf */,
|
B66236BF24FDB7A6006CABD7 /* Credits.rtf */,
|
||||||
|
@ -933,6 +943,7 @@
|
||||||
B6109A1A24F5F377005CB652 /* microeng.plist in Resources */,
|
B6109A1A24F5F377005CB652 /* microeng.plist in Resources */,
|
||||||
B6374AD5260ECB400045CA16 /* maciisi.plist in Resources */,
|
B6374AD5260ECB400045CA16 /* maciisi.plist in Resources */,
|
||||||
B6109A3E24F5F377005CB652 /* albert.plist in Resources */,
|
B6109A3E24F5F377005CB652 /* albert.plist in Resources */,
|
||||||
|
B6665C1C265C639A00254939 /* Autocomplete.xib in Resources */,
|
||||||
B66236C124FDB7A6006CABD7 /* Credits.rtf in Resources */,
|
B66236C124FDB7A6006CABD7 /* Credits.rtf in Resources */,
|
||||||
B6109A4224F5F377005CB652 /* apple2p.plist in Resources */,
|
B6109A4224F5F377005CB652 /* apple2p.plist in Resources */,
|
||||||
B65085B925B616AC00354EC9 /* maclc2.plist in Resources */,
|
B65085B925B616AC00354EC9 /* maclc2.plist in Resources */,
|
||||||
|
@ -1001,6 +1012,7 @@
|
||||||
B63C1BA6250192D800511A71 /* cecm.plist in Resources */,
|
B63C1BA6250192D800511A71 /* cecm.plist in Resources */,
|
||||||
B6E4B5BF24FDE2670094A35C /* agat9.plist in Resources */,
|
B6E4B5BF24FDE2670094A35C /* agat9.plist in Resources */,
|
||||||
B6E4B5C024FDE2670094A35C /* LaunchWindow.xib in Resources */,
|
B6E4B5C024FDE2670094A35C /* LaunchWindow.xib in Resources */,
|
||||||
|
B6665C1D265C639A00254939 /* Autocomplete.xib in Resources */,
|
||||||
B6DE323726044C1100418375 /* caution@2x.png in Resources */,
|
B6DE323726044C1100418375 /* caution@2x.png in Resources */,
|
||||||
B6E4B5C124FDE2670094A35C /* apple1.plist in Resources */,
|
B6E4B5C124FDE2670094A35C /* apple1.plist in Resources */,
|
||||||
B6E4B5C224FDE2670094A35C /* ace100.plist in Resources */,
|
B6E4B5C224FDE2670094A35C /* ace100.plist in Resources */,
|
||||||
|
@ -1143,6 +1155,7 @@
|
||||||
B6152B5A25F5B57E00605E6E /* Media.m in Sources */,
|
B6152B5A25F5B57E00605E6E /* Media.m in Sources */,
|
||||||
B6152B5625F4549F00605E6E /* Slot.m in Sources */,
|
B6152B5625F4549F00605E6E /* Slot.m in Sources */,
|
||||||
B6D6DE4124FADFAC00661A5F /* LaunchWindowController.m in Sources */,
|
B6D6DE4124FADFAC00661A5F /* LaunchWindowController.m in Sources */,
|
||||||
|
B6665C14265A0E3E00254939 /* AutocompleteControl.m in Sources */,
|
||||||
B64E15A924EA1D5300E8AD3D /* MachineViewController.m in Sources */,
|
B64E15A924EA1D5300E8AD3D /* MachineViewController.m in Sources */,
|
||||||
B64979C224EF6703008ABD20 /* MediaViewController.m in Sources */,
|
B64979C224EF6703008ABD20 /* MediaViewController.m in Sources */,
|
||||||
B60A6E1424EE0AE2004B7EEF /* FlippedView.m in Sources */,
|
B60A6E1424EE0AE2004B7EEF /* FlippedView.m in Sources */,
|
||||||
|
@ -1177,6 +1190,7 @@
|
||||||
B6E4B5B324FDE2670094A35C /* MediaViewController.m in Sources */,
|
B6E4B5B324FDE2670094A35C /* MediaViewController.m in Sources */,
|
||||||
B64AF1F7250ED5E400A09B9B /* TableCellView.m in Sources */,
|
B64AF1F7250ED5E400A09B9B /* TableCellView.m in Sources */,
|
||||||
B6E4B5B424FDE2670094A35C /* FlippedView.m in Sources */,
|
B6E4B5B424FDE2670094A35C /* FlippedView.m in Sources */,
|
||||||
|
B6665C15265A0E3E00254939 /* AutocompleteControl.m in Sources */,
|
||||||
B6E4B5B524FDE2670094A35C /* AppDelegate.m in Sources */,
|
B6E4B5B524FDE2670094A35C /* AppDelegate.m in Sources */,
|
||||||
B66D0FE82611386C000902F1 /* SoftwareList.m in Sources */,
|
B66D0FE82611386C000902F1 /* SoftwareList.m in Sources */,
|
||||||
B6E4B5B624FDE2670094A35C /* LogWindowController.m in Sources */,
|
B6E4B5B624FDE2670094A35C /* LogWindowController.m in Sources */,
|
||||||
|
@ -1228,6 +1242,14 @@
|
||||||
name = Credits.rtf;
|
name = Credits.rtf;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
B6665C1A265C639900254939 /* Autocomplete.xib */ = {
|
||||||
|
isa = PBXVariantGroup;
|
||||||
|
children = (
|
||||||
|
B6665C1B265C639900254939 /* Base */,
|
||||||
|
);
|
||||||
|
name = Autocomplete.xib;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
B6BA258324E99BEB005FB8FF /* MainMenu.xib */ = {
|
B6BA258324E99BEB005FB8FF /* MainMenu.xib */ = {
|
||||||
isa = PBXVariantGroup;
|
isa = PBXVariantGroup;
|
||||||
children = (
|
children = (
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
//
|
||||||
|
// AutocompleteControl.h
|
||||||
|
// Autocomplete
|
||||||
|
//
|
||||||
|
// Created by Kelvin Sherlock on 2/20/2021.
|
||||||
|
// Copyright © 2021 Kelvin Sherlock. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@class AutocompleteControl;
|
||||||
|
|
||||||
|
@protocol AutocompleteItem
|
||||||
|
-(NSString *)menuTitle;
|
||||||
|
-(NSAttributedString *)menuAttributedTitle; //?? can it still handle color?
|
||||||
|
-(BOOL)menuEnabled;
|
||||||
|
-(BOOL)menuIsHeader;
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@protocol AutoCompleteDelegate
|
||||||
|
|
||||||
|
-(NSArray<id<AutocompleteItem>> *)autocomplete: (AutocompleteControl *)control completionsForString: (NSString *)string;
|
||||||
|
-(NSArray<id<AutocompleteItem>> *)autocomplete: (AutocompleteControl *)control completionsForItem: (id<AutocompleteItem>)item;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface AutocompleteControl : NSSearchField
|
||||||
|
|
||||||
|
@property NSInteger minWidth;
|
||||||
|
@property NSInteger maxDisplayItems;
|
||||||
|
@property (nullable, weak) id<AutoCompleteDelegate> autocompleteDelegate;
|
||||||
|
|
||||||
|
-(void)invalidate;
|
||||||
|
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,822 @@
|
||||||
|
//
|
||||||
|
// AutocompleteControl.m
|
||||||
|
// Autocomplete
|
||||||
|
//
|
||||||
|
// Created by Kelvin Sherlock on 2/20/2021.
|
||||||
|
// Copyright © 2021 Kelvin Sherlock. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "AutocompleteControl.h"
|
||||||
|
#include <wctype.h>
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Todo --
|
||||||
|
- when there is a value, can filter the list by only including header items and the selected value
|
||||||
|
- draw inactive menu items
|
||||||
|
- when menu is hidden then text is manually deleted (not esc canceled), then down/up arrow the list needs to update.
|
||||||
|
- eliminate nib and do it manually.
|
||||||
|
- when menus is too tall, macos moves it to the top of the screen.
|
||||||
|
- 1. it's not moved someplace more appropriate when the size shrinks
|
||||||
|
- 2. it should display to the left or right in that case.
|
||||||
|
- need to know parent's frame.
|
||||||
|
|
||||||
|
- fuzzy search - minimum distance between letters?
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
@interface ACMenuView : NSView
|
||||||
|
@property (nonatomic) NSArray<id<AutocompleteItem>> *items;
|
||||||
|
|
||||||
|
@property (weak) AutocompleteControl *parent;
|
||||||
|
|
||||||
|
-(void)reset;
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@interface AutocompleteControl ()
|
||||||
|
{
|
||||||
|
IBOutlet NSPanel *_panel;
|
||||||
|
__weak IBOutlet ACMenuView *_menuView;
|
||||||
|
__weak IBOutlet NSScrollView *_scrollView;
|
||||||
|
|
||||||
|
id<AutocompleteItem> _value;
|
||||||
|
BOOL _editing;
|
||||||
|
BOOL _dirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface AutocompleteControl (SearchField) <NSSearchFieldDelegate>
|
||||||
|
-(void)fixTextColor: (BOOL)editing;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation AutocompleteControl
|
||||||
|
|
||||||
|
-(void)_init {
|
||||||
|
|
||||||
|
[self setDelegate: self];
|
||||||
|
[self setPlaceholderString: @""];
|
||||||
|
[(NSSearchFieldCell *)[self cell] setSearchButtonCell: nil];
|
||||||
|
|
||||||
|
NSBundle *bundle = [NSBundle mainBundle];
|
||||||
|
NSNib *nib = [[NSNib alloc] initWithNibNamed: @"Autocomplete" bundle: bundle];
|
||||||
|
|
||||||
|
NSArray *topLevel = nil;
|
||||||
|
[nib instantiateWithOwner: self topLevelObjects: &topLevel];
|
||||||
|
|
||||||
|
[_panel setMovable: NO];
|
||||||
|
[_panel setBecomesKeyOnlyIfNeeded: YES];
|
||||||
|
[_menuView setParent: self];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(id)initWithFrame:(NSRect)frameRect {
|
||||||
|
if ((self = [super initWithFrame: frameRect])) {
|
||||||
|
[self _init];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(id)initWithCoder:(NSCoder *)coder {
|
||||||
|
if ((self = [super initWithCoder: coder])) {
|
||||||
|
[self _init];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
-(NSString *)stringValue {
|
||||||
|
return [super stringValue];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
-(id)objectValue {
|
||||||
|
return _value;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)setStringValue:(NSString *)stringValue {
|
||||||
|
[super setStringValue: stringValue];
|
||||||
|
if (_value && [[_value menuTitle] isEqualToString: stringValue] == NO)
|
||||||
|
_value = nil;
|
||||||
|
|
||||||
|
// todo -- search for a matching item, update text color.
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)setObjectValue:(id)objectValue {
|
||||||
|
if (_value == objectValue) return;
|
||||||
|
if (![objectValue conformsToProtocol: @protocol(AutocompleteItem)]) {
|
||||||
|
_value = nil;
|
||||||
|
[super setStringValue: @""];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_value = objectValue;
|
||||||
|
if (!_value) [super setStringValue: @""]; //
|
||||||
|
else {
|
||||||
|
[super setStringValue: [_value menuTitle]];
|
||||||
|
NSArray *array = [_autocompleteDelegate autocomplete: self completionsForItem: _value];
|
||||||
|
[_menuView setItems: array];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-(BOOL)valid {
|
||||||
|
return _value != nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
-(void)hideSuggestions: (id)sender {
|
||||||
|
|
||||||
|
if (![_panel isVisible]) return;
|
||||||
|
|
||||||
|
NSWindow *window = [self window];
|
||||||
|
|
||||||
|
[window removeChildWindow: _panel];
|
||||||
|
[_panel orderOut: sender];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)showSuggestions: (id)sender {
|
||||||
|
|
||||||
|
if ([_panel isVisible]) return;
|
||||||
|
NSWindow *window = [self window];
|
||||||
|
|
||||||
|
NSRect wFrame = [_panel frame];
|
||||||
|
NSRect vFrame = [self frame];
|
||||||
|
|
||||||
|
NSRect rect = { .origin = vFrame.origin, .size = wFrame.size };
|
||||||
|
rect = [window convertRectToScreen:rect];
|
||||||
|
|
||||||
|
rect.origin.y -= wFrame.size.height + 4;
|
||||||
|
rect.size.width = MAX(vFrame.size.width, _minWidth);
|
||||||
|
// todo - min width option.
|
||||||
|
[_panel setFrame: rect display: YES];
|
||||||
|
|
||||||
|
//[_panel setFrameOrigin: rect.origin];
|
||||||
|
[window addChildWindow: _panel ordered: NSWindowAbove];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)updateSuggestions {
|
||||||
|
|
||||||
|
if (!_autocompleteDelegate) return;
|
||||||
|
NSString *needle = [self stringValue];
|
||||||
|
|
||||||
|
NSArray *items = [_autocompleteDelegate autocomplete: self completionsForString: needle];
|
||||||
|
|
||||||
|
[_menuView setItems: items];
|
||||||
|
if ([items count]) {
|
||||||
|
[self showSuggestions: nil];
|
||||||
|
} else {
|
||||||
|
[self hideSuggestions: nil];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)invalidate {
|
||||||
|
|
||||||
|
if (!_autocompleteDelegate) return;
|
||||||
|
|
||||||
|
NSArray *items = nil;
|
||||||
|
|
||||||
|
/* if there is an object value, try to retain it. */
|
||||||
|
if (_value) {
|
||||||
|
|
||||||
|
[_menuView reset];
|
||||||
|
items = [_autocompleteDelegate autocomplete: self completionsForItem: _value];
|
||||||
|
if (items) {
|
||||||
|
[_menuView setItems: items];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_value = nil;
|
||||||
|
[self invoke];
|
||||||
|
}
|
||||||
|
NSString *needle = [self stringValue];
|
||||||
|
|
||||||
|
if ([needle length]) {
|
||||||
|
_dirty = YES;
|
||||||
|
}
|
||||||
|
// if only 1 match, auto-set value?
|
||||||
|
items = [_autocompleteDelegate autocomplete: self completionsForString: needle];
|
||||||
|
[self fixTextColor: _editing];
|
||||||
|
[_menuView setItems: items];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// prevent action messages from the search field/cell.
|
||||||
|
-(BOOL)sendAction:(SEL)action to:(id)target {
|
||||||
|
if (action == [self action] && target == [self target]) return NO;
|
||||||
|
return [super sendAction: action to: target];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)invoke {
|
||||||
|
_dirty = NO;
|
||||||
|
[super sendAction: [self action] to: [self target]];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@implementation AutocompleteControl (SearchField)
|
||||||
|
|
||||||
|
-(void)fixTextColor: (BOOL)editing {
|
||||||
|
NSColor *color = editing || _value ? [NSColor controlTextColor] : [NSColor systemRedColor];
|
||||||
|
[self setTextColor: color];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)controlTextDidChange:(NSNotification *)notification {
|
||||||
|
NSLog(@"controlTextDidChange");
|
||||||
|
|
||||||
|
if (_value) {
|
||||||
|
_dirty = YES;
|
||||||
|
_value = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString *s = [self stringValue];
|
||||||
|
if ([s length]) {
|
||||||
|
[self updateSuggestions];
|
||||||
|
} else {
|
||||||
|
_dirty = YES;
|
||||||
|
_value = nil;
|
||||||
|
[_menuView reset];
|
||||||
|
[_menuView setItems: nil];
|
||||||
|
[self hideSuggestions: nil];
|
||||||
|
[self invoke];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- (void)controlTextDidBeginEditing:(NSNotification *)obj {
|
||||||
|
NSLog(@"controlTextDidBeginEditing");
|
||||||
|
|
||||||
|
_editing = YES;
|
||||||
|
_dirty = NO;
|
||||||
|
[self fixTextColor: YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- (void)controlTextDidEndEditing:(NSNotification *)obj {
|
||||||
|
NSLog(@"controlTextDidEndEditing");
|
||||||
|
|
||||||
|
_editing = NO;
|
||||||
|
|
||||||
|
[self hideSuggestions: nil];
|
||||||
|
|
||||||
|
if (_dirty) {
|
||||||
|
_value = nil;
|
||||||
|
[self invoke];
|
||||||
|
}
|
||||||
|
[self fixTextColor: NO];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
-(BOOL)control:(NSControl *)control textShouldBeginEditing:(NSText *)fieldEditor {
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor {
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)selectItem:(id<AutocompleteItem>)item withSelector:(SEL)selector {
|
||||||
|
|
||||||
|
// for newline/mousedown, will still retain focus after updating
|
||||||
|
// so we need to invalidate the value if it's edited further.
|
||||||
|
if (selector == @selector(insertNewline:) || selector == @selector(mouseDown:) || selector == @selector(insertTab:)) {
|
||||||
|
|
||||||
|
_value = item;
|
||||||
|
NSString *str = [item menuTitle];
|
||||||
|
|
||||||
|
[super setStringValue: str];
|
||||||
|
|
||||||
|
[self hideSuggestions: nil];
|
||||||
|
|
||||||
|
NSText *fieldEditor = [self currentEditor];
|
||||||
|
//[fieldEditor setSelectedRange: NSMakeRange([str length], 0)];
|
||||||
|
[fieldEditor setSelectedRange: NSMakeRange(0, [str length])];
|
||||||
|
|
||||||
|
[self invoke];
|
||||||
|
|
||||||
|
// need to invalidate the menu so it reloads
|
||||||
|
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
NSArray *array = [_autocompleteDelegate autocomplete: self completionsForItem: _value];
|
||||||
|
[_menuView setItems: array];
|
||||||
|
#else
|
||||||
|
[_menuView setItems: nil];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
NSLog(@"selectItem:withSelector:");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector {
|
||||||
|
|
||||||
|
if (commandSelector == @selector(moveUp:)) {
|
||||||
|
//[self showSuggestions: nil];
|
||||||
|
if ([_panel isVisible]) {
|
||||||
|
[_menuView moveUp: textView];
|
||||||
|
} else {
|
||||||
|
[self updateSuggestions];
|
||||||
|
}
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (commandSelector == @selector(moveDown:)) {
|
||||||
|
//[self showSuggestions: nil];
|
||||||
|
if ([_panel isVisible]) {
|
||||||
|
[_menuView moveDown: textView];
|
||||||
|
} else {
|
||||||
|
[self updateSuggestions];
|
||||||
|
}
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (commandSelector == @selector(insertNewline:)) {
|
||||||
|
if ([_panel isVisible])
|
||||||
|
[_menuView insertNewline: textView];
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (commandSelector == @selector(insertTab:)) {
|
||||||
|
if ([_panel isVisible])
|
||||||
|
[_menuView insertTab: textView];
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (commandSelector == @selector(complete:)) {
|
||||||
|
if ([_panel isVisible]) {
|
||||||
|
[self hideSuggestions: nil];
|
||||||
|
} else {
|
||||||
|
[self updateSuggestions];
|
||||||
|
}
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// esc ?
|
||||||
|
// if panel open, hide
|
||||||
|
// if panel closed, delete.
|
||||||
|
if (commandSelector == @selector(cancelOperation:)) {
|
||||||
|
|
||||||
|
if ([_panel isVisible]) {
|
||||||
|
[self hideSuggestions: nil];
|
||||||
|
} else {
|
||||||
|
_value = nil;
|
||||||
|
[super setStringValue: @""];
|
||||||
|
|
||||||
|
[self hideSuggestions: nil];
|
||||||
|
[_menuView reset];
|
||||||
|
//[_menuView setItems: _completions];
|
||||||
|
// sigh...
|
||||||
|
#if 0
|
||||||
|
NSArray *items =[_autocompleteDelegate autocomplete: self completionsForString: @""];
|
||||||
|
[_menuView setItems: items];
|
||||||
|
#endif
|
||||||
|
[self invoke];
|
||||||
|
}
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
//NSLog(@"%@", NSStringFromSelector(commandSelector));
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@interface ACMenuView () {
|
||||||
|
id<AutocompleteItem> _value;
|
||||||
|
NSInteger _index;
|
||||||
|
NSInteger _count;
|
||||||
|
NSArray<id<AutocompleteItem>> *_items;
|
||||||
|
NSTrackingArea *_trackingArea;
|
||||||
|
NSColor *_backgroundColor;
|
||||||
|
NSColor *_selectedColor;
|
||||||
|
BOOL _tracking;
|
||||||
|
BOOL _clipped;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation ACMenuView
|
||||||
|
|
||||||
|
#define MENU_FONT_SIZE 11
|
||||||
|
#define MENU_HEIGHT 14
|
||||||
|
#define MARGIN_TOP 0 //6
|
||||||
|
#define MARGIN_BOTTOM 0 //6
|
||||||
|
#define INDENT 7
|
||||||
|
#define HEADER_INDENT 7
|
||||||
|
#define MAX_DISPLAY_ITEMS 16
|
||||||
|
|
||||||
|
|
||||||
|
-(void)_init {
|
||||||
|
_backgroundColor = [NSColor windowBackgroundColor];
|
||||||
|
_selectedColor = [NSColor selectedContentBackgroundColor];
|
||||||
|
|
||||||
|
NSTrackingAreaOptions options = NSTrackingMouseMoved | NSTrackingMouseEnteredAndExited | NSTrackingInVisibleRect | NSTrackingActiveInActiveApp;
|
||||||
|
_trackingArea = [[NSTrackingArea alloc] initWithRect: NSZeroRect
|
||||||
|
options: options
|
||||||
|
owner: self
|
||||||
|
userInfo: nil];
|
||||||
|
|
||||||
|
[self addTrackingArea: _trackingArea];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(id)initWithCoder:(NSCoder *)coder {
|
||||||
|
if ((self = [super initWithCoder: coder])) {
|
||||||
|
[self _init];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
-(BOOL)isFlipped {
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(BOOL)acceptsFirstMouse:(NSEvent *)event {
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
static CGFloat HeightForItems(NSUInteger count) {
|
||||||
|
return count * MENU_HEIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(NSSize)intrinsicContentSize {
|
||||||
|
return NSMakeSize(NSViewNoIntrinsicMetric, _count * MENU_HEIGHT + MARGIN_TOP + MARGIN_BOTTOM);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSSize)sizeThatFits:(NSSize)size {
|
||||||
|
size.height = _count * MENU_HEIGHT + MARGIN_TOP + MARGIN_BOTTOM;
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)sizeToFit {
|
||||||
|
NSSize size = [self frame].size;
|
||||||
|
size.height = _count * MENU_HEIGHT + MARGIN_TOP + MARGIN_BOTTOM;
|
||||||
|
[self setFrameSize: size];
|
||||||
|
[self setNeedsDisplay: YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)reset {
|
||||||
|
[self invalidateRow: _index];
|
||||||
|
_index = -1;
|
||||||
|
_value = nil;
|
||||||
|
_items = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)setItems:(NSArray *)items {
|
||||||
|
if (_items == items) return;
|
||||||
|
_items = [items copy];
|
||||||
|
_index = -1;
|
||||||
|
_count = [items count];
|
||||||
|
|
||||||
|
|
||||||
|
if (!_items) return;
|
||||||
|
|
||||||
|
// also check enabled status....
|
||||||
|
if (_value) {
|
||||||
|
_index = [_items indexOfObject: _value];
|
||||||
|
if (_index == NSNotFound) {
|
||||||
|
_index = -1;
|
||||||
|
_value = nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if only 1 entry, auto-select it.
|
||||||
|
if (!_value) {
|
||||||
|
NSInteger count = -1;
|
||||||
|
for (id<AutocompleteItem> item in _items) {
|
||||||
|
++count;
|
||||||
|
if ([item menuIsHeader]) continue;
|
||||||
|
if (_value) {
|
||||||
|
_value = nil;
|
||||||
|
_index = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_value = item;
|
||||||
|
_index = count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
NSInteger displayCount = MIN(_count, MAX_DISPLAY_ITEMS);
|
||||||
|
CGFloat newHeight = HeightForItems(displayCount) + 8 ; // 4px top/bottom
|
||||||
|
NSWindow *window = [self window];
|
||||||
|
NSRect wFrame = [window frame];
|
||||||
|
|
||||||
|
NSRect contentRect = [[[self enclosingScrollView] contentView] frame];
|
||||||
|
|
||||||
|
//NSSize size = [self intrinsicContentSize];
|
||||||
|
//NSInteger minWidth = [_parent minWidth];
|
||||||
|
//size.width = MAX(wFrame.size.width, minWidth);
|
||||||
|
//size.height += 8;
|
||||||
|
|
||||||
|
CGFloat delta = wFrame.size.height - newHeight;
|
||||||
|
|
||||||
|
wFrame.origin.y += delta;
|
||||||
|
wFrame.size.height = newHeight;
|
||||||
|
|
||||||
|
_clipped = (_count > displayCount);
|
||||||
|
|
||||||
|
[self setFrameSize: NSMakeSize(contentRect.size.width /*- 15.0*/, HeightForItems(_count))];
|
||||||
|
[self setNeedsDisplay: YES];
|
||||||
|
[window setFrame: wFrame display: YES];
|
||||||
|
|
||||||
|
if (_value) {
|
||||||
|
[self scrollToRow: _index position: ScrollToCenter force: NO];
|
||||||
|
} else {
|
||||||
|
[self scrollToRow: 0 position: ScrollToTop force: YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
//[self sizeToFit];
|
||||||
|
//[[self window] setContentSize: [self frame].size];
|
||||||
|
|
||||||
|
//NSLog(@"%@", NSStringFromRect(wFrame));
|
||||||
|
}
|
||||||
|
|
||||||
|
-(id<AutocompleteItem>)itemAtPoint: (NSPoint)point indexPtr: (NSInteger *)indexPtr {
|
||||||
|
|
||||||
|
NSInteger index = floor(point.y / MENU_HEIGHT);
|
||||||
|
if (index < 0 || index >= _count) return nil;
|
||||||
|
if (indexPtr) *indexPtr = index;
|
||||||
|
return [_items objectAtIndex: index];
|
||||||
|
}
|
||||||
|
|
||||||
|
enum {
|
||||||
|
ScrollToTop,
|
||||||
|
ScrollToBottom,
|
||||||
|
ScrollToCenter,
|
||||||
|
};
|
||||||
|
|
||||||
|
-(void)scrollToRow: (NSInteger)row position: (unsigned)position force: (BOOL)force {
|
||||||
|
if (row < 0) return;
|
||||||
|
if (!_clipped) return;
|
||||||
|
|
||||||
|
NSScrollView *scrollView = [self enclosingScrollView];
|
||||||
|
NSClipView *clipView = [scrollView contentView];
|
||||||
|
|
||||||
|
NSRect visibleRect = [self visibleRect];
|
||||||
|
if (!force) {
|
||||||
|
NSRect mRect = NSMakeRect(0, row * MENU_HEIGHT, 1 , MENU_HEIGHT);
|
||||||
|
if (NSContainsRect(visibleRect, mRect)) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSInteger topRow = row;
|
||||||
|
switch (position) {
|
||||||
|
case ScrollToTop:
|
||||||
|
break;
|
||||||
|
case ScrollToBottom:
|
||||||
|
topRow -= MAX_DISPLAY_ITEMS -1;
|
||||||
|
break;
|
||||||
|
case ScrollToCenter:
|
||||||
|
topRow -= MAX_DISPLAY_ITEMS/2 - 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (topRow < 0) topRow = 0;
|
||||||
|
if (topRow > _count - MAX_DISPLAY_ITEMS)
|
||||||
|
topRow = _count - MAX_DISPLAY_ITEMS;
|
||||||
|
NSPoint point = NSMakePoint(0, topRow * MENU_HEIGHT);
|
||||||
|
|
||||||
|
//[self scrollClipView: clipView toPoint: point];
|
||||||
|
[clipView scrollToPoint: point];
|
||||||
|
[scrollView reflectScrolledClipView: clipView];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
-(void)moveUp:(id)sender {
|
||||||
|
if (_count == 0 || _index <= 0) return;
|
||||||
|
|
||||||
|
NSInteger index = 0;
|
||||||
|
id<AutocompleteItem> value = nil;
|
||||||
|
for (index = _index - 1; index >= 0; --index) {
|
||||||
|
value = [_items objectAtIndex: index];
|
||||||
|
if ([value menuIsHeader]) continue;
|
||||||
|
if (![value menuEnabled]) continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (index < 0) return;
|
||||||
|
if (index == _index) return;
|
||||||
|
[self invalidateRow: _index];
|
||||||
|
[self invalidateRow: index];
|
||||||
|
|
||||||
|
_index = index;
|
||||||
|
_value = value;
|
||||||
|
[self scrollToRow: index position: ScrollToTop force: NO];
|
||||||
|
[_parent selectItem: _value withSelector: _cmd];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)moveDown:(id)sender {
|
||||||
|
|
||||||
|
// _index -1 selects first item.
|
||||||
|
if (_count == 0 || _index == _count - 1) return;
|
||||||
|
|
||||||
|
NSInteger index = 0;
|
||||||
|
id<AutocompleteItem> value = nil;
|
||||||
|
for (index = _index + 1; index < _count ; ++index) {
|
||||||
|
value = [_items objectAtIndex: index];
|
||||||
|
if ([value menuIsHeader]) continue;
|
||||||
|
if (![value menuEnabled]) continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (index == _count) return;
|
||||||
|
if (index == _index) return;
|
||||||
|
[self invalidateRow: _index];
|
||||||
|
[self invalidateRow: index];
|
||||||
|
|
||||||
|
_index = index;
|
||||||
|
_value = value;
|
||||||
|
|
||||||
|
[self scrollToRow: index position: ScrollToBottom force: NO];
|
||||||
|
[_parent selectItem: _value withSelector: _cmd];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)insertNewline:(id)sender {
|
||||||
|
if (_value) {
|
||||||
|
[_parent selectItem: _value withSelector: _cmd];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)insertTab:(id)sender {
|
||||||
|
// if only one option, autocomplete?
|
||||||
|
if (_value) {
|
||||||
|
[_parent selectItem: _value withSelector: _cmd];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)mouseMoved:(NSEvent *)event {
|
||||||
|
//NSLog(@"mouse moved");
|
||||||
|
|
||||||
|
if (!_tracking) return;
|
||||||
|
|
||||||
|
NSPoint p = [event locationInWindow];
|
||||||
|
p = [self convertPoint: p fromView: nil];
|
||||||
|
|
||||||
|
|
||||||
|
NSInteger index;
|
||||||
|
id<AutocompleteItem> value = [self itemAtPoint: p indexPtr: &index];
|
||||||
|
if (!value) return;
|
||||||
|
if (index == _index) return;
|
||||||
|
if ([value menuIsHeader]) return;
|
||||||
|
if (![value menuEnabled]) return;
|
||||||
|
|
||||||
|
[self invalidateRow: _index];
|
||||||
|
[self invalidateRow: index];
|
||||||
|
|
||||||
|
_index = index;
|
||||||
|
_value = value;
|
||||||
|
[_parent selectItem: _value withSelector: _cmd];
|
||||||
|
}
|
||||||
|
-(void)mouseDown:(NSEvent *)event {
|
||||||
|
if (!_tracking) return;
|
||||||
|
|
||||||
|
NSPoint p = [event locationInWindow];
|
||||||
|
p = [self convertPoint: p fromView: nil];
|
||||||
|
|
||||||
|
NSInteger index;
|
||||||
|
id<AutocompleteItem> value = [self itemAtPoint: p indexPtr: &index];
|
||||||
|
if (!value) return;
|
||||||
|
if (index != _index) {
|
||||||
|
if ([value menuIsHeader]) return;
|
||||||
|
if (![value menuEnabled]) return;
|
||||||
|
|
||||||
|
[self invalidateRow: _index];
|
||||||
|
[self invalidateRow: index];
|
||||||
|
|
||||||
|
_index = index;
|
||||||
|
_value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[_parent selectItem: _value withSelector: _cmd];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)mouseEntered:(NSEvent *)event {
|
||||||
|
//NSLog(@"mouse entered");
|
||||||
|
_tracking = YES;
|
||||||
|
|
||||||
|
}
|
||||||
|
-(void)mouseExited:(NSEvent *)event {
|
||||||
|
//NSLog(@"mouse exited");
|
||||||
|
|
||||||
|
_tracking = NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)invalidateRow:(NSInteger)row {
|
||||||
|
if (row < 0 || row >= _count) return;
|
||||||
|
|
||||||
|
NSRect r = NSZeroRect;
|
||||||
|
NSRect bounds = [self bounds];
|
||||||
|
|
||||||
|
r.size.width = bounds.size.width;
|
||||||
|
r.size.height = MENU_HEIGHT;
|
||||||
|
r.origin.y = MENU_HEIGHT * row + MARGIN_TOP;
|
||||||
|
//NSLog(@"Invalidating %ld - %@", row, NSStringFromRect(r));
|
||||||
|
[self setNeedsDisplayInRect: r];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void DrawString(NSString *str, NSDictionary *attr, CGRect rect) {
|
||||||
|
|
||||||
|
NSSize size = [str sizeWithAttributes: attr];
|
||||||
|
if (size.width <= rect.size.width) {
|
||||||
|
[str drawInRect: rect withAttributes: attr];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
NSMutableString *mstr = [str mutableCopy];
|
||||||
|
// binary search is probably the best way to handle it :/
|
||||||
|
NSInteger l = [mstr length];
|
||||||
|
while (l > 2) {
|
||||||
|
[mstr replaceCharactersInRange: NSMakeRange(l-2, 2) withString: @"…"];
|
||||||
|
--l;
|
||||||
|
size = [mstr sizeWithAttributes: attr];
|
||||||
|
if (size.width <= rect.size.width) {
|
||||||
|
[mstr drawInRect: rect withAttributes: attr];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)drawItem: (id<AutocompleteItem>)item inRect: (NSRect)rect {
|
||||||
|
NSColor *textColor = [NSColor textColor];
|
||||||
|
if (!item) return;
|
||||||
|
|
||||||
|
if (item == _value) {
|
||||||
|
textColor = [NSColor selectedMenuItemTextColor];
|
||||||
|
[_selectedColor setFill];
|
||||||
|
NSRectFill(rect);
|
||||||
|
}
|
||||||
|
NSString *string = [item menuTitle];
|
||||||
|
|
||||||
|
if ([item menuIsHeader]) {
|
||||||
|
textColor = [NSColor secondaryLabelColor];
|
||||||
|
NSDictionary *attr = @{
|
||||||
|
NSForegroundColorAttributeName: textColor,
|
||||||
|
NSFontAttributeName: [NSFont systemFontOfSize: MENU_FONT_SIZE], // [NSFont boldSystemFontOfSize: 13],
|
||||||
|
};
|
||||||
|
NSRect r = NSInsetRect(rect, HEADER_INDENT, 0);
|
||||||
|
DrawString(string, attr, r);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
NSDictionary *attr = @{
|
||||||
|
NSForegroundColorAttributeName: textColor,
|
||||||
|
NSFontAttributeName: [NSFont systemFontOfSize: MENU_FONT_SIZE],
|
||||||
|
};
|
||||||
|
NSRect r = NSInsetRect(rect, INDENT, 0);
|
||||||
|
r.origin.x += INDENT;
|
||||||
|
r.size.width -= INDENT;
|
||||||
|
DrawString(string, attr, r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)drawRect:(NSRect)dirtyRect {
|
||||||
|
[super drawRect:dirtyRect];
|
||||||
|
|
||||||
|
NSRect r = [self bounds];
|
||||||
|
NSInteger begin = floor((NSMinY(dirtyRect) - MARGIN_TOP) / MENU_HEIGHT);
|
||||||
|
NSInteger end = ceil((NSMaxY(dirtyRect) - MARGIN_TOP) / MENU_HEIGHT);
|
||||||
|
if (begin < 0) begin = 0;
|
||||||
|
if (end > _count) end = _count;
|
||||||
|
|
||||||
|
|
||||||
|
r.origin.y = MENU_HEIGHT * begin + MARGIN_TOP;
|
||||||
|
r.size.height = MENU_HEIGHT;
|
||||||
|
for (NSInteger index = begin; index < end; ++index) {
|
||||||
|
id<AutocompleteItem> item = [_items objectAtIndex: index];
|
||||||
|
[self drawItem: item inRect: r];
|
||||||
|
r.origin.y += MENU_HEIGHT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
/* custom scroller that doesn't draw a background. */
|
||||||
|
@interface ACScroller : NSScroller
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation ACScroller
|
||||||
|
|
||||||
|
|
||||||
|
-(void)drawRect:(NSRect)dirtyRect {
|
||||||
|
[[NSColor windowBackgroundColor] set];
|
||||||
|
NSRectFill(dirtyRect);
|
||||||
|
[self drawKnob];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@interface ACPanel : NSPanel
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation ACPanel
|
||||||
|
|
||||||
|
/* needed to prevent the pop-up child window from being moved when offscreen. */
|
||||||
|
- (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen {
|
||||||
|
return frameRect;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,55 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="15705" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="macosx"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="15705"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<objects>
|
||||||
|
<customObject id="-2" userLabel="File's Owner" customClass="AutocompleteControl">
|
||||||
|
<connections>
|
||||||
|
<outlet property="_menuView" destination="F2h-9b-Ouu" id="min-gE-sma"/>
|
||||||
|
<outlet property="_panel" destination="OYW-cj-FD5" id="DYJ-rN-6Ep"/>
|
||||||
|
<outlet property="_scrollView" destination="VQ6-PE-hTH" id="iKA-Ps-nkl"/>
|
||||||
|
</connections>
|
||||||
|
</customObject>
|
||||||
|
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||||
|
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||||
|
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" titlebarAppearsTransparent="YES" titleVisibility="hidden" id="OYW-cj-FD5" customClass="ACPanel">
|
||||||
|
<windowStyleMask key="styleMask" titled="YES" utility="YES" nonactivatingPanel="YES" fullSizeContentView="YES"/>
|
||||||
|
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||||
|
<rect key="contentRect" x="272" y="172" width="300" height="400"/>
|
||||||
|
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
|
||||||
|
<view key="contentView" id="3pz-HL-mV1">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="300" height="400"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="0.0" horizontalPageScroll="0.0" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" horizontalScrollElasticity="none" id="VQ6-PE-hTH">
|
||||||
|
<rect key="frame" x="0.0" y="4" width="300" height="392"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="HWy-NN-aOV">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="300" height="392"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<view id="F2h-9b-Ouu" customClass="ACMenuView">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="300" height="392"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||||
|
</view>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" name="windowBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</clipView>
|
||||||
|
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="Az6-1a-Hei">
|
||||||
|
<rect key="frame" x="-100" y="-100" width="168" height="15"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="tng-g2-1LZ" customClass="ACScroller">
|
||||||
|
<rect key="frame" x="285" y="0.0" width="15" height="392"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</scroller>
|
||||||
|
</scrollView>
|
||||||
|
</subviews>
|
||||||
|
</view>
|
||||||
|
<point key="canvasLocation" x="127" y="73"/>
|
||||||
|
</window>
|
||||||
|
</objects>
|
||||||
|
</document>
|
|
@ -14,6 +14,7 @@
|
||||||
<outlet property="mediaView" destination="J9O-xI-P5J" id="PmZ-VC-4SN"/>
|
<outlet property="mediaView" destination="J9O-xI-P5J" id="PmZ-VC-4SN"/>
|
||||||
<outlet property="slotController" destination="lyS-mc-3Tf" id="LXo-Ii-fDX"/>
|
<outlet property="slotController" destination="lyS-mc-3Tf" id="LXo-Ii-fDX"/>
|
||||||
<outlet property="slotView" destination="P9d-sS-qEb" id="qlH-u0-hzq"/>
|
<outlet property="slotView" destination="P9d-sS-qEb" id="qlH-u0-hzq"/>
|
||||||
|
<outlet property="softwareListControl" destination="pNl-g4-ZLh" id="U0i-R9-GLE"/>
|
||||||
<outlet property="window" destination="Vze-YF-m6e" id="JUs-Eb-MW8"/>
|
<outlet property="window" destination="Vze-YF-m6e" id="JUs-Eb-MW8"/>
|
||||||
</connections>
|
</connections>
|
||||||
</customObject>
|
</customObject>
|
||||||
|
@ -372,7 +373,7 @@ DQ
|
||||||
</tabViewItems>
|
</tabViewItems>
|
||||||
</tabView>
|
</tabView>
|
||||||
<customView fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="J9O-xI-P5J" customClass="FlippedView">
|
<customView fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="J9O-xI-P5J" customClass="FlippedView">
|
||||||
<rect key="frame" x="382" y="92" width="316" height="363"/>
|
<rect key="frame" x="382" y="92" width="316" height="331"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
|
||||||
</customView>
|
</customView>
|
||||||
<scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" horizontalScrollElasticity="none" id="2ue-XT-byh">
|
<scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" horizontalScrollElasticity="none" id="2ue-XT-byh">
|
||||||
|
@ -409,6 +410,18 @@ DQ
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</scroller>
|
</scroller>
|
||||||
</scrollView>
|
</scrollView>
|
||||||
|
<searchField wantsLayer="YES" verticalHuggingPriority="750" textCompletion="NO" id="pNl-g4-ZLh" customClass="AutocompleteControl">
|
||||||
|
<rect key="frame" x="382" y="431" width="316" height="22"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||||
|
<searchFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" borderStyle="bezel" placeholderString="Software List" usesSingleLineMode="YES" bezelStyle="round" id="sMi-a3-XEn">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="systemRedColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</searchFieldCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="softwareChanged:" target="-2" id="879-da-UEc"/>
|
||||||
|
</connections>
|
||||||
|
</searchField>
|
||||||
</subviews>
|
</subviews>
|
||||||
</view>
|
</view>
|
||||||
<point key="canvasLocation" x="769" y="120.5"/>
|
<point key="canvasLocation" x="769" y="120.5"/>
|
||||||
|
|
|
@ -13,8 +13,11 @@
|
||||||
#import "MachineViewController.h"
|
#import "MachineViewController.h"
|
||||||
#import "LogWindowController.h"
|
#import "LogWindowController.h"
|
||||||
|
|
||||||
|
#import "AutocompleteControl.h"
|
||||||
|
#import "SoftwareList.h"
|
||||||
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <wctype.h>
|
||||||
|
|
||||||
static NSString *kMyContext = @"kMyContext";
|
static NSString *kMyContext = @"kMyContext";
|
||||||
static NSString *kContextMachine = @"kContextMachine";
|
static NSString *kContextMachine = @"kContextMachine";
|
||||||
|
@ -57,8 +60,16 @@ static NSString *kContextMachine = @"kContextMachine";
|
||||||
|
|
||||||
@property NSInteger mameWindowMode;
|
@property NSInteger mameWindowMode;
|
||||||
|
|
||||||
|
@property (weak) IBOutlet AutocompleteControl *softwareListControl;
|
||||||
|
@property SoftwareSet *softwareSet;
|
||||||
|
@property Software *software;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@interface LaunchWindowController (SoftwareList)
|
||||||
|
|
||||||
|
-(void)updateSoftwareList;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
@implementation LaunchWindowController
|
@implementation LaunchWindowController
|
||||||
|
|
||||||
|
@ -94,6 +105,7 @@ static NSString *kContextMachine = @"kContextMachine";
|
||||||
@"mameVGM", @"mameVGMPath",
|
@"mameVGM", @"mameVGMPath",
|
||||||
@"mameShareDirectory",
|
@"mameShareDirectory",
|
||||||
@"mameBGFX", @"mameBackend", @"mameEffects",
|
@"mameBGFX", @"mameBackend", @"mameEffects",
|
||||||
|
@"software",
|
||||||
];
|
];
|
||||||
|
|
||||||
for (NSString *key in keys) {
|
for (NSString *key in keys) {
|
||||||
|
@ -108,6 +120,10 @@ static NSString *kContextMachine = @"kContextMachine";
|
||||||
|
|
||||||
[_machineViewController addObserver: self forKeyPath: @"machine" options: 0 context: (__bridge void * _Nullable)kContextMachine];
|
[_machineViewController addObserver: self forKeyPath: @"machine" options: 0 context: (__bridge void * _Nullable)kContextMachine];
|
||||||
|
|
||||||
|
|
||||||
|
[_softwareListControl setMinWidth: 250];
|
||||||
|
[_softwareListControl setHidden: YES];
|
||||||
|
|
||||||
[self buildCommandLine];
|
[self buildCommandLine];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,6 +136,7 @@ static NSString *kContextMachine = @"kContextMachine";
|
||||||
NSString *machine = [_machineViewController machine];
|
NSString *machine = [_machineViewController machine];
|
||||||
[self setMameMachine: machine];
|
[self setMameMachine: machine];
|
||||||
[_slotController setMachine: machine];
|
[_slotController setMachine: machine];
|
||||||
|
[self updateSoftwareList];
|
||||||
[self buildCommandLine];
|
[self buildCommandLine];
|
||||||
} else {
|
} else {
|
||||||
[super observeValueForKeyPath: keyPath ofObject: object change: change context: context];
|
[super observeValueForKeyPath: keyPath ofObject: object change: change context: context];
|
||||||
|
@ -279,6 +296,14 @@ static NSString *ShellQuote(NSString *s) {
|
||||||
//[argv addObject: @"mame"];
|
//[argv addObject: @"mame"];
|
||||||
[argv addObject: _mameMachine];
|
[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];
|
||||||
|
}
|
||||||
|
|
||||||
// -confirm_quit?
|
// -confirm_quit?
|
||||||
[argv addObject: @"-skip_gameinfo"];
|
[argv addObject: @"-skip_gameinfo"];
|
||||||
|
|
||||||
|
@ -483,3 +508,30 @@ static NSString *ShellQuote(NSString *s) {
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@implementation LaunchWindowController (SoftwareList)
|
||||||
|
|
||||||
|
-(void)updateSoftwareList {
|
||||||
|
|
||||||
|
_softwareSet = [SoftwareSet softwareSetForMachine: _mameMachine];
|
||||||
|
|
||||||
|
[_softwareListControl setAutocompleteDelegate: _softwareSet];
|
||||||
|
|
||||||
|
if (_softwareSet) {
|
||||||
|
[_softwareListControl invalidate];
|
||||||
|
[_softwareListControl setHidden: NO];
|
||||||
|
} else {
|
||||||
|
_software = nil;
|
||||||
|
[_softwareListControl setHidden: YES];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
- (IBAction)softwareChanged:(id)sender {
|
||||||
|
|
||||||
|
id o = [(NSControl *)sender objectValue];
|
||||||
|
NSLog(@"%@", o);
|
||||||
|
[self setSoftware: o];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
|
@ -5944,7 +5944,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
<string>apple2_cass.xml</string>
|
<string>apple2_cass.xml</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
|
@ -5884,7 +5884,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2P</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
<string>apple2_cass.xml</string>
|
<string>apple2_cass.xml</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
|
@ -5884,7 +5884,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2P</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
<string>apple2_cass.xml</string>
|
<string>apple2_cass.xml</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
|
@ -5884,7 +5884,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2P</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
<string>apple2_cass.xml</string>
|
<string>apple2_cass.xml</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
|
@ -5884,7 +5884,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2P</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
<string>apple2_cass.xml</string>
|
<string>apple2_cass.xml</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
|
@ -5944,7 +5944,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
<string>apple2_cass.xml</string>
|
<string>apple2_cass.xml</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
|
@ -485,7 +485,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2C</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
|
@ -447,7 +447,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2C</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
|
@ -7672,7 +7672,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2E</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
|
@ -7672,7 +7672,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2EE</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
|
@ -7672,7 +7672,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2EE</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
|
@ -7672,7 +7672,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2E</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
|
@ -7672,7 +7672,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2EE</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
|
@ -7672,7 +7672,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2E</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
|
@ -7672,7 +7672,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2E</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
|
@ -7481,7 +7481,12 @@
|
||||||
<array>
|
<array>
|
||||||
<string>apple2gs.xml</string>
|
<string>apple2gs.xml</string>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2GS</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
|
@ -7521,7 +7521,12 @@
|
||||||
<array>
|
<array>
|
||||||
<string>apple2gs.xml</string>
|
<string>apple2gs.xml</string>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2GS</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
|
@ -7521,7 +7521,12 @@
|
||||||
<array>
|
<array>
|
||||||
<string>apple2gs.xml</string>
|
<string>apple2gs.xml</string>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2GS</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
|
@ -5884,7 +5884,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2P</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
<string>apple2_cass.xml</string>
|
<string>apple2_cass.xml</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
|
@ -5884,7 +5884,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2P</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
<string>apple2_cass.xml</string>
|
<string>apple2_cass.xml</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
|
@ -5944,7 +5944,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
<string>apple2_cass.xml</string>
|
<string>apple2_cass.xml</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
|
@ -6609,7 +6609,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2E</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
<string>cecflop.xml</string>
|
<string>cecflop.xml</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
|
@ -6609,7 +6609,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2E</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
<string>cecflop.xml</string>
|
<string>cecflop.xml</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
|
@ -6609,7 +6609,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2E</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
<string>cecflop.xml</string>
|
<string>cecflop.xml</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
|
@ -6609,7 +6609,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2E</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
<string>cecflop.xml</string>
|
<string>cecflop.xml</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
|
@ -6609,7 +6609,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2E</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
<string>cecflop.xml</string>
|
<string>cecflop.xml</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
|
@ -5884,7 +5884,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2P</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
<string>apple2_cass.xml</string>
|
<string>apple2_cass.xml</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
|
@ -5884,7 +5884,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2P</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
<string>apple2_cass.xml</string>
|
<string>apple2_cass.xml</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
|
@ -5884,7 +5884,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2P</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
<string>apple2_cass.xml</string>
|
<string>apple2_cass.xml</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
|
@ -5884,7 +5884,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2P</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
<string>apple2_cass.xml</string>
|
<string>apple2_cass.xml</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
|
@ -5884,7 +5884,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2P</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
<string>apple2_cass.xml</string>
|
<string>apple2_cass.xml</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
|
@ -445,7 +445,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2C</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
|
@ -5157,7 +5157,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2C</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
|
@ -5157,7 +5157,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2C</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
|
@ -5884,7 +5884,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2P</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
<string>apple2_cass.xml</string>
|
<string>apple2_cass.xml</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
|
@ -5884,7 +5884,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2P</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
<string>apple2_cass.xml</string>
|
<string>apple2_cass.xml</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
|
@ -5884,7 +5884,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2P</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
<string>apple2_cass.xml</string>
|
<string>apple2_cass.xml</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
|
@ -7672,7 +7672,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2E</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
|
@ -5884,7 +5884,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2P</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
<string>apple2_cass.xml</string>
|
<string>apple2_cass.xml</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
|
@ -7672,7 +7672,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2E</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
|
@ -5884,7 +5884,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2P</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
<string>apple2_cass.xml</string>
|
<string>apple2_cass.xml</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
<string>a2ap16</string>
|
<string>a2ap16</string>
|
||||||
<string>a2ap16a</string>
|
<string>a2ap16a</string>
|
||||||
<string>a2aplcrd</string>
|
<string>a2aplcrd</string>
|
||||||
|
<string>a2booti</string>
|
||||||
<string>a2bufgrapplerplus</string>
|
<string>a2bufgrapplerplus</string>
|
||||||
<string>a2bufgrapplerplusa</string>
|
<string>a2bufgrapplerplusa</string>
|
||||||
<string>a2cffa02</string>
|
<string>a2cffa02</string>
|
||||||
|
@ -154,6 +155,7 @@
|
||||||
<string>macsefd</string>
|
<string>macsefd</string>
|
||||||
<string>maxxi</string>
|
<string>maxxi</string>
|
||||||
<string>microeng</string>
|
<string>microeng</string>
|
||||||
|
<string>mockingboardd</string>
|
||||||
<string>mprof3</string>
|
<string>mprof3</string>
|
||||||
<string>nb_48gc</string>
|
<string>nb_48gc</string>
|
||||||
<string>nb_824gc</string>
|
<string>nb_824gc</string>
|
||||||
|
|
|
@ -5884,7 +5884,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2P</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
<string>apple2_cass.xml</string>
|
<string>apple2_cass.xml</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
|
@ -7672,7 +7672,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2E</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
|
@ -7672,7 +7672,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2E</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
|
@ -5884,7 +5884,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2P</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
<string>apple2_cass.xml</string>
|
<string>apple2_cass.xml</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
|
@ -5884,7 +5884,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2P</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
<string>apple2_cass.xml</string>
|
<string>apple2_cass.xml</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
|
@ -5884,7 +5884,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2P</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
<string>apple2_cass.xml</string>
|
<string>apple2_cass.xml</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
|
@ -6609,7 +6609,12 @@
|
||||||
<key>software</key>
|
<key>software</key>
|
||||||
<array>
|
<array>
|
||||||
<string>apple2_flop_clcracked.xml</string>
|
<string>apple2_flop_clcracked.xml</string>
|
||||||
<string>apple2_flop_orig.xml</string>
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>apple2_flop_orig.xml</string>
|
||||||
|
<key>filter</key>
|
||||||
|
<string>A2E</string>
|
||||||
|
</dict>
|
||||||
<string>apple2_flop_misc.xml</string>
|
<string>apple2_flop_misc.xml</string>
|
||||||
<string>cecflop.xml</string>
|
<string>cecflop.xml</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
|
@ -10,20 +10,37 @@
|
||||||
#define SoftwareList_h
|
#define SoftwareList_h
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
#import "AutocompleteControl.h"
|
||||||
|
|
||||||
@interface SoftwareList : NSObject
|
|
||||||
|
@interface SoftwareList : NSObject <AutocompleteItem>
|
||||||
@property NSString *name;
|
@property NSString *name;
|
||||||
@property NSString *title;
|
@property NSString *title;
|
||||||
@property NSArray *items;
|
@property NSArray *items;
|
||||||
|
|
||||||
|
-(SoftwareList *)filter: (NSString *)filter;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface Software : NSObject
|
@interface Software : NSObject <AutocompleteItem>
|
||||||
@property NSString *name;
|
@property NSString *name;
|
||||||
@property NSString *title;
|
@property NSString *title;
|
||||||
|
@property NSString *compatibility;
|
||||||
|
@property NSString *list;
|
||||||
|
|
||||||
|
-(NSString *)fullName;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface SoftwareSet : NSObject <NSFastEnumeration, AutoCompleteDelegate>
|
||||||
|
|
||||||
|
+(instancetype)softwareSetForMachine: (NSString *)machine;
|
||||||
|
-(BOOL)nameIsUnique: (NSString *)name;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
NSArray<SoftwareList *> *SoftwareListForMachine(NSString *machine);
|
//NSArray<SoftwareList *> *SoftwareListForMachine(NSString *machine);
|
||||||
|
|
||||||
|
|
||||||
#endif /* SoftwareList_h */
|
#endif /* SoftwareList_h */
|
||||||
|
|
|
@ -7,22 +7,174 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#include "Ample.h"
|
#include <wctype.h>
|
||||||
|
|
||||||
|
#include "Ample.h"
|
||||||
#import "SoftwareList.h"
|
#import "SoftwareList.h"
|
||||||
|
|
||||||
@implementation Software
|
@implementation Software
|
||||||
|
|
||||||
|
-(BOOL)filter: (NSString *)filter {
|
||||||
|
|
||||||
|
if (!_compatibility || ![_compatibility length]) return YES;
|
||||||
|
|
||||||
|
unichar *needle;
|
||||||
|
unichar *haystack;
|
||||||
|
NSUInteger needle_length;
|
||||||
|
NSUInteger haystack_length;
|
||||||
|
BOOL ok = NO;
|
||||||
|
|
||||||
|
haystack_length = [_compatibility length];
|
||||||
|
if (!haystack_length) return YES;
|
||||||
|
needle_length = [filter length];
|
||||||
|
if (!needle_length) return NO;
|
||||||
|
|
||||||
|
if (needle_length > haystack_length) return NO;
|
||||||
|
|
||||||
|
haystack = malloc((haystack_length + 1) * sizeof(unichar));
|
||||||
|
[_compatibility getCharacters: haystack range: NSMakeRange(0, haystack_length)];
|
||||||
|
|
||||||
|
needle = malloc((needle_length + 1) * sizeof(unichar));
|
||||||
|
[filter getCharacters: needle range: NSMakeRange(0, needle_length)];
|
||||||
|
|
||||||
|
haystack[haystack_length] = 0;
|
||||||
|
needle[needle_length] = 0;
|
||||||
|
|
||||||
|
NSUInteger i = 0;
|
||||||
|
unichar c;
|
||||||
|
do {
|
||||||
|
if (!memcmp(needle, haystack + i, sizeof(unichar) * needle_length)) {
|
||||||
|
i += needle_length;
|
||||||
|
c = haystack[i];
|
||||||
|
if (c == ',' || c == 0) { ok = YES; break; }
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
c = haystack[i++];
|
||||||
|
} while ( c && c != ',');
|
||||||
|
} while (c);
|
||||||
|
|
||||||
|
free(needle);
|
||||||
|
free(haystack);
|
||||||
|
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (nonnull NSAttributedString *)menuAttributedTitle {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)menuEnabled {
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)menuIsHeader {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (nonnull NSString *)menuTitle {
|
||||||
|
return _title;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(NSString *)fullName {
|
||||||
|
if (![_list length]) return _name;
|
||||||
|
return [NSString stringWithFormat: @"%@:%@", _list, _name];
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation SoftwareList
|
@implementation SoftwareList
|
||||||
|
|
||||||
|
-(SoftwareList *)filter: (NSString *)filter {
|
||||||
|
|
||||||
|
unichar *needle = NULL;
|
||||||
|
__block unichar *haystack = NULL;
|
||||||
|
NSUInteger needle_length = 0;
|
||||||
|
__block NSUInteger max_haystack_length = 0;
|
||||||
|
|
||||||
|
|
||||||
|
needle_length = [filter length];
|
||||||
|
if (!needle_length) return self;
|
||||||
|
|
||||||
|
needle = malloc(needle_length * sizeof(unichar) + sizeof(unichar));
|
||||||
|
[filter getCharacters: needle range: NSMakeRange(0, needle_length)];
|
||||||
|
needle[needle_length] = 0;
|
||||||
|
|
||||||
|
max_haystack_length = 127;
|
||||||
|
haystack = malloc(max_haystack_length * sizeof(unichar) + sizeof(unichar));
|
||||||
|
|
||||||
|
|
||||||
|
NSPredicate *p = [NSPredicate predicateWithBlock: ^BOOL(Software *o, NSDictionary *bindings){
|
||||||
|
|
||||||
|
NSString *s = [o compatibility];
|
||||||
|
NSUInteger length = [s length];
|
||||||
|
if (length == 0) return YES;
|
||||||
|
if (length < needle_length) return NO;
|
||||||
|
|
||||||
|
if (length > max_haystack_length) {
|
||||||
|
max_haystack_length = length;
|
||||||
|
haystack = realloc(haystack, sizeof(unichar ) * length + sizeof(unichar));
|
||||||
|
}
|
||||||
|
|
||||||
|
[s getCharacters: haystack range: NSMakeRange(0, length)];
|
||||||
|
haystack[length] = 0;
|
||||||
|
|
||||||
|
|
||||||
|
NSUInteger i = 0;
|
||||||
|
unichar c;
|
||||||
|
do {
|
||||||
|
if (!memcmp(needle, haystack + i, sizeof(unichar) * needle_length)) {
|
||||||
|
i += needle_length;
|
||||||
|
c = haystack[i];
|
||||||
|
if (c == ',' || c == 0) return YES;
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
c = haystack[i++];
|
||||||
|
} while ( c && c != ',');
|
||||||
|
} while (c);
|
||||||
|
return NO;
|
||||||
|
}];
|
||||||
|
|
||||||
|
NSArray *items = [_items filteredArrayUsingPredicate: p];
|
||||||
|
free(needle);
|
||||||
|
free(haystack);
|
||||||
|
|
||||||
|
if ([items count] == [_items count]) return self;
|
||||||
|
|
||||||
|
SoftwareList *rv = [SoftwareList new];
|
||||||
|
[rv setItems: items];
|
||||||
|
[rv setName: _name];
|
||||||
|
[rv setTitle: _title];
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
- (nonnull NSAttributedString *)menuAttributedTitle {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)menuEnabled {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)menuIsHeader {
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (nonnull NSString *)menuTitle {
|
||||||
|
return _title;
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@interface SoftwareListDelegate : NSObject<NSXMLParserDelegate> {
|
@interface SoftwareListDelegate : NSObject<NSXMLParserDelegate> {
|
||||||
unsigned _state;
|
unsigned _state;
|
||||||
NSString *_name;
|
NSString *_name;
|
||||||
NSString *_description;
|
NSString *_description;
|
||||||
|
NSString *_compatibility;
|
||||||
NSMutableArray *_array;
|
NSMutableArray *_array;
|
||||||
SoftwareList *_list;
|
SoftwareList *_list;
|
||||||
}
|
}
|
||||||
|
@ -82,6 +234,11 @@
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if ([@"sharedfeat" isEqualToString: elementName]) {
|
||||||
|
if ([@"compatibility" isEqualToString: [attributeDict objectForKey: @"name"]]) {
|
||||||
|
_compatibility = [attributeDict objectForKey: @"value"];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
|
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
|
||||||
|
@ -110,10 +267,13 @@
|
||||||
Software *s = [Software new];
|
Software *s = [Software new];
|
||||||
[s setTitle: _description];
|
[s setTitle: _description];
|
||||||
[s setName: _name];
|
[s setName: _name];
|
||||||
|
[s setCompatibility: _compatibility];
|
||||||
|
[s setList: [_list name]];
|
||||||
[_array addObject: s];
|
[_array addObject: s];
|
||||||
}
|
}
|
||||||
_name = nil;
|
_name = nil;
|
||||||
_description = nil;
|
_description = nil;
|
||||||
|
_compatibility = nil;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -160,6 +320,7 @@ NSArray<SoftwareList *> *SoftwareListForMachine(NSString *machine) {
|
||||||
if (!cache)
|
if (!cache)
|
||||||
cache = [NSCache new];
|
cache = [NSCache new];
|
||||||
|
|
||||||
|
if (!machine) return nil;
|
||||||
machine = InternString(machine);
|
machine = InternString(machine);
|
||||||
NSArray *a = [cache objectForKey: machine];
|
NSArray *a = [cache objectForKey: machine];
|
||||||
if (a) return a;
|
if (a) return a;
|
||||||
|
@ -172,19 +333,46 @@ NSArray<SoftwareList *> *SoftwareListForMachine(NSString *machine) {
|
||||||
NSArray *list = [d objectForKey: @"software"];
|
NSArray *list = [d objectForKey: @"software"];
|
||||||
|
|
||||||
NSMutableArray *tmp = [NSMutableArray new];
|
NSMutableArray *tmp = [NSMutableArray new];
|
||||||
for (NSString *xml in list) {
|
for (NSObject *o in list) {
|
||||||
SoftwareList *sw;
|
SoftwareList *sw;
|
||||||
NSURL *url = SupportDirectory();
|
NSURL *url = SupportDirectory();
|
||||||
url = [url URLByAppendingPathComponent: @"hash"];
|
|
||||||
url = [url URLByAppendingPathComponent: xml];
|
|
||||||
|
|
||||||
|
NSString *xml = nil;
|
||||||
NSError *error = nil;
|
NSString *filter = nil;
|
||||||
sw = LoadSoftwareList(url, &error);
|
if ([o isKindOfClass: [NSString class]]) {
|
||||||
if (error) {
|
xml = (NSString *)o;
|
||||||
NSLog(@"SoftwareListForMachine: %@ %@: %@", machine, xml, error);
|
} else if ([o isKindOfClass: [NSDictionary class]]) {
|
||||||
|
xml = [(NSDictionary *)o objectForKey: @"name"];
|
||||||
|
filter = [(NSDictionary *)o objectForKey: @"filter"];
|
||||||
|
} else if ([o isKindOfClass: [NSArray class]]) {
|
||||||
|
// [ xml, filter ]
|
||||||
|
xml = [(NSArray *)o objectAtIndex: 0];
|
||||||
|
filter = [(NSArray *)o objectAtIndex: 1];
|
||||||
|
}
|
||||||
|
else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!xml) continue;
|
||||||
|
|
||||||
|
xml = InternString(xml);
|
||||||
|
sw = [cache objectForKey: xml];
|
||||||
|
if (!sw) {
|
||||||
|
url = [url URLByAppendingPathComponent: @"hash"];
|
||||||
|
url = [url URLByAppendingPathComponent: xml];
|
||||||
|
|
||||||
|
|
||||||
|
NSError *error = nil;
|
||||||
|
sw = LoadSoftwareList(url, &error);
|
||||||
|
if (error) {
|
||||||
|
NSLog(@"SoftwareListForMachine: %@ %@: %@", machine, xml, error);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
[cache setObject: sw forKey: xml];
|
||||||
|
}
|
||||||
|
if (filter) {
|
||||||
|
sw = [sw filter: filter];
|
||||||
|
}
|
||||||
|
if (!sw) continue;
|
||||||
|
|
||||||
[tmp addObject: sw];
|
[tmp addObject: sw];
|
||||||
}
|
}
|
||||||
|
@ -202,3 +390,215 @@ NSArray<SoftwareList *> *SoftwareListForMachine(NSString *machine) {
|
||||||
return tmp;
|
return tmp;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@interface SoftwareSet () {
|
||||||
|
|
||||||
|
NSArray<SoftwareList *> *_items;
|
||||||
|
NSCountedSet *_set;
|
||||||
|
NSCache *_cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation SoftwareSet
|
||||||
|
|
||||||
|
-(NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(__unsafe_unretained id _Nullable [])buffer count:(NSUInteger)len {
|
||||||
|
return [_items countByEnumeratingWithState: state objects: buffer count: len];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)buildSet {
|
||||||
|
if (_set) return;
|
||||||
|
_set = [NSCountedSet new];
|
||||||
|
|
||||||
|
for (SoftwareList *list in _items) {
|
||||||
|
for (Software *s in [list items]) {
|
||||||
|
[_set addObject: [s name]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-(BOOL)nameIsUnique:(NSString *)name {
|
||||||
|
if (![name length]) return YES;
|
||||||
|
|
||||||
|
if (!_set) [self buildSet];
|
||||||
|
return [_set countForObject: name] <= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
+(instancetype)softwareSetForMachine:(NSString *)machine {
|
||||||
|
|
||||||
|
static NSCache *cache;
|
||||||
|
|
||||||
|
if (!cache)
|
||||||
|
cache = [NSCache new];
|
||||||
|
|
||||||
|
if (!machine) return nil;
|
||||||
|
machine = InternString(machine);
|
||||||
|
SoftwareSet *s= [cache objectForKey: machine];
|
||||||
|
if (s) return s;
|
||||||
|
|
||||||
|
NSBundle *bundle = [NSBundle mainBundle];
|
||||||
|
NSURL *url= [bundle URLForResource: machine withExtension: @"plist"];
|
||||||
|
|
||||||
|
NSDictionary *d = [NSDictionary dictionaryWithContentsOfURL: url];
|
||||||
|
if (!d) return nil;
|
||||||
|
NSArray *list = [d objectForKey: @"software"];
|
||||||
|
|
||||||
|
NSMutableArray *tmp = [NSMutableArray new];
|
||||||
|
for (NSObject *o in list) {
|
||||||
|
SoftwareList *sw;
|
||||||
|
NSURL *url = SupportDirectory();
|
||||||
|
|
||||||
|
NSString *xml = nil;
|
||||||
|
NSString *filter = nil;
|
||||||
|
if ([o isKindOfClass: [NSString class]]) {
|
||||||
|
xml = (NSString *)o;
|
||||||
|
} else if ([o isKindOfClass: [NSDictionary class]]) {
|
||||||
|
xml = [(NSDictionary *)o objectForKey: @"name"];
|
||||||
|
filter = [(NSDictionary *)o objectForKey: @"filter"];
|
||||||
|
} else if ([o isKindOfClass: [NSArray class]]) {
|
||||||
|
// [ xml, filter ]
|
||||||
|
xml = [(NSArray *)o objectAtIndex: 0];
|
||||||
|
filter = [(NSArray *)o objectAtIndex: 1];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!xml) continue;
|
||||||
|
|
||||||
|
xml = InternString(xml);
|
||||||
|
sw = [cache objectForKey: xml];
|
||||||
|
if (!sw) {
|
||||||
|
url = [url URLByAppendingPathComponent: @"hash"];
|
||||||
|
url = [url URLByAppendingPathComponent: xml];
|
||||||
|
|
||||||
|
|
||||||
|
NSError *error = nil;
|
||||||
|
sw = LoadSoftwareList(url, &error);
|
||||||
|
if (error) {
|
||||||
|
NSLog(@"SoftwareListForMachine: %@ %@: %@", machine, xml, error);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
[cache setObject: sw forKey: xml];
|
||||||
|
}
|
||||||
|
if (filter) {
|
||||||
|
sw = [sw filter: filter];
|
||||||
|
}
|
||||||
|
if (!sw) continue;
|
||||||
|
|
||||||
|
[tmp addObject: sw];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (![tmp count]) return nil;
|
||||||
|
|
||||||
|
s = [SoftwareSet new];
|
||||||
|
s->_items = tmp;
|
||||||
|
[cache setObject: s forKey: machine];
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (nonnull NSArray<id<AutocompleteItem>> *)autocomplete:(AutocompleteControl *)control completionsForItem:(id<AutocompleteItem>)item {
|
||||||
|
|
||||||
|
for (SoftwareList *list in _items) {
|
||||||
|
NSArray *items = [list items];
|
||||||
|
if ([items containsObject: item]) {
|
||||||
|
return @[ list, item ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (nonnull NSArray<id<AutocompleteItem>> *)autocomplete:(nonnull AutocompleteControl *)control completionsForString:(nonnull NSString *)string {
|
||||||
|
|
||||||
|
if (!_cache) {
|
||||||
|
_cache = [NSCache new];
|
||||||
|
[_cache setCountLimit: 10];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
enum { max_haystack_length = 256, max_needle_length = 256 };
|
||||||
|
|
||||||
|
unichar needle_data[max_needle_length];
|
||||||
|
|
||||||
|
if (!_items) return @[];
|
||||||
|
|
||||||
|
NSUInteger needle_length = [string length];
|
||||||
|
needle_length = MIN(needle_length, max_needle_length);
|
||||||
|
|
||||||
|
[string getCharacters: needle_data range: NSMakeRange(0, needle_length)];
|
||||||
|
|
||||||
|
for (NSUInteger i = 0; i < needle_length; ++i)
|
||||||
|
needle_data[i] = towlower(needle_data[i]);
|
||||||
|
|
||||||
|
string = InternString([NSString stringWithCharacters: needle_data length: needle_length]);
|
||||||
|
|
||||||
|
NSArray *a = [_cache objectForKey: string];
|
||||||
|
if (a) return a;
|
||||||
|
|
||||||
|
|
||||||
|
NSMutableArray *rv = [NSMutableArray new];
|
||||||
|
|
||||||
|
if (needle_length == 0) {
|
||||||
|
for(SoftwareList *list in _items) {
|
||||||
|
[rv addObject: list];
|
||||||
|
[rv addObjectsFromArray: [list items]];
|
||||||
|
}
|
||||||
|
[_cache setObject: rv forKey: string];
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if (needle_length < 2) return nil;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const unichar *needle_data_ptr = needle_data;
|
||||||
|
NSPredicate *p = [NSPredicate predicateWithBlock: ^BOOL(Software *o, NSDictionary *bindings){
|
||||||
|
// prefix match.
|
||||||
|
|
||||||
|
unichar haystack_data[max_haystack_length];
|
||||||
|
NSString *haystack;
|
||||||
|
NSUInteger length;
|
||||||
|
|
||||||
|
haystack = [o name];
|
||||||
|
|
||||||
|
length = [haystack length];
|
||||||
|
length = MIN(length, max_haystack_length);
|
||||||
|
if (length >= needle_length) {
|
||||||
|
[haystack getCharacters: haystack_data range: NSMakeRange(0, length)];
|
||||||
|
for (NSUInteger i = 0; i < length; ++i)
|
||||||
|
haystack_data[i] = towlower(haystack_data[i]);
|
||||||
|
if (!memcmp(haystack_data, needle_data_ptr, needle_length * sizeof(unichar))) return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
haystack = [o title];
|
||||||
|
length = [haystack length];
|
||||||
|
length = MIN(length, max_haystack_length);
|
||||||
|
if (length >= needle_length) {
|
||||||
|
[haystack getCharacters: haystack_data range: NSMakeRange(0, length)];
|
||||||
|
for (NSUInteger i = 0; i < length; ++i)
|
||||||
|
haystack_data[i] = towlower(haystack_data[i]);
|
||||||
|
if (!memcmp(haystack_data, needle_data_ptr, needle_length * sizeof(unichar))) return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NO;
|
||||||
|
}];
|
||||||
|
|
||||||
|
for (SoftwareList *list in _items) {
|
||||||
|
|
||||||
|
NSArray *items = [list items];
|
||||||
|
|
||||||
|
NSArray *tmp = [items filteredArrayUsingPredicate: p];
|
||||||
|
// add header ... ?
|
||||||
|
if (![tmp count]) continue;
|
||||||
|
[rv addObject: list]; // header
|
||||||
|
[rv addObjectsFromArray: tmp];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[_cache setObject: rv forKey: string];
|
||||||
|
return rv;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
|
@ -232,9 +232,15 @@ def find_media(parent, include_slots=False):
|
||||||
return media
|
return media
|
||||||
|
|
||||||
|
|
||||||
|
def one_software(x):
|
||||||
|
xml = x.get("name") + ".xml"
|
||||||
|
filter = x.get("filter")
|
||||||
|
if filter: return { "name": xml, "filter": filter }
|
||||||
|
return xml
|
||||||
|
|
||||||
def find_software(parent):
|
def find_software(parent):
|
||||||
swl = parent.findall("./softwarelist")
|
swl = parent.findall("./softwarelist")
|
||||||
return [x.get("name") + ".xml" for x in swl]
|
return [one_software(x) for x in swl]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue