new d/l manager from josh aas (bug 223583)

This commit is contained in:
pinkerton%aol.net 2003-12-30 21:15:47 +00:00
Родитель 6caa49d6ee
Коммит 6909924f9e
20 изменённых файлов: 1132 добавлений и 538 удалений

Просмотреть файл

@ -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/images/toolbar/dl_cancel.tif Normal file

Двоичный файл не отображается.

Двоичные данные
camino/resources/images/toolbar/dl_clearall.tif Normal file

Двоичный файл не отображается.

Двоичные данные
camino/resources/images/toolbar/dl_open.tif Normal file

Двоичный файл не отображается.

Двоичные данные
camino/resources/images/toolbar/dl_remove.tif Normal file

Двоичный файл не отображается.

Двоичные данные
camino/resources/images/toolbar/dl_reveal.tif Normal file

Двоичный файл не отображается.

Двоичный файл не отображается.

Просмотреть файл

@ -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 сгенерированный

Двоичный файл не отображается.

Просмотреть файл

@ -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 сгенерированный

Двоичный файл не отображается.

Просмотреть файл

@ -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;
}