Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
branch: master
Fetching contributors…

Cannot retrieve contributors at this time

394 lines (299 sloc) 16.916 kB
//
// DPSetupWindow.m
// DPSetupWindow
//
// Created by Dan Palmer on 05/10/2012.
// Copyright (c) 2012 Dan Palmer. All rights reserved.
//
#import "DPSetupWindow.h"
@interface DPSetupWindow ()
@property (retain) NSImageView *imageView;
@property (retain) NSBox *contentBox;
@property (retain) NSButton *cancelButton;
@property (retain) NSButton *backButton;
@property (retain) NSButton *nextButton;
@property (copy) void(^completionHandler)(BOOL);
@property (assign) NSViewController *currentViewController;
@end
@implementation DPSetupWindow
- (id)initWithViewControllers:(NSArray *)viewControllers completionHandler:(void (^)(BOOL completed))completionHandler {
NSRect contentRect = NSMakeRect(0, 0, 580, 420);
self = [super initWithContentRect:contentRect styleMask:(NSTitledWindowMask|NSClosableWindowMask) backing:NSBackingStoreBuffered defer:YES];
if (self == nil) return nil;
if (!viewControllers || [viewControllers count] == 0) return nil;
if (!completionHandler) return nil;
currentStage = -1;
_animates = YES;
_funnelsRepresentedObjects = NO;
[self setViewControllers:viewControllers];
[self setCompletionHandler:completionHandler];
[self setContentView:[self initialiseContentViewForRect:contentRect]];
[self next:nil];
[self addNotifications];
return self;
}
- (void)dealloc{
[self removeNotifications];
}
- (NSView *)initialiseContentViewForRect:(NSRect)contentRect {
NSView *contentView = [[NSView alloc] initWithFrame:contentRect];
_cancelButton = [[NSButton alloc] initWithFrame:NSMakeRect(145, 13, 97, 32)];
[_cancelButton setBezelStyle:NSRoundedBezelStyle];
[_cancelButton setTarget:self];
[_cancelButton setAction:@selector(cancel:)];
[_cancelButton setTitle:@"Cancel"];
[_cancelButton setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]]];
[contentView addSubview:_cancelButton];
_backButton = [[NSButton alloc] initWithFrame:NSMakeRect(372, 13, 97, 32)];
[_backButton setBezelStyle:NSRoundedBezelStyle];
[_backButton setTarget:self];
[_backButton setAction:@selector(back:)];
[_backButton setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]]];
[contentView addSubview:_backButton];
_nextButton = [[NSButton alloc] initWithFrame:NSMakeRect(469, 13, 97, 32)];
[_nextButton setBezelStyle:NSRoundedBezelStyle];
[_nextButton setTarget:self];
[_nextButton setAction:@selector(next:)];
[_nextButton setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]]];
[contentView addSubview:_nextButton];
_imageView = [[NSImageView alloc] initWithFrame:NSMakeRect(-40, 60, 320, 320)];
[_imageView setAlphaValue:0.3];
[_imageView setImageScaling:NSImageScaleProportionallyUpOrDown];
[_imageView setImage:[[NSApplication sharedApplication] applicationIconImage]];
[contentView addSubview:_imageView];
_contentBox = [[NSBox alloc] initWithFrame:NSMakeRect(148, 57, 415, 345)];
[_contentBox setTitlePosition:(NSNoTitle)];
[contentView addSubview:_contentBox];
return contentView;
}
- (void) addNotifications{
[[NSNotificationCenter defaultCenter] addObserverForName:kDPNotification_addNextViewController
object:nil
queue:[NSOperationQueue currentQueue]
usingBlock:^(NSNotification *note) {
NSDictionary *userInfo = [note userInfo];
NSViewController<DPSetupWindowStageViewController> *viewController = nil;
viewController = [userInfo objectForKey:kDPNotification_key_viewController];
if (viewController) {
NSMutableArray *newViewControllers = [[self viewControllers] mutableCopy];
[newViewControllers insertObject:viewController atIndex:(currentStage + 1)];
[self setViewControllers:[NSArray arrayWithArray:newViewControllers]];
}
}];
[[NSNotificationCenter defaultCenter] addObserverForName:kDPNotification_addFinalViewController
object:nil
queue:[NSOperationQueue currentQueue]
usingBlock:^(NSNotification *note) {
NSDictionary *userInfo = [note userInfo];
NSViewController<DPSetupWindowStageViewController> *viewController = nil;
viewController = [userInfo objectForKey:kDPNotification_key_viewController];
if (viewController) {
NSArray *newViewControllers = [[self viewControllers] arrayByAddingObject:viewController];
[self setViewControllers:newViewControllers];
}
}];
[[NSNotificationCenter defaultCenter] addObserverForName:kDPNotification_deleteViewController
object:nil
queue:[NSOperationQueue currentQueue]
usingBlock:^(NSNotification *note) {
NSDictionary *userInfo = [note userInfo];
id mustDeleteController = nil;
Class mustDeleteClass = nil;
mustDeleteClass = [userInfo objectForKey:kDPNotification_key_viewControllerClass];
NSMutableArray *viewControllers = [[self viewControllers] mutableCopy];
for (id controller in viewControllers)
if ([controller class]==mustDeleteClass)
mustDeleteController = controller;
if (mustDeleteController &&
mustDeleteController != [viewControllers objectAtIndex:currentStage]) {
@try {
[self deregisterObserversForViewController:mustDeleteController];
}
@catch (NSException *exception) {}
[viewControllers removeObject:mustDeleteController];
[self setViewControllers:[NSArray arrayWithArray:viewControllers]];
}
}];
}
- (void) removeNotifications{
[[NSNotificationCenter defaultCenter] removeObserver:self
name:kDPNotification_addNextViewController
object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:kDPNotification_addFinalViewController
object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:kDPNotification_deleteViewController
object:nil];
}
#pragma mark -
#pragma mark Customisation
- (void)setBackgroundImage:(NSImage *)backgroundImage {
_backgroundImage = backgroundImage;
[[self imageView] setImage:backgroundImage];
}
- (NSImage *)backgroundImage {
return _backgroundImage;
}
#pragma mark -
#pragma mark Flow Control
typedef enum {
DPSetupWindowNextDirection = 1,
DPSetupWindowBackDirection = -1
} DPSetupWindowDirection;
- (void)progressToNextStage {
[self shift:DPSetupWindowNextDirection];
}
- (void)revertToPreviousStage {
[self shift:DPSetupWindowBackDirection];
}
- (void)resetToZeroStage{
for (id viewController in [self viewControllers])
if ([viewController respondsToSelector:@selector(resetToInitialState)])
[viewController resetToInitialState];
NSViewController<DPSetupWindowStageViewController> *previousViewController = nil;
if(currentStage>0 &&
currentStage<=[[self viewControllers] count])
previousViewController = [[self viewControllers] objectAtIndex:currentStage];
else
return;
//rewind
currentStage = 0;
NSViewController<DPSetupWindowStageViewController> *nextViewController = [[self viewControllers] objectAtIndex:currentStage];
NSView *view = [nextViewController view];
[nextViewController performSelectorIfExists:@selector(willProgressToStage)];
void (^finished)() = ^{
[previousViewController performSelectorIfExists:@selector(didProgressToNextStage)];
[nextViewController performSelectorIfExists:@selector(didProgressToStage)];
};
[view setFrame:NSMakeRect(0, 0, 400, 330)];
if (previousViewController)
[[previousViewController view] removeFromSuperviewWithoutNeedingDisplay];
[[self contentBox] addSubview:view];
finished();
[self registerObserversForPreviousViewController:previousViewController nextViewController:nextViewController];
[self recalculateButtonEnabledStates];
[self resetButtonTitles];
}
- (void)next:(id)sender {
[self shift:DPSetupWindowNextDirection];
}
- (void)back:(id)sender {
[self shift:DPSetupWindowBackDirection];
}
- (void)shift:(DPSetupWindowDirection)direction {
if ((currentStage == [[self viewControllers] count] && direction == DPSetupWindowNextDirection) ||
(currentStage == 0 && direction == DPSetupWindowBackDirection)) {
return;
}
NSViewController<DPSetupWindowStageViewController> *previousViewController = nil;
if (currentStage >= 0 && currentStage < [[self viewControllers] count]) {
previousViewController = [[self viewControllers] objectAtIndex:currentStage];
}
if (direction == DPSetupWindowNextDirection) {
[previousViewController performSelectorIfExists:@selector(willProgressToNextStage)];
} else if (direction == DPSetupWindowBackDirection) {
[previousViewController performSelectorIfExists:@selector(willRevertToPreviousStage)];
}
NSInteger nextStage = currentStage + direction;
if (nextStage == [[self viewControllers] count]) {
//[self deregisterObserversForViewController:previousViewController];
[self completionHandler](YES);
return;
}
NSViewController<DPSetupWindowStageViewController> *nextViewController = [[self viewControllers] objectAtIndex:nextStage];
NSView *view = [nextViewController view];
if (direction == DPSetupWindowNextDirection) {
[nextViewController performSelectorIfExists:@selector(willProgressToStage)];
} else if (direction == DPSetupWindowBackDirection) {
[nextViewController performSelectorIfExists:@selector(willRevertToStage)];
}
void (^finished)() = ^{
if (direction == DPSetupWindowNextDirection) {
[previousViewController performSelectorIfExists:@selector(didProgressToNextStage)];
[nextViewController performSelectorIfExists:@selector(didProgressToStage)];
} else if (direction == DPSetupWindowBackDirection) {
[previousViewController performSelectorIfExists:@selector(didRevertToPreviousStage)];
[nextViewController performSelectorIfExists:@selector(didRevertToStage)];
}
};
if ([self funnelsRepresentedObjects]) {
[nextViewController setRepresentedObject:[previousViewController representedObject]];
}
if ([self animates] && previousViewController) {
[NSAnimationContext beginGrouping];
if ([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask) {
[[NSAnimationContext currentContext] setDuration:3.0];
}
[[NSAnimationContext currentContext] setCompletionHandler:^{
[[previousViewController view] removeFromSuperviewWithoutNeedingDisplay];
finished();
}];
[view setFrame:NSMakeRect((400 * direction), 0, 400, 330)];
[[self contentBox] addSubview:view];
[[view animator] setFrameOrigin:NSMakePoint(0, 0)];
[[[previousViewController view] animator] setFrameOrigin:NSMakePoint((-400 * direction), 0)];
[NSAnimationContext endGrouping];
} else {
[view setFrame:NSMakeRect(0, 0, 400, 330)];
if (previousViewController) {
[[previousViewController view] removeFromSuperviewWithoutNeedingDisplay];
}
[[self contentBox] addSubview:view];
finished();
}
currentStage = nextStage;
[self registerObserversForPreviousViewController:previousViewController nextViewController:nextViewController];
[self recalculateButtonEnabledStates];
[self resetButtonTitles];
}
- (void)cancelOperation:(id)sender {
[self cancel:sender];
}
- (void)cancel:(id)sender {
[[NSApplication sharedApplication] endSheet:self returnCode:0];
[self completionHandler](NO);
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
[self recalculateButtonEnabledStates];
}
- (void)recalculateButtonEnabledStates {
NSViewController<DPSetupWindowStageViewController> *currentViewController = [[self viewControllers] objectAtIndex:currentStage];
if ([currentViewController respondsToSelector:@selector(canContinue)]) {
[[self nextButton] setEnabled:[currentViewController canContinue]];
} else {
[[self nextButton] setEnabled:YES];
}
if ([currentViewController respondsToSelector:@selector(canGoBack)]) {
[[self backButton] setEnabled:[currentViewController canGoBack]];
} else {
[[self backButton] setEnabled:YES];
}
if (currentStage == 0) {
[[self backButton] setEnabled:NO];
}
}
- (void)registerObserversForPreviousViewController:(NSViewController *)previousViewController nextViewController:(NSViewController *)nextViewController {
[previousViewController removeObserver:self forKeyPath:@"canContinue"];
[previousViewController removeObserver:self forKeyPath:@"canGoBack"];
[nextViewController addObserver:self forKeyPath:@"canContinue" options:(NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld) context:NULL];
[nextViewController addObserver:self forKeyPath:@"canGoBack" options:(NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld) context:NULL];
}
- (void)deregisterObserversForViewController:(NSViewController *)viewController {
[viewController removeObserver:self forKeyPath:@"canContinue"];
[viewController removeObserver:self forKeyPath:@"canGoBack"];
}
- (void)resetButtonTitles {
NSViewController<DPSetupWindowStageViewController> *currentViewController = [[self viewControllers] objectAtIndex:currentStage];
if ([currentViewController respondsToSelector:@selector(continueButtonTitle)]) {
[[self nextButton] setTitle:[currentViewController continueButtonTitle]];
} else {
[[self nextButton] setTitle:@"Continue"];
}
if ([currentViewController respondsToSelector:@selector(backButtonTitle)]) {
[[self backButton] setTitle:[currentViewController backButtonTitle]];
} else {
[[self backButton] setTitle:@"Back"];
}
}
@end
Jump to Line
Something went wrong with that request. Please try again.