source: trunk/macosx/PrefsController.m @ 6171

Last change on this file since 6171 was 6171, checked in by livings124, 14 years ago

updated "remote" prefs icon

  • Property svn:keywords set to Date Rev Author Id
File size: 38.1 KB
Line 
1/******************************************************************************
2 * $Id: PrefsController.m 6171 2008-06-13 15:38:02Z livings124 $
3 *
4 * Copyright (c) 2005-2008 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 "BlocklistDownloader.h"
27#import "NSApplicationAdditions.h"
28#import "NSStringAdditions.h"
29#import "UKKQueue.h"
30#import "EMKeychainProxy.h"
31
32#define DOWNLOAD_FOLDER     0
33#define DOWNLOAD_TORRENT    2
34
35#define PROXY_HTTP      0
36#define PROXY_SOCKS4    1
37#define PROXY_SOCKS5    2
38
39#define RPC_ACCESS_ALLOW    0
40#define RPC_ACCESS_DENY     1
41
42#define RPC_IP_ADD_TAG      0
43#define RPC_IP_REMOVE_TAG   1
44
45#define UPDATE_SECONDS 86400
46
47#define TOOLBAR_GENERAL     @"TOOLBAR_GENERAL"
48#define TOOLBAR_TRANSFERS   @"TOOLBAR_TRANSFERS"
49#define TOOLBAR_BANDWIDTH   @"TOOLBAR_BANDWIDTH"
50#define TOOLBAR_PEERS       @"TOOLBAR_PEERS"
51#define TOOLBAR_NETWORK     @"TOOLBAR_NETWORK"
52#define TOOLBAR_REMOTE      @"TOOLBAR_REMOTE"
53
54@interface PrefsController (Private)
55
56- (void) setPrefView: (id) sender;
57
58- (void) folderSheetClosed: (NSOpenPanel *) openPanel returnCode: (int) code contextInfo: (void *) info;
59- (void) incompleteFolderSheetClosed: (NSOpenPanel *) openPanel returnCode: (int) code contextInfo: (void *) info;
60- (void) importFolderSheetClosed: (NSOpenPanel *) openPanel returnCode: (int) code contextInfo: (void *) info;
61
62@end
63
64@implementation PrefsController
65
66- (id) initWithHandle: (tr_handle *) handle
67{
68    if ((self = [super initWithWindowNibName: @"PrefsWindow"]))
69    {
70        fDefaults = [NSUserDefaults standardUserDefaults];
71        fHandle = handle;
72       
73        //checks for old version speeds of -1
74        if ([fDefaults integerForKey: @"UploadLimit"] < 0)
75        {
76            [fDefaults setInteger: 20 forKey: @"UploadLimit"];
77            [fDefaults setBool: NO forKey: @"CheckUpload"];
78        }
79        if ([fDefaults integerForKey: @"DownloadLimit"] < 0)
80        {
81            [fDefaults setInteger: 20 forKey: @"DownloadLimit"];
82            [fDefaults setBool: NO forKey: @"CheckDownload"];
83        }
84       
85        //check for old version download location (before 1.1)
86        NSString * choice;
87        if ((choice = [fDefaults stringForKey: @"DownloadChoice"]))
88        {
89            [fDefaults setBool: [choice isEqualToString: @"Constant"] forKey: @"DownloadLocationConstant"];
90            [fDefaults setBool: YES forKey: @"DownloadAsk"];
91           
92            [fDefaults removeObjectForKey: @"DownloadChoice"];
93        }
94       
95        //set check for update to right value
96        [self setCheckForUpdate: nil];
97       
98        //set auto import
99        NSString * autoPath;
100        if ([fDefaults boolForKey: @"AutoImport"] && (autoPath = [fDefaults stringForKey: @"AutoImportDirectory"]))
101            [[UKKQueue sharedFileWatcher] addPath: [autoPath stringByExpandingTildeInPath]];
102       
103        //set encryption
104        [self setEncryptionMode: nil];
105       
106        //actually set bandwidth limits
107        [self applySpeedSettings: nil];
108       
109        //set proxy type
110        [self updateProxyType];
111       
112        [self updateProxyPassword];
113       
114        //update rpc access list
115        fRPCAccessArray = [[fDefaults arrayForKey: @"RPCAccessList"] mutableCopy];
116        if (!fRPCAccessArray)
117            fRPCAccessArray = [[NSMutableArray arrayWithObject: [NSDictionary dictionaryWithObjectsAndKeys: @"127.0.0.1", @"IP",
118                                [NSNumber numberWithBool: YES], @"Allow", nil]] retain];
119        [self updateRPCAccessList];
120    }
121   
122    return self;
123}
124
125- (tr_handle *) handle
126{
127    return fHandle;
128}
129
130- (void) dealloc
131{
132    [fPortStatusTimer invalidate];
133    if (fPortChecker)
134    {
135        [fPortChecker cancelProbe];
136        [fPortChecker release];
137    }
138   
139    [fRPCAccessArray release];
140   
141    [super dealloc];
142}
143
144- (void) awakeFromNib
145{
146    fHasLoaded = YES;
147   
148    NSToolbar * toolbar = [[NSToolbar alloc] initWithIdentifier: @"Preferences Toolbar"];
149    [toolbar setDelegate: self];
150    [toolbar setAllowsUserCustomization: NO];
151    [toolbar setDisplayMode: NSToolbarDisplayModeIconAndLabel];
152    [toolbar setSizeMode: NSToolbarSizeModeRegular];
153    [toolbar setSelectedItemIdentifier: TOOLBAR_GENERAL];
154    [[self window] setToolbar: toolbar];
155    [toolbar release];
156   
157    [self setPrefView: nil];
158   
159    if (![NSApp isOnLeopardOrBetter])
160    {
161        [fRPCAddRemoveControl sizeToFit];
162        [fRPCAddRemoveControl setLabel: @"+" forSegment: RPC_IP_ADD_TAG];
163        [fRPCAddRemoveControl setLabel: @"-" forSegment: RPC_IP_REMOVE_TAG];
164    }
165   
166    //set download folder
167    [fFolderPopUp selectItemAtIndex: [fDefaults boolForKey: @"DownloadLocationConstant"] ? DOWNLOAD_FOLDER : DOWNLOAD_TORRENT];
168   
169    //set stop ratio
170    [self updateRatioStopField];
171   
172    //set limits
173    [self updateLimitFields];
174   
175    //set speed limit
176    [fSpeedLimitUploadField setIntValue: [fDefaults integerForKey: @"SpeedLimitUploadLimit"]];
177    [fSpeedLimitDownloadField setIntValue: [fDefaults integerForKey: @"SpeedLimitDownloadLimit"]];
178   
179    //set port
180    [fPortField setIntValue: [fDefaults integerForKey: @"BindPort"]];
181    fNatStatus = -1;
182   
183    [self updatePortStatus];
184    fPortStatusTimer = [NSTimer scheduledTimerWithTimeInterval: 5.0 target: self
185                        selector: @selector(updatePortStatus) userInfo: nil repeats: YES];
186   
187    //set peer connections
188    [fPeersGlobalField setIntValue: [fDefaults integerForKey: @"PeersTotal"]];
189    [fPeersTorrentField setIntValue: [fDefaults integerForKey: @"PeersTorrent"]];
190   
191    //set queue values
192    [fQueueDownloadField setIntValue: [fDefaults integerForKey: @"QueueDownloadNumber"]];
193    [fQueueSeedField setIntValue: [fDefaults integerForKey: @"QueueSeedNumber"]];
194    [fStalledField setIntValue: [fDefaults integerForKey: @"StalledMinutes"]];
195   
196    //set proxy fields
197    [fProxyAddressField setStringValue: [fDefaults stringForKey: @"ProxyAddress"]];
198    int proxyType;
199    switch(tr_sessionGetProxyType(fHandle))
200    {
201        case TR_PROXY_SOCKS4:
202            proxyType = PROXY_SOCKS4;
203            break;
204        case TR_PROXY_SOCKS5:
205            proxyType = PROXY_SOCKS5;
206            break;
207        case TR_PROXY_HTTP:
208            proxyType = PROXY_HTTP;
209    }
210    [fProxyTypePopUp selectItemAtIndex: proxyType];
211    [fProxyPasswordField setStringValue: [NSString stringWithUTF8String: tr_sessionGetProxyPassword(fHandle)]];
212   
213    //set blocklist
214    [self updateBlocklistFields];
215   
216    //set rpc port
217    [fRPCPortField setIntValue: [fDefaults integerForKey: @"RPCPort"]];
218}
219
220- (void) setUpdater: (SUUpdater *) updater
221{
222    fUpdater = updater;
223}
224
225- (NSToolbarItem *) toolbar: (NSToolbar *) toolbar itemForItemIdentifier: (NSString *) ident willBeInsertedIntoToolbar: (BOOL) flag
226{
227    NSToolbarItem * item = [[NSToolbarItem alloc] initWithItemIdentifier: ident];
228
229    if ([ident isEqualToString: TOOLBAR_GENERAL])
230    {
231        [item setLabel: NSLocalizedString(@"General", "Preferences -> toolbar item title")];
232        [item setImage: [NSImage imageNamed: [NSApp isOnLeopardOrBetter] ? NSImageNamePreferencesGeneral : @"Preferences.png"]];
233        [item setTarget: self];
234        [item setAction: @selector(setPrefView:)];
235        [item setAutovalidates: NO];
236    }
237    else if ([ident isEqualToString: TOOLBAR_TRANSFERS])
238    {
239        [item setLabel: NSLocalizedString(@"Transfers", "Preferences -> toolbar item title")];
240        [item setImage: [NSImage imageNamed: @"Transfers.png"]];
241        [item setTarget: self];
242        [item setAction: @selector(setPrefView:)];
243        [item setAutovalidates: NO];
244    }
245    else if ([ident isEqualToString: TOOLBAR_BANDWIDTH])
246    {
247        [item setLabel: NSLocalizedString(@"Bandwidth", "Preferences -> toolbar item title")];
248        [item setImage: [NSImage imageNamed: @"Bandwidth.png"]];
249        [item setTarget: self];
250        [item setAction: @selector(setPrefView:)];
251        [item setAutovalidates: NO];
252    }
253    else if ([ident isEqualToString: TOOLBAR_PEERS])
254    {
255        [item setLabel: NSLocalizedString(@"Peers", "Preferences -> toolbar item title")];
256        [item setImage: [NSImage imageNamed: [NSApp isOnLeopardOrBetter] ? NSImageNameUserGroup : @"Peers.png"]];
257        [item setTarget: self];
258        [item setAction: @selector(setPrefView:)];
259        [item setAutovalidates: NO];
260    }
261    else if ([ident isEqualToString: TOOLBAR_NETWORK])
262    {
263        [item setLabel: NSLocalizedString(@"Network", "Preferences -> toolbar item title")];
264        [item setImage: [NSImage imageNamed: [NSApp isOnLeopardOrBetter] ? NSImageNameNetwork : @"Network.png"]];
265        [item setTarget: self];
266        [item setAction: @selector(setPrefView:)];
267        [item setAutovalidates: NO];
268    }
269    else if ([ident isEqualToString: TOOLBAR_REMOTE])
270    {
271        [item setLabel: NSLocalizedString(@"Remote", "Preferences -> toolbar item title")];
272        [item setImage: [NSImage imageNamed: @"Remote.png"]];
273        [item setTarget: self];
274        [item setAction: @selector(setPrefView:)];
275        [item setAutovalidates: NO];
276    }
277    else
278    {
279        [item release];
280        return nil;
281    }
282
283    return [item autorelease];
284}
285
286- (NSArray *) toolbarSelectableItemIdentifiers: (NSToolbar *) toolbar
287{
288    return [self toolbarDefaultItemIdentifiers: toolbar];
289}
290
291- (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar
292{
293    return [self toolbarAllowedItemIdentifiers: toolbar];
294}
295
296- (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar
297{
298    return [NSArray arrayWithObjects: TOOLBAR_GENERAL, TOOLBAR_TRANSFERS, TOOLBAR_BANDWIDTH,
299                                        TOOLBAR_PEERS, TOOLBAR_NETWORK, TOOLBAR_REMOTE, nil];
300}
301
302//used by ipc
303- (void) updatePortField
304{
305    [fPortField setIntValue: [fDefaults integerForKey: @"BindPort"]];
306}
307
308- (void) setPort: (id) sender
309{
310    int port = [sender intValue];
311    [fDefaults setInteger: port forKey: @"BindPort"];
312    tr_sessionSetPeerPort(fHandle, port);
313   
314    fPeerPort = -1;
315    [self updatePortStatus];
316}
317
318- (void) setNat: (id) sender
319{
320    tr_sessionSetPortForwardingEnabled(fHandle, [fDefaults boolForKey: @"NatTraversal"]);
321   
322    fNatStatus = -1;
323    [self updatePortStatus];
324}
325
326- (void) updatePortStatus
327{
328    const tr_port_forwarding fwd = tr_sessionGetPortForwarding(fHandle);
329    const int port = tr_sessionGetPeerPort(fHandle);
330
331    if (fNatStatus != fwd || fPeerPort != port )
332    {
333        fNatStatus = fwd;
334        fPeerPort = port;
335       
336        [fPortStatusField setStringValue: @""];
337        [fPortStatusImage setImage: nil];
338        [fPortStatusProgress startAnimation: self];
339       
340        if (fPortChecker)
341        {
342            [fPortChecker cancelProbe];
343            [fPortChecker release];
344        }
345        fPortChecker = [[PortChecker alloc] initForPort: fPeerPort withDelegate: self];
346    }
347}
348
349- (void) portCheckerDidFinishProbing: (PortChecker *) portChecker
350{
351    [fPortStatusProgress stopAnimation: self];
352    switch ([fPortChecker status])
353    {
354        case PORT_STATUS_OPEN:
355            [fPortStatusField setStringValue: NSLocalizedString(@"Port is open", "Preferences -> Network -> port status")];
356            [fPortStatusImage setImage: [NSImage imageNamed: @"GreenDot.png"]];
357            break;
358        case PORT_STATUS_CLOSED:
359            [fPortStatusField setStringValue: NSLocalizedString(@"Port is closed", "Preferences -> Network -> port status")];
360            [fPortStatusImage setImage: [NSImage imageNamed: @"RedDot.png"]];
361            break;
362        case PORT_STATUS_STEALTH:
363            [fPortStatusField setStringValue: NSLocalizedString(@"Port is stealth", "Preferences -> Network -> port status")];
364            [fPortStatusImage setImage: [NSImage imageNamed: @"RedDot.png"]];
365            break;
366        case PORT_STATUS_ERROR:
367            [fPortStatusField setStringValue: NSLocalizedString(@"Unable to check status", "Preferences -> Network -> port status")];
368            [fPortStatusImage setImage: [NSImage imageNamed: @"YellowDot.png"]];
369            break;
370    }
371    [fPortChecker release];
372    fPortChecker = nil;
373}
374
375- (NSArray *) sounds
376{
377    NSMutableArray * sounds = [NSMutableArray array];
378   
379    NSMutableArray * directories = [NSMutableArray arrayWithObjects: @"/System/Library/Sounds", @"/Library/Sounds", nil];
380    if ([NSApp isOnLeopardOrBetter])
381        [directories addObject: [NSHomeDirectory() stringByAppendingPathComponent: @"Library/Sounds"]];
382   
383    BOOL isDirectory;
384    NSString * directory;
385    NSEnumerator * enumerator = [directories objectEnumerator];
386    while ((directory = [enumerator nextObject]))
387        if ([[NSFileManager defaultManager] fileExistsAtPath: directory isDirectory: &isDirectory] && isDirectory)
388        {
389            NSString * sound;
390            NSEnumerator * soundEnumerator = [[[NSFileManager defaultManager] directoryContentsAtPath: directory] objectEnumerator];
391            while ((sound = [soundEnumerator nextObject]))
392            {
393                sound = [sound stringByDeletingPathExtension];
394                if ([NSSound soundNamed: sound])
395                    [sounds addObject: sound];
396            }
397        }
398   
399    return sounds;
400}
401
402- (void) setSound: (id) sender
403{
404    //play sound when selecting
405    NSSound * sound;
406    if ((sound = [NSSound soundNamed: [sender titleOfSelectedItem]]))
407        [sound play];
408}
409
410- (void) setPeersGlobal: (id) sender
411{
412    int count = [sender intValue];
413    [fDefaults setInteger: count forKey: @"PeersTotal"];
414    tr_sessionSetPeerLimit(fHandle, count);
415}
416
417- (void) setPeersTorrent: (id) sender
418{
419    int count = [sender intValue];
420    [fDefaults setInteger: count forKey: @"PeersTorrent"];
421}
422
423- (void) setPEX: (id) sender
424{
425    tr_sessionSetPexEnabled(fHandle, [fDefaults boolForKey: @"PEXGlobal"]);
426}
427
428- (void) setEncryptionMode: (id) sender
429{
430    tr_sessionSetEncryption(fHandle, [fDefaults boolForKey: @"EncryptionPrefer"] ?
431        ([fDefaults boolForKey: @"EncryptionRequire"] ? TR_ENCRYPTION_REQUIRED : TR_ENCRYPTION_PREFERRED) : TR_PLAINTEXT_PREFERRED);
432}
433
434- (void) setBlocklistEnabled: (id) sender
435{
436    BOOL enable = [sender state] == NSOnState;
437    [fDefaults setBool: enable forKey: @"Blocklist"];
438    tr_blocklistSetEnabled(fHandle, enable);
439}
440
441- (void) updateBlocklist: (id) sender
442{
443    [BlocklistDownloader downloadWithPrefsController: self];
444}
445
446- (void) updateBlocklistFields
447{
448    BOOL exists = tr_blocklistExists(fHandle);
449   
450    if (exists)
451    {
452        NSNumberFormatter * numberFormatter = [[NSNumberFormatter alloc] init];
453        [numberFormatter setNumberStyle: NSNumberFormatterDecimalStyle];
454        [numberFormatter setMaximumFractionDigits: 0];
455        NSString * countString = [numberFormatter stringFromNumber: [NSNumber numberWithInt: tr_blocklistGetRuleCount(fHandle)]];
456        [numberFormatter release];
457       
458        [fBlocklistMessageField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%@ IP address rules in list",
459            "Prefs -> blocklist -> message"), countString]];
460    }
461    else
462        [fBlocklistMessageField setStringValue: NSLocalizedString(@"A blocklist must first be downloaded",
463            "Prefs -> blocklist -> message")];
464   
465    [fBlocklistEnableCheck setEnabled: exists];
466    [fBlocklistEnableCheck setState: exists && [fDefaults boolForKey: @"Blocklist"]];
467}
468
469- (void) applySpeedSettings: (id) sender
470{
471    if ([fDefaults boolForKey: @"SpeedLimit"])
472    {
473        tr_sessionSetSpeedLimitEnabled(fHandle, TR_UP, 1);
474        tr_sessionSetSpeedLimit(fHandle, TR_UP, [fDefaults integerForKey: @"SpeedLimitUploadLimit"]);
475       
476        tr_sessionSetSpeedLimitEnabled(fHandle, TR_DOWN, 1);
477        tr_sessionSetSpeedLimit(fHandle, TR_DOWN, [fDefaults integerForKey: @"SpeedLimitDownloadLimit"]);
478    }
479    else
480    {
481        tr_sessionSetSpeedLimitEnabled(fHandle, TR_UP, [fDefaults boolForKey: @"CheckUpload"]);
482        tr_sessionSetSpeedLimit(fHandle, TR_UP, [fDefaults integerForKey: @"UploadLimit"]);
483       
484        tr_sessionSetSpeedLimitEnabled(fHandle, TR_DOWN, [fDefaults boolForKey: @"CheckDownload"]);
485        tr_sessionSetSpeedLimit(fHandle, TR_DOWN, [fDefaults integerForKey: @"DownloadLimit"]);
486    }
487}
488
489- (void) applyRatioSetting: (id) sender
490{
491    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
492}
493
494- (void) updateRatioStopField
495{
496    if (!fHasLoaded)
497        return;
498   
499    [fRatioStopField setFloatValue: [fDefaults floatForKey: @"RatioLimit"]];
500   
501    [self applyRatioSetting: nil];
502}
503
504- (void) setRatioStop: (id) sender
505{
506    [fDefaults setFloat: [sender floatValue] forKey: @"RatioLimit"];
507    [self applyRatioSetting: nil];
508}
509
510- (void) updateLimitFields
511{
512    if (!fHasLoaded)
513        return;
514   
515    [fUploadField setIntValue: [fDefaults integerForKey: @"UploadLimit"]];
516    [fDownloadField setIntValue: [fDefaults integerForKey: @"DownloadLimit"]];
517}
518
519- (void) setGlobalLimit: (id) sender
520{
521    [fDefaults setInteger: [sender intValue] forKey: sender == fUploadField ? @"UploadLimit" : @"DownloadLimit"];
522    [self applySpeedSettings: self];
523}
524
525- (void) setSpeedLimit: (id) sender
526{
527    [fDefaults setInteger: [sender intValue] forKey: sender == fSpeedLimitUploadField
528                                                        ? @"SpeedLimitUploadLimit" : @"SpeedLimitDownloadLimit"];
529    [self applySpeedSettings: self];
530}
531
532- (void) setAutoSpeedLimit: (id) sender
533{
534    [[NSNotificationCenter defaultCenter] postNotificationName: @"AutoSpeedLimitChange" object: self];
535}
536
537- (BOOL) control: (NSControl *) control textShouldBeginEditing: (NSText *) fieldEditor
538{
539    [fInitialString release];
540    fInitialString = [[control stringValue] retain];
541   
542    return YES;
543}
544
545- (BOOL) control: (NSControl *) control didFailToFormatString: (NSString *) string errorDescription: (NSString *) error
546{
547    NSBeep();
548    if (fInitialString)
549    {
550        [control setStringValue: fInitialString];
551        [fInitialString release];
552        fInitialString = nil;
553    }
554    return NO;
555}
556
557- (void) setBadge: (id) sender
558{
559    [[NSNotificationCenter defaultCenter] postNotificationName: @"DockBadgeChange" object: self];
560}
561
562- (void) resetWarnings: (id) sender
563{
564    [fDefaults setBool: YES forKey: @"WarningDuplicate"];
565    [fDefaults setBool: YES forKey: @"WarningRemainingSpace"];
566    [fDefaults setBool: YES forKey: @"WarningFolderDataSameName"];
567    [fDefaults setBool: YES forKey: @"WarningResetStats"];
568    [fDefaults setBool: YES forKey: @"WarningCreatorBlankAddress"];
569    [fDefaults setBool: YES forKey: @"WarningRemoveBuiltInTracker"];
570}
571
572- (void) setCheckForUpdate: (id) sender
573{
574    NSTimeInterval seconds = [fDefaults boolForKey: @"CheckForUpdates"] ? UPDATE_SECONDS : 0;
575    [fDefaults setInteger: seconds forKey: @"SUScheduledCheckInterval"];
576    if (fUpdater)
577        [fUpdater scheduleCheckWithInterval: seconds];
578}
579
580- (void) setQueue: (id) sender
581{
582    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateQueue" object: self];
583}
584
585- (void) setQueueNumber: (id) sender
586{
587    [fDefaults setInteger: [sender intValue] forKey: sender == fQueueDownloadField ? @"QueueDownloadNumber" : @"QueueSeedNumber"];
588    [self setQueue: nil];
589}
590
591- (void) setStalled: (id) sender
592{
593    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateQueue" object: self];
594}
595
596- (void) setStalledMinutes: (id) sender
597{
598    [fDefaults setInteger: [sender intValue] forKey: @"StalledMinutes"];
599    [self setStalled: nil];
600}
601
602- (void) setDownloadLocation: (id) sender
603{
604    [fDefaults setBool: [fFolderPopUp indexOfSelectedItem] == DOWNLOAD_FOLDER forKey: @"DownloadLocationConstant"];
605}
606
607- (void) folderSheetShow: (id) sender
608{
609    NSOpenPanel * panel = [NSOpenPanel openPanel];
610
611    [panel setPrompt: NSLocalizedString(@"Select", "Preferences -> Open panel prompt")];
612    [panel setAllowsMultipleSelection: NO];
613    [panel setCanChooseFiles: NO];
614    [panel setCanChooseDirectories: YES];
615    [panel setCanCreateDirectories: YES];
616
617    [panel beginSheetForDirectory: nil file: nil types: nil
618        modalForWindow: [self window] modalDelegate: self didEndSelector:
619        @selector(folderSheetClosed:returnCode:contextInfo:) contextInfo: nil];
620}
621
622- (void) incompleteFolderSheetShow: (id) sender
623{
624    NSOpenPanel * panel = [NSOpenPanel openPanel];
625
626    [panel setPrompt: NSLocalizedString(@"Select", "Preferences -> Open panel prompt")];
627    [panel setAllowsMultipleSelection: NO];
628    [panel setCanChooseFiles: NO];
629    [panel setCanChooseDirectories: YES];
630    [panel setCanCreateDirectories: YES];
631
632    [panel beginSheetForDirectory: nil file: nil types: nil
633        modalForWindow: [self window] modalDelegate: self didEndSelector:
634        @selector(incompleteFolderSheetClosed:returnCode:contextInfo:) contextInfo: nil];
635}
636
637- (void) setAutoImport: (id) sender
638{
639    NSString * path;
640    if ((path = [fDefaults stringForKey: @"AutoImportDirectory"]))
641    {
642        path = [path stringByExpandingTildeInPath];
643        if ([fDefaults boolForKey: @"AutoImport"])
644            [[UKKQueue sharedFileWatcher] addPath: path];
645        else
646            [[UKKQueue sharedFileWatcher] removePathFromQueue: path];
647       
648        [[NSNotificationCenter defaultCenter] postNotificationName: @"AutoImportSettingChange" object: self];
649    }
650    else
651        [self importFolderSheetShow: nil];
652}
653
654- (void) importFolderSheetShow: (id) sender
655{
656    NSOpenPanel * panel = [NSOpenPanel openPanel];
657
658    [panel setPrompt: NSLocalizedString(@"Select", "Preferences -> Open panel prompt")];
659    [panel setAllowsMultipleSelection: NO];
660    [panel setCanChooseFiles: NO];
661    [panel setCanChooseDirectories: YES];
662    [panel setCanCreateDirectories: YES];
663
664    [panel beginSheetForDirectory: nil file: nil types: nil
665        modalForWindow: [self window] modalDelegate: self didEndSelector:
666        @selector(importFolderSheetClosed:returnCode:contextInfo:) contextInfo: nil];
667}
668
669- (void) setAutoSize: (id) sender
670{
671    [[NSNotificationCenter defaultCenter] postNotificationName: @"AutoSizeSettingChange" object: self];
672}
673
674- (void) setProxyEnabled: (id) sender
675{
676    BOOL enable = [fDefaults boolForKey: @"Proxy"];
677    tr_sessionSetProxyEnabled(fHandle, [fDefaults boolForKey: @"Proxy"]);
678}
679
680- (void) setProxyAddress: (id) sender
681{
682    NSString * address = [sender stringValue];
683    BOOL blank = [address isEqualToString: @""];
684   
685    if (!blank && [address rangeOfString: @"://"].location == NSNotFound)
686        address = [@"http://" stringByAppendingString: address];
687   
688    if (blank || tr_httpIsValidURL([address UTF8String]))
689    {
690        tr_sessionSetProxy(fHandle, [address UTF8String]);
691        [sender setStringValue: address];
692        [fDefaults setObject: address forKey: @"ProxyAddress"];
693    }
694    else
695    {
696        NSBeep();
697        [sender setStringValue: [fDefaults stringForKey: @"ProxyAddress"]];
698    }
699}
700
701- (void) setProxyType: (id) sender
702{
703    NSString * type;
704    switch ([sender indexOfSelectedItem])
705    {
706        case PROXY_HTTP:
707            type = @"HTTP";
708            break;
709        case PROXY_SOCKS4:
710            type = @"SOCKS4";
711            break;
712        case PROXY_SOCKS5:
713            type = @"SOCKS5";
714    }
715   
716    [fDefaults setObject: type forKey: @"ProxyType"];
717    [self updateProxyType];
718}
719
720- (void) updateProxyType
721{
722    NSString * typeString = [fDefaults stringForKey: @"ProxyType"];
723    tr_proxy_type type;
724    if ([typeString isEqualToString: @"SOCKS4"])
725        type = TR_PROXY_SOCKS4;
726    else if ([typeString isEqualToString: @"SOCKS5"])
727        type = TR_PROXY_SOCKS4;
728    else
729    {
730        //safety
731        if (![typeString isEqualToString: @"HTTP"])
732        {
733            typeString = @"HTTP";
734            [fDefaults setObject: typeString forKey: @"ProxyType"];
735        }
736        type = TR_PROXY_HTTP;
737    }
738   
739    tr_sessionSetProxyType(fHandle, type);
740}
741
742- (void) setProxyAuthorize: (id) sender
743{
744    BOOL enable = [fDefaults boolForKey: @"ProxyAuthorize"];
745    tr_sessionSetProxyAuthEnabled(fHandle, enable);
746}
747
748- (void) setProxyUsername: (id) sender
749{
750    tr_sessionSetProxyUsername(fHandle, [[fDefaults stringForKey: @"ProxyUsername"] UTF8String]);
751}
752
753- (void) setProxyPassword: (id) sender
754{
755    NSString * password = [[sender stringValue] retain];
756   
757    EMGenericKeychainItem * keychainItem = [[EMKeychainProxy sharedProxy] genericKeychainItemForService: @"Transmission:Proxy"
758                                            withUsername: @"Proxy"];
759    if (keychainItem)
760    {
761        if (![password isEqualToString: @""])
762            [keychainItem setPassword: password];
763        else
764            [keychainItem removeFromKeychain];
765    }
766    else
767    {
768        if (![password isEqualToString: @""])
769            [[EMKeychainProxy sharedProxy] addGenericKeychainItemForService: @"Transmission:Proxy" withUsername: @"Proxy"
770                password: password];
771    }
772   
773    tr_sessionSetProxyPassword(fHandle, [password UTF8String]);
774}
775
776//user will only be prompted if Keychain is locked and Transmission has changed since last launched
777- (void) updateProxyPassword
778{
779    NSString * password;
780    EMGenericKeychainItem * keychainItem = [[EMKeychainProxy sharedProxy] genericKeychainItemForService: @"Transmission:Proxy"
781                                            withUsername: @"Proxy"];
782    if (!(password = [keychainItem password]))
783        password = @"";
784   
785    tr_sessionSetProxyPassword(fHandle, [password UTF8String]);
786   
787    [fProxyPasswordField setStringValue: password];
788}
789
790- (void) setRPCEnabled: (id) sender
791{
792    tr_sessionSetRPCEnabled(fHandle, [fDefaults boolForKey: @"RPC"]);
793}
794
795- (void) setRPCAuthorize: (id) sender
796{
797    tr_sessionSetRPCPasswordEnabled(fHandle, [fDefaults boolForKey: @"RPCAuthorize"]);
798}
799
800- (void) setRPCUsername: (id) sender
801{
802    tr_sessionSetRPCUsername(fHandle, [[fDefaults stringForKey: @"RPCUsername"] UTF8String]);
803}
804
805- (void) setRPCPassword: (id) sender
806{
807    tr_sessionSetRPCPassword(fHandle, [[fDefaults stringForKey: @"RPCPassword"] UTF8String]);
808}
809
810- (void) setRPCPort: (id) sender
811{
812    int port = [sender intValue];
813    [fDefaults setInteger: port forKey: @"RPCPort"];
814    tr_sessionSetRPCPort(fHandle, port);
815}
816
817- (void) updateRPCAccessList
818{
819    NSMutableArray * components = [NSMutableArray arrayWithCapacity: [fRPCAccessArray count]];
820   
821    NSEnumerator * enumerator = [fRPCAccessArray objectEnumerator];
822    NSDictionary * dict;
823    while ((dict = [enumerator nextObject]))
824        [components addObject: [NSString stringWithFormat: @"%c%@", [[dict objectForKey: @"Allow"] boolValue] ? '+' : '-',
825                                [dict objectForKey: @"IP"]]];
826   
827    NSString * string = [components componentsJoinedByString: @","];
828   
829    char * error = NULL;
830    if (tr_sessionSetRPCACL(fHandle, [string UTF8String], &error))
831    {
832        NSLog([NSString stringWithUTF8String: error]);
833        tr_free(error);
834    }
835}
836
837- (void) addRemoveRPCIP: (id) sender
838{
839    //don't allow add/remove when currently adding - it leads to weird results
840    if ([fRPCAccessTable editedRow] != -1)
841        return;
842   
843    if ([[sender cell] tagForSegment: [sender selectedSegment]] == RPC_IP_REMOVE_TAG)
844    {
845        [fRPCAccessArray removeObjectsAtIndexes: [fRPCAccessTable selectedRowIndexes]];
846        [fRPCAccessTable deselectAll: self];
847        [fRPCAccessTable reloadData];
848       
849        [fDefaults setObject: fRPCAccessArray forKey: @"RPCAccessList"];
850        [self updateRPCAccessList];
851    }
852    else
853    {
854        [fRPCAccessArray addObject: [NSDictionary dictionaryWithObjectsAndKeys: @"", @"IP",
855                                        [NSNumber numberWithBool: YES], @"Allow", nil]];
856        [fRPCAccessTable reloadData];
857       
858        int row = [fRPCAccessArray count] - 1;
859        [fRPCAccessTable selectRow: row byExtendingSelection: NO];
860        [fRPCAccessTable editColumn: 0 row: row withEvent: nil select: YES];
861    }
862}
863
864- (NSInteger) numberOfRowsInTableView: (NSTableView *) tableView
865{
866    return [fRPCAccessArray count];
867}
868
869- (id) tableView: (NSTableView *) tableView objectValueForTableColumn: (NSTableColumn *) tableColumn row: (NSInteger) row
870{
871    NSDictionary * dict = [fRPCAccessArray objectAtIndex: row];
872   
873    NSString * ident = [tableColumn identifier];
874    if ([ident isEqualToString: @"Permission"])
875    {
876        int allow = [[dict objectForKey: @"Allow"] boolValue] ? RPC_ACCESS_ALLOW : RPC_ACCESS_DENY;
877        return [NSNumber numberWithInt: allow];
878    }
879    else
880        return [dict objectForKey: @"IP"];
881}
882
883- (void) tableView: (NSTableView *) tableView setObjectValue: (id) object forTableColumn: (NSTableColumn *) tableColumn
884    row: (NSInteger) row
885{
886    NSDictionary * oldDict = [fRPCAccessArray objectAtIndex: row];
887   
888    NSString * ident = [tableColumn identifier];
889    if ([ident isEqualToString: @"Permission"])
890    {
891        NSNumber * allow = [NSNumber numberWithBool: [object intValue] == RPC_ACCESS_ALLOW];
892        NSDictionary * newDict = [NSDictionary dictionaryWithObjectsAndKeys: [oldDict objectForKey: @"IP"], @"IP", allow, @"Allow", nil];
893        [fRPCAccessArray replaceObjectAtIndex: row withObject: newDict];
894    }
895    else
896    {
897        NSArray * components = [object componentsSeparatedByString: @"."];
898        NSMutableArray * newComponents = [NSMutableArray arrayWithCapacity: 4];
899       
900        //create better-formatted ip string
901        if ([components count] == 4)
902        {
903            NSEnumerator * enumerator = [components objectEnumerator];
904            NSString * component;
905            while ((component = [enumerator nextObject]))
906            {
907                if ([component isEqualToString: @"*"])
908                    [newComponents addObject: component];
909                else
910                    [newComponents addObject: [[NSNumber numberWithInt: [component intValue]] stringValue]];
911            }
912        }
913       
914        NSString * newIP = [newComponents componentsJoinedByString: @"."];
915       
916        //verify ip string
917        if (!tr_sessionTestRPCACL(fHandle, [[@"+" stringByAppendingString: newIP] UTF8String], NULL))
918        {
919            NSDictionary * newDict = [NSDictionary dictionaryWithObjectsAndKeys: newIP, @"IP",
920                                        [oldDict objectForKey: @"Allow"], @"Allow", nil];
921            [fRPCAccessArray replaceObjectAtIndex: row withObject: newDict];
922           
923            NSSortDescriptor * descriptor = [[[NSSortDescriptor alloc] initWithKey: @"IP" ascending: YES selector: @selector(compareIP:)]
924                                                autorelease];
925            [fRPCAccessArray sortUsingDescriptors: [NSArray arrayWithObject: descriptor]];
926        }
927        else
928        {
929            NSBeep();
930           
931            if ([[oldDict objectForKey: @"IP"] isEqualToString: @""])
932                [fRPCAccessArray removeObjectAtIndex: row];
933        }
934       
935        [fRPCAccessTable deselectAll: self];
936        [fRPCAccessTable reloadData];
937    }
938   
939    [fDefaults setObject: fRPCAccessArray forKey: @"RPCAccessList"];
940    [self updateRPCAccessList];
941}
942
943- (void) tableViewSelectionDidChange: (NSNotification *) notification
944{
945    [fRPCAddRemoveControl setEnabled: [fRPCAccessTable numberOfSelectedRows] > 0 forSegment: RPC_IP_REMOVE_TAG];
946}
947
948- (void) helpForPeers: (id) sender
949{
950    [[NSHelpManager sharedHelpManager] openHelpAnchor: @"PeersPrefs"
951        inBook: [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleHelpBookName"]];
952}
953
954- (void) helpForNetwork: (id) sender
955{
956    [[NSHelpManager sharedHelpManager] openHelpAnchor: @"NetworkPrefs"
957        inBook: [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleHelpBookName"]];
958}
959
960- (void) helpForRemote: (id) sender
961{
962    [[NSHelpManager sharedHelpManager] openHelpAnchor: @"RemotePrefs"
963        inBook: [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleHelpBookName"]];
964}
965
966- (void) rpcUpdatePrefs
967{
968    //encryption
969    tr_encryption_mode encryptionMode = tr_sessionGetEncryption(fHandle);
970    [fDefaults setBool: encryptionMode != TR_PLAINTEXT_PREFERRED forKey: @"EncryptionPrefer"];
971    [fDefaults setBool: encryptionMode == TR_ENCRYPTION_REQUIRED forKey: @"EncryptionRequire"];
972   
973    //download directory
974    NSString * downloadLocation = [[NSString stringWithUTF8String: tr_sessionGetDownloadDir(fHandle)] stringByStandardizingPath];
975    [fDefaults setObject: downloadLocation forKey: @"DownloadFolder"];
976   
977    //peers
978    uint16_t peersTotal = tr_sessionGetPeerLimit(fHandle);
979    [fDefaults setInteger: peersTotal forKey: @"PeersTotal"];
980   
981    //pex
982    BOOL pex = tr_sessionIsPexEnabled(fHandle);
983    [fDefaults setBool: pex forKey: @"PEXGlobal"];
984   
985    //port
986    int port = tr_sessionGetPeerPort(fHandle);
987    [fDefaults setInteger: port forKey: @"BindPort"];
988   
989    BOOL nat = tr_sessionIsPortForwardingEnabled(fHandle);
990    [fDefaults setBool: nat forKey: @"NatTraversal"];
991   
992    fPeerPort = -1;
993    fNatStatus = -1;
994    [self updatePortStatus];
995   
996    //speed limit - down
997    BOOL downLimitEnabled = tr_sessionIsSpeedLimitEnabled(fHandle, TR_DOWN);
998    [fDefaults setBool: downLimitEnabled forKey: @"CheckDownload"];
999   
1000    int downLimit = tr_sessionGetSpeedLimit(fHandle, TR_DOWN);
1001    [fDefaults setInteger: downLimit forKey: @"DownloadLimit"];
1002   
1003    //speed limit - up
1004    BOOL upLimitEnabled = tr_sessionIsSpeedLimitEnabled(fHandle, TR_UP);
1005    [fDefaults setBool: upLimitEnabled forKey: @"CheckUpload"];
1006   
1007    int upLimit = tr_sessionGetSpeedLimit(fHandle, TR_UP);
1008    [fDefaults setInteger: upLimit forKey: @"UploadLimit"];
1009   
1010    //update gui if loaded
1011    if (fHasLoaded)
1012    {
1013        //encryption handled by bindings
1014       
1015        //download directory handled by bindings
1016       
1017        [fPeersGlobalField setIntValue: peersTotal];
1018       
1019        //pex handled by bindings
1020       
1021        [fPortField setIntValue: port];
1022        //port forwarding (nat) handled by bindings
1023       
1024        //limit check handled by bindings
1025        [fDownloadField setIntValue: downLimit];
1026       
1027        //limit check handled by bindings
1028        [fUploadField setIntValue: upLimit];
1029    }
1030}
1031
1032@end
1033
1034@implementation PrefsController (Private)
1035
1036- (void) setPrefView: (id) sender
1037{
1038    NSView * view = fGeneralView;
1039    if (sender)
1040    {
1041        NSString * identifier = [sender itemIdentifier];
1042        if ([identifier isEqualToString: TOOLBAR_TRANSFERS])
1043            view = fTransfersView;
1044        else if ([identifier isEqualToString: TOOLBAR_BANDWIDTH])
1045            view = fBandwidthView;
1046        else if ([identifier isEqualToString: TOOLBAR_PEERS])
1047            view = fPeersView;
1048        else if ([identifier isEqualToString: TOOLBAR_NETWORK])
1049            view = fNetworkView;
1050        else if ([identifier isEqualToString: TOOLBAR_REMOTE])
1051            view = fRemoteView;
1052        else; //general view already selected
1053    }
1054   
1055    NSWindow * window = [self window];
1056    if ([window contentView] == view)
1057        return;
1058   
1059    NSRect windowRect = [window frame];
1060    float difference = ([view frame].size.height - [[window contentView] frame].size.height) * [window userSpaceScaleFactor];
1061    windowRect.origin.y -= difference;
1062    windowRect.size.height += difference;
1063   
1064    [view setHidden: YES];
1065    [window setContentView: view];
1066    [window setFrame: windowRect display: YES animate: YES];
1067    [view setHidden: NO];
1068   
1069    //set title label
1070    if (sender)
1071        [window setTitle: [sender label]];
1072    else
1073    {
1074        NSToolbar * toolbar = [window toolbar];
1075        NSString * itemIdentifier = [toolbar selectedItemIdentifier];
1076        NSEnumerator * enumerator = [[toolbar items] objectEnumerator];
1077        NSToolbarItem * item;
1078        while ((item = [enumerator nextObject]))
1079            if ([[item itemIdentifier] isEqualToString: itemIdentifier])
1080            {
1081                [window setTitle: [item label]];
1082                break;
1083            }
1084    }
1085   
1086    //for network view make sure progress indicator hides itself (get around a Tiger bug)
1087    if (![NSApp isOnLeopardOrBetter] && view == fNetworkView && [fPortStatusImage image])
1088        [fPortStatusProgress setDisplayedWhenStopped: NO];
1089}
1090
1091- (void) folderSheetClosed: (NSOpenPanel *) openPanel returnCode: (int) code contextInfo: (void *) info
1092{
1093    if (code == NSOKButton)
1094    {
1095        [fFolderPopUp selectItemAtIndex: DOWNLOAD_FOLDER];
1096        [fDefaults setObject: [[openPanel filenames] objectAtIndex: 0] forKey: @"DownloadFolder"];
1097        [fDefaults setObject: @"Constant" forKey: @"DownloadChoice"];
1098    }
1099    else
1100    {
1101        //reset if cancelled
1102        [fFolderPopUp selectItemAtIndex: [fDefaults boolForKey: @"DownloadLocationConstant"] ? DOWNLOAD_FOLDER : DOWNLOAD_TORRENT];
1103    }
1104}
1105
1106- (void) incompleteFolderSheetClosed: (NSOpenPanel *) openPanel returnCode: (int) code contextInfo: (void *) info
1107{
1108    if (code == NSOKButton)
1109        [fDefaults setObject: [[openPanel filenames] objectAtIndex: 0] forKey: @"IncompleteDownloadFolder"];
1110    [fIncompleteFolderPopUp selectItemAtIndex: 0];
1111}
1112
1113- (void) importFolderSheetClosed: (NSOpenPanel *) openPanel returnCode: (int) code contextInfo: (void *) info
1114{
1115    NSString * path = [fDefaults stringForKey: @"AutoImportDirectory"];
1116    if (code == NSOKButton)
1117    {
1118        UKKQueue * sharedQueue = [UKKQueue sharedFileWatcher];
1119        if (path)
1120            [sharedQueue removePathFromQueue: [path stringByExpandingTildeInPath]];
1121       
1122        path = [[openPanel filenames] objectAtIndex: 0];
1123        [fDefaults setObject: path forKey: @"AutoImportDirectory"];
1124        [sharedQueue addPath: [path stringByExpandingTildeInPath]];
1125       
1126        [[NSNotificationCenter defaultCenter] postNotificationName: @"AutoImportSettingChange" object: self];
1127    }
1128    else if (!path)
1129        [fDefaults setBool: NO forKey: @"AutoImport"];
1130   
1131    [fImportFolderPopUp selectItemAtIndex: 0];
1132}
1133
1134@end
Note: See TracBrowser for help on using the repository browser.