source: trunk/macosx/GroupsController.m @ 7389

Last change on this file since 7389 was 7389, checked in by livings124, 12 years ago

ensure auto-grouping will never be used on Tiger

  • Property svn:keywords set to Date Rev Author Id
File size: 17.6 KB
Line 
1/******************************************************************************
2 * $Id: GroupsController.m 7389 2008-12-14 16:50:41Z livings124 $
3 *
4 * Copyright (c) 2007-2008 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 "GroupsController.h"
26#import "CTGradient.h"
27#import "NSBezierPathAdditions.h"
28#import "NSApplicationAdditions.h"
29
30#define ICON_WIDTH 16.0
31#define ICON_WIDTH_SMALL 12.0
32
33@interface GroupsController (Private)
34
35- (void) saveGroups;
36
37- (NSImage *) imageForGroup: (NSMutableDictionary *) dict;
38
39- (BOOL) torrent: (Torrent *) torrent doesMatchRulesForGroupAtIndex: (NSInteger) index;
40
41@end
42
43@implementation GroupsController
44
45GroupsController * fGroupsInstance = nil;
46+ (GroupsController *) groups
47{
48    if (!fGroupsInstance)
49        fGroupsInstance = [[GroupsController alloc] init];
50    return fGroupsInstance;
51}
52
53- (id) init
54{
55    if ((self = [super init]))
56    {
57        NSData * data;
58        if ((data = [[NSUserDefaults standardUserDefaults] dataForKey: @"Groups"]))
59            fGroups = [[NSUnarchiver unarchiveObjectWithData: data] retain];
60        else
61        {
62            //default groups
63            NSMutableDictionary * red = [NSMutableDictionary dictionaryWithObjectsAndKeys:
64                                            [NSColor redColor], @"Color",
65                                            NSLocalizedString(@"Red", "Groups -> Name"), @"Name",
66                                            [NSNumber numberWithInt: 0], @"Index", nil];
67           
68            NSMutableDictionary * orange = [NSMutableDictionary dictionaryWithObjectsAndKeys:
69                                            [NSColor orangeColor], @"Color",
70                                            NSLocalizedString(@"Orange", "Groups -> Name"), @"Name",
71                                            [NSNumber numberWithInt: 1], @"Index", nil];
72           
73            NSMutableDictionary * yellow = [NSMutableDictionary dictionaryWithObjectsAndKeys:
74                                            [NSColor yellowColor], @"Color",
75                                            NSLocalizedString(@"Yellow", "Groups -> Name"), @"Name",
76                                            [NSNumber numberWithInt: 2], @"Index", nil];
77           
78            NSMutableDictionary * green = [NSMutableDictionary dictionaryWithObjectsAndKeys:
79                                            [NSColor greenColor], @"Color",
80                                            NSLocalizedString(@"Green", "Groups -> Name"), @"Name",
81                                            [NSNumber numberWithInt: 3], @"Index", nil];
82           
83            NSMutableDictionary * blue = [NSMutableDictionary dictionaryWithObjectsAndKeys:
84                                            [NSColor blueColor], @"Color",
85                                            NSLocalizedString(@"Blue", "Groups -> Name"), @"Name",
86                                            [NSNumber numberWithInt: 4], @"Index", nil];
87           
88            NSMutableDictionary * purple = [NSMutableDictionary dictionaryWithObjectsAndKeys:
89                                            [NSColor purpleColor], @"Color",
90                                            NSLocalizedString(@"Purple", "Groups -> Name"), @"Name",
91                                            [NSNumber numberWithInt: 5], @"Index", nil];
92           
93            NSMutableDictionary * gray = [NSMutableDictionary dictionaryWithObjectsAndKeys:
94                                            [NSColor grayColor], @"Color",
95                                            NSLocalizedString(@"Gray", "Groups -> Name"), @"Name",
96                                            [NSNumber numberWithInt: 6], @"Index", nil];
97           
98            fGroups = [[NSMutableArray alloc] initWithObjects: red, orange, yellow, green, blue, purple, gray, nil];
99            [self saveGroups]; //make sure this is saved right away
100        }
101    }
102   
103    return self;
104}
105
106- (void) dealloc
107{
108    [fGroups release];
109    [super dealloc];
110}
111
112- (NSInteger) numberOfGroups
113{
114    return [fGroups count];
115}
116
117- (NSInteger) rowValueForIndex: (NSInteger) index
118{
119    if (index != -1)
120    {
121        for (NSInteger i = 0; i < [fGroups count]; i++)
122            if (index == [[[fGroups objectAtIndex: i] objectForKey: @"Index"] intValue])
123                return i;
124    }
125    return -1;
126}
127
128- (NSInteger) indexForRow: (NSInteger) row
129{
130    return [[[fGroups objectAtIndex: row] objectForKey: @"Index"] intValue];
131}
132
133- (NSString *) nameForIndex: (NSInteger) index
134{
135    NSInteger orderIndex = [self rowValueForIndex: index];
136    return orderIndex != -1 ? [[fGroups objectAtIndex: orderIndex] objectForKey: @"Name"] : nil;
137}
138
139- (void) setName: (NSString *) name forIndex: (NSInteger) index
140{
141    NSInteger orderIndex = [self rowValueForIndex: index];
142    [[fGroups objectAtIndex: orderIndex] setObject: name forKey: @"Name"];
143    [self saveGroups];
144   
145    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateGroups" object: self];
146}
147
148- (NSImage *) imageForIndex: (NSInteger) index
149{
150    NSInteger orderIndex = [self rowValueForIndex: index];
151    return orderIndex != -1 ? [self imageForGroup: [fGroups objectAtIndex: orderIndex]]
152                            : [NSImage imageNamed: @"GroupsNoneTemplate.png"];
153}
154
155- (NSColor *) colorForIndex: (NSInteger) index
156{
157    NSInteger orderIndex = [self rowValueForIndex: index];
158    return orderIndex != -1 ? [[fGroups objectAtIndex: orderIndex] objectForKey: @"Color"] : nil;
159}
160
161- (void) setColor: (NSColor *) color forIndex: (NSInteger) index
162{
163    NSMutableDictionary * dict = [fGroups objectAtIndex: [self rowValueForIndex: index]];
164    [dict removeObjectForKey: @"Icon"];
165   
166    [dict setObject: color forKey: @"Color"];
167   
168    [[GroupsController groups] saveGroups];
169    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateGroups" object: self];
170}
171
172- (BOOL) usesCustomDownloadLocationForIndex: (NSInteger) index
173{
174    if (![self customDownloadLocationForIndex: index])
175        return NO;
176
177    NSInteger orderIndex = [self rowValueForIndex: index];
178    return [[[fGroups objectAtIndex: orderIndex] objectForKey: @"UsesCustomDownloadLocation"] boolValue];
179}
180
181- (void) setUsesCustomDownloadLocation: (BOOL) useCustomLocation forIndex: (NSInteger) index
182{
183    NSMutableDictionary * dict = [fGroups objectAtIndex: [self rowValueForIndex: index]];
184   
185    [dict setObject: [NSNumber numberWithBool: useCustomLocation] forKey: @"UsesCustomDownloadLocation"];
186   
187    [[GroupsController groups] saveGroups];
188}
189
190- (NSString *) customDownloadLocationForIndex: (NSInteger) index
191{
192    NSInteger orderIndex = [self rowValueForIndex: index];
193    return orderIndex != -1 ? [[fGroups objectAtIndex: orderIndex] objectForKey: @"CustomDownloadLocation"] : nil;
194}
195
196- (void) setCustomDownloadLocation: (NSString *) location forIndex: (NSInteger) index
197{
198    NSMutableDictionary * dict = [fGroups objectAtIndex: [self rowValueForIndex: index]];
199    [dict setObject: location forKey: @"CustomDownloadLocation"];
200   
201    [[GroupsController groups] saveGroups];
202}
203
204- (BOOL) usesAutoAssignRulesForIndex: (NSInteger) index
205{
206    if (![NSApp isOnLeopardOrBetter])
207        return NO;
208   
209    NSInteger orderIndex = [self rowValueForIndex: index];
210    if (orderIndex == -1)
211        return NO;
212   
213    NSNumber * assignRules = [[fGroups objectAtIndex: orderIndex] objectForKey: @"UsesAutoAssignRules"];
214    return assignRules && [assignRules boolValue];
215}
216
217- (void) setUsesAutoAssignRules: (BOOL) useAutoAssignRules forIndex: (NSInteger) index
218{
219    NSMutableDictionary * dict = [fGroups objectAtIndex: [self rowValueForIndex: index]];
220   
221    [dict setObject: [NSNumber numberWithBool: useAutoAssignRules] forKey: @"UsesAutoAssignRules"];
222   
223    [[GroupsController groups] saveGroups];
224}
225
226- (NSArray *) autoAssignRulesForIndex: (NSInteger) index
227{
228    NSInteger orderIndex = [self rowValueForIndex: index];
229    return orderIndex != -1 ? [[fGroups objectAtIndex: orderIndex] objectForKey: @"AutoAssignRules"] : nil;
230}
231
232- (void) setAutoAssignRules: (NSArray *) rules forIndex: (NSInteger) index
233{
234    NSMutableDictionary * dict = [fGroups objectAtIndex: [self rowValueForIndex: index]];
235   
236    if (rules && [rules count] > 0)
237    {
238        [dict setObject: rules forKey: @"AutoAssignRules"];
239       
240        [[GroupsController groups] saveGroups];
241    }
242    else
243    {
244        [dict removeObjectForKey: @"AutoAssignRules"];
245        [self setUsesAutoAssignRules: NO forIndex: index];
246    }
247}
248
249- (BOOL) rulesNeedAllForIndex: (NSInteger) index
250{
251    NSInteger orderIndex = [self rowValueForIndex: index];
252    if (orderIndex == -1)
253        return YES;
254   
255    NSNumber * enforceAll = [[fGroups objectAtIndex: orderIndex] objectForKey: @"AssignRulesNeedAll"];
256    return !enforceAll || [enforceAll boolValue];
257}
258
259- (void) setRulesNeedAllForIndex: (BOOL) all forIndex: (NSInteger) index
260{
261    NSMutableDictionary * group = [fGroups objectAtIndex: [self rowValueForIndex: index]];
262   
263    [group setObject: [NSNumber numberWithBool: all] forKey: @"AssignRulesNeedAll"];
264   
265    [[GroupsController groups] saveGroups];
266}
267
268- (void) addNewGroup
269{
270    //find the lowest index
271    NSInteger index;
272    for (index = 0; index < [fGroups count]; index++)
273    {
274        BOOL found = NO;
275        NSEnumerator * enumerator = [fGroups objectEnumerator];
276        NSDictionary * dict;
277        while ((dict = [enumerator nextObject]))
278            if ([[dict objectForKey: @"Index"] intValue] == index)
279            {
280                found = YES;
281                break;
282            }
283       
284        if (!found)
285            break;
286    }
287   
288    [fGroups addObject: [NSMutableDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt: index], @"Index",
289                            [NSColor cyanColor], @"Color", @"", @"Name", nil]];
290   
291    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateGroups" object: self];
292    [self saveGroups];
293}
294
295- (void) removeGroupWithRowIndex: (NSInteger) row
296{
297    NSInteger index = [[[fGroups objectAtIndex: row] objectForKey: @"Index"] intValue];
298    [fGroups removeObjectAtIndex: row];
299   
300    [[NSNotificationCenter defaultCenter] postNotificationName: @"GroupValueRemoved" object: self userInfo:
301        [NSDictionary dictionaryWithObject: [NSNumber numberWithInt: index] forKey: @"Index"]];
302   
303    if (index == [[NSUserDefaults standardUserDefaults] integerForKey: @"FilterGroup"])
304        [[NSUserDefaults standardUserDefaults] setInteger: -2 forKey: @"FilterGroup"];
305   
306    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateGroups" object: self];
307    [self saveGroups];
308}
309
310- (void) moveGroupAtRow: (NSInteger) oldRow toRow: (NSInteger) newRow
311{
312    if (oldRow < newRow)
313        newRow--;
314   
315    //remove objects to reinsert
316    id movingGroup = [[fGroups objectAtIndex: oldRow] retain];
317    [fGroups removeObjectAtIndex: oldRow];
318   
319    //insert objects at new location
320    [fGroups insertObject: movingGroup atIndex: newRow];
321   
322    [movingGroup release];
323   
324    [self saveGroups];
325    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateGroups" object: self];
326}
327
328- (NSMenu *) groupMenuWithTarget: (id) target action: (SEL) action isSmall: (BOOL) small
329{
330    NSMenu * menu = [[NSMenu alloc] initWithTitle: @"Groups"];
331   
332    NSMenuItem * item = [[NSMenuItem alloc] initWithTitle: NSLocalizedString(@"None", "Groups -> Menu") action: action
333                            keyEquivalent: @""];
334    [item setTarget: target];
335    [item setTag: -1];
336   
337    NSImage * icon = [NSImage imageNamed: @"GroupsNoneTemplate.png"];
338    if (small)
339    {
340        icon = [icon copy];
341        [icon setScalesWhenResized: YES];
342        [icon setSize: NSMakeSize(ICON_WIDTH_SMALL, ICON_WIDTH_SMALL)];
343       
344        [item setImage: icon];
345        [icon release];
346    }
347    else
348        [item setImage: icon];
349   
350    [menu addItem: item];
351    [item release];
352   
353    NSEnumerator * enumerator = [fGroups objectEnumerator];
354    NSMutableDictionary * dict;
355    while ((dict = [enumerator nextObject]))
356    {
357        item = [[NSMenuItem alloc] initWithTitle: [dict objectForKey: @"Name"] action: action keyEquivalent: @""];
358        [item setTarget: target];
359       
360        [item setTag: [[dict objectForKey: @"Index"] intValue]];
361       
362        NSImage * icon = [self imageForGroup: dict];
363        if (small)
364        {
365            icon = [icon copy];
366            [icon setScalesWhenResized: YES];
367            [icon setSize: NSMakeSize(ICON_WIDTH_SMALL, ICON_WIDTH_SMALL)];
368           
369            [item setImage: icon];
370            [icon release];
371        }
372        else
373            [item setImage: icon];
374       
375        [menu addItem: item];
376        [item release];
377    }
378   
379    return [menu autorelease];
380}
381
382- (NSInteger) groupIndexForTorrent: (Torrent *) torrent;
383{
384    NSEnumerator * enumerator = [fGroups objectEnumerator];
385    NSMutableDictionary * group;
386    while ((group = [enumerator nextObject]))
387    {
388        NSInteger row = [[group objectForKey: @"Index"] intValue];
389        if ([self torrent: torrent doesMatchRulesForGroupAtIndex: row])
390            return row;
391    }
392    return -1; // Default to no group
393}
394
395@end
396
397@implementation GroupsController (Private)
398
399- (void) saveGroups
400{
401    //don't archive the icon
402    NSMutableArray * groups = [NSMutableArray arrayWithCapacity: [fGroups count]];
403    NSEnumerator * enumerator = [fGroups objectEnumerator];
404    NSDictionary * dict;
405    while ((dict = [enumerator nextObject]))
406    {
407        NSMutableDictionary * tempDict = [dict mutableCopy];
408        [tempDict removeObjectForKey: @"Icon"];
409        [groups addObject: tempDict];
410        [tempDict release];
411    }
412   
413    [[NSUserDefaults standardUserDefaults] setObject: [NSArchiver archivedDataWithRootObject: groups] forKey: @"Groups"];
414}
415
416- (NSImage *) imageForGroup: (NSMutableDictionary *) dict
417{
418    NSImage * image;
419    if ((image = [dict objectForKey: @"Icon"]))
420        return image;
421   
422    NSRect rect = NSMakeRect(0.0, 0.0, ICON_WIDTH, ICON_WIDTH);
423   
424    NSBezierPath * bp = [NSBezierPath bezierPathWithRoundedRect: rect radius: 3.0];
425    NSImage * icon = [[NSImage alloc] initWithSize: rect.size];
426   
427    NSColor * color = [dict objectForKey: @"Color"];
428   
429    [icon lockFocus];
430   
431    //border
432    CTGradient * gradient = [CTGradient gradientWithBeginningColor: [color blendedColorWithFraction: 0.45 ofColor:
433                                [NSColor whiteColor]] endingColor: color];
434    [gradient fillBezierPath: bp angle: 270.0];
435   
436    //inside
437    bp = [NSBezierPath bezierPathWithRoundedRect: NSInsetRect(rect, 1.0, 1.0) radius: 3.0];
438    gradient = [CTGradient gradientWithBeginningColor: [color blendedColorWithFraction: 0.75 ofColor: [NSColor whiteColor]]
439                endingColor: [color blendedColorWithFraction: 0.2 ofColor: [NSColor whiteColor]]];
440    [gradient fillBezierPath: bp angle: 270.0];
441   
442    [icon unlockFocus];
443   
444    [dict setObject: icon forKey: @"Icon"];
445    [icon release];
446   
447    return icon;
448}
449
450- (BOOL) torrent: (Torrent *) torrent doesMatchRulesForGroupAtIndex: (NSInteger) index
451{
452    if (![self usesAutoAssignRulesForIndex: index])
453        return NO;
454   
455    const BOOL needAll = [self rulesNeedAllForIndex: index];
456    BOOL anyPassed = NO;
457   
458    NSEnumerator * iterator = [[self autoAssignRulesForIndex: index] objectEnumerator];
459    NSArray * rule;
460    while ((rule = [iterator nextObject]))
461    {
462        NSString * type = [rule objectAtIndex: 0], * place = [rule objectAtIndex: 1], * givenValue = [rule objectAtIndex: 2];
463        NSArray * values;
464        if ([type isEqualToString: @"title"])
465            values = [NSArray arrayWithObject: [torrent name]];
466        else if ([type isEqualToString: @"tracker"])
467            values = [torrent allTrackers: NO];
468        else
469            continue;
470       
471        NSStringCompareOptions options;
472        if ([place isEqualToString: @"begins"])
473            options = NSCaseInsensitiveSearch | NSAnchoredSearch;
474        else if ([place isEqualToString: @"ends"])
475            options = NSCaseInsensitiveSearch | NSBackwardsSearch | NSAnchoredSearch;
476        else if ([place isEqualToString: @"contains"])
477            options = NSCaseInsensitiveSearch;
478        else
479            continue;
480       
481        BOOL match = NO;
482       
483        NSEnumerator * enumerator = [values objectEnumerator];
484        NSString * value;
485        while ((value = [enumerator nextObject]))
486        {
487            NSRange result = [value rangeOfString: givenValue options: options];
488            if (result.location != NSNotFound)
489            {
490                match = YES;
491                anyPassed = YES;
492                break;
493            }
494        }
495       
496        if (match && !needAll)
497            return YES;
498        else if (!match && needAll)
499            return NO;
500        else;
501    }
502   
503    return anyPassed && needAll;
504}
505
506@end
Note: See TracBrowser for help on using the repository browser.