diff --git a/Source/Frameworks/gyroflow/Cargo.toml b/Source/Frameworks/gyroflow/Cargo.toml index b5ac89b1..385ace1b 100644 --- a/Source/Frameworks/gyroflow/Cargo.toml +++ b/Source/Frameworks/gyroflow/Cargo.toml @@ -13,7 +13,7 @@ argh = "*" serde = "1.0" serde_json = "1.0" libc = "0.2" -gyroflow-core = { git = "https://github.com/gyroflow/gyroflow.git", default-features = false, rev = "10c8853", features = ["bundle-lens-profiles"] } +gyroflow-core = { git = "https://github.com/gyroflow/gyroflow.git", default-features = false, rev = "2c1cd24", features = ["bundle-lens-profiles"] } log = "0.4.17" oslog = "0.2.0" lazy_static = "1.4.0" diff --git a/Source/Frameworks/gyroflow/inc/gyroflow.h b/Source/Frameworks/gyroflow/inc/gyroflow.h index 42c0f120..355fbada 100644 --- a/Source/Frameworks/gyroflow/inc/gyroflow.h +++ b/Source/Frameworks/gyroflow/inc/gyroflow.h @@ -102,3 +102,12 @@ const char* loadPreset( const char* gyroflow_project_data, const char* lens_profile_path ); + +//--------------------------------------------------------- +// Gets the Lens Identifier: +//--------------------------------------------------------- +const char* getLensIdentifier( + const char* gyroflow_project_data +); + + diff --git a/Source/Frameworks/gyroflow/src/lib.rs b/Source/Frameworks/gyroflow/src/lib.rs index ce739120..abf23ec9 100644 --- a/Source/Frameworks/gyroflow/src/lib.rs +++ b/Source/Frameworks/gyroflow/src/lib.rs @@ -171,6 +171,78 @@ pub extern "C" fn getDefaultValues( } } +/// Gets the lens identifier. +/// +/// # Arguments +/// +/// * `gyroflow_project_data` - A pointer to a C-style string containing the Gyroflow Project data. +/// +/// # Returns +/// +/// A pointer to a C-style string containing the lens identifier or "FAIL". +/// +/// # Safety +/// +/// This function is marked as unsafe because it accepts a raw pointer as an argument. It is the caller's responsibility to ensure that the pointer is valid and points to a null-terminated string. +#[no_mangle] +pub extern "C" fn getLensIdentifier( + gyroflow_project_data: *const c_char, +) -> *const c_char { + //--------------------------------------------------------- + // Convert the Gyroflow Project data to a `&str`: + //--------------------------------------------------------- + let gyroflow_project_data_pointer = unsafe { CStr::from_ptr(gyroflow_project_data) }; + let gyroflow_project_data_string = gyroflow_project_data_pointer.to_string_lossy(); + + let mut stab = StabilizationManager::default(); + { + //--------------------------------------------------------- + // Find first lens profile database with loaded profiles: + //--------------------------------------------------------- + let lock = MANAGER_CACHE.lock().unwrap(); + for (_, v) in lock.iter() { + if v.lens_profile_db.read().loaded { + stab.lens_profile_db = v.lens_profile_db.clone(); + break; + } + } + } + + //--------------------------------------------------------- + // Import the `gyroflow_project_data_string`: + //--------------------------------------------------------- + let blocking = true; + let cancel_flag = Arc::new(AtomicBool::new(false)); + let mut is_preset = false; + match stab.import_gyroflow_data( + gyroflow_project_data_string.as_bytes(), + blocking, + None, + |_|(), + cancel_flag, + &mut is_preset + ) { + Ok(_) => { + //--------------------------------------------------------- + // Get the Lens Identifier: + //--------------------------------------------------------- + let identifier = stab.lens.read().identifier.to_string(); + let result = CString::new(identifier).unwrap(); + return result.into_raw(); + }, + Err(e) => { + //--------------------------------------------------------- + // An error has occurred: + //--------------------------------------------------------- + log::error!("[Gyroflow Toolbox Rust] Error importing gyroflow data: {:?}", e); + + let error_msg = format!("{}", e); + let result = CString::new(error_msg).unwrap(); + return result.into_raw() + }, + } +} + /// Checks if the official lens is loaded. /// /// # Arguments diff --git a/Source/Gyroflow/Plugin/GyroflowPlugIn.h b/Source/Gyroflow/Plugin/GyroflowPlugIn.h index 172bfd50..217ee7a3 100644 --- a/Source/Gyroflow/Plugin/GyroflowPlugIn.h +++ b/Source/Gyroflow/Plugin/GyroflowPlugIn.h @@ -74,6 +74,11 @@ NSView* exportGyroflowProjectView; NSView* openUserGuideView; NSView* settingsView; + + //--------------------------------------------------------- + // Cached Lens Profile Lookup: + //--------------------------------------------------------- + NSDictionary *lensProfilesLookup; } @property (assign) id _Nonnull apiManager; @end diff --git a/Source/Gyroflow/Plugin/GyroflowPlugIn.m b/Source/Gyroflow/Plugin/GyroflowPlugIn.m index d4959a29..6948a0c3 100644 --- a/Source/Gyroflow/Plugin/GyroflowPlugIn.m +++ b/Source/Gyroflow/Plugin/GyroflowPlugIn.m @@ -44,6 +44,19 @@ - (nullable instancetype)initWithAPIManager:(id)newApiManager; NSLog(@"[Gyroflow Toolbox Renderer] Version: %@ (%@)", version, build); NSLog(@"[Gyroflow Toolbox Renderer] applicationSupportDirectory: '%@'", applicationSupportDirectory); + //--------------------------------------------------------- + // Get the Lens Profiles path: + //--------------------------------------------------------- + NSBundle *mainBundle = [NSBundle mainBundle]; + NSString *lensProfilesPath = [mainBundle pathForResource:@"Lens Profiles" ofType:nil inDirectory:nil]; + + //--------------------------------------------------------- + // Build cache of all the Lens Profile Names: + //--------------------------------------------------------- + lensProfilesLookup = [self getLensProfileIdentifiersFromDirectory:lensProfilesPath]; + + NSLog(@"[Gyroflow Toolbox Renderer] lensProfilesLookup: %@", lensProfilesLookup); + //--------------------------------------------------------- // Cache the API Manager: //--------------------------------------------------------- @@ -167,7 +180,7 @@ - (NSView*)createViewForParameterID:(UInt32)parameterID NSView* view = [[CustomButtonView alloc] initWithAPIManager:_apiManager parentPlugin:self buttonID:kCB_LaunchGyroflow - buttonTitle:@"Open in Gyroflow"]; + buttonTitle:@"Launch Gyroflow"]; launchGyroflowView = view; return view; } else if (parameterID == kCB_LoadLastGyroflowProject) { @@ -1525,11 +1538,6 @@ - (BOOL)renderErrorMessageWithID:(NSString*)errorMessageID correctedHeight:(floa //--------------------------------------------------------- // A filter that resizes and changes the aspect ratio of // an image: - // - // NOTE: In v1.0.0 we use MPSImageLanczosScale, however - // I've changed to MPSImageBilinearScale as it's - // faster, and we probably prefer speed over - // quality for thumbnails. //--------------------------------------------------------- MPSImageBilinearScale *filter = [[[MPSImageBilinearScale alloc] initWithDevice:commandQueue.device] autorelease]; [filter setScaleTransform:&transform]; @@ -1549,11 +1557,11 @@ - (BOOL)renderErrorMessageWithID:(NSString*)errorMessageID correctedHeight:(floa MTLRenderPassColorAttachmentDescriptor* colorAttachmentDescriptor = [[MTLRenderPassColorAttachmentDescriptor alloc] init]; colorAttachmentDescriptor.texture = outputTexture; - colorAttachmentDescriptor.clearColor = MTLClearColorMake(0.0, 0.0, 1.0, 1.0); + colorAttachmentDescriptor.clearColor = MTLClearColorMake(1.0, 1.0, 1.0, 1.0); // Black colorAttachmentDescriptor.loadAction = MTLLoadActionClear; - MTLRenderPassDescriptor* renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor]; + MTLRenderPassDescriptor* renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor]; renderPassDescriptor.colorAttachments [ 0 ] = colorAttachmentDescriptor; - id commandEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; + id commandEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; //--------------------------------------------------------- // Calculate the vertex coordinates and the texture @@ -1977,38 +1985,10 @@ - (BOOL)renderDestinationImage:(FxImageTile *)destinationImage //--------------------------------------------------------- // -#pragma mark - Buttons +#pragma mark - Settings Menu // //--------------------------------------------------------- -//--------------------------------------------------------- -// Custom Button View Pressed: -//--------------------------------------------------------- -- (void)customButtonViewPressed:(UInt32)buttonID -{ - if (buttonID == kCB_LaunchGyroflow) { - [self buttonLaunchGyroflow]; - } else if (buttonID == kCB_LoadLastGyroflowProject) { - [self buttonLoadLastGyroflowProject]; - } else if (buttonID == kCB_ImportGyroflowProject) { - [self buttonImportGyroflowProject]; - } else if (buttonID == kCB_ReloadGyroflowProject) { - [self buttonReloadGyroflowProject]; - } else if (buttonID == kCB_ExportGyroflowProject) { - [self buttonExportGyroflowProject]; - } else if (buttonID == kCB_ImportMediaFile) { - [self buttonImportMediaFile]; - } else if (buttonID == kCB_RevealInFinder) { - [self buttonRevealInFinder]; - } else if (buttonID == kCB_LoadPresetLensProfile) { - [self buttonLoadPresetLensProfile]; - } else if (buttonID == kCB_OpenUserGuide) { - [self buttonOpenUserGuide]; - } else if (buttonID == kCB_Settings) { - [self buttonSettings]; - } -} - //--------------------------------------------------------- // BUTTON: 'Settings' //--------------------------------------------------------- @@ -2033,6 +2013,26 @@ -(void)buttonSettings { // "Show Alerts" Sub Menu Items: //--------------------------------------------------------- { + //--------------------------------------------------------- + // Load Preset/Lens Profile Success: + //--------------------------------------------------------- + NSMenuItem *suppressLoadPresetLensProfileSuccess = [[[NSMenuItem alloc] initWithTitle:@"Load Preset/Lens Profile Success" action:@selector(toggleMenuItem:) keyEquivalent:@""] autorelease]; + suppressLoadPresetLensProfileSuccess.identifier = @"suppressLoadPresetLensProfileSuccess"; + suppressLoadPresetLensProfileSuccess.target = self; + suppressLoadPresetLensProfileSuccess.enabled = YES; + suppressLoadPresetLensProfileSuccess.state = [self boolToControlState:[userDefaults boolForKey:@"suppressLoadPresetLensProfileSuccess"]]; + [disableAlertSubMenu addItem:suppressLoadPresetLensProfileSuccess]; + + //--------------------------------------------------------- + // No Lens Profile Detected: + //--------------------------------------------------------- + NSMenuItem *suppressNoLensProfileDetected = [[[NSMenuItem alloc] initWithTitle:@"No Lens Profile Detected" action:@selector(toggleMenuItem:) keyEquivalent:@""] autorelease]; + suppressNoLensProfileDetected.identifier = @"suppressNoLensProfileDetected"; + suppressNoLensProfileDetected.target = self; + suppressNoLensProfileDetected.enabled = YES; + suppressNoLensProfileDetected.state = [self boolToControlState:[userDefaults boolForKey:@"suppressNoLensProfileDetected"]]; + [disableAlertSubMenu addItem:suppressNoLensProfileDetected]; + //--------------------------------------------------------- // Request Sandbox Access: //--------------------------------------------------------- @@ -2042,25 +2042,59 @@ -(void)buttonSettings { suppressRequestSandboxAccessAlert.enabled = YES; suppressRequestSandboxAccessAlert.state = [self boolToControlState:[userDefaults boolForKey:@"suppressRequestSandboxAccessAlert"]]; [disableAlertSubMenu addItem:suppressRequestSandboxAccessAlert]; + + //--------------------------------------------------------- + // Successfully Imported: + //--------------------------------------------------------- + NSMenuItem *suppressSuccessfullyImported = [[[NSMenuItem alloc] initWithTitle:@"Successfully Imported" action:@selector(toggleMenuItem:) keyEquivalent:@""] autorelease]; + suppressSuccessfullyImported.identifier = @"suppressSuccessfullyImported"; + suppressSuccessfullyImported.target = self; + suppressSuccessfullyImported.enabled = YES; + suppressSuccessfullyImported.state = [self boolToControlState:[userDefaults boolForKey:@"suppressSuccessfullyImported"]]; + [disableAlertSubMenu addItem:suppressSuccessfullyImported]; //--------------------------------------------------------- - // No Lens Profile Detected: + // Successfully Reloaded: //--------------------------------------------------------- - NSMenuItem *suppressNoLensProfileDetected = [[[NSMenuItem alloc] initWithTitle:@"No Lens Profile Detected" action:@selector(toggleMenuItem:) keyEquivalent:@""] autorelease]; - suppressNoLensProfileDetected.identifier = @"suppressNoLensProfileDetected"; - suppressNoLensProfileDetected.target = self; - suppressNoLensProfileDetected.enabled = YES; - suppressNoLensProfileDetected.state = [self boolToControlState:[userDefaults boolForKey:@"suppressNoLensProfileDetected"]]; - [disableAlertSubMenu addItem:suppressNoLensProfileDetected]; + NSMenuItem *suppressSuccessfullyReloaded = [[[NSMenuItem alloc] initWithTitle:@"Successfully Reloaded" action:@selector(toggleMenuItem:) keyEquivalent:@""] autorelease]; + suppressSuccessfullyReloaded.identifier = @"suppressSuccessfullyReloaded"; + suppressSuccessfullyReloaded.target = self; + suppressSuccessfullyReloaded.enabled = YES; + suppressSuccessfullyReloaded.state = [self boolToControlState:[userDefaults boolForKey:@"suppressSuccessfullyReloaded"]]; + [disableAlertSubMenu addItem:suppressSuccessfullyReloaded]; } + //--------------------------------------------------------- // Add "Show Alerts" Sub Menu: //--------------------------------------------------------- - NSMenuItem *disableAlertSubMenuItem = [[[NSMenuItem alloc] initWithTitle:@"Disable Alerts" action:nil keyEquivalent:@""] autorelease]; + NSMenuItem *disableAlertSubMenuItem = [[[NSMenuItem alloc] initWithTitle:@"Disabled Alerts" action:nil keyEquivalent:@""] autorelease]; [disableAlertSubMenuItem setSubmenu:disableAlertSubMenu]; - [settingsMenu addItem:disableAlertSubMenuItem]; + //--------------------------------------------------------- + // Add Separator: + //--------------------------------------------------------- + NSMenuItem *separator = [NSMenuItem separatorItem]; + separator.target = self; + separator.enabled = YES; + [settingsMenu addItem:separator]; + + //--------------------------------------------------------- + // Show Log File in Finder: + //--------------------------------------------------------- + NSMenuItem *showLogFileInFinder = [[[NSMenuItem alloc] initWithTitle:@"Show Log Files in Finder" action:@selector(showLogFileInFinder:) keyEquivalent:@""] autorelease]; + showLogFileInFinder.target = self; + showLogFileInFinder.enabled = YES; + [settingsMenu addItem:showLogFileInFinder]; + + //--------------------------------------------------------- + // Reset Settings: + //--------------------------------------------------------- + NSMenuItem *resetSettings = [[[NSMenuItem alloc] initWithTitle:@"Reset All Settings" action:@selector(resetSettings:) keyEquivalent:@""] autorelease]; + resetSettings.target = self; + resetSettings.enabled = YES; + [settingsMenu addItem:resetSettings]; + //--------------------------------------------------------- // Get the current mouse location: //--------------------------------------------------------- @@ -2073,6 +2107,36 @@ -(void)buttonSettings { }); } +//--------------------------------------------------------- +// Show Log File in Finder: +//--------------------------------------------------------- +- (void)showLogFileInFinder:(id)sender { + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); + NSString *applicationSupportDirectory = [paths firstObject]; + NSString* logPath = [applicationSupportDirectory stringByAppendingString:@"/FxPlug.log"]; + [[NSWorkspace sharedWorkspace] selectFile:logPath inFileViewerRootedAtPath:@""]; +} + +//--------------------------------------------------------- +// Reset Settings: +//--------------------------------------------------------- +- (void)resetSettings:(id)sender { + //--------------------------------------------------------- + // Get User Defaults: + //--------------------------------------------------------- + NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; + + [userDefaults removeObjectForKey:@"suppressLoadPresetLensProfileSuccess"]; + [userDefaults removeObjectForKey:@"suppressNoLensProfileDetected"]; + [userDefaults removeObjectForKey:@"suppressRequestSandboxAccessAlert"]; + [userDefaults removeObjectForKey:@"suppressSuccessfullyImported"]; + [userDefaults removeObjectForKey:@"suppressSuccessfullyReloaded"]; + + [userDefaults removeObjectForKey:@"gyroFlowPreferencesBookmarkData"]; + [userDefaults removeObjectForKey:@"lastProject"]; + [userDefaults removeObjectForKey:@"brawToolboxDocumentBookmarkData"]; +} + //--------------------------------------------------------- // Toggle Menu Item: //--------------------------------------------------------- @@ -2083,6 +2147,40 @@ - (void)toggleMenuItem:(id)sender { [userDefaults setBool:state forKey:menuItem.identifier]; } +//--------------------------------------------------------- +// +#pragma mark - Buttons +// +//--------------------------------------------------------- + +//--------------------------------------------------------- +// Custom Button View Pressed: +//--------------------------------------------------------- +- (void)customButtonViewPressed:(UInt32)buttonID +{ + if (buttonID == kCB_LaunchGyroflow) { + [self buttonLaunchGyroflow]; + } else if (buttonID == kCB_LoadLastGyroflowProject) { + [self buttonLoadLastGyroflowProject]; + } else if (buttonID == kCB_ImportGyroflowProject) { + [self buttonImportGyroflowProject]; + } else if (buttonID == kCB_ReloadGyroflowProject) { + [self buttonReloadGyroflowProject]; + } else if (buttonID == kCB_ExportGyroflowProject) { + [self buttonExportGyroflowProject]; + } else if (buttonID == kCB_ImportMediaFile) { + [self buttonImportMediaFile]; + } else if (buttonID == kCB_RevealInFinder) { + [self buttonRevealInFinder]; + } else if (buttonID == kCB_LoadPresetLensProfile) { + [self buttonLoadPresetLensProfileIsImporting:NO]; + } else if (buttonID == kCB_OpenUserGuide) { + [self buttonOpenUserGuide]; + } else if (buttonID == kCB_Settings) { + [self buttonSettings]; + } +} + //--------------------------------------------------------- // BUTTON: 'Export Gyroflow Project' //--------------------------------------------------------- @@ -2222,51 +2320,7 @@ - (void)buttonOpenUserGuide { //--------------------------------------------------------- // BUTTON: 'Load Preset/Lens Profile' //--------------------------------------------------------- -- (void)buttonLoadPresetLensProfile { - - //--------------------------------------------------------- - // Get the Lens Profiles path: - //--------------------------------------------------------- - NSBundle *mainBundle = [NSBundle mainBundle]; - NSString *lensProfilesPath = [mainBundle pathForResource:@"Lens Profiles" ofType:nil inDirectory:nil]; - NSURL *lensProfilesURL = [NSURL fileURLWithPath:lensProfilesPath]; - - //--------------------------------------------------------- - // Setup an NSOpenPanel: - //--------------------------------------------------------- - NSOpenPanel* panel = [NSOpenPanel openPanel]; - [panel setCanChooseDirectories:NO]; - [panel setCanCreateDirectories:YES]; - [panel setCanChooseFiles:YES]; - [panel setAllowsMultipleSelection:NO]; - [panel setDirectoryURL:lensProfilesURL]; - - //--------------------------------------------------------- - // Limit the file type to Gyroflow supported media files: - //--------------------------------------------------------- - UTType *gyroflow = [UTType typeWithFilenameExtension:@"gyroflow"]; - UTType *json = [UTType typeWithFilenameExtension:@"json"]; - - NSArray *allowedContentTypes = [NSArray arrayWithObjects:gyroflow, json, nil]; - [panel setAllowedContentTypes:allowedContentTypes]; - - //--------------------------------------------------------- - // Open the panel: - //--------------------------------------------------------- - NSModalResponse result = [panel runModal]; - if (result != NSModalResponseOK) { - return; - } - - NSURL *url = [panel URL]; - - //--------------------------------------------------------- - // Start accessing security scoped resource: - //--------------------------------------------------------- - if (![url startAccessingSecurityScopedResource]) { - [self showAlertWithMessage:@"An error has occurred." info:@"Failed to startAccessingSecurityScopedResource. This shouldn't happen."]; - return; - } +- (void)buttonLoadPresetLensProfileIsImporting:(BOOL)isImporting { //--------------------------------------------------------- // Load the Custom Parameter Action API: @@ -2322,6 +2376,71 @@ - (void)buttonLoadPresetLensProfile { return; } + //--------------------------------------------------------- + // Get the Lens Identifier from the Gyroflow Project: + //--------------------------------------------------------- + const char* loadedLensIdentifierInGyroflowProject = getLensIdentifier([gyroflowProjectData UTF8String]); + NSString *loadedLensIdentifierInGyroflowProjectString = [NSString stringWithUTF8String:loadedLensIdentifierInGyroflowProject]; + + //--------------------------------------------------------- + // Get the Lens Profiles path: + //--------------------------------------------------------- + NSBundle *mainBundle = [NSBundle mainBundle]; + NSString *lensProfilesPath = [mainBundle pathForResource:@"Lens Profiles" ofType:nil inDirectory:nil]; + NSURL *lensProfilesURL = [NSURL fileURLWithPath:lensProfilesPath]; + + //--------------------------------------------------------- + // Try match the Lens Identifier with a JSON file: + //--------------------------------------------------------- + if (loadedLensIdentifierInGyroflowProjectString) { + NSString *path = lensProfilesLookup[loadedLensIdentifierInGyroflowProjectString]; + //NSLog(@"[Gyroflow Toolbox Renderer] path: %@", path); + if (path) { + lensProfilesURL = [NSURL fileURLWithPath:path]; + } else { + NSLog(@"[Gyroflow Toolbox Renderer] WARNING - Failed to find matching identifier: %@", loadedLensIdentifierInGyroflowProjectString); + } + } + + //--------------------------------------------------------- + // Limit the file type to Gyroflow supported media files: + //--------------------------------------------------------- + UTType *gyroflow = [UTType typeWithFilenameExtension:@"gyroflow"]; + UTType *json = [UTType typeWithFilenameExtension:@"json"]; + + NSArray *allowedContentTypes = [NSArray arrayWithObjects:gyroflow, json, nil]; + + //--------------------------------------------------------- + // Setup an NSOpenPanel: + //--------------------------------------------------------- + NSOpenPanel* panel = [NSOpenPanel openPanel]; + [panel setCanChooseDirectories:NO]; + [panel setCanCreateDirectories:YES]; + [panel setCanChooseFiles:YES]; + [panel setAllowsMultipleSelection:NO]; + [panel setDirectoryURL:lensProfilesURL]; + + + [panel setAllowedContentTypes:allowedContentTypes]; + + //--------------------------------------------------------- + // Open the panel: + //--------------------------------------------------------- + NSModalResponse result = [panel runModal]; + if (result != NSModalResponseOK) { + return; + } + + NSURL *url = [panel URL]; + + //--------------------------------------------------------- + // Start accessing security scoped resource: + //--------------------------------------------------------- + if (![url startAccessingSecurityScopedResource]) { + [self showAlertWithMessage:@"An error has occurred." info:@"Failed to startAccessingSecurityScopedResource. This shouldn't happen."]; + return; + } + //--------------------------------------------------------- // Process the file depending on the file type: //--------------------------------------------------------- @@ -2329,6 +2448,7 @@ - (void)buttonLoadPresetLensProfile { NSString *extension = [[url pathExtension] lowercaseString]; NSString *loadResultString = nil; + BOOL isJSON = NO; if ([extension isEqualToString:@"json"]) { //--------------------------------------------------------- // Attempt to load the JSON Lens Profile: @@ -2338,6 +2458,7 @@ - (void)buttonLoadPresetLensProfile { [filePath UTF8String] ); loadResultString = [NSString stringWithUTF8String: loadResult]; + isJSON = YES; } else { //--------------------------------------------------------- // Attempt to load the Gyroflow Project Preset: @@ -2376,8 +2497,41 @@ - (void)buttonLoadPresetLensProfile { //--------------------------------------------------------- // Show success message: //--------------------------------------------------------- - // TODO: Make lens profile complete alert optional - [self showAlertWithMessage:@"Import Complete!" info:@"Your Lens Profile has been correctly applied."]; + NSString *message; //= @"The selected Lens Profile has been successfully applied."; + + if (isImporting) { + if (isJSON) { + message = @"The Gyroflow Project, and the selected Preset has been successfully imported into Final Cut Pro.\n\nYou can now adjust the parameters as required via the Video Inspector."; + } else { + message = @"The Gyroflow Project, and the selected Lens Profile has been successfully imported into Final Cut Pro.\n\nYou can now adjust the parameters as required via the Video Inspector."; + } + } else { + if (isJSON) { + message = @"The selected Preset has been successfully applied."; + } else { + message = @"The selected Lens Profile has been successfully applied."; + } + } + + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + if (![defaults boolForKey:@"suppressLoadPresetLensProfileSuccess"]) { + NSAlert *alert = [[[NSAlert alloc] init] autorelease]; + alert.icon = [NSImage imageNamed:@"GyroflowToolbox"]; + alert.alertStyle = NSAlertStyleInformational; + alert.messageText = @"Successfully Imported!"; + alert.informativeText = message; + alert.showsSuppressionButton = YES; + [alert beginSheetModalForWindow:loadLastGyroflowProjectView.window completionHandler:^(NSModalResponse result) { + if ([alert suppressionButton].state == NSControlStateValueOn) { + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"suppressLoadPresetLensProfileSuccess"]; + } + + //--------------------------------------------------------- + // Close the alert: + //--------------------------------------------------------- + [alert.window orderOut:nil]; + }]; + } //--------------------------------------------------------- // Stop Action API: @@ -2989,8 +3143,7 @@ - (void)buttonReloadGyroflowProject { //--------------------------------------------------------- // Show success message: //--------------------------------------------------------- - // TODO: Success message should be optional. - [self showAlertWithMessage:@"Success!" info:@"The Gyroflow Project has been successfully reloaded from disk."]; + [self showSuccessfullyReloadedAlert]; //--------------------------------------------------------- // Stop Action API & Stop Accessing Resource: @@ -3110,8 +3263,32 @@ - (void)buttonReloadGyroflowProject { //--------------------------------------------------------- // Show success message: //--------------------------------------------------------- - // TODO: This reload success message should be optional. - [self showAlertWithMessage:@"Success!" info:@"The Gyroflow Project has been successfully reloaded from disk."]; + [self showSuccessfullyReloadedAlert]; +} + +//--------------------------------------------------------- +// Show Successfully Reloaded Alert: +//--------------------------------------------------------- +- (void)showSuccessfullyReloadedAlert { + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + if (![defaults boolForKey:@"suppressSuccessfullyReloaded"]) { + NSAlert *alert = [[[NSAlert alloc] init] autorelease]; + alert.icon = [NSImage imageNamed:@"GyroflowToolbox"]; + alert.alertStyle = NSAlertStyleInformational; + alert.messageText = @"Successfully Reloaded!"; + alert.informativeText = @"The Gyroflow Project has been successfully reloaded from disk."; + alert.showsSuppressionButton = YES; + [alert beginSheetModalForWindow:loadLastGyroflowProjectView.window completionHandler:^(NSModalResponse result) { + if ([alert suppressionButton].state == NSControlStateValueOn) { + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"suppressSuccessfullyReloaded"]; + } + + //--------------------------------------------------------- + // Close the alert: + //--------------------------------------------------------- + [alert.window orderOut:nil]; + }]; + } } //--------------------------------------------------------- @@ -3498,6 +3675,31 @@ - (void)readLastProjectFromGyroflowPreferencesFile { // //--------------------------------------------------------- +//--------------------------------------------------------- +// Show Successfully Imported Alert: +//--------------------------------------------------------- +- (void)showSuccessfullyImportedAlert { + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + if (![defaults boolForKey:@"suppressSuccessfullyImported"]) { + NSAlert *alert = [[[NSAlert alloc] init] autorelease]; + alert.icon = [NSImage imageNamed:@"GyroflowToolbox"]; + alert.alertStyle = NSAlertStyleInformational; + alert.messageText = @"Successfully Imported!"; + alert.informativeText = @"The Gyroflow Project has been successfully imported into Final Cut Pro.\n\nYou can now adjust the parameters as required via the Video Inspector."; + alert.showsSuppressionButton = YES; + [alert beginSheetModalForWindow:loadLastGyroflowProjectView.window completionHandler:^(NSModalResponse result) { + if ([alert suppressionButton].state == NSControlStateValueOn) { + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"suppressSuccessfullyImported"]; + } + + //--------------------------------------------------------- + // Close the alert: + //--------------------------------------------------------- + [alert.window orderOut:nil]; + }]; + } +} + //--------------------------------------------------------- // Import Media with Optional URL: //--------------------------------------------------------- @@ -3696,7 +3898,7 @@ - (BOOL)importMediaWithOptionalURL:(NSURL*)optionalURL { ); NSString *getDefaultValuesResultString = [NSString stringWithUTF8String:getDefaultValuesResult]; - NSLog(@"[Gyroflow Toolbox Renderer] getDefaultValuesResult: %@", getDefaultValuesResultString); + //NSLog(@"[Gyroflow Toolbox Renderer] getDefaultValuesResult: %@", getDefaultValuesResultString); //--------------------------------------------------------- // Use the Action API to allow us to change the parameters: @@ -3719,7 +3921,7 @@ - (BOOL)importMediaWithOptionalURL:(NSURL*)optionalURL { // Update 'Media Path': //--------------------------------------------------------- [paramSetAPI setStringParameterValue:path toParameter:kCB_MediaPath]; - NSLog(@"[Gyroflow Toolbox Renderer] mediaPath: %@", path); + //NSLog(@"[Gyroflow Toolbox Renderer] mediaPath: %@", path); //--------------------------------------------------------- // Update 'Media Bookmark Data': @@ -3837,11 +4039,13 @@ - (BOOL)importMediaWithOptionalURL:(NSURL*)optionalURL { if ([alert suppressionButton].state == NSControlStateValueOn) { [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"suppressNoLensProfileDetected"]; } - [self buttonLoadPresetLensProfile]; + [self buttonLoadPresetLensProfileIsImporting:YES]; }]; } else { - [self buttonLoadPresetLensProfile]; + [self buttonLoadPresetLensProfileIsImporting:YES]; } + } else { + [self showSuccessfullyImportedAlert]; } return YES; @@ -4071,8 +4275,7 @@ - (void)importGyroflowProjectWithOptionalURL:(NSURL*)optionalURL { //--------------------------------------------------------- // Show Victory Message: //--------------------------------------------------------- - // TODO: This import success alert should be optional. - [self showAlertWithMessage:@"Success!" info:@"The Gyroflow Project has been successfully imported.\n\nYou can now adjust the parameters as required."]; + [self showSuccessfullyImportedAlert]; } //--------------------------------------------------------- @@ -4152,7 +4355,7 @@ - (void)importBRAWToolboxClipWithPath:(NSString*)path bookmarkDataString:(NSStri alert.icon = [NSImage imageNamed:@"GyroflowToolbox"]; alert.alertStyle = NSAlertStyleInformational; alert.messageText = @"Gyroflow Toolbox Requires Permission"; - alert.informativeText = @"To make it easier to import BRAW Toolbox clips into Gyroflow Toolbox, you'll need to grant Gyroflow Toolbox sandbox access to a BRAW Toolbox helper file.\n\nOn the next panel, please select 'Grant Sandbox Access' to continue."; + alert.informativeText = @"To make it easier to import BRAW Toolbox clips into Gyroflow Toolbox, you'll need to grant Gyroflow Toolbox sandbox access to a BRAW Toolbox helper file.\n\nOn the next panel, please select 'Grant Access' to continue."; [alert beginSheetModalForWindow:importMediaFileView.window completionHandler:^(NSModalResponse result){ //--------------------------------------------------------- @@ -4423,6 +4626,46 @@ - (void)importBRAWToolboxClipWithPath:(NSString*)path bookmarkDataString:(NSStri // //--------------------------------------------------------- +//--------------------------------------------------------- +// Get Lens Profile Names from Directory: +//--------------------------------------------------------- +- (NSDictionary *)getLensProfileIdentifiersFromDirectory:(NSString *)directoryPath { + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSMutableArray *jsonFilePaths = [NSMutableArray array]; + NSDictionary *result = [NSMutableDictionary dictionary]; + + //--------------------------------------------------------- + // Enumerate all files in the directory and its + // sub-directories: + //--------------------------------------------------------- + NSDirectoryEnumerator *enumerator = [fileManager enumeratorAtURL:[NSURL fileURLWithPath:directoryPath] + includingPropertiesForKeys:nil + options:0 + errorHandler:nil]; + + for (NSURL *fileURL in enumerator) { + if ([[fileURL pathExtension] isEqualToString:@"json"]) { + [jsonFilePaths addObject:fileURL.path]; + } + } + + //--------------------------------------------------------- + // Read each JSON file and extract the "identifier": + //--------------------------------------------------------- + for (NSString *filePath in jsonFilePaths) { + NSData *data = [NSData dataWithContentsOfFile:filePath]; + if (data) { + NSError *error = nil; + NSDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; + if (error == nil && jsonObject[@"identifier"]) { + [(NSMutableDictionary *)result setObject:filePath forKey:jsonObject[@"identifier"]]; + } + } + } + + return [result copy]; +} + //--------------------------------------------------------- // Converts a NSNumber to a Control State: //--------------------------------------------------------- @@ -4545,6 +4788,7 @@ - (void)showAlertWithMessage:(NSString*)message info:(NSString*)info { alert.alertStyle = NSAlertStyleInformational; alert.messageText = message; alert.informativeText = info; + alert.window.appearance = [NSAppearance appearanceNamed:NSAppearanceNameVibrantDark]; [alert runModal]; }); } @@ -4559,6 +4803,7 @@ - (void)showAsyncAlertWithMessage:(NSString*)message info:(NSString*)info { alert.alertStyle = NSAlertStyleInformational; alert.messageText = message; alert.informativeText = info; + alert.window.appearance = [NSAppearance appearanceNamed:NSAppearanceNameVibrantDark]; [alert runModal]; }); }