text:
//
//  MyDocument.m
//  HexFiend_2
//
//  Created by Peter Ammon on 11/3/07.
//  Copyright 2007 ridiculous_fish. All rights reserved.
//

#import "MyDocument.h"
#import "HFBannerDividerThumb.h"
#import "HFDocumentOperationView.h"
#import "DataInspectorRepresenter.h"
#import <HexFiend/HexFiend.h>
#include <pthread.h>

static const char *const kProgressContext = "context";

enum {
    HFSaveSuccessful,
    HFSaveCancelled,
    HFSaveError
};

static BOOL isRunningOnLeopardOrLater(void) {
    return NSAppKitVersionNumber >= 860.;
}

static inline Class preferredByteArrayClass(void) {
    return [HFBTreeByteArray class];
}

@interface MyDocument (ForwardDeclarations)
- (NSString *)documentWindowTitleFormatString;
@end

/* Subclass to display custom window title that shows progress */
@interface MyDocumentWindowController : NSWindowController

@end

@implementation MyDocumentWindowController

- (NSString *)windowTitleForDocumentDisplayName:(NSString *)displayName {
    NSString *result;
    NSString *superDisplayName = [super windowTitleForDocumentDisplayName:displayName];

    /* Apply a format string */
    NSString *formatString = [[self document] documentWindowTitleFormatString];
    result = [NSString stringWithFormat:formatString, superDisplayName];

    return result;
}

@end

#define USERDEFS_KEY_FOR_REP(r) @"RepresenterIsShown " @#r

@implementation MyDocument

+ (void)initialize {
    if (self == [MyDocument class]) {
        NSNumber *yes = [NSNumber numberWithBool:YES];
        NSDictionary *defs = [[NSDictionary alloc] initWithObjectsAndKeys:
                              yes, @"AntialiasText",
                              @"Monaco", @"DefaultFontName",
                              [NSNumber numberWithDouble:10.], @"DefaultFontSize",
                              [NSNumber numberWithInt:4], @"BytesPerColumn",
                              yes, USERDEFS_KEY_FOR_REP(lineCountingRepresenter),
                              yes, USERDEFS_KEY_FOR_REP(hexRepresenter),
                              yes, USERDEFS_KEY_FOR_REP(asciiRepresenter),
                              yes, USERDEFS_KEY_FOR_REP(dataInspectorRepresenter),
                              yes, USERDEFS_KEY_FOR_REP(statusBarRepresenter),
                              yes, USERDEFS_KEY_FOR_REP(scrollRepresenter),
                              nil];
        [[NSUserDefaults standardUserDefaults] registerDefaults:defs];
        [defs release];

	// Get notified when we are about to save a document, so we can try to break dependencies on the file in other documents
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(prepareForChangeInFileByBreakingFileDependencies:) name:HFPrepareForChangeInFileNotification object:nil];
    }
}

- (NSString *)windowNibName {
    // Implement this to return a nib to load OR implement -makeWindowControllers to manually create your controllers.
    return @"MyDocument";
}

- (NSWindow *)window {
    NSArray *windowControllers = [self windowControllers];
    HFASSERT([windowControllers count] == 1);
    return [[windowControllers objectAtIndex:0] window];
}

- (NSArray *)representers {
    return [NSArray arrayWithObjects:lineCountingRepresenter, hexRepresenter, asciiRepresenter, scrollRepresenter, dataInspectorRepresenter, statusBarRepresenter, nil];
}

- (BOOL)representerIsShown:(HFRepresenter *)representer {
    NSParameterAssert(representer);
    return [[layoutRepresenter representers] containsObject:representer];
}

- (void)showViewForRepresenter:(HFRepresenter *)rep {
    NSView *repView = [rep view];
    HFASSERT([repView superview] == nil && [repView window] == nil);
    [controller addRepresenter:rep];
    [layoutRepresenter addRepresenter:rep];
}

- (void)hideViewForRepresenter:(HFRepresenter *)rep {
    HFASSERT(rep != NULL);
    HFASSERT([[layoutRepresenter representers] indexOfObjectIdenticalTo:rep] != NSNotFound);
    [controller removeRepresenter:rep];
    [layoutRepresenter removeRepresenter:rep];
}

/* Code to save to user defs (NO) or apply from user defs (YES) the default representers to show. */
- (void)saveOrApplyDefaultRepresentersToDisplay:(BOOL)isApplying {
    const struct {
        NSString *name;
        HFRepresenter *rep;
    } shownRepresentersData[] = {
        {USERDEFS_KEY_FOR_REP(lineCountingRepresenter), lineCountingRepresenter},
        {USERDEFS_KEY_FOR_REP(hexRepresenter), hexRepresenter},
        {USERDEFS_KEY_FOR_REP(asciiRepresenter), asciiRepresenter},
        {USERDEFS_KEY_FOR_REP(dataInspectorRepresenter), dataInspectorRepresenter},
        {USERDEFS_KEY_FOR_REP(statusBarRepresenter), statusBarRepresenter},
        {USERDEFS_KEY_FOR_REP(scrollRepresenter), scrollRepresenter}
    };
    NSUInteger i;
    NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
    for (i=0; i < sizeof shownRepresentersData / sizeof *shownRepresentersData; i++) {
        if (isApplying) {
            /* Read from user defaults */
            NSNumber *boolObject = [defs objectForKey:shownRepresentersData[i].name];
            if (boolObject != nil) {
                BOOL shouldShow = [boolObject boolValue];
                HFRepresenter *rep = shownRepresentersData[i].rep;
                if (shouldShow != [self representerIsShown:rep]) {
                    if (shouldShow) [self showViewForRepresenter:rep];
                    else [self hideViewForRepresenter:rep];
                }
            }
        }
        else {
            /* Save to user defaults */
            BOOL isShown = [self representerIsShown:shownRepresentersData[i].rep];
            [defs setBool:isShown forKey:shownRepresentersData[i].name];
        }
    }
}

- (void)saveDefaultRepresentersToDisplay {
    [self saveOrApplyDefaultRepresentersToDisplay:NO];
}

- (void)applyDefaultRepresentersToDisplay {
    [self saveOrApplyDefaultRepresentersToDisplay:YES];
}

/* Return a format string that can take one argument which is the document name. */
- (NSString *)documentWindowTitleFormatString {
    NSMutableString *result = [NSMutableString stringWithString:@"%@"]; //format specifier that is replaced with document name

    if ([controller inOverwriteMode]) {
        [result appendString:@" **OVERWRITE MODE**"];
    }

    HFDocumentOperationView * const views[] = {findReplaceView, moveSelectionByView, saveView};
    NSUInteger i;
    BOOL hasAppendedProgressMarker = NO;
    for (i=0; i < sizeof views / sizeof *views; i++) {
        HFDocumentOperationView *view = views[i];
        if (view != nil && view != operationView && [view operationIsRunning]) {
            /* If we're gonna show our save view after a delay, then don't include the save view in the title */
            if (view == saveView && showSaveViewAfterDelayTimer != nil) continue;
            NSString *displayName = [view displayName];
            double progress = [view progress];
            if (displayName != nil && progress != -1) {
                if (! hasAppendedProgressMarker) {
                    [result appendString:@" ("];
                    hasAppendedProgressMarker = YES;
                }
                else {
                    [result appendString:@", "];
                }
                if (displayName) {
                    /* %%%% is the right way to get a single % after applying this appendFormat: and then applying this return value as a format string as well */
                    [result appendFormat:@"%@: %d%%%%", displayName, (int)(100. * progress)];
                }
            }
        }
    }
    if (hasAppendedProgressMarker) [result appendString:@")"];
    return result;
}

- (void)updateDocumentWindowTitle {
    [[self windowControllers] makeObjectsPerformSelector:@selector(synchronizeWindowTitleWithDocumentName)];
}

- (void)makeWindowControllers {
    NSString *windowNibName = [self windowNibName];
    if (windowNibName != nil) {
        NSWindowController *windowController = [[MyDocumentWindowController alloc] initWithWindowNibName:windowNibName owner:self];
        [self addWindowController:windowController];
        [windowController release];
    }
}

- (NSSize)minimumWindowFrameSizeForProposedSize:(NSSize)frameSize {
    NSView *layoutView = [layoutRepresenter view];
    NSSize proposedSizeInLayoutCoordinates = [layoutView convertSize:frameSize fromView:nil];
    CGFloat resultingWidthInLayoutCoordinates = [layoutRepresenter minimumViewWidthForLayoutInProposedWidth:proposedSizeInLayoutCoordinates.width];
    NSSize resultSize = [layoutView convertSize:NSMakeSize(resultingWidthInLayoutCoordinates, proposedSizeInLayoutCoordinates.height) toView:nil];
    return resultSize;
}

- (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)frameSize {
    if (sender != [self window] || layoutRepresenter == nil) return frameSize;
    return [self minimumWindowFrameSizeForProposedSize:frameSize];
}

/* Relayout the window without increasing its window frame size */
- (void)relayoutAndResizeWindowPreservingFrame {
    NSWindow *window = [self window];
    NSRect windowFrame = [window frame];
    windowFrame.size = [self minimumWindowFrameSizeForProposedSize:windowFrame.size];
    [window setFrame:windowFrame display:YES];
}

/* Relayout the window to support the given number of bytes per line */
- (void)relayoutAndResizeWindowForBytesPerLine:(NSUInteger)bytesPerLine {
    NSWindow *window = [self window];
    NSRect windowFrame = [window frame];
    NSView *layoutView = [layoutRepresenter view];
    CGFloat minViewWidth = [layoutRepresenter minimumViewWidthForBytesPerLine:bytesPerLine];
    CGFloat minWindowWidth = [layoutView convertSize:NSMakeSize(minViewWidth, 1) toView:nil].width;
    windowFrame.size.width = minWindowWidth;
    [window setFrame:windowFrame display:YES];
}

- (void)windowControllerDidLoadNib:(NSWindowController *)windowController {
    USE(windowController);

    NSWindow *window = [windowController window];
    [containerView setVertical:NO];
    if ([containerView respondsToSelector:@selector(setDividerStyle:)]) {
        [containerView setDividerStyle:2/*NSSplitViewDividerStyleThin*/];
    }
    [containerView setDelegate:(id)self];

    NSView *layoutView = [layoutRepresenter view];
    [layoutView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
    [layoutView setFrame:[containerView bounds]];
    [containerView addSubview:layoutView];
    [self applyDefaultRepresentersToDisplay];
    NSRect windowFrame = [window frame];
    windowFrame.size = [self minimumWindowFrameSizeForProposedSize:windowFrame.size];
    [window setFrame:windowFrame display:NO];
}

/* When our line counting view needs more space, we increase the size of our window, and also move it left by the same amount so that the other content does not appear to move. */
- (void)lineCountingViewChangedWidth:(NSNotification *)note {
    USE(note);
    HFASSERT([note object] == lineCountingRepresenter);
    NSView *lineCountingView = [lineCountingRepresenter view];

    /* Don't do anything window changing if we're not in a window yet */
    NSWindow *lineCountingViewWindow = [lineCountingView window];
    if (! lineCountingViewWindow) return;

    HFASSERT(lineCountingViewWindow == [self window]);

    CGFloat currentWidth = NSWidth([lineCountingView frame]);
    CGFloat newWidth = [lineCountingRepresenter preferredWidth];
    if (newWidth != currentWidth) {
        CGFloat widthChange = newWidth - currentWidth; //if we shrink, widthChange will be negative
        CGFloat windowWidthChange = [[lineCountingView superview] convertSize:NSMakeSize(widthChange, 0) toView:nil].width;
        windowWidthChange = (windowWidthChange < 0 ? HFFloor(windowWidthChange) : HFCeil(windowWidthChange));

        /* convertSize: has a nasty habit of stomping on negatives.  Make our window width change negative if our view-space horizontal change was negative. */
#if __LP64__
        windowWidthChange = copysign(windowWidthChange, widthChange);
#else
        windowWidthChange = copysignf(windowWidthChange, widthChange);
#endif

        NSRect windowFrame = [lineCountingViewWindow frame];
        windowFrame.size.width += windowWidthChange;

	/* If we are not setting the font, we want to grow the window left, so that the content area is preserved.  If we are setting the font, grow the window right. */
	if (! currentlySettingFont) windowFrame.origin.x -= windowWidthChange;
        [lineCountingViewWindow setFrame:windowFrame display:YES animate:NO];
    }
}

- (void)dataInspectorDeletedAllRows:(NSNotification *)note {
    DataInspectorRepresenter *inspector = [note object];
    [self hideViewForRepresenter:inspector];
    [self saveDefaultRepresentersToDisplay]; // Save the representers in UserDefaults so we start out next time the same way
}

/* Called when our data inspector changes its size (number of rows) */
- (void)dataInspectorChangedRowCount:(NSNotification *)note {
    DataInspectorRepresenter *inspector = [note object];
    CGFloat newHeight = (CGFloat)[[[note userInfo] objectForKey:@"height"] doubleValue];
    NSView *dataInspectorView = [inspector view];
    NSSize size = [dataInspectorView frame].size;
    size.height = newHeight;
    [dataInspectorView setFrameSize:size];
    [layoutRepresenter performLayout];
}

- init {
    [super init];
    lineCountingRepresenter = [[HFLineCountingRepresenter alloc] init];
    hexRepresenter = [[HFHexTextRepresenter alloc] init];
    asciiRepresenter = [[HFStringEncodingTextRepresenter alloc] init];
    scrollRepresenter = [[HFVerticalScrollerRepresenter alloc] init];
    layoutRepresenter = [[HFLayoutRepresenter alloc] init];
    statusBarRepresenter = [[HFStatusBarRepresenter alloc] init];
    dataInspectorRepresenter = [[DataInspectorRepresenter alloc] init];

    [[hexRepresenter view] setAutoresizingMask:NSViewHeightSizable];
    [[asciiRepresenter view] setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];

    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    [center addObserver:self selector:@selector(lineCountingViewChangedWidth:) name:HFLineCountingRepresenterMinimumViewWidthChanged object:lineCountingRepresenter];
    [center addObserver:self selector:@selector(dataInspectorChangedRowCount:) name:DataInspectorDidChangeRowCount object:dataInspectorRepresenter];
    [center addObserver:self selector:@selector(dataInspectorDeletedAllRows:) name:DataInspectorDidDeleteAllRows object:dataInspectorRepresenter];
    NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];

    controller = [[HFController alloc] init];
    [controller setShouldAntialias:[defs boolForKey:@"AntialiasText"]];
    [controller setUndoManager:[self undoManager]];
    [controller setBytesPerColumn:[defs integerForKey:@"BytesPerColumn"]];
    [controller addRepresenter:layoutRepresenter];

    NSString *fontName = [defs stringForKey:@"DefaultFontName"];
    CGFloat fontSize = [defs floatForKey:@"DefaultFontSize"];
    NSFont *font = [NSFont fontWithName:fontName size:fontSize];
    if (font != nil) {
        [controller setFont: font];
    }

#if ! NDEBUG
    static BOOL hasAddedMenu = NO;
    if (! hasAddedMenu) {
        hasAddedMenu = YES;
        NSMenu *menu = [[[NSApp mainMenu] itemWithTitle:@"Debug"] submenu];
        [menu addItem:[NSMenuItem separatorItem]];
        [menu addItemWithTitle:@"Show ByteArray" action:@selector(_showByteArray:) keyEquivalent:@"k"];
        [[[menu itemArray] lastObject] setKeyEquivalentModifierMask:NSCommandKeyMask];
    }
#endif
    return self;
}

#if ! NDEBUG
- (void)_showByteArray:sender {
    USE(sender);
    NSLog(@"%@", [controller byteArray]);
}
#endif

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [[self representers] makeObjectsPerformSelector:@selector(release)];
    [controller release];
    [bannerView release];
    [saveError release];

    /* Release and stop observing our banner views.  Note that any of these may be nil. */
    HFDocumentOperationView *views[] = {findReplaceView, moveSelectionByView, jumpToOffsetView, saveView};
    for (NSUInteger i = 0; i < sizeof views / sizeof *views; i++) {
	[views[i] removeObserver:self forKeyPath:@"progress"];
	[views[i] release];
    }
    [bannerDividerThumb release];
    [super dealloc];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if (context == kProgressContext) {
        /* One of our HFDocumentOperationViews changed progress.  Update our title bar to reflect that.  But we don't show progress for the currently displayed banner. */
        if (object != operationView) {
            [self updateDocumentWindowTitle];
        }
    }
    else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

- (HFDocumentOperationView *)newOperationViewForNibName:(NSString *)name displayName:(NSString *)displayName {
    HFASSERT(name);
    HFDocumentOperationView *result = [[HFDocumentOperationView viewWithNibNamed:name owner:self] retain];
    [result setDisplayName:displayName];
    [result setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
    [result setFrameSize:NSMakeSize(NSWidth([containerView frame]), 0)];
    [result setFrameOrigin:NSZeroPoint];
    [result addObserver:self forKeyPath:@"progress" options:0 context:(void *)kProgressContext];
    return result;
}

- (void)prepareBannerWithView:(HFDocumentOperationView *)newSubview withTargetFirstResponder:(id)targetFirstResponder {
    HFASSERT(operationView == nil);
    operationView = newSubview;
    bannerTargetHeight = [newSubview defaultHeight];
    if (! bannerView) bannerView = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 1, 1)];
    NSRect containerBounds = [containerView bounds];
    NSRect bannerFrame = NSMakeRect(NSMinX(containerBounds), NSMaxY(containerBounds), NSWidth(containerBounds), 0);
    [bannerView setFrame:bannerFrame];
    bannerStartTime = 0;
    bannerIsShown = YES;
    bannerGrowing = YES;
    targetFirstResponderInBanner = targetFirstResponder;
    if (isRunningOnLeopardOrLater()) {
        if (! bannerDividerThumb) bannerDividerThumb = [[HFBannerDividerThumb alloc] initWithFrame:NSMakeRect(0, 0, 14, 14)];
        [bannerDividerThumb setAutoresizingMask:0];
        [bannerDividerThumb setFrameOrigin:NSMakePoint(3, 0)];
        [bannerDividerThumb removeFromSuperview];
        [bannerView addSubview:bannerDividerThumb];
    }
    if (newSubview) {
        NSSize newSubviewSize = [newSubview frame].size;
        if (newSubviewSize.width != NSWidth(containerBounds)) {
            newSubviewSize.width = NSWidth(containerBounds);
            [newSubview setFrameSize:newSubviewSize];
        }
        if (bannerDividerThumb) [bannerView addSubview:newSubview positioned:NSWindowBelow relativeTo:bannerDividerThumb];
        else [bannerView addSubview:newSubview];
    }
    [bannerResizeTimer invalidate];
    [bannerResizeTimer release];
    bannerResizeTimer = [[NSTimer scheduledTimerWithTimeInterval:1. / 60. target:self selector:@selector(animateBanner:) userInfo:nil repeats:YES] retain];
    [self updateDocumentWindowTitle];
}

