зеркало из https://github.com/mozilla/gecko-dev.git
new d/l manager from josh aas (bug 223583)
This commit is contained in:
Родитель
6caa49d6ee
Коммит
6909924f9e
|
@ -761,6 +761,7 @@
|
|||
3FB2BAB10545EA80002B9691,
|
||||
3FB2BAB30545EA80002B9691,
|
||||
3FB2BAB50545EA80002B9691,
|
||||
3FC0FB8105A0D2DC002F47DE,
|
||||
);
|
||||
isa = PBXHeadersBuildPhase;
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -849,6 +850,11 @@
|
|||
3FB2BB030545EBF2002B9691,
|
||||
3F63719C057CE0DE00B62EE2,
|
||||
3FAA54C80587778700BBF630,
|
||||
3FC0FB8805A0D2F4002F47DE,
|
||||
3FC0FB8905A0D2F4002F47DE,
|
||||
3FC0FB8A05A0D2F4002F47DE,
|
||||
3FC0FB8B05A0D2F4002F47DE,
|
||||
3FC0FB8C05A0D2F4002F47DE,
|
||||
);
|
||||
isa = PBXResourcesBuildPhase;
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -930,6 +936,7 @@
|
|||
3FB2BAB20545EA80002B9691,
|
||||
3FB2BAB40545EA80002B9691,
|
||||
3FB2BAB60545EA80002B9691,
|
||||
3FC0FB8205A0D2DC002F47DE,
|
||||
);
|
||||
isa = PBXSourcesBuildPhase;
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -2336,6 +2343,92 @@
|
|||
settings = {
|
||||
};
|
||||
};
|
||||
3FC0FB7F05A0D2DC002F47DE = {
|
||||
fileEncoding = 30;
|
||||
isa = PBXFileReference;
|
||||
name = ProgressView.h;
|
||||
path = src/download/ProgressView.h;
|
||||
refType = 2;
|
||||
};
|
||||
3FC0FB8005A0D2DC002F47DE = {
|
||||
fileEncoding = 30;
|
||||
isa = PBXFileReference;
|
||||
name = ProgressView.mm;
|
||||
path = src/download/ProgressView.mm;
|
||||
refType = 2;
|
||||
};
|
||||
3FC0FB8105A0D2DC002F47DE = {
|
||||
fileRef = 3FC0FB7F05A0D2DC002F47DE;
|
||||
isa = PBXBuildFile;
|
||||
settings = {
|
||||
};
|
||||
};
|
||||
3FC0FB8205A0D2DC002F47DE = {
|
||||
fileRef = 3FC0FB8005A0D2DC002F47DE;
|
||||
isa = PBXBuildFile;
|
||||
settings = {
|
||||
};
|
||||
};
|
||||
3FC0FB8305A0D2F4002F47DE = {
|
||||
isa = PBXFileReference;
|
||||
name = dl_cancel.tif;
|
||||
path = resources/images/toolbar/dl_cancel.tif;
|
||||
refType = 2;
|
||||
};
|
||||
3FC0FB8405A0D2F4002F47DE = {
|
||||
isa = PBXFileReference;
|
||||
name = dl_clearall.tif;
|
||||
path = resources/images/toolbar/dl_clearall.tif;
|
||||
refType = 2;
|
||||
};
|
||||
3FC0FB8505A0D2F4002F47DE = {
|
||||
isa = PBXFileReference;
|
||||
name = dl_open.tif;
|
||||
path = resources/images/toolbar/dl_open.tif;
|
||||
refType = 2;
|
||||
};
|
||||
3FC0FB8605A0D2F4002F47DE = {
|
||||
isa = PBXFileReference;
|
||||
name = dl_remove.tif;
|
||||
path = resources/images/toolbar/dl_remove.tif;
|
||||
refType = 2;
|
||||
};
|
||||
3FC0FB8705A0D2F4002F47DE = {
|
||||
isa = PBXFileReference;
|
||||
name = dl_reveal.tif;
|
||||
path = resources/images/toolbar/dl_reveal.tif;
|
||||
refType = 2;
|
||||
};
|
||||
3FC0FB8805A0D2F4002F47DE = {
|
||||
fileRef = 3FC0FB8305A0D2F4002F47DE;
|
||||
isa = PBXBuildFile;
|
||||
settings = {
|
||||
};
|
||||
};
|
||||
3FC0FB8905A0D2F4002F47DE = {
|
||||
fileRef = 3FC0FB8405A0D2F4002F47DE;
|
||||
isa = PBXBuildFile;
|
||||
settings = {
|
||||
};
|
||||
};
|
||||
3FC0FB8A05A0D2F4002F47DE = {
|
||||
fileRef = 3FC0FB8505A0D2F4002F47DE;
|
||||
isa = PBXBuildFile;
|
||||
settings = {
|
||||
};
|
||||
};
|
||||
3FC0FB8B05A0D2F4002F47DE = {
|
||||
fileRef = 3FC0FB8605A0D2F4002F47DE;
|
||||
isa = PBXBuildFile;
|
||||
settings = {
|
||||
};
|
||||
};
|
||||
3FC0FB8C05A0D2F4002F47DE = {
|
||||
fileRef = 3FC0FB8705A0D2F4002F47DE;
|
||||
isa = PBXBuildFile;
|
||||
settings = {
|
||||
};
|
||||
};
|
||||
//3F0
|
||||
//3F1
|
||||
//3F2
|
||||
|
@ -2966,6 +3059,8 @@
|
|||
F50D9DE302ECC2C601BB4219,
|
||||
F5F415CA03B9223E01A80166,
|
||||
F517395B020CE3740189DA0C,
|
||||
3FC0FB7F05A0D2DC002F47DE,
|
||||
3FC0FB8005A0D2DC002F47DE,
|
||||
);
|
||||
isa = PBXGroup;
|
||||
name = Camino;
|
||||
|
@ -4357,6 +4452,11 @@
|
|||
};
|
||||
F5264FCB020D4C25010001CA = {
|
||||
children = (
|
||||
3FC0FB8305A0D2F4002F47DE,
|
||||
3FC0FB8405A0D2F4002F47DE,
|
||||
3FC0FB8505A0D2F4002F47DE,
|
||||
3FC0FB8605A0D2F4002F47DE,
|
||||
3FC0FB8705A0D2F4002F47DE,
|
||||
F5555C8202022F070164C72B,
|
||||
F5555C8302022F070164C72B,
|
||||
F5555C8402022F070164C72B,
|
||||
|
|
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичные данные
camino/resources/localized/English.lproj/Localizable.strings
Двоичные данные
camino/resources/localized/English.lproj/Localizable.strings
Двоичный файл не отображается.
|
@ -12,11 +12,7 @@
|
|||
{
|
||||
CLASS = ProgressDlgController;
|
||||
LANGUAGE = ObjC;
|
||||
OUTLETS = {
|
||||
mNoDownloadsText = NSTextField;
|
||||
mScrollView = NSScrollView;
|
||||
mStackView = CHStackView;
|
||||
};
|
||||
OUTLETS = {mScrollView = NSScrollView; mStackView = CHStackView; };
|
||||
SUPERCLASS = NSWindowController;
|
||||
},
|
||||
{
|
||||
|
|
|
@ -5,8 +5,12 @@
|
|||
<key>IBDocumentLocation</key>
|
||||
<string>24 42 592 284 0 0 1600 1002 </string>
|
||||
<key>IBFramework Version</key>
|
||||
<string>286.0</string>
|
||||
<string>349.0</string>
|
||||
<key>IBOpenObjects</key>
|
||||
<array>
|
||||
<integer>121</integer>
|
||||
</array>
|
||||
<key>IBSystem Version</key>
|
||||
<string>6G30</string>
|
||||
<string>7D24</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
Двоичные данные
camino/resources/localized/English.lproj/ProgressDialog.nib/objects.nib
сгенерированный
Двоичные данные
camino/resources/localized/English.lproj/ProgressDialog.nib/objects.nib
сгенерированный
Двоичный файл не отображается.
|
@ -1,19 +1,15 @@
|
|||
{
|
||||
IBClasses = (
|
||||
{CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; },
|
||||
{CLASS = ProgressView; LANGUAGE = ObjC; SUPERCLASS = NSView; },
|
||||
{
|
||||
ACTIONS = {close = id; open = id; reveal = id; stop = id; toggleDisclosure = id; };
|
||||
CLASS = ProgressViewController;
|
||||
LANGUAGE = ObjC;
|
||||
OUTLETS = {
|
||||
mCompletedView = NSView;
|
||||
mCompletedViewCompact = NSView;
|
||||
mExpandedCancelButton = NSButton;
|
||||
mExpandedOpenButton = NSButton;
|
||||
mExpandedRevealButton = NSButton;
|
||||
mProgressBar = NSProgressIndicator;
|
||||
mProgressView = NSView;
|
||||
mProgressViewCompact = NSView;
|
||||
};
|
||||
SUPERCLASS = NSObject;
|
||||
}
|
||||
|
|
|
@ -3,25 +3,27 @@
|
|||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IBDocumentLocation</key>
|
||||
<string>75 299 448 284 0 0 1600 1002 </string>
|
||||
<string>101 124 448 284 0 0 1280 832 </string>
|
||||
<key>IBEditorPositions</key>
|
||||
<dict>
|
||||
<key>27</key>
|
||||
<string>573 628 420 80 0 0 1600 1002 </string>
|
||||
<key>5</key>
|
||||
<string>573 564 420 208 0 0 1600 1002 </string>
|
||||
<string>188 648 346 92 0 0 1280 832 </string>
|
||||
<key>71</key>
|
||||
<string>573 564 420 208 0 0 1600 1002 </string>
|
||||
<key>97</key>
|
||||
<string>573 628 420 80 0 0 1600 1002 </string>
|
||||
<string>264 467 346 92 0 0 1280 832 </string>
|
||||
</dict>
|
||||
<key>IBFramework Version</key>
|
||||
<string>286.0</string>
|
||||
<string>349.0</string>
|
||||
<key>IBLockedObjects</key>
|
||||
<array>
|
||||
<integer>5</integer>
|
||||
<integer>71</integer>
|
||||
</array>
|
||||
<key>IBOpenObjects</key>
|
||||
<array>
|
||||
<integer>71</integer>
|
||||
<integer>5</integer>
|
||||
</array>
|
||||
<key>IBSystem Version</key>
|
||||
<string>6G30</string>
|
||||
<string>7D24</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
Двоичные данные
camino/resources/localized/English.lproj/ProgressView.nib/objects.nib
сгенерированный
Двоичные данные
camino/resources/localized/English.lproj/ProgressView.nib/objects.nib
сгенерированный
Двоичный файл не отображается.
|
@ -22,6 +22,7 @@
|
|||
* Contributor(s):
|
||||
* Simon Fraser <sfraser@netscape.com>
|
||||
* Calum Robinson <calumr@mac.com>
|
||||
* Josh Aas <josha@mac.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
|
@ -37,6 +38,7 @@
|
|||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
#import "CHDownloadProgressDisplay.h"
|
||||
|
@ -56,56 +58,54 @@
|
|||
CHDownloadProgressDisplay protocol, which are used to display
|
||||
the progress of a single download. It does so by returning instances of
|
||||
ProgressViewController, which manage an NSView that contains a progress
|
||||
indicator, some text fields for status info and a cancel button.
|
||||
indicator and some text fields for status info.
|
||||
|
||||
After a ProgressViewController is requested, the CHStackView is reloaded,
|
||||
which causes it to ask the ProgressDlgController (it's datasource) to
|
||||
provide it with a list of all the subviews to be diaplyed. It calculates
|
||||
it's new frame, and arranges the subviews in a vertical list.
|
||||
|
||||
The ProgressDlgController now needs to resize its window. It knows when
|
||||
to do this because is watches for changes in the CHStackViews frame (using
|
||||
NSViews built in NSNotification for this).
|
||||
|
||||
|
||||
Expanding/contracting download progress views
|
||||
|
||||
When a disclosure triangle is clicked, the ProgressViewController just swaps
|
||||
the expanded view for a smaller one. It saves the new state as the users
|
||||
preference for "browser.download.compactView". If the option key was held down,
|
||||
a notification is posted (that all ProgressViewControllers listen for) that
|
||||
makes all ProgressViewControllers change their state to the new state of the sender.
|
||||
|
||||
it's new frame, and arranges the subviews in a vertical list.
|
||||
*/
|
||||
|
||||
#import "CHDownloadProgressDisplay.h"
|
||||
#import "ProgressViewController.h"
|
||||
|
||||
//
|
||||
// interface ProgressDlgController
|
||||
//
|
||||
// A window controller managing multiple simultaneous downloads. The user
|
||||
// can cancel, remove, or get information about any of the downloads it
|
||||
// manages. It maintains one |ProgressViewController| object for each download.
|
||||
//
|
||||
|
||||
@interface ProgressDlgController : NSWindowController<CHDownloadDisplayFactory, CHStackViewDataSource>
|
||||
{
|
||||
IBOutlet CHStackView *mStackView;
|
||||
IBOutlet NSScrollView *mScrollView;
|
||||
IBOutlet NSTextField *mNoDownloadsText;
|
||||
|
||||
NSSize mDefaultWindowSize;
|
||||
NSTimer *mDownloadTimer;
|
||||
NSMutableArray *mProgressViewControllers;
|
||||
NSSize mDefaultWindowSize;
|
||||
NSTimer *mDownloadTimer; // used for updating the status, STRONG ref
|
||||
NSMutableArray *mProgressViewControllers; // the downloads we manage, STRONG ref
|
||||
int mNumActiveDownloads;
|
||||
int mSelectionPivotIndex;
|
||||
}
|
||||
|
||||
+ (ProgressDlgController *)sharedDownloadController;
|
||||
+(ProgressDlgController *)sharedDownloadController;
|
||||
|
||||
- (int)numDownloadsInProgress;
|
||||
-(IBAction)cancel:(id)sender;
|
||||
-(IBAction)remove:(id)sender;
|
||||
-(IBAction)reveal:(id)sender;
|
||||
-(IBAction)cleanUpDownloads:(id)sender;
|
||||
-(IBAction)open:(id)sender;
|
||||
|
||||
- (void)autosaveWindowFrame;
|
||||
-(int)numDownloadsInProgress;
|
||||
|
||||
- (void)setupDownloadTimer;
|
||||
- (void)killDownloadTimer;
|
||||
- (void)setDownloadProgress:(NSTimer *)aTimer;
|
||||
-(void)setupDownloadTimer;
|
||||
-(void)killDownloadTimer;
|
||||
-(void)setDownloadProgress:(NSTimer *)aTimer;
|
||||
|
||||
- (void)didStartDownload:(id <CHDownloadProgressDisplay>)progressDisplay;
|
||||
- (void)didEndDownload:(id <CHDownloadProgressDisplay>)progressDisplay;
|
||||
- (void)removeDownload:(id <CHDownloadProgressDisplay>)progressDisplay;
|
||||
|
||||
- (NSApplicationTerminateReply)allowTerminate;
|
||||
-(void)didStartDownload:(id <CHDownloadProgressDisplay>)progressDisplay;
|
||||
-(void)didEndDownload:(id <CHDownloadProgressDisplay>)progressDisplay;
|
||||
-(void)removeDownload:(id <CHDownloadProgressDisplay>)progressDisplay;
|
||||
-(NSApplicationTerminateReply)allowTerminate;
|
||||
|
||||
@end
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
*
|
||||
* Contributor(s):
|
||||
* Calum Robinson <calumr@mac.com>
|
||||
* Josh Aas <josha@mac.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
|
@ -40,189 +41,397 @@
|
|||
|
||||
#import "ProgressDlgController.h"
|
||||
|
||||
#import "ProgressViewController.h"
|
||||
#import "PreferenceManager.h"
|
||||
#import "ProgressView.h"
|
||||
|
||||
static NSString *ProgressWindowFrameSaveName = @"ProgressWindow";
|
||||
|
||||
|
||||
static NSString *ProgressWindowFrameSaveName = @"ProgressWindow";
|
||||
|
||||
@interface ProgressDlgController(PrivateProgressDlgController)
|
||||
|
||||
- (void)resizeWindowToFit;
|
||||
- (void)rebuildViews;
|
||||
- (NSSize)windowSizeForStackSize:(NSSize)stackSize;
|
||||
-(void)rebuildViews;
|
||||
-(NSMutableArray*)getSelectedProgressViewControllers;
|
||||
-(void)deselectAllDLInstances:(NSArray*)instances;
|
||||
-(void)makeDLInstanceVisibleIfItsNotAlready:(ProgressViewController*)controller;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@implementation ProgressDlgController
|
||||
|
||||
static id gSharedProgressController = nil;
|
||||
|
||||
+ (ProgressDlgController *)sharedDownloadController;
|
||||
+(ProgressDlgController *)sharedDownloadController
|
||||
{
|
||||
if (gSharedProgressController == nil)
|
||||
if (gSharedProgressController == nil) {
|
||||
gSharedProgressController = [[ProgressDlgController alloc] init];
|
||||
}
|
||||
|
||||
return gSharedProgressController;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
-(id)init
|
||||
{
|
||||
if ((self == [super initWithWindowNibName:@"ProgressDialog"]))
|
||||
{
|
||||
// Register for notifications when the stack view changes size
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(stackViewResized:)
|
||||
name:StackViewResizedNotificationName
|
||||
object:nil];
|
||||
|
||||
if ((self == [super initWithWindowNibName:@"ProgressDialog"])) {
|
||||
mProgressViewControllers = [[NSMutableArray alloc] init];
|
||||
|
||||
mDefaultWindowSize = [[self window] frame].size;
|
||||
// it would be nice if we could get the frame from the name, and then
|
||||
// mess with it before setting it.
|
||||
[[self window] setFrameUsingName:ProgressWindowFrameSaveName];
|
||||
// set the window to its default height
|
||||
NSRect windowFrame = [[self window] frame];
|
||||
windowFrame.size.height = mDefaultWindowSize.height;
|
||||
[[self window] setFrame:windowFrame display:NO];
|
||||
|
||||
// We provide the views for the stack view, from mProgressViewControllers
|
||||
[mStackView setDataSource:self];
|
||||
|
||||
[mScrollView setDrawsBackground:NO];
|
||||
[mNoDownloadsText retain]; // so we can remove it from its superview
|
||||
mSelectionPivotIndex = -1;
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(DLInstanceSelected:)
|
||||
name:@"DownloadInstanceSelected" object:nil];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
-(void)awakeFromNib
|
||||
{
|
||||
if (self == gSharedProgressController)
|
||||
gSharedProgressController = nil;
|
||||
NSToolbar *toolbar = [[NSToolbar alloc] initWithIdentifier:@"dlmanager"];
|
||||
[toolbar setDelegate:self];
|
||||
[toolbar setAllowsUserCustomization:YES];
|
||||
[toolbar setAutosavesConfiguration:YES];
|
||||
[[self window] setToolbar:toolbar];
|
||||
}
|
||||
|
||||
-(void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
if (self == gSharedProgressController) {
|
||||
gSharedProgressController = nil;
|
||||
}
|
||||
[mProgressViewControllers release];
|
||||
[mNoDownloadsText release];
|
||||
[self killDownloadTimer];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)didStartDownload:(id <CHDownloadProgressDisplay>)progressDisplay
|
||||
// cancel all selected instances
|
||||
-(IBAction)cancel:(id)sender
|
||||
{
|
||||
NSMutableArray* selected = [self getSelectedProgressViewControllers];
|
||||
unsigned count = [selected count];
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
[[selected objectAtIndex:i] cancel:self];
|
||||
}
|
||||
}
|
||||
|
||||
// reveal all selected instances in the Finder
|
||||
-(IBAction)reveal:(id)sender
|
||||
{
|
||||
NSMutableArray* selected = [self getSelectedProgressViewControllers];
|
||||
unsigned count = [selected count];
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
[[selected objectAtIndex:i] reveal:self];
|
||||
}
|
||||
}
|
||||
|
||||
// open all selected instances
|
||||
-(IBAction)open:(id)sender
|
||||
{
|
||||
NSMutableArray* selected = [self getSelectedProgressViewControllers];
|
||||
unsigned count = [selected count];
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
[[selected objectAtIndex:i] open:self];
|
||||
}
|
||||
}
|
||||
|
||||
// remove all selected instances, don't remove anything that is active as a guard against bad things
|
||||
-(IBAction)remove:(id)sender
|
||||
{
|
||||
// take care of selecting a download instance to replace the selection being removed
|
||||
NSMutableArray* selected = [self getSelectedProgressViewControllers];
|
||||
unsigned selectedCount = [selected count];
|
||||
unsigned indexOfLastSelection = [mProgressViewControllers indexOfObject:[selected objectAtIndex:(((int)selectedCount) - 1)]];
|
||||
// if dl instance after last selection exists, select it or look for something else to select
|
||||
if ((indexOfLastSelection + 1) < [mProgressViewControllers count]) {
|
||||
[[((ProgressViewController*)[mProgressViewControllers objectAtIndex:(indexOfLastSelection + 1)]) view] setSelected:YES];
|
||||
}
|
||||
else { // find the first unselected DL instance before the last one marked for removal and select it
|
||||
// use an int in the loop, not unsigned because we might make it negative
|
||||
for (int i = ([mProgressViewControllers count] - 1); i >= 0; i--) {
|
||||
if (![((ProgressViewController*)[mProgressViewControllers objectAtIndex:i]) isSelected]) {
|
||||
[[((ProgressViewController*)[mProgressViewControllers objectAtIndex:i]) view] setSelected:YES];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
mSelectionPivotIndex = -1; // nothing is selected any more so nothing to pivot on
|
||||
|
||||
// now remove stuff
|
||||
for (unsigned i = 0; i < selectedCount; i++) {
|
||||
if (![[selected objectAtIndex:i] isActive]) {
|
||||
[self removeDownload:[selected objectAtIndex:i]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove all inactive instances
|
||||
-(IBAction)cleanUpDownloads:(id)sender
|
||||
{
|
||||
for (int i = 0; i < [mProgressViewControllers count]; i++) {
|
||||
if ((![[mProgressViewControllers objectAtIndex:i] isActive]) || [[mProgressViewControllers objectAtIndex:i] isCanceled]) {
|
||||
[self removeDownload:[mProgressViewControllers objectAtIndex:i]]; // remove the download
|
||||
i--; // leave index at the same position because the dl there got removed
|
||||
}
|
||||
}
|
||||
mSelectionPivotIndex = -1;
|
||||
}
|
||||
|
||||
// calculate what buttons should be enabled/disabled because the user changed the selection state
|
||||
-(void)DLInstanceSelected:(NSNotification*)notification
|
||||
{
|
||||
// make sure the notification object is the kind we want
|
||||
ProgressView* sender = ((ProgressView*)[notification object]);
|
||||
if (![sender isKindOfClass:[ProgressView class]])
|
||||
return;
|
||||
|
||||
NSMutableArray* selectedArray = [self getSelectedProgressViewControllers];
|
||||
int lastMod = [sender lastModifier];
|
||||
|
||||
// check for key modifiers and select more instances if appropriate
|
||||
// if its shift key, extend the selection one way or another
|
||||
// if its command key, just let the selection happen wherever it is (don't mess with it here)
|
||||
// if its not either clear all selections except the one that just happened
|
||||
if (lastMod == kNoKey) {
|
||||
// deselect everything
|
||||
[self deselectAllDLInstances:selectedArray];
|
||||
[sender setSelected:YES];
|
||||
mSelectionPivotIndex = [mProgressViewControllers indexOfObject:[sender getController]];
|
||||
}
|
||||
else if (lastMod == kCommandKey) {
|
||||
if (![sender isSelected]) {
|
||||
// if this was at the pivot index set the pivot index to -1
|
||||
if ([mProgressViewControllers indexOfObject:[sender getController]] == mSelectionPivotIndex) {
|
||||
mSelectionPivotIndex = -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ([selectedArray count] == 1) {
|
||||
mSelectionPivotIndex = [mProgressViewControllers indexOfObject:[sender getController]];
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (lastMod == kShiftKey) {
|
||||
if (mSelectionPivotIndex == -1) {
|
||||
mSelectionPivotIndex = [mProgressViewControllers indexOfObject:[sender getController]];
|
||||
}
|
||||
else {
|
||||
if ([selectedArray count] == 1) {
|
||||
mSelectionPivotIndex = [mProgressViewControllers indexOfObject:[sender getController]];
|
||||
}
|
||||
else {
|
||||
int senderLocation = [mProgressViewControllers indexOfObject:[sender getController]];
|
||||
// deselect everything
|
||||
[self deselectAllDLInstances:selectedArray];
|
||||
if (senderLocation <= mSelectionPivotIndex) {
|
||||
for (int i = senderLocation; i <= mSelectionPivotIndex; i++) {
|
||||
[[((ProgressViewController*)[mProgressViewControllers objectAtIndex:i]) view] setSelected:YES];
|
||||
}
|
||||
}
|
||||
else if (senderLocation > mSelectionPivotIndex) {
|
||||
for (int i = mSelectionPivotIndex; i <= senderLocation; i++) {
|
||||
[[((ProgressViewController*)[mProgressViewControllers objectAtIndex:i]) view] setSelected:YES];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-(void)keyDown:(NSEvent *)theEvent
|
||||
{
|
||||
// we don't care about anything if no downloads exist
|
||||
if ([mProgressViewControllers count] > 0) {
|
||||
int key = [[theEvent characters] characterAtIndex:0];
|
||||
int instanceToSelect = -1;
|
||||
unsigned int mods = [theEvent modifierFlags];
|
||||
if (key == NSUpArrowFunctionKey) { // was it the up arrow key that got pressed?
|
||||
// find the first selected item
|
||||
int i; // we use this outside the loop so declare it here
|
||||
for (i = 0; i < [mProgressViewControllers count]; i++) {
|
||||
if ([[mProgressViewControllers objectAtIndex:i] isSelected]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// deselect everything if the shift key isn't a modifier
|
||||
if (!(mods & NSShiftKeyMask)) {
|
||||
[self deselectAllDLInstances:[self getSelectedProgressViewControllers]];
|
||||
}
|
||||
if (i == [mProgressViewControllers count]) { // if nothing was selected select the first item
|
||||
instanceToSelect = 0;
|
||||
}
|
||||
else if (i == 0) { // if selection was already at the top leave it there
|
||||
instanceToSelect = 0;
|
||||
}
|
||||
else { // select the next highest instance
|
||||
instanceToSelect = i - 1;
|
||||
}
|
||||
// select and make sure its visible
|
||||
if (instanceToSelect != -1) {
|
||||
[[((ProgressViewController*)[mProgressViewControllers objectAtIndex:instanceToSelect]) view] setSelected:YES];
|
||||
[self makeDLInstanceVisibleIfItsNotAlready:((ProgressViewController*)[mProgressViewControllers objectAtIndex:instanceToSelect])];
|
||||
if (!(mods & NSShiftKeyMask)) {
|
||||
mSelectionPivotIndex = instanceToSelect;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (key == NSDownArrowFunctionKey) { // was it the down arrow key that got pressed?
|
||||
// find the last selected item
|
||||
int instanceToSelect = -1;
|
||||
int i; // we use this outside the coming loop so declare it here
|
||||
for (i = [mProgressViewControllers count] - 1; i >= 0 ; i--) {
|
||||
if ([[mProgressViewControllers objectAtIndex:i] isSelected]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// deselect everything if the shift key isn't a modifier
|
||||
if (!(mods & NSShiftKeyMask)) {
|
||||
[self deselectAllDLInstances:[self getSelectedProgressViewControllers]];
|
||||
}
|
||||
if (i < 0) { // if nothing was selected select the first item
|
||||
instanceToSelect = ([mProgressViewControllers count] - 1);
|
||||
}
|
||||
else if (i == ([mProgressViewControllers count] - 1)) { // if selection was already at the bottom leave it there
|
||||
instanceToSelect = ([mProgressViewControllers count] - 1);
|
||||
}
|
||||
else { // select the next lowest instance
|
||||
instanceToSelect = i + 1;
|
||||
}
|
||||
if (instanceToSelect != -1) {
|
||||
[[((ProgressViewController*)[mProgressViewControllers objectAtIndex:instanceToSelect]) view] setSelected:YES];
|
||||
[self makeDLInstanceVisibleIfItsNotAlready:((ProgressViewController*)[mProgressViewControllers objectAtIndex:instanceToSelect])];
|
||||
if (!(mods & NSShiftKeyMask)) {
|
||||
mSelectionPivotIndex = instanceToSelect;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (key == (int)'\177') { // delete key - remove all selected items unless an active one is selected
|
||||
NSMutableArray *selected = [self getSelectedProgressViewControllers];
|
||||
BOOL activeFound = NO;
|
||||
for (unsigned i = 0; i < [selected count]; i++) {
|
||||
if ([[selected objectAtIndex:i] isActive]) {
|
||||
activeFound = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (activeFound) {
|
||||
NSBeep();
|
||||
}
|
||||
else {
|
||||
[self remove:self];
|
||||
}
|
||||
}
|
||||
else if (key == NSPageUpFunctionKey) {
|
||||
if ([mProgressViewControllers count] > 0) {
|
||||
// make the first instance completely visible
|
||||
[self makeDLInstanceVisibleIfItsNotAlready:((ProgressViewController*)[mProgressViewControllers objectAtIndex:0])];
|
||||
}
|
||||
}
|
||||
else if (key == NSPageDownFunctionKey) {
|
||||
if ([mProgressViewControllers count] > 0) {
|
||||
// make the last instance completely visible
|
||||
[self makeDLInstanceVisibleIfItsNotAlready:((ProgressViewController*)[mProgressViewControllers lastObject])];
|
||||
}
|
||||
}
|
||||
else { // ignore any other keys
|
||||
NSBeep();
|
||||
}
|
||||
}
|
||||
else {
|
||||
NSBeep();
|
||||
}
|
||||
}
|
||||
|
||||
-(void)deselectAllDLInstances:(NSArray*)instances
|
||||
{
|
||||
unsigned count = [instances count];
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
[[((ProgressViewController*)[instances objectAtIndex:i]) view] setSelected:NO];
|
||||
}
|
||||
}
|
||||
|
||||
// return a mutable array with instance in order top-down
|
||||
-(NSMutableArray*)getSelectedProgressViewControllers
|
||||
{
|
||||
NSMutableArray *selectedArray = [[NSMutableArray alloc] init];
|
||||
unsigned selectedCount = [mProgressViewControllers count];
|
||||
for (unsigned i = 0; i < selectedCount; i++) {
|
||||
if ([[mProgressViewControllers objectAtIndex:i] isSelected]) {
|
||||
// insert at zero so they're in order to-down
|
||||
[selectedArray addObject:[mProgressViewControllers objectAtIndex:i]];
|
||||
}
|
||||
}
|
||||
[selectedArray autorelease];
|
||||
return selectedArray;
|
||||
}
|
||||
|
||||
-(void)makeDLInstanceVisibleIfItsNotAlready:(ProgressViewController*)controller
|
||||
{
|
||||
NSRect instanceFrame = [[controller view] frame];
|
||||
NSRect visibleRect = [[mScrollView contentView] documentVisibleRect];
|
||||
// for some reason there is a 1 pixel discrepancy we need to get rid of here
|
||||
instanceFrame.size.width -= 1;
|
||||
// NSLog([NSString stringWithFormat:@"Instance Frame: %@", NSStringFromRect(instanceFrame)]);
|
||||
// NSLog([NSString stringWithFormat:@"Visible Rect: %@", NSStringFromRect(visibleRect)]);
|
||||
if (!NSContainsRect(visibleRect, instanceFrame)) { // if instance isn't completely visible
|
||||
if (instanceFrame.origin.y < visibleRect.origin.y) { // if the dl instance is at least partly above visible rect
|
||||
// just go to the instance's frame origin point
|
||||
[[mScrollView contentView] scrollToPoint:instanceFrame.origin];
|
||||
}
|
||||
else { // if the dl instance is at least partly below visible rect
|
||||
// take instance's frame origin y, subtract content view height,
|
||||
// add instance view height, no parenthesizing
|
||||
NSPoint adjustedPoint = NSMakePoint(0,(instanceFrame.origin.y - [[mScrollView contentView] frame].size.height) + instanceFrame.size.height);
|
||||
[[mScrollView contentView] scrollToPoint:adjustedPoint];
|
||||
}
|
||||
[mScrollView reflectScrolledClipView:[mScrollView contentView]];
|
||||
}
|
||||
}
|
||||
|
||||
-(void)didStartDownload:(id <CHDownloadProgressDisplay>)progressDisplay
|
||||
{
|
||||
if (![[self window] isVisible]) {
|
||||
[self showWindow:nil]; // make sure the window is visible
|
||||
[self showWindow:nil]; // make sure the window is visible
|
||||
}
|
||||
|
||||
|
||||
[self rebuildViews];
|
||||
[self setupDownloadTimer];
|
||||
|
||||
// make sure new download is visible
|
||||
[self makeDLInstanceVisibleIfItsNotAlready:progressDisplay];
|
||||
}
|
||||
|
||||
- (void)didEndDownload:(id <CHDownloadProgressDisplay>)progressDisplay
|
||||
-(void)didEndDownload:(id <CHDownloadProgressDisplay>)progressDisplay
|
||||
{
|
||||
[self rebuildViews]; // to swap in the completed view
|
||||
[self rebuildViews]; // to swap in the completed view
|
||||
}
|
||||
|
||||
- (void)removeDownload:(id <CHDownloadProgressDisplay>)progressDisplay
|
||||
-(void)removeDownload:(id <CHDownloadProgressDisplay>)progressDisplay
|
||||
{
|
||||
[mProgressViewControllers removeObject:progressDisplay];
|
||||
|
||||
if ([mProgressViewControllers count] == 0)
|
||||
{
|
||||
if ([mProgressViewControllers count] == 0) {
|
||||
// Stop doing stuff if there aren't any downloads going on
|
||||
[self killDownloadTimer];
|
||||
}
|
||||
|
||||
[self rebuildViews];
|
||||
}
|
||||
|
||||
- (void)stackViewResized:(NSNotification *)notification
|
||||
{
|
||||
NSDictionary* userInfo = [notification userInfo];
|
||||
NSSize oldStackSize = [[userInfo objectForKey:@"oldsize"] sizeValue];
|
||||
|
||||
// this code is used to auto-resize the downloads window when
|
||||
// its contents change size, if the window is in its standard, "zoomed"
|
||||
// state. This allows the user to choose between auto-resizing behavior,
|
||||
// by leaving the window alone, or their own size, by resizing it.
|
||||
|
||||
// get the size the window would have been if it had been in the
|
||||
// standard state, given the old size of the contents
|
||||
NSSize oldZoomedWindowSize = [self windowSizeForStackSize:oldStackSize];
|
||||
NSSize curWindowSize = [[self window] frame].size;
|
||||
|
||||
// only resize if the window matches the stack size
|
||||
if (CHCloseSizes(oldZoomedWindowSize, curWindowSize, 4.0))
|
||||
[self resizeWindowToFit];
|
||||
}
|
||||
|
||||
// given the dimensions of our stack view, return the dimensions of the window,
|
||||
// assuming the window is zoomed to show as much of the contents as possible.
|
||||
- (NSSize)windowSizeForStackSize:(NSSize)stackSize
|
||||
{
|
||||
NSSize actualScrollFrame = [mScrollView frame].size;
|
||||
NSSize scrollFrameSize = [NSScrollView frameSizeForContentSize:stackSize
|
||||
hasHorizontalScroller:NO hasVerticalScroller:YES borderType:NSNoBorder];
|
||||
|
||||
// frameSizeForContentSize seems to return a width 1 pixel too narrow
|
||||
scrollFrameSize.width += 1;
|
||||
|
||||
NSRect contentRect = [[[self window] contentView] frame];
|
||||
contentRect.size.width += scrollFrameSize.width - actualScrollFrame.width;
|
||||
contentRect.size.height += scrollFrameSize.height - actualScrollFrame.height;
|
||||
contentRect.origin = [[self window] convertBaseToScreen:contentRect.origin]; // convert to screen
|
||||
|
||||
NSRect advisoryWindowFrame = [NSWindow frameRectForContentRect:contentRect styleMask:[[self window] styleMask]];
|
||||
NSRect constrainedRect = [[self window] constrainFrameRect:advisoryWindowFrame toScreen:[[self window] screen]];
|
||||
return constrainedRect.size;
|
||||
}
|
||||
|
||||
- (void)resizeWindowToFit
|
||||
{
|
||||
if ([mProgressViewControllers count] > 0)
|
||||
{
|
||||
NSSize scrollFrameSize = [NSScrollView frameSizeForContentSize:[mStackView bounds].size
|
||||
hasHorizontalScroller:NO hasVerticalScroller:YES borderType:NSNoBorder];
|
||||
NSSize curScrollFrameSize = [mScrollView frame].size;
|
||||
|
||||
NSRect windowFrame = [[self window] frame];
|
||||
|
||||
float frameDelta = (scrollFrameSize.height - curScrollFrameSize.height);
|
||||
windowFrame.size.height += frameDelta;
|
||||
windowFrame.origin.y -= frameDelta; // maintain top
|
||||
|
||||
[[self window] setFrame:windowFrame display:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)rebuildViews
|
||||
-(void)rebuildViews
|
||||
{
|
||||
[mStackView reloadSubviews];
|
||||
|
||||
if ([mProgressViewControllers count] == 0)
|
||||
{
|
||||
[[[self window] contentView] addSubview:mNoDownloadsText];
|
||||
}
|
||||
else
|
||||
{
|
||||
[mNoDownloadsText removeFromSuperview];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
- (int)numDownloadsInProgress
|
||||
-(int)numDownloadsInProgress
|
||||
{
|
||||
unsigned int numViews = [mProgressViewControllers count];
|
||||
int numActive = 0;
|
||||
unsigned numViews = [mProgressViewControllers count];
|
||||
int numActive = 0;
|
||||
|
||||
for (unsigned int i = 0; i < numViews; i++)
|
||||
{
|
||||
if ([[mProgressViewControllers objectAtIndex:i] isActive])
|
||||
for (unsigned int i = 0; i < numViews; i++) {
|
||||
if ([[mProgressViewControllers objectAtIndex:i] isActive]) {
|
||||
++numActive;
|
||||
}
|
||||
}
|
||||
return numActive;
|
||||
}
|
||||
|
@ -232,15 +441,14 @@ static id gSharedProgressController = nil;
|
|||
[[self window] saveFrameUsingName:ProgressWindowFrameSaveName];
|
||||
}
|
||||
|
||||
- (void)windowWillClose:(NSNotification *)notification
|
||||
-(void)windowWillClose:(NSNotification *)notification
|
||||
{
|
||||
[self autosaveWindowFrame];
|
||||
}
|
||||
|
||||
- (void)killDownloadTimer
|
||||
-(void)killDownloadTimer
|
||||
{
|
||||
if (mDownloadTimer)
|
||||
{
|
||||
if (mDownloadTimer) {
|
||||
[mDownloadTimer invalidate];
|
||||
[mDownloadTimer release];
|
||||
mDownloadTimer = nil;
|
||||
|
@ -250,25 +458,22 @@ static id gSharedProgressController = nil;
|
|||
- (void)setupDownloadTimer
|
||||
{
|
||||
[self killDownloadTimer];
|
||||
|
||||
mDownloadTimer = [[NSTimer scheduledTimerWithTimeInterval:1.0
|
||||
target:self
|
||||
selector:@selector(setDownloadProgress:)
|
||||
userInfo:nil
|
||||
repeats:YES] retain];
|
||||
target:self
|
||||
selector:@selector(setDownloadProgress:)
|
||||
userInfo:nil
|
||||
repeats:YES] retain];
|
||||
}
|
||||
|
||||
// Called by our timer to refresh all the download stats
|
||||
- (void)setDownloadProgress:(NSTimer *)aTimer
|
||||
{
|
||||
[mProgressViewControllers makeObjectsPerformSelector:@selector(refreshDownloadInfo)];
|
||||
// if the window is minimized, we want to update the dock image here. But how?
|
||||
}
|
||||
|
||||
- (NSApplicationTerminateReply)allowTerminate
|
||||
-(NSApplicationTerminateReply)allowTerminate
|
||||
{
|
||||
if ([self numDownloadsInProgress] > 0)
|
||||
{
|
||||
if ([self numDownloadsInProgress] > 0) {
|
||||
// make sure the window is visible
|
||||
[self showWindow:self];
|
||||
|
||||
|
@ -276,7 +481,7 @@ static id gSharedProgressController = nil;
|
|||
NSString *message = NSLocalizedString(@"QuitWithDownloadsExpl", @"");
|
||||
NSString *okButton = NSLocalizedString(@"QuitWithdownloadsButtonDefault",@"Cancel");
|
||||
NSString *altButton = NSLocalizedString(@"QuitWithdownloadsButtonAlt",@"Quit");
|
||||
|
||||
|
||||
// while the panel is up, download dialogs won't update (no timers firing) but
|
||||
// downloads continue (PLEvents being processed)
|
||||
id panel = NSGetAlertPanel(alert, message, okButton, altButton, nil, message);
|
||||
|
@ -293,30 +498,166 @@ static id gSharedProgressController = nil;
|
|||
|
||||
return (sheetResult == NSAlertDefaultReturn) ? NSTerminateCancel : NSTerminateNow;
|
||||
}
|
||||
|
||||
|
||||
return NSTerminateNow;
|
||||
}
|
||||
|
||||
- (void)sheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
|
||||
-(void)sheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
|
||||
{
|
||||
[NSApp stopModalWithCode:returnCode];
|
||||
}
|
||||
|
||||
-(BOOL)shouldAllowCancelAction
|
||||
{
|
||||
NSMutableArray* selectedArray = [self getSelectedProgressViewControllers];
|
||||
unsigned selectedCount = [selectedArray count];
|
||||
// if no selections are inactive or canceled then allow cancel
|
||||
for (unsigned i = 0; i < selectedCount; i++) {
|
||||
if ((![[selectedArray objectAtIndex:i] isActive]) || [[selectedArray objectAtIndex:i] isCanceled]) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
-(BOOL)shouldAllowRemoveAction
|
||||
{
|
||||
NSMutableArray* selectedArray = [self getSelectedProgressViewControllers];
|
||||
unsigned selectedCount = [selectedArray count];
|
||||
// if no selections are active then allow remove
|
||||
for (unsigned i = 0; i < selectedCount; i++) {
|
||||
if ([[selectedArray objectAtIndex:i] isActive]) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
-(BOOL)shouldAllowOpenAction
|
||||
{
|
||||
NSMutableArray* selectedArray = [self getSelectedProgressViewControllers];
|
||||
unsigned selectedCount = [selectedArray count];
|
||||
// if no selections are are active or canceled then allow open
|
||||
for (unsigned i = 0; i < selectedCount; i++) {
|
||||
if ([[selectedArray objectAtIndex:i] isActive] || [[selectedArray objectAtIndex:i] isCanceled]) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
-(BOOL)validateMenuItem:(id <NSMenuItem>)menuItem {
|
||||
SEL action = [menuItem action];
|
||||
if (action == @selector(cancel:)) {
|
||||
return [self shouldAllowCancelAction];
|
||||
}
|
||||
else if (action == @selector(remove:)) {
|
||||
return [self shouldAllowRemoveAction];
|
||||
}
|
||||
else if (action == @selector(open:)) {
|
||||
return [self shouldAllowOpenAction];
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
-(BOOL)validateToolbarItem:(NSToolbarItem *)theItem
|
||||
{
|
||||
SEL action = [theItem action];
|
||||
|
||||
// validate items not dependent on the current selection
|
||||
if (action == @selector(cleanUpDownloads:)) {
|
||||
unsigned pcControllersCount = [mProgressViewControllers count];
|
||||
for (unsigned i = 0; i < pcControllersCount; i++) {
|
||||
if ((![[mProgressViewControllers objectAtIndex:i] isActive]) ||
|
||||
[[mProgressViewControllers objectAtIndex:i] isCanceled]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
// validate items that depend on current selection
|
||||
if ([[self getSelectedProgressViewControllers] count] == 0) {
|
||||
return NO;
|
||||
}
|
||||
else if (action == @selector(remove:)) {
|
||||
return [self shouldAllowRemoveAction];
|
||||
}
|
||||
else if (action == @selector(open:)) {
|
||||
return [self shouldAllowOpenAction];
|
||||
}
|
||||
else if (action == @selector(cancel:)) {
|
||||
return [self shouldAllowCancelAction];
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
-(NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag
|
||||
{
|
||||
NSToolbarItem *theItem = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
|
||||
[theItem setTarget:self];
|
||||
[theItem setEnabled:NO];
|
||||
if ([itemIdentifier isEqualToString:@"removebutton"]) {
|
||||
[theItem setToolTip:NSLocalizedString(@"dlRemoveButtonTooltip", @"Remove selected download(s)")];
|
||||
[theItem setLabel:NSLocalizedString(@"dlRemoveButtonLabel", @"Remove")];
|
||||
[theItem setAction:@selector(remove:)];
|
||||
[theItem setImage:[NSImage imageNamed:@"dl_remove.tif"]];
|
||||
}
|
||||
else if ([itemIdentifier isEqualToString:@"cancelbutton"]) {
|
||||
[theItem setToolTip:NSLocalizedString(@"dlCancelButtonTooltip", @"Cancel selected download(s)")];
|
||||
[theItem setLabel:NSLocalizedString(@"dlCancelButtonLabel", @"Cancel")];
|
||||
[theItem setAction:@selector(cancel:)];
|
||||
[theItem setImage:[NSImage imageNamed:@"dl_cancel.tif"]];
|
||||
}
|
||||
else if ([itemIdentifier isEqualToString:@"revealbutton"]) {
|
||||
[theItem setToolTip:NSLocalizedString(@"dlRevealButtonTooltip", @"Show selected download(s) in Finder")];
|
||||
[theItem setLabel:NSLocalizedString(@"dlRevealButtonLabel", @"Show")];
|
||||
[theItem setAction:@selector(reveal:)];
|
||||
[theItem setImage:[NSImage imageNamed:@"dl_reveal.tif"]];
|
||||
}
|
||||
else if ([itemIdentifier isEqualToString:@"openbutton"]) {
|
||||
[theItem setToolTip:NSLocalizedString(@"dlOpenButtonTooltip", @"Open saved file(s)")];
|
||||
[theItem setLabel:NSLocalizedString(@"dlOpenButtonLabel", @"Open")];
|
||||
[theItem setAction:@selector(open:)];
|
||||
[theItem setImage:[NSImage imageNamed:@"dl_open.tif"]];
|
||||
}
|
||||
else if ([itemIdentifier isEqualToString:@"cleanupbutton"]) {
|
||||
[theItem setToolTip:NSLocalizedString(@"dlCleanUpButtonTooltip", @"Remove all inactive download(s)")];
|
||||
[theItem setLabel:NSLocalizedString(@"dlCleanUpButtonLabel", @"Clean Up")];
|
||||
[theItem setAction:@selector(cleanUpDownloads:)];
|
||||
[theItem setImage:[NSImage imageNamed:@"dl_clearall.tif"]];
|
||||
}
|
||||
else {
|
||||
return nil;
|
||||
}
|
||||
return theItem;
|
||||
}
|
||||
|
||||
-(NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar
|
||||
{
|
||||
return [NSArray arrayWithObjects:@"removebutton", @"cleanupbutton", @"cancelbutton", @"openbutton", @"revealbutton", NSToolbarFlexibleSpaceItemIdentifier, nil];
|
||||
}
|
||||
|
||||
-(NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar *)toolbar
|
||||
{
|
||||
return [NSArray arrayWithObjects:@"cleanupbutton", @"removebutton", @"cancelbutton", @"openbutton", NSToolbarFlexibleSpaceItemIdentifier, @"revealbutton", nil];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
// implement to zoom to a size that just fits the contents
|
||||
- (NSRect)windowWillUseStandardFrame:(NSWindow *)sender defaultFrame:(NSRect)defaultFrame
|
||||
-(NSRect)windowWillUseStandardFrame:(NSWindow *)sender defaultFrame:(NSRect)defaultFrame
|
||||
{
|
||||
NSSize scrollFrameSize = [NSScrollView frameSizeForContentSize:[mStackView bounds].size
|
||||
hasHorizontalScroller:NO hasVerticalScroller:YES borderType:NSNoBorder];
|
||||
hasHorizontalScroller:NO hasVerticalScroller:YES borderType:NSNoBorder];
|
||||
|
||||
NSSize curScrollFrameSize = [mScrollView frame].size;
|
||||
float frameDelta = (scrollFrameSize.height - curScrollFrameSize.height);
|
||||
|
||||
|
||||
NSRect windowFrame = [[self window] frame];
|
||||
windowFrame.size.height += frameDelta;
|
||||
windowFrame.origin.y -= frameDelta; // maintain top
|
||||
|
||||
|
||||
windowFrame.size.width = mDefaultWindowSize.width;
|
||||
// cocoa will ensure that the window fits onscreen for us
|
||||
return windowFrame;
|
||||
|
@ -325,31 +666,30 @@ static id gSharedProgressController = nil;
|
|||
#pragma mark -
|
||||
|
||||
/*
|
||||
CHStackView datasource methods
|
||||
*/
|
||||
CHStackView datasource methods
|
||||
*/
|
||||
|
||||
- (int)subviewsForStackView:(CHStackView *)stackView
|
||||
-(int)subviewsForStackView:(CHStackView *)stackView
|
||||
{
|
||||
return [mProgressViewControllers count];
|
||||
}
|
||||
|
||||
- (NSView *)viewForStackView:(CHStackView *)aResizingView atIndex:(int)index
|
||||
-(NSView *)viewForStackView:(CHStackView *)aResizingView atIndex:(int)index
|
||||
{
|
||||
return [[mProgressViewControllers objectAtIndex:index] view];
|
||||
return [((ProgressViewController*)[mProgressViewControllers objectAtIndex:index]) view];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
/*
|
||||
Just create a progress view, but don't display it (otherwise the URL fields etc.
|
||||
are just blank)
|
||||
*/
|
||||
- (id <CHDownloadProgressDisplay>)createProgressDisplay
|
||||
Just create a progress view, but don't display it (otherwise the URL fields etc.
|
||||
are just blank)
|
||||
*/
|
||||
-(id <CHDownloadProgressDisplay>)createProgressDisplay
|
||||
{
|
||||
ProgressViewController *newController = [[ProgressViewController alloc] init];
|
||||
ProgressViewController *newController = [[[ProgressViewController alloc] init] autorelease];
|
||||
[newController setProgressWindowController:self];
|
||||
//[mProgressViewControllers addObject:newController];
|
||||
[mProgressViewControllers insertObject:newController atIndex:0]; // new downoads at the top
|
||||
[mProgressViewControllers addObject:newController];
|
||||
|
||||
return newController;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.1 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Josh Aas.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2003
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Josh Aas <josha@mac.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the NPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the NPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import "ProgressViewController.h"
|
||||
|
||||
//
|
||||
// interface ProgressView
|
||||
//
|
||||
// A NSView representing the state of a download in the download manager. There
|
||||
// will be two of these per download: one for while it's downloading, the other
|
||||
// for after it complete.
|
||||
//
|
||||
|
||||
@interface ProgressView : NSView
|
||||
{
|
||||
@private
|
||||
BOOL mIsSelected;
|
||||
int mLastModifier;
|
||||
ProgressViewController* mProgressController; // WEAK reference
|
||||
}
|
||||
|
||||
// get/set whether the view is selected in the download list
|
||||
-(BOOL)isSelected;
|
||||
-(void)setSelected:(BOOL)flag;
|
||||
|
||||
// returns the most recent modifier key used during the last
|
||||
// click on this view
|
||||
-(int)lastModifier;
|
||||
|
||||
// get/set our owning controller, to which we maintain a weak link
|
||||
-(void)setController:(ProgressViewController*)controller;
|
||||
-(ProgressViewController*)getController;
|
||||
|
||||
@end
|
|
@ -0,0 +1,135 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.1 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Josh Aas.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2003
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Josh Aas <josha@mac.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the NPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the NPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
|
||||
#import "ProgressView.h"
|
||||
|
||||
@implementation ProgressView
|
||||
|
||||
- (id)initWithFrame:(NSRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
mLastModifier = kNoKey;
|
||||
mIsSelected = NO;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void)drawRect:(NSRect)rect
|
||||
{
|
||||
if (mIsSelected) {
|
||||
[[NSColor selectedTextBackgroundColor] set];
|
||||
}
|
||||
else {
|
||||
[[NSColor whiteColor] set];
|
||||
}
|
||||
[NSBezierPath fillRect:[self bounds]];
|
||||
}
|
||||
|
||||
-(void)mouseDown:(NSEvent*)theEvent
|
||||
{
|
||||
unsigned int mods = [theEvent modifierFlags];
|
||||
mLastModifier = kNoKey;
|
||||
BOOL shouldSelect = YES;
|
||||
// set mLastModifier to any relevant modifier key
|
||||
if (!((mods & NSShiftKeyMask) && (mods & NSCommandKeyMask))) {
|
||||
if (mods & NSShiftKeyMask) {
|
||||
mLastModifier = kShiftKey;
|
||||
}
|
||||
else if (mods & NSCommandKeyMask) {
|
||||
if (mIsSelected) {
|
||||
shouldSelect = NO;
|
||||
}
|
||||
mLastModifier = kCommandKey;
|
||||
}
|
||||
}
|
||||
[self setSelected:shouldSelect];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"DownloadInstanceSelected" object:self];
|
||||
}
|
||||
|
||||
-(BOOL)isSelected
|
||||
{
|
||||
return mIsSelected;
|
||||
}
|
||||
|
||||
-(void)setSelected:(BOOL)flag
|
||||
{
|
||||
mIsSelected = flag;
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
-(int)lastModifier
|
||||
{
|
||||
return mLastModifier;
|
||||
}
|
||||
|
||||
-(void)setController:(ProgressViewController*)controller
|
||||
{
|
||||
// Don't retain since this view will only exist if its controller does
|
||||
mProgressController = controller;
|
||||
}
|
||||
|
||||
-(ProgressViewController*)getController
|
||||
{
|
||||
return mProgressController;
|
||||
}
|
||||
|
||||
-(NSMenu*)menuForEvent:(NSEvent*)theEvent
|
||||
{
|
||||
// if the item is unselected, select it and deselect everything else before displaying the contextual menu
|
||||
if (!mIsSelected) {
|
||||
mLastModifier = kNoKey; // control is only special because it means its contextual menu time
|
||||
mIsSelected = YES;
|
||||
[self display]; // change selection immediately
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"DownloadInstanceSelected" object:self];
|
||||
}
|
||||
return [[self getController] contextualMenu];
|
||||
}
|
||||
|
||||
-(NSView*)hitTest:(NSPoint)aPoint
|
||||
{
|
||||
if (NSMouseInRect(aPoint, [self frame], YES)) {
|
||||
return self;
|
||||
}
|
||||
else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -14,7 +14,7 @@
|
|||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2002
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
|
@ -22,6 +22,7 @@
|
|||
* Contributor(s):
|
||||
* Calum Robinson <calumr@mac.com>
|
||||
* Simon Fraser <sfraser@netscape.com>
|
||||
* Josh Aas <josha@mac.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
|
@ -37,40 +38,45 @@
|
|||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
|
||||
#import "CHDownloadProgressDisplay.h"
|
||||
|
||||
class CHDownloader;
|
||||
@class ProgressDlgController;
|
||||
@class ProgressView;
|
||||
|
||||
// do this because there is no "no" key in the set of key modifiers.
|
||||
// it is in this header because both classes that need it include this file
|
||||
// (ProgressDlgController and ProgressView)
|
||||
enum {
|
||||
kNoKey = 0,
|
||||
kShiftKey = 1,
|
||||
kCommandKey = 2
|
||||
};
|
||||
|
||||
//
|
||||
// interface ProgressViewController
|
||||
//
|
||||
// There is a 1-to-1 correspondence between a download in the manager and
|
||||
// one of these objects. It implements what you can do with the item as
|
||||
// well as managing the download. It holds onto two views, one for while
|
||||
// the item is downloading, the other for after it has completed.
|
||||
//
|
||||
@interface ProgressViewController : NSObject<CHDownloadProgressDisplay>
|
||||
{
|
||||
// we share one progress bar between both views. It's in the expanded
|
||||
// view by default
|
||||
IBOutlet NSProgressIndicator *mProgressBar;
|
||||
IBOutlet NSProgressIndicator *mProgressBar;
|
||||
|
||||
// in-progress expanded view
|
||||
IBOutlet NSView *mProgressView;
|
||||
IBOutlet NSButton *mExpandedCancelButton;
|
||||
IBOutlet ProgressView *mProgressView; // in-progress view, STRONG ref
|
||||
IBOutlet ProgressView *mCompletedView; // completed view, STRONG ref
|
||||
|
||||
// in-progress collapsed view
|
||||
IBOutlet NSView *mProgressViewCompact;
|
||||
|
||||
// completed expanded view
|
||||
IBOutlet NSView *mCompletedView;
|
||||
IBOutlet NSButton *mExpandedRevealButton;
|
||||
IBOutlet NSButton *mExpandedOpenButton;
|
||||
|
||||
// completed collapsed view
|
||||
IBOutlet NSView *mCompletedViewCompact;
|
||||
|
||||
BOOL mViewIsCompact;
|
||||
BOOL mIsFileSave;
|
||||
BOOL mUserCancelled;
|
||||
BOOL mDownloadingError;
|
||||
BOOL mDownloadDone;
|
||||
BOOL mRemoveWhenDone;
|
||||
|
||||
NSTimeInterval mDownloadTime; // only set when done
|
||||
NSTimeInterval mDownloadTime; // only set when done
|
||||
|
||||
long mCurrentProgress; // if progress bar is indeterminate, can still calc stats.
|
||||
long mDownloadSize;
|
||||
|
@ -79,27 +85,28 @@ class CHDownloader;
|
|||
NSString *mDestPath;
|
||||
NSDate *mStartTime;
|
||||
|
||||
CHDownloader *mDownloader; // we hold a ref to this
|
||||
CHDownloader *mDownloader; // wraps gecko download, STRONG ref
|
||||
|
||||
ProgressDlgController *mProgressWindowController; // not retained
|
||||
ProgressDlgController *mProgressWindowController; // window controller, WEAK ref (owns us)
|
||||
}
|
||||
|
||||
+ (NSString *)formatTime:(int)aSeconds;
|
||||
+ (NSString *)formatFuzzyTime:(int)aSeconds;
|
||||
+ (NSString *)formatBytes:(float)aBytes;
|
||||
+(NSString *)formatTime:(int)aSeconds;
|
||||
+(NSString *)formatFuzzyTime:(int)aSeconds;
|
||||
+(NSString *)formatBytes:(float)aBytes;
|
||||
|
||||
- (NSView *)view;
|
||||
-(ProgressView *)view;
|
||||
|
||||
- (IBAction)close:(id)sender;
|
||||
-(IBAction)cancel:(id)sender;
|
||||
-(IBAction)remove:(id)sender;
|
||||
-(IBAction)reveal:(id)sender;
|
||||
-(IBAction)open:(id)sender;
|
||||
|
||||
- (IBAction)stop:(id)sender;
|
||||
- (IBAction)toggleDisclosure:(id)sender;
|
||||
-(BOOL)isActive;
|
||||
-(BOOL)isCanceled;
|
||||
-(BOOL)isSelected;
|
||||
|
||||
- (IBAction)reveal:(id)sender;
|
||||
- (IBAction)open:(id)sender;
|
||||
-(NSMenu*)contextualMenu;
|
||||
|
||||
- (BOOL)isActive;
|
||||
|
||||
- (void)setProgressWindowController:(ProgressDlgController*)progressWindowController;
|
||||
-(void)setProgressWindowController:(ProgressDlgController*)progressWindowController;
|
||||
|
||||
@end
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
* Contributor(s):
|
||||
* Calum Robinson <calumr@mac.com>
|
||||
* Simon Fraser <sfraser@netscape.com>
|
||||
* Josh Aas <josha@mac.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
|
@ -43,60 +44,50 @@
|
|||
#import "ProgressViewController.h"
|
||||
#import "ProgressDlgController.h"
|
||||
#import "PreferenceManager.h"
|
||||
#import "ProgressView.h"
|
||||
|
||||
enum
|
||||
{
|
||||
kLabelTagFilename = 1000,
|
||||
kLabelTagProgress,
|
||||
kLabelTagSource,
|
||||
kLabelTagDestination,
|
||||
enum {
|
||||
kLabelTagFilename = 1000,
|
||||
kLabelTagStatus,
|
||||
kLabelTagTimeRemaining,
|
||||
kLabelTagStatus, // 1005
|
||||
kLabelTagTimeRemainingLabel
|
||||
kLabelTagIcon
|
||||
};
|
||||
|
||||
|
||||
// Notification sent when user holds option key and expands/contracts a progress view
|
||||
static NSString *ProgressViewsShouldResize = @"ProgressViewsShouldResize";
|
||||
|
||||
@interface ProgressViewController(ProgressViewControllerPrivate)
|
||||
|
||||
- (void)viewDidLoad;
|
||||
- (void)refreshDownloadInfo;
|
||||
- (void)moveProgressBarToCurrentView;
|
||||
- (void)updateButtons;
|
||||
- (void)launchFileIfAppropriate;
|
||||
-(void)viewDidLoad;
|
||||
-(void)refreshDownloadInfo;
|
||||
-(void)launchFileIfAppropriate;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ProgressViewController
|
||||
|
||||
+ (NSString *)formatTime:(int)seconds
|
||||
+(NSString *)formatTime:(int)seconds
|
||||
{
|
||||
NSMutableString *theTime = [NSMutableString stringWithCapacity:8];
|
||||
|
||||
|
||||
NSString *padZero = [NSString stringWithString:@"0"];
|
||||
//write out new elapsed time
|
||||
if (seconds >= 3600)
|
||||
{
|
||||
if (seconds >= 3600) {
|
||||
[theTime appendFormat:@"%d:",(seconds / 3600)];
|
||||
seconds = seconds % 3600;
|
||||
}
|
||||
|
||||
|
||||
NSString *elapsedMin = [NSString stringWithFormat:@"%d:",(seconds / 60)];
|
||||
if ([elapsedMin length] == 2)
|
||||
[theTime appendString:[padZero stringByAppendingString:elapsedMin]];
|
||||
else
|
||||
[theTime appendString:elapsedMin];
|
||||
|
||||
|
||||
seconds = seconds % 60;
|
||||
NSString *elapsedSec = [NSString stringWithFormat:@"%d",seconds];
|
||||
|
||||
|
||||
if ([elapsedSec length] == 2)
|
||||
[theTime appendString:elapsedSec];
|
||||
else
|
||||
[theTime appendString:[padZero stringByAppendingString:elapsedSec]];
|
||||
|
||||
|
||||
return theTime;
|
||||
}
|
||||
|
||||
|
@ -115,7 +106,7 @@ static NSString *ProgressViewsShouldResize = @"ProgressViewsShouldResize";
|
|||
seconds = seconds/60;
|
||||
if (seconds < 60) {
|
||||
if (seconds < 2)
|
||||
return [NSString stringWithFormat:NSLocalizedString(@"AboutMin",@"About a minute")];
|
||||
return [NSString stringWithFormat:NSLocalizedString(@"AboutMin",@"About a minute")];
|
||||
// OK, tell the good people how much time we have left.
|
||||
return [NSString stringWithFormat:NSLocalizedString(@"AboutMins",@"About %d minutes"), seconds];
|
||||
}
|
||||
|
@ -126,7 +117,7 @@ static NSString *ProgressViewsShouldResize = @"ProgressViewsShouldResize";
|
|||
return [NSString stringWithFormat:NSLocalizedString(@"AboutHours", @"Over %d hours"), seconds];
|
||||
}
|
||||
|
||||
+ (NSString *)formatBytes:(float)bytes
|
||||
+(NSString*)formatBytes:(float)bytes
|
||||
{
|
||||
// if bytes are negative, we return question marks.
|
||||
if (bytes < 0)
|
||||
|
@ -149,406 +140,360 @@ static NSString *ProgressViewsShouldResize = @"ProgressViewsShouldResize";
|
|||
|
||||
#pragma mark -
|
||||
|
||||
- (id)init
|
||||
-(id)init
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
[NSBundle loadNibNamed:@"ProgressView" owner:self];
|
||||
|
||||
[self viewDidLoad];
|
||||
|
||||
// Register for notifications when one of the progress views is expanded/contracted
|
||||
// whilst holding the option button
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(changeCollapsedStateNotification:)
|
||||
name:ProgressViewsShouldResize
|
||||
object:nil];
|
||||
|
||||
[mExpandedRevealButton setEnabled:NO];
|
||||
[mExpandedOpenButton setEnabled:NO];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
-(void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self
|
||||
name:ProgressViewsShouldResize
|
||||
object:nil];
|
||||
|
||||
// if we get here because we're quitting, the listener will still be alive
|
||||
// yet we're going away. As a result, we need to tell the d/l listener to
|
||||
// forget it ever met us and necko will clean it up on its own.
|
||||
if (mDownloader)
|
||||
if (mDownloader) {
|
||||
mDownloader->DetachDownloadDisplay();
|
||||
}
|
||||
NS_IF_RELEASE(mDownloader);
|
||||
|
||||
|
||||
[mStartTime release];
|
||||
[mSourceURL release];
|
||||
[mDestPath release];
|
||||
[mProgressBar release];
|
||||
|
||||
|
||||
// loading the nib has retained these, we have to release them.
|
||||
[mCompletedView release];
|
||||
[mProgressView release];
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
// Save the expand/contract view pref (called when the user clicks the dislosure triangle)
|
||||
- (void)setCompactViewPref
|
||||
-(void)viewDidLoad
|
||||
{
|
||||
[[PreferenceManager sharedInstance] setPref:"browser.download.compactView" toBoolean:mViewIsCompact];
|
||||
[mProgressBar retain]; // make sure it survives being moved between views
|
||||
// this isn't necessarily better. Need to profile.
|
||||
[mProgressBar setUsesThreadedAnimation:YES];
|
||||
// give the views this controller as their controller
|
||||
[mCompletedView setController:self];
|
||||
[mProgressView setController:self];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
-(ProgressView*)view
|
||||
{
|
||||
mViewIsCompact = [[PreferenceManager sharedInstance] getBooleanPref:"browser.download.compactView" withSuccess:NULL];
|
||||
[mProgressBar retain]; // make sure it survives being moved between views
|
||||
|
||||
if (mViewIsCompact)
|
||||
[self moveProgressBarToCurrentView];
|
||||
|
||||
// this isn't necessarily better. Need to profile.
|
||||
[mProgressBar setUsesThreadedAnimation:YES];
|
||||
return (mDownloadDone ? mCompletedView : mProgressView);
|
||||
}
|
||||
|
||||
- (NSView *)view
|
||||
-(IBAction)copySourceURL:(id)sender
|
||||
{
|
||||
if (mViewIsCompact)
|
||||
return (mDownloadDone ? mCompletedViewCompact : mProgressViewCompact);
|
||||
else
|
||||
return (mDownloadDone ? mCompletedView : mProgressView);
|
||||
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
|
||||
[pasteboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
|
||||
[pasteboard setString:mSourceURL forType:NSStringPboardType];
|
||||
}
|
||||
|
||||
- (IBAction)toggleDisclosure:(id)sender
|
||||
{
|
||||
mViewIsCompact = !mViewIsCompact;
|
||||
|
||||
[self moveProgressBarToCurrentView];
|
||||
|
||||
// Is option/alt held down?
|
||||
if ([[[sender window] currentEvent] modifierFlags] & NSAlternateKeyMask)
|
||||
{
|
||||
// Get all progress views to look the same as self
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:ProgressViewsShouldResize
|
||||
object:[NSNumber numberWithBool:mViewIsCompact]];
|
||||
}
|
||||
|
||||
// Set the pref only when the user clicks the disclosure triangle
|
||||
[self setCompactViewPref];
|
||||
|
||||
// Re-calculate the new view & window sizes
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:StackViewReloadNotificationName
|
||||
object:self];
|
||||
[self refreshDownloadInfo];
|
||||
}
|
||||
|
||||
- (void)changeCollapsedStateNotification:(NSNotification *)notification
|
||||
{
|
||||
// note that this will get called on the view that is being clicked, as well
|
||||
// as the other views. Don't do redundant work here, like redrawing.
|
||||
mViewIsCompact = [[notification object] boolValue];
|
||||
// Don't call [enclosingStackView reloadSubviews]; here, because it will be done
|
||||
// by the original view that was option-clicked, not us
|
||||
}
|
||||
|
||||
-(void)cancel
|
||||
-(IBAction)cancel:(id)sender
|
||||
{
|
||||
mUserCancelled = YES;
|
||||
|
||||
if (mDownloader) // we should always have one
|
||||
|
||||
if (mDownloader) { // we should always have one
|
||||
mDownloader->CancelDownload();
|
||||
|
||||
}
|
||||
|
||||
// note that we never want to delete downloaded files here,
|
||||
// because the file does not have its final path yet.
|
||||
// Necko will delete the evil temp file.
|
||||
}
|
||||
|
||||
- (IBAction)close:(id)sender
|
||||
-(IBAction)reveal:(id)sender
|
||||
{
|
||||
if (!mDownloadDone)
|
||||
{
|
||||
mRemoveWhenDone = YES;
|
||||
[self cancel];
|
||||
}
|
||||
else
|
||||
{
|
||||
[mProgressWindowController removeDownload:self];
|
||||
if (![[NSWorkspace sharedWorkspace] selectFile:mDestPath
|
||||
inFileViewerRootedAtPath:[mDestPath stringByDeletingLastPathComponent]]) {
|
||||
NSBeep();
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)stop:(id)sender
|
||||
-(IBAction)open:(id)sender
|
||||
{
|
||||
[self cancel];
|
||||
if (![[NSWorkspace sharedWorkspace] openFile:mDestPath]) {
|
||||
NSBeep();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
- (IBAction)reveal:(id)sender
|
||||
-(IBAction)remove:(id)sender
|
||||
{
|
||||
if ([[NSWorkspace sharedWorkspace] selectFile:mDestPath
|
||||
inFileViewerRootedAtPath:[mDestPath stringByDeletingLastPathComponent]])
|
||||
return;
|
||||
// hmmm. it didn't work. that's odd. need localized error messages. for now, just beep.
|
||||
NSBeep();
|
||||
}
|
||||
|
||||
- (IBAction)open:(id)sender
|
||||
{
|
||||
if ([[NSWorkspace sharedWorkspace] openFile:mDestPath])
|
||||
return;
|
||||
// hmmm. it didn't work. that's odd. need localized error message. for now, just beep.
|
||||
NSBeep();
|
||||
[mProgressWindowController removeDownload:self];
|
||||
}
|
||||
|
||||
// Called just before the view will be shown to the user
|
||||
- (void)downloadDidStart
|
||||
-(void)downloadDidStart
|
||||
{
|
||||
mStartTime = [[NSDate alloc] init];
|
||||
// [mProgressBar startAnimation:self]; // moved to onStartDownload
|
||||
[self refreshDownloadInfo];
|
||||
}
|
||||
|
||||
- (void)downloadDidEnd
|
||||
-(void)downloadDidEnd
|
||||
{
|
||||
if (!mDownloadDone) // some error conditions can cause this to get called twice
|
||||
{
|
||||
if (!mDownloadDone) { // some error conditions can cause this to get called twice
|
||||
// retain the selection when the view switches - do this before mDownloadDone changes
|
||||
[mCompletedView setSelected:[self isSelected]];
|
||||
|
||||
mDownloadDone = YES;
|
||||
mDownloadTime = -[mStartTime timeIntervalSinceNow];
|
||||
[mProgressBar stopAnimation:self];
|
||||
|
||||
[mExpandedCancelButton setEnabled:NO];
|
||||
|
||||
// get the Finder to update
|
||||
// get the Finder to update - doesn't work on some earlier version of Mac OS X
|
||||
// (I think it was fixed by 10.2.2 or so)
|
||||
[[NSWorkspace sharedWorkspace] noteFileSystemChanged:mDestPath];
|
||||
|
||||
|
||||
[self refreshDownloadInfo];
|
||||
[self launchFileIfAppropriate];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)launchFileIfAppropriate
|
||||
-(void)launchFileIfAppropriate
|
||||
{
|
||||
if (!mIsFileSave && !mUserCancelled && !mDownloadingError)
|
||||
{
|
||||
if ([[PreferenceManager sharedInstance] getBooleanPref:"browser.download.autoDispatch" withSuccess:NULL])
|
||||
if (!mIsFileSave && !mUserCancelled && !mDownloadingError) {
|
||||
if ([[PreferenceManager sharedInstance] getBooleanPref:"browser.download.autoDispatch" withSuccess:NULL]) {
|
||||
[[NSWorkspace sharedWorkspace] openFile:mDestPath withApplication:nil andDeactivate:NO];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this handles lots of things.
|
||||
- (void)refreshDownloadInfo
|
||||
// this handles lots of things - all of the status updates
|
||||
-(void)refreshDownloadInfo
|
||||
{
|
||||
NSView* curView = [self view];
|
||||
|
||||
NSString* filename = [mSourceURL lastPathComponent];
|
||||
NSString *destPath = [mDestPath stringByAbbreviatingWithTildeInPath];
|
||||
NSString* tooltipFormat = NSLocalizedString(mDownloadDone ? @"DownloadedTooltipFormat" : @"DownloadingTooltipFormat", @"");
|
||||
|
||||
id iconLabel = [curView viewWithTag:kLabelTagIcon];
|
||||
|
||||
id filenameLabel = [curView viewWithTag:kLabelTagFilename];
|
||||
[filenameLabel setStringValue:filename];
|
||||
[filenameLabel setToolTip:[NSString stringWithFormat:tooltipFormat, [mSourceURL lastPathComponent], mSourceURL, destPath]];
|
||||
|
||||
id destLabel = [curView viewWithTag:kLabelTagDestination];
|
||||
[destLabel setStringValue:destPath];
|
||||
|
||||
id locationLabel = [curView viewWithTag:kLabelTagSource];
|
||||
[locationLabel setStringValue:mSourceURL];
|
||||
|
||||
if (mDownloadDone)
|
||||
{
|
||||
id statusLabel = [curView viewWithTag:kLabelTagStatus];
|
||||
if (statusLabel)
|
||||
{
|
||||
NSString* statusString;
|
||||
if (mUserCancelled)
|
||||
statusString = NSLocalizedString(@"DownloadCancelled", @"Cancelled");
|
||||
else if (mDownloadingError)
|
||||
statusString = NSLocalizedString(@"DownloadInterrupted", @"Interrupted");
|
||||
else
|
||||
statusString = NSLocalizedString(@"DownloadCompleted", @"Completed");
|
||||
|
||||
[statusLabel setStringValue: statusString];
|
||||
if (iconLabel) { // update the icon image
|
||||
NSImage *iconImage = [[NSWorkspace sharedWorkspace] iconForFile:mDestPath];
|
||||
// sometimes the finder doesn't have an icon for us (rarely)
|
||||
// when that happens just leave it at what it was before
|
||||
if (iconImage != nil) {
|
||||
[iconLabel setImage:iconImage];
|
||||
}
|
||||
|
||||
// set progress label
|
||||
id progressLabel = [curView viewWithTag:kLabelTagProgress];
|
||||
if (progressLabel)
|
||||
{
|
||||
float byteSec = mCurrentProgress / mDownloadTime;
|
||||
// show how much we downloaded, become some types of disconnects make us think
|
||||
// we finished successfully
|
||||
[progressLabel setStringValue:[NSString stringWithFormat:
|
||||
NSLocalizedString(@"DownloadDoneStatusString", @"%@ of %@ done (at %@/sec)"),
|
||||
[[self class] formatBytes:mCurrentProgress],
|
||||
[[self class] formatBytes:mDownloadSize],
|
||||
[[self class] formatBytes:byteSec]]];
|
||||
}
|
||||
|
||||
id timeLabel = [curView viewWithTag:kLabelTagTimeRemaining];
|
||||
if (timeLabel)
|
||||
[timeLabel setStringValue:[[self class] formatTime:(int)mDownloadTime]];
|
||||
|
||||
id timeLabelLabel = [curView viewWithTag:kLabelTagTimeRemainingLabel];
|
||||
if (timeLabelLabel)
|
||||
[timeLabelLabel setStringValue:NSLocalizedString(@"DownloadRemainingLabelDone", @"Time elapsed:")];
|
||||
|
||||
[self updateButtons];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (mDownloadDone) { // just update the status field
|
||||
id statusLabel = [curView viewWithTag:kLabelTagStatus];
|
||||
if (statusLabel) {
|
||||
NSString* statusString;
|
||||
if (mUserCancelled) {
|
||||
statusString = NSLocalizedString(@"DownloadCancelled", @"Cancelled");
|
||||
}
|
||||
else if (mDownloadingError) {
|
||||
statusString = NSLocalizedString(@"DownloadInterrupted", @"Interrupted");
|
||||
}
|
||||
else {
|
||||
statusString = [NSString stringWithFormat:NSLocalizedString(@"DownloadCompleted", @"Completed in %@ (%@)"),
|
||||
[[self class] formatTime:(int)mDownloadTime], [[self class] formatBytes:mDownloadSize]];
|
||||
}
|
||||
|
||||
[statusLabel setStringValue:statusString];
|
||||
}
|
||||
}
|
||||
else {
|
||||
NSTimeInterval elapsedTime = -[mStartTime timeIntervalSinceNow];
|
||||
|
||||
|
||||
// update status field
|
||||
id progressLabel = [curView viewWithTag:kLabelTagProgress];
|
||||
if (progressLabel)
|
||||
{
|
||||
NSString *statusLabelString = NSLocalizedString(@"DownloadStatusString", @"%@ of %@ total (at %@/sec)");
|
||||
id statusLabel = [curView viewWithTag:kLabelTagStatus];
|
||||
if (statusLabel) {
|
||||
NSString *statusLabelString = NSLocalizedString(@"DownloadStatusString", @"%@ of %@ (at %@/sec)");
|
||||
float byteSec = mCurrentProgress / elapsedTime;
|
||||
[progressLabel setStringValue:[NSString stringWithFormat:statusLabelString,
|
||||
[[self class] formatBytes:mCurrentProgress],
|
||||
(mDownloadSize > 0 ? [[self class] formatBytes:mDownloadSize] : @"?"),
|
||||
[[self class] formatBytes:byteSec]]];
|
||||
[statusLabel setStringValue:[NSString stringWithFormat:statusLabelString,
|
||||
[[self class] formatBytes:mCurrentProgress],
|
||||
(mDownloadSize > 0 ? [[self class] formatBytes:mDownloadSize] : @"?"),
|
||||
[[self class] formatBytes:byteSec]]];
|
||||
}
|
||||
|
||||
id timeLabel = [curView viewWithTag:kLabelTagTimeRemaining];
|
||||
if (timeLabel)
|
||||
{
|
||||
if (mDownloadSize > 0)
|
||||
{
|
||||
if (timeLabel) {
|
||||
if (mDownloadSize > 0) {
|
||||
int secToGo = (int)ceil((elapsedTime * mDownloadSize / mCurrentProgress) - elapsedTime);
|
||||
[timeLabel setStringValue:[[self class] formatFuzzyTime:secToGo]];
|
||||
}
|
||||
else // mDownloadSize is undetermined. Set remaining time to question marks.
|
||||
{
|
||||
NSString *calculatingString = NSLocalizedString(@"DownloadCalculatingString", @"Unknown");
|
||||
[timeLabel setStringValue:calculatingString];
|
||||
else { // mDownloadSize is undetermined. Set remaining time to question marks.
|
||||
[timeLabel setStringValue:NSLocalizedString(@"DownloadCalculatingString", @"Unknown")];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateButtons
|
||||
{
|
||||
// note: this will stat every time, which will be expensive! We could use
|
||||
// FNNotify/FNSubscribe to avoid this (writing a Cocoa wrapper around it).
|
||||
if (mDownloadDone && !mDownloadingError)
|
||||
{
|
||||
BOOL destFileExists = [[NSFileManager defaultManager] fileExistsAtPath:mDestPath];
|
||||
[mExpandedRevealButton setEnabled:destFileExists];
|
||||
[mExpandedOpenButton setEnabled:destFileExists];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)moveProgressBarToCurrentView
|
||||
{
|
||||
[mProgressBar moveToView:(mViewIsCompact ? mProgressViewCompact : mProgressView) resize:YES];
|
||||
[mProgressBar startAnimation:self]; // this is necessary to keep it animating for some reason
|
||||
}
|
||||
|
||||
- (void)setProgressWindowController:(ProgressDlgController*)progressWindowController
|
||||
-(void)setProgressWindowController:(ProgressDlgController*)progressWindowController
|
||||
{
|
||||
mProgressWindowController = progressWindowController;
|
||||
}
|
||||
|
||||
- (BOOL)isActive
|
||||
-(BOOL)isActive
|
||||
{
|
||||
return !mDownloadDone;
|
||||
}
|
||||
|
||||
-(BOOL)isCanceled
|
||||
{
|
||||
return mUserCancelled;
|
||||
}
|
||||
|
||||
-(BOOL)isSelected
|
||||
{
|
||||
return [[self view] isSelected];
|
||||
}
|
||||
|
||||
-(NSMenu*)contextualMenu
|
||||
{
|
||||
NSMenu *menu = [[NSMenu alloc] init];
|
||||
NSMenuItem *revealItem;
|
||||
NSMenuItem *cancelItem;
|
||||
NSMenuItem *removeItem;
|
||||
NSMenuItem *openItem;
|
||||
NSMenuItem *copySourceURLItem;
|
||||
|
||||
revealItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"dlRevealCMLabel", @"Show in Finder")
|
||||
action:@selector(reveal:) keyEquivalent:@""];
|
||||
cancelItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"dlCancelCMLabel", @"Cancel")
|
||||
action:@selector(cancel:) keyEquivalent:@""];
|
||||
removeItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"dlRemoveCMLabel", @"Remove")
|
||||
action:@selector(remove:) keyEquivalent:@""];
|
||||
openItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"dlOpenCMLabel", @"Open")
|
||||
action:@selector(open:) keyEquivalent:@""];
|
||||
copySourceURLItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"dlCopySourceURLCMLabel", @"Copy Source URL")
|
||||
action:@selector(copySourceURL:) keyEquivalent:@""];
|
||||
|
||||
[revealItem setTarget:mProgressWindowController];
|
||||
[cancelItem setTarget:mProgressWindowController];
|
||||
[removeItem setTarget:mProgressWindowController];
|
||||
[openItem setTarget:mProgressWindowController];
|
||||
[copySourceURLItem setTarget:self];
|
||||
|
||||
[menu addItem:revealItem];
|
||||
[menu addItem:cancelItem];
|
||||
[menu addItem:removeItem];
|
||||
[menu addItem:openItem];
|
||||
[menu addItem:[NSMenuItem separatorItem]];
|
||||
[menu addItem:copySourceURLItem];
|
||||
|
||||
[revealItem release];
|
||||
[cancelItem release];
|
||||
[removeItem release];
|
||||
[openItem release];
|
||||
[copySourceURLItem release];
|
||||
|
||||
return [menu autorelease];
|
||||
}
|
||||
|
||||
- (BOOL)validateMenuItem:(id <NSMenuItem>)menuItem
|
||||
{
|
||||
SEL action = [menuItem action];
|
||||
if (action == @selector(cancel:)) {
|
||||
return ((!mUserCancelled) && (!mDownloadDone));
|
||||
}
|
||||
if (action == @selector(remove:)) {
|
||||
return (mUserCancelled || mDownloadDone);
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)onStartDownload:(BOOL)isFileSave
|
||||
-(void)onStartDownload:(BOOL)isFileSave
|
||||
{
|
||||
mIsFileSave = isFileSave;
|
||||
[self downloadDidStart];
|
||||
[mProgressWindowController didStartDownload:self];
|
||||
|
||||
|
||||
// need to do this after the view as been put in the window, otherwise it doesn't work
|
||||
[mProgressBar startAnimation:self];
|
||||
}
|
||||
|
||||
- (void)onEndDownload:(BOOL)completedOK
|
||||
-(void)onEndDownload:(BOOL)completedOK
|
||||
{
|
||||
mDownloadingError = !completedOK;
|
||||
|
||||
|
||||
[self downloadDidEnd];
|
||||
[mProgressWindowController didEndDownload:self];
|
||||
if (mRemoveWhenDone)
|
||||
[mProgressWindowController removeDownload:self];
|
||||
}
|
||||
|
||||
- (void)setProgressTo:(long)aCurProgress ofMax:(long)aMaxProgress
|
||||
-(void)setProgressTo:(long)aCurProgress ofMax:(long)aMaxProgress
|
||||
{
|
||||
mCurrentProgress = aCurProgress; // fall back for stat calcs
|
||||
mDownloadSize = aMaxProgress;
|
||||
|
||||
if (![mProgressBar isIndeterminate]) //most likely - just update value
|
||||
{
|
||||
if (aCurProgress == aMaxProgress) //handles little bug in FTP download size
|
||||
if (![mProgressBar isIndeterminate]) { //most likely - just update value
|
||||
if (aCurProgress == aMaxProgress) { //handles little bug in FTP download size
|
||||
[mProgressBar setMaxValue:aMaxProgress];
|
||||
|
||||
}
|
||||
[mProgressBar setDoubleValue:aCurProgress];
|
||||
}
|
||||
else if (aMaxProgress > 0) // ok, we're starting up with good max & cur values
|
||||
{
|
||||
else if (aMaxProgress > 0) { // ok, we're starting up with good max & cur values
|
||||
[mProgressBar setIndeterminate:NO];
|
||||
[mProgressBar setMaxValue:aMaxProgress];
|
||||
[mProgressBar setDoubleValue:aCurProgress];
|
||||
} // if neither case was true, it's barber pole city.
|
||||
} // if neither case was true, it's barber pole city
|
||||
}
|
||||
|
||||
-(void)setDownloadListener:(CHDownloader*)aDownloader
|
||||
{
|
||||
if (mDownloader != aDownloader)
|
||||
if (mDownloader != aDownloader) {
|
||||
NS_IF_RELEASE(mDownloader);
|
||||
}
|
||||
|
||||
NS_IF_ADDREF(mDownloader = aDownloader);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
This is kind of a hack. It should probably be done somewhere else so Mozilla can have
|
||||
it too, but until Apple fixes the problems with the setting of comments without
|
||||
reverting to Applescript, I have left it in.
|
||||
|
||||
Turned off for now, until we find a better way to do this. Won't Carbon APIs work?
|
||||
*/
|
||||
This is kind of a hack. It should probably be done somewhere else so Mozilla can have
|
||||
it too, but until Apple fixes the problems with the setting of comments without
|
||||
reverting to Applescript, I have left it in.
|
||||
|
||||
Turned off for now, until we find a better way to do this. Won't Carbon APIs work?
|
||||
*/
|
||||
- (void)tryToSetFinderComments
|
||||
{
|
||||
if (mDestPath && mSourceURL)
|
||||
{
|
||||
if (mDestPath && mSourceURL) {
|
||||
CFURLRef fileURL = CFURLCreateWithFileSystemPath( NULL,
|
||||
(CFStringRef)mDestPath,
|
||||
kCFURLPOSIXPathStyle,
|
||||
NO);
|
||||
|
||||
NSString *hfsPath = (NSString *)CFURLCopyFileSystemPath(fileURL,
|
||||
kCFURLHFSPathStyle);
|
||||
kCFURLHFSPathStyle);
|
||||
|
||||
CFRelease(fileURL);
|
||||
|
||||
NSAppleScript *setCommentScript = [[NSAppleScript alloc] initWithSource:
|
||||
[NSString stringWithFormat:@"tell application \"Finder\" to set comment of file \"%@\" to \"%@\"", hfsPath, mSourceURL]];
|
||||
[NSString stringWithFormat:@"tell application \"Finder\" to set comment of file \"%@\" to \"%@\"", hfsPath, mSourceURL]];
|
||||
NSDictionary *errorInfo = NULL;
|
||||
|
||||
[setCommentScript executeAndReturnError:&errorInfo];
|
||||
|
||||
if (errorInfo)
|
||||
{
|
||||
NSLog(@"Get error when running AppleScript to set comments for '%@':\n %@",
|
||||
mDestPath,
|
||||
[errorInfo objectForKey:NSAppleScriptErrorMessage]);
|
||||
if (errorInfo) {
|
||||
NSLog(@"Get error when running AppleScript to set comments for '%@':\n %@",
|
||||
mDestPath,
|
||||
[errorInfo objectForKey:NSAppleScriptErrorMessage]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
- (void)setSourceURL:(NSString*)aSourceURL
|
||||
-(void)setSourceURL:(NSString*)aSourceURL
|
||||
{
|
||||
[mSourceURL autorelease];
|
||||
mSourceURL = [aSourceURL copy];
|
||||
|
||||
[mProgressView setToolTip:mSourceURL];
|
||||
[mCompletedView setToolTip:mSourceURL];
|
||||
//[self tryToSetFinderComments];
|
||||
}
|
||||
|
||||
- (void)setDestinationPath:(NSString*)aDestPath
|
||||
-(void)setDestinationPath:(NSString*)aDestPath
|
||||
{
|
||||
[mDestPath autorelease];
|
||||
mDestPath = [aDestPath copy];
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
|
@ -20,6 +21,7 @@
|
|||
*
|
||||
* Contributor(s):
|
||||
* Calum Robinson <calumr@mac.com>
|
||||
* Josh Aas <josha@mac.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
|
@ -34,13 +36,13 @@
|
|||
* the terms of any one of the NPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/*
|
||||
This class was inspired by the OAStack view from OmniGroup, but not copied directly. This
|
||||
implementation is a little simpler, which is fine.
|
||||
|
||||
/*
|
||||
This class was inspired by the OAStack view from OmniGroup, but not copied directly. This
|
||||
implementation is a little simpler, which is fine.
|
||||
|
||||
It's like NSTableView, except the data source returns views instead of strings/images etc.
|
||||
*/
|
||||
It's like NSTableView, except the data source returns views instead of strings/images etc.
|
||||
*/
|
||||
|
||||
#import "CHStackView.h"
|
||||
|
||||
|
@ -49,56 +51,55 @@ NSString* StackViewResizedNotificationName = @"StackViewResized";
|
|||
|
||||
@implementation CHStackView
|
||||
|
||||
- (id)initWithFrame:(NSRect)frameRect
|
||||
-(id)initWithFrame:(NSRect)frameRect
|
||||
{
|
||||
if ((self = [super initWithFrame:frameRect]))
|
||||
{
|
||||
if ((self = [super initWithFrame:frameRect])) {
|
||||
// Register for notifications when one of the subviews changes size
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(reloadNotification:)
|
||||
name:StackViewReloadNotificationName
|
||||
object:nil];
|
||||
|
||||
selector:@selector(reloadNotification:)
|
||||
name:StackViewReloadNotificationName
|
||||
object:nil];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
-(void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)setDataSource:(id)aDataSource
|
||||
-(void)setDataSource:(id)aDataSource
|
||||
{
|
||||
mDataSource = aDataSource; // should this retain?
|
||||
|
||||
mDataSource = aDataSource; // should this retain?
|
||||
[self reloadSubviews];
|
||||
}
|
||||
|
||||
- (void)reloadNotification:(NSNotification *)notification
|
||||
-(void)reloadNotification:(NSNotification*)notification
|
||||
{
|
||||
[self reloadSubviews];
|
||||
}
|
||||
|
||||
- (void)reloadSubviews
|
||||
-(void)reloadSubviews
|
||||
{
|
||||
NSRect newFrame = [self frame];
|
||||
NSSize oldSize = [self bounds].size;
|
||||
int i, subviewCount = [mDataSource subviewsForStackView:self];
|
||||
|
||||
NSPoint nextOrigin = NSZeroPoint;
|
||||
|
||||
// we'll maintain the width of the stack view, assuming that it's
|
||||
// scaled by its superview.
|
||||
// maintain the width of the stack view, assuming that it's scaled by its superview.
|
||||
newFrame.size.height = 0.0;
|
||||
|
||||
[[self subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)];
|
||||
// be really careful about this since we're changing the length of the array
|
||||
// as we go through it
|
||||
while ([[self subviews] count] > 0) {
|
||||
[((NSView*)[[self subviews] objectAtIndex:0]) removeFromSuperview];
|
||||
}
|
||||
|
||||
for (i = 0; i < subviewCount; i++)
|
||||
{
|
||||
for (i = 0; i < subviewCount; i++) {
|
||||
NSView *subview = [mDataSource viewForStackView:self atIndex:i];
|
||||
NSRect subviewFrame = [subview frame];
|
||||
NSBox *separator;
|
||||
|
||||
unsigned int autoResizeMask = [subview autoresizingMask];
|
||||
if (autoResizeMask & NSViewWidthSizable)
|
||||
|
@ -112,16 +113,13 @@ NSString* StackViewResizedNotificationName = @"StackViewResized";
|
|||
|
||||
[self addSubview:subview];
|
||||
|
||||
// If there are more subviews, add a separator
|
||||
if (i + 1 < subviewCount)
|
||||
{
|
||||
NSBox *separator = [[NSBox alloc] initWithFrame:
|
||||
NSMakeRect(nextOrigin.x, nextOrigin.y - 1.0, NSWidth([subview frame]), 1.0)];
|
||||
[separator setBoxType:NSBoxSeparator];
|
||||
[separator setAutoresizingMask:NSViewWidthSizable];
|
||||
[self addSubview:separator];
|
||||
[separator release];
|
||||
}
|
||||
// always add a separator
|
||||
separator = [[NSBox alloc] initWithFrame:
|
||||
NSMakeRect(nextOrigin.x, nextOrigin.y - 1.0, NSWidth([subview frame]), 1.0)];
|
||||
[separator setBoxType:NSBoxSeparator];
|
||||
[separator setAutoresizingMask:NSViewWidthSizable];
|
||||
[self addSubview:separator];
|
||||
[separator release];
|
||||
}
|
||||
|
||||
NSRect newBounds = newFrame;
|
||||
|
@ -129,13 +127,13 @@ NSString* StackViewResizedNotificationName = @"StackViewResized";
|
|||
|
||||
[self setFrame:newFrame];
|
||||
[self setNeedsDisplay:YES];
|
||||
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:StackViewResizedNotificationName
|
||||
object:self
|
||||
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSValue valueWithSize:oldSize], @"oldsize", nil]];
|
||||
object:self
|
||||
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSValue valueWithSize:oldSize], @"oldsize", nil]];
|
||||
}
|
||||
|
||||
- (BOOL)isFlipped
|
||||
-(BOOL)isFlipped
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче