mirror of
https://github.com/transmission/transmission.git
synced 2026-02-15 07:26:49 +00:00
* Set filter to "All" when hiding the filter bar Otherwise the filters will reset but the tab, for example "Downloading" will still be selected when the filter is shown again. We want to keep the tabs synchronized with filters state. * Remove unused BOOL parameter (the method is always called with YES) --------- Co-authored-by: beyondcompute <beyondcompute@users.noreply.github.com>
440 lines
14 KiB
Plaintext
440 lines
14 KiB
Plaintext
// This file Copyright © 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
|
|
{
|
|
[super 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)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
|
|
{
|
|
[NSUserDefaults.standardUserDefaults setInteger:kGroupFilterAllTag forKey:@"FilterGroup"];
|
|
|
|
[self updateGroupsButton];
|
|
|
|
[self setFilter:self.fNoFilterButton];
|
|
|
|
self.fSearchField.stringValue = @"";
|
|
[self setSearchText:self.fSearchField];
|
|
}
|
|
|
|
- (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
|