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