// Copyright 2003 - 2004 Nathan Hamblen
// Modified BSD license:
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
// 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import "EditController.h"
#import "MyDocument.h"
// Controller for edit windows for internet / dynamic locations. The user interface varies slightly between the two types (see typeChanged:). There is a field to edit the location, a button to go to the location (or switch to fill in for dynamic locations), a view of the site icon for the location and a button to update that view, and a save button (functionally identical to save menu command and keyboard shortcut).
@implementation EditController
#pragma mark Initilization
- (id)init
{
self = [super initWithWindowNibName:@"Edit"];
mDynamicLocationInfoSuperview = nil;
return self;
}
- (void)windowDidLoad
{
NSNotificationCenter *noteCenter = [NSNotificationCenter defaultCenter];
MyDocument *doc = [self document];
int newState;
NSString *initialUrl = [doc url];
[[self window] setDelegate: self];
// see respective methods for notification info
[noteCenter addObserver:self selector:@selector(siteImageChanged:)
name:@"SiteImageChanged" object:[self document]];
[noteCenter addObserver:self selector:@selector(updateDocument:)
name:@"NeedsUpdate" object:[self document]];
// if not set in [doc url]
if (!initialUrl)
initialUrl = [doc dynamicLocation];
// if set one way or the other
if (initialUrl)
[mURLField setStringValue: initialUrl];
// if a site icon was found (online) for the location while loading it from disk
if ([doc siteImage])
[mSiteIcon setImage:[doc siteImage]];
[mSiteIconProgress setStyle:NSProgressIndicatorSpinningStyle];
[mSiteIconProgress setDisplayedWhenStopped:NO];
newState = ([doc dynamicLocation]) ? NSOnState : NSOffState;
[mDynamicToggle setState:newState];
[self typeChanged:self];
// if initialUrl is unfilled, then this is a new webloc and we don't need to look for a site icon
if (initialUrl)
[self refreshSiteIcon:nil];
}
#pragma mark Handle site icons
// Tries to retrieve a site icon for the current web site. Note that we do not intend to support any kind of concurrent loading; when the refresh button is pressed any current load is told to cancel itself.
- (IBAction)refreshSiteIcon:(id)sender
{
MyDocument *doc = [self document];
[mSiteIconProgress startAnimation:sender];
[doc refreshSiteImage];
}
- (void)siteImageChanged:(NSNotification *)notification
{
[mSiteIconProgress stopAnimation:self];
[mSiteIcon setImage:[((MyDocument *)[self document]) siteImage]];
}
#pragma mark Support undo
- (id)windowWillReturnFieldEditor:(NSWindow *)aWindow toObject:(id)anObject
{
if ([anObject isEqual:mURLField]) {
if (!mUndoEditor) {
mUndoEditor = [[NSTextView alloc] initWithFrame:[anObject bounds]];
[mUndoEditor setFieldEditor:YES];
[mUndoEditor setAllowsUndo:YES];
}
return mUndoEditor;
}
return nil;
}
// change the field and register an undo even so that things don't get screwy with the text field's built-in undo
- (void)setUrlFieldUndoably:(NSString *)newValue
{
[[self undoManager] registerUndoWithTarget:self
selector:@selector(setUrlFieldUndoably:)
object:[mURLField stringValue]];
[mURLField setStringValue:newValue];
}
#pragma mark Go to / save
// Update the document object with the information in the view objects. The document object requests this update with a notification.
- (void)updateDocument:(NSNotification *)notification
{
MyDocument *doc = [self document];
NSString *trimmedUrlStr = [[mURLField stringValue]
stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSRange httpRange = [trimmedUrlStr rangeOfString:@"http"];
if (httpRange.location != 0)
trimmedUrlStr = [@"http://" stringByAppendingString:trimmedUrlStr];
if ([[mURLField stringValue] length] != [trimmedUrlStr length])
[self setUrlFieldUndoably:trimmedUrlStr];
if ([mDynamicToggle state] == NSOnState)
[doc setDynamicLocation:trimmedUrlStr];
else
[doc setUrl:trimmedUrlStr];
}
// If editing a standard location, this opens that location in a browser immediately. If editing a dynamic location, this attempts to save the location; the callback from the save function will switch to fill-in (non-edit) mode if the save was successful. If already in fill-in mode, this method fills the dynamic location with the form data and opens the resulting URL in the browser.
- (IBAction)goToLocation:(id)sender
{
MyDocument *doc = [self document];
[self updateDocument:nil];
if ([doc validate])
{
if ([mDynamicToggle state] == NSOffState)
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[doc url]]];
else
{
if ([doc isDocumentEdited])
[doc saveDocumentWithDelegate:self
didSaveSelector:@selector(savedForSwitch:didSave:contextInfo:)
contextInfo:nil];
else
[doc switchToFillin:YES];
}
}
}
// Callback function for save invoked by goToLocation when editing a dynamic location. If the save was completed, we switch modes to "go" to the location.
- (void)savedForSwitch:(MyDocument *)doc didSave:(BOOL)didSave
contextInfo:(void *)contextInfo
{
if (didSave)
[doc switchToFillin:YES];
}
#pragma mark Internet loc / Dyn loc switch
// Called when the checkbox selecting type is changed and also by windowControllerDidLoadNib. It sets the file type for the document, hides or shows the information on dynamic locations, and sets the name of the goto button.
- (IBAction)typeChanged:(id)sender
{
if ([sender isKindOfClass:[NSUndoManager class]])
{
int newState = ([mDynamicToggle state] == NSOnState) ? NSOffState : NSOnState;
[mDynamicToggle setState:newState];
}
if (sender != self)
[[self undoManager] registerUndoWithTarget:self
selector:@selector(typeChanged:)
object:[self undoManager]];
if ([mDynamicToggle state] == NSOffState)
{
[mGoToLocation setTitle:@"Go to Location"];
mDynamicLocationInfoSuperview = [mDynamicLocationInfo superview];
[mDynamicLocationInfo retain];
[mDynamicLocationInfo removeFromSuperview];
[mDynamicLocationInfoSuperview setNeedsDisplay:YES];
}
else
{
[mGoToLocation setTitle:@"Fill in Location"];
if (mDynamicLocationInfoSuperview)
{
[mDynamicLocationInfoSuperview addSubview:mDynamicLocationInfo];
[mDynamicLocationInfoSuperview setNeedsDisplay:YES];
[mDynamicLocationInfo release];
}
}
// the doc type needs to be kept in sync before its saved
[self updateDocument:nil];
}
#pragma mark Deallocation
- (void)dealloc
{
NSNotificationCenter *noteCenter = [NSNotificationCenter defaultCenter];
[noteCenter removeObserver:self];
if (mUndoEditor) [mUndoEditor release];
if (mDynamicLocationInfo && ![mDynamicLocationInfo superview]) [mDynamicLocationInfo release];
[super dealloc];
}
@end