1/* 2 GNUstep ProjectCenter - http://www.gnustep.org/experience/ProjectCenter.html 3 4 Copyright (C) 2001-2013 Free Software Foundation 5 6 Authors: Philippe C.D. Robert 7 Serg Stoyan 8 Riccardo Mottola 9 10 This file is part of GNUstep. 11 12 This application is free software; you can redistribute it and/or 13 modify it under the terms of the GNU General Public 14 License as published by the Free Software Foundation; either 15 version 2 of the License, or (at your option) any later version. 16 17 This application is distributed in the hope that it will be useful, 18 but WITHOUT ANY WARRANTY; without even the implied warranty of 19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 Library General Public License for more details. 21 22 You should have received a copy of the GNU General Public 23 License along with this library; if not, write to the Free 24 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. 25*/ 26 27#import <ProjectCenter/PCDefines.h> 28#import <ProjectCenter/PCProject.h> 29#import <ProjectCenter/PCFileManager.h> 30#import <ProjectCenter/PCFileCreator.h> 31 32#import <ProjectCenter/PCLogController.h> 33 34static PCFileCreator *_creator = nil; 35static NSDictionary *dict = nil; 36 37@implementation PCFileCreator 38 39+ (id)sharedCreator 40{ 41 if (_creator == nil) 42 { 43 NSDictionary *classDict; 44 NSDictionary *headerDict; 45 NSDictionary *ccDict; 46 NSDictionary *chDict; 47 NSDictionary *protocolDict; 48 NSDictionary *gsmarkupDict; 49 NSString *descr; 50 NSString *template; 51 NSBundle *bundle; 52 53 _creator = [[[self class] alloc] init]; 54 bundle = [NSBundle bundleForClass:[self class]]; 55 56 // Setting up the dictionary needed for registration! 57 58 // Objective C Class 59 descr = @"Generic Objective-C class.\n\n" 60 @"This is a plain subclass of NSObject which includes" 61 @" only Foundation.h."; 62 template = [bundle pathForResource:@"class" ofType:@"template"]; 63 classDict = [NSDictionary dictionaryWithObjectsAndKeys: 64 PCClasses, @"ProjectKey", 65 descr, @"TypeDescription", 66 template,@"TemplateFile", 67 nil]; 68 69 // Objective C Header 70 descr = @"Generic Objective-C header.\n\n" 71 @"This is a plain interface subclassing NSObject." 72 @" The file includes Foundation.h"; 73 template = [bundle pathForResource:@"header" ofType:@"template"]; 74 headerDict =[NSDictionary dictionaryWithObjectsAndKeys: 75 PCHeaders,@"ProjectKey", 76 descr,@"TypeDescription", 77 template,@"TemplateFile", 78 nil]; 79 80 // C File 81 descr = @"Generic ANSI-C implementation file.\n\n" 82 @"This file contains no Objective-C dependency in any form."; 83 template = [bundle pathForResource:@"cfile" ofType:@"template"]; 84 ccDict = [NSDictionary dictionaryWithObjectsAndKeys: 85 PCOtherSources,@"ProjectKey", 86 descr,@"TypeDescription", 87 template,@"TemplateFile", 88 nil]; 89 90 // C Header 91 descr = @"Generic ANSI-C header.\n\n" 92 @"This file contains no Objective-C dependency in any form."; 93 template = [bundle pathForResource:@"cheader" ofType:@"template"]; 94 chDict = [NSDictionary dictionaryWithObjectsAndKeys: 95 PCHeaders,@"ProjectKey", 96 descr,@"TypeDescription", 97 template,@"TemplateFile", 98 nil]; 99 100 // Objective C Protocol 101 descr = @"Generic Objective-C protocol.\n\n" 102 @"This is common Objective-C protocol, comparable" 103 @" i.e. to a Java interface."; 104 template = [bundle pathForResource:@"protocol" ofType:@"template"]; 105 protocolDict = [NSDictionary dictionaryWithObjectsAndKeys: 106 PCHeaders,@"ProjectKey", 107 descr,@"TypeDescription", 108 template,@"TemplateFile", 109 nil]; 110 111 // GSMarkup 112 descr = @"Generic GSMarkup File.\n\n" 113 @"This is the interface description of GNUstep Renaissance."; 114 template = [bundle pathForResource:@"gsmarkup" ofType:@"template"]; 115 gsmarkupDict =[NSDictionary dictionaryWithObjectsAndKeys: 116 PCGSMarkupFiles,@"ProjectKey", 117 descr,@"TypeDescription", 118 template, @"TemplateFile", 119 nil]; 120 121 122 dict = [[NSDictionary alloc] initWithObjectsAndKeys: 123 ccDict, CFile, 124 chDict, CHeader, 125 protocolDict, ProtocolFile, 126 headerDict, ObjCHeader, 127 classDict, ObjCClass, 128 gsmarkupDict, GSMarkupFile, 129 nil]; 130 } 131 132 return _creator; 133} 134 135- (id)init 136{ 137 self = [super init]; 138 activeProject = nil; 139 140 return self; 141} 142 143- (void)dealloc 144{ 145 RELEASE(newFilePanel); 146 RELEASE(dict); 147 148 [super dealloc]; 149} 150 151- (NSDictionary *)creatorDictionary 152{ 153 return dict; 154} 155 156- (void)newFileInProject:(PCProject *)aProject 157{ 158 // Set to nil after panel closing 159 activeProject = aProject; 160 [self showNewFilePanel]; 161} 162 163- (void)createFileOfType:(NSString *)fileType 164 path:(NSString *)path 165 project:(PCProject *)project 166{ 167 NSDictionary *newFiles; 168 169 newFiles = [self filesToCreateForFileOfType:fileType 170 path:path 171 withComplementary:YES]; 172 173 [self createFiles:newFiles inProject:project]; 174} 175 176- (NSDictionary *)filesToCreateForFileOfType:(NSString *)type 177 path:(NSString *)path 178 withComplementary:(BOOL)complementary 179{ 180 NSMutableDictionary *files = nil; 181 NSString *newFile = nil; 182 183 // A class and possibly a header 184 files = [NSMutableDictionary dictionaryWithCapacity:2]; 185 186 // Remove file extension from "path" 187 if (![[path pathExtension] isEqualToString: @""]) 188 { 189 path = [path stringByDeletingPathExtension]; 190 } 191 192 // Objective-C Class 193 if ([type isEqualToString:ObjCClass]) 194 { 195 newFile = [path stringByAppendingPathExtension:@"m"]; 196 [files setObject:[dict objectForKey:ObjCClass] forKey:newFile]; 197 } 198 // C File 199 else if ([type isEqualToString:CFile]) 200 { 201 newFile = [path stringByAppendingPathExtension:@"c"]; 202 [files setObject:[dict objectForKey:CFile] forKey:newFile]; 203 } 204 205 // C Header 206 // When creating C file also create C Header file 207 if ([type isEqualToString:CHeader] || 208 ([type isEqualToString:CFile] && complementary)) 209 { 210 newFile = [path stringByAppendingPathExtension:@"h"]; 211 [files setObject:[dict objectForKey:CHeader] forKey:newFile]; 212 } 213 // Objective-C Header 214 // When creating Objective C Class file also create Objective C Header file 215 else if ([type isEqualToString:ObjCHeader] || 216 ([type isEqualToString:ObjCClass] && complementary)) 217 { 218 newFile = [path stringByAppendingPathExtension:@"h"]; 219 [files setObject:[dict objectForKey:ObjCHeader] forKey:newFile]; 220 } 221 // GSMarkup 222 else if ([type isEqualToString:GSMarkupFile]) 223 { 224 newFile = [path stringByAppendingPathExtension:@"gsmarkup"]; 225 [files setObject:[dict objectForKey:GSMarkupFile] forKey:newFile]; 226 } 227 // Objective-C Protocol 228 else if ([type isEqualToString:ProtocolFile]) 229 { 230 newFile = [path stringByAppendingPathExtension:@"h"]; 231 [files setObject:[dict objectForKey:ProtocolFile] forKey:newFile]; 232 } 233 234 return files; 235} 236 237- (BOOL)createFiles:(NSDictionary *)fileList 238 inProject:(PCProject *)aProject 239{ 240 PCFileManager *pcfm = [PCFileManager defaultManager]; 241 NSEnumerator *enumerator = [[fileList allKeys] objectEnumerator]; 242 NSString *template = nil; 243 NSString *newFile = nil; 244 NSDictionary *fileType = nil; 245 NSString *key = nil; 246 247 while ((newFile = [enumerator nextObject])) 248 { 249 fileType = [fileList objectForKey:newFile]; 250 key = [fileType objectForKey:@"ProjectKey"]; 251 template = [fileType objectForKey:@"TemplateFile"]; 252 253 if ([pcfm copyFile:template toFile:newFile]) 254 { 255 [self replaceTagsInFileAtPath:newFile withProject:aProject]; 256 [aProject addFiles:[NSArray arrayWithObject:newFile] 257 forKey:key 258 notify:YES]; 259 } 260 } 261 262 // Notify the browser! 263 [[NSNotificationCenter defaultCenter] 264 postNotificationName:@"ProjectDictDidChangeNotification" 265 object:self]; 266 267 return YES; 268} 269 270- (void)replaceTagsInFileAtPath:(NSString *)newFile 271 withProject:(PCProject *)aProject 272{ 273 NSString *projectName = [aProject projectName]; 274 NSString *date = [[NSCalendarDate calendarDate] description]; 275 int year = [[NSCalendarDate calendarDate] yearOfCommonEra]; 276 NSString *aFile = [newFile lastPathComponent]; 277 NSString *UCfn = [[aFile stringByDeletingPathExtension] uppercaseString]; 278 NSString *fn = [aFile stringByDeletingPathExtension]; 279 NSRange subRange; 280 281#ifdef WIN32 282 file = [[NSMutableString stringWithContentsOfFile: newFile 283 encoding: NSUTF8StringEncoding 284 error: NULL] retain]; 285#else 286 file = [[NSMutableString stringWithContentsOfFile:newFile] retain]; 287#endif 288 289 while ((subRange = [file rangeOfString:@"$FULLFILENAME$"]).length) 290 { 291 [file replaceCharactersInRange:subRange withString:aFile]; 292 } 293 294 while ((subRange = [file rangeOfString:@"$FILENAME$"]).length) 295 { 296 [file replaceCharactersInRange:subRange withString:fn]; 297 } 298 299 while ((subRange = [file rangeOfString:@"$UCFILENAME$"]).length) 300 { 301 [file replaceCharactersInRange:subRange withString:UCfn]; 302 } 303 304 while ((subRange = [file rangeOfString:@"$USERNAME$"]).length) 305 { 306 [file replaceCharactersInRange:subRange withString:NSUserName()]; 307 } 308 309 while ((subRange = [file rangeOfString:@"$FULLUSERNAME$"]).length) 310 { 311 [file replaceCharactersInRange:subRange withString:NSFullUserName()]; 312 } 313 314 while ((subRange = [file rangeOfString:@"$PROJECTNAME$"]).length) 315 { 316 [file replaceCharactersInRange:subRange withString:projectName]; 317 } 318 319 while ((subRange = [file rangeOfString:@"$DATE$"]).length) 320 { 321 [file replaceCharactersInRange:subRange withString:date]; 322 } 323 324 while ((subRange = [file rangeOfString:@"$YEAR$"]).length) 325 { 326 [file replaceCharactersInRange:subRange 327 withString:[[NSNumber numberWithInt:year] stringValue]]; 328 } 329 330#ifdef WIN32 331 [file writeToFile: newFile 332 atomically: YES 333 encoding: NSUTF8StringEncoding 334 error: NULL]; 335#else 336 [file writeToFile:newFile atomically:YES]; 337#endif 338 339 [file release]; 340} 341 342@end 343 344 345@implementation PCFileCreator (UInterface) 346 347// ============================================================================ 348// ==== "New File in Project" Panel 349// ============================================================================ 350- (void)showNewFilePanel 351{ 352 if (!newFilePanel) 353 { 354 if ([NSBundle loadNibNamed:@"NewFile" owner:self] == NO) 355 { 356 PCLogError(self, @"error loading NewFile NIB!"); 357 return; 358 } 359 [newFilePanel setFrameAutosaveName:@"NewFile"]; 360 if (![newFilePanel setFrameUsingName: @"NewFile"]) 361 { 362 [newFilePanel center]; 363 } 364 [nfImage setImage:[NSApp applicationIconImage]]; 365 [nfTypePB setRefusesFirstResponder:YES]; 366 [nfTypePB removeAllItems]; 367 [nfTypePB addItemsWithTitles: 368 [[dict allKeys] 369 sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]]; 370 [nfTypePB selectItemAtIndex:0]; 371 [nfCancelButton setRefusesFirstResponder:YES]; 372 [nfCreateButton setRefusesFirstResponder:YES]; 373 [nfAddHeaderButton setRefusesFirstResponder:YES]; 374 [newFilePanel setDefaultButtonCell:[nfCreateButton cell]]; 375 } 376 377 [self newFilePopupChanged:nfTypePB]; 378 379 [newFilePanel makeKeyAndOrderFront:self]; 380 [nfNameField setStringValue:@""]; 381 [newFilePanel makeFirstResponder:nfNameField]; 382 383 [newFilePanel setLevel:NSModalPanelWindowLevel]; 384 [NSApp runModalForWindow:newFilePanel]; 385} 386 387- (void)closeNewFilePanel:(id)sender 388{ 389 [newFilePanel orderOut:self]; 390 [NSApp stopModal]; 391 392 activeProject = nil; 393} 394 395- (void)createFile:(id)sender 396{ 397 if ([self createFile]) 398 { 399 [self closeNewFilePanel:self]; 400 } 401 else 402 { 403 [newFilePanel makeKeyAndOrderFront:self]; 404 } 405} 406 407- (void)newFilePopupChanged:(id)sender 408{ 409 NSString *typeTitle = [sender titleOfSelectedItem]; 410 NSDictionary *fileType = [dict objectForKey:typeTitle]; 411 412 if (!fileType) 413 { 414 return; 415 } 416 417 [nfDescriptionTV setString:[fileType objectForKey:@"TypeDescription"]]; 418 [nfAddHeaderButton setState:NSOffState]; 419 if ([typeTitle isEqualToString:ObjCClass] || 420 [typeTitle isEqualToString:CFile]) 421 { 422 [nfAddHeaderButton setEnabled:YES]; 423 } 424 else 425 { 426 [nfAddHeaderButton setEnabled:NO]; 427 } 428} 429 430- (void)controlTextDidChange:(NSNotification *)aNotif 431{ 432 if ([aNotif object] != nfNameField) 433 { 434 return; 435 } 436 437 // TODO: Add check for valid file names 438 if ([[nfNameField stringValue] length] > 0) 439 { 440 [nfCreateButton setEnabled:YES]; 441 } 442 else 443 { 444 [nfCreateButton setEnabled:NO]; 445 } 446} 447 448- (BOOL)createFile 449{ 450 NSString *fileName = [nfNameField stringValue]; 451 NSString *fileType = [nfTypePB titleOfSelectedItem]; 452 NSString *path = nil; 453 NSString *key = nil; 454 NSDictionary *newFiles = nil; 455 NSEnumerator *enumerator = nil; 456 NSString *filePath = nil; 457 NSFileManager *fm = [NSFileManager defaultManager]; 458 BOOL complementary; 459 460 path = [[activeProject projectPath] stringByAppendingPathComponent:fileName]; 461 // Create file 462 if (path) 463 { 464 // Get file list for creation 465 complementary = [nfAddHeaderButton state]==NSOnState ? YES : NO; 466 newFiles = [self filesToCreateForFileOfType:fileType 467 path:path 468 withComplementary:complementary]; 469 470 // Check if project already has files with such names 471 enumerator = [[newFiles allKeys] objectEnumerator]; 472 while ((filePath = [enumerator nextObject])) 473 { 474 key = [[newFiles objectForKey:filePath] objectForKey:@"ProjectKey"]; 475 fileName = [filePath lastPathComponent]; 476 if (![activeProject doesAcceptFile:fileName forKey:key]) 477 { 478 NSRunAlertPanel(@"New File in Project", 479 @"Project %@ already has file %@ in %@", 480 @"OK", nil, nil, 481 [activeProject projectName], fileName, key); 482 return NO; 483 } 484 if ([fm fileExistsAtPath:filePath]) 485 { 486 int ret; 487 488 ret = NSRunAlertPanel 489 (@"New File in Project", 490 @"Project directory %@ already has file %@.\n" 491 @"Do you want to overwrite it?", 492 @"Stop", @"Overwrite", nil, 493 [filePath stringByDeletingLastPathComponent], 494 fileName); 495 496 if (ret == NSAlertDefaultReturn) // Stop 497 { 498 return NO; 499 } 500 else // Overwrite. Remove destination of copy operation 501 { 502 [fm removeFileAtPath:filePath handler:nil]; 503 } 504 } 505 } 506 507 // Create files 508 return [self createFiles:newFiles inProject:activeProject]; 509 } 510 511 return NO; 512} 513 514@end 515 516