mirror of
https://github.com/ksherlock/ample.git
synced 2024-10-31 15:04:56 +00:00
215 lines
6.0 KiB
Objective-C
215 lines
6.0 KiB
Objective-C
//
|
|
// 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];
|
|
url = [base URLByAppendingPathComponent: tmp];
|
|
|
|
ok = [data writeToURL: url options: NSDataWritingWithoutOverwriting error: &error];
|
|
if (ok) {
|
|
name = tmp;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!ok) return NO;
|
|
|
|
if (!_menu) return YES; // ?
|
|
|
|
NSUInteger ix = [_urls indexOfObjectPassingTest: ^BOOL(NSURL *object, NSUInteger index, BOOL *stop){
|
|
NSString *path = [object lastPathComponent];
|
|
return [name caseInsensitiveCompare: path] == NSOrderedAscending;
|
|
}];
|
|
|
|
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle: name action: @selector(bookmarkMenu:) keyEquivalent: @""];
|
|
[item setRepresentedObject: url];
|
|
|
|
if (ix == NSNotFound) {
|
|
_urls = [_urls arrayByAddingObject: url];
|
|
[_menu addItem: item];
|
|
} else {
|
|
|
|
NSInteger n = [_menu numberOfItems];
|
|
[_menu insertItem: item atIndex: n - [_urls count] + ix];
|
|
NSMutableArray *tmp = [_urls mutableCopy];
|
|
|
|
[tmp insertObject: url atIndex: ix];
|
|
}
|
|
|
|
return YES;
|
|
}
|
|
|
|
-(NSDictionary *)loadBookmarkFromURL: (NSURL *)url {
|
|
|
|
NSDictionary *d;
|
|
|
|
if (@available(macOS 10.13, *)) {
|
|
NSError *error = nil;
|
|
d = [NSDictionary dictionaryWithContentsOfURL: url error: &error];
|
|
if (!d) NSLog(@"Error loading %@: %@", url, error);
|
|
} else {
|
|
d = [NSDictionary dictionaryWithContentsOfURL: url];
|
|
if (!d) NSLog(@"Error loading %@", url);
|
|
}
|
|
return d;
|
|
}
|
|
|
|
|
|
-(void)loadBookmarks {
|
|
|
|
NSURL *url = [self bookmarkDirectory];
|
|
|
|
NSFileManager *fm = [NSFileManager defaultManager];
|
|
|
|
NSError *error = nil;
|
|
|
|
NSArray *files = [fm contentsOfDirectoryAtURL: url
|
|
includingPropertiesForKeys: nil
|
|
options: NSDirectoryEnumerationSkipsHiddenFiles
|
|
error: &error];
|
|
|
|
// bleh, has to create 2 new NSStrings for every comparison
|
|
files = [files sortedArrayUsingComparator: ^(NSURL *a, NSURL *b){
|
|
NSString *aa = [a lastPathComponent];
|
|
NSString *bb = [b lastPathComponent];
|
|
return [aa caseInsensitiveCompare: bb];
|
|
}];
|
|
|
|
|
|
_urls = files;
|
|
}
|
|
|
|
-(void)updateMenu {
|
|
|
|
NSArray *menus = [_menu itemArray];
|
|
for (NSMenuItem *item in [menus reverseObjectEnumerator]) {
|
|
if ([item tag] == 0xdeadbeef) [_menu removeItem: item];
|
|
}
|
|
for (NSURL *url in _urls) {
|
|
NSString *title = [url lastPathComponent]; // [[url lastPathComponent] stringByDeletingPathExtension];
|
|
|
|
NSMenuItem *item = [_menu addItemWithTitle: title action: @selector(bookmarkMenu:) keyEquivalent: @""];
|
|
[item setRepresentedObject: url];
|
|
[item setTag: 0xdeadbeef];
|
|
}
|
|
}
|
|
|
|
@end
|