mirror of
https://github.com/ksherlock/ample.git
synced 2024-11-19 02:11:08 +00:00
bookmark manager
This commit is contained in:
parent
0f3e6c8307
commit
4baf545245
@ -64,6 +64,8 @@
|
||||
B6152B5B25F5B57E00605E6E /* Media.m in Sources */ = {isa = PBXBuildFile; fileRef = B6152B5925F5B57E00605E6E /* Media.m */; };
|
||||
B615A99F26640940001FBF99 /* SlotView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B6E9A18125088B36005E7525 /* SlotView.xib */; };
|
||||
B615A9A026640A70001FBF99 /* SlotViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B6E9A17F25088B1B005E7525 /* SlotViewController.m */; };
|
||||
B63005332666D6940014C381 /* BookmarkManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B63005322666D6940014C381 /* BookmarkManager.m */; };
|
||||
B63005342666D6940014C381 /* BookmarkManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B63005322666D6940014C381 /* BookmarkManager.m */; };
|
||||
B6374AC4260EBBCF0045CA16 /* pty_shell.c in Sources */ = {isa = PBXBuildFile; fileRef = B6374AB6260EBB970045CA16 /* pty_shell.c */; };
|
||||
B6374AC5260EBC5A0045CA16 /* pty_shell in CopyFiles */ = {isa = PBXBuildFile; fileRef = B6374ABD260EBBC90045CA16 /* pty_shell */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
B6374AD1260ECB400045CA16 /* macclas2.plist in Resources */ = {isa = PBXBuildFile; fileRef = B6374AC9260ECB3F0045CA16 /* macclas2.plist */; };
|
||||
@ -375,6 +377,8 @@
|
||||
B6152B5525F4549F00605E6E /* Slot.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Slot.m; sourceTree = "<group>"; };
|
||||
B6152B5825F5B4F100605E6E /* Media.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Media.h; sourceTree = "<group>"; };
|
||||
B6152B5925F5B57E00605E6E /* Media.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Media.m; sourceTree = "<group>"; };
|
||||
B63005312666D6940014C381 /* BookmarkManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BookmarkManager.h; sourceTree = "<group>"; };
|
||||
B63005322666D6940014C381 /* BookmarkManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BookmarkManager.m; sourceTree = "<group>"; };
|
||||
B6374AB6260EBB970045CA16 /* pty_shell.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = pty_shell.c; sourceTree = "<group>"; };
|
||||
B6374ABD260EBBC90045CA16 /* pty_shell */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = pty_shell; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
B6374AC9260ECB3F0045CA16 /* macclas2.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = macclas2.plist; sourceTree = "<group>"; };
|
||||
@ -690,6 +694,8 @@
|
||||
B608E17E2502FE0C00D53465 /* TransparentScroller.m */,
|
||||
B66D0FE62611386B000902F1 /* SoftwareList.m */,
|
||||
B66D0FE926113AA8000902F1 /* SoftwareList.h */,
|
||||
B63005312666D6940014C381 /* BookmarkManager.h */,
|
||||
B63005322666D6940014C381 /* BookmarkManager.m */,
|
||||
B6BA563A251685DA00B0C47D /* Window Controllers */,
|
||||
B6B9EA652506A5550080E70D /* EjectButton.h */,
|
||||
B6B9EA642506A5550080E70D /* EjectButton.m */,
|
||||
@ -1154,6 +1160,7 @@
|
||||
B60A6E1424EE0AE2004B7EEF /* FlippedView.m in Sources */,
|
||||
B6BA258024E99BE9005FB8FF /* AppDelegate.m in Sources */,
|
||||
B6004DF024FB05D600D38596 /* LogWindowController.m in Sources */,
|
||||
B63005332666D6940014C381 /* BookmarkManager.m in Sources */,
|
||||
B66236A924FD9A34006CABD7 /* PreferencesWindowController.m in Sources */,
|
||||
B63C1F0F25B1447C0016A611 /* CheatSheetWindowController.m in Sources */,
|
||||
B64AF1F2250ECB2E00A09B9B /* DiskImagesWindowController.m in Sources */,
|
||||
@ -1183,6 +1190,7 @@
|
||||
B6E4B5B324FDE2670094A35C /* MediaViewController.m in Sources */,
|
||||
B64AF1F7250ED5E400A09B9B /* TableCellView.m in Sources */,
|
||||
B6E4B5B424FDE2670094A35C /* FlippedView.m in Sources */,
|
||||
B63005342666D6940014C381 /* BookmarkManager.m in Sources */,
|
||||
B615A9A026640A70001FBF99 /* SlotViewController.m in Sources */,
|
||||
B6665C15265A0E3E00254939 /* AutocompleteControl.m in Sources */,
|
||||
B6E4B5B524FDE2670094A35C /* AppDelegate.m in Sources */,
|
||||
|
34
Ample/BookmarkManager.h
Normal file
34
Ample/BookmarkManager.h
Normal file
@ -0,0 +1,34 @@
|
||||
//
|
||||
// BookmarkManager.h
|
||||
// Ample
|
||||
//
|
||||
// Created by Kelvin Sherlock on 6/1/2021.
|
||||
// Copyright © 2021 Kelvin Sherlock. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class NSMenu;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface BookmarkManager : NSObject
|
||||
|
||||
@property (weak) IBOutlet NSMenu *menu;
|
||||
|
||||
+(instancetype)sharedManager;
|
||||
|
||||
-(void)loadBookmarks;
|
||||
-(void)updateMenu;
|
||||
|
||||
-(BOOL)validateName: (NSString *)name;
|
||||
|
||||
-(BOOL)saveBookmark: (NSDictionary *)bookmark name: (NSString *)name;
|
||||
-(NSDictionary *)loadBookmarkFromURL: (NSURL *)url;
|
||||
|
||||
-(BOOL)saveDefault: (NSDictionary *)bookmark;
|
||||
-(NSDictionary *)loadDefault;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
214
Ample/BookmarkManager.m
Normal file
214
Ample/BookmarkManager.m
Normal file
@ -0,0 +1,214 @@
|
||||
//
|
||||
// BookmarkManager.m
|
||||
// Ample
|
||||
//
|
||||
// Created by Kelvin Sherlock on 6/1/2021.
|
||||
// Copyright © 2021 Kelvin Sherlock. All rights reserved.
|
||||
//
|
||||
|
||||
#import "BookmarkManager.h"
|
||||
#import "Ample.h"
|
||||
|
||||
@interface BookmarkManager () {
|
||||
NSArray<NSURL *> *_urls;
|
||||
NSURL *_bookmarkDirectory;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation BookmarkManager
|
||||
|
||||
static BookmarkManager *singleton = nil;
|
||||
|
||||
-(void)awakeFromNib {
|
||||
if (!singleton) singleton = self;
|
||||
}
|
||||
|
||||
+(instancetype)sharedManager {
|
||||
if (!singleton) singleton = [BookmarkManager new];
|
||||
return singleton;
|
||||
}
|
||||
|
||||
-(instancetype)init {
|
||||
if (singleton) return singleton;
|
||||
return [super init];
|
||||
}
|
||||
|
||||
-(NSURL *)bookmarkDirectory {
|
||||
|
||||
if (_bookmarkDirectory) return _bookmarkDirectory;
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
|
||||
NSURL *url = SupportDirectory();
|
||||
url = [url URLByAppendingPathComponent: @"Bookmarks"];
|
||||
NSError *error = nil;
|
||||
[fm createDirectoryAtURL: url withIntermediateDirectories: YES attributes: nil error: &error];
|
||||
if (error) NSLog(@"%@", error);
|
||||
_bookmarkDirectory = url;
|
||||
return url;
|
||||
}
|
||||
|
||||
/* disallow leading .
|
||||
* disallow : or / characters.
|
||||
*/
|
||||
-(BOOL)validateName: (NSString *)name {
|
||||
|
||||
enum { kMaxLength = 128 };
|
||||
unichar buffer[kMaxLength];
|
||||
NSUInteger length = [name length];
|
||||
if (length == 0 || length > kMaxLength) return NO;
|
||||
[name getCharacters: buffer range: NSMakeRange(0, length)];
|
||||
if (buffer[0] == '.') return NO;
|
||||
for (unsigned i = 0; i < length; ++i) {
|
||||
unichar c = buffer[i];
|
||||
if (c == ':' || c == '/') return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
-(NSDictionary *)loadDefault {
|
||||
NSURL *url = [self bookmarkDirectory];
|
||||
url = [url URLByAppendingPathComponent: @".Default"];
|
||||
|
||||
NSDictionary *d;
|
||||
|
||||
if (@available(macOS 10.13, *)) {
|
||||
NSError *error = nil;
|
||||
d = [NSDictionary dictionaryWithContentsOfURL: url error: &error];
|
||||
if (!d) NSLog(@"Error loading %@: %@", url, error);
|
||||
} else {
|
||||
d = [NSDictionary dictionaryWithContentsOfURL: url];
|
||||
if (!d) NSLog(@"Error loading %@", url);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
/* save as .Default */
|
||||
-(BOOL)saveDefault: (NSDictionary *)bookmark {
|
||||
|
||||
NSURL *url = [self bookmarkDirectory];
|
||||
url = [url URLByAppendingPathComponent: @".Default"];
|
||||
|
||||
NSError *error = nil;
|
||||
BOOL ok = NO;
|
||||
if (@available(macOS 10.13, *)) {
|
||||
ok = [bookmark writeToURL: url error: &error];
|
||||
if (!ok) NSLog(@"%@", error);
|
||||
} else {
|
||||
ok = [bookmark writeToURL: url atomically: YES];
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
-(BOOL)saveBookmark: (NSDictionary *)bookmark name: (NSString *)name {
|
||||
|
||||
NSError *error;
|
||||
NSData *data = [NSPropertyListSerialization dataWithPropertyList: bookmark
|
||||
format: NSPropertyListXMLFormat_v1_0
|
||||
options: 0
|
||||
error: &error];
|
||||
|
||||
|
||||
|
||||
NSURL *base = [self bookmarkDirectory];
|
||||
|
||||
NSURL *url = [base URLByAppendingPathComponent: name];
|
||||
|
||||
BOOL ok = [data writeToURL: url options: NSDataWritingWithoutOverwriting error: &error];
|
||||
|
||||
if (!ok) {
|
||||
for (unsigned i = 1 ; i < 100; ++i) {
|
||||
NSString *tmp = [name stringByAppendingFormat: @"(%d)", i];
|
||||
[base URLByAppendingPathComponent: tmp];
|
||||
|
||||
ok = [data writeToURL: url options: NSDataWritingWithoutOverwriting error: &error];
|
||||
if (ok) {
|
||||
name = tmp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ok) return NO;
|
||||
|
||||
if (!_menu) return YES; // ?
|
||||
|
||||
NSUInteger ix = [_urls indexOfObjectPassingTest: ^BOOL(NSURL *object, NSUInteger index, BOOL *stop){
|
||||
NSString *path = [object lastPathComponent];
|
||||
return [name caseInsensitiveCompare: path] == NSOrderedAscending;
|
||||
}];
|
||||
|
||||
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle: name action: @selector(loadBookmark:) keyEquivalent: @""];
|
||||
[item setRepresentedObject: url];
|
||||
|
||||
if (ix == NSNotFound) {
|
||||
_urls = [_urls arrayByAddingObject: url];
|
||||
[_menu addItem: item];
|
||||
} else {
|
||||
|
||||
NSInteger n = [_menu numberOfItems];
|
||||
[_menu insertItem: item atIndex: n - [_urls count] + ix];
|
||||
NSMutableArray *tmp = [_urls mutableCopy];
|
||||
|
||||
[tmp insertObject: url atIndex: ix];
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
-(NSDictionary *)loadBookmarkFromURL: (NSURL *)url {
|
||||
|
||||
NSDictionary *d;
|
||||
|
||||
if (@available(macOS 10.13, *)) {
|
||||
NSError *error = nil;
|
||||
d = [NSDictionary dictionaryWithContentsOfURL: url error: &error];
|
||||
if (!d) NSLog(@"Error loading %@: %@", url, error);
|
||||
} else {
|
||||
d = [NSDictionary dictionaryWithContentsOfURL: url];
|
||||
if (!d) NSLog(@"Error loading %@", url);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
-(void)loadBookmarks {
|
||||
|
||||
NSURL *url = [self bookmarkDirectory];
|
||||
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
|
||||
NSError *error = nil;
|
||||
|
||||
NSArray *files = [fm contentsOfDirectoryAtURL: url
|
||||
includingPropertiesForKeys: nil
|
||||
options: NSDirectoryEnumerationSkipsHiddenFiles
|
||||
error: &error];
|
||||
|
||||
// bleh, has to create 2 new NSStrings for every comparison
|
||||
files = [files sortedArrayUsingComparator: ^(NSURL *a, NSURL *b){
|
||||
NSString *aa = [a lastPathComponent];
|
||||
NSString *bb = [b lastPathComponent];
|
||||
return [aa caseInsensitiveCompare: bb];
|
||||
}];
|
||||
|
||||
|
||||
_urls = files;
|
||||
}
|
||||
|
||||
-(void)updateMenu {
|
||||
|
||||
NSArray *menus = [_menu itemArray];
|
||||
for (NSMenuItem *item in [menus reverseObjectEnumerator]) {
|
||||
if ([item tag] == 0xdeadbeef) [_menu removeItem: item];
|
||||
}
|
||||
for (NSURL *url in _urls) {
|
||||
NSString *title = [url lastPathComponent]; // [[url lastPathComponent] stringByDeletingPathExtension];
|
||||
|
||||
NSMenuItem *item = [_menu addItemWithTitle: title action: @selector(loadBookmark:) keyEquivalent: @""];
|
||||
[item setRepresentedObject: url];
|
||||
[item setTag: 0xdeadbeef];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
Loading…
Reference in New Issue
Block a user