source: trunk/Cocoa/Pester/Source/PSApplication.m @ 61

Last change on this file since 61 was 61, checked in by Nicholas Riley, 19 years ago

Pester 1.1b1.

PSPowerManager: Fixed delegate method selectors to better reflect what
is going on (Apple's docs in IOKit Fundamentals help with this; the
kIOMessage*Sleep constants are really poorly named).

VERSION: Updated for 1.1b1.

PSSpeechAlert.h: Fixed company name.

PSAlert.[hm]: Added -prepareForAlarm: to support PSWakeAlert.

PSTimer.[hm]: Replacement for NSTimer that works properly across
sleep/wake cycles and will schedule wake timers.

PSAlerts.[hm]: Added -prepareForAlarm: to support PSWakeAlert.

Read Me.rtfd: Updated for 1.1b1.

PSAlarm.[hm]: Added -setWakeUp:, invoke -[PSAlerts prepareForAlarm],
replaced alarm timer NSTimer with PSTimer.

PSApplication.[hm]: Replaced dock update timer NSTimer with PSTimer.
Uncovered some issues, need to fix later. Enable alarm discard for
beta release.

PSWakeAlert.[hm]: Shared alert implementation for wakeup. Doesn't do
anything at trigger time, but uses new preparation interface to work
at alarm set time (should work for repeating alarms too, but I didn't
bother to test...)

PSAlarmSetController.m: Added support for PSWakeAlert. Save default
alert information on quit. Removed debug statements on hide/unhide;
it works fine regardless of whether the app is explicitly hidden or
the window hides itself.

PSAlarms.m: PSTimer support - invoke +[PSTimer setUp] to initialize
timer list.

File size: 8.8 KB
Line 
1//
2//  PSApplication.m
3//  Pester
4//
5//  Created by Nicholas Riley on Fri Oct 11 2002.
6//  Copyright (c) 2002 Nicholas Riley. All rights reserved.
7//
8
9#import "PSApplication.h"
10#import "PSAlarmSetController.h"
11#import "PSAlarmAlertController.h"
12#import "PSAlarmsController.h"
13#import "PSAlarm.h"
14#import "PSAlarms.h"
15#import "PSTimer.h"
16
17@implementation PSApplication
18
19- (void)finishLaunching;
20{
21    appIconImage = [[NSImage imageNamed: @"NSApplicationIcon"] retain];
22    [[NSNotificationCenter defaultCenter] addObserver: [PSAlarmAlertController class] selector: @selector(controllerWithTimerExpiredNotification:) name: PSAlarmTimerExpiredNotification object: nil];
23    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(nextAlarmDidChange:) name: PSAlarmsNextAlarmDidChangeNotification object: nil];
24    // XXX exception handling
25    [PSAlarms setUp];
26    [self setDelegate: self];
27    [super finishLaunching];
28}
29
30- (IBAction)showHelp:(id)sender;
31{
32    [[NSWorkspace sharedWorkspace] openFile: [[NSBundle mainBundle] pathForResource: @"Read Me" ofType: @"rtfd"]];
33}
34
35- (IBAction)stopAlerts:(id)sender;
36{
37    [PSAlarmAlertController stopAlerts: sender];
38}       
39
40- (IBAction)orderFrontSetAlarmPanel:(id)sender;
41{
42    [NSApp activateIgnoringOtherApps: YES];
43    [alarmSetController showWindow: self];
44}
45
46- (IBAction)orderFrontAlarmsPanel:(id)sender;
47{
48    [NSApp activateIgnoringOtherApps: YES];
49    if (alarmsController == nil) {
50        alarmsController = [[PSAlarmsController alloc] init];
51    }
52    [alarmsController showWindow: self];
53}
54
55- (void)_resetUpdateTimer;
56{
57    if (dockUpdateTimer != nil) {
58        [dockUpdateTimer invalidate];
59        [dockUpdateTimer release];
60        dockUpdateInterval = 0;
61        dockUpdateTimer = nil;
62    }
63}
64
65- (void)_setUpdateTimerForInterval:(NSTimeInterval)interval alarm:(PSAlarm *)alarm repeats:(BOOL)repeats;
66{
67    dockUpdateTimer = [PSTimer scheduledTimerWithTimeInterval: interval target: self selector: @selector(_updateDockTile:) userInfo: alarm repeats: repeats];
68    [dockUpdateTimer retain];
69    dockUpdateInterval = interval; // because [timer timeInterval] always returns 0 once set
70}
71
72- (void)_updateDockTile:(PSTimer *)timer;
73{
74    PSAlarm *alarm = [timer userInfo];
75    NSTimeInterval timeRemaining;
76    NSString *tileString;
77    if (timer == nil) alarm = [[PSAlarms allAlarms] nextAlarm];
78    if (alarm == nil) return;
79    tileString = [alarm timeRemainingString];
80    timeRemaining = [alarm timeRemaining]; // want to err on the side of timeRemaining being smaller, otherwise ÇexpiredÈ can appear
81    {
82        NSMutableDictionary *atts = [NSMutableDictionary dictionary];
83        NSSize imageSize = [appIconImage size];
84        NSImage *tile = [[NSImage alloc] initWithSize: imageSize];
85        NSSize textSize;
86        NSPoint textOrigin;
87        NSRect frameRect;
88        float fontSize = 37;
89       
90        do {
91            fontSize -= 1;
92            [atts setObject: [NSFont boldSystemFontOfSize: fontSize] forKey: NSFontAttributeName];
93            textSize = [tileString sizeWithAttributes: atts];
94        } while (textSize.width > imageSize.width - 8);
95
96        textOrigin = NSMakePoint(imageSize.width / 2 - textSize.width / 2,
97                                 imageSize.height / 2 - textSize.height / 2);
98        frameRect = NSInsetRect(NSMakeRect(textOrigin.x, textOrigin.y, textSize.width, textSize.height),
99                                -4, -2);
100       
101        [tile lockFocus];
102        // draw the grayed-out app icon
103        [appIconImage dissolveToPoint: NSZeroPoint fraction: 0.5];
104        // draw the frame
105        [[NSColor colorWithCalibratedWhite: 0.1 alpha: 0.5] set];
106        NSRectFill(frameRect);
107        // draw a gray two-pixel text shadow
108        [atts setObject: [NSColor grayColor] forKey: NSForegroundColorAttributeName];
109        textOrigin.x++; textOrigin.y--;
110        [tileString drawAtPoint: textOrigin withAttributes: atts];
111        textOrigin.x++; textOrigin.y--;
112        [tileString drawAtPoint: textOrigin withAttributes: atts];
113        // draw white text
114        textOrigin.x -= 2; textOrigin.y += 2;
115        [atts setObject: [NSColor whiteColor] forKey: NSForegroundColorAttributeName];
116        [tileString drawAtPoint: textOrigin withAttributes: atts];
117       
118        [tile unlockFocus];
119        [NSApp setApplicationIconImage: tile];
120        [tile release];
121    }
122    // NSLog(@"_updateDockTile > time remaining %@ (%.6lf), last time interval %.6lf", tileString, timeRemaining, dockUpdateInterval);
123    if (timeRemaining > 61) {
124        NSTimeInterval nextUpdate = ((unsigned long long)timeRemaining) % 60;
125        if (nextUpdate <= 1) nextUpdate = 60;
126        [self _resetUpdateTimer];
127        [self _setUpdateTimerForInterval: nextUpdate alarm: alarm repeats: NO];
128        // NSLog(@"_updateDockTile > set timer for %.0lf seconds", nextUpdate);
129    } else if (timer == nil || dockUpdateInterval > 1) {
130        [self _resetUpdateTimer];
131        [self _setUpdateTimerForInterval: 1 alarm: alarm repeats: YES];
132        // NSLog(@"_updateDockTile > set timer for 1 second");
133    } else if (timeRemaining <= 1) {
134        [self _resetUpdateTimer];
135    }
136}
137
138- (void)nextAlarmDidChange:(NSNotification *)notification;
139{
140    PSAlarm *nextAlarm = [notification object];
141    // NSLog(@"nextAlarmDidChange: %@", nextAlarm);
142    [self _resetUpdateTimer];
143    if (nextAlarm == nil) {
144        [NSApp setApplicationIconImage: appIconImage];
145    } else {
146        [self _updateDockTile: nil];
147    }
148}
149
150@end
151
152@implementation PSApplication (NSApplicationDelegate)
153
154- (BOOL)applicationShouldHandleReopen:(NSApplication *)sender hasVisibleWindows:(BOOL)flag;
155{
156    if (!flag && ![[PSAlarms allAlarms] alarmsExpiring]) [alarmSetController showWindow: self];
157    return YES;
158}
159
160- (NSMenu *)applicationDockMenu:(NSApplication *)sender;
161{
162    NSMenu *dockMenu = [[NSMenu alloc] initWithTitle: @""];
163    PSAlarms *alarms = [PSAlarms allAlarms];
164    PSAlarm *nextAlarm = [alarms nextAlarm];
165    NSMenuItem *item;
166    if (nextAlarm == nil) {
167        [dockMenu addItemWithTitle: @"No Pending Alarms" action: nil keyEquivalent: @""];
168    } else {
169        [dockMenu addItemWithTitle: @"Next Alarm" action: nil keyEquivalent: @""];
170        [dockMenu addItemWithTitle: [NSString stringWithFormat: @"   %@", [nextAlarm message]] action: nil keyEquivalent: @""];
171        [dockMenu addItemWithTitle: [NSString stringWithFormat: @"   %@ %@", [nextAlarm shortDateString], [nextAlarm timeString]] action: nil keyEquivalent: @""];
172        [dockMenu addItemWithTitle: [NSString stringWithFormat: @"   Remaining: %@", [nextAlarm timeRemainingString]] action: nil keyEquivalent: @""];
173    }
174    [dockMenu addItem: [NSMenuItem separatorItem]];
175    item = [dockMenu addItemWithTitle: @"Set AlarmÉ" action: @selector(orderFrontSetAlarmPanel:) keyEquivalent: @""];
176    [item setTarget: self];
177    item = [dockMenu addItemWithTitle: [NSString stringWithFormat: @"All Alarms (%d)É", [alarms alarmCount]] action: @selector(orderFrontAlarmsPanel:) keyEquivalent: @""];
178    [item setTarget: self];
179    return [dockMenu autorelease];
180}
181
182@end
183
184@implementation PSApplication (NSApplicationNotifications)
185
186- (void)applicationDidFinishLaunching:(NSNotification *)notification;
187{
188    // XXX import panel will not be frontmost window if you switch to another app while Pester is launching; Mac OS X bug?
189    PSAlarms *allAlarms = [PSAlarms allAlarms];
190    unsigned version1AlarmCount = [allAlarms countOfVersion1Alarms];
191    if (version1AlarmCount > 0) {
192        int answer = NSRunAlertPanel(@"Import alarms from older Pester version?", @"Pester found %u alarm%@ created with an older version. These alarms must be converted for use with this version of Pester, and will be unavailable in previous versions after conversion. New alarms created with this version of Pester will not appear in Pester version 1.1a3 or earlier.",
193                                     @"Import", @"Discard", @"DonÕt Import",
194                                     version1AlarmCount, version1AlarmCount == 1 ? @"" : @"s");
195        switch (answer) {
196            case NSAlertDefaultReturn:
197                NS_DURING
198                    [allAlarms importVersion1Alarms];
199                NS_HANDLER
200                    NSRunAlertPanel(@"Error occurred importing alarms", @"Pester was unable to convert some alarms created with an older version. Those alarms which could be read have been converted. The previous-format alarms have been retained; try using an older version of Pester to read them.\n\n%@", nil, nil, nil, [localException reason]);
201                    NS_VOIDRETURN;
202                NS_ENDHANDLER
203            case NSAlertAlternateReturn:
204                [allAlarms discardVersion1Alarms];
205                break;
206            case NSAlertOtherReturn:
207                break;
208        }
209    }
210}
211
212- (void)applicationWillTerminate:(NSNotification *)notification;
213{
214    [NSApp setApplicationIconImage: appIconImage];
215}
216
217@end
Note: See TracBrowser for help on using the repository browser.