/****************************************************************************** * Copyright (c) 2011-2012 Transmission authors and contributors * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. *****************************************************************************/ #import "FilterBarController.h" #import "FilterButton.h" #import "GroupsController.h" #import "NSStringAdditions.h" #define FILTER_TYPE_TAG_NAME 401 #define FILTER_TYPE_TAG_TRACKER 402 #define SEARCH_MIN_WIDTH 48.0 #define SEARCH_MAX_WIDTH 95.0 @interface FilterBarController (Private) - (void) resizeBar; - (void) updateGroupsButton; - (void) updateGroups: (NSNotification *) notification; @end @implementation FilterBarController - (instancetype) init { return (self = [super initWithNibName: @"FilterBar" bundle: nil]); } - (void) awakeFromNib { //localizations fNoFilterButton.title = NSLocalizedString(@"All", "Filter Bar -> filter button"); fActiveFilterButton.title = NSLocalizedString(@"Active", "Filter Bar -> filter button"); fDownloadFilterButton.title = NSLocalizedString(@"Downloading", "Filter Bar -> filter button"); fSeedFilterButton.title = NSLocalizedString(@"Seeding", "Filter Bar -> filter button"); fPauseFilterButton.title = NSLocalizedString(@"Paused", "Filter Bar -> filter button"); fNoFilterButton.cell.backgroundStyle = NSBackgroundStyleRaised; fActiveFilterButton.cell.backgroundStyle = NSBackgroundStyleRaised; fDownloadFilterButton.cell.backgroundStyle = NSBackgroundStyleRaised; fSeedFilterButton.cell.backgroundStyle = NSBackgroundStyleRaised; fPauseFilterButton.cell.backgroundStyle = NSBackgroundStyleRaised; [fSearchField.searchMenuTemplate itemWithTag: FILTER_TYPE_TAG_NAME].title = NSLocalizedString(@"Name", "Filter Bar -> filter menu"); [fSearchField.searchMenuTemplate itemWithTag: FILTER_TYPE_TAG_TRACKER].title = NSLocalizedString(@"Tracker", "Filter Bar -> filter menu"); [fGroupsButton.menu itemWithTag: GROUP_FILTER_ALL_TAG].title = NSLocalizedString(@"All Groups", "Filter Bar -> group filter menu"); [self resizeBar]; //set current filter NSString * filterType = [NSUserDefaults.standardUserDefaults stringForKey: @"Filter"]; NSButton * currentFilterButton; if ([filterType isEqualToString: FILTER_ACTIVE]) currentFilterButton = fActiveFilterButton; else if ([filterType isEqualToString: FILTER_PAUSE]) currentFilterButton = fPauseFilterButton; else if ([filterType isEqualToString: FILTER_SEED]) currentFilterButton = fSeedFilterButton; else if ([filterType isEqualToString: FILTER_DOWNLOAD]) currentFilterButton = fDownloadFilterButton; else { //safety if (![filterType isEqualToString: FILTER_NONE]) [NSUserDefaults.standardUserDefaults setObject: FILTER_NONE forKey: @"Filter"]; currentFilterButton = fNoFilterButton; } currentFilterButton.state = NSOnState; //set filter search type NSString * filterSearchType = [NSUserDefaults.standardUserDefaults stringForKey: @"FilterSearchType"]; NSMenu * filterSearchMenu = fSearchField.searchMenuTemplate; NSString * filterSearchTypeTitle; if ([filterSearchType isEqualToString: FILTER_TYPE_TRACKER]) filterSearchTypeTitle = [filterSearchMenu itemWithTag: FILTER_TYPE_TAG_TRACKER].title; else { //safety if (![filterType isEqualToString: FILTER_TYPE_NAME]) [NSUserDefaults.standardUserDefaults setObject: FILTER_TYPE_NAME forKey: @"FilterSearchType"]; filterSearchTypeTitle = [filterSearchMenu itemWithTag: FILTER_TYPE_TAG_NAME].title; } fSearchField.placeholderString = filterSearchTypeTitle; NSString * searchString; if ((searchString = [NSUserDefaults.standardUserDefaults stringForKey: @"FilterSearchString"])) fSearchField.stringValue = searchString; [self updateGroupsButton]; [NSNotificationCenter.defaultCenter addObserver: self selector: @selector(resizeBar) name: NSWindowDidResizeNotification object: self.view.window]; //update when groups change [NSNotificationCenter.defaultCenter addObserver: self selector: @selector(updateGroups:) name: @"UpdateGroups" object: nil]; } - (void) dealloc { [NSNotificationCenter.defaultCenter removeObserver: self]; } - (void) setFilter: (id) sender { NSString * oldFilterType = [NSUserDefaults.standardUserDefaults stringForKey: @"Filter"]; NSButton * prevFilterButton; if ([oldFilterType isEqualToString: FILTER_PAUSE]) prevFilterButton = fPauseFilterButton; else if ([oldFilterType isEqualToString: FILTER_ACTIVE]) prevFilterButton = fActiveFilterButton; else if ([oldFilterType isEqualToString: FILTER_SEED]) prevFilterButton = fSeedFilterButton; else if ([oldFilterType isEqualToString: FILTER_DOWNLOAD]) prevFilterButton = fDownloadFilterButton; else prevFilterButton = fNoFilterButton; if (sender != prevFilterButton) { prevFilterButton.state = NSOffState; [sender setState: NSOnState]; NSString * filterType; if (sender == fActiveFilterButton) filterType = FILTER_ACTIVE; else if (sender == fDownloadFilterButton) filterType = FILTER_DOWNLOAD; else if (sender == fPauseFilterButton) filterType = FILTER_PAUSE; else if (sender == fSeedFilterButton) filterType = FILTER_SEED; else filterType = FILTER_NONE; [NSUserDefaults.standardUserDefaults setObject: filterType forKey: @"Filter"]; } else [sender setState: NSOnState]; [NSNotificationCenter.defaultCenter postNotificationName: @"ApplyFilter" object: nil]; } - (void) switchFilter: (BOOL) right { NSString * filterType = [NSUserDefaults.standardUserDefaults stringForKey: @"Filter"]; NSButton * button; if ([filterType isEqualToString: FILTER_NONE]) button = right ? fActiveFilterButton : fPauseFilterButton; else if ([filterType isEqualToString: FILTER_ACTIVE]) button = right ? fDownloadFilterButton : fNoFilterButton; else if ([filterType isEqualToString: FILTER_DOWNLOAD]) button = right ? fSeedFilterButton : fActiveFilterButton; else if ([filterType isEqualToString: FILTER_SEED]) button = right ? fPauseFilterButton : fDownloadFilterButton; else if ([filterType isEqualToString: FILTER_PAUSE]) button = right ? fNoFilterButton : fSeedFilterButton; else button = fNoFilterButton; [self setFilter: button]; } - (void) setSearchText: (id) sender { [NSUserDefaults.standardUserDefaults setObject: fSearchField.stringValue forKey: @"FilterSearchString"]; [NSNotificationCenter.defaultCenter postNotificationName: @"ApplyFilter" object: nil]; } - (void) focusSearchField { [self.view.window makeFirstResponder: fSearchField]; } - (void) setSearchType: (id) sender { NSString * oldFilterType = [NSUserDefaults.standardUserDefaults stringForKey: @"FilterSearchType"]; NSInteger prevTag, currentTag = [sender tag]; if ([oldFilterType isEqualToString: FILTER_TYPE_TRACKER]) prevTag = FILTER_TYPE_TAG_TRACKER; else prevTag = FILTER_TYPE_TAG_NAME; if (currentTag != prevTag) { NSString * filterType; if (currentTag == FILTER_TYPE_TAG_TRACKER) filterType = FILTER_TYPE_TRACKER; else filterType = FILTER_TYPE_NAME; [NSUserDefaults.standardUserDefaults setObject: filterType forKey: @"FilterSearchType"]; fSearchField.placeholderString = [sender title]; } [NSNotificationCenter.defaultCenter postNotificationName: @"ApplyFilter" object: nil]; } - (void) setGroupFilter: (id) sender { [NSUserDefaults.standardUserDefaults setInteger: [sender tag] forKey: @"FilterGroup"]; [self updateGroupsButton]; [NSNotificationCenter.defaultCenter postNotificationName: @"ApplyFilter" object: nil]; } - (void) reset: (BOOL) updateUI { [NSUserDefaults.standardUserDefaults setInteger: GROUP_FILTER_ALL_TAG forKey: @"FilterGroup"]; if (updateUI) { [self updateGroupsButton]; [self setFilter: fNoFilterButton]; fSearchField.stringValue = @""; [self setSearchText: fSearchField]; } else { [NSUserDefaults.standardUserDefaults setObject: FILTER_NONE forKey: @"Filter"]; [NSUserDefaults.standardUserDefaults removeObjectForKey: @"FilterSearchString"]; } } - (NSArray *) searchStrings { return [fSearchField.stringValue betterComponentsSeparatedByCharactersInSet: NSCharacterSet.whitespaceAndNewlineCharacterSet]; } - (void) setCountAll: (NSUInteger) all active: (NSUInteger) active downloading: (NSUInteger) downloading seeding: (NSUInteger) seeding paused: (NSUInteger) paused { [fNoFilterButton setCount: all]; [fActiveFilterButton setCount: active]; [fDownloadFilterButton setCount: downloading]; [fSeedFilterButton setCount: seeding]; [fPauseFilterButton setCount: paused]; } - (void) menuNeedsUpdate: (NSMenu *) menu { if (menu == fGroupsButton.menu) { for (NSInteger i = menu.numberOfItems-1; i >= 3; i--) [menu removeItemAtIndex: i]; NSMenu * groupMenu = [GroupsController.groups groupMenuWithTarget: self action: @selector(setGroupFilter:) isSmall: YES]; const NSInteger groupMenuCount = groupMenu.numberOfItems; for (NSInteger i = 0; i < groupMenuCount; i++) { NSMenuItem * item = [groupMenu itemAtIndex: 0]; [groupMenu removeItemAtIndex: 0]; [menu addItem: item]; } } } - (BOOL) validateMenuItem: (NSMenuItem *) menuItem { const SEL action = menuItem.action; //check proper filter search item if (action == @selector(setSearchType:)) { NSString * filterType = [NSUserDefaults.standardUserDefaults stringForKey: @"FilterSearchType"]; BOOL state; if (menuItem.tag == FILTER_TYPE_TAG_TRACKER) state = [filterType isEqualToString: FILTER_TYPE_TRACKER]; else state = [filterType isEqualToString: FILTER_TYPE_NAME]; menuItem.state = state ? NSOnState : NSOffState; return YES; } if (action == @selector(setGroupFilter:)) { menuItem.state = menuItem.tag == [NSUserDefaults.standardUserDefaults integerForKey: @"FilterGroup"] ? NSOnState : NSOffState; return YES; } return YES; } @end @implementation FilterBarController (Private) - (void) resizeBar { //replace all buttons [fNoFilterButton sizeToFit]; [fActiveFilterButton sizeToFit]; [fDownloadFilterButton sizeToFit]; [fSeedFilterButton sizeToFit]; [fPauseFilterButton sizeToFit]; NSRect allRect = fNoFilterButton.frame; NSRect activeRect = fActiveFilterButton.frame; NSRect downloadRect = fDownloadFilterButton.frame; NSRect seedRect = fSeedFilterButton.frame; NSRect pauseRect = fPauseFilterButton.frame; //size search filter to not overlap buttons NSRect searchFrame = fSearchField.frame; searchFrame.origin.x = NSMaxX(pauseRect) + 5.0; searchFrame.size.width = NSWidth(self.view.frame) - searchFrame.origin.x - 5.0; //make sure it is not too long if (NSWidth(searchFrame) > SEARCH_MAX_WIDTH) { searchFrame.origin.x += NSWidth(searchFrame) - SEARCH_MAX_WIDTH; searchFrame.size.width = SEARCH_MAX_WIDTH; } else if (NSWidth(searchFrame) < SEARCH_MIN_WIDTH) { searchFrame.origin.x += NSWidth(searchFrame) - SEARCH_MIN_WIDTH; searchFrame.size.width = SEARCH_MIN_WIDTH; //calculate width the buttons can take up const CGFloat allowedWidth = (searchFrame.origin.x - 5.0) - allRect.origin.x; const CGFloat currentWidth = NSWidth(allRect) + NSWidth(activeRect) + NSWidth(downloadRect) + NSWidth(seedRect) + NSWidth(pauseRect) + 4.0; //add 4 for space between buttons const CGFloat ratio = allowedWidth / currentWidth; //decrease button widths proportionally allRect.size.width = NSWidth(allRect) * ratio; activeRect.size.width = NSWidth(activeRect) * ratio; downloadRect.size.width = NSWidth(downloadRect) * ratio; seedRect.size.width = NSWidth(seedRect) * ratio; pauseRect.size.width = NSWidth(pauseRect) * ratio; } else; activeRect.origin.x = NSMaxX(allRect) + 1.0; downloadRect.origin.x = NSMaxX(activeRect) + 1.0; seedRect.origin.x = NSMaxX(downloadRect) + 1.0; pauseRect.origin.x = NSMaxX(seedRect) + 1.0; fNoFilterButton.frame = allRect; fActiveFilterButton.frame = activeRect; fDownloadFilterButton.frame = downloadRect; fSeedFilterButton.frame = seedRect; fPauseFilterButton.frame = pauseRect; fSearchField.frame = searchFrame; } - (void) updateGroupsButton { const NSInteger groupIndex = [NSUserDefaults.standardUserDefaults integerForKey: @"FilterGroup"]; NSImage * icon; NSString * toolTip; if (groupIndex == GROUP_FILTER_ALL_TAG) { icon = [NSImage imageNamed: @"PinTemplate"]; toolTip = NSLocalizedString(@"All Groups", "Groups -> Button"); } else { icon = [GroupsController.groups imageForIndex: groupIndex]; NSString * groupName = groupIndex != -1 ? [GroupsController.groups nameForIndex: groupIndex] : NSLocalizedString(@"None", "Groups -> Button"); toolTip = [NSLocalizedString(@"Group", "Groups -> Button") stringByAppendingFormat: @": %@", groupName]; } [fGroupsButton.menu itemAtIndex: 0].image = icon; fGroupsButton.toolTip = toolTip; } - (void) updateGroups: (NSNotification *) notification { [self updateGroupsButton]; } @end