/AppKit/CPDocument.j
http://github.com/cacaodev/cappuccino · Unknown · 920 lines · 757 code · 163 blank · 0 comment · 0 complexity · 9c94d25031367a70c39f529770468472 MD5 · raw file
- /*
- * CPDocument.j
- * AppKit
- *
- * Created by Francisco Tolmasky.
- * Copyright 2008, 280 North, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
- @import <Foundation/CPString.j>
- @import <Foundation/CPArray.j>
- @import "CPAlert.j"
- @import "CPResponder.j"
- @import "CPSavePanel.j"
- @import "CPViewController.j"
- @import "CPWindowController.j"
- @class CPDocumentController
- @global CPApp
- /*
- @global
- @group CPSaveOperationType
- */
- CPSaveOperation = 0;
- /*
- @global
- @group CPSaveOperationType
- */
- CPSaveAsOperation = 1;
- /*
- @global
- @group CPSaveOperationType
- */
- CPSaveToOperation = 2;
- /*
- @global
- @group CPSaveOperationType
- */
- CPAutosaveOperation = 3;
- /*
- @global
- @group CPDocumentChangeType
- */
- CPChangeDone = 0;
- /*
- @global
- @group CPDocumentChangeType
- */
- CPChangeUndone = 1;
- /*
- @global
- @group CPDocumentChangeType
- */
- CPChangeCleared = 2;
- /*
- @global
- @group CPDocumentChangeType
- */
- CPChangeReadOtherContents = 3;
- /*
- @global
- @group CPDocumentChangeType
- */
- CPChangeAutosaved = 4;
- CPDocumentWillSaveNotification = @"CPDocumentWillSaveNotification";
- CPDocumentDidSaveNotification = @"CPDocumentDidSaveNotification";
- CPDocumentDidFailToSaveNotification = @"CPDocumentDidFailToSaveNotification";
- var CPDocumentUntitledCount = 0;
- /*!
- @ingroup appkit
- @class CPDocument
- CPDocument is used to represent a document/file in a Cappuccino application.
- In a document-based application, generally multiple documents are open simultaneously
- (multiple text documents, slide presentations, spreadsheets, etc.), and multiple
- CPDocuments should be used to represent this.
- */
- @implementation CPDocument : CPResponder
- {
- CPWindow _window; // For outlet purposes.
- CPView _view; // For outlet purposes
- CPDictionary _viewControllersForWindowControllers;
- CPURL _fileURL;
- CPString _fileType;
- CPArray _windowControllers;
- unsigned _untitledDocumentIndex;
- BOOL _hasUndoManager;
- CPUndoManager _undoManager;
- int _changeCount;
- CPURLConnection _readConnection;
- CPURLRequest _writeRequest;
- CPAlert _canCloseAlert;
- }
- /*!
- Initializes an empty document.
- @return the initialized document
- */
- - (id)init
- {
- self = [super init];
- if (self)
- {
- _windowControllers = [];
- _viewControllersForWindowControllers = @{};
- _hasUndoManager = YES;
- _changeCount = 0;
- [self setNextResponder:CPApp];
- }
- return self;
- }
- /*!
- Initializes the document with a specific data type.
- @param aType the type of document to initialize
- @param anError not used
- @return the initialized document
- */
- - (id)initWithType:(CPString)aType error:(/*{*/CPError/*}*/)anError
- {
- self = [self init];
- if (self)
- [self setFileType:aType];
- return self;
- }
- /*!
- Initializes a document of a specific type located at a URL. Notifies
- the provided delegate after initialization.
- @param anAbsoluteURL the url of the document content
- @param aType the type of document located at the URL
- @param aDelegate the delegate to notify
- @param aDidReadSelector the selector used to notify the delegate
- @param aContextInfo context information passed to the delegate
- after initialization
- @return the initialized document
- */
- - (id)initWithContentsOfURL:(CPURL)anAbsoluteURL ofType:(CPString)aType delegate:(id)aDelegate didReadSelector:(SEL)aDidReadSelector contextInfo:(id)aContextInfo
- {
- self = [self init];
- if (self)
- {
- [self setFileURL:anAbsoluteURL];
- [self setFileType:aType];
- [self readFromURL:anAbsoluteURL ofType:aType delegate:aDelegate didReadSelector:aDidReadSelector contextInfo:aContextInfo];
- }
- return self;
- }
- /*!
- Initializes the document from a URL.
- @param anAbsoluteURL the document location
- @param absoluteContentsURL the location of the document's contents
- @param aType the type of the contents
- @param aDelegate this object will receive a callback after the document's contents are loaded
- @param aDidReadSelector the message selector that will be sent to \c aDelegate
- @param aContextInfo passed as the argument to the message sent to the \c aDelegate
- @return the initialized document
- */
- - (id)initForURL:(CPURL)anAbsoluteURL withContentsOfURL:(CPURL)absoluteContentsURL ofType:(CPString)aType delegate:(id)aDelegate didReadSelector:(SEL)aDidReadSelector contextInfo:(id)aContextInfo
- {
- self = [self init];
- if (self)
- {
- [self setFileURL:anAbsoluteURL];
- [self setFileType:aType];
- [self readFromURL:absoluteContentsURL ofType:aType delegate:aDelegate didReadSelector:aDidReadSelector contextInfo:aContextInfo];
- }
- return self;
- }
- /*!
- Returns the receiver's data in a specified type. The default implementation just
- throws an exception.
- @param aType the format of the data
- @param anError not used
- @throws CPUnsupportedMethodException if this method hasn't been overridden by the subclass
- @return the document data
- */
- - (CPData)dataOfType:(CPString)aType error:(/*{*/CPError/*}*/)anError
- {
- [CPException raise:CPUnsupportedMethodException
- reason:"dataOfType:error: must be overridden by the document subclass."];
- }
- /*!
- Sets the content of the document by reading the provided
- data. The default implementation just throws an exception.
- @param aData the document's data
- @param aType the document type
- @param anError not used
- @throws CPUnsupportedMethodException if this method hasn't been
- overridden by the subclass
- */
- - (void)readFromData:(CPData)aData ofType:(CPString)aType error:(CPError)anError
- {
- [CPException raise:CPUnsupportedMethodException
- reason:"readFromData:ofType: must be overridden by the document subclass."];
- }
- - (void)viewControllerWillLoadCib:(CPViewController)aViewController
- {
- }
- - (void)viewControllerDidLoadCib:(CPViewController)aViewController
- {
- }
- // Creating and managing window controllers
- /*!
- Creates the window controller for this document.
- */
- - (void)makeWindowControllers
- {
- [self makeViewAndWindowControllers];
- }
- - (void)makeViewAndWindowControllers
- {
- var viewCibName = [self viewCibName],
- windowCibName = [self windowCibName],
- viewController = nil,
- windowController = nil;
- // Create our view controller if we have a cib for it.
- if ([viewCibName length])
- viewController = [[CPViewController alloc] initWithCibName:viewCibName bundle:nil owner:self];
- // From a cib if we have one.
- if ([windowCibName length])
- windowController = [[CPWindowController alloc] initWithWindowCibName:windowCibName owner:self];
- // If not you get a standard window capable of displaying multiple documents and view
- else if (viewController)
- {
- var view = [viewController view],
- viewFrame = [view frame];
- viewFrame.origin = CGPointMake(50, 50);
- var theWindow = [[CPWindow alloc] initWithContentRect:viewFrame styleMask:CPTitledWindowMask | CPClosableWindowMask | CPMiniaturizableWindowMask | CPResizableWindowMask];
- windowController = [[CPWindowController alloc] initWithWindow:theWindow];
- }
- if (windowController && viewController)
- [windowController setSupportsMultipleDocuments:YES];
- if (windowController)
- [self addWindowController:windowController];
- if (viewController)
- [self addViewController:viewController forWindowController:windowController];
- }
- /*!
- Returns the document's window controllers
- */
- - (CPArray)windowControllers
- {
- return _windowControllers;
- }
- /*!
- Add a controller to the document's list of controllers. This should
- be called after making a new window controller.
- @param aWindowController the controller to add
- */
- - (void)addWindowController:(CPWindowController)aWindowController
- {
- [_windowControllers addObject:aWindowController];
- if ([aWindowController document] !== self)
- [aWindowController setDocument:self];
- }
- /*!
- Remove a controller to the document's list of controllers. This should
- be called after closing the controller's window.
- @param aWindowController the controller to remove
- */
- - (void)removeWindowController:(CPWindowController)aWindowController
- {
- if (aWindowController)
- [_windowControllers removeObject:aWindowController];
- if ([aWindowController document] === self)
- [aWindowController setDocument:nil];
- }
- - (CPView)view
- {
- return _view;
- }
- - (CPArray)viewControllers
- {
- return [_viewControllersForWindowControllers allValues];
- }
- - (void)addViewController:(CPViewController)aViewController forWindowController:(CPWindowController)aWindowController
- {
- // FIXME: exception if we don't own the window controller?
- [_viewControllersForWindowControllers setObject:aViewController forKey:[aWindowController UID]];
- if ([aWindowController document] === self)
- [aWindowController setViewController:aViewController];
- }
- - (void)removeViewController:(CPViewController)aViewController
- {
- [_viewControllersForWindowControllers removeObject:aViewController];
- }
- - (CPViewController)viewControllerForWindowController:(CPWindowController)aWindowController
- {
- return [_viewControllersForWindowControllers objectForKey:[aWindowController UID]];
- }
- // Managing Document Windows
- /*!
- Shows all the document's windows.
- */
- - (void)showWindows
- {
- [_windowControllers makeObjectsPerformSelector:@selector(setDocument:) withObject:self];
- [_windowControllers makeObjectsPerformSelector:@selector(showWindow:) withObject:self];
- }
- /*!
- Returns the name of the document as displayed in the title bar.
- */
- - (CPString)displayName
- {
- if (_fileURL)
- return [_fileURL lastPathComponent];
- if (!_untitledDocumentIndex)
- _untitledDocumentIndex = ++CPDocumentUntitledCount;
- if (_untitledDocumentIndex == 1)
- return @"Untitled";
- return @"Untitled " + _untitledDocumentIndex;
- }
- - (CPString)viewCibName
- {
- return nil;
- }
- /*!
- Returns the document's Cib name
- */
- - (CPString)windowCibName
- {
- return nil;
- }
- /*!
- Called after \c aWindowController loads the document's Nib file.
- @param aWindowController the controller that loaded the Nib file
- */
- - (void)windowControllerDidLoadCib:(CPWindowController)aWindowController
- {
- }
- /*!
- Called before \c aWindowController will load the document's Nib file.
- @param aWindowController the controller that will load the Nib file
- */
- - (void)windowControllerWillLoadCib:(CPWindowController)aWindowController
- {
- }
- // Reading from and Writing to URLs
- /*!
- Set the document's data from a URL. Notifies the provided delegate afterwards.
- @param anAbsoluteURL the URL to the document's content
- @param aType the document type
- @param aDelegate delegate to notify after reading the data
- @param aDidReadSelector message that will be sent to the delegate
- @param aContextInfo context information that gets sent to the delegate
- */
- - (void)readFromURL:(CPURL)anAbsoluteURL ofType:(CPString)aType delegate:(id)aDelegate didReadSelector:(SEL)aDidReadSelector contextInfo:(id)aContextInfo
- {
- [_readConnection cancel];
- // FIXME: Oh man is this every looking for trouble, we need to handle login at the Cappuccino level, with HTTP Errors.
- _readConnection = [CPURLConnection connectionWithRequest:[CPURLRequest requestWithURL:anAbsoluteURL] delegate:self];
- _readConnection.session = _CPReadSessionMake(aType, aDelegate, aDidReadSelector, aContextInfo);
- }
- /*!
- Returns the path to the document's file.
- */
- - (CPURL)fileURL
- {
- return _fileURL;
- }
- /*!
- Sets the path to the document's file.
- @param aFileURL the path to the document's file
- */
- - (void)setFileURL:(CPURL)aFileURL
- {
- if (_fileURL === aFileURL)
- return;
- _fileURL = aFileURL;
- [_windowControllers makeObjectsPerformSelector:@selector(synchronizeWindowTitleWithDocumentName)];
- }
- /*!
- Saves the document to the specified URL. Notifies the provided delegate
- with the provided selector and context info afterwards.
- @param anAbsoluteURL the url to write the document data to
- @param aTypeName the document type
- @param aSaveOperation the type of save operation
- @param aDelegate the delegate to notify after saving
- @param aDidSaveSelector the selector to send the delegate
- @param aContextInfo context info that gets passed to the delegate
- */
- - (void)saveToURL:(CPURL)anAbsoluteURL ofType:(CPString)aTypeName forSaveOperation:(CPSaveOperationType)aSaveOperation delegate:(id)aDelegate didSaveSelector:(SEL)aDidSaveSelector contextInfo:(id)aContextInfo
- {
- var data = [self dataOfType:[self fileType] error:nil],
- oldChangeCount = _changeCount;
- _writeRequest = [CPURLRequest requestWithURL:anAbsoluteURL];
- // FIXME: THIS IS WRONG! We need a way to decide
- if ([CPPlatform isBrowser])
- [_writeRequest setHTTPMethod:@"POST"];
- else
- [_writeRequest setHTTPMethod:@"PUT"];
- [_writeRequest setHTTPBody:[data rawString]];
- [_writeRequest setValue:@"close" forHTTPHeaderField:@"Connection"];
- if (aSaveOperation === CPSaveOperation)
- [_writeRequest setValue:@"true" forHTTPHeaderField:@"x-cappuccino-overwrite"];
- if (aSaveOperation !== CPSaveToOperation)
- [self updateChangeCount:CPChangeCleared];
- // FIXME: Oh man is this every looking for trouble, we need to handle login at the Cappuccino level, with HTTP Errors.
- var connection = [CPURLConnection connectionWithRequest:_writeRequest delegate:self];
- connection.session = _CPSaveSessionMake(anAbsoluteURL, aSaveOperation, oldChangeCount, aDelegate, aDidSaveSelector, aContextInfo, connection);
- }
- /*
- Implemented as a delegate method for CPURLConnection
- @ignore
- */
- - (void)connection:(CPURLConnection)aConnection didReceiveResponse:(CPURLResponse)aResponse
- {
- // If we got this far and it wasn't an HTTP request, then everything is fine.
- if (![aResponse isKindOfClass:[CPHTTPURLResponse class]])
- return;
- var statusCode = [aResponse statusCode];
- // Nothing to do if everything is hunky dory.
- if (statusCode === 200)
- return;
- var session = aConnection.session;
- if (aConnection == _readConnection)
- {
- [aConnection cancel];
- alert("There was an error retrieving the document.");
- objj_msgSend(session.delegate, session.didReadSelector, self, NO, session.contextInfo);
- }
- else
- {
- // 409: Conflict, in Cappuccino, overwrite protection for documents.
- if (statusCode == 409)
- {
- [aConnection cancel];
- if (confirm("There already exists a file with that name, would you like to overwrite it?"))
- {
- [_writeRequest setValue:@"true" forHTTPHeaderField:@"x-cappuccino-overwrite"];
- [aConnection start];
- }
- else
- {
- if (session.saveOperation != CPSaveToOperation)
- {
- _changeCount += session.changeCount;
- [_windowControllers makeObjectsPerformSelector:@selector(setDocumentEdited:) withObject:[self isDocumentEdited]];
- }
- _writeRequest = nil;
- objj_msgSend(session.delegate, session.didSaveSelector, self, NO, session.contextInfo);
- [self _sendDocumentSavedNotification:NO];
- }
- }
- }
- }
- /*
- Implemented as a delegate method for CPURLConnection
- @ignore
- */
- - (void)connection:(CPURLConnection)aConnection didReceiveData:(CPString)aData
- {
- var session = aConnection.session;
- // READ
- if (aConnection == _readConnection)
- {
- [self readFromData:[CPData dataWithRawString:aData] ofType:session.fileType error:nil];
- objj_msgSend(session.delegate, session.didReadSelector, self, YES, session.contextInfo);
- }
- else
- {
- if (session.saveOperation != CPSaveToOperation)
- [self setFileURL:session.absoluteURL];
- _writeRequest = nil;
- objj_msgSend(session.delegate, session.didSaveSelector, self, YES, session.contextInfo);
- [self _sendDocumentSavedNotification:YES];
- }
- }
- /*
- Implemented as a delegate method for CPURLConnection
- @ignore
- */
- - (void)connection:(CPURLConnection)aConnection didFailWithError:(CPError)anError
- {
- var session = aConnection.session;
- if (_readConnection == aConnection)
- objj_msgSend(session.delegate, session.didReadSelector, self, NO, session.contextInfo);
- else
- {
- if (session.saveOperation != CPSaveToOperation)
- {
- _changeCount += session.changeCount;
- [_windowControllers makeObjectsPerformSelector:@selector(setDocumentEdited:) withObject:[self isDocumentEdited]];
- }
- _writeRequest = nil;
- alert("There was an error saving the document.");
- objj_msgSend(session.delegate, session.didSaveSelector, self, NO, session.contextInfo);
- [self _sendDocumentSavedNotification:NO];
- }
- }
- /*
- Implemented as a delegate method for CPURLConnection
- @ignore
- */
- - (void)connectionDidFinishLoading:(CPURLConnection)aConnection
- {
- if (_readConnection == aConnection)
- _readConnection = nil;
- }
- // Managing Document Status
- /*!
- Returns \c YES if there are any unsaved changes.
- */
- - (BOOL)isDocumentEdited
- {
- return _changeCount != 0;
- }
- /*!
- Updates the number of unsaved changes to the document.
- @param aChangeType a new document change to apply
- */
- - (void)updateChangeCount:(CPDocumentChangeType)aChangeType
- {
- if (aChangeType == CPChangeDone)
- ++_changeCount;
- else if (aChangeType == CPChangeUndone)
- --_changeCount;
- else if (aChangeType == CPChangeCleared)
- _changeCount = 0;
- /*else if (aChangeType == CPCHangeReadOtherContents)
- else if (aChangeType == CPChangeAutosaved)*/
- [_windowControllers makeObjectsPerformSelector:@selector(setDocumentEdited:) withObject:[self isDocumentEdited]];
- }
- // Managing File Types
- /*!
- Sets the document's file type
- @param aType the document's type
- */
- - (void)setFileType:(CPString)aType
- {
- _fileType = aType;
- }
- /*!
- Returns the document's file type
- */
- - (CPString)fileType
- {
- return _fileType;
- }
- // Working with Undo Manager
- /*!
- Returns \c YES if the document has a
- CPUndoManager.
- */
- - (BOOL)hasUndoManager
- {
- return _hasUndoManager;
- }
- /*!
- Sets whether the document should have a CPUndoManager.
- @param aFlag \c YES makes the document have an undo manager
- */
- - (void)setHasUndoManager:(BOOL)aFlag
- {
- if (_hasUndoManager == aFlag)
- return;
- _hasUndoManager = aFlag;
- if (!_hasUndoManager)
- [self setUndoManager:nil];
- }
- /* @ignore */
- - (void)_undoManagerWillCloseGroup:(CPNotification)aNotification
- {
- var undoManager = [aNotification object];
- if ([undoManager isUndoing] || [undoManager isRedoing])
- return;
- [self updateChangeCount:CPChangeDone];
- }
- /* @ignore */
- - (void)_undoManagerDidUndoChange:(CPNotification)aNotification
- {
- [self updateChangeCount:CPChangeUndone];
- }
- /* @ignore */
- - (void)_undoManagerDidRedoChange:(CPNotification)aNotification
- {
- [self updateChangeCount:CPChangeDone];
- }
- /*
- Sets the document's undo manager. This method will add the
- undo manager as an observer to the notification center.
- @param anUndoManager the new undo manager for the document
- */
- - (void)setUndoManager:(CPUndoManager)anUndoManager
- {
- var defaultCenter = [CPNotificationCenter defaultCenter];
- if (_undoManager)
- {
- [defaultCenter removeObserver:self
- name:CPUndoManagerDidUndoChangeNotification
- object:_undoManager];
- [defaultCenter removeObserver:self
- name:CPUndoManagerDidRedoChangeNotification
- object:_undoManager];
- [defaultCenter removeObserver:self
- name:CPUndoManagerWillCloseUndoGroupNotification
- object:_undoManager];
- }
- _undoManager = anUndoManager;
- if (_undoManager)
- {
- [defaultCenter addObserver:self
- selector:@selector(_undoManagerDidUndoChange:)
- name:CPUndoManagerDidUndoChangeNotification
- object:_undoManager];
- [defaultCenter addObserver:self
- selector:@selector(_undoManagerDidRedoChange:)
- name:CPUndoManagerDidRedoChangeNotification
- object:_undoManager];
- [defaultCenter addObserver:self
- selector:@selector(_undoManagerWillCloseGroup:)
- name:CPUndoManagerWillCloseUndoGroupNotification
- object:_undoManager];
- }
- }
- /*!
- Returns the document's undo manager. If the document
- should have one, but the manager is \c nil, it
- will be created and then returned.
- @return the document's undo manager
- */
- - (CPUndoManager)undoManager
- {
- if (_hasUndoManager && !_undoManager)
- [self setUndoManager:[[CPUndoManager alloc] init]];
- return _undoManager;
- }
- /*
- Implemented as a delegate of a CPWindow
- @ignore
- */
- - (CPUndoManager)windowWillReturnUndoManager:(CPWindow)aWindow
- {
- return [self undoManager];
- }
- // Handling User Actions
- /*!
- Saves the document. If the document does not
- have a file path to save to (\c fileURL)
- then \c -saveDocumentAs: will be called.
- @param aSender the object requesting the save
- */
- - (void)saveDocument:(id)aSender
- {
- [self saveDocumentWithDelegate:nil didSaveSelector:nil contextInfo:nil];
- }
- - (void)saveDocumentWithDelegate:(id)delegate didSaveSelector:(SEL)didSaveSelector contextInfo:(Object)contextInfo
- {
- if (_fileURL)
- {
- [[CPNotificationCenter defaultCenter]
- postNotificationName:CPDocumentWillSaveNotification
- object:self];
- [self saveToURL:_fileURL ofType:[self fileType] forSaveOperation:CPSaveOperation delegate:delegate didSaveSelector:didSaveSelector contextInfo:contextInfo];
- }
- else
- [self _saveDocumentAsWithDelegate:delegate didSaveSelector:didSaveSelector contextInfo:contextInfo];
- }
- /*!
- Saves the document to a user specified path.
- @param aSender the object requesting the operation
- */
- - (void)saveDocumentAs:(id)aSender
- {
- [self _saveDocumentAsWithDelegate:nil didSaveSelector:nil contextInfo:nil];
- }
- - (void)_saveDocumentAsWithDelegate:(id)delegate didSaveSelector:(SEL)didSaveSelector contextInfo:(Object)contextInfo
- {
- var savePanel = [CPSavePanel savePanel],
- response = [savePanel runModal];
- if (!response)
- return;
- var saveURL = [savePanel URL];
- [[CPNotificationCenter defaultCenter]
- postNotificationName:CPDocumentWillSaveNotification
- object:self];
- [self saveToURL:saveURL ofType:[self fileType] forSaveOperation:CPSaveAsOperation delegate:delegate didSaveSelector:didSaveSelector contextInfo:contextInfo];
- }
- /*
- @ignore
- */
- - (void)_sendDocumentSavedNotification:(BOOL)didSave
- {
- if (didSave)
- [[CPNotificationCenter defaultCenter]
- postNotificationName:CPDocumentDidSaveNotification
- object:self];
- else
- [[CPNotificationCenter defaultCenter]
- postNotificationName:CPDocumentDidFailToSaveNotification
- object:self];
- }
- @end
- @implementation CPDocument (ClosingDocuments)
- - (void)close
- {
- [_windowControllers makeObjectsPerformSelector:@selector(removeDocumentAndCloseIfNecessary:) withObject:self];
- [[CPDocumentController sharedDocumentController] removeDocument:self];
- }
- - (void)shouldCloseWindowController:(CPWindowController)controller delegate:(id)delegate shouldCloseSelector:(SEL)selector contextInfo:(Object)info
- {
- if ([controller shouldCloseDocument] || ([_windowControllers count] < 2 && [_windowControllers indexOfObject:controller] !== CPNotFound))
- [self canCloseDocumentWithDelegate:self shouldCloseSelector:@selector(_document:shouldClose:context:) contextInfo:{delegate:delegate, selector:selector, context:info}];
- else if ([delegate respondsToSelector:selector])
- objj_msgSend(delegate, selector, self, YES, info);
- }
- - (void)_document:(CPDocument)aDocument shouldClose:(BOOL)shouldClose context:(Object)context
- {
- if (aDocument === self && shouldClose)
- [self close];
- objj_msgSend(context.delegate, context.selector, aDocument, shouldClose, context.context);
- }
- - (void)canCloseDocumentWithDelegate:(id)aDelegate shouldCloseSelector:(SEL)aSelector contextInfo:(Object)context
- {
- if (![self isDocumentEdited])
- return [aDelegate respondsToSelector:aSelector] && objj_msgSend(aDelegate, aSelector, self, YES, context);
- _canCloseAlert = [[CPAlert alloc] init];
- [_canCloseAlert setDelegate:self];
- [_canCloseAlert setAlertStyle:CPWarningAlertStyle];
- [_canCloseAlert setTitle:@"Unsaved Document"];
- [_canCloseAlert setMessageText:@"Do you want to save the changes you've made to the document \"" + ([self displayName] || [self fileName]) + "\"?"];
- [_canCloseAlert addButtonWithTitle:@"Save"];
- [_canCloseAlert addButtonWithTitle:@"Cancel"];
- [_canCloseAlert addButtonWithTitle:@"Don't Save"];
- _canCloseAlert._context = {delegate:aDelegate, selector:aSelector, context:context};
- [_canCloseAlert runModal];
- }
- - (void)alertDidEnd:(CPAlert)alert returnCode:(int)returnCode
- {
- if (alert !== _canCloseAlert)
- return;
- var delegate = alert._context.delegate,
- selector = alert._context.selector,
- context = alert._context.context;
- if (returnCode === 0)
- [self saveDocumentWithDelegate:delegate didSaveSelector:selector contextInfo:context];
- else
- objj_msgSend(delegate, selector, self, returnCode === 2, context);
- _canCloseAlert = nil;
- }
- @end
- var _CPReadSessionMake = function(aType, aDelegate, aDidReadSelector, aContextInfo)
- {
- return { fileType:aType, delegate:aDelegate, didReadSelector:aDidReadSelector, contextInfo:aContextInfo };
- };
- var _CPSaveSessionMake = function(anAbsoluteURL, aSaveOperation, aChangeCount, aDelegate, aDidSaveSelector, aContextInfo, aConnection)
- {
- return { absoluteURL:anAbsoluteURL, saveOperation:aSaveOperation, changeCount:aChangeCount, delegate:aDelegate, didSaveSelector:aDidSaveSelector, contextInfo:aContextInfo, connection:aConnection };
- };