- Display playlist total duration in units up to weeks and down to just seconds, and only pluralize units as necessary - Major change: Implemented a SQLite disk backed playlist, track data, and queue storage system, which will be synchronized from the player in real time, and will hopefully survive system or app crashes. Existing plist playlist will be imported on first run, and removed on shutdown.
627 lines
21 KiB
Objective-C
627 lines
21 KiB
Objective-C
#import "AppController.h"
|
|
#import "FileTreeController.h"
|
|
#import "FileTreeViewController.h"
|
|
#import "FileTreeOutlineView.h"
|
|
#import "PlaybackController.h"
|
|
#import "PlaylistController.h"
|
|
#import "PlaylistView.h"
|
|
#import "PlaylistEntry.h"
|
|
#import "PlaylistLoader.h"
|
|
#import "OpenURLPanel.h"
|
|
#import "SpotlightWindowController.h"
|
|
#import "StringToURLTransformer.h"
|
|
#import "FontSizetoLineHeightTransformer.h"
|
|
#import "Cog-Swift.h"
|
|
#import "PathNode.h"
|
|
#import <CogAudio/Status.h>
|
|
|
|
#import "Logging.h"
|
|
#import "MiniModeMenuTitleTransformer.h"
|
|
#import "DualWindow.h"
|
|
|
|
#import <MASShortcut/Shortcut.h>
|
|
#import "Shortcuts.h"
|
|
|
|
void* kAppControllerContext = &kAppControllerContext;
|
|
|
|
|
|
@implementation AppController {
|
|
BOOL _isFullToolbarStyle;
|
|
}
|
|
|
|
+ (void)initialize
|
|
{
|
|
// Register transformers
|
|
NSValueTransformer *stringToURLTransformer = [[StringToURLTransformer alloc] init];
|
|
[NSValueTransformer setValueTransformer:stringToURLTransformer
|
|
forName:@"StringToURLTransformer"];
|
|
|
|
NSValueTransformer *fontSizetoLineHeightTransformer =
|
|
[[FontSizetoLineHeightTransformer alloc] init];
|
|
[NSValueTransformer setValueTransformer:fontSizetoLineHeightTransformer
|
|
forName:@"FontSizetoLineHeightTransformer"];
|
|
|
|
NSValueTransformer *miniModeMenuTitleTransformer = [[MiniModeMenuTitleTransformer alloc] init];
|
|
[NSValueTransformer setValueTransformer:miniModeMenuTitleTransformer
|
|
forName:@"MiniModeMenuTitleTransformer"];
|
|
}
|
|
|
|
|
|
- (id)init
|
|
{
|
|
self = [super init];
|
|
if (self)
|
|
{
|
|
[self initDefaults];
|
|
|
|
queue = [[NSOperationQueue alloc]init];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (IBAction)openFiles:(id)sender
|
|
{
|
|
NSOpenPanel *p;
|
|
|
|
p = [NSOpenPanel openPanel];
|
|
|
|
[p setAllowedFileTypes:[playlistLoader acceptableFileTypes]];
|
|
[p setCanChooseDirectories:YES];
|
|
[p setAllowsMultipleSelection:YES];
|
|
[p setResolvesAliases:YES];
|
|
|
|
[p beginSheetModalForWindow:mainWindow completionHandler:^(NSInteger result) {
|
|
if ( result == NSModalResponseOK ) {
|
|
[self->playlistLoader willInsertURLs:[p URLs] origin:URLOriginInternal];
|
|
[self->playlistLoader didInsertURLs:[self->playlistLoader addURLs:[p URLs] sort:YES] origin:URLOriginInternal];
|
|
} else {
|
|
[p close];
|
|
}
|
|
}];
|
|
}
|
|
|
|
- (IBAction)savePlaylist:(id)sender
|
|
{
|
|
NSSavePanel *p;
|
|
|
|
p = [NSSavePanel savePanel];
|
|
|
|
/* Yes, this is deprecated. Yes, this is required to give the dialog
|
|
* a default set of filename extensions to save, including adding an
|
|
* extension if the user does not supply one. */
|
|
[p setAllowedFileTypes:[NSArray arrayWithObjects:@"m3u", @"pls", nil]];
|
|
|
|
[p beginSheetModalForWindow:mainWindow completionHandler:^(NSInteger result) {
|
|
if ( result == NSModalResponseOK ) {
|
|
[self->playlistLoader save:[[p URL] path]];
|
|
} else {
|
|
[p close];
|
|
}
|
|
}];
|
|
}
|
|
|
|
- (IBAction)openURL:(id)sender
|
|
{
|
|
OpenURLPanel *p;
|
|
|
|
p = [OpenURLPanel openURLPanel];
|
|
|
|
[p beginSheetWithWindow:mainWindow delegate:self didEndSelector:@selector(openURLPanelDidEnd:returnCode:contextInfo:) contextInfo:nil];
|
|
}
|
|
|
|
- (void)openURLPanelDidEnd:(OpenURLPanel *)panel returnCode:(int)returnCode contextInfo:(void *)contextInfo
|
|
{
|
|
if (returnCode == NSModalResponseOK)
|
|
{
|
|
[playlistLoader willInsertURLs:[NSArray arrayWithObject:[panel url]] origin:URLOriginInternal];
|
|
[playlistLoader didInsertURLs:[playlistLoader addURLs:[NSArray arrayWithObject:[panel url]] sort:NO] origin:URLOriginInternal];
|
|
}
|
|
}
|
|
|
|
- (IBAction)delEntries:(id)sender
|
|
{
|
|
[playlistController remove:self];
|
|
}
|
|
|
|
- (PlaylistEntry *)currentEntry
|
|
{
|
|
return [playlistController currentEntry];
|
|
}
|
|
|
|
- (BOOL)application:(NSApplication *)sender delegateHandlesKey:(NSString *)key
|
|
{
|
|
return [key isEqualToString:@"currentEntry"] || [key isEqualToString:@"play"];
|
|
}
|
|
|
|
- (void)awakeFromNib
|
|
{
|
|
[[totalTimeField cell] setBackgroundStyle:NSBackgroundStyleRaised];
|
|
|
|
[self.infoButton setToolTip:NSLocalizedString(@"InfoButtonTooltip", @"")];
|
|
[self.infoButtonMini setToolTip:NSLocalizedString(@"InfoButtonTooltip", @"")];
|
|
[shuffleButton setToolTip:NSLocalizedString(@"ShuffleButtonTooltip", @"")];
|
|
[repeatButton setToolTip:NSLocalizedString(@"RepeatButtonTooltip", @"")];
|
|
[randomizeButton setToolTip:NSLocalizedString(@"RandomizeButtonTooltip", @"")];
|
|
[fileButton setToolTip:NSLocalizedString(@"FileButtonTooltip", @"")];
|
|
|
|
[self registerHotKeys];
|
|
|
|
(void) [spotlightWindowController init];
|
|
|
|
[[playlistController undoManager] disableUndoRegistration];
|
|
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
|
|
NSString *basePath = [[paths firstObject] stringByAppendingPathComponent:@"Cog"];
|
|
|
|
NSString *dbFilename = @"Default.sqlite";
|
|
|
|
NSString *oldFilename = @"Default.m3u";
|
|
NSString *newFilename = @"Default.xml";
|
|
|
|
if ([[NSFileManager defaultManager] fileExistsAtPath:[basePath stringByAppendingPathComponent:dbFilename]])
|
|
{
|
|
[playlistLoader addDatabase];
|
|
}
|
|
else if ([[NSFileManager defaultManager] fileExistsAtPath:[basePath stringByAppendingPathComponent:newFilename]])
|
|
{
|
|
[playlistLoader addURL:[NSURL fileURLWithPath:[basePath stringByAppendingPathComponent:newFilename]]];
|
|
}
|
|
else
|
|
{
|
|
[playlistLoader addURL:[NSURL fileURLWithPath:[basePath stringByAppendingPathComponent:oldFilename]]];
|
|
}
|
|
|
|
[[playlistController undoManager] enableUndoRegistration];
|
|
|
|
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"resumePlaybackOnStartup"])
|
|
{
|
|
int lastStatus = (int) [[NSUserDefaults standardUserDefaults] integerForKey:@"lastPlaybackStatus"];
|
|
int lastIndex = (int) [[NSUserDefaults standardUserDefaults] integerForKey:@"lastTrackPlaying"];
|
|
|
|
if (lastStatus != CogStatusStopped && lastIndex >= 0)
|
|
{
|
|
[playbackController playEntryAtIndex:lastIndex startPaused:(lastStatus == CogStatusPaused)];
|
|
[playbackController seek:[NSNumber numberWithDouble:[[NSUserDefaults standardUserDefaults] floatForKey:@"lastTrackPosition"]]];
|
|
}
|
|
}
|
|
|
|
// Restore mini mode
|
|
[self setMiniMode:[[NSUserDefaults standardUserDefaults] boolForKey:@"miniMode"]];
|
|
|
|
[self setToolbarStyle:[[NSUserDefaults standardUserDefaults] boolForKey:@"toolbarStyleFull"]];
|
|
|
|
[self setFloatingMiniWindow:[[NSUserDefaults standardUserDefaults]
|
|
boolForKey:@"floatingMiniWindow"]];
|
|
|
|
// We need file tree view to restore its state here
|
|
// so attempt to access file tree view controller's root view
|
|
// to force it to read nib and create file tree view for us
|
|
//
|
|
// TODO: there probably is a more elegant way to do all this
|
|
// but i'm too stupid/tired to figure it out now
|
|
[fileTreeViewController view];
|
|
|
|
FileTreeOutlineView* outlineView = [fileTreeViewController outlineView];
|
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(nodeExpanded:) name:NSOutlineViewItemDidExpandNotification object:outlineView];
|
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(nodeCollapsed:) name:NSOutlineViewItemDidCollapseNotification object:outlineView];
|
|
|
|
NSArray *expandedNodesArray = [[NSUserDefaults standardUserDefaults] valueForKey:@"fileTreeViewExpandedNodes"];
|
|
|
|
if (expandedNodesArray)
|
|
{
|
|
expandedNodes = [[NSMutableSet alloc] initWithArray:expandedNodesArray];
|
|
}
|
|
else
|
|
{
|
|
expandedNodes = [[NSMutableSet alloc] init];
|
|
}
|
|
|
|
DLog(@"Nodes to expand: %@", [expandedNodes description]);
|
|
|
|
DLog(@"Num of rows: %ld", [outlineView numberOfRows]);
|
|
|
|
if (!outlineView)
|
|
{
|
|
DLog(@"outlineView is NULL!");
|
|
}
|
|
|
|
[outlineView reloadData];
|
|
|
|
for (NSInteger i=0; i<[outlineView numberOfRows]; i++)
|
|
{
|
|
PathNode *pn = [outlineView itemAtRow:i];
|
|
NSString *str = [[pn URL] absoluteString];
|
|
|
|
if ([expandedNodes containsObject:str])
|
|
{
|
|
[outlineView expandItem:pn];
|
|
}
|
|
}
|
|
|
|
[self addObserver:self
|
|
forKeyPath:@"playlistController.currentEntry"
|
|
options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
|
|
context:kAppControllerContext];
|
|
}
|
|
|
|
- (void)observeValueForKeyPath:(NSString *)keyPath
|
|
ofObject:(id)object
|
|
change:(NSDictionary<NSKeyValueChangeKey,id> *)change
|
|
context:(void *)context {
|
|
if (context != kAppControllerContext) {
|
|
return;
|
|
}
|
|
|
|
if ([keyPath isEqualToString:@"playlistController.currentEntry"]) {
|
|
PlaylistEntry *entry = playlistController.currentEntry;
|
|
if (!entry) {
|
|
miniWindow.title = @"Cog";
|
|
mainWindow.title = @"Cog";
|
|
if (@available(macOS 11.0, *)) {
|
|
miniWindow.subtitle = @"";
|
|
mainWindow.subtitle = @"";
|
|
}
|
|
|
|
self.infoButton.imageScaling = NSImageScaleNone;
|
|
self.infoButton.image = [NSImage imageNamed:@"infoTemplate"];
|
|
self.infoButtonMini.imageScaling = NSImageScaleNone;
|
|
self.infoButtonMini.image = [NSImage imageNamed:@"infoTemplate"];
|
|
}
|
|
|
|
if (entry.albumArt) {
|
|
self.infoButton.imageScaling = NSImageScaleProportionallyUpOrDown;
|
|
self.infoButton.image = playlistController.currentEntry.albumArt;
|
|
self.infoButtonMini.imageScaling = NSImageScaleProportionallyUpOrDown;
|
|
self.infoButtonMini.image = playlistController.currentEntry.albumArt;
|
|
} else {
|
|
self.infoButton.imageScaling = NSImageScaleNone;
|
|
self.infoButton.image = [NSImage imageNamed:@"infoTemplate"];
|
|
self.infoButtonMini.imageScaling = NSImageScaleNone;
|
|
self.infoButtonMini.image = [NSImage imageNamed:@"infoTemplate"];
|
|
}
|
|
|
|
if (@available(macOS 11.0, *)) {
|
|
NSString *title = @"Cog";
|
|
if (entry.title) {
|
|
title = entry.title;
|
|
}
|
|
miniWindow.title = title;
|
|
mainWindow.title = title;
|
|
|
|
NSString *subtitle = @"";
|
|
NSMutableArray<NSString *> *subtitleItems = [NSMutableArray array];
|
|
if (entry.album && ![entry.album isEqualToString:@""]) {
|
|
[subtitleItems addObject:entry.album];
|
|
}
|
|
if (entry.artist && ![entry.artist isEqualToString:@""]) {
|
|
[subtitleItems addObject:entry.artist];
|
|
}
|
|
|
|
if ([subtitleItems count]) {
|
|
subtitle = [subtitleItems componentsJoinedByString:@" - "];
|
|
}
|
|
|
|
miniWindow.subtitle = subtitle;
|
|
mainWindow.subtitle = subtitle;
|
|
} else {
|
|
NSString *title = @"Cog";
|
|
if (entry.display) {
|
|
title = entry.display;
|
|
}
|
|
miniWindow.title = title;
|
|
mainWindow.title = title;
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)nodeExpanded:(NSNotification*)notification
|
|
{
|
|
PathNode* node = [[notification userInfo] objectForKey:@"NSObject"];
|
|
NSString* url = [[node URL] absoluteString];
|
|
|
|
[expandedNodes addObject:url];
|
|
}
|
|
|
|
- (void)nodeCollapsed:(NSNotification*)notification
|
|
{
|
|
PathNode* node = [[notification userInfo] objectForKey:@"NSObject"];
|
|
NSString* url = [[node URL] absoluteString];
|
|
|
|
[expandedNodes removeObject:url];
|
|
}
|
|
|
|
- (void)applicationWillTerminate:(NSNotification *)aNotification
|
|
{
|
|
CogStatus currentStatus = [playbackController playbackStatus];
|
|
NSInteger lastTrackPlaying = -1;
|
|
double lastTrackPosition = 0;
|
|
|
|
if (currentStatus == CogStatusStopping)
|
|
currentStatus = CogStatusStopped;
|
|
|
|
[[NSUserDefaults standardUserDefaults] setInteger:currentStatus forKey:@"lastPlaybackStatus"];
|
|
|
|
if (currentStatus != CogStatusStopped)
|
|
{
|
|
PlaylistEntry * pe = [playlistController currentEntry];
|
|
lastTrackPlaying = [pe index];
|
|
lastTrackPosition = [pe currentPosition];
|
|
}
|
|
|
|
[[NSUserDefaults standardUserDefaults] setInteger:lastTrackPlaying forKey:@"lastTrackPlaying"];
|
|
[[NSUserDefaults standardUserDefaults] setDouble:lastTrackPosition forKey:@"lastTrackPosition"];
|
|
|
|
[playbackController stop:self];
|
|
|
|
NSFileManager *fileManager = [NSFileManager defaultManager];
|
|
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
|
|
NSString *folder = [[paths firstObject] stringByAppendingPathComponent:@"Cog"];
|
|
|
|
if ([fileManager fileExistsAtPath: folder] == NO)
|
|
{
|
|
[fileManager createDirectoryAtPath: folder withIntermediateDirectories:NO attributes:nil error:nil];
|
|
}
|
|
|
|
[playlistController clearFilterPredicate:self];
|
|
|
|
NSString * fileName = @"Default.xml";
|
|
|
|
NSError *error;
|
|
[[NSFileManager defaultManager] removeItemAtPath:[folder stringByAppendingPathComponent:fileName] error:&error];
|
|
|
|
fileName = @"Default.m3u";
|
|
|
|
[[NSFileManager defaultManager] removeItemAtPath:[folder stringByAppendingPathComponent:fileName] error:&error];
|
|
|
|
DLog(@"Saving expanded nodes: %@", [expandedNodes description]);
|
|
|
|
[[NSUserDefaults standardUserDefaults] setValue:[expandedNodes allObjects] forKey:@"fileTreeViewExpandedNodes"];
|
|
// Workaround window not restoring it's size and position.
|
|
[miniWindow setContentSize:NSMakeSize(miniWindow.frame.size.width, 1)];
|
|
}
|
|
|
|
- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag
|
|
{
|
|
if (flag == NO)
|
|
[mainWindow makeKeyAndOrderFront:self]; // TODO: do we really need this? We never close the main window.
|
|
|
|
for(NSWindow* win in [NSApp windows]) // Maximizing all windows
|
|
if([win isMiniaturized])
|
|
[win deminiaturize:self];
|
|
|
|
return NO;
|
|
}
|
|
|
|
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
|
|
{
|
|
NSArray* urls = [NSArray arrayWithObject:[NSURL fileURLWithPath:filename]];
|
|
[playlistLoader willInsertURLs:urls origin:URLOriginExternal];
|
|
[playlistLoader didInsertURLs:[playlistLoader addURLs:urls sort:NO] origin:URLOriginExternal];
|
|
return YES;
|
|
}
|
|
|
|
- (void)application:(NSApplication *)theApplication openFiles:(NSArray *)filenames
|
|
{
|
|
//Need to convert to urls
|
|
NSMutableArray *urls = [NSMutableArray array];
|
|
|
|
for (NSString *filename in filenames)
|
|
{
|
|
[urls addObject:[NSURL fileURLWithPath:filename]];
|
|
}
|
|
[playlistLoader willInsertURLs:urls origin:URLOriginExternal];
|
|
[playlistLoader didInsertURLs:[playlistLoader addURLs:urls sort:YES] origin:URLOriginExternal];
|
|
[theApplication replyToOpenOrPrint:NSApplicationDelegateReplySuccess];
|
|
}
|
|
|
|
- (IBAction)openLiberapayPage:(id)sender
|
|
{
|
|
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://liberapay.com/kode54"]];
|
|
}
|
|
|
|
- (IBAction)openPaypalPage:(id)sender
|
|
{
|
|
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://www.paypal.com/paypalme/kode54"]];
|
|
}
|
|
|
|
- (IBAction)openKofiPage:(id)sender
|
|
{
|
|
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://ko-fi.com/kode54"]];
|
|
}
|
|
|
|
|
|
- (IBAction)feedback:(id)sender
|
|
{
|
|
NSString *version = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
|
|
|
|
NSArray<NSURLQueryItem *> *query = @[
|
|
[NSURLQueryItem queryItemWithName:@"labels" value:@"bug"],
|
|
[NSURLQueryItem queryItemWithName:@"template" value:@"bug_report.md"],
|
|
[NSURLQueryItem queryItemWithName:@"title"
|
|
value:[NSString stringWithFormat:@"[Cog %@] ", version]]
|
|
];
|
|
NSURLComponents *components =
|
|
[NSURLComponents componentsWithString:@"https://github.com/losnoco/Cog/issues/new"];
|
|
|
|
components.queryItems = query;
|
|
|
|
[[NSWorkspace sharedWorkspace] openURL:components.URL];
|
|
}
|
|
|
|
- (void)initDefaults
|
|
{
|
|
NSMutableDictionary *userDefaultsValuesDict = [NSMutableDictionary dictionary];
|
|
|
|
// Font defaults
|
|
float fFontSize = [NSFont systemFontSizeForControlSize:NSControlSizeSmall];
|
|
NSNumber *fontSize = [NSNumber numberWithFloat:fFontSize];
|
|
[userDefaultsValuesDict setObject:fontSize forKey:@"fontSize"];
|
|
|
|
NSString *feedURLdefault = @"https://f.losno.co/cog/mercury.xml";
|
|
[userDefaultsValuesDict setObject:feedURLdefault forKey:@"SUFeedURL"];
|
|
|
|
[userDefaultsValuesDict setObject:@"clearAndPlay" forKey:@"openingFilesBehavior"];
|
|
[userDefaultsValuesDict setObject:@"enqueue" forKey:@"openingFilesAlteredBehavior"];
|
|
|
|
[userDefaultsValuesDict setObject:@"albumGainWithPeak" forKey:@"volumeScaling"];
|
|
|
|
[userDefaultsValuesDict setObject:@"cubic" forKey:@"resampling"];
|
|
|
|
[userDefaultsValuesDict setObject:[NSNumber numberWithInteger:CogStatusStopped] forKey:@"lastPlaybackStatus"];
|
|
[userDefaultsValuesDict setObject:[NSNumber numberWithInteger:-1] forKey:@"lastTrackPlaying"];
|
|
[userDefaultsValuesDict setObject:[NSNumber numberWithDouble:0] forKey:@"lastTrackPosition"];
|
|
|
|
[userDefaultsValuesDict setObject:@"dls appl" forKey:@"midi.plugin"];
|
|
|
|
[userDefaultsValuesDict setObject:@"default" forKey:@"midi.flavor"];
|
|
|
|
[userDefaultsValuesDict setObject:[NSNumber numberWithBool:NO] forKey:@"resumePlaybackOnStartup"];
|
|
|
|
//Register and sync defaults
|
|
[[NSUserDefaults standardUserDefaults] registerDefaults:userDefaultsValuesDict];
|
|
[[NSUserDefaults standardUserDefaults] synchronize];
|
|
|
|
//And if the existing feed URL is broken due to my ineptitude with the above defaults, fix it
|
|
NSSet<NSString *> *brokenFeedURLs = [NSSet setWithObjects:
|
|
@"https://kode54.net/cog/stable.xml",
|
|
@"https://kode54.net/cog/mercury.xml"
|
|
@"https://www.kode54.net/cog/mercury.xml",
|
|
nil];
|
|
NSString *feedURL = [[NSUserDefaults standardUserDefaults] stringForKey:@"SUFeedURL"];
|
|
if ([brokenFeedURLs containsObject:feedURL]) {
|
|
[[NSUserDefaults standardUserDefaults] setValue:feedURLdefault forKey:@"SUFeedURL"];
|
|
}
|
|
|
|
if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"midi.plugin"] isEqualToString:@"BASSMIDI"]) {
|
|
[[NSUserDefaults standardUserDefaults] setValue:@"FluidSynth" forKey:@"midi.plugin"];
|
|
}
|
|
}
|
|
|
|
/* Unassign previous handler first, so dealloc can unregister it from the global map before the new instances are assigned */
|
|
- (void)registerHotKeys
|
|
{
|
|
MASShortcutBinder *binder = [MASShortcutBinder sharedBinder];
|
|
[binder bindShortcutWithDefaultsKey:CogPlayShortcutKey toAction:^{
|
|
[self clickPlay];
|
|
}];
|
|
|
|
[binder bindShortcutWithDefaultsKey:CogNextShortcutKey toAction:^{
|
|
[self clickNext];
|
|
}];
|
|
|
|
[binder bindShortcutWithDefaultsKey:CogPrevShortcutKey toAction:^{
|
|
[self clickPrev];
|
|
}];
|
|
|
|
[binder bindShortcutWithDefaultsKey:CogSpamShortcutKey toAction:^{
|
|
[self clickSpam];
|
|
}];
|
|
}
|
|
|
|
- (void)clickPlay
|
|
{
|
|
[playbackController playPauseResume:self];
|
|
}
|
|
|
|
- (void)clickPause
|
|
{
|
|
[playbackController pause:self];
|
|
}
|
|
|
|
- (void)clickStop
|
|
{
|
|
[playbackController stop:self];
|
|
}
|
|
|
|
- (void)clickPrev
|
|
{
|
|
[playbackController prev:nil];
|
|
}
|
|
|
|
- (void)clickNext
|
|
{
|
|
[playbackController next:nil];
|
|
}
|
|
|
|
- (void)clickSpam
|
|
{
|
|
[playbackController spam];
|
|
}
|
|
|
|
- (void)clickSeek:(NSTimeInterval)position
|
|
{
|
|
[playbackController seek:self toTime:position];
|
|
}
|
|
|
|
- (void)changeFontSize:(float)size
|
|
{
|
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
float fCurrentSize = [defaults floatForKey:@"fontSize"];
|
|
NSNumber *newSize = [NSNumber numberWithFloat:(fCurrentSize + size)];
|
|
[defaults setObject:newSize forKey:@"fontSize"];
|
|
}
|
|
|
|
- (IBAction)increaseFontSize:(id)sender
|
|
{
|
|
[self changeFontSize:1];
|
|
}
|
|
|
|
- (IBAction)decreaseFontSize:(id)sender
|
|
{
|
|
[self changeFontSize:-1];
|
|
|
|
}
|
|
|
|
- (IBAction)toggleMiniMode:(id)sender
|
|
{
|
|
[self setMiniMode:(!miniMode)];
|
|
}
|
|
|
|
- (BOOL)miniMode
|
|
{
|
|
return miniMode;
|
|
}
|
|
|
|
- (void)setMiniMode:(BOOL)newMiniMode
|
|
{
|
|
miniMode = newMiniMode;
|
|
[[NSUserDefaults standardUserDefaults] setBool:miniMode forKey:@"miniMode"];
|
|
|
|
NSWindow *windowToShow = miniMode ? miniWindow : mainWindow;
|
|
NSWindow *windowToHide = miniMode ? mainWindow : miniWindow;
|
|
[windowToHide close];
|
|
[windowToShow makeKeyAndOrderFront:self];
|
|
}
|
|
|
|
- (IBAction)toggleToolbarStyle:(id)sender {
|
|
[self setToolbarStyle:!_isFullToolbarStyle];
|
|
}
|
|
|
|
- (void)setToolbarStyle:(BOOL)full {
|
|
_isFullToolbarStyle = full;
|
|
[[NSUserDefaults standardUserDefaults] setBool:full forKey:@"toolbarStyleFull"];
|
|
DLog("Changed toolbar style: %@", (full ? @"full" : @"compact"));
|
|
|
|
if (@available(macOS 11.0, *)) {
|
|
NSWindowToolbarStyle style =
|
|
full ? NSWindowToolbarStyleExpanded : NSWindowToolbarStyleUnified;
|
|
mainWindow.toolbarStyle = style;
|
|
miniWindow.toolbarStyle = style;
|
|
} else {
|
|
NSWindowTitleVisibility titleVisibility = full ? NSWindowTitleVisible : NSWindowTitleHidden;
|
|
mainWindow.titleVisibility = titleVisibility;
|
|
miniWindow.titleVisibility = titleVisibility;
|
|
}
|
|
|
|
// Fix empty area after changing toolbar style in mini window as it has no content view
|
|
[miniWindow setContentSize:NSMakeSize(miniWindow.frame.size.width, 0)];
|
|
}
|
|
|
|
- (void)setFloatingMiniWindow:(BOOL)floatingMiniWindow {
|
|
_floatingMiniWindow = floatingMiniWindow;
|
|
[[NSUserDefaults standardUserDefaults] setBool:floatingMiniWindow forKey:@"floatingMiniWindow"];
|
|
NSWindowLevel level = floatingMiniWindow ? NSFloatingWindowLevel : NSNormalWindowLevel;
|
|
[miniWindow setLevel:level];
|
|
}
|
|
|
|
@end
|