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