1 /******************************************************************************
2 * $Id: GroupsController.m 13251 2012-03-13 02:52:11Z livings124 $
4 * Copyright (c) 2007-2012 Transmission authors and contributors
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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)
35 - (NSImage *) imageForGroup: (NSMutableDictionary *) dict;
37 - (BOOL) torrent: (Torrent *) torrent doesMatchRulesForGroupAtIndex: (NSInteger) index;
41 @implementation GroupsController
43 GroupsController * fGroupsInstance = nil;
44 + (GroupsController *) groups
47 fGroupsInstance = [[GroupsController alloc] init];
48 return fGroupsInstance;
53 if ((self = [super init]))
56 if ((data = [[NSUserDefaults standardUserDefaults] dataForKey: @"GroupDicts"]))
57 fGroups = [[NSKeyedUnarchiver unarchiveObjectWithData: data] retain];
58 else if ((data = [[NSUserDefaults standardUserDefaults] dataForKey: @"Groups"])) //handle old groups
60 fGroups = [[NSUnarchiver unarchiveObjectWithData: data] retain];
61 [[NSUserDefaults standardUserDefaults] removeObjectForKey: @"Groups"];
67 NSMutableDictionary * red = [NSMutableDictionary dictionaryWithObjectsAndKeys:
68 [NSColor redColor], @"Color",
69 NSLocalizedString(@"Red", "Groups -> Name"), @"Name",
70 [NSNumber numberWithInteger: 0], @"Index", nil];
72 NSMutableDictionary * orange = [NSMutableDictionary dictionaryWithObjectsAndKeys:
73 [NSColor orangeColor], @"Color",
74 NSLocalizedString(@"Orange", "Groups -> Name"), @"Name",
75 [NSNumber numberWithInteger: 1], @"Index", nil];
77 NSMutableDictionary * yellow = [NSMutableDictionary dictionaryWithObjectsAndKeys:
78 [NSColor yellowColor], @"Color",
79 NSLocalizedString(@"Yellow", "Groups -> Name"), @"Name",
80 [NSNumber numberWithInteger: 2], @"Index", nil];
82 NSMutableDictionary * green = [NSMutableDictionary dictionaryWithObjectsAndKeys:
83 [NSColor greenColor], @"Color",
84 NSLocalizedString(@"Green", "Groups -> Name"), @"Name",
85 [NSNumber numberWithInteger: 3], @"Index", nil];
87 NSMutableDictionary * blue = [NSMutableDictionary dictionaryWithObjectsAndKeys:
88 [NSColor blueColor], @"Color",
89 NSLocalizedString(@"Blue", "Groups -> Name"), @"Name",
90 [NSNumber numberWithInteger: 4], @"Index", nil];
92 NSMutableDictionary * purple = [NSMutableDictionary dictionaryWithObjectsAndKeys:
93 [NSColor purpleColor], @"Color",
94 NSLocalizedString(@"Purple", "Groups -> Name"), @"Name",
95 [NSNumber numberWithInteger: 5], @"Index", nil];
97 NSMutableDictionary * gray = [NSMutableDictionary dictionaryWithObjectsAndKeys:
98 [NSColor grayColor], @"Color",
99 NSLocalizedString(@"Gray", "Groups -> Name"), @"Name",
100 [NSNumber numberWithInteger: 6], @"Index", nil];
102 fGroups = [[NSMutableArray alloc] initWithObjects: red, orange, yellow, green, blue, purple, gray, nil];
103 [self saveGroups]; //make sure this is saved right away
116 - (NSInteger) numberOfGroups
118 return [fGroups count];
121 - (NSInteger) rowValueForIndex: (NSInteger) index
125 for (NSInteger i = 0; i < [fGroups count]; i++)
126 if (index == [[[fGroups objectAtIndex: i] objectForKey: @"Index"] integerValue])
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"];
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"];
170 [dict setObject: color forKey: @"Color"];
172 [[GroupsController groups] saveGroups];
173 [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateGroups" object: self];
176 - (BOOL) usesCustomDownloadLocationForIndex: (NSInteger) index
178 if (![self customDownloadLocationForIndex: index])
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]];
189 [dict setObject: [NSNumber numberWithBool: useCustomLocation] forKey: @"UsesCustomDownloadLocation"];
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"];
205 [[GroupsController groups] saveGroups];
208 - (BOOL) usesAutoAssignRulesForIndex: (NSInteger) index
210 NSInteger orderIndex = [self rowValueForIndex: index];
211 if (orderIndex == -1)
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]];
222 [dict setObject: [NSNumber numberWithBool: useAutoAssignRules] forKey: @"UsesAutoGroupRules"];
224 [[GroupsController groups] saveGroups];
227 - (NSPredicate *) autoAssignRulesForIndex: (NSInteger) index
229 NSInteger orderIndex = [self rowValueForIndex: index];
230 if (orderIndex == -1)
233 return [[fGroups objectAtIndex: orderIndex] objectForKey: @"AutoGroupRules"];
236 - (void) setAutoAssignRules: (NSPredicate *) predicate forIndex: (NSInteger) index
238 NSMutableDictionary * dict = [fGroups objectAtIndex: [self rowValueForIndex: index]];
242 [dict setObject: predicate forKey: @"AutoGroupRules"];
243 [[GroupsController groups] saveGroups];
247 [dict removeObjectForKey: @"AutoGroupRules"];
248 [self setUsesAutoAssignRules: NO forIndex: index];
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]];
259 const NSInteger index = [candidates firstIndex];
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]];
264 [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateGroups" object: self];
268 - (void) removeGroupWithRowIndex: (NSInteger) row
270 NSInteger index = [[[fGroups objectAtIndex: row] objectForKey: @"Index"] integerValue];
271 [fGroups removeObjectAtIndex: row];
273 [[NSNotificationCenter defaultCenter] postNotificationName: @"GroupValueRemoved" object: self userInfo:
274 [NSDictionary dictionaryWithObject: [NSNumber numberWithInteger: index] forKey: @"Index"]];
276 if (index == [[NSUserDefaults standardUserDefaults] integerForKey: @"FilterGroup"])
277 [[NSUserDefaults standardUserDefaults] setInteger: -2 forKey: @"FilterGroup"];
279 [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateGroups" object: self];
283 - (void) moveGroupAtRow: (NSInteger) oldRow toRow: (NSInteger) newRow
285 [fGroups moveObjectAtIndex: oldRow toIndex: newRow];
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"];
295 NSMenuItem * item = [[NSMenuItem alloc] initWithTitle: NSLocalizedString(@"None", "Groups -> Menu") action: action
297 [item setTarget: target];
300 NSImage * icon = [NSImage imageNamed: @"GroupsNoneTemplate.png"];
304 [icon setSize: NSMakeSize(ICON_WIDTH_SMALL, ICON_WIDTH_SMALL)];
306 [item setImage: icon];
310 [item setImage: icon];
312 [menu addItem: item];
315 for (NSMutableDictionary * dict in fGroups)
317 item = [[NSMenuItem alloc] initWithTitle: [dict objectForKey: @"Name"] action: action keyEquivalent: @""];
318 [item setTarget: target];
320 [item setTag: [[dict objectForKey: @"Index"] integerValue]];
322 NSImage * icon = [self imageForGroup: dict];
326 [icon setSize: NSMakeSize(ICON_WIDTH_SMALL, ICON_WIDTH_SMALL)];
328 [item setImage: icon];
332 [item setImage: icon];
334 [menu addItem: item];
338 return [menu autorelease];
341 - (NSInteger) groupIndexForTorrent: (Torrent *) torrent;
343 for (NSDictionary * group in fGroups)
345 NSInteger row = [[group objectForKey: @"Index"] integerValue];
346 if ([self torrent: torrent doesMatchRulesForGroupAtIndex: row])
354 @implementation GroupsController (Private)
358 //don't archive the icon
359 NSMutableArray * groups = [NSMutableArray arrayWithCapacity: [fGroups count]];
360 for (NSDictionary * dict in fGroups)
362 NSMutableDictionary * tempDict = [dict mutableCopy];
363 [tempDict removeObjectForKey: @"Icon"];
364 [groups addObject: tempDict];
368 [[NSUserDefaults standardUserDefaults] setObject: [NSKeyedArchiver archivedDataWithRootObject: groups] forKey: @"GroupDicts"];
371 - (NSImage *) imageForGroup: (NSMutableDictionary *) dict
374 if ((image = [dict objectForKey: @"Icon"]))
377 NSRect rect = NSMakeRect(0.0, 0.0, ICON_WIDTH, ICON_WIDTH);
379 NSBezierPath * bp = [NSBezierPath bezierPathWithRoundedRect: rect xRadius: 3.0 yRadius: 3.0];
380 NSImage * icon = [[NSImage alloc] initWithSize: rect.size];
382 NSColor * color = [dict objectForKey: @"Color"];
387 NSGradient * gradient = [[NSGradient alloc] initWithStartingColor: [color blendedColorWithFraction: 0.45 ofColor:
388 [NSColor whiteColor]] endingColor: color];
389 [gradient drawInBezierPath: bp angle: 270.0];
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];
401 [dict setObject: icon forKey: @"Icon"];
407 - (BOOL) torrent: (Torrent *) torrent doesMatchRulesForGroupAtIndex: (NSInteger) index
409 if (![self usesAutoAssignRulesForIndex: index])
412 NSPredicate * predicate = [self autoAssignRulesForIndex: index];
416 eval = [predicate evaluateWithObject: torrent];
418 @catch (NSException * exception)
420 NSLog(@"Error when evaluating predicate (%@) - %@", predicate, exception);