Revision: 29531
          http://sourceforge.net/p/bibdesk/svn/29531
Author:   hofman
Date:     2025-09-05 15:54:40 +0000 (Fri, 05 Sep 2025)
Log Message:
-----------
Allow download manager to handle BDSKDownload objects. Declare delegate 
conformances in header. Conditionally download using BDSKDownload from text 
import webview on 11.2-, and add Download Linked File context menu item. 
Replace Download Linked File context menu item also on 11.3+ as it does not 
work in all OS versions.

Modified Paths:
--------------
    trunk/bibdesk/BDSKDownloadManager.h
    trunk/bibdesk/BDSKDownloadManager.m
    trunk/bibdesk/BDSKTextImportController.m
    trunk/bibdesk/de.lproj/Localizable.strings
    trunk/bibdesk/en.lproj/Localizable.strings
    trunk/bibdesk/fr.lproj/Localizable.strings

Modified: trunk/bibdesk/BDSKDownloadManager.h
===================================================================
--- trunk/bibdesk/BDSKDownloadManager.h 2025-09-04 16:06:45 UTC (rev 29530)
+++ trunk/bibdesk/BDSKDownloadManager.h 2025-09-05 15:54:40 UTC (rev 29531)
@@ -38,9 +38,21 @@
 
 #import <Cocoa/Cocoa.h>
 #import <WebKit/WebKit.h>
+#import "BDSKDownloader.h"
 
 NS_ASSUME_NONNULL_BEGIN
 
+#if MAC_OS_X_VERSION_MAX_ALLOWED < 110300
+@protocol WKDownloadDelegate <NSObject>
+@end
+@interface WKDownload : NSObject <NSProgressReporting>
+@property (nonatomic, readonly, nullable) NSURLRequest *originalRequest;
+@property (nonatomic, readonly, weak) WKWebView *webView;
+@property (nonatomic, weak) id <WKDownloadDelegate> delegate;
+- (void)cancel:(void(^)(NSData * resumeData))completionHandler;
+@end
+#endif
+
 extern NSString *BDSKDownloadsDidChangeNotification;
 
 typedef NS_ENUM(NSInteger, BDSKDownloadStatus) {
@@ -49,7 +61,7 @@
     BDSKDownloadStatusFailed
 };
 
-@interface BDSKDownloadManager : NSObject <WebDownloadDelegate> {
+@interface BDSKDownloadManager : NSObject <WebDownloadDelegate, 
WKDownloadDelegate, BDSKDownloadDelegate> {
     NSMutableArray *downloads;
 }
 

Modified: trunk/bibdesk/BDSKDownloadManager.m
===================================================================
--- trunk/bibdesk/BDSKDownloadManager.m 2025-09-04 16:06:45 UTC (rev 29530)
+++ trunk/bibdesk/BDSKDownloadManager.m 2025-09-05 15:54:40 UTC (rev 29531)
@@ -40,7 +40,6 @@
 #import "BDSKStringConstants.h"
 #import "NSURL_BDSKExtensions.h"
 #import "NSFileManager_BDSKExtensions.h"
-#import <WebKit/WebKit.h>
 
 #define BDSKRemoveFinishedDownloadsKey @"BDSKRemoveFinishedDownloads"
 #define BDSKRemoveFailedDownloadsKey   @"BDSKRemoveFailedDownloads"
@@ -47,20 +46,6 @@
 
 NSString *BDSKDownloadsDidChangeNotification = 
@"BDSKDownloadsDidChangeNotification";
 
-#if MAC_OS_X_VERSION_MAX_ALLOWED < 110300
-@protocol WKDownloadDelegate <NSObject>
-@end
-@interface WKDownload : NSObject <NSProgressReporting>
-@property (nonatomic, readonly, nullable) NSURLRequest *originalRequest;
-@property (nonatomic, readonly, weak) WKWebView *webView;
-@property (nonatomic, weak) id <WKDownloadDelegate> delegate;
-- (void)cancel:(void(^)(NSData * resumeData))completionHandler;
-@end
-#endif
-
-@interface BDSKDownloadManager () <WKDownloadDelegate>
-@end
-
 @implementation BDSKDownloadManager
 
 @synthesize downloads;
@@ -102,7 +87,6 @@
 }
 
 - (void)addDownload:(id)download {
-    [download setDelegate:self];
     [downloads addObject:[[BDSKWebDownload alloc] initWithDownload:download 
URL:[[download originalRequest] URL]]];
     [self notifyUpdate];
 }
@@ -203,10 +187,10 @@
 #pragma mark NSURLDownload delegate protocol
 
 - (void)downloadDidBegin:(NSURLDownload *)download {
-    [downloads addObject:[[BDSKWebDownload alloc] initWithDownload:download 
URL:[[download request] URL]]];
-    [self notifyUpdate];
+    [self addDownload:download];
 }
 
+// both in NSURLDownloadDelegate and WKDownloadDelegate
 - (void)downloadDidFinish:(id)download {
     BDSKWebDownload *webDownload = [self webDownloadForDownload:download];
     
@@ -272,8 +256,12 @@
 // -download:decideDestinationWithSuggestedFilename: is not functional anymore
 // it returns after the destination is already returned and downloading to 
disk has already started
 
-- (void)download:(NSURLDownload *)download didCreateDestination:(NSString 
*)path {
-    [[self webDownloadForDownload:download] setFileURL:[NSURL 
fileURLWithPath:path isDirectory:NO]];
+// both in NSURLDownloadDelegate and BDSKDownloadDelegate
+- (void)download:(id)download didCreateDestination:(id)location {
+    if ([location isKindOfClass:[NSURL class]])
+        [[self webDownloadForDownload:download] setFileURL:location];
+    else if ([location isKindOfClass:[NSString class]])
+        [[self webDownloadForDownload:download] setFileURL:[NSURL 
fileURLWithPath:location isDirectory:NO]];
 }
 
 - (BOOL)download:(NSURLDownload *)download 
shouldDecodeSourceDataOfMIMEType:(NSString *)encodingType {
@@ -280,6 +268,68 @@
     return YES;
 }
 
+#pragma mark BDSKDownload delegate protocol
+
+- (void)download:(BDSKDownload *)download didCompleteWithError:(NSError 
*)error {
+    BDSKWebDownload *webDownload = [self webDownloadForDownload:download];
+    
+    if (error) {
+        [webDownload setStatus:BDSKDownloadStatusFailed];
+        
+        if ([[NSUserDefaults standardUserDefaults] 
boolForKey:BDSKRemoveFailedDownloadsKey] && webDownload)
+            [downloads removeObject:webDownload];
+        [self notifyUpdate];
+        
+        if ([[error domain] isEqualToString:NSURLErrorDomain] && [error code] 
== NSURLErrorCancelled)
+            return;
+        
+        NSString *errorDescription = [error localizedDescription] ?: 
NSLocalizedString(@"An error occured during download.", @"Informative text in 
alert dialog");
+        NSAlert *alert = [[NSAlert alloc] init];
+        [alert setMessageText:NSLocalizedString(@"Download Failed", @"Message 
in alert dialog when download failed")];
+        [alert setInformativeText:errorDescription];
+        [alert runModal];
+    } else {
+        [webDownload setStatus:BDSKDownloadStatusFinished];
+        
+        if ([[NSUserDefaults standardUserDefaults] 
boolForKey:BDSKRemoveFinishedDownloadsKey] && webDownload)
+            [downloads removeObject:webDownload];
+        [self notifyUpdate];
+
+    }
+}
+
+- (void)download:(BDSKDownload *)download 
decideDestinationWithSuggestedFilename:(NSString *)filename 
completionHandler:(void (^)(NSURL *destinationURL, BOOL 
allowOverwrite))completionHandler {
+    NSString *extension = [filename pathExtension];
+   
+    NSString *downloadsDirectory = [[[NSUserDefaults standardUserDefaults] 
stringForKey:BDSKDownloadsDirectoryKey] stringByExpandingTildeInPath];
+    NSURL *downloadsURL = nil;
+    if (downloadsDirectory)
+        downloadsURL = [NSURL fileURLWithPath:downloadsDirectory 
isDirectory:YES];
+    else
+        downloadsURL = [[NSFileManager defaultManager] downloadFolderURL];
+    
+    NSSavePanel *sPanel = [NSSavePanel savePanel];
+    if (NO == [extension isEqualToString:@""])
+        [sPanel setAllowedFileTypes:@[extension]];
+    [sPanel setAllowsOtherFileTypes:YES];
+    [sPanel setCanSelectHiddenExtension:YES];
+    [sPanel setNameFieldStringValue:filename];
+    [sPanel setDirectoryURL:downloadsURL];
+    
+    if ([sPanel runModal] == NSModalResponseOK) {
+        completionHandler([sPanel URL], YES);
+    } else {
+        [download cancel];
+        
+        BDSKWebDownload *webDownload = [self webDownloadForDownload:download];
+        [webDownload setStatus:BDSKDownloadStatusFailed];
+        
+        if ([[NSUserDefaults standardUserDefaults] 
boolForKey:BDSKRemoveFailedDownloadsKey] && webDownload)
+            [downloads removeObject:webDownload];
+        [self notifyUpdate];
+    }
+}
+
 #pragma mark WKDownload delegate protocol
 
 - (void)download:(WKDownload *)download didFailWithError:(NSError *)error 
resumeData:(NSData *)resumeData API_AVAILABLE(macos(11.3)) {
@@ -387,7 +437,7 @@
 }
 
 - (void)cancel {
-    if ([download isKindOfClass:[NSURLDownload class]])
+    if ([download isKindOfClass:[NSURLDownload class]] || [download 
isKindOfClass:[BDSKDownload class]])
         [download cancel];
     else if (@available(macOS 11.3, *))
         [(WKDownload *)download cancel:nil];

Modified: trunk/bibdesk/BDSKTextImportController.m
===================================================================
--- trunk/bibdesk/BDSKTextImportController.m    2025-09-04 16:06:45 UTC (rev 
29530)
+++ trunk/bibdesk/BDSKTextImportController.m    2025-09-05 15:54:40 UTC (rev 
29531)
@@ -91,6 +91,7 @@
     BDSKWebViewCustomMenuActionOpenLink,
     BDSKWebViewCustomMenuActionBookmarkLink,
     BDSKWebViewCustomMenuActionAddLink,
+    BDSKWebViewCustomMenuActionSaveLink,
     BDSKWebViewCustomMenuActionDownloadLink
 };
 
@@ -100,6 +101,12 @@
 @class WKDownload;
 #define WKNavigationActionPolicyDownload 2
 #define WKNavigationResponsePolicyDownload 2
+@interface WKWebView (BDSKBigSurDeclarations)
+- (void)startDownloadUsingRequest:(NSURLRequest *)request 
completionHandler:(void(^)(WKDownload *))completionHandler;
+@end
+@interface WKNavigationAction (BDSKBigSurDeclarations)
+@property (nonatomic, readonly) BOOL shouldPerformDownload;
+@end
 #endif
 
 #pragma mark -
@@ -937,12 +944,15 @@
 }
 
 - (void)webView:(WKWebView *)aWebView 
decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse 
decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
-    if ([navigationResponse canShowMIMEType])
+    if ([navigationResponse canShowMIMEType]) {
         decisionHandler(WKNavigationResponsePolicyAllow);
-    else if (@available(macOS 11.3, *))
+    } else if (@available(macOS 11.3, *)) {
         decisionHandler(WKNavigationResponsePolicyDownload);
-    else
+    } else {
+        BDSKDownload *aDownload = [[BDSKDownloader sharedDownloader] 
startFileDownloadWithRequest:[NSURLRequest requestWithURL:[[navigationResponse 
response] URL]] delegate:[BDSKDownloadManager sharedManager]];
+        [[BDSKDownloadManager sharedManager] addDownload:aDownload];
         decisionHandler(WKNavigationResponsePolicyCancel);
+    }
 }
 
 - (void)webView:(WKWebView *)aWebView 
decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction 
preferences:(WKWebpagePreferences *)preferences decisionHandler:(void 
(^)(WKNavigationActionPolicy, WKWebpagePreferences *))decisionHandler  
API_AVAILABLE(macos(10.15)) {
@@ -951,6 +961,10 @@
             decisionHandler(WKNavigationActionPolicyDownload, preferences);
         else
             decisionHandler(WKNavigationActionPolicyAllow, preferences);
+    } else if ([navigationAction navigationType] == 
WKNavigationTypeFormSubmitted || [[[navigationAction request] 
valueForHTTPHeaderField:@"Content-Disposition"] hasPrefix:@"attachment"]) {
+        BDSKDownload *aDownload = [[BDSKDownloader sharedDownloader] 
startFileDownloadWithRequest:[navigationAction request] 
delegate:[BDSKDownloadManager sharedManager]];
+        [[BDSKDownloadManager sharedManager] addDownload:aDownload];
+        decisionHandler(WKNavigationActionPolicyCancel, preferences);
     } else {
         decisionHandler(WKNavigationActionPolicyAllow, preferences);
     }
@@ -957,10 +971,12 @@
 }
 
 - (void)webView:(WKWebView *)aWebView navigationAction:(WKNavigationAction 
*)navigationAction didBecomeDownload:(WKDownload *)aDownload 
API_AVAILABLE(macos(11.3)) {
+    [aDownload setDelegate:[BDSKDownloadManager sharedManager]];
     [[BDSKDownloadManager sharedManager] addDownload:aDownload];
 }
 
 - (void)webView:(WKWebView *)aWebView navigationResponse:(WKNavigationResponse 
*)navigationResponse didBecomeDownload:(WKDownload *)aDownload 
API_AVAILABLE(macos(11.3)) {
+    [aDownload setDelegate:[BDSKDownloadManager sharedManager]];
     [[BDSKDownloadManager sharedManager] addDownload:aDownload];
 }
 
@@ -1045,7 +1061,7 @@
         else
             [[self publication] addFileForURL:url autoFile:YES 
runScriptHook:NO];
         [[self undoManager] setActionName:NSLocalizedString(@"Edit 
Publication", @"Undo action name")];
-    } else if (customMenuAction == BDSKWebViewCustomMenuActionDownloadLink) {
+    } else if (customMenuAction == BDSKWebViewCustomMenuActionSaveLink) {
         if (isDownloading) {
             NSBeep();
             return nil;
@@ -1058,6 +1074,16 @@
             [alert setInformativeText:NSLocalizedString(@"The URL to download 
is either invalid or unsupported.", @"Informative text in alert dialog")];
             [alert beginSheetModalForWindow:[self window] 
completionHandler:NULL];
         }
+    } else if (customMenuAction == BDSKWebViewCustomMenuActionDownloadLink) {
+        if (@available(macOS 11.3, *)) {
+            [webView startDownloadUsingRequest:request 
completionHandler:^(WKDownload *aDownload){
+                [aDownload setDelegate:[BDSKDownloadManager sharedManager]];
+                [[BDSKDownloadManager sharedManager] addDownload:aDownload];
+            }];
+        } else {
+            BDSKDownload *aDownload = [[BDSKDownloader sharedDownloader] 
startFileDownloadWithRequest:request delegate:[BDSKDownloadManager 
sharedManager]];
+            [[BDSKDownloadManager sharedManager] addDownload:aDownload];
+        }
     } else {
         [[NSWorkspace sharedWorkspace] openURLWithDefaultApp:url];
     }
@@ -1076,7 +1102,7 @@
             NSMenuItem *openLinkItem = item;
             [menu removeItemAtIndex:i];
             
-            item = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Save 
Link as Local File", @"Menu item title") 
action:@selector(performCustomMenuAction:) keyEquivalent:@""];
+            item = [[NSMenuItem alloc] 
initWithTitle:NSLocalizedString(@"Download Linked File", @"Menu item title") 
action:@selector(performCustomMenuAction:) keyEquivalent:@""];
             [item setTarget:self];
             [item setTag:BDSKWebViewCustomMenuActionDownloadLink];
             [item setIdentifier:@"BDSKMenuItemIdentifierDownloadLink"];
@@ -1083,6 +1109,13 @@
             [item setRepresentedObject:openLinkItem];
             [menu insertItem:item atIndex:i];
             
+            item = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Save 
Link as Local File", @"Menu item title") 
action:@selector(performCustomMenuAction:) keyEquivalent:@""];
+            [item setTarget:self];
+            [item setTag:BDSKWebViewCustomMenuActionSaveLink];
+            [item setIdentifier:@"BDSKMenuItemIdentifierSaveLink"];
+            [item setRepresentedObject:openLinkItem];
+            [menu insertItem:item atIndex:i];
+            
             if (useURLFields)
                 title = NSLocalizedString(@"Copy Link to Url Field", @"Menu 
item title");
             else
@@ -1109,7 +1142,11 @@
             [menu insertItem:item atIndex:i];
             
             if (j >= 0)
-                j += 3;
+                j += 4;
+        } else if ([itemID 
isEqualToString:@"WKMenuItemIdentifierDownloadLinkedFile"]) {
+            [menu removeItemAtIndex:i];
+            if (j > 0)
+                j--;
         } else if ([itemID isEqualToString:@"WKMenuItemIdentifierCopy"] ||
                    [itemID isEqualToString:@"WKMenuItemIdentifierCopyLink"]) {
             if (j == -1)

Modified: trunk/bibdesk/de.lproj/Localizable.strings
===================================================================
(Binary files differ)

Modified: trunk/bibdesk/en.lproj/Localizable.strings
===================================================================
(Binary files differ)

Modified: trunk/bibdesk/fr.lproj/Localizable.strings
===================================================================
(Binary files differ)

This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.



_______________________________________________
Bibdesk-commit mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/bibdesk-commit

Reply via email to