source: trunk/macosx/PrefsController.m @ 13108

Last change on this file since 13108 was 13108, checked in by livings124, 10 years ago

eliminate all warnings by switching NSOpenPanel/NSSavePanels to use completion handler blocks

  • Property svn:keywords set to Date Rev Author Id
File size: 51.3 KB
Line 
1/******************************************************************************
2 * $Id: PrefsController.m 13108 2011-12-11 22:31:01Z livings124 $
3 *
4 * Copyright (c) 2005-2011 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 "PrefsController.h"
26#import "BlocklistDownloaderViewController.h"
27#import "BlocklistScheduler.h"
28#import "PortChecker.h"
29#import "BonjourController.h"
30#import "NSStringAdditions.h"
31#import "UKKQueue.h"
32
33#import "transmission.h"
34#import "utils.h"
35
36#import <Growl/Growl.h>
37#import <Sparkle/Sparkle.h>
38
39#define DOWNLOAD_FOLDER     0
40#define DOWNLOAD_TORRENT    2
41
42#define RPC_IP_ADD_TAG      0
43#define RPC_IP_REMOVE_TAG   1
44
45#define TOOLBAR_GENERAL     @"TOOLBAR_GENERAL"
46#define TOOLBAR_TRANSFERS   @"TOOLBAR_TRANSFERS"
47#define TOOLBAR_GROUPS      @"TOOLBAR_GROUPS"
48#define TOOLBAR_BANDWIDTH   @"TOOLBAR_BANDWIDTH"
49#define TOOLBAR_PEERS       @"TOOLBAR_PEERS"
50#define TOOLBAR_NETWORK     @"TOOLBAR_NETWORK"
51#define TOOLBAR_REMOTE      @"TOOLBAR_REMOTE"
52
53#define RPC_KEYCHAIN_SERVICE    "Transmission:Remote"
54#define RPC_KEYCHAIN_NAME       "Remote"
55
56#define WEBUI_URL   @"http://localhost:%d/"
57
58@interface PrefsController (Private)
59
60- (void) setPrefView: (id) sender;
61
62- (void) setKeychainPassword: (const char *) password forService: (const char *) service username: (const char *) username;
63
64@end
65
66@implementation PrefsController
67
68tr_session * fHandle;
69+ (void) setHandle: (tr_session *) handle
70{
71    fHandle = handle;
72}
73
74+ (tr_session *) handle
75{
76    return fHandle;
77}
78
79- (id) init
80{
81    if ((self = [super initWithWindowNibName: @"PrefsWindow"]))
82    {
83        fDefaults = [NSUserDefaults standardUserDefaults];
84       
85        //check for old version download location (before 1.1)
86        NSString * choice;
87        if ((choice = [fDefaults stringForKey: @"DownloadChoice"]))
88        {
89            [fDefaults setBool: [choice isEqualToString: @"Constant"] forKey: @"DownloadLocationConstant"];
90            [fDefaults setBool: YES forKey: @"DownloadAsk"];
91           
92            [fDefaults removeObjectForKey: @"DownloadChoice"];
93        }
94       
95        //check for old version blocklist (before 2.12)
96        NSDate * blocklistDate;
97        if ((blocklistDate = [fDefaults objectForKey: @"BlocklistLastUpdate"]))
98        {
99            [fDefaults setObject: blocklistDate forKey: @"BlocklistNewLastUpdateSuccess"];
100            [fDefaults setObject: blocklistDate forKey: @"BlocklistNewLastUpdate"];
101            [fDefaults removeObjectForKey: @"BlocklistLastUpdate"];
102           
103            NSString * blocklistDir = [NSHomeDirectory() stringByAppendingPathComponent:
104                                        @"/Library/Application Support/Transmission/blocklists/"];
105            [[NSFileManager defaultManager] moveItemAtPath: [blocklistDir stringByAppendingPathComponent: @"level1.bin"]
106                toPath: [blocklistDir stringByAppendingPathComponent: [NSString stringWithUTF8String: DEFAULT_BLOCKLIST_FILENAME]]
107                error: nil];
108        }
109       
110        //save a new random port
111        if ([fDefaults boolForKey: @"RandomPort"])
112            [fDefaults setInteger: tr_sessionGetPeerPort(fHandle) forKey: @"BindPort"];
113       
114        //set auto import
115        NSString * autoPath;
116        if ([fDefaults boolForKey: @"AutoImport"] && (autoPath = [fDefaults stringForKey: @"AutoImportDirectory"]))
117            [[UKKQueue sharedFileWatcher] addPath: [autoPath stringByExpandingTildeInPath]];
118       
119        //set blocklist scheduler
120        [[BlocklistScheduler scheduler] updateSchedule];
121       
122        //set encryption
123        [self setEncryptionMode: nil];
124       
125        //update rpc whitelist
126        [self updateRPCPassword];
127       
128        fRPCWhitelistArray = [[fDefaults arrayForKey: @"RPCWhitelist"] mutableCopy];
129        if (!fRPCWhitelistArray)
130            fRPCWhitelistArray = [[NSMutableArray arrayWithObject: @"127.0.0.1"] retain];
131        [self updateRPCWhitelist];
132       
133        //reset old Sparkle settings from previous versions
134        [fDefaults removeObjectForKey: @"SUScheduledCheckInterval"];
135        if ([fDefaults objectForKey: @"CheckForUpdates"])
136        {
137            [[SUUpdater sharedUpdater] setAutomaticallyChecksForUpdates: [fDefaults boolForKey: @"CheckForUpdates"]];
138            [fDefaults removeObjectForKey: @"CheckForUpdates"];
139        }
140       
141        [self setAutoUpdateToBeta: nil];
142    }
143   
144    return self;
145}
146
147- (void) dealloc
148{
149    [[NSNotificationCenter defaultCenter] removeObserver: self];
150   
151    [fPortStatusTimer invalidate];
152    if (fPortChecker)
153    {
154        [fPortChecker cancelProbe];
155        [fPortChecker release];
156    }
157   
158    [fRPCWhitelistArray release];
159   
160    [fRPCPassword release];
161   
162    [super dealloc];
163}
164
165- (void) awakeFromNib
166{
167    fHasLoaded = YES;
168   
169    NSToolbar * toolbar = [[NSToolbar alloc] initWithIdentifier: @"Preferences Toolbar"];
170    [toolbar setDelegate: self];
171    [toolbar setAllowsUserCustomization: NO];
172    [toolbar setDisplayMode: NSToolbarDisplayModeIconAndLabel];
173    [toolbar setSizeMode: NSToolbarSizeModeRegular];
174    [toolbar setSelectedItemIdentifier: TOOLBAR_GENERAL];
175    [[self window] setToolbar: toolbar];
176    [toolbar release];
177   
178    [self setPrefView: nil];
179   
180    [fBuiltInGrowlButton setState: [GrowlApplicationBridge shouldUseBuiltInNotifications]];
181    const BOOL growlRunning = [GrowlApplicationBridge isGrowlRunning];
182    [fBuiltInGrowlButton setHidden: growlRunning];
183    [fGrowlInstalledField setHidden: !growlRunning];
184   
185    //set download folder
186    [fFolderPopUp selectItemAtIndex: [fDefaults boolForKey: @"DownloadLocationConstant"] ? DOWNLOAD_FOLDER : DOWNLOAD_TORRENT];
187   
188    //set stop ratio
189    [fRatioStopField setFloatValue: [fDefaults floatForKey: @"RatioLimit"]];
190   
191    //set idle seeding minutes
192    [fIdleStopField setIntegerValue: [fDefaults integerForKey: @"IdleLimitMinutes"]];
193   
194    //set limits
195    [self updateLimitFields];
196   
197    //set speed limit
198    [fSpeedLimitUploadField setIntValue: [fDefaults integerForKey: @"SpeedLimitUploadLimit"]];
199    [fSpeedLimitDownloadField setIntValue: [fDefaults integerForKey: @"SpeedLimitDownloadLimit"]];
200   
201    //set port
202    [fPortField setIntValue: [fDefaults integerForKey: @"BindPort"]];
203    fNatStatus = -1;
204   
205    [self updatePortStatus];
206    fPortStatusTimer = [NSTimer scheduledTimerWithTimeInterval: 5.0 target: self
207                        selector: @selector(updatePortStatus) userInfo: nil repeats: YES];
208   
209    //set peer connections
210    [fPeersGlobalField setIntValue: [fDefaults integerForKey: @"PeersTotal"]];
211    [fPeersTorrentField setIntValue: [fDefaults integerForKey: @"PeersTorrent"]];
212   
213    //set queue values
214    [fQueueDownloadField setIntValue: [fDefaults integerForKey: @"QueueDownloadNumber"]];
215    [fQueueSeedField setIntValue: [fDefaults integerForKey: @"QueueSeedNumber"]];
216    [fStalledField setIntValue: [fDefaults integerForKey: @"StalledMinutes"]];
217   
218    //set blocklist
219    NSString * blocklistURL = [fDefaults stringForKey: @"BlocklistURL"];
220    if (blocklistURL)
221        [fBlocklistURLField setStringValue: blocklistURL];
222   
223    [self updateBlocklistButton];
224    [self updateBlocklistFields];
225   
226    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(updateLimitFields)
227                                                 name: @"UpdateSpeedLimitValuesOutsidePrefs" object: nil];
228   
229    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(updateRatioStopField)
230                                                 name: @"UpdateRatioStopValueOutsidePrefs" object: nil];
231   
232    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(updateLimitStopField)
233                                                 name: @"UpdateIdleStopValueOutsidePrefs" object: nil];
234   
235    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(updateBlocklistFields)
236        name: @"BlocklistUpdated" object: nil];
237   
238    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(updateBlocklistURLField)
239        name: NSControlTextDidChangeNotification object: fBlocklistURLField];
240   
241    //set rpc port
242    [fRPCPortField setIntValue: [fDefaults integerForKey: @"RPCPort"]];
243   
244    //set rpc password
245    if (fRPCPassword)
246        [fRPCPasswordField setStringValue: fRPCPassword];
247}
248
249- (NSToolbarItem *) toolbar: (NSToolbar *) toolbar itemForItemIdentifier: (NSString *) ident willBeInsertedIntoToolbar: (BOOL) flag
250{
251    NSToolbarItem * item = [[NSToolbarItem alloc] initWithItemIdentifier: ident];
252
253    if ([ident isEqualToString: TOOLBAR_GENERAL])
254    {
255        [item setLabel: NSLocalizedString(@"General", "Preferences -> toolbar item title")];
256        [item setImage: [NSImage imageNamed: NSImageNamePreferencesGeneral]];
257        [item setTarget: self];
258        [item setAction: @selector(setPrefView:)];
259        [item setAutovalidates: NO];
260    }
261    else if ([ident isEqualToString: TOOLBAR_TRANSFERS])
262    {
263        [item setLabel: NSLocalizedString(@"Transfers", "Preferences -> toolbar item title")];
264        [item setImage: [NSImage imageNamed: @"Transfers.png"]];
265        [item setTarget: self];
266        [item setAction: @selector(setPrefView:)];
267        [item setAutovalidates: NO];
268    }
269    else if ([ident isEqualToString: TOOLBAR_GROUPS])
270    {
271        [item setLabel: NSLocalizedString(@"Groups", "Preferences -> toolbar item title")];
272        [item setImage: [NSImage imageNamed: @"Groups.png"]];
273        [item setTarget: self];
274        [item setAction: @selector(setPrefView:)];
275        [item setAutovalidates: NO];
276    }
277    else if ([ident isEqualToString: TOOLBAR_BANDWIDTH])
278    {
279        [item setLabel: NSLocalizedString(@"Bandwidth", "Preferences -> toolbar item title")];
280        [item setImage: [NSImage imageNamed: @"Bandwidth.png"]];
281        [item setTarget: self];
282        [item setAction: @selector(setPrefView:)];
283        [item setAutovalidates: NO];
284    }
285    else if ([ident isEqualToString: TOOLBAR_PEERS])
286    {
287        [item setLabel: NSLocalizedString(@"Peers", "Preferences -> toolbar item title")];
288        [item setImage: [NSImage imageNamed: NSImageNameUserGroup]];
289        [item setTarget: self];
290        [item setAction: @selector(setPrefView:)];
291        [item setAutovalidates: NO];
292    }
293    else if ([ident isEqualToString: TOOLBAR_NETWORK])
294    {
295        [item setLabel: NSLocalizedString(@"Network", "Preferences -> toolbar item title")];
296        [item setImage: [NSImage imageNamed: NSImageNameNetwork]];
297        [item setTarget: self];
298        [item setAction: @selector(setPrefView:)];
299        [item setAutovalidates: NO];
300    }
301    else if ([ident isEqualToString: TOOLBAR_REMOTE])
302    {
303        [item setLabel: NSLocalizedString(@"Remote", "Preferences -> toolbar item title")];
304        [item setImage: [NSImage imageNamed: @"Remote.png"]];
305        [item setTarget: self];
306        [item setAction: @selector(setPrefView:)];
307        [item setAutovalidates: NO];
308    }
309    else
310    {
311        [item release];
312        return nil;
313    }
314
315    return [item autorelease];
316}
317
318- (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar
319{
320    return [NSArray arrayWithObjects: TOOLBAR_GENERAL, TOOLBAR_TRANSFERS, TOOLBAR_GROUPS, TOOLBAR_BANDWIDTH,
321                                        TOOLBAR_PEERS, TOOLBAR_NETWORK, TOOLBAR_REMOTE, nil];
322}
323
324- (NSArray *) toolbarSelectableItemIdentifiers: (NSToolbar *) toolbar
325{
326    return [self toolbarAllowedItemIdentifiers: toolbar];
327}
328
329- (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar
330{
331    return [self toolbarAllowedItemIdentifiers: toolbar];
332}
333
334//for a beta release, always use the beta appcast
335#if defined(TR_BETA_RELEASE)
336#define SPARKLE_TAG YES
337#else
338#define SPARKLE_TAG [fDefaults boolForKey: @"AutoUpdateBeta"]
339#endif
340- (void) setAutoUpdateToBeta: (id) sender
341{
342    [[SUUpdater sharedUpdater] setAllowedTags: SPARKLE_TAG ? [NSSet setWithObject: @"beta"] : nil];
343}
344
345- (void) setPort: (id) sender
346{
347    const tr_port port = [sender intValue];
348    [fDefaults setInteger: port forKey: @"BindPort"];
349    tr_sessionSetPeerPort(fHandle, port);
350   
351    fPeerPort = -1;
352    [self updatePortStatus];
353}
354
355- (void) randomPort: (id) sender
356{
357    const tr_port port = tr_sessionSetPeerPortRandom(fHandle);
358    [fDefaults setInteger: port forKey: @"BindPort"];
359    [fPortField setIntValue: port];
360   
361    fPeerPort = -1;
362    [self updatePortStatus];
363}
364
365- (void) setRandomPortOnStart: (id) sender
366{
367    tr_sessionSetPeerPortRandomOnStart(fHandle, [sender state] == NSOnState);
368}
369
370- (void) setNat: (id) sender
371{
372    tr_sessionSetPortForwardingEnabled(fHandle, [fDefaults boolForKey: @"NatTraversal"]);
373   
374    fNatStatus = -1;
375    [self updatePortStatus];
376}
377
378- (void) updatePortStatus
379{
380    const tr_port_forwarding fwd = tr_sessionGetPortForwarding(fHandle);
381    const int port = tr_sessionGetPeerPort(fHandle);
382    BOOL natStatusChanged = (fNatStatus != fwd);
383    BOOL peerPortChanged = (fPeerPort != port);
384
385    if (natStatusChanged || peerPortChanged)
386    {
387        fNatStatus = fwd;
388        fPeerPort = port;
389       
390        [fPortStatusField setStringValue: @""];
391        [fPortStatusImage setImage: nil];
392        [fPortStatusProgress startAnimation: self];
393       
394        if (fPortChecker)
395        {
396            [fPortChecker cancelProbe];
397            [fPortChecker release];
398        }
399        BOOL delay = natStatusChanged || tr_sessionIsPortForwardingEnabled(fHandle);
400        fPortChecker = [[PortChecker alloc] initForPort: fPeerPort delay: delay withDelegate: self];
401    }
402}
403
404- (void) portCheckerDidFinishProbing: (PortChecker *) portChecker
405{
406    [fPortStatusProgress stopAnimation: self];
407    switch ([fPortChecker status])
408    {
409        case PORT_STATUS_OPEN:
410            [fPortStatusField setStringValue: NSLocalizedString(@"Port is open", "Preferences -> Network -> port status")];
411            [fPortStatusImage setImage: [NSImage imageNamed: @"GreenDot.png"]];
412            break;
413        case PORT_STATUS_CLOSED:
414            [fPortStatusField setStringValue: NSLocalizedString(@"Port is closed", "Preferences -> Network -> port status")];
415            [fPortStatusImage setImage: [NSImage imageNamed: @"RedDot.png"]];
416            break;
417        case PORT_STATUS_ERROR:
418            [fPortStatusField setStringValue: NSLocalizedString(@"Port check site is down", "Preferences -> Network -> port status")];
419            [fPortStatusImage setImage: [NSImage imageNamed: @"YellowDot.png"]];
420            break;
421        default:
422            NSAssert1(NO, @"Port checker returned invalid status: %d", [fPortChecker status]);
423            break;
424    }
425    [fPortChecker release];
426    fPortChecker = nil;
427}
428
429- (NSArray *) sounds
430{
431    NSMutableArray * sounds = [NSMutableArray array];
432   
433    NSArray * directories = NSSearchPathForDirectoriesInDomains(NSAllLibrariesDirectory,
434                                NSUserDomainMask | NSLocalDomainMask | NSSystemDomainMask, YES);
435   
436    for (NSString * directory in directories)
437    {
438        directory = [directory stringByAppendingPathComponent: @"Sounds"];
439       
440        BOOL isDirectory;
441        if ([[NSFileManager defaultManager] fileExistsAtPath: directory isDirectory: &isDirectory] && isDirectory)
442        {
443            NSArray * directoryContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath: directory error: NULL];
444            for (NSString * sound in directoryContents)
445            {
446                sound = [sound stringByDeletingPathExtension];
447                if ([NSSound soundNamed: sound])
448                    [sounds addObject: sound];
449            }
450        }
451    }
452   
453    return sounds;
454}
455
456- (void) setSound: (id) sender
457{
458    //play sound when selecting
459    NSSound * sound;
460    if ((sound = [NSSound soundNamed: [sender titleOfSelectedItem]]))
461        [sound play];
462}
463
464- (void) setUTP: (id) sender
465{
466    tr_sessionSetUTPEnabled(fHandle, [fDefaults boolForKey: @"UTPGlobal"]);
467}
468
469- (void) setPeersGlobal: (id) sender
470{
471    const int count = [sender intValue];
472    [fDefaults setInteger: count forKey: @"PeersTotal"];
473    tr_sessionSetPeerLimit(fHandle, count);
474}
475
476- (void) setPeersTorrent: (id) sender
477{
478    const int count = [sender intValue];
479    [fDefaults setInteger: count forKey: @"PeersTorrent"];
480    tr_sessionSetPeerLimitPerTorrent(fHandle, count);
481}
482
483- (void) setPEX: (id) sender
484{
485    tr_sessionSetPexEnabled(fHandle, [fDefaults boolForKey: @"PEXGlobal"]);
486}
487
488- (void) setDHT: (id) sender
489{
490    tr_sessionSetDHTEnabled(fHandle, [fDefaults boolForKey: @"DHTGlobal"]);
491}
492
493- (void) setLPD: (id) sender
494{
495    tr_sessionSetLPDEnabled(fHandle, [fDefaults boolForKey: @"LocalPeerDiscoveryGlobal"]);
496}
497
498- (void) setEncryptionMode: (id) sender
499{
500    const tr_encryption_mode mode = [fDefaults boolForKey: @"EncryptionPrefer"] ?
501        ([fDefaults boolForKey: @"EncryptionRequire"] ? TR_ENCRYPTION_REQUIRED : TR_ENCRYPTION_PREFERRED) : TR_CLEAR_PREFERRED;
502    tr_sessionSetEncryption(fHandle, mode);
503}
504
505- (void) setBlocklistEnabled: (id) sender
506{
507    tr_blocklistSetEnabled(fHandle, [fDefaults boolForKey: @"BlocklistNew"]);
508   
509    [[BlocklistScheduler scheduler] updateSchedule];
510   
511    [self updateBlocklistButton];
512}
513
514- (void) updateBlocklist: (id) sender
515{
516    [BlocklistDownloaderViewController downloadWithPrefsController: self];
517}
518
519- (void) setBlocklistAutoUpdate: (id) sender
520{
521    [[BlocklistScheduler scheduler] updateSchedule];
522}
523
524- (void) updateBlocklistFields
525{
526    const BOOL exists = tr_blocklistExists(fHandle);
527   
528    if (exists)
529    {
530        NSString * countString = [NSString formattedUInteger: tr_blocklistGetRuleCount(fHandle)];
531        [fBlocklistMessageField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%@ IP address rules in list",
532            "Prefs -> blocklist -> message"), countString]];
533    }
534    else
535        [fBlocklistMessageField setStringValue: NSLocalizedString(@"A blocklist must first be downloaded",
536            "Prefs -> blocklist -> message")];
537   
538    NSString * updatedDateString;
539    if (exists)
540    {
541        NSDate * updatedDate = [fDefaults objectForKey: @"BlocklistNewLastUpdateSuccess"];
542       
543        if (updatedDate)
544            updatedDateString = [NSDateFormatter localizedStringFromDate: updatedDate dateStyle: NSDateFormatterFullStyle timeStyle: NSDateFormatterShortStyle];
545        else
546            updatedDateString = NSLocalizedString(@"N/A", "Prefs -> blocklist -> message");
547    }
548    else
549        updatedDateString = NSLocalizedString(@"Never", "Prefs -> blocklist -> message");
550   
551    [fBlocklistDateField setStringValue: [NSString stringWithFormat: @"%@: %@",
552        NSLocalizedString(@"Last updated", "Prefs -> blocklist -> message"), updatedDateString]];
553}
554
555- (void) updateBlocklistURLField
556{
557    NSString * blocklistString = [fBlocklistURLField stringValue];
558   
559    [fDefaults setObject: blocklistString forKey: @"BlocklistURL"];
560    tr_blocklistSetURL(fHandle, [blocklistString UTF8String]);
561   
562    [self updateBlocklistButton];
563}
564
565- (void) updateBlocklistButton
566{
567    NSString * blocklistString = [fDefaults objectForKey: @"BlocklistURL"];
568    const BOOL enable = (blocklistString && ![blocklistString isEqualToString: @""])
569                            && [fDefaults boolForKey: @"BlocklistNew"];
570    [fBlocklistButton setEnabled: enable];
571}
572
573- (void) setAutoStartDownloads: (id) sender
574{
575    tr_sessionSetPaused(fHandle, ![fDefaults boolForKey: @"AutoStartDownload"]);
576}
577
578- (void) applySpeedSettings: (id) sender
579{
580    tr_sessionLimitSpeed(fHandle, TR_UP, [fDefaults boolForKey: @"CheckUpload"]);
581    tr_sessionSetSpeedLimit_KBps(fHandle, TR_UP, [fDefaults integerForKey: @"UploadLimit"]);
582   
583    tr_sessionLimitSpeed(fHandle, TR_DOWN, [fDefaults boolForKey: @"CheckDownload"]);
584    tr_sessionSetSpeedLimit_KBps(fHandle, TR_DOWN, [fDefaults integerForKey: @"DownloadLimit"]);
585   
586    [[NSNotificationCenter defaultCenter] postNotificationName: @"SpeedLimitUpdate" object: nil];
587}
588
589- (void) applyAltSpeedSettings
590{
591    tr_sessionSetAltSpeed_KBps(fHandle, TR_UP, [fDefaults integerForKey: @"SpeedLimitUploadLimit"]);
592    tr_sessionSetAltSpeed_KBps(fHandle, TR_DOWN, [fDefaults integerForKey: @"SpeedLimitDownloadLimit"]);
593       
594    [[NSNotificationCenter defaultCenter] postNotificationName: @"SpeedLimitUpdate" object: nil];
595}
596
597- (void) applyRatioSetting: (id) sender
598{
599    tr_sessionSetRatioLimited(fHandle, [fDefaults boolForKey: @"RatioCheck"]);
600    tr_sessionSetRatioLimit(fHandle, [fDefaults floatForKey: @"RatioLimit"]);
601   
602    //reload main table for seeding progress
603    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
604   
605    //reload global settings in inspector
606    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateGlobalOptions" object: nil];
607}
608
609- (void) setRatioStop: (id) sender
610{
611    [fDefaults setFloat: [sender floatValue] forKey: @"RatioLimit"];
612   
613    [self applyRatioSetting: nil];
614}
615
616- (void) updateRatioStopField
617{
618    if (fHasLoaded)
619        [fRatioStopField setFloatValue: [fDefaults floatForKey: @"RatioLimit"]];
620}
621
622- (void) updateRatioStopFieldOld
623{
624    [self updateRatioStopField];
625   
626    [self applyRatioSetting: nil];
627}
628
629- (void) applyIdleStopSetting: (id) sender
630{
631    tr_sessionSetIdleLimited(fHandle, [fDefaults boolForKey: @"IdleLimitCheck"]);
632    tr_sessionSetIdleLimit(fHandle, [fDefaults integerForKey: @"IdleLimitMinutes"]);
633   
634    //reload main table for remaining seeding time
635    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
636   
637    //reload global settings in inspector
638    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateGlobalOptions" object: nil];
639}
640
641- (void) setIdleStop: (id) sender
642{
643    [fDefaults setInteger: [sender integerValue] forKey: @"IdleLimitMinutes"];
644   
645    [self applyIdleStopSetting: nil];
646}
647
648- (void) updateLimitStopField
649{
650    if (fHasLoaded)
651        [fIdleStopField setIntegerValue: [fDefaults integerForKey: @"IdleLimitMinutes"]];
652}
653
654- (void) updateLimitFields
655{
656    if (!fHasLoaded)
657        return;
658   
659    [fUploadField setIntValue: [fDefaults integerForKey: @"UploadLimit"]];
660    [fDownloadField setIntValue: [fDefaults integerForKey: @"DownloadLimit"]];
661}
662
663- (void) setGlobalLimit: (id) sender
664{
665    [fDefaults setInteger: [sender intValue] forKey: sender == fUploadField ? @"UploadLimit" : @"DownloadLimit"];
666    [self applySpeedSettings: self];
667}
668
669- (void) setSpeedLimit: (id) sender
670{
671    [fDefaults setInteger: [sender intValue] forKey: sender == fSpeedLimitUploadField
672                                                        ? @"SpeedLimitUploadLimit" : @"SpeedLimitDownloadLimit"];
673    [self applyAltSpeedSettings];
674}
675
676- (void) setAutoSpeedLimit: (id) sender
677{
678    tr_sessionUseAltSpeedTime(fHandle, [fDefaults boolForKey: @"SpeedLimitAuto"]);
679}
680
681- (void) setAutoSpeedLimitTime: (id) sender
682{
683    tr_sessionSetAltSpeedBegin(fHandle, [PrefsController dateToTimeSum: [fDefaults objectForKey: @"SpeedLimitAutoOnDate"]]);
684    tr_sessionSetAltSpeedEnd(fHandle, [PrefsController dateToTimeSum: [fDefaults objectForKey: @"SpeedLimitAutoOffDate"]]);
685}
686
687- (void) setAutoSpeedLimitDay: (id) sender
688{
689    tr_sessionSetAltSpeedDay(fHandle, [[sender selectedItem] tag]);
690}
691
692+ (NSInteger) dateToTimeSum: (NSDate *) date
693{
694    NSCalendar * calendar = [NSCalendar currentCalendar];
695    NSDateComponents * components = [calendar components: NSHourCalendarUnit | NSMinuteCalendarUnit fromDate: date];
696    return [components hour] * 60 + [components minute];
697}
698
699+ (NSDate *) timeSumToDate: (NSInteger) sum
700{
701    NSDateComponents * comps = [[[NSDateComponents alloc] init] autorelease];
702    [comps setHour: sum / 60];
703    [comps setMinute: sum % 60];
704   
705    return [[NSCalendar currentCalendar] dateFromComponents: comps];
706}
707
708- (BOOL) control: (NSControl *) control textShouldBeginEditing: (NSText *) fieldEditor
709{
710    [fInitialString release];
711    fInitialString = [[control stringValue] retain];
712   
713    return YES;
714}
715
716- (BOOL) control: (NSControl *) control didFailToFormatString: (NSString *) string errorDescription: (NSString *) error
717{
718    NSBeep();
719    if (fInitialString)
720    {
721        [control setStringValue: fInitialString];
722        [fInitialString release];
723        fInitialString = nil;
724    }
725    return NO;
726}
727
728- (void) setBadge: (id) sender
729{
730    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: self];
731}
732
733- (IBAction) setBuiltInGrowlEnabled: (id) sender
734{
735    [GrowlApplicationBridge setShouldUseBuiltInNotifications: [sender state] == NSOnState];
736}
737
738- (void) resetWarnings: (id) sender
739{
740    [fDefaults removeObjectForKey: @"WarningDuplicate"];
741    [fDefaults removeObjectForKey: @"WarningRemainingSpace"];
742    [fDefaults removeObjectForKey: @"WarningFolderDataSameName"];
743    [fDefaults removeObjectForKey: @"WarningResetStats"];
744    [fDefaults removeObjectForKey: @"WarningCreatorBlankAddress"];
745    [fDefaults removeObjectForKey: @"WarningCreatorPrivateBlankAddress"];
746    [fDefaults removeObjectForKey: @"WarningRemoveTrackers"];
747    [fDefaults removeObjectForKey: @"WarningInvalidOpen"];
748    [fDefaults removeObjectForKey: @"WarningRemoveCompleted"];
749    [fDefaults removeObjectForKey: @"WarningDonate"];
750    //[fDefaults removeObjectForKey: @"WarningLegal"];
751}
752
753- (void) setDefaultForMagnets: (id) sender
754{
755    NSString * bundleID = [[NSBundle mainBundle] bundleIdentifier];
756    const OSStatus result = LSSetDefaultHandlerForURLScheme((CFStringRef)@"magnet", (CFStringRef)bundleID);
757    if (result != noErr)
758        NSLog(@"Failed setting default magnet link handler");
759}
760
761- (void) setQueue: (id) sender
762{
763    //let's just do both - easier that way
764    tr_sessionSetQueueEnabled(fHandle, TR_DOWN, [fDefaults boolForKey: @"Queue"]);
765    tr_sessionSetQueueEnabled(fHandle, TR_UP, [fDefaults boolForKey: @"QueueSeed"]);
766   
767    //handle if any transfers switch from queued to paused
768    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateQueue" object: self];
769}
770
771- (void) setQueueNumber: (id) sender
772{
773    const NSInteger number = [sender intValue];
774    const BOOL seed = sender == fQueueSeedField;
775   
776    [fDefaults setInteger: number forKey: seed ? @"QueueSeedNumber" : @"QueueDownloadNumber"];
777   
778    tr_sessionSetQueueSize(fHandle, seed ? TR_UP : TR_DOWN, number);
779}
780
781- (void) setStalled: (id) sender
782{
783    tr_sessionSetQueueStalledEnabled(fHandle, [fDefaults boolForKey: @"CheckStalled"]);
784   
785    //reload main table for stalled status
786    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
787}
788
789- (void) setStalledMinutes: (id) sender
790{
791    const NSInteger min = [sender intValue];
792    [fDefaults setInteger: min forKey: @"StalledMinutes"];
793    tr_sessionSetQueueStalledMinutes(fHandle, min);
794   
795    //reload main table for stalled status
796    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: self];
797}
798
799- (void) setDownloadLocation: (id) sender
800{
801    [fDefaults setBool: [fFolderPopUp indexOfSelectedItem] == DOWNLOAD_FOLDER forKey: @"DownloadLocationConstant"];
802}
803
804- (void) folderSheetShow: (id) sender
805{
806    NSOpenPanel * panel = [NSOpenPanel openPanel];
807
808    [panel setPrompt: NSLocalizedString(@"Select", "Preferences -> Open panel prompt")];
809    [panel setAllowsMultipleSelection: NO];
810    [panel setCanChooseFiles: NO];
811    [panel setCanChooseDirectories: YES];
812    [panel setCanCreateDirectories: YES];
813   
814    [panel beginSheetModalForWindow: [self window] completionHandler: ^(NSInteger result) {
815        if (result == NSFileHandlingPanelOKButton)
816        {
817            [fFolderPopUp selectItemAtIndex: DOWNLOAD_FOLDER];
818           
819            NSString * folder = [[[panel URLs] objectAtIndex: 0] path];
820            [fDefaults setObject: folder forKey: @"DownloadFolder"];
821            [fDefaults setObject: @"Constant" forKey: @"DownloadChoice"];
822           
823            tr_sessionSetDownloadDir(fHandle, [folder UTF8String]);
824        }
825        else
826        {
827            //reset if cancelled
828            [fFolderPopUp selectItemAtIndex: [fDefaults boolForKey: @"DownloadLocationConstant"] ? DOWNLOAD_FOLDER : DOWNLOAD_TORRENT];
829        }
830    }];
831}
832
833- (void) incompleteFolderSheetShow: (id) sender
834{
835    NSOpenPanel * panel = [NSOpenPanel openPanel];
836
837    [panel setPrompt: NSLocalizedString(@"Select", "Preferences -> Open panel prompt")];
838    [panel setAllowsMultipleSelection: NO];
839    [panel setCanChooseFiles: NO];
840    [panel setCanChooseDirectories: YES];
841    [panel setCanCreateDirectories: YES];
842   
843    [panel beginSheetModalForWindow: [self window] completionHandler: ^(NSInteger result) {
844        if (result == NSFileHandlingPanelOKButton)
845        {
846            NSString * folder = [[[panel URLs] objectAtIndex: 0] path];
847            [fDefaults setObject: folder forKey: @"IncompleteDownloadFolder"];
848           
849            tr_sessionSetIncompleteDir(fHandle, [folder UTF8String]);
850        }
851        [fIncompleteFolderPopUp selectItemAtIndex: 0];
852    }];
853}
854
855- (void) doneScriptSheetShow:(id)sender
856{
857    NSOpenPanel * panel = [NSOpenPanel openPanel];
858   
859    [panel setPrompt: NSLocalizedString(@"Select", "Preferences -> Open panel prompt")];
860    [panel setAllowsMultipleSelection: NO];
861    [panel setCanChooseFiles: YES];
862    [panel setCanChooseDirectories: NO];
863    [panel setCanCreateDirectories: NO];
864   
865    [panel beginSheetModalForWindow: [self window] completionHandler: ^(NSInteger result) {
866        if (result == NSFileHandlingPanelOKButton)
867        {
868            NSString * filePath = [[[panel URLs] objectAtIndex: 0] path];
869           
870            [fDefaults setObject: filePath forKey: @"DoneScriptPath"];
871            tr_sessionSetTorrentDoneScript(fHandle, [filePath UTF8String]);
872           
873            [fDefaults setBool: YES forKey: @"DoneScriptEnabled"];
874            tr_sessionSetTorrentDoneScriptEnabled(fHandle, YES);
875        }
876        [fDoneScriptPopUp selectItemAtIndex: 0];
877    }];
878}
879
880- (void) setUseIncompleteFolder: (id) sender
881{
882    tr_sessionSetIncompleteDirEnabled(fHandle, [fDefaults boolForKey: @"UseIncompleteDownloadFolder"]);
883}
884
885- (void) setRenamePartialFiles: (id) sender
886{
887    tr_sessionSetIncompleteFileNamingEnabled(fHandle, [fDefaults boolForKey: @"RenamePartialFiles"]);
888}
889
890- (void) setDoneScriptEnabled: (id) sender
891{
892    if ([fDefaults boolForKey: @"DoneScriptEnabled"] && ![[NSFileManager defaultManager] fileExistsAtPath: [fDefaults stringForKey:@"DoneScriptPath"]])
893    {
894        // enabled is set but script file doesn't exist, so prompt for one and disable until they pick one
895        [fDefaults setBool: NO forKey: @"DoneScriptEnabled"];
896        [self doneScriptSheetShow: sender];
897    }
898    tr_sessionSetTorrentDoneScriptEnabled(fHandle, [fDefaults boolForKey: @"DoneScriptEnabled"]);
899}
900
901- (void) setAutoImport: (id) sender
902{
903    NSString * path;
904    if ((path = [fDefaults stringForKey: @"AutoImportDirectory"]))
905    {
906        path = [path stringByExpandingTildeInPath];
907        if ([fDefaults boolForKey: @"AutoImport"])
908            [[UKKQueue sharedFileWatcher] addPath: path];
909        else
910            [[UKKQueue sharedFileWatcher] removePathFromQueue: path];
911       
912        [[NSNotificationCenter defaultCenter] postNotificationName: @"AutoImportSettingChange" object: self];
913    }
914    else
915        [self importFolderSheetShow: nil];
916}
917
918- (void) importFolderSheetShow: (id) sender
919{
920    NSOpenPanel * panel = [NSOpenPanel openPanel];
921
922    [panel setPrompt: NSLocalizedString(@"Select", "Preferences -> Open panel prompt")];
923    [panel setAllowsMultipleSelection: NO];
924    [panel setCanChooseFiles: NO];
925    [panel setCanChooseDirectories: YES];
926    [panel setCanCreateDirectories: YES];
927
928    [panel beginSheetModalForWindow: [self window] completionHandler: ^(NSInteger result) {
929        NSString * path = [fDefaults stringForKey: @"AutoImportDirectory"];
930        if (result == NSFileHandlingPanelOKButton)
931        {
932            UKKQueue * sharedQueue = [UKKQueue sharedFileWatcher];
933            if (path)
934                [sharedQueue removePathFromQueue: [path stringByExpandingTildeInPath]];
935           
936            path = [[[panel URLs] objectAtIndex: 0] path];
937            [fDefaults setObject: path forKey: @"AutoImportDirectory"];
938            [sharedQueue addPath: [path stringByExpandingTildeInPath]];
939           
940            [[NSNotificationCenter defaultCenter] postNotificationName: @"AutoImportSettingChange" object: self];
941        }
942        else if (!path)
943            [fDefaults setBool: NO forKey: @"AutoImport"];
944       
945        [fImportFolderPopUp selectItemAtIndex: 0];
946    }];
947}
948
949- (void) setAutoSize: (id) sender
950{
951    [[NSNotificationCenter defaultCenter] postNotificationName: @"AutoSizeSettingChange" object: self];
952}
953
954- (void) setRPCEnabled: (id) sender
955{
956    BOOL enable = [fDefaults boolForKey: @"RPC"];
957    tr_sessionSetRPCEnabled(fHandle, enable);
958   
959    [self setRPCWebUIDiscovery: nil];
960}
961
962- (void) linkWebUI: (id) sender
963{
964    NSString * urlString = [NSString stringWithFormat: WEBUI_URL, [fDefaults integerForKey: @"RPCPort"]];
965    [[NSWorkspace sharedWorkspace] openURL: [NSURL URLWithString: urlString]];
966}
967
968- (void) setRPCAuthorize: (id) sender
969{
970    tr_sessionSetRPCPasswordEnabled(fHandle, [fDefaults boolForKey: @"RPCAuthorize"]);
971}
972
973- (void) setRPCUsername: (id) sender
974{
975    tr_sessionSetRPCUsername(fHandle, [[fDefaults stringForKey: @"RPCUsername"] UTF8String]);
976}
977
978- (void) setRPCPassword: (id) sender
979{
980    [fRPCPassword release];
981    fRPCPassword = [[sender stringValue] copy];
982   
983    const char * password = [[sender stringValue] UTF8String];
984    [self setKeychainPassword: password forService: RPC_KEYCHAIN_SERVICE username: RPC_KEYCHAIN_NAME];
985   
986    tr_sessionSetRPCPassword(fHandle, password);
987}
988
989- (void) updateRPCPassword
990{
991    UInt32 passwordLength;
992    const char * password = nil;
993    SecKeychainFindGenericPassword(NULL, strlen(RPC_KEYCHAIN_SERVICE), RPC_KEYCHAIN_SERVICE,
994        strlen(RPC_KEYCHAIN_NAME), RPC_KEYCHAIN_NAME, &passwordLength, (void **)&password, NULL);
995   
996    [fRPCPassword release];
997    if (password != NULL)
998    {
999        char fullPassword[passwordLength+1];
1000        strncpy(fullPassword, password, passwordLength);
1001        fullPassword[passwordLength] = '\0';
1002        SecKeychainItemFreeContent(NULL, (void *)password);
1003       
1004        tr_sessionSetRPCPassword(fHandle, fullPassword);
1005       
1006        fRPCPassword = [[NSString alloc] initWithUTF8String: fullPassword];
1007        [fRPCPasswordField setStringValue: fRPCPassword];
1008    }
1009    else
1010        fRPCPassword = nil;
1011}
1012
1013- (void) setRPCPort: (id) sender
1014{
1015    int port = [sender intValue];
1016    [fDefaults setInteger: port forKey: @"RPCPort"];
1017    tr_sessionSetRPCPort(fHandle, port);
1018   
1019    [self setRPCWebUIDiscovery: nil];
1020}
1021
1022- (void) setRPCUseWhitelist: (id) sender
1023{
1024    tr_sessionSetRPCWhitelistEnabled(fHandle, [fDefaults boolForKey: @"RPCUseWhitelist"]);
1025}
1026
1027- (void) setRPCWebUIDiscovery: (id) sender
1028{
1029    if ([fDefaults boolForKey:@"RPC"] && [fDefaults boolForKey: @"RPCWebDiscovery"])
1030        [[BonjourController defaultController] startWithPort: [fDefaults integerForKey: @"RPCPort"]];
1031    else
1032        [[BonjourController defaultController] stop];
1033}
1034
1035- (void) updateRPCWhitelist
1036{
1037    NSString * string = [fRPCWhitelistArray componentsJoinedByString: @","];
1038    tr_sessionSetRPCWhitelist(fHandle, [string UTF8String]);
1039}
1040
1041- (void) addRemoveRPCIP: (id) sender
1042{
1043    //don't allow add/remove when currently adding - it leads to weird results
1044    if ([fRPCWhitelistTable editedRow] != -1)
1045        return;
1046   
1047    if ([[sender cell] tagForSegment: [sender selectedSegment]] == RPC_IP_REMOVE_TAG)
1048    {
1049        [fRPCWhitelistArray removeObjectsAtIndexes: [fRPCWhitelistTable selectedRowIndexes]];
1050        [fRPCWhitelistTable deselectAll: self];
1051        [fRPCWhitelistTable reloadData];
1052       
1053        [fDefaults setObject: fRPCWhitelistArray forKey: @"RPCWhitelist"];
1054        [self updateRPCWhitelist];
1055    }
1056    else
1057    {
1058        [fRPCWhitelistArray addObject: @""];
1059        [fRPCWhitelistTable reloadData];
1060       
1061        const int row = [fRPCWhitelistArray count] - 1;
1062        [fRPCWhitelistTable selectRowIndexes: [NSIndexSet indexSetWithIndex: row] byExtendingSelection: NO];
1063        [fRPCWhitelistTable editColumn: 0 row: row withEvent: nil select: YES];
1064    }
1065}
1066
1067- (NSInteger) numberOfRowsInTableView: (NSTableView *) tableView
1068{
1069    return [fRPCWhitelistArray count];
1070}
1071
1072- (id) tableView: (NSTableView *) tableView objectValueForTableColumn: (NSTableColumn *) tableColumn row: (NSInteger) row
1073{
1074    return [fRPCWhitelistArray objectAtIndex: row];
1075}
1076
1077- (void) tableView: (NSTableView *) tableView setObjectValue: (id) object forTableColumn: (NSTableColumn *) tableColumn
1078    row: (NSInteger) row
1079{
1080    NSArray * components = [object componentsSeparatedByString: @"."];
1081    NSMutableArray * newComponents = [NSMutableArray arrayWithCapacity: 4];
1082       
1083    //create better-formatted ip string
1084    BOOL valid = false;
1085    if ([components count] == 4)
1086    {
1087        valid = true;
1088        for (NSString * component in components)
1089        {
1090            if ([component isEqualToString: @"*"])
1091                [newComponents addObject: component];
1092            else
1093            {
1094                int num = [component intValue];
1095                if (num >= 0 && num < 256)
1096                    [newComponents addObject: [[NSNumber numberWithInt: num] stringValue]];
1097                else
1098                {
1099                    valid = false;
1100                    break;
1101                }
1102            }
1103        }
1104    }
1105   
1106    NSString * newIP;
1107    if (valid)
1108    {
1109        newIP = [newComponents componentsJoinedByString: @"."];
1110       
1111        //don't allow the same ip address
1112        if ([fRPCWhitelistArray containsObject: newIP] && ![[fRPCWhitelistArray objectAtIndex: row] isEqualToString: newIP])
1113            valid = false;
1114    }
1115   
1116    if (valid)
1117    {
1118        [fRPCWhitelistArray replaceObjectAtIndex: row withObject: newIP];
1119        [fRPCWhitelistArray sortUsingSelector: @selector(compareNumeric:)];
1120    }
1121    else
1122    {
1123        NSBeep();
1124        if ([[fRPCWhitelistArray objectAtIndex: row] isEqualToString: @""])
1125            [fRPCWhitelistArray removeObjectAtIndex: row];
1126    }
1127       
1128    [fRPCWhitelistTable deselectAll: self];
1129    [fRPCWhitelistTable reloadData];
1130   
1131    [fDefaults setObject: fRPCWhitelistArray forKey: @"RPCWhitelist"];
1132    [self updateRPCWhitelist];
1133}
1134
1135- (void) tableViewSelectionDidChange: (NSNotification *) notification
1136{
1137    [fRPCAddRemoveControl setEnabled: [fRPCWhitelistTable numberOfSelectedRows] > 0 forSegment: RPC_IP_REMOVE_TAG];
1138}
1139
1140- (void) helpForScript: (id) sender
1141{
1142    [[NSHelpManager sharedHelpManager] openHelpAnchor: @"script"
1143        inBook: [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleHelpBookName"]];
1144}
1145
1146- (void) helpForPeers: (id) sender
1147{
1148    [[NSHelpManager sharedHelpManager] openHelpAnchor: @"peers"
1149        inBook: [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleHelpBookName"]];
1150}
1151
1152- (void) helpForNetwork: (id) sender
1153{
1154    [[NSHelpManager sharedHelpManager] openHelpAnchor: @"network"
1155        inBook: [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleHelpBookName"]];
1156}
1157
1158- (void) helpForRemote: (id) sender
1159{
1160    [[NSHelpManager sharedHelpManager] openHelpAnchor: @"remote"
1161        inBook: [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleHelpBookName"]];
1162}
1163
1164- (void) rpcUpdatePrefs
1165{
1166    //encryption
1167    const tr_encryption_mode encryptionMode = tr_sessionGetEncryption(fHandle);
1168    [fDefaults setBool: encryptionMode != TR_CLEAR_PREFERRED forKey: @"EncryptionPrefer"];
1169    [fDefaults setBool: encryptionMode == TR_ENCRYPTION_REQUIRED forKey: @"EncryptionRequire"];
1170   
1171    //download directory
1172    NSString * downloadLocation = [[NSString stringWithUTF8String: tr_sessionGetDownloadDir(fHandle)] stringByStandardizingPath];
1173    [fDefaults setObject: downloadLocation forKey: @"DownloadFolder"];
1174   
1175    NSString * incompleteLocation = [[NSString stringWithUTF8String: tr_sessionGetIncompleteDir(fHandle)] stringByStandardizingPath];
1176    [fDefaults setObject: incompleteLocation forKey: @"IncompleteDownloadFolder"];
1177   
1178    const BOOL useIncomplete = tr_sessionIsIncompleteDirEnabled(fHandle);
1179    [fDefaults setBool: useIncomplete forKey: @"UseIncompleteDownloadFolder"];
1180   
1181    const BOOL usePartialFileRanaming = tr_sessionIsIncompleteFileNamingEnabled(fHandle);
1182    [fDefaults setBool: usePartialFileRanaming forKey: @"RenamePartialFiles"];
1183   
1184    //utp
1185    const BOOL utp = tr_sessionIsUTPEnabled(fHandle);
1186    [fDefaults setBool: utp forKey: @"UTPGlobal"];
1187   
1188    //peers
1189    const uint16_t peersTotal = tr_sessionGetPeerLimit(fHandle);
1190    [fDefaults setInteger: peersTotal forKey: @"PeersTotal"];
1191   
1192    const uint16_t peersTorrent = tr_sessionGetPeerLimitPerTorrent(fHandle);
1193    [fDefaults setInteger: peersTorrent forKey: @"PeersTorrent"];
1194   
1195    //pex
1196    const BOOL pex = tr_sessionIsPexEnabled(fHandle);
1197    [fDefaults setBool: pex forKey: @"PEXGlobal"];
1198   
1199    //dht
1200    const BOOL dht = tr_sessionIsDHTEnabled(fHandle);
1201    [fDefaults setBool: dht forKey: @"DHTGlobal"];
1202   
1203    //lpd
1204    const BOOL lpd = tr_sessionIsLPDEnabled(fHandle);
1205    [fDefaults setBool: lpd forKey: @"LocalPeerDiscoveryGlobal"];
1206   
1207    //auto start
1208    const BOOL autoStart = !tr_sessionGetPaused(fHandle);
1209    [fDefaults setBool: autoStart forKey: @"AutoStartDownload"];
1210   
1211    //port
1212    const tr_port port = tr_sessionGetPeerPort(fHandle);
1213    [fDefaults setInteger: port forKey: @"BindPort"];
1214   
1215    const BOOL nat = tr_sessionIsPortForwardingEnabled(fHandle);
1216    [fDefaults setBool: nat forKey: @"NatTraversal"];
1217   
1218    fPeerPort = -1;
1219    fNatStatus = -1;
1220    [self updatePortStatus];
1221   
1222    const BOOL randomPort = tr_sessionGetPeerPortRandomOnStart(fHandle);
1223    [fDefaults setBool: randomPort forKey: @"RandomPort"];
1224   
1225    //speed limit - down
1226    const BOOL downLimitEnabled = tr_sessionIsSpeedLimited(fHandle, TR_DOWN);
1227    [fDefaults setBool: downLimitEnabled forKey: @"CheckDownload"];
1228   
1229    const int downLimit = tr_sessionGetSpeedLimit_KBps(fHandle, TR_DOWN);
1230    [fDefaults setInteger: downLimit forKey: @"DownloadLimit"];
1231   
1232    //speed limit - up
1233    const BOOL upLimitEnabled = tr_sessionIsSpeedLimited(fHandle, TR_UP);
1234    [fDefaults setBool: upLimitEnabled forKey: @"CheckUpload"];
1235   
1236    const int upLimit = tr_sessionGetSpeedLimit_KBps(fHandle, TR_UP);
1237    [fDefaults setInteger: upLimit forKey: @"UploadLimit"];
1238   
1239    //alt speed limit enabled
1240    const BOOL useAltSpeed = tr_sessionUsesAltSpeed(fHandle);
1241    [fDefaults setBool: useAltSpeed forKey: @"SpeedLimit"];
1242   
1243    //alt speed limit - down
1244    const int downLimitAlt = tr_sessionGetAltSpeed_KBps(fHandle, TR_DOWN);
1245    [fDefaults setInteger: downLimitAlt forKey: @"SpeedLimitDownloadLimit"];
1246   
1247    //alt speed limit - up
1248    const int upLimitAlt = tr_sessionGetAltSpeed_KBps(fHandle, TR_UP);
1249    [fDefaults setInteger: upLimitAlt forKey: @"SpeedLimitUploadLimit"];
1250   
1251    //alt speed limit schedule
1252    const BOOL useAltSpeedSched = tr_sessionUsesAltSpeedTime(fHandle);
1253    [fDefaults setBool: useAltSpeedSched forKey: @"SpeedLimitAuto"];
1254   
1255    NSDate * limitStartDate = [PrefsController timeSumToDate: tr_sessionGetAltSpeedBegin(fHandle)];
1256    [fDefaults setObject: limitStartDate forKey: @"SpeedLimitAutoOnDate"];
1257   
1258    NSDate * limitEndDate = [PrefsController timeSumToDate: tr_sessionGetAltSpeedEnd(fHandle)];
1259    [fDefaults setObject: limitEndDate forKey: @"SpeedLimitAutoOffDate"];
1260   
1261    const int limitDay = tr_sessionGetAltSpeedDay(fHandle);
1262    [fDefaults setInteger: limitDay forKey: @"SpeedLimitAutoDay"];
1263   
1264    //blocklist
1265    const BOOL blocklist = tr_blocklistIsEnabled(fHandle);
1266    [fDefaults setBool: blocklist forKey: @"BlocklistNew"];
1267   
1268    NSString * blocklistURL = [NSString stringWithUTF8String: tr_blocklistGetURL(fHandle)];
1269    [fDefaults setObject: blocklistURL forKey: @"BlocklistURL"];
1270   
1271    //seed ratio
1272    const BOOL ratioLimited = tr_sessionIsRatioLimited(fHandle);
1273    [fDefaults setBool: ratioLimited forKey: @"RatioCheck"];
1274   
1275    const float ratioLimit = tr_sessionGetRatioLimit(fHandle);
1276    [fDefaults setFloat: ratioLimit forKey: @"RatioLimit"];
1277   
1278    //idle seed limit
1279    const BOOL idleLimited = tr_sessionIsIdleLimited(fHandle);
1280    [fDefaults setBool: idleLimited forKey: @"IdleLimitCheck"];
1281   
1282    const NSUInteger idleLimitMin = tr_sessionGetIdleLimit(fHandle);
1283    [fDefaults setInteger: idleLimitMin forKey: @"IdleLimitMinutes"];
1284   
1285    //queue
1286    const BOOL downloadQueue = tr_sessionGetQueueEnabled(fHandle, TR_DOWN);
1287    [fDefaults setBool: downloadQueue forKey: @"Queue"];
1288   
1289    const int downloadQueueNum = tr_sessionGetQueueSize(fHandle, TR_DOWN);
1290    [fDefaults setInteger: downloadQueueNum forKey: @"QueueDownloadNumber"];
1291   
1292    const BOOL seedQueue = tr_sessionGetQueueEnabled(fHandle, TR_UP);
1293    [fDefaults setBool: seedQueue forKey: @"QueueSeed"];
1294   
1295    const int seedQueueNum = tr_sessionGetQueueSize(fHandle, TR_UP);
1296    [fDefaults setInteger: seedQueueNum forKey: @"QueueSeedNumber"];
1297   
1298    const BOOL checkStalled = tr_sessionGetQueueStalledEnabled(fHandle);
1299    [fDefaults setBool: checkStalled forKey: @"CheckStalled"];
1300   
1301    const int stalledMinutes = tr_sessionGetQueueStalledMinutes(fHandle);
1302    [fDefaults setInteger: stalledMinutes forKey: @"StalledMinutes"];
1303   
1304    //done script
1305    const BOOL doneScriptEnabled = tr_sessionIsTorrentDoneScriptEnabled(fHandle);
1306    [fDefaults setBool: doneScriptEnabled forKey: @"DoneScriptEnabled"];
1307   
1308    NSString * doneScriptPath = [NSString stringWithUTF8String: tr_sessionGetTorrentDoneScript(fHandle)];
1309    [fDefaults setObject: doneScriptPath forKey: @"DoneScriptPath"];
1310   
1311    //update gui if loaded
1312    if (fHasLoaded)
1313    {
1314        //encryption handled by bindings
1315       
1316        //download directory handled by bindings
1317       
1318        //utp handled by bindings
1319       
1320        [fPeersGlobalField setIntValue: peersTotal];
1321        [fPeersTorrentField setIntValue: peersTorrent];
1322       
1323        //pex handled by bindings
1324       
1325        //dht handled by bindings
1326       
1327        //lpd handled by bindings
1328       
1329        [fPortField setIntValue: port];
1330        //port forwarding (nat) handled by bindings
1331        //random port handled by bindings
1332       
1333        //limit check handled by bindings
1334        [fDownloadField setIntValue: downLimit];
1335       
1336        //limit check handled by bindings
1337        [fUploadField setIntValue: upLimit];
1338       
1339        [fSpeedLimitDownloadField setIntValue: downLimitAlt];
1340       
1341        [fSpeedLimitUploadField setIntValue: upLimitAlt];
1342       
1343        //speed limit schedule handled by bindings
1344       
1345        //speed limit schedule times and day handled by bindings
1346       
1347        [fBlocklistURLField setStringValue: blocklistURL];
1348        [self updateBlocklistButton];
1349        [self updateBlocklistFields];
1350       
1351        //ratio limit enabled handled by bindings
1352        [fRatioStopField setFloatValue: ratioLimit];
1353       
1354        //idle limit enabled handled by bindings
1355        [fIdleStopField setIntegerValue: idleLimitMin];
1356       
1357        //queues enabled handled by bindings
1358        [fQueueDownloadField setIntValue: downloadQueueNum];
1359        [fQueueSeedField setIntValue: seedQueueNum];
1360       
1361        //check stalled handled by bindings
1362        [fStalledField setIntValue: stalledMinutes];
1363    }
1364   
1365    [[NSNotificationCenter defaultCenter] postNotificationName: @"SpeedLimitUpdate" object: nil];
1366   
1367    //reload global settings in inspector
1368    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateGlobalOptions" object: nil];
1369}
1370
1371@end
1372
1373@implementation PrefsController (Private)
1374
1375- (void) setPrefView: (id) sender
1376{
1377    NSString * identifier;
1378    if (sender)
1379    {
1380        identifier = [sender itemIdentifier];
1381        [[NSUserDefaults standardUserDefaults] setObject: identifier forKey: @"SelectedPrefView"];
1382    }
1383    else
1384        identifier = [[NSUserDefaults standardUserDefaults] stringForKey: @"SelectedPrefView"];
1385   
1386    NSView * view;
1387    if ([identifier isEqualToString: TOOLBAR_TRANSFERS])
1388        view = fTransfersView;
1389    else if ([identifier isEqualToString: TOOLBAR_GROUPS])
1390        view = fGroupsView;
1391    else if ([identifier isEqualToString: TOOLBAR_BANDWIDTH])
1392        view = fBandwidthView;
1393    else if ([identifier isEqualToString: TOOLBAR_PEERS])
1394        view = fPeersView;
1395    else if ([identifier isEqualToString: TOOLBAR_NETWORK])
1396        view = fNetworkView;
1397    else if ([identifier isEqualToString: TOOLBAR_REMOTE])
1398        view = fRemoteView;
1399    else
1400    {
1401        identifier = TOOLBAR_GENERAL; //general view is the default selected
1402        view = fGeneralView;
1403    }
1404   
1405    [[[self window] toolbar] setSelectedItemIdentifier: identifier];
1406   
1407    NSWindow * window = [self window];
1408    if ([window contentView] == view)
1409        return;
1410   
1411    NSRect windowRect = [window frame];
1412    const CGFloat difference = (NSHeight([view frame]) - NSHeight([[window contentView] frame])) * [window userSpaceScaleFactor];
1413    windowRect.origin.y -= difference;
1414    windowRect.size.height += difference;
1415   
1416    [view setHidden: YES];
1417    [window setContentView: view];
1418    [window setFrame: windowRect display: YES animate: YES];
1419    [view setHidden: NO];
1420   
1421    //set title label
1422    if (sender)
1423        [window setTitle: [sender label]];
1424    else
1425    {
1426        NSToolbar * toolbar = [window toolbar];
1427        NSString * itemIdentifier = [toolbar selectedItemIdentifier];
1428        for (NSToolbarItem * item in [toolbar items])
1429            if ([[item itemIdentifier] isEqualToString: itemIdentifier])
1430            {
1431                [window setTitle: [item label]];
1432                break;
1433            }
1434    }
1435}
1436
1437- (void) setKeychainPassword: (const char *) password forService: (const char *) service username: (const char *) username
1438{
1439    SecKeychainItemRef item = NULL;
1440    NSUInteger passwordLength = strlen(password);
1441   
1442    OSStatus result = SecKeychainFindGenericPassword(NULL, strlen(service), service, strlen(username), username, NULL, NULL, &item);
1443    if (result == noErr && item)
1444    {
1445        if (passwordLength > 0) //found, so update
1446        {
1447            result = SecKeychainItemModifyAttributesAndData(item, NULL, passwordLength, (const void *)password);
1448            if (result != noErr)
1449                NSLog(@"Problem updating Keychain item: %s", GetMacOSStatusErrorString(result));
1450        }
1451        else //remove the item
1452        {
1453            result = SecKeychainItemDelete(item);
1454            if (result != noErr)
1455                NSLog(@"Problem removing Keychain item: %s", GetMacOSStatusErrorString(result));
1456        }
1457    }
1458    else if (result == errSecItemNotFound) //not found, so add
1459    {
1460        if (passwordLength > 0)
1461        {
1462            result = SecKeychainAddGenericPassword(NULL, strlen(service), service, strlen(username), username,
1463                        passwordLength, (const void *)password, NULL);
1464            if (result != noErr)
1465                NSLog(@"Problem adding Keychain item: %s", GetMacOSStatusErrorString(result));
1466        }
1467    }
1468    else
1469        NSLog(@"Problem accessing Keychain: %s", GetMacOSStatusErrorString(result));
1470}
1471
1472@end
Note: See TracBrowser for help on using the repository browser.