Downloads - Perform write on background thread, add cancel and clear, indicate complete/cancel in progress cell
This commit is contained in:
parent
38b61f46e7
commit
74e3fe059f
|
@ -2,9 +2,11 @@
|
|||
|
||||
@class DownloadManager;
|
||||
@class DownloadItem;
|
||||
struct download_context;
|
||||
|
||||
@protocol DownloadManagerDelegate
|
||||
-(void)downloadManagerDidAddDownload: (DownloadManager*)aDownloadManager;
|
||||
-(void)downloadManager: (DownloadManager*)aDownloadManager didRemoveItems: (NSArray*)downloadItems;
|
||||
-(void)downloadManager: (DownloadManager*)aDownloadManager didUpdateItem: (DownloadItem*)aDownloadItem;
|
||||
@end
|
||||
|
||||
|
@ -19,11 +21,14 @@
|
|||
NSOutputStream *outputStream;
|
||||
NSString *error;
|
||||
DownloadManager *manager;
|
||||
NSTimeInterval lastWrite;
|
||||
struct download_context *ctx;
|
||||
}
|
||||
-(BOOL)appendToDownload: (NSData*)data;
|
||||
-(void)cancel;
|
||||
-(void)complete;
|
||||
-(BOOL)isComplete;
|
||||
-(BOOL)isCancelled;
|
||||
-(void)failWithMessage: (NSString*)message;
|
||||
-(NSURL*)destination;
|
||||
-(NSString*)detailsText;
|
||||
|
@ -38,8 +43,10 @@
|
|||
id<DownloadManagerDelegate> delegate;
|
||||
}
|
||||
+(DownloadManager*)defaultDownloadManager;
|
||||
-(DownloadItem*)createDownloadForDestination: (NSURL*)path withSizeInBytes: (NSUInteger)size;
|
||||
-(DownloadItem*)createDownloadForDestination: (NSURL*)path withContext: (struct download_context*)ctx;
|
||||
-(NSArray*)downloads;
|
||||
-(void)removeDownloadsAtIndexes: (NSIndexSet*)anIndexSet;
|
||||
-(void)cancelDownloadsAtIndexes: (NSIndexSet*)anIndexSet;
|
||||
-(id)delegate;
|
||||
-(void)setDelegate: (id<DownloadManagerDelegate>)aDelegate;
|
||||
@end
|
|
@ -1,9 +1,12 @@
|
|||
#import <Cocoa/Cocoa.h>
|
||||
#import "DownloadManager.h"
|
||||
#import "desktop/download.h"
|
||||
|
||||
// TODO: - Verify behavior of performOnBackgroundThread on a multiprocessor system!!
|
||||
|
||||
@implementation DownloadItem
|
||||
|
||||
-(id)initWithManager: (DownloadManager*)aManager destination: (NSURL*)aDestination size: (NSInteger)aSize index: (NSUInteger)anIndex {
|
||||
-(id)initWithManager: (DownloadManager*)aManager destination: (NSURL*)aDestination size: (NSInteger)aSize index: (NSUInteger)anIndex ctx: (struct download_context*)aCtx {
|
||||
if (self = [super init]) {
|
||||
error = nil;
|
||||
index = anIndex;
|
||||
|
@ -18,6 +21,7 @@
|
|||
append: NO];
|
||||
[outputStream retain];
|
||||
[outputStream open];
|
||||
ctx = aCtx;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
@ -30,23 +34,41 @@
|
|||
if (error) {
|
||||
[error release];
|
||||
}
|
||||
download_context_destroy(ctx);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
-(BOOL)appendToDownload: (NSData*)data {
|
||||
// NOTE: - Not sure if this really works on mp systems...
|
||||
// Does this get queued sequentially? If not need to upkeep our own thread...
|
||||
[self performSelectorInBackground: @selector(reallyWriteData:) withObject: data];
|
||||
return YES;
|
||||
}
|
||||
|
||||
-(void)reallyWriteData: (NSData*)data {
|
||||
NSUInteger len = [data length];
|
||||
NSUInteger writtenNow = [outputStream write: [data bytes] maxLength: len];
|
||||
written += writtenNow;
|
||||
// Unless im misunderstanding download_context_get_total_length appears to return
|
||||
// a too-small non-zero value for download size when called in
|
||||
// gnustep_download_create...
|
||||
// a too-small non-zero value for download size when called so...
|
||||
size = MAX(written, size);
|
||||
NSTimeInterval time = [startDate timeIntervalSinceNow];
|
||||
// Only notify the manager at most once in each second.
|
||||
if ((int)time != (int)lastWrite) {
|
||||
[self performSelectorOnMainThread: @selector(notifyManager) withObject: nil
|
||||
waitUntilDone: NO];
|
||||
}
|
||||
lastWrite = time;
|
||||
}
|
||||
-(void)notifyManager {
|
||||
[[manager delegate] downloadManager: manager didUpdateItem: self];
|
||||
return writtenNow == len;
|
||||
}
|
||||
|
||||
-(void)cancel {
|
||||
NSLog(@"cancel!!");
|
||||
if (!completed) {
|
||||
cancelled = YES;
|
||||
download_context_abort(ctx);
|
||||
[self complete];
|
||||
}
|
||||
}
|
||||
|
@ -69,6 +91,9 @@
|
|||
return completed;
|
||||
}
|
||||
|
||||
-(BOOL)isCancelled {
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
-(NSURL*)destination {
|
||||
return destination;
|
||||
|
@ -84,7 +109,12 @@
|
|||
}
|
||||
NSUInteger bytesLeft = size - written;
|
||||
double kibLeft = (double)bytesLeft / 1024.0;
|
||||
return [NSString stringWithFormat: @"%.2f KiB", kibLeft];
|
||||
if (kibLeft < 1024.0) {
|
||||
return [NSString stringWithFormat: @"%.2f KiB", kibLeft];
|
||||
} else {
|
||||
double mibLeft = (double)kibLeft / 1024.0;
|
||||
return [NSString stringWithFormat: @"%.2f MiB", mibLeft];
|
||||
}
|
||||
}
|
||||
|
||||
-(NSString*)speedText {
|
||||
|
@ -93,7 +123,12 @@
|
|||
}
|
||||
NSTimeInterval secondsPassed = -[startDate timeIntervalSinceNow];
|
||||
double kibPerSecond = (double)written / (secondsPassed * 1024.0);
|
||||
return [NSString stringWithFormat: @"%.2f KiB/s", kibPerSecond];
|
||||
if (kibPerSecond < 1024.0) {
|
||||
return [NSString stringWithFormat: @"%.2f KiB/s", kibPerSecond];
|
||||
} else {
|
||||
double mibPerSecond = kibPerSecond / 1024.0;
|
||||
return [NSString stringWithFormat: @"%.2f MiB/s", mibPerSecond];
|
||||
}
|
||||
}
|
||||
|
||||
-(double)completionProgress {
|
||||
|
@ -149,9 +184,11 @@
|
|||
[super dealloc];
|
||||
}
|
||||
|
||||
-(DownloadItem*)createDownloadForDestination: (NSURL*)path withSizeInBytes: (NSUInteger)size {
|
||||
-(DownloadItem*)createDownloadForDestination: (NSURL*)path withContext: (struct download_context*)ctx {
|
||||
// TODO: - dataSize is smaller than the actual size in some cases. Why?
|
||||
NSUInteger dataSize = download_context_get_total_length(ctx);
|
||||
DownloadItem *item = [[DownloadItem alloc] initWithManager: self destination: path
|
||||
size: size index: [downloads count]];
|
||||
size: dataSize index: [downloads count] ctx: ctx];
|
||||
[downloads addObject: item];
|
||||
[item release];
|
||||
[delegate downloadManagerDidAddDownload: self];
|
||||
|
@ -170,4 +207,19 @@
|
|||
return delegate;
|
||||
}
|
||||
|
||||
-(void)removeDownloadsAtIndexes: (NSIndexSet*)anIndexSet {
|
||||
NSUInteger dlCount = [downloads count];
|
||||
if ([anIndexSet indexGreaterThanOrEqualToIndex: dlCount] == NSNotFound) {
|
||||
NSArray *items = [downloads objectsAtIndexes: anIndexSet];
|
||||
[downloads removeObjectsAtIndexes: anIndexSet];
|
||||
[delegate downloadManager: self didRemoveItems: items];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-(void)cancelDownloadsAtIndexes: (NSIndexSet*)anIndexSet {
|
||||
NSArray *items = [downloads objectsAtIndexes: anIndexSet];
|
||||
[items makeObjectsPerformSelector: @selector(cancel)];
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,9 +1,13 @@
|
|||
#import <Cocoa/Cocoa.h>
|
||||
#import "DownloadManager.h"
|
||||
|
||||
|
||||
@interface DownloadsWindowController: NSWindowController<NSTableViewDataSource, DownloadManagerDelegate> {
|
||||
id tableView;
|
||||
}
|
||||
|
||||
-(void)remove: (id)sender;
|
||||
-(void)cancel: (id)sender;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
#import "DownloadManager.h"
|
||||
#import "ProgressBarCell.h"
|
||||
|
||||
#define TAG_MENU_REMOVE 5
|
||||
#define TAG_MENU_CANCEL 3
|
||||
|
||||
@implementation DownloadsWindowController
|
||||
|
||||
-(id)init {
|
||||
|
@ -35,7 +38,13 @@
|
|||
objectAtIndex: rowIndex];
|
||||
NSString *identifier = [aTableColumn identifier];
|
||||
if ([identifier isEqual: @"progress"]) {
|
||||
return [NSNumber numberWithDouble: [item completionProgress]];
|
||||
if ([item isCancelled]) {
|
||||
return @"Cancelled";
|
||||
} else if ([item isComplete]) {
|
||||
return @"Complete";
|
||||
} else {
|
||||
return [NSNumber numberWithDouble: [item completionProgress]];
|
||||
}
|
||||
} else if ([identifier isEqual: @"details"]) {
|
||||
return [item detailsText];
|
||||
} else if ([identifier isEqual: @"remaining"]) {
|
||||
|
@ -67,11 +76,45 @@
|
|||
[tableView reloadData];
|
||||
}
|
||||
|
||||
-(void)downloadManager: (DownloadManager*)aDownloadManager didRemoveItems: (NSArray*)downloadItems {
|
||||
[tableView reloadData];
|
||||
}
|
||||
|
||||
-(void)downloadManager: (DownloadManager*)aDownloadManager didUpdateItem: (DownloadItem*)aDownloadItem {
|
||||
NSIndexSet *rows = [NSIndexSet indexSetWithIndex: [aDownloadItem index]];
|
||||
NSIndexSet *columns = [NSIndexSet indexSetWithIndexesInRange: NSMakeRange(0, 4)];
|
||||
[tableView reloadDataForRowIndexes: rows columnIndexes: columns];
|
||||
}
|
||||
|
||||
// MARK: - Menu Stuff
|
||||
|
||||
-(BOOL)validateMenuItem: (NSMenuItem*)aMenuItem {
|
||||
NSInteger tag = [aMenuItem tag];
|
||||
if (tag == TAG_MENU_REMOVE || tag == TAG_MENU_CANCEL) {
|
||||
return [tableView numberOfSelectedRows] > 0;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
-(void)remove: (id)sender {
|
||||
id row;
|
||||
NSMutableIndexSet *indices = [NSMutableIndexSet indexSet];
|
||||
NSEnumerator *selectedRows = [tableView selectedRowEnumerator];
|
||||
while ((row = [selectedRows nextObject]) != nil) {
|
||||
[indices addIndex: [row integerValue]];
|
||||
}
|
||||
[[DownloadManager defaultDownloadManager] removeDownloadsAtIndexes: indices];
|
||||
}
|
||||
|
||||
-(void)cancel: (id)sender {
|
||||
id row;
|
||||
NSMutableIndexSet *indices = [NSMutableIndexSet indexSet];
|
||||
NSEnumerator *selectedRows = [tableView selectedRowEnumerator];
|
||||
while ((row = [selectedRows nextObject]) != nil) {
|
||||
[indices addIndex: [row integerValue]];
|
||||
}
|
||||
[[DownloadManager defaultDownloadManager] cancelDownloadsAtIndexes: indices];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -4,16 +4,31 @@
|
|||
@implementation ProgressBarCell
|
||||
|
||||
-(void)drawInteriorWithFrame: (NSRect)cellFrame inView: (NSView*)controlView {
|
||||
double progress = [[self objectValue] doubleValue];
|
||||
double progress = 0.0;
|
||||
NSString *text = nil;
|
||||
NSColor *color = nil;
|
||||
if ([[self objectValue] isKindOfClass: [NSNumber class]]) {
|
||||
progress = [[self objectValue] doubleValue];
|
||||
} else if ([[self objectValue] isKindOfClass: [NSString class]]) {
|
||||
text = [self objectValue];
|
||||
}
|
||||
if ([self isHighlighted]) {
|
||||
color = [NSColor whiteColor];
|
||||
} else {
|
||||
color = [NSColor colorWithDeviceRed: 0.2 green: 0.5 blue: 0.9 alpha: 1.0];
|
||||
}
|
||||
cellFrame.size.width *= progress;
|
||||
[NSGraphicsContext saveGraphicsState];
|
||||
if ([self isHighlighted]) {
|
||||
[[NSColor whiteColor] set];
|
||||
} else {
|
||||
[[NSColor colorWithDeviceRed: 0.2 green: 0.5 blue: 0.9 alpha: 1.0] set];
|
||||
}
|
||||
[color set];
|
||||
[NSBezierPath fillRect: cellFrame];
|
||||
[NSGraphicsContext restoreGraphicsState];
|
||||
if (text != nil) {
|
||||
NSAttributedString *str = [[NSAttributedString alloc] initWithString: text
|
||||
attributes: [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
color, NSForegroundColorAttributeName, nil]];
|
||||
[str drawAtPoint: cellFrame.origin];
|
||||
[str release];
|
||||
}
|
||||
[NSGraphicsContext restoreGraphicsState];;
|
||||
}
|
||||
|
||||
-(void)highlight: (BOOL)lit withFrame: (NSRect)cellFrame inView: (NSView*)controlView {
|
||||
|
|
Binary file not shown.
|
@ -15,6 +15,8 @@
|
|||
"didTapNewWindow:",
|
||||
"findNext:",
|
||||
"findPrevious:",
|
||||
"removeAll:",
|
||||
"remove:",
|
||||
"showAll:",
|
||||
"showDownloadsWindow:",
|
||||
"showFindPanel:"
|
||||
|
|
Binary file not shown.
|
@ -13,10 +13,8 @@
|
|||
static struct gui_download_window *gnustep_download_create(struct download_context *ctx, struct gui_window *parent) {
|
||||
NSLog(@"gnustep_download_create");
|
||||
NSURL *url = [[NSApp delegate] requestDownloadDestination];
|
||||
// TODO: - dataSize is smaller than the actual size in some cases. Why?
|
||||
NSUInteger dataSize = download_context_get_total_length(ctx);
|
||||
DownloadItem *download = [[DownloadManager defaultDownloadManager]
|
||||
createDownloadForDestination: url withSizeInBytes: dataSize];
|
||||
createDownloadForDestination: url withContext: ctx];
|
||||
[[NSApp delegate] showDownloadsWindow: nil];
|
||||
return (struct gui_download_window*)download;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue