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/CPObject.j>
00024 @import <Foundation/CPBundle.j>
00025
00026 @import "CPDocument.j"
00027 @import "CPOpenPanel.j";
00028
00029
00030 var CPSharedDocumentController = nil;
00031
00037 @implementation CPDocumentController : CPObject
00038 {
00039 CPArray _documents;
00040 CPArray _documentTypes;
00041 }
00042
00048 + (id)sharedDocumentController
00049 {
00050 if (!CPSharedDocumentController)
00051 [[self alloc] init];
00052
00053 return CPSharedDocumentController;
00054 }
00055
00056
00057
00058
00059 - (id)init
00060 {
00061 self = [super init];
00062
00063 if (self)
00064 {
00065 _documents = [[CPArray alloc] init];
00066
00067 if (!CPSharedDocumentController)
00068 CPSharedDocumentController = self;
00069
00070 _documentTypes = [[[CPBundle mainBundle] infoDictionary] objectForKey:@"CPBundleDocumentTypes"];
00071 }
00072 return self;
00073 }
00074
00075
00076
00084 - (CPDocument)documentForURL:(CPURL)aURL
00085 {
00086 var index = 0,
00087 count = [_documents count];
00088
00089 for (; index < count; ++index)
00090 {
00091 var theDocument = _documents[index];
00092
00093 if ([[theDocument fileURL] isEqual:aURL])
00094 return theDocument;
00095 }
00096
00097 return nil;
00098 }
00099
00105 - (void)openUntitledDocumentOfType:(CPString)aType display:(BOOL)shouldDisplay
00106 {
00107 var theDocument = [self makeUntitledDocumentOfType:aType error:nil];
00108
00109 if (theDocument)
00110 [self addDocument:theDocument];
00111
00112 if (shouldDisplay)
00113 {
00114 [theDocument makeWindowControllers];
00115 [theDocument showWindows];
00116 }
00117
00118 return theDocument;
00119 }
00120
00127 - (CPDocument)makeUntitledDocumentOfType:(CPString)aType error:({CPError})anError
00128 {
00129 return [[[self documentClassForType:aType] alloc] initWithType:aType error:anError];
00130 }
00131
00139 - (CPDocument)openDocumentWithContentsOfURL:(CPURL)anAbsoluteURL display:(BOOL)shouldDisplay error:(CPError)anError
00140 {
00141 var result = [self documentForURL:anAbsoluteURL];
00142
00143 if (!result)
00144 {
00145 var type = [self typeForContentsOfURL:anAbsoluteURL error:anError];
00146
00147 result = [self makeDocumentWithContentsOfURL:anAbsoluteURL ofType:type delegate:self didReadSelector:@selector(document:didRead:contextInfo:) contextInfo:[CPDictionary dictionaryWithObject:shouldDisplay forKey:@"shouldDisplay"]];
00148
00149 [self addDocument:result];
00150
00151 if (result)
00152 [self noteNewRecentDocument:result];
00153 }
00154 else if (shouldDisplay)
00155 [result showWindows];
00156
00157 return result;
00158 }
00159
00168 - (CPDocument)reopenDocumentForURL:(CPURL)anAbsoluteURL withContentsOfURL:(CPURL)absoluteContentsURL error:(CPError)anError
00169 {
00170 return [self makeDocumentForURL:anAbsoluteURL withContentsOfURL:absoluteContentsURL ofType:[[_documentTypes objectAtIndex:0] objectForKey:@"CPBundleTypeName"] delegate:self didReadSelector:@selector(document:didRead:contextInfo:) contextInfo:nil];
00171 }
00172
00182 - (CPDocument)makeDocumentWithContentsOfURL:(CPURL)anAbsoluteURL ofType:(CPString)aType delegate:(id)aDelegate didReadSelector:(SEL)aSelector contextInfo:(id)aContextInfo
00183 {
00184 return [[[self documentClassForType:aType] alloc] initWithContentsOfURL:anAbsoluteURL ofType:aType delegate:aDelegate didReadSelector:aSelector contextInfo:aContextInfo];
00185 }
00186
00198 - (CPDocument)makeDocumentForURL:(CPURL)anAbsoluteURL withContentsOfURL:(CPURL)absoluteContentsURL ofType:(CPString)aType delegate:(id)aDelegate didReadSelector:(SEL)aSelector contextInfo:(id)aContextInfo
00199 {
00200 return [[[self documentClassForType:aType] alloc] initForURL:anAbsoluteURL withContentsOfURL:absoluteContentsURL ofType:aType delegate:aDelegate didReadSelector:aSelector contextInfo:aContextInfo];
00201 }
00202
00203
00204
00205
00206
00207 - (void)document:(CPDocument)aDocument didRead:(BOOL)didRead contextInfo:(id)aContextInfo
00208 {
00209 if (!didRead)
00210 return;
00211
00212 [aDocument makeWindowControllers];
00213
00214 if ([aContextInfo objectForKey:@"shouldDisplay"])
00215 [aDocument showWindows];
00216 }
00217
00222 - (CFAction)newDocument:(id)aSender
00223 {
00224 [self openUntitledDocumentOfType:[[_documentTypes objectAtIndex:0] objectForKey:@"CPBundleTypeName"] display:YES];
00225 }
00226
00227 -(void)openDocument:(id)aSender
00228 {
00229 var openPanel = [CPOpenPanel openPanel];
00230
00231 [openPanel runModal];
00232
00233 var URLs = [openPanel URLs],
00234 index = 0,
00235 count = [URLs count];
00236
00237 for (; index < count; ++index)
00238 [self openDocumentWithContentsOfURL:[CPURL URLWithString:URLs[index]] display:YES error:nil];
00239 }
00240
00241
00242
00247 - (CPArray)documents
00248 {
00249 return _documents;
00250 }
00251
00256 - (void)addDocument:(CPDocument)aDocument
00257 {
00258 [_documents addObject:aDocument];
00259 }
00260
00265 - (void)removeDocument:(CPDocument)aDocument
00266 {
00267 [_documents removeObjectIdenticalTo:aDocument];
00268 }
00269
00270 - (CPString)defaultType
00271 {
00272 return [_documentTypes[0] objectForKey:@"CPBundleTypeName"];
00273 }
00274
00275 - (CPString)typeForContentsOfURL:(CPURL)anAbsoluteURL error:(CPError)outError
00276 {
00277 var index = 0,
00278 count = _documentTypes.length,
00279
00280 extension = [[anAbsoluteURL pathExtension] lowercaseString],
00281 starType = nil;
00282
00283 for (; index < count; ++index)
00284 {
00285 var documentType = _documentTypes[index],
00286 extensions = [documentType objectForKey:@"CFBundleTypeExtensions"],
00287 extensionIndex = 0,
00288 extensionCount = extensions.length;
00289
00290 for (; extensionIndex < extensionCount; ++extensionIndex)
00291 {
00292 var thisExtension = [extensions[extensionIndex] lowercaseString];
00293 if (thisExtension === extension)
00294 return [documentType objectForKey:@"CPBundleTypeName"];
00295
00296 if (thisExtension === "****")
00297 starType = [documentType objectForKey:@"CPBundleTypeName"];
00298 }
00299 }
00300
00301 return starType || [self defaultType];
00302 }
00303
00304
00305
00306
00307 - (CPDictionary)_infoForType:(CPString)aType
00308 {
00309 var i = 0,
00310 count = [_documentTypes count];
00311
00312 for (;i < count; ++i)
00313 {
00314 var documentType = _documentTypes[i];
00315
00316 if ([documentType objectForKey:@"CPBundleTypeName"] == aType)
00317 return documentType;
00318 }
00319
00320 return nil;
00321 }
00322
00328 - (Class)documentClassForType:(CPString)aType
00329 {
00330 var className = [[self _infoForType:aType] objectForKey:@"CPDocumentClass"];
00331
00332 return className ? CPClassFromString(className) : nil;
00333 }
00334
00335 @end
00336
00337 @implementation CPDocumentController (Closing)
00338
00339 - (void)closeAllDocumentsWithDelegate:(id)aDelegate didCloseAllSelector:(SEL)didCloseSelector contextInfo:(Object)info
00340 {
00341 var context = {
00342 delegate: aDelegate,
00343 selector: didCloseSelector,
00344 context: info
00345 };
00346
00347 [self _closeDocumentsStartingWith:nil shouldClose:YES context:context];
00348 }
00349
00350
00351 - (void)_closeDocumentsStartingWith:(CPDocument)aDocument shouldClose:(BOOL)shouldClose context:(Object)context
00352 {
00353 if (shouldClose)
00354 {
00355 [aDocument close];
00356
00357 if ([[self documents] count] > 0)
00358 {
00359 [[[self documents] lastObject] canCloseDocumentWithDelegate:self
00360 shouldCloseSelector:@selector(_closeDocumentsStartingWith:shouldClose:context:)
00361 contextInfo:context];
00362 return;
00363 }
00364 }
00365
00366 if ([context.delegate respondsToSelector:context.selector])
00367 objj_msgSend(context.delegate, context.selector, self, [[self documents] count] === 0, context.context);
00368 }
00369
00370 @end
00371
00372 @implementation CPDocumentController (Recents)
00373
00374 - (CPArray)recentDocumentURLs
00375 {
00376
00377 if (typeof window["cpRecentDocumentURLs"] === 'function')
00378 return window.cpRecentDocumentURLs();
00379
00380 return [];
00381 }
00382
00383 - (void)clearRecentDocuments:(id)sender
00384 {
00385 if (typeof window["cpClearRecentDocuments"] === 'function')
00386 window.cpClearRecentDocuments();
00387
00388 [self _updateRecentDocumentsMenu];
00389 }
00390
00391 - (void)noteNewRecentDocument:(CPDocument)aDocument
00392 {
00393 [self noteNewRecentDocumentURL:[[aDocument fileURL] absoluteString]];
00394 }
00395
00396 - (void)noteNewRecentDocumentURL:(CPString)aURL
00397 {
00398 if (typeof window["cpNoteNewRecentDocumentPath"] === 'function')
00399 window.cpNoteNewRecentDocumentPath(aURL);
00400
00401 [self _updateRecentDocumentsMenu];
00402 }
00403
00404 - (void)_removeAllRecentDocumentsFromMenu:(CPMenu)aMenu
00405 {
00406 var items = [aMenu itemArray],
00407 count = [items count];
00408
00409 while (count--)
00410 {
00411 var item = items[count];
00412
00413 if ([item action] === @selector(_openRecentDocument:))
00414 [aMenu removeItemAtIndex:count];
00415 }
00416 }
00417
00418 - (void)_updateRecentDocumentsMenu
00419 {
00420 var menu = [[CPApp mainMenu] _menuWithName:@"_CPRecentDocumentsMenu"],
00421 recentDocuments = [self recentDocumentURLs],
00422 menuItems = [menu itemArray],
00423 documentCount = [recentDocuments count],
00424 menuItemCount = [menuItems count];
00425
00426 [self _removeAllRecentDocumentsFromMenu:menu];
00427
00428 if (menuItemCount)
00429 {
00430 if (!documentCount)
00431 {
00432 if ([menuItems[0] isSeparatorItem])
00433 [menu removeItemAtIndex:0];
00434 }
00435 else
00436 {
00437 if (![menuItems[0] isSeparatorItem])
00438 [menu insertItem:[CPMenuItem separatorItem] atIndex:0];
00439 }
00440 }
00441
00442 while (documentCount--)
00443 {
00444 var path = recentDocuments[documentCount],
00445 item = [[CPMenuItem alloc] initWithTitle:[path lastPathComponent] action:@selector(_openRecentDocument:) keyEquivalent:nil];
00446
00447 [item setTag:path];
00448 [menu insertItem:item atIndex:0];
00449 }
00450 }
00451
00452 - (void)_openRecentDocument:(id)sender
00453 {
00454 [self openDocumentWithContentsOfURL:[sender tag] display:YES error:nil];
00455 }
00456
00457 @end