source: trunk/macosx/TorrentTableView.m @ 12769

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

Add some warnings and excessive Lion checks

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