Native Mac OS X OtaClock replica
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

MainWindow.m 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. //
  2. // MainWindow.m
  3. // OtaClock
  4. //
  5. // Created by Thomas Buck on 16.08.15.
  6. // Copyright (c) 2015 xythobuz. All rights reserved.
  7. //
  8. #import "MainView.h"
  9. #import "MainWindow.h"
  10. #define RESIZE_START 1
  11. #define RESIZE_END 10
  12. #define RESIZE_STEP 1
  13. #define CONFIG_START_SCALE @"window_scale"
  14. #define CONFIG_KEEP_POSITION @"keep_position"
  15. #define CONFIG_KEEP_ON_TOP @"keep_on_top"
  16. #define CONFIG_MILITARY_TIME @"military_time"
  17. #define CONFIG_SHOW_DATE @"show_date"
  18. #define CONFIG_ALARM_TIME @"alarm_time"
  19. #define MOUSE_CENTER_X 67
  20. #define MOUSE_CENTER_Y 47
  21. #define EYE_BLINK 0
  22. #define EYE_TOP_RIGHT 4
  23. #define EYE_TOP_LEFT 2
  24. #define EYE_BOTTOM_RIGHT 3
  25. #define EYE_BOTTOM_LEFT 1
  26. #define MAX_BLINK_DELAY 5.0
  27. #define UNBLINK_DELAY 0.1
  28. @interface MainWindow ()
  29. @property (assign) NSSize defaultSize;
  30. @property (assign) NSInteger startScale;
  31. @property (assign) NSInteger lastEyeState;
  32. @property (assign) BOOL currentlyBlinking, showDate;
  33. @property (weak) IBOutlet MainView *mainView;
  34. @property (weak) IBOutlet NSDatePicker *alarmDatePicker;
  35. @property (weak) IBOutlet NSMenuItem *lockPositionItem;
  36. @property (weak) IBOutlet NSMenuItem *keepOnTopItem;
  37. @property (weak) IBOutlet NSMenuItem *setAlarmItem;
  38. @property (weak) IBOutlet NSMenuItem *showDateItem;
  39. @property (weak) IBOutlet NSMenuItem *changeSize1;
  40. @property (weak) IBOutlet NSMenuItem *changeSize2;
  41. @property (weak) IBOutlet NSMenuItem *changeSize3;
  42. @property (weak) IBOutlet NSMenuItem *changeSize4;
  43. @property (weak) IBOutlet NSMenuItem *changeSize5;
  44. @property (weak) IBOutlet NSMenuItem *changeSize6;
  45. @property (weak) IBOutlet NSMenuItem *changeSize7;
  46. @property (weak) IBOutlet NSMenuItem *changeSize8;
  47. @property (weak) IBOutlet NSMenuItem *changeSize9;
  48. @property (weak) IBOutlet NSMenuItem *changeSize10;
  49. @end
  50. @implementation MainWindow
  51. @synthesize dragStart;
  52. @synthesize keepPosition;
  53. @synthesize defaultSize;
  54. @synthesize startScale;
  55. @synthesize lastEyeState;
  56. @synthesize currentlyBlinking, showDate;
  57. - (id)initWithContentRect:(NSRect)contentRect
  58. styleMask:(NSUInteger)aStyle
  59. backing:(NSBackingStoreType)bufferingType
  60. defer:(BOOL)flag {
  61. self = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
  62. if (self != nil) {
  63. [self setAlphaValue:1.0];
  64. [self setOpaque:NO];
  65. lastEyeState = EYE_TOP_LEFT;
  66. currentlyBlinking = NO;
  67. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  68. // load and see if window should be kept on top
  69. if ([defaults objectForKey:CONFIG_KEEP_ON_TOP] != nil) {
  70. if ([defaults boolForKey:CONFIG_KEEP_ON_TOP]) {
  71. [self setLevel:NSFloatingWindowLevel];
  72. }
  73. }
  74. // load and see if window pos should be fixed
  75. if ([defaults objectForKey:CONFIG_KEEP_POSITION] == nil) {
  76. keepPosition = NO;
  77. } else {
  78. keepPosition = [defaults boolForKey:CONFIG_KEEP_POSITION];
  79. }
  80. // load window scale factor
  81. if ([defaults objectForKey:CONFIG_START_SCALE] == nil) {
  82. startScale = 1;
  83. } else {
  84. startScale = [defaults integerForKey:CONFIG_START_SCALE];
  85. }
  86. }
  87. [self setAcceptsMouseMovedEvents:YES];
  88. [NSEvent addGlobalMonitorForEventsMatchingMask:NSMouseMovedMask handler:^(NSEvent *mouseMovedEvent) { [self mouseMoved:mouseMovedEvent]; }];
  89. return self;
  90. }
  91. - (void)setDefaultBackgroundSize:(NSSize)size {
  92. defaultSize = size;
  93. NSRect frame = [self frame];
  94. frame.size = defaultSize;
  95. frame.size.width *= startScale;
  96. frame.size.height *= startScale;
  97. // We need to do all initialization of view state in here, because they are not ready in init
  98. if (keepPosition) [self.lockPositionItem setState:NSOnState];
  99. if ([self level] == NSFloatingWindowLevel) [self.keepOnTopItem setState:NSOnState];
  100. if (startScale == 1) [self.changeSize1 setState:NSOnState];
  101. if (startScale == 2) [self.changeSize2 setState:NSOnState];
  102. if (startScale == 3) [self.changeSize3 setState:NSOnState];
  103. if (startScale == 4) [self.changeSize4 setState:NSOnState];
  104. if (startScale == 5) [self.changeSize5 setState:NSOnState];
  105. if (startScale == 6) [self.changeSize6 setState:NSOnState];
  106. if (startScale == 7) [self.changeSize7 setState:NSOnState];
  107. if (startScale == 8) [self.changeSize8 setState:NSOnState];
  108. if (startScale == 9) [self.changeSize9 setState:NSOnState];
  109. if (startScale == 10) [self.changeSize10 setState:NSOnState];
  110. [[self.mainView render] drawWithEye:lastEyeState]; // Initialize render image
  111. [self unblink]; // Schedule next blinking
  112. // Start time keeping
  113. [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(updateTime:) userInfo:nil repeats:YES];
  114. [self updateTime:nil];
  115. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  116. // load alarm date
  117. if ([defaults objectForKey:CONFIG_ALARM_TIME] == nil) {
  118. self.alarmDatePicker.dateValue = [NSDate date];
  119. } else {
  120. self.alarmDatePicker.dateValue = [defaults objectForKey:CONFIG_ALARM_TIME];
  121. }
  122. self.setAlarmItem.view = self.alarmDatePicker;
  123. // load show date state
  124. if ([defaults objectForKey:CONFIG_SHOW_DATE] == nil) {
  125. showDate = YES;
  126. } else {
  127. showDate = [defaults boolForKey:CONFIG_SHOW_DATE];
  128. }
  129. if (showDate == YES) {
  130. self.showDateItem.state = NSOnState;
  131. } else {
  132. self.showDateItem.state = NSOffState;
  133. }
  134. [[self.mainView render] drawDate:showDate];
  135. [self setFrame:frame display:YES];
  136. }
  137. - (void)updateTime:(id)sender {
  138. [[self.mainView render] drawWithDate:[NSDate date]];
  139. self.mainView.needsDisplay = YES;
  140. }
  141. - (void)blink {
  142. if (currentlyBlinking == NO) {
  143. currentlyBlinking = YES;
  144. [[self.mainView render] drawWithEye:EYE_BLINK];
  145. self.mainView.needsDisplay = YES;
  146. }
  147. [self performSelector:@selector(unblink) withObject:nil afterDelay:UNBLINK_DELAY];
  148. }
  149. - (void)unblink {
  150. if (currentlyBlinking == YES) {
  151. currentlyBlinking = NO;
  152. [[self.mainView render] drawWithEye:lastEyeState];
  153. self.mainView.needsDisplay = YES;
  154. }
  155. [self performSelector:@selector(blink) withObject:nil afterDelay:(((float)rand() / RAND_MAX) * MAX_BLINK_DELAY)];
  156. }
  157. - (IBAction)alarmDateSelected:(id)sender {
  158. // TODO do something with this alarm time...
  159. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  160. [defaults setObject:self.alarmDatePicker.dateValue forKey:CONFIG_ALARM_TIME];
  161. [defaults synchronize];
  162. }
  163. - (IBAction)showDate:(id)sender {
  164. if (showDate == YES) {
  165. showDate = NO;
  166. self.showDateItem.state = NSOffState;
  167. } else {
  168. showDate = YES;
  169. self.showDateItem.state = NSOnState;
  170. }
  171. [[self.mainView render] drawDate:showDate];
  172. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  173. [defaults setBool:showDate forKey:CONFIG_SHOW_DATE];
  174. [defaults synchronize];
  175. }
  176. - (IBAction)changeSize:(NSMenuItem *)sender {
  177. NSRect frame = [self frame];
  178. [self.changeSize1 setState:NSOffState];
  179. [self.changeSize2 setState:NSOffState];
  180. [self.changeSize3 setState:NSOffState];
  181. [self.changeSize4 setState:NSOffState];
  182. [self.changeSize5 setState:NSOffState];
  183. [self.changeSize6 setState:NSOffState];
  184. [self.changeSize7 setState:NSOffState];
  185. [self.changeSize8 setState:NSOffState];
  186. [self.changeSize9 setState:NSOffState];
  187. [self.changeSize10 setState:NSOffState];
  188. BOOL found = NO;
  189. for (int i = RESIZE_START; i <= RESIZE_END; i += RESIZE_STEP) {
  190. NSString *title = [NSString stringWithFormat:@"x%d", i];
  191. if ([[sender title] isEqualToString:title]) {
  192. [sender setState:NSOnState];
  193. NSSize newSize = defaultSize;
  194. newSize.height *= i;
  195. newSize.width *= i;
  196. frame.size = newSize;
  197. found = YES;
  198. startScale = i;
  199. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  200. [defaults setInteger:i forKey:CONFIG_START_SCALE];
  201. [defaults synchronize];
  202. break;
  203. }
  204. }
  205. if (found == NO) {
  206. NSLog(@"Unknown changeSize sender: %@", sender);
  207. } else {
  208. [self setFrame:frame display:YES];
  209. }
  210. }
  211. - (IBAction)lockPosition:(NSMenuItem *)sender {
  212. BOOL state = [sender state];
  213. if (state == NSOffState) {
  214. // Lock position
  215. state = NSOnState;
  216. [sender setState:state];
  217. self.keepPosition = YES;
  218. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  219. [defaults setBool:YES forKey:CONFIG_KEEP_POSITION];
  220. [defaults synchronize];
  221. } else {
  222. // Unlock position
  223. state = NSOffState;
  224. [sender setState:state];
  225. self.keepPosition = NO;
  226. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  227. [defaults setBool:NO forKey:CONFIG_KEEP_POSITION];
  228. [defaults synchronize];
  229. }
  230. }
  231. - (IBAction)keepOnTop:(NSMenuItem *)sender {
  232. BOOL state = [sender state];
  233. if (state == NSOffState) {
  234. // Keep window on top
  235. state = NSOnState;
  236. [sender setState:state];
  237. [self setLevel:NSFloatingWindowLevel];
  238. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  239. [defaults setBool:YES forKey:CONFIG_KEEP_ON_TOP];
  240. [defaults synchronize];
  241. } else {
  242. // Don't keep window on top
  243. state = NSOffState;
  244. [sender setState:state];
  245. [self setLevel:NSNormalWindowLevel];
  246. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  247. [defaults setBool:NO forKey:CONFIG_KEEP_ON_TOP];
  248. [defaults synchronize];
  249. }
  250. }
  251. - (BOOL)canBecomeKeyWindow {
  252. return YES;
  253. }
  254. - (void)mouseDown:(NSEvent *)theEvent {
  255. dragStart = [theEvent locationInWindow];
  256. }
  257. - (void)mouseDragged:(NSEvent *)theEvent {
  258. NSRect screenVisibleFrame = [[NSScreen mainScreen] visibleFrame];
  259. NSRect windowFrame = [self frame];
  260. NSPoint newOrigin = windowFrame.origin;
  261. if (self.keepPosition == NO) {
  262. NSPoint currentLocation = [theEvent locationInWindow];
  263. newOrigin.x += (currentLocation.x - dragStart.x);
  264. newOrigin.y += (currentLocation.y - dragStart.y);
  265. if ((newOrigin.y + windowFrame.size.height) > (screenVisibleFrame.origin.y + screenVisibleFrame.size.height)) {
  266. newOrigin.y = screenVisibleFrame.origin.y + (screenVisibleFrame.size.height - windowFrame.size.height);
  267. }
  268. [self setFrameOrigin:newOrigin];
  269. }
  270. }
  271. - (void)mouseMoved:(NSEvent *)theEvent {
  272. NSPoint mousePoint = [NSEvent mouseLocation];
  273. mousePoint.x -= [self frame].origin.x;
  274. mousePoint.y -= [self frame].origin.y;
  275. BOOL top = (mousePoint.y > (MOUSE_CENTER_Y * startScale));
  276. BOOL right = (mousePoint.x > (MOUSE_CENTER_X * startScale));
  277. NSInteger eyeState = EYE_BOTTOM_LEFT;
  278. if (top && right) {
  279. eyeState = EYE_TOP_RIGHT;
  280. } else if (top && (!right)) {
  281. eyeState = EYE_TOP_LEFT;
  282. } else if ((!top) && right) {
  283. eyeState = EYE_BOTTOM_RIGHT;
  284. }
  285. if (eyeState != lastEyeState) {
  286. lastEyeState = eyeState;
  287. if (currentlyBlinking == NO) {
  288. [[self.mainView render] drawWithEye:lastEyeState];
  289. self.mainView.needsDisplay = YES;
  290. }
  291. }
  292. }
  293. @end