source: trunk/Cocoa/Pester/Source/PSAlarmNotifierController.m @ 516

Last change on this file since 516 was 356, checked in by Nicholas Riley, 13 years ago

PSAlarmNotifierController: Fix crashing bug on alarm dismissal by retaining self after window is closed but before alert completes (alert does not retain alarm, but notifier controller does).

File size: 6.2 KB
Line 
1//
2//  PSAlarmNotifierController.m
3//  Pester
4//
5//  Created by Nicholas Riley on Tue Oct 08 2002.
6//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
7//
8
9#import "PSAlarmNotifierController.h"
10#import "PSAlarmAlertController.h"
11#import "PSAlarm.h"
12#import "PSNotifierAlert.h"
13#import "PSSnoozeUntilController.h"
14#import "NJRIntervalField.h"
15
16static NSString * const PSAlarmSnoozeInterval = @"Pester alarm snooze interval"; // NSUserDefaults key
17
18@interface PSAlarmNotifierController (Private)
19
20- (void)update:(id)sender;
21- (void)updateNextDateDisplay:(id)sender;
22
23@end
24
25@implementation PSAlarmNotifierController
26
27// XXX should use NSNonactivatingPanelMask on 10.2?
28
29- (id)initWithAlarm:(PSAlarm *)anAlarm;
30{
31    if ([self initWithWindowNibName: @"Notifier"]) {
32        NSWindow *window = [self window];
33        NSRect frameRect = [window frame];
34        alarm = [anAlarm retain];
35        [messageField setStringValue: [alarm message]];
36        [dateField setStringValue: [alarm dateTimeString]];
37        if (![self setSnoozeInterval: [alarm snoozeInterval]] &&
38            ![self setSnoozeInterval: [[[NSUserDefaults standardUserDefaults] objectForKey: PSAlarmSnoozeInterval] doubleValue]])
39            [self setSnoozeInterval: 15 * 60]; // 15 minutes
40        if ([alarm isRepeating]) {
41            [intervalField setStringValue:
42                [NSString stringWithFormat: @"every %@", [[alarm intervalString] lowercaseString]]];
43            [self updateNextDateDisplay: nil];
44            updateTimer = [NSTimer scheduledTimerWithTimeInterval: 1 target: self selector: @selector(updateNextDateDisplay:) userInfo: nil repeats: YES];
45            frameRect.size = [window maxSize];
46        } else {
47            frameRect.size = [window minSize];
48        }
49        [window setFrame: frameRect display: NO];
50        [window center];
51        [window makeKeyAndOrderFront: nil];
52        [window orderFrontRegardless];
53    }
54    return self;
55}
56
57- (void)dealloc;
58{
59    [alarm release]; alarm = nil;
60    [updateTimer invalidate]; updateTimer = nil;
61    [super dealloc];
62}
63
64- (void)updateNextDateDisplay:(id)sender;
65{
66    if (!canSnooze) {
67        NSString *nextDateTimeString = [alarm nextDateTimeString];
68        if (nextDateTimeString == nil) { // no longer repeating
69            [updateTimer invalidate]; updateTimer = nil;
70        } else {
71            [nextDateField setStringValue: nextDateTimeString];
72        }
73    }
74}
75
76- (void)update:(id)sender;
77{
78    snoozeInterval = [snoozeIntervalField interval];
79    canSnooze = (snoozeInterval > 0);
80    if (canSnooze) [nextDateField setStringValue: @"after snooze"];
81    [snoozeButton setEnabled: canSnooze];
82    [canSnooze ? snoozeButton : okButton setKeyEquivalent: @"\r"];
83    [canSnooze ? okButton : snoozeButton setKeyEquivalent: @""];
84}
85
86- (IBAction)close:(id)sender;
87{
88    [PSAlarmAlertController stopAlerts: sender];
89    [self retain];
90    [self close]; // releases self in windowWillClose:
91    [[PSNotifierAlert alert] completedForAlarm: alarm];
92    [self release];
93}
94
95- (IBAction)snoozeUntil:(NSMenuItem *)sender;
96{
97    [PSSnoozeUntilController snoozeUntilControllerWithNotifierController: self];
98}
99
100- (IBAction)snoozeIntervalUnitsChanged:(NSPopUpButton *)sender;
101{
102    if ([[sender selectedItem] tag] > 0) [self update: nil];
103}
104
105- (NSTimeInterval)snoozeInterval;
106{
107    return snoozeInterval;
108}
109
110- (BOOL)setSnoozeInterval:(NSTimeInterval)interval;
111{
112    snoozeInterval = interval;
113    return [snoozeIntervalField setInterval: interval];
114}
115
116- (IBAction)snooze:(NSButton *)sender;
117{
118    snoozeInterval = [snoozeIntervalField interval];
119    [alarm setSnoozeInterval: snoozeInterval];
120    [[NSUserDefaults standardUserDefaults] setObject: [NSNumber numberWithDouble: snoozeInterval] forKey: PSAlarmSnoozeInterval];
121    [self close: sender];
122}
123
124- (void)snoozeUntilDate:(NSCalendarDate *)date;
125{
126    [alarm setSnoozeInterval: [date timeIntervalSinceNow]];
127    [self close: self];
128}
129
130- (IBAction)stopRepeating:(NSButton *)sender;
131{
132    NSWindow *window = [self window];
133    NSRect frameRect = [window frame];
134    NSSize newSize = [window minSize];
135   
136    [alarm setRepeating: NO];
137    [sender setEnabled: NO];
138    frameRect.origin.y += frameRect.size.height - newSize.height;
139    frameRect.size = newSize;
140    [window setFrame: frameRect display: YES animate: YES];
141}
142
143@end
144
145@implementation PSAlarmNotifierController (NSControlSubclassDelegate)
146
147- (BOOL)control:(NSControl *)control didFailToFormatString:(NSString *)string errorDescription:(NSString *)error;
148{
149    if (control == snoozeIntervalField)
150        [snoozeIntervalField handleDidFailToFormatString: string errorDescription: error label: @"snooze interval"];
151    return NO;
152}
153
154- (void)control:(NSControl *)control didFailToValidatePartialString:(NSString *)string errorDescription:(NSString *)error;
155{
156    // NSLog(@"UPDATING FROM validation");
157    [self update: control]; // switch to snooze if someone types something weird...
158}
159
160- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector;
161{
162    // NSLog(@"UPDATING from textView: %@", NSStringFromSelector(commandSelector));
163    if (commandSelector == @selector(cancel:)) {
164        // if someone just wants the stupid thing to go away and presses escape, donÕt hinder them
165        [self close: control];
166        return YES;
167    }
168    // if someone invokes the default button or switches fields, donÕt override it
169    if (commandSelector == @selector(insertNewline:) ||
170        commandSelector == @selector(insertTab:) ||
171        commandSelector == @selector(insertBacktab:)) return NO;
172    [self update: control]; // ...or if they type a navigation key...
173    return NO; // we donÕt handle it
174}
175
176@end
177
178@implementation PSAlarmNotifierController (NSControlSubclassNotifications)
179
180- (void)controlTextDidChange:(NSNotification *)notification;
181{
182    // NSLog(@"UPDATING FROM controlTextDidChange: %@", [notification object]);
183    [self update: [notification object]]; // ...or if they modify the snooze interval
184}
185
186@end
187
188@implementation PSAlarmNotifierController (NSWindowNotifications)
189
190- (void)windowWillClose:(NSNotification *)notification;
191{
192    // canÕt rely on dealloc to invalidate the timer, because it retains this object
193    [updateTimer invalidate]; updateTimer = nil;
194    [self release]; // in non-document-based apps, this is needed; see docs
195}
196
197@end
Note: See TracBrowser for help on using the repository browser.