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 8.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  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 5
  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 MOUSE_CENTER_X 67
  17. #define MOUSE_CENTER_Y 47
  18. #define EYE_BLINK 0
  19. #define EYE_TOP_RIGHT 4
  20. #define EYE_TOP_LEFT 2
  21. #define EYE_BOTTOM_RIGHT 3
  22. #define EYE_BOTTOM_LEFT 1
  23. #define MAX_BLINK_DELAY 5.0
  24. #define UNBLINK_DELAY 0.1
  25. @interface MainWindow ()
  26. @property (assign) NSSize defaultSize;
  27. @property (assign) NSInteger startScale;
  28. @property (assign) NSInteger lastEyeState;
  29. @property (assign) BOOL currentlyBlinking;
  30. @property (weak) IBOutlet MainView *mainView;
  31. @property (weak) IBOutlet NSMenuItem *lockPositionItem;
  32. @property (weak) IBOutlet NSMenuItem *keepOnTopItem;
  33. @property (weak) IBOutlet NSMenuItem *changeSize1;
  34. @property (weak) IBOutlet NSMenuItem *changeSize2;
  35. @property (weak) IBOutlet NSMenuItem *changeSize3;
  36. @property (weak) IBOutlet NSMenuItem *changeSize4;
  37. @property (weak) IBOutlet NSMenuItem *changeSize5;
  38. @end
  39. @implementation MainWindow
  40. @synthesize dragStart;
  41. @synthesize keepPosition;
  42. @synthesize defaultSize;
  43. @synthesize startScale;
  44. @synthesize lastEyeState;
  45. @synthesize currentlyBlinking;
  46. - (id)initWithContentRect:(NSRect)contentRect
  47. styleMask:(NSUInteger)aStyle
  48. backing:(NSBackingStoreType)bufferingType
  49. defer:(BOOL)flag {
  50. self = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
  51. if (self != nil) {
  52. [self setAlphaValue:1.0];
  53. [self setOpaque:NO];
  54. lastEyeState = EYE_TOP_LEFT;
  55. currentlyBlinking = NO;
  56. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  57. // load and see if window should be kept on top
  58. if ([defaults objectForKey:CONFIG_KEEP_ON_TOP] != nil) {
  59. if ([defaults boolForKey:CONFIG_KEEP_ON_TOP]) {
  60. [self setLevel:NSFloatingWindowLevel];
  61. }
  62. }
  63. // load and see if window pos should be fixed
  64. if ([defaults objectForKey:CONFIG_KEEP_POSITION] == nil) {
  65. keepPosition = NO;
  66. } else {
  67. keepPosition = [defaults boolForKey:CONFIG_KEEP_POSITION];
  68. }
  69. // load window scale factor
  70. if ([defaults objectForKey:CONFIG_START_SCALE] == nil) {
  71. startScale = 1;
  72. } else {
  73. startScale = [defaults integerForKey:CONFIG_START_SCALE];
  74. }
  75. }
  76. [self setAcceptsMouseMovedEvents:YES];
  77. [NSEvent addGlobalMonitorForEventsMatchingMask:NSMouseMovedMask handler:^(NSEvent *mouseMovedEvent) { [self mouseMoved:mouseMovedEvent]; }];
  78. return self;
  79. }
  80. - (void)setDefaultBackgroundSize:(NSSize)size {
  81. defaultSize = size;
  82. NSRect frame = [self frame];
  83. frame.size = defaultSize;
  84. frame.size.width *= startScale;
  85. frame.size.height *= startScale;
  86. // We need to do all initialization of view state in here, because they are not ready in init
  87. if (keepPosition) [self.lockPositionItem setState:NSOnState];
  88. if ([self level] == NSFloatingWindowLevel) [self.keepOnTopItem setState:NSOnState];
  89. if (startScale == 1) [self.changeSize1 setState:NSOnState];
  90. if (startScale == 2) [self.changeSize2 setState:NSOnState];
  91. if (startScale == 3) [self.changeSize3 setState:NSOnState];
  92. if (startScale == 4) [self.changeSize4 setState:NSOnState];
  93. if (startScale == 5) [self.changeSize5 setState:NSOnState];
  94. [[self.mainView render] drawWithEye:lastEyeState]; // Initialize render image
  95. [self unblink]; // Schedule next blinking
  96. // Start time keeping
  97. [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(updateTime:) userInfo:nil repeats:YES];
  98. [self updateTime:nil];
  99. [self setFrame:frame display:YES];
  100. }
  101. - (void)updateTime:(id)sender {
  102. [[self.mainView render] drawWithDate:[NSDate date]];
  103. }
  104. - (void)blink {
  105. if (currentlyBlinking == NO) {
  106. currentlyBlinking = YES;
  107. [[self.mainView render] drawWithEye:EYE_BLINK];
  108. self.mainView.needsDisplay = YES;
  109. }
  110. [self performSelector:@selector(unblink) withObject:nil afterDelay:UNBLINK_DELAY];
  111. }
  112. - (void)unblink {
  113. if (currentlyBlinking == YES) {
  114. currentlyBlinking = NO;
  115. [[self.mainView render] drawWithEye:lastEyeState];
  116. self.mainView.needsDisplay = YES;
  117. }
  118. [self performSelector:@selector(blink) withObject:nil afterDelay:(((float)rand() / RAND_MAX) * MAX_BLINK_DELAY)];
  119. }
  120. - (IBAction)changeSize:(NSMenuItem *)sender {
  121. NSRect frame = [self frame];
  122. [self.changeSize1 setState:NSOffState];
  123. [self.changeSize2 setState:NSOffState];
  124. [self.changeSize3 setState:NSOffState];
  125. [self.changeSize4 setState:NSOffState];
  126. [self.changeSize5 setState:NSOffState];
  127. BOOL found = NO;
  128. for (int i = RESIZE_START; i <= RESIZE_END; i += RESIZE_STEP) {
  129. NSString *title = [NSString stringWithFormat:@"%dx", i];
  130. if ([[sender title] isEqualToString:title]) {
  131. [sender setState:NSOnState];
  132. NSSize newSize = defaultSize;
  133. newSize.height *= i;
  134. newSize.width *= i;
  135. frame.size = newSize;
  136. found = YES;
  137. startScale = i;
  138. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  139. [defaults setInteger:i forKey:CONFIG_START_SCALE];
  140. [defaults synchronize];
  141. break;
  142. }
  143. }
  144. if (found == NO) {
  145. NSLog(@"Unknown changeSize sender: %@", sender);
  146. } else {
  147. [self setFrame:frame display:YES];
  148. }
  149. }
  150. - (IBAction)lockPosition:(NSMenuItem *)sender {
  151. BOOL state = [sender state];
  152. if (state == NSOffState) {
  153. // Lock position
  154. state = NSOnState;
  155. [sender setState:state];
  156. self.keepPosition = YES;
  157. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  158. [defaults setBool:YES forKey:CONFIG_KEEP_POSITION];
  159. [defaults synchronize];
  160. } else {
  161. // Unlock position
  162. state = NSOffState;
  163. [sender setState:state];
  164. self.keepPosition = NO;
  165. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  166. [defaults setBool:NO forKey:CONFIG_KEEP_POSITION];
  167. [defaults synchronize];
  168. }
  169. }
  170. - (IBAction)keepOnTop:(NSMenuItem *)sender {
  171. BOOL state = [sender state];
  172. if (state == NSOffState) {
  173. // Keep window on top
  174. state = NSOnState;
  175. [sender setState:state];
  176. [self setLevel:NSFloatingWindowLevel];
  177. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  178. [defaults setBool:YES forKey:CONFIG_KEEP_ON_TOP];
  179. [defaults synchronize];
  180. } else {
  181. // Don't keep window on top
  182. state = NSOffState;
  183. [sender setState:state];
  184. [self setLevel:NSNormalWindowLevel];
  185. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  186. [defaults setBool:NO forKey:CONFIG_KEEP_ON_TOP];
  187. [defaults synchronize];
  188. }
  189. }
  190. - (BOOL)canBecomeKeyWindow {
  191. return YES;
  192. }
  193. - (void)mouseDown:(NSEvent *)theEvent {
  194. dragStart = [theEvent locationInWindow];
  195. }
  196. - (void)mouseDragged:(NSEvent *)theEvent {
  197. NSRect screenVisibleFrame = [[NSScreen mainScreen] visibleFrame];
  198. NSRect windowFrame = [self frame];
  199. NSPoint newOrigin = windowFrame.origin;
  200. if (self.keepPosition == NO) {
  201. NSPoint currentLocation = [theEvent locationInWindow];
  202. newOrigin.x += (currentLocation.x - dragStart.x);
  203. newOrigin.y += (currentLocation.y - dragStart.y);
  204. if ((newOrigin.y + windowFrame.size.height) > (screenVisibleFrame.origin.y + screenVisibleFrame.size.height)) {
  205. newOrigin.y = screenVisibleFrame.origin.y + (screenVisibleFrame.size.height - windowFrame.size.height);
  206. }
  207. [self setFrameOrigin:newOrigin];
  208. }
  209. }
  210. - (void)mouseMoved:(NSEvent *)theEvent {
  211. NSPoint mousePoint = [NSEvent mouseLocation];
  212. mousePoint.x -= [self frame].origin.x;
  213. mousePoint.y -= [self frame].origin.y;
  214. BOOL top = (mousePoint.y > (MOUSE_CENTER_Y * startScale));
  215. BOOL right = (mousePoint.x > (MOUSE_CENTER_X * startScale));
  216. NSInteger eyeState = EYE_BOTTOM_LEFT;
  217. if (top && right) {
  218. eyeState = EYE_TOP_RIGHT;
  219. } else if (top && (!right)) {
  220. eyeState = EYE_TOP_LEFT;
  221. } else if ((!top) && right) {
  222. eyeState = EYE_BOTTOM_RIGHT;
  223. }
  224. if (eyeState != lastEyeState) {
  225. lastEyeState = eyeState;
  226. if (currentlyBlinking == NO) {
  227. [[self.mainView render] drawWithEye:lastEyeState];
  228. self.mainView.needsDisplay = YES;
  229. }
  230. }
  231. }
  232. @end