source: trunk/macosx/TorrentTableView.m @ 5156

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

clicking on the group speed rates will toggle with ratio

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