source: trunk/macosx/TorrentTableView.m @ 13166

Last change on this file since 13166 was 13166, checked in by livings124, 10 years ago

Reselect proper rows after removing transfers on Snow Leopard

  • Property svn:keywords set to Date Rev Author Id
File size: 29.6 KB
Line 
1/******************************************************************************
2 * $Id: TorrentTableView.m 13166 2012-01-15 07:39:24Z livings124 $
3 *
4 * Copyright (c) 2005-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 "TorrentTableView.h"
26#import "Controller.h"
27#import "FileListNode.h"
28#import "InfoOptionsViewController.h"
29#import "NSApplicationAdditions.h"
30#import "NSStringAdditions.h"
31#import "Torrent.h"
32#import "TorrentCell.h"
33#import "TorrentGroup.h"
34
35#define MAX_GROUP 999999
36
37//eliminate when Lion-only
38#define ACTION_MENU_GLOBAL_TAG 101
39#define ACTION_MENU_UNLIMITED_TAG 102
40#define ACTION_MENU_LIMIT_TAG 103
41
42#define ACTION_MENU_PRIORITY_HIGH_TAG 101
43#define ACTION_MENU_PRIORITY_NORMAL_TAG 102
44#define ACTION_MENU_PRIORITY_LOW_TAG 103
45
46#define TOGGLE_PROGRESS_SECONDS 0.175
47
48@interface TorrentTableView (Private)
49
50- (BOOL) pointInGroupStatusRect: (NSPoint) point;
51
52- (void) setGroupStatusColumns;
53
54@end
55
56@implementation TorrentTableView
57
58- (id) initWithCoder: (NSCoder *) decoder
59{
60    if ((self = [super initWithCoder: decoder]))
61    {
62        fDefaults = [NSUserDefaults standardUserDefaults];
63       
64        fTorrentCell = [[TorrentCell alloc] init];
65       
66        NSData * groupData = [fDefaults dataForKey: @"CollapsedGroups"];
67        if (groupData)
68            fCollapsedGroups = [[NSUnarchiver unarchiveObjectWithData: groupData] mutableCopy];
69        else
70            fCollapsedGroups = [[NSMutableIndexSet alloc] init];
71       
72        fMouseRow = -1;
73        fMouseControlRow = -1;
74        fMouseRevealRow = -1;
75        fMouseActionRow = -1;
76        #warning we can get rid of the on 10.7
77        fActionPushedRow = -1;
78       
79        fActionPopoverShown = NO;
80       
81        [self setDelegate: self];
82       
83        fPiecesBarPercent = [fDefaults boolForKey: @"PiecesBar"] ? 1.0 : 0.0;
84    }
85   
86    return self;
87}
88
89- (void) dealloc
90{
91    [[NSNotificationCenter defaultCenter] removeObserver: self];
92   
93    [fCollapsedGroups release];
94   
95    [fPiecesBarAnimation release];
96    [fMenuTorrent release];
97   
98    [fSelectedValues release];
99   
100    [fTorrentCell release];
101   
102    [super dealloc];
103}
104
105- (void) awakeFromNib
106{
107    //set group columns to show ratio, needs to be in awakeFromNib to size columns correctly
108    [self setGroupStatusColumns];
109   
110    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(reloadData)
111                                                 name: @"ReloadTorrentTable" object: nil];
112}
113
114- (BOOL) isGroupCollapsed: (NSInteger) value
115{
116    if (value == -1)
117        value = MAX_GROUP;
118   
119    return [fCollapsedGroups containsIndex: value];
120}
121
122- (void) removeCollapsedGroup: (NSInteger) value
123{
124    if (value == -1)
125        value = MAX_GROUP;
126   
127    [fCollapsedGroups removeIndex: value];
128}
129
130- (void) removeAllCollapsedGroups
131{
132    [fCollapsedGroups removeAllIndexes];
133}
134
135- (void) saveCollapsedGroups
136{
137    [fDefaults setObject: [NSArchiver archivedDataWithRootObject: fCollapsedGroups] forKey: @"CollapsedGroups"];
138}
139
140- (BOOL) outlineView: (NSOutlineView *) outlineView isGroupItem: (id) item
141{
142    return ![item isKindOfClass: [Torrent class]];
143}
144
145- (CGFloat) outlineView: (NSOutlineView *) outlineView heightOfRowByItem: (id) item
146{
147    return [item isKindOfClass: [Torrent class]] ? [self rowHeight] : GROUP_SEPARATOR_HEIGHT;
148}
149
150- (NSCell *) outlineView: (NSOutlineView *) outlineView dataCellForTableColumn: (NSTableColumn *) tableColumn item: (id) item
151{
152    const BOOL group = ![item isKindOfClass: [Torrent class]];
153    if (!tableColumn)
154        return !group ? fTorrentCell : nil;
155    else
156        return group ? [tableColumn dataCellForRow: [self rowForItem: item]] : nil;
157}
158
159- (void) outlineView: (NSOutlineView *) outlineView willDisplayCell: (id) cell forTableColumn: (NSTableColumn *) tableColumn
160    item: (id) item
161{
162    if ([item isKindOfClass: [Torrent class]])
163    {
164        if (!tableColumn)
165        {
166            [cell setRepresentedObject: item];
167           
168            const NSInteger row = [self rowForItem: item];
169            [cell setHover: row == fMouseRow];
170            [cell setControlHover: row == fMouseControlRow];
171            [cell setRevealHover: row == fMouseRevealRow];
172            [cell setActionHover: row == fMouseActionRow];
173            [cell setActionPushed: row == fActionPushedRow];
174        }
175    }
176    else
177    {
178        NSString * ident = [tableColumn identifier];
179        if ([ident isEqualToString: @"UL Image"] || [ident isEqualToString: @"DL Image"])
180        {
181            //ensure arrows are white only when selected
182            [[cell image] setTemplate: [cell backgroundStyle] == NSBackgroundStyleLowered];
183        }
184    }
185}
186
187- (NSRect) frameOfCellAtColumn: (NSInteger) column row: (NSInteger) row
188{
189    if (column == -1)
190        return [self rectOfRow: row];
191    else
192    {
193        NSRect rect = [super frameOfCellAtColumn: column row: row];
194       
195        //adjust placement for proper vertical alignment
196        if (column == [self columnWithIdentifier: @"Group"])
197            rect.size.height -= 1.0f;
198       
199        return rect;
200    }
201}
202
203- (NSString *) outlineView: (NSOutlineView *) outlineView typeSelectStringForTableColumn: (NSTableColumn *) tableColumn item: (id) item
204{
205    return [item isKindOfClass: [Torrent class]] ? [(Torrent *)item name]
206            : [[self preparedCellAtColumn: [self columnWithIdentifier: @"Group"] row: [self rowForItem: item]] stringValue];
207}
208
209- (NSString *) outlineView: (NSOutlineView *) outlineView toolTipForCell: (NSCell *) cell rect: (NSRectPointer) rect tableColumn: (NSTableColumn *) column item: (id) item mouseLocation: (NSPoint) mouseLocation
210{
211    NSString * ident = [column identifier];
212    if ([ident isEqualToString: @"DL"] || [ident isEqualToString: @"DL Image"])
213        return NSLocalizedString(@"Download speed", "Torrent table -> group row -> tooltip");
214    else if ([ident isEqualToString: @"UL"] || [ident isEqualToString: @"UL Image"])
215        return [fDefaults boolForKey: @"DisplayGroupRowRatio"] ? NSLocalizedString(@"Ratio", "Torrent table -> group row -> tooltip")
216                : NSLocalizedString(@"Upload speed", "Torrent table -> group row -> tooltip");
217    else if (ident)
218    {
219        NSUInteger count = [[item torrents] count];
220        if (count == 1)
221            return NSLocalizedString(@"1 transfer", "Torrent table -> group row -> tooltip");
222        else
223            return [NSString stringWithFormat: NSLocalizedString(@"%@ transfers", "Torrent table -> group row -> tooltip"),
224                    [NSString formattedUInteger: count]];
225    }
226    else
227        return nil;
228}
229
230- (void) updateTrackingAreas
231{
232    [super updateTrackingAreas];
233    [self removeTrackingAreas];
234   
235    const NSRange rows = [self rowsInRect: [self visibleRect]];
236    if (rows.length == 0)
237        return;
238   
239    NSPoint mouseLocation = [self convertPoint: [[self window] convertScreenToBase: [NSEvent mouseLocation]] fromView: nil];
240    for (NSUInteger row = rows.location; row < NSMaxRange(rows); row++)
241    {
242        if (![[self itemAtRow: row] isKindOfClass: [Torrent class]])
243            continue;
244       
245        NSDictionary * userInfo = [NSDictionary dictionaryWithObject: [NSNumber numberWithInteger: row] forKey: @"Row"];
246        TorrentCell * cell = (TorrentCell *)[self preparedCellAtColumn: -1 row: row];
247        [cell addTrackingAreasForView: self inRect: [self rectOfRow: row] withUserInfo: userInfo mouseLocation: mouseLocation];
248    }
249}
250
251- (void) removeTrackingAreas
252{
253    fMouseRow = -1;
254    fMouseControlRow = -1;
255    fMouseRevealRow = -1;
256    fMouseActionRow = -1;
257   
258    for (NSTrackingArea * area in [self trackingAreas])
259    {
260        if ([area owner] == self && [[area userInfo] objectForKey: @"Row"])
261            [self removeTrackingArea: area];
262    }
263}
264
265- (void) setRowHover: (NSInteger) row
266{
267    NSAssert([fDefaults boolForKey: @"SmallView"], @"cannot set a hover row when not in compact view");
268   
269    fMouseRow = row;
270    if (row >= 0)
271        [self setNeedsDisplayInRect: [self rectOfRow: row]];
272}
273
274- (void) setControlButtonHover: (NSInteger) row
275{
276    fMouseControlRow = row;
277    if (row >= 0)
278        [self setNeedsDisplayInRect: [self rectOfRow: row]];
279}
280
281- (void) setRevealButtonHover: (NSInteger) row
282{
283    fMouseRevealRow = row;
284    if (row >= 0)
285        [self setNeedsDisplayInRect: [self rectOfRow: row]];
286}
287
288- (void) setActionButtonHover: (NSInteger) row
289{
290    fMouseActionRow = row;
291    if (row >= 0)
292        [self setNeedsDisplayInRect: [self rectOfRow: row]];
293}
294
295- (void) mouseEntered: (NSEvent *) event
296{
297    NSDictionary * dict = (NSDictionary *)[event userData];
298   
299    NSNumber * row;
300    if ((row = [dict objectForKey: @"Row"]))
301    {
302        NSInteger rowVal = [row integerValue];
303        NSString * type = [dict objectForKey: @"Type"];
304        if ([type isEqualToString: @"Action"])
305            fMouseActionRow = rowVal;
306        else if ([type isEqualToString: @"Control"])
307            fMouseControlRow = rowVal;
308        else if ([type isEqualToString: @"Reveal"])
309            fMouseRevealRow = rowVal;
310        else
311        {
312            fMouseRow = rowVal;
313            if (![fDefaults boolForKey: @"SmallView"])
314                return;
315        }
316       
317        [self setNeedsDisplayInRect: [self rectOfRow: rowVal]];
318    }
319}
320
321- (void) mouseExited: (NSEvent *) event
322{
323    NSDictionary * dict = (NSDictionary *)[event userData];
324   
325    NSNumber * row;
326    if ((row = [dict objectForKey: @"Row"]))
327    {
328        NSString * type = [dict objectForKey: @"Type"];
329        if ([type isEqualToString: @"Action"])
330            fMouseActionRow = -1;
331        else if ([type isEqualToString: @"Control"])
332            fMouseControlRow = -1;
333        else if ([type isEqualToString: @"Reveal"])
334            fMouseRevealRow = -1;
335        else
336        {
337            fMouseRow = -1;
338            if (![fDefaults boolForKey: @"SmallView"])
339                return;
340        }
341       
342        [self setNeedsDisplayInRect: [self rectOfRow: [row integerValue]]];
343    }
344}
345
346- (void) outlineViewSelectionIsChanging: (NSNotification *) notification
347{
348    #warning elliminate when view-based?
349    //if pushing a button, don't change the selected rows
350    if (fSelectedValues)
351        [self selectValues: fSelectedValues];
352}
353
354- (void) outlineViewItemDidExpand: (NSNotification *) notification
355{
356    NSInteger value = [[[notification userInfo] objectForKey: @"NSObject"] groupIndex];
357    if (value < 0)
358        value = MAX_GROUP;
359   
360    if ([fCollapsedGroups containsIndex: value])
361    {
362        [fCollapsedGroups removeIndex: value];
363        [[NSNotificationCenter defaultCenter] postNotificationName: @"OutlineExpandCollapse" object: self];
364    }
365}
366
367- (void) outlineViewItemDidCollapse: (NSNotification *) notification
368{
369    NSInteger value = [[[notification userInfo] objectForKey: @"NSObject"] groupIndex];
370    if (value < 0)
371        value = MAX_GROUP;
372   
373    [fCollapsedGroups addIndex: value];
374    [[NSNotificationCenter defaultCenter] postNotificationName: @"OutlineExpandCollapse" object: self];
375}
376
377- (void) mouseDown: (NSEvent *) event
378{
379    NSPoint point = [self convertPoint: [event locationInWindow] fromView: nil];
380    const NSInteger row = [self rowAtPoint: point];
381   
382    //check to toggle group status before anything else
383    if ([self pointInGroupStatusRect: point])
384    {
385        [fDefaults setBool: ![fDefaults boolForKey: @"DisplayGroupRowRatio"] forKey: @"DisplayGroupRowRatio"];
386        [self setGroupStatusColumns];
387       
388        return;
389    }
390   
391    const BOOL pushed = row != -1 && (fMouseActionRow == row || fMouseRevealRow == row || fMouseControlRow == row);
392   
393    //if pushing a button, don't change the selected rows
394    if (pushed)
395        fSelectedValues = [[self selectedValues] retain];
396   
397    [super mouseDown: event];
398   
399    [fSelectedValues release];
400    fSelectedValues = nil;
401   
402    //avoid weird behavior when showing menu by doing this after mouse down
403    if (row != -1 && fMouseActionRow == row)
404    {
405        if (![NSApp isOnLionOrBetter])
406        {
407            fActionPushedRow = row;
408            [self setNeedsDisplayInRect: [self rectOfRow: row]]; //ensure button is pushed down
409        }
410       
411        #warning maybe make appear on mouse down
412        [self displayTorrentActionPopoverForEvent: event];
413       
414        if (![NSApp isOnLionOrBetter])
415        {
416            fActionPushedRow = -1;
417            [self setNeedsDisplayInRect: [self rectOfRow: row]];
418        }
419    }
420    else if (!pushed && [event clickCount] == 2) //double click
421    {
422        id item = nil;
423        if (row != -1)
424            item = [self itemAtRow: row];
425       
426        if (!item || [item isKindOfClass: [Torrent class]])
427            [fController showInfo: nil];
428        else
429        {
430            if ([self isItemExpanded: item])
431                [self collapseItem: item];
432            else
433                [self expandItem: item];
434        }
435    }
436    else;
437}
438
439- (void) selectValues: (NSArray *) values
440{
441    NSMutableIndexSet * indexSet = [NSMutableIndexSet indexSet];
442   
443    for (id item in values)
444    {
445        if ([item isKindOfClass: [Torrent class]])
446        {
447            const NSInteger index = [self rowForItem: item];
448            if (index != -1)
449                [indexSet addIndex: index];
450        }
451        else
452        {
453            const NSInteger group = [item groupIndex];
454            for (NSInteger i = 0; i < [self numberOfRows]; i++)
455            {
456                id tableItem = [self itemAtRow: i];
457                if ([tableItem isKindOfClass: [TorrentGroup class]] && group == [tableItem groupIndex])
458                {
459                    [indexSet addIndex: i];
460                    break;
461                }
462            }
463        }
464    }
465   
466    [self selectRowIndexes: indexSet byExtendingSelection: NO];
467}
468
469- (NSArray *) selectedValues
470{
471    NSIndexSet * selectedIndexes = [self selectedRowIndexes];
472    NSMutableArray * values = [NSMutableArray arrayWithCapacity: [selectedIndexes count]];
473   
474    for (NSUInteger i = [selectedIndexes firstIndex]; i != NSNotFound; i = [selectedIndexes indexGreaterThanIndex: i])
475        [values addObject: [self itemAtRow: i]];
476   
477    return values;
478}
479
480- (NSArray *) selectedTorrents
481{
482    NSIndexSet * selectedIndexes = [self selectedRowIndexes];
483    NSMutableArray * torrents = [NSMutableArray arrayWithCapacity: [selectedIndexes count]]; //take a shot at guessing capacity
484   
485    for (NSUInteger i = [selectedIndexes firstIndex]; i != NSNotFound; i = [selectedIndexes indexGreaterThanIndex: i])
486    {
487        id item = [self itemAtRow: i];
488        if ([item isKindOfClass: [Torrent class]])
489            [torrents addObject: item];
490        else
491        {
492            NSArray * groupTorrents = [item torrents];
493            [torrents addObjectsFromArray: groupTorrents];
494            if ([self isItemExpanded: item])
495                i +=[groupTorrents count];
496        }
497    }
498   
499    return torrents;
500}
501
502- (NSMenu *) menuForEvent: (NSEvent *) event
503{
504    NSInteger row = [self rowAtPoint: [self convertPoint: [event locationInWindow] fromView: nil]];
505    if (row >= 0)
506    {
507        if (![self isRowSelected: row])
508            [self selectRowIndexes: [NSIndexSet indexSetWithIndex: row] byExtendingSelection: NO];
509        return fContextRow;
510    }
511    else
512    {
513        [self deselectAll: self];
514        return fContextNoRow;
515    }
516}
517
518//make sure that the pause buttons become orange when holding down the option key
519- (void) flagsChanged: (NSEvent *) event
520{
521    [self display];
522    [super flagsChanged: event];
523}
524
525//option-command-f will focus the filter bar's search field
526- (void) keyDown: (NSEvent *) event
527{
528    const unichar firstChar = [[event charactersIgnoringModifiers] characterAtIndex: 0];
529   
530    if (firstChar == 'f' && [event modifierFlags] & NSAlternateKeyMask && [event modifierFlags] & NSCommandKeyMask)
531        [fController focusFilterField];
532    else if (firstChar == ' ')
533        [fController toggleQuickLook: nil];
534    else if ([event keyCode] == 53) //esc key
535        [self deselectAll: nil];
536    else
537        [super keyDown: event];
538}
539
540- (NSRect) iconRectForRow: (NSInteger) row
541{
542    return [fTorrentCell iconRectForBounds: [self rectOfRow: row]];
543}
544
545- (void) paste: (id) sender
546{
547    NSURL * url;
548    if ((url = [NSURL URLFromPasteboard: [NSPasteboard generalPasteboard]]))
549        [fController openURL: [url absoluteString]];
550    else if ([NSApp isOnLionOrBetter])
551    {
552        NSArray * items = [[NSPasteboard generalPasteboard] readObjectsForClasses: [NSArray arrayWithObject: [NSString class]] options: nil];
553        if (items)
554        {
555            NSDataDetector * detector = [NSDataDetectorLion dataDetectorWithTypes: NSTextCheckingTypeLink error: nil];
556            for (NSString * pbItem in items)
557            {
558                if ([pbItem rangeOfString: @"magnet:" options: (NSAnchoredSearch | NSCaseInsensitiveSearch)].location != NSNotFound)
559                    [fController openURL: pbItem];
560                else
561                {
562                    #warning only accept full text?
563                    for (NSTextCheckingResult * result in [detector matchesInString: pbItem options: 0 range: NSMakeRange(0, [pbItem length])])
564                        [fController openURL: [[result URL] absoluteString]];
565                }
566            }
567        }
568    }
569}
570
571- (BOOL) validateMenuItem: (NSMenuItem *) menuItem
572{
573    SEL action = [menuItem action];
574   
575    if (action == @selector(paste:))
576    {
577        if ([[[NSPasteboard generalPasteboard] types] containsObject: NSURLPboardType])
578            return YES;
579       
580        if ([NSApp isOnLionOrBetter])
581        {
582            NSArray * items = [[NSPasteboard generalPasteboard] readObjectsForClasses: [NSArray arrayWithObject: [NSString class]] options: nil];
583            if (items)
584            {
585                NSDataDetector * detector = [NSDataDetectorLion dataDetectorWithTypes: NSTextCheckingTypeLink error: nil];
586                for (NSString * pbItem in items)
587                {
588                    if (([pbItem rangeOfString: @"magnet:" options: (NSAnchoredSearch | NSCaseInsensitiveSearch)].location != NSNotFound)
589                        || [detector firstMatchInString: pbItem options: 0 range: NSMakeRange(0, [pbItem length])])
590                        return YES;
591                }
592            }
593        }
594       
595        return NO;
596    }
597   
598    return YES;
599}
600
601- (void) toggleControlForTorrent: (Torrent *) torrent
602{
603    if ([torrent isActive])
604        [fController stopTorrents: [NSArray arrayWithObject: torrent]];
605    else
606    {
607        if ([NSEvent modifierFlags] & NSAlternateKeyMask)
608            [fController resumeTorrentsNoWait: [NSArray arrayWithObject: torrent]];
609        else if ([torrent waitingToStart])
610            [fController stopTorrents: [NSArray arrayWithObject: torrent]];
611        else
612            [fController resumeTorrents: [NSArray arrayWithObject: torrent]];
613    }
614}
615
616- (void) displayTorrentActionPopoverForEvent: (NSEvent *) event
617{
618    const NSInteger row = [self rowAtPoint: [self convertPoint: [event locationInWindow] fromView: nil]];
619    if (row < 0)
620        return;
621   
622    const NSRect rect = [fTorrentCell iconRectForBounds: [self rectOfRow: row]];
623   
624    if ([NSApp isOnLionOrBetter])
625    {
626        if (fActionPopoverShown)
627            return;
628       
629        Torrent * torrent = [self itemAtRow: row];
630       
631        NSPopover * popover = [[NSPopoverLion alloc] init];
632        [popover setBehavior: NSPopoverBehaviorTransient];
633        InfoOptionsViewController * infoViewController = [[InfoOptionsViewController alloc] init];
634        [popover setContentViewController: infoViewController];
635        [popover setDelegate: self];
636       
637        [popover showRelativeToRect: rect ofView: self preferredEdge: NSMaxYEdge];
638        [infoViewController setInfoForTorrents: [NSArray arrayWithObject: torrent]];
639        [infoViewController updateInfo];
640       
641        [infoViewController release];
642        [popover release];
643    }
644    else
645    {
646        //update file action menu
647        fMenuTorrent = [[self itemAtRow: row] retain];
648       
649        //update global limit check
650        [fGlobalLimitItem setState: [fMenuTorrent usesGlobalSpeedLimit] ? NSOnState : NSOffState];
651       
652        //place menu below button
653        NSPoint location = rect.origin;
654        location.y += NSHeight(rect) + 5.0;
655       
656        location = [self convertPoint: location toView: self];
657        [fActionMenu popUpMenuPositioningItem: nil atLocation: location inView: self];
658       
659        [fMenuTorrent release];
660        fMenuTorrent = nil;
661    }
662}
663
664//don't show multiple popovers when clicking the gear button repeatedly
665- (void) popoverWillShow: (NSNotification *) notification
666{
667    fActionPopoverShown = YES;
668}
669
670- (void) popoverWillClose: (NSNotification *) notification
671{
672    fActionPopoverShown = NO;
673}
674
675//eliminate when Lion-only, along with all the menu item instance variables
676- (void) menuNeedsUpdate: (NSMenu *) menu
677{
678    //this method seems to be called when it shouldn't be
679    if (!fMenuTorrent || ![menu supermenu])
680        return;
681   
682    if (menu == fUploadMenu || menu == fDownloadMenu)
683    {
684        NSMenuItem * item;
685        if ([menu numberOfItems] == 3)
686        {
687            const NSInteger speedLimitActionValue[] = { 0, 5, 10, 20, 30, 40, 50, 75, 100, 150, 200, 250, 500,
688                                                        750, 1000, 1500, 2000, -1 };
689           
690            for (NSInteger i = 0; speedLimitActionValue[i] != -1; i++)
691            {
692                item = [[NSMenuItem alloc] initWithTitle: [NSString stringWithFormat: NSLocalizedString(@"%d KB/s",
693                        "Action menu -> upload/download limit"), speedLimitActionValue[i]] action: @selector(setQuickLimit:)
694                        keyEquivalent: @""];
695                [item setTarget: self];
696                [item setRepresentedObject: [NSNumber numberWithInt: speedLimitActionValue[i]]];
697                [menu addItem: item];
698                [item release];
699            }
700        }
701       
702        const BOOL upload = menu == fUploadMenu;
703        const BOOL limit = [fMenuTorrent usesSpeedLimit: upload];
704       
705        item = [menu itemWithTag: ACTION_MENU_LIMIT_TAG];
706        [item setState: limit ? NSOnState : NSOffState];
707        [item setTitle: [NSString stringWithFormat: NSLocalizedString(@"Limit (%d KB/s)",
708                            "torrent action menu -> upload/download limit"), [fMenuTorrent speedLimit: upload]]];
709       
710        item = [menu itemWithTag: ACTION_MENU_UNLIMITED_TAG];
711        [item setState: !limit ? NSOnState : NSOffState];
712    }
713    else if (menu == fRatioMenu)
714    {
715        NSMenuItem * item;
716        if ([menu numberOfItems] == 4)
717        {
718            const float ratioLimitActionValue[] = { 0.25, 0.5, 0.75, 1.0, 1.5, 2.0, 3.0, -1.0 };
719           
720            for (NSInteger i = 0; ratioLimitActionValue[i] != -1.0; i++)
721            {
722                item = [[NSMenuItem alloc] initWithTitle: [NSString localizedStringWithFormat: @"%.2f", ratioLimitActionValue[i]]
723                        action: @selector(setQuickRatio:) keyEquivalent: @""];
724                [item setTarget: self];
725                [item setRepresentedObject: [NSNumber numberWithFloat: ratioLimitActionValue[i]]];
726                [menu addItem: item];
727                [item release];
728            }
729        }
730       
731        const tr_ratiolimit mode = [fMenuTorrent ratioSetting];
732       
733        item = [menu itemWithTag: ACTION_MENU_LIMIT_TAG];
734        [item setState: mode == TR_RATIOLIMIT_SINGLE ? NSOnState : NSOffState];
735        [item setTitle: [NSString localizedStringWithFormat: NSLocalizedString(@"Stop at Ratio (%.2f)",
736            "torrent action menu -> ratio stop"), [fMenuTorrent ratioLimit]]];
737       
738        item = [menu itemWithTag: ACTION_MENU_UNLIMITED_TAG];
739        [item setState: mode == TR_RATIOLIMIT_UNLIMITED ? NSOnState : NSOffState];
740       
741        item = [menu itemWithTag: ACTION_MENU_GLOBAL_TAG];
742        [item setState: mode == TR_RATIOLIMIT_GLOBAL ? NSOnState : NSOffState];
743    }
744    else if (menu == fPriorityMenu)
745    {
746        const tr_priority_t priority = [fMenuTorrent priority];
747       
748        NSMenuItem * item = [menu itemWithTag: ACTION_MENU_PRIORITY_HIGH_TAG];
749        [item setState: priority == TR_PRI_HIGH ? NSOnState : NSOffState];
750       
751        item = [menu itemWithTag: ACTION_MENU_PRIORITY_NORMAL_TAG];
752        [item setState: priority == TR_PRI_NORMAL ? NSOnState : NSOffState];
753       
754        item = [menu itemWithTag: ACTION_MENU_PRIORITY_LOW_TAG];
755        [item setState: priority == TR_PRI_LOW ? NSOnState : NSOffState];
756    }
757}
758
759//the following methods might not be needed when Lion-only
760- (void) setQuickLimitMode: (id) sender
761{
762    const BOOL limit = [sender tag] == ACTION_MENU_LIMIT_TAG;
763    [fMenuTorrent setUseSpeedLimit: limit upload: [sender menu] == fUploadMenu];
764   
765    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateOptions" object: nil];
766}
767
768- (void) setQuickLimit: (id) sender
769{
770    const BOOL upload = [sender menu] == fUploadMenu;
771    [fMenuTorrent setUseSpeedLimit: YES upload: upload];
772    [fMenuTorrent setSpeedLimit: [[sender representedObject] intValue] upload: upload];
773   
774    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateOptions" object: nil];
775}
776
777- (void) setGlobalLimit: (id) sender
778{
779    [fMenuTorrent setUseGlobalSpeedLimit: [(NSButton *)sender state] != NSOnState];
780   
781    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateOptions" object: nil];
782}
783
784- (void) setQuickRatioMode: (id) sender
785{
786    tr_ratiolimit mode;
787    switch ([sender tag])
788    {
789        case ACTION_MENU_UNLIMITED_TAG:
790            mode = TR_RATIOLIMIT_UNLIMITED;
791            break;
792        case ACTION_MENU_LIMIT_TAG:
793            mode = TR_RATIOLIMIT_SINGLE;
794            break;
795        case ACTION_MENU_GLOBAL_TAG:
796            mode = TR_RATIOLIMIT_GLOBAL;
797            break;
798        default:
799            return;
800    }
801   
802    [fMenuTorrent setRatioSetting: mode];
803   
804    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateOptions" object: nil];
805}
806
807- (void) setQuickRatio: (id) sender
808{
809    [fMenuTorrent setRatioSetting: TR_RATIOLIMIT_SINGLE];
810    [fMenuTorrent setRatioLimit: [[sender representedObject] floatValue]];
811   
812    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateOptions" object: nil];
813}
814
815- (void) setPriority: (id) sender
816{
817    tr_priority_t priority;
818    switch ([sender tag])
819    {
820        case ACTION_MENU_PRIORITY_HIGH_TAG:
821            priority = TR_PRI_HIGH;
822            break;
823        case ACTION_MENU_PRIORITY_NORMAL_TAG:
824            priority = TR_PRI_NORMAL;
825            break;
826        case ACTION_MENU_PRIORITY_LOW_TAG:
827            priority = TR_PRI_LOW;
828            break;
829        default:
830            NSAssert1(NO, @"Unknown priority: %d", [sender tag]);
831    }
832   
833    [fMenuTorrent setPriority: priority];
834   
835    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
836}
837
838- (void) togglePiecesBar
839{
840    //stop previous animation
841    if (fPiecesBarAnimation)
842        [fPiecesBarAnimation release];
843   
844    NSMutableArray * progressMarks = [NSMutableArray arrayWithCapacity: 16];
845    for (NSAnimationProgress i = 0.0625; i <= 1.0; i += 0.0625)
846        [progressMarks addObject: [NSNumber numberWithDouble: i]];
847   
848    fPiecesBarAnimation = [[NSAnimation alloc] initWithDuration: TOGGLE_PROGRESS_SECONDS animationCurve: NSAnimationEaseIn];
849    [fPiecesBarAnimation setAnimationBlockingMode: NSAnimationNonblocking];
850    [fPiecesBarAnimation setProgressMarks: progressMarks];
851    [fPiecesBarAnimation setDelegate: self];
852   
853    [fPiecesBarAnimation startAnimation];
854}
855
856- (void) animationDidEnd: (NSAnimation *) animation
857{
858    if (animation == fPiecesBarAnimation)
859    {
860        [fPiecesBarAnimation release];
861        fPiecesBarAnimation = nil;
862    }
863}
864
865- (void) animation: (NSAnimation *) animation didReachProgressMark: (NSAnimationProgress) progress
866{
867    if (animation == fPiecesBarAnimation)
868    {
869        if ([fDefaults boolForKey: @"PiecesBar"])
870            fPiecesBarPercent = progress;
871        else
872            fPiecesBarPercent = 1.0 - progress;
873       
874        [self reloadData];
875    }
876}
877
878- (CGFloat) piecesBarPercent
879{
880    return fPiecesBarPercent;
881}
882
883@end
884
885@implementation TorrentTableView (Private)
886
887- (BOOL) pointInGroupStatusRect: (NSPoint) point
888{
889    NSInteger row = [self rowAtPoint: point];
890    if (row < 0 || [[self itemAtRow: row] isKindOfClass: [Torrent class]])
891        return NO;
892   
893    NSString * ident = [[[self tableColumns] objectAtIndex: [self columnAtPoint: point]] identifier];
894    return [ident isEqualToString: @"UL"] || [ident isEqualToString: @"UL Image"]
895            || [ident isEqualToString: @"DL"] || [ident isEqualToString: @"DL Image"];
896}
897
898- (void) setGroupStatusColumns
899{
900    const BOOL ratio = [fDefaults boolForKey: @"DisplayGroupRowRatio"];
901   
902    [[self tableColumnWithIdentifier: @"DL"] setHidden: ratio];
903    [[self tableColumnWithIdentifier: @"DL Image"] setHidden: ratio];
904}
905
906@end
Note: See TracBrowser for help on using the repository browser.