source: trunk/macosx/PrefsController.m @ 992

Last change on this file since 992 was 992, checked in by livings124, 16 years ago

refresh port status when changing autoport setting

  • Property svn:keywords set to Date Rev Author Id
File size: 32.0 KB
Line 
1/******************************************************************************
2 * $Id: PrefsController.m 992 2006-10-11 18:55:20Z livings124 $
3 *
4 * Copyright (c) 2005-2006 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 "StringAdditions.h"
27#import "UKKQueue.h"
28
29#define MIN_PORT    1
30#define MAX_PORT    65535
31
32#define DOWNLOAD_FOLDER     0
33#define DOWNLOAD_TORRENT    2
34#define DOWNLOAD_ASK        3
35
36#define UPDATE_DAILY    0
37#define UPDATE_WEEKLY   1
38#define UPDATE_NEVER    2
39
40#define TOOLBAR_GENERAL     @"General"
41#define TOOLBAR_TRANSFERS   @"Transfers"
42#define TOOLBAR_BANDWIDTH   @"Bandwidth"
43#define TOOLBAR_NETWORK     @"Network"
44
45@interface PrefsController (Private)
46
47- (void) showGeneralPref: (id) sender;
48- (void) showTransfersPref: (id) sender;
49- (void) showBandwidthPref: (id) sender;
50- (void) showNetworkPref: (id) sender;
51
52- (void) setPrefView: (NSView *) view;
53
54- (void) folderSheetClosed: (NSOpenPanel *) openPanel returnCode: (int) code contextInfo: (void *) info;
55- (void) updatePopUp;
56
57- (void) importFolderSheetClosed: (NSOpenPanel *) openPanel returnCode: (int) code contextInfo: (void *) info;
58- (void) updateImportPopUp;
59
60@end
61
62@implementation PrefsController
63
64- (id) initWithWindowNibName: (NSString *) name handle: (tr_handle_t *) handle
65{
66    if ((self = [self initWithWindowNibName: name]))
67    {
68        fDefaults = [NSUserDefaults standardUserDefaults];
69        fHandle = handle;
70       
71        [[self window] update]; //make sure nib is loaded right away
72    }
73    return self;
74}
75
76- (void) dealloc
77{
78    [fNatStatusTimer invalidate];
79
80    [fDownloadFolder release];
81    [fImportFolder release];
82    [super dealloc];
83}
84
85- (void) awakeFromNib
86{
87    fToolbar = [[NSToolbar alloc] initWithIdentifier: @"Preferences Toolbar"];
88    [fToolbar setDelegate: self];
89    [fToolbar setAllowsUserCustomization: NO];
90    [[self window] setToolbar: fToolbar];
91    [fToolbar setDisplayMode: NSToolbarDisplayModeIconAndLabel];
92    [fToolbar setSizeMode: NSToolbarSizeModeRegular];
93
94    [fToolbar setSelectedItemIdentifier: TOOLBAR_GENERAL];
95    [self showGeneralPref: nil];
96   
97    //set download folder
98    NSString * downloadChoice = [fDefaults stringForKey: @"DownloadChoice"];
99    fDownloadFolder = [[[fDefaults stringForKey: @"DownloadFolder"] stringByExpandingTildeInPath] retain];
100    if ([downloadChoice isEqualToString: @"Constant"])
101        [fFolderPopUp selectItemAtIndex: DOWNLOAD_FOLDER];
102    else if ([downloadChoice isEqualToString: @"Torrent"])
103        [fFolderPopUp selectItemAtIndex: DOWNLOAD_TORRENT];
104    else
105        [fFolderPopUp selectItemAtIndex: DOWNLOAD_ASK];
106    [self updatePopUp];
107   
108    //set auto import
109    fImportFolder = [[[fDefaults stringForKey: @"AutoImportDirectory"] stringByExpandingTildeInPath] retain];
110    [self updateImportPopUp];
111 
112    BOOL autoImport = [fDefaults boolForKey: @"AutoImport"];
113    [fAutoImportCheck setState: autoImport];
114    [fImportFolderPopUp setEnabled: autoImport];
115   
116    if (autoImport)
117        [[UKKQueue sharedFileWatcher] addPath: fImportFolder];
118   
119    //set auto size
120    [fAutoSizeCheck setState: [fDefaults boolForKey: @"AutoSize"]];
121   
122    //set bind port
123    int bindPort = [fDefaults integerForKey: @"BindPort"];
124    [fPortField setIntValue: bindPort];
125    tr_setBindPort(fHandle, bindPort);
126   
127    [self updatePortStatus];
128   
129    //set NAT
130    BOOL natShouldEnable = [fDefaults boolForKey: @"NatTraversal"];
131    if (natShouldEnable)
132        tr_natTraversalEnable(fHandle);
133    [fNatCheck setState: natShouldEnable];
134   
135    fNatStatus = -1;
136    [self updateNatStatus];
137    fNatStatusTimer = [NSTimer scheduledTimerWithTimeInterval: 5.0 target: self
138                        selector: @selector(updateNatStatus) userInfo: nil repeats: YES];
139   
140    //checks for old version upload speed of -1
141    if ([fDefaults integerForKey: @"UploadLimit"] < 0)
142    {
143        [fDefaults setInteger: 20 forKey: @"UploadLimit"];
144        [fDefaults setBool: NO forKey: @"CheckUpload"];
145    }
146   
147    //set upload limit
148    BOOL checkUpload = [fDefaults boolForKey: @"CheckUpload"];
149    int uploadLimit = [fDefaults integerForKey: @"UploadLimit"];
150   
151    [fUploadCheck setState: checkUpload];
152    [fUploadField setIntValue: uploadLimit];
153    [fUploadField setEnabled: checkUpload];
154
155        //set download limit
156    BOOL checkDownload = [fDefaults boolForKey: @"CheckDownload"];
157    int downloadLimit = [fDefaults integerForKey: @"DownloadLimit"];
158   
159    [fDownloadCheck setState: checkDownload];
160    [fDownloadField setIntValue: downloadLimit];
161    [fDownloadField setEnabled: checkDownload];
162   
163    //set speed limit
164    int speedLimitUploadLimit = [fDefaults integerForKey: @"SpeedLimitUploadLimit"];
165    [fSpeedLimitUploadField setIntValue: speedLimitUploadLimit];
166   
167    int speedLimitDownloadLimit = [fDefaults integerForKey: @"SpeedLimitDownloadLimit"];
168    [fSpeedLimitDownloadField setIntValue: speedLimitDownloadLimit];
169   
170    //actually set bandwidth limits
171    if ([fDefaults boolForKey: @"SpeedLimit"])
172    {
173        tr_setUploadLimit(fHandle, speedLimitUploadLimit);
174        tr_setDownloadLimit(fHandle, speedLimitDownloadLimit);
175    }
176    else
177    {
178        tr_setUploadLimit(fHandle, checkUpload ? uploadLimit : -1);
179        tr_setDownloadLimit(fHandle, checkDownload ? downloadLimit : -1);
180    }
181   
182    //set auto speed limit
183    BOOL speedLimitAuto = [fDefaults boolForKey: @"SpeedLimitAuto"];
184    [fSpeedLimitAutoCheck setState: speedLimitAuto];
185   
186    int speedLimitAutoOnHour = [fDefaults integerForKey: @"SpeedLimitAutoOnHour"];
187    [fSpeedLimitAutoOnField setStringValue: [NSString stringWithFormat: @"%02d", speedLimitAutoOnHour]];
188    [fSpeedLimitAutoOnField setEnabled: speedLimitAuto];
189   
190    int speedLimitAutoOffHour = [fDefaults integerForKey: @"SpeedLimitAutoOffHour"];
191    [fSpeedLimitAutoOffField setStringValue: [NSString stringWithFormat: @"%02d", speedLimitAutoOffHour]];
192    [fSpeedLimitAutoOffField setEnabled: speedLimitAuto];
193   
194    //set ratio limit
195    BOOL ratioCheck = [fDefaults boolForKey: @"RatioCheck"];
196    [fRatioCheck setState: ratioCheck];
197    [fRatioField setEnabled: ratioCheck];
198    [fRatioField setFloatValue: [fDefaults floatForKey: @"RatioLimit"]];
199   
200    //set remove and quit prompts
201    BOOL isQuitCheck = [fDefaults boolForKey: @"CheckQuit"],
202        isRemoveCheck = [fDefaults boolForKey: @"CheckRemove"];
203   
204    [fQuitCheck setState: isQuitCheck];
205    [fRemoveCheck setState: isRemoveCheck];
206   
207    [fQuitDownloadingCheck setState: [fDefaults boolForKey: @"CheckQuitDownloading"]];
208    [fQuitDownloadingCheck setEnabled: isQuitCheck];
209    [fRemoveDownloadingCheck setState: [fDefaults boolForKey: @"CheckRemoveDownloading"]];
210    [fRemoveDownloadingCheck setEnabled: isRemoveCheck];
211
212    //set dock badging
213    [fBadgeDownloadRateCheck setState: [fDefaults boolForKey: @"BadgeDownloadRate"]];
214    [fBadgeUploadRateCheck setState: [fDefaults boolForKey: @"BadgeUploadRate"]];
215   
216    //set play sound
217    NSMutableArray * sounds = [NSMutableArray array];
218    NSEnumerator * soundEnumerator,
219                * soundDirectoriesEnumerator = [[NSArray arrayWithObjects: @"System/Library/Sounds",
220                        [NSHomeDirectory() stringByAppendingPathComponent: @"Library/Sounds"], nil] objectEnumerator];
221    NSString * soundPath, * sound;
222   
223    //get list of all sounds and sort alphabetically
224    while ((soundPath = [soundDirectoriesEnumerator nextObject]))
225        if (soundEnumerator = [[NSFileManager defaultManager] enumeratorAtPath: soundPath])
226            while ((sound = [soundEnumerator nextObject]))
227            {
228                sound = [sound stringByDeletingPathExtension];
229                if ([NSSound soundNamed: sound])
230                    [sounds addObject: sound];
231            }
232   
233    [sounds sortUsingSelector: @selector(caseInsensitiveCompare:)];
234   
235    //set download sound
236    [fDownloadSoundPopUp removeAllItems];
237    [fDownloadSoundPopUp addItemsWithTitles: sounds];
238   
239    BOOL playDownloadSound = [fDefaults boolForKey: @"PlayDownloadSound"];
240    [fPlayDownloadSoundCheck setState: playDownloadSound];
241    [fDownloadSoundPopUp setEnabled: playDownloadSound];
242   
243    int downloadSoundIndex = [fDownloadSoundPopUp indexOfItemWithTitle: [fDefaults stringForKey: @"DownloadSound"]];
244    if (downloadSoundIndex >= 0)
245        [fDownloadSoundPopUp selectItemAtIndex: downloadSoundIndex];
246    else
247        [fDefaults setObject: [fDownloadSoundPopUp titleOfSelectedItem] forKey: @"DownloadSound"];
248   
249    //set seeding sound
250    [fSeedingSoundPopUp removeAllItems];
251    [fSeedingSoundPopUp addItemsWithTitles: sounds];
252   
253    BOOL playSeedingSound = [fDefaults boolForKey: @"PlaySeedingSound"];
254    [fPlaySeedingSoundCheck setState: playSeedingSound];
255    [fSeedingSoundPopUp setEnabled: playSeedingSound];
256   
257    int seedingSoundIndex = [fDownloadSoundPopUp indexOfItemWithTitle: [fDefaults stringForKey: @"SeedingSound"]];
258    if (seedingSoundIndex >= 0)
259        [fSeedingSoundPopUp selectItemAtIndex: seedingSoundIndex];
260    else
261        [fDefaults setObject: [fSeedingSoundPopUp titleOfSelectedItem] forKey: @"SeedingSound"];
262   
263    //set start settings
264    BOOL useQueue = [fDefaults boolForKey: @"Queue"];
265    [fQueueCheck setState: useQueue];
266    [fQueueNumberField setEnabled: useQueue];
267    [fQueueNumberField setIntValue: [fDefaults integerForKey: @"QueueDownloadNumber"]];
268   
269    [fStartAtOpenCheck setState: [fDefaults boolForKey: @"AutoStartDownload"]];
270   
271    //set private torrents
272    BOOL copyTorrents = [fDefaults boolForKey: @"SavePrivateTorrent"];
273    [fCopyTorrentCheck setState: copyTorrents];
274   
275    [fDeleteOriginalTorrentCheck setEnabled: copyTorrents];
276    [fDeleteOriginalTorrentCheck setState: [fDefaults boolForKey: @"DeleteOriginalTorrent"]];
277
278    //set update check
279    NSString * updateCheck = [fDefaults stringForKey: @"UpdateCheck"];
280    if ([updateCheck isEqualToString: @"Weekly"])
281        [fUpdatePopUp selectItemAtIndex: UPDATE_WEEKLY];
282    else if ([updateCheck isEqualToString: @"Never"])
283        [fUpdatePopUp selectItemAtIndex: UPDATE_NEVER];
284    else
285        [fUpdatePopUp selectItemAtIndex: UPDATE_DAILY];
286}
287
288- (NSToolbarItem *) toolbar: (NSToolbar *) t itemForItemIdentifier:
289    (NSString *) ident willBeInsertedIntoToolbar: (BOOL) flag
290{
291    NSToolbarItem * item;
292    item = [[NSToolbarItem alloc] initWithItemIdentifier: ident];
293
294    if ([ident isEqualToString: TOOLBAR_GENERAL])
295    {
296        [item setLabel: TOOLBAR_GENERAL];
297        [item setImage: [NSImage imageNamed: @"Preferences.png"]];
298        [item setTarget: self];
299        [item setAction: @selector(showGeneralPref:)];
300    }
301    else if ([ident isEqualToString: TOOLBAR_TRANSFERS])
302    {
303        [item setLabel: TOOLBAR_TRANSFERS];
304        [item setImage: [NSImage imageNamed: @"Transfers.png"]];
305        [item setTarget: self];
306        [item setAction: @selector(showTransfersPref:)];
307    }
308    else if ([ident isEqualToString: TOOLBAR_BANDWIDTH])
309    {
310        [item setLabel: TOOLBAR_BANDWIDTH];
311        [item setImage: [NSImage imageNamed: @"Bandwidth.png"]];
312        [item setTarget: self];
313        [item setAction: @selector(showBandwidthPref:)];
314    }
315    else if ([ident isEqualToString: TOOLBAR_NETWORK])
316    {
317        [item setLabel: TOOLBAR_NETWORK];
318        [item setImage: [NSImage imageNamed: @"Network.png"]];
319        [item setTarget: self];
320        [item setAction: @selector(showNetworkPref:)];
321    }
322    else
323    {
324        [item release];
325        return nil;
326    }
327
328    return item;
329}
330
331- (NSArray *) toolbarSelectableItemIdentifiers: (NSToolbar *) toolbar
332{
333    return [self toolbarDefaultItemIdentifiers: toolbar];
334}
335
336- (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar
337{
338    return [self toolbarAllowedItemIdentifiers: toolbar];
339}
340
341- (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar
342{
343    return [NSArray arrayWithObjects:
344            TOOLBAR_GENERAL, TOOLBAR_TRANSFERS,
345            TOOLBAR_BANDWIDTH, TOOLBAR_NETWORK, nil];
346}
347
348- (void) setPort: (id) sender
349{
350    int bindPort = [sender intValue];
351    if (![[NSString stringWithInt: bindPort] isEqualToString: [sender stringValue]]
352            || bindPort < MIN_PORT || bindPort > MAX_PORT)
353    {
354        NSBeep();
355        bindPort = [fDefaults integerForKey: @"BindPort"];
356        [sender setIntValue: bindPort];
357    }
358    else
359    {
360        tr_setBindPort(fHandle, bindPort);
361        [fDefaults setInteger: bindPort forKey: @"BindPort"];
362       
363        [self updateNatStatus];
364        [self updatePortStatus];
365    }
366}
367
368- (void) updatePortStatus
369{
370    long sytemVersion;
371    [fPortStatusField setStringValue: @""];
372    [fPortStatusImage setImage: nil];
373   
374    Gestalt('sysv', & sytemVersion);
375    if (sytemVersion >= 0x1040)
376    {
377        //NSXML features are unfortunately only available since Mac OS X v10.4
378        PortChecker * checker = [[PortChecker alloc] initWithDelegate: self];
379
380        [fPortStatusField setStringValue: [@"Checking port status" stringByAppendingEllipsis]];
381        [fPortStatusProgress startAnimation: self];
382       
383        [checker probePort: [fDefaults integerForKey: @"BindPort"]];
384    }
385}
386
387- (void) portCheckerDidFinishProbing: (PortChecker *) portChecker
388{
389    [fPortStatusProgress stopAnimation: self];
390    switch ([portChecker status])
391    {
392        case PORT_STATUS_OPEN:
393            [fPortStatusField setStringValue: @"Port is open"];
394            [fPortStatusImage setImage: [NSImage imageNamed: @"GreenDot.tiff"]];
395            break;
396        case PORT_STATUS_STEALTH:
397            [fPortStatusField setStringValue: @"Port is stealth"];
398            [fPortStatusImage setImage: [NSImage imageNamed: @"RedDot.tiff"]];
399            break;
400        case PORT_STATUS_CLOSED:
401            [fPortStatusField setStringValue: @"Port is closed"];
402            [fPortStatusImage setImage: [NSImage imageNamed: @"RedDot.tiff"]];
403            break;
404        case PORT_STATUS_ERROR:
405            [fPortStatusField setStringValue: @"Unable to check port status"];
406            [fPortStatusImage setImage: [NSImage imageNamed: @"YellowDot.tiff"]];
407            break;
408    }
409    [portChecker release];
410}
411
412- (void) setNat: (id) sender
413{
414    BOOL enable = [sender state] == NSOnState;
415    enable ? tr_natTraversalEnable(fHandle) : tr_natTraversalDisable(fHandle);
416    [fDefaults setBool: enable forKey: @"NatTraversal"];
417   
418    [self updateNatStatus];
419}
420
421- (void) updateNatStatus
422{
423    int status = tr_natTraversalStatus(fHandle);
424    if (fNatStatus == status)
425        return;
426    fNatStatus = status;
427   
428    if (status == 2)
429    {
430        [fNatStatusField setStringValue: @"Port successfully mapped"];
431        [fNatStatusImage setImage: [NSImage imageNamed: @"GreenDot.tiff"]];
432    }
433    else if (status == 3 || status == 4)
434    {
435        [fNatStatusField setStringValue: @"Error mapping port"];
436        [fNatStatusImage setImage: [NSImage imageNamed: @"RedDot.tiff"]];
437    }
438    else
439    {
440        [fNatStatusField setStringValue: @""];
441        [fNatStatusImage setImage: nil];
442    }
443   
444    [self updatePortStatus];
445}
446
447- (void) setLimit: (id) sender
448{
449    NSString * key;
450    NSButton * check;
451    NSString * type;
452    if (sender == fUploadField)
453    {
454        key = @"UploadLimit";
455        check = fUploadCheck;
456        type = @"Upload";
457    }
458    else
459    {
460        key = @"DownloadLimit";
461        check = fDownloadCheck;
462        type = @"Download";
463    }
464
465    int limit = [sender intValue];
466    if (![[sender stringValue] isEqualToString: [NSString stringWithFormat: @"%d", limit]] || limit < 0)
467    {
468        NSBeep();
469        limit = [fDefaults integerForKey: key];
470        [sender setIntValue: limit];
471    }
472    else
473    {
474        if (![fDefaults boolForKey: @"SpeedLimit"])
475        {
476            int realLimit = [check state] ? limit : -1;
477            if (sender == fUploadField)
478                tr_setUploadLimit(fHandle, realLimit);
479            else
480                tr_setDownloadLimit(fHandle, realLimit);
481        }
482       
483        [fDefaults setInteger: limit forKey: key];
484    }
485   
486    NSDictionary * dict = [[NSDictionary alloc] initWithObjectsAndKeys:
487                                    [NSNumber numberWithBool: [check state]], @"Enable",
488                                    [NSNumber numberWithInt: limit], @"Limit",
489                                    type, @"Type", nil];
490    [[NSNotificationCenter defaultCenter] postNotificationName: @"LimitGlobalChange" object: dict];
491}
492
493- (void) setLimitCheck: (id) sender
494{
495    NSString * key;
496    NSTextField * field;
497    if (sender == fUploadCheck)
498    {
499        key = @"CheckUpload";
500        field = fUploadField;
501    }
502    else
503    {
504        key = @"CheckDownload";
505        field = fDownloadField;
506    }
507   
508    BOOL check = [sender state] == NSOnState;
509    [self setLimit: field];
510    [field setEnabled: check];
511   
512    [fDefaults setBool: check forKey: key];
513}
514
515- (void) setLimitEnabled: (BOOL) enable type: (NSString *) type
516{
517    NSButton * check = [type isEqualToString: @"Upload"] ? fUploadCheck : fDownloadCheck;
518    [check setState: enable ? NSOnState : NSOffState];
519    [self setLimitCheck: check];
520}
521
522- (void) setQuickLimit: (int) limit type: (NSString *) type
523{
524    NSButton * check;
525    if ([type isEqualToString: @"Upload"])
526    {
527        [fUploadField setIntValue: limit];
528        check = fUploadCheck;
529    }
530    else
531    {
532        [fDownloadField setIntValue: limit];
533        check = fDownloadCheck;
534    }
535    [check setState: NSOnState];
536    [self setLimitCheck: check];
537}
538
539- (void) enableSpeedLimit: (BOOL) enable
540{
541    if ([fDefaults boolForKey: @"SpeedLimit"] != enable)
542    {
543        [fDefaults setBool: enable forKey: @"SpeedLimit"];
544       
545        if (enable)
546        {
547            tr_setUploadLimit(fHandle, [fDefaults integerForKey: @"SpeedLimitUploadLimit"]);
548            tr_setDownloadLimit(fHandle, [fDefaults integerForKey: @"SpeedLimitDownloadLimit"]);
549        }
550        else
551        {
552            tr_setUploadLimit(fHandle, [fUploadCheck state] ? [fDefaults integerForKey: @"UploadLimit"] : -1);
553            tr_setDownloadLimit(fHandle, [fDownloadCheck state] ? [fDefaults integerForKey: @"DownloadLimit"] : -1);
554        }
555    }
556}
557
558- (void) setSpeedLimit: (id) sender
559{
560    NSString * key = sender == fSpeedLimitUploadField ? @"SpeedLimitUploadLimit" : @"SpeedLimitDownloadLimit";
561
562    int limit = [sender intValue];
563    if (![[sender stringValue] isEqualToString: [NSString stringWithFormat: @"%d", limit]] || limit < 0)
564    {
565        NSBeep();
566        limit = [fDefaults integerForKey: key];
567        [sender setIntValue: limit];
568    }
569    else
570    {
571        if ([fDefaults boolForKey: @"SpeedLimit"])
572        {
573            if (sender == fSpeedLimitUploadField)
574                tr_setUploadLimit(fHandle, limit);
575            else
576                tr_setDownloadLimit(fHandle, limit);
577        }
578       
579        [fDefaults setInteger: limit forKey: key];
580    }
581}
582
583- (void) setAutoSpeedLimitCheck: (id) sender
584{
585    BOOL check = [sender state] == NSOnState;
586   
587    [fDefaults setBool: check forKey: @"SpeedLimitAuto"];
588
589    [self setAutoSpeedLimitHour: fSpeedLimitAutoOnField];
590    [fSpeedLimitAutoOnField setEnabled: check];
591   
592    [self setAutoSpeedLimitHour: fSpeedLimitAutoOffField];
593    [fSpeedLimitAutoOffField setEnabled: check];
594}
595
596- (void) setAutoSpeedLimitHour: (id) sender
597{
598    NSString * key = (sender == fSpeedLimitAutoOnField) ? @"SpeedLimitAutoOnHour" : @"SpeedLimitAutoOffHour";
599
600    int hour = [sender intValue];
601   
602    //allow numbers under ten in the format 0x
603    if (!([[sender stringValue] isEqualToString: [NSString stringWithFormat: @"%d", hour]]
604        || [[sender stringValue] isEqualToString: [NSString stringWithFormat: @"%02d", hour]]) || hour < 0 || hour > 23
605        || [fSpeedLimitAutoOnField intValue] == [fSpeedLimitAutoOffField intValue])
606    {
607        NSBeep();
608        hour = [fDefaults integerForKey: key];
609        [sender setStringValue: [NSString stringWithFormat: @"%02d", hour]];
610    }
611    else
612        [fDefaults setInteger: hour forKey: key];
613   
614    [sender setStringValue: [NSString stringWithFormat: @"%02d", hour]]; //ensure number has 2 digits
615   
616    [[NSNotificationCenter defaultCenter] postNotificationName: @"AutoSpeedLimitChange" object: self];
617}
618
619- (void) setRatio: (id) sender
620{
621    float ratioLimit = [sender floatValue];
622    if (![[sender stringValue] isEqualToString: [NSString stringWithFormat: @"%.2f", ratioLimit]]
623            || ratioLimit < 0)
624    {
625        NSBeep();
626        ratioLimit = [fDefaults floatForKey: @"RatioLimit"];
627        [sender setFloatValue: ratioLimit];
628    }
629    else
630        [fDefaults setFloat: ratioLimit forKey: @"RatioLimit"];
631   
632    NSDictionary * dict = [[NSDictionary alloc] initWithObjectsAndKeys:
633                                [NSNumber numberWithBool: [fRatioCheck state]], @"Enable",
634                                [NSNumber numberWithFloat: ratioLimit], @"Ratio", nil];
635    [[NSNotificationCenter defaultCenter] postNotificationName: @"RatioGlobalChange" object: dict];
636}
637
638- (void) setRatioCheck: (id) sender
639{
640    BOOL check = [sender state] == NSOnState;
641    [self setRatio: fRatioField];
642    [fRatioField setEnabled: check];
643   
644    [fDefaults setBool: check forKey: @"RatioCheck"];
645}
646
647- (void) setRatioEnabled: (BOOL) enable
648{
649    int state = enable ? NSOnState : NSOffState;
650   
651    [fRatioCheck setState: state];
652    [self setRatioCheck: fRatioCheck];
653}
654
655- (void) setQuickRatio: (float) ratioLimit
656{
657    [fRatioField setFloatValue: ratioLimit];
658   
659    [fRatioCheck setState: NSOnState];
660    [self setRatioCheck: fRatioCheck];
661}
662
663- (void) setShowMessage: (id) sender
664{
665    BOOL state = [sender state];
666
667    if (sender == fQuitCheck)
668    {
669        [fDefaults setBool: state forKey: @"CheckQuit"];
670        [fQuitDownloadingCheck setEnabled: state];
671    }
672    else if (sender == fRemoveCheck)
673    {
674        [fDefaults setBool: state forKey: @"CheckRemove"];
675        [fRemoveDownloadingCheck setEnabled: state];
676    }
677    if (sender == fQuitDownloadingCheck)
678        [fDefaults setBool: state forKey: @"CheckQuitDownloading"];
679    else if (sender == fRemoveDownloadingCheck)
680        [fDefaults setBool: state forKey: @"CheckRemoveDownloading"];
681    else;
682}
683
684- (void) setBadge: (id) sender
685{   
686    if (sender == fBadgeDownloadRateCheck)
687        [fDefaults setBool: [sender state] forKey: @"BadgeDownloadRate"];
688    else if (sender == fBadgeUploadRateCheck)
689        [fDefaults setBool: [sender state] forKey: @"BadgeUploadRate"];
690    else;
691   
692    [[NSNotificationCenter defaultCenter] postNotificationName: @"DockBadgeChange" object: self];
693}
694
695- (void) setPlaySound: (id) sender
696{
697    BOOL state = [sender state];
698
699    if (sender == fPlayDownloadSoundCheck)
700    {
701        [fDownloadSoundPopUp setEnabled: state];
702        [fDefaults setBool: state forKey: @"PlayDownloadSound"];
703    }
704    else if (sender == fPlaySeedingSoundCheck)
705    {
706        [fSeedingSoundPopUp setEnabled: state];
707        [fDefaults setBool: state forKey: @"PlaySeedingSound"];
708    }
709    else;
710}
711
712- (void) setSound: (id) sender
713{
714    //play sound when selecting
715    NSString * soundName = [sender titleOfSelectedItem];
716    NSSound * sound;
717    if ((sound = [NSSound soundNamed: soundName]))
718        [sound play];
719
720    if (sender == fDownloadSoundPopUp)
721        [fDefaults setObject: soundName forKey: @"DownloadSound"];
722    else if (sender == fSeedingSoundPopUp)
723        [fDefaults setObject: soundName forKey: @"SeedingSound"];
724    else;
725}
726
727- (void) setUpdate: (id) sender
728{
729    int index = [fUpdatePopUp indexOfSelectedItem];
730    NSTimeInterval seconds;
731    if (index == UPDATE_DAILY)
732    {
733        [fDefaults setObject: @"Daily" forKey: @"UpdateCheck"];
734        seconds = 86400;
735    }
736    else if (index == UPDATE_WEEKLY)
737    {
738        [fDefaults setObject: @"Weekly" forKey: @"UpdateCheck"];
739        seconds = 604800;
740    }
741    else
742    {
743        [fDefaults setObject: @"Never" forKey: @"UpdateCheck"];
744        seconds = 0;
745    }
746
747    [fDefaults setInteger: seconds forKey: @"SUScheduledCheckInterval"];
748    [fUpdater scheduleCheckWithInterval: seconds];
749}
750
751- (void) setStartAtOpen: (id) sender
752{
753    [fDefaults setBool: [sender state] == NSOnState forKey: @"AutoStartDownload"];
754}
755
756- (void) setUseQueue: (id) sender
757{
758    BOOL useQueue = [sender state] == NSOnState;
759   
760    [fDefaults setBool: useQueue forKey: @"Queue"];
761    [self setQueueNumber: fQueueNumberField];
762    [fQueueNumberField setEnabled: useQueue];
763}
764
765- (void) setQueueNumber: (id) sender
766{
767    int queueNumber = [sender intValue];
768    if (![[sender stringValue] isEqualToString: [NSString stringWithInt: queueNumber]] || queueNumber < 1)
769    {
770        NSBeep();
771        queueNumber = [fDefaults integerForKey: @"QueueDownloadNumber"];
772        [sender setIntValue: queueNumber];
773    }
774    else
775        [fDefaults setInteger: queueNumber forKey: @"QueueDownloadNumber"];
776   
777    [[NSNotificationCenter defaultCenter] postNotificationName: @"GlobalStartSettingChange" object: self];
778}
779
780- (void) setMoveTorrent: (id) sender
781{
782    int state = [sender state];
783    if (sender == fCopyTorrentCheck)
784    {
785        [fDefaults setBool: state forKey: @"SavePrivateTorrent"];
786       
787        [fDeleteOriginalTorrentCheck setEnabled: state];
788        if (state == NSOffState)
789        {
790            [fDeleteOriginalTorrentCheck setState: NSOffState];
791            [fDefaults setBool: NO forKey: @"DeleteOriginalTorrent"];
792        }
793    }
794    else
795        [fDefaults setBool: state forKey: @"DeleteOriginalTorrent"];
796}
797
798- (void) setDownloadLocation: (id) sender
799{
800    //Download folder
801    switch ([fFolderPopUp indexOfSelectedItem])
802    {
803        case DOWNLOAD_FOLDER:
804            [fDefaults setObject: @"Constant" forKey: @"DownloadChoice"];
805            break;
806        case DOWNLOAD_TORRENT:
807            [fDefaults setObject: @"Torrent" forKey: @"DownloadChoice"];
808            break;
809        case DOWNLOAD_ASK:
810            [fDefaults setObject: @"Ask" forKey: @"DownloadChoice"];
811            break;
812    }
813}
814
815- (void) checkUpdate
816{
817    [fUpdater checkForUpdates: nil];
818}
819
820- (void) folderSheetShow: (id) sender
821{
822    NSOpenPanel * panel = [NSOpenPanel openPanel];
823
824    [panel setPrompt: @"Select"];
825    [panel setAllowsMultipleSelection: NO];
826    [panel setCanChooseFiles: NO];
827    [panel setCanChooseDirectories: YES];
828    [panel setCanCreateDirectories: YES];
829
830    [panel beginSheetForDirectory: nil file: nil types: nil
831        modalForWindow: [self window] modalDelegate: self didEndSelector:
832        @selector(folderSheetClosed:returnCode:contextInfo:) contextInfo: nil];
833}
834
835- (void) setAutoImport: (id) sender
836{
837    int state = [fAutoImportCheck state];
838    [fDefaults setBool: state forKey: @"AutoImport"];
839    [fImportFolderPopUp setEnabled: state];
840   
841    if (state == NSOnState)
842        [[UKKQueue sharedFileWatcher] addPath: fImportFolder];
843    else
844        [[UKKQueue sharedFileWatcher] removePathFromQueue: fImportFolder];
845   
846    [[NSNotificationCenter defaultCenter] postNotificationName: @"AutoImportSettingChange" object: self];
847}
848
849- (void) importFolderSheetShow: (id) sender
850{
851    NSOpenPanel * panel = [NSOpenPanel openPanel];
852
853    [panel setPrompt: @"Select"];
854    [panel setAllowsMultipleSelection: NO];
855    [panel setCanChooseFiles: NO];
856    [panel setCanChooseDirectories: YES];
857    [panel setCanCreateDirectories: YES];
858
859    [panel beginSheetForDirectory: nil file: nil types: nil
860        modalForWindow: [self window] modalDelegate: self didEndSelector:
861        @selector(importFolderSheetClosed:returnCode:contextInfo:) contextInfo: nil];
862}
863
864- (void) setAutoSize: (id) sender
865{
866    [fDefaults setBool: [sender state] forKey: @"AutoSize"];
867   
868    [[NSNotificationCenter defaultCenter] postNotificationName: @"AutoSizeSettingChange" object: self];
869}
870
871- (void) helpForNetwork: (id) sender
872{
873    [[NSHelpManager sharedHelpManager] openHelpAnchor: @"PortForwarding"
874        inBook: [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleHelpBookName"]];
875}
876
877@end
878
879@implementation PrefsController (Private)
880
881- (void) showGeneralPref: (id) sender
882{
883    [self setPrefView: fGeneralView];
884}
885
886- (void) showTransfersPref: (id) sender
887{
888    [self setPrefView: fTransfersView];
889}
890
891- (void) showBandwidthPref: (id) sender
892{
893    [self setPrefView: fBandwidthView];
894}
895
896- (void) showNetworkPref: (id) sender
897{
898    [self setPrefView: fNetworkView];
899   
900    //make sure progress indicator hides itself
901    if ([fPortStatusImage image])
902        [fPortStatusProgress setDisplayedWhenStopped: NO];
903}
904
905- (void) setPrefView: (NSView *) view
906{
907    NSWindow * window = [self window];
908   
909    if ([window contentView] == view)
910        return;
911   
912    NSRect windowRect = [window frame];
913    int difference = [view frame].size.height - [[window contentView] frame].size.height;
914    windowRect.origin.y -= difference;
915    windowRect.size.height += difference;
916
917    [window setTitle: [fToolbar selectedItemIdentifier]];
918   
919    [view setHidden: YES];
920    [window setContentView: view];
921    [window setFrame: windowRect display: YES animate: YES];
922    [view setHidden: NO];
923}
924
925- (void) folderSheetClosed: (NSOpenPanel *) openPanel returnCode: (int) code contextInfo: (void *) info
926{
927    if (code == NSOKButton)
928    {
929        [fDownloadFolder release];
930        fDownloadFolder = [[[openPanel filenames] objectAtIndex: 0] retain];
931       
932        [fFolderPopUp selectItemAtIndex: DOWNLOAD_FOLDER];
933        [fDefaults setObject: fDownloadFolder forKey: @"DownloadFolder"];
934        [fDefaults setObject: @"Constant" forKey: @"DownloadChoice"];
935       
936        [self updatePopUp];
937    }
938    else
939    {
940        //reset if cancelled
941        NSString * downloadChoice = [fDefaults stringForKey: @"DownloadChoice"];
942        if ([downloadChoice isEqualToString: @"Constant"])
943            [fFolderPopUp selectItemAtIndex: DOWNLOAD_FOLDER];
944        else if ([downloadChoice isEqualToString: @"Torrent"])
945            [fFolderPopUp selectItemAtIndex: DOWNLOAD_TORRENT];
946        else
947            [fFolderPopUp selectItemAtIndex: DOWNLOAD_ASK];
948    }
949}
950
951- (void) updatePopUp
952{
953    //get and resize the icon
954    NSImage * icon = [[NSWorkspace sharedWorkspace] iconForFile: fDownloadFolder];
955    [icon setScalesWhenResized: YES];
956    [icon setSize: NSMakeSize(16.0, 16.0)];
957
958    //update menu item
959    NSMenuItem * menuItem = (NSMenuItem *) [fFolderPopUp itemAtIndex: 0];
960    [menuItem setTitle: [fDownloadFolder lastPathComponent]];
961    [menuItem setImage: icon];
962}
963
964- (void) importFolderSheetClosed: (NSOpenPanel *) openPanel returnCode: (int) code contextInfo: (void *) info
965{
966    if (code == NSOKButton)
967    {
968        UKKQueue * sharedQueue = [UKKQueue sharedFileWatcher];
969        [sharedQueue removePathFromQueue: fImportFolder];
970       
971        [fImportFolder release];
972        fImportFolder = [[[openPanel filenames] objectAtIndex: 0] retain];
973       
974        [fDefaults setObject: fImportFolder forKey: @"AutoImportDirectory"];
975       
976        [self updateImportPopUp];
977       
978        [sharedQueue addPath: fImportFolder];
979       
980        [[NSNotificationCenter defaultCenter] postNotificationName: @"AutoImportSettingChange" object: self];
981    }
982    [fImportFolderPopUp selectItemAtIndex: 0];
983}
984
985- (void) updateImportPopUp
986{
987    //get and resize the icon
988    NSImage * icon = [[NSWorkspace sharedWorkspace] iconForFile: fImportFolder];
989    [icon setScalesWhenResized: YES];
990    [icon setSize: NSMakeSize(16.0, 16.0)];
991
992    //update menu item
993    NSMenuItem * menuItem = (NSMenuItem *) [fImportFolderPopUp itemAtIndex: 0];
994    [menuItem setTitle: [fImportFolder lastPathComponent]];
995    [menuItem setImage: icon];
996}
997
998@end
Note: See TracBrowser for help on using the repository browser.