00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 @import <Foundation/CPString.j>
00024 @import <Foundation/CPArray.j>
00025
00026 @import "CPResponder.j"
00027 @import "CPWindowController.j"
00028
00029
00030
00031
00032
00033
00034 CPSaveOperation = 0;
00035
00036
00037
00038
00039 CPSaveAsOperation = 1;
00040
00041
00042
00043
00044 CPSaveToOperation = 2;
00045
00046
00047
00048
00049 CPAutosaveOperation = 3;
00050
00051
00052
00053
00054
00055 CPChangeDone = 0;
00056
00057
00058
00059
00060 CPChangeUndone = 1;
00061
00062
00063
00064
00065 CPChangeCleared = 2;
00066
00067
00068
00069
00070 CPChangeReadOtherContents = 3;
00071
00072
00073
00074
00075 CPChangeAutosaved = 4;
00076
00077 CPDocumentWillSaveNotification = @"CPDocumentWillSaveNotification";
00078 CPDocumentDidSaveNotification = @"CPDocumentDidSaveNotification";
00079 CPDocumentDidFailToSaveNotification = @"CPDocumentDidFailToSaveNotification";
00080
00081 var CPDocumentUntitledCount = 0;
00082
00090 @implementation CPDocument : CPResponder
00091 {
00092 CPURL _fileURL;
00093 CPString _fileType;
00094 CPArray _windowControllers;
00095 unsigned _untitledDocumentIndex;
00096
00097 BOOL _hasUndoManager;
00098 CPUndoManager _undoManager;
00099
00100 int _changeCount;
00101
00102 CPURLConnection _readConnection;
00103 CPURLRequest _writeRequest;
00104 }
00105
00110 - (id)init
00111 {
00112 self = [super init];
00113
00114 if (self)
00115 {
00116 _windowControllers = [];
00117
00118 _hasUndoManager = YES;
00119 _changeCount = 0;
00120
00121 [self setNextResponder:CPApp];
00122 }
00123
00124 return self;
00125 }
00126
00133 - (id)initWithType:(CPString)aType error:({CPError})anError
00134 {
00135 self = [self init];
00136
00137 if (self)
00138 [self setFileType:aType];
00139
00140 return self;
00141 }
00142
00154 - (id)initWithContentsOfURL:(CPURL)anAbsoluteURL ofType:(CPString)aType delegate:(id)aDelegate didReadSelector:(SEL)aDidReadSelector contextInfo:(id)aContextInfo
00155 {
00156 self = [self init];
00157
00158 if (self)
00159 {
00160 [self readFromURL:anAbsoluteURL ofType:aType delegate:aDelegate didReadSelector:aDidReadSelector contextInfo:aContextInfo];
00161
00162 [self setFileURL:anAbsoluteURL];
00163 [self setFileType:aType];
00164 }
00165
00166 return self;
00167 }
00168
00179 - (id)initForURL:(CPURL)anAbsoluteURL withContentsOfURL:(CPURL)absoluteContentsURL ofType:(CPString)aType delegate:(id)aDelegate didReadSelector:(SEL)aDidReadSelector contextInfo:(id)aContextInfo
00180 {
00181 self = [self init];
00182
00183 if (self)
00184 {
00185 [self readFromURL:absoluteContentsURL ofType:aType delegate:aDelegate didReadSelector:aDidReadSelector contextInfo:aContextInfo];
00186
00187 [self setFileURL:anAbsoluteURL];
00188 [self setFileType:aType];
00189 }
00190
00191 return self;
00192 }
00193
00202 - (CPData)dataOfType:(CPString)aType error:({CPError})anError
00203 {
00204 [CPException raise:CPUnsupportedMethodException
00205 reason:"dataOfType:error: must be overridden by the document subclass."];
00206 }
00207
00217 - (void)readFromData:(CPData)aData ofType:(CPString)aType error:(CPError)anError
00218 {
00219 [CPException raise:CPUnsupportedMethodException
00220 reason:"readFromData:ofType: must be overridden by the document subclass."];
00221 }
00222
00223
00227 - (void)makeWindowControllers
00228 {
00229 var controller = [[CPWindowController alloc] initWithWindowCibName:nil];
00230
00231 [self addWindowController:controller];
00232 }
00233
00237 - (CPArray)windowControllers
00238 {
00239 return _windowControllers;
00240 }
00241
00247 - (void)addWindowController:(CPWindowController)aWindowController
00248 {
00249 [_windowControllers addObject:aWindowController];
00250
00251 if ([aWindowController document] != self)
00252 {
00253 [aWindowController setNextResponder:self];
00254 [aWindowController setDocument:self];
00255 }
00256 }
00257
00258
00262 - (void)showWindows
00263 {
00264 [_windowControllers makeObjectsPerformSelector:@selector(showWindow:) withObject:self];
00265 }
00266
00270 - (CPString)displayName
00271 {
00272 if (_fileURL)
00273 return [_fileURL lastPathComponent];
00274
00275 if (!_untitledDocumentIndex)
00276 _untitledDocumentIndex = ++CPDocumentUntitledCount;
00277
00278 if (_untitledDocumentIndex == 1)
00279 return @"Untitled";
00280
00281 return @"Untitled " + _untitledDocumentIndex;
00282 }
00283
00287 - (CPString)windowCibName
00288 {
00289 return nil;
00290 }
00291
00296 - (void)windowControllerDidLoadNib:(CPWindowController)aWindowController
00297 {
00298 }
00299
00304 - (void)windowControllerWillLoadNib:(CPWindowController)aWindowController
00305 {
00306 }
00307
00308
00317 - (void)readFromURL:(CPURL)anAbsoluteURL ofType:(CPString)aType delegate:(id)aDelegate didReadSelector:(SEL)aDidReadSelector contextInfo:(id)aContextInfo
00318 {
00319 [_readConnection cancel];
00320
00321
00322 _readConnection = [CPURLConnection connectionWithRequest:[CPURLRequest requestWithURL:anAbsoluteURL] delegate:self];
00323
00324 _readConnection.session = _CPReadSessionMake(aType, aDelegate, aDidReadSelector, aContextInfo);
00325 }
00326
00330 - (CPURL)fileURL
00331 {
00332 return _fileURL;
00333 }
00334
00339 - (void)setFileURL:(CPURL)aFileURL
00340 {
00341 if (_fileURL == aFileURL)
00342 return;
00343
00344 _fileURL = aFileURL;
00345
00346 [_windowControllers makeObjectsPerformSelector:@selector(synchronizeWindowTitleWithDocumentName)];
00347 }
00348
00359 - (void)saveToURL:(CPURL)anAbsoluteURL ofType:(CPString)aTypeName forSaveOperation:(CPSaveOperationType)aSaveOperation delegate:(id)aDelegate didSaveSelector:(SEL)aDidSaveSelector contextInfo:(id)aContextInfo
00360 {
00361 var data = [self dataOfType:[self fileType] error:nil],
00362 oldChangeCount = _changeCount;
00363
00364 _writeRequest = [CPURLRequest requestWithURL:anAbsoluteURL];
00365
00366 [_writeRequest setHTTPMethod:@"POST"];
00367 [_writeRequest setHTTPBody:[data string]];
00368
00369 [_writeRequest setValue:@"close" forHTTPHeaderField:@"Connection"];
00370
00371 if (aSaveOperation == CPSaveOperation)
00372 [_writeRequest setValue:@"true" forHTTPHeaderField:@"x-cappuccino-overwrite"];
00373
00374 if (aSaveOperation != CPSaveToOperation)
00375 [self updateChangeCount:CPChangeCleared];
00376
00377
00378 var connection = [CPURLConnection connectionWithRequest:_writeRequest delegate:self];
00379
00380 connection.session = _CPSaveSessionMake(anAbsoluteURL, aSaveOperation, oldChangeCount, aDelegate, aDidSaveSelector, aContextInfo, connection);
00381 }
00382
00383
00384
00385
00386
00387 - (void)connection:(CPURLConnection)aConnection didReceiveResponse:(CPURLResponse)aResponse
00388 {
00389 var statusCode = [aResponse statusCode];
00390
00391
00392 if (statusCode == 200)
00393 return;
00394
00395 var session = aConnection.session;
00396
00397 if (aConnection == _readConnection)
00398 {
00399 [aConnection cancel];
00400
00401 alert("There was an error retrieving the document.");
00402
00403 objj_msgSend(session.delegate, session.didReadSelector, self, NO, session.contextInfo);
00404 }
00405 else
00406 {
00407
00408 if (statusCode == 409)
00409 {
00410 [aConnection cancel];
00411
00412 if (confirm("There already exists a file with that name, would you like to overwrite it?"))
00413 {
00414 [_writeRequest setValue:@"true" forHTTPHeaderField:@"x-cappuccino-overwrite"];
00415
00416 [aConnection start];
00417 }
00418 else
00419 {
00420 if (session.saveOperation != CPSaveToOperation)
00421 {
00422 _changeCount += session.changeCount;
00423 [_windowControllers makeObjectsPerformSelector:@selector(setDocumentEdited:) withObject:[self isDocumentEdited]];
00424 }
00425
00426 _writeRequest = nil;
00427
00428 objj_msgSend(session.delegate, session.didSaveSelector, self, NO, session.contextInfo);
00429 }
00430 }
00431 }
00432 }
00433
00434
00435
00436
00437
00438 - (void)connection:(CPURLConnection)aConnection didReceiveData:(CPString)aData
00439 {
00440 var session = aConnection.session;
00441
00442
00443 if (aConnection == _readConnection)
00444 {
00445 [self readFromData:[CPData dataWithString:aData] ofType:session.fileType error:nil];
00446
00447 objj_msgSend(session.delegate, session.didReadSelector, self, YES, session.contextInfo);
00448 }
00449 else
00450 {
00451 if (session.saveOperation != CPSaveToOperation)
00452 [self setFileURL:session.absoluteURL];
00453
00454 _writeRequest = nil;
00455
00456 objj_msgSend(session.delegate, session.didSaveSelector, self, YES, session.contextInfo);
00457 }
00458 }
00459
00460
00461
00462
00463
00464 - (void)connection:(CPURLConnection)aConnection didFailWithError:(CPError)anError
00465 {
00466 var session = aConnection.session;
00467
00468 if (_readConnection == aConnection)
00469 objj_msgSend(session.delegate, session.didReadSelector, self, NO, session.contextInfo);
00470
00471 else
00472 {
00473 if (session.saveOperation != CPSaveToOperation)
00474 {
00475 _changeCount += session.changeCount;
00476 [_windowControllers makeObjectsPerformSelector:@selector(setDocumentEdited:) withObject:[self isDocumentEdited]];
00477 }
00478
00479 _writeRequest = nil;
00480
00481 alert("There was an error saving the document.");
00482
00483 objj_msgSend(session.delegate, session.didSaveSelector, self, NO, session.contextInfo);
00484 }
00485 }
00486
00487
00488
00489
00490
00491 - (void)connectionDidFinishLoading:(CPURLConnection)aConnection
00492 {
00493 if (_readConnection == aConnection)
00494 _readConnection = nil;
00495 }
00496
00497
00501 - (BOOL)isDocumentEdited
00502 {
00503 return _changeCount != 0;
00504 }
00505
00510 - (void)updateChangeCount:(CPDocumentChangeType)aChangeType
00511 {
00512 if (aChangeType == CPChangeDone)
00513 ++_changeCount;
00514 else if (aChangeType == CPChangeUndone)
00515 --_changeCount;
00516 else if (aChangeType == CPChangeCleared)
00517 _changeCount = 0;
00518
00519
00520
00521
00522 [_windowControllers makeObjectsPerformSelector:@selector(setDocumentEdited:) withObject:[self isDocumentEdited]];
00523 }
00524
00525
00530 - (void)setFileType:(CPString)aType
00531 {
00532 _fileType = aType;
00533 }
00534
00538 - (CPString)fileType
00539 {
00540 return _fileType;
00541 }
00542
00543
00548 - (BOOL)hasUndoManager
00549 {
00550 return _hasUndoManager;
00551 }
00552
00557 - (void)setHasUndoManager:(BOOL)aFlag
00558 {
00559 if (_hasUndoManager == aFlag)
00560 return;
00561
00562 _hasUndoManager = aFlag;
00563
00564 if (!_hasUndoManager)
00565 [self setUndoManager:nil];
00566 }
00567
00568
00569 - (void)_undoManagerWillCloseGroup:(CPNotification)aNotification
00570 {
00571 var undoManager = [aNotification object];
00572
00573 if ([undoManager isUndoing] || [undoManager isRedoing])
00574 return;
00575
00576 [self updateChangeCount:CPChangeDone];
00577 }
00578
00579
00580 - (void)_undoManagerDidUndoChange:(CPNotification)aNotification
00581 {
00582 [self updateChangeCount:CPChangeUndone];
00583 }
00584
00585
00586 - (void)_undoManagerDidRedoChange:(CPNotification)aNotification
00587 {
00588 [self updateChangeCount:CPChangeDone];
00589 }
00590
00591
00592
00593
00594
00595
00596 - (void)setUndoManager:(CPUndoManager)anUndoManager
00597 {
00598 var defaultCenter = [CPNotificationCenter defaultCenter];
00599
00600 if (_undoManager)
00601 {
00602 [defaultCenter removeObserver:self
00603 name:CPUndoManagerDidUndoChangeNotification
00604 object:_undoManager];
00605
00606 [defaultCenter removeObserver:self
00607 name:CPUndoManagerDidRedoChangeNotification
00608 object:_undoManager];
00609
00610 [defaultCenter removeObserver:self
00611 name:CPUndoManagerWillCloseUndoGroupNotification
00612 object:_undoManager];
00613 }
00614
00615 _undoManager = anUndoManager;
00616
00617 if (_undoManager)
00618 {
00619
00620 [defaultCenter addObserver:self
00621 selector:@selector(_undoManagerDidUndoChange:)
00622 name:CPUndoManagerDidUndoChangeNotification
00623 object:_undoManager];
00624
00625 [defaultCenter addObserver:self
00626 selector:@selector(_undoManagerDidRedoChange:)
00627 name:CPUndoManagerDidRedoChangeNotification
00628 object:_undoManager];
00629
00630 [defaultCenter addObserver:self
00631 selector:@selector(_undoManagerWillCloseGroup:)
00632 name:CPUndoManagerWillCloseUndoGroupNotification
00633 object:_undoManager];
00634 }
00635 }
00636
00643 - (CPUndoManager)undoManager
00644 {
00645 if (_hasUndoManager && !_undoManager)
00646 [self setUndoManager:[[CPUndoManager alloc] init]];
00647
00648 return _undoManager;
00649 }
00650
00651
00652
00653
00654
00655 - (CPUndoManager)windowWillReturnUndoManager:(CPWindow)aWindow
00656 {
00657 return [self undoManager];
00658 }
00659
00660
00667 - (void)saveDocument:(id)aSender
00668 {
00669 if (_fileURL)
00670 {
00671 [[CPNotificationCenter defaultCenter]
00672 postNotificationName:CPDocumentWillSaveNotification
00673 object:self];
00674
00675 [self saveToURL:_fileURL ofType:[self fileType] forSaveOperation:CPSaveOperation delegate:self didSaveSelector:@selector(document:didSave:contextInfo:) contextInfo:NULL];
00676 }
00677 else
00678 [self saveDocumentAs:self];
00679 }
00680
00685 - (void)saveDocumentAs:(id)aSender
00686 {
00687 _documentName = window.prompt("Document Name:");
00688
00689 if (!_documentName)
00690 return;
00691
00692 [[CPNotificationCenter defaultCenter]
00693 postNotificationName:CPDocumentWillSaveNotification
00694 object:self];
00695
00696 [self saveToURL:[self proposedFileURL] ofType:[self fileType] forSaveOperation:CPSaveAsOperation delegate:self didSaveSelector:@selector(document:didSave:contextInfo:) contextInfo:NULL];
00697 }
00698
00699
00700
00701
00702 - (void)document:(id)aDocument didSave:(BOOL)didSave contextInfo:(id)aContextInfo
00703 {
00704 if (didSave)
00705 [[CPNotificationCenter defaultCenter]
00706 postNotificationName:CPDocumentDidSaveNotification
00707 object:self];
00708 else
00709 [[CPNotificationCenter defaultCenter]
00710 postNotificationName:CPDocumentDidFailToSaveNotification
00711 object:self];
00712 }
00713
00714 @end
00715
00716 var _CPReadSessionMake = function(aType, aDelegate, aDidReadSelector, aContextInfo)
00717 {
00718 return { fileType:aType, delegate:aDelegate, didReadSelector:aDidReadSelector, contextInfo:aContextInfo };
00719 }
00720
00721 var _CPSaveSessionMake = function(anAbsoluteURL, aSaveOperation, aChangeCount, aDelegate, aDidSaveSelector, aContextInfo, aConnection)
00722 {
00723 return { absoluteURL:anAbsoluteURL, saveOperation:aSaveOperation, changeCount:aChangeCount, delegate:aDelegate, didSaveSelector:aDidSaveSelector, contextInfo:aContextInfo, connection:aConnection };
00724 }