source: trunk/macosx/TorrentTableView.m @ 10206

Last change on this file since 10206 was 10206, checked in by livings124, 12 years ago

remove temporary asserts

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