FreeRDP
BlockAlertView.m
1 //
2 // BlockAlertView.m
3 //
4 //
5 
6 #import "BlockAlertView.h"
7 #import "BlockBackground.h"
8 #import "BlockUI.h"
9 
10 @implementation BlockAlertView
11 
12 @synthesize view = _view;
13 @synthesize backgroundImage = _backgroundImage;
14 @synthesize vignetteBackground = _vignetteBackground;
15 
16 static UIImage *background = nil;
17 static UIImage *backgroundlandscape = nil;
18 static UIFont *titleFont = nil;
19 static UIFont *messageFont = nil;
20 static UIFont *buttonFont = nil;
21 
22 #pragma mark - init
23 
24 + (void)initialize
25 {
26  if (self == [BlockAlertView class])
27  {
28  background = [UIImage imageNamed:kAlertViewBackground];
29  background =
30  [[background stretchableImageWithLeftCapWidth:0
31  topCapHeight:kAlertViewBackgroundCapHeight] retain];
32 
33  backgroundlandscape = [UIImage imageNamed:kAlertViewBackgroundLandscape];
34  backgroundlandscape = [[backgroundlandscape
35  stretchableImageWithLeftCapWidth:0
36  topCapHeight:kAlertViewBackgroundCapHeight] retain];
37 
38  titleFont = [kAlertViewTitleFont retain];
39  messageFont = [kAlertViewMessageFont retain];
40  buttonFont = [kAlertViewButtonFont retain];
41  }
42 }
43 
44 + (BlockAlertView *)alertWithTitle:(NSString *)title message:(NSString *)message
45 {
46  return [[[BlockAlertView alloc] initWithTitle:title message:message] autorelease];
47 }
48 
49 + (void)showInfoAlertWithTitle:(NSString *)title message:(NSString *)message
50 {
51  BlockAlertView *alert = [[BlockAlertView alloc] initWithTitle:title message:message];
52  [alert setCancelButtonWithTitle:NSLocalizedString(@"Dismiss", nil) block:nil];
53  [alert show];
54  [alert release];
55 }
56 
57 + (void)showErrorAlert:(NSError *)error
58 {
59  BlockAlertView *alert = [[BlockAlertView alloc]
60  initWithTitle:NSLocalizedString(@"Operation Failed", nil)
61  message:[NSString
62  stringWithFormat:NSLocalizedString(
63  @"The operation did not complete successfully: %@",
64  nil),
65  error]];
66  [alert setCancelButtonWithTitle:@"Dismiss" block:nil];
67  [alert show];
68  [alert release];
69 }
70 
72 #pragma mark - NSObject
73 
74 - (void)addComponents:(CGRect)frame
75 {
76  if (_title)
77  {
78  CGSize size = [_title sizeWithFont:titleFont
79  constrainedToSize:CGSizeMake(frame.size.width - kAlertViewBorder * 2, 1000)
80  lineBreakMode:NSLineBreakByWordWrapping];
81 
82  UILabel *labelView = [[UILabel alloc]
83  initWithFrame:CGRectMake(kAlertViewBorder, _height,
84  frame.size.width - kAlertViewBorder * 2, size.height)];
85  labelView.font = titleFont;
86  labelView.numberOfLines = 0;
87  labelView.lineBreakMode = NSLineBreakByWordWrapping;
88  labelView.textColor = kAlertViewTitleTextColor;
89  labelView.backgroundColor = [UIColor clearColor];
90  labelView.textAlignment = NSTextAlignmentCenter;
91  labelView.shadowColor = kAlertViewTitleShadowColor;
92  labelView.shadowOffset = kAlertViewTitleShadowOffset;
93  labelView.text = _title;
94  [_view addSubview:labelView];
95  [labelView release];
96 
97  _height += size.height + kAlertViewBorder;
98  }
99 
100  if (_message)
101  {
102  CGSize size =
103  [_message sizeWithFont:messageFont
104  constrainedToSize:CGSizeMake(frame.size.width - kAlertViewBorder * 2, 1000)
105  lineBreakMode:NSLineBreakByWordWrapping];
106 
107  UILabel *labelView = [[UILabel alloc]
108  initWithFrame:CGRectMake(kAlertViewBorder, _height,
109  frame.size.width - kAlertViewBorder * 2, size.height)];
110  labelView.font = messageFont;
111  labelView.numberOfLines = 0;
112  labelView.lineBreakMode = NSLineBreakByWordWrapping;
113  labelView.textColor = kAlertViewMessageTextColor;
114  labelView.backgroundColor = [UIColor clearColor];
115  labelView.textAlignment = NSTextAlignmentCenter;
116  labelView.shadowColor = kAlertViewMessageShadowColor;
117  labelView.shadowOffset = kAlertViewMessageShadowOffset;
118  labelView.text = _message;
119  [_view addSubview:labelView];
120  [labelView release];
121 
122  _height += size.height + kAlertViewBorder;
123  }
124 }
125 
126 - (void)setupDisplay
127 {
128  [[_view subviews] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
129  [obj removeFromSuperview];
130  }];
131 
132  UIWindow *parentView = [BlockBackground sharedInstance];
133  CGRect frame = parentView.bounds;
134  frame.origin.x = floorf((frame.size.width - background.size.width) * 0.5);
135  frame.size.width = background.size.width;
136 
137  UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
138  if (UIInterfaceOrientationIsLandscape(orientation))
139  {
140  frame.size.width += 150;
141  frame.origin.x -= 75;
142  }
143 
144  _view.frame = frame;
145 
146  _height = kAlertViewBorder + 15;
147 
148  if (NeedsLandscapePhoneTweaks)
149  {
150  _height -= 15; // landscape phones need to trimmed a bit
151  }
152 
153  [self addComponents:frame];
154 
155  if (_shown)
156  [self show];
157 }
158 
159 - (id)initWithTitle:(NSString *)title message:(NSString *)message
160 {
161  self = [super init];
162 
163  if (self)
164  {
165  _title = [title copy];
166  _message = [message copy];
167 
168  _view = [[UIView alloc] init];
169 
170  _view.autoresizingMask =
171  UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin |
172  UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
173 
174  _blocks = [[NSMutableArray alloc] init];
175 
176  [[NSNotificationCenter defaultCenter]
177  addObserver:self
178  selector:@selector(setupDisplay)
179  name:UIApplicationDidChangeStatusBarOrientationNotification
180  object:nil];
181 
182  if ([self class] == [BlockAlertView class])
183  [self setupDisplay];
184 
185  _vignetteBackground = NO;
186  }
187 
188  return self;
189 }
190 
191 - (void)dealloc
192 {
193  [_title release];
194  [_message release];
195  [_backgroundImage release];
196  [_view release];
197  [_blocks release];
198  [super dealloc];
199 }
200 
202 #pragma mark - Public
203 
204 - (void)addButtonWithTitle:(NSString *)title color:(NSString *)color block:(void (^)())block
205 {
206  [_blocks addObject:[NSArray arrayWithObjects:block ? [[block copy] autorelease] : [NSNull null],
207  title, color, nil]];
208 }
209 
210 - (void)addButtonWithTitle:(NSString *)title block:(void (^)())block
211 {
212  [self addButtonWithTitle:title color:@"gray" block:block];
213 }
214 
215 - (void)setCancelButtonWithTitle:(NSString *)title block:(void (^)())block
216 {
217  [self addButtonWithTitle:title color:@"black" block:block];
218 }
219 
220 - (void)setDestructiveButtonWithTitle:(NSString *)title block:(void (^)())block
221 {
222  [self addButtonWithTitle:title color:@"red" block:block];
223 }
224 
225 - (void)show
226 {
227  _shown = YES;
228 
229  BOOL isSecondButton = NO;
230  NSUInteger index = 0;
231  for (NSUInteger i = 0; i < _blocks.count; i++)
232  {
233  NSArray *block = [_blocks objectAtIndex:i];
234  NSString *title = [block objectAtIndex:1];
235  NSString *color = [block objectAtIndex:2];
236 
237  UIImage *image =
238  [UIImage imageNamed:[NSString stringWithFormat:@"alert-%@-button.png", color]];
239  image = [image stretchableImageWithLeftCapWidth:(int)(image.size.width + 1) >> 1
240  topCapHeight:0];
241 
242  CGFloat maxHalfWidth = floorf((_view.bounds.size.width - kAlertViewBorder * 3) * 0.5);
243  CGFloat width = _view.bounds.size.width - kAlertViewBorder * 2;
244  CGFloat xOffset = kAlertViewBorder;
245  if (isSecondButton)
246  {
247  width = maxHalfWidth;
248  xOffset = width + kAlertViewBorder * 2;
249  isSecondButton = NO;
250  }
251  else if (i + 1 < _blocks.count)
252  {
253  // In this case there's another button.
254  // Let's check if they fit on the same line.
255  CGSize size = [title sizeWithFont:buttonFont
256  minFontSize:10
257  actualFontSize:nil
258  forWidth:_view.bounds.size.width - kAlertViewBorder * 2
259  lineBreakMode:NSLineBreakByClipping];
260 
261  if (size.width < maxHalfWidth - kAlertViewBorder)
262  {
263  // It might fit. Check the next Button
264  NSArray *block2 = [_blocks objectAtIndex:i + 1];
265  NSString *title2 = [block2 objectAtIndex:1];
266  size = [title2 sizeWithFont:buttonFont
267  minFontSize:10
268  actualFontSize:nil
269  forWidth:_view.bounds.size.width - kAlertViewBorder * 2
270  lineBreakMode:NSLineBreakByClipping];
271 
272  if (size.width < maxHalfWidth - kAlertViewBorder)
273  {
274  // They'll fit!
275  isSecondButton = YES; // For the next iteration
276  width = maxHalfWidth;
277  }
278  }
279  }
280  else if (_blocks.count == 1)
281  {
282  // In this case this is the ony button. We'll size according to the text
283  CGSize size = [title sizeWithFont:buttonFont
284  minFontSize:10
285  actualFontSize:nil
286  forWidth:_view.bounds.size.width - kAlertViewBorder * 2
287  lineBreakMode:UILineBreakModeClip];
288 
289  size.width = MAX(size.width, 80);
290  if (size.width + 2 * kAlertViewBorder < width)
291  {
292  width = size.width + 2 * kAlertViewBorder;
293  xOffset = floorf((_view.bounds.size.width - width) * 0.5);
294  }
295  }
296 
297  UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
298  button.frame = CGRectMake(xOffset, _height, width, kAlertButtonHeight);
299  button.titleLabel.font = buttonFont;
300  if (IOS_LESS_THAN_6)
301  {
302 #pragma clang diagnostic push
303 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
304  button.titleLabel.minimumFontSize = 10;
305 #pragma clang diagnostic pop
306  }
307  else
308  {
309  button.titleLabel.adjustsFontSizeToFitWidth = YES;
310  button.titleLabel.adjustsLetterSpacingToFitWidth = YES;
311  button.titleLabel.minimumScaleFactor = 0.1;
312  }
313  button.titleLabel.textAlignment = NSTextAlignmentCenter;
314  button.titleLabel.shadowOffset = kAlertViewButtonShadowOffset;
315  button.backgroundColor = [UIColor clearColor];
316  button.tag = i + 1;
317 
318  [button setBackgroundImage:image forState:UIControlStateNormal];
319  [button setTitleColor:kAlertViewButtonTextColor forState:UIControlStateNormal];
320  [button setTitleShadowColor:kAlertViewButtonShadowColor forState:UIControlStateNormal];
321  [button setTitle:title forState:UIControlStateNormal];
322  button.accessibilityLabel = title;
323 
324  [button addTarget:self
325  action:@selector(buttonClicked:)
326  forControlEvents:UIControlEventTouchUpInside];
327 
328  [_view addSubview:button];
329 
330  if (!isSecondButton)
331  _height += kAlertButtonHeight + kAlertViewBorder;
332 
333  index++;
334  }
335 
336  _height += 10; // Margin for the shadow
337 
338  if (_height < background.size.height)
339  {
340  CGFloat offset = background.size.height - _height;
341  _height = background.size.height;
342  CGRect frame;
343  for (NSUInteger i = 0; i < _blocks.count; i++)
344  {
345  UIButton *btn = (UIButton *)[_view viewWithTag:i + 1];
346  frame = btn.frame;
347  frame.origin.y += offset;
348  btn.frame = frame;
349  }
350  }
351 
352  CGRect frame = _view.frame;
353  frame.origin.y = -_height;
354  frame.size.height = _height;
355  _view.frame = frame;
356 
357  UIImageView *modalBackground = [[UIImageView alloc] initWithFrame:_view.bounds];
358 
359  if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation]))
360  modalBackground.image = backgroundlandscape;
361  else
362  modalBackground.image = background;
363 
364  modalBackground.contentMode = UIViewContentModeScaleToFill;
365  modalBackground.autoresizingMask =
366  UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
367  [_view insertSubview:modalBackground atIndex:0];
368  [modalBackground release];
369 
370  if (_backgroundImage)
371  {
372  [BlockBackground sharedInstance].backgroundImage = _backgroundImage;
373  [_backgroundImage release];
374  _backgroundImage = nil;
375  }
376 
377  [BlockBackground sharedInstance].vignetteBackground = _vignetteBackground;
378  [[BlockBackground sharedInstance] addToMainWindow:_view];
379 
380  __block CGPoint center = _view.center;
381  center.y = floorf([BlockBackground sharedInstance].bounds.size.height * 0.5) + kAlertViewBounce;
382 
383  _cancelBounce = NO;
384 
385  [UIView animateWithDuration:0.4
386  delay:0.0
387  options:UIViewAnimationCurveEaseOut
388  animations:^{
389  [BlockBackground sharedInstance].alpha = 1.0f;
390  _view.center = center;
391  }
392  completion:^(BOOL finished) {
393  if (_cancelBounce)
394  return;
395 
396  [UIView animateWithDuration:0.1
397  delay:0.0
398  options:0
399  animations:^{
400  center.y -= kAlertViewBounce;
401  _view.center = center;
402  }
403  completion:^(BOOL finished) {
404  [[NSNotificationCenter defaultCenter]
405  postNotificationName:@"AlertViewFinishedAnimations"
406  object:nil];
407  }];
408  }];
409 
410  [self retain];
411 }
412 
413 - (void)dismissWithClickedButtonIndex:(NSInteger)buttonIndex animated:(BOOL)animated
414 {
415  _shown = NO;
416 
417  [[NSNotificationCenter defaultCenter] removeObserver:self];
418 
419  if (buttonIndex >= 0 && buttonIndex < [_blocks count])
420  {
421  id obj = [[_blocks objectAtIndex:buttonIndex] objectAtIndex:0];
422  if (![obj isEqual:[NSNull null]])
423  {
424  ((void (^)())obj)();
425  }
426  }
427 
428  if (animated)
429  {
430  [UIView animateWithDuration:0.1
431  delay:0.0
432  options:0
433  animations:^{
434  CGPoint center = _view.center;
435  center.y += 20;
436  _view.center = center;
437  }
438  completion:^(BOOL finished) {
439  [UIView animateWithDuration:0.4
440  delay:0.0
441  options:UIViewAnimationCurveEaseIn
442  animations:^{
443  CGRect frame = _view.frame;
444  frame.origin.y = -frame.size.height;
445  _view.frame = frame;
446  [[BlockBackground sharedInstance] reduceAlphaIfEmpty];
447  }
448  completion:^(BOOL finished) {
449  [[BlockBackground sharedInstance] removeView:_view];
450  [_view release];
451  _view = nil;
452  [self autorelease];
453  }];
454  }];
455  }
456  else
457  {
458  [[BlockBackground sharedInstance] removeView:_view];
459  [_view release];
460  _view = nil;
461  [self autorelease];
462  }
463 }
464 
466 #pragma mark - Action
467 
468 - (void)buttonClicked:(id)sender
469 {
470  /* Run the button's block */
471  int buttonIndex = [sender tag] - 1;
472  [self dismissWithClickedButtonIndex:buttonIndex animated:YES];
473 }
474 
475 @end