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

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

Info.plist: Updated for 1.4njr1.

AntiRSI.xcodeproj: Disable prebinding.

AntiRSI.[hm]: Added session time support; consolidated break window update drawing; fixed -[AntiRSI validateMenuItem:] so it compares selectors rather than titles.

English.lproj/MainMenu.nib: Redid preferences window, menu bar, dock menu, etc.

File size: 21.0 KB
RevLine 
[325]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
[328]26extern CFTimeInterval CGSSecondsSinceLastInputEvent(unsigned long eventType);
[325]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;
[329]141 work_break_taking_cached_t = 0;
142 work_break_taking_cached_date = 0;
[325]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"];
[329]181 [initial setObject:@"Smooth" forKey:@"sample_interval"];
[325]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];
[329]208
209 // about dialog
210 [version setStringValue:[NSString stringWithFormat:@"Version %@", sVersion]];
[325]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
[329]235 // just did a whole micropause beyond normal time
[325]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
[328]248 CFTimeInterval idle_time = CGSSecondsSinceLastInputEvent(kCGAnyInputEventType);
249 // CFTimeInterval cgs_idle_time = idle_time;
[325]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) {
[328]252 idle_time = 0;
[325]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 };
[328]255 CFTimeInterval event_idle_time;
256 idle_time = DBL_MAX;
[325]257 for (CGEventType *eventType = eventTypes ; *eventType != kCGEventNull ; eventType++) {
[328]258 event_idle_time = CGEventSourceSecondsSinceLastEventType(kCGEventSourceStateCombinedSessionState, *eventType);
259 if (event_idle_time < idle_time) idle_time = event_idle_time;
[325]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;
[330]281 if (idle_time < 1) {
282 session_t += tick_time;
283 }
[325]284 micro_pause_taking_t = 0;
[329]285 if (work_break_taking_t > 0) {
286 work_break_taking_cached_t = work_break_taking_t;
287 work_break_taking_cached_date = date;
288 }
[325]289 work_break_taking_t = 0;
290 } else if (micro_pause_t > 0) {
291 // oke, leaway is over, increase micro_pause_taking_t unless micro_pause is already over
292 //micro_pause_t stays put
293 work_break_t += tick_time;
294 micro_pause_taking_t += tick_time;
295 work_break_taking_t = 0;
296 }
297
298 // if micro_pause_taking_t is above micro_pause_duration, then micro pause is over,
299 // if still idleing workbreak_taking_t kicks in unless it is already over
300 if (micro_pause_taking_t >= micro_pause_duration && work_break_t > 0) {
301 work_break_taking_t += tick_time;
302 micro_pause_t = 0;
303 }
304
305 // if work_break_taking_t is above work_break_duration, then work break is over
306 if (work_break_taking_t >= work_break_duration) {
307 micro_pause_t = 0;
308 work_break_t = 0;
[329]309 // micro_pause_taking_t stays put
[325]310 // work_break_taking_t stays put
311 }
312
313 // if user needs to take a micro pause
314 if (micro_pause_t >= micro_pause_period) {
315 // anticipate next workbreak by not issuing this micro_pause ...
316 if (work_break_t > work_break_period - (micro_pause_period / 2)) {
317 work_break_t = work_break_period;
318 [self doWorkBreak];
319 } else {
320 [self doMicroPause];
321 }
322 }
323
324 // if user needs to take a work break
325 if (work_break_t >= work_break_period) {
326 // stop micro_pause stuff
327 micro_pause_t = 0;
328 micro_pause_taking_t = micro_pause_duration;
329 // and display window
330 [self doWorkBreak];
331 }
332 break;
333
334 // taking a micro pause with window
335 case s_taking_micro_pause:
336 // continue updating timers
337 micro_pause_taking_t += tick_time;
338 work_break_t += tick_time;
339
340 // if we don't break, or interrupt the break, reset it
341 if (idle_time < 1 && !slack) {
342 micro_pause_taking_t = 0;
[330]343 session_t += tick_time;
[325]344 }
345
346 // update window
[330]347 [self updateBreakWindowDuration:micro_pause_duration progress:micro_pause_taking_t
348 nextBreak:work_break_period - work_break_t];
[325]349
350 // check if we done enough
351 if (micro_pause_taking_t > micro_pause_duration) {
352 micro_pause_t = 0;
353 [self endBreak];
354 }
355
356 // if workbreak must be run ...
357 if (work_break_t >= work_break_period) {
358 // stop micro_pause stuff
359 micro_pause_t = 0;
360 micro_pause_taking_t = micro_pause_duration;
361 // and display window
362 [self doWorkBreak];
363 } else {
364 double slip = (micro_pause_duration - micro_pause_taking_t) - (int)(micro_pause_duration - micro_pause_taking_t);
365 [self installTimer: slip < 0.1 ? 1 : slip];
366 }
367 break;
368
369 // taking a work break with window
370 case s_taking_work_break:
371 // increase work_break_taking_t
372 if (idle_time >= 2 || work_break_taking_t < 3) {
373 work_break_taking_t += tick_time;
[330]374 } else if (idle_time < 1) {
375 session_t += tick_time;
376 }
[325]377
378 // draw window
[330]379 [self updateBreakWindowDuration:work_break_duration progress:work_break_taking_t
380 nextBreak:work_break_period + work_break_duration - work_break_taking_t];
[325]381
382 // and check if we done enough
383 if (work_break_taking_t > work_break_duration) {
384 micro_pause_t = 0;
385 micro_pause_taking_t = micro_pause_duration;
386 work_break_t = 0;
387 work_break_taking_t = work_break_duration;
388 [self endBreak];
389 } else {
390 double slip = (work_break_duration - work_break_taking_t) - (int)(work_break_duration - work_break_taking_t);
391 [self installTimer: slip < 0.1 ? 1 : slip];
392 }
393 break;
394 }
395
396 // draw dock image
397 if (draw_dock_image) [self drawDockImage];
398}
399
[330]400// dock image
401- (NSMenu *)applicationDockMenu:(NSApplication *)sender;
402{
403 [session_time_item setTitle:[self sessionTimeString]];
404 return dock_menu;
405}
406
[325]407// draw the dock icon
408- (void)drawDockImage
409{
410 [dock_image lockFocus];
411
412 // clear all
413 [[NSColor clearColor] set];
414 NSRectFill(NSMakeRect(0,0,127,127));
415
416 NSBezierPath* p;
417 float end;
418
419 //draw background circle
420 [darkbackground set];
421 p =[NSBezierPath bezierPathWithOvalInRect:NSMakeRect(6,6,115,115)];
422 [p setLineWidth:4];
423 [p stroke];
424
425 //fill
426 [background set];
427 [[NSBezierPath bezierPathWithOvalInRect:NSMakeRect(8,8,111,111)] fill];
428
429 //put dot in middle
430 [darkbackground set];
431 [[NSBezierPath bezierPathWithOvalInRect:NSMakeRect(59,59,9,9)] fill];
432
433 // reuse this one
434 p = [NSBezierPath bezierPath];
435
436 // draw work_break
437 [elapsed set];
438 end = 360 - (360.0 / work_break_period * work_break_t - 90);
439 if (end <= 90) end=90.1;
440 [p appendBezierPathWithArcWithCenter:NSMakePoint(63.5, 63.5) radius:40 startAngle:90 endAngle:end clockwise:YES];
441 [p setLineWidth:22];
442 [p stroke];
443
444 // draw work break taking
445 [taking set];
446 [p removeAllPoints];
447 end = 360 - (360.0 / work_break_duration * work_break_taking_t - 90);
448 if (end <= 90) end=90.1;
449 [p appendBezierPathWithArcWithCenter:NSMakePoint(63.5, 63.5) radius:40 startAngle:90 endAngle:end clockwise:YES];
450 [p setLineWidth:18];
451 [p stroke];
452
453 // draw micro pause
454 [elapsed set];
455 [p removeAllPoints];
456 end = 360 - (360.0 / micro_pause_period * micro_pause_t - 90);
457 if (end <= 90) end = 90.1;
458 [p appendBezierPathWithArcWithCenter:NSMakePoint(63.5, 63.5) radius:17 startAngle:90 endAngle:end clockwise:YES];
459 [p setLineWidth:22];
460 [p stroke];
461
462 // draw micro pause taking
463 [taking set];
464 [p removeAllPoints];
465 end = 360 - (360.0 / micro_pause_duration * micro_pause_taking_t - 90);
466 if (end <= 90) end = 90.1;
467 [p appendBezierPathWithArcWithCenter:NSMakePoint(63.5, 63.5) radius:17 startAngle:90 endAngle:end clockwise:YES];
468 [p setLineWidth:18];
469 [p stroke];
470
471 [dock_image unlockFocus];
472
473 // and set it in the dock check draw_dock_image one last time ...
474 if (draw_dock_image_q) [NSApp setApplicationIconImage:dock_image];
475}
476
477// done with micro pause or work break
478- (void)endBreak
479{
480 [main_window orderOut:NULL];
481 state = s_normal;
482 // reset time interval to user's choice
483 [self installTimer:sample_interval];
484}
485
486// display micro_pause window with appropriate widgets and progress bar
487- (void)doMicroPause
488{
489 micro_pause_taking_t = 0;
[328]490 [status setStringValue:@"Micro Pause"];
[325]491 [progress setMaxValue:micro_pause_duration];
492 [progress setDoubleValue:micro_pause_taking_t];
493 [progress setWarningValue: 1];
494 [progress setCriticalValue: micro_pause_duration];
495 [postpone setHidden:YES];
496 state = s_taking_micro_pause;
497 [self tick: nil];
498 [main_window center];
499 [main_window orderFrontRegardless];
500}
501
502// display work_break window with appropriate widgets and progress bar
503- (void)doWorkBreak
504{
505 work_break_taking_t = 0;
[329]506 // incase you were already having an implicit work break and clicked the take work break now button
507 // not more then 20 seconds ago we took a natural break longer then 0.2 * normal work break duration
508 if (date - work_break_taking_cached_date < 20 && work_break_taking_cached_t > work_break_duration * 0.2) {
509 work_break_taking_t = work_break_taking_cached_t;
510 }
[328]511 [status setStringValue:@"Work Break"];
[325]512 [progress setMaxValue:work_break_duration / 60];
513 [progress setDoubleValue:work_break_taking_t / 60 - 0.5];
514 [progress setWarningValue: 0];
515 [progress setCriticalValue: 0.4];
516 [postpone setHidden:NO];
517 state = s_taking_work_break;
518 [self tick: nil];
519 [main_window center];
520 [main_window orderFrontRegardless];
521}
522
[330]523- (NSString *)sessionTimeString;
[325]524{
[330]525 return [NSString stringWithFormat:@"Session: %d:%02d:%02d", (int)session_t / 3600, (int)session_t / 60, lrint(session_t) % 60];
[325]526}
527
[330]528- (void)updateBreakWindowDuration:(double)duration progress:(double)progress_t nextBreak:(double)nextBreak;
[325]529{
[330]530 // progress
531 [progress setDoubleValue:duration >= 60 ? (progress_t / 60 - 0.5) : progress_t];
532
533 // time left
534 double timeLeft = duration - progress_t;
535 [time setStringValue:[NSString stringWithFormat:@"%d:%02d", (int)timeLeft / 60, lrint(timeLeft) % 60]];
536
537 // cumulative typing time in this session (e.g. today)
538 [session_time setStringValue:[self sessionTimeString]];
539
540 // next break
541 int minutes = round(nextBreak / 60.0);
[325]542
543 // nice hours, minutes ...
544 if (minutes > 60) {
545 [next_break setStringValue:[NSString stringWithFormat:@"next break in %d:%02d hours",
546 minutes / 60, minutes % 60]];
547 } else {
548 [next_break setStringValue:[NSString stringWithFormat:@"next break in %d minutes", minutes]];
549 }
[330]550
551 // if user likes to be interrupted
552 if (lock_focus) {
553 [NSApp activateIgnoringOtherApps:YES];
554 [main_window makeKeyAndOrderFront:self];
555 }
[325]556}
557
[329]558// goto website
559- (IBAction)gotoWebsite:(id)sender
560{
561 [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:sURL]];
562}
563
564// check for update
565- (IBAction)checkForUpdate:(id)sender
566{
567 NSString *latest_version =
568 [NSString stringWithContentsOfURL: [NSURL URLWithString:sLatestVersionURL]];
569
570 if (latest_version == Nil) latest_version = @"";
571 latest_version = [latest_version stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
572
573 if ([latest_version length] == 0) {
574 NSRunInformationalAlertPanel(
575 @"Unable to Determine",
576 @"Unable to determine the latest AntiRSI version number.",
577 @"Ok", nil, nil);
578 } else if ([latest_version compare:sVersion] == NSOrderedDescending) {
579 int r = NSRunInformationalAlertPanel(
580 @"New Version",
581 [NSString stringWithFormat:@"A new version (%@) of AntiRSI is available; would you like to go to the website now?", latest_version],
582 @"Goto Website", @"Cancel", nil);
583 if (r == NSOKButton) {
584 [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:sURL]];
585 }
586 } else {
587 NSRunInformationalAlertPanel(
588 @"No Update Available",
589 @"This is the latest version of AntiRSI.",
590 @"OK", nil, nil);
591 }
592}
593
[325]594// stop work break and postpone by 10 minutes
595- (IBAction)postpone:(id)sender
596{
597 if (s_taking_work_break == state) {
598 micro_pause_t = 0;
599 micro_pause_taking_t = 0;
600 work_break_taking_t = 0;
[329]601 work_break_taking_cached_t = 0;
[325]602 work_break_t -= 10*60; // decrease with 10 minutes
603 if (work_break_t < 0) work_break_t = 0;
604 [self endBreak];
605 }
606}
607
608- (IBAction)breakNow:(id)sender
609{
610 [self doWorkBreak];
611}
612
[330]613- (IBAction)resetSession:(id)sender;
614{
615 if (s_normal != state) {
616 [self endBreak];
617 }
618 session_t = 0;
619}
620
[325]621// validate menu items
622- (BOOL)validateMenuItem:(NSMenuItem *)anItem
623{
[330]624 if ([anItem action] == @selector(breakNow:) && state == s_normal)
[325]625 return YES;
626
[330]627 if ([anItem action] == @selector(postpone:) && state == s_taking_work_break)
[325]628 return YES;
[330]629
630 if ([anItem action] == @selector(resetSession:))
[329]631 return YES;
[330]632
633 if ([anItem action] == @selector(gotoWebsite:))
634 return YES;
635
636 if ([anItem action] == @selector(checkForUpdate:))
637 return YES;
638
[325]639 return NO;
640}
641
642// we are delegate of NSApplication, so we can restore the icon on quit.
643- (void)applicationWillTerminate:(NSNotification *)aNotification
644{
645 // make sure timer doesn't tick once more ...
646 draw_dock_image_q = NO;
647 [mtimer invalidate];
648 [mtimer autorelease];
649 mtimer = nil;
650 [dock_image release];
651 // stupid fix for icon beeing restored ... it is not my fault,
652 // the dock or NSImage or setApplicationIconImage seem to be caching or taking
653 // snapshot or something ... !
654 [NSApp setApplicationIconImage:original_dock_image];
655 [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
656 [NSApp setApplicationIconImage:original_dock_image];
657
658}
659
660@end
661
Note: See TracBrowser for help on using the repository browser.