source: trunk/macosx/InfoWindowController.m @ 2363

Last change on this file since 2363 was 2363, checked in by livings124, 15 years ago

get the mac os build building again

  • Property svn:keywords set to Date Rev Author Id
File size: 44.3 KB
Line 
1/******************************************************************************
2 * $Id: InfoWindowController.m 2363 2007-07-15 21:57:42Z livings124 $
3 *
4 * Copyright (c) 2006-2007 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 "InfoWindowController.h"
26#import "FileBrowserCell.h"
27#import "FilePriorityCell.h"
28#import "StringAdditions.h"
29
30#define MIN_WINDOW_WIDTH 300
31#define MAX_WINDOW_WIDTH 5000
32
33#define FILE_ROW_SMALL_HEIGHT 18.0
34
35#define TAB_INFO_IDENT @"Info"
36#define TAB_ACTIVITY_IDENT @"Activity"
37#define TAB_PEERS_IDENT @"Peers"
38#define TAB_FILES_IDENT @"Files"
39#define TAB_OPTIONS_IDENT @"Options"
40
41//15 spacing at the bottom of each tab
42#define TAB_INFO_HEIGHT 268.0
43#define TAB_ACTIVITY_HEIGHT 274.0
44#define TAB_PEERS_HEIGHT 279.0
45#define TAB_FILES_HEIGHT 279.0
46#define TAB_OPTIONS_HEIGHT 158.0
47
48#define PIECES_CONTROL_PROGRESS 0
49#define PIECES_CONTROL_AVAILABLE 1
50
51#define OPTION_POPUP_GLOBAL 0
52#define OPTION_POPUP_NO_LIMIT 1
53#define OPTION_POPUP_LIMIT 2
54
55#define INVALID -99
56
57@interface InfoWindowController (Private)
58
59- (void) updateInfoGeneral;
60- (void) updateInfoActivity;
61- (void) updateInfoPeers;
62- (void) updateInfoFiles;
63- (void) updateInfoSettings;
64
65- (void) setWindowForTab: (NSString *) identifier animate: (BOOL) animate;
66- (NSArray *) peerSortDescriptors;
67
68- (int) stateSettingToPopUpIndex: (int) index;
69- (int) popUpIndexToStateSetting: (int) index;
70
71@end
72
73@implementation InfoWindowController
74
75- (id) initWithWindowNibName: (NSString *) name
76{
77    if ((self = [super initWithWindowNibName: name]))
78    {
79        fAppIcon = [NSImage imageNamed: @"NSApplicationIcon"];
80        fDotGreen = [NSImage imageNamed: @"GreenDot.tiff"];
81        fDotRed = [NSImage imageNamed: @"RedDot.tiff"];
82    }
83    return self;
84}
85
86- (void) awakeFromNib
87{
88    //window location and size
89    NSPanel * window = (NSPanel *)[self window];
90   
91    [window setFrameAutosaveName: @"InspectorWindowFrame"];
92    [window setFrameUsingName: @"InspectorWindowFrame"];
93   
94    //select tab
95    NSString * identifier = [[NSUserDefaults standardUserDefaults] stringForKey: @"InspectorSelected"];
96    if ([fTabView indexOfTabViewItemWithIdentifier: identifier] == NSNotFound)
97        identifier = TAB_INFO_IDENT;
98   
99    [fTabView selectTabViewItemWithIdentifier: identifier];
100    [self setWindowForTab: identifier animate: NO];
101   
102    //initially sort peer table by IP
103    if ([[fPeerTable sortDescriptors] count] == 0)
104        [fPeerTable setSortDescriptors: [NSArray arrayWithObject: [[fPeerTable tableColumnWithIdentifier: @"IP"]
105                                            sortDescriptorPrototype]]];
106   
107    //set file table
108    [fFileOutline setDoubleAction: @selector(revealFile:)];
109   
110    //set file outline
111    FilePriorityCell * priorityCell = [[[FilePriorityCell alloc] init] autorelease];
112    [[fFileOutline tableColumnWithIdentifier: @"Priority"] setDataCell: priorityCell];
113   
114    //set blank inspector
115    [self updateInfoForTorrents: [NSArray array]];
116}
117
118- (void) dealloc
119{
120    if (fTorrents)
121        [fTorrents release];
122    if (fPeers)
123        [fPeers release];
124    if (fFiles)
125        [fFiles release];
126   
127    [super dealloc];
128}
129
130- (void) updateInfoForTorrents: (NSArray *) torrents
131{
132    if (fTorrents)
133        [fTorrents release];
134    fTorrents = [torrents retain];
135
136    int numberSelected = [fTorrents count];
137    if (numberSelected != 1)
138    {
139        if (numberSelected > 0)
140        {
141            [fNameField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%d Torrents Selected",
142                                            "Inspector -> above tabs -> selected torrents"), numberSelected]];
143       
144            uint64_t size = 0;
145            NSEnumerator * enumerator = [torrents objectEnumerator];
146            Torrent * torrent;
147            while ((torrent = [enumerator nextObject]))
148                size += [torrent size];
149           
150            [fSizeField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%@ Total",
151                "Inspector -> above tabs -> total size (several torrents selected)"), [NSString stringForFileSize: size]]];
152        }
153        else
154        {
155            [fNameField setStringValue: NSLocalizedString(@"No Torrents Selected",
156                                                            "Inspector -> above tabs -> selected torrents")];
157            [fSizeField setStringValue: @""];
158   
159            [fDownloadedValidField setStringValue: @""];
160            [fDownloadedTotalField setStringValue: @""];
161            [fUploadedTotalField setStringValue: @""];
162        }
163       
164        [fImageView setImage: fAppIcon];
165       
166        [fNameField setToolTip: nil];
167
168        [fTrackerField setStringValue: @""];
169        [fTrackerField setToolTip: nil];
170        [fPiecesField setStringValue: @""];
171        [fHashField setStringValue: @""];
172        [fHashField setToolTip: nil];
173        [fSecureField setStringValue: @""];
174        [fCommentView setString: @""];
175       
176        [fCreatorField setStringValue: @""];
177        [fDateCreatedField setStringValue: @""];
178        [fCommentView setSelectable: NO];
179       
180        [fTorrentLocationField setStringValue: @""];
181        [fTorrentLocationField setToolTip: nil];
182        [fDataLocationField setStringValue: @""];
183        [fDataLocationField setToolTip: nil];
184       
185        [fRevealDataButton setHidden: YES];
186        [fRevealTorrentButton setHidden: YES];
187       
188        //don't allow empty fields to be selected
189        [fTrackerField setSelectable: NO];
190        [fHashField setSelectable: NO];
191        [fCreatorField setSelectable: NO];
192        [fTorrentLocationField setSelectable: NO];
193        [fDataLocationField setSelectable: NO];
194       
195        [fStateField setStringValue: @""];
196        [fProgressField setStringValue: @""];
197        [fRatioField setStringValue: @""];
198       
199        [fSeedersField setStringValue: @""];
200        [fLeechersField setStringValue: @""];
201        [fCompletedFromTrackerField setStringValue: @""];
202        [fConnectedPeersField setStringValue: NSLocalizedString(@"info not available", "Inspector -> Peers tab -> peers")];
203        [fDownloadingFromField setStringValue: @""];
204        [fUploadingToField setStringValue: @""];
205        [fSwarmSpeedField setStringValue: @""];
206        [fErrorMessageView setString: @""];
207        [fErrorMessageView setSelectable: NO];
208       
209        [fDateAddedField setStringValue: @""];
210        [fDateCompletedField setStringValue: @""];
211        [fDateActivityField setStringValue: @""];
212       
213        [fPiecesControl setSelected: NO forSegment: PIECES_CONTROL_AVAILABLE];
214        [fPiecesControl setSelected: NO forSegment: PIECES_CONTROL_PROGRESS];
215        [fPiecesControl setEnabled: NO];
216        [fPiecesView setTorrent: nil];
217       
218        if (fPeers)
219        {
220            [fPeers release];
221            fPeers = nil;
222        }
223       
224        if (fFiles)
225        {
226            [fFiles release];
227            fFiles = nil;
228        }
229        [fFileTableStatusField setStringValue: NSLocalizedString(@"info not available",
230                                        "Inspector -> Files tab -> bottom text (number of files)")];
231    }
232    else
233    {   
234        Torrent * torrent = [fTorrents objectAtIndex: 0];
235       
236        [fImageView setImage: [torrent icon]];
237       
238        NSString * name = [torrent name];
239        [fNameField setStringValue: name];
240        [fNameField setToolTip: name];
241        [fSizeField setStringValue: [NSString stringForFileSize: [torrent size]]];
242       
243        NSString * hashString = [torrent hashString],
244                * commentString = [torrent comment];
245        [fPiecesField setStringValue: [NSString stringWithFormat: @"%d, %@", [torrent pieceCount],
246                                        [NSString stringForFileSize: [torrent pieceSize]]]];
247        [fHashField setStringValue: hashString];
248        [fHashField setToolTip: hashString];
249        [fSecureField setStringValue: [torrent privateTorrent]
250                        ? NSLocalizedString(@"Private Torrent, PEX disabled", "Inspector -> is private torrent")
251                        : NSLocalizedString(@"Public Torrent", "Inspector -> is not private torrent")];
252        [fCommentView setString: commentString];
253       
254        [fCreatorField setStringValue: [torrent creator]];
255        [fDateCreatedField setObjectValue: [torrent dateCreated]];
256       
257        BOOL publicTorrent = [torrent publicTorrent];
258        [fTorrentLocationField setStringValue: publicTorrent
259                    ? [[torrent publicTorrentLocation] stringByAbbreviatingWithTildeInPath]
260                    : NSLocalizedString(@"Transmission Support Folder", "Torrent -> location when deleting original")];
261        if (publicTorrent)
262            [fTorrentLocationField setToolTip: [NSString stringWithFormat: @"%@\n\n%@",
263                        [torrent publicTorrentLocation], [torrent torrentLocation]]];
264        else
265            [fTorrentLocationField setToolTip: [torrent torrentLocation]];
266       
267        [fDateAddedField setObjectValue: [torrent dateAdded]];
268       
269        [fRevealDataButton setHidden: NO];
270        [fRevealTorrentButton setHidden: ![torrent publicTorrent]];
271       
272        //allow these fields to be selected
273        [fTrackerField setSelectable: YES];
274        [fHashField setSelectable: YES];
275        [fCommentView setSelectable: YES];
276        [fCreatorField setSelectable: YES];
277        [fTorrentLocationField setSelectable: YES];
278        [fDataLocationField setSelectable: YES];
279       
280        //set pieces view
281        BOOL piecesAvailableSegment = [[NSUserDefaults standardUserDefaults] boolForKey: @"PiecesViewShowAvailability"];
282        [fPiecesControl setSelected: piecesAvailableSegment forSegment: PIECES_CONTROL_AVAILABLE];
283        [fPiecesControl setSelected: !piecesAvailableSegment forSegment: PIECES_CONTROL_PROGRESS];
284        [fPiecesControl setEnabled: YES];
285        [fPiecesView setTorrent: torrent];
286       
287        //set file table
288        [fFileOutline deselectAll: nil];
289        if (fFiles)
290            [fFiles release];
291        fFiles = [[torrent fileList] retain];
292       
293        [self updateInfoFiles];
294       
295        int fileCount = [torrent fileCount];
296        if (fileCount != 1)
297            [fFileTableStatusField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%d files total",
298                                "Inspector -> Files tab -> bottom text (number of files)"), fileCount]];
299        else
300            [fFileTableStatusField setStringValue: NSLocalizedString(@"1 file total",
301                                "Inspector -> Files tab -> bottom text (number of files)")];
302    }
303   
304    //update stats and settings
305    [self updateInfoSettings];
306   
307    [fPeerTable reloadData];
308    [fFileOutline deselectAll: nil];
309    [fFileOutline reloadData];
310}
311
312- (Torrent *) selectedTorrent
313{
314    return fTorrents && [fTorrents count] > 0 ? [fTorrents objectAtIndex: 0] : nil;
315}
316
317- (void) updateInfoStats
318{
319    NSString * ident = [[fTabView selectedTabViewItem] identifier];
320    if ([ident isEqualToString: TAB_ACTIVITY_IDENT])
321        [self updateInfoActivity];
322    else if ([ident isEqualToString: TAB_PEERS_IDENT])
323        [self updateInfoPeers];
324    else if ([ident isEqualToString: TAB_INFO_IDENT])
325        [self updateInfoGeneral];
326    else if ([ident isEqualToString: TAB_FILES_IDENT])
327        [self updateInfoFiles];
328    else;
329}
330
331- (void) updateInfoGeneral
332{   
333    if ([fTorrents count] != 1)
334        return;
335   
336    Torrent * torrent = [fTorrents objectAtIndex: 0];
337   
338    NSString * tracker = [[torrent trackerAddress] stringByAppendingString: [torrent trackerAddressAnnounce]];
339    [fTrackerField setStringValue: tracker];
340    [fTrackerField setToolTip: tracker];
341   
342    NSString * location = [torrent dataLocation];
343    [fDataLocationField setStringValue: [location stringByAbbreviatingWithTildeInPath]];
344    [fDataLocationField setToolTip: location];
345}
346
347- (void) updateInfoActivity
348{
349    int numberSelected = [fTorrents count];
350    if (numberSelected == 0)
351        return;
352   
353    uint64_t  downloadedValid = 0, downloadedTotal = 0, uploadedTotal = 0;
354    Torrent * torrent;
355    NSEnumerator * enumerator = [fTorrents objectEnumerator];
356    while ((torrent = [enumerator nextObject]))
357    {
358        downloadedValid += [torrent downloadedValid];
359        downloadedTotal += [torrent downloadedTotal];
360        uploadedTotal += [torrent uploadedTotal];
361    }
362
363    [fDownloadedValidField setStringValue: [NSString stringForFileSize: downloadedValid]];
364    [fDownloadedTotalField setStringValue: [NSString stringForFileSize: downloadedTotal]];
365    [fUploadedTotalField setStringValue: [NSString stringForFileSize: uploadedTotal]];
366   
367    if (numberSelected == 1)
368    {
369        torrent = [fTorrents objectAtIndex: 0];
370       
371        [fStateField setStringValue: [torrent stateString]];
372        [fProgressField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%.2f%% (%.2f%% selected)",
373                    "Inspector -> Activity tab -> progress"), 100.0 * [torrent progress], 100.0 * [torrent progressDone]]];
374        [fRatioField setStringValue: [NSString stringForRatio: [torrent ratio]]];
375        [fSwarmSpeedField setStringValue: [torrent isActive] ? [NSString stringForSpeed: [torrent swarmSpeed]] : @""];
376       
377        NSString * errorMessage = [torrent errorMessage];
378        if (![errorMessage isEqualToString: [fErrorMessageView string]])
379        {
380            [fErrorMessageView setString: errorMessage];
381            [fErrorMessageView setSelectable: ![errorMessage isEqualToString: @""]];
382        }
383       
384        [fDateCompletedField setObjectValue: [torrent dateCompleted]];
385        [fDateActivityField setObjectValue: [torrent dateActivity]];
386       
387        [fPiecesView updateView: NO];
388    }
389}
390
391- (void) updateInfoPeers
392{
393    if ([fTorrents count] != 1)
394        return;
395    Torrent * torrent = [fTorrents objectAtIndex: 0];
396   
397    int seeders = [torrent seeders], leechers = [torrent leechers], downloaded = [torrent completedFromTracker];
398    [fSeedersField setStringValue: seeders < 0 ? @"" : [NSString stringWithInt: seeders]];
399    [fLeechersField setStringValue: leechers < 0 ? @"" : [NSString stringWithInt: leechers]];
400    [fCompletedFromTrackerField setStringValue: downloaded < 0 ? @"" : [NSString stringWithInt: downloaded]];
401   
402    BOOL active = [torrent isActive];
403   
404    if (active)
405    {
406        int total = [torrent totalPeersConnected];
407        NSString * connected = [NSString stringWithFormat:
408                                NSLocalizedString(@"%d Connected", "Inspector -> Peers tab -> peers"), total];
409       
410        if (total > 0)
411        {
412            NSMutableArray * components = [NSMutableArray arrayWithCapacity: 4];
413            int count;
414            if ((count = [torrent totalPeersTracker]) > 0)
415                [components addObject: [NSString stringWithFormat:
416                                        NSLocalizedString(@"%d tracker", "Inspector -> Peers tab -> peers"), count]];
417            if ((count = [torrent totalPeersIncoming]) > 0)
418                [components addObject: [NSString stringWithFormat:
419                                        NSLocalizedString(@"%d incoming", "Inspector -> Peers tab -> peers"), count]];
420            if ((count = [torrent totalPeersPex]) > 0)
421                [components addObject: [NSString stringWithFormat:
422                                        NSLocalizedString(@"%d PEX", "Inspector -> Peers tab -> peers"), count]];
423            if ((count = [torrent totalPeersCache]) > 0)
424                [components addObject: [NSString stringWithFormat:
425                                        NSLocalizedString(@"%d cache", "Inspector -> Peers tab -> peers"), count]];
426           
427            connected = [NSString stringWithFormat: @"%@: %@", connected, [components componentsJoinedByString: @", "]];
428        }
429       
430        [fConnectedPeersField setStringValue: connected];
431    }
432    else
433        [fConnectedPeersField setStringValue: NSLocalizedString(@"info not available", "Inspector -> Peers tab -> peers")];
434   
435    [fDownloadingFromField setStringValue: active ? [NSString stringWithInt: [torrent peersUploading]] : @""];
436    [fUploadingToField setStringValue: active ? [NSString stringWithInt: [torrent peersDownloading]] : @""];
437   
438    if (fPeers)
439        [fPeers release];
440    fPeers = [[[torrent peers] sortedArrayUsingDescriptors: [self peerSortDescriptors]] retain];
441   
442    [fPeerTable reloadData];
443}
444
445- (void) updateInfoFiles
446{
447    if ([fTorrents count] == 1)
448    {
449        [[fTorrents objectAtIndex: 0] updateFileStat];
450        [fFileOutline reloadData];
451    }
452}
453
454- (void) updateInfoSettings
455{
456    if ([fTorrents count] > 0)
457    {
458        Torrent * torrent;
459       
460        //set bandwidth limits
461        NSEnumerator * enumerator = [fTorrents objectEnumerator];
462        torrent = [enumerator nextObject]; //first torrent
463       
464        int checkUpload = [torrent checkUpload],
465            checkDownload = [torrent checkDownload],
466            uploadLimit = [torrent uploadLimit],
467            downloadLimit = [torrent downloadLimit];
468       
469        while ((checkUpload != INVALID || uploadLimit != INVALID
470                || checkDownload != INVALID || downloadLimit != INVALID)
471                && (torrent = [enumerator nextObject]))
472        {
473            if (checkUpload != INVALID && checkUpload != [torrent checkUpload])
474                checkUpload = INVALID;
475           
476            if (uploadLimit != INVALID && uploadLimit != [torrent uploadLimit])
477                uploadLimit = INVALID;
478           
479            if (checkDownload != INVALID && checkDownload != [torrent checkDownload])
480                checkDownload = INVALID;
481           
482            if (downloadLimit != INVALID && downloadLimit != [torrent downloadLimit])
483                downloadLimit = INVALID;
484        }
485       
486        [fUploadLimitPopUp setEnabled: YES];
487        [fUploadLimitPopUp selectItemAtIndex: [self stateSettingToPopUpIndex: checkUpload]];
488        [fUploadLimitLabel setHidden: checkUpload != NSOnState];
489        [fUploadLimitField setHidden: checkUpload != NSOnState];
490        if (uploadLimit != INVALID)
491            [fUploadLimitField setIntValue: uploadLimit];
492        else
493            [fUploadLimitField setStringValue: @""];
494       
495        [fDownloadLimitPopUp setEnabled: YES];
496        [fDownloadLimitPopUp selectItemAtIndex: [self stateSettingToPopUpIndex: checkDownload]];
497        [fDownloadLimitLabel setHidden: checkDownload != NSOnState];
498        [fDownloadLimitField setHidden: checkDownload != NSOnState];
499        if (downloadLimit != INVALID)
500            [fDownloadLimitField setIntValue: downloadLimit];
501        else
502            [fDownloadLimitField setStringValue: @""];
503       
504        //set ratio settings
505        enumerator = [fTorrents objectEnumerator];
506        torrent = [enumerator nextObject]; //first torrent
507       
508        int checkRatio = [torrent ratioSetting];
509        float ratioLimit = [torrent ratioLimit];
510       
511        while ((checkRatio != INVALID || checkRatio != INVALID)
512                && (torrent = [enumerator nextObject]))
513        {
514            if (checkRatio != INVALID && checkRatio != [torrent ratioSetting])
515                checkRatio = INVALID;
516           
517            if (ratioLimit != INVALID && ratioLimit != [torrent ratioLimit])
518                ratioLimit = INVALID;
519        }
520       
521        [fRatioPopUp setEnabled: YES];
522        [fRatioPopUp selectItemAtIndex: [self stateSettingToPopUpIndex: checkRatio]];
523        [fRatioLimitField setHidden: checkRatio != NSOnState];
524        if (ratioLimit != INVALID)
525            [fRatioLimitField setFloatValue: ratioLimit];
526        else
527            [fRatioLimitField setStringValue: @""];
528               
529                //set pex check
530                enumerator = [fTorrents objectEnumerator];
531        torrent = [enumerator nextObject]; //first torrent
532               
533                BOOL pexEnabled = ![torrent privateTorrent];
534                int pexState = [torrent pex] ? NSOnState : NSOffState;
535               
536                while ((pexEnabled || pexState != NSMixedState)
537                && (torrent = [enumerator nextObject]))
538        {
539            if (pexEnabled)
540                pexEnabled = ![torrent privateTorrent];
541           
542            if (pexState != NSMixedState && pexState != ([torrent pex] ? NSOnState : NSOffState))
543                pexState = NSMixedState;
544        }
545               
546                [fPexCheck setEnabled: pexEnabled];
547                [fPexCheck setState: pexState];
548    }
549    else
550    {
551        [fUploadLimitPopUp setEnabled: NO];
552        [fUploadLimitPopUp selectItemAtIndex: -1];
553        [fUploadLimitField setHidden: YES];
554        [fUploadLimitLabel setHidden: YES];
555        [fUploadLimitField setStringValue: @""];
556       
557        [fDownloadLimitPopUp setEnabled: NO];
558        [fDownloadLimitPopUp selectItemAtIndex: -1];
559        [fDownloadLimitField setHidden: YES];
560        [fDownloadLimitLabel setHidden: YES];
561        [fDownloadLimitField setStringValue: @""];
562       
563        [fRatioPopUp setEnabled: NO];
564        [fRatioPopUp selectItemAtIndex: -1];
565        [fRatioLimitField setHidden: YES];
566        [fRatioLimitField setStringValue: @""];
567               
568                [fPexCheck setEnabled: NO];
569        [fPexCheck setState: NSOffState];
570    }
571   
572    [self updateInfoStats];
573}
574
575- (void) updateRatioForTorrent: (Torrent *) torrent
576{
577    if ([fTorrents containsObject: torrent])
578        [self updateInfoSettings];
579}
580
581- (int) stateSettingToPopUpIndex: (int) index
582{
583    if (index == NSOnState)
584        return OPTION_POPUP_LIMIT;
585    else if (index == NSOffState)
586        return OPTION_POPUP_NO_LIMIT;
587    else if (index == NSMixedState)
588        return OPTION_POPUP_GLOBAL;
589    else
590        return -1;
591}
592
593- (int) popUpIndexToStateSetting: (int) index
594{
595    if (index == OPTION_POPUP_LIMIT)
596        return NSOnState;
597    else if (index == OPTION_POPUP_NO_LIMIT)
598        return NSOffState;
599    else if (index == OPTION_POPUP_GLOBAL)
600        return NSMixedState;
601    else
602        return INVALID;
603}
604
605- (BOOL) validateMenuItem: (NSMenuItem *) menuItem
606{
607    SEL action = [menuItem action];
608   
609    if (action == @selector(revealFile:))
610        return [fFileOutline numberOfSelectedRows] > 0 &&
611            [[[fTabView selectedTabViewItem] identifier] isEqualToString: TAB_FILES_IDENT];
612   
613    if (action == @selector(setCheck:))
614    {
615        if ([fFileOutline numberOfSelectedRows] <= 0)
616            return NO;
617       
618        Torrent * torrent = [fTorrents objectAtIndex: 0];
619        NSIndexSet * indexSet = [fFileOutline selectedRowIndexes];
620        NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
621        int i, index, state = (menuItem == fFileCheckItem) ? NSOnState : NSOffState;
622        for (i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
623            [itemIndexes addIndexes: [[fFileOutline itemAtRow: i] objectForKey: @"Indexes"]];
624       
625        return [torrent checkForFiles: itemIndexes] != state && [torrent canChangeDownloadCheckForFiles: itemIndexes];
626    }
627   
628    if (action == @selector(setOnlySelectedCheck:))
629    {
630        if ([fFileOutline numberOfSelectedRows] <= 0)
631            return NO;
632       
633        Torrent * torrent = [fTorrents objectAtIndex: 0];
634        NSIndexSet * indexSet = [fFileOutline selectedRowIndexes];
635        NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
636        int i, index;
637        for (i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
638            [itemIndexes addIndexes: [[fFileOutline itemAtRow: i] objectForKey: @"Indexes"]];
639           
640        return [torrent canChangeDownloadCheckForFiles: itemIndexes];
641    }
642   
643    if (action == @selector(setPriority:))
644    {
645        if ([fFileOutline numberOfSelectedRows] <= 0)
646        {
647            [menuItem setState: NSOffState];
648            return NO;
649        }
650       
651        //determine which priorities are checked
652        NSIndexSet * indexSet = [fFileOutline selectedRowIndexes];
653        BOOL current = NO, other = NO;
654        int i, priority;
655        Torrent * torrent = [fTorrents objectAtIndex: 0];
656       
657        if (menuItem == fFilePriorityHigh)
658            priority = TR_PRI_HIGH;
659        else if (menuItem == fFilePriorityLow)
660            priority = TR_PRI_LOW;
661        else
662            priority = TR_PRI_NORMAL;
663       
664        NSIndexSet * fileIndexSet;
665        for (i = [indexSet firstIndex]; i != NSNotFound && (!current || !other); i = [indexSet indexGreaterThanIndex: i])
666        {
667            fileIndexSet = [[fFileOutline itemAtRow: i] objectForKey: @"Indexes"];
668            if (![torrent canChangeDownloadCheckForFiles: fileIndexSet])
669                continue;
670            else if ([torrent hasFilePriority: priority forIndexes: fileIndexSet])
671                current = YES;
672            else
673                other = YES;
674        }
675       
676        [menuItem setState: current ? NSOnState : NSOffState];
677        return current || other;
678    }
679   
680    return YES;
681}
682
683- (NSRect) windowWillUseStandardFrame: (NSWindow *) window defaultFrame: (NSRect) defaultFrame
684{
685    NSRect windowRect = [window frame];
686    windowRect.size.width = [window minSize].width;
687    return windowRect;
688}
689
690- (void) tabView: (NSTabView *) tabView didSelectTabViewItem: (NSTabViewItem *) tabViewItem
691{
692    NSString * identifier = [tabViewItem identifier];
693    [self setWindowForTab: identifier animate: YES];
694    [[NSUserDefaults standardUserDefaults] setObject: identifier forKey: @"InspectorSelected"];
695}
696
697- (void) setWindowForTab: (NSString *) identifier animate: (BOOL) animate
698{
699    [self updateInfoStats];
700   
701    float height;
702    if ([identifier isEqualToString: TAB_ACTIVITY_IDENT])
703    {
704        height = TAB_ACTIVITY_HEIGHT;
705        [fPiecesView updateView: YES];
706    }
707    else if ([identifier isEqualToString: TAB_PEERS_IDENT])
708        height = TAB_PEERS_HEIGHT;
709    else if ([identifier isEqualToString: TAB_FILES_IDENT])
710        height = TAB_FILES_HEIGHT;
711    else if ([identifier isEqualToString: TAB_OPTIONS_IDENT])
712        height = TAB_OPTIONS_HEIGHT;
713    else
714        height = TAB_INFO_HEIGHT;
715   
716    NSWindow * window = [self window];
717    NSRect frame = [window frame];
718    NSView * view = [[fTabView selectedTabViewItem] view];
719   
720    float difference = height - [view frame].size.height;
721    frame.origin.y -= difference;
722    frame.size.height += difference;
723   
724    if (animate)
725    {
726        [view setHidden: YES];
727        [window setFrame: frame display: YES animate: YES];
728        [view setHidden: NO];
729    }
730    else
731        [window setFrame: frame display: YES];
732   
733    [window setMinSize: NSMakeSize(MIN_WINDOW_WIDTH, frame.size.height)];
734    [window setMaxSize: NSMakeSize(MAX_WINDOW_WIDTH, frame.size.height)];
735}
736
737- (void) setNextTab
738{
739    if ([fTabView indexOfTabViewItem: [fTabView selectedTabViewItem]] == [fTabView numberOfTabViewItems] - 1)
740        [fTabView selectFirstTabViewItem: nil];
741    else
742        [fTabView selectNextTabViewItem: nil];
743}
744
745- (void) setPreviousTab
746{
747    if ([fTabView indexOfTabViewItem: [fTabView selectedTabViewItem]] == 0)
748        [fTabView selectLastTabViewItem: nil];
749    else
750        [fTabView selectPreviousTabViewItem: nil];
751}
752
753- (int) numberOfRowsInTableView: (NSTableView *) tableView
754{
755    if (tableView == fPeerTable)
756        return fPeers ? [fPeers count] : 0;
757    return 0;
758}
759
760- (id) tableView: (NSTableView *) tableView objectValueForTableColumn: (NSTableColumn *) column row: (int) row
761{
762    if (tableView == fPeerTable)
763    {
764        NSString * ident = [column identifier];
765        NSDictionary * peer = [fPeers objectAtIndex: row];
766       
767        if ([ident isEqualToString: @"Connected"])
768            return [[peer objectForKey: @"Connected"] boolValue] ? fDotGreen : fDotRed;
769        else if ([ident isEqualToString: @"Client"])
770            return [peer objectForKey: @"Client"];
771        else if  ([ident isEqualToString: @"Progress"])
772            return [peer objectForKey: @"Progress"]; //returning nil is fine
773        else if ([ident isEqualToString: @"UL To"])
774        {
775            NSNumber * rate;
776            return (rate = [peer objectForKey: @"UL To Rate"]) ? [NSString stringForSpeedAbbrev: [rate floatValue]] : @"";
777        }
778        else if ([ident isEqualToString: @"DL From"])
779        {
780            NSNumber * rate;
781            return (rate = [peer objectForKey: @"DL From Rate"]) ? [NSString stringForSpeedAbbrev: [rate floatValue]] : @"";
782        }
783        else
784            return [peer objectForKey: @"IP"];
785    }
786    return nil;
787}
788
789- (void) tableView: (NSTableView *) tableView willDisplayCell: (id) cell
790            forTableColumn: (NSTableColumn *) tableColumn row: (int) row
791{
792    if (tableView == fPeerTable)
793    {
794        if ([[tableColumn identifier] isEqualToString: @"Progress"])
795            [cell setHidden: ![[[fPeers objectAtIndex: row] objectForKey: @"Connected"] boolValue]];
796    }
797}
798
799- (void) tableView: (NSTableView *) tableView didClickTableColumn: (NSTableColumn *) tableColumn
800{
801    if (tableView == fPeerTable)
802    {
803        if (fPeers)
804        {
805            NSArray * oldPeers = fPeers;
806            fPeers = [[fPeers sortedArrayUsingDescriptors: [self peerSortDescriptors]] retain];
807            [oldPeers release];
808            [tableView reloadData];
809        }
810    }
811}
812
813- (BOOL) tableView: (NSTableView *) tableView shouldSelectRow:(int) row
814{
815    return tableView != fPeerTable;
816}
817
818- (NSString *) tableView: (NSTableView *) tableView toolTipForCell: (NSCell *) cell rect: (NSRectPointer) rect
819                tableColumn: (NSTableColumn *) column row: (int) row mouseLocation: (NSPoint) mouseLocation
820{
821    if (tableView == fPeerTable)
822    {
823        NSDictionary * peer = [fPeers objectAtIndex: row];
824       
825        NSMutableArray * components = [NSMutableArray arrayWithCapacity: 3];
826       
827        if ([[peer objectForKey: @"Connected"] boolValue])
828            [components addObject: [NSString stringWithFormat:
829                                    NSLocalizedString(@"Progress: %.1f%%", "Inspector -> Peers tab -> table row tooltip"),
830                                    [[peer objectForKey: @"Progress"] floatValue] * 100.0]];
831       
832        int port;
833        if ((port = [[peer objectForKey: @"Port"] intValue]) > 0)
834            [components addObject: [NSString stringWithFormat:
835                                    NSLocalizedString(@"Port: %d", "Inspector -> Peers tab -> table row tooltip"), port]];
836        else
837            [components addObject: NSLocalizedString(@"Port: N/A", "Inspector -> Peers tab -> table row tooltip")];
838       
839        int from = [[peer objectForKey: @"From"] intValue];
840        if (from == TR_PEER_FROM_INCOMING)
841            [components addObject: NSLocalizedString(@"From: incoming connection", "Inspector -> Peers tab -> table row tooltip")];
842        else if (from == TR_PEER_FROM_CACHE)
843            [components addObject: NSLocalizedString(@"From: cache", "Inspector -> Peers tab -> table row tooltip")];
844        else if (from == TR_PEER_FROM_PEX)
845            [components addObject: NSLocalizedString(@"From: peer exchange", "Inspector -> Peers tab -> table row tooltip")];
846        else
847            [components addObject: NSLocalizedString(@"From: tracker", "Inspector -> Peers tab -> table row tooltip")];
848       
849        return [components componentsJoinedByString: @"\n"];
850    }
851    return nil;
852}
853
854- (int) outlineView: (NSOutlineView *) outlineView numberOfChildrenOfItem: (id) item
855{
856    if (!item)
857        return [fFiles count];
858    return [[item objectForKey: @"IsFolder"] boolValue] ? [[item objectForKey: @"Children"] count] : 0;
859}
860
861- (BOOL) outlineView: (NSOutlineView *) outlineView isItemExpandable: (id) item
862{
863    return [[item objectForKey: @"IsFolder"] boolValue];
864}
865
866- (id) outlineView: (NSOutlineView *) outlineView child: (int) index ofItem: (id) item
867{
868    return [(item ? [item objectForKey: @"Children"] : fFiles) objectAtIndex: index];
869}
870
871- (id) outlineView: (NSOutlineView *) outlineView objectValueForTableColumn: (NSTableColumn *) tableColumn byItem: (id) item
872{
873    if ([[tableColumn identifier] isEqualToString: @"Check"])
874        return [NSNumber numberWithInt: [[fTorrents objectAtIndex: 0] checkForFiles: [item objectForKey: @"Indexes"]]];
875    else
876        return item;
877}
878
879- (void) outlineView: (NSOutlineView *) outlineView willDisplayCell: (id) cell
880            forTableColumn: (NSTableColumn *) tableColumn item: (id) item
881{
882    NSString * identifier = [tableColumn identifier];
883    if ([identifier isEqualToString: @"Name"])
884    {
885        if ([[item objectForKey: @"IsFolder"] boolValue])
886            [cell setImage: nil];
887        else
888        {
889            [cell setImage: [item objectForKey: @"Icon"]];
890            [(FileBrowserCell *)cell setProgress: [[fTorrents objectAtIndex: 0] fileProgress:
891                                                    [[item objectForKey: @"Indexes"] firstIndex]]];
892        }
893    }
894    else if ([identifier isEqualToString: @"Check"])
895        [cell setEnabled: [[fTorrents objectAtIndex: 0] canChangeDownloadCheckForFiles: [item objectForKey: @"Indexes"]]];
896    else if ([identifier isEqualToString: @"Priority"])
897        [(FilePriorityCell *)cell setItem: item];
898    else;
899}
900
901- (void) outlineView: (NSOutlineView *) outlineView setObjectValue: (id) object
902        forTableColumn: (NSTableColumn *) tableColumn byItem: (id) item
903{
904    NSString * identifier = [tableColumn identifier];
905    if ([identifier isEqualToString: @"Check"])
906    {
907        Torrent * torrent = [fTorrents objectAtIndex: 0];
908        NSIndexSet * indexSet;
909        if ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask)
910            indexSet = [NSIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [torrent fileCount])];
911        else
912            indexSet = [item objectForKey: @"Indexes"];
913       
914        [torrent setFileCheckState: [object intValue] != NSOffState ? NSOnState : NSOffState forIndexes: indexSet];
915        [fFileOutline reloadData];
916    }
917    else;
918}
919
920- (NSString *) outlineView: (NSOutlineView *) outlineView toolTipForCell: (NSCell *) cell rect: (NSRectPointer) rect
921        tableColumn: (NSTableColumn *) tableColumn item: (id) item mouseLocation: (NSPoint) mouseLocation
922{
923    NSString * ident = [tableColumn identifier];
924    if ([ident isEqualToString: @"Name"])
925        return [[[fTorrents objectAtIndex: 0] downloadFolder] stringByAppendingPathComponent: [item objectForKey: @"Path"]];
926    else if ([ident isEqualToString: @"Check"])
927    {
928        int check = [cell state];
929        if (check == NSOffState)
930            return NSLocalizedString(@"Don't Download", "Inspector -> files tab -> tooltip");
931        else if (check == NSMixedState)
932            return NSLocalizedString(@"Download Some", "Inspector -> files tab -> tooltip");
933        else
934            return NSLocalizedString(@"Download", "Inspector -> files tab -> tooltip");
935    }
936    else if ([ident isEqualToString: @"Priority"])
937    {
938        NSSet * priorities = [[fTorrents objectAtIndex: 0] filePrioritiesForIndexes: [item objectForKey: @"Indexes"]];
939       
940        int count = [priorities count];
941        if (count == 0)
942            return NSLocalizedString(@"Priority Not Available", "Inspector -> files tab -> tooltip");
943        else if (count > 1)
944            return NSLocalizedString(@"Multiple Priorities", "Inspector -> files tab -> tooltip");
945        else
946        {
947            int priority = [[priorities anyObject] intValue];
948            if (priority == TR_PRI_LOW)
949                return NSLocalizedString(@"Low Priority", "Inspector -> files tab -> tooltip");
950            else if (priority == TR_PRI_HIGH)
951                return NSLocalizedString(@"High Priority", "Inspector -> files tab -> tooltip");
952            else
953                return NSLocalizedString(@"Normal Priority", "Inspector -> files tab -> tooltip");
954        }
955    }
956    else
957        return nil;
958}
959
960- (float) outlineView: (NSOutlineView *) outlineView heightOfRowByItem: (id) item
961{
962    if ([[item objectForKey: @"IsFolder"] boolValue])
963        return FILE_ROW_SMALL_HEIGHT;
964    else
965        return [outlineView rowHeight];
966}
967
968- (void) setFileOutlineHoverRowForEvent: (NSEvent *) event
969{
970    [fFileOutline setHoverRowForEvent: [[[fTabView selectedTabViewItem] identifier] isEqualToString: TAB_FILES_IDENT]
971                                        ? event : nil];
972}
973
974- (NSArray *) peerSortDescriptors
975{
976    NSMutableArray * descriptors = [NSMutableArray array];
977   
978    NSArray * oldDescriptors = [fPeerTable sortDescriptors];
979    BOOL useSecond = YES, asc = YES;
980    if ([oldDescriptors count] > 0)
981    {
982        NSSortDescriptor * descriptor = [oldDescriptors objectAtIndex: 0];
983        [descriptors addObject: descriptor];
984       
985        if ((useSecond = ![[descriptor key] isEqualToString: @"IP"]))
986            asc = [descriptor ascending];
987    }
988   
989    //sort by IP after primary sort
990    if (useSecond)
991    {
992        NSSortDescriptor * secondDescriptor = [[NSSortDescriptor alloc] initWithKey: @"IP" ascending: asc
993                                                                        selector: @selector(compareIP:)];
994        [descriptors addObject: secondDescriptor];
995        [secondDescriptor release];
996    }
997   
998    return descriptors;
999}
1000
1001- (void) setPiecesView: (id) sender
1002{
1003    [self setPiecesViewForAvailable: [sender selectedSegment] == PIECES_CONTROL_AVAILABLE];
1004}
1005
1006- (void) setPiecesViewForAvailable: (BOOL) available
1007{
1008    [fPiecesControl setSelected: available forSegment: PIECES_CONTROL_AVAILABLE];
1009    [fPiecesControl setSelected: !available forSegment: PIECES_CONTROL_PROGRESS];
1010   
1011    [[NSUserDefaults standardUserDefaults] setBool: available forKey: @"PiecesViewShowAvailability"];
1012    [fPiecesView updateView: YES];
1013}
1014
1015- (void) revealTorrentFile: (id) sender
1016{
1017    if ([fTorrents count] > 0)
1018        [[fTorrents objectAtIndex: 0] revealPublicTorrent];
1019}
1020
1021- (void) revealDataFile: (id) sender
1022{
1023    if ([fTorrents count] > 0)
1024        [[fTorrents objectAtIndex: 0] revealData];
1025}
1026
1027- (void) revealFile: (id) sender
1028{
1029    if (!fFiles)
1030        return;
1031   
1032    NSString * folder = [[fTorrents objectAtIndex: 0] downloadFolder];
1033    NSIndexSet * indexes = [fFileOutline selectedRowIndexes];
1034    int i;
1035    for (i = [indexes firstIndex]; i != NSNotFound; i = [indexes indexGreaterThanIndex: i])
1036        [[NSWorkspace sharedWorkspace] selectFile: [folder stringByAppendingPathComponent:
1037                [[fFileOutline itemAtRow: i] objectForKey: @"Path"]] inFileViewerRootedAtPath: nil];
1038}
1039
1040- (void) setCheck: (id) sender
1041{
1042    int state = sender == fFileCheckItem ? NSOnState : NSOffState;
1043   
1044    Torrent * torrent = [fTorrents objectAtIndex: 0];
1045    NSIndexSet * indexSet = [fFileOutline selectedRowIndexes];
1046    NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
1047    int i;
1048    for (i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
1049        [itemIndexes addIndexes: [[fFileOutline itemAtRow: i] objectForKey: @"Indexes"]];
1050   
1051    [torrent setFileCheckState: state forIndexes: itemIndexes];
1052    [fFileOutline reloadData];
1053}
1054
1055- (void) setOnlySelectedCheck: (id) sender
1056{
1057    Torrent * torrent = [fTorrents objectAtIndex: 0];
1058    NSIndexSet * indexSet = [fFileOutline selectedRowIndexes];
1059    NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
1060    int i;
1061    for (i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
1062        [itemIndexes addIndexes: [[fFileOutline itemAtRow: i] objectForKey: @"Indexes"]];
1063   
1064    [torrent setFileCheckState: NSOnState forIndexes: itemIndexes];
1065   
1066    NSMutableIndexSet * remainingItemIndexes = [NSMutableIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [torrent fileCount])];
1067    [remainingItemIndexes removeIndexes: itemIndexes];
1068    [torrent setFileCheckState: NSOffState forIndexes: remainingItemIndexes];
1069   
1070    [fFileOutline reloadData];
1071}
1072
1073- (void) setPriority: (id) sender
1074{
1075    int priority;
1076    if (sender == fFilePriorityHigh)
1077        priority = TR_PRI_HIGH;
1078    else if (sender == fFilePriorityLow)
1079        priority = TR_PRI_LOW;
1080    else
1081        priority = TR_PRI_NORMAL;
1082   
1083    Torrent * torrent = [fTorrents objectAtIndex: 0];
1084    NSIndexSet * indexSet = [fFileOutline selectedRowIndexes];
1085    NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
1086    int i;
1087    for (i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
1088        [itemIndexes addIndexes: [[fFileOutline itemAtRow: i] objectForKey: @"Indexes"]];
1089   
1090    [torrent setFilePriority: priority forIndexes: itemIndexes];
1091    [fFileOutline reloadData];
1092}
1093
1094- (void) setLimitSetting: (id) sender
1095{
1096    BOOL upload = sender == fUploadLimitPopUp;
1097    int setting;
1098    if ((setting = [self popUpIndexToStateSetting: [sender indexOfSelectedItem]]) == INVALID)
1099        return;
1100   
1101    Torrent * torrent;
1102    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1103    while ((torrent = [enumerator nextObject]))
1104        upload ? [torrent setCheckUpload: setting] : [torrent setCheckDownload: setting];
1105   
1106    NSTextField * field = upload ? fUploadLimitField : fDownloadLimitField;
1107    [field setHidden: setting != NSOnState];
1108    if (setting == NSOnState)
1109    {
1110        [field selectText: self];
1111        [[self window] makeKeyAndOrderFront:self];
1112    }
1113   
1114    NSTextField * label = upload ? fUploadLimitLabel : fDownloadLimitLabel;
1115    [label setHidden: setting != NSOnState];
1116}
1117
1118- (void) setSpeedLimit: (id) sender
1119{
1120    BOOL upload = sender == fUploadLimitField;
1121   
1122    Torrent * torrent;
1123    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1124
1125    int limit = [sender intValue];
1126    if (![[sender stringValue] isEqualToString: [NSString stringWithFormat: @"%i", limit]] || limit < 0)
1127    {
1128        NSBeep();
1129       
1130        torrent = [enumerator nextObject]; //use first torrent
1131        limit = upload ? [torrent uploadLimit] : [torrent downloadLimit];
1132        while ((torrent = [enumerator nextObject]))
1133            if (limit != (upload ? [torrent uploadLimit] : [torrent downloadLimit]))
1134            {
1135                [sender setStringValue: @""];
1136                return;
1137            }
1138       
1139        [sender setIntValue: limit];
1140    }
1141    else
1142    {
1143        while ((torrent = [enumerator nextObject]))
1144            upload ? [torrent setUploadLimit: limit] : [torrent setDownloadLimit: limit];
1145    }
1146}
1147
1148- (void) setRatioSetting: (id) sender
1149{
1150    int setting;
1151    if ((setting = [self popUpIndexToStateSetting: [sender indexOfSelectedItem]]) == INVALID)
1152        return;
1153   
1154    Torrent * torrent;
1155    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1156    while ((torrent = [enumerator nextObject]))
1157        [torrent setRatioSetting: setting];
1158   
1159    [fRatioLimitField setHidden: setting != NSOnState];
1160    if (setting == NSOnState)
1161    {
1162        [fRatioLimitField selectText: self];
1163        [[self window] makeKeyAndOrderFront:self];
1164    }
1165   
1166    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
1167}
1168
1169- (void) setRatioLimit: (id) sender
1170{
1171    Torrent * torrent;
1172    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1173
1174    float ratioLimit = [sender floatValue];
1175    if (![[sender stringValue] isEqualToString: [NSString stringWithFormat: @"%.2f", ratioLimit]] || ratioLimit < 0)
1176    {
1177        NSBeep();
1178        float ratioLimit = [[enumerator nextObject] ratioLimit]; //use first torrent
1179        while ((torrent = [enumerator nextObject]))
1180            if (ratioLimit != [torrent ratioLimit])
1181            {
1182                [sender setStringValue: @""];
1183                return;
1184            }
1185       
1186        [sender setFloatValue: ratioLimit];
1187    }
1188    else
1189    {
1190        while ((torrent = [enumerator nextObject]))
1191            [torrent setRatioLimit: ratioLimit];
1192    }
1193   
1194    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
1195}
1196
1197- (void) setPex: (id) sender
1198{
1199        int state = [sender state];
1200        if (state == NSMixedState)
1201        {
1202                state = NSOnState;
1203                [sender setState: state];
1204        }
1205       
1206        Torrent * torrent;
1207    NSEnumerator * enumerator = [fTorrents objectEnumerator];
1208       
1209        while ((torrent = [enumerator nextObject]))
1210                [torrent setPex: state == NSOnState];
1211}
1212
1213@end
Note: See TracBrowser for help on using the repository browser.