- (BOOL)readFromURL:(NSURL *)absoluteURL ofType:(NSString *)typeName error:(NSError **)outError {
    USE(typeName);
    USE(outError);
    BOOL result = NO;
    HFASSERT([absoluteURL isFileURL]);
    HFFileReference *fileReference = [[[HFFileReference alloc] initWithPath:[absoluteURL path] error:outError] autorelease];
    if (fileReference) {
        HFFileByteSlice *byteSlice = [[[HFFileByteSlice alloc] initWithFile:fileReference] autorelease];
        HFByteArray *byteArray = [[[preferredByteArrayClass() alloc] init] autorelease];
        [byteArray insertByteSlice:byteSlice inRange:HFRangeMake(0, 0)];
        [controller setByteArray:byteArray];
        result = YES;
    }
    return result;
}

- (IBAction)toggleVisibleControllerView:(id)sender {
    USE(sender);
    NSUInteger arrayIndex = [sender tag] - 1;
    NSArray *representers = [self representers];
    if (arrayIndex >= [representers count]) {
        NSBeep();
    }
    else {
        HFRepresenter *rep = [representers objectAtIndex:arrayIndex];
        if ([self representerIsShown:rep]) {
            [self hideViewForRepresenter:rep];
            [self relayoutAndResizeWindowPreservingFrame];
        }
        else {
            [self showViewForRepresenter:rep];
            [self relayoutAndResizeWindowPreservingFrame];
        }
        [self saveDefaultRepresentersToDisplay];
    }
}

- (void)setFont:(NSFont *)font {
    HFASSERT(font != nil);
    NSWindow *window = [self window];
    NSDisableScreenUpdates();
    NSUInteger bytesPerLine = [controller bytesPerLine];
    /* Record that we are currently setting the font.  We use this to decide which direction to grow the window if our line numbers change. */
    currentlySettingFont = YES;
    [controller setFont:font];
    [self relayoutAndResizeWindowForBytesPerLine:bytesPerLine];
    currentlySettingFont = NO;
    [window display];
    NSEnableScreenUpdates();
    NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
    [defs setDouble:[font pointSize] forKey:@"DefaultFontSize"];
    [defs setObject:[font fontName] forKey:@"DefaultFontName"];
}

- (NSFont *)font {
    return [controller font];
}

- (void)setFontSizeFromMenuItem:(NSMenuItem *)item {
    NSString *fontName = [[self font] fontName];
    [self setFont:[NSFont fontWithName:fontName size:(CGFloat)[item tag]]];
}

- (IBAction)setAntialiasFromMenuItem:(id)sender {
    USE(sender);
    BOOL newVal = ! [controller shouldAntialias];
    [controller setShouldAntialias:newVal];
    [[NSUserDefaults standardUserDefaults] setBool:newVal forKey:@"AntialiasText"];
}

- (BOOL)validateMenuItem:(NSMenuItem *)item {
    SEL action = [item action];
    if (action == @selector(toggleVisibleControllerView:)) {
        NSUInteger arrayIndex = [item tag] - 1;
        NSArray *representers = [self representers];
        if (arrayIndex >= [representers count]) {
            return NO;
        }
        else {
            HFRepresenter *rep = [representers objectAtIndex:arrayIndex];
            [item setState:[[controller representers] containsObject:rep]];
            return YES;
        }
    }
    else if (action == @selector(performFindPanelAction:)) {
        switch ([item tag]) {
            case NSFindPanelActionShowFindPanel:
            case NSFindPanelActionNext:
            case NSFindPanelActionPrevious:
                return YES;
            default:
                return NO;
        }
    }
    else if (action == @selector(setFontSizeFromMenuItem:)) {
        [item setState:[[self font] pointSize] == [item tag]];
        return YES;
    }
    else if (action == @selector(setAntialiasFromMenuItem:)) {
        [item setState:[controller shouldAntialias]];
        return YES;
    }
    else if (action == @selector(toggleOverwriteMode:)) {
        [item setState:[controller inOverwriteMode]];
        /* We can toggle overwrite mode only if the controller doesn't require that it be on */
        return ! [controller requiresOverwriteMode];
    }
    else if (action == @selector(modifyByteGrouping:)) {
        [item setState:(NSUInteger)[item tag] == [controller bytesPerColumn]];
        return YES;
    }
    else return [super validateMenuItem:item];
}

- (void)finishedAnimation {
    if (! bannerGrowing) {
        bannerIsShown = NO;
        [bannerDividerThumb removeFromSuperview];
        [bannerView removeFromSuperview];
        [[[[bannerView subviews] copy] autorelease] makeObjectsPerformSelector:@selector(removeFromSuperview)];
        [bannerView release];
        bannerView = nil;
        operationView = nil;
        [self updateDocumentWindowTitle];
        [containerView setNeedsDisplay:YES];
        if (commandToRunAfterBannerIsDoneHiding) {
            SEL command = commandToRunAfterBannerIsDoneHiding;
            commandToRunAfterBannerIsDoneHiding = NULL;
            [self performSelector:command withObject:nil];
        }
    }
}

- (void)restoreFirstResponderToSavedResponder {
    NSWindow *window = [self window];
    NSMutableArray *views = [NSMutableArray array];
    FOREACH(HFRepresenter *, rep, [self representers]) {
        NSView *view = [rep view];
        if ([view window] == window) {
            /* If we're the saved first responder, try it first */
            if (view == savedFirstResponder) [views insertObject:view atIndex:0];
            else [views addObject:view];
        }
    }

    /* Try each view we identified */
    FOREACH(NSView *, view, views) {
        if ([window makeFirstResponder:view]) return;
    }

    /* No luck - set it to the window */
    [window makeFirstResponder:window];
}

- (void)saveFirstResponderIfNotInBannerAndThenSetItTo:(id)newResponder {
    id potentialSavedFirstResponder = [[self window] firstResponder];
    if ([potentialSavedFirstResponder isKindOfClass:[NSView class]] && [potentialSavedFirstResponder ancestorSharedWithView:bannerView] != findReplaceView) {
	savedFirstResponder = potentialSavedFirstResponder;
    }
    [[self window] makeFirstResponder:newResponder];
}

- (void)animateBanner:(NSTimer *)timer {
    BOOL isFirstCall = (bannerStartTime == 0);
    CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
    if (isFirstCall) bannerStartTime = now;
    CFAbsoluteTime diff = now - bannerStartTime;
    double amount = diff / .15;
    amount = fmin(fmax(amount, 0), 1);
    if (! bannerGrowing) amount = 1. - amount;
    if (bannerGrowing && diff >= 0 && [bannerView superview] != containerView) {
        [containerView addSubview:bannerView positioned:NSWindowBelow relativeTo:[layoutRepresenter view]];
        if (targetFirstResponderInBanner) {
            NSWindow *window = [self window];
            savedFirstResponder = [window firstResponder];
            [window makeFirstResponder:targetFirstResponderInBanner];
        }
    }
    CGFloat height = (CGFloat)round(bannerTargetHeight * amount);
    NSRect bannerFrame = [bannerView frame];
    bannerFrame.size.height = height;
    [bannerView setFrame:bannerFrame];
    [containerView display];
    if (isFirstCall) {
        /* The first display can take some time, which can cause jerky animation; so we start the animation after it */
        bannerStartTime = CFAbsoluteTimeGetCurrent();
    }
    if ((bannerGrowing && amount >= 1.) || (!bannerGrowing && amount <= 0.)) {
        if (timer == bannerResizeTimer && bannerResizeTimer != nil) {
            [bannerResizeTimer invalidate];
            [bannerResizeTimer release];
            bannerResizeTimer = nil;
        }
        [self finishedAnimation];
    }
}

- (BOOL)canSwitchToNewBanner {
    return operationView == nil || operationView != saveView;
}

- (void)hideBannerFirstThenDo:(SEL)command {
    HFASSERT(bannerIsShown);
    bannerGrowing = NO;
    bannerStartTime = 0;
    /* If the first responder is in our banner, move it to our view */
    NSWindow *window = [self window];
    id firstResponder = [window firstResponder];
    bannerTargetHeight = NSHeight([bannerView frame]);
    commandToRunAfterBannerIsDoneHiding = command;
    if ([firstResponder isKindOfClass:[NSView class]] && [firstResponder ancestorSharedWithView:bannerView] == bannerView) {
        [self restoreFirstResponderToSavedResponder];
    }
    [bannerResizeTimer invalidate];
    [bannerResizeTimer release];
    bannerResizeTimer = [[NSTimer scheduledTimerWithTimeInterval:1. / 60. target:self selector:@selector(animateBanner:) userInfo:nil repeats:YES] retain];
}

- (void)hideBannerImmediately {
    HFASSERT(bannerIsShown);
    NSWindow *window = [self window];
    bannerGrowing = NO;
    bannerStartTime = 0;
    bannerTargetHeight = NSHeight([bannerView frame]);
    /* If the first responder is in our banner, move it to our view */
    id firstResponder = [window firstResponder];
    if ([firstResponder isKindOfClass:[NSView class]] && [firstResponder ancestorSharedWithView:bannerView] == bannerView) {
        [self restoreFirstResponderToSavedResponder];
    }
    while (bannerIsShown) {
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
        [self animateBanner:nil];
        [window displayIfNeeded];
        [pool drain];
    }
}

- (void)showSaveBannerHavingDelayed:(NSTimer *)timer {
    HFASSERT(saveView != nil);
    USE(timer);
    if (operationView != nil && operationView != saveView) {
        [self hideBannerImmediately];
    }
    [self prepareBannerWithView:saveView withTargetFirstResponder:nil];
}

