1 /******************************************************************************
2 * $Id: PrefsController.m 14161 2013-08-10 02:29:12Z livings124 $
4 * Copyright (c) 2005-2012 Transmission authors and contributors
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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 *****************************************************************************/
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 "NSApplicationAdditions.h"
32 #import "NSStringAdditions.h"
35 #import "transmission.h"
38 #import <Growl/Growl.h>
39 #import <Sparkle/Sparkle.h>
41 #define DOWNLOAD_FOLDER 0
42 #define DOWNLOAD_TORRENT 2
44 #define RPC_IP_ADD_TAG 0
45 #define RPC_IP_REMOVE_TAG 1
47 #define TOOLBAR_GENERAL @"TOOLBAR_GENERAL"
48 #define TOOLBAR_TRANSFERS @"TOOLBAR_TRANSFERS"
49 #define TOOLBAR_GROUPS @"TOOLBAR_GROUPS"
50 #define TOOLBAR_BANDWIDTH @"TOOLBAR_BANDWIDTH"
51 #define TOOLBAR_PEERS @"TOOLBAR_PEERS"
52 #define TOOLBAR_NETWORK @"TOOLBAR_NETWORK"
53 #define TOOLBAR_REMOTE @"TOOLBAR_REMOTE"
55 #define RPC_KEYCHAIN_SERVICE "Transmission:Remote"
56 #define RPC_KEYCHAIN_NAME "Remote"
58 #define WEBUI_URL @"http://localhost:%ld/"
60 @interface PrefsController (Private)
62 - (void) setPrefView: (id) sender;
64 - (void) updateGrowlButton;
66 - (void) setKeychainPassword: (const char *) password forService: (const char *) service username: (const char *) username;
70 @implementation PrefsController
72 - (id) initWithHandle: (tr_session *) handle
74 if ((self = [super initWithWindowNibName: @"PrefsWindow"]))
78 fDefaults = [NSUserDefaults standardUserDefaults];
80 //check for old version download location (before 1.1)
82 if ((choice = [fDefaults stringForKey: @"DownloadChoice"]))
84 [fDefaults setBool: [choice isEqualToString: @"Constant"] forKey: @"DownloadLocationConstant"];
85 [fDefaults setBool: YES forKey: @"DownloadAsk"];
87 [fDefaults removeObjectForKey: @"DownloadChoice"];
90 //check for old version blocklist (before 2.12)
91 NSDate * blocklistDate;
92 if ((blocklistDate = [fDefaults objectForKey: @"BlocklistLastUpdate"]))
94 [fDefaults setObject: blocklistDate forKey: @"BlocklistNewLastUpdateSuccess"];
95 [fDefaults setObject: blocklistDate forKey: @"BlocklistNewLastUpdate"];
96 [fDefaults removeObjectForKey: @"BlocklistLastUpdate"];
98 NSURL * blocklistDir = [[[[NSFileManager defaultManager] URLsForDirectory: NSApplicationDirectory inDomains: NSUserDomainMask] objectAtIndex: 0] URLByAppendingPathComponent: @"Transmission/blocklists/"];
99 [[NSFileManager defaultManager] moveItemAtURL: [blocklistDir URLByAppendingPathComponent: @"level1.bin"]
100 toURL: [blocklistDir URLByAppendingPathComponent: [NSString stringWithUTF8String: DEFAULT_BLOCKLIST_FILENAME]]
104 //save a new random port
105 if ([fDefaults boolForKey: @"RandomPort"])
106 [fDefaults setInteger: tr_sessionGetPeerPort(fHandle) forKey: @"BindPort"];
110 if ([fDefaults boolForKey: @"AutoImport"] && (autoPath = [fDefaults stringForKey: @"AutoImportDirectory"]))
111 [[(Controller *)[NSApp delegate] fileWatcherQueue] addPath: [autoPath stringByExpandingTildeInPath] notifyingAbout: VDKQueueNotifyAboutWrite];
113 //set special-handling of magnet link add window checkbox
114 [self updateShowAddMagnetWindowField];
116 //set blocklist scheduler
117 [[BlocklistScheduler scheduler] updateSchedule];
120 [self setEncryptionMode: nil];
122 //update rpc whitelist
123 [self updateRPCPassword];
125 fRPCWhitelistArray = [[fDefaults arrayForKey: @"RPCWhitelist"] mutableCopy];
126 if (!fRPCWhitelistArray)
127 fRPCWhitelistArray = [[NSMutableArray arrayWithObject: @"127.0.0.1"] retain];
128 [self updateRPCWhitelist];
130 //reset old Sparkle settings from previous versions
131 [fDefaults removeObjectForKey: @"SUScheduledCheckInterval"];
132 if ([fDefaults objectForKey: @"CheckForUpdates"])
134 [[SUUpdater sharedUpdater] setAutomaticallyChecksForUpdates: [fDefaults boolForKey: @"CheckForUpdates"]];
135 [fDefaults removeObjectForKey: @"CheckForUpdates"];
139 [GrowlApplicationBridge setShouldUseBuiltInNotifications: ![NSApp isOnMountainLionOrBetter] && [fDefaults boolForKey: @"DisplayNotifications"]];
141 [self setAutoUpdateToBeta: nil];
149 [[NSNotificationCenter defaultCenter] removeObserver: self];
151 [fPortStatusTimer invalidate];
152 [fPortStatusTimer release];
155 [fPortChecker cancelProbe];
156 [fPortChecker release];
159 [fRPCWhitelistArray release];
161 [fRPCPassword release];
166 - (void) awakeFromNib
170 if ([NSApp isOnLionOrBetter])
171 [[self window] setRestorationClass: [self class]];
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];
182 [self setPrefView: nil];
184 //make sure proper notification settings are shown
185 [self updateGrowlButton];
187 //set download folder
188 [fFolderPopUp selectItemAtIndex: [fDefaults boolForKey: @"DownloadLocationConstant"] ? DOWNLOAD_FOLDER : DOWNLOAD_TORRENT];
191 [fRatioStopField setFloatValue: [fDefaults floatForKey: @"RatioLimit"]];
193 //set idle seeding minutes
194 [fIdleStopField setIntegerValue: [fDefaults integerForKey: @"IdleLimitMinutes"]];
197 [self updateLimitFields];
200 [fSpeedLimitUploadField setIntValue: [fDefaults integerForKey: @"SpeedLimitUploadLimit"]];
201 [fSpeedLimitDownloadField setIntValue: [fDefaults integerForKey: @"SpeedLimitDownloadLimit"]];
204 [fPortField setIntValue: [fDefaults integerForKey: @"BindPort"]];
207 [self updatePortStatus];
208 fPortStatusTimer = [[NSTimer scheduledTimerWithTimeInterval: 5.0 target: self selector: @selector(updatePortStatus) userInfo: nil repeats: YES] retain];
210 //set peer connections
211 [fPeersGlobalField setIntValue: [fDefaults integerForKey: @"PeersTotal"]];
212 [fPeersTorrentField setIntValue: [fDefaults integerForKey: @"PeersTorrent"]];
215 [fQueueDownloadField setIntValue: [fDefaults integerForKey: @"QueueDownloadNumber"]];
216 [fQueueSeedField setIntValue: [fDefaults integerForKey: @"QueueSeedNumber"]];
217 [fStalledField setIntValue: [fDefaults integerForKey: @"StalledMinutes"]];
220 NSString * blocklistURL = [fDefaults stringForKey: @"BlocklistURL"];
222 [fBlocklistURLField setStringValue: blocklistURL];
224 [self updateBlocklistButton];
225 [self updateBlocklistFields];
227 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(updateLimitFields)
228 name: @"UpdateSpeedLimitValuesOutsidePrefs" object: nil];
230 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(updateRatioStopField)
231 name: @"UpdateRatioStopValueOutsidePrefs" object: nil];
233 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(updateLimitStopField)
234 name: @"UpdateIdleStopValueOutsidePrefs" object: nil];
236 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(updateBlocklistFields)
237 name: @"BlocklistUpdated" object: nil];
239 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(updateBlocklistURLField)
240 name: NSControlTextDidChangeNotification object: fBlocklistURLField];
243 [fRPCPortField setIntValue: [fDefaults integerForKey: @"RPCPort"]];
247 [fRPCPasswordField setStringValue: fRPCPassword];
250 - (NSToolbarItem *) toolbar: (NSToolbar *) toolbar itemForItemIdentifier: (NSString *) ident willBeInsertedIntoToolbar: (BOOL) flag
252 NSToolbarItem * item = [[NSToolbarItem alloc] initWithItemIdentifier: ident];
254 if ([ident isEqualToString: TOOLBAR_GENERAL])
256 [item setLabel: NSLocalizedString(@"General", "Preferences -> toolbar item title")];
257 [item setImage: [NSImage imageNamed: NSImageNamePreferencesGeneral]];
258 [item setTarget: self];
259 [item setAction: @selector(setPrefView:)];
260 [item setAutovalidates: NO];
262 else if ([ident isEqualToString: TOOLBAR_TRANSFERS])
264 [item setLabel: NSLocalizedString(@"Transfers", "Preferences -> toolbar item title")];
265 [item setImage: [NSImage imageNamed: @"Transfers"]];
266 [item setTarget: self];
267 [item setAction: @selector(setPrefView:)];
268 [item setAutovalidates: NO];
270 else if ([ident isEqualToString: TOOLBAR_GROUPS])
272 [item setLabel: NSLocalizedString(@"Groups", "Preferences -> toolbar item title")];
273 [item setImage: [NSImage imageNamed: @"Groups"]];
274 [item setTarget: self];
275 [item setAction: @selector(setPrefView:)];
276 [item setAutovalidates: NO];
278 else if ([ident isEqualToString: TOOLBAR_BANDWIDTH])
280 [item setLabel: NSLocalizedString(@"Bandwidth", "Preferences -> toolbar item title")];
281 [item setImage: [NSImage imageNamed: @"Bandwidth"]];
282 [item setTarget: self];
283 [item setAction: @selector(setPrefView:)];
284 [item setAutovalidates: NO];
286 else if ([ident isEqualToString: TOOLBAR_PEERS])
288 [item setLabel: NSLocalizedString(@"Peers", "Preferences -> toolbar item title")];
289 [item setImage: [NSImage imageNamed: NSImageNameUserGroup]];
290 [item setTarget: self];
291 [item setAction: @selector(setPrefView:)];
292 [item setAutovalidates: NO];
294 else if ([ident isEqualToString: TOOLBAR_NETWORK])
296 [item setLabel: NSLocalizedString(@"Network", "Preferences -> toolbar item title")];
297 [item setImage: [NSImage imageNamed: NSImageNameNetwork]];
298 [item setTarget: self];
299 [item setAction: @selector(setPrefView:)];
300 [item setAutovalidates: NO];
302 else if ([ident isEqualToString: TOOLBAR_REMOTE])
304 [item setLabel: NSLocalizedString(@"Remote", "Preferences -> toolbar item title")];
305 [item setImage: [NSImage imageNamed: @"Remote"]];
306 [item setTarget: self];
307 [item setAction: @selector(setPrefView:)];
308 [item setAutovalidates: NO];
316 return [item autorelease];
319 - (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar
321 return [NSArray arrayWithObjects: TOOLBAR_GENERAL, TOOLBAR_TRANSFERS, TOOLBAR_GROUPS, TOOLBAR_BANDWIDTH,
322 TOOLBAR_PEERS, TOOLBAR_NETWORK, TOOLBAR_REMOTE, nil];
325 - (NSArray *) toolbarSelectableItemIdentifiers: (NSToolbar *) toolbar
327 return [self toolbarAllowedItemIdentifiers: toolbar];
330 - (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar
332 return [self toolbarAllowedItemIdentifiers: toolbar];
335 - (void) windowDidBecomeMain: (NSNotification *) notification
337 //this is a good place to see if Growl was quit/launched
338 [self updateGrowlButton];
341 + (void) restoreWindowWithIdentifier: (NSString *) identifier state: (NSCoder *) state completionHandler: (void (^)(NSWindow *, NSError *)) completionHandler
343 NSWindow * window = [[(Controller *)[NSApp delegate] prefsController] window];
344 completionHandler(window, nil);
347 //for a beta release, always use the beta appcast
348 #if defined(TR_BETA_RELEASE)
349 #define SPARKLE_TAG YES
351 #define SPARKLE_TAG [fDefaults boolForKey: @"AutoUpdateBeta"]
353 - (void) setAutoUpdateToBeta: (id) sender
355 [[SUUpdater sharedUpdater] setAllowedTags: SPARKLE_TAG ? [NSSet setWithObject: @"beta"] : nil];
358 - (void) setPort: (id) sender
360 const tr_port port = [sender intValue];
361 [fDefaults setInteger: port forKey: @"BindPort"];
362 tr_sessionSetPeerPort(fHandle, port);
365 [self updatePortStatus];
368 - (void) randomPort: (id) sender
370 const tr_port port = tr_sessionSetPeerPortRandom(fHandle);
371 [fDefaults setInteger: port forKey: @"BindPort"];
372 [fPortField setIntValue: port];
375 [self updatePortStatus];
378 - (void) setRandomPortOnStart: (id) sender
380 tr_sessionSetPeerPortRandomOnStart(fHandle, [(NSButton *)sender state] == NSOnState);
383 - (void) setNat: (id) sender
385 tr_sessionSetPortForwardingEnabled(fHandle, [fDefaults boolForKey: @"NatTraversal"]);
388 [self updatePortStatus];
391 - (void) updatePortStatus
393 const tr_port_forwarding fwd = tr_sessionGetPortForwarding(fHandle);
394 const int port = tr_sessionGetPeerPort(fHandle);
395 BOOL natStatusChanged = (fNatStatus != fwd);
396 BOOL peerPortChanged = (fPeerPort != port);
398 if (natStatusChanged || peerPortChanged)
403 [fPortStatusField setStringValue: @""];
404 [fPortStatusImage setImage: nil];
405 [fPortStatusProgress startAnimation: self];
409 [fPortChecker cancelProbe];
410 [fPortChecker release];
412 BOOL delay = natStatusChanged || tr_sessionIsPortForwardingEnabled(fHandle);
413 fPortChecker = [[PortChecker alloc] initForPort: fPeerPort delay: delay withDelegate: self];
417 - (void) portCheckerDidFinishProbing: (PortChecker *) portChecker
419 [fPortStatusProgress stopAnimation: self];
420 switch ([fPortChecker status])
422 case PORT_STATUS_OPEN:
423 [fPortStatusField setStringValue: NSLocalizedString(@"Port is open", "Preferences -> Network -> port status")];
424 [fPortStatusImage setImage: [NSImage imageNamed: @"GreenDot"]];
426 case PORT_STATUS_CLOSED:
427 [fPortStatusField setStringValue: NSLocalizedString(@"Port is closed", "Preferences -> Network -> port status")];
428 [fPortStatusImage setImage: [NSImage imageNamed: @"RedDot"]];
430 case PORT_STATUS_ERROR:
431 [fPortStatusField setStringValue: NSLocalizedString(@"Port check site is down", "Preferences -> Network -> port status")];
432 [fPortStatusImage setImage: [NSImage imageNamed: @"YellowDot"]];
435 NSAssert1(NO, @"Port checker returned invalid status: %d", [fPortChecker status]);
438 [fPortChecker release];
444 NSMutableArray * sounds = [NSMutableArray array];
446 NSArray * directories = NSSearchPathForDirectoriesInDomains(NSAllLibrariesDirectory, NSUserDomainMask | NSLocalDomainMask | NSSystemDomainMask, YES);
448 for (NSString * directory in directories)
450 directory = [directory stringByAppendingPathComponent: @"Sounds"];
453 if ([[NSFileManager defaultManager] fileExistsAtPath: directory isDirectory: &isDirectory] && isDirectory)
455 NSArray * directoryContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath: directory error: NULL];
456 for (NSString * sound in directoryContents)
458 sound = [sound stringByDeletingPathExtension];
459 if ([NSSound soundNamed: sound])
460 [sounds addObject: sound];
468 - (void) setSound: (id) sender
470 //play sound when selecting
472 if ((sound = [NSSound soundNamed: [sender titleOfSelectedItem]]))
476 - (void) setUTP: (id) sender
478 tr_sessionSetUTPEnabled(fHandle, [fDefaults boolForKey: @"UTPGlobal"]);
481 - (void) setPeersGlobal: (id) sender
483 const int count = [sender intValue];
484 [fDefaults setInteger: count forKey: @"PeersTotal"];
485 tr_sessionSetPeerLimit(fHandle, count);
488 - (void) setPeersTorrent: (id) sender
490 const int count = [sender intValue];
491 [fDefaults setInteger: count forKey: @"PeersTorrent"];
492 tr_sessionSetPeerLimitPerTorrent(fHandle, count);
495 - (void) setPEX: (id) sender
497 tr_sessionSetPexEnabled(fHandle, [fDefaults boolForKey: @"PEXGlobal"]);
500 - (void) setDHT: (id) sender
502 tr_sessionSetDHTEnabled(fHandle, [fDefaults boolForKey: @"DHTGlobal"]);
505 - (void) setLPD: (id) sender
507 tr_sessionSetLPDEnabled(fHandle, [fDefaults boolForKey: @"LocalPeerDiscoveryGlobal"]);
510 - (void) setEncryptionMode: (id) sender
512 const tr_encryption_mode mode = [fDefaults boolForKey: @"EncryptionPrefer"] ?
513 ([fDefaults boolForKey: @"EncryptionRequire"] ? TR_ENCRYPTION_REQUIRED : TR_ENCRYPTION_PREFERRED) : TR_CLEAR_PREFERRED;
514 tr_sessionSetEncryption(fHandle, mode);
517 - (void) setBlocklistEnabled: (id) sender
519 tr_blocklistSetEnabled(fHandle, [fDefaults boolForKey: @"BlocklistNew"]);
521 [[BlocklistScheduler scheduler] updateSchedule];
523 [self updateBlocklistButton];
526 - (void) updateBlocklist: (id) sender
528 [BlocklistDownloaderViewController downloadWithPrefsController: self];
531 - (void) setBlocklistAutoUpdate: (id) sender
533 [[BlocklistScheduler scheduler] updateSchedule];
536 - (void) updateBlocklistFields
538 const BOOL exists = tr_blocklistExists(fHandle);
542 NSString * countString = [NSString formattedUInteger: tr_blocklistGetRuleCount(fHandle)];
543 [fBlocklistMessageField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%@ IP address rules in list",
544 "Prefs -> blocklist -> message"), countString]];
547 [fBlocklistMessageField setStringValue: NSLocalizedString(@"A blocklist must first be downloaded",
548 "Prefs -> blocklist -> message")];
550 NSString * updatedDateString;
553 NSDate * updatedDate = [fDefaults objectForKey: @"BlocklistNewLastUpdateSuccess"];
556 updatedDateString = [NSDateFormatter localizedStringFromDate: updatedDate dateStyle: NSDateFormatterFullStyle timeStyle: NSDateFormatterShortStyle];
558 updatedDateString = NSLocalizedString(@"N/A", "Prefs -> blocklist -> message");
561 updatedDateString = NSLocalizedString(@"Never", "Prefs -> blocklist -> message");
563 [fBlocklistDateField setStringValue: [NSString stringWithFormat: @"%@: %@",
564 NSLocalizedString(@"Last updated", "Prefs -> blocklist -> message"), updatedDateString]];
567 - (void) updateBlocklistURLField
569 NSString * blocklistString = [fBlocklistURLField stringValue];
571 [fDefaults setObject: blocklistString forKey: @"BlocklistURL"];
572 tr_blocklistSetURL(fHandle, [blocklistString UTF8String]);
574 [self updateBlocklistButton];
577 - (void) updateBlocklistButton
579 NSString * blocklistString = [fDefaults objectForKey: @"BlocklistURL"];
580 const BOOL enable = (blocklistString && ![blocklistString isEqualToString: @""])
581 && [fDefaults boolForKey: @"BlocklistNew"];
582 [fBlocklistButton setEnabled: enable];
585 - (void) setAutoStartDownloads: (id) sender
587 tr_sessionSetPaused(fHandle, ![fDefaults boolForKey: @"AutoStartDownload"]);
590 - (void) applySpeedSettings: (id) sender
592 tr_sessionLimitSpeed(fHandle, TR_UP, [fDefaults boolForKey: @"CheckUpload"]);
593 tr_sessionSetSpeedLimit_KBps(fHandle, TR_UP, [fDefaults integerForKey: @"UploadLimit"]);
595 tr_sessionLimitSpeed(fHandle, TR_DOWN, [fDefaults boolForKey: @"CheckDownload"]);
596 tr_sessionSetSpeedLimit_KBps(fHandle, TR_DOWN, [fDefaults integerForKey: @"DownloadLimit"]);
598 [[NSNotificationCenter defaultCenter] postNotificationName: @"SpeedLimitUpdate" object: nil];
601 - (void) applyAltSpeedSettings
603 tr_sessionSetAltSpeed_KBps(fHandle, TR_UP, [fDefaults integerForKey: @"SpeedLimitUploadLimit"]);
604 tr_sessionSetAltSpeed_KBps(fHandle, TR_DOWN, [fDefaults integerForKey: @"SpeedLimitDownloadLimit"]);
606 [[NSNotificationCenter defaultCenter] postNotificationName: @"SpeedLimitUpdate" object: nil];
609 - (void) applyRatioSetting: (id) sender
611 tr_sessionSetRatioLimited(fHandle, [fDefaults boolForKey: @"RatioCheck"]);
612 tr_sessionSetRatioLimit(fHandle, [fDefaults floatForKey: @"RatioLimit"]);
614 //reload main table for seeding progress
615 [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
617 //reload global settings in inspector
618 [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateGlobalOptions" object: nil];
621 - (void) setRatioStop: (id) sender
623 [fDefaults setFloat: [sender floatValue] forKey: @"RatioLimit"];
625 [self applyRatioSetting: nil];
628 - (void) updateRatioStopField
631 [fRatioStopField setFloatValue: [fDefaults floatForKey: @"RatioLimit"]];
634 - (void) updateRatioStopFieldOld
636 [self updateRatioStopField];
638 [self applyRatioSetting: nil];
641 - (void) applyIdleStopSetting: (id) sender
643 tr_sessionSetIdleLimited(fHandle, [fDefaults boolForKey: @"IdleLimitCheck"]);
644 tr_sessionSetIdleLimit(fHandle, [fDefaults integerForKey: @"IdleLimitMinutes"]);
646 //reload main table for remaining seeding time
647 [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
649 //reload global settings in inspector
650 [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateGlobalOptions" object: nil];
653 - (void) setIdleStop: (id) sender
655 [fDefaults setInteger: [sender integerValue] forKey: @"IdleLimitMinutes"];
657 [self applyIdleStopSetting: nil];
660 - (void) updateLimitStopField
663 [fIdleStopField setIntegerValue: [fDefaults integerForKey: @"IdleLimitMinutes"]];
666 - (void) updateLimitFields
671 [fUploadField setIntValue: [fDefaults integerForKey: @"UploadLimit"]];
672 [fDownloadField setIntValue: [fDefaults integerForKey: @"DownloadLimit"]];
675 - (void) setGlobalLimit: (id) sender
677 [fDefaults setInteger: [sender intValue] forKey: sender == fUploadField ? @"UploadLimit" : @"DownloadLimit"];
678 [self applySpeedSettings: self];
681 - (void) setSpeedLimit: (id) sender
683 [fDefaults setInteger: [sender intValue] forKey: sender == fSpeedLimitUploadField
684 ? @"SpeedLimitUploadLimit" : @"SpeedLimitDownloadLimit"];
685 [self applyAltSpeedSettings];
688 - (void) setAutoSpeedLimit: (id) sender
690 tr_sessionUseAltSpeedTime(fHandle, [fDefaults boolForKey: @"SpeedLimitAuto"]);
693 - (void) setAutoSpeedLimitTime: (id) sender
695 tr_sessionSetAltSpeedBegin(fHandle, [PrefsController dateToTimeSum: [fDefaults objectForKey: @"SpeedLimitAutoOnDate"]]);
696 tr_sessionSetAltSpeedEnd(fHandle, [PrefsController dateToTimeSum: [fDefaults objectForKey: @"SpeedLimitAutoOffDate"]]);
699 - (void) setAutoSpeedLimitDay: (id) sender
701 tr_sessionSetAltSpeedDay(fHandle, [[sender selectedItem] tag]);
704 + (NSInteger) dateToTimeSum: (NSDate *) date
706 NSCalendar * calendar = [NSCalendar currentCalendar];
707 NSDateComponents * components = [calendar components: NSHourCalendarUnit | NSMinuteCalendarUnit fromDate: date];
708 return [components hour] * 60 + [components minute];
711 + (NSDate *) timeSumToDate: (NSInteger) sum
713 NSDateComponents * comps = [[[NSDateComponents alloc] init] autorelease];
714 [comps setHour: sum / 60];
715 [comps setMinute: sum % 60];
717 return [[NSCalendar currentCalendar] dateFromComponents: comps];
720 - (BOOL) control: (NSControl *) control textShouldBeginEditing: (NSText *) fieldEditor
722 [fInitialString release];
723 fInitialString = [[control stringValue] retain];
728 - (BOOL) control: (NSControl *) control didFailToFormatString: (NSString *) string errorDescription: (NSString *) error
733 [control setStringValue: fInitialString];
734 [fInitialString release];
735 fInitialString = nil;
740 - (void) setBadge: (id) sender
742 [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: self];
745 - (IBAction) setBuiltInGrowlEnabled: (id) sender
747 const BOOL enable = [(NSButton *)sender state] == NSOnState;
748 [fDefaults setBool: enable forKey: @"DisplayNotifications"];
749 [GrowlApplicationBridge setShouldUseBuiltInNotifications: enable];
752 - (IBAction) openGrowlApp: (id) sender
754 [GrowlApplicationBridge openGrowlPreferences: YES];
757 - (void) openNotificationSystemPrefs: (id) sender
759 [[NSWorkspace sharedWorkspace] openURL: [NSURL fileURLWithPath:@"/System/Library/PreferencePanes/Notifications.prefPane"]];
762 - (void) resetWarnings: (id) sender
764 [fDefaults removeObjectForKey: @"WarningDuplicate"];
765 [fDefaults removeObjectForKey: @"WarningRemainingSpace"];
766 [fDefaults removeObjectForKey: @"WarningFolderDataSameName"];
767 [fDefaults removeObjectForKey: @"WarningResetStats"];
768 [fDefaults removeObjectForKey: @"WarningCreatorBlankAddress"];
769 [fDefaults removeObjectForKey: @"WarningCreatorPrivateBlankAddress"];
770 [fDefaults removeObjectForKey: @"WarningRemoveTrackers"];
771 [fDefaults removeObjectForKey: @"WarningInvalidOpen"];
772 [fDefaults removeObjectForKey: @"WarningRemoveCompleted"];
773 [fDefaults removeObjectForKey: @"WarningDonate"];
774 //[fDefaults removeObjectForKey: @"WarningLegal"];
777 - (void) setDefaultForMagnets: (id) sender
779 NSString * bundleID = [[NSBundle mainBundle] bundleIdentifier];
780 const OSStatus result = LSSetDefaultHandlerForURLScheme((CFStringRef)@"magnet", (CFStringRef)bundleID);
782 NSLog(@"Failed setting default magnet link handler");
785 - (void) setQueue: (id) sender
787 //let's just do both - easier that way
788 tr_sessionSetQueueEnabled(fHandle, TR_DOWN, [fDefaults boolForKey: @"Queue"]);
789 tr_sessionSetQueueEnabled(fHandle, TR_UP, [fDefaults boolForKey: @"QueueSeed"]);
791 //handle if any transfers switch from queued to paused
792 [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateQueue" object: self];
795 - (void) setQueueNumber: (id) sender
797 const NSInteger number = [sender intValue];
798 const BOOL seed = sender == fQueueSeedField;
800 [fDefaults setInteger: number forKey: seed ? @"QueueSeedNumber" : @"QueueDownloadNumber"];
802 tr_sessionSetQueueSize(fHandle, seed ? TR_UP : TR_DOWN, number);
805 - (void) setStalled: (id) sender
807 tr_sessionSetQueueStalledEnabled(fHandle, [fDefaults boolForKey: @"CheckStalled"]);
809 //reload main table for stalled status
810 [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
813 - (void) setStalledMinutes: (id) sender
815 const NSInteger min = [sender intValue];
816 [fDefaults setInteger: min forKey: @"StalledMinutes"];
817 tr_sessionSetQueueStalledMinutes(fHandle, min);
819 //reload main table for stalled status
820 [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: self];
823 - (void) setDownloadLocation: (id) sender
825 [fDefaults setBool: [fFolderPopUp indexOfSelectedItem] == DOWNLOAD_FOLDER forKey: @"DownloadLocationConstant"];
826 [self updateShowAddMagnetWindowField];
829 - (void) folderSheetShow: (id) sender
831 NSOpenPanel * panel = [NSOpenPanel openPanel];
833 [panel setPrompt: NSLocalizedString(@"Select", "Preferences -> Open panel prompt")];
834 [panel setAllowsMultipleSelection: NO];
835 [panel setCanChooseFiles: NO];
836 [panel setCanChooseDirectories: YES];
837 [panel setCanCreateDirectories: YES];
839 [panel beginSheetModalForWindow: [self window] completionHandler: ^(NSInteger result) {
840 if (result == NSFileHandlingPanelOKButton)
842 [fFolderPopUp selectItemAtIndex: DOWNLOAD_FOLDER];
844 NSString * folder = [[[panel URLs] objectAtIndex: 0] path];
845 [fDefaults setObject: folder forKey: @"DownloadFolder"];
846 [fDefaults setBool: YES forKey: @"DownloadLocationConstant"];
847 [self updateShowAddMagnetWindowField];
849 tr_sessionSetDownloadDir(fHandle, [folder UTF8String]);
854 [fFolderPopUp selectItemAtIndex: [fDefaults boolForKey: @"DownloadLocationConstant"] ? DOWNLOAD_FOLDER : DOWNLOAD_TORRENT];
859 - (void) incompleteFolderSheetShow: (id) sender
861 NSOpenPanel * panel = [NSOpenPanel openPanel];
863 [panel setPrompt: NSLocalizedString(@"Select", "Preferences -> Open panel prompt")];
864 [panel setAllowsMultipleSelection: NO];
865 [panel setCanChooseFiles: NO];
866 [panel setCanChooseDirectories: YES];
867 [panel setCanCreateDirectories: YES];
869 [panel beginSheetModalForWindow: [self window] completionHandler: ^(NSInteger result) {
870 if (result == NSFileHandlingPanelOKButton)
872 NSString * folder = [[[panel URLs] objectAtIndex: 0] path];
873 [fDefaults setObject: folder forKey: @"IncompleteDownloadFolder"];
875 tr_sessionSetIncompleteDir(fHandle, [folder UTF8String]);
877 [fIncompleteFolderPopUp selectItemAtIndex: 0];
881 - (void) doneScriptSheetShow:(id)sender
883 NSOpenPanel * panel = [NSOpenPanel openPanel];
885 [panel setPrompt: NSLocalizedString(@"Select", "Preferences -> Open panel prompt")];
886 [panel setAllowsMultipleSelection: NO];
887 [panel setCanChooseFiles: YES];
888 [panel setCanChooseDirectories: NO];
889 [panel setCanCreateDirectories: NO];
891 [panel beginSheetModalForWindow: [self window] completionHandler: ^(NSInteger result) {
892 if (result == NSFileHandlingPanelOKButton)
894 NSString * filePath = [[[panel URLs] objectAtIndex: 0] path];
896 [fDefaults setObject: filePath forKey: @"DoneScriptPath"];
897 tr_sessionSetTorrentDoneScript(fHandle, [filePath UTF8String]);
899 [fDefaults setBool: YES forKey: @"DoneScriptEnabled"];
900 tr_sessionSetTorrentDoneScriptEnabled(fHandle, YES);
902 [fDoneScriptPopUp selectItemAtIndex: 0];
906 - (void) setUseIncompleteFolder: (id) sender
908 tr_sessionSetIncompleteDirEnabled(fHandle, [fDefaults boolForKey: @"UseIncompleteDownloadFolder"]);
911 - (void) setRenamePartialFiles: (id) sender
913 tr_sessionSetIncompleteFileNamingEnabled(fHandle, [fDefaults boolForKey: @"RenamePartialFiles"]);
916 - (void) setShowAddMagnetWindow: (id) sender
918 [fDefaults setBool: ([fShowMagnetAddWindowCheck state] == NSOnState) forKey: @"MagnetOpenAsk"];
921 - (void) updateShowAddMagnetWindowField
923 if (![fDefaults boolForKey: @"DownloadLocationConstant"])
925 //always show the add window for magnet links when the download location is the same as the torrent file
926 [fShowMagnetAddWindowCheck setState: NSOnState];
927 [fShowMagnetAddWindowCheck setEnabled: NO];
931 [fShowMagnetAddWindowCheck setState: [fDefaults boolForKey: @"MagnetOpenAsk"]];
932 [fShowMagnetAddWindowCheck setEnabled: YES];
936 - (void) setDoneScriptEnabled: (id) sender
938 if ([fDefaults boolForKey: @"DoneScriptEnabled"] && ![[NSFileManager defaultManager] fileExistsAtPath: [fDefaults stringForKey:@"DoneScriptPath"]])
940 // enabled is set but script file doesn't exist, so prompt for one and disable until they pick one
941 [fDefaults setBool: NO forKey: @"DoneScriptEnabled"];
942 [self doneScriptSheetShow: sender];
944 tr_sessionSetTorrentDoneScriptEnabled(fHandle, [fDefaults boolForKey: @"DoneScriptEnabled"]);
947 - (void) setAutoImport: (id) sender
950 if ((path = [fDefaults stringForKey: @"AutoImportDirectory"]))
952 VDKQueue * watcherQueue = [(Controller *)[NSApp delegate] fileWatcherQueue];
953 if ([fDefaults boolForKey: @"AutoImport"])
955 path = [path stringByExpandingTildeInPath];
956 [watcherQueue addPath: path notifyingAbout: VDKQueueNotifyAboutWrite];
959 [watcherQueue removeAllPaths];
961 [[NSNotificationCenter defaultCenter] postNotificationName: @"AutoImportSettingChange" object: self];
964 [self importFolderSheetShow: nil];
967 - (void) importFolderSheetShow: (id) sender
969 NSOpenPanel * panel = [NSOpenPanel openPanel];
971 [panel setPrompt: NSLocalizedString(@"Select", "Preferences -> Open panel prompt")];
972 [panel setAllowsMultipleSelection: NO];
973 [panel setCanChooseFiles: NO];
974 [panel setCanChooseDirectories: YES];
975 [panel setCanCreateDirectories: YES];
977 [panel beginSheetModalForWindow: [self window] completionHandler: ^(NSInteger result) {
978 if (result == NSFileHandlingPanelOKButton)
980 VDKQueue * watcherQueue = [(Controller *)[NSApp delegate] fileWatcherQueue];
981 [watcherQueue removeAllPaths];
983 NSString * path = [[[panel URLs] objectAtIndex: 0] path];
984 [fDefaults setObject: path forKey: @"AutoImportDirectory"];
985 [watcherQueue addPath: [path stringByExpandingTildeInPath] notifyingAbout: VDKQueueNotifyAboutWrite];
987 [[NSNotificationCenter defaultCenter] postNotificationName: @"AutoImportSettingChange" object: self];
991 NSString * path = [fDefaults stringForKey: @"AutoImportDirectory"];
993 [fDefaults setBool: NO forKey: @"AutoImport"];
996 [fImportFolderPopUp selectItemAtIndex: 0];
1000 - (void) setAutoSize: (id) sender
1002 [[NSNotificationCenter defaultCenter] postNotificationName: @"AutoSizeSettingChange" object: self];
1005 - (void) setRPCEnabled: (id) sender
1007 BOOL enable = [fDefaults boolForKey: @"RPC"];
1008 tr_sessionSetRPCEnabled(fHandle, enable);
1010 [self setRPCWebUIDiscovery: nil];
1013 - (void) linkWebUI: (id) sender
1015 NSString * urlString = [NSString stringWithFormat: WEBUI_URL, [fDefaults integerForKey: @"RPCPort"]];
1016 [[NSWorkspace sharedWorkspace] openURL: [NSURL URLWithString: urlString]];
1019 - (void) setRPCAuthorize: (id) sender
1021 tr_sessionSetRPCPasswordEnabled(fHandle, [fDefaults boolForKey: @"RPCAuthorize"]);
1024 - (void) setRPCUsername: (id) sender
1026 tr_sessionSetRPCUsername(fHandle, [[fDefaults stringForKey: @"RPCUsername"] UTF8String]);
1029 - (void) setRPCPassword: (id) sender
1031 [fRPCPassword release];
1032 fRPCPassword = [[sender stringValue] copy];
1034 const char * password = [[sender stringValue] UTF8String];
1035 [self setKeychainPassword: password forService: RPC_KEYCHAIN_SERVICE username: RPC_KEYCHAIN_NAME];
1037 tr_sessionSetRPCPassword(fHandle, password);
1040 - (void) updateRPCPassword
1042 UInt32 passwordLength;
1043 const char * password = nil;
1044 SecKeychainFindGenericPassword(NULL, strlen(RPC_KEYCHAIN_SERVICE), RPC_KEYCHAIN_SERVICE,
1045 strlen(RPC_KEYCHAIN_NAME), RPC_KEYCHAIN_NAME, &passwordLength, (void **)&password, NULL);
1047 [fRPCPassword release];
1048 if (password != NULL)
1050 char fullPassword[passwordLength+1];
1051 strncpy(fullPassword, password, passwordLength);
1052 fullPassword[passwordLength] = '\0';
1053 SecKeychainItemFreeContent(NULL, (void *)password);
1055 tr_sessionSetRPCPassword(fHandle, fullPassword);
1057 fRPCPassword = [[NSString alloc] initWithUTF8String: fullPassword];
1058 [fRPCPasswordField setStringValue: fRPCPassword];
1064 - (void) setRPCPort: (id) sender
1066 int port = [sender intValue];
1067 [fDefaults setInteger: port forKey: @"RPCPort"];
1068 tr_sessionSetRPCPort(fHandle, port);
1070 [self setRPCWebUIDiscovery: nil];
1073 - (void) setRPCUseWhitelist: (id) sender
1075 tr_sessionSetRPCWhitelistEnabled(fHandle, [fDefaults boolForKey: @"RPCUseWhitelist"]);
1078 - (void) setRPCWebUIDiscovery: (id) sender
1080 if ([fDefaults boolForKey:@"RPC"] && [fDefaults boolForKey: @"RPCWebDiscovery"])
1081 [[BonjourController defaultController] startWithPort: [fDefaults integerForKey: @"RPCPort"]];
1084 if ([BonjourController defaultControllerExists])
1085 [[BonjourController defaultController] stop];
1089 - (void) updateRPCWhitelist
1091 NSString * string = [fRPCWhitelistArray componentsJoinedByString: @","];
1092 tr_sessionSetRPCWhitelist(fHandle, [string UTF8String]);
1095 - (void) addRemoveRPCIP: (id) sender
1097 //don't allow add/remove when currently adding - it leads to weird results
1098 if ([fRPCWhitelistTable editedRow] != -1)
1101 if ([[sender cell] tagForSegment: [sender selectedSegment]] == RPC_IP_REMOVE_TAG)
1103 [fRPCWhitelistArray removeObjectsAtIndexes: [fRPCWhitelistTable selectedRowIndexes]];
1104 [fRPCWhitelistTable deselectAll: self];
1105 [fRPCWhitelistTable reloadData];
1107 [fDefaults setObject: fRPCWhitelistArray forKey: @"RPCWhitelist"];
1108 [self updateRPCWhitelist];
1112 [fRPCWhitelistArray addObject: @""];
1113 [fRPCWhitelistTable reloadData];
1115 const int row = [fRPCWhitelistArray count] - 1;
1116 [fRPCWhitelistTable selectRowIndexes: [NSIndexSet indexSetWithIndex: row] byExtendingSelection: NO];
1117 [fRPCWhitelistTable editColumn: 0 row: row withEvent: nil select: YES];
1121 - (NSInteger) numberOfRowsInTableView: (NSTableView *) tableView
1123 return [fRPCWhitelistArray count];
1126 - (id) tableView: (NSTableView *) tableView objectValueForTableColumn: (NSTableColumn *) tableColumn row: (NSInteger) row
1128 return [fRPCWhitelistArray objectAtIndex: row];
1131 - (void) tableView: (NSTableView *) tableView setObjectValue: (id) object forTableColumn: (NSTableColumn *) tableColumn
1132 row: (NSInteger) row
1134 NSArray * components = [object componentsSeparatedByString: @"."];
1135 NSMutableArray * newComponents = [NSMutableArray arrayWithCapacity: 4];
1137 //create better-formatted ip string
1139 if ([components count] == 4)
1142 for (NSString * component in components)
1144 if ([component isEqualToString: @"*"])
1145 [newComponents addObject: component];
1148 int num = [component intValue];
1149 if (num >= 0 && num < 256)
1150 [newComponents addObject: [[NSNumber numberWithInt: num] stringValue]];
1163 newIP = [newComponents componentsJoinedByString: @"."];
1165 //don't allow the same ip address
1166 if ([fRPCWhitelistArray containsObject: newIP] && ![[fRPCWhitelistArray objectAtIndex: row] isEqualToString: newIP])
1172 [fRPCWhitelistArray replaceObjectAtIndex: row withObject: newIP];
1173 [fRPCWhitelistArray sortUsingSelector: @selector(compareNumeric:)];
1178 if ([[fRPCWhitelistArray objectAtIndex: row] isEqualToString: @""])
1179 [fRPCWhitelistArray removeObjectAtIndex: row];
1182 [fRPCWhitelistTable deselectAll: self];
1183 [fRPCWhitelistTable reloadData];
1185 [fDefaults setObject: fRPCWhitelistArray forKey: @"RPCWhitelist"];
1186 [self updateRPCWhitelist];
1189 - (void) tableViewSelectionDidChange: (NSNotification *) notification
1191 [fRPCAddRemoveControl setEnabled: [fRPCWhitelistTable numberOfSelectedRows] > 0 forSegment: RPC_IP_REMOVE_TAG];
1194 - (void) helpForScript: (id) sender
1196 [[NSHelpManager sharedHelpManager] openHelpAnchor: @"script"
1197 inBook: [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleHelpBookName"]];
1200 - (void) helpForPeers: (id) sender
1202 [[NSHelpManager sharedHelpManager] openHelpAnchor: @"peers"
1203 inBook: [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleHelpBookName"]];
1206 - (void) helpForNetwork: (id) sender
1208 [[NSHelpManager sharedHelpManager] openHelpAnchor: @"network"
1209 inBook: [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleHelpBookName"]];
1212 - (void) helpForRemote: (id) sender
1214 [[NSHelpManager sharedHelpManager] openHelpAnchor: @"remote"
1215 inBook: [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleHelpBookName"]];
1218 - (void) rpcUpdatePrefs
1221 const tr_encryption_mode encryptionMode = tr_sessionGetEncryption(fHandle);
1222 [fDefaults setBool: encryptionMode != TR_CLEAR_PREFERRED forKey: @"EncryptionPrefer"];
1223 [fDefaults setBool: encryptionMode == TR_ENCRYPTION_REQUIRED forKey: @"EncryptionRequire"];
1225 //download directory
1226 NSString * downloadLocation = [[NSString stringWithUTF8String: tr_sessionGetDownloadDir(fHandle)] stringByStandardizingPath];
1227 [fDefaults setObject: downloadLocation forKey: @"DownloadFolder"];
1229 NSString * incompleteLocation = [[NSString stringWithUTF8String: tr_sessionGetIncompleteDir(fHandle)] stringByStandardizingPath];
1230 [fDefaults setObject: incompleteLocation forKey: @"IncompleteDownloadFolder"];
1232 const BOOL useIncomplete = tr_sessionIsIncompleteDirEnabled(fHandle);
1233 [fDefaults setBool: useIncomplete forKey: @"UseIncompleteDownloadFolder"];
1235 const BOOL usePartialFileRanaming = tr_sessionIsIncompleteFileNamingEnabled(fHandle);
1236 [fDefaults setBool: usePartialFileRanaming forKey: @"RenamePartialFiles"];
1239 const BOOL utp = tr_sessionIsUTPEnabled(fHandle);
1240 [fDefaults setBool: utp forKey: @"UTPGlobal"];
1243 const uint16_t peersTotal = tr_sessionGetPeerLimit(fHandle);
1244 [fDefaults setInteger: peersTotal forKey: @"PeersTotal"];
1246 const uint16_t peersTorrent = tr_sessionGetPeerLimitPerTorrent(fHandle);
1247 [fDefaults setInteger: peersTorrent forKey: @"PeersTorrent"];
1250 const BOOL pex = tr_sessionIsPexEnabled(fHandle);
1251 [fDefaults setBool: pex forKey: @"PEXGlobal"];
1254 const BOOL dht = tr_sessionIsDHTEnabled(fHandle);
1255 [fDefaults setBool: dht forKey: @"DHTGlobal"];
1258 const BOOL lpd = tr_sessionIsLPDEnabled(fHandle);
1259 [fDefaults setBool: lpd forKey: @"LocalPeerDiscoveryGlobal"];
1262 const BOOL autoStart = !tr_sessionGetPaused(fHandle);
1263 [fDefaults setBool: autoStart forKey: @"AutoStartDownload"];
1266 const tr_port port = tr_sessionGetPeerPort(fHandle);
1267 [fDefaults setInteger: port forKey: @"BindPort"];
1269 const BOOL nat = tr_sessionIsPortForwardingEnabled(fHandle);
1270 [fDefaults setBool: nat forKey: @"NatTraversal"];
1274 [self updatePortStatus];
1276 const BOOL randomPort = tr_sessionGetPeerPortRandomOnStart(fHandle);
1277 [fDefaults setBool: randomPort forKey: @"RandomPort"];
1279 //speed limit - down
1280 const BOOL downLimitEnabled = tr_sessionIsSpeedLimited(fHandle, TR_DOWN);
1281 [fDefaults setBool: downLimitEnabled forKey: @"CheckDownload"];
1283 const int downLimit = tr_sessionGetSpeedLimit_KBps(fHandle, TR_DOWN);
1284 [fDefaults setInteger: downLimit forKey: @"DownloadLimit"];
1287 const BOOL upLimitEnabled = tr_sessionIsSpeedLimited(fHandle, TR_UP);
1288 [fDefaults setBool: upLimitEnabled forKey: @"CheckUpload"];
1290 const int upLimit = tr_sessionGetSpeedLimit_KBps(fHandle, TR_UP);
1291 [fDefaults setInteger: upLimit forKey: @"UploadLimit"];
1293 //alt speed limit enabled
1294 const BOOL useAltSpeed = tr_sessionUsesAltSpeed(fHandle);
1295 [fDefaults setBool: useAltSpeed forKey: @"SpeedLimit"];
1297 //alt speed limit - down
1298 const int downLimitAlt = tr_sessionGetAltSpeed_KBps(fHandle, TR_DOWN);
1299 [fDefaults setInteger: downLimitAlt forKey: @"SpeedLimitDownloadLimit"];
1301 //alt speed limit - up
1302 const int upLimitAlt = tr_sessionGetAltSpeed_KBps(fHandle, TR_UP);
1303 [fDefaults setInteger: upLimitAlt forKey: @"SpeedLimitUploadLimit"];
1305 //alt speed limit schedule
1306 const BOOL useAltSpeedSched = tr_sessionUsesAltSpeedTime(fHandle);
1307 [fDefaults setBool: useAltSpeedSched forKey: @"SpeedLimitAuto"];
1309 NSDate * limitStartDate = [PrefsController timeSumToDate: tr_sessionGetAltSpeedBegin(fHandle)];
1310 [fDefaults setObject: limitStartDate forKey: @"SpeedLimitAutoOnDate"];
1312 NSDate * limitEndDate = [PrefsController timeSumToDate: tr_sessionGetAltSpeedEnd(fHandle)];
1313 [fDefaults setObject: limitEndDate forKey: @"SpeedLimitAutoOffDate"];
1315 const int limitDay = tr_sessionGetAltSpeedDay(fHandle);
1316 [fDefaults setInteger: limitDay forKey: @"SpeedLimitAutoDay"];
1319 const BOOL blocklist = tr_blocklistIsEnabled(fHandle);
1320 [fDefaults setBool: blocklist forKey: @"BlocklistNew"];
1322 NSString * blocklistURL = [NSString stringWithUTF8String: tr_blocklistGetURL(fHandle)];
1323 [fDefaults setObject: blocklistURL forKey: @"BlocklistURL"];
1326 const BOOL ratioLimited = tr_sessionIsRatioLimited(fHandle);
1327 [fDefaults setBool: ratioLimited forKey: @"RatioCheck"];
1329 const float ratioLimit = tr_sessionGetRatioLimit(fHandle);
1330 [fDefaults setFloat: ratioLimit forKey: @"RatioLimit"];
1333 const BOOL idleLimited = tr_sessionIsIdleLimited(fHandle);
1334 [fDefaults setBool: idleLimited forKey: @"IdleLimitCheck"];
1336 const NSUInteger idleLimitMin = tr_sessionGetIdleLimit(fHandle);
1337 [fDefaults setInteger: idleLimitMin forKey: @"IdleLimitMinutes"];
1340 const BOOL downloadQueue = tr_sessionGetQueueEnabled(fHandle, TR_DOWN);
1341 [fDefaults setBool: downloadQueue forKey: @"Queue"];
1343 const int downloadQueueNum = tr_sessionGetQueueSize(fHandle, TR_DOWN);
1344 [fDefaults setInteger: downloadQueueNum forKey: @"QueueDownloadNumber"];
1346 const BOOL seedQueue = tr_sessionGetQueueEnabled(fHandle, TR_UP);
1347 [fDefaults setBool: seedQueue forKey: @"QueueSeed"];
1349 const int seedQueueNum = tr_sessionGetQueueSize(fHandle, TR_UP);
1350 [fDefaults setInteger: seedQueueNum forKey: @"QueueSeedNumber"];
1352 const BOOL checkStalled = tr_sessionGetQueueStalledEnabled(fHandle);
1353 [fDefaults setBool: checkStalled forKey: @"CheckStalled"];
1355 const int stalledMinutes = tr_sessionGetQueueStalledMinutes(fHandle);
1356 [fDefaults setInteger: stalledMinutes forKey: @"StalledMinutes"];
1359 const BOOL doneScriptEnabled = tr_sessionIsTorrentDoneScriptEnabled(fHandle);
1360 [fDefaults setBool: doneScriptEnabled forKey: @"DoneScriptEnabled"];
1362 NSString * doneScriptPath = [NSString stringWithUTF8String: tr_sessionGetTorrentDoneScript(fHandle)];
1363 [fDefaults setObject: doneScriptPath forKey: @"DoneScriptPath"];
1365 //update gui if loaded
1368 //encryption handled by bindings
1370 //download directory handled by bindings
1372 //utp handled by bindings
1374 [fPeersGlobalField setIntValue: peersTotal];
1375 [fPeersTorrentField setIntValue: peersTorrent];
1377 //pex handled by bindings
1379 //dht handled by bindings
1381 //lpd handled by bindings
1383 [fPortField setIntValue: port];
1384 //port forwarding (nat) handled by bindings
1385 //random port handled by bindings
1387 //limit check handled by bindings
1388 [fDownloadField setIntValue: downLimit];
1390 //limit check handled by bindings
1391 [fUploadField setIntValue: upLimit];
1393 [fSpeedLimitDownloadField setIntValue: downLimitAlt];
1395 [fSpeedLimitUploadField setIntValue: upLimitAlt];
1397 //speed limit schedule handled by bindings
1399 //speed limit schedule times and day handled by bindings
1401 [fBlocklistURLField setStringValue: blocklistURL];
1402 [self updateBlocklistButton];
1403 [self updateBlocklistFields];
1405 //ratio limit enabled handled by bindings
1406 [fRatioStopField setFloatValue: ratioLimit];
1408 //idle limit enabled handled by bindings
1409 [fIdleStopField setIntegerValue: idleLimitMin];
1411 //queues enabled handled by bindings
1412 [fQueueDownloadField setIntValue: downloadQueueNum];
1413 [fQueueSeedField setIntValue: seedQueueNum];
1415 //check stalled handled by bindings
1416 [fStalledField setIntValue: stalledMinutes];
1419 [[NSNotificationCenter defaultCenter] postNotificationName: @"SpeedLimitUpdate" object: nil];
1421 //reload global settings in inspector
1422 [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateGlobalOptions" object: nil];
1427 @implementation PrefsController (Private)
1429 - (void) setPrefView: (id) sender
1431 NSString * identifier;
1434 identifier = [sender itemIdentifier];
1435 [[NSUserDefaults standardUserDefaults] setObject: identifier forKey: @"SelectedPrefView"];
1438 identifier = [[NSUserDefaults standardUserDefaults] stringForKey: @"SelectedPrefView"];
1441 if ([identifier isEqualToString: TOOLBAR_TRANSFERS])
1442 view = fTransfersView;
1443 else if ([identifier isEqualToString: TOOLBAR_GROUPS])
1445 else if ([identifier isEqualToString: TOOLBAR_BANDWIDTH])
1446 view = fBandwidthView;
1447 else if ([identifier isEqualToString: TOOLBAR_PEERS])
1449 else if ([identifier isEqualToString: TOOLBAR_NETWORK])
1450 view = fNetworkView;
1451 else if ([identifier isEqualToString: TOOLBAR_REMOTE])
1455 identifier = TOOLBAR_GENERAL; //general view is the default selected
1456 view = fGeneralView;
1459 [[[self window] toolbar] setSelectedItemIdentifier: identifier];
1461 NSWindow * window = [self window];
1462 if ([window contentView] == view)
1465 NSRect windowRect = [window frame];
1466 const CGFloat difference = (NSHeight([view frame]) - NSHeight([[window contentView] frame])) * [window userSpaceScaleFactor];
1467 windowRect.origin.y -= difference;
1468 windowRect.size.height += difference;
1470 [view setHidden: YES];
1471 [window setContentView: view];
1472 [window setFrame: windowRect display: YES animate: YES];
1473 [view setHidden: NO];
1477 [window setTitle: [sender label]];
1480 NSToolbar * toolbar = [window toolbar];
1481 NSString * itemIdentifier = [toolbar selectedItemIdentifier];
1482 for (NSToolbarItem * item in [toolbar items])
1483 if ([[item itemIdentifier] isEqualToString: itemIdentifier])
1485 [window setTitle: [item label]];
1491 - (void) updateGrowlButton
1493 if ([GrowlApplicationBridge isGrowlRunning])
1495 [fBuiltInGrowlButton setHidden: YES];
1496 [fGrowlAppButton setHidden: NO];
1499 [fGrowlAppButton setEnabled: NO && [GrowlApplicationBridge isGrowlURLSchemeAvailable]];
1500 [fGrowlAppButton setTitle: NSLocalizedString(@"Configure In Growl", "Prefs -> Notifications")];
1501 [fGrowlAppButton sizeToFit];
1503 [fGrowlAppButton setTarget: self];
1504 [fGrowlAppButton setAction: @selector(openGrowlApp:)];
1506 else if ([NSApp isOnMountainLionOrBetter])
1508 [fBuiltInGrowlButton setHidden: YES];
1509 [fGrowlAppButton setHidden: NO];
1511 [fGrowlAppButton setEnabled: YES];
1512 [fGrowlAppButton setTitle: NSLocalizedString(@"Configure In System Preferences", "Prefs -> Notifications")];
1513 [fGrowlAppButton sizeToFit];
1515 [fGrowlAppButton setTarget: self];
1516 [fGrowlAppButton setAction: @selector(openNotificationSystemPrefs:)];
1520 [fBuiltInGrowlButton setHidden: NO];
1521 [fGrowlAppButton setHidden: YES];
1523 [fBuiltInGrowlButton setState: [fDefaults boolForKey: @"DisplayNotifications"]];
1527 - (void) setKeychainPassword: (const char *) password forService: (const char *) service username: (const char *) username
1529 SecKeychainItemRef item = NULL;
1530 NSUInteger passwordLength = strlen(password);
1532 OSStatus result = SecKeychainFindGenericPassword(NULL, strlen(service), service, strlen(username), username, NULL, NULL, &item);
1533 if (result == noErr && item)
1535 if (passwordLength > 0) //found, so update
1537 result = SecKeychainItemModifyAttributesAndData(item, NULL, passwordLength, (const void *)password);
1538 if (result != noErr)
1539 NSLog(@"Problem updating Keychain item: %s", GetMacOSStatusErrorString(result));
1541 else //remove the item
1543 result = SecKeychainItemDelete(item);
1544 if (result != noErr)
1545 NSLog(@"Problem removing Keychain item: %s", GetMacOSStatusErrorString(result));
1548 else if (result == errSecItemNotFound) //not found, so add
1550 if (passwordLength > 0)
1552 result = SecKeychainAddGenericPassword(NULL, strlen(service), service, strlen(username), username,
1553 passwordLength, (const void *)password, NULL);
1554 if (result != noErr)
1555 NSLog(@"Problem adding Keychain item: %s", GetMacOSStatusErrorString(result));
1559 NSLog(@"Problem accessing Keychain: %s", GetMacOSStatusErrorString(result));