source: trunk/macosx/TorrentTableView.m @ 11409

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

a couple of float -> double changes

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