source: trunk/macosx/TorrentTableView.m @ 5273

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

fix display issue on launch when group row is set to show ratio

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