1
0
Fork 0
mirror of https://github.com/transmission/transmission synced 2024-12-24 16:52:39 +00:00
transmission/macosx/FilterBarController.mm
2023-02-11 14:49:42 -06:00

451 lines
14 KiB
Text

// This file Copyright © 2011-2023 Transmission authors and contributors.
// It may be used under the MIT (SPDX: MIT) license.
// License text can be found in the licenses/ folder.
#import "FilterBarController.h"
#import "FilterButton.h"
#import "GroupsController.h"
#import "NSStringAdditions.h"
FilterType const FilterTypeNone = @"None";
FilterType const FilterTypeActive = @"Active";
FilterType const FilterTypeDownload = @"Download";
FilterType const FilterTypeSeed = @"Seed";
FilterType const FilterTypePause = @"Pause";
FilterType const FilterTypeError = @"Error";
FilterSearchType const FilterSearchTypeName = @"Name";
FilterSearchType const FilterSearchTypeTracker = @"Tracker";
NSInteger const kGroupFilterAllTag = -2;
typedef NS_ENUM(NSInteger, FilterTypeTag) {
FilterTypeTagName = 401,
FilterTypeTagTracker = 402,
};
@interface FilterBarController ()<NSSearchFieldDelegate>
@property(nonatomic) IBOutlet FilterButton* fNoFilterButton;
@property(nonatomic) IBOutlet FilterButton* fActiveFilterButton;
@property(nonatomic) IBOutlet FilterButton* fDownloadFilterButton;
@property(nonatomic) IBOutlet FilterButton* fSeedFilterButton;
@property(nonatomic) IBOutlet FilterButton* fPauseFilterButton;
@property(nonatomic) IBOutlet FilterButton* fErrorFilterButton;
@property(nonatomic) IBOutlet NSSearchField* fSearchField;
@property(nonatomic) IBOutlet NSLayoutConstraint* fSearchFieldMinWidthConstraint;
@property(nonatomic) IBOutlet NSPopUpButton* fGroupsButton;
@end
@implementation FilterBarController
- (instancetype)init
{
self = [super initWithNibName:@"FilterBar" bundle:nil];
return self;
}
- (void)awakeFromNib
{
//localizations
self.fNoFilterButton.title = NSLocalizedString(@"All", "Filter Bar -> filter button");
self.fActiveFilterButton.title = NSLocalizedString(@"Active", "Filter Bar -> filter button");
self.fDownloadFilterButton.title = NSLocalizedString(@"Downloading", "Filter Bar -> filter button");
self.fSeedFilterButton.title = NSLocalizedString(@"Seeding", "Filter Bar -> filter button");
self.fPauseFilterButton.title = NSLocalizedString(@"Paused", "Filter Bar -> filter button");
self.fErrorFilterButton.title = NSLocalizedString(@"Error", "Filter Bar -> filter button");
self.fNoFilterButton.cell.backgroundStyle = NSBackgroundStyleRaised;
self.fActiveFilterButton.cell.backgroundStyle = NSBackgroundStyleRaised;
self.fDownloadFilterButton.cell.backgroundStyle = NSBackgroundStyleRaised;
self.fSeedFilterButton.cell.backgroundStyle = NSBackgroundStyleRaised;
self.fPauseFilterButton.cell.backgroundStyle = NSBackgroundStyleRaised;
self.fErrorFilterButton.cell.backgroundStyle = NSBackgroundStyleRaised;
[self.fSearchField.searchMenuTemplate itemWithTag:FilterTypeTagName].title = NSLocalizedString(@"Name", "Filter Bar -> filter menu");
[self.fSearchField.searchMenuTemplate itemWithTag:FilterTypeTagTracker].title = NSLocalizedString(@"Tracker", "Filter Bar -> filter menu");
[self.fGroupsButton.menu itemWithTag:kGroupFilterAllTag].title = NSLocalizedString(@"All Groups", "Filter Bar -> group filter menu");
//set current filter
NSString* filterType = [NSUserDefaults.standardUserDefaults stringForKey:@"Filter"];
NSButton* currentFilterButton;
if ([filterType isEqualToString:FilterTypeActive])
{
currentFilterButton = self.fActiveFilterButton;
}
else if ([filterType isEqualToString:FilterTypePause])
{
currentFilterButton = self.fPauseFilterButton;
}
else if ([filterType isEqualToString:FilterTypeSeed])
{
currentFilterButton = self.fSeedFilterButton;
}
else if ([filterType isEqualToString:FilterTypeDownload])
{
currentFilterButton = self.fDownloadFilterButton;
}
else if ([filterType isEqualToString:FilterTypeError])
{
currentFilterButton = self.fErrorFilterButton;
}
else
{
//safety
if (![filterType isEqualToString:FilterTypeNone])
{
[NSUserDefaults.standardUserDefaults setObject:FilterTypeNone forKey:@"Filter"];
}
currentFilterButton = self.fNoFilterButton;
}
currentFilterButton.state = NSControlStateValueOn;
//set filter search type
NSString* filterSearchType = [NSUserDefaults.standardUserDefaults stringForKey:@"FilterSearchType"];
NSMenu* filterSearchMenu = self.fSearchField.searchMenuTemplate;
NSString* filterSearchTypeTitle;
if ([filterSearchType isEqualToString:FilterSearchTypeTracker])
{
filterSearchTypeTitle = [filterSearchMenu itemWithTag:FilterTypeTagTracker].title;
}
else
{
//safety
if (![filterType isEqualToString:FilterSearchTypeName])
{
[NSUserDefaults.standardUserDefaults setObject:FilterSearchTypeName forKey:@"FilterSearchType"];
}
filterSearchTypeTitle = [filterSearchMenu itemWithTag:FilterTypeTagName].title;
}
self.fSearchField.placeholderString = filterSearchTypeTitle;
NSString* searchString;
if ((searchString = [NSUserDefaults.standardUserDefaults stringForKey:@"FilterSearchString"]))
{
self.fSearchField.stringValue = searchString;
}
[self updateGroupsButton];
// update when groups change
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(updateGroups:) name:@"UpdateGroups" object:nil];
// update when filter change
self.fSearchField.delegate = self;
}
- (void)dealloc
{
[NSNotificationCenter.defaultCenter removeObserver:self];
}
- (void)setFilter:(id)sender
{
NSString* oldFilterType = [NSUserDefaults.standardUserDefaults stringForKey:@"Filter"];
NSButton* prevFilterButton;
if ([oldFilterType isEqualToString:FilterTypePause])
{
prevFilterButton = self.fPauseFilterButton;
}
else if ([oldFilterType isEqualToString:FilterTypeActive])
{
prevFilterButton = self.fActiveFilterButton;
}
else if ([oldFilterType isEqualToString:FilterTypeSeed])
{
prevFilterButton = self.fSeedFilterButton;
}
else if ([oldFilterType isEqualToString:FilterTypeDownload])
{
prevFilterButton = self.fDownloadFilterButton;
}
else if ([oldFilterType isEqualToString:FilterTypeError])
{
prevFilterButton = self.fErrorFilterButton;
}
else
{
prevFilterButton = self.fNoFilterButton;
}
if (sender != prevFilterButton)
{
prevFilterButton.state = NSControlStateValueOff;
[sender setState:NSControlStateValueOn];
FilterType filterType;
if (sender == self.fActiveFilterButton)
{
filterType = FilterTypeActive;
}
else if (sender == self.fDownloadFilterButton)
{
filterType = FilterTypeDownload;
}
else if (sender == self.fPauseFilterButton)
{
filterType = FilterTypePause;
}
else if (sender == self.fSeedFilterButton)
{
filterType = FilterTypeSeed;
}
else if (sender == self.fErrorFilterButton)
{
filterType = FilterTypeError;
}
else
{
filterType = FilterTypeNone;
}
[NSUserDefaults.standardUserDefaults setObject:filterType forKey:@"Filter"];
}
else
{
[sender setState:NSControlStateValueOn];
}
[NSNotificationCenter.defaultCenter postNotificationName:@"ApplyFilter" object:nil];
}
- (void)switchFilter:(BOOL)right
{
NSString* filterType = [NSUserDefaults.standardUserDefaults stringForKey:@"Filter"];
NSButton* button;
if ([filterType isEqualToString:FilterTypeNone])
{
button = right ? self.fActiveFilterButton : self.fErrorFilterButton;
}
else if ([filterType isEqualToString:FilterTypeActive])
{
button = right ? self.fDownloadFilterButton : self.fNoFilterButton;
}
else if ([filterType isEqualToString:FilterTypeDownload])
{
button = right ? self.fSeedFilterButton : self.fActiveFilterButton;
}
else if ([filterType isEqualToString:FilterTypeSeed])
{
button = right ? self.fPauseFilterButton : self.fDownloadFilterButton;
}
else if ([filterType isEqualToString:FilterTypePause])
{
button = right ? self.fErrorFilterButton : self.fSeedFilterButton;
}
else if ([filterType isEqualToString:FilterTypeError])
{
button = right ? self.fNoFilterButton : self.fPauseFilterButton;
}
else
{
button = self.fNoFilterButton;
}
[self setFilter:button];
}
- (void)setSearchText:(id)sender
{
[NSUserDefaults.standardUserDefaults setObject:self.fSearchField.stringValue forKey:@"FilterSearchString"];
[NSNotificationCenter.defaultCenter postNotificationName:@"ApplyFilter" object:nil];
}
- (void)focusSearchField
{
[self.view.window makeFirstResponder:self.fSearchField];
}
- (BOOL)isFocused
{
NSTextView* textView = (NSTextView*)self.fSearchField.window.firstResponder;
return [self.fSearchField.window.firstResponder isKindOfClass:NSTextView.class] &&
[self.fSearchField.window fieldEditor:NO forObject:nil] != nil && [self.fSearchField isEqualTo:textView.delegate];
}
- (void)searchFieldDidStartSearching:(NSSearchField*)sender
{
[self.fSearchFieldMinWidthConstraint animator].constant = 95;
}
- (void)searchFieldDidEndSearching:(NSSearchField*)sender
{
[self.fSearchFieldMinWidthConstraint animator].constant = 48;
}
- (void)setSearchType:(id)sender
{
NSString* oldFilterType = [NSUserDefaults.standardUserDefaults stringForKey:@"FilterSearchType"];
NSInteger prevTag, currentTag = [sender tag];
if ([oldFilterType isEqualToString:FilterSearchTypeTracker])
{
prevTag = FilterTypeTagTracker;
}
else
{
prevTag = FilterTypeTagName;
}
if (currentTag != prevTag)
{
FilterSearchType filterType;
if (currentTag == FilterTypeTagTracker)
{
filterType = FilterSearchTypeTracker;
}
else
{
filterType = FilterSearchTypeName;
}
[NSUserDefaults.standardUserDefaults setObject:filterType forKey:@"FilterSearchType"];
self.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:kGroupFilterAllTag forKey:@"FilterGroup"];
if (updateUI)
{
[self updateGroupsButton];
[self setFilter:self.fNoFilterButton];
self.fSearchField.stringValue = @"";
[self setSearchText:self.fSearchField];
}
else
{
[NSUserDefaults.standardUserDefaults setObject:FilterTypeNone forKey:@"Filter"];
[NSUserDefaults.standardUserDefaults removeObjectForKey:@"FilterSearchString"];
}
}
- (NSArray<NSString*>*)searchStrings
{
return [self.fSearchField.stringValue nonEmptyComponentsSeparatedByCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet];
}
- (void)setCountAll:(NSUInteger)all
active:(NSUInteger)active
downloading:(NSUInteger)downloading
seeding:(NSUInteger)seeding
paused:(NSUInteger)paused
error:(NSUInteger)error
{
self.fNoFilterButton.count = all;
self.fActiveFilterButton.count = active;
self.fDownloadFilterButton.count = downloading;
self.fSeedFilterButton.count = seeding;
self.fPauseFilterButton.count = paused;
self.fErrorFilterButton.count = error;
}
- (void)menuNeedsUpdate:(NSMenu*)menu
{
if (menu == self.fGroupsButton.menu)
{
//remove all items except first three
for (NSInteger i = menu.numberOfItems - 1; i >= 3; i--)
{
[menu removeItemAtIndex:i];
}
NSMenu* groupMenu = [GroupsController.groups groupMenuWithTarget:self action:@selector(setGroupFilter:) isSmall:YES];
NSInteger const 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
{
SEL const action = menuItem.action;
//check proper filter search item
if (action == @selector(setSearchType:))
{
NSString* filterType = [NSUserDefaults.standardUserDefaults stringForKey:@"FilterSearchType"];
BOOL state;
if (menuItem.tag == FilterTypeTagTracker)
{
state = [filterType isEqualToString:FilterSearchTypeTracker];
}
else
{
state = [filterType isEqualToString:FilterSearchTypeName];
}
menuItem.state = state ? NSControlStateValueOn : NSControlStateValueOff;
return YES;
}
if (action == @selector(setGroupFilter:))
{
menuItem.state = menuItem.tag == [NSUserDefaults.standardUserDefaults integerForKey:@"FilterGroup"] ? NSControlStateValueOn :
NSControlStateValueOff;
return YES;
}
return YES;
}
#pragma mark - Private
- (void)updateGroupsButton
{
NSInteger const groupIndex = [NSUserDefaults.standardUserDefaults integerForKey:@"FilterGroup"];
NSImage* icon;
NSString* toolTip;
if (groupIndex == kGroupFilterAllTag)
{
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];
}
[self.fGroupsButton.menu itemAtIndex:0].image = icon;
self.fGroupsButton.toolTip = toolTip;
}
- (void)updateGroups:(NSNotification*)notification
{
[self updateGroupsButton];
}
@end