1/****************************************************************************** 2 * Copyright (c) 2011-2012 Transmission authors and contributors 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 * DEALINGS IN THE SOFTWARE. 21 *****************************************************************************/ 22 23#import "FilterBarController.h" 24#import "FilterButton.h" 25#import "GroupsController.h" 26#import "NSStringAdditions.h" 27 28#define FILTER_TYPE_TAG_NAME 401 29#define FILTER_TYPE_TAG_TRACKER 402 30 31#define SEARCH_MIN_WIDTH 48.0 32#define SEARCH_MAX_WIDTH 95.0 33 34@interface FilterBarController (Private) 35 36- (void) resizeBar; 37- (void) updateGroupsButton; 38- (void) updateGroups: (NSNotification *) notification; 39 40@end 41 42@implementation FilterBarController 43 44- (id) init 45{ 46 return (self = [super initWithNibName: @"FilterBar" bundle: nil]); 47} 48 49- (void) awakeFromNib 50{ 51 //localizations 52 [fNoFilterButton setTitle: NSLocalizedString(@"All", "Filter Bar -> filter button")]; 53 [fActiveFilterButton setTitle: NSLocalizedString(@"Active", "Filter Bar -> filter button")]; 54 [fDownloadFilterButton setTitle: NSLocalizedString(@"Downloading", "Filter Bar -> filter button")]; 55 [fSeedFilterButton setTitle: NSLocalizedString(@"Seeding", "Filter Bar -> filter button")]; 56 [fPauseFilterButton setTitle: NSLocalizedString(@"Paused", "Filter Bar -> filter button")]; 57 58 [[fNoFilterButton cell] setBackgroundStyle: NSBackgroundStyleRaised]; 59 [[fActiveFilterButton cell] setBackgroundStyle: NSBackgroundStyleRaised]; 60 [[fDownloadFilterButton cell] setBackgroundStyle: NSBackgroundStyleRaised]; 61 [[fSeedFilterButton cell] setBackgroundStyle: NSBackgroundStyleRaised]; 62 [[fPauseFilterButton cell] setBackgroundStyle: NSBackgroundStyleRaised]; 63 64 [[[[fSearchField cell] searchMenuTemplate] itemWithTag: FILTER_TYPE_TAG_NAME] setTitle: 65 NSLocalizedString(@"Name", "Filter Bar -> filter menu")]; 66 [[[[fSearchField cell] searchMenuTemplate] itemWithTag: FILTER_TYPE_TAG_TRACKER] setTitle: 67 NSLocalizedString(@"Tracker", "Filter Bar -> filter menu")]; 68 69 [[[fGroupsButton menu] itemWithTag: GROUP_FILTER_ALL_TAG] setTitle: 70 NSLocalizedString(@"All Groups", "Filter Bar -> group filter menu")]; 71 72 [self resizeBar]; 73 74 //set current filter 75 NSString * filterType = [[NSUserDefaults standardUserDefaults] stringForKey: @"Filter"]; 76 77 NSButton * currentFilterButton; 78 if ([filterType isEqualToString: FILTER_ACTIVE]) 79 currentFilterButton = fActiveFilterButton; 80 else if ([filterType isEqualToString: FILTER_PAUSE]) 81 currentFilterButton = fPauseFilterButton; 82 else if ([filterType isEqualToString: FILTER_SEED]) 83 currentFilterButton = fSeedFilterButton; 84 else if ([filterType isEqualToString: FILTER_DOWNLOAD]) 85 currentFilterButton = fDownloadFilterButton; 86 else 87 { 88 //safety 89 if (![filterType isEqualToString: FILTER_NONE]) 90 [[NSUserDefaults standardUserDefaults] setObject: FILTER_NONE forKey: @"Filter"]; 91 currentFilterButton = fNoFilterButton; 92 } 93 [currentFilterButton setState: NSOnState]; 94 95 //set filter search type 96 NSString * filterSearchType = [[NSUserDefaults standardUserDefaults] stringForKey: @"FilterSearchType"]; 97 98 NSMenu * filterSearchMenu = [[fSearchField cell] searchMenuTemplate]; 99 NSString * filterSearchTypeTitle; 100 if ([filterSearchType isEqualToString: FILTER_TYPE_TRACKER]) 101 filterSearchTypeTitle = [[filterSearchMenu itemWithTag: FILTER_TYPE_TAG_TRACKER] title]; 102 else 103 { 104 //safety 105 if (![filterType isEqualToString: FILTER_TYPE_NAME]) 106 [[NSUserDefaults standardUserDefaults] setObject: FILTER_TYPE_NAME forKey: @"FilterSearchType"]; 107 filterSearchTypeTitle = [[filterSearchMenu itemWithTag: FILTER_TYPE_TAG_NAME] title]; 108 } 109 [[fSearchField cell] setPlaceholderString: filterSearchTypeTitle]; 110 111 NSString * searchString; 112 if ((searchString = [[NSUserDefaults standardUserDefaults] stringForKey: @"FilterSearchString"])) 113 [fSearchField setStringValue: searchString]; 114 115 [self updateGroupsButton]; 116 117 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(resizeBar) 118 name: NSWindowDidResizeNotification object: [[self view] window]]; 119 120 //update when groups change 121 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(updateGroups:) 122 name: @"UpdateGroups" object: nil]; 123} 124 125- (void) dealloc 126{ 127 [[NSNotificationCenter defaultCenter] removeObserver: self]; 128} 129 130- (void) setFilter: (id) sender 131{ 132 NSString * oldFilterType = [[NSUserDefaults standardUserDefaults] stringForKey: @"Filter"]; 133 134 NSButton * prevFilterButton; 135 if ([oldFilterType isEqualToString: FILTER_PAUSE]) 136 prevFilterButton = fPauseFilterButton; 137 else if ([oldFilterType isEqualToString: FILTER_ACTIVE]) 138 prevFilterButton = fActiveFilterButton; 139 else if ([oldFilterType isEqualToString: FILTER_SEED]) 140 prevFilterButton = fSeedFilterButton; 141 else if ([oldFilterType isEqualToString: FILTER_DOWNLOAD]) 142 prevFilterButton = fDownloadFilterButton; 143 else 144 prevFilterButton = fNoFilterButton; 145 146 if (sender != prevFilterButton) 147 { 148 [prevFilterButton setState: NSOffState]; 149 [sender setState: NSOnState]; 150 151 NSString * filterType; 152 if (sender == fActiveFilterButton) 153 filterType = FILTER_ACTIVE; 154 else if (sender == fDownloadFilterButton) 155 filterType = FILTER_DOWNLOAD; 156 else if (sender == fPauseFilterButton) 157 filterType = FILTER_PAUSE; 158 else if (sender == fSeedFilterButton) 159 filterType = FILTER_SEED; 160 else 161 filterType = FILTER_NONE; 162 163 [[NSUserDefaults standardUserDefaults] setObject: filterType forKey: @"Filter"]; 164 } 165 else 166 [sender setState: NSOnState]; 167 168 [[NSNotificationCenter defaultCenter] postNotificationName: @"ApplyFilter" object: nil]; 169} 170 171- (void) switchFilter: (BOOL) right 172{ 173 NSString * filterType = [[NSUserDefaults standardUserDefaults] stringForKey: @"Filter"]; 174 175 NSButton * button; 176 if ([filterType isEqualToString: FILTER_NONE]) 177 button = right ? fActiveFilterButton : fPauseFilterButton; 178 else if ([filterType isEqualToString: FILTER_ACTIVE]) 179 button = right ? fDownloadFilterButton : fNoFilterButton; 180 else if ([filterType isEqualToString: FILTER_DOWNLOAD]) 181 button = right ? fSeedFilterButton : fActiveFilterButton; 182 else if ([filterType isEqualToString: FILTER_SEED]) 183 button = right ? fPauseFilterButton : fDownloadFilterButton; 184 else if ([filterType isEqualToString: FILTER_PAUSE]) 185 button = right ? fNoFilterButton : fSeedFilterButton; 186 else 187 button = fNoFilterButton; 188 189 [self setFilter: button]; 190} 191 192- (void) setSearchText: (id) sender 193{ 194 [[NSUserDefaults standardUserDefaults] setObject: [fSearchField stringValue] forKey: @"FilterSearchString"]; 195 [[NSNotificationCenter defaultCenter] postNotificationName: @"ApplyFilter" object: nil]; 196} 197 198- (void) focusSearchField 199{ 200 [[[self view] window] makeFirstResponder: fSearchField]; 201} 202 203- (void) setSearchType: (id) sender 204{ 205 NSString * oldFilterType = [[NSUserDefaults standardUserDefaults] stringForKey: @"FilterSearchType"]; 206 207 NSInteger prevTag, currentTag = [sender tag]; 208 if ([oldFilterType isEqualToString: FILTER_TYPE_TRACKER]) 209 prevTag = FILTER_TYPE_TAG_TRACKER; 210 else 211 prevTag = FILTER_TYPE_TAG_NAME; 212 213 if (currentTag != prevTag) 214 { 215 NSString * filterType; 216 if (currentTag == FILTER_TYPE_TAG_TRACKER) 217 filterType = FILTER_TYPE_TRACKER; 218 else 219 filterType = FILTER_TYPE_NAME; 220 221 [[NSUserDefaults standardUserDefaults] setObject: filterType forKey: @"FilterSearchType"]; 222 223 [[fSearchField cell] setPlaceholderString: [sender title]]; 224 } 225 226 [[NSNotificationCenter defaultCenter] postNotificationName: @"ApplyFilter" object: nil]; 227} 228 229- (void) setGroupFilter: (id) sender 230{ 231 [[NSUserDefaults standardUserDefaults] setInteger: [sender tag] forKey: @"FilterGroup"]; 232 [self updateGroupsButton]; 233 234 [[NSNotificationCenter defaultCenter] postNotificationName: @"ApplyFilter" object: nil]; 235} 236 237- (void) reset: (BOOL) updateUI 238{ 239 [[NSUserDefaults standardUserDefaults] setInteger: GROUP_FILTER_ALL_TAG forKey: @"FilterGroup"]; 240 241 if (updateUI) 242 { 243 [self updateGroupsButton]; 244 245 [self setFilter: fNoFilterButton]; 246 247 [fSearchField setStringValue: @""]; 248 [self setSearchText: fSearchField]; 249 } 250 else 251 { 252 [[NSUserDefaults standardUserDefaults] setObject: FILTER_NONE forKey: @"Filter"]; 253 [[NSUserDefaults standardUserDefaults] removeObjectForKey: @"FilterSearchString"]; 254 } 255} 256 257- (NSArray *) searchStrings 258{ 259 return [[fSearchField stringValue] betterComponentsSeparatedByCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]]; 260} 261 262- (void) setCountAll: (NSUInteger) all active: (NSUInteger) active downloading: (NSUInteger) downloading 263 seeding: (NSUInteger) seeding paused: (NSUInteger) paused 264{ 265 [fNoFilterButton setCount: all]; 266 [fActiveFilterButton setCount: active]; 267 [fDownloadFilterButton setCount: downloading]; 268 [fSeedFilterButton setCount: seeding]; 269 [fPauseFilterButton setCount: paused]; 270} 271 272- (void) menuNeedsUpdate: (NSMenu *) menu 273{ 274 if (menu == [fGroupsButton menu]) 275 { 276 for (NSInteger i = [menu numberOfItems]-1; i >= 3; i--) 277 [menu removeItemAtIndex: i]; 278 279 NSMenu * groupMenu = [[GroupsController groups] groupMenuWithTarget: self action: @selector(setGroupFilter:) isSmall: YES]; 280 281 const NSInteger groupMenuCount = [groupMenu numberOfItems]; 282 for (NSInteger i = 0; i < groupMenuCount; i++) 283 { 284 NSMenuItem * item = [groupMenu itemAtIndex: 0]; 285 [groupMenu removeItemAtIndex: 0]; 286 [menu addItem: item]; 287 } 288 } 289} 290 291- (BOOL) validateMenuItem: (NSMenuItem *) menuItem 292{ 293 const SEL action = [menuItem action]; 294 295 //check proper filter search item 296 if (action == @selector(setSearchType:)) 297 { 298 NSString * filterType = [[NSUserDefaults standardUserDefaults] stringForKey: @"FilterSearchType"]; 299 300 BOOL state; 301 if ([menuItem tag] == FILTER_TYPE_TAG_TRACKER) 302 state = [filterType isEqualToString: FILTER_TYPE_TRACKER]; 303 else 304 state = [filterType isEqualToString: FILTER_TYPE_NAME]; 305 306 [menuItem setState: state ? NSOnState : NSOffState]; 307 return YES; 308 } 309 310 if (action == @selector(setGroupFilter:)) 311 { 312 [menuItem setState: [menuItem tag] == [[NSUserDefaults standardUserDefaults] integerForKey: @"FilterGroup"] 313 ? NSOnState : NSOffState]; 314 return YES; 315 } 316 317 return YES; 318} 319 320@end 321 322@implementation FilterBarController (Private) 323 324- (void) resizeBar 325{ 326 //replace all buttons 327 [fNoFilterButton sizeToFit]; 328 [fActiveFilterButton sizeToFit]; 329 [fDownloadFilterButton sizeToFit]; 330 [fSeedFilterButton sizeToFit]; 331 [fPauseFilterButton sizeToFit]; 332 333 NSRect allRect = [fNoFilterButton frame]; 334 NSRect activeRect = [fActiveFilterButton frame]; 335 NSRect downloadRect = [fDownloadFilterButton frame]; 336 NSRect seedRect = [fSeedFilterButton frame]; 337 NSRect pauseRect = [fPauseFilterButton frame]; 338 339 //size search filter to not overlap buttons 340 NSRect searchFrame = [fSearchField frame]; 341 searchFrame.origin.x = NSMaxX(pauseRect) + 5.0; 342 searchFrame.size.width = NSWidth([[self view] frame]) - searchFrame.origin.x - 5.0; 343 344 //make sure it is not too long 345 if (NSWidth(searchFrame) > SEARCH_MAX_WIDTH) 346 { 347 searchFrame.origin.x += NSWidth(searchFrame) - SEARCH_MAX_WIDTH; 348 searchFrame.size.width = SEARCH_MAX_WIDTH; 349 } 350 else if (NSWidth(searchFrame) < SEARCH_MIN_WIDTH) 351 { 352 searchFrame.origin.x += NSWidth(searchFrame) - SEARCH_MIN_WIDTH; 353 searchFrame.size.width = SEARCH_MIN_WIDTH; 354 355 //calculate width the buttons can take up 356 const CGFloat allowedWidth = (searchFrame.origin.x - 5.0) - allRect.origin.x; 357 const CGFloat currentWidth = NSWidth(allRect) + NSWidth(activeRect) + NSWidth(downloadRect) + NSWidth(seedRect) 358 + NSWidth(pauseRect) + 4.0; //add 4 for space between buttons 359 const CGFloat ratio = allowedWidth / currentWidth; 360 361 //decrease button widths proportionally 362 allRect.size.width = NSWidth(allRect) * ratio; 363 activeRect.size.width = NSWidth(activeRect) * ratio; 364 downloadRect.size.width = NSWidth(downloadRect) * ratio; 365 seedRect.size.width = NSWidth(seedRect) * ratio; 366 pauseRect.size.width = NSWidth(pauseRect) * ratio; 367 } 368 else; 369 370 activeRect.origin.x = NSMaxX(allRect) + 1.0; 371 downloadRect.origin.x = NSMaxX(activeRect) + 1.0; 372 seedRect.origin.x = NSMaxX(downloadRect) + 1.0; 373 pauseRect.origin.x = NSMaxX(seedRect) + 1.0; 374 375 [fNoFilterButton setFrame: allRect]; 376 [fActiveFilterButton setFrame: activeRect]; 377 [fDownloadFilterButton setFrame: downloadRect]; 378 [fSeedFilterButton setFrame: seedRect]; 379 [fPauseFilterButton setFrame: pauseRect]; 380 381 [fSearchField setFrame: searchFrame]; 382} 383 384- (void) updateGroupsButton 385{ 386 const NSInteger groupIndex = [[NSUserDefaults standardUserDefaults] integerForKey: @"FilterGroup"]; 387 388 NSImage * icon; 389 NSString * toolTip; 390 if (groupIndex == GROUP_FILTER_ALL_TAG) 391 { 392 icon = [NSImage imageNamed: @"PinTemplate"]; 393 toolTip = NSLocalizedString(@"All Groups", "Groups -> Button"); 394 } 395 else 396 { 397 icon = [[GroupsController groups] imageForIndex: groupIndex]; 398 NSString * groupName = groupIndex != -1 ? [[GroupsController groups] nameForIndex: groupIndex] 399 : NSLocalizedString(@"None", "Groups -> Button"); 400 toolTip = [NSLocalizedString(@"Group", "Groups -> Button") stringByAppendingFormat: @": %@", groupName]; 401 } 402 403 [[[fGroupsButton menu] itemAtIndex: 0] setImage: icon]; 404 [fGroupsButton setToolTip: toolTip]; 405} 406 407- (void) updateGroups: (NSNotification *) notification 408{ 409 [self updateGroupsButton]; 410} 411 412@end 413