source: trunk/macosx/TorrentTableView.m @ 7659

Last change on this file since 7659 was 7659, checked in by livings124, 13 years ago

update the Mac code's copyright dates

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