source: trunk/macosx/NSStringAdditions.m @ 14045

Last change on this file since 14045 was 14045, checked in by livings124, 9 years ago

Simplify betterComponentsSeparatedByCharactersInSet:

  • Property svn:keywords set to Date Rev Author Id
File size: 11.6 KB
Line 
1/******************************************************************************
2 * $Id: NSStringAdditions.m 14045 2013-03-07 23:55:31Z livings124 $
3 *
4 * Copyright (c) 2005-2012 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#import "NSApplicationAdditions.h"
26#import "NSStringAdditions.h"
27
28#import "transmission.h"
29#import "utils.h"
30
31@interface NSString (Private)
32
33+ (NSString *) stringForFileSizeLion: (uint64_t) size showUnitUnless: (NSString *) notAllowedUnit unitsUsed: (NSString **) unitUsed;
34
35+ (NSString *) stringForSpeed: (CGFloat) speed kb: (NSString *) kb mb: (NSString *) mb gb: (NSString *) gb;
36
37@end
38
39@implementation NSString (NSStringAdditions)
40
41+ (NSString *) ellipsis
42{
43        return [NSString stringWithUTF8String: "\xE2\x80\xA6"];
44}
45
46- (NSString *) stringByAppendingEllipsis
47{
48        return [self stringByAppendingString: [NSString ellipsis]];
49}
50
51#warning use localizedStringWithFormat: directly when 10.8-only
52+ (NSString *) formattedUInteger: (NSUInteger) value
53{
54    if ([NSApp isOnMountainLionOrBetter])
55        return [NSString localizedStringWithFormat: @"%lu", value];
56    else
57    {
58        static NSNumberFormatter * numberFormatter;
59        static dispatch_once_t onceToken;
60        dispatch_once(&onceToken, ^{
61            numberFormatter = [[NSNumberFormatter alloc] init];
62            [numberFormatter setNumberStyle: NSNumberFormatterDecimalStyle];
63            [numberFormatter setMaximumFractionDigits: 0];
64        });
65       
66        return [numberFormatter stringFromNumber: [NSNumber numberWithUnsignedInteger: value]];
67    }
68}
69
70#warning should we take long long instead?
71+ (NSString *) stringForFileSize: (uint64_t) size
72{
73    if ([NSApp isOnMountainLionOrBetter])
74        return [NSByteCountFormatterMtLion stringFromByteCount: size countStyle: NSByteCountFormatterCountStyleFile];
75    else
76        return [self stringForFileSizeLion: size showUnitUnless: nil unitsUsed: nil];
77}
78
79#warning should we take long long instead?
80+ (NSString *) stringForFilePartialSize: (uint64_t) partialSize fullSize: (uint64_t) fullSize
81{
82    NSString * partialString, * fullString;
83    if ([NSApp isOnMountainLionOrBetter])
84    {
85        NSByteCountFormatter * fileSizeFormatter = [[NSByteCountFormatterMtLion alloc] init];
86       
87        fullString = [fileSizeFormatter stringFromByteCount: fullSize];
88       
89        //figure out the magniture of the two, since we can't rely on comparing the units because of localization and pluralization issues (for example, "1 byte of 2 bytes")
90        BOOL partialUnitsSame;
91        if (partialSize == 0)
92            partialUnitsSame = YES; //we want to just show "0" when we have no partial data, so always set to the same units
93        else
94        {
95            const unsigned int magnitudePartial = log(partialSize)/log(1000);
96            const unsigned int magnitudeFull = fullSize < 1000 ? 0 : log(fullSize)/log(1000); //we have to catch 0 with a special case, so might as well avoid the math for all of magnitude 0
97            partialUnitsSame = magnitudePartial == magnitudeFull;
98        }
99       
100        [fileSizeFormatter setIncludesUnit: !partialUnitsSame];
101        partialString = [fileSizeFormatter stringFromByteCount: partialSize];
102       
103        [fileSizeFormatter release];
104    }
105    else
106    {
107        NSString * units;
108        fullString = [self stringForFileSizeLion: fullSize showUnitUnless: nil unitsUsed: &units];
109        partialString = [self stringForFileSizeLion: partialSize showUnitUnless: units unitsUsed: nil];
110    }
111   
112    return [NSString stringWithFormat: NSLocalizedString(@"%@ of %@", "file size string"), partialString, fullString];
113}
114
115+ (NSString *) stringForSpeed: (CGFloat) speed
116{
117    return [self stringForSpeed: speed
118                kb: NSLocalizedString(@"KB/s", "Transfer speed (kilobytes per second)")
119                mb: NSLocalizedString(@"MB/s", "Transfer speed (megabytes per second)")
120                gb: NSLocalizedString(@"GB/s", "Transfer speed (gigabytes per second)")];
121}
122
123+ (NSString *) stringForSpeedAbbrev: (CGFloat) speed
124{
125    return [self stringForSpeed: speed kb: @"K" mb: @"M" gb: @"G"];
126}
127
128+ (NSString *) stringForRatio: (CGFloat) ratio
129{
130    //N/A is different than libtransmission's
131    if ((int)ratio == TR_RATIO_NA)
132        return NSLocalizedString(@"N/A", "No Ratio");
133    else if ((int)ratio == TR_RATIO_INF)
134        return [NSString stringWithUTF8String: "\xE2\x88\x9E"];
135    else
136    {
137        if (ratio < 10.0)
138            return [NSString localizedStringWithFormat: @"%.2f", tr_truncd(ratio, 2)];
139        else if (ratio < 100.0)
140            return [NSString localizedStringWithFormat: @"%.1f", tr_truncd(ratio, 1)];
141        else
142            return [NSString localizedStringWithFormat: @"%.0f", tr_truncd(ratio, 0)];
143    }
144}
145
146+ (NSString *) percentString: (CGFloat) progress longDecimals: (BOOL) longDecimals
147{
148    if (progress >= 1.0)
149        return [NSString localizedStringWithFormat: @"%d%%", 100];
150    else if (longDecimals)
151        return [NSString localizedStringWithFormat: @"%.2f%%", tr_truncd(progress * 100.0, 2)];
152    else
153        return [NSString localizedStringWithFormat: @"%.1f%%", tr_truncd(progress * 100.0, 1)];
154}
155
156+ (NSString *) timeString: (uint64_t) seconds showSeconds: (BOOL) showSeconds
157{
158    return [NSString timeString: seconds showSeconds: showSeconds maxFields: NSUIntegerMax];
159}
160
161+ (NSString *) timeString: (uint64_t) seconds showSeconds: (BOOL) showSeconds maxFields: (NSUInteger) max
162{
163    NSParameterAssert(max > 0);
164   
165    NSMutableArray * timeArray = [NSMutableArray arrayWithCapacity: MIN(max, 5)];
166    NSUInteger remaining = seconds; //causes problems for some users when it's a uint64_t
167   
168    if (seconds >= 31557600) //official amount of seconds in one year
169    {
170        const NSUInteger years = remaining / 31557600;
171        if (years == 1)
172            [timeArray addObject: NSLocalizedString(@"1 year", "time string")];
173        else
174            [timeArray addObject: [NSString stringWithFormat: NSLocalizedString(@"%u years", "time string"), years]];
175        remaining %= 31557600;
176        --max;
177    }
178    if (max > 0 && seconds >= (24 * 60 * 60))
179    {
180        const NSUInteger days = remaining / (24 * 60 * 60);
181        if (days == 1)
182            [timeArray addObject: NSLocalizedString(@"1 day", "time string")];
183        else
184            [timeArray addObject: [NSString stringWithFormat: NSLocalizedString(@"%u days", "time string"), days]];
185        remaining %= (24 * 60 * 60);
186        --max;
187    }
188    if (max > 0 && seconds >= (60 * 60))
189    {
190        [timeArray addObject: [NSString stringWithFormat: NSLocalizedString(@"%u hr", "time string"), remaining / (60 * 60)]];
191        remaining %= (60 * 60);
192        --max;
193    }
194    if (max > 0 && (!showSeconds || seconds >= 60))
195    {
196        [timeArray addObject: [NSString stringWithFormat: NSLocalizedString(@"%u min", "time string"), remaining / 60]];
197        remaining %= 60;
198        --max;
199    }
200    if (max > 0 && showSeconds)
201        [timeArray addObject: [NSString stringWithFormat: NSLocalizedString(@"%u sec", "time string"), remaining]];
202   
203    return [timeArray componentsJoinedByString: @" "];
204}
205
206- (NSComparisonResult) compareNumeric: (NSString *) string
207{
208    const NSStringCompareOptions comparisonOptions = NSNumericSearch | NSForcedOrderingSearch;
209    return [self compare: string options: comparisonOptions range: NSMakeRange(0, [self length]) locale: [NSLocale currentLocale]];
210}
211
212- (NSArray *) betterComponentsSeparatedByCharactersInSet: (NSCharacterSet *) separators
213{
214    NSMutableArray * components = [NSMutableArray array];
215   
216    NSCharacterSet * includededCharSet = [separators invertedSet];
217    NSUInteger index = 0;
218    const NSUInteger fullLength = [self length];
219    do
220    {
221        const NSUInteger start = [self rangeOfCharacterFromSet: includededCharSet options: 0 range: NSMakeRange(index, fullLength - index)].location;
222        if (start == NSNotFound)
223            break;
224       
225        const NSRange endRange = [self rangeOfCharacterFromSet: separators options: 0 range: NSMakeRange(start, fullLength - start)];
226        if (endRange.location == NSNotFound)
227        {
228            [components addObject: [self substringFromIndex: start]];
229            break;
230        }
231       
232        [components addObject: [self substringWithRange: NSMakeRange(start, endRange.location - start)]];
233       
234        index = NSMaxRange(endRange);
235    }
236    while (YES);
237   
238    return components;
239}
240
241@end
242
243@implementation NSString (Private)
244
245+ (NSString *) stringForFileSizeLion: (uint64_t) size showUnitUnless: (NSString *) notAllowedUnit unitsUsed: (NSString **) unitUsed
246{
247    double convertedSize;
248    NSString * unit;
249    NSUInteger decimals;
250    if (size < pow(1000, 2))
251    {
252        convertedSize = size / 1000.0;
253        unit = NSLocalizedString(@"KB", "File size - kilobytes");
254        decimals = convertedSize >= 10.0 ? 0 : 1;
255    }
256    else if (size < pow(1000, 3))
257    {
258        convertedSize = size / powf(1000.0, 2);
259        unit = NSLocalizedString(@"MB", "File size - megabytes");
260        decimals = 1;
261    }
262    else if (size < pow(1000, 4))
263    {
264        convertedSize = size / powf(1000.0, 3);
265        unit = NSLocalizedString(@"GB", "File size - gigabytes");
266        decimals = 2;
267    }
268    else
269    {
270        convertedSize = size / powf(1000.0, 4);
271        unit = NSLocalizedString(@"TB", "File size - terabytes");
272        decimals = 2;
273    }
274   
275    //match Finder's behavior
276    NSNumberFormatter * numberFormatter = [[NSNumberFormatter alloc] init];
277    [numberFormatter setNumberStyle: NSNumberFormatterDecimalStyle];
278    [numberFormatter setMinimumFractionDigits: 0];
279    [numberFormatter setMaximumFractionDigits: decimals];
280   
281    NSString * fileSizeString = [numberFormatter stringFromNumber: [NSNumber numberWithFloat: convertedSize]];
282    [numberFormatter release];
283   
284    if (!notAllowedUnit || ![unit isEqualToString: notAllowedUnit])
285        fileSizeString = [fileSizeString stringByAppendingFormat: @" %@", unit];
286   
287    if (unitUsed)
288        *unitUsed = unit;
289   
290    return fileSizeString;
291}
292
293+ (NSString *) stringForSpeed: (CGFloat) speed kb: (NSString *) kb mb: (NSString *) mb gb: (NSString *) gb
294{
295    if (speed <= 999.95) //0.0 KB/s to 999.9 KB/s
296        return [NSString localizedStringWithFormat: @"%.1f %@", speed, kb];
297   
298    speed /= 1000.0;
299   
300    if (speed <= 99.995) //1.00 MB/s to 99.99 MB/s
301        return [NSString localizedStringWithFormat: @"%.2f %@", speed, mb];
302    else if (speed <= 999.95) //100.0 MB/s to 999.9 MB/s
303        return [NSString localizedStringWithFormat: @"%.1f %@", speed, mb];
304    else //insane speeds
305        return [NSString localizedStringWithFormat: @"%.2f %@", (speed / 1000.0), gb];
306}
307
308@end
Note: See TracBrowser for help on using the repository browser.