PageRenderTime 180ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/PGAlertView.m

https://github.com/leecbaker/Sequential
Objective C | 517 lines | 402 code | 89 blank | 26 comment | 28 complexity | 3629d2bc8339ef581047bb7fbc272738 MD5 | raw file
  1. /* Copyright © 2007-2009, The Sequential Project
  2. All rights reserved.
  3. Redistribution and use in source and binary forms, with or without
  4. modification, are permitted provided that the following conditions are met:
  5. * Redistributions of source code must retain the above copyright
  6. notice, this list of conditions and the following disclaimer.
  7. * Redistributions in binary form must reproduce the above copyright
  8. notice, this list of conditions and the following disclaimer in the
  9. documentation and/or other materials provided with the distribution.
  10. * Neither the name of the the Sequential Project nor the
  11. names of its contributors may be used to endorse or promote products
  12. derived from this software without specific prior written permission.
  13. THIS SOFTWARE IS PROVIDED BY THE SEQUENTIAL PROJECT ''AS IS'' AND ANY
  14. EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  15. WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  16. DISCLAIMED. IN NO EVENT SHALL THE SEQUENTIAL PROJECT BE LIABLE FOR ANY
  17. DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  18. (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  19. LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  20. ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  21. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  22. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
  23. #import "PGAlertView.h"
  24. // Views
  25. #import "PGBezelPanel.h"
  26. // Other Sources
  27. #import "PGAppKitAdditions.h"
  28. #import "PGDelayedPerforming.h"
  29. #import "PGFoundationAdditions.h"
  30. #import "PGGeometry.h"
  31. #define PGAlertViewSize 100.0f
  32. #define PGMarginSize 4.0f
  33. @interface PGAlertView(Private)
  34. - (void)_updateCurrentGraphic;
  35. @end
  36. @interface PGCannotGoRightGraphic : PGAlertGraphic
  37. @end
  38. @interface PGCannotGoLeftGraphic : PGCannotGoRightGraphic
  39. @end
  40. @interface PGLoopedLeftGraphic : PGAlertGraphic
  41. @end
  42. @interface PGLoopedRightGraphic : PGLoopedLeftGraphic
  43. @end
  44. @implementation PGAlertView
  45. #pragma mark -PGAlertView
  46. @synthesize currentGraphic = _currentGraphic;
  47. @synthesize frameCount = _frameCount;
  48. #pragma mark -
  49. - (void)pushGraphic:(PGAlertGraphic *)aGraphic window:(NSWindow *)window
  50. {
  51. NSParameterAssert(aGraphic);
  52. NSUInteger const i = [_graphicStack indexOfObject:aGraphic];
  53. if(0 == i) {
  54. [self PG_cancelPreviousPerformRequestsWithSelector:@selector(popGraphicIdenticalTo:) object:_currentGraphic];
  55. } else {
  56. [_graphicStack insertObject:aGraphic atIndex:0];
  57. [self _updateCurrentGraphic];
  58. }
  59. NSTimeInterval const fadeOutDelay = [_currentGraphic fadeOutDelay];
  60. if(fadeOutDelay >= 0.01f) [self PG_performSelector:@selector(popGraphicIdenticalTo:) withObject:_currentGraphic fireDate:nil interval:fadeOutDelay options:PGCompareArgumentPointer];
  61. if(window && [[self window] respondsToSelector:@selector(displayOverWindow:)]) [(PGBezelPanel *)[self window] displayOverWindow:window];
  62. }
  63. - (void)popGraphic:(PGAlertGraphic *)aGraphic
  64. {
  65. NSParameterAssert(aGraphic);
  66. NSUInteger const i = [_graphicStack indexOfObject:aGraphic];
  67. if(NSNotFound == i) return;
  68. [_graphicStack removeObjectAtIndex:i];
  69. [self _updateCurrentGraphic];
  70. }
  71. - (void)popGraphicIdenticalTo:(PGAlertGraphic *)aGraphic
  72. {
  73. NSParameterAssert(aGraphic);
  74. NSUInteger const i = [_graphicStack indexOfObjectIdenticalTo:aGraphic];
  75. if(NSNotFound == i) return;
  76. [_graphicStack removeObjectAtIndex:i];
  77. [self _updateCurrentGraphic];
  78. }
  79. - (void)popGraphicsOfType:(PGAlertGraphicType)type
  80. {
  81. for(PGAlertGraphic *const graphic in [[_graphicStack copy] autorelease]) if([graphic graphicType] == type) [_graphicStack removeObjectIdenticalTo:graphic];
  82. [self _updateCurrentGraphic];
  83. }
  84. #pragma mark -
  85. - (void)animateOneFrame:(PGAlertView *)anAlertView
  86. {
  87. NSParameterAssert(_currentGraphic);
  88. _frameCount = (_frameCount + 1) % _currentGraphic.frameCount;
  89. [_currentGraphic animateOneFrame:self];
  90. }
  91. #pragma mark -PGAlertView(Private)
  92. - (void)_updateCurrentGraphic
  93. {
  94. if(![_graphicStack count]) {
  95. if([_currentGraphic fadeOutDelay]) [(PGBezelPanel *)[self window] fadeOut];
  96. else [[self window] close];
  97. return;
  98. }
  99. [_currentGraphic release];
  100. _currentGraphic = [[_graphicStack objectAtIndex:0] retain];
  101. [_frameTimer invalidate];
  102. _frameCount = 0;
  103. NSTimeInterval const frameDelay = _currentGraphic.frameDelay;
  104. _frameTimer = _currentGraphic.frameCount > 1 ? [self PG_performSelector:@selector(animateOneFrame:) withObject:self fireDate:nil interval:frameDelay options:PGRetainTarget | PGRepeatOnInterval] : nil;
  105. [self setNeedsDisplay:YES];
  106. }
  107. #pragma mark -NSView
  108. - (id)initWithFrame:(NSRect)aRect
  109. {
  110. if((self = [super initWithFrame:aRect])) {
  111. _graphicStack = [[NSMutableArray alloc] init];
  112. }
  113. return self;
  114. }
  115. - (BOOL)isOpaque
  116. {
  117. return YES;
  118. }
  119. - (void)drawRect:(NSRect)aRect
  120. {
  121. [_currentGraphic drawInView:self];
  122. }
  123. - (void)viewWillMoveToWindow:(NSWindow *)aWindow
  124. {
  125. [[self window] PG_removeObserver:self name:NSWindowWillCloseNotification];
  126. if(aWindow) [aWindow PG_addObserver:self selector:@selector(windowWillClose:) name:NSWindowWillCloseNotification];
  127. else [self windowWillClose:nil];
  128. }
  129. #pragma mark -NSObject
  130. - (void)dealloc
  131. {
  132. [self PG_cancelPreviousPerformRequests];
  133. [self PG_removeObserver];
  134. [_graphicStack release];
  135. [_currentGraphic release];
  136. [_frameTimer invalidate];
  137. [super dealloc];
  138. }
  139. #pragma mark -<NSWindowDelegate>
  140. - (void)windowWillClose:(NSNotification *)aNotif
  141. {
  142. [_frameTimer invalidate];
  143. _frameTimer = nil;
  144. [_graphicStack removeAllObjects];
  145. }
  146. #pragma mark -<PGBezelPanelContentView>
  147. - (NSRect)bezelPanel:(PGBezelPanel *)sender frameForContentRect:(NSRect)aRect scale:(CGFloat)scaleFactor
  148. {
  149. CGFloat const scaledPanelSize = scaleFactor * PGAlertViewSize;
  150. return PGIntegralRect(NSMakeRect(
  151. NSMinX(aRect) + PGMarginSize,
  152. NSMaxY(aRect) - scaledPanelSize - PGMarginSize,
  153. scaledPanelSize,
  154. scaledPanelSize
  155. ));
  156. }
  157. @end
  158. @implementation PGAlertGraphic
  159. #pragma mark +PGAlertGraphic
  160. + (id)cannotGoRightGraphic
  161. {
  162. return [[[PGCannotGoRightGraphic alloc] init] autorelease];
  163. }
  164. + (id)cannotGoLeftGraphic
  165. {
  166. return [[[PGCannotGoLeftGraphic alloc] init] autorelease];
  167. }
  168. + (id)loopedRightGraphic
  169. {
  170. return [[[PGLoopedRightGraphic alloc] init] autorelease];
  171. }
  172. + (id)loopedLeftGraphic
  173. {
  174. return [[[PGLoopedLeftGraphic alloc] init] autorelease];
  175. }
  176. #pragma mark -PGAlertGraphic
  177. - (PGAlertGraphicType)graphicType
  178. {
  179. return PGSingleImageGraphic;
  180. }
  181. - (NSTimeInterval)fadeOutDelay
  182. {
  183. return 1.0f;
  184. }
  185. - (NSTimeInterval)frameDelay
  186. {
  187. return 0.0f;
  188. }
  189. - (NSUInteger)frameCount
  190. {
  191. return 1;
  192. }
  193. #pragma mark -
  194. - (void)drawInView:(PGAlertView *)anAlertView
  195. {
  196. NSInteger count, i;
  197. NSRect const *rects;
  198. [anAlertView getRectsBeingDrawn:&rects count:&count];
  199. [[NSColor PG_bezelBackgroundColor] set];
  200. CGFloat const f = PGAlertViewSize / 300.0f;
  201. for(i = count; i--;) {
  202. NSRectFill(NSIntersectionRect(rects[i], PGIntegralRect(NSMakeRect( 0.0f * f, 50.0f * f, 50.0f * f, 200.0f * f))));
  203. NSRectFill(NSIntersectionRect(rects[i], PGIntegralRect(NSMakeRect( 50.0f * f, 0.0f * f, 200.0f * f, 300.0f * f))));
  204. NSRectFill(NSIntersectionRect(rects[i], PGIntegralRect(NSMakeRect(250.0f * f, 50.0f * f, 50.0f * f, 200.0f * f))));
  205. }
  206. NSRect const corners[] = {
  207. PGIntegralRect(NSMakeRect(250.0f * f, 250.0f * f, 50.0f * f, 50.0f * f)),
  208. PGIntegralRect(NSMakeRect( 0.0f * f, 250.0f * f, 50.0f * f, 50.0f * f)),
  209. PGIntegralRect(NSMakeRect( 0.0f * f, 0.0f * f, 50.0f * f, 50.0f * f)),
  210. PGIntegralRect(NSMakeRect(250.0f * f, 0.0f * f, 50.0f * f, 50.0f * f))
  211. };
  212. NSPoint const centers[] = {
  213. PGIntegralPoint(NSMakePoint(250.0f * f, 250.0f * f)),
  214. PGIntegralPoint(NSMakePoint( 50.0f * f, 250.0f * f)),
  215. PGIntegralPoint(NSMakePoint( 50.0f * f, 50.0f * f)),
  216. PGIntegralPoint(NSMakePoint(250.0f * f, 50.0f * f))
  217. };
  218. for(i = numberof(corners); i--;) {
  219. NSRect const corner = corners[i];
  220. if(!PGIntersectsRectList(corner, rects, count)) continue;
  221. [[NSColor clearColor] set];
  222. NSRectFill(corners[i]);
  223. [[NSColor PG_bezelBackgroundColor] set];
  224. NSBezierPath *const path = [NSBezierPath bezierPath];
  225. [path moveToPoint:centers[i]];
  226. [path appendBezierPathWithArcWithCenter:centers[i] radius:50.0f * f startAngle:90.0f * i endAngle:90.0f * (i + 1)];
  227. [path closePath];
  228. [path fill];
  229. }
  230. NSShadow *const shadow = [[[NSShadow alloc] init] autorelease];
  231. [shadow setShadowBlurRadius:4.0f];
  232. [shadow setShadowOffset:NSMakeSize(0.0f, -1.0f)];
  233. [shadow setShadowColor:[NSColor blackColor]];
  234. [shadow set];
  235. }
  236. - (void)flipHorizontally
  237. {
  238. NSAffineTransform *const flip = [[[NSAffineTransform alloc] init] autorelease];
  239. [flip translateXBy:PGAlertViewSize yBy:0.0f];
  240. [flip scaleXBy:-1.0f yBy:1.0f];
  241. [flip concat];
  242. }
  243. #pragma mark -
  244. - (void)animateOneFrame:(PGAlertView *)anAlertView {}
  245. #pragma mark -<NSObject>
  246. - (NSUInteger)hash
  247. {
  248. return [[self class] hash];
  249. }
  250. - (BOOL)isEqual:(id)anObject
  251. {
  252. return [anObject isMemberOfClass:[self class]];
  253. }
  254. @end
  255. @implementation PGCannotGoRightGraphic
  256. #pragma mark -PGAlertGraphic
  257. - (void)drawInView:(PGAlertView *)anAlertView
  258. {
  259. [super drawInView:anAlertView];
  260. CGFloat const f = PGAlertViewSize / 300.0f;
  261. CGFloat const small = 5.0f * f;
  262. CGFloat const large = 10.0f * f;
  263. [[NSColor PG_bezelForegroundColor] set];
  264. NSBezierPath *const arrow = [NSBezierPath bezierPath];
  265. [arrow appendBezierPathWithArcWithCenter:NSMakePoint(180.0f * f, 150.0f * f) radius:large startAngle:315.0f endAngle:45.0f];
  266. [arrow appendBezierPathWithArcWithCenter:NSMakePoint(140.0f * f, 200.0f * f) radius:small startAngle:45.0f endAngle:90.0f];
  267. [arrow appendBezierPathWithArcWithCenter:NSMakePoint(125.0f * f, 200.0f * f) radius:small startAngle:90.0f endAngle:180.0f];
  268. [arrow appendBezierPathWithArcWithCenter:NSMakePoint(115.0f * f, 180.0f * f) radius:small startAngle:0.0f endAngle:270.0f clockwise:YES];
  269. [arrow appendBezierPathWithArcWithCenter:NSMakePoint( 75.0f * f, 170.0f * f) radius:small startAngle:90.0f endAngle:180.0f];
  270. [arrow appendBezierPathWithArcWithCenter:NSMakePoint( 75.0f * f, 130.0f * f) radius:small startAngle:180.0f endAngle:270.0f];
  271. [arrow appendBezierPathWithArcWithCenter:NSMakePoint(115.0f * f, 120.0f * f) radius:small startAngle:90.0f endAngle:0.0f clockwise:YES];
  272. [arrow appendBezierPathWithArcWithCenter:NSMakePoint(125.0f * f, 100.0f * f) radius:small startAngle:180.0f endAngle:270.0f];
  273. [arrow appendBezierPathWithArcWithCenter:NSMakePoint(140.0f * f, 100.0f * f) radius:small startAngle:270.0f endAngle:315.0f];
  274. [arrow fill];
  275. NSBezierPath *const wall = [NSBezierPath bezierPath];
  276. [wall setLineWidth:20.0f * f];
  277. [wall setLineCapStyle:NSRoundLineCapStyle];
  278. [wall moveToPoint:NSMakePoint(210.0f * f, 220.0f * f)];
  279. [wall lineToPoint:NSMakePoint(210.0f * f, 80.0f * f)];
  280. [wall stroke];
  281. }
  282. @end
  283. @implementation PGCannotGoLeftGraphic
  284. #pragma mark -PGAlertGraphic
  285. - (void)drawInView:(PGAlertView *)anAlertView
  286. {
  287. [self flipHorizontally];
  288. [super drawInView:anAlertView];
  289. }
  290. @end
  291. @implementation PGLoopedLeftGraphic
  292. #pragma mark -PGAlertGraphic
  293. - (PGAlertGraphicType)graphicType
  294. {
  295. return PGInterImageGraphic;
  296. }
  297. - (NSTimeInterval)fadeOutDelay
  298. {
  299. return 1.0f;
  300. }
  301. #pragma mark -
  302. - (void)drawInView:(PGAlertView *)anAlertView
  303. {
  304. [super drawInView:anAlertView];
  305. [[NSColor PG_bezelForegroundColor] set];
  306. NSBezierPath *const s = [NSBezierPath bezierPath];
  307. CGFloat const f = PGAlertViewSize / 300.0f;
  308. [s appendBezierPathWithArcWithCenter:NSMakePoint(105.0f * f, 155.0f * f) radius:65.0f * f startAngle: 90.0f endAngle:270.0f clockwise:NO];
  309. [s appendBezierPathWithArcWithCenter:NSMakePoint(150.0f * f, 85.0f * f) radius: 5.0f * f startAngle: 90.0f endAngle: 0.0f clockwise:YES];
  310. [s appendBezierPathWithArcWithCenter:NSMakePoint(160.0f * f, 65.0f * f) radius: 5.0f * f startAngle:180.0f endAngle:270.0f clockwise:NO];
  311. [s appendBezierPathWithArcWithCenter:NSMakePoint(165.0f * f, 65.0f * f) radius: 5.0f * f startAngle:270.0f endAngle:-45.0f clockwise:NO];
  312. [s appendBezierPathWithArcWithCenter:NSMakePoint(200.0f * f, 105.0f * f) radius:10.0f * f startAngle:-45.0f endAngle: 45.0f clockwise:NO];
  313. [s appendBezierPathWithArcWithCenter:NSMakePoint(165.0f * f, 145.0f * f) radius: 5.0f * f startAngle: 45.0f endAngle: 90.0f clockwise:NO];
  314. [s appendBezierPathWithArcWithCenter:NSMakePoint(160.0f * f, 145.0f * f) radius: 5.0f * f startAngle: 90.0f endAngle:180.0f clockwise:NO];
  315. [s appendBezierPathWithArcWithCenter:NSMakePoint(150.0f * f, 125.0f * f) radius: 5.0f * f startAngle: 0.0f endAngle:270.0f clockwise:YES];
  316. [s appendBezierPathWithArcWithCenter:NSMakePoint(105.0f * f, 155.0f * f) radius:35.0f * f startAngle:270.0f endAngle: 90.0f clockwise:YES];
  317. [s appendBezierPathWithArcWithCenter:NSMakePoint(195.0f * f, 155.0f * f) radius:35.0f * f startAngle: 90.0f endAngle: 0.0f clockwise:YES];
  318. [s appendBezierPathWithArcWithCenter:NSMakePoint(245.0f * f, 155.0f * f) radius:15.0f * f startAngle:180.0f endAngle: 0.0f clockwise:NO];
  319. [s appendBezierPathWithArcWithCenter:NSMakePoint(195.0f * f, 155.0f * f) radius:65.0f * f startAngle: 0.0f endAngle: 90.0f clockwise:NO];
  320. [s fill];
  321. }
  322. @end
  323. @implementation PGLoopedRightGraphic
  324. #pragma mark -PGAlertGraphic
  325. - (void)drawInView:(PGAlertView *)anAlertView
  326. {
  327. [self flipHorizontally];
  328. [super drawInView:anAlertView];
  329. }
  330. @end
  331. @implementation PGLoadingGraphic
  332. #pragma mark +PGLoadingGraphic
  333. + (id)loadingGraphic
  334. {
  335. return [[[PGLoadingGraphic alloc] init] autorelease];
  336. }
  337. #pragma mark -PGLoadingGraphic
  338. @synthesize progress = _progress;
  339. - (void)setProgress:(CGFloat)progress
  340. {
  341. _progress = MIN(MAX(progress, 0.0f), 1.0f);
  342. }
  343. #pragma mark -PGAlertGraphic
  344. - (NSTimeInterval)fadeOutDelay
  345. {
  346. return CGFLOAT_MAX;
  347. }
  348. - (NSTimeInterval)frameDelay
  349. {
  350. return 1.0f / 12.0f;
  351. }
  352. - (NSUInteger)frameCount
  353. {
  354. return 12;
  355. }
  356. #pragma mark -
  357. - (void)drawInView:(PGAlertView *)anAlertView
  358. {
  359. [super drawInView:anAlertView];
  360. CGFloat const f = PGAlertViewSize / 300.0f;
  361. if(_progress) {
  362. NSBezierPath *const progressPath = [NSBezierPath bezierPath];
  363. NSPoint const center = NSMakePoint(150.0f * f, 150.0f * f);
  364. [progressPath appendBezierPathWithArcWithCenter:center radius:110.0f * f startAngle:90.0f endAngle:90.0f - _progress * 360.0f clockwise:YES];
  365. [progressPath appendBezierPathWithArcWithCenter:center radius:55.0f * f startAngle:90.0f - _progress * 360.0f endAngle:90.0f clockwise:NO];
  366. [[NSColor PG_bezelForegroundColor] set];
  367. [progressPath fill];
  368. } else {
  369. [NSBezierPath PG_drawSpinnerInRect:NSMakeRect(40.0f * f, 40.0f * f, 220.0f * f, 220.0f * f) startAtPetal:[anAlertView frameCount]];
  370. }
  371. }
  372. #pragma mark -
  373. - (void)animateOneFrame:(PGAlertView *)anAlertView
  374. {
  375. CGFloat const f = PGAlertViewSize / 300.0f;
  376. [anAlertView setNeedsDisplayInRect:NSMakeRect( 25.0f * f, 50.0f * f, 25.0f * f, 200.0f * f)];
  377. [anAlertView setNeedsDisplayInRect:NSMakeRect( 50.0f * f, 25.0f * f, 200.0f * f, 250.0f * f)];
  378. [anAlertView setNeedsDisplayInRect:NSMakeRect(250.0f * f, 50.0f * f, 25.0f * f, 200.0f * f)];
  379. }
  380. #pragma mark -<NSObject>
  381. - (NSUInteger)hash
  382. {
  383. return (NSUInteger)self;
  384. }
  385. - (BOOL)isEqual:(id)anObject
  386. {
  387. return anObject == self;
  388. }
  389. @end
  390. @implementation PGBezierPathIconGraphic
  391. #pragma mark +PGBezierPathIconGraphic
  392. + (id)graphicWithIconType:(AEIconType)type
  393. {
  394. return [[[self alloc] initWithIconType:type] autorelease];
  395. }
  396. #pragma mark -PGBezierPathIconGraphic
  397. - (id)initWithIconType:(AEIconType)type
  398. {
  399. if((self = [super init])) {
  400. _iconType = type;
  401. }
  402. return self;
  403. }
  404. #pragma mark -PGAlertGraphic
  405. - (NSTimeInterval)fadeOutDelay
  406. {
  407. return 1.0f;
  408. }
  409. #pragma mark -
  410. - (void)drawInView:(PGAlertView *)anAlertView
  411. {
  412. [super drawInView:anAlertView];
  413. NSRect const b = [anAlertView bounds];
  414. [[NSColor PG_bezelForegroundColor] set];
  415. [NSBezierPath PG_drawIcon:_iconType inRect:PGCenteredSizeInRect(NSMakeSize(PGAlertViewSize / 2.0f, PGAlertViewSize / 2.0f), b)];
  416. }
  417. #pragma mark -<NSObject>
  418. - (NSUInteger)hash
  419. {
  420. return [[self class] hash] ^ _iconType;
  421. }
  422. - (BOOL)isEqual:(id)anObject
  423. {
  424. return [anObject isMemberOfClass:[self class]] && ((PGBezierPathIconGraphic *)anObject)->_iconType == _iconType;
  425. }
  426. @end