source: trunk/macosx/TorrentTableView.m @ 5063

Last change on this file since 5063 was 5063, checked in by livings124, 14 years ago

ensure up and down group arrows are colored correctly

  • Property svn:keywords set to Date Rev Author Id
File size: 26.1 KB
Line 
1/******************************************************************************
2 * $Id: TorrentTableView.m 5063 2008-02-19 02:59:01Z livings124 $
3 *
4 * Copyright (c) 2005-2008 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 "TorrentCell.h"
27#import "Torrent.h"
28#import "NSApplicationAdditions.h"
29#import "NSMenuAdditions.h"
30
31#define MAX_GROUP (INT_MAX-10)
32
33#define ACTION_MENU_GLOBAL_TAG 101
34#define ACTION_MENU_UNLIMITED_TAG 102
35#define ACTION_MENU_LIMIT_TAG 103
36
37#define PIECE_CHANGE 0.1
38#define PIECE_TIME 0.01
39
40@interface TorrentTableView (Private)
41
42- (BOOL) pointInControlRect: (NSPoint) point;
43- (BOOL) pointInRevealRect: (NSPoint) point;
44- (BOOL) pointInActionRect: (NSPoint) point;
45
46- (BOOL) pointInProgressRect: (NSPoint) point;
47- (BOOL) pointInMinimalStatusRect: (NSPoint) point;
48- (void) updateFileMenu: (NSMenu *) menu forFiles: (NSArray *) files;
49
50- (void) resizePiecesBarIncrement;
51
52@end
53
54@implementation TorrentTableView
55
56- (id) initWithCoder: (NSCoder *) decoder
57{
58    if ((self = [super initWithCoder: decoder]))
59    {
60        fDefaults = [NSUserDefaults standardUserDefaults];
61       
62        fTorrentCell = [[TorrentCell alloc] init];
63        if (![NSApp isOnLeopardOrBetter])
64            [[self tableColumnWithIdentifier: @"Group"] setDataCell: fTorrentCell];
65       
66        NSData * groupData = [fDefaults dataForKey: @"CollapsedGroups"];
67        if (groupData)
68            fCollapsedGroups = [[NSUnarchiver unarchiveObjectWithData: groupData] mutableCopy];
69        else
70            fCollapsedGroups = [[NSMutableIndexSet alloc] init];
71       
72        fMouseControlRow = -1;
73        fMouseRevealRow = -1;
74        fMouseActionRow = -1;
75        fActionPushedRow = -1;
76       
77        [self setDelegate: self];
78       
79        fPiecesBarPercent = [fDefaults boolForKey: @"PiecesBar"] ? 1.0 : 0.0;
80    }
81   
82    return self;
83}
84
85- (void) dealloc
86{
87    [fCollapsedGroups release];
88   
89    [fPiecesBarTimer invalidate];
90    [fMenuTorrent release];
91   
92    [fSelectedValues release];
93   
94    [fTorrentCell release];
95   
96    [super dealloc];
97}
98
99- (BOOL) isGroupCollapsed: (int) value
100{
101    if (value < 0)
102        value = MAX_GROUP;
103   
104    return [fCollapsedGroups containsIndex: value];
105}
106
107- (void) removeCollapsedGroup: (int) value
108{
109    if (value < 0)
110        value = MAX_GROUP;
111   
112    [fCollapsedGroups removeIndex: value];
113}
114
115- (void) removeAllCollapsedGroups
116{
117    [fCollapsedGroups removeAllIndexes];
118}
119
120- (void) saveCollapsedGroups
121{
122    [fDefaults setObject: [NSArchiver archivedDataWithRootObject: fCollapsedGroups] forKey: @"CollapsedGroups"];
123}
124
125- (BOOL) outlineView: (NSOutlineView *) outlineView isGroupItem: (id) item
126{
127    return ![item isKindOfClass: [Torrent class]];
128}
129
130- (CGFloat) outlineView: (NSOutlineView *) outlineView heightOfRowByItem: (id) item
131{
132    return [item isKindOfClass: [Torrent class]] ? [self rowHeight] : GROUP_SEPARATOR_HEIGHT;
133}
134
135- (NSCell *) outlineView: (NSOutlineView *) outlineView dataCellForTableColumn: (NSTableColumn *) tableColumn item: (id) item
136{
137    BOOL group = ![item isKindOfClass: [Torrent class]];
138    if (!tableColumn)
139        return !group ? fTorrentCell : nil;
140    else
141        return group ? [tableColumn dataCellForRow: [self rowForItem: item]] : nil;
142}
143
144- (void) outlineView: (NSOutlineView *) outlineView willDisplayCell: (id) cell forTableColumn: (NSTableColumn *) tableColumn
145    item: (id) item
146{
147    if ([item isKindOfClass: [Torrent class]])
148    {
149        [cell setRepresentedObject: item];
150       
151        int row = [self rowForItem: item];
152        [cell setControlHover: row == fMouseControlRow];
153        [cell setRevealHover: row == fMouseRevealRow];
154        [cell setActionHover: row == fMouseActionRow];
155        [cell setActionPushed: row == fActionPushedRow];
156    }
157    else
158    {
159        if ([[tableColumn identifier] isEqualToString: @"UL Image"] || [[tableColumn identifier] isEqualToString: @"DL Image"])
160        {
161            //ensure white only when selected
162            NSImage * image = [cell image];
163            BOOL template = [cell backgroundStyle] == NSBackgroundStyleLowered;
164            if ([image isTemplate] != template)
165            {
166                [image setTemplate: template];
167                [cell setImage: nil];
168                [cell setImage: image];
169            }
170        }
171    }
172}
173
174- (NSRect) frameOfCellAtColumn: (NSInteger) column row: (NSInteger) row
175{
176    if (column == -1 || (![NSApp isOnLeopardOrBetter] && [[self itemAtRow: row] isKindOfClass: [Torrent class]]))
177        return [self rectOfRow: row];
178    else
179    {
180        NSRect rect = [super frameOfCellAtColumn: column row: row];
181       
182        //adjust placement for proper vertical alignment
183        if (column == [self columnWithIdentifier: @"Group"])
184            rect.size.height -= 1.0;
185       
186        return rect;
187    }
188}
189
190- (NSString *) outlineView: (NSOutlineView *) outlineView typeSelectStringForTableColumn: (NSTableColumn *) tableColumn item: (id) item
191{
192    return [item isKindOfClass: [Torrent class]] ? [item name]
193            : [[self preparedCellAtColumn: -1 row: [self rowForItem: item]] stringValue];
194}
195
196- (void) updateTrackingAreas
197{
198    [super updateTrackingAreas];
199    [self removeButtonTrackingAreas];
200   
201    NSRange rows = [self rowsInRect: [self visibleRect]];
202    if (rows.length == 0)
203        return;
204   
205    NSPoint mouseLocation = [self convertPoint: [[self window] convertScreenToBase: [NSEvent mouseLocation]] fromView: nil];
206   
207    NSUInteger row;
208    for (row = rows.location; row < NSMaxRange(rows); row++)
209    {
210        if (![[self itemAtRow: row] isKindOfClass: [Torrent class]])
211            continue;
212       
213        NSDictionary * userInfo = [NSDictionary dictionaryWithObject: [NSNumber numberWithInt: row] forKey: @"Row"];
214        TorrentCell * cell = (TorrentCell *)[self preparedCellAtColumn: -1 row: row];
215        [cell addTrackingAreasForView: self inRect: [self rectOfRow: row] withUserInfo: userInfo
216                mouseLocation: mouseLocation];
217    }
218}
219
220- (void) removeButtonTrackingAreas
221{
222    fMouseControlRow = -1;
223    fMouseRevealRow = -1;
224    fMouseActionRow = -1;
225   
226    if (![NSApp isOnLeopardOrBetter])
227        return;
228   
229    NSEnumerator * enumerator = [[self trackingAreas] objectEnumerator];
230    NSTrackingArea * area;
231    while ((area = [enumerator nextObject]))
232    {
233        if ([area owner] == self && [[area userInfo] objectForKey: @"Row"])
234            [self removeTrackingArea: area];
235    }
236}
237
238- (void) setControlButtonHover: (int) row
239{
240    fMouseControlRow = row;
241    if (row >= 0)
242        [self setNeedsDisplayInRect: [self rectOfRow: row]];
243}
244
245- (void) setRevealButtonHover: (int) row
246{
247    fMouseRevealRow = row;
248    if (row >= 0)
249        [self setNeedsDisplayInRect: [self rectOfRow: row]];
250}
251
252- (void) setActionButtonHover: (int) row
253{
254    fMouseActionRow = row;
255    if (row >= 0)
256        [self setNeedsDisplayInRect: [self rectOfRow: row]];
257}
258
259- (void) mouseEntered: (NSEvent *) event
260{
261    NSDictionary * dict = (NSDictionary *)[event userData];
262   
263    NSNumber * row;
264    if ((row = [dict objectForKey: @"Row"]))
265    {
266        int rowVal = [row intValue];
267        NSString * type = [dict objectForKey: @"Type"];
268        if ([type isEqualToString: @"Action"])
269            fMouseActionRow = rowVal;
270        else if ([type isEqualToString: @"Control"])
271            fMouseControlRow = rowVal;
272        else
273            fMouseRevealRow = rowVal;
274       
275        [self setNeedsDisplayInRect: [self rectOfRow: rowVal]];
276    }
277}
278
279- (void) mouseExited: (NSEvent *) event
280{
281    NSDictionary * dict = (NSDictionary *)[event userData];
282   
283    NSNumber * row;
284    if ((row = [dict objectForKey: @"Row"]))
285    {
286        NSString * type = [dict objectForKey: @"Type"];
287        if ([type isEqualToString: @"Action"])
288            fMouseActionRow = -1;
289        else if ([type isEqualToString: @"Control"])
290            fMouseControlRow = -1;
291        else
292            fMouseRevealRow = -1;
293       
294        [self setNeedsDisplayInRect: [self rectOfRow: [row intValue]]];
295    }
296}
297
298- (void) outlineViewSelectionIsChanging: (NSNotification *) notification
299{
300    if (fSelectedValues)
301        [self selectValues: fSelectedValues];
302}
303
304- (void) outlineViewItemDidExpand: (NSNotification *) notification
305{
306    int value = [[[[notification userInfo] objectForKey: @"NSObject"] objectForKey: @"Group"] intValue];
307    [fCollapsedGroups removeIndex: value >= 0 ? value : MAX_GROUP];
308   
309    [[NSNotificationCenter defaultCenter] postNotificationName: @"OutlineExpandCollapse" object: self];
310}
311
312- (void) outlineViewItemDidCollapse: (NSNotification *) notification
313{
314    int value = [[[[notification userInfo] objectForKey: @"NSObject"] objectForKey: @"Group"] intValue];
315    [fCollapsedGroups addIndex: value >= 0 ? value : MAX_GROUP];
316   
317    [[NSNotificationCenter defaultCenter] postNotificationName: @"OutlineExpandCollapse" object: self];
318}
319
320- (void) mouseDown: (NSEvent *) event
321{
322    NSPoint point = [self convertPoint: [event locationInWindow] fromView: nil];
323   
324    BOOL pushed = [self pointInControlRect: point] || [self pointInRevealRect: point] || [self pointInActionRect: point]
325                    || [self pointInProgressRect: point] || [self pointInMinimalStatusRect: point];
326   
327    //if pushing a button, don't change the selected rows
328    if (pushed)
329        fSelectedValues = [[self selectedValues] retain];
330   
331    [super mouseDown: event];
332   
333    [fSelectedValues release];
334    fSelectedValues = nil;
335   
336    //avoid weird behavior when showing menu by doing this after mouse down
337    if ([self pointInActionRect: point])
338    {
339        int row = [self rowAtPoint: point];
340       
341        fActionPushedRow = row;
342        [self setNeedsDisplayInRect: [self rectOfRow: row]]; //ensure button is pushed down
343       
344        [self displayTorrentMenuForEvent: event];
345       
346        fActionPushedRow = -1;
347        [self setNeedsDisplayInRect: [self rectOfRow: row]];
348    }
349    else if (!pushed && [event clickCount] == 2) //double click
350    {
351        id item = [self itemAtRow: [self rowAtPoint: point]];
352       
353        if ([item isKindOfClass: [Torrent class]])
354            [fController showInfo: nil];
355        else
356        {
357            if ([self isItemExpanded: item])
358                [self collapseItem: item];
359            else
360                [self expandItem: item];
361        }
362    }
363    else;
364}
365
366- (void) selectValues: (NSArray *) values
367{
368    NSMutableIndexSet * indexSet = [NSMutableIndexSet indexSet];
369   
370    NSEnumerator * enumerator = [values objectEnumerator];
371    id item;
372    while ((item = [enumerator nextObject]))
373    {
374        if ([item isKindOfClass: [Torrent class]])
375        {
376            NSUInteger index = [self rowForItem: item];
377            if (index != -1)
378                [indexSet addIndex: index];
379        }
380        else
381        {
382            NSNumber * group = [item objectForKey: @"Group"];
383            int i;
384            for (i = 0; i < [self numberOfRows]; i++)
385            {
386                id tableItem = [self itemAtRow: i];
387                if (![tableItem isKindOfClass: [Torrent class]] && [group isEqualToNumber: [tableItem objectForKey: @"Group"]])
388                {
389                    [indexSet addIndex: i];
390                    break;
391                }
392            }
393        }
394    }
395   
396    [self selectRowIndexes: indexSet byExtendingSelection: NO];
397}
398
399- (NSArray *) selectedValues
400{
401    NSIndexSet * selectedIndexes = [self selectedRowIndexes];
402    NSMutableArray * values = [NSMutableArray arrayWithCapacity: [selectedIndexes count]];
403   
404    NSUInteger i;
405    for (i = [selectedIndexes firstIndex]; i != NSNotFound; i = [selectedIndexes indexGreaterThanIndex: i])
406        [values addObject: [self itemAtRow: i]];
407   
408    return values;
409}
410
411- (NSArray *) selectedTorrents
412{
413    NSIndexSet * selectedIndexes = [self selectedRowIndexes];
414    NSMutableArray * torrents = [NSMutableArray array];
415   
416    NSUInteger i;
417    for (i = [selectedIndexes firstIndex]; i != NSNotFound; i = [selectedIndexes indexGreaterThanIndex: i])
418    {
419        id item = [self itemAtRow: i];
420        if ([item isKindOfClass: [Torrent class]])
421        {
422            if (![torrents containsObject: item])
423                [torrents addObject: item];
424        }
425        else
426            [torrents addObjectsFromArray: [item objectForKey: @"Torrents"]];
427    }
428   
429    return torrents;
430}
431
432- (NSMenu *) menuForEvent: (NSEvent *) event
433{
434    int row = [self rowAtPoint: [self convertPoint: [event locationInWindow] fromView: nil]];
435    if (row >= 0)
436    {
437        if (![self isRowSelected: row])
438            [self selectRowIndexes: [NSIndexSet indexSetWithIndex: row] byExtendingSelection: NO];
439        return fContextRow;
440    }
441    else
442    {
443        [self deselectAll: self];
444        return fContextNoRow;
445    }
446}
447
448//make sure that the pause buttons become orange when holding down the option key
449- (void) flagsChanged: (NSEvent *) event
450{
451    [self display];
452    [super flagsChanged: event];
453}
454
455- (void) toggleControlForTorrent: (Torrent *) torrent
456{
457    if ([torrent isActive])
458        [fController stopTorrents: [NSArray arrayWithObject: torrent]];
459    else
460    {
461        if ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask)
462            [fController resumeTorrentsNoWait: [NSArray arrayWithObject: torrent]];
463        else if ([torrent waitingToStart])
464            [fController stopTorrents: [NSArray arrayWithObject: torrent]];
465        else
466            [fController resumeTorrents: [NSArray arrayWithObject: torrent]];
467    }
468}
469
470- (void) displayTorrentMenuForEvent: (NSEvent *) event
471{
472    int row = [self rowAtPoint: [self convertPoint: [event locationInWindow] fromView: nil]];
473    if (row < 0)
474        return;
475   
476    //get and update file menu
477    fMenuTorrent = [[self itemAtRow: row] retain];
478    NSMenu * fileMenu = [fMenuTorrent fileMenu];
479    [self updateFileMenu: fileMenu forFiles: [fMenuTorrent fileList]];
480   
481    //add file menu items to action menu
482    NSRange range = NSMakeRange(0, [fileMenu numberOfItems]);
483    [fActionMenu appendItemsFromMenu: fileMenu atIndexes: [NSIndexSet indexSetWithIndexesInRange: range] atBottom: YES];
484   
485    //place menu below button
486    NSRect rect = [fTorrentCell iconRectForBounds: [self rectOfRow: row]];
487    NSPoint location = rect.origin;
488    location.y += rect.size.height + 5.0;
489    location = [self convertPoint: location toView: nil];
490   
491    NSEvent * newEvent = [NSEvent mouseEventWithType: [event type] location: location
492        modifierFlags: [event modifierFlags] timestamp: [event timestamp] windowNumber: [event windowNumber]
493        context: [event context] eventNumber: [event eventNumber] clickCount: [event clickCount] pressure: [event pressure]];
494   
495    [NSMenu popUpContextMenu: fActionMenu withEvent: newEvent forView: self];
496   
497    //move file menu items back to the torrent's file menu
498    range.location = [fActionMenu numberOfItems] - range.length;
499    [fileMenu appendItemsFromMenu: fActionMenu atIndexes: [NSIndexSet indexSetWithIndexesInRange: range] atBottom: YES];
500   
501    [fMenuTorrent release];
502    fMenuTorrent = nil;
503}
504
505- (void) menuNeedsUpdate: (NSMenu *) menu
506{
507    //this method seems to be called when it shouldn't be
508    if (!fMenuTorrent || ![menu supermenu])
509        return;
510   
511    if (menu == fUploadMenu || menu == fDownloadMenu)
512    {
513        NSMenuItem * item;
514        if ([menu numberOfItems] == 4)
515        {
516            const int speedLimitActionValue[] = { 0, 5, 10, 20, 30, 40, 50, 75, 100, 150, 200, 250, 500, 750, -1 };
517           
518            int i;
519            for (i = 0; speedLimitActionValue[i] != -1; i++)
520            {
521                item = [[NSMenuItem alloc] initWithTitle: [NSString stringWithFormat: NSLocalizedString(@"%d KB/s",
522                        "Action menu -> upload/download limit"), speedLimitActionValue[i]] action: @selector(setQuickLimit:)
523                        keyEquivalent: @""];
524                [item setTarget: self];
525                [item setRepresentedObject: [NSNumber numberWithInt: speedLimitActionValue[i]]];
526                [menu addItem: item];
527                [item release];
528            }
529        }
530       
531        BOOL upload = menu == fUploadMenu;
532        int mode = [fMenuTorrent speedMode: upload];
533       
534        item = [menu itemWithTag: ACTION_MENU_LIMIT_TAG];
535        [item setState: mode == TR_SPEEDLIMIT_SINGLE ? NSOnState : NSOffState];
536        [item setTitle: [NSString stringWithFormat: NSLocalizedString(@"Limit (%d KB/s)",
537                            "torrent action menu -> upload/download limit"), [fMenuTorrent speedLimit: upload]]];
538       
539        item = [menu itemWithTag: ACTION_MENU_UNLIMITED_TAG];
540        [item setState: mode == TR_SPEEDLIMIT_UNLIMITED ? NSOnState : NSOffState];
541       
542        item = [menu itemWithTag: ACTION_MENU_GLOBAL_TAG];
543        [item setState: mode == TR_SPEEDLIMIT_GLOBAL ? NSOnState : NSOffState];
544    }
545    else if (menu == fRatioMenu)
546    {
547        NSMenuItem * item;
548        if ([menu numberOfItems] == 4)
549        {
550            const float ratioLimitActionValue[] = { 0.25, 0.5, 0.75, 1.0, 1.5, 2.0, 3.0, -1.0 };
551           
552            int i;
553            for (i = 0; ratioLimitActionValue[i] != -1.0; i++)
554            {
555                item = [[NSMenuItem alloc] initWithTitle: [NSString stringWithFormat: @"%.2f", ratioLimitActionValue[i]]
556                        action: @selector(setQuickRatio:) keyEquivalent: @""];
557                [item setTarget: self];
558                [item setRepresentedObject: [NSNumber numberWithFloat: ratioLimitActionValue[i]]];
559                [menu addItem: item];
560                [item release];
561            }
562        }
563       
564        int mode = [fMenuTorrent ratioSetting];
565       
566        item = [menu itemWithTag: ACTION_MENU_LIMIT_TAG];
567        [item setState: mode == NSOnState ? NSOnState : NSOffState];
568        [item setTitle: [NSString stringWithFormat: NSLocalizedString(@"Stop at Ratio (%.2f)", "torrent action menu -> ratio stop"),
569                            [fMenuTorrent ratioLimit]]];
570       
571        item = [menu itemWithTag: ACTION_MENU_UNLIMITED_TAG];
572        [item setState: mode == NSOffState ? NSOnState : NSOffState];
573       
574        item = [menu itemWithTag: ACTION_MENU_GLOBAL_TAG];
575        [item setState: mode == NSMixedState ? NSOnState : NSOffState];
576    }
577    else  //assume the menu is part of the file list
578    {
579        NSMenu * supermenu = [menu supermenu];
580        [self updateFileMenu: menu forFiles: [[[supermenu itemAtIndex: [supermenu indexOfItemWithSubmenu: menu]]
581                                                    representedObject] objectForKey: @"Children"]];
582    }
583}
584
585- (void) setQuickLimitMode: (id) sender
586{
587    int mode;
588    switch ([sender tag])
589    {
590        case ACTION_MENU_UNLIMITED_TAG:
591            mode = TR_SPEEDLIMIT_UNLIMITED;
592            break;
593        case ACTION_MENU_LIMIT_TAG:
594            mode = TR_SPEEDLIMIT_SINGLE;
595            break;
596        case ACTION_MENU_GLOBAL_TAG:
597            mode = TR_SPEEDLIMIT_GLOBAL;
598            break;
599        default:
600            return;
601    }
602   
603    [fMenuTorrent setSpeedMode: mode upload: [sender menu] == fUploadMenu];
604   
605    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateOptions" object: nil];
606}
607
608- (void) setQuickLimit: (id) sender
609{
610    BOOL upload = [sender menu] == fUploadMenu;
611    [fMenuTorrent setSpeedMode: TR_SPEEDLIMIT_SINGLE upload: upload];
612    [fMenuTorrent setSpeedLimit: [[sender representedObject] intValue] upload: upload];
613   
614    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateOptions" object: nil];
615}
616
617- (void) setQuickRatioMode: (id) sender
618{
619    int mode;
620    switch ([sender tag])
621    {
622        case ACTION_MENU_UNLIMITED_TAG:
623            mode = NSOffState;
624            break;
625        case ACTION_MENU_LIMIT_TAG:
626            mode = NSOnState;
627            break;
628        case ACTION_MENU_GLOBAL_TAG:
629            mode = NSMixedState;
630            break;
631        default:
632            return;
633    }
634   
635    [fMenuTorrent setRatioSetting: mode];
636   
637    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateOptions" object: nil];
638}
639
640- (void) setQuickRatio: (id) sender
641{
642    [fMenuTorrent setRatioSetting: NSOnState];
643    [fMenuTorrent setRatioLimit: [[sender representedObject] floatValue]];
644   
645    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateOptions" object: nil];
646}
647
648- (void) checkFile: (id) sender
649{
650    NSIndexSet * indexSet = [[sender representedObject] objectForKey: @"Indexes"];
651    [fMenuTorrent setFileCheckState: [sender state] != NSOnState ? NSOnState : NSOffState forIndexes: indexSet];
652   
653    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateStats" object: nil];
654}
655
656- (void) togglePiecesBar
657{
658    [self resizePiecesBarIncrement];
659   
660    if (!fPiecesBarTimer)
661    {
662        fPiecesBarTimer = [NSTimer scheduledTimerWithTimeInterval: PIECE_TIME target: self
663                            selector: @selector(resizePiecesBarIncrement) userInfo: nil repeats: YES];
664        [[NSRunLoop currentRunLoop] addTimer: fPiecesBarTimer forMode: NSModalPanelRunLoopMode];
665        [[NSRunLoop currentRunLoop] addTimer: fPiecesBarTimer forMode: NSEventTrackingRunLoopMode];
666    }
667}
668
669- (float) piecesBarPercent
670{
671    return fPiecesBarPercent;
672}
673
674@end
675
676@implementation TorrentTableView (Private)
677
678- (BOOL) pointInControlRect: (NSPoint) point
679{
680    int row = [self rowAtPoint: point];
681    if (row < 0 || ![[self itemAtRow: row] isKindOfClass: [Torrent class]])
682        return NO;
683   
684    return NSPointInRect(point, [fTorrentCell controlButtonRectForBounds: [self rectOfRow: row]]);
685}
686
687- (BOOL) pointInRevealRect: (NSPoint) point
688{
689    int row = [self rowAtPoint: point];
690    if (row < 0 || ![[self itemAtRow: row] isKindOfClass: [Torrent class]])
691        return NO;
692   
693    return NSPointInRect(point, [fTorrentCell revealButtonRectForBounds: [self rectOfRow: row]]);
694}
695
696- (BOOL) pointInActionRect: (NSPoint) point
697{
698    int row = [self rowAtPoint: point];
699    if (row < 0 || ![[self itemAtRow: row] isKindOfClass: [Torrent class]])
700        return NO;
701   
702    return NSPointInRect(point, [fTorrentCell iconRectForBounds: [self rectOfRow: row]]);
703}
704
705- (BOOL) pointInProgressRect: (NSPoint) point
706{
707    int row = [self rowAtPoint: point];
708    if (row < 0 || ![[self itemAtRow: row] isKindOfClass: [Torrent class]] || [fDefaults boolForKey: @"SmallView"])
709        return NO;
710   
711    TorrentCell * cell;
712    if ([NSApp isOnLeopardOrBetter])
713        cell = (TorrentCell *)[self preparedCellAtColumn: -1 row: row];
714    else
715    {
716        cell = fTorrentCell;
717        [cell setRepresentedObject: [self itemAtRow: row]];
718    }
719    return NSPointInRect(point, [cell progressRectForBounds: [self rectOfRow: row]]);
720}
721
722- (BOOL) pointInMinimalStatusRect: (NSPoint) point
723{
724    int row = [self rowAtPoint: point];
725    if (row < 0 || ![[self itemAtRow: row] isKindOfClass: [Torrent class]] || ![fDefaults boolForKey: @"SmallView"])
726        return NO;
727   
728    TorrentCell * cell;
729    if ([NSApp isOnLeopardOrBetter])
730        cell = (TorrentCell *)[self preparedCellAtColumn: -1 row: row];
731    else
732    {
733        cell = fTorrentCell;
734        [cell setRepresentedObject: [self itemAtRow: row]];
735    }
736    return NSPointInRect(point, [cell minimalStatusRectForBounds: [self rectOfRow: row]]);
737}
738
739- (void) updateFileMenu: (NSMenu *) menu forFiles: (NSArray *) files
740{
741    BOOL create = [menu numberOfItems] <= 0;
742   
743    NSEnumerator * enumerator = [files objectEnumerator];
744    NSDictionary * dict;
745    NSMenuItem * item;
746    while ((dict = [enumerator nextObject]))
747    {
748        NSString * name = [dict objectForKey: @"Name"];
749       
750        if (create)
751        {
752            item = [[NSMenuItem alloc] initWithTitle: name action: @selector(checkFile:) keyEquivalent: @""];
753           
754            NSImage * icon;
755            if (![[dict objectForKey: @"IsFolder"] boolValue])
756                icon = [[NSWorkspace sharedWorkspace] iconForFileType: [name pathExtension]];
757            else
758            {
759                NSMenu * itemMenu = [[NSMenu alloc] initWithTitle: name];
760                [itemMenu setAutoenablesItems: NO];
761                [item setSubmenu: itemMenu];
762                [itemMenu setDelegate: self];
763                [itemMenu release];
764               
765                icon = [[NSWorkspace sharedWorkspace] iconForFileType: NSFileTypeForHFSTypeCode('fldr')];
766            }
767           
768            [item setRepresentedObject: dict];
769           
770            [icon setScalesWhenResized: YES];
771            [icon setSize: NSMakeSize(16.0, 16.0)];
772            [item setImage: icon];
773           
774            [menu addItem: item];
775            [item release];
776        }
777        else
778            item = [menu itemWithTitle: name];
779       
780        NSIndexSet * indexSet = [dict objectForKey: @"Indexes"];
781        [item setState: [fMenuTorrent checkForFiles: indexSet]];
782        [item setEnabled: [fMenuTorrent canChangeDownloadCheckForFiles: indexSet]];
783    }
784}
785
786- (void) resizePiecesBarIncrement
787{
788    BOOL done;
789    if ([fDefaults boolForKey: @"PiecesBar"])
790    {
791        fPiecesBarPercent = MIN(fPiecesBarPercent + PIECE_CHANGE, 1.0);
792        done = fPiecesBarPercent == 1.0;
793    }
794    else
795    {
796        fPiecesBarPercent = MAX(fPiecesBarPercent - PIECE_CHANGE, 0.0);
797        done = fPiecesBarPercent == 0.0;
798    }
799   
800    if (done)
801    {
802        [fPiecesBarTimer invalidate];
803        fPiecesBarTimer = nil;
804    }
805   
806    [self reloadData];
807}
808
809@end
Note: See TracBrowser for help on using the repository browser.