- (BOOL)writeSafelyToURL:(NSURL *)inAbsoluteURL ofType:(NSString *)inTypeName forSaveOperation:(NSSaveOperationType)saveOperation error:(NSError **)outError {
    USE(inTypeName);
    *outError = NULL;

    HFASSERT(! saveInProgress);
    saveInProgress = YES;

    [HFController prepareForChangeInFile:inAbsoluteURL fromWritingByteArray:[controller byteArray]];

    showSaveViewAfterDelayTimer = [[NSTimer scheduledTimerWithTimeInterval:.5 target:self selector:@selector(showSaveBannerHavingDelayed:) userInfo:nil repeats:NO] retain];

    if (! saveView) saveView = [self newOperationViewForNibName:@"SaveBanner" displayName:@"Saving"];
    saveResult = 0;

    struct HFDocumentOperationCallbacks callbacks = {
        .target = self,
        .userInfo = [NSDictionary dictionaryWithObjectsAndKeys:inAbsoluteURL, @"targetURL", nil],
        .startSelector = @selector(threadedStartSave:),
        .endSelector = @selector(endSave:)
    };

    [[controller byteArray] incrementChangeLockCounter];

    [[saveView viewNamed:@"saveLabelField"] setStringValue:[NSString stringWithFormat:@"Saving \"%@\"", [self displayName]]];

    [saveView startOperationWithCallbacks:callbacks];

    while ([saveView operationIsRunning]) {
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
        @try {
            NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantFuture] inMode:NSDefaultRunLoopMode dequeue:YES];
            if (event) [NSApp sendEvent:event];
        }
        @catch (NSException *localException) {
            NSLog(@"Exception thrown during save: %@", localException);
        }
        @finally {
            [pool drain];
        }
    }

    [showSaveViewAfterDelayTimer invalidate];
    [showSaveViewAfterDelayTimer release];
    showSaveViewAfterDelayTimer = nil;

    [[controller byteArray] decrementChangeLockCounter];

    /* If we save to a file, then we've probably overwritten some source data, so throw away undo and just reset the document to reference the new file.  Only do this if there was no error.

    Note that this is actually quite wrong.  It's entirely possible that e.g. there was an error after the file was touched, e.g. when writing to the file.  In that case, we do want to just reference the file again.

    What we really need to know is "has a backing file been touched by this operation."  But we don't have access to that information yet.
    */
    if ((saveResult != HFSaveError) && (saveOperation == NSSaveOperation || saveOperation == NSSaveAsOperation)) {
        [[self undoManager] removeAllActions];
        HFFileReference *fileReference = [[[HFFileReference alloc] initWithPath:[inAbsoluteURL path] error:NULL] autorelease];
        if (fileReference) {
            HFFileByteSlice *byteSlice = [[[HFFileByteSlice alloc] initWithFile:fileReference] autorelease];
            HFByteArray *byteArray = [[[preferredByteArrayClass() alloc] init] autorelease];
            [byteArray insertByteSlice:byteSlice inRange:HFRangeMake(0, 0)];
            [controller setByteArray:byteArray];
        }
    }

    if (operationView != nil && operationView == saveView) [self hideBannerFirstThenDo:NULL];

    if (outError) *outError = saveError;
    [saveError autorelease];
    saveError = nil;

    saveInProgress = NO;

    return saveResult != HFSaveError;
}

- (BOOL)displayCurrentSaveOperation {
    BOOL result = NO;
    if (saveInProgress) {
        HFASSERT(saveView != nil);
        result = YES;
        if (operationView == saveView) {
            /* Already showing the save view */
        }
        else if (operationView == nil) {
            [self prepareBannerWithView:saveView withTargetFirstResponder:nil];
        }
    }
    return result;
}

/* Prevent saving during saves */
- (IBAction)saveDocument:(id)sender {
    if ([self displayCurrentSaveOperation]) return;
    [super saveDocument:sender];
}

- (IBAction)saveDocumentAs:(id)sender {
    if ([self displayCurrentSaveOperation]) return;
    [super saveDocumentAs:sender];
}

- (IBAction)saveDocumentTo:(id)sender {
    if ([self displayCurrentSaveOperation]) return;
    [super saveDocumentTo:sender];
}

- (void)showFindPanel:(NSMenuItem *)item {
    if (operationView != nil && operationView == findReplaceView) {
	[self saveFirstResponderIfNotInBannerAndThenSetItTo:[findReplaceView viewNamed:@"searchField"]];
	return;
    }
    if (! [self canSwitchToNewBanner]) {
        NSBeep();
        return;
    }
    USE(item);
    if (bannerIsShown) {
        [self hideBannerFirstThenDo:_cmd];
        return;
    }

    if (! findReplaceView) {
        findReplaceView = [self newOperationViewForNibName:@"FindReplaceBanner" displayName:@"Finding"];
        [[findReplaceView viewNamed:@"searchField"] setTarget:self];
        [[findReplaceView viewNamed:@"searchField"] setAction:@selector(findNext:)];
        [[findReplaceView viewNamed:@"replaceField"] setTarget:self];
        [[findReplaceView viewNamed:@"replaceField"] setAction:@selector(findNext:)]; //yes, this should be findNext:, not replace:, because when you just hit return in the replace text field, it only finds; replace is for the replace button
    }

    [self prepareBannerWithView:findReplaceView withTargetFirstResponder:[findReplaceView viewNamed:@"searchField"]];
}

- (NSRect)splitView:(NSSplitView *)splitView additionalEffectiveRectOfDividerAtIndex:(NSInteger)dividerIndex {
    USE(dividerIndex);
    HFASSERT(splitView == containerView);
    if (bannerDividerThumb) return [bannerDividerThumb convertRect:[bannerDividerThumb bounds] toView:containerView];
    else return NSZeroRect;
}

- (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview {
    HFASSERT(splitView == containerView);
    return subview == bannerView;
}

- (BOOL)splitView:(NSSplitView *)splitView shouldCollapseSubview:(NSView *)subview forDoubleClickOnDividerAtIndex:(NSInteger)dividerIndex {
    HFASSERT(splitView == containerView);
    USE(dividerIndex);
    if (subview == bannerView && subview != NULL) {
        [self hideBannerFirstThenDo:NULL];
    }
    return NO;
}

- (void)removeBannerIfSufficientlyShort:unused {
    USE(unused);
    willRemoveBannerIfSufficientlyShortAfterDrag = NO;
    if (bannerIsShown && bannerResizeTimer == NULL && NSHeight([bannerView frame]) < 20.) {
        [self hideBannerFirstThenDo:NULL];
    }
}

- (void)splitViewDidResizeSubviews:(NSNotification *)notification {
    USE(notification);
    /* If the user drags the banner so that it is very small, we want it to shrink to nothing when it is released.  We handle this by checking if we are in live resize, and setting a timer to fire in NSDefaultRunLoopMode to remove the banner. */
    if (willRemoveBannerIfSufficientlyShortAfterDrag == NO && bannerResizeTimer == nil && [containerView inLiveResize]) {
        willRemoveBannerIfSufficientlyShortAfterDrag = YES;
        [self performSelector:@selector(removeBannerIfSufficientlyShort:) withObject:nil afterDelay:0. inModes:[NSArray arrayWithObject:NSDefaultRunLoopMode]];
    }
}

- (void)cancelOperation:sender {
    USE(sender);
    if (bannerIsShown) {
        [self hideBannerFirstThenDo:NULL];
    }
    else {
        NSBeep();
    }
}

- (id)threadedStartSave:(HFProgressTracker *)tracker {
    HFByteArray *byteArray = [controller byteArray];
    NSDictionary *userInfo = [tracker userInfo];
    NSURL *targetURL = [userInfo objectForKey:@"targetURL"];
    NSError *error = nil;
    BOOL result = [byteArray writeToFile:targetURL trackingProgress:tracker error:&error];
    [tracker noteFinished:self];
    saveError = [error retain];
    if (tracker->cancelRequested) return [NSNumber numberWithInt:HFSaveCancelled];
    else if (! result) return [NSNumber numberWithInt:HFSaveError];
    else return [NSNumber numberWithInt:HFSaveSuccessful];
}

- (void)endSave:(id)result {
#if __LP64__
    saveResult = [result integerValue];
#else
    saveResult = [result intValue]; //Tiger compatibility
#endif
    /* Post an event so our event loop wakes up */
    [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined location:NSZeroPoint modifierFlags:0 timestamp:0 windowNumber:0 context:NULL subtype:0 data1:0 data2:0] atStart:NO];
}

- (id)threadedStartFind:(HFProgressTracker *)tracker {
    HFASSERT(tracker != NULL);
    unsigned long long searchResult;
    NSDictionary *userInfo = [tracker userInfo];
    HFByteArray *needle = [userInfo objectForKey:@"needle"];
    HFByteArray *haystack = [userInfo objectForKey:@"haystack"];
    BOOL forwards = [[userInfo objectForKey:@"forwards"] boolValue];
    HFRange searchRange1 = [[userInfo objectForKey:@"range1"] HFRange];
    HFRange searchRange2 = [[userInfo objectForKey:@"range2"] HFRange];

    [tracker setMaxProgress:[haystack length]];

    //    CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
    searchResult = [haystack indexOfBytesEqualToBytes:needle inRange:searchRange1 searchingForwards:forwards trackingProgress:tracker];
    //    CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();
    //    printf("Diff: %f\n", end - start);

    if (searchResult == ULLONG_MAX) {
        searchResult = [haystack indexOfBytesEqualToBytes:needle inRange:searchRange2 searchingForwards:forwards trackingProgress:tracker];
    }

    if (tracker->cancelRequested) return nil;
    else return [NSNumber numberWithUnsignedLongLong:searchResult];
}

- (void)findEnded:(NSNumber *)val {
    NSDictionary *userInfo = [[findReplaceView progressTracker] userInfo];
    HFByteArray *needle = [userInfo objectForKey:@"needle"];
    HFByteArray *haystack = [userInfo objectForKey:@"haystack"];
    /* nil val means cancelled */
    if (val) {
        unsigned long long searchResult = [val unsignedLongLongValue];
        if (searchResult != ULLONG_MAX) {
            HFRange resultRange = HFRangeMake(searchResult, [needle length]);
            [controller setSelectedContentsRanges:[HFRangeWrapper withRanges:&resultRange count:1]];
            [controller maximizeVisibilityOfContentsRange:resultRange];
            [self restoreFirstResponderToSavedResponder];
            [controller pulseSelection];
        }
        else {
            NSBeep();
        }
    }
    [needle decrementChangeLockCounter];
    [haystack decrementChangeLockCounter];

}

- (void)findNextBySearchingForwards:(BOOL)forwards {
    if ([operationView operationIsRunning]) {
	NSBeep();
	return;
    }
    HFByteArray *needle = [[findReplaceView viewNamed:@"searchField"] objectValue];
    if ([needle length] > 0) {
        HFByteArray *haystack = [controller byteArray];
        unsigned long long startLocation = [controller maximumSelectionLocation];
        unsigned long long endLocation = [controller minimumSelectionLocation];
        unsigned long long haystackLength = [haystack length];
        HFASSERT(startLocation <= [haystack length]);
        HFRange earlierRange = HFRangeMake(0, endLocation);
        HFRange laterRange = HFRangeMake(startLocation, haystackLength - startLocation);

        // if searching forwards, we search the range after the selection first; if searching backwards, we search the range before the selection first.
        HFRange searchRange1 = (forwards ? laterRange : earlierRange);
        HFRange searchRange2 = (forwards ? earlierRange : laterRange);

        NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
                                  needle, @"needle",
                                  haystack, @"haystack",
                                  [NSNumber numberWithBool:forwards], @"forwards",
                                  [HFRangeWrapper withRange:searchRange1], @"range1",
                                  [HFRangeWrapper withRange:searchRange2], @"range2",
                                  nil];

        struct HFDocumentOperationCallbacks callbacks = {
            .target = self,
            .userInfo = userInfo,
            .startSelector = @selector(threadedStartFind:),
            .endSelector = @selector(findEnded:)
        };

        [needle incrementChangeLockCounter];
        [haystack incrementChangeLockCounter];

        [findReplaceView startOperationWithCallbacks:callbacks];
    }
}

- (id)threadedStartReplaceAll:(HFProgressTracker *)tracker {
    HFASSERT(tracker != NULL);
    NSDictionary *userInfo = [tracker userInfo];
    HFByteArray *needle = [userInfo objectForKey:@"needle"];
    HFByteArray *haystack = [userInfo objectForKey:@"haystack"];
    HFByteArray *replacementValue = [userInfo objectForKey:@"replacementValue"];
    const unsigned long long needleLength = [needle length];
    const unsigned long long replacementLength = [replacementValue length];
    const unsigned long long haystackLength = [haystack length];
    [tracker setMaxProgress:haystackLength];

    /* Perform our changes in a copy of haystack, and then set that copy back on our controller */
    HFByteArray *newHaystack = [[haystack mutableCopy] autorelease];
    unsigned long long newHaystackLength = haystackLength;

    HFRange remainingRange = HFRangeMake(0, haystackLength);
    while (remainingRange.length > 0) {
        if (tracker && tracker->cancelRequested) goto cancelled;
        unsigned long long foundLocation = [haystack indexOfBytesEqualToBytes:needle inRange:remainingRange searchingForwards:YES trackingProgress:tracker];
        if (foundLocation == ULLONG_MAX) break;
        HFASSERT(foundLocation < haystackLength);
        HFASSERT(HFSum(foundLocation, needleLength) < haystackLength);
        unsigned long long offsetFromHaystackEnd = haystackLength - foundLocation;
        HFASSERT(offsetFromHaystackEnd <= newHaystackLength);
        unsigned long long offsetIntoNewHaystack = newHaystackLength - offsetFromHaystackEnd;
        HFASSERT(HFSum(offsetIntoNewHaystack, needleLength) <= newHaystackLength);
        if (tracker && tracker->cancelRequested) goto cancelled;
        [newHaystack insertByteArray:replacementValue inRange:HFRangeMake(offsetIntoNewHaystack, needleLength)];
        newHaystackLength += (replacementLength - needleLength);
        remainingRange.location = HFSum(foundLocation, needleLength);
        remainingRange.length = haystackLength - remainingRange.location;
    }
    if (tracker && tracker->cancelRequested) goto cancelled;
    return newHaystack;

cancelled:;
    return nil;
}

- (void)replaceAllEnded:(HFByteArray *)newValue {
    [[[findReplaceView viewNamed:@"searchField"] objectValue] decrementChangeLockCounter];
    [[controller byteArray] decrementChangeLockCounter];
    if (newValue != nil) {
        [controller replaceByteArray:newValue];
    }
}

- (void)findNext:sender {
    USE(sender);
    if ([operationView operationIsRunning]) {
	NSBeep();
	return;
    }
    [self findNextBySearchingForwards:YES];
}

- (void)findPrevious:sender {
    USE(sender);
    if ([operationView operationIsRunning]) {
	NSBeep();
	return;
    }
    [self findNextBySearchingForwards:NO];
}

- (IBAction)replace:sender {
    USE(sender);
    if ([operationView operationIsRunning]) {
	NSBeep();
	return;
    }
    HFByteArray *replaceArray = [[findReplaceView viewNamed:@"replaceField"] objectValue];
    HFASSERT(replaceArray != NULL);
    [controller insertByteArray:replaceArray replacingPreviousBytes:0 allowUndoCoalescing:NO];

}

- (IBAction)replaceAndFind:sender {
    if ([operationView operationIsRunning]) {
	NSBeep();
	return;
    }
    [self replace:sender];
    [self findNext:sender];
}

- (IBAction)replaceAll:sender {
    if ([operationView operationIsRunning]) {
	NSBeep();
	return;
    }
    USE(sender);
    HFByteArray *replacementValue = [[findReplaceView viewNamed:@"replaceField"] objectValue];
    HFASSERT(replacementValue != NULL);
    HFByteArray *needle = [[findReplaceView viewNamed:@"searchField"] objectValue];
    if ([needle length] == 0) {
        NSBeep();
        return;
    }
    HFByteArray *haystack = [controller byteArray];
    NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
			      replacementValue, @"replacementValue",
			      needle, @"needle",
			      haystack, @"haystack",
			      nil];

    struct HFDocumentOperationCallbacks callbacks = {
        .target = self,
        .userInfo = userInfo,
        .startSelector = @selector(threadedStartReplaceAll:),
        .endSelector = @selector(replaceAllEnded:)
    };
    [needle incrementChangeLockCounter];
    [haystack incrementChangeLockCounter];
    [findReplaceView startOperationWithCallbacks:callbacks];
}

- (void)performHFFindPanelAction:(NSMenuItem *)item {
    switch ([item tag]) {
        case NSFindPanelActionShowFindPanel:
            [self showFindPanel:item];
            break;
        case NSFindPanelActionNext:
            [self findNext:item];
            break;
        case NSFindPanelActionPrevious:
            [self findPrevious:item];
            break;
        default:
            NSLog(@"Unhandled item %@", item);
            break;
    }
}

- (void)showNavigationBannerSettingExtendSelectionCheckboxTo:(BOOL)extend {
    if (moveSelectionByView == operationView && moveSelectionByView != nil) {
        [[moveSelectionByView viewNamed:@"extendSelectionByCheckbox"] setIntValue:extend];
	[self saveFirstResponderIfNotInBannerAndThenSetItTo:[moveSelectionByView viewNamed:@"moveSelectionByTextField"]];
        return;
    }
    if (! moveSelectionByView) moveSelectionByView = [self newOperationViewForNibName:@"MoveSelectionByBanner" displayName:@"Moving Selection"];
    [[moveSelectionByView viewNamed:@"extendSelectionByCheckbox"] setIntValue:extend];
    [self prepareBannerWithView:moveSelectionByView withTargetFirstResponder:[moveSelectionByView viewNamed:@"moveSelectionByTextField"]];

}

- (void)moveSelectionForwards:(NSMenuItem *)sender {
    USE(sender);
    if (! [self canSwitchToNewBanner]) {
        NSBeep();
        return;
    }
    if (operationView != nil && operationView != moveSelectionByView) {
        [self hideBannerFirstThenDo:_cmd];
        return;
    }
    [self showNavigationBannerSettingExtendSelectionCheckboxTo:NO];
}

- (void)extendSelectionForwards:(NSMenuItem *)sender {
    USE(sender);
    if (! [self canSwitchToNewBanner]) {
        NSBeep();
        return;
    }
    if (operationView != nil && operationView != moveSelectionByView) {
        [self hideBannerFirstThenDo:_cmd];
        return;
    }
    [self showNavigationBannerSettingExtendSelectionCheckboxTo:YES];
}

- (void)jumpToOffset:(NSMenuItem *)sender {
    USE(sender);
    if (! [self canSwitchToNewBanner]) {
        NSBeep();
        return;
    }
    if (operationView != nil && operationView != jumpToOffsetView) {
        [self hideBannerFirstThenDo:_cmd];
        return;
    }
    if (! jumpToOffsetView) jumpToOffsetView = [self newOperationViewForNibName:@"JumpToOffsetBanner" displayName:@"Jumping to Offset"];
    [self prepareBannerWithView:jumpToOffsetView withTargetFirstResponder:[jumpToOffsetView viewNamed:@"moveSelectionByTextField"]];
}

- (BOOL)parseSuffixMultiplier:(const char *)multiplier intoMultiplier:(unsigned long long *)multiplierResultValue {
    NSParameterAssert(multiplier != NULL);
    NSParameterAssert(multiplierResultValue != NULL);
    size_t length = strlen(multiplier);
    /* Allow spaces at the end */
    while (length > 0 && multiplier[length-1] == ' ') length--;
    /* Allow an optional trailing b or B (e.g. MB or M) */
    if (length > 0 && strchr("bB", multiplier[length-1]) != NULL) length--;

    /* If this exhausted our string, return success, e.g. so that the user can type "5 b" and it will return a multiplier of 1 */
    if (length == 0) {
        *multiplierResultValue = 1;
        return YES;
    }

    /* Now check each SI suffix */
    const char * const decimalSuffixes[] = {"k", "m", "g", "t", "p", "e", "z", "y"};
    const char * const binarySuffixes[] = {"ki", "mi", "gi", "ti", "pi", "ei", "zi", "yi"};
    NSUInteger i;
    unsigned long long suffixMultiplier = 1;
    BOOL suffixMultiplierDidOverflow = NO;
    for (i=0; i < sizeof decimalSuffixes / sizeof *decimalSuffixes; i++) {
        unsigned long long product = suffixMultiplier * 1000;
        suffixMultiplierDidOverflow = suffixMultiplierDidOverflow || (product/1000 != suffixMultiplier);
        suffixMultiplier = product;
        if (! strncasecmp(multiplier, decimalSuffixes[i], length)) {
            if (suffixMultiplierDidOverflow) suffixMultiplier = ULLONG_MAX;
            *multiplierResultValue = suffixMultiplier;
            return ! suffixMultiplierDidOverflow;
        }
    }
    suffixMultiplier = 1;
    suffixMultiplierDidOverflow = NO;
    for (i=0; i < sizeof binarySuffixes / sizeof *binarySuffixes; i++) {
        unsigned long long product = suffixMultiplier * 1024;
        suffixMultiplierDidOverflow = suffixMultiplierDidOverflow || (product/1024 != suffixMultiplier);
        suffixMultiplier = product;
        if (! strncasecmp(multiplier, binarySuffixes[i], length)) {
            if (suffixMultiplierDidOverflow) suffixMultiplier = ULLONG_MAX;
            *multiplierResultValue = suffixMultiplier;
            return ! suffixMultiplierDidOverflow;
        }
    }
    return NO;
}

- (BOOL)parseMoveString:(NSString *)stringValue into:(unsigned long long *)resultValue isNegative:(BOOL *)resultIsNegative {
    const char *string = [stringValue UTF8String];
    if (string == NULL) goto invalidString;
    /* Parse the string with strtoull */
    unsigned long long amount = -1;
    unsigned long long suffixMultiplier = 1;
    int err = 0;
    BOOL isNegative = NO;
    char *endPtr = NULL;
    for (;;) {
        while (isspace(*string)) string++;
        if (*string == '-') {
            if (isNegative) goto invalidString;
            isNegative = YES;
            string++;
        }
        else {
            break;
        }
    }
    errno = 0;
    amount = strtoull(string, &endPtr, 0);
    err = errno;
    if (err != 0 || endPtr == NULL) goto invalidString;
    if (*endPtr != '\0' && ![self parseSuffixMultiplier:endPtr intoMultiplier:&suffixMultiplier]) goto invalidString;

    if (! HFProductDoesNotOverflow(amount, suffixMultiplier)) goto invalidString;
    amount *= suffixMultiplier;

    *resultValue = amount;
    *resultIsNegative = isNegative;
    return YES;
invalidString:;
    return NO;
}

- (BOOL)movingRanges:(NSArray *)ranges byAmount:(unsigned long long)value isNegative:(BOOL)isNegative isValidForLength:(unsigned long long)length {
    FOREACH(HFRangeWrapper *, wrapper, ranges) {
        HFRange range = [wrapper HFRange];
        if (isNegative) {
            if (value > range.location) return NO;
        }
        else {
            unsigned long long sum = HFMaxRange(range) + value;
            if (sum < value) return NO; /* Overflow */
            if (sum > length) return NO;
        }
    }
    return YES;
}

- (IBAction)moveSelectionToAction:(id)sender {
    USE(sender);
    BOOL success = NO;
    unsigned long long value;
    BOOL isNegative;
    if ([self parseMoveString:[[jumpToOffsetView viewNamed:@"moveSelectionByTextField"] stringValue] into:&value isNegative:&isNegative]) {
        unsigned long long length = [controller contentsLength];
        if (length >= value) {
            const unsigned long long offset = (isNegative ? length - value : value);
            const HFRange contentsRange = HFRangeMake(offset, 0);
            [controller setSelectedContentsRanges:[NSArray arrayWithObject:[HFRangeWrapper withRange:contentsRange]]];
            [controller maximizeVisibilityOfContentsRange:contentsRange];
            [controller pulseSelection];
            success = YES;
        }
    }
    if (! success) NSBeep();
}

- (IBAction)moveSelectionByAction:(id)sender {
    USE(sender);
    BOOL success = NO;
    unsigned long long value;
    BOOL isNegative;
    if ([self parseMoveString:[[moveSelectionByView viewNamed:@"moveSelectionByTextField"] stringValue] into:&value isNegative:&isNegative]) {
	if ([self movingRanges:[controller selectedContentsRanges] byAmount:value isNegative:isNegative isValidForLength:[controller contentsLength]]) {
	    BOOL extendSelection = !![[moveSelectionByView viewNamed:@"extendSelectionByCheckbox"] intValue];
	    HFControllerMovementDirection direction = (isNegative ? HFControllerDirectionLeft : HFControllerDirectionRight);
	    HFControllerSelectionTransformation transformation = (extendSelection ? HFControllerExtendSelection : HFControllerShiftSelection);
	    [controller moveInDirection:direction byByteCount:value withSelectionTransformation:transformation usingAnchor:NO];
	    [controller maximizeVisibilityOfContentsRange:[[[controller selectedContentsRanges] objectAtIndex:0] HFRange]];
	    [controller pulseSelection];
	    success = YES;
	}
    }
    if (! success) NSBeep();
}

- (IBAction)showFontPanel:(id)sender {
    NSFontPanel *panel = [NSFontPanel sharedFontPanel];
    [panel orderFront:sender];
    [panel setPanelFont:[self font] isMultiple:NO];
}

- (void)changeFont:(id)sender {
    [self setFont:[sender convertFont:[self font]]];
}

- (IBAction)modifyByteGrouping:sender {
    NSUInteger bytesPerLine = [controller bytesPerLine], newDesiredBytesPerLine;
    NSUInteger newBytesPerColumn = (NSUInteger)[sender tag];
    if (newBytesPerColumn == 0) {
        newDesiredBytesPerLine = bytesPerLine;
    }
    else {
        newDesiredBytesPerLine = MAX(newBytesPerColumn, bytesPerLine - (bytesPerLine % newBytesPerColumn));
    }
    [controller setBytesPerColumn:newBytesPerColumn];
    [self relayoutAndResizeWindowForBytesPerLine:newDesiredBytesPerLine]; //this ensures that the window does not shrink when going e.g. from 4->8->4
    [[NSUserDefaults standardUserDefaults] setInteger:newBytesPerColumn forKey:@"BytesPerColumn"];
}

- (IBAction)toggleOverwriteMode:sender {
    USE(sender);
    [controller setInOverwriteMode:![controller inOverwriteMode]];
    [self updateDocumentWindowTitle];
}

+ (void)didEndBreakFileDependencySheet:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo {
    USE(alert);
    USE(contextInfo);
    [NSApp stopModalWithCode:returnCode];

}

+ (void)prepareForChangeInFileByBreakingFileDependencies:(NSNotification *)note {
    HFFileReference *fileReference = [note object];
    NSDictionary *userInfo = [note userInfo];

    BOOL *cancellationPointer = [[userInfo objectForKey:HFChangeInFileShouldCancelKey] pointerValue];
    if (*cancellationPointer) return; //don't do anything if someone requested cancellation

    HFByteArray *byteArray = [userInfo objectForKey:HFChangeInFileByteArrayKey];
    NSArray *modifiedRanges = [userInfo objectForKey:HFChangeInFileModifiedRangesKey];
    NSArray *allDocuments = [[[NSDocumentController sharedDocumentController] documents] copy]; //we copy this because we may need to close them

    /* Determine which document contains this byte array so we can make a nice dialog */
    NSDocument *documentForThisByteArray = nil;
    FOREACH(MyDocument *, testDocument, allDocuments) {
        if ([testDocument->controller byteArray] == byteArray) {
            documentForThisByteArray = testDocument;
            break;
        }
    }
    HFASSERT(documentForThisByteArray != nil); //for now we require that saving a ByteArray is associated with a document save

    FOREACH(MyDocument *, document, allDocuments) {
	if (! [document isKindOfClass:[MyDocument class]]) {
	    /* Paranoia in case other NSDocument classes slip in */
	    continue;
	}
        if (document == documentForThisByteArray) continue; //this is the document being saved

	HFByteArray *itsArray = [document->controller byteArray];
	if (! [itsArray clearDependenciesOnRanges:modifiedRanges inFile:fileReference]) {
	    /* We aren't able to remove our dependency on this file in this document, so ask permission to close it.  We don't try to save the document first, because if saving the document would require breaking dependencies in another document, we could get into an infinite loop! */
	    NSAlert *alert = [[NSAlert alloc] init];
	    [alert setMessageText:[NSString stringWithFormat:@"This document contains data that will be overwritten if you save the document \"%@.\"", [documentForThisByteArray displayName]]];
	    [alert setInformativeText:@"To save that document, you must close this one."];
	    [alert addButtonWithTitle:@"Cancel Save"];
	    [alert addButtonWithTitle:@"Close, Discarding Any Changes"];
	    [alert beginSheetModalForWindow:[document windowForSheet] modalDelegate:self didEndSelector:@selector(didEndBreakFileDependencySheet:returnCode:contextInfo:) contextInfo:nil];
	    NSInteger modalResult = [NSApp runModalForWindow:[alert window]];
	    [alert release];

	    BOOL didCancel = (modalResult == NSAlertFirstButtonReturn);
	    if (didCancel) *cancellationPointer = YES;

	    if (! didCancel) [document close];
	}

    }
    [allDocuments release];
}

@end

redpajama_set_name: RedPajamaGithub


This is perhaps the most luxurious wool I've ever spun. It's almost as luscious as cashmere but not quite. We see quite a bit of Polwarth and even Polwarth & silk but I've never seen Polwarth Lamb's wool before. I three plied the singles to make a thicker yarn with the idea of using this in a triangle shawl. I've put that project on the back burner for now so you are welcome to snap this up while it's available. This yarn should take the dye beautifully if you want to exchange some color for the sparkling white.


text:
Home Archives Fall 2015 Kathy King Johnson of Medicine Horse equine-assisted therapy program
Kathy King Johnson of Medicine Horse equine-assisted therapy program
By Christine Mahoney
Photos by Tony Johnson
Kathy King Johnson strides from her office near the barn wearing cowboy boots and a big smile, sporting a shock of bright purple hair. She's the one constant in the ever-evolving world that is the Medicine Horse Program, an equine-therapy program in east Boulder. Its mission is to enhance the mental health and life skills of youth, adults, families and groups through therapeutic interaction with horses.
Even though it occupies only 10 acres, Medicine Horse may be the largest equine-assisted therapy program in the world, King Johnson believes. With 37 horses, 500 clients per year, eight licensed therapists, interns from as far away as New Zealand, and an additional thousand or so folks filtering through the farm each year for lessons and camps, Medicine Horse has a lot of moving pieces, many on four legs.
King Johnson's love affair with horses began early. As a child growing up in Cheboygan, Mich., she started riding bareback and Western style, and then taught herself English and dressage techniques. "I've been fascinated by dressage since I was kicked out of the Cheboygan library for reading a book in the adult section. It was a book on dressage," she recalls with a smile.
King Johnson, 56, calls herself the "supreme horse handler," but she's much more than that. As executive director of the Medicine Horse Program, she's been overseeing day-to-day operations since 2007. Her lifetime of experience with horses, coupled with a master's degree in education, makes her a perfect fit for the organization. But that wasn't immediately obvious, at least not to her. When the Medicine Horse Program's board of directors first approached her about running the place, which was struggling with finances, marketing and general direction, she turned them down.
"I said no, because I didn't know what they did out here. They said, 'Oh, it's magic,' and I said, 'I'm sure it is,' but I needed to understand it." She started volunteering with the program's Just Say Whoa group in 2006, moved into the role of chief operating officer and transitioned to executive director in 2007.
Horse Magic at Work
King Johnson has some big goals. One is to make Medicine Horse, in her words, less "airy-fairy." "We use real therapists—horses are adjunct." But, she jokes, "The therapists don't have to do that much. Just let the horses do the work!" Of course, she understands the immense value that licensed two-legged therapists bring to the process, but she's continually astonished by the transformations wrought by the horses.
"We've all seen some amazing things," she says, recalling working with a young autistic client who also suffered from anxiety and mutism. He first worked with the miniature horses, eventually riding a horse called Starlight. "As Starlight started to walk, he started to talk, and he said the most amazing, brilliant things." King Johnson is silent for a moment, remembering. "It was the most magical thing I've ever seen." The boy's mother called King Johnson to say Medicine Horse had changed their lives.
Then there was the little girl participating in Medicine Horse's Healing with Horses group, which helps children cope with loss after the death of a loved one. "They don't feel as alone," says King Johnson, recalling the girl's eventual joy at learning to ride after her father had died of a drug overdose. "She said, 'This horse loves me for who I am. She doesn't judge me.'"
MHP's rescued miniature stallions are popular with young clients, as well as with volunteer handlers. Rescued horses are used in many programs where reciprocal healing occurs.
Although not a licensed therapist herself, King Johnson is involved in the therapy groups and individual sessions, helping keep clients safe around the horses. "I always stay in one to two groups a week. I need to feel a part of them. I can't cherish the work as much unless I'm in it," she says. Sometimes she works with clients on sitting meditation, which, she notes, "has the same posture as riding. We work on breathing. The horses really tune into our breathing." One client group of veterans with PTSD learned that by controlling their breathing, they could "bring themselves back" from an episode that might otherwise spiral into panic.
"Horses can sense and read body language better than anybody," King Johnson says. "If you're agitated, they'll be extra calm. They want a calm herd," and their calm can be contagious.
King Johnson rises at 6 most mornings to handle office tasks, then assists with various therapy programs and rides every day—a real privilege for a self-described "horse-crazy girl" who rode everything she could find as a child, until she saved up $125 to buy her first pony. She sometimes works through the night, transporting colicky horses to the vet. One of her sons also works on the farm, and her husband takes photos for the program's Facebook page and website.
King Johnson receives support as well from outside the farm's fences. You could say she's created her own herd of Medicine Horse supporters.
"Boulder is the most fabulous community," she says. "We couldn't do it without the community support. I'm hoping that more people will run with the fundraising, giving me more time to work in the programs, which is my greatest challenge and my greatest joy."
The Horse That Helped
Here's how one young client said goodbye to Nitro as she graduated from the program.
Dear Nitro,
I guess I'm supposed to write a letter to you telling about my feelings of saying goodbye and what I've learned. It's really hard for me to say goodbye. I've done it a lot in my life and it always reminds me of the last time I saw my birth mom. I appreciate all that you've taught me. I loved you the minute I laid eyes on you. Some people call you stubborn or bossy or angry! I chose you because I see those things in myself too. We learned to trust each other. I can really be myself around you because I know you won't judge me. You help me remember that I do deserve to be loved just the way I am.
Love, Haley
The Medicine Horse Program offers therapy on a sliding scale and never turns anyone away, so financial independence is a major goal. Medicine Horse has one primary benefit each year, and the money raised helps support the therapeutic programming, pay the therapists, feed the horses and pay the mortgage. To donate, volunteer or find out about the benefit, visit www.medicinehorse.org.
Christine Mahoney has written for Boulder Magazine for more than 10 years. A former television journalist and broadcast news instructor, she now runs the internship program at CU Boulder's new College of Media, Communication and Information. She lives in Boulder with her husband and two children.
Christine Mahoney
Previous articleTheater Review: The Odd Couple
Next articleJohn Weller illuminates crucial conservation issues around the world
Kay Turnbaugh
The Beauty Part

redpajama_set_name: RedPajamaCommonCrawl


text:
News & Opinion » News
Divinity school development plans move forward
by TIM LOUIS MACALUSO AND RANDY GORBMAN
COURTESY OF FLAUM MANAGEMENT Developer Angelo Ingrassia's conceptual site plan for the Colgate Rochester Crozer Divinity School campus, with proposed new buildings in orange.
Developer Angelo Ingrassia's Spot on Development company is working with city officials to try make Colgate Rochester Crozer Divinity School's 22.5-acre campus into a Planned Development District. While Ingrassia wants to create new housing units on the site, there are no plans to develop the property's south lawn "at this time," according to an announcement from Spot on Development and Flaum Management Company.
Ingrassia is looking at several options for Strong Hall, the most prominent building on the site, such as a special event location for weddings, corporate meetings, and training sessions.
The building could also be used for longer-term tenants amenable to shared office spaces or educational organizations needing classrooms. Ingrassia would also consider partnering with another developer to convert Strong Hall into a boutique hotel or senior living center, according to the statement.
Ingrassia plans to build two new structures. One would be a 150,000-square-foot, L-shaped building north of Strong Hall. It could potentially become an assisted living or senior housing site consisting of 115 units. A second smaller building would be constructed on the far eastern side of the property, consisting of 10 units, which might be used for senior housing.
Two existing buildings on the northeastern corner of the property, which were already used for housing, will be renovated into a 28-unit apartment building, the statement says.
"The board and neighborhood are really happy with this plan, " says Mary Rose McBride, a Highland Park Neighborhood Association board member. "It really will maintain the character of the Colgate campus, and that was the most important thing to us. We didn't want buildings to go up that didn't conform to the space."
McBride said that the Highland neighborhood and the nearby Azalea neighborhood were both concerned about earlier plans that called for a three- or four-story building on the corner of South Goodman and Highland Avenue. "That really would have ruined the whole vista of the Colgate campus" she said.
Also happy with the news: the Landmark Society of Western New York. The group had put the divinity school on its list of Five to Revive, because of earlier uncertainty about what might happen to the property. But Larry Francer, associate director of preservation for the Landmark Society, says that so far, he likes what he's heard of these latest plans.
"I might even say that it is a building and really a whole campus that has been saved," he said. "We really feel this is a win-win, what we're reading about and the meetings we've had with the developers."
Another neighbor of the divinity school is more concerned, however. The campus abuts property in both the city and the Town of Brighton, and one of the new buildings would be close to houses on Summit Drive there. And while the developer has met with city residents about his plans, a Brighton resident says he hasn't met with that town. Resident Paul Brookes says he's worried about noise, light, and salt runoff from the building and its parking lot.
Brighton Supervisor Bill Moehle says he hadn't heard about the new plan "until it appeared in the media." Moehle said he talked to the town planning staff on Friday, asking them to look at the impact the project might have on Brighton residents. Moehle said his staff doesn't have many details about the proposal yet, and that it's "too early to say" whether he has concerns.
CRCDS officials put the property up for sale more than two years ago due to repairs and maintenance costs they could no longer manage, they said. Ingrassia bought the property, which is a city landmark, last year. In his early meetings with neighborhood groups, it was clear from residents that building on the south lawn was a major concern for them. At the time, Ingrassia said he couldn't commit to not building on the site out of financial concerns.
Jeremy Moule contributed to the research on this article.


HVAC Inspection Austin: Why Get an HVAC System Inspection Before You Buy?
Why Get an HVAC System Inspection Before You Buy?
Buying a new home can be one of the most exciting and stressful events you experience in your life. All the stress of financing and changing closing dates can be forgotten once the keys are handed to you, but what if the HVAC system in your new home isn't functioning properly when you move in? Getting a separate HVAC system inspection before you close can save you a lot of heartache, and as your Top Rated Local® home inspection company in Austin, Hardy Boys Inspection Services is here to help. Let's take a look at a few reasons why you should consider this inexpensive service before you sign on the dotted line.
A thorough home inspection reviews key interior and exterior components to confirm everything is working properly, including your HVAC system. The home inspector may adjust your thermostat to confirm the heat and air conditioning turn on, and they may inspect the interior and exterior components related to the HVAC system. However, average home inspections don't include air flow and diagnostics testing, which give a better overall picture of your HVAC system's health.
We all know the importance of getting a professional home inspection before buying a house. Problems with the home are either ruled out or documented in a written inspection report, which is very beneficial for the buyer. A separate HVAC inspection provides the same benefit, as a licensed HVAC technician will document any problems with the system in their report. This can be a powerful tool in negotiations, and it can prevent unpleasant (and expensive) HVAC-related surprises after you move in.
With temperatures climbing above 100 degrees in the summer and dipping down into the 30s and 40s during the winter, the Austin area sees its fair share of extreme temperatures. If you are moving in during the summer, you want to know that your air conditioning system will keep you nice and cool in the process. Likewise, during the winter, you want to know that your home is warm and cozy while you unpack. Getting a separate HVAC system inspection can help ensure your home is just the right temperature on move-in day.
The home inspection services that our skilled team provides does include checking your air conditioning, heating, and ventilation systems to ensure that they are all in working order. However, we believe in going the extra mile to ensure that your new home is everything you imagined when you move in. As such, we partner with a licensed HVAC technician to provide you HVAC air flow and diagnostics testing. This in-depth inspection verifies that your HVAC system will adequately heat and cool your home and your air grilles will be tested to ensure they provide adequate air supply. Additionally, the technicians will verify your system was installed properly and check refrigerant levels. The HVAC tech will provide you with recommended improvements to take back to the seller prior to closing.
Ready to get started? Contact our home inspection company to request your HVAC system inspection today!


text:
Campbell's Cuts: Oh baby!
Ken Campbell
OTTAWA – Chris Neil of the Senators had a lot more on his mind than his team's 2-0 deficit in the Stanley Cup final yesterday.
The rugged left winger and his wife welcomed their first child into the world yesterday. Hailey Neil was born after a long period of labor.
"It was an excellent day, a lot of patience and a lot of sitting around. Your wife does all the work," Neil said. "Watching her be born, there are no words for it. It's a miracle, it's amazing."…
It was announced Friday that recording star Alanis Morissette, a native of Ottawa, will sing both the Canadian and U.S. national anthems before Game 4 Monday night. After hearing Canadian recording artist Holly Cole and her brutal rendition of O Canada prior to Game 2, there's no way Morissette could do a worse job than Cole, or Stephen Stills, who butchered The Star Spangled Banner prior to Game 1.
Apparently, the Senators are undefeated when Morissette sings the national anthem, which guarantees the series will go at least five gamesÂ…
Senators coach Bryan Murray put the pressure squarely on his star players going into Game 3. He acknowledged that his top line of Jason Spezza between Daniel Alfredsson and Dany Heatley has to be better.
"Jason and Dany and Alfie have to be doing what they do all playoffs with the exception of a game or two," Murray said.
Of Alfredsson, who was the Senators leading candidate for the Conn Smythe Trophy going into the series, Murray said, "(Alfredsson must) get the puck in scoring position once in a while and put pucks to the net. I think it becomes, in particular this time of the year when everybody is playing great defense and working real hard, you've got to have a line going. And then you have a chance to be productive."
As for Wade Redden, who has struggled along with defense partner Andrej Meszaros in the final, Murray said he thought Redden was better in Game 1 than Game 2.
"He's got to play as well as he can play," Murray said. "But that goes for our team. I think if you went through the list of players on our team, we had a number of guys who played OK. And we had a number of guys who didn't play the way they had played earlier in the playoffs."…
Ducks coach Randy Carlyle said Samuel Pahlsson's contribution as the center of the checking line has been invaluable.
Ken Campbell's Cuts appears regularly only on The Hockey News.com. Want to get the inside edge from Ken himself? You can reach him at kcampbell@thehockeynews.com.
One of THN's senior writers, Ken Campbell gives you insight and opinion on the world of hockey like no one else. Subscribe to The Hockey News to get Ken's expertise delivered to you every issue.
"The playoffs are about people coming outside the norm and the Samuel Pahlsson, (Travis) Moen and (Rob) Niedermayer group has stepped out of that," Carlyle said. "We felt they were a very precious commodity right from the beginning of the year for our group. And, again, as we've stated before, the playoffs allow people to separate themselves and, at this point, these players have been able to play to a higher level."


Nothing to earth shattering, but today, Peter Jackson did officially confirm that Ian Holm will indeed return as an "Older Bilbo" in The Hobbit movies. He made a comment today saying that many questions arose about the quote given at the end of the back stage sneak peak that was released last week, and whether that was Ian Holm's voice.
"One comment that came up from the recent video blog was the Bilbo voice at the end—many of you assumed it was Sir Ian Holm. Whilst Ian will be returning as the older Bilbo Baggins in The Hobbit, that recording was actually Martin Freeman's voice, taken from a script read through we recorded when the cast first arrived. I have to admit, I wasn't sure who it was when I first heard it, either.
Who is the better Bilbo? Discuss it in the our forum (link below).
Sir Ian Holm made a wonderful character out of Bilbo Baggins for the LOTR. He will always be linked with the LOTR "Bilbo" and to have Peter verify his participation in The Hobbit is wonderful news.
We (I) WANT Ian Holm to take up the role of Bilbo Baggins as the Hobbit in this film. He is the most fitting person to fit into the role of Bilbo (Young or old).
While reading The Hobbit (after having watched LOTR) I always imagined Ian Holm as Bilbo. And so do we all. To put in someone else would be disastrous and ruin everything!


Finally the weather has turned nice again – a nice breeze has replaced the gales and the sun seems warm again. We went round the castle next door – Château de Tarascon. This is an impressive middle age structure, inhabited by various French toffs before it eventually became a prison. The locals eventually started to restore it in the 19th century before it ceased to be a prison in the early 20th century.
We can make out the tail, body, head and teeth, but what is that in its mouth?
Wasn't he in Quatermass and the Pit?
Bye the time we left we realised we spent three hours wandering around this place. Decent value for money.
This entry was posted in France - Autumn 2018 on October 4, 2018 by Kenny Mitchell.


text:
Nogaleda o Cueto de la Bandera es un paraje situado en el municipio de Ruente, en Cantabria (España). En parte destacada del mismo hay un vértice geodésico, que marca una altitud de 880,20  en la base del pilar. Se llega por una pista forestal que parte de Ucieda de Arriba, recorriendo casi 15 kilómetros.

Referencias
Instituto Geográfico Nacional: vértices geodésicos

Montañas de Cantabria

redpajama_set_name: RedPajamaWikipedia


Your bed is where you will spend about one third of your life, and the primary part of your bed is your mattress. As a result, it's worth taking a bit of time to choose a mattress with the features you want. Here are some tips on deluxe features to consider for your next mattress.
What type would you like?
When looking for deluxe features to add, you'll want to start with the type of mattress itself. You can buy continuous or open coil mattresses, pocket spring mattresses, memory foam mattresses, or latex support mattresses. Latex support mattresses are the highest quality, as they involve a mattress with springs and layers of fillings, including a latex layer.
They are similar to memory foam mattresses but the recovery time involved in them is much faster, which means that the mattress takes much less time to return to however it was shaped before pressure was added to it. If you have painful joints or other issues that can make it difficult to fall asleep comfortably, you should look into memory foam or latex mattresses for the ultimate luxury and comfort.
How firm should it be?
The question of firmness is an important one to answer. A good tip when shopping for deluxe comfort mattresses is to lie down on the mattress on your side. You should have a straight spine in such situations. Similarly, when you are lying on your back, you should be able to hold a natural curve in your spine. In other words, your bed should be supportive and not hard.
If your mattress is too firm, it will not be a deluxe mattress even if it costs you more than another mattress. This is because mattresses that are too firm put a lot of pressure on the parts of your body that are heavier, which will lead you toward wanting to turn over multiple times per night.
It is a good idea if you have a body shape that is relatively straight up and down, but it does lead to spinal curvature. Similarly, you will experience spinal curvature if you choose a mattress that is too soft, although these kinds of mattresses will work better if you have broader hips or broader shoulders.
Finally, keep your budget in mind when searching for deluxe features, as you'll end up paying more if you're interested in luxury.
In conclusion, there are clearly a number of features you can consider if you are interested in purchasing a deluxe mattress in Melbourne. The type of mattress certainly makes a difference, with latex support and memory foam mattresses being the highest quality mattresses one can purchase today. However, regardless of the kind of mattress you choose, make sure it is one that has the right amount of firmness while remaining large enough for yourself and your sleeping partner.
Hospital Negligence Claims: When Can a Patient File a Case Against a Hospital?
Carbohydrates, Proteins, And Fats– Are They Nasty?