source: trunk/macosx/PrefsController.m @ 13321

Last change on this file since 13321 was 13321, checked in by livings124, 9 years ago

#4922 On Lion, use window restoration on the preferences window

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