source: trunk/macosx/TorrentTableView.m @ 5069

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

first go at getting the app to work nicely with Tiger again

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