source: trunk/macosx/PrefsController.m @ 13318

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

#4920 On Lion, use window restoration on the message log and stats window

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