source: trunk/Cocoa/AntiRSI/AntiRSI.m @ 329

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

AntiRSI changes for "something" called 1.4, with much of the code but not the same UI as Onne Gorter's released 1.4.

Info.plist, English.lproj/InfoPlist.strings: Updated for 1.4.

AntiRSI.[hm]: Some of Onne Gorter's changes, update checking, "go to Web site" and crediting idle time to work break, and "AntiRSI Help". Most of these are not hooked up in the UI as above. Default to smooth sampling.

AntiRSI.xcodeproj: Build fat (i386/ppc).

English.lproj/MainMenu.nib: Some changes...

File size: 20.1 KB
Line 
1/*
2 author: Onne Gorter
3 
4 This file is part of AntiRSI.
5 
6 AntiRSI is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 
11 AntiRSI is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with AntiRSI; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21#import "AntiRSI.h"
22
23#include <math.h>
24#include <ApplicationServices/ApplicationServices.h>
25
26extern CFTimeInterval CGSSecondsSinceLastInputEvent(unsigned long eventType);
27
28@implementation AntiRSI
29
30// bindings methods
31- (void)setMicro_pause_duration:(float)f
32{
33        micro_pause_duration = round(f);
34        if (s_taking_micro_pause == state) {
35                [progress setMaxValue:micro_pause_duration];
36                [progress setDoubleValue:micro_pause_taking_t];
37        }
38}
39
40- (void)setMicro_pause_period:(float)f
41{       micro_pause_period = 60 * round(f); }
42
43- (void)setWork_break_duration:(float)f
44{   
45        work_break_duration = 60 * round(f);
46        if (s_taking_work_break == state) {
47                [progress setMaxValue:work_break_duration / 60];
48                [progress setDoubleValue:work_break_taking_t / 60 - 0.5];
49        }
50}
51
52- (void)setWork_break_period:(float)f
53{       work_break_period = 60 * round(f); }
54
55- (void)installTimer:(double)interval
56{
57        if (mtimer != nil) {
58                [mtimer invalidate];
59                [mtimer autorelease];
60        }
61        mtimer = [[NSTimer scheduledTimerWithTimeInterval:interval target:self selector:@selector(tick:)
62                                             userInfo:nil repeats:YES] retain];
63}
64
65- (void)setSample_interval:(NSString *)s
66{
67        sample_interval = 1;
68        if ([s isEqualToString:@"Super Smooth"]) sample_interval = 0.1;
69        if ([s isEqualToString:@"Smooth"]) sample_interval = 0.33;
70        if ([s isEqualToString:@"Normal"]) sample_interval = 1;
71        if ([s isEqualToString:@"Low"]) sample_interval = 2;
72       
73        [self installTimer:sample_interval];
74}
75
76- (void)setDraw_dock_image:(BOOL)b
77{
78        draw_dock_image=b;
79        if (!b) {
80                [NSApp setApplicationIconImage:[NSImage imageNamed:@"AntiRSI"]];
81        } else {
82                [self drawDockImage];
83        }
84}
85
86- (void)setBackground:(NSColor *)c
87{
88        [background autorelease];
89        background=[c retain];
90       
91        // make new darkbackground color
92        float r,g,b,a;
93        [background getRed:&r green:&g blue:&b alpha:&a];
94        [darkbackground autorelease];
95        darkbackground=[[NSColor colorWithCalibratedRed:r*0.35 green:g*0.35 blue:b*0.35 alpha:a+0.2] retain];
96       
97        [self drawDockImage];
98}
99
100- (void)setElapsed:(NSColor *)c
101{
102        [elapsed autorelease];
103        elapsed=[c retain];
104        [self drawDockImage];
105}
106
107- (void)setTaking:(NSColor *)c
108{
109        [taking autorelease];
110        taking=[c retain];
111        [self drawDockImage];
112}
113
114// end of bindings
115
116- (void)awakeFromNib
117{
118        // want transparancy
119        [NSColor setIgnoresAlpha:NO];
120       
121        // initial colors
122        elapsed = [[NSColor colorWithCalibratedRed:0.3 green:0.3 blue:0.9 alpha:0.95] retain];
123        taking = [[NSColor colorWithCalibratedRed:0.3 green:0.9 blue:0.3 alpha:0.90] retain];
124        background = [NSColor colorWithCalibratedRed:0.9 green:0.9 blue:0.9 alpha:0.7];
125       
126        //initial values
127        micro_pause_period = 4*60;
128        micro_pause_duration = 13;
129        work_break_period = 50*60;
130        work_break_duration = 8*60;
131        sample_interval = 1;
132       
133        // set current state
134        state = s_normal;
135       
136        // set timers to 0
137        micro_pause_t = 0;
138        work_break_t = 0;
139        micro_pause_taking_t = 0;
140        work_break_taking_t = 0;
141        work_break_taking_cached_t = 0;
142        work_break_taking_cached_date = 0;
143       
144        // initialize dock image
145        dock_image = [[NSImage alloc] initWithSize:NSMakeSize(128,128)];
146        [dock_image setCacheMode:NSImageCacheNever];
147        original_dock_image = [NSImage imageNamed:@"AntiRSI"];
148        draw_dock_image_q = YES;
149       
150        // setup main window that will show either micropause or workbreak
151        main_window = [[NSWindow alloc] initWithContentRect:[view frame]
152                                                                                          styleMask:NSBorderlessWindowMask
153                                                                                                backing:NSBackingStoreBuffered defer:YES];
154        [main_window setBackgroundColor:[NSColor clearColor]];
155        [main_window setLevel:NSStatusWindowLevel];
156        [main_window setAlphaValue:0.85];
157        [main_window setOpaque:NO];
158        [main_window setHasShadow:NO];
159        [main_window setMovableByWindowBackground:YES];
160        [main_window center];
161        [main_window setContentView:view];
162    [progress setEnabled:NO];
163       
164        // initialze history filter
165        h0 = 0;
166        h1 = 0;
167        h2 = 0;
168       
169        // initialize ticks
170        date = [NSDate timeIntervalSinceReferenceDate];
171       
172        // set background now
173        [self setBackground:background];
174       
175        // create initial values
176        NSMutableDictionary* initial = [NSMutableDictionary dictionaryWithCapacity:10];
177        [initial setObject:[NSNumber numberWithFloat:4] forKey:@"micro_pause_period"];
178        [initial setObject:[NSNumber numberWithFloat:13] forKey:@"micro_pause_duration"];
179        [initial setObject:[NSNumber numberWithFloat:50] forKey:@"work_break_period"];
180        [initial setObject:[NSNumber numberWithFloat:8] forKey:@"work_break_duration"];
181        [initial setObject:@"Smooth" forKey:@"sample_interval"];
182        [initial setObject:[NSNumber numberWithBool:YES] forKey:@"draw_dock_image"];
183        [initial setObject:[NSNumber numberWithBool:NO] forKey:@"lock_focus"];
184        [initial setObject:[NSArchiver archivedDataWithRootObject:elapsed] forKey:@"elapsed"];
185        [initial setObject:[NSArchiver archivedDataWithRootObject:taking] forKey:@"taking"];
186        [initial setObject:[NSArchiver archivedDataWithRootObject:background] forKey:@"background"];
187        [[NSUserDefaultsController sharedUserDefaultsController] setInitialValues:initial];
188
189        // bind to defauls controller
190        id dc = [NSUserDefaultsController sharedUserDefaultsController];
191        [self bind:@"micro_pause_period" toObject:dc withKeyPath:@"values.micro_pause_period" options:nil];
192        [self bind:@"micro_pause_duration" toObject:dc withKeyPath:@"values.micro_pause_duration" options:nil];
193        [self bind:@"work_break_period" toObject:dc withKeyPath:@"values.work_break_period" options:nil];
194        [self bind:@"work_break_duration" toObject:dc withKeyPath:@"values.work_break_duration" options:nil];
195        [self bind:@"sample_interval" toObject:dc withKeyPath:@"values.sample_interval" options:nil];
196        [self bind:@"draw_dock_image" toObject:dc withKeyPath:@"values.draw_dock_image" options:nil];
197        [self bind:@"lock_focus" toObject:dc withKeyPath:@"values.lock_focus" options:nil];
198        NSDictionary* unarchive = [NSDictionary dictionaryWithObject:NSUnarchiveFromDataTransformerName forKey:@"NSValueTransformerName"];
199        [self bind:@"elapsed" toObject:dc withKeyPath:@"values.elapsed" options:unarchive];
200        [self bind:@"taking" toObject:dc withKeyPath:@"values.taking" options:unarchive];
201        [self bind:@"background" toObject:dc withKeyPath:@"values.background" options:unarchive];
202
203        // alert every binding
204        [[NSUserDefaultsController sharedUserDefaultsController] revert:self];
205
206        // start the timer
207        [self installTimer:sample_interval];
208       
209        // about dialog
210        [version setStringValue:[NSString stringWithFormat:@"Version %@", sVersion]];
211}
212
213// tick every second and update status
214- (void)tick:(NSTimer *)timer
215{
216        // calculate time since last tick
217        double new_date = [NSDate timeIntervalSinceReferenceDate];
218        double tick_time = new_date - date;
219        date = new_date;
220       
221        // check if we are still on track of normal time, otherwise we might have slept or something
222        if (tick_time > work_break_duration) {
223                // set timers to 0
224                micro_pause_t = 0;
225                work_break_t = 0;
226                micro_pause_taking_t = micro_pause_duration;
227                work_break_taking_t = work_break_duration;
228                if (s_normal != state) {
229                        [self endBreak];
230                }
231                // and do stuff on next tick
232                return;
233        }
234       
235        // just did a whole micropause beyond normal time
236        if (tick_time > micro_pause_duration && s_taking_work_break != state) {
237                // set micro_pause timers to 0
238                micro_pause_t = 0;
239                micro_pause_taking_t = micro_pause_duration;
240                if (s_normal != state) {
241                        [self endBreak];
242                }
243                // and do stuff on next tick
244                return;
245        }
246       
247        // get idle time in seconds
248        CFTimeInterval idle_time = CGSSecondsSinceLastInputEvent(kCGAnyInputEventType);
249    // CFTimeInterval cgs_idle_time = idle_time;
250    // from other people's reverse engineering of this function, on MDD G4s this can return a large positive number when input is in progress
251    if (idle_time >= 18446744000.0) {
252        idle_time = 0;
253    } else if (CGEventSourceSecondsSinceLastEventType != NULL) {
254                CGEventType eventTypes[] = { kCGEventLeftMouseDown, kCGEventLeftMouseUp, kCGEventRightMouseDown, kCGEventRightMouseUp, kCGEventMouseMoved, kCGEventLeftMouseDragged, kCGEventRightMouseDragged, kCGEventKeyDown, kCGEventKeyUp, kCGEventFlagsChanged, kCGEventScrollWheel, kCGEventTabletPointer, kCGEventTabletProximity, kCGEventOtherMouseDown, kCGEventOtherMouseUp, kCGEventOtherMouseDragged, kCGEventNull };
255        CFTimeInterval event_idle_time;
256        idle_time = DBL_MAX;
257        for (CGEventType *eventType = eventTypes ; *eventType != kCGEventNull ; eventType++) {
258            event_idle_time = CGEventSourceSecondsSinceLastEventType(kCGEventSourceStateCombinedSessionState, *eventType);
259            if (event_idle_time < idle_time) idle_time = event_idle_time;
260        }
261    }
262    // NSLog(@"CGEventSource %.2f, CGS %.2f", idle_time, cgs_idle_time);
263   
264        // calculate slack, this gives a sort of 3 history filtered idea.
265        BOOL slack = (h2 + h1 + h0 > 15);
266       
267        // if new event comes in history bumps up
268        if (h0 >= idle_time || idle_time < sample_interval) {
269                h2 = h1;
270                h1 = h0;
271        }
272        h0 = idle_time;
273       
274        switch (state) {
275                case s_normal:
276                        // idle_time needs to be at least 0.3 * micro_pause_duration before kicking in
277                        // but we cut the user some slack based on previous idle_times
278                        if (idle_time <= micro_pause_duration * 0.3 && !slack) {
279                                micro_pause_t += tick_time;
280                                work_break_t += tick_time;
281                                micro_pause_taking_t = 0;
282                                if (work_break_taking_t > 0) {
283                                        work_break_taking_cached_t = work_break_taking_t;
284                                        work_break_taking_cached_date = date;
285                                }
286                                work_break_taking_t = 0;
287                        } else if (micro_pause_t > 0) {
288                        // oke, leaway is over, increase micro_pause_taking_t unless micro_pause is already over
289                                //micro_pause_t stays put
290                                work_break_t += tick_time;
291                                micro_pause_taking_t += tick_time;
292                                work_break_taking_t = 0;
293                        }
294                       
295                        // if micro_pause_taking_t is above micro_pause_duration, then micro pause is over,
296                        // if still idleing workbreak_taking_t kicks in unless it is already over
297                        if (micro_pause_taking_t >= micro_pause_duration && work_break_t > 0) {
298                                work_break_taking_t += tick_time;
299                                micro_pause_t = 0;
300                        }
301                       
302                        // if work_break_taking_t is above work_break_duration, then work break is over
303                        if (work_break_taking_t >= work_break_duration) {
304                                micro_pause_t = 0;
305                                work_break_t = 0;
306                                // micro_pause_taking_t stays put
307                                // work_break_taking_t stays put
308                        }
309               
310                        // if user needs to take a micro pause
311                        if (micro_pause_t >= micro_pause_period) {
312                                // anticipate next workbreak by not issuing this micro_pause ...
313                                if (work_break_t > work_break_period - (micro_pause_period / 2)) {
314                                        work_break_t = work_break_period;
315                                        [self doWorkBreak];
316                                } else {
317                                        [self doMicroPause];
318                                }
319                        }
320                       
321                        // if user needs to take a work break
322                        if (work_break_t >= work_break_period) {
323                                // stop micro_pause stuff
324                                micro_pause_t = 0;
325                                micro_pause_taking_t = micro_pause_duration;
326                                // and display window
327                                [self doWorkBreak];
328                        }
329                break;
330
331                // taking a micro pause with window
332                case s_taking_micro_pause:
333                        // continue updating timers
334                        micro_pause_taking_t += tick_time;
335                        work_break_t += tick_time;
336                       
337                        // if we don't break, or interrupt the break, reset it
338                        if (idle_time < 1 && !slack) {
339                                micro_pause_taking_t = 0;
340                        }
341                               
342                        // update window
343                        [progress setDoubleValue:micro_pause_taking_t];
344                        [self drawTimeLeft:micro_pause_duration - micro_pause_taking_t];
345                        [self drawNextBreak:work_break_period - work_break_t];
346
347                        // if user likes to be interrupted
348                        if (lock_focus) {
349                                [NSApp activateIgnoringOtherApps:YES];
350                                [main_window makeKeyAndOrderFront:self];
351                        }
352                       
353                        // check if we done enough
354                        if (micro_pause_taking_t > micro_pause_duration) {
355                                micro_pause_t = 0;
356                                [self endBreak];
357                        }
358               
359                        // if workbreak must be run ...
360                        if (work_break_t >= work_break_period) {
361                                // stop micro_pause stuff
362                                micro_pause_t = 0;
363                                micro_pause_taking_t = micro_pause_duration;
364                                // and display window
365                                [self doWorkBreak];
366                        } else {
367                double slip = (micro_pause_duration - micro_pause_taking_t) - (int)(micro_pause_duration - micro_pause_taking_t);
368                [self installTimer: slip < 0.1 ? 1 : slip];
369            }
370                        break;
371               
372                // taking a work break with window
373                case s_taking_work_break:
374                        // increase work_break_taking_t
375                        if (idle_time >= 2 || work_break_taking_t < 3) {
376                                work_break_taking_t += tick_time;
377                        }
378                       
379                        // draw window
380                        [progress setDoubleValue:work_break_taking_t / 60 - 0.5];
381                        [self drawTimeLeft:work_break_duration - work_break_taking_t];
382                        [self drawNextBreak:work_break_period + work_break_duration - work_break_taking_t];
383                       
384                        // if user likes to be interrupted
385                        if (lock_focus) {
386                                [NSApp activateIgnoringOtherApps:YES];
387                                [main_window makeKeyAndOrderFront:self];
388                        }
389
390                        // and check if we done enough
391                        if (work_break_taking_t > work_break_duration) {
392                                micro_pause_t = 0;
393                                micro_pause_taking_t = micro_pause_duration;
394                                work_break_t = 0;
395                                work_break_taking_t = work_break_duration;
396                                [self endBreak];
397                        } else {
398                double slip = (work_break_duration - work_break_taking_t) - (int)(work_break_duration - work_break_taking_t);
399                [self installTimer: slip < 0.1 ? 1 : slip];
400            }
401                        break;
402        }
403       
404        // draw dock image
405        if (draw_dock_image) [self drawDockImage];
406}
407
408// draw the dock icon
409- (void)drawDockImage
410{
411        [dock_image lockFocus];
412       
413        // clear all
414        [[NSColor clearColor] set]; 
415        NSRectFill(NSMakeRect(0,0,127,127));
416       
417        NSBezierPath* p;
418        float end;
419       
420        //draw background circle
421        [darkbackground set];
422        p =[NSBezierPath bezierPathWithOvalInRect:NSMakeRect(6,6,115,115)];
423        [p setLineWidth:4];
424        [p stroke];
425       
426        //fill
427        [background set];
428        [[NSBezierPath bezierPathWithOvalInRect:NSMakeRect(8,8,111,111)] fill];
429       
430        //put dot in middle
431        [darkbackground set];
432        [[NSBezierPath bezierPathWithOvalInRect:NSMakeRect(59,59,9,9)] fill];
433
434        // reuse this one
435        p = [NSBezierPath bezierPath];
436
437        // draw work_break
438        [elapsed set];
439        end = 360 - (360.0 / work_break_period * work_break_t - 90);
440        if (end <= 90) end=90.1;
441        [p appendBezierPathWithArcWithCenter:NSMakePoint(63.5, 63.5) radius:40 startAngle:90 endAngle:end clockwise:YES];
442        [p setLineWidth:22];
443        [p stroke];
444       
445        // draw work break taking
446        [taking set];
447        [p removeAllPoints];
448        end = 360 - (360.0 / work_break_duration * work_break_taking_t - 90);
449        if (end <= 90) end=90.1;
450        [p appendBezierPathWithArcWithCenter:NSMakePoint(63.5, 63.5) radius:40 startAngle:90 endAngle:end clockwise:YES];
451        [p setLineWidth:18];
452        [p stroke];
453       
454        // draw micro pause
455        [elapsed set];
456        [p removeAllPoints];
457        end = 360 - (360.0 / micro_pause_period * micro_pause_t - 90);
458        if (end <= 90) end = 90.1;
459        [p appendBezierPathWithArcWithCenter:NSMakePoint(63.5, 63.5) radius:17 startAngle:90 endAngle:end clockwise:YES];
460        [p setLineWidth:22];
461        [p stroke];
462       
463        // draw micro pause taking
464        [taking set];
465        [p removeAllPoints];
466        end = 360 - (360.0 / micro_pause_duration * micro_pause_taking_t - 90);
467        if (end <= 90) end = 90.1;
468        [p appendBezierPathWithArcWithCenter:NSMakePoint(63.5, 63.5) radius:17 startAngle:90 endAngle:end clockwise:YES];
469        [p setLineWidth:18];
470        [p stroke];
471       
472        [dock_image unlockFocus];
473
474        // and set it in the dock check draw_dock_image one last time ...
475        if (draw_dock_image_q) [NSApp setApplicationIconImage:dock_image];
476}
477
478// done with micro pause or work break
479- (void)endBreak
480{
481        [main_window orderOut:NULL];
482        state = s_normal;
483        // reset time interval to user's choice
484        [self installTimer:sample_interval];
485}
486
487// display micro_pause window with appropriate widgets and progress bar
488- (void)doMicroPause
489{
490        micro_pause_taking_t = 0;
491        [status setStringValue:@"Micro Pause"];
492        [progress setMaxValue:micro_pause_duration];
493        [progress setDoubleValue:micro_pause_taking_t];
494    [progress setWarningValue: 1];
495    [progress setCriticalValue: micro_pause_duration];
496        [postpone setHidden:YES];
497        state = s_taking_micro_pause;
498        [self tick: nil];
499        [main_window center];
500        [main_window orderFrontRegardless];
501}
502
503// display work_break window with appropriate widgets and progress bar
504- (void)doWorkBreak
505{
506        work_break_taking_t = 0;
507        // incase you were already having an implicit work break and clicked the take work break now button
508        // not more then 20 seconds ago we took a natural break longer then 0.2 * normal work break duration
509        if (date - work_break_taking_cached_date < 20 && work_break_taking_cached_t > work_break_duration * 0.2) {
510                work_break_taking_t = work_break_taking_cached_t;
511        }
512        [status setStringValue:@"Work Break"];
513        [progress setMaxValue:work_break_duration / 60];
514        [progress setDoubleValue:work_break_taking_t / 60 - 0.5];
515    [progress setWarningValue: 0];
516    [progress setCriticalValue: 0.4];
517        [postpone setHidden:NO];
518        state = s_taking_work_break;
519    [self tick: nil];
520        [main_window center];
521        [main_window orderFrontRegardless];
522}
523
524// diplays time left
525- (void)drawTimeLeft:(double)seconds
526{
527        [time setStringValue:[NSString stringWithFormat:@"%d:%02d", lrint(seconds) / 60, lrint(seconds) % 60]];
528}
529
530// displays next break
531- (void)drawNextBreak:(int)seconds
532{
533        int minutes = round(seconds / 60.0) ;
534       
535        // nice hours, minutes ...
536        if (minutes > 60) {
537                [next_break setStringValue:[NSString stringWithFormat:@"next break in %d:%02d hours",
538                        minutes / 60, minutes % 60]];
539        } else {
540                [next_break setStringValue:[NSString stringWithFormat:@"next break in %d minutes", minutes]];
541        }
542}
543
544// goto website
545- (IBAction)gotoWebsite:(id)sender
546{
547        [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:sURL]];
548}
549
550// check for update
551- (IBAction)checkForUpdate:(id)sender
552{
553        NSString *latest_version =
554        [NSString stringWithContentsOfURL: [NSURL URLWithString:sLatestVersionURL]];
555       
556        if (latest_version == Nil) latest_version = @"";
557        latest_version = [latest_version stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
558       
559        if ([latest_version length] == 0) {
560                NSRunInformationalAlertPanel(
561                        @"Unable to Determine",
562                        @"Unable to determine the latest AntiRSI version number.",
563                        @"Ok", nil, nil);
564        } else if ([latest_version compare:sVersion] == NSOrderedDescending) {
565                int r = NSRunInformationalAlertPanel(
566                        @"New Version",
567                        [NSString stringWithFormat:@"A new version (%@) of AntiRSI is available; would you like to go to the website now?", latest_version],
568                        @"Goto Website", @"Cancel", nil);
569                if (r == NSOKButton) {
570                        [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:sURL]];
571                }
572    } else {
573        NSRunInformationalAlertPanel(
574                        @"No Update Available",
575                        @"This is the latest version of AntiRSI.",
576                        @"OK", nil, nil);
577    }
578}
579
580// stop work break and postpone by 10 minutes
581- (IBAction)postpone:(id)sender
582{
583        if (s_taking_work_break == state) {
584                micro_pause_t = 0;
585                micro_pause_taking_t = 0;
586                work_break_taking_t = 0;
587                work_break_taking_cached_t = 0;
588                work_break_t -= 10*60; // decrease with 10 minutes
589                if (work_break_t < 0) work_break_t = 0;
590                [self endBreak];
591        }
592}
593
594- (IBAction)breakNow:(id)sender
595{
596        [self doWorkBreak];
597}
598
599// validate menu items
600- (BOOL)validateMenuItem:(NSMenuItem *)anItem
601{
602        if ([[anItem title] isEqualToString:@"Take Break Now"] && state == s_normal) {
603                return YES;
604        }
605       
606        if ([[anItem title] isEqualToString:@"Postpone Break"] && state == s_taking_work_break) {
607                return YES;
608        }
609       
610        if ([[anItem title] isEqualToString:@"AntiRSI Help"]) {
611                return YES;
612        }
613       
614        return NO;
615}
616
617// we are delegate of NSApplication, so we can restore the icon on quit.
618- (void)applicationWillTerminate:(NSNotification *)aNotification
619{
620        // make sure timer doesn't tick once more ...
621        draw_dock_image_q = NO;
622        [mtimer invalidate];
623        [mtimer autorelease];
624        mtimer = nil;
625        [dock_image release];
626        // stupid fix for icon beeing restored ... it is not my fault,
627        // the dock or NSImage or setApplicationIconImage seem to be caching or taking
628        // snapshot or something ... !
629        [NSApp setApplicationIconImage:original_dock_image];
630        [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
631        [NSApp setApplicationIconImage:original_dock_image];
632
633}
634
635@end
636
Note: See TracBrowser for help on using the repository browser.