32a2ac90e0393b668b6292d6d654e8552f20ea34
[vlc.git] / modules / gui / macosx / misc.m
1 /*****************************************************************************
2  * misc.m: code not specific to vlc
3  *****************************************************************************
4  * Copyright (C) 2003-2015 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
8  *          Felix Paul K├╝hne <fkuehne at videolan dot org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #import "CompatibilityFixes.h"
26 #import "misc.h"
27 #import "VLCMain.h"                                          /* VLCApplication */
28 #import "VLCMainWindow.h"
29 #import "VLCMainMenu.h"
30 #import "VLCControlsBarCommon.h"
31 #import "VLCCoreInteraction.h"
32 #import <vlc_keys.h>
33
34 /*****************************************************************************
35  * VLCDragDropView
36  *****************************************************************************/
37
38 @implementation VLCDropDisabledImageView
39
40 - (void)awakeFromNib
41 {
42     [self unregisterDraggedTypes];
43 }
44
45 @end
46
47 /*****************************************************************************
48  * VLCDragDropView
49  *****************************************************************************/
50
51 @interface VLCDragDropView()
52 {
53     bool b_activeDragAndDrop;
54 }
55 @end
56
57 @implementation VLCDragDropView
58
59 - (id)initWithFrame:(NSRect)frame
60 {
61     self = [super initWithFrame:frame];
62     if (self) {
63         // default value
64         [self setDrawBorder:YES];
65     }
66
67     return self;
68 }
69
70 - (void)enablePlaylistItems
71 {
72     [self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, @"VLCPlaylistItemPboardType", nil]];
73 }
74
75 - (BOOL)mouseDownCanMoveWindow
76 {
77     return YES;
78 }
79
80 - (void)dealloc
81 {
82     [self unregisterDraggedTypes];
83 }
84
85 - (void)awakeFromNib
86 {
87     [self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
88 }
89
90 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
91 {
92     if ((NSDragOperationGeneric & [sender draggingSourceOperationMask]) == NSDragOperationGeneric) {
93         b_activeDragAndDrop = YES;
94         [self setNeedsDisplay:YES];
95
96         return NSDragOperationCopy;
97     }
98
99     return NSDragOperationNone;
100 }
101
102 - (void)draggingEnded:(id < NSDraggingInfo >)sender
103 {
104     b_activeDragAndDrop = NO;
105     [self setNeedsDisplay:YES];
106 }
107
108 - (void)draggingExited:(id < NSDraggingInfo >)sender
109 {
110     b_activeDragAndDrop = NO;
111     [self setNeedsDisplay:YES];
112 }
113
114 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
115 {
116     return YES;
117 }
118
119 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
120 {
121     BOOL b_returned;
122
123     if (_dropHandler && [_dropHandler respondsToSelector:@selector(performDragOperation:)])
124         b_returned = [_dropHandler performDragOperation:sender];
125     else // default
126         b_returned = [[VLCCoreInteraction sharedInstance] performDragOperation:sender];
127
128     [self setNeedsDisplay:YES];
129     return b_returned;
130 }
131
132 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
133 {
134     [self setNeedsDisplay:YES];
135 }
136
137 - (void)drawRect:(NSRect)dirtyRect
138 {
139     if ([self drawBorder] && b_activeDragAndDrop) {
140         NSRect frameRect = [self bounds];
141
142         [[NSColor selectedControlColor] set];
143         NSFrameRectWithWidthUsingOperation(frameRect, 2., NSCompositeSourceOver);
144     }
145
146     [super drawRect:dirtyRect];
147 }
148
149 @end
150
151
152 /*****************************************************************************
153  * MPSlider
154  *****************************************************************************/
155 @implementation MPSlider
156
157 void _drawKnobInRect(NSRect knobRect)
158 {
159     // Center knob in given rect
160     knobRect.origin.x += (int)((float)(knobRect.size.width - 7)/2.0);
161     knobRect.origin.y += (int)((float)(knobRect.size.height - 7)/2.0);
162
163     // Draw diamond
164     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 6, 1, 1), NSCompositeSourceOver);
165     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 5, 3, 1), NSCompositeSourceOver);
166     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 4, 5, 1), NSCompositeSourceOver);
167     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 0, knobRect.origin.y + 3, 7, 1), NSCompositeSourceOver);
168     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 1, knobRect.origin.y + 2, 5, 1), NSCompositeSourceOver);
169     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 2, knobRect.origin.y + 1, 3, 1), NSCompositeSourceOver);
170     NSRectFillUsingOperation(NSMakeRect(knobRect.origin.x + 3, knobRect.origin.y + 0, 1, 1), NSCompositeSourceOver);
171 }
172
173 void _drawFrameInRect(NSRect frameRect)
174 {
175     // Draw frame
176     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width, 1), NSCompositeSourceOver);
177     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y + frameRect.size.height-1, frameRect.size.width, 1), NSCompositeSourceOver);
178     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
179     NSRectFillUsingOperation(NSMakeRect(frameRect.origin.x+frameRect.size.width-1, frameRect.origin.y, 1, frameRect.size.height), NSCompositeSourceOver);
180 }
181
182 - (void)drawRect:(NSRect)rect
183 {
184     // Draw default to make sure the slider behaves correctly
185     [[NSGraphicsContext currentContext] saveGraphicsState];
186     NSRectClip(NSZeroRect);
187     [super drawRect:rect];
188     [[NSGraphicsContext currentContext] restoreGraphicsState];
189
190     // Full size
191     rect = [self bounds];
192     int diff = (int)(([[self cell] knobThickness] - 7.0)/2.0) - 1;
193     rect.origin.x += diff-1;
194     rect.origin.y += diff;
195     rect.size.width -= 2*diff-2;
196     rect.size.height -= 2*diff;
197
198     // Draw dark
199     NSRect knobRect = [[self cell] knobRectFlipped:NO];
200     [[[NSColor blackColor] colorWithAlphaComponent:0.6] set];
201     _drawFrameInRect(rect);
202     _drawKnobInRect(knobRect);
203
204     // Draw shadow
205     [[[NSColor blackColor] colorWithAlphaComponent:0.1] set];
206     rect.origin.x++;
207     rect.origin.y++;
208     knobRect.origin.x++;
209     knobRect.origin.y++;
210     _drawFrameInRect(rect);
211     _drawKnobInRect(knobRect);
212 }
213
214 @end
215
216 /*****************************************************************************
217  * ProgressView
218  *****************************************************************************/
219
220 @implementation VLCProgressView : NSView
221
222 - (void)scrollWheel:(NSEvent *)o_event
223 {
224     BOOL b_forward = NO;
225     CGFloat f_deltaY = [o_event deltaY];
226     CGFloat f_deltaX = [o_event deltaX];
227
228     if ([o_event isDirectionInvertedFromDevice])
229         f_deltaX = -f_deltaX; // optimisation, actually double invertion of f_deltaY here
230     else
231         f_deltaY = -f_deltaY;
232
233     // positive for left / down, negative otherwise
234     CGFloat f_delta = f_deltaX + f_deltaY;
235     CGFloat f_abs;
236     int i_vlckey;
237
238     if (f_delta > 0.0f)
239         f_abs = f_delta;
240     else {
241         b_forward = YES;
242         f_abs = -f_delta;
243     }
244
245     for (NSUInteger i = 0; i < (int)(f_abs/4.+1.) && f_abs > 0.05 ; i++) {
246         if (b_forward)
247             [[VLCCoreInteraction sharedInstance] forwardExtraShort];
248         else
249             [[VLCCoreInteraction sharedInstance] backwardExtraShort];
250     }
251 }
252
253 - (BOOL)acceptsFirstResponder
254 {
255     return YES;
256 }
257
258 @end
259
260 /*****************************************************************************
261  * TimeLineSlider
262  *****************************************************************************/
263
264 @interface TimeLineSlider()
265 {
266     NSImage *o_knob_img;
267     NSRect img_rect;
268     BOOL b_dark;
269 }
270 @end
271
272 @implementation TimeLineSlider
273
274 - (void)awakeFromNib
275 {
276     if (config_GetInt( getIntf(), "macosx-interfacestyle" )) {
277         o_knob_img = imageFromRes(@"progression-knob_dark");
278         b_dark = YES;
279     } else {
280         o_knob_img = imageFromRes(@"progression-knob");
281         b_dark = NO;
282     }
283     img_rect.size = [o_knob_img size];
284     img_rect.origin.x = img_rect.origin.y = 0;
285 }
286
287 - (CGFloat)knobPosition
288 {
289     NSRect knobRect = [[self cell] knobRectFlipped:NO];
290     knobRect.origin.x += knobRect.size.width / 2;
291     return knobRect.origin.x;
292 }
293
294 - (void)drawKnobInRect:(NSRect)knobRect
295 {
296     knobRect.origin.x += (knobRect.size.width - img_rect.size.width) / 2;
297     knobRect.size.width = img_rect.size.width;
298     knobRect.size.height = img_rect.size.height;
299     [o_knob_img drawInRect:knobRect fromRect:img_rect operation:NSCompositeSourceOver fraction:1];
300 }
301
302 - (void)drawRect:(NSRect)rect
303 {
304     [[(VLCVideoWindowCommon *)[self window] controlsBar] drawFancyGradientEffectForTimeSlider];
305     msleep(10000); //wait for the gradient to draw completely
306
307     /* Draw default to make sure the slider behaves correctly */
308     [[NSGraphicsContext currentContext] saveGraphicsState];
309     NSRectClip(NSZeroRect);
310     [super drawRect:rect];
311     [[NSGraphicsContext currentContext] restoreGraphicsState];
312
313     NSRect knobRect = [[self cell] knobRectFlipped:NO];
314     knobRect.origin.y+=1;
315     [self drawKnobInRect: knobRect];
316 }
317
318 @end
319
320 /*****************************************************************************
321  * VLCVolumeSliderCommon
322  *****************************************************************************/
323
324 @implementation VLCVolumeSliderCommon : NSSlider
325
326 - (void)scrollWheel:(NSEvent *)o_event
327 {
328     BOOL b_up = NO;
329     CGFloat f_deltaY = [o_event deltaY];
330     CGFloat f_deltaX = [o_event deltaX];
331
332     if ([o_event isDirectionInvertedFromDevice])
333         f_deltaX = -f_deltaX; // optimisation, actually double invertion of f_deltaY here
334     else
335         f_deltaY = -f_deltaY;
336
337     // positive for left / down, negative otherwise
338     CGFloat f_delta = f_deltaX + f_deltaY;
339     CGFloat f_abs;
340
341     if (f_delta > 0.0f)
342         f_abs = f_delta;
343     else {
344         b_up = YES;
345         f_abs = -f_delta;
346     }
347
348     for (NSUInteger i = 0; i < (int)(f_abs/4.+1.) && f_abs > 0.05 ; i++) {
349         if (b_up)
350             [[VLCCoreInteraction sharedInstance] volumeUp];
351         else
352             [[VLCCoreInteraction sharedInstance] volumeDown];
353     }
354 }
355
356 - (void)drawFullVolumeMarker
357 {
358     CGFloat maxAudioVol = self.maxValue / AOUT_VOLUME_DEFAULT;
359     if (maxAudioVol < 1.)
360         return;
361
362     NSColor *drawingColor;
363     // for bright artwork, a black color is used and vice versa
364     if (_usesBrightArtwork)
365         drawingColor = [[NSColor blackColor] colorWithAlphaComponent:.4];
366     else
367         drawingColor = [[NSColor whiteColor] colorWithAlphaComponent:.4];
368
369     NSBezierPath* bezierPath = [NSBezierPath bezierPath];
370     [self drawFullVolBezierPath:bezierPath];
371     [bezierPath closePath];
372
373     bezierPath.lineWidth = 1.;
374     [drawingColor setStroke];
375     [bezierPath stroke];
376 }
377
378 - (CGFloat)fullVolumePos
379 {
380     CGFloat maxAudioVol = self.maxValue / AOUT_VOLUME_DEFAULT;
381     CGFloat sliderRange = [self frame].size.width - [self knobThickness];
382     CGFloat sliderOrigin = [self knobThickness] / 2.;
383
384     return 1. / maxAudioVol * sliderRange + sliderOrigin;
385 }
386
387 - (void)drawFullVolBezierPath:(NSBezierPath*)bezierPath
388 {
389     CGFloat fullVolPos = [self fullVolumePos];
390     [bezierPath moveToPoint:NSMakePoint(fullVolPos, [self frame].size.height - 3.)];
391     [bezierPath lineToPoint:NSMakePoint(fullVolPos, 2.)];
392 }
393
394 @end
395
396 @implementation VolumeSliderCell
397
398 - (BOOL)continueTracking:(NSPoint)lastPoint at:(NSPoint)currentPoint inView:(NSView *)controlView
399 {
400     VLCVolumeSliderCommon *o_slider = (VLCVolumeSliderCommon *)controlView;
401     CGFloat fullVolumePos = [o_slider fullVolumePos] + 2.;
402
403     CGPoint snapToPoint = currentPoint;
404     if (ABS(fullVolumePos - currentPoint.x) <= 4.)
405         snapToPoint.x = fullVolumePos;
406
407     return [super continueTracking:lastPoint at:snapToPoint inView:controlView];
408 }
409
410 @end
411
412 /*****************************************************************************
413  * ITSlider
414  *****************************************************************************/
415
416 @interface ITSlider()
417 {
418     NSImage *img;
419     NSRect image_rect;
420 }
421 @end
422
423 @implementation ITSlider
424
425 - (void)awakeFromNib
426 {
427     BOOL b_dark = config_GetInt( getIntf(), "macosx-interfacestyle" );
428     if (b_dark)
429         img = imageFromRes(@"volume-slider-knob_dark");
430     else
431         img = imageFromRes(@"volume-slider-knob");
432
433     image_rect.size = [img size];
434     image_rect.origin.x = 0;
435
436     if (b_dark)
437         image_rect.origin.y = -1;
438     else
439         image_rect.origin.y = 0;
440 }
441
442 - (void)drawKnobInRect:(NSRect)knobRect
443 {
444     knobRect.origin.x += (knobRect.size.width - image_rect.size.width) / 2;
445     knobRect.size.width = image_rect.size.width;
446     knobRect.size.height = image_rect.size.height;
447     [img drawInRect:knobRect fromRect:image_rect operation:NSCompositeSourceOver fraction:1];
448 }
449
450 - (void)drawRect:(NSRect)rect
451 {
452     /* Draw default to make sure the slider behaves correctly */
453     [[NSGraphicsContext currentContext] saveGraphicsState];
454     NSRectClip(NSZeroRect);
455     [super drawRect:rect];
456     [[NSGraphicsContext currentContext] restoreGraphicsState];
457
458     [self drawFullVolumeMarker];
459
460     NSRect knobRect = [[self cell] knobRectFlipped:NO];
461     knobRect.origin.y+=2;
462     [self drawKnobInRect: knobRect];
463 }
464
465 @end
466
467 /*****************************************************************************
468  * VLCTimeField implementation
469  *****************************************************************************
470  * we need this to catch our click-event in the controller window
471  *****************************************************************************/
472
473 @interface VLCTimeField()
474 {
475     NSShadow * o_string_shadow;
476     NSTextAlignment textAlignment;
477
478     NSString *o_remaining_identifier;
479     BOOL b_time_remaining;
480 }
481 @end
482
483 @implementation VLCTimeField
484 + (void)initialize
485 {
486     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
487     NSDictionary *appDefaults = [NSDictionary dictionaryWithObjectsAndKeys:
488                                  @"NO", @"DisplayTimeAsTimeRemaining",
489                                  @"YES", @"DisplayFullscreenTimeAsTimeRemaining",
490                                  nil];
491
492     [defaults registerDefaults:appDefaults];
493 }
494
495 - (void)setRemainingIdentifier:(NSString *)o_string
496 {
497     o_remaining_identifier = o_string;
498     b_time_remaining = [[NSUserDefaults standardUserDefaults] boolForKey:o_remaining_identifier];
499 }
500
501 - (void)setAlignment:(NSTextAlignment)alignment
502 {
503     textAlignment = alignment;
504     [self setStringValue:[self stringValue]];
505 }
506
507 - (void)setStringValue:(NSString *)string
508 {
509     if (!o_string_shadow) {
510         o_string_shadow = [[NSShadow alloc] init];
511         [o_string_shadow setShadowColor: [NSColor colorWithCalibratedWhite:1.0 alpha:0.5]];
512         [o_string_shadow setShadowOffset:NSMakeSize(0.0, -1.0)];
513         [o_string_shadow setShadowBlurRadius:0.0];
514     }
515
516     NSMutableAttributedString *o_attributed_string = [[NSMutableAttributedString alloc] initWithString:string attributes: nil];
517     NSUInteger i_stringLength = [string length];
518
519     [o_attributed_string addAttribute: NSShadowAttributeName value: o_string_shadow range: NSMakeRange(0, i_stringLength)];
520     [o_attributed_string setAlignment: textAlignment range: NSMakeRange(0, i_stringLength)];
521     [self setAttributedStringValue: o_attributed_string];
522 }
523
524 - (void)mouseDown: (NSEvent *)ourEvent
525 {
526     if ( [ourEvent clickCount] > 1 )
527         [[[VLCMain sharedInstance] mainMenu] goToSpecificTime: nil];
528     else
529     {
530         if (o_remaining_identifier) {
531             b_time_remaining = [[NSUserDefaults standardUserDefaults] boolForKey:o_remaining_identifier];
532             b_time_remaining = !b_time_remaining;
533             [[NSUserDefaults standardUserDefaults] setObject:(b_time_remaining ? @"YES" : @"NO") forKey:o_remaining_identifier];
534         } else {
535             b_time_remaining = !b_time_remaining;
536         }
537     }
538
539     [[self nextResponder] mouseDown:ourEvent];
540 }
541
542 - (BOOL)timeRemaining
543 {
544     if (o_remaining_identifier)
545         return [[NSUserDefaults standardUserDefaults] boolForKey:o_remaining_identifier];
546     else
547         return b_time_remaining;
548 }
549
550 @end
551
552 /*****************************************************************************
553  * VLCMainWindowSplitView implementation
554  * comment 1 + 2 taken from NSSplitView.h (10.7 SDK)
555  *****************************************************************************/
556 @implementation VLCMainWindowSplitView : NSSplitView
557 /* Return the color of the dividers that the split view is drawing between subviews. The default implementation of this method returns [NSColor clearColor] for the thick divider style. It will also return [NSColor clearColor] for the thin divider style when the split view is in a textured window. All other thin dividers are drawn with a color that looks good between two white panes. You can override this method to change the color of dividers.
558  */
559 - (NSColor *)dividerColor
560 {
561     return [NSColor colorWithCalibratedRed:.60 green:.60 blue:.60 alpha:1.];
562 }
563
564 /* Return the thickness of the dividers that the split view is drawing between subviews. The default implementation returns a value that depends on the divider style. You can override this method to change the size of dividers.
565  */
566 - (CGFloat)dividerThickness
567 {
568     return 1.0;
569 }
570 @end
571
572 /*****************************************************************************
573  * VLCThreePartImageView interface
574  *****************************************************************************/
575
576 @interface VLCThreePartImageView()
577 {
578     NSImage *_left_img;
579     NSImage *_middle_img;
580     NSImage *_right_img;
581 }
582 @end
583
584 @implementation VLCThreePartImageView
585
586 - (void)setImagesLeft:(NSImage *)left middle: (NSImage *)middle right:(NSImage *)right
587 {
588     _left_img = left;
589     _middle_img = middle;
590     _right_img = right;
591 }
592
593 - (void)drawRect:(NSRect)rect
594 {
595     NSRect bnds = [self bounds];
596     NSDrawThreePartImage( bnds, _left_img, _middle_img, _right_img, NO, NSCompositeSourceOver, 1, NO );
597 }
598
599 @end
600
601 @interface PositionFormatter()
602 {
603     NSCharacterSet *o_forbidden_characters;
604 }
605 @end
606
607 @implementation PositionFormatter
608
609 - (id)init
610 {
611     self = [super init];
612     NSMutableCharacterSet *nonNumbers = [[[NSCharacterSet decimalDigitCharacterSet] invertedSet] mutableCopy];
613     [nonNumbers removeCharactersInString:@"-:"];
614     o_forbidden_characters = [nonNumbers copy];
615
616     return self;
617 }
618
619 - (NSString*)stringForObjectValue:(id)obj
620 {
621     if([obj isKindOfClass:[NSString class]])
622         return obj;
623     if([obj isKindOfClass:[NSNumber class]])
624         return [obj stringValue];
625
626     return nil;
627 }
628
629 - (BOOL)getObjectValue:(id*)obj forString:(NSString*)string errorDescription:(NSString**)error
630 {
631     *obj = [string copy];
632     return YES;
633 }
634
635 - (BOOL)isPartialStringValid:(NSString*)partialString newEditingString:(NSString**)newString errorDescription:(NSString**)error
636 {
637     if ([partialString rangeOfCharacterFromSet:o_forbidden_characters options:NSLiteralSearch].location != NSNotFound) {
638         return NO;
639     } else {
640         return YES;
641     }
642 }
643
644 @end
645
646 @implementation NSView (EnableSubviews)
647
648 - (void)enableSubviews:(BOOL)b_enable
649 {
650     for (NSView *o_view in [self subviews]) {
651         [o_view enableSubviews:b_enable];
652
653         // enable NSControl
654         if ([o_view respondsToSelector:@selector(setEnabled:)]) {
655             [(NSControl *)o_view setEnabled:b_enable];
656         }
657         // also "enable / disable" text views
658         if ([o_view respondsToSelector:@selector(setTextColor:)]) {
659             if (b_enable == NO) {
660                 [(NSTextField *)o_view setTextColor:[NSColor disabledControlTextColor]];
661             } else {
662                 [(NSTextField *)o_view setTextColor:[NSColor controlTextColor]];
663             }
664         }
665
666     }
667 }
668
669 @end
670
671 /*****************************************************************************
672  * VLCByteCountFormatter addition
673  *****************************************************************************/
674
675 @implementation VLCByteCountFormatter
676
677 + (NSString *)stringFromByteCount:(long long)byteCount countStyle:(NSByteCountFormatterCountStyle)countStyle
678 {
679     // Use native implementation on >= mountain lion
680     Class byteFormatterClass = NSClassFromString(@"NSByteCountFormatter");
681     if (byteFormatterClass && [byteFormatterClass respondsToSelector:@selector(stringFromByteCount:countStyle:)]) {
682         return [byteFormatterClass stringFromByteCount:byteCount countStyle:NSByteCountFormatterCountStyleFile];
683     }
684
685     float devider = 0.;
686     float returnValue = 0.;
687     NSString *suffix;
688
689     NSNumberFormatter *theFormatter = [[NSNumberFormatter alloc] init];
690     [theFormatter setLocale:[NSLocale currentLocale]];
691     [theFormatter setAllowsFloats:YES];
692
693     NSString *returnString = @"";
694
695     if (countStyle != NSByteCountFormatterCountStyleDecimal)
696         devider = 1024.;
697     else
698         devider = 1000.;
699
700     if (byteCount < 1000) {
701         returnValue = byteCount;
702         suffix = _NS("B");
703         [theFormatter setMaximumFractionDigits:0];
704         goto end;
705     }
706
707     if (byteCount < 1000000) {
708         returnValue = byteCount / devider;
709         suffix = _NS("KB");
710         [theFormatter setMaximumFractionDigits:0];
711         goto end;
712     }
713
714     if (byteCount < 1000000000) {
715         returnValue = byteCount / devider / devider;
716         suffix = _NS("MB");
717         [theFormatter setMaximumFractionDigits:1];
718         goto end;
719     }
720
721     [theFormatter setMaximumFractionDigits:2];
722     if (byteCount < 1000000000000) {
723         returnValue = byteCount / devider / devider / devider;
724         suffix = _NS("GB");
725         goto end;
726     }
727
728     returnValue = byteCount / devider / devider / devider / devider;
729     suffix = _NS("TB");
730
731 end:
732     returnString = [NSString stringWithFormat:@"%@ %@", [theFormatter stringFromNumber:[NSNumber numberWithFloat:returnValue]], suffix];
733
734     return returnString;
735 }
736
737 @end