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
00092 @implementation CPDocument : CPResponder
00093 {
00094 CPURL _fileURL;
00095 CPString _fileType;
00096 CPArray _windowControllers;
00097 unsigned _untitledDocumentIndex;
00098
00099 BOOL _hasUndoManager;
00100 CPUndoManager _undoManager;
00101
00102 int _changeCount;
00103
00104 CPURLConnection _readConnection;
00105 CPURLRequest _writeRequest;
00106 }
00107
00112 - (id)init
00113 {
00114 self = [super init];
00115
00116 if (self)
00117 {
00118 _windowControllers = [];
00119
00120 _hasUndoManager = YES;
00121 _changeCount = 0;
00122
00123 [self setNextResponder:CPApp];
00124 }
00125
00126 return self;
00127 }
00128
00135 - (id)initWithType:(CPString)aType error:({CPError})anError
00136 {
00137 self = [self init];
00138
00139 if (self)
00140 [self setFileType:aType];
00141
00142 return self;
00143 }
00144
00156 - (id)initWithContentsOfURL:(CPURL)anAbsoluteURL ofType:(CPString)aType delegate:(id)aDelegate didReadSelector:(SEL)aDidReadSelector contextInfo:(id)aContextInfo
00157 {
00158 self = [self init];
00159
00160 if (self)
00161 {
00162 [self readFromURL:anAbsoluteURL ofType:aType delegate:aDelegate didReadSelector:aDidReadSelector contextInfo:aContextInfo];
00163
00164 [self setFileURL:anAbsoluteURL];
00165 [self setFileType:aType];
00166 }
00167
00168 return self;
00169 }
00170
00181 - (id)initForURL:(CPURL)anAbsoluteURL withContentsOfURL:(CPURL)absoluteContentsURL ofType:(CPString)aType delegate:(id)aDelegate didReadSelector:(SEL)aDidReadSelector contextInfo:(id)aContextInfo
00182 {
00183 self = [self init];
00184
00185 if (self)
00186 {
00187 [self readFromURL:absoluteContentsURL ofType:aType delegate:aDelegate didReadSelector:aDidReadSelector contextInfo:aContextInfo];
00188
00189 [self setFileURL:anAbsoluteURL];
00190 [self setFileType:aType];
00191 }
00192
00193 return self;
00194 }
00195
00204 - (CPData)dataOfType:(CPString)aType error:({CPError})anError
00205 {
00206 [CPException raise:CPUnsupportedMethodException
00207 reason:"dataOfType:error: must be overridden by the document subclass."];
00208 }
00209
00219 - (void)readFromData:(CPData)aData ofType:(CPString)aType error:(CPError)anError
00220 {
00221 [CPException raise:CPUnsupportedMethodException
00222 reason:"readFromData:ofType: must be overridden by the document subclass."];
00223 }
00224
00225
00229 - (void)makeWindowControllers
00230 {
00231 var controller = [[CPWindowController alloc] initWithWindowCibName:nil];
00232
00233 [self addWindowController:controller];
00234 }
00235
00239 - (CPArray)windowControllers
00240 {
00241 return _windowControllers;
00242 }
00243
00249 - (void)addWindowController:(CPWindowController)aWindowController
00250 {
00251 [_windowControllers addObject:aWindowController];
00252
00253 if ([aWindowController document] != self)
00254 {
00255 [aWindowController setNextResponder:self];
00256 [aWindowController setDocument:self];
00257 }
00258 }
00259
00260
00264 - (void)showWindows
00265 {
00266 [_windowControllers makeObjectsPerformSelector:@selector(showWindow:) withObject:self];
00267 }
00268
00272 - (CPString)displayName
00273 {
00274 if (_fileURL)
00275 return [_fileURL lastPathComponent];
00276
00277 if (!_untitledDocumentIndex)
00278 _untitledDocumentIndex = ++CPDocumentUntitledCount;
00279
00280 if (_untitledDocumentIndex == 1)
00281 return @"Untitled";
00282
00283 return @"Untitled " + _untitledDocumentIndex;
00284 }
00285
00289 - (CPString)windowCibName
00290 {
00291 return nil;
00292 }
00293
00298 - (void)windowControllerDidLoadNib:(CPWindowController)aWindowController
00299 {
00300 }
00301
00306 - (void)windowControllerWillLoadNib:(CPWindowController)aWindowController
00307 {
00308 }
00309
00310
00319 - (void)readFromURL:(CPURL)anAbsoluteURL ofType:(CPString)aType delegate:(id)aDelegate didReadSelector:(SEL)aDidReadSelector contextInfo:(id)aContextInfo
00320 {
00321 [_readConnection cancel];
00322
00323
00324 _readConnection = [CPURLConnection connectionWithRequest:[CPURLRequest requestWithURL:anAbsoluteURL] delegate:self];
00325
00326 _readConnection.session = _CPReadSessionMake(aType, aDelegate, aDidReadSelector, aContextInfo);
00327 }
00328
00332 - (CPURL)fileURL
00333 {
00334 return _fileURL;
00335 }
00336
00341 - (void)setFileURL:(CPURL)aFileURL
00342 {
00343 if (_fileURL == aFileURL)
00344 return;
00345
00346 _fileURL = aFileURL;
00347
00348 [_windowControllers makeObjectsPerformSelector:@selector(synchronizeWindowTitleWithDocumentName)];
00349 }
00350
00361 - (void)saveToURL:(CPURL)anAbsoluteURL ofType:(CPString)aTypeName forSaveOperation:(CPSaveOperationType)aSaveOperation delegate:(id)aDelegate didSaveSelector:(SEL)aDidSaveSelector contextInfo:(id)aContextInfo
00362 {
00363 var data = [self dataOfType:[self fileType] error:nil],
00364 oldChangeCount = _changeCount;
00365
00366 _writeRequest = [CPURLRequest requestWithURL:anAbsoluteURL];
00367
00368 [_writeRequest setHTTPMethod:@"POST"];
00369 [_writeRequest setHTTPBody:[data string]];
00370
00371 [_writeRequest setValue:@"close" forHTTPHeaderField:@"Connection"];
00372
00373 if (aSaveOperation == CPSaveOperation)
00374 [_writeRequest setValue:@"true" forHTTPHeaderField:@"x-cappuccino-overwrite"];
00375
00376 if (aSaveOperation != CPSaveToOperation)
00377 [self updateChangeCount:CPChangeCleared];
00378
00379
00380 var connection = [CPURLConnection connectionWithRequest:_writeRequest delegate:self];
00381
00382 connection.session = _CPSaveSessionMake(anAbsoluteURL, aSaveOperation, oldChangeCount, aDelegate, aDidSaveSelector, aContextInfo, connection);
00383 }
00384
00385
00386
00387
00388
00389 - (void)connection:(CPURLConnection)aConnection didReceiveResponse:(CPURLResponse)aResponse
00390 {
00391
00392 if (![aResponse isKindOfClass:[CPHTTPURLResponse class]])
00393 return;
00394
00395 var statusCode = [aResponse statusCode];
00396
00397
00398 if (statusCode === 200)
00399 return;
00400
00401 var session = aConnection.session;
00402
00403 if (aConnection == _readConnection)
00404 {
00405 [aConnection cancel];
00406
00407 alert("There was an error retrieving the document.");
00408
00409 objj_msgSend(session.delegate, session.didReadSelector, self, NO, session.contextInfo);
00410 }
00411 else
00412 {
00413
00414 if (statusCode == 409)
00415 {
00416 [aConnection cancel];
00417
00418 if (confirm("There already exists a file with that name, would you like to overwrite it?"))
00419 {
00420 [_writeRequest setValue:@"true" forHTTPHeaderField:@"x-cappuccino-overwrite"];
00421
00422 [aConnection start];
00423 }
00424 else
00425 {
00426 if (session.saveOperation != CPSaveToOperation)
00427 {
00428 _changeCount += session.changeCount;
00429 [_windowControllers makeObjectsPerformSelector:@selector(setDocumentEdited:) withObject:[self isDocumentEdited]];
00430 }
00431
00432 _writeRequest = nil;
00433
00434 objj_msgSend(session.delegate, session.didSaveSelector, self, NO, session.contextInfo);
00435 }
00436 }
00437 }
00438 }
00439
00440
00441
00442
00443
00444 - (void)connection:(CPURLConnection)aConnection didReceiveData:(CPString)aData
00445 {
00446 var session = aConnection.session;
00447
00448
00449 if (aConnection == _readConnection)
00450 {
00451 [self readFromData:[CPData dataWithString:aData] ofType:session.fileType error:nil];
00452
00453 objj_msgSend(session.delegate, session.didReadSelector, self, YES, session.contextInfo);
00454 }
00455 else
00456 {
00457 if (session.saveOperation != CPSaveToOperation)
00458 [self setFileURL:session.absoluteURL];
00459
00460 _writeRequest = nil;
00461
00462 objj_msgSend(session.delegate, session.didSaveSelector, self, YES, session.contextInfo);
00463 }
00464 }
00465
00466
00467
00468
00469
00470 - (void)connection:(CPURLConnection)aConnection didFailWithError:(CPError)anError
00471 {
00472 var session = aConnection.session;
00473
00474 if (_readConnection == aConnection)
00475 objj_msgSend(session.delegate, session.didReadSelector, self, NO, session.contextInfo);
00476
00477 else
00478 {
00479 if (session.saveOperation != CPSaveToOperation)
00480 {
00481 _changeCount += session.changeCount;
00482 [_windowControllers makeObjectsPerformSelector:@selector(setDocumentEdited:) withObject:[self isDocumentEdited]];
00483 }
00484
00485 _writeRequest = nil;
00486
00487 alert("There was an error saving the document.");
00488
00489 objj_msgSend(session.delegate, session.didSaveSelector, self, NO, session.contextInfo);
00490 }
00491 }
00492
00493
00494
00495
00496
00497 - (void)connectionDidFinishLoading:(CPURLConnection)aConnection
00498 {
00499 if (_readConnection == aConnection)
00500 _readConnection = nil;
00501 }
00502
00503
00507 - (BOOL)isDocumentEdited
00508 {
00509 return _changeCount != 0;
00510 }
00511
00516 - (void)updateChangeCount:(CPDocumentChangeType)aChangeType
00517 {
00518 if (aChangeType == CPChangeDone)
00519 ++_changeCount;
00520 else if (aChangeType == CPChangeUndone)
00521 --_changeCount;
00522 else if (aChangeType == CPChangeCleared)
00523 _changeCount = 0;
00524
00525
00526
00527
00528 [_windowControllers makeObjectsPerformSelector:@selector(setDocumentEdited:) withObject:[self isDocumentEdited]];
00529 }
00530
00531
00536 - (void)setFileType:(CPString)aType
00537 {
00538 _fileType = aType;
00539 }
00540
00544 - (CPString)fileType
00545 {
00546 return _fileType;
00547 }
00548
00549
00554 - (BOOL)hasUndoManager
00555 {
00556 return _hasUndoManager;
00557 }
00558
00563 - (void)setHasUndoManager:(BOOL)aFlag
00564 {
00565 if (_hasUndoManager == aFlag)
00566 return;
00567
00568 _hasUndoManager = aFlag;
00569
00570 if (!_hasUndoManager)
00571 [self setUndoManager:nil];
00572 }
00573
00574
00575 - (void)_undoManagerWillCloseGroup:(CPNotification)aNotification
00576 {
00577 var undoManager = [aNotification object];
00578
00579 if ([undoManager isUndoing] || [undoManager isRedoing])
00580 return;
00581
00582 [self updateChangeCount:CPChangeDone];
00583 }
00584
00585
00586 - (void)_undoManagerDidUndoChange:(CPNotification)aNotification
00587 {
00588 [self updateChangeCount:CPChangeUndone];
00589 }
00590
00591
00592 - (void)_undoManagerDidRedoChange:(CPNotification)aNotification
00593 {
00594 [self updateChangeCount:CPChangeDone];
00595 }
00596
00597
00598
00599
00600
00601
00602 - (void)setUndoManager:(CPUndoManager)anUndoManager
00603 {
00604 var defaultCenter = [CPNotificationCenter defaultCenter];
00605
00606 if (_undoManager)
00607 {
00608 [defaultCenter removeObserver:self
00609 name:CPUndoManagerDidUndoChangeNotification
00610 object:_undoManager];
00611
00612 [defaultCenter removeObserver:self
00613 name:CPUndoManagerDidRedoChangeNotification
00614 object:_undoManager];
00615
00616 [defaultCenter removeObserver:self
00617 name:CPUndoManagerWillCloseUndoGroupNotification
00618 object:_undoManager];
00619 }
00620
00621 _undoManager = anUndoManager;
00622
00623 if (_undoManager)
00624 {
00625
00626 [defaultCenter addObserver:self
00627 selector:@selector(_undoManagerDidUndoChange:)
00628 name:CPUndoManagerDidUndoChangeNotification
00629 object:_undoManager];
00630
00631 [defaultCenter addObserver:self
00632 selector:@selector(_undoManagerDidRedoChange:)
00633 name:CPUndoManagerDidRedoChangeNotification
00634 object:_undoManager];
00635
00636 [defaultCenter addObserver:self
00637 selector:@selector(_undoManagerWillCloseGroup:)
00638 name:CPUndoManagerWillCloseUndoGroupNotification
00639 object:_undoManager];
00640 }
00641 }
00642
00649 - (CPUndoManager)undoManager
00650 {
00651 if (_hasUndoManager && !_undoManager)
00652 [self setUndoManager:[[CPUndoManager alloc] init]];
00653
00654 return _undoManager;
00655 }
00656
00657
00658
00659
00660
00661 - (CPUndoManager)windowWillReturnUndoManager:(CPWindow)aWindow
00662 {
00663 return [self undoManager];
00664 }
00665
00666
00673 - (void)saveDocument:(id)aSender
00674 {
00675 if (_fileURL)
00676 {
00677 [[CPNotificationCenter defaultCenter]
00678 postNotificationName:CPDocumentWillSaveNotification
00679 object:self];
00680
00681 [self saveToURL:_fileURL ofType:[self fileType] forSaveOperation:CPSaveOperation delegate:self didSaveSelector:@selector(document:didSave:contextInfo:) contextInfo:NULL];
00682 }
00683 else
00684 [self saveDocumentAs:self];
00685 }
00686
00691 - (void)saveDocumentAs:(id)aSender
00692 {
00693 _documentName = window.prompt("Document Name:");
00694
00695 if (!_documentName)
00696 return;
00697
00698 [[CPNotificationCenter defaultCenter]
00699 postNotificationName:CPDocumentWillSaveNotification
00700 object:self];
00701
00702 [self saveToURL:[self proposedFileURL] ofType:[self fileType] forSaveOperation:CPSaveAsOperation delegate:self didSaveSelector:@selector(document:didSave:contextInfo:) contextInfo:NULL];
00703 }
00704
00705
00706
00707
00708 - (void)document:(id)aDocument didSave:(BOOL)didSave contextInfo:(id)aContextInfo
00709 {
00710 if (didSave)
00711 [[CPNotificationCenter defaultCenter]
00712 postNotificationName:CPDocumentDidSaveNotification
00713 object:self];
00714 else
00715 [[CPNotificationCenter defaultCenter]
00716 postNotificationName:CPDocumentDidFailToSaveNotification
00717 object:self];
00718 }
00719
00720 @end
00721
00722 var _CPReadSessionMake = function(aType, aDelegate, aDidReadSelector, aContextInfo)
00723 {
00724 return { fileType:aType, delegate:aDelegate, didReadSelector:aDidReadSelector, contextInfo:aContextInfo };
00725 }
00726
00727 var _CPSaveSessionMake = function(anAbsoluteURL, aSaveOperation, aChangeCount, aDelegate, aDidSaveSelector, aContextInfo, aConnection)
00728 {
00729 return { absoluteURL:anAbsoluteURL, saveOperation:aSaveOperation, changeCount:aChangeCount, delegate:aDelegate, didSaveSelector:aDidSaveSelector, contextInfo:aContextInfo, connection:aConnection };
00730 }