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

Last change on this file since 60 was 60, checked in by Nicholas Riley, 17 years ago

PSScriptAlert.m: Removed reference to NDAppleScriptObject.

PSMovieAlertController.[hm]: Added minimum sizing.

NJRCenteringMovieView.[hm]: Support for centering movie inside its
frame if the frame is too large for the movie; sizes movie
proportionally if necessary (not used in Pester).

NSImage-NJRExtensions.[hm]: Fixed copyright.

Read Me.rtfd: Updated with bug fixes.

PSMovieAlert.m: Note bug in NSMovieView, currently doesn't affect us.

NJRQTMediaPopUpButton.m: Work around background processor use bug when
NSMovieView has movie set but not playing.

NJRVoicePopUpButton.m: Removed obsolete comment.

PSApplication.m: Only show alarm set window on rapp if alerts aren't
expiring.

PSAlarms.[hm]: Added -alarmsExpiring for support of conditional
rapp window open feature in PSApplication.

PSAlarmSetController.m: Stop window update timer and movie playback on
hide, restart timer on activate - fixes background processor usage.

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