API 0.9.5
AppKit/CPDocumentController.j
Go to the documentation of this file.
00001 /*
00002  * CPDocumentController.j
00003  * AppKit
00004  *
00005  * Created by Francisco Tolmasky.
00006  * Copyright 2008, 280 North, Inc.
00007  *
00008  * This library is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU Lesser General Public
00010  * License as published by the Free Software Foundation; either
00011  * version 2.1 of the License, or (at your option) any later version.
00012  *
00013  * This library is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00016  * Lesser General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU Lesser General Public
00019  * License along with this library; if not, write to the Free Software
00020  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00021  */
00022 
00023 
00024 
00025 
00026 var CPSharedDocumentController = nil;
00027 
00033 @implementation CPDocumentController : CPObject
00034 {
00035     CPArray _documents;
00036     CPArray _documentTypes;
00037 }
00038 
00044 + (id)sharedDocumentController
00045 {
00046     if (!CPSharedDocumentController)
00047         [[self alloc] init];
00048 
00049     return CPSharedDocumentController;
00050 }
00051 
00052 /*
00053     @ignore
00054 */
00055 - (id)init
00056 {
00057     self = [super init];
00058 
00059     if (self)
00060     {
00061         _documents = [[CPArray alloc] init];
00062 
00063         if (!CPSharedDocumentController)
00064             CPSharedDocumentController = self;
00065 
00066         _documentTypes = [[[CPBundle mainBundle] infoDictionary] objectForKey:@"CPBundleDocumentTypes"];
00067     }
00068     return self;
00069 }
00070 
00071 // Creating and Opening Documents
00072 
00080 - (CPDocument)documentForURL:(CPURL)aURL
00081 {
00082     var index = 0,
00083         count = [_documents count];
00084 
00085     for (; index < count; ++index)
00086     {
00087         var theDocument = _documents[index];
00088 
00089         if ([[theDocument fileURL] isEqual:aURL])
00090             return theDocument;
00091     }
00092 
00093     return nil;
00094 }
00095 
00101 - (void)openUntitledDocumentOfType:(CPString)aType display:(BOOL)shouldDisplay
00102 {
00103     var theDocument = [self makeUntitledDocumentOfType:aType error:nil];
00104 
00105     if (theDocument)
00106         [self addDocument:theDocument];
00107 
00108     if (shouldDisplay)
00109     {
00110         [theDocument makeWindowControllers];
00111         [theDocument showWindows];
00112     }
00113 
00114     return theDocument;
00115 }
00116 
00123 - (CPDocument)makeUntitledDocumentOfType:(CPString)aType error:({CPError})anError
00124 {
00125     return [[[self documentClassForType:aType] alloc] initWithType:aType error:anError];
00126 }
00127 
00135 - (CPDocument)openDocumentWithContentsOfURL:(CPURL)anAbsoluteURL display:(BOOL)shouldDisplay error:(CPError)anError
00136 {
00137     var result = [self documentForURL:anAbsoluteURL];
00138 
00139     if (!result)
00140     {
00141         var type = [self typeForContentsOfURL:anAbsoluteURL error:anError];
00142 
00143         result = [self makeDocumentWithContentsOfURL:anAbsoluteURL ofType:type delegate:self didReadSelector:@selector(document:didRead:contextInfo:) contextInfo:[CPDictionary dictionaryWithObject:shouldDisplay forKey:@"shouldDisplay"]];
00144 
00145         [self addDocument:result];
00146 
00147         if (result)
00148             [self noteNewRecentDocument:result];
00149     }
00150     else if (shouldDisplay)
00151         [result showWindows];
00152 
00153     return result;
00154 }
00155 
00164 - (CPDocument)reopenDocumentForURL:(CPURL)anAbsoluteURL withContentsOfURL:(CPURL)absoluteContentsURL error:(CPError)anError
00165 {
00166     return [self makeDocumentForURL:anAbsoluteURL withContentsOfURL:absoluteContentsURL  ofType:[[_documentTypes objectAtIndex:0] objectForKey:@"CPBundleTypeName"] delegate:self didReadSelector:@selector(document:didRead:contextInfo:) contextInfo:nil];
00167 }
00168 
00178 - (CPDocument)makeDocumentWithContentsOfURL:(CPURL)anAbsoluteURL ofType:(CPString)aType delegate:(id)aDelegate didReadSelector:(SEL)aSelector contextInfo:(id)aContextInfo
00179 {
00180     return [[[self documentClassForType:aType] alloc] initWithContentsOfURL:anAbsoluteURL ofType:aType delegate:aDelegate didReadSelector:aSelector contextInfo:aContextInfo];
00181 }
00182 
00194 - (CPDocument)makeDocumentForURL:(CPURL)anAbsoluteURL withContentsOfURL:(CPURL)absoluteContentsURL ofType:(CPString)aType delegate:(id)aDelegate didReadSelector:(SEL)aSelector contextInfo:(id)aContextInfo
00195 {
00196     return [[[self documentClassForType:aType] alloc] initForURL:anAbsoluteURL withContentsOfURL:absoluteContentsURL ofType:aType delegate:aDelegate didReadSelector:aSelector contextInfo:aContextInfo];
00197 }
00198 
00199 /*
00200     Implemented delegate method
00201     @ignore
00202 */
00203 - (void)document:(CPDocument)aDocument didRead:(BOOL)didRead contextInfo:(id)aContextInfo
00204 {
00205     if (!didRead)
00206         return;
00207 
00208     [aDocument makeWindowControllers];
00209 
00210     if ([aContextInfo objectForKey:@"shouldDisplay"])
00211         [aDocument showWindows];
00212 }
00213 
00218 - (CFAction)newDocument:(id)aSender
00219 {
00220     [self openUntitledDocumentOfType:[[_documentTypes objectAtIndex:0] objectForKey:@"CPBundleTypeName"] display:YES];
00221 }
00222 
00223 - (void)openDocument:(id)aSender
00224 {
00225     var openPanel = [CPOpenPanel openPanel];
00226 
00227     [openPanel runModal];
00228 
00229     var URLs = [openPanel URLs],
00230         index = 0,
00231         count = [URLs count];
00232 
00233     for (; index < count; ++index)
00234         [self openDocumentWithContentsOfURL:[CPURL URLWithString:URLs[index]] display:YES error:nil];
00235 }
00236 
00237 // Managing Documents
00238 
00243 - (CPArray)documents
00244 {
00245     return _documents;
00246 }
00247 
00252 - (void)addDocument:(CPDocument)aDocument
00253 {
00254     [_documents addObject:aDocument];
00255 }
00256 
00261 - (void)removeDocument:(CPDocument)aDocument
00262 {
00263     [_documents removeObjectIdenticalTo:aDocument];
00264 }
00265 
00266 - (CPString)defaultType
00267 {
00268     return [_documentTypes[0] objectForKey:@"CPBundleTypeName"];
00269 }
00270 
00271 - (CPString)typeForContentsOfURL:(CPURL)anAbsoluteURL error:(CPError)outError
00272 {
00273     var index = 0,
00274         count = _documentTypes.length,
00275 
00276         extension = [[anAbsoluteURL pathExtension] lowercaseString],
00277         starType = nil;
00278 
00279     for (; index < count; ++index)
00280     {
00281         var documentType = _documentTypes[index],
00282             extensions = [documentType objectForKey:@"CFBundleTypeExtensions"],
00283             extensionIndex = 0,
00284             extensionCount = extensions.length;
00285 
00286         for (; extensionIndex < extensionCount; ++extensionIndex)
00287         {
00288             var thisExtension = [extensions[extensionIndex] lowercaseString];
00289             if (thisExtension === extension)
00290                 return [documentType objectForKey:@"CPBundleTypeName"];
00291 
00292             if (thisExtension === "****")
00293                 starType = [documentType objectForKey:@"CPBundleTypeName"];
00294         }
00295     }
00296 
00297     return starType || [self defaultType];
00298 }
00299 
00300 // Managing Document Types
00301 
00302 /* @ignore */
00303 - (CPDictionary)_infoForType:(CPString)aType
00304 {
00305     var i = 0,
00306         count = [_documentTypes count];
00307 
00308     for (;i < count; ++i)
00309     {
00310         var documentType = _documentTypes[i];
00311 
00312         if ([documentType objectForKey:@"CPBundleTypeName"] == aType)
00313             return documentType;
00314     }
00315 
00316     return nil;
00317 }
00318 
00324 - (Class)documentClassForType:(CPString)aType
00325 {
00326     var className = [[self _infoForType:aType] objectForKey:@"CPDocumentClass"];
00327 
00328     return className ? CPClassFromString(className) : nil;
00329 }
00330 
00331 @end
00332 
00333 @implementation CPDocumentController (Closing)
00334 
00335 - (void)closeAllDocumentsWithDelegate:(id)aDelegate didCloseAllSelector:(SEL)didCloseSelector contextInfo:(Object)info
00336 {
00337     var context = {
00338         delegate: aDelegate,
00339         selector: didCloseSelector,
00340         context: info
00341     };
00342 
00343     [self _closeDocumentsStartingWith:nil shouldClose:YES context:context];
00344 }
00345 
00346 // Recursive callback method. Start it by passing in a document of nil.
00347 - (void)_closeDocumentsStartingWith:(CPDocument)aDocument shouldClose:(BOOL)shouldClose context:(Object)context
00348 {
00349     if (shouldClose)
00350     {
00351         [aDocument close];
00352 
00353         if ([[self documents] count] > 0)
00354         {
00355             [[[self documents] lastObject] canCloseDocumentWithDelegate:self
00356                                                     shouldCloseSelector:@selector(_closeDocumentsStartingWith:shouldClose:context:)
00357                                                             contextInfo:context];
00358             return;
00359         }
00360     }
00361 
00362     if ([context.delegate respondsToSelector:context.selector])
00363         objj_msgSend(context.delegate, context.selector, self, [[self documents] count] === 0, context.context);
00364 }
00365 
00366 @end
00367 
00368 @implementation CPDocumentController (Recents)
00369 
00370 - (CPArray)recentDocumentURLs
00371 {
00372     // FIXME move this to CP land
00373     if (typeof window["cpRecentDocumentURLs"] === 'function')
00374         return window.cpRecentDocumentURLs();
00375 
00376     return [];
00377 }
00378 
00379 - (void)clearRecentDocuments:(id)sender
00380 {
00381     if (typeof window["cpClearRecentDocuments"] === 'function')
00382         window.cpClearRecentDocuments();
00383 
00384    [self _updateRecentDocumentsMenu];
00385 }
00386 
00387 - (void)noteNewRecentDocument:(CPDocument)aDocument
00388 {
00389     [self noteNewRecentDocumentURL:[aDocument fileURL]];
00390 }
00391 
00392 - (void)noteNewRecentDocumentURL:(CPURL)aURL
00393 {
00394     var urlAsString = [aURL isKindOfClass:CPString] ? aURL : [aURL absoluteString];
00395     if (typeof window["cpNoteNewRecentDocumentPath"] === 'function')
00396         window.cpNoteNewRecentDocumentPath(urlAsString);
00397 
00398    [self _updateRecentDocumentsMenu];
00399 }
00400 
00401 - (void)_removeAllRecentDocumentsFromMenu:(CPMenu)aMenu
00402 {
00403     var items = [aMenu itemArray],
00404         count = [items count];
00405 
00406     while (count--)
00407     {
00408         var item = items[count];
00409 
00410         if ([item action] === @selector(_openRecentDocument:))
00411             [aMenu removeItemAtIndex:count];
00412     }
00413 }
00414 
00415 - (void)_updateRecentDocumentsMenu
00416 {
00417     var menu = [[CPApp mainMenu] _menuWithName:@"_CPRecentDocumentsMenu"],
00418         recentDocuments = [self recentDocumentURLs],
00419         menuItems = [menu itemArray],
00420         documentCount = [recentDocuments count],
00421         menuItemCount = [menuItems count];
00422 
00423     [self _removeAllRecentDocumentsFromMenu:menu];
00424 
00425     if (menuItemCount)
00426     {
00427         if (!documentCount)
00428         {
00429             if ([menuItems[0] isSeparatorItem])
00430                 [menu removeItemAtIndex:0];
00431         }
00432         else
00433         {
00434             if (![menuItems[0] isSeparatorItem])
00435                 [menu insertItem:[CPMenuItem separatorItem] atIndex:0];
00436         }
00437     }
00438 
00439     while (documentCount--)
00440     {
00441         var path = recentDocuments[documentCount],
00442             item = [[CPMenuItem alloc] initWithTitle:[path lastPathComponent] action:@selector(_openRecentDocument:) keyEquivalent:nil];
00443 
00444         [item setTag:path];
00445         [menu insertItem:item atIndex:0];
00446     }
00447 }
00448 
00449 - (void)_openRecentDocument:(id)sender
00450 {
00451     [self openDocumentWithContentsOfURL:[sender tag] display:YES error:nil];
00452 }
00453 
00454 @end
 All Classes Files Functions Variables Defines