apps-projectcenter/Library/PCProjectManager.m
Richard Frith-MacDonald dec193e4c5 Support specification of help file.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/apps/projectcenter/trunk@23665 72102866-910b-0410-8b05-ffd578937521
2006-09-29 08:25:12 +00:00

1286 lines
32 KiB
Objective-C

/*
GNUstep ProjectCenter - http: //www.gnustep.org
Copyright (C) 2000-2004 Free Software Foundation
Authors: Philippe C.D. Robert
Serg Stoyan
This file is part of GNUstep.
This application is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This application 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
Library General Public License for more details.
You should have received a copy of the GNU General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
*/
#include "PCDefines.h"
#include "PCPrefController.h"
#include "PCLogController.h"
#include "PCBundleLoader.h"
#include "PCFileManager.h"
#include "PCProjectManager.h"
#include "PCProject.h"
#include "PCProjectWindow.h"
#include "PCProjectBrowser.h"
#include "PCProjectInspector.h"
#include "PCProjectEditor.h"
#include "PCEditor.h"
#include "PCBuildPanel.h"
#include "PCLaunchPanel.h"
#include "PCLoadedFilesPanel.h"
#include "PCServer.h"
#include "ProjectType.h"
NSString *PCActiveProjectDidChangeNotification = @"PCActiveProjectDidChange";
@implementation PCProjectManager
// ============================================================================
// ==== Intialization & deallocation
// ============================================================================
- (id)init
{
if ((self = [super init]))
{
NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
buildPanel = nil;
launchPanel = nil;
loadedFilesPanel = nil;
findPanel = nil;
[self loadProjectTypeBunldes];
loadedProjects = [[NSMutableDictionary alloc] init];
nonProjectEditors = [[NSMutableDictionary alloc] init];
rootBuildPath = [[defs stringForKey: RootBuildDirectory] copy];
if (!rootBuildPath || [rootBuildPath isEqualToString: @""])
{
rootBuildPath = [NSTemporaryDirectory() copy];
}
[[NSNotificationCenter defaultCenter]
addObserver: self
selector: @selector(resetSaveTimer:)
name: PCSavePeriodDidChangeNotification
object: nil];
[[NSNotificationCenter defaultCenter]
addObserver: self
selector: @selector(editorDidClose:)
name: PCEditorDidCloseNotification
object: nil];
fileManager = [[PCFileManager alloc] initWithProjectManager: self];
}
return self;
}
- (void)dealloc
{
#ifdef DEVELOPMENT
NSLog (@"PCProjectManager: dealloc");
#endif
[[NSNotificationCenter defaultCenter] removeObserver: self];
if ([saveTimer isValid])
{
[saveTimer invalidate];
}
RELEASE(loadedProjects);
RELEASE(nonProjectEditors);
RELEASE(fileManager);
RELEASE(bundleLoader);
RELEASE(projectTypes);
RELEASE(projectTypeAccessaryView);
RELEASE(fileTypeAccessaryView);
RELEASE(rootBuildPath);
if (projectInspector) RELEASE(projectInspector);
if (loadedFilesPanel) RELEASE(loadedFilesPanel);
if (buildPanel) RELEASE(buildPanel);
if (launchPanel) RELEASE(launchPanel);
[super dealloc];
}
- (void)setDelegate: (id)aDelegate
{
delegate = aDelegate;
}
- (id)delegate
{
return delegate;
}
- (void)setPrefController: (id)aController
{
prefController = aController;
}
- (id)prefController
{
return prefController;
}
- (void)createProjectTypeAccessaryView
{
NSRect fr = NSMakeRect(20,30,160,20);
if (projectTypeAccessaryView != nil)
{
return;
}
// For "Open Project" and "New Project" panels
projectTypePopup = [[NSPopUpButton alloc] initWithFrame: fr pullsDown: NO];
[projectTypePopup setRefusesFirstResponder: YES];
[projectTypePopup setAutoenablesItems: NO];
[projectTypePopup addItemsWithTitles:
[[projectTypes allKeys]
sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)]];
[projectTypePopup sizeToFit];
[projectTypeAccessaryView sizeToFit];
[projectTypePopup selectItemAtIndex: 0];
projectTypeAccessaryView = [[NSBox alloc] init];
[projectTypeAccessaryView setTitle: @"Project Types"];
[projectTypeAccessaryView setTitlePosition: NSAtTop];
[projectTypeAccessaryView setBorderType: NSGrooveBorder];
[projectTypeAccessaryView addSubview: projectTypePopup];
[projectTypeAccessaryView sizeToFit];
[projectTypeAccessaryView setAutoresizingMask: NSViewMinXMargin
| NSViewMaxXMargin];
RELEASE(projectTypePopup);
}
// ============================================================================
// ==== Timer handling
// ============================================================================
- (BOOL)startSaveTimer
{
NSTimeInterval interval;
interval = [[[PCPrefController sharedPCPreferences]
objectForKey: AutoSavePeriod] intValue];
if (interval > 0 && saveTimer == nil)
{
saveTimer = [NSTimer
scheduledTimerWithTimeInterval: interval
target: self
selector: @selector(saveAllProjects)
userInfo: nil
repeats: YES];
return YES;
}
return NO;
}
- (BOOL)resetSaveTimer: (NSNotification *)notif
{
[self stopSaveTimer];
return [self startSaveTimer];
}
- (BOOL)stopSaveTimer
{
if (saveTimer && [saveTimer isValid])
{
[saveTimer invalidate];
saveTimer = nil;
return YES;
}
return NO;
}
// ============================================================================
// ==== Accessory methods
// ============================================================================
- (PCFileManager *)fileManager
{
return fileManager;
}
- (PCProjectInspector *)projectInspector
{
if (!projectInspector)
{
projectInspector =
[[PCProjectInspector alloc] initWithProjectManager: self];
}
return projectInspector;
}
- (NSPanel *)inspectorPanel
{
return [[self projectInspector] panel];
}
- (void)showProjectInspector: (id)sender
{
[[[self projectInspector] panel] makeKeyAndOrderFront: self];
}
- (NSPanel *)loadedFilesPanel
{
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
if (!loadedFilesPanel
&& [[ud objectForKey: SeparateLoadedFiles] isEqualToString: @"YES"])
{
loadedFilesPanel =
[[PCLoadedFilesPanel alloc] initWithProjectManager: self];
}
return loadedFilesPanel;
}
- (void)showProjectLoadedFiles: (id)sender
{
if ([[[[NSUserDefaults standardUserDefaults] dictionaryRepresentation]
objectForKey: SeparateLoadedFiles] isEqualToString: @"YES"])
{
[[self loadedFilesPanel] orderFront: nil];
}
}
- (NSPanel *)buildPanel
{
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
if (!buildPanel
&& [[ud objectForKey: SeparateBuilder] isEqualToString: @"YES"])
{
buildPanel = [[PCBuildPanel alloc] initWithProjectManager: self];
}
return buildPanel;
}
- (NSPanel *)launchPanel
{
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
if (!launchPanel
&& [[ud objectForKey: SeparateLauncher] isEqualToString: @"YES"])
{
launchPanel = [[PCLaunchPanel alloc] initWithProjectManager: self];
}
return launchPanel;
}
- (NSPanel *)projectFinderPanel
{
return findPanel;
}
- (NSString *)rootBuildPath
{
return rootBuildPath;
}
- (NSString *)projectPath
{
return [activeProject projectPath];
}
- (NSString *)selectedFileName
{
return [[activeProject projectBrowser] nameOfSelectedFile];
}
// ============================================================================
// ==== Project management
// ============================================================================
- (NSMutableDictionary *)loadedProjects
{
return loadedProjects;
}
- (PCProject *)activeProject
{
return activeProject;
}
- (PCProject *)rootActiveProject
{
PCProject *rootProject = nil;
if (!activeProject)
{
return nil;
}
rootProject = activeProject;
while ([rootProject isSubproject] == YES)
{
rootProject = [rootProject superProject];
}
return rootProject;
}
- (void)setActiveProject: (PCProject *)aProject
{
if (aProject != activeProject)
{
activeProject = aProject;
[[NSNotificationCenter defaultCenter]
postNotificationName: PCActiveProjectDidChangeNotification
object: activeProject];
}
}
- (void)saveAllProjectsIfNeeded
{
NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
// PCLogInfo(self, @"saveAllProjectsIfNeeded");
// If this method was called not by NSTimer, check if we should save projects
if ([[defs objectForKey: SaveOnQuit] isEqualToString: @"YES"])
{
[self saveAllProjects];
}
}
- (BOOL)saveAllProjects
{
NSEnumerator *enumerator = [loadedProjects keyEnumerator];
NSString *key;
PCProject *project;
while ((key = [enumerator nextObject]))
{
project = [loadedProjects objectForKey: key];
if ([project save] == NO)
{
return NO;
}
}
return YES;
}
// ============================================================================
// ==== Project actions
// ============================================================================
- (NSString *)convertLegacyProject: (NSMutableDictionary *)pDict
atPath: (NSString *)aPath
{
NSString *pPath = nil;
NSString *projectClassName = nil;
NSString *projectTypeName = nil;
NSString *_projectPath = nil;
NSFileManager *fm = [NSFileManager defaultManager];
NSString *_resPath = nil;
NSArray *_fromDirArray = nil;
NSString *_fromDirPath = nil;
NSString *_file = nil;
NSString *_2file = nil;
NSString *_resFile = nil;
int i = 0;
id<ProjectType> projectCreator;
NSMutableArray *otherResArray = nil;
NSString *plistFile = nil;
projectClassName = [pDict objectForKey: PCProjectBuilderClass];
if (projectClassName == nil)
{
// Project created by 0.4 release or later
return nil;
}
// Gorm project type doesn't exists anymore
if ([projectClassName isEqualToString: @"PCGormProj"])
{
projectTypeName = [NSString stringWithString: @"Application"];
projectClassName = [projectTypes objectForKey: projectTypeName];
}
// Handling directory layout
_projectPath = [aPath stringByDeletingLastPathComponent];
_resPath = [_projectPath stringByAppendingPathComponent: @"Resources"];
[fm createDirectoryAtPath: _resPath attributes: nil];
// Documents
_fromDirPath = [_projectPath stringByAppendingPathComponent: @"Documentation"];
_fromDirArray = [pDict objectForKey: PCDocuFiles];
for (i = 0; i < [_fromDirArray count]; i++)
{
_resFile = [_fromDirArray objectAtIndex: i];
_file = [_fromDirPath stringByAppendingPathComponent: _resFile];
_2file = [_resPath stringByAppendingPathComponent: _resFile];
[fm movePath: _file toPath: _2file handler: nil];
}
[fm removeFileAtPath: _fromDirPath handler: nil];
// Images
_fromDirPath = [_projectPath stringByAppendingPathComponent: @"Images"];
_fromDirArray = [pDict objectForKey: PCImages];
for (i = 0; i < [_fromDirArray count]; i++)
{
_resFile = [_fromDirArray objectAtIndex: i];
_file = [_fromDirPath stringByAppendingPathComponent: _resFile];
_2file = [_resPath stringByAppendingPathComponent: _resFile];
[fm movePath: _file toPath: _2file handler: nil];
}
[fm removeFileAtPath: _fromDirPath handler: nil];
// Interfaces
_fromDirArray = [pDict objectForKey: PCInterfaces];
for (i = 0; i < [_fromDirArray count]; i++)
{
_resFile = [_fromDirArray objectAtIndex: i];
_file = [_projectPath stringByAppendingPathComponent: _resFile];
_2file = [_resPath stringByAppendingPathComponent: _resFile];
[fm movePath: _file toPath: _2file handler: nil];
}
// Other resources
otherResArray = [NSMutableArray
arrayWithArray: [pDict objectForKey: PCOtherResources]];
plistFile = [NSString stringWithFormat: @"%@Info.plist",
[pDict objectForKey: PCProjectName]];
for (i = 0; i < [otherResArray count]; i++)
{
_resFile = [otherResArray objectAtIndex: i];
_file = [_projectPath stringByAppendingPathComponent: _resFile];
if ([_resFile isEqualToString: plistFile])
{
_2file =
[_resPath stringByAppendingPathComponent: @"Info-gnustep.plist"];
[otherResArray replaceObjectAtIndex: i
withObject: @"Info-gnustep.plist"];
[pDict setObject: otherResArray forKey: PCOtherResources];
}
else
{
_2file = [_resPath stringByAppendingPathComponent: _resFile];
}
[fm movePath: _file toPath: _2file handler: nil];
}
// GNUmakefiles will be generated in [PCProject initWithProjectDictionary: ]
// Remove obsolete records from project dictionary and write to PC.project
pPath = [[aPath stringByDeletingLastPathComponent]
stringByAppendingPathComponent: @"PC.project"];
projectCreator = [NSClassFromString(projectClassName) sharedCreator];
projectTypeName = [projectCreator projectTypeName];
[pDict setObject: projectTypeName forKey: PCProjectType];
[pDict removeObjectForKey: PCProjectBuilderClass];
[pDict removeObjectForKey: PCPrincipalClass];
if ([pDict writeToFile: pPath atomically: YES] == YES)
{
// [[NSFileManager defaultManager] removeFileAtPath: aPath handler: nil];
}
return projectClassName;
}
- (PCProject *)loadProjectAt: (NSString *)aPath
{
NSMutableDictionary *projectFile = nil;
NSString *projectTypeName = nil;
NSString *projectClassName = nil;
id<ProjectType> projectCreator;
PCProject *project = nil;
projectFile = [NSMutableDictionary dictionaryWithContentsOfFile: aPath];
// For compatibility with 0.3.x projects
projectClassName = [self convertLegacyProject: projectFile atPath: aPath];
if (projectClassName)
{
aPath = [[aPath stringByDeletingLastPathComponent]
stringByAppendingPathComponent: @"PC.project"];
}
// No conversion were taken
if (projectClassName == nil)
{
projectTypeName = [projectFile objectForKey: PCProjectType];
projectClassName = [projectTypes objectForKey: projectTypeName];
}
projectCreator = [NSClassFromString(projectClassName) sharedCreator];
if ((project = [projectCreator openProjectAt: aPath]))
{
PCLogStatus(self, @"Project %@ loaded as %@",
[project projectName], [projectCreator projectTypeName]);
// Started only if there's not save timer yet
[self startSaveTimer];
[project validateProjectDict];
return project;
}
NSRunAlertPanel(@"Loading Project Failed!",
@"Could not load project '%@'!",
@"OK",nil,nil,aPath);
return nil;
}
- (BOOL)openProjectAt: (NSString *)aPath
{
NSDictionary *pDict = [NSDictionary dictionaryWithContentsOfFile: aPath];
NSString *projectName = nil;
PCProject *project = nil;
NSDictionary *wap = nil;
BOOL isDir = NO;
projectName = [pDict objectForKey: PCProjectName];
if ((project = [loadedProjects objectForKey: projectName]) != nil)
{
[[project projectWindow] makeKeyAndOrderFront: self];
return YES;
}
if ([[NSFileManager defaultManager] fileExistsAtPath: aPath
isDirectory: &isDir] && !isDir)
{
project = [self loadProjectAt: aPath];
if (!project)
{
return NO;
}
[loadedProjects setObject: project forKey: projectName];
[self setActiveProject: project];
[project setProjectManager: self];
// Windows and panels
wap = [pDict objectForKey: @"PC_WINDOWS"];
if ([[wap allKeys] containsObject: @"ProjectBuild"])
{
[[project projectWindow] showProjectBuild: self];
}
if ([[wap allKeys] containsObject: @"ProjectLaunch"])
{
[[project projectWindow] showProjectLaunch: self];
}
if ([[wap allKeys] containsObject: @"LoadedFiles"])
{
[[project projectWindow] showProjectLoadedFiles: self];
}
[[project projectWindow] orderFront: self];
return YES;
}
return NO;
}
- (PCProject *)createProjectOfType: (NSString *)projectType
path: (NSString *)aPath
{
NSString *className = [projectTypes objectForKey: projectType];
Class creatorClass = NSClassFromString(className);
PCProject *project = nil;
NSString *projectName = [aPath lastPathComponent];
if ((project = [loadedProjects objectForKey: projectName]) != nil)
{
[[project projectWindow] makeKeyAndOrderFront: self];
return project;
}
if (![creatorClass conformsToProtocol: @protocol(ProjectType)])
{
[NSException raise: NOT_A_PROJECT_TYPE_EXCEPTION
format: @"%@ does not conform to ProjectType!", projectType];
return nil;
}
if (!(project = [[creatorClass sharedCreator] createProjectAt: aPath]))
{
return nil;
}
[project setProjectManager: self];
[self startSaveTimer];
return project;
}
- (void)openProject
{
NSArray *files = nil;
NSString *filePath = nil;
NSArray *fileTypes = [NSArray arrayWithObjects: @"project",@"pcproj",nil];
files = [fileManager filesForOpenOfType: fileTypes
multiple: NO
title: @"Open Project"
accView: nil];
filePath = [files objectAtIndex: 0];
if (filePath != nil && [self openProjectAt: filePath] == NO)
{
NSRunAlertPanel(@"Attention!",
@"Couldn't open project %@!",
@"OK",nil,nil,
[filePath stringByDeletingLastPathComponent]);
}
}
- (void)newProject
{
NSString *filePath = nil;
NSArray *fileTypes = [NSArray arrayWithObjects: @"project",@"pcproj",nil];
NSString *projectType = nil;
PCProject *project = nil;
[self createProjectTypeAccessaryView];
filePath = [fileManager fileForSaveOfType: fileTypes
title: @"New Project"
accView: projectTypeAccessaryView];
if (filePath != nil)
{
projectType = [projectTypePopup titleOfSelectedItem];
if (!(project = [self createProjectOfType: projectType path: filePath]))
{
NSRunAlertPanel(@"Attention!",
@"Failed to create %@!",
@"OK",nil,nil,filePath);
}
[loadedProjects setObject: project forKey: [project projectName]];
[self setActiveProject: project];
[[project projectWindow] orderFront: self];
}
}
- (BOOL)saveProject
{
PCProject *rootProject = [self rootActiveProject];
if (!rootProject)
{
return NO;
}
// PCLogInfo(self, @"save root project: %@", [rootProject projectName]);
// Save PC.project and the makefiles!
if ([rootProject save] == NO)
{
NSRunAlertPanel(@"Attention!",
@"Couldn't save project %@!",
@"OK",nil,nil,[activeProject projectName]);
return NO;
}
return YES;
}
- (BOOL)addProjectFiles
{
PCProject *project = [self rootActiveProject];
NSString *category = [[project projectBrowser] nameOfSelectedCategory];
NSString *categoryKey = [activeProject keyForCategory: category];
NSMutableArray *files = nil;
NSString *file = nil;
NSString *projectFile = nil;
NSArray *fileTypes = [project fileTypesForCategoryKey: categoryKey];
files = [fileManager filesForAddOfTypes: fileTypes];
/* PCLogInfo(self, @"[addProjectFiles] %@ to category: %@ of project %@",
files, categoryKey, [activeProject projectName]);*/
// Category may be changed
category = [[project projectBrowser] nameOfSelectedCategory];
categoryKey = [activeProject keyForCategory: category];
// No files was selected
if (!files)
{
return NO;
}
file = [[files objectAtIndex: 0] lastPathComponent];
projectFile = [activeProject projectFileFromFile: [files objectAtIndex: 0]
forKey: categoryKey];
if (![projectFile isEqualToString: file])
{
[activeProject addFiles: files forKey: categoryKey notify: YES];
}
else
{
// Copy and add files
[activeProject addAndCopyFiles: files forKey: categoryKey];
}
return YES;
}
- (BOOL)saveProjectFiles
{
return [[activeProject projectEditor] saveAllFiles];
}
- (BOOL)removeProjectFiles
{
PCProject *project = [self rootActiveProject];
NSArray *files = [[project projectBrowser] selectedFiles];
NSString *category = [[project projectBrowser] nameOfSelectedCategory];
NSString *categoryKey = [project keyForCategory: category];
NSString *directory = [activeProject dirForCategoryKey: categoryKey];
NSString *removeString = [NSString stringWithString: @"Remove files..."];
NSMutableArray *subprojs = [NSMutableArray array];
int i;
// Determining target project
if ([categoryKey isEqualToString: PCSubprojects])
{
if ([activeProject isSubproject])
{
project = [activeProject superProject];
[self setActiveProject: project];
}
removeString = [NSString stringWithString: @"Remove subprojects..."];
directory = [project dirForCategoryKey: categoryKey];
}
else
{
project = activeProject;
}
/* PCLogInfo(self, @"%@: %@ from %@", removeString, files, directory);
PCLogInfo(self, @"[removeProjectFiles]: %@ KEY: %@",
[activeProject projectName], categoryKey);*/
if (files)
{
int ret;
if ([categoryKey isEqualToString: PCLibraries])
{
ret = NSRunAlertPanel(@"Remove",
@"Remove libraries from Project?",
@"Remove",
@"Cancel",
nil);
}
else
{
ret = NSRunAlertPanel(@"Remove",
removeString,
@"...from Project and Disk",
@"...from Project only",
@"Cancel");
}
if (ret == NSAlertDefaultReturn || ret == NSAlertAlternateReturn)
{
BOOL flag = (ret == NSAlertDefaultReturn) ? YES : NO;
// Remove from projectDict
ret = [project removeFiles: files forKey: categoryKey notify: YES];
// Remove files from disk
if (flag && ret && ![categoryKey isEqualToString: PCLibraries])
{
if ([categoryKey isEqualToString: PCSubprojects])
{
for (i = 0; i < [files count]; i++)
{
[subprojs addObject:
[[files objectAtIndex: i]
stringByAppendingPathExtension: @"subproj"]];
}
files = subprojs;
}
ret = [fileManager removeFiles: files
fromDirectory: directory];
}
if (!ret)
{
NSRunAlertPanel(@"Alert",
@"Error removing files from project %@!",
@"OK", nil, nil, [activeProject projectName]);
return NO;
}
else if (flag)
{
// Save project because we've removed file(s) from disk
// Should be fixed later (add pending removal of files?)
[activeProject save];
}
}
else
{
return NO;
}
} // files
return YES;
}
- (void)closeProject: (PCProject *)aProject
{
PCProject *currentProject = nil;
NSString *projectName = [aProject projectName];
currentProject = RETAIN([loadedProjects objectForKey: projectName]);
if (!currentProject)
{
return;
}
// Remove it from the loaded projects!
[loadedProjects removeObjectForKey: projectName];
if ([loadedProjects count] == 0)
{
if (projectInspector)
{
[projectInspector close];
}
if (loadedFilesPanel && [loadedFilesPanel isVisible])
{
[loadedFilesPanel close];
}
if (buildPanel && [buildPanel isVisible])
{
[buildPanel close];
}
if (launchPanel && [launchPanel isVisible])
{
[launchPanel close];
}
[self setActiveProject: nil];
[self stopSaveTimer];
}
else if (currentProject == [self activeProject])
{
[self setActiveProject: [[loadedProjects allValues] lastObject]];
}
RELEASE(currentProject);
}
- (void)closeProject
{
NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
if ([[defs objectForKey: SaveOnQuit] isEqualToString: @"YES"])
{
[activeProject save];
}
[activeProject close: self];
}
- (BOOL)closeAllProjects
{
PCProject *project = nil;
NSEnumerator *enumerator = [loadedProjects objectEnumerator];
NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
// PCLogInfo(self, @"loaded %i projects", [loadedProjects count]);
while ([loadedProjects count] > 0)
{
project = [enumerator nextObject];
if ([[defs objectForKey: SaveOnQuit] isEqualToString: @"YES"])
{
[project save];
}
if ([project close: self] == NO)
{
return NO;
}
}
return YES;
}
// ============================================================================
// ==== File actions
// ============================================================================
- (void)openFile
{
NSArray *files = nil;
NSString *filePath = nil;
files = [fileManager filesForOpenOfType: nil
multiple: NO
title: @"Open File"
accView: nil];
filePath = [files objectAtIndex: 0];
if (filePath != nil)
{
[self openFileWithEditor: filePath];
}
}
- (void)newFile
{
[fileManager showNewFilePanel];
}
- (BOOL)saveFile
{
return [[activeProject projectEditor] saveFile];
}
- (BOOL)saveFileAs: (NSString *)path
{
return [[activeProject projectEditor] saveFileAs: path];
}
- (BOOL)saveFileTo
{
NSString *filePath = nil;
filePath = [fileManager fileForSaveOfType: nil
title: @"Save To..."
accView: nil];
if (filePath != nil && ![[activeProject projectEditor] saveFileTo: filePath])
{
NSRunAlertPanel(@"Alert", @"Couldn't save file to\n%@!",
@"OK", nil, nil, filePath);
return NO;
}
return YES;
}
- (BOOL)revertFileToSaved
{
return [[activeProject projectEditor] revertFileToSaved];
}
- (BOOL)renameFile
{
[self showProjectInspector: self];
[projectInspector selectSectionWithTitle: @"File Attributes"];
[projectInspector beginFileRename];
return YES;
}
- (void)closeFile
{
return [[activeProject projectEditor] closeActiveEditor: self];
}
// Project menu
// ============================================================================
// ==== Non project editors
// ============================================================================
- (void)openFileWithEditor: (NSString *)path
{
PCEditor *editor;
editor = [PCProjectEditor openFileInEditor: path];
[nonProjectEditors setObject: editor forKey: path];
RELEASE(editor);
}
- (void)editorDidClose: (NSNotification *)aNotif
{
PCEditor *editor = [aNotif object];
[nonProjectEditors removeObjectForKey: [editor path]];
}
@end
@implementation PCProjectManager (FileManagerDelegates)
// willCreateFile
- (NSString *)fileManager: (id)sender
willCreateFile: (NSString *)aFile
withKey: (NSString *)key
{
NSString *path = nil;
if ([activeProject doesAcceptFile: aFile forKey: key])
{
path = [[activeProject projectPath] stringByAppendingPathComponent: aFile];
}
return path;
}
// didCreateFiles
- (void)fileManager: (id)sender
didCreateFile: (NSString *)aFile
withKey: (NSString *)key
{
[activeProject addFiles: [NSArray arrayWithObject: aFile]
forKey: key
notify: YES];
}
@end
@implementation PCProjectManager (ProjectRegistration)
- (void)loadProjectTypeBunldes
{
projectTypes = [[NSMutableDictionary alloc] init];
bundleLoader = [[PCBundleLoader alloc] init];
[bundleLoader setDelegate: self];
[bundleLoader loadBundles];
}
- (PCBundleLoader *)bundleLoader
{
return bundleLoader;
}
- (NSDictionary *)projectTypes
{
return projectTypes;
}
- (void)bundleLoader: (id)sender didLoadBundle: (NSBundle *)aBundle
{
Class principalClass;
NSString *projectTypeName = nil;
NSAssert(aBundle,@"No valid bundle!");
principalClass = [aBundle principalClass];
projectTypeName = [[principalClass sharedCreator] projectTypeName];
if (![projectTypes objectForKey: projectTypeName])
{
[projectTypes setObject: NSStringFromClass(principalClass)
forKey: projectTypeName];
}
}
@end
@implementation PCProjectManager (Subprojects)
- (BOOL)newSubproject
{
// PCLogInfo(self, @"newSubproject");
if (!nsPanel)
{
if ([NSBundle loadNibNamed: @"NewSubproject" owner: self] == NO)
{
PCLogError(self, @"error loading NewSubproject NIB!");
return NO;
}
[nsPanel setFrameAutosaveName: @"NewSubproject"];
if (![nsPanel setFrameUsingName: @"NewSubproject"])
{
[nsPanel center];
}
[nsImage setImage: [NSApp applicationIconImage]];
[nsTypePB removeAllItems];
[nsTypePB addItemsWithTitles:
[[activeProject allowableSubprojectTypes]
sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)]];
[nsTypePB setRefusesFirstResponder: YES];
[nsTypePB selectItemAtIndex: 0];
[nsCancelButton setRefusesFirstResponder: YES];
[nsCreateButton setRefusesFirstResponder: YES];
}
[projectNameField setStringValue: [activeProject projectName]];
[nsPanel makeKeyAndOrderFront: nil];
[nsNameField setStringValue: @""];
[nsPanel makeFirstResponder: nsNameField];
return YES;
}
- (void)closeNewSubprojectPanel: (id)sender
{
[nsPanel orderOut: self];
}
- (BOOL)createSubproject: (id)sender
{
[nsPanel orderOut: self];
return [self createSubproject];
}
- (BOOL)createSubproject
{
PCProject *subproject = nil;
NSString *spName = [nsNameField stringValue];
NSString *spPath = nil;
NSString *spType = [nsTypePB titleOfSelectedItem];
if (![[spName pathExtension] isEqualToString: @"subproj"])
{
spName = [[nsNameField stringValue]
stringByAppendingPathExtension: @"subproj"];
}
spPath = [[activeProject projectPath] stringByAppendingPathComponent: spName];
PCLogStatus(self, @"creating subproject with type %@ at path %@",
spType, spPath);
// Create subproject
subproject = [self createSubprojectOfType: spType path: spPath];
return YES;
}
- (PCProject *)createSubprojectOfType: (NSString *)projectType
path: (NSString *)aPath
{
NSString *className = [projectTypes objectForKey: projectType];
Class creatorClass = NSClassFromString(className);
PCProject *subproject = nil;
/* NSString *subprojectName = [aPath lastPathComponent];
if ((project = [activeProject objectForKey: projectName]) != nil)
{
[[project projectWindow] makeKeyAndOrderFront: self];
return project;
}*/
if (![creatorClass conformsToProtocol: @protocol(ProjectType)])
{
[NSException raise: NOT_A_PROJECT_TYPE_EXCEPTION
format: @"%@ does not conform to ProjectType!", projectType];
return nil;
}
if (!(subproject = [[creatorClass sharedCreator] createProjectAt: aPath]))
{
return nil;
}
[subproject setIsSubproject: YES];
[subproject setSuperProject: activeProject];
[subproject setProjectManager: self];
// PCLogInfo(self, @"{createSubproject} add to %@", [activeProject projectName]);
[activeProject addSubproject: subproject];
return subproject;
}
- (void)controlTextDidChange: (NSNotification *)aNotif
{
if ([aNotif object] != nsNameField)
{
return;
}
// TODO: Add check for valid subproject named
if ([[nsNameField stringValue] length] > 0)
{
[nsCreateButton setEnabled: YES];
}
else
{
[nsCreateButton setEnabled: NO];
}
}
- (BOOL)addSubproject
{
NSFileManager *fm = [NSFileManager defaultManager];
NSMutableArray *files = nil;
NSString *pcProject = nil;
NSString *spDir = nil;
NSDictionary *spDict = nil;
NSString *spName = nil;
int i;
files = [fileManager
filesForAddOfTypes: [NSArray arrayWithObjects: @"subproj",nil]];
// Validate if it real projects
for (i = 0; i < [files count]; i++)
{
spDir = [files objectAtIndex: i];
pcProject = [spDir stringByAppendingPathComponent: @"PC.project"];
if (![[spDir pathExtension] isEqualToString: @"subproj"]
|| ![fm fileExistsAtPath: pcProject])
{
[files removeObjectAtIndex: i];
}
}
// PCLogInfo(self, @"{addSubproject} %@", files);
if (![fileManager copyFiles: files
intoDirectory: [activeProject projectPath]])
{
return NO;
}
for (i = 0; i < [files count]; i++)
{
spDir = [files objectAtIndex: i];
pcProject = [spDir stringByAppendingPathComponent: @"PC.project"];
spDict = [NSDictionary dictionaryWithContentsOfFile: pcProject];
spName = [spDict objectForKey: PCProjectName];
// PCLogInfo(self, @"{addSubproject} dir: %@ file: %@", spDir, pcProject);
[activeProject addSubprojectWithName: spName];
}
return YES;
}
@end