source: trunk/macosx/InfoWindowController.m @ 1867

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

have the secondary sort alternate ascending/descending when the primary does

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