transmission 2.51 update
[tomato.git] / release / src / router / transmission / macosx / GroupsController.m
blobcadea283a5e6fdd04e417d65eee7aa32da852f99
1 /******************************************************************************
2  * $Id: GroupsController.m 13251 2012-03-13 02:52:11Z livings124 $
3  *
4  * Copyright (c) 2007-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  *****************************************************************************/
25 #import "GroupsController.h"
26 #import "NSMutableArrayAdditions.h"
28 #define ICON_WIDTH 16.0
29 #define ICON_WIDTH_SMALL 12.0
31 @interface GroupsController (Private)
33 - (void) saveGroups;
35 - (NSImage *) imageForGroup: (NSMutableDictionary *) dict;
37 - (BOOL) torrent: (Torrent *) torrent doesMatchRulesForGroupAtIndex: (NSInteger) index;
39 @end
41 @implementation GroupsController
43 GroupsController * fGroupsInstance = nil;
44 + (GroupsController *) groups
46     if (!fGroupsInstance)
47         fGroupsInstance = [[GroupsController alloc] init];
48     return fGroupsInstance;
51 - (id) init
53     if ((self = [super init]))
54     {
55         NSData * data;
56         if ((data = [[NSUserDefaults standardUserDefaults] dataForKey: @"GroupDicts"]))
57             fGroups = [[NSKeyedUnarchiver unarchiveObjectWithData: data] retain];
58         else if ((data = [[NSUserDefaults standardUserDefaults] dataForKey: @"Groups"])) //handle old groups
59         {
60             fGroups = [[NSUnarchiver unarchiveObjectWithData: data] retain];
61             [[NSUserDefaults standardUserDefaults] removeObjectForKey: @"Groups"];
62             [self saveGroups];
63         }
64         else
65         {
66             //default groups
67             NSMutableDictionary * red = [NSMutableDictionary dictionaryWithObjectsAndKeys:
68                                             [NSColor redColor], @"Color",
69                                             NSLocalizedString(@"Red", "Groups -> Name"), @"Name",
70                                             [NSNumber numberWithInteger: 0], @"Index", nil];
71             
72             NSMutableDictionary * orange = [NSMutableDictionary dictionaryWithObjectsAndKeys:
73                                             [NSColor orangeColor], @"Color",
74                                             NSLocalizedString(@"Orange", "Groups -> Name"), @"Name",
75                                             [NSNumber numberWithInteger: 1], @"Index", nil];
76             
77             NSMutableDictionary * yellow = [NSMutableDictionary dictionaryWithObjectsAndKeys:
78                                             [NSColor yellowColor], @"Color",
79                                             NSLocalizedString(@"Yellow", "Groups -> Name"), @"Name",
80                                             [NSNumber numberWithInteger: 2], @"Index", nil];
81             
82             NSMutableDictionary * green = [NSMutableDictionary dictionaryWithObjectsAndKeys:
83                                             [NSColor greenColor], @"Color",
84                                             NSLocalizedString(@"Green", "Groups -> Name"), @"Name",
85                                             [NSNumber numberWithInteger: 3], @"Index", nil];
86             
87             NSMutableDictionary * blue = [NSMutableDictionary dictionaryWithObjectsAndKeys:
88                                             [NSColor blueColor], @"Color",
89                                             NSLocalizedString(@"Blue", "Groups -> Name"), @"Name",
90                                             [NSNumber numberWithInteger: 4], @"Index", nil];
91             
92             NSMutableDictionary * purple = [NSMutableDictionary dictionaryWithObjectsAndKeys:
93                                             [NSColor purpleColor], @"Color",
94                                             NSLocalizedString(@"Purple", "Groups -> Name"), @"Name",
95                                             [NSNumber numberWithInteger: 5], @"Index", nil];
96             
97             NSMutableDictionary * gray = [NSMutableDictionary dictionaryWithObjectsAndKeys:
98                                             [NSColor grayColor], @"Color",
99                                             NSLocalizedString(@"Gray", "Groups -> Name"), @"Name",
100                                             [NSNumber numberWithInteger: 6], @"Index", nil];
101             
102             fGroups = [[NSMutableArray alloc] initWithObjects: red, orange, yellow, green, blue, purple, gray, nil];
103             [self saveGroups]; //make sure this is saved right away
104         }
105     }
106     
107     return self;
110 - (void) dealloc
112     [fGroups release];
113     [super dealloc];
116 - (NSInteger) numberOfGroups
118     return [fGroups count];
121 - (NSInteger) rowValueForIndex: (NSInteger) index
123     if (index != -1)
124     {
125         for (NSInteger i = 0; i < [fGroups count]; i++)
126             if (index == [[[fGroups objectAtIndex: i] objectForKey: @"Index"] integerValue])
127                 return i;
128     }
129     return -1;
132 - (NSInteger) indexForRow: (NSInteger) row
134     return [[[fGroups objectAtIndex: row] objectForKey: @"Index"] integerValue];
137 - (NSString *) nameForIndex: (NSInteger) index
139     NSInteger orderIndex = [self rowValueForIndex: index];
140     return orderIndex != -1 ? [[fGroups objectAtIndex: orderIndex] objectForKey: @"Name"] : nil;
143 - (void) setName: (NSString *) name forIndex: (NSInteger) index
145     NSInteger orderIndex = [self rowValueForIndex: index];
146     [[fGroups objectAtIndex: orderIndex] setObject: name forKey: @"Name"];
147     [self saveGroups];
148     
149     [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateGroups" object: self];
152 - (NSImage *) imageForIndex: (NSInteger) index
154     NSInteger orderIndex = [self rowValueForIndex: index];
155     return orderIndex != -1 ? [self imageForGroup: [fGroups objectAtIndex: orderIndex]]
156                             : [NSImage imageNamed: @"GroupsNoneTemplate.png"];
159 - (NSColor *) colorForIndex: (NSInteger) index
161     NSInteger orderIndex = [self rowValueForIndex: index];
162     return orderIndex != -1 ? [[fGroups objectAtIndex: orderIndex] objectForKey: @"Color"] : nil;
165 - (void) setColor: (NSColor *) color forIndex: (NSInteger) index
167     NSMutableDictionary * dict = [fGroups objectAtIndex: [self rowValueForIndex: index]];
168     [dict removeObjectForKey: @"Icon"];
169     
170     [dict setObject: color forKey: @"Color"];
171     
172     [[GroupsController groups] saveGroups];
173     [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateGroups" object: self];
176 - (BOOL) usesCustomDownloadLocationForIndex: (NSInteger) index
178     if (![self customDownloadLocationForIndex: index])
179         return NO;
181     NSInteger orderIndex = [self rowValueForIndex: index];
182     return [[[fGroups objectAtIndex: orderIndex] objectForKey: @"UsesCustomDownloadLocation"] boolValue];
185 - (void) setUsesCustomDownloadLocation: (BOOL) useCustomLocation forIndex: (NSInteger) index
187     NSMutableDictionary * dict = [fGroups objectAtIndex: [self rowValueForIndex: index]];
188     
189     [dict setObject: [NSNumber numberWithBool: useCustomLocation] forKey: @"UsesCustomDownloadLocation"];
190     
191     [[GroupsController groups] saveGroups];
194 - (NSString *) customDownloadLocationForIndex: (NSInteger) index
196     NSInteger orderIndex = [self rowValueForIndex: index];
197     return orderIndex != -1 ? [[fGroups objectAtIndex: orderIndex] objectForKey: @"CustomDownloadLocation"] : nil;
200 - (void) setCustomDownloadLocation: (NSString *) location forIndex: (NSInteger) index
202     NSMutableDictionary * dict = [fGroups objectAtIndex: [self rowValueForIndex: index]];
203     [dict setObject: location forKey: @"CustomDownloadLocation"];
204     
205     [[GroupsController groups] saveGroups];
208 - (BOOL) usesAutoAssignRulesForIndex: (NSInteger) index
210     NSInteger orderIndex = [self rowValueForIndex: index];
211     if (orderIndex == -1)
212         return NO;
213     
214     NSNumber * assignRules = [[fGroups objectAtIndex: orderIndex] objectForKey: @"UsesAutoGroupRules"];
215     return assignRules && [assignRules boolValue];
218 - (void) setUsesAutoAssignRules: (BOOL) useAutoAssignRules forIndex: (NSInteger) index
220     NSMutableDictionary * dict = [fGroups objectAtIndex: [self rowValueForIndex: index]];
221     
222     [dict setObject: [NSNumber numberWithBool: useAutoAssignRules] forKey: @"UsesAutoGroupRules"];
223     
224     [[GroupsController groups] saveGroups];
227 - (NSPredicate *) autoAssignRulesForIndex: (NSInteger) index
229     NSInteger orderIndex = [self rowValueForIndex: index];
230     if (orderIndex == -1)
231                 return nil;
232         
233         return [[fGroups objectAtIndex: orderIndex] objectForKey: @"AutoGroupRules"];
236 - (void) setAutoAssignRules: (NSPredicate *) predicate forIndex: (NSInteger) index
238     NSMutableDictionary * dict = [fGroups objectAtIndex: [self rowValueForIndex: index]];
239     
240     if (predicate)
241     {
242         [dict setObject: predicate forKey: @"AutoGroupRules"];
243         [[GroupsController groups] saveGroups];
244     }
245     else
246     {
247         [dict removeObjectForKey: @"AutoGroupRules"];
248         [self setUsesAutoAssignRules: NO forIndex: index];
249     }
252 - (void) addNewGroup
254     //find the lowest index
255     NSMutableIndexSet * candidates = [NSMutableIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [fGroups count]+1)];
256     for (NSDictionary * dict in fGroups)
257         [candidates removeIndex: [[dict objectForKey: @"Index"] integerValue]];
258     
259     const NSInteger index = [candidates firstIndex];
260     
261     [fGroups addObject: [NSMutableDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInteger: index], @"Index",
262                             [NSColor colorWithCalibratedRed: 0.0 green: 0.65 blue: 1.0 alpha: 1.0], @"Color", @"", @"Name", nil]];
263     
264     [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateGroups" object: self];
265     [self saveGroups];
268 - (void) removeGroupWithRowIndex: (NSInteger) row
270     NSInteger index = [[[fGroups objectAtIndex: row] objectForKey: @"Index"] integerValue];
271     [fGroups removeObjectAtIndex: row];
272     
273     [[NSNotificationCenter defaultCenter] postNotificationName: @"GroupValueRemoved" object: self userInfo:
274         [NSDictionary dictionaryWithObject: [NSNumber numberWithInteger: index] forKey: @"Index"]];
275     
276     if (index == [[NSUserDefaults standardUserDefaults] integerForKey: @"FilterGroup"])
277         [[NSUserDefaults standardUserDefaults] setInteger: -2 forKey: @"FilterGroup"];
278     
279     [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateGroups" object: self];
280     [self saveGroups];
283 - (void) moveGroupAtRow: (NSInteger) oldRow toRow: (NSInteger) newRow
285     [fGroups moveObjectAtIndex: oldRow toIndex: newRow];
286     
287     [self saveGroups];
288     [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateGroups" object: self];
291 - (NSMenu *) groupMenuWithTarget: (id) target action: (SEL) action isSmall: (BOOL) small
293     NSMenu * menu = [[NSMenu alloc] initWithTitle: @"Groups"];
294     
295     NSMenuItem * item = [[NSMenuItem alloc] initWithTitle: NSLocalizedString(@"None", "Groups -> Menu") action: action
296                             keyEquivalent: @""];
297     [item setTarget: target];
298     [item setTag: -1];
299     
300     NSImage * icon = [NSImage imageNamed: @"GroupsNoneTemplate.png"];
301     if (small)
302     {
303         icon = [icon copy];
304         [icon setSize: NSMakeSize(ICON_WIDTH_SMALL, ICON_WIDTH_SMALL)];
305         
306         [item setImage: icon];
307         [icon release];
308     }
309     else
310         [item setImage: icon];
311     
312     [menu addItem: item];
313     [item release];
314     
315     for (NSMutableDictionary * dict in fGroups)
316     {
317         item = [[NSMenuItem alloc] initWithTitle: [dict objectForKey: @"Name"] action: action keyEquivalent: @""];
318         [item setTarget: target];
319         
320         [item setTag: [[dict objectForKey: @"Index"] integerValue]];
321         
322         NSImage * icon = [self imageForGroup: dict];
323         if (small)
324         {
325             icon = [icon copy];
326             [icon setSize: NSMakeSize(ICON_WIDTH_SMALL, ICON_WIDTH_SMALL)];
327             
328             [item setImage: icon];
329             [icon release];
330         }
331         else
332             [item setImage: icon];
333         
334         [menu addItem: item];
335         [item release];
336     }
337     
338     return [menu autorelease];
341 - (NSInteger) groupIndexForTorrent: (Torrent *) torrent;
343     for (NSDictionary * group in fGroups)
344     {
345         NSInteger row = [[group objectForKey: @"Index"] integerValue];
346         if ([self torrent: torrent doesMatchRulesForGroupAtIndex: row])
347             return row;
348     }
349     return -1;
352 @end
354 @implementation GroupsController (Private)
356 - (void) saveGroups
358     //don't archive the icon
359     NSMutableArray * groups = [NSMutableArray arrayWithCapacity: [fGroups count]];
360     for (NSDictionary * dict in fGroups)
361     {
362         NSMutableDictionary * tempDict = [dict mutableCopy];
363         [tempDict removeObjectForKey: @"Icon"];
364         [groups addObject: tempDict];
365         [tempDict release];
366     }
367     
368     [[NSUserDefaults standardUserDefaults] setObject: [NSKeyedArchiver archivedDataWithRootObject: groups] forKey: @"GroupDicts"];
371 - (NSImage *) imageForGroup: (NSMutableDictionary *) dict
373     NSImage * image;
374     if ((image = [dict objectForKey: @"Icon"]))
375         return image;
376     
377     NSRect rect = NSMakeRect(0.0, 0.0, ICON_WIDTH, ICON_WIDTH);
378     
379     NSBezierPath * bp = [NSBezierPath bezierPathWithRoundedRect: rect xRadius: 3.0 yRadius: 3.0];
380     NSImage * icon = [[NSImage alloc] initWithSize: rect.size];
381     
382     NSColor * color = [dict objectForKey: @"Color"];
383     
384     [icon lockFocus];
385     
386     //border
387     NSGradient * gradient = [[NSGradient alloc] initWithStartingColor: [color blendedColorWithFraction: 0.45 ofColor:
388                                 [NSColor whiteColor]] endingColor: color];
389     [gradient drawInBezierPath: bp angle: 270.0];
390     [gradient release];
391     
392     //inside
393     bp = [NSBezierPath bezierPathWithRoundedRect: NSInsetRect(rect, 1.0, 1.0) xRadius: 3.0 yRadius: 3.0];
394     gradient = [[NSGradient alloc] initWithStartingColor: [color blendedColorWithFraction: 0.75 ofColor: [NSColor whiteColor]]
395                 endingColor: [color blendedColorWithFraction: 0.2 ofColor: [NSColor whiteColor]]];
396     [gradient drawInBezierPath: bp angle: 270.0];
397     [gradient release];
398     
399     [icon unlockFocus];
400     
401     [dict setObject: icon forKey: @"Icon"];
402     [icon release];
403     
404     return icon;
407 - (BOOL) torrent: (Torrent *) torrent doesMatchRulesForGroupAtIndex: (NSInteger) index
409     if (![self usesAutoAssignRulesForIndex: index])
410         return NO;
411         
412     NSPredicate * predicate = [self autoAssignRulesForIndex: index];
413     BOOL eval = NO;
414     @try
415     {
416         eval = [predicate evaluateWithObject: torrent];
417     }
418     @catch (NSException * exception)
419     {
420         NSLog(@"Error when evaluating predicate (%@) - %@", predicate, exception);
421     }
422     @finally
423     {
424         return eval;
425     }
428 @end