38 CPWarningAlertStyle = 0;
43 CPInformationalAlertStyle = 1;
48 CPCriticalAlertStyle = 2;
50 var bottomHeight = 71;
77 BOOL _showSuppressionButton;
79 CPAlertStyle _alertStyle;
90 Function _didEndBlock;
92 _CPAlertThemeView _themeView;
94 int _defaultWindowStyle;
104 #pragma mark Creating Alerts
116 + (
CPAlert)alertWithMessageText:(
CPString)aMessage defaultButton:(
CPString)defaultButtonTitle alternateButton:(
CPString)alternateButtonTitle otherButton:(
CPString)otherButtonTitle informativeTextWithFormat:(
CPString)informativeText
118 var newAlert = [[
self alloc] init];
120 [newAlert setMessageText:aMessage];
121 [newAlert addButtonWithTitle:defaultButtonTitle];
123 if (alternateButtonTitle)
124 [newAlert addButtonWithTitle:alternateButtonTitle];
126 if (otherButtonTitle)
127 [newAlert addButtonWithTitle:otherButtonTitle];
130 [newAlert setInformativeText:informativeText];
143 var newAlert = [[
self alloc] init];
145 [newAlert setMessageText:anErrorMessage];
146 [newAlert setAlertStyle:CPCriticalAlertStyle];
161 _alertStyle = CPWarningAlertStyle;
164 _defaultWindowStyle = _CPModalWindowMask;
165 _themeView = [_CPAlertThemeView new];
167 _messageLabel = [
CPTextField labelWithTitle:@"Alert"];
172 _alertHelpButton = [[
CPButton alloc] initWithFrame:CGRectMake(0.0, 0.0, 16.0, 16.0)];
173 [_alertHelpButton setTarget:self];
174 [_alertHelpButton setAction:@selector(_showHelp:)];
180 #pragma mark Accessors
184 return [_themeView theme];
192 - (void)setTheme:(
CPTheme)aTheme
194 if (aTheme === [
self theme])
197 if (aTheme === [
CPTheme defaultHudTheme])
204 [_themeView setTheme:aTheme];
207 - (void)setValue:(
id)aValue forThemeAttribute:(
CPString)aName
209 [_themeView setValue:aValue forThemeAttribute:aName];
212 - (void)setValue:(
id)aValue forThemeAttribute:(
CPString)aName inState:(CPThemeState)aState
214 [_themeView setValue:aValue forThemeAttribute:aName inState:aState];
219 - (void)setWindowStyle:(
int)style
221 CPLog.warn(
"DEPRECATED: setWindowStyle: is deprecated. use setTheme: instead");
223 [
self setTheme:(style === CPHUDBackgroundWindowMask) ? [
CPTheme defaultHudTheme] : [
CPTheme defaultTheme]];
229 CPLog.warn(
"DEPRECATED: windowStyle: is deprecated. use theme instead");
230 return _defaultWindowStyle;
239 - (void)setMessageText:(
CPString)text
241 [_messageLabel setStringValue:text];
252 return [_messageLabel stringValue];
260 - (void)setInformativeText:(
CPString)text
262 [_informativeLabel setStringValue:text];
273 return [_informativeLabel stringValue];
285 [_window setTitle:aTitle];
293 - (void)setAccessoryView:(
CPView)aView
295 _accessoryView = aView;
304 - (void)setShowsSuppressionButton:(BOOL)shouldShowSuppressionButton
306 _showSuppressionButton = shouldShowSuppressionButton;
310 #pragma mark Accessing Buttons
324 - (void)addButtonWithTitle:(
CPString)aTitle
326 var bounds = [[_window contentView] bounds],
327 count = [_buttons count],
329 button = [[
CPButton alloc] initWithFrame:CGRectMakeZero()];
331 [button setTitle:aTitle];
332 [button setTag:count];
333 [button setTarget:self];
334 [button setAction:@selector(_takeReturnCodeFrom:)];
336 [[_window contentView] addSubview:button];
339 [button setKeyEquivalent:CPCarriageReturnCharacter];
340 else if ([aTitle lowercaseString] ===
@"cancel")
341 [button setKeyEquivalent:CPEscapeFunctionKey];
343 [_buttons insertObject:button atIndex:0];
351 - (void)_layoutMessageView
353 var inset = [_themeView currentValueForThemeAttribute:@"content-inset"],
354 sizeWithFontCorrection = 6.0,
356 messageLabelTextSize;
358 [_messageLabel setTextColor:[_themeView currentValueForThemeAttribute:@"message-text-color"]];
359 [_messageLabel setFont:[_themeView currentValueForThemeAttribute:@"message-text-font"]];
360 [_messageLabel setTextShadowColor:[_themeView currentValueForThemeAttribute:@"message-text-shadow-color"]];
361 [_messageLabel setTextShadowOffset:[_themeView currentValueForThemeAttribute:@"message-text-shadow-offset"]];
362 [_messageLabel setAlignment:[_themeView currentValueForThemeAttribute:@"message-text-alignment"]];
363 [_messageLabel setLineBreakMode:CPLineBreakByWordWrapping];
365 messageLabelWidth = CGRectGetWidth([[_window contentView]
frame]) - inset.left - inset.right;
366 messageLabelTextSize = [[_messageLabel stringValue] sizeWithFont:[_messageLabel font] inWidth:messageLabelWidth];
368 [_messageLabel setFrame:CGRectMake(inset.left, inset.top, messageLabelTextSize.width, messageLabelTextSize.height + sizeWithFontCorrection)];
374 - (void)_layoutInformativeView
376 var inset = [_themeView currentValueForThemeAttribute:@"content-inset"],
377 defaultElementsMargin = [_themeView currentValueForThemeAttribute:@"default-elements-margin"],
378 sizeWithFontCorrection = 6.0,
379 informativeLabelWidth,
380 informativeLabelOriginY,
381 informativeLabelTextSize;
383 [_informativeLabel setTextColor:[_themeView currentValueForThemeAttribute:@"informative-text-color"]];
384 [_informativeLabel setFont:[_themeView currentValueForThemeAttribute:@"informative-text-font"]];
385 [_informativeLabel setTextShadowColor:[_themeView currentValueForThemeAttribute:@"informative-text-shadow-color"]];
386 [_informativeLabel setTextShadowOffset:[_themeView currentValueForThemeAttribute:@"informative-text-shadow-offset"]];
387 [_informativeLabel setAlignment:[_themeView currentValueForThemeAttribute:@"informative-text-alignment"]];
388 [_informativeLabel setLineBreakMode:CPLineBreakByWordWrapping];
390 informativeLabelWidth = CGRectGetWidth([[_window contentView]
frame]) - inset.left - inset.right;
391 informativeLabelOriginY = [_messageLabel frameOrigin].y + [_messageLabel frameSize].height + defaultElementsMargin;
392 informativeLabelTextSize = [[_informativeLabel stringValue] sizeWithFont:[_informativeLabel font] inWidth:informativeLabelWidth];
394 [_informativeLabel setFrame:CGRectMake(inset.left, informativeLabelOriginY, informativeLabelTextSize.width, informativeLabelTextSize.height + sizeWithFontCorrection)];
400 - (void)_layoutAccessoryView
405 var inset = [_themeView currentValueForThemeAttribute:@"content-inset"],
406 defaultElementsMargin = [_themeView currentValueForThemeAttribute:@"default-elements-margin"],
407 accessoryViewWidth = CGRectGetWidth([[_window contentView]
frame]) - inset.left - inset.right,
408 accessoryViewOriginY = CGRectGetMaxY([_informativeLabel
frame]) + defaultElementsMargin;
410 [_accessoryView setFrameOrigin:CGPointMake(inset.left, accessoryViewOriginY)];
411 [[_window contentView] addSubview:_accessoryView];
417 - (void)_layoutSuppressionButton
419 if (!_showSuppressionButton)
422 var inset = [_themeView currentValueForThemeAttribute:@"content-inset"],
423 suppressionViewXOffset = [_themeView currentValueForThemeAttribute:@"suppression-button-x-offset"],
424 suppressionViewYOffset = [_themeView currentValueForThemeAttribute:@"suppression-button-y-offset"],
425 defaultElementsMargin = [_themeView currentValueForThemeAttribute:@"default-elements-margin"],
426 suppressionButtonViewOriginY = CGRectGetMaxY([(_accessoryView || _informativeLabel)
frame]) + defaultElementsMargin + suppressionViewYOffset;
428 [_suppressionButton setTextColor:[_themeView currentValueForThemeAttribute:@"suppression-button-text-color"]];
429 [_suppressionButton setFont:[_themeView currentValueForThemeAttribute:@"suppression-button-text-font"]];
430 [_suppressionButton setTextShadowColor:[_themeView currentValueForThemeAttribute:@"suppression-button-text-shadow-color"]];
431 [_suppressionButton setTextShadowOffset:[_themeView currentValueForThemeAttribute:@"suppression-button-text-shadow-offset"]];
432 [_suppressionButton sizeToFit];
434 [_suppressionButton setFrameOrigin:CGPointMake(inset.left + suppressionViewXOffset, suppressionButtonViewOriginY)];
435 [[_window contentView] addSubview:_suppressionButton];
441 - (CGSize)_layoutButtonsFromView:(
CPView)lastView
443 var inset = [_themeView currentValueForThemeAttribute:@"content-inset"],
444 minimumSize = [_themeView currentValueForThemeAttribute:@"size"],
445 buttonOffset = [_themeView currentValueForThemeAttribute:@"button-offset"],
446 helpLeftOffset = [_themeView currentValueForThemeAttribute:@"help-image-left-offset"],
447 aRepresentativeButton = [_buttons objectAtIndex:0],
448 defaultElementsMargin = [_themeView currentValueForThemeAttribute:@"default-elements-margin"],
449 panelSize = [[_window contentView] frame].size,
453 theme = [
self theme],
456 [aRepresentativeButton setTheme:[
self theme]];
457 [aRepresentativeButton sizeToFit];
459 panelSize.height = CGRectGetMaxY([lastView
frame]) + defaultElementsMargin + [aRepresentativeButton frameSize].height;
460 if (panelSize.height < minimumSize.height)
461 panelSize.height = minimumSize.height;
463 buttonsOriginY = panelSize.height - [aRepresentativeButton frameSize].height + buttonOffset;
464 offsetX = panelSize.width - inset.right;
466 switch ([_window styleMask])
468 case _CPModalWindowMask:
469 buttonMarginY = [_themeView currentValueForThemeAttribute:@"modal-window-button-margin-y"];
470 buttonMarginX = [_themeView currentValueForThemeAttribute:@"modal-window-button-margin-x"];
474 buttonMarginY = [_themeView currentValueForThemeAttribute:@"standard-window-button-margin-y"];
475 buttonMarginX = [_themeView currentValueForThemeAttribute:@"standard-window-button-margin-x"];
479 for (var i = [_buttons count] - 1; i >= 0 ; i--)
481 var button = _buttons[i];
482 [button setTheme:[
self theme]];
485 var buttonFrame = [button frame],
486 width = MAX(80.0, CGRectGetWidth(buttonFrame)),
487 height = CGRectGetHeight(buttonFrame);
490 [button setFrame:CGRectMake(offsetX + buttonMarginX, buttonsOriginY + buttonMarginY, width, height)];
496 var helpImage = [_themeView currentValueForThemeAttribute:@"help-image"],
497 helpImagePressed = [_themeView currentValueForThemeAttribute:@"help-image-pressed"],
498 helpImageSize = helpImage ? [helpImage size] : CGSizeMakeZero(),
499 helpFrame = CGRectMake(helpLeftOffset, buttonsOriginY, helpImageSize.width, helpImageSize.height);
501 [_alertHelpButton setImage:helpImage];
502 [_alertHelpButton setAlternateImage:helpImagePressed];
503 [_alertHelpButton setBordered:NO];
504 [_alertHelpButton setFrame:helpFrame];
507 panelSize.height += [aRepresentativeButton frameSize].height + inset.bottom + buttonOffset;
520 [
self _createWindowWithStyle:nil];
522 var iconOffset = [_themeView currentValueForThemeAttribute:@"image-offset"],
529 case CPWarningAlertStyle:
530 theImage = [_themeView currentValueForThemeAttribute:@"warning-image"];
532 case CPInformationalAlertStyle:
533 theImage = [_themeView currentValueForThemeAttribute:@"information-image"];
535 case CPCriticalAlertStyle:
536 theImage = [_themeView currentValueForThemeAttribute:@"error-image"];
540 [_alertImageView setImage:theImage];
542 var imageSize = theImage ? [theImage size] : CGSizeMakeZero();
543 [_alertImageView setFrame:CGRectMake(iconOffset.x, iconOffset.y, imageSize.width, imageSize.height)];
545 [
self _layoutMessageView];
546 [
self _layoutInformativeView];
547 [
self _layoutAccessoryView];
548 [
self _layoutSuppressionButton];
550 var lastView = _informativeLabel;
551 if (_showSuppressionButton)
552 lastView = _suppressionButton;
553 else if (_accessoryView)
554 lastView = _accessoryView;
556 finalSize = [
self _layoutButtonsFromView:lastView];
558 finalSize.height -= 26;
560 [_window setFrameSize:finalSize];
565 [_window setMovable:YES];
566 [_window setMovableByWindowBackground:YES];
572 #pragma mark Displaying Alerts
581 if (!([_window styleMask] & _defaultWindowStyle))
584 [
self _createWindowWithStyle:_defaultWindowStyle];
588 [CPApp runModalForWindow:_window];
595 - (void)runModalWithDidEndBlock:(Function )block
597 _didEndBlock = block;
610 - (void)beginSheetModalForWindow:(
CPWindow)aWindow modalDelegate:(
id)modalDelegate didEndSelector:(
SEL)alertDidEndSelector contextInfo:(
id)contextInfo
612 if (!([_window styleMask] & CPDocModalWindowMask))
615 [
self _createWindowWithStyle:CPDocModalWindowMask];
620 _modalDelegate = modalDelegate;
621 _didEndSelector = alertDidEndSelector;
623 [CPApp beginSheet:_window modalForWindow:aWindow modalDelegate:self didEndSelector:@selector(_alertDidEnd:returnCode:contextInfo:) contextInfo:contextInfo];
631 - (void)beginSheetModalForWindow:(
CPWindow)aWindow
633 [
self beginSheetModalForWindow:aWindow modalDelegate:nil didEndSelector:nil contextInfo:nil];
643 - (void)beginSheetModalForWindow:(
CPWindow)aWindow didEndBlock:(Function )block
645 _didEndBlock = block;
647 [
self beginSheetModalForWindow:aWindow modalDelegate:nil didEndSelector:nil contextInfo:nil];
655 - (void)_createWindowWithStyle:(
int)forceStyle
657 var
frame = CGRectMakeZero();
658 frame.size = [_themeView currentValueForThemeAttribute:@"size"];
660 _window = [[
CPPanel alloc] initWithContentRect:frame styleMask:forceStyle || _defaultWindowStyle];
661 [_window setLevel:CPStatusWindowLevel];
662 [_window setPlatformWindow:[[CPApp keyWindow] platformWindow]];
665 [_window setTitle:_title];
667 var contentView = [_window contentView],
668 count = [_buttons count];
672 [contentView addSubview:_buttons[count]];
674 [
self addButtonWithTitle:@"OK"];
676 [contentView addSubview:_messageLabel];
677 [contentView addSubview:_alertImageView];
678 [contentView addSubview:_informativeLabel];
681 [contentView addSubview:_alertHelpButton];
687 - (@action)_showHelp:(
id)aSender
689 if ([_delegate respondsToSelector:
@selector(alertShowHelp:)])
690 [_delegate alertShowHelp:self];
696 - (@action)_takeReturnCodeFrom:(
id)aSender
698 if ([_window isSheet])
700 [_window orderOut:nil];
701 [CPApp endSheet:_window returnCode:[aSender tag]];
708 [
self _alertDidEnd:_window returnCode:[aSender tag] contextInfo:nil];
715 - (void)_alertDidEnd:(
CPWindow)aWindow returnCode:(
int)returnCode contextInfo:(
id)contextInfo
719 if (typeof(_didEndBlock) ===
"function")
720 _didEndBlock(
self, returnCode);
722 CPLog.warn(
"%s: didEnd block is not a function", [
self description]);
727 else if (_modalDelegate)
730 objj_msgSend(_modalDelegate, _didEndSelector,
self, returnCode, contextInfo);
735 objj_msgSend(_delegate, _didEndSelector,
self, returnCode);
736 else if ([_delegate respondsToSelector:
@selector(alertDidEnd:returnCode:)])
737 [_delegate alertDidEnd:self returnCode:returnCode];
742 @implementation _CPAlertThemeView :
CPView
755 @"size": CGSizeMake(400.0, 110.0),
756 @"content-inset": CGInsetMake(15, 15, 15, 50),
757 @"informative-offset": 6,
758 @"button-offset": 10,
763 @"message-text-shadow-offset": CGSizeMakeZero(),
767 @"informative-text-shadow-color": [
CPNull null],
768 @"informative-text-shadow-offset": CGSizeMakeZero(),
769 @"image-offset": CGPointMake(15, 12),
774 @"help-image-left-offset": 15,
776 @"suppression-button-y-offset": 0.0,
777 @"suppression-button-x-offset": 0.0,
778 @"default-elements-margin": 3.0,
781 @"suppression-button-text-shadow-color": [
CPNull null],
782 @"suppression-button-text-shadow-offset": 0.0,
783 @"modal-window-button-margin-y": 0.0,
784 @"modal-window-button-margin-x": 0.0,
785 @"standard-window-button-margin-y": 0.0,
786 @"standard-window-button-margin-x": 0.0,
792 @implementation CPAlert (CPSynthesizedAccessors)
805 - (void)setShowsHelp:(BOOL)aValue
813 - (BOOL)showsSuppressionButton
815 return _showSuppressionButton;
821 - (void)setShowsSuppressionButton:(BOOL)aValue
823 _showSuppressionButton = aValue;
829 - (CPAlertStyle)alertStyle
837 - (void)setAlertStyle:(CPAlertStyle)aValue
839 _alertStyle = aValue;
863 return _accessoryView;
869 - (void)setAccessoryView:(
CPView)aValue
871 _accessoryView = aValue;
885 - (void)setIcon:(
CPImage)aValue
903 return _suppressionButton;
917 - (void)setDelegate:(
id)aValue
925 - (SEL)didEndSelector
927 return _didEndSelector;
933 - (void)setDidEndSelector:(
SEL)aValue
935 _didEndSelector = aValue;
941 - (_CPAlertThemeView)themeView