Added `-[NSArray validateAsPropertyList]` and `-[NSDictionary validateAsPropertyList...
[adiumx.git] / Source / ESFileTransferProgressView.m
bloba362157facbde03e6e43a0888fccd81aa7cda7a2
1 /* 
2  * Adium is the legal property of its developers, whose names are listed in the copyright file included
3  * with this source distribution.
4  * 
5  * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
6  * General Public License as published by the Free Software Foundation; either version 2 of the License,
7  * or (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
10  * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
11  * Public License for more details.
12  * 
13  * You should have received a copy of the GNU General Public License along with this program; if not,
14  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
15  */
17 #import "ESFileTransferProgressRow.h"
18 #import "ESFileTransferProgressView.h"
19 #import <AIUtilities/AIParagraphStyleAdditions.h>
20 #import <AIUtilities/AIRolloverButton.h>
21 #import <AIUtilities/AIImageAdditions.h>
22 #import <AIUtilities/AIStringAdditions.h>
24 #define NORMAL_TEXT_COLOR               [NSColor controlTextColor]
25 #define SELECTED_TEXT_COLOR             [NSColor whiteColor]
26 #define TRANSFER_STATUS_COLOR   [NSColor disabledControlTextColor]
28 @interface ESFileTransferProgressView (PRIVATE)
29 - (void)updateHeaderLine;
30 - (void)updateButtonReveal;
31 - (void)updateButtonStopResume;
32 @end
34 @implementation ESFileTransferProgressView
36 - (void)awakeFromNib
38         if ([[self superclass] instancesRespondToSelector:@selector(awakeFromNib)]) {
39         [super awakeFromNib];
40     }
42         [progressIndicator setUsesThreadedAnimation:YES];
43         [progressIndicator setIndeterminate:YES];
44         progressVisible = YES;
45                 
46         showingDetails = NO;
47         [view_details retain];
49         [button_stopResume setDelegate:self];
50         [button_reveal setDelegate:self];
52         buttonStopResumeIsHovered = NO;
53         buttonRevealIsHovered = NO;
56 - (void)dealloc
58         [view_details release];
59         
60         [super dealloc];
63 #pragma mark Source and destination
64 - (void)setSourceName:(NSString *)inSourceName
66         [textField_source setStringValue:(inSourceName ? inSourceName : @"")];
68 - (void)setSourceIcon:(NSImage *)inSourceIcon
70         [imageView_source setImage:inSourceIcon];
72 - (void)setDestinationName:(NSString *)inDestinationName
74         [textField_destination setStringValue:(inDestinationName ? inDestinationName : @"")];
76 - (void)setDestinationIcon:(NSImage *)inDestinationIcon
78         [imageView_destination setImage:inDestinationIcon];
81 #pragma mark File and its icon
82 - (void)setFileName:(NSString *)inFileName
84         [textField_fileName setStringValue:(inFileName ? 
85                                                                            inFileName : 
86                                                                            [AILocalizedString(@"Initializing transfer",nil) stringByAppendingEllipsis])];
88 - (void)setIconImage:(NSImage *)inIconImage
90         [button_icon setImage:inIconImage];
93 #pragma mark Progress
94 - (void)setProgressDoubleValue:(double)inPercent
96         [progressIndicator setDoubleValue:inPercent];
98 - (void)setProgressIndeterminate:(BOOL)flag
100         [progressIndicator setIndeterminate:flag];      
102 - (void)setProgressAnimation:(BOOL)flag
104         if (flag) {
105                 [progressIndicator startAnimation:self];
106         } else {
107                 [progressIndicator stopAnimation:self]; 
108         }
110 - (void)setProgressVisible:(BOOL)flag
112         if (flag != progressVisible) {
113                 progressVisible = flag;
114                 if (progressVisible) {
115                         //Redisplay the progress bar.  We never do this at present, so unimplemented for now.
116                 } else {
117                         NSRect  progressRect = [progressIndicator frame];
118                         NSRect  frame;
119                         float   distanceToMove = progressRect.size.height / 2;
120                         
121                         [progressIndicator setDisplayedWhenStopped:NO];
122                         [progressIndicator setIndeterminate:YES];
123                         [progressIndicator stopAnimation:self];
125                         //I don't trust setDisplayedWhenStopped... call me crazy.
126                         [progressIndicator setFrame:NSZeroRect];
127                         [progressIndicator setNeedsDisplay:YES];
128                         
129                         //Top objects moving down
130                         {
131                                 frame = [textField_fileName frame];
132                                 frame.origin.y -= distanceToMove;
133                                 //Don't let it be any further right than the progress bar used to be to avoid our buttons
134                                 frame.size.width = (progressRect.origin.x + progressRect.size.width) - frame.origin.x;
135                                 [textField_fileName setFrame:frame];
136                         }
137                         
138                         //Bottom objects moving up
139                         {
140                                 frame = [twiddle_details frame];
141                                 frame.origin.y += distanceToMove;
142                                 [twiddle_details setFrame:frame];
144                                 frame = [textField_detailsLabel frame];
145                                 frame.origin.y += distanceToMove;
146                                 [textField_detailsLabel setFrame:frame];
147                                                                 
148                                 frame = [box_transferStatusFrame frame];
149                                 frame.origin.y += distanceToMove;
150                                 //Don't let it be any further right than the progress bar used to be to avoid our buttons
151                                 frame.size.width = (progressRect.origin.x + progressRect.size.width) - frame.origin.x;
152                                 [box_transferStatusFrame setFrame:frame];
153                         }
154                 }
155                 
156                 //Display immediately
157                 [[self window] display];
158         }
161 - (void)setTransferBytesStatus:(NSString *)inTransferBytesStatus
162                            remainingStatus:(NSString *)inTransferRemainingStatus
163                                    speedStatus:(NSString *)inTransferSpeedStatus
165         [transferStatus release];
166         
167         if (inTransferBytesStatus && inTransferRemainingStatus) {
168                 transferStatus = [NSString stringWithFormat:@"%@ - %@",
169                         inTransferBytesStatus,
170                         inTransferRemainingStatus];
171         } else if (inTransferBytesStatus) {
172                 transferStatus = inTransferBytesStatus;
173         } else if (inTransferRemainingStatus) {
174                 transferStatus = inTransferRemainingStatus;             
175         } else {
176                 transferStatus = @"";
177         }
178         
179         [transferStatus retain];
180         
181 //      [textField_transferStatus setStringValue:transferStatus];
182         [self setNeedsDisplayInRect:[box_transferStatusFrame frame]];
183         [textField_rate setStringValue:(inTransferSpeedStatus ? inTransferSpeedStatus : @"")];
186 #pragma mark Details
187 //Sent when the details twiddle is clicked
188 - (IBAction)toggleDetails:(id)sender
190         NSRect  detailsFrame = [view_details frame];
191         NSRect  primaryControlsFrame = [box_primaryControls frame];
192         NSRect  oldFrame = [self frame];
193         NSRect  newFrame = oldFrame;
194         
195         showingDetails = !showingDetails;
197         if (showingDetails) {
198                 //Increase our height to make space
199                 newFrame.size.height += detailsFrame.size.height;
200                 newFrame.origin.y -= detailsFrame.size.height;
201                 [self setFrame:newFrame];
202                 
203                 //Move the box with our primary controls up
204                 primaryControlsFrame.origin.y += detailsFrame.size.height;
205                 [box_primaryControls setFrame:primaryControlsFrame];
206                         
207                 //Add the details subview
208                 [self addSubview:view_details];
209                 
210                 //Line up the details frame with the twiddle which revealed it
211                 detailsFrame.origin.x = [twiddle_details frame].origin.x;
212                 detailsFrame.origin.y = 0;
214                 [view_details setFrame:detailsFrame];
215         
216                 //Update the twiddle
217                 [twiddle_details setState:NSOnState];
218         } else {
219                 newFrame.size.height -= detailsFrame.size.height;
220                 newFrame.origin.y += detailsFrame.size.height;
222                 [self setFrame:newFrame];
223                 
224                 //Move the box with our primary controls back down
225                 primaryControlsFrame.origin.y -= detailsFrame.size.height;
226                 [box_primaryControls setFrame:primaryControlsFrame];
227                 
228                 [view_details removeFromSuperview];
229                 
230                 //Update the twiddle
231                 [twiddle_details setState:NSOffState];
232         }
233         
234         //Let the owner know our height changed so other rows can be adjusted accordingly
235         [owner fileTransferProgressView:self
236                                   heightChangedFrom:oldFrame.size.height
237                                                                  to:newFrame.size.height];
240 - (void)setShowsDetails:(BOOL)flag
242         if (showingDetails != flag) {
243                 [self toggleDetails:nil];       
244         }
247 - (void)setAllowsCancel:(BOOL)flag
249         [button_stopResume setEnabled:flag];
252 - (void)updateColors
254         NSColor *newColor;
255         
256         if (isSelected && [[self window] isKeyWindow]) {
257                 newColor = SELECTED_TEXT_COLOR;
258         } else {
259                 newColor = NORMAL_TEXT_COLOR;
260         }
262         [textField_rate setTextColor:newColor];
263         [textField_source setTextColor:newColor];
264         [textField_destination setTextColor:newColor];          
265         [textField_fileName setTextColor:newColor];
266         
267         [self updateButtonStopResume];
268         [self updateButtonReveal];
269         [self setNeedsDisplay:YES];
272 - (void)windowDidChangeKey:(NSNotification *)notification
274         [self updateColors];
277 - (void)viewDidMoveToWindow
279         [[NSNotificationCenter defaultCenter] removeObserver:self
280                                                                                                         name:NSWindowDidBecomeKeyNotification
281                                                                                                   object:nil];
282         [[NSNotificationCenter defaultCenter] removeObserver:self
283                                                                                                         name:NSWindowDidResignKeyNotification
284                                                                                                   object:nil];
285         if ([self window]) {
286                 [[NSNotificationCenter defaultCenter] addObserver:self
287                                                                                                  selector:@selector(windowDidChangeKey:)
288                                                                                                          name:NSWindowDidBecomeKeyNotification
289                                                                                                    object:[self window]];
290                 [[NSNotificationCenter defaultCenter] addObserver:self
291                                                                                                  selector:@selector(windowDidChangeKey:)
292                                                                                                          name:NSWindowDidResignKeyNotification
293                                                                                                    object:[self window]];
294         }
297 #pragma mark Selection
298 - (void)setIsHighlighted:(BOOL)flag
300         if (isSelected != flag) {
301                 isSelected = flag;
302                 
303                 [self updateColors];
304         }
307 - (void)updateButtonStopResume
309         if (isSelected) {
310                 [button_stopResume setImage:[NSImage imageNamed:(buttonStopResumeIsHovered ? @"FTProgressStopRollover_Selected" : @"FTProgressStop_Selected")
311                                                                                            forClass:[self class]]];
312                 
313                 [button_stopResume setAlternateImage:[NSImage imageNamed:@"FTProgressStopPressed_Selected" forClass:[self class]]];
314                 
315         } else {
316                 [button_stopResume setImage:[NSImage imageNamed:(buttonStopResumeIsHovered ? @"FTProgressStopRollover" : @"FTProgressStop")
317                                                                                            forClass:[self class]]];
318                 
319                 [button_stopResume setAlternateImage:[NSImage imageNamed:@"FTProgressStopPressed" forClass:[self class]]];
320         }
323 - (void)updateButtonReveal
325         if (isSelected) {
326                 [button_reveal setImage:[NSImage imageNamed:(buttonRevealIsHovered ? @"FTProgressRevealRollover_Selected" : @"FTProgressReveal_Selected")
327                                                                                    forClass:[self class]]];
328                 
329                 [button_reveal setAlternateImage:[NSImage imageNamed:@"FTProgressRevealPressed_Selected" forClass:[self class]]];
331         } else {
332                 [button_reveal setImage:[NSImage imageNamed:(buttonRevealIsHovered ? @"FTProgressRevealRollover" : @"FTProgressReveal")
333                                                                                    forClass:[self class]]];
335                 [button_reveal setAlternateImage:[NSImage imageNamed:@"FTProgressRevealPressed" forClass:[self class]]];
337         }
339 - (void)rolloverButton:(AIRolloverButton *)inButton mouseChangedToInsideButton:(BOOL)isInside
341         if (inButton == button_stopResume) {
342                 buttonStopResumeIsHovered = isInside;
343                 [self updateButtonStopResume];
344                 
345         } else if (inButton == button_reveal) {
346                 buttonRevealIsHovered = isInside;
347                 [self updateButtonReveal];
349         }
352 static NSDictionary     *transferStatusAttributes = nil;
353 static NSDictionary     *transferStatusSelectedAttributes = nil;
355 //Draw the transfer status after other views draw.  This lets us use custom drawing behavior including the
356 //NSLineBreakByTruncatingTail paragraph style.  We draw into a frame reserved for us by box_transferStatusFrame;
357 //this lets us not worry about autosizing and positioning since the view takes care of that for us.
358 - (void)drawRect:(NSRect)rect
360         [super drawRect:rect];
362         NSDictionary    *attributes;
363         NSRect                  primaryControlsRect = [box_primaryControls frame];
364         NSRect                  targetRect = [box_transferStatusFrame frame];
366         targetRect.origin.x += primaryControlsRect.origin.x;
367         targetRect.origin.y += primaryControlsRect.origin.y;
369         if (isSelected && [[self window] isKeyWindow]) {
370                 if (!transferStatusSelectedAttributes) {
371                         NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle styleWithAlignment:NSLeftTextAlignment
372                                                                                                                                                                         lineBreakMode:NSLineBreakByTruncatingTail];
373                         [paragraphStyle setMaximumLineHeight:[box_transferStatusFrame frame].size.height];
374                         
375                         transferStatusSelectedAttributes = [[NSDictionary dictionaryWithObjectsAndKeys:
376                                 paragraphStyle, NSParagraphStyleAttributeName,
377                                 [NSFont systemFontOfSize:9], NSFontAttributeName, 
378                                 SELECTED_TEXT_COLOR, NSForegroundColorAttributeName, nil] retain];
379                 }
380                 
381                 attributes = transferStatusSelectedAttributes;
382         } else {
383                 if (!transferStatusAttributes) {
384                         NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle styleWithAlignment:NSLeftTextAlignment
385                                                                                                                                                                         lineBreakMode:NSLineBreakByTruncatingTail];
386                         [paragraphStyle setMaximumLineHeight:[box_transferStatusFrame frame].size.height];
387                         
388                         transferStatusAttributes = [[NSDictionary dictionaryWithObjectsAndKeys:
389                                 paragraphStyle, NSParagraphStyleAttributeName,
390                                 [NSFont systemFontOfSize:9], NSFontAttributeName, 
391                                 TRANSFER_STATUS_COLOR, NSForegroundColorAttributeName, nil] retain];
392                 }
393                 
394                 attributes = transferStatusAttributes;
395         }
396         
397         [transferStatus drawInRect:targetRect
398                                 withAttributes:attributes];
401 #pragma mark Menu
402 - (NSMenu *)menuForEvent:(NSEvent *)inEvent
404         return [owner menuForEvent:inEvent];
407 #pragma mark Accessibility
409 - (id)accessibilityAttributeValue:(NSString *)attribute
411         id value;
412         
413         if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
414                 value = NSAccessibilityRowRole;
416         } else if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) {
417                 if (![progressIndicator isIndeterminate]) {
418                         //We are in the concrete phase of an active transfer
419                         value = [NSString stringWithFormat:
420                                          AILocalizedString(@"Transferring %@ from %@ to %@ at %@ : %@", "e.g: Transferring file.zip from Evan to Joel at 45 kb/sec : 5 minutes remaining. Keep the spaces around the colon."),
421                                          [textField_fileName stringValue],
422                                          [textField_source stringValue],
423                                          [textField_destination stringValue], 
424                                          [textField_rate stringValue],
425                                          (transferStatus ? transferStatus : @"")];
426                         
427                 } else {
428                         value = [NSString stringWithFormat:
429                                          AILocalizedString(@"Transfer of %@ from %@ to %@ : %@", "e.g: Transfer of file.zip from Evan to Joel : Upload complete. Keep the spaces around the colon"),
430                                          [textField_fileName stringValue],
431                                          [textField_source stringValue],
432                                          [textField_destination stringValue], 
433                                          (transferStatus ? transferStatus : @"")];
434                 }
436         } else if ([attribute isEqualToString:NSAccessibilityTitleAttribute]) {
437                 value = AILocalizedString(@"File transfer", nil);
438                 
439         } else if ([attribute isEqualToString:NSAccessibilityEnabledAttribute]) {
440                 //Never report as disabled, so we don't say 'dimmed' all the time
441                 value = [NSNumber numberWithBool:YES];
443         } else {
444                 value = [super accessibilityAttributeValue:attribute];
445         }
446         
447         return value;
450 @end