source: trunk/macosx/TorrentTableView.m @ 5163

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

make the group "ying-yang" full sized; hide dl speed columns when showing ratio

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