source: trunk/macosx/InfoTrackersViewController.m @ 12991

Last change on this file since 12991 was 12991, checked in by livings124, 11 years ago

Silence some new warnings with LLVM 3.0/Xcode 4.2

  • Property svn:keywords set to Date Rev Author Id
File size: 13.5 KB
Line 
1/******************************************************************************
2 * $Id: InfoTrackersViewController.m 12991 2011-10-18 02:30:22Z livings124 $
3 *
4 * Copyright (c) 2010-2011 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 "InfoTrackersViewController.h"
26#import "Torrent.h"
27#import "TrackerCell.h"
28#import "TrackerNode.h"
29#import "TrackerTableView.h"
30
31#define TRACKER_GROUP_SEPARATOR_HEIGHT 14.0
32
33#define TRACKER_ADD_TAG 0
34#define TRACKER_REMOVE_TAG 1
35
36
37@interface InfoTrackersViewController (Private)
38
39- (void) setupInfo;
40
41- (void) addTrackers;
42- (void) removeTrackers;
43
44@end
45
46@implementation InfoTrackersViewController
47
48- (id) init
49{
50    if ((self = [super initWithNibName: @"InfoTrackersView" bundle: nil]))
51    {
52        [self setTitle: NSLocalizedString(@"Trackers", "Inspector view -> title")];
53       
54        fTrackerCell = [[TrackerCell alloc] init];
55    }
56   
57    return self;
58}
59
60- (void) awakeFromNib
61{
62    [[fTrackerAddRemoveControl cell] setToolTip: NSLocalizedString(@"Add a tracker", "Inspector view -> tracker buttons")
63        forSegment: TRACKER_ADD_TAG];
64    [[fTrackerAddRemoveControl cell] setToolTip: NSLocalizedString(@"Remove selected trackers", "Inspector view -> tracker buttons")
65        forSegment: TRACKER_REMOVE_TAG];
66   
67    const CGFloat height = [[NSUserDefaults standardUserDefaults] floatForKey: @"InspectorContentHeightTracker"];
68    if (height != 0.0)
69    {
70        NSRect viewRect = [[self view] frame];
71        viewRect.size.height = height;
72        [[self view] setFrame: viewRect];
73    }
74}
75
76- (void) dealloc
77{
78    [fTorrents release];
79    [fTrackers release];
80    [fTrackerCell release];
81   
82    [super dealloc];
83}
84
85- (void) setInfoForTorrents: (NSArray *) torrents
86{
87    //don't check if it's the same in case the metadata changed
88    [fTorrents release];
89    fTorrents = [torrents retain];
90   
91    fSet = NO;
92}
93
94- (void) updateInfo
95{
96    if (!fSet)
97        [self setupInfo];
98   
99    if ([fTorrents count] == 0)
100        return;
101   
102    //get updated tracker stats
103    if ([fTrackerTable editedRow] == -1)
104    {
105        [fTrackers release];
106       
107        if ([fTorrents count] == 1)
108            fTrackers = [[[fTorrents objectAtIndex: 0] allTrackerStats] retain];
109        else
110        {
111            fTrackers = [[NSMutableArray alloc] init];
112            for (Torrent * torrent in fTorrents)
113                [fTrackers addObjectsFromArray: [torrent allTrackerStats]];
114        }
115       
116        [fTrackerTable setTrackers: fTrackers];
117        [fTrackerTable reloadData];
118    }
119    else
120    {
121        NSAssert1([fTorrents count] == 1, @"Attempting to add tracker with %d transfers selected", [fTorrents count]);
122       
123        NSIndexSet * addedIndexes = [NSIndexSet indexSetWithIndexesInRange: NSMakeRange([fTrackers count]-2, 2)];
124        NSArray * tierAndTrackerBeingAdded = [fTrackers objectsAtIndexes: addedIndexes];
125       
126        [fTrackers release];
127        fTrackers = [[[fTorrents objectAtIndex: 0] allTrackerStats] retain];
128        [fTrackers addObjectsFromArray: tierAndTrackerBeingAdded];
129       
130        [fTrackerTable setTrackers: fTrackers];
131       
132        NSIndexSet * updateIndexes = [NSIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [fTrackers count]-2)],
133                * columnIndexes = [NSIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [[fTrackerTable tableColumns] count])];
134        [fTrackerTable reloadDataForRowIndexes: updateIndexes columnIndexes: columnIndexes];
135    }
136}
137
138- (void) saveViewSize
139{
140    [[NSUserDefaults standardUserDefaults] setFloat: NSHeight([[self view] frame]) forKey: @"InspectorContentHeightTracker"];
141}
142
143- (void) clearView
144{
145    [fTrackers release];
146    fTrackers = nil;
147}
148
149- (NSInteger) numberOfRowsInTableView: (NSTableView *) tableView
150{
151    return fTrackers ? [fTrackers count] : 0;
152}
153
154- (id) tableView: (NSTableView *) tableView objectValueForTableColumn: (NSTableColumn *) column row: (NSInteger) row
155{
156    id item = [fTrackers objectAtIndex: row];
157   
158    if ([item isKindOfClass: [NSDictionary class]])
159    {
160        const NSInteger tier = [[item objectForKey: @"Tier"] integerValue];
161        NSString * tierString = tier == -1 ? NSLocalizedString(@"New Tier", "Inspector -> tracker table")
162                                : [NSString stringWithFormat: NSLocalizedString(@"Tier %d", "Inspector -> tracker table"), tier];
163       
164        if ([fTorrents count] > 1)
165            tierString = [tierString stringByAppendingFormat: @" - %@", [item objectForKey: @"Name"]];
166        return tierString;
167    }
168    else
169        return item; //TrackerNode or NSString
170}
171
172- (NSCell *) tableView: (NSTableView *) tableView dataCellForTableColumn: (NSTableColumn *) tableColumn row: (NSInteger) row
173{
174    const BOOL tracker = [[fTrackers objectAtIndex: row] isKindOfClass: [TrackerNode class]];
175    return tracker ? fTrackerCell : [tableColumn dataCellForRow: row];
176}
177
178- (CGFloat) tableView: (NSTableView *) tableView heightOfRow: (NSInteger) row
179{
180    //check for NSDictionay instead of TrackerNode because of display issue when adding a row
181    if ([[fTrackers objectAtIndex: row] isKindOfClass: [NSDictionary class]])
182        return TRACKER_GROUP_SEPARATOR_HEIGHT;
183    else
184        return [tableView rowHeight];
185}
186
187- (BOOL) tableView: (NSTableView *) tableView shouldEditTableColumn: (NSTableColumn *) tableColumn row: (NSInteger) row
188{
189    //don't allow tier row to be edited by double-click
190    return NO;
191}
192
193- (void) tableViewSelectionDidChange: (NSNotification *) notification
194{
195    [fTrackerAddRemoveControl setEnabled: [fTrackerTable numberOfSelectedRows] > 0 forSegment: TRACKER_REMOVE_TAG];
196}
197
198- (BOOL) tableView: (NSTableView *) tableView isGroupRow: (NSInteger) row
199{
200    return ![[fTrackers objectAtIndex: row] isKindOfClass: [TrackerNode class]] && [tableView editedRow] != row;
201}
202
203- (NSString *) tableView: (NSTableView *) tableView toolTipForCell: (NSCell *) cell rect: (NSRectPointer) rect
204                tableColumn: (NSTableColumn *) column row: (NSInteger) row mouseLocation: (NSPoint) mouseLocation
205{
206    id node = [fTrackers objectAtIndex: row];
207    if ([node isKindOfClass: [TrackerNode class]])
208        return [(TrackerNode *)node fullAnnounceAddress];
209    else
210        return nil;
211}
212
213- (void) tableView: (NSTableView *) tableView setObjectValue: (id) object forTableColumn: (NSTableColumn *) tableColumn
214    row: (NSInteger) row
215{
216    Torrent * torrent= [fTorrents objectAtIndex: 0];
217   
218    BOOL added = NO;
219    for (NSString * tracker in [object componentsSeparatedByString: @"\n"])
220        if ([torrent addTrackerToNewTier: tracker])
221            added = YES;
222   
223    if (!added)
224        NSBeep();
225   
226    //reset table with either new or old value
227    [fTrackers release];
228    fTrackers = [[torrent allTrackerStats] retain];
229   
230    [fTrackerTable setTrackers: fTrackers];
231    [fTrackerTable reloadData];
232    [fTrackerTable deselectAll: self];
233   
234    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil]; //incase sort by tracker
235}
236
237- (void) addRemoveTracker: (id) sender
238{
239    //don't allow add/remove when currently adding - it leads to weird results
240    if ([fTrackerTable editedRow] != -1)
241        return;
242   
243    [self updateInfo];
244   
245    if ([[sender cell] tagForSegment: [sender selectedSegment]] == TRACKER_REMOVE_TAG)
246        [self removeTrackers];
247    else
248        [self addTrackers];
249}
250
251@end
252
253@implementation InfoTrackersViewController (Private)
254
255- (void) setupInfo
256{
257    const NSUInteger numberSelected = [fTorrents count];
258    if (numberSelected != 1)
259    {
260        if (numberSelected == 0)
261        {
262            [fTrackers release];
263            fTrackers = nil;
264           
265            [fTrackerTable setTrackers: nil];
266            [fTrackerTable reloadData];
267        }
268       
269        [fTrackerTable setTorrent: nil];
270       
271        [fTrackerAddRemoveControl setEnabled: NO forSegment: TRACKER_ADD_TAG];
272        [fTrackerAddRemoveControl setEnabled: NO forSegment: TRACKER_REMOVE_TAG];
273    }
274    else
275    {
276        [fTrackerTable setTorrent: [fTorrents objectAtIndex: 0]];
277       
278        [fTrackerAddRemoveControl setEnabled: YES forSegment: TRACKER_ADD_TAG];
279        [fTrackerAddRemoveControl setEnabled: NO forSegment: TRACKER_REMOVE_TAG];
280    }
281   
282    [fTrackerTable deselectAll: self];
283   
284    fSet = YES;
285}
286
287#warning doesn't like blank addresses
288- (void) addTrackers
289{
290    [[[self view] window] makeKeyWindow];
291   
292    NSAssert1([fTorrents count] == 1, @"Attempting to add tracker with %d transfers selected", [fTorrents count]);
293   
294    [fTrackers addObject: [NSDictionary dictionaryWithObject: [NSNumber numberWithInteger: -1] forKey: @"Tier"]];
295    [fTrackers addObject: @""];
296   
297    [fTrackerTable setTrackers: fTrackers];
298    [fTrackerTable reloadData];
299    [fTrackerTable selectRowIndexes: [NSIndexSet indexSetWithIndex: [fTrackers count]-1] byExtendingSelection: NO];
300    [fTrackerTable editColumn: [fTrackerTable columnWithIdentifier: @"Tracker"] row: [fTrackers count]-1 withEvent: nil select: YES];
301}
302
303- (void) removeTrackers
304{
305    NSMutableDictionary * removeIdentifiers = [NSMutableDictionary dictionaryWithCapacity: [fTorrents count]];
306    NSUInteger removeCount = 0;
307       
308    NSIndexSet * selectedIndexes = [fTrackerTable selectedRowIndexes];
309    BOOL groupSelected = NO;
310    for (NSUInteger i = 0; i < [fTrackers count]; ++i)
311    {
312        id object = [fTrackers objectAtIndex: i];
313        if ([object isKindOfClass: [TrackerNode class]])
314        {
315            if (groupSelected || [selectedIndexes containsIndex: i])
316            {
317                Torrent * torrent = [(TrackerNode *)object torrent];
318                NSMutableSet * removeSet;
319                if (!(removeSet = [removeIdentifiers objectForKey: torrent]))
320                {
321                    removeSet = [NSMutableSet set];
322                    [removeIdentifiers setObject: removeSet forKey: torrent];
323                }
324               
325                [removeSet addObject: [(TrackerNode *)object fullAnnounceAddress]];
326                ++removeCount;
327            }
328        }
329        else
330        {
331            groupSelected = [selectedIndexes containsIndex: i];
332            if (!groupSelected && i > [selectedIndexes lastIndex])
333                break;
334        }
335    }
336   
337    //we might have no trackers if remove right after a failed add (race condition ftw)
338    #warning look into having a failed add apply right away, so that this can become an assert
339    if (removeCount == 0)
340        return;
341   
342    if ([[NSUserDefaults standardUserDefaults] boolForKey: @"WarningRemoveTrackers"])
343    {
344        NSAlert * alert = [[NSAlert alloc] init];
345       
346        if (removeCount > 1)
347        {
348            [alert setMessageText: [NSString stringWithFormat: NSLocalizedString(@"Are you sure you want to remove %d trackers?",
349                                                                "Remove trackers alert -> title"), removeCount]];
350            [alert setInformativeText: NSLocalizedString(@"Once removed, Transmission will no longer attempt to contact them."
351                                        " This cannot be undone.", "Remove trackers alert -> message")];
352        }
353        else
354        {
355            [alert setMessageText: NSLocalizedString(@"Are you sure you want to remove this tracker?", "Remove trackers alert -> title")];
356            [alert setInformativeText: NSLocalizedString(@"Once removed, Transmission will no longer attempt to contact it."
357                                        " This cannot be undone.", "Remove trackers alert -> message")];
358        }
359       
360        [alert addButtonWithTitle: NSLocalizedString(@"Remove", "Remove trackers alert -> button")];
361        [alert addButtonWithTitle: NSLocalizedString(@"Cancel", "Remove trackers alert -> button")];
362       
363        [alert setShowsSuppressionButton: YES];
364
365        NSInteger result = [alert runModal];
366        if ([[alert suppressionButton] state] == NSOnState)
367            [[NSUserDefaults standardUserDefaults] setBool: NO forKey: @"WarningRemoveTrackers"];
368        [alert release];
369       
370        if (result != NSAlertFirstButtonReturn)
371            return;
372    }
373   
374    for (Torrent * torrent in removeIdentifiers)
375        [torrent removeTrackers: [removeIdentifiers objectForKey: torrent]];
376   
377    //reset table with either new or old value
378    [fTrackers release];
379    fTrackers = [[NSMutableArray alloc] init];
380    for (Torrent * torrent in fTorrents)
381        [fTrackers addObjectsFromArray: [torrent allTrackerStats]];
382   
383    [fTrackerTable setTrackers: fTrackers];
384    [fTrackerTable reloadData];
385    [fTrackerTable deselectAll: self];
386   
387    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil]; //incase sort by tracker
388}
389
390@end
Note: See TracBrowser for help on using the repository browser.