From fe35baa88e128f54d766fcb8b76857b036c91088 Mon Sep 17 00:00:00 2001 From: der richter Date: Sun, 3 Mar 2024 02:49:11 +0100 Subject: mac/menu: rewrite menu bar in swift --- meson.build | 12 +- osdep/mac/application.h | 1 - osdep/mac/application_objc.h | 2 +- osdep/mac/menu_bar.swift | 464 +++++++++++++++++++++++ osdep/mac/menubar.h | 30 -- osdep/mac/menubar.m | 853 ------------------------------------------- osdep/mac/menubar_objc.h | 25 -- osdep/mac/meson.build | 4 + osdep/mac/swift_bridge.h | 3 + video/out/mac/window.swift | 10 +- 10 files changed, 483 insertions(+), 921 deletions(-) create mode 100644 osdep/mac/menu_bar.swift delete mode 100644 osdep/mac/menubar.h delete mode 100644 osdep/mac/menubar.m delete mode 100644 osdep/mac/menubar_objc.h diff --git a/meson.build b/meson.build index c58de3e175..dd07caeb1e 100644 --- a/meson.build +++ b/meson.build @@ -396,8 +396,7 @@ if features['cocoa'] 'osdep/path-mac.m', 'osdep/utils-mac.c', 'osdep/mac/application.m', - 'osdep/mac/events.m', - 'osdep/mac/menubar.m') + 'osdep/mac/events.m') endif if posix @@ -1516,6 +1515,7 @@ if features['cocoa'] and features['swift'] swift_sources += files('osdep/mac/libmpv_helper.swift', 'osdep/mac/log_helper.swift', 'osdep/mac/mpv_helper.swift', + 'osdep/mac/menu_bar.swift', 'osdep/mac/precise_timer.swift', 'osdep/mac/swift_compat.swift', 'osdep/mac/swift_extensions.swift', @@ -1549,10 +1549,6 @@ if features['macos-media-player'] swift_sources += files('osdep/mac/remote_command_center.swift') endif -if features['swift'] and swift_sources.length() > 0 - subdir('osdep/mac') -endif - macos_touchbar = get_option('macos-touchbar').require( features['cocoa'] and cc.has_header('AppKit/NSTouchBar.h'), error_message: 'Either cocoa could not be found or AppKit/NSTouchBar.h could not be found!', @@ -1562,6 +1558,10 @@ if features['macos-touchbar'] sources += files('osdep/mac/touchbar.m') endif +if features['swift'] and swift_sources.length() > 0 + subdir('osdep/mac') +endif + # manpages manpage = 'DOCS/man/mpv.rst' diff --git a/osdep/mac/application.h b/osdep/mac/application.h index 6b83abd248..06e5f1dcb5 100644 --- a/osdep/mac/application.h +++ b/osdep/mac/application.h @@ -18,7 +18,6 @@ #ifndef MAC_APPLICATION #define MAC_APPLICATION -#include "osdep/mac/menubar.h" #include "options/m_option.h" enum { diff --git a/osdep/mac/application_objc.h b/osdep/mac/application_objc.h index ad4f13ec5d..6e492eef0e 100644 --- a/osdep/mac/application_objc.h +++ b/osdep/mac/application_objc.h @@ -17,9 +17,9 @@ #import #include "osdep/mac/application.h" -#import "osdep/mac/menubar_objc.h" @class CocoaCB; +@class MenuBar; struct mpv_event; struct mpv_handle; diff --git a/osdep/mac/menu_bar.swift b/osdep/mac/menu_bar.swift new file mode 100644 index 0000000000..c5e39d8670 --- /dev/null +++ b/osdep/mac/menu_bar.swift @@ -0,0 +1,464 @@ +/* + * This file is part of mpv. + * + * mpv is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * mpv is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with mpv. If not, see . + */ + +extension MenuBar { + enum MenuKey { + case normalSize + case halfSize + case doubleSize + case minimize + case zoom + } + + struct Config { + let name: String + let key: String + let modifiers: NSEvent.ModifierFlags + let action: Selector? + let target: AnyObject? + let command: String + let url: String? + let file: String? + let alertTitle1: String? + let alertText1: String? + let alertTitle2: String? + let alertText2: String? + let alertTitle3: String? + let alertText3: String? + let commandSpecial: MenuKey? + var menuItem: NSMenuItem? + var configs: [Config]? + + init( + name: String = "", + key: String = "", + modifiers: NSEvent.ModifierFlags = .command, + action: Selector? = nil, + target: AnyObject? = nil, + command: String = "", + url: String? = nil, + file: String? = nil, + alertTitle1: String? = nil, + alertText1: String? = nil, + alertTitle2: String? = nil, + alertText2: String? = nil, + alertTitle3: String? = nil, + alertText3: String? = nil, + commandSpecial: MenuKey? = nil, + menuItem: NSMenuItem? = nil, + configs: [Config]? = nil + ) { + self.name = name + self.key = key + self.modifiers = modifiers + self.action = action + self.target = target + self.command = command + self.url = url + self.file = file + self.alertTitle1 = alertTitle1 + self.alertText1 = alertText1 + self.alertTitle2 = alertTitle2 + self.alertText2 = alertText2 + self.alertTitle3 = alertTitle3 + self.alertText3 = alertText3 + self.commandSpecial = commandSpecial + self.menuItem = menuItem + self.configs = configs + } + } +} + +class MenuBar: NSObject { + var menuConfigs: [Config] = [] + let appIcon: NSImage + + @objc override init() { + UserDefaults.standard.set(false, forKey: "NSFullScreenMenuItemEverywhere") + UserDefaults.standard.set(true, forKey: "NSDisabledDictationMenuItem") + UserDefaults.standard.set(true, forKey: "NSDisabledCharacterPaletteMenuItem") + NSWindow.allowsAutomaticWindowTabbing = false + appIcon = (NSApp as? Application)?.getMPVIcon() ?? NSImage(size: NSSize(width: 1, height: 1)) + + super.init() + + let appMenuConfigs = [ + Config(name: "About mpv", action: #selector(about), target: self), + Config(name: "separator"), + Config( + name: "Preferences…", + key: ",", + action: #selector(preferences(_:)), + target: self, + file: "mpv.conf", + alertTitle1: "No Application found to open your config file.", + alertText1: "Please open the mpv.conf file with your preferred text editor in the now open folder to edit your config.", + alertTitle2: "No config file found.", + alertText2: "Please create a mpv.conf file with your preferred text editor in the now open folder.", + alertTitle3: "No config path or file found.", + alertText3: "Please create the following path ~/.config/mpv/ and a mpv.conf file within with your preferred text editor." + ), + Config( + name: "Keyboard Shortcuts Config…", + action: #selector(preferences(_:)), + target: self, + file: "input.conf", + alertTitle1: "No Application found to open your config file.", + alertText1: "Please open the input.conf file with your preferred text editor in the now open folder to edit your config.", + alertTitle2: "No config file found.", + alertText2: "Please create a input.conf file with your preferred text editor in the now open folder.", + alertTitle3: "No config path or file found.", + alertText3: "Please create the following path ~/.config/mpv/ and a input.conf file within with your preferred text editor." + ), + Config(name: "separator"), + Config(name: "Services"), + Config(name: "separator"), + Config(name: "Hide mpv", key: "h", action: #selector(NSApp.hide(_:))), + Config(name: "Hide Others", key: "h", modifiers: [.command, .option], action: #selector(NSApp.hideOtherApplications(_:))), + Config(name: "Show All", action: #selector(NSApp.unhideAllApplications(_:))), + Config(name: "separator"), + Config(name: "Quit and Remember Position", action: #selector(quit(_:)), target: self, command: "quit-watch-later"), + Config(name: "Quit mpv", key: "q", action: #selector(quit(_:)), target: self, command: "quit"), + ] + + let fileMenuConfigs = [ + Config(name: "Open File…", key: "o", action: #selector(openFile), target: self), + Config(name: "Open URL…", key: "O", action: #selector(openUrl), target: self), + Config(name: "Open Playlist…", action: #selector(openPlaylist), target: self), + Config(name: "separator"), + Config(name: "Close", key: "w", action: #selector(NSWindow.performClose(_:))), + Config(name: "Save Screenshot", action: #selector(command(_:)), target: self, command: "async screenshot"), + ] + + let editMenuConfigs = [ + Config(name: "Undo", key: "z", action: Selector(("undo:"))), + Config(name: "Redo", key: "Z", action: Selector(("redo:"))), + Config(name: "separator"), + Config(name: "Cut", key: "x", action: #selector(NSText.cut(_:))), + Config(name: "Copy", key: "c", action: #selector(NSText.copy(_:))), + Config(name: "Paste", key: "v", action: #selector(NSText.paste(_:))), + Config(name: "Select All", key: "a", action: #selector(NSResponder.selectAll(_:))), + ] + + var viewMenuConfigs = [ + Config(name: "Toggle Fullscreen", action: #selector(command(_:)), target: self, command: "cycle fullscreen"), + Config(name: "Toggle Float on Top", action: #selector(command(_:)), target: self, command: "cycle ontop"), + Config( + name: "Toggle Visibility on All Workspaces", + action: #selector(command(_:)), + target: self, + command: "cycle on-all-workspaces" + ), + ] +#if HAVE_MACOS_TOUCHBAR + viewMenuConfigs += [ + Config(name: "separator"), + Config(name: "Customize Touch Bar…", action: #selector(NSApp.toggleTouchBarCustomizationPalette(_:))), + ] +#endif + + let videoMenuConfigs = [ + Config(name: "Zoom Out", action: #selector(command(_:)), target: self, command: "add panscan -0.1"), + Config(name: "Zoom In", action: #selector(command(_:)), target: self, command: "add panscan 0.1"), + Config(name: "Reset Zoom", action: #selector(command(_:)), target: self, command: "set panscan 0"), + Config(name: "separator"), + Config(name: "Aspect Ratio 4:3", action: #selector(command(_:)), target: self, command: "set video-aspect-override \"4:3\""), + Config(name: "Aspect Ratio 16:9", action: #selector(command(_:)), target: self, command: "set video-aspect-override \"16:9\""), + Config(name: "Aspect Ratio 1.85:1", action: #selector(command(_:)), target: self, command: "set video-aspect-override \"1.85:1\""), + Config(name: "Aspect Ratio 2.35:1", action: #selector(command(_:)), target: self, command: "set video-aspect-override \"2.35:1\""), + Config(name: "Reset Aspect Ratio", action: #selector(command(_:)), target: self, command: "set video-aspect-override \"-1\""), + Config(name: "separator"), + Config(name: "Rotate Left", action: #selector(command(_:)), target: self, command: "cycle-values video-rotate 0 270 180 90"), + Config(name: "Rotate Right", action: #selector(command(_:)), target: self, command: "cycle-values video-rotate 90 180 270 0"), + Config(name: "Reset Rotation", action: #selector(command(_:)), target: self, command: "set video-rotate 0"), + Config(name: "separator"), + Config(name: "Half Size", key: "0", commandSpecial: .halfSize), + Config(name: "Normal Size", key: "1", commandSpecial: .normalSize), + Config(name: "Double Size", key: "2", commandSpecial: .doubleSize), + ] + + let audioMenuConfigs = [ + Config(name: "Next Audio Track", action: #selector(command(_:)), target: self, command: "cycle audio"), + Config(name: "Previous Audio Track", action: #selector(command(_:)), target: self, command: "cycle audio down"), + Config(name: "separator"), + Config(name: "Toggle Mute", action: #selector(command(_:)), target: self, command: "cycle mute"), + Config(name: "separator"), + Config(name: "Play Audio Later", action: #selector(command(_:)), target: self, command: "add audio-delay 0.1"), + Config(name: "Play Audio Earlier", action: #selector(command(_:)), target: self, command: "add audio-delay -0.1"), + Config(name: "Reset Audio Delay", action: #selector(command(_:)), target: self, command: "set audio-delay 0.0"), + ] + + let subtitleMenuConfigs = [ + Config(name: "Next Subtitle Track", action: #selector(command(_:)), target: self, command: "cycle sub"), + Config(name: "Previous Subtitle Track", action: #selector(command(_:)), target: self, command: "cycle sub down"), + Config(name: "separator"), + Config(name: "Toggle Force Style", action: #selector(command(_:)), target: self, command: "cycle-values sub-ass-override \"force\" \"no\""), + Config(name: "separator"), + Config(name: "Display Subtitles Later", action: #selector(command(_:)), target: self, command: "add sub-delay 0.1"), + Config(name: "Display Subtitles Earlier", action: #selector(command(_:)), target: self, command: "add sub-delay -0.1"), + Config(name: "Reset Subtitle Delay", action: #selector(command(_:)), target: self, command: "set sub-delay 0.0"), + ] + + let playbackMenuConfigs = [ + Config(name: "Toggle Pause", action: #selector(command(_:)), target: self, command: "cycle pause"), + Config(name: "Increase Speed", action: #selector(command(_:)), target: self, command: "add speed 0.1"), + Config(name: "Decrease Speed", action: #selector(command(_:)), target: self, command: "add speed -0.1"), + Config(name: "Reset Speed", action: #selector(command(_:)), target: self, command: "set speed 1.0"), + Config(name: "separator"), + Config(name: "Show Playlist", action: #selector(command(_:)), target: self, command: "script-message osc-playlist"), + Config(name: "Show Chapters", action: #selector(command(_:)), target: self, command: "script-message osc-chapterlist"), + Config(name: "Show Tracks", action: #selector(command(_:)), target: self, command: "script-message osc-tracklist"), + Config(name: "separator"), + Config(name: "Next File", action: #selector(command(_:)), target: self, command: "playlist-next"), + Config(name: "Previous File", action: #selector(command(_:)), target: self, command: "playlist-prev"), + Config(name: "Toggle Loop File", action: #selector(command(_:)), target: self, command: "cycle-values loop-file \"inf\" \"no\""), + Config(name: "Toggle Loop Playlist", action: #selector(command(_:)), target: self, command: "cycle-values loop-playlist \"inf\" \"no\""), + Config(name: "Shuffle", action: #selector(command(_:)), target: self, command: "playlist-shuffle"), + Config(name: "separator"), + Config(name: "Next Chapter", action: #selector(command(_:)), target: self, command: "add chapter 1"), + Config(name: "Previous Chapter", action: #selector(command(_:)), target: self, command: "add chapter -1"), + Config(name: "separator"), + Config(name: "Step Forward", action: #selector(command(_:)), target: self, command: "frame-step"), + Config(name: "Step Backward", action: #selector(command(_:)), target: self, command: "frame-back-step"), + ] + + let windowMenuConfigs = [ + Config(name: "Minimize", key: "m", commandSpecial: .minimize), + Config(name: "Zoom", key: "z", commandSpecial: .zoom), + ] + + let helpMenuConfigs = [ + Config(name: "mpv Website…", action: #selector(url(_:)), target: self, url: "https://mpv.io"), + Config(name: "mpv on GitHub…", action: #selector(url(_:)), target: self, url: "https://github.com/mpv-player/mpv"), + Config(name: "separator"), + Config(name: "Online Manual…", action: #selector(url(_:)), target: self, url: "https://mpv.io/manual/master/"), + Config(name: "Online Wiki…", action: #selector(url(_:)), target: self, url: "https://github.com/mpv-player/mpv/wiki"), + Config(name: "Release Notes…", action: #selector(url(_:)), target: self, url: "https://github.com/mpv-player/mpv/blob/master/RELEASE_NOTES"), + Config(name: "Keyboard Shortcuts…", action: #selector(url(_:)), target: self, url: "https://github.com/mpv-player/mpv/blob/master/etc/input.conf"), + Config(name: "separator"), + Config(name: "Report Issue…", action: #selector(url(_:)), target: self, url: "https://github.com/mpv-player/mpv/issues/new/choose"), + Config( + name: "Show log File…", + action: #selector(showFile(_:)), + target: self, + file: NSHomeDirectory() + "/Library/Logs/mpv.log", + alertTitle1: "No log File found.", + alertText1: "You deactivated logging for the Bundle." + ), + ] + + menuConfigs = [ + Config(name: "Apple", configs: appMenuConfigs), + Config(name: "File", configs: fileMenuConfigs), + Config(name: "Edit", configs: editMenuConfigs), + Config(name: "View", configs: viewMenuConfigs), + Config(name: "Video", configs: videoMenuConfigs), + Config(name: "Audio", configs: audioMenuConfigs), + Config(name: "Subtitle", configs: subtitleMenuConfigs), + Config(name: "Playback", configs: playbackMenuConfigs), + Config(name: "Window", configs: windowMenuConfigs), + Config(name: "Help", configs: helpMenuConfigs), + ] + + NSApp.mainMenu = generateMainMenu() + } + + func generateMainMenu() -> NSMenu { + let mainMenu = NSMenu(title: "MainMenu") + NSApp.servicesMenu = NSMenu() + + for (menuConfigIndex, menuConfig) in menuConfigs.enumerated() { + let menu = NSMenu(title: menuConfig.name) + let item = NSMenuItem(title: menuConfig.name, action: nil, keyEquivalent: menuConfig.key) + mainMenu.addItem(item) + mainMenu.setSubmenu(menu, for: item) + menuConfigs[menuConfigIndex].menuItem = item + + for (subConfigIndex, subConfig) in (menuConfig.configs ?? []).enumerated() { +#if HAVE_MACOS_TOUCHBAR + if subConfig.action == "toggleTouchBarCustomizationPalette:" { + continue + } +#endif + + if subConfig.name == "Show log File…" && ProcessInfo.processInfo.environment["MPVBUNDLE"] != "true" { + continue + } + + if subConfig.name == "separator" { + menu.addItem(NSMenuItem.separator()) + } else { + let subItem = NSMenuItem(title: subConfig.name, action: subConfig.action, keyEquivalent: subConfig.key) + subItem.target = subConfig.target + subItem.keyEquivalentModifierMask = subConfig.modifiers + menu.addItem(subItem) + menuConfigs[menuConfigIndex].configs?[subConfigIndex].menuItem = subItem + + if subConfig.name == "Services" { + subItem.submenu = NSApp.servicesMenu + } + } + } + } + + return mainMenu + } + + @objc func about() { + NSApp.orderFrontStandardAboutPanel(options: [ + .applicationName: "mpv", + .applicationIcon: appIcon, + .applicationVersion: String(cString: swift_mpv_version), + .init(rawValue: "Copyright"): String(cString: swift_mpv_copyright), + ]) + } + + @objc func preferences(_ menuItem: NSMenuItem) { + guard let menuConfig = getConfigFromMenu(menuItem: menuItem) else { return } + let configPaths: [String] = [ + NSHomeDirectory() + "/.mpv/", + NSHomeDirectory() + "/.config/mpv/", + ] + + for path in configPaths { + let configFile = path + (menuConfig.file ?? "") + + if FileManager.default.fileExists(atPath: configFile) { + if NSWorkspace.shared.openFile(configFile) { + return + } + NSWorkspace.shared.openFile(path) + alert(title: menuConfig.alertTitle1 ?? "", text: menuConfig.alertText1 ?? "") + return + } + + if NSWorkspace.shared.openFile(path) { + alert(title: menuConfig.alertTitle2 ?? "", text: menuConfig.alertText2 ?? "") + return + } + } + + alert(title: menuConfig.alertTitle3 ?? "", text: menuConfig.alertText3 ?? "") + } + + @objc func quit(_ menuItem: NSMenuItem) { + guard let menuConfig = getConfigFromMenu(menuItem: menuItem) else { return } + menuConfig.command.withCString { + (NSApp as? Application)?.stopMPV(UnsafeMutablePointer(mutating: $0)) + } + } + + @objc func openFile() { + let panel = NSOpenPanel() + panel.allowsMultipleSelection = true + panel.canChooseDirectories = true + + if panel.runModal() == .OK { + var files: [String] = [] + for url in panel.urls { + files += [url.path] + } + (NSApp as? Application)?.openFiles(files) + } + } + + @objc func openPlaylist() { + let panel = NSOpenPanel() + + if panel.runModal() == .OK { + "loadlist \"\(panel.urls[0].path)\"".withCString { + (NSApp as? Application)?.queueCommand(UnsafeMutablePointer(mutating: $0)) + } + } + } + + @objc func openUrl() { + let alert = NSAlert() + alert.messageText = "Open URL" + alert.icon = appIcon + alert.addButton(withTitle: "Ok") + alert.addButton(withTitle: "Cancel") + + let input = NSTextField(frame: NSRect(x: 0, y: 0, width: 300, height: 24)) + input.placeholderString = "URL" + alert.accessoryView = input + + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) { + input.becomeFirstResponder() + } + + if alert.runModal() == .alertFirstButtonReturn && input.stringValue.count > 0 { + (NSApp as? Application)?.openFiles([input.stringValue]) + } + } + + @objc func command(_ menuItem: NSMenuItem) { + guard let menuConfig = getConfigFromMenu(menuItem: menuItem) else { return } + menuConfig.command.withCString { + (NSApp as? Application)?.queueCommand(UnsafeMutablePointer(mutating: $0)) + } + } + + @objc func url(_ menuItem: NSMenuItem) { + guard let menuConfig = getConfigFromMenu(menuItem: menuItem), + let url = URL(string: menuConfig.url ?? "") else { return } + NSWorkspace.shared.open(url) + } + + @objc func showFile(_ menuItem: NSMenuItem) { + guard let menuConfig = getConfigFromMenu(menuItem: menuItem) else { return } + let url = URL(fileURLWithPath: menuConfig.file ?? "") + if FileManager.default.fileExists(atPath: url.path) { + NSWorkspace.shared.activateFileViewerSelecting([url]) + return + } + + alert(title: menuConfig.alertTitle1 ?? "", text: menuConfig.alertText1 ?? "") + } + + func alert(title: String, text: String) { + let alert = NSAlert() + alert.messageText = title + alert.informativeText = text + alert.icon = appIcon + alert.addButton(withTitle: "Ok") + alert.runModal() + } + + func getConfigFromMenu(menuItem: NSMenuItem) -> Config? { + for menuConfig in menuConfigs { + for subConfig in menuConfig.configs ?? [] { + if subConfig.menuItem == menuItem { + return subConfig + } + } + } + + return nil + } + + func register(_ selector: Selector, key: MenuKey) { + for menuConfig in menuConfigs { + for subConfig in menuConfig.configs ?? [] { + if subConfig.commandSpecial == key { + subConfig.menuItem?.action = selector + return + } + } + } + } +} diff --git a/osdep/mac/menubar.h b/osdep/mac/menubar.h deleted file mode 100644 index 65e3dda34a..0000000000 --- a/osdep/mac/menubar.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * This file is part of mpv. - * - * mpv is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * mpv is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with mpv. If not, see . - */ - -#ifndef MAC_MENUBAR -#define MAC_MENUBAR - -// Menu Keys identifying menu items -typedef enum { - MPM_H_SIZE, - MPM_N_SIZE, - MPM_D_SIZE, - MPM_MINIMIZE, - MPM_ZOOM, -} MPMenuKey; - -#endif /* MAC_MENUBAR */ diff --git a/osdep/mac/menubar.m b/osdep/mac/menubar.m deleted file mode 100644 index 6a4ea96afe..0000000000 --- a/osdep/mac/menubar.m +++ /dev/null @@ -1,853 +0,0 @@ -/* - * This file is part of mpv. - * - * mpv is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * mpv is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with mpv. If not, see . - */ - -#include "config.h" -#include "common/common.h" - -#import "osdep/mac/menubar_objc.h" -#import "osdep/mac/application_objc.h" - -@implementation MenuBar -{ - NSArray *menuTree; -} - -- (id)init -{ - if (self = [super init]) { - NSUserDefaults *userDefaults =[NSUserDefaults standardUserDefaults]; - [userDefaults setBool:NO forKey:@"NSFullScreenMenuItemEverywhere"]; - [userDefaults setBool:YES forKey:@"NSDisabledDictationMenuItem"]; - [userDefaults setBool:YES forKey:@"NSDisabledCharacterPaletteMenuItem"]; - [NSWindow setAllowsAutomaticWindowTabbing: NO]; - - menuTree = @[ - @{ - @"name": @"Apple", - @"menu": @[ - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"About mpv", - @"action" : @"about", - @"key" : @"", - @"target" : self - }], - @{ @"name": @"separator" }, - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Preferences…", - @"action" : @"preferences:", - @"key" : @",", - @"target" : self, - @"file" : @"mpv.conf", - @"alertTitle1": @"No Application found to open your config file.", - @"alertText1" : @"Please open the mpv.conf file with " - "your preferred text editor in the now " - "open folder to edit your config.", - @"alertTitle2": @"No config file found.", - @"alertText2" : @"Please create a mpv.conf file with your " - "preferred text editor in the now open folder.", - @"alertTitle3": @"No config path or file found.", - @"alertText3" : @"Please create the following path ~/.config/mpv/ " - "and a mpv.conf file within with your preferred " - "text editor." - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Keyboard Shortcuts Config…", - @"action" : @"preferences:", - @"key" : @"", - @"target" : self, - @"file" : @"input.conf", - @"alertTitle1": @"No Application found to open your config file.", - @"alertText1" : @"Please open the input.conf file with " - "your preferred text editor in the now " - "open folder to edit your config.", - @"alertTitle2": @"No config file found.", - @"alertText2" : @"Please create a input.conf file with your " - "preferred text editor in the now open folder.", - @"alertTitle3": @"No config path or file found.", - @"alertText3" : @"Please create the following path ~/.config/mpv/ " - "and a input.conf file within with your preferred " - "text editor." - }], - @{ @"name": @"separator" }, - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Services", - @"key" : @"", - }], - @{ @"name": @"separator" }, - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Hide mpv", - @"action" : @"hide:", - @"key" : @"h", - @"target" : NSApp - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Hide Others", - @"action" : @"hideOtherApplications:", - @"key" : @"h", - @"modifiers" : [NSNumber numberWithUnsignedInteger: - NSEventModifierFlagCommand | - NSEventModifierFlagOption], - @"target" : NSApp - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Show All", - @"action" : @"unhideAllApplications:", - @"key" : @"", - @"target" : NSApp - }], - @{ @"name": @"separator" }, - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Quit and Remember Position", - @"action" : @"quit:", - @"key" : @"", - @"target" : self, - @"cmd" : @"quit-watch-later" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Quit mpv", - @"action" : @"quit:", - @"key" : @"q", - @"target" : self, - @"cmd" : @"quit" - }] - ] - }, - @{ - @"name": @"File", - @"menu": @[ - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Open File…", - @"action" : @"openFile", - @"key" : @"o", - @"target" : self - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Open URL…", - @"action" : @"openURL", - @"key" : @"O", - @"target" : self - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Open Playlist…", - @"action" : @"openPlaylist", - @"key" : @"", - @"target" : self - }], - @{ @"name": @"separator" }, - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Close", - @"action" : @"performClose:", - @"key" : @"w" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Save Screenshot", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"async screenshot" - }] - ] - }, - @{ - @"name": @"Edit", - @"menu": @[ - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Undo", - @"action" : @"undo:", - @"key" : @"z" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Redo", - @"action" : @"redo:", - @"key" : @"Z" - }], - @{ @"name": @"separator" }, - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Cut", - @"action" : @"cut:", - @"key" : @"x" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Copy", - @"action" : @"copy:", - @"key" : @"c" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Paste", - @"action" : @"paste:", - @"key" : @"v" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Select All", - @"action" : @"selectAll:", - @"key" : @"a" - }] - ] - }, - @{ - @"name": @"View", - @"menu": @[ - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Toggle Fullscreen", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"cycle fullscreen" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Toggle Float on Top", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"cycle ontop" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Toggle Visibility on All Workspaces", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"cycle on-all-workspaces" - }], -#if HAVE_MACOS_TOUCHBAR - @{ @"name": @"separator" }, - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Customize Touch Bar…", - @"action" : @"toggleTouchBarCustomizationPalette:", - @"key" : @"", - @"target" : NSApp - }] -#endif - ] - }, - @{ - @"name": @"Video", - @"menu": @[ - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Zoom Out", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"add panscan -0.1" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Zoom In", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"add panscan 0.1" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Reset Zoom", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"set panscan 0" - }], - @{ @"name": @"separator" }, - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Aspect Ratio 4:3", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"set video-aspect-override \"4:3\"" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Aspect Ratio 16:9", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"set video-aspect-override \"16:9\"" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Aspect Ratio 1.85:1", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"set video-aspect-override \"1.85:1\"" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Aspect Ratio 2.35:1", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"set video-aspect-override \"2.35:1\"" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Reset Aspect Ratio", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"set video-aspect-override \"-1\"" - }], - @{ @"name": @"separator" }, - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Rotate Left", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"cycle-values video-rotate 0 270 180 90" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Rotate Right", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"cycle-values video-rotate 90 180 270 0" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Reset Rotation", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"set video-rotate 0" - }], - @{ @"name": @"separator" }, - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Half Size", - @"key" : @"0", - @"cmdSpecial" : [NSNumber numberWithInt:MPM_H_SIZE] - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Normal Size", - @"key" : @"1", - @"cmdSpecial" : [NSNumber numberWithInt:MPM_N_SIZE] - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Double Size", - @"key" : @"2", - @"cmdSpecial" : [NSNumber numberWithInt:MPM_D_SIZE] - }] - ] - }, - @{ - @"name": @"Audio", - @"menu": @[ - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Next Audio Track", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"cycle audio" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Previous Audio Track", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"cycle audio down" - }], - @{ @"name": @"separator" }, - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Toggle Mute", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"cycle mute" - }], - @{ @"name": @"separator" }, - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Play Audio Later", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"add audio-delay 0.1" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Play Audio Earlier", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"add audio-delay -0.1" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Reset Audio Delay", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"set audio-delay 0.0 " - }] - ] - }, - @{ - @"name": @"Subtitle", - @"menu": @[ - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Next Subtitle Track", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"cycle sub" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Previous Subtitle Track", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"cycle sub down" - }], - @{ @"name": @"separator" }, - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Toggle Force Style", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"cycle-values sub-ass-override \"force\" \"no\"" - }], - @{ @"name": @"separator" }, - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Display Subtitles Later", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"add sub-delay 0.1" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Display Subtitles Earlier", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"add sub-delay -0.1" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Reset Subtitle Delay", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"set sub-delay 0.0" - }] - ] - }, - @{ - @"name": @"Playback", - @"menu": @[ - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Toggle Pause", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"cycle pause" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Increase Speed", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"add speed 0.1" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Decrease Speed", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"add speed -0.1" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Reset Speed", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"set speed 1.0" - }], - @{ @"name": @"separator" }, - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Show Playlist", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"script-message osc-playlist" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Show Chapters", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"script-message osc-chapterlist" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Show Tracks", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"script-message osc-tracklist" - }], - @{ @"name": @"separator" }, - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Next File", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"playlist-next" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Previous File", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"playlist-prev" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Toggle Loop File", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"cycle-values loop-file \"inf\" \"no\"" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Toggle Loop Playlist", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"cycle-values loop-playlist \"inf\" \"no\"" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Shuffle", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"playlist-shuffle" - }], - @{ @"name": @"separator" }, - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Next Chapter", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"add chapter 1" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Previous Chapter", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"add chapter -1" - }], - @{ @"name": @"separator" }, - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Step Forward", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"frame-step" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Step Backward", - @"action" : @"cmd:", - @"key" : @"", - @"target" : self, - @"cmd" : @"frame-back-step" - }] - ] - }, - @{ - @"name": @"Window", - @"menu": @[ - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Minimize", - @"key" : @"m", - @"cmdSpecial" : [NSNumber numberWithInt:MPM_MINIMIZE] - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Zoom", - @"key" : @"z", - @"cmdSpecial" : [NSNumber numberWithInt:MPM_ZOOM] - }] - ] - }, - @{ - @"name": @"Help", - @"menu": @[ - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"mpv Website…", - @"action" : @"url:", - @"key" : @"", - @"target" : self, - @"url" : @"https://mpv.io" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"mpv on GitHub…", - @"action" : @"url:", - @"key" : @"", - @"target" : self, - @"url" : @"https://github.com/mpv-player/mpv" - }], - @{ @"name": @"separator" }, - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Online Manual…", - @"action" : @"url:", - @"key" : @"", - @"target" : self, - @"url" : @"https://mpv.io/manual/master/" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Online Wiki…", - @"action" : @"url:", - @"key" : @"", - @"target" : self, - @"url" : @"https://github.com/mpv-player/mpv/wiki" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Release Notes…", - @"action" : @"url:", - @"key" : @"", - @"target" : self, - @"url" : @"https://github.com/mpv-player/mpv/blob/master/RELEASE_NOTES" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Keyboard Shortcuts…", - @"action" : @"url:", - @"key" : @"", - @"target" : self, - @"url" : @"https://github.com/mpv-player/mpv/blob/master/etc/input.conf" - }], - @{ @"name": @"separator" }, - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Report Issue…", - @"action" : @"url:", - @"key" : @"", - @"target" : self, - @"url" : @"https://github.com/mpv-player/mpv/issues/new/choose" - }], - [NSMutableDictionary dictionaryWithDictionary:@{ - @"name" : @"Show log File…", - @"action" : @"showFile:", - @"key" : @"", - @"target" : self, - @"file" : @"~/Library/Logs/mpv.log", - @"alertTitle" : @"No log File found.", - @"alertText" : @"You deactivated logging for the Bundle." - }] - ] - } - ]; - - [NSApp setMainMenu:[self mainMenu]]; - } - - return self; -} - -- (NSMenu *)mainMenu -{ - NSMenu *mainMenu = [[NSMenu alloc] initWithTitle:@"MainMenu"]; - [NSApp setServicesMenu:[[NSMenu alloc] init]]; - NSString* bundle = [[[NSProcessInfo processInfo] environment] objectForKey:@"MPVBUNDLE"]; - - for(id mMenu in menuTree) { - NSMenu *menu = [[NSMenu alloc] initWithTitle:mMenu[@"name"]]; - NSMenuItem *mItem = [mainMenu addItemWithTitle:mMenu[@"name"] - action:nil - keyEquivalent:@""]; - [mainMenu setSubmenu:menu forItem:mItem]; - - for(id subMenu in mMenu[@"menu"]) { - NSString *name = subMenu[@"name"]; - NSString *action = subMenu[@"action"]; - -#if HAVE_MACOS_TOUCHBAR - if ([action isEqual:@"toggleTouchBarCustomizationPalette:"]) { - continue; - } -#endif - - if ([name isEqual:@"Show log File…"] && ![bundle isEqual:@"true"]) { - continue; - } - - if ([name isEqual:@"separator"]) { - [menu addItem:[NSMenuItem separatorItem]]; - } else { - NSMenuItem *iItem = [menu addItemWithTitle:name - action:NSSelectorFromString(action) - keyEquivalent:subMenu[@"key"]]; - [iItem setTarget:subMenu[@"target"]]; - [subMenu setObject:iItem forKey:@"menuItem"]; - - NSNumber *m = subMenu[@"modifiers"]; - if (m) { - [iItem setKeyEquivalentModifierMask:m.unsignedIntegerValue]; - } - - if ([subMenu[@"name"] isEqual:@"Services"]) { - iItem.submenu = [NSApp servicesMenu]; - } - } - } - } - - return mainMenu; -} - -- (void)about -{ - NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: - @"mpv", @"ApplicationName", - [(Application *)NSApp getMPVIcon], @"ApplicationIcon", - [NSString stringWithUTF8String:mpv_copyright], @"Copyright", - [NSString stringWithUTF8String:mpv_version], @"ApplicationVersion", - nil]; - [NSApp orderFrontStandardAboutPanelWithOptions:options]; -} - -- (void)preferences:(NSMenuItem *)menuItem -{ - NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; - NSFileManager *fileManager = [NSFileManager defaultManager]; - NSMutableDictionary *mItemDict = [self getDictFromMenuItem:menuItem]; - NSArray *configPaths = @[ - [NSString stringWithFormat:@"%@/.mpv/", NSHomeDirectory()], - [NSString stringWithFormat:@"%@/.config/mpv/", NSHomeDirectory()]]; - - for (id path in configPaths) { - NSString *fileP = [path stringByAppendingString:mItemDict[@"file"]]; - if ([fileManager fileExistsAtPath:fileP]){ - if ([workspace openFile:fileP]) - return; - [workspace openFile:path]; - [self alertWithTitle:mItemDict[@"alertTitle1"] - andText:mItemDict[@"alertText1"]]; - return; - } - if ([workspace openFile:path]) { - [self alertWithTitle:mItemDict[@"alertTitle2"] - andText:mItemDict[@"alertText2"]]; - return; - } - } - - [self alertWithTitle:mItemDict[@"alertTitle3"] - andText:mItemDict[@"alertText3"]]; -} - -- (void)quit:(NSMenuItem *)menuItem -{ - NSString *cmd = [self getDictFromMenuItem:menuItem][@"cmd"]; - [(Application *)NSApp stopMPV:(char *)[cmd UTF8String]]; -} - -- (void)openFile -{ - NSOpenPanel *panel = [[NSOpenPanel alloc] init]; - [panel setCanChooseDirectories:YES]; - [panel setAllowsMultipleSelection:YES]; - - if ([panel runModal] == NSModalResponseOK){ - NSMutableArray *fileArray = [[NSMutableArray alloc] init]; - for (id url in [panel URLs]) - [fileArray addObject:[url path]]; - [(Application *)NSApp openFiles:fileArray]; - } -} - -- (void)openPlaylist -{ - NSOpenPanel *panel = [[NSOpenPanel alloc] init]; - - if ([panel runModal] == NSModalResponseOK){ - NSString *pl = [NSString stringWithFormat:@"loadlist \"%@\"", - [panel URLs][0].path]; - [(Application *)NSApp queueCommand:(char *)[pl UTF8String]]; - } -} - -- (void)openURL -{ - NSAlert *alert = [[NSAlert alloc] init]; - [alert setMessageText:@"Open URL"]; - [alert addButtonWithTitle:@"Ok"]; - [alert addButtonWithTitle:@"Cancel"]; - [alert setIcon:[(Application *)NSApp getMPVIcon]]; - - NSTextField *input = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 300, 24)]; - [input setPlaceholderString:@"URL"]; - [alert setAccessoryView:input]; - - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - [input becomeFirstResponder]; - }); - - if ([alert runModal] == NSAlertFirstButtonReturn && [input stringValue].length > 0) { - NSArray *url = [NSArray arrayWithObjects:[input stringValue], nil]; - [(Application *)NSApp openFiles:url]; - } -} - -- (void)cmd:(NSMenuItem *)menuItem -{ - NSString *cmd = [self getDictFromMenuItem:menuItem][@"cmd"]; - [(Application *)NSApp queueCommand:(char *)[cmd UTF8String]]; -} - -- (void)url:(NSMenuItem *)menuItem -{ - NSString *url = [self getDictFromMenuItem:menuItem][@"url"]; - [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:url]]; -} - -- (void)showFile:(NSMenuItem *)menuItem -{ - NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; - NSFileManager *fileManager = [NSFileManager defaultManager]; - NSMutableDictionary *mItemDict = [self getDictFromMenuItem:menuItem]; - NSString *file = [mItemDict[@"file"] stringByExpandingTildeInPath]; - - if ([fileManager fileExistsAtPath:file]){ - NSURL *url = [NSURL fileURLWithPath:file]; - NSArray *urlArray = [NSArray arrayWithObjects:url, nil]; - - [workspace activateFileViewerSelectingURLs:urlArray]; - return; - } - - [self alertWithTitle:mItemDict[@"alertTitle"] - andText:mItemDict[@"alertText"]]; -} - -- (void)alertWithTitle:(NSString *)title andText:(NSString *)text -{ - NSAlert *alert = [[NSAlert