diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..567609b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+build/
diff --git a/FreeScaler.xcodeproj/project.pbxproj b/FreeScaler.xcodeproj/project.pbxproj
index a9aba80..0f5f31d 100644
--- a/FreeScaler.xcodeproj/project.pbxproj
+++ b/FreeScaler.xcodeproj/project.pbxproj
@@ -433,13 +433,26 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = FreeScaler/FreeScaler.entitlements;
- "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = "";
- "DEVELOPMENT_TEAM[sdk=macosx*]" = RNLG254GCK;
+ "DEVELOPMENT_TEAM[sdk=macosx*]" = "";
+ ENABLE_APP_SANDBOX = YES;
+ ENABLE_FILE_ACCESS_DOWNLOADS_FOLDER = readonly;
+ ENABLE_FILE_ACCESS_PICTURE_FOLDER = readonly;
ENABLE_HARDENED_RUNTIME = YES;
+ ENABLE_INCOMING_NETWORK_CONNECTIONS = NO;
+ ENABLE_OUTGOING_NETWORK_CONNECTIONS = NO;
+ ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = NO;
+ ENABLE_RESOURCE_ACCESS_BLUETOOTH = NO;
+ ENABLE_RESOURCE_ACCESS_CALENDARS = NO;
+ ENABLE_RESOURCE_ACCESS_CAMERA = NO;
+ ENABLE_RESOURCE_ACCESS_CONTACTS = NO;
+ ENABLE_RESOURCE_ACCESS_LOCATION = NO;
+ ENABLE_RESOURCE_ACCESS_PRINTING = NO;
+ ENABLE_RESOURCE_ACCESS_USB = NO;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.photography";
INFOPLIST_KEY_NSHumanReadableCopyright = "";
@@ -454,7 +467,7 @@
PRODUCT_BUNDLE_IDENTIFIER = it.murus.FreeScaler;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
- "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "FreeScaler DeveloperID";
+ "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
};
@@ -466,13 +479,26 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = FreeScaler/FreeScaler.entitlements;
- "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = "";
- "DEVELOPMENT_TEAM[sdk=macosx*]" = RNLG254GCK;
+ "DEVELOPMENT_TEAM[sdk=macosx*]" = "";
+ ENABLE_APP_SANDBOX = YES;
+ ENABLE_FILE_ACCESS_DOWNLOADS_FOLDER = readonly;
+ ENABLE_FILE_ACCESS_PICTURE_FOLDER = readonly;
ENABLE_HARDENED_RUNTIME = YES;
+ ENABLE_INCOMING_NETWORK_CONNECTIONS = NO;
+ ENABLE_OUTGOING_NETWORK_CONNECTIONS = NO;
+ ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = NO;
+ ENABLE_RESOURCE_ACCESS_BLUETOOTH = NO;
+ ENABLE_RESOURCE_ACCESS_CALENDARS = NO;
+ ENABLE_RESOURCE_ACCESS_CAMERA = NO;
+ ENABLE_RESOURCE_ACCESS_CONTACTS = NO;
+ ENABLE_RESOURCE_ACCESS_LOCATION = NO;
+ ENABLE_RESOURCE_ACCESS_PRINTING = NO;
+ ENABLE_RESOURCE_ACCESS_USB = NO;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.photography";
INFOPLIST_KEY_NSHumanReadableCopyright = "";
@@ -487,7 +513,7 @@
PRODUCT_BUNDLE_IDENTIFIER = it.murus.FreeScaler;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
- "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "FreeScaler DeveloperID";
+ "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
};
diff --git a/FreeScaler/AppDelegate.swift b/FreeScaler/AppDelegate.swift
index 96326f0..159422b 100644
--- a/FreeScaler/AppDelegate.swift
+++ b/FreeScaler/AppDelegate.swift
@@ -31,9 +31,82 @@ import Cocoa
@main
class AppDelegate: NSObject, NSApplicationDelegate {
- func applicationDidFinishLaunching(_ aNotification: Notification) { self.startFreeScaler() }
+ func applicationDidFinishLaunching(_ aNotification: Notification) {
+ buildMainMenu()
+ self.startFreeScaler()
+ }
func applicationWillTerminate(_ aNotification: Notification) { }
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true }
func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { return true }
+
+ @objc func showCustomModelsDirInFinder(_ sender: Any) {
+ createCustomModelsDirectoryIfNeeded()
+ NSWorkspace.shared.activateFileViewerSelecting([URL(fileURLWithPath: customModelsPath).absoluteURL])
+ }
+
+ // MARK: - Main Menu (built programmatically to avoid XIB loading inconsistencies)
+
+ private func buildMainMenu() {
+ let mainMenu = NSMenu()
+
+ // App menu
+ let appMenuItem = NSMenuItem()
+ let appMenu = NSMenu()
+ appMenu.addItem(withTitle: "About FreeScaler", action: #selector(NSApplication.orderFrontStandardAboutPanel(_:)), keyEquivalent: "")
+ appMenu.addItem(.separator())
+ let servicesItem = NSMenuItem(title: "Services", action: nil, keyEquivalent: "")
+ let servicesMenu = NSMenu(title: "Services")
+ servicesItem.submenu = servicesMenu
+ NSApp.servicesMenu = servicesMenu
+ appMenu.addItem(servicesItem)
+ appMenu.addItem(.separator())
+ appMenu.addItem(withTitle: "Hide FreeScaler", action: #selector(NSApplication.hide(_:)), keyEquivalent: "h")
+ let hideOthersItem = appMenu.addItem(withTitle: "Hide Others", action: #selector(NSApplication.hideOtherApplications(_:)), keyEquivalent: "h")
+ hideOthersItem.keyEquivalentModifierMask = [.command, .option]
+ appMenu.addItem(withTitle: "Show All", action: #selector(NSApplication.unhideAllApplications(_:)), keyEquivalent: "")
+ appMenu.addItem(.separator())
+ appMenu.addItem(withTitle: "Quit FreeScaler", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q")
+ appMenuItem.submenu = appMenu
+ mainMenu.addItem(appMenuItem)
+
+ // File menu
+ let fileMenuItem = NSMenuItem()
+ let fileMenu = NSMenu(title: "File")
+ fileMenu.addItem(withTitle: "Import…", action: #selector(FSMainWindowController.clickImport(_:)), keyEquivalent: "i")
+ fileMenu.addItem(.separator())
+ fileMenu.addItem(withTitle: "Close", action: #selector(NSWindow.performClose(_:)), keyEquivalent: "w")
+ fileMenu.addItem(.separator())
+ fileMenu.addItem(withTitle: "Show Custom Models In Finder", action: #selector(showCustomModelsDirInFinder(_:)), keyEquivalent: "")
+ fileMenuItem.submenu = fileMenu
+ mainMenu.addItem(fileMenuItem)
+
+ // Edit menu
+ let editMenuItem = NSMenuItem()
+ let editMenu = NSMenu(title: "Edit")
+ editMenu.addItem(withTitle: "Undo", action: Selector(("undo:")), keyEquivalent: "z")
+ let redoItem = editMenu.addItem(withTitle: "Redo", action: Selector(("redo:")), keyEquivalent: "z")
+ redoItem.keyEquivalentModifierMask = [.command, .shift]
+ editMenu.addItem(.separator())
+ editMenu.addItem(withTitle: "Cut", action: #selector(NSText.cut(_:)), keyEquivalent: "x")
+ editMenu.addItem(withTitle: "Copy", action: #selector(NSText.copy(_:)), keyEquivalent: "c")
+ editMenu.addItem(withTitle: "Paste", action: #selector(NSText.paste(_:)), keyEquivalent: "v")
+ editMenu.addItem(withTitle: "Delete", action: #selector(NSText.delete(_:)), keyEquivalent: "")
+ editMenu.addItem(withTitle: "Select All", action: #selector(NSText.selectAll(_:)), keyEquivalent: "a")
+ editMenuItem.submenu = editMenu
+ mainMenu.addItem(editMenuItem)
+
+ // Window menu
+ let windowMenuItem = NSMenuItem()
+ let windowMenu = NSMenu(title: "Window")
+ windowMenu.addItem(withTitle: "Minimize", action: #selector(NSWindow.performMiniaturize(_:)), keyEquivalent: "m")
+ windowMenu.addItem(withTitle: "Zoom", action: #selector(NSWindow.performZoom(_:)), keyEquivalent: "")
+ windowMenu.addItem(.separator())
+ windowMenu.addItem(withTitle: "Bring All to Front", action: #selector(NSApplication.arrangeInFront(_:)), keyEquivalent: "")
+ windowMenuItem.submenu = windowMenu
+ NSApp.windowsMenu = windowMenu
+ mainMenu.addItem(windowMenuItem)
+
+ NSApp.mainMenu = mainMenu
+ }
}
diff --git a/FreeScaler/Base.lproj/MainMenu.xib b/FreeScaler/Base.lproj/MainMenu.xib
index df640ab..4d85882 100644
--- a/FreeScaler/Base.lproj/MainMenu.xib
+++ b/FreeScaler/Base.lproj/MainMenu.xib
@@ -13,179 +13,5 @@