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