Merge branch 'refs/heads/XcodeCapp3'

This commit is contained in:
Aparajita Fishman committed 2013-05-08 10:24:21 -04:00
commit e45c05b08e
131 files changed
+10292 -7352

No files matched your search

+1
View File
@@ -11,5 +11,6 @@ xcuserdata/
!*.xcodeproj/project.pbxproj
*.xCodeSupport/
*.XcodeSupport/
*XcodeSupport/
*.sublime-project
*.sublime-workspace
-5
View File
@@ -1,5 +0,0 @@
.DS_Store
*.mode1v3
*.pbxuser
*.perspectivev3
build
-76
View File
@@ -1,76 +0,0 @@
/*
* This file is a part of program XcodeCapp
* Copyright (C) 2011 Antoine Mercadal (<primalmotion@archipelproject.org>)
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#import <Cocoa/Cocoa.h>
#import <CoreServices/CoreServices.h>
#import "PRHEmptyGrowlDelegate.h"
#import "TNXCodeCapp.h"
#import "TNErrorDataView.h"
@interface AppController : NSObject <NSMenuDelegate, NSTableViewDataSource, NSTableViewDelegate>
{
IBOutlet NSMenu *statusMenu;
IBOutlet NSMenuItem *menuItemOpenXCode;
IBOutlet NSMenuItem *menuItemStartStop;
IBOutlet NSPanel *errorsPanel;
IBOutlet NSTableView *errorsTable;
IBOutlet NSPanel *aboutWindow;
IBOutlet NSWindow *helpWindow;
IBOutlet NSTextView *helpTextView;
IBOutlet NSTextField *labelVersion;
IBOutlet NSPopUpButton *buttonPreferencesAPIMode;
IBOutlet NSButton *checkBoxPreferencesReactMode;
IBOutlet NSUserDefaultsController *preferencesController;
IBOutlet TNXCodeCapp *__strong xcc;
IBOutlet NSWindow *windowDebug;
IBOutlet NSMenuItem *menuDebug;
IBOutlet NSMenuItem *menuHistory;
IBOutlet TNErrorDataView *dataViewError;
NSImage *_iconActive;
NSImage *_iconInactive;
NSImage *_iconWorking;
NSStatusItem *_statusItem;
PRHEmptyGrowlDelegate *growlDelegateRef;
NSData *_archivedDataView;
}
@property BOOL supportsFileModeListening;
@property (strong) TNXCodeCapp *xcc;
+ (AppController *)sharedAppController;
- (BOOL)validateMenuItem:(NSMenuItem*)menuItem;
- (void)registerDefaults;
- (void)growlWithTitle:(NSString *)aTitle message:(NSString *)aMessage;
- (void)openCenteredWindow:(NSWindow *)aWindow;
- (void)_prepareHistoryMenu;
- (IBAction)chooseFolder:(id)aSender;
- (IBAction)openErrors:(id)sender;
- (IBAction)clearErrors:(id)sender;
- (IBAction)openXCode:(id)aSender;
- (IBAction)stopListener:(id)aSender;
- (IBAction)openHelp:(id)aSender;
- (IBAction)openAbout:(id)aSender;
- (IBAction)updatePreferences:(id)aSender;
- (IBAction)switchProject:(id)aSender;
- (IBAction)clearProjectHistory:(id)aSender;
@end
-443
View File
@@ -1,443 +0,0 @@
/*
* This file is a part of program XcodeCapp
* Copyright (C) 2011 Antoine Mercadal (<primalmotion@archipelproject.org>)
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#import <AppKit/NSApplication.h>
#import "AppController.h"
#include "macros.h"
AppController *SharedAppControllerInstance = nil;
float heightForStringDrawing(NSString *myString, NSFont *myFont, float myWidth)
{
NSTextStorage *textStorage = [[NSTextStorage alloc] initWithString:myString];
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithContainerSize:NSMakeSize(myWidth, FLT_MAX)];
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
[layoutManager addTextContainer:textContainer];
[textStorage addLayoutManager:layoutManager];
[textStorage addAttribute:NSFontAttributeName value:myFont range:NSMakeRange(0, [textStorage length])];
[textContainer setLineFragmentPadding:0.0];
(void) [layoutManager glyphRangeForTextContainer:textContainer];
return [layoutManager usedRectForTextContainer:textContainer].size.height;
}
@implementation AppController
@synthesize supportsFileModeListening;
@synthesize xcc;
+ (AppController *)sharedAppController
{
return SharedAppControllerInstance;
}
#pragma mark -
#pragma mark Initialization
/*!
Called when NIB is ready
*/
- (void)awakeFromNib
{
SharedAppControllerInstance = self;
_archivedDataView = [NSKeyedArchiver archivedDataWithRootObject:dataViewError];
if (!growlDelegateRef)
growlDelegateRef = [[PRHEmptyGrowlDelegate alloc] init];
[GrowlApplicationBridge setGrowlDelegate:growlDelegateRef];
NSBundle *bundle = [NSBundle mainBundle];
[labelVersion setStringValue:[NSString stringWithFormat:@"Version %@", [bundle objectForInfoDictionaryKey:@"CFBundleVersion"]]];
[self registerDefaults];
_iconInactive = [[NSImage alloc] initWithContentsOfFile:[bundle pathForResource:@"xcodecapp-icon-inactive" ofType:@"png"]];
_iconActive = [[NSImage alloc] initWithContentsOfFile:[bundle pathForResource:@"xcodecapp-icon-active" ofType:@"png"]];
_iconWorking = [[NSImage alloc] initWithContentsOfFile:[bundle pathForResource:@"xcodecapp-icon-working" ofType:@"png"]];
[_iconActive setSize:NSMakeSize(14.0, 16.0)];
[_iconInactive setSize:NSMakeSize(14.0, 16.0)];
[_iconWorking setSize:NSMakeSize(14.0, 16.0)];
_statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
[_statusItem setMenu:statusMenu];
[_statusItem setImage:_iconInactive];
[_statusItem setHighlightMode:YES];
[statusMenu setDelegate:self];
if ([[NSUserDefaults standardUserDefaults] integerForKey:@"FirstLaunch"])
{
[[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithInt:0] forKey:@"FirstLaunch"];
[self openHelp:self];
}
[xcc setDelegate:self];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(XCodeCappConversionDidStart:) name:XCCConversionStartNotification object:xcc];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(XCodeCappConversionDidStop:) name:XCCConversionStopNotification object:xcc];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(XCodeCappPopulateProject:) name:XCCDidPopulateProjectNotification object:xcc];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(XCodeCappListeningDidStart:) name:XCCListeningStartNotification object:xcc];
[helpTextView setTextContainerInset:NSSizeFromCGSize(CGSizeMake(10.0, 10.0))];
[xcc start];
[self _prepareHistoryMenu];
}
/*!
Checks if aplication should show the debug window
*/
- (void)applicationDidFinishLaunching:(NSNotification *)notif
{
CGEventRef event = CGEventCreate(NULL);
CGEventFlags modifiers = CGEventGetFlags(event);
CFRelease(event);
if (modifiers & kCGEventFlagMaskAlternate)
{
[statusMenu insertItem:menuDebug atIndex:6];
}
}
/*!
Register the application defaults
*/
- (void)registerDefaults
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSNumber *streamEventIdSinceNow = [NSNumber numberWithUnsignedLongLong:kFSEventStreamEventIdSinceNow];
NSMutableDictionary *appDefaults = [NSMutableDictionary new];
[appDefaults setObject:streamEventIdSinceNow forKey:@"lastEventId"];
[appDefaults setObject:[NSNumber numberWithInt:1] forKey:@"FirstLaunch"];
[appDefaults setObject:[NSNumber numberWithInt:0] forKey:@"XCCAPIMode"];
[appDefaults setObject:[NSNumber numberWithInt:1] forKey:@"XCCReactMode"];
[appDefaults setObject:[NSNumber numberWithInt:1] forKey:@"XCCReopenLastProject"];
[appDefaults setObject:[[NSArray alloc] init] forKey:@"XCCProjectHistory"];
[defaults registerDefaults:appDefaults];
}
#pragma mark -
#pragma mark Notification handlers
/*!
Handle cleaning operation when application will stop.
It will stop the FSEvent listener, and store the last event id
*/
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)app
{
[xcc clear];
return NSTerminateNow;
}
/*!
Called when XCC start a conversion
@param aNotification the notification
*/
- (void)XCodeCappConversionDidStart:(NSNotification *)aNotification
{
[_statusItem setImage:_iconWorking];
}
/*!
Called when XCC finish a conversion
@param aNotification the notification
*/
- (void)XCodeCappConversionDidStop:(NSNotification *)aNotification
{
[_statusItem setImage:_iconActive];
if ([errorsPanel isVisible])
[errorsTable reloadData];
}
/*!
Called when XCC has populated a project
@param aNotification the notification
*/
- (void)XCodeCappPopulateProject:(NSNotification *)aNotification
{
[self growlWithTitle:@"Project loaded" message:[[[aNotification userInfo] objectForKey:@"URL"] path]];
}
/*!
Called when XCC start a to listen to a project
@param aNotification the notification
*/
- (void)XCodeCappListeningDidStart:(NSNotification *)aNotification
{
[_statusItem setImage:_iconActive];
[menuItemStartStop setTitle:[NSString stringWithFormat:@"Stop Listening to “%@”", [xcc currentProjectName]]];
[menuItemStartStop setAction:@selector(stopListener:)];
[self growlWithTitle:@"Listening to project" message:[[xcc currentProjectURL] path]];
}
#pragma mark -
#pragma mark Utilities
/*!
Simple growl wrapper
@param aTitle the growl title
@param aMessage the growl message
*/
- (void)growlWithTitle:(NSString *)aTitle message:(NSString *)aMessage
{
[GrowlApplicationBridge notifyWithTitle:aTitle
description:aMessage
notificationName:@"DefaultNotifications"
iconData:nil
priority:0
isSticky:NO
clickContext:nil];
}
/*!
Prepare the history menu
*/
- (void)_prepareHistoryMenu
{
NSMenu *menu = [[NSMenu alloc] init];
NSArray *projectHistory = [[NSUserDefaults standardUserDefaults] objectForKey:@"XCCProjectHistory"];
for(int i = 0; i < [projectHistory count]; i++)
{
NSString *itemTitle = [[projectHistory objectAtIndex:i] lastPathComponent];
NSString *projectPath = [[projectHistory objectAtIndex:i] stringByStandardizingPath];
NSString *currentProjectPath = [[[xcc currentProjectURL] path] stringByStandardizingPath];
NSMenuItem *item = [menu addItemWithTitle:itemTitle action:@selector(switchProject:) keyEquivalent:@""];
[item setRepresentedObject:projectPath];
if ([currentProjectPath isEqualToString:projectPath])
[item setAction:nil];
}
[menu addItem:[NSMenuItem separatorItem]];
[menu addItemWithTitle:@"Clear history" action:@selector(clearProjectHistory:) keyEquivalent:@""];
[menuHistory setEnabled:([projectHistory count]) ? YES : NO];
[menuHistory setSubmenu:menu];
}
#pragma mark -
#pragma mark Actions
/*!
Save preferences
@param aSender the sender of the action
*/
- (IBAction)updatePreferences:(id)aSender
{
[preferencesController save:aSender];
NSLog(@"Preferences change notified");
[xcc configure];
}
/*!
Open the folder chooser and eventually start to listen
@param aSender the sender of the action
*/
- (IBAction)chooseFolder:(id)aSender
{
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
[openPanel setCanChooseDirectories:YES];
[openPanel setCanCreateDirectories:YES];
[openPanel setTitle:@"Choose Cappuccino Project"];
[openPanel setCanChooseFiles:NO];
if ([openPanel runModal] != NSFileHandlingPanelOKButton)
return;
NSString *projectPath = [NSString stringWithFormat:@"%@/", [[[openPanel URLs] objectAtIndex:0] path]];
NSMutableArray *projectHistory = [NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:@"XCCProjectHistory"]];
if ([projectHistory containsObject:[projectPath stringByStandardizingPath]])
[projectHistory removeObject:[projectPath stringByStandardizingPath]];
[projectHistory insertObject:[projectPath stringByStandardizingPath] atIndex:0];
[[NSUserDefaults standardUserDefaults] setObject:projectHistory forKey:@"XCCProjectHistory"];
[xcc listenProjectAtPath:projectPath];
[self _prepareHistoryMenu];
}
/*!
Stop listening to a project
@param aSender the sender of the action
*/
- (IBAction)stopListener:(id)aSender
{
[xcc clear];
[_statusItem setImage:_iconInactive];
[menuItemStartStop setTitle:@"Listen to Project…"];
[menuItemStartStop setAction:@selector(chooseFolder:)];
[self _prepareHistoryMenu];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"LastOpenedPath"];
}
- (IBAction)switchProject:(id)aSender
{
NSString *newPath = [aSender representedObject];
[self stopListener:aSender];
[xcc listenProjectAtPath:newPath];
[self _prepareHistoryMenu];
}
- (IBAction)clearProjectHistory:(id)aSender
{
[[NSUserDefaults standardUserDefaults] setObject:[NSArray array] forKey:@"XCCProjectHistory"];
[self _prepareHistoryMenu];
}
/*!
Open the xCode support project in xCode
@param aSender the sender of the action
*/
- (IBAction)openXCode:(id)aSender
{
if (![xcc currentProjectURL])
return;
DLog(@"Opening Xcode project at URL: '%@'", [[xcc XCodeSupportProject] path]);
system([[NSString stringWithFormat:@"open \"%@\"", [[xcc XCodeSupportProject] path]] UTF8String]);
}
/*!
Open the errors window
@param aSender the sender of the action
*/
- (IBAction)openErrors:(id)aSender
{
[self openCenteredWindow:errorsPanel];
}
/*!
Clear all errors in errors table
@param aSender the sender of the action
*/
- (IBAction)clearErrors:(id)sender
{
[[xcc errorList] removeAllObjects];
[errorsTable reloadData];
}
/*!
Open the help file
@param aSender the sender of the action
*/
- (IBAction)openHelp:(id)aSender
{
[helpTextView readRTFDFromFile:[[NSBundle mainBundle] pathForResource:@"help" ofType:@"rtfd"]];
[self openCenteredWindow:helpWindow];
}
/*!
Open the about window
@param aSender the sender of the action
*/
- (IBAction)openAbout:(id)aSender
{
[self openCenteredWindow:aboutWindow];
}
/*!
Open the preferences window
@param aSender the sender of the action
*/
- (IBAction)openPreferences:(id)aSender
{
[self openCenteredWindow:windowDebug];
}
/*!
Open a centered window.
*/
- (void)openCenteredWindow:(NSWindow *)aWindow
{
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
[aWindow center];
[aWindow makeKeyAndOrderFront:nil];
}
#pragma mark -
#pragma mark Delegates
- (BOOL)validateMenuItem:(NSMenuItem *)aMenuItem
{
if (aMenuItem == menuItemOpenXCode)
return !![xcc currentProjectURL];
return YES;
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
{
return [[xcc errorList] count];
}
- (id)tableView:(NSTableView*)aTableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
return [[xcc errorList] objectAtIndex:row];
}
- (void)tableViewColumnDidResize:(NSNotification *)tableView
{
[errorsTable noteHeightOfRowsWithIndexesChanged:
[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [[xcc errorList] count])]];
}
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
return [NSKeyedUnarchiver unarchiveObjectWithData:_archivedDataView];
}
- (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)aRow
{
NSString *content = [[[xcc errorList] objectAtIndex:aRow] objectForKey:@"message"];
NSFont *currentFont = [[dataViewError fieldMessage] font];
float height = heightForStringDrawing(content, currentFont, [tableView frame].size.width);
return height + 31;
}
@end
Binary file not shown.
File diff suppressed because it is too large. Load diff
Binary file not shown.
-63
View File
@@ -1,63 +0,0 @@
/*
* This file is a part of program xcodecapp-cocoa
* Copyright (C) 2011 Antoine Mercadal (primalmotion@archipelproject.org)
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#import "FSEventCallback.h"
/*!
This is the FSEvent callback
*/
void fsevents_callback(ConstFSEventStreamRef streamRef,
void *userData,
size_t numEvents,
void *eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[])
{
TNXCodeCapp *xcc = (TNXCodeCapp *)userData;
size_t i;
for(i = 0; i < numEvents; i++)
{
NSString *path = [(NSArray *)eventPaths objectAtIndex:i];
#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7)
// kFSEventStreamEventFlagItemIsFile = 0x00010000
if (!(eventFlags[i] & 0x00010000)
|| [xcc isPathMatchingIgnoredPaths:path]
|| (![xcc isXIBFile:path] && ![xcc isObjJFile:path] && ![xcc isXCCIgnoreFile:path]))
continue;
// kFSEventStreamEventFlagItemRemoved = 0x00000200
if (eventFlags[i] & 0x00000200)
{
[xcc handleFileRemoval:path];
}
else
{
NSLog(@"this file has been modified or created");
[xcc handleFileModification:path notify:YES];
}
#else
NSArray *subpaths = [[[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:NULL];
for(NSString *currentPath in subpaths)
[xcc handleFileModification:currentPath notify:YES];
#endif
[xcc updateLastEventId:eventIds[i]];
}
}
-40
View File
@@ -1,40 +0,0 @@
/*
* This file is a part of program XcodeCapp
* Copyright (C) 2011 Antoine Mercadal (<primalmotion@archipelproject.org>)
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef xcodecapp_cocoa_FSEventCallback_h
#define xcodecapp_cocoa_FSEventCallback_h
#import "TNXCodeCapp.h"
#import <Cocoa/Cocoa.h>
#if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_6
# define kFSEventStreamCreateFlagFileEvents 0x00000010
# define kFSEventStreamEventFlagItemIsFile 0x00010000
# define kFSEventStreamEventFlagItemRemoved 0x00000200
# define kFSEventStreamEventFlagItemCreated 0x00000200
# define kFSEventStreamEventFlagItemModified 0x00001000
# define kFSEventStreamEventFlagItemInodeMetaMod 0x00000400
# define kFSEventStreamEventFlagItemRenamed 0x00000800
# define kFSEventStreamEventFlagItemFinderInfoMod 0x00002000
# define kFSEventStreamEventFlagItemChangeOwner 0x00004000
# define kFSEventStreamEventFlagItemXattrMod 0x00008000
#endif
void fsevents_callback(ConstFSEventStreamRef, void*, size_t, void*, const FSEventStreamEventFlags*, const FSEventStreamEventId*);
#endif
-117
View File
@@ -1,117 +0,0 @@
/*
* This file is a part of program XcodeCapp
* Copyright (C) 2011 Antoine Mercadal (<primalmotion@archipelproject.org>)
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#import "AppController.h"
#import "FSEventCallback.h"
#import "macros.h"
/*!
This is the FSEvent callback for 10.7
*/
void fsevents_callback(ConstFSEventStreamRef streamRef,
void *userData,
size_t numEvents,
void *eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[])
{
TNXCodeCapp *xcc = (__bridge TNXCodeCapp *)userData;
BOOL useFileBasedListening = [xcc supportsFileBasedListening];
size_t i;
for (i = 0; i < numEvents; i++)
{
[xcc updateLastEventId:eventIds[i]];
FSEventStreamEventFlags flags = eventFlags[i];
NSString *path = [[(__bridge NSArray *)eventPaths objectAtIndex:i] stringByStandardizingPath];
if (useFileBasedListening)
{
BOOL conditionIsFile = flags & kFSEventStreamEventFlagItemIsFile;
BOOL conditionIsDirectory = NO;
BOOL conditionIsIgnored = [xcc isPathMatchingIgnoredPaths:path];
BOOL conditionIsValidFile = [xcc isXIBFile:path] || [xcc isObjJFile:path] || [xcc isXCCIgnoreFile:path];
BOOL conditionPathExists = [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&conditionIsDirectory];
if (conditionIsIgnored)
continue;
if (conditionIsFile && !conditionIsValidFile)
continue;
// Events are not so reliable. For example, moving a folder to the trash is not
// a deletion. In order to simplify the code, we simply tidyUp the project when we receive
// an event.
[xcc tidyShadowedFiles];
if (conditionIsDirectory)
continue;
if (!conditionPathExists)
{
DLog(@"File removed: %@", path);
[xcc handleFileRemoval:path];
}
else
{
DLog(@"File modified/added: %@", path);
[xcc handleFileModification:path notify:YES];
}
}
else
{
// We should drop support for Snow Leopard soon.
BOOL isDir = NO;
[[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir];
// If for some reasons the path is not a directory,
// we don't want to deal with it in this mode.
if (!isDir)
continue;
[xcc tidyShadowedFiles];
NSArray *subpaths = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:NULL];
for (NSString *subpath in subpaths)
{
NSString *fullPath = [[NSString stringWithFormat:@"%@/%@", path, subpath] stringByStandardizingPath];
if ([xcc isPathMatchingIgnoredPaths:fullPath]
|| (![xcc isXIBFile:fullPath] && ![xcc isObjJFile:fullPath] && ![xcc isXCCIgnoreFile:fullPath]))
continue;
NSDate *lastModifiedDate = [xcc lastModificationDateForPath:fullPath];
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:fullPath error:nil];
NSDate *fileModDate = [fileAttributes objectForKey:NSFileModificationDate];
if ([fileModDate compare:lastModifiedDate] == NSOrderedDescending)
{
[xcc updateLastModificationDate:fileModDate forPath:fullPath];
[xcc handleFileModification:fullPath notify:YES];
}
}
}
}
[xcc updateUserDefaultsWithLastEventId];
}
Binary file not shown.
@@ -1,6 +0,0 @@
#include "GrowlDefines.h"
#ifdef __OBJC__
# include "GrowlApplicationBridge.h"
#endif
#include "GrowlApplicationBridge-Carbon.h"
@@ -1,780 +0,0 @@
//
// GrowlApplicationBridge-Carbon.h
// Growl
//
// Created by Mac-arena the Bored Zo on Wed Jun 18 2004.
// Based on GrowlApplicationBridge.h by Evan Schoenberg.
// This source code is in the public domain. You may freely link it into any
// program.
//
#ifndef _GROWLAPPLICATIONBRIDGE_CARBON_H_
#define _GROWLAPPLICATIONBRIDGE_CARBON_H_
#include <sys/cdefs.h>
#include <Carbon/Carbon.h>
#ifndef GROWL_EXPORT
#define GROWL_EXPORT __attribute__((visibility("default"))) DEPRECATED_ATTRIBUTE
#endif
/*! @header GrowlApplicationBridge-Carbon.h
* @abstract Declares an API that Carbon applications can use to interact with Growl.
* @discussion GrowlApplicationBridge uses a delegate to provide information //XXX
* to Growl (such as your application's name and what notifications it may
* post) and to provide information to your application (such as that Growl
* is listening for notifications or that a notification has been clicked).
*
* You can set the Growldelegate with Growl_SetDelegate and find out the
* current delegate with Growl_GetDelegate. See struct Growl_Delegate for more
* information about the delegate.
*/
__BEGIN_DECLS
/*! @struct Growl_Delegate
* @abstract Delegate to supply GrowlApplicationBridge with information and respond to events.
* @discussion The Growl delegate provides your interface to
* GrowlApplicationBridge. When GrowlApplicationBridge needs information about
* your application, it looks for it in the delegate; when Growl or the user
* does something that you might be interested in, GrowlApplicationBridge
* looks for a callback in the delegate and calls it if present
* (meaning, if it is not <code>NULL</code>).
* XXX on all of that
* @field size The size of the delegate structure.
* @field applicationName The name of your application.
* @field registrationDictionary A dictionary describing your application and the notifications it can send out.
* @field applicationIconData Your application's icon.
* @field growlInstallationWindowTitle The title of the installation window.
* @field growlInstallationInformation Text to display in the installation window.
* @field growlUpdateWindowTitle The title of the update window.
* @field growlUpdateInformation Text to display in the update window.
* @field referenceCount A count of owners of the delegate.
* @field retain Called when GrowlApplicationBridge receives this delegate.
* @field release Called when GrowlApplicationBridge no longer needs this delegate.
* @field growlIsReady Called when GrowlHelperApp is listening for notifications.
* @field growlNotificationWasClicked Called when a Growl notification is clicked.
* @field growlNotificationTimedOut Called when a Growl notification timed out.
*/
struct Growl_Delegate {
/* @discussion This should be sizeof(struct Growl_Delegate).
*/
size_t size;
/*All of these attributes are optional.
*Optional attributes can be NULL; required attributes that
* are NULL cause setting the Growl delegate to fail.
*XXX - move optional/required status into the discussion for each field
*/
/* This name is used both internally and in the Growl preferences.
*
* This should remain stable between different versions and incarnations of
* your application.
* For example, "SurfWriter" is a good app name, whereas "SurfWriter 2.0" and
* "SurfWriter Lite" are not.
*
* This can be <code>NULL</code> if it is provided elsewhere, namely in an
* auto-discoverable plist file in your app bundle
* (XXX refer to more information on that) or in registrationDictionary.
*/
CFStringRef applicationName;
/*
* Must contain at least these keys:
* GROWL_NOTIFICATIONS_ALL (CFArray):
* Contains the names of all notifications your application may post.
*
* Can also contain these keys:
* GROWL_NOTIFICATIONS_DEFAULT (CFArray):
* Names of notifications that should be enabled by default.
* If omitted, GROWL_NOTIFICATIONS_ALL will be used.
* GROWL_APP_NAME (CFString):
* Same as the applicationName member of this structure.
* If both are present, the applicationName member shall prevail.
* If this key is present, you may omit applicationName (set it to <code>NULL</code>).
* GROWL_APP_ICON (CFData):
* Same as the iconData member of this structure.
* If both are present, the iconData member shall prevail.
* If this key is present, you may omit iconData (set it to <code>NULL</code>).
*
* If you change the contents of this dictionary after setting the delegate,
* be sure to call Growl_Reregister.
*
* This can be <code>NULL</code> if you have an auto-discoverable plist file in your app
* bundle. (XXX refer to more information on that)
*/
CFDictionaryRef registrationDictionary;
/* The data can be in any format supported by NSImage. As of
* Mac OS X 10.3, this includes the .icns, TIFF, JPEG, GIF, PNG, PDF, and
* PICT formats.
*
* If this is not supplied, Growl will look up your application's icon by
* its application name.
*/
CFDataRef applicationIconData;
/* Installer display attributes
*
* These four attributes are used by the Growl installer, if this framework
* supports it.
* For any of these being <code>NULL</code>, a localised default will be
* supplied.
*/
/* If this is <code>NULL</code>, Growl will use a default,
* localized title.
*
* Only used if you're using Growl-WithInstaller.framework. Otherwise,
* this member is ignored.
*/
CFStringRef growlInstallationWindowTitle;
/* This information may be as long or short as desired (the
* window will be sized to fit it). If Growl is not installed, it will
* be displayed to the user as an explanation of what Growl is and what
* it can do in your application.
* It should probably note that no download is required to install.
*
* If this is <code>NULL</code>, Growl will use a default, localized
* explanation.
*
* Only used if you're using Growl-WithInstaller.framework. Otherwise,
* this member is ignored.
*/
CFStringRef growlInstallationInformation;
/* If this is <code>NULL</code>, Growl will use a default,
* localized title.
*
* Only used if you're using Growl-WithInstaller.framework. Otherwise,
* this member is ignored.
*/
CFStringRef growlUpdateWindowTitle;
/* This information may be as long or short as desired (the
* window will be sized to fit it). If an older version of Growl is
* installed, it will be displayed to the user as an explanation that an
* updated version of Growl is included in your application and
* no download is required.
*
* If this is <code>NULL</code>, Growl will use a default, localized
* explanation.
*
* Only used if you're using Growl-WithInstaller.framework. Otherwise,
* this member is ignored.
*/
CFStringRef growlUpdateInformation;
/* This member is provided for use by your retain and release
* callbacks (see below).
*
* GrowlApplicationBridge never directly uses this member. Instead, it
* calls your retain callback (if non-<code>NULL</code>) and your release
* callback (if non-<code>NULL</code>).
*/
unsigned referenceCount;
//Functions. Currently all of these are optional (any of them can be NULL).
/* When you call Growl_SetDelegate(newDelegate), it will call
* oldDelegate->release(oldDelegate), and then it will call
* newDelegate->retain(newDelegate), and the return value from retain
* is what will be set as the delegate.
* (This means that this member works like CFRetain and -[NSObject retain].)
* This member is optional (it can be <code>NULL</code>).
* For a delegate allocated with malloc, this member would be
* <code>NULL</code>.
* @result A delegate to which GrowlApplicationBridge holds a reference.
*/
void *(*retain)(void *);
/* When you call Growl_SetDelegate(newDelegate), it will call
* oldDelegate->release(oldDelegate), and then it will call
* newDelegate->retain(newDelegate), and the return value from retain
* is what will be set as the delegate.
* (This means that this member works like CFRelease and
* -[NSObject release].)
* This member is optional (it can be NULL).
* For a delegate allocated with malloc, this member might be
* <code>free</code>(3).
*/
void (*release)(void *);
/* Informs the delegate that Growl (specifically, the GrowlHelperApp) was
* launched successfully (or was already running). The application can
* take actions with the knowledge that Growl is installed and functional.
*/
void (*growlIsReady)(void);
/* Informs the delegate that a Growl notification was clicked. It is only
* sent for notifications sent with a non-<code>NULL</code> clickContext,
* so if you want to receive a message when a notification is clicked,
* clickContext must not be <code>NULL</code> when calling
* Growl_PostNotification or
* Growl_NotifyWithTitleDescriptionNameIconPriorityStickyClickContext.
*/
void (*growlNotificationWasClicked)(CFPropertyListRef clickContext);
/* Informs the delegate that a Growl notification timed out. It is only
* sent for notifications sent with a non-<code>NULL</code> clickContext,
* so if you want to receive a message when a notification is clicked,
* clickContext must not be <code>NULL</code> when calling
* Growl_PostNotification or
* Growl_NotifyWithTitleDescriptionNameIconPriorityStickyClickContext.
*/
void (*growlNotificationTimedOut)(CFPropertyListRef clickContext);
};
/*! @struct Growl_Notification
* @abstract Structure describing a Growl notification.
* @discussion XXX
* @field size The size of the notification structure.
* @field name Identifies the notification.
* @field title Short synopsis of the notification.
* @field description Additional text.
* @field iconData An icon for the notification.
* @field priority An indicator of the notification's importance.
* @field reserved Bits reserved for future usage.
* @field isSticky Requests that a notification stay on-screen until dismissed explicitly.
* @field clickContext An identifier to be passed to your click callback when a notification is clicked.
* @field clickCallback A callback to call when the notification is clicked.
*/
struct Growl_Notification {
/* This should be sizeof(struct Growl_Notification).
*/
size_t size;
/* The notification name distinguishes one type of
* notification from another. The name should be human-readable, as it
* will be displayed in the Growl preference pane.
*
* The name is used in the GROWL_NOTIFICATIONS_ALL and
* GROWL_NOTIFICATIONS_DEFAULT arrays in the registration dictionary, and
* in this member of the Growl_Notification structure.
*/
CFStringRef name;
/* A notification's title describes the notification briefly.
* It should be easy to read quickly by the user.
*/
CFStringRef title;
/* The description supplements the title with more
* information. It is usually longer and sometimes involves a list of
* subjects. For example, for a 'Download complete' notification, the
* description might have one filename per line. GrowlMail in Growl 0.6
* uses a description of '%d new mail(s)' (formatted with the number of
* messages).
*/
CFStringRef description;
/* The notification icon usually indicates either what
* happened (it may have the same icon as e.g. a toolbar item that
* started the process that led to the notification), or what it happened
* to (e.g. a document icon).
*
* The icon data is optional, so it can be <code>NULL</code>. In that
* case, the application icon is used alone. Not all displays support
* icons.
*
* The data can be in any format supported by NSImage. As of Mac OS X
* 10.3, this includes the .icns, TIFF, JPEG, GIF, PNG, PDF, and PICT form
* ats.
*/
CFDataRef iconData;
/* Priority is new in Growl 0.6, and is represented as a
* signed integer from -2 to +2. 0 is Normal priority, -2 is Very Low
* priority, and +2 is Very High priority.
*
* Not all displays support priority. If you do not wish to assign a
* priority to your notification, assign 0.
*/
signed int priority;
/* These bits are not used in Growl 0.6. Set them to 0.
*/
unsigned reserved: 31;
/* When the sticky bit is clear, in most displays,
* notifications disappear after a certain amount of time. Sticky
* notifications, however, remain on-screen until the user dismisses them
* explicitly, usually by clicking them.
*
* Sticky notifications were introduced in Growl 0.6. Most notifications
* should not be sticky. Not all displays support sticky notifications,
* and the user may choose in Growl's preference pane to force the
* notification to be sticky or non-sticky, in which case the sticky bit
* in the notification will be ignored.
*/
unsigned isSticky: 1;
/* If this is not <code>NULL</code>, and your click callback
* is not <code>NULL</code> either, this will be passed to the callback
* when your notification is clicked by the user.
*
* Click feedback was introduced in Growl 0.6, and it is optional. Not
* all displays support click feedback.
*/
CFPropertyListRef clickContext;
/* If this is not <code>NULL</code>, it will be called instead
* of the Growl delegate's click callback when clickContext is
* non-<code>NULL</code> and the notification is clicked on by the user.
*
* Click feedback was introduced in Growl 0.6, and it is optional. Not
* all displays support click feedback.
*
* The per-notification click callback is not yet supported as of Growl
* 0.7.
*/
void (*clickCallback)(CFPropertyListRef clickContext);
CFStringRef identifier;
};
#pragma mark -
#pragma mark Easy initialisers
/*! @defined InitGrowlDelegate
* @abstract Callable macro. Initializes a Growl delegate structure to defaults.
* @discussion Call with a pointer to a struct Growl_Delegate. All of the
* members of the structure will be set to 0 or <code>NULL</code>, except for
* size (which will be set to <code>sizeof(struct Growl_Delegate)</code>) and
* referenceCount (which will be set to 1).
*/
#define InitGrowlDelegate(delegate) \
do { \
if (delegate) { \
(delegate)->size = sizeof(struct Growl_Delegate); \
(delegate)->applicationName = NULL; \
(delegate)->registrationDictionary = NULL; \
(delegate)->applicationIconData = NULL; \
(delegate)->growlInstallationWindowTitle = NULL; \
(delegate)->growlInstallationInformation = NULL; \
(delegate)->growlUpdateWindowTitle = NULL; \
(delegate)->growlUpdateInformation = NULL; \
(delegate)->referenceCount = 1U; \
(delegate)->retain = NULL; \
(delegate)->release = NULL; \
(delegate)->growlIsReady = NULL; \
(delegate)->growlNotificationWasClicked = NULL; \
(delegate)->growlNotificationTimedOut = NULL; \
} \
} while(0)
/*! @defined InitGrowlNotification
* @abstract Callable macro. Initializes a Growl notification structure to defaults.
* @discussion Call with a pointer to a struct Growl_Notification. All of
* the members of the structure will be set to 0 or <code>NULL</code>, except
* for size (which will be set to
* <code>sizeof(struct Growl_Notification)</code>).
*/
#define InitGrowlNotification(notification) \
do { \
if (notification) { \
(notification)->size = sizeof(struct Growl_Notification); \
(notification)->name = NULL; \
(notification)->title = NULL; \
(notification)->description = NULL; \
(notification)->iconData = NULL; \
(notification)->priority = 0; \
(notification)->reserved = 0U; \
(notification)->isSticky = false; \
(notification)->clickContext = NULL; \
(notification)->clickCallback = NULL; \
(notification)->identifier = NULL; \
} \
} while(0)
#pragma mark -
#pragma mark Public API
// @functiongroup Managing the Growl delegate
/*! @function Growl_SetDelegate
* @abstract Replaces the current Growl delegate with a new one, or removes
* the Growl delegate.
* @param newDelegate
* @result Returns false and does nothing else if a pointer that was passed in
* is unsatisfactory (because it is non-<code>NULL</code>, but at least one
* required member of it is <code>NULL</code>). Otherwise, sets or unsets the
* delegate and returns true.
* @discussion When <code>newDelegate</code> is non-<code>NULL</code>, sets
* the delegate to <code>newDelegate</code>. When it is <code>NULL</code>,
* the current delegate will be unset, and no delegate will be in place.
*
* It is legal for <code>newDelegate</code> to be the current delegate;
* nothing will happen, and Growl_SetDelegate will return true. It is also
* legal for it to be <code>NULL</code>, as described above; again, it will
* return true.
*
* If there was a delegate in place before the call, Growl_SetDelegate will
* call the old delegate's release member if it was non-<code>NULL</code>. If
* <code>newDelegate</code> is non-<code>NULL</code>, Growl_SetDelegate will
* call <code>newDelegate->retain</code>, and set the delegate to its return
* value.
*
* If you are using Growl-WithInstaller.framework, and an older version of
* Growl is installed on the user's system, the user will automatically be
* prompted to update.
*
* GrowlApplicationBridge currently does not copy this structure, nor does it
* retain any of the CF objects in the structure (it regards the structure as
* a container that retains the objects when they are added and releases them
* when they are removed or the structure is destroyed). Also,
* GrowlApplicationBridge currently does not modify any member of the
* structure, except possibly the referenceCount by calling the retain and
* release members.
*/
GROWL_EXPORT Boolean Growl_SetDelegate(struct Growl_Delegate *newDelegate);
/*! @function Growl_GetDelegate
* @abstract Returns the current Growl delegate, if any.
* @result The current Growl delegate.
* @discussion Returns the last pointer passed into Growl_SetDelegate, or
* <code>NULL</code> if no such call has been made.
*
* This function follows standard Core Foundation reference-counting rules.
* Because it is a Get function, not a Copy function, it will not retain the
* delegate on your behalf. You are responsible for retaining and releasing
* the delegate as needed.
*/
GROWL_EXPORT struct Growl_Delegate *Growl_GetDelegate(void);
#pragma mark -
// @functiongroup Posting Growl notifications
/*! @function Growl_PostNotification
* @abstract Posts a Growl notification.
* @param notification The notification to post.
* @discussion This is the preferred means for sending a Growl notification.
* The notification name and at least one of the title and description are
* required (all three are preferred). All other parameters may be
* <code>NULL</code> (or 0 or false as appropriate) to accept default values.
*
* If using the Growl-WithInstaller framework, if Growl is not installed the
* user will be prompted to install Growl.
* If the user cancels, this function will have no effect until the next
* application session, at which time when it is called the user will be
* prompted again. The user is also given the option to not be prompted again.
* If the user does choose to install Growl, the requested notification will
* be displayed once Growl is installed and running.
*/
GROWL_EXPORT void Growl_PostNotification(const struct Growl_Notification *notification);
/*! @function Growl_PostNotificationWithDictionary
* @abstract Notifies using a userInfo dictionary suitable for passing to
* CFDistributedNotificationCenter.
* @param userInfo The dictionary to notify with.
* @discussion Before Growl 0.6, your application would have posted
* notifications using CFDistributedNotificationCenter by creating a userInfo
* dictionary with the notification data. This had the advantage of allowing
* you to add other data to the dictionary for programs besides Growl that
* might be listening.
*
* This function allows you to use such dictionaries without being restricted
* to using CFDistributedNotificationCenter. The keys for this dictionary
* can be found in GrowlDefines.h.
*/
GROWL_EXPORT void Growl_PostNotificationWithDictionary(CFDictionaryRef userInfo);
/*! @function Growl_NotifyWithTitleDescriptionNameIconPriorityStickyClickContext
* @abstract Posts a Growl notification using parameter values.
* @param title The title of the notification.
* @param description The description of the notification.
* @param notificationName The name of the notification as listed in the
* registration dictionary.
* @param iconData Data representing a notification icon. Can be <code>NULL</code>.
* @param priority The priority of the notification (-2 to +2, with -2
* being Very Low and +2 being Very High).
* @param isSticky If true, requests that this notification wait for a
* response from the user.
* @param clickContext An object to pass to the clickCallback, if any. Can
* be <code>NULL</code>, in which case the clickCallback is not called.
* @discussion Creates a temporary Growl_Notification, fills it out with the
* supplied information, and calls Growl_PostNotification on it.
* See struct Growl_Notification and Growl_PostNotification for more
* information.
*
* The icon data can be in any format supported by NSImage. As of Mac OS X
* 10.3, this includes the .icns, TIFF, JPEG, GIF, PNG, PDF, and PICT formats.
*/
GROWL_EXPORT void Growl_NotifyWithTitleDescriptionNameIconPriorityStickyClickContext(
/*inhale*/
CFStringRef title,
CFStringRef description,
CFStringRef notificationName,
CFDataRef iconData,
signed int priority,
Boolean isSticky,
CFPropertyListRef clickContext);
#pragma mark -
// @functiongroup Registering
/*! @function Growl_RegisterWithDictionary
* @abstract Register your application with Growl without setting a delegate.
* @discussion When you call this function with a dictionary,
* GrowlApplicationBridge registers your application using that dictionary.
* If you pass <code>NULL</code>, GrowlApplicationBridge will ask the delegate
* (if there is one) for a dictionary, and if that doesn't work, it will look
* in your application's bundle for an auto-discoverable plist.
* (XXX refer to more information on that)
*
* If you pass a dictionary to this function, it must include the
* <code>GROWL_APP_NAME</code> key, unless a delegate is set.
*
* This function is mainly an alternative to the delegate system introduced
* with Growl 0.6. Without a delegate, you cannot receive callbacks such as
* <code>growlIsReady</code> (since they are sent to the delegate). You can,
* however, set a delegate after registering without one.
*
* This function was introduced in Growl.framework 0.7.
* @result <code>false</code> if registration failed (e.g. if Growl isn't installed).
*/
GROWL_EXPORT Boolean Growl_RegisterWithDictionary(CFDictionaryRef regDict);
/*! @function Growl_Reregister
* @abstract Updates your registration with Growl.
* @discussion If your application changes the contents of the
* GROWL_NOTIFICATIONS_ALL key in the registrationDictionary member of the
* Growl delegate, or if it changes the value of that member, or if it
* changes the contents of its auto-discoverable plist, call this function
* to have Growl update its registration information for your application.
*
* Otherwise, this function does not normally need to be called. If you're
* using a delegate, your application will be registered when you set the
* delegate if both the delegate and its registrationDictionary member are
* non-<code>NULL</code>.
*
* This function is now implemented using
* <code>Growl_RegisterWithDictionary</code>.
*/
GROWL_EXPORT void Growl_Reregister(void);
#pragma mark -
/*! @function Growl_SetWillRegisterWhenGrowlIsReady
* @abstract Tells GrowlApplicationBridge to register with Growl when Growl
* launches (or not).
* @discussion When Growl has started listening for notifications, it posts a
* <code>GROWL_IS_READY</code> notification on the Distributed Notification
* Center. GrowlApplicationBridge listens for this notification, using it to
* perform various tasks (such as calling your delegate's
* <code>growlIsReady</code> callback, if it has one). If this function is
* called with <code>true</code>, one of those tasks will be to reregister
* with Growl (in the manner of <code>Growl_Reregister</code>).
*
* This attribute is automatically set back to <code>false</code>
* (the default) after every <code>GROWL_IS_READY</code> notification.
* @param flag <code>true</code> if you want GrowlApplicationBridge to register with
* Growl when next it is ready; <code>false</code> if not.
*/
GROWL_EXPORT void Growl_SetWillRegisterWhenGrowlIsReady(Boolean flag);
/*! @function Growl_WillRegisterWhenGrowlIsReady
* @abstract Reports whether GrowlApplicationBridge will register with Growl
* when Growl next launches.
* @result <code>true</code> if GrowlApplicationBridge will register with
* Growl when next it posts GROWL_IS_READY; <code>false</code> if not.
*/
GROWL_EXPORT Boolean Growl_WillRegisterWhenGrowlIsReady(void);
#pragma mark -
// @functiongroup Obtaining registration dictionaries
/*! @function Growl_CopyRegistrationDictionaryFromDelegate
* @abstract Asks the delegate for a registration dictionary.
* @discussion If no delegate is set, or if the delegate's
* <code>registrationDictionary</code> member is <code>NULL</code>, this
* function returns <code>NULL</code>.
*
* This function does not attempt to clean up the dictionary in any way - for
* example, if it is missing the <code>GROWL_APP_NAME</code> key, the result
* will be missing it too. Use
* <code>Growl_CreateRegistrationDictionaryByFillingInDictionary</code> or
* <code>Growl_CreateRegistrationDictionaryByFillingInDictionaryRestrictedToKeys</code>
* to try to fill in missing keys.
*
* This function was introduced in Growl.framework 0.7.
* @result A registration dictionary.
*/
GROWL_EXPORT CFDictionaryRef Growl_CopyRegistrationDictionaryFromDelegate(void);
/*! @function Growl_CopyRegistrationDictionaryFromBundle
* @abstract Looks in a bundle for a registration dictionary.
* @discussion This function looks in a bundle for an auto-discoverable
* registration dictionary file using <code>CFBundleCopyResourceURL</code>.
* If it finds one, it loads the file using <code>CFPropertyList</code> and
* returns the result.
*
* If you pass <code>NULL</code> as the bundle, the main bundle is examined.
*
* This function does not attempt to clean up the dictionary in any way - for
* example, if it is missing the <code>GROWL_APP_NAME</code> key, the result
* will be missing it too. Use
* <code>Growl_CreateRegistrationDictionaryByFillingInDictionary:</code> or
* <code>Growl_CreateRegistrationDictionaryByFillingInDictionaryRestrictedToKeys</code>
* to try to fill in missing keys.
*
* This function was introduced in Growl.framework 0.7.
* @result A registration dictionary.
*/
GROWL_EXPORT CFDictionaryRef Growl_CopyRegistrationDictionaryFromBundle(CFBundleRef bundle);
/*! @function Growl_CreateBestRegistrationDictionary
* @abstract Obtains a registration dictionary, filled out to the best of
* GrowlApplicationBridge's knowledge.
* @discussion This function creates a registration dictionary as best
* GrowlApplicationBridge knows how.
*
* First, GrowlApplicationBridge examines the Growl delegate (if there is
* one) and gets the registration dictionary from that. If no such dictionary
* was obtained, GrowlApplicationBridge looks in your application's main
* bundle for an auto-discoverable registration dictionary file. If that
* doesn't exist either, this function returns <code>NULL</code>.
*
* Second, GrowlApplicationBridge calls
* <code>Growl_CreateRegistrationDictionaryByFillingInDictionary</code> with
* whatever dictionary was obtained. The result of that function is the
* result of this function.
*
* GrowlApplicationBridge uses this function when you call
* <code>Growl_SetDelegate</code>, or when you call
* <code>Growl_RegisterWithDictionary</code> with <code>NULL</code>.
*
* This function was introduced in Growl.framework 0.7.
* @result A registration dictionary.
*/
GROWL_EXPORT CFDictionaryRef Growl_CreateBestRegistrationDictionary(void);
#pragma mark -
// @functiongroup Filling in registration dictionaries
/*! @function Growl_CreateRegistrationDictionaryByFillingInDictionary
* @abstract Tries to fill in missing keys in a registration dictionary.
* @param regDict The dictionary to fill in.
* @result The dictionary with the keys filled in.
* @discussion This function examines the passed-in dictionary for missing keys,
* and tries to work out correct values for them. As of 0.7, it uses:
*
* Key Value
* --- -----
* <code>GROWL_APP_NAME</code> <code>CFBundleExecutableName</code>
* <code>GROWL_APP_ICON</code> The icon of the application.
* <code>GROWL_APP_LOCATION</code> The location of the application.
* <code>GROWL_NOTIFICATIONS_DEFAULT</code> <code>GROWL_NOTIFICATIONS_ALL</code>
*
* Keys are only filled in if missing; if a key is present in the dictionary,
* its value will not be changed.
*
* This function was introduced in Growl.framework 0.7.
*/
GROWL_EXPORT CFDictionaryRef Growl_CreateRegistrationDictionaryByFillingInDictionary(CFDictionaryRef regDict);
/*! @function Growl_CreateRegistrationDictionaryByFillingInDictionaryRestrictedToKeys
* @abstract Tries to fill in missing keys in a registration dictionary.
* @param regDict The dictionary to fill in.
* @param keys The keys to fill in. If <code>NULL</code>, any missing keys are filled in.
* @result The dictionary with the keys filled in.
* @discussion This function examines the passed-in dictionary for missing keys,
* and tries to work out correct values for them. As of 0.7, it uses:
*
* Key Value
* --- -----
* <code>GROWL_APP_NAME</code> <code>CFBundleExecutableName</code>
* <code>GROWL_APP_ICON</code> The icon of the application.
* <code>GROWL_APP_LOCATION</code> The location of the application.
* <code>GROWL_NOTIFICATIONS_DEFAULT</code> <code>GROWL_NOTIFICATIONS_ALL</code>
*
* Only those keys that are listed in <code>keys</code> will be filled in.
* Other missing keys are ignored. Also, keys are only filled in if missing;
* if a key is present in the dictionary, its value will not be changed.
*
* This function was introduced in Growl.framework 0.7.
*/
GROWL_EXPORT CFDictionaryRef Growl_CreateRegistrationDictionaryByFillingInDictionaryRestrictedToKeys(CFDictionaryRef regDict, CFSetRef keys);
/*! @brief Tries to fill in missing keys in a notification dictionary.
* @param notifDict The dictionary to fill in.
* @return The dictionary with the keys filled in. This will be a separate instance from \a notifDict.
* @discussion This function examines the \a notifDict for missing keys, and
* tries to get them from the last known registration dictionary. As of 1.1,
* the keys that it will look for are:
*
* \li <code>GROWL_APP_NAME</code>
* \li <code>GROWL_APP_ICON</code>
*
* @since Growl.framework 1.1
*/
GROWL_EXPORT CFDictionaryRef Growl_CreateNotificationDictionaryByFillingInDictionary(CFDictionaryRef notifDict);
#pragma mark -
// @functiongroup Querying Growl's status
/*! @function Growl_IsInstalled
* @abstract Determines whether the Growl prefpane and its helper app are
* installed.
* @result Returns true if Growl is installed, false otherwise.
*/
GROWL_EXPORT Boolean Growl_IsInstalled(void);
/*! @function Growl_IsRunning
* @abstract Cycles through the process list to find whether GrowlHelperApp
* is running.
* @result Returns true if Growl is running, false otherwise.
*/
GROWL_EXPORT Boolean Growl_IsRunning(void);
#pragma mark -
// @functiongroup Launching Growl
/*! @typedef GrowlLaunchCallback
* @abstract Callback to notify you that Growl is running.
* @param context The context pointer passed to Growl_LaunchIfInstalled.
* @discussion Growl_LaunchIfInstalled calls this callback function if Growl
* was already running or if it launched Growl successfully.
*/
typedef void (*GrowlLaunchCallback)(void *context);
/*! @function Growl_LaunchIfInstalled
* @abstract Launches GrowlHelperApp if it is not already running.
* @param callback A callback function which will be called if Growl was successfully
* launched or was already running. Can be <code>NULL</code>.
* @param context The context pointer to pass to the callback. Can be <code>NULL</code>.
* @result Returns true if Growl was successfully launched or was already
* running; returns false and does not call the callback otherwise.
* @discussion Returns true and calls the callback (if the callback is not
* <code>NULL</code>) if the Growl helper app began launching or was already
* running. Returns false and performs no other action if Growl could not be
* launched (e.g. because the Growl preference pane is not properly installed).
*
* If <code>Growl_CreateBestRegistrationDictionary</code> returns
* non-<code>NULL</code>, this function will register with Growl atomically.
*
* The callback should take a single argument; this is to allow applications
* to have context-relevant information passed back. It is perfectly
* acceptable for context to be <code>NULL</code>. The callback itself can be
* <code>NULL</code> if you don't want one.
*/
GROWL_EXPORT Boolean Growl_LaunchIfInstalled(GrowlLaunchCallback callback, void *context);
#pragma mark -
#pragma mark Constants
/*! @defined GROWL_PREFPANE_BUNDLE_IDENTIFIER
* @abstract The CFBundleIdentifier of the Growl preference pane bundle.
* @discussion GrowlApplicationBridge uses this to determine whether Growl is
* currently installed, by searching for the Growl preference pane. Your
* application probably does not need to use this macro itself.
*/
#ifndef GROWL_PREFPANE_BUNDLE_IDENTIFIER
#define GROWL_PREFPANE_BUNDLE_IDENTIFIER CFSTR("com.growl.prefpanel")
#endif
__END_DECLS
#endif /* _GROWLAPPLICATIONBRIDGE_CARBON_H_ */
+25 -8
View File
@@ -8,21 +8,38 @@ var OS = require("os"),
task ("build", function()
{
// If sw_vers does not exist, we aren't on Mac OS X
if (!executableExists("sw_vers"))
OS.exit(0);
// No building on 10.6
var p = OS.popen(["sw_vers", "-productVersion"]);
if (p.wait() === 0)
{
var versions = p.stdout.read().split("."),
majorVersion = parseInt(versions[0], 10),
minorVersion = parseInt(versions[1], 10),
buildVersion = parseInt(versions[2], 10);
if (majorVersion < 10 || minorVersion < 7)
{
colorPrint("XcodeCapp can only be built on Mac OS X 10.7+. Please go to www.cappuccino-project.org to download the latest XcodeCapp.", "red");
OS.exit(0);
}
}
if (executableExists("xcodebuild"))
{
var args = "-sdk macosx -alltargets -configuration Release",
supportPath = FILE.join($BUILD_CJS_CAPPUCCINO, "support", applicationName),
installPath = FILE.join("/", "Applications", applicationName);
// Remove an old symlink, the application is built directly into /Applications now.
if (FILE.isLink(installPath))
FILE.remove(installPath);
if (OS.system("xcodebuild " + args))
OS.exit(1);
rm_rf(supportPath);
FILE.mkdirs(supportPath);
cp_r(FILE.join("build", "Release", "XcodeCapp.app"), supportPath);
FILE.chmod(FILE.join(supportPath, "Contents", "MacOS", "XcodeCapp"), 0755);
OS.system(["ln", "-sf", supportPath, installPath]);
}
else
{
-18
View File
@@ -1,18 +0,0 @@
//
// PRHEmptyGrowlDelegate.h
// XcodeCapp
//
// Created by Andrea D'Amore on 19/06/11.
// Copyright 2011 by author. All rights reserved.
//
// taken from
// http://groups.google.com/group/growl-development/browse_thread/thread/6b0c3fb9fa31f765
#import <Cocoa/Cocoa.h>
#import <Growl/Growl.h>
@interface PRHEmptyGrowlDelegate : NSObject <GrowlApplicationBridgeDelegate> {
}
@end
-14
View File
@@ -1,14 +0,0 @@
//
// PRHEmptyGrowlDelegate.h
// XcodeCapp
//
// Created by Andrea D'Amore on 19/06/11.
// Copyright 2011 by author. All rights reserved.
//
#import "PRHEmptyGrowlDelegate.h"
@implementation PRHEmptyGrowlDelegate
@end
-19
View File
@@ -1,19 +0,0 @@
# xCodeCapp-Cocoa
xcodecapp-cocoa is a port from the original xcodecapp application. It works basically the same than this
tools shipped with Cappuccino framework but have serveral advantages:
* It uses the FSEventStream system to be notified when a file changes. So no useless looping
* It consumes about no CPU when idle
* It allows you to choose graphically the project you want to use
* It will keep track of your already generated project helper
* It supports .xcodecapp-ignore
* It uses Growl to notify you when a conversion is done.
# License
All the code is distributed under AGPL v3.0 license. The parse.j comes from Cappuccino parser and uses the Cappuccino license.
# Author
Antoine Meradal <primalmotion@archipelproject.org>
-34
View File
@@ -1,34 +0,0 @@
/*
* This file is a part of program XcodeCapp
* Copyright (C) 2013 Antoine Mercadal (<primalmotion@archipelproject.org>)
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#import <Cocoa/Cocoa.h>
@interface TNErrorDataView : NSView
{
IBOutlet NSTextField *fieldFileName;
IBOutlet NSTextField *__strong fieldMessage;
IBOutlet NSButton *buttonOpenFile;
NSString *_fullPath;
}
@property (strong) NSTextField *fieldMessage;
- (IBAction)openFile:(id)aSender;
@end
-75
View File
@@ -1,75 +0,0 @@
/*
* This file is a part of program XcodeCapp
* Copyright (C) 2013 Antoine Mercadal (<primalmotion@archipelproject.org>)
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#import "TNErrorDataView.h"
@implementation TNErrorDataView
@synthesize fieldMessage;
/*!
Set the data view's object value
@param aValue the dictionary representing the error
*/
- (void)setObjectValue:(NSDictionary *)aValue
{
[fieldMessage setStringValue:[aValue valueForKey:@"message"]];
[fieldFileName setStringValue:[aValue valueForKey:@"file"]];
_fullPath = [aValue valueForKey:@"path"];
}
#pragma -
#pragma Actions
/*!
Open the errored file in default editor
@param aSender the sender of the action
*/
- (IBAction)openFile:(id)aSender
{
NSWorkspace *workspace = [NSWorkspace sharedWorkspace];
[workspace openFile:_fullPath];
}
#pragma -
#pragma CPCoding
- (id)initWithCoder:(NSCoder*)aCoder
{
if (self = [super initWithCoder:aCoder])
{
fieldFileName = [aCoder decodeObjectForKey:@"fieldFileName"];
fieldMessage = [aCoder decodeObjectForKey:@"fieldMessage"];
buttonOpenFile = [aCoder decodeObjectForKey:@"buttonOpenFile"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder*)aCoder
{
[super encodeWithCoder:aCoder];
[aCoder encodeObject:fieldFileName forKey:@"fieldFileName"];
[aCoder encodeObject:fieldMessage forKey:@"fieldMessage"];
[aCoder encodeObject:buttonOpenFile forKey:@"buttonOpenFile"];
}
@end
-102
View File
@@ -1,102 +0,0 @@
/*
* This file is a part of program XcodeCapp
* Copyright (C) 2011 Antoine Mercadal (<primalmotion@archipelproject.org>)
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#import <Foundation/Foundation.h>
#import "PRHEmptyGrowlDelegate.h"
#import "FSEventCallback.h"
extern NSString * const XCCDidPopulateProjectNotification;
extern NSString * const XCCConversionStartNotification;
extern NSString * const XCCConversionStopNotification;
extern NSString * const XCCListeningStartNotification;
@interface TNXCodeCapp : NSObject
{
FSEventStreamRef stream;
NSFileManager *fm;
NSMutableArray *errorList;
NSMutableSet *ignoredFilePaths;
NSNumber *lastEventId;
NSString *currentAPIMode;
NSString *currentProjectName;
NSString *parserPath;
NSString *XCodeSupportPBXPath;
NSString *XCodeSupportProjectName;
NSString *XCodeTemplatePBXPath;
NSString *profilePath;
NSString *shellPath;
NSString *PBXModifierScriptPath;
NSURL *currentProjectURL;
NSURL *XCodeSupportProject;
NSURL *XCodeSupportProjectSources;
PRHEmptyGrowlDelegate *growlDelegateRef;
NSObject *delegate;
NSDate *appStartedTimestamp;
NSMutableDictionary *pathModificationDates;
BOOL supportsFileBasedListening;
BOOL reactToInodeModification;
BOOL isListening;
BOOL isUsingFileLevelAPI;
BOOL supportFileLevelAPI;
}
@property (strong) NSObject* delegate;
@property (strong) NSMutableArray* errorList;
@property (strong) NSURL* XCodeSupportProject;
@property (strong) NSURL* currentProjectURL;
@property (strong) NSString* currentProjectName;
@property (strong) NSString* currentAPIMode;
@property BOOL supportsFileBasedListening;
@property BOOL reactToInodeModification;
@property BOOL isListening;
@property BOOL supportFileLevelAPI;
@property BOOL isUsingFileLevelAPI;
- (BOOL)isObjJFile:(NSString*)path;
- (void)computeIgnoredPaths;
- (BOOL)isPathMatchingIgnoredPaths:(NSString*)aPath;
- (BOOL)isXIBFile:(NSString *)path;
- (BOOL)isXCCIgnoreFile:(NSString *)path;
- (BOOL)prepareXCodeSupportProject;
- (NSURL*)shadowHeaderURLForSourceURL:(NSURL*)aSourceURL;
- (void)cleanUpShadowsRelatedToSourceURL:(NSURL*)aSourceURL;
- (NSURL*)shadowImplementationURLForSourceURL:(NSURL*)aSourceURL;
- (NSURL*)sourceURLForShadowName:(NSString *)aString;
- (void)handleFileModification:(NSString*)fullPath notify:(BOOL)shouldNotify;
- (void)handleFileRemoval:(NSString*)fullPath;
- (void)initializeEventStreamWithPath:(NSString*)aPath;
- (void)stopEventStream;
- (void)updateLastEventId:(uint64_t)eventId;
- (void)updateUserDefaultsWithLastEventId;
- (void)synchronizeUserDefaultsWithDisk;
- (void)listenProjectAtPath:(NSString *)path;
- (void)clear;
- (void)start;
- (void)configure;
- (void)tidyShadowedFiles;
@end
@interface TNXCodeCapp (SnowLeopard)
- (void)updateLastModificationDate:(NSDate *)date forPath:(NSString *)path;
- (NSDate*)lastModificationDateForPath:(NSString *)path;
@end
-882
View File
@@ -1,882 +0,0 @@
/*
* This file is a part of program XcodeCapp
* Copyright (C) 2011 Antoine Mercadal (<primalmotion@archipelproject.org>)
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#import "TNXCodeCapp.h"
#include "macros.h"
NSString * const XCCUnderscoreReplacement = @"DoNotPutThisStringInFileNameOrWorldWillDie";
NSString * const XCCDidPopulateProjectNotification = @"XCCDidPopulateProjectNotification";
NSString * const XCCConversionStartNotification = @"XCCConversionStartNotification";
NSString * const XCCConversionStopNotification = @"XCCConversionStopNotification";
NSString * const XCCListeningStartNotification = @"XCCListeningStartNotification";
@implementation TNXCodeCapp
@synthesize delegate;
@synthesize errorList;
@synthesize XCodeSupportProject;
@synthesize currentProjectURL;
@synthesize currentProjectName;
@synthesize supportsFileBasedListening;
@synthesize reactToInodeModification;
@synthesize currentAPIMode;
@synthesize isListening;
@synthesize supportFileLevelAPI;
@synthesize isUsingFileLevelAPI;
#pragma mark - Initialization
/*!
Initialize the AppController
*/
- (id)init
{
self = [super init];
if (self)
{
errorList = [NSMutableArray arrayWithCapacity:10];
fm = [NSFileManager defaultManager];
ignoredFilePaths = [NSMutableSet new];
parserPath = [[NSBundle mainBundle] pathForResource:@"parser" ofType:@"j"];
lastEventId = [[NSUserDefaults standardUserDefaults] objectForKey:@"lastEventId"];
appStartedTimestamp = [NSDate date];
[self setIsListening:NO];
[self setIsUsingFileLevelAPI:NO];
SInt32 versionMajor = 0;
SInt32 versionMinor = 0;
Gestalt(gestaltSystemVersionMajor, &versionMajor);
Gestalt(gestaltSystemVersionMinor, &versionMinor);
[self setSupportFileLevelAPI:versionMajor >= 10 && versionMinor >= 7];
// Uncomment to simulate 10.6 mode
// [self setSupportFileLevelAPI:NO];
[self configure];
NSString* myShell = [[[NSProcessInfo processInfo] environment] objectForKey:@"SHELL"];
if (myShell)
{
shellPath = myShell;
}
else
{
shellPath = @"/bin/bash";
}
if([shellPath isEqualToString:@"/bin/bash"])
{
if([fm fileExistsAtPath:[@"~/.bash_profile" stringByExpandingTildeInPath]])
profilePath = [@"source ~/.bash_profile" stringByExpandingTildeInPath];
else if([fm fileExistsAtPath:[@"~/.bashrc" stringByExpandingTildeInPath]])
profilePath = [@"source ~/.bashrc" stringByExpandingTildeInPath];
else if([fm fileExistsAtPath:[@"~/.profile" stringByExpandingTildeInPath]])
profilePath = [@"source ~/.profile" stringByExpandingTildeInPath];
else
profilePath = @"";
}
else if ([shellPath isEqualToString:@"/bin/zsh"])
{
if([fm fileExistsAtPath:[@"~/.zshrc" stringByExpandingTildeInPath]])
profilePath = [@"source ~/.zshrc" stringByExpandingTildeInPath];
else if([fm fileExistsAtPath:[@"~/.profile" stringByExpandingTildeInPath]])
profilePath = [@"source ~/.profile" stringByExpandingTildeInPath];
else
profilePath = @"";
}
else
{
NSAlert *alert = [NSAlert alertWithMessageText:@"Shell not recognized."
defaultButton:@"OK"
alternateButton:nil
otherButton:nil
informativeTextWithFormat:@"You are running %@ as your shell, which is not supported. Please change your shell to either BASH or ZSH.", shellPath];
[alert runModal];
profilePath = @"";
}
}
return self;
}
- (void)start
{
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"XCCReopenLastProject"])
return;
NSString *lastOpenedPath = [[NSUserDefaults standardUserDefaults] objectForKey:@"LastOpenedPath"];
if (lastOpenedPath)
{
if ([fm fileExistsAtPath:lastOpenedPath])
{
[self listenProjectAtPath:[NSString stringWithFormat:@"%@/", lastOpenedPath]];
}
else
{
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"LastOpenedPath"];
}
}
}
#pragma mark - Project Management
/*!
Check if .XcodeSupport needs to be initialized.
If not needed, check that all J files are mirrored. If no,
then launch conversion for missing mirrored h files
@return YES or NO
*/
- (BOOL)prepareXCodeSupportProject
{
XCodeSupportProjectName = [NSString stringWithFormat:@"%@.xcodeproj/", currentProjectName];
XCodeTemplatePBXPath = [[NSBundle mainBundle] pathForResource:@"project.pbxproj" ofType:@"sample"];
XCodeSupportProject = [NSURL URLWithString:XCodeSupportProjectName relativeToURL:currentProjectURL];
XCodeSupportProjectSources = [NSURL URLWithString:@".XcodeSupport/" relativeToURL:currentProjectURL];
XCodeSupportPBXPath = [NSString stringWithFormat:@"%@/project.pbxproj", [XCodeSupportProject path]];
PBXModifierScriptPath = [[NSBundle mainBundle] pathForResource:@"pbxprojModifier" ofType:@"py"];
//[fm removeItemAtURL:XCodeSupportProjectSources error:nil];
//[fm removeItemAtURL:XCodeSupportProject error:nil];
// create the template project if it doesn't exist
if (![fm fileExistsAtPath:[XCodeSupportProjectSources path]])
{
NSLog(@"prepareXCodeSupportProject: Xcode support folder created at: %@", [XCodeSupportProject path]);
[fm createDirectoryAtPath:[XCodeSupportProject path] withIntermediateDirectories:YES attributes:nil error:nil];
DLog(@"prepareXCodeSupportProject: Copying project.pbxproj from %@ to %@", XCodeTemplatePBXPath, [XCodeSupportProject path]);
[fm copyItemAtPath:XCodeTemplatePBXPath toPath:XCodeSupportPBXPath error:nil];
DLog(@"prepareXCodeSupportProject: Reading the content of the project.pbxproj");
NSMutableString *PBXContent = [NSMutableString stringWithContentsOfFile:XCodeSupportPBXPath encoding:NSUTF8StringEncoding error:nil];
[PBXContent writeToFile:XCodeSupportPBXPath atomically:YES encoding:NSUTF8StringEncoding error:nil];
DLog(@"prepareXCodeSupportProject: PBX file adapted to the project");
[self createXcodeSupportProjectSourcesDirIfNecessary];
return NO;
}
[self createXcodeSupportProjectSourcesDirIfNecessary];
return YES;
}
/*!
Create the .XcodeSupport/Sources folder if necessary.
*/
- (void)createXcodeSupportProjectSourcesDirIfNecessary
{
if ([fm fileExistsAtPath:[XCodeSupportProjectSources path]])
return;
DLog(@"createXcodeSupportProjectSourcesDirIfNecessary: Creating source folder %@", [XCodeSupportProjectSources path]);
[fm createDirectoryAtPath:[XCodeSupportProjectSources path] withIntermediateDirectories:YES attributes:nil error:nil];
}
/*!
Initialize the creation of the .XcodeSupport project. This
Operation is threaded
@param arguments Thread arguments (not used)
@param shouldNotify is YES, XCCDidPopulateProjectNotification will be send
*/
- (void)populateXCodeProject:(NSNumber *)shouldNotify
{
if ([shouldNotify boolValue])
[delegate performSelector:@selector(growlWithTitle:message:) withObject:@"Loading project" withObject:[currentProjectURL path]];
NSArray *subdpaths = [fm subpathsAtPath:[currentProjectURL path]];
for (NSString *p in subdpaths)
{
NSString *filePath = [NSString stringWithFormat:@"%@/%@", [currentProjectURL path], p];
BOOL isDir = NO;
[fm fileExistsAtPath:filePath isDirectory:&isDir];
if (isDir || ![self isObjJFile:filePath] || [self isPathMatchingIgnoredPaths:filePath])
continue;
NSURL *eventualShadow = [self shadowHeaderURLForSourceURL:[NSURL fileURLWithPath:filePath]];
if (![fm fileExistsAtPath:[eventualShadow path]])
{
DLog(@"populateXCodeProject: Computing missing shadow file for %@", filePath);
[self handleFileModification:filePath notify:NO];
}
}
if ([shouldNotify boolValue])
{
NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:currentProjectURL, @"URL", nil];
[[NSNotificationCenter defaultCenter] postNotificationName:XCCDidPopulateProjectNotification object:self userInfo:info];
}
}
/*!
Start all needed processes for listening to a given path
@param path The folder path to listen to
*/
- (void)listenProjectAtPath:(NSString *)path
{
NSMutableString *tempName = [NSMutableString stringWithString:[path lastPathComponent]];
currentProjectURL = [NSURL fileURLWithPath:path];
[tempName replaceOccurrencesOfString:@" "
withString:@"_"
options:NSCaseInsensitiveSearch
range:NSMakeRange(0, [tempName length])];
currentProjectName = [NSString stringWithString:tempName];
[self computeIgnoredPaths];
BOOL isProjectReady = [self prepareXCodeSupportProject];
[NSThread detachNewThreadSelector:@selector(populateXCodeProject:) toTarget:self withObject:[NSNumber numberWithBool:!isProjectReady]];
[self initializeEventStreamWithPath:[currentProjectURL path]];
NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:path, @"path", [NSNumber numberWithInt:(isProjectReady) ? 1 : 0], @"ready", nil];
[[NSNotificationCenter defaultCenter] postNotificationName:XCCListeningStartNotification object:self userInfo:info];
[[NSUserDefaults standardUserDefaults] setObject:[currentProjectURL path] forKey:@"LastOpenedPath"];
}
#pragma mark - Event Stream
/*!
Initializes the FSEvent stream
@param aPath the path of the folder to listen
*/
- (void)initializeEventStreamWithPath:(NSString*)aPath
{
if ([self isListening])
return;
[self stopEventStream];
NSMutableArray *pathsToWatch = [NSMutableArray arrayWithObject:aPath];
void *appPointer = (__bridge void *)self;
FSEventStreamContext context = {0, appPointer, NULL, NULL, NULL};
CFTimeInterval latency = 2.0;
FSEventStreamCreateFlags flags = 0;
if (supportsFileBasedListening)
{
DLog(@"initializeEventStreamWithPath: Initializing the FSEventStream at file level (clean)");
flags = kFSEventStreamCreateFlagUseCFTypes | kFSEventStreamCreateFlagNoDefer | kFSEventStreamCreateFlagFileEvents;
}
else
{
NSLog(@"Initializing the FSEventStream at folder level (dirty)");
flags = kFSEventStreamCreateFlagUseCFTypes;
}
// add symlinked directories
NSArray *fileList = [fm contentsOfDirectoryAtPath:aPath error:nil];
for (NSString *node in fileList)
{
NSDictionary *attributes = [fm attributesOfItemAtPath:aPath error:nil];
if ([[attributes objectForKey:@"NSFileType"] isEqualTo:NSFileTypeDirectory])
{
NSString *subDirectoryPath = [aPath stringByAppendingPathComponent:node];
NSString *symlinkDestination = [fm destinationOfSymbolicLinkAtPath:subDirectoryPath error:nil];
if (symlinkDestination)
{
[pathsToWatch addObject:subDirectoryPath];
}
}
}
stream = FSEventStreamCreate(NULL, &fsevents_callback, &context, (__bridge CFArrayRef) pathsToWatch,
[lastEventId unsignedLongLongValue], latency, flags);
FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
FSEventStreamStart(stream);
[self setIsListening:YES];
}
/*!
Stop listening the FSEvent stream if active
*/
- (void)stopEventStream
{
if (stream)
{
FSEventStreamStop(stream);
FSEventStreamUnscheduleFromRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
FSEventStreamInvalidate(stream);
FSEventStreamRelease(stream);
stream = NULL;
}
[self setIsListening:NO];
}
/*!
Stops and clear the worker
*/
- (void)clear
{
[self updateUserDefaultsWithLastEventId];
[self synchronizeUserDefaultsWithDisk];
currentProjectURL = nil;
currentProjectName = nil;
[ignoredFilePaths removeAllObjects];
[self stopEventStream];
}
/*!
Choose the API mode according to default
*/
- (void)configure
{
if (![self supportFileLevelAPI])
{
DLog(@"configure: System doesn't support file level API");
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithInt:2] forKey:@"XCCAPIMode"];
}
switch ([[NSUserDefaults standardUserDefaults] integerForKey:@"XCCAPIMode"])
{
case 0:
supportsFileBasedListening = [self supportFileLevelAPI] ? YES : NO;
break;
case 1:
supportsFileBasedListening = YES;
break;
case 2:
supportsFileBasedListening = NO;
break;
}
if (supportsFileBasedListening)
{
DLog(@"configure: using 10.7+ mode listening (clean)");
[self setCurrentAPIMode:@"File level (Lion)"];
[self setIsUsingFileLevelAPI:YES];
reactToInodeModification = [[NSUserDefaults standardUserDefaults] boolForKey:@"XCCReactMode"];
}
else
{
DLog(@"configure: using 10.6 mode listening (dirty)");
reactToInodeModification = NO;
[self setCurrentAPIMode:@"Folder level (Snow Leopard)"];
[self setIsUsingFileLevelAPI:NO];
}
}
/*!
Update the last event ID. We use a method because
This is called from outside the class, in the FSEvent callback
@param eventId the current event ID value
*/
- (void)updateLastEventId:(uint64_t)eventId
{
lastEventId = [NSNumber numberWithUnsignedLongLong:eventId];
}
/*!
Updates the user defaults with the last recorded event Id.
*/
- (void)updateUserDefaultsWithLastEventId
{
if (lastEventId && [lastEventId longLongValue] != 0)
{
[[NSUserDefaults standardUserDefaults] setObject:lastEventId forKey:@"lastEventId"];
}
}
/*!
Tells the standard user defaults to synchronize with disk.
*/
- (void)synchronizeUserDefaultsWithDisk
{
[[NSUserDefaults standardUserDefaults] synchronize];
}
#pragma mark - Shell Helpers
/*!
Run a NSTask with the given arguments
@param arguments NSArray containing the NSTask arguments
@return NSarray containing the return code (int) and the eventual response (string)
*/
- (NSArray *)runTask:(NSArray *)arguments
{
NSTask *task;
NSData *stdOut;
NSString *response;
NSNumber *status;
task = [[NSTask alloc] init];
[task setLaunchPath:shellPath];
[task setArguments: arguments];
[task setStandardOutput:[NSPipe pipe]];
[task launch];
[task waitUntilExit];
stdOut = [[[task standardOutput] fileHandleForReading] availableData];
response = [[NSString alloc] initWithData:stdOut encoding:NSUTF8StringEncoding];
status = [NSNumber numberWithInt:[task terminationStatus]];
return [NSArray arrayWithObjects:status, response, nil];
}
#pragma mark - Event Handlers
/*!
Handle a file modification. If it's a .J or XIB or NIB, it will
perform the according conversion. If it's .xcodecapp-ignore, it will
update the list of ignored files.
@param fullPath the full path of the modified file
@param shouldNotify if YES, Growl notifications will be displayed
*/
- (void)handleFileModification:(NSString*)fullPath notify:(BOOL)shouldNotify
{
if (![self isXIBFile:fullPath] && ![self isObjJFile:fullPath] && ![self isXCCIgnoreFile:fullPath])
return;
if ([self isPathMatchingIgnoredPaths:fullPath] || ![fm fileExistsAtPath:fullPath])
return;
DLog(@"handleFileModification:notify: Parsing modified file: %@", fullPath);
NSArray *arguments = nil;
NSArray *PBXArguments = nil;
NSString *successTitle = nil;
NSString *successMsg = nil;
NSString *response = nil;
NSNumber *status = [NSNumber numberWithInt:0];
NSString *splitPath = [fullPath substringFromIndex:[[currentProjectURL path] length] + 1];
NSURL *encodedURL = [NSURL URLWithString:[fullPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSURL *shadowHeaderURL = [self shadowHeaderURLForSourceURL:encodedURL];
NSURL *shadowImplementationURL = [self shadowImplementationURLForSourceURL:encodedURL];
DLog(@"handleFileModification:notify: Shadow header path: %@", shadowHeaderURL);
DLog(@"handleFileModification:notify: Shadow implementation path: %@", shadowImplementationURL);
[[NSNotificationCenter defaultCenter] postNotificationName:XCCConversionStartNotification object:self];
if ([self isXIBFile:fullPath])
{
arguments = [NSArray arrayWithObjects: @"-c",
[NSString stringWithFormat:@"(%@; nib2cib '%@';) 2>&1", profilePath, fullPath],@"",nil];
successTitle = @"XIB converted";
successMsg = splitPath;
}
else if ([self isObjJFile:fullPath])
{
arguments = [NSArray arrayWithObjects: @"-c",
[NSString stringWithFormat:@"(%@; objj '%@' '%@' '%@' '%@';) 2>&1",
profilePath,
parserPath,
fullPath,
[shadowHeaderURL path],
[shadowImplementationURL path]],nil];
PBXArguments = [NSArray arrayWithObjects: @"-c",
[NSString stringWithFormat:@"(%@; python %@ add '%@' '%@' '%@' '%@' '%@') 2>&1",
profilePath,
PBXModifierScriptPath,
XCodeSupportPBXPath,
[shadowHeaderURL path],
[shadowImplementationURL path],
fullPath,
[currentProjectURL path]],nil];
successTitle = @"Objective-J source processed";
successMsg = splitPath;
}
else if ([self isXCCIgnoreFile:fullPath])
{
[self computeIgnoredPaths];
successTitle = @".xcodecapp-ignore processed";
successMsg = @"Ignored files list updated";
arguments = nil;
}
// Run the task and get the response if needed
if (arguments)
{
DLog(@"handleFileModification:notify: Running conversion task...");
NSArray *statusInfo = [self runTask:arguments];
status = [statusInfo objectAtIndex:0];
response = [statusInfo objectAtIndex:1];
DLog(@"handleFileModification:notify: Conversion task result/response: %@/%@", status, response);
if ([status intValue] == 0 && shouldNotify)
{
[delegate performSelector:@selector(growlWithTitle:message:) withObject:successTitle withObject:successMsg];
}
else if (![status intValue] == 0)
{
if (response)
{
NSDictionary *errorDictionary = [NSDictionary dictionaryWithObjectsAndKeys:response, @"message",
splitPath, @"file",
fullPath, @"path", nil];
[errorList addObject:errorDictionary];
}
[delegate performSelector:@selector(growlWithTitle:message:) withObject:@"Error processing file" withObject:splitPath];
}
}
if (PBXArguments)
{
DLog(@"handleFileModification:notify: Running update PBX task...");
NSArray *statusInfo = [self runTask:PBXArguments];
status = [statusInfo objectAtIndex:0];
response = [statusInfo objectAtIndex:1];
DLog(@"handleFileModification:notify: Update PBX Task result/response: %@/%@", status, response);
}
[[NSNotificationCenter defaultCenter] postNotificationName:XCCConversionStopNotification object:self];
DLog(@"handleFileModification:notify: Processed: %@", fullPath);
}
/*!
Handle a file deletion. If it's a .J, it will
remove the shadowed .h file. If it's .xcodecapp-ignore
it will reset the list of ignored files.
@param fullPath the full path of the modified file
@param shouldNotify if YES, Growl notifications will be displayed
*/
- (void)handleFileRemoval:(NSString*)fullPath
{
if ([self isPathMatchingIgnoredPaths:fullPath] || [fm fileExistsAtPath:fullPath])
return;
NSString *encodedPath = [fullPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
if ([self isObjJFile:fullPath])
[self cleanUpShadowsRelatedToSourceURL:[NSURL URLWithString:encodedPath]];
else if ([self isXCCIgnoreFile:fullPath])
[self computeIgnoredPaths];
}
#pragma mark - Source Files Management
/*!
Check if given full path is Objective-J file
@param path the path to check
@return YES or NO
*/
- (BOOL)isObjJFile:(NSString *)path
{
return [[[path pathExtension] uppercaseString] isEqual:@"J"];
}
/*!
Check if given full path is XIB or NIB file
@param path the path to check
@return YES or NO
*/
- (BOOL)isXIBFile:(NSString *)path
{
path = [[path pathExtension] uppercaseString];
return [path isEqual:@"XIB"] || [path isEqual:@"NIB"];
}
/*!
Check if given full path is the .xcodecapp-ignore file
@param path the path to check
@return YES or NO
*/
- (BOOL)isXCCIgnoreFile:(NSString *)path
{
path = [path lastPathComponent];
return [path isEqual:@".xcodecapp-ignore"];
}
#pragma mark - Shadow Files Management
/*!
Compute the mirorred (shadow) header file name for a given path
@param aSourceURL the origin path
@return NSURL representing the shadow URL for the header file
*/
- (NSURL *)shadowHeaderURLForSourceURL:(NSURL*)aSourceURL
{
if (!aSourceURL)
[NSException raise:NSInvalidArgumentException format:@"shadowHeaderURLForSourceURL: aSource URL must not be null"];
NSMutableString *flattenedPath = [NSMutableString stringWithString:[aSourceURL path]];
// Replace "_" with a substring that is unlikely to be in a filename
[flattenedPath replaceOccurrencesOfString:@"_"
withString:XCCUnderscoreReplacement
options:0
range:NSMakeRange(0, [flattenedPath length])];
[flattenedPath replaceOccurrencesOfString:@"/"
withString:@"_"
options:0
range:NSMakeRange(0, [flattenedPath length])];
DLog(@"shadowHeaderURLForSourceURL: Flattened path: %@", flattenedPath);
NSString *basename = [NSString stringWithFormat:@"%@.h", [[flattenedPath stringByDeletingPathExtension] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
return [NSURL URLWithString:basename relativeToURL:XCodeSupportProjectSources];
}
/*!
Compute the mirorred (shadow) implementation file name for a given path
@param aSourceURL the origin path
@return NSURL representing the shadow URL for the implementation file
*/
- (NSURL *)shadowImplementationURLForSourceURL:(NSURL*)aSourceURL
{
if (!aSourceURL)
[NSException raise:NSInvalidArgumentException format:@"shadowImplementationURLForSourceURL: aSource URL must not be null"];
NSURL *shadowHeaderURL = [self shadowHeaderURLForSourceURL:aSourceURL];
NSURL *shadowImplPath = [[shadowHeaderURL URLByDeletingPathExtension] URLByAppendingPathExtension:@"m"];
return shadowImplPath;
}
/*!
Compute the Cappuccino source file that is related to the given shadow header file URL
@param aString the origin path
@return NSURL representing the URL for the related Cappuccino source file
*/
- (NSURL *)sourceURLForShadowName:(NSString *)aString
{
NSMutableString * unshadowedPath = [NSMutableString stringWithString:aString];
[unshadowedPath replaceOccurrencesOfString:@"_"
withString:@"/"
options:0
range:NSMakeRange(0, [unshadowedPath length])];
[unshadowedPath replaceOccurrencesOfString:XCCUnderscoreReplacement
withString:@"_"
options:0
range:NSMakeRange(0, [unshadowedPath length])];
[unshadowedPath replaceOccurrencesOfString:@".h"
withString:@".j"
options:0
range:NSMakeRange(0, [unshadowedPath length])];
return [NSURL fileURLWithPath:[NSString stringWithString:unshadowedPath]];
}
/*!
Clean up any shadow files and PBX entries related to given the Cappuccino source file URL
@param anURL the Cappuccino source file URL
*/
- (void)cleanUpShadowsRelatedToSourceURL:(NSURL *)anURL
{
NSURL *shadowHeaderURL = [self shadowHeaderURLForSourceURL:anURL];
NSURL *shadowImplementationURL = [self shadowImplementationURLForSourceURL:anURL];
DLog(@"cleanUpShadowsRelatedToSourceURL: Removing shadow header file: %@", shadowHeaderURL);
[fm removeItemAtURL:shadowHeaderURL error:nil];
DLog(@"cleanUpShadowsRelatedToSourceURL: Removing shadow implementation file: %@", shadowImplementationURL);
[fm removeItemAtURL:shadowImplementationURL error:nil];
DLog(@"cleanUpShadowsRelatedToSourceURL:Removing PBX reference task...");
NSArray *PBXArguments = [NSArray arrayWithObjects: @"-c",
[NSString stringWithFormat:@"(%@; python %@ remove '%@' '%@' '%@' '%@' '%@') 2>&1",
profilePath,
PBXModifierScriptPath,
XCodeSupportPBXPath,
[shadowHeaderURL path],
[shadowImplementationURL path],
[anURL path],
[currentProjectURL path]],nil];
NSArray *statusInfo = [self runTask:PBXArguments];
NSNumber *status = [statusInfo objectAtIndex:0];
NSString *response = [statusInfo objectAtIndex:1];
DLog(@"cleanUpShadowsRelatedToSourceURL: PBX Reference removal status/response: %@/%@", status, response);
}
/*!
Clean the support folder according to files present in given path
*/
- (void)tidyShadowedFiles
{
NSArray *subpaths = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:[XCodeSupportProjectSources path] error:NULL];
for (NSString *subpath in subpaths)
{
if (![[subpath pathExtension] isEqual:@".h"] || [[subpath lastPathComponent] isEqual:@"xcc_general_include.h"])
continue;
NSURL *unshadowed = [self sourceURLForShadowName:subpath];
if (![fm fileExistsAtPath:[unshadowed path]])
{
[self cleanUpShadowsRelatedToSourceURL:unshadowed];
if (![self supportFileLevelAPI] && [self respondsToSelector:@selector(updateLastModificationDate:forPath:)])
[self performSelector:@selector(updateLastModificationDate:forPath:) withObject:nil withObject:unshadowed];
}
}
}
#pragma mark - XCC Ignore management
/*!
Compute the ignored paths according to any existing
.xcodecapp-ignore file
*/
- (void)computeIgnoredPaths
{
NSString *ignorePath = [NSString stringWithFormat:@"%@/.xcodecapp-ignore", [currentProjectURL path]];
[ignoredFilePaths removeAllObjects];
if ([fm fileExistsAtPath:ignorePath])
{
NSString *ignoreFileContent = [NSString stringWithContentsOfFile:ignorePath encoding:NSUTF8StringEncoding error:nil];
NSArray *ignoredPatterns = [ignoreFileContent componentsSeparatedByString:@"\n"];
for (NSString *pattern in ignoredPatterns)
{
if ([pattern length])
[ignoredFilePaths addObject:pattern];
}
}
[ignoredFilePaths addObject:@"*/.git/*"];
[ignoredFilePaths addObject:@"*/.svn/*"];
[ignoredFilePaths addObject:@"*/.hg/*"];
[ignoredFilePaths addObject:@"*/Frameworks/*"];
[ignoredFilePaths addObject:@"*/.XcodeSupport/*"];
[ignoredFilePaths addObject:@"*/Build/*"];
[ignoredFilePaths addObject:@"*/NS_*.j"];
[ignoredFilePaths addObject:@"*main.j"];
[ignoredFilePaths addObject:@"*.xcodeproj/*"];
[ignoredFilePaths addObject:@"*.DS_Store"];
NSLog(@"Ignoring file paths: %@", ignoredFilePaths);
}
/*!
Check is given path should be ignored
@param aPath the path to check
@return YES if it should be ignored, NO otherwise
*/
- (BOOL)isPathMatchingIgnoredPaths:(NSString*)aPath
{
if ([ignoredFilePaths count] == 0)
return NO;
for (NSString *ignoredPath in ignoredFilePaths)
{
if ([ignoredPath length] == 0)
continue;
NSMutableString *regexp = [ignoredPath mutableCopy];
[regexp replaceOccurrencesOfString:@"/"
withString:@"\\/"
options:0
range:NSMakeRange(0, [regexp length])];
[regexp replaceOccurrencesOfString:@"."
withString:@"\\."
options:0
range:NSMakeRange(0, [regexp length])];
[regexp replaceOccurrencesOfString:@"*"
withString:@".*"
options:0
range:NSMakeRange(0, [regexp length])];
[regexp replaceOccurrencesOfString:@" "
withString:@"\\ "
options:0
range:NSMakeRange(0, [regexp length])];
NSPredicate *regextest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regexp];
if ([regextest evaluateWithObject:aPath])
return YES;
}
return NO;
}
@end
@implementation TNXCodeCapp (SnowLeopard)
- (void)updateLastModificationDate:(NSDate *)date forPath:(NSString *)path
{
if (!pathModificationDates)
{
pathModificationDates = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"pathModificationDates"] mutableCopy];
if (!pathModificationDates)
pathModificationDates = [NSMutableDictionary new];
}
if (date)
[pathModificationDates setObject:date forKey:path];
else
[pathModificationDates removeObjectForKey:path];
[[NSUserDefaults standardUserDefaults] setObject:pathModificationDates forKey:@"pathModificationDates"];
}
- (NSDate *)lastModificationDateForPath:(NSString *)path
{
if (!pathModificationDates)
{
pathModificationDates = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"pathModificationDates"] mutableCopy];
if (!pathModificationDates)
pathModificationDates = [NSMutableDictionary new];
}
if ([pathModificationDates valueForKey:path] != nil)
return [pathModificationDates valueForKey:path];
else
return appStartedTimestamp;
}
@end
Binary file not shown.
File diff suppressed because it is too large. Load diff
@@ -1,152 +0,0 @@
// !$*UTF8*$!
{
29B97313FDCFA39411CA2CEA /* Project object */ = {
activeArchitecture = i386;
activeBuildConfigurationName = Debug;
activeExecutable = 8E01A8CD0DE519E70008BB35 /* xcodecapp-cocoa */;
activeTarget = 8D1107260486CEB800E47090 /* xcodecapp-cocoa */;
addToTargets = (
8D1107260486CEB800E47090 /* xcodecapp-cocoa */,
);
breakpoints = (
8EF107B90DFCF23200C52EB1 /* AppController.m:30 */,
8EF107CA0DFCF4A100C52EB1 /* AppController.m:62 */,
);
codeSenseManager = 8E01A8DC0DE51A060008BB35 /* Code sense */;
executables = (
8E01A8CD0DE519E70008BB35 /* xcodecapp-cocoa */,
);
perUserDictionary = {
PBXConfiguration.PBXFileTableDataSource3.PBXFileTableDataSource = {
PBXFileTableDataSourceColumnSortingDirectionKey = "-1";
PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID;
PBXFileTableDataSourceColumnWidthsKey = (
20,
71,
20,
48,
43,
43,
20,
);
PBXFileTableDataSourceColumnsKey = (
PBXFileDataSource_FiletypeID,
PBXFileDataSource_Filename_ColumnID,
PBXFileDataSource_Built_ColumnID,
PBXFileDataSource_ObjectSize_ColumnID,
PBXFileDataSource_Errors_ColumnID,
PBXFileDataSource_Warnings_ColumnID,
PBXFileDataSource_Target_ColumnID,
);
};
PBXPerProjectTemplateStateSaveDate = 244845075;
PBXWorkspaceStateSaveDate = 244845075;
};
sourceControlManager = 8E01A8DB0DE51A060008BB35 /* Source Control */;
userBuildSettings = {
};
};
29B97316FDCFA39411CA2CEA /* main.m */ = {
uiCtxt = {
sepNavIntBoundsRect = "{{0, 0}, {1176, 392}}";
sepNavSelRange = "{818, 0}";
sepNavVisRange = "{297, 521}";
};
};
8D1107260486CEB800E47090 /* xcodecapp-cocoa */ = {
activeExec = 0;
executables = (
8E01A8CD0DE519E70008BB35 /* xcodecapp-cocoa */,
);
};
8E01A8CD0DE519E70008BB35 /* xcodecapp-cocoa */ = {
isa = PBXExecutable;
activeArgIndices = (
);
argumentStrings = (
);
autoAttachOnCrash = 1;
breakpointsEnabled = 0;
configStateDict = {
};
customDataFormattersEnabled = 1;
debuggerPlugin = GDBDebugging;
disassemblyDisplayState = 0;
dylibVariantSuffix = "";
enableDebugStr = 1;
environmentEntries = (
);
executableSystemSymbolLevel = 0;
executableUserSymbolLevel = 0;
libgmallocEnabled = 0;
name = xcodecapp-cocoa;
savedGlobals = {
};
sourceDirectories = (
);
variableFormatDictionary = {
"*eventIds-long long unsigned int-mycallback" = 3;
};
};
8E01A8DB0DE51A060008BB35 /* Source Control */ = {
isa = PBXSourceControlManager;
fallbackIsa = XCSourceControlManager;
isSCMEnabled = 0;
scmConfiguration = {
};
};
8E01A8DC0DE51A060008BB35 /* Code sense */ = {
isa = PBXCodeSenseManager;
indexTemplatePath = "";
};
8E01A91D0DE9EED20008BB35 /* AppController.h */ = {
uiCtxt = {
sepNavIntBoundsRect = "{{0, 0}, {692, 280}}";
sepNavSelRange = "{234, 37}";
sepNavVisRange = "{146, 185}";
sepNavWindowFrame = "{{112, 264}, {1441, 774}}";
};
};
8E01A91E0DE9EED20008BB35 /* AppController.m */ = {
uiCtxt = {
sepNavIntBoundsRect = "{{0, 0}, {692, 2324}}";
sepNavSelRange = "{4949, 0}";
sepNavVisRange = "{3760, 551}";
sepNavWindowFrame = "{{15, -1}, {1441, 774}}";
};
};
8EF107B90DFCF23200C52EB1 /* AppController.m:30 */ = {
isa = PBXFileBreakpoint;
actions = (
);
breakpointStyle = 0;
continueAfterActions = 0;
countType = 0;
delayBeforeContinue = 0;
fileReference = 8E01A91E0DE9EED20008BB35 /* AppController.m */;
functionName = "-awakeFromNib";
hitCount = 1;
ignoreCount = 0;
lineNumber = 30;
location = xcodecapp-cocoa;
modificationTime = 234708321.122562;
state = 2;
};
8EF107CA0DFCF4A100C52EB1 /* AppController.m:62 */ = {
isa = PBXFileBreakpoint;
actions = (
);
breakpointStyle = 0;
continueAfterActions = 0;
countType = 0;
delayBeforeContinue = 0;
fileReference = 8E01A91E0DE9EED20008BB35 /* AppController.m */;
functionName = "mycallback()";
hitCount = 0;
ignoreCount = 0;
lineNumber = 62;
location = xcodecapp-cocoa;
modificationTime = 234708306.739971;
state = 2;
};
}
@@ -7,419 +7,448 @@
objects = {
/* Begin PBXBuildFile section */
0218442515D32B5D00A782B8 /* pbxprojModifier.py in Resources */ = {isa = PBXBuildFile; fileRef = 0218442415D32B5D00A782B8 /* pbxprojModifier.py */; };
022BCEA71468632000B72910 /* project.pbxproj.sample in Resources */ = {isa = PBXBuildFile; fileRef = 022BCEA61468632000B72910 /* project.pbxproj.sample */; };
024A041413699DD400DCBE4D /* parser.j in Resources */ = {isa = PBXBuildFile; fileRef = 024A041113699D6800DCBE4D /* parser.j */; };
026F3B6213866B0B00EE5B83 /* xcodecapp-icon-inactive.png in Resources */ = {isa = PBXBuildFile; fileRef = 026F3B6113866B0B00EE5B83 /* xcodecapp-icon-inactive.png */; };
026F3B6513866E7D00EE5B83 /* xcodecapp-icon-active.png in Resources */ = {isa = PBXBuildFile; fileRef = 026F3B6413866E7D00EE5B83 /* xcodecapp-icon-active.png */; };
027D999B13696A7000D3DB13 /* MainMenu.nib in Resources */ = {isa = PBXBuildFile; fileRef = 29B97318FDCFA39411CA2CEA /* MainMenu.nib */; };
027D999C13696A7000D3DB13 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; };
027D999E13696A7000D3DB13 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; };
027D999F13696A7000D3DB13 /* AppController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E01A91E0DE9EED20008BB35 /* AppController.m */; };
027D99A113696A7000D3DB13 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
027D99A213696A7000D3DB13 /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8E01A8DD0DE51A7C0008BB35 /* CoreServices.framework */; };
027FE49D1462D74C00B1AB92 /* TNXCodeCapp.m in Sources */ = {isa = PBXBuildFile; fileRef = 027FE49C1462D74C00B1AB92 /* TNXCodeCapp.m */; };
027FE4BA1462F64E00B1AB92 /* xcodecapp-icon-working.png in Resources */ = {isa = PBXBuildFile; fileRef = 027FE4B91462F64E00B1AB92 /* xcodecapp-icon-working.png */; };
027FE4BD1462F7EB00B1AB92 /* FSEventCallback.m in Sources */ = {isa = PBXBuildFile; fileRef = 027FE4BC1462F7EB00B1AB92 /* FSEventCallback.m */; };
0289AC9414668CAF003CD975 /* XcodeCapp.icns in Resources */ = {isa = PBXBuildFile; fileRef = 0289AC9314668CAF003CD975 /* XcodeCapp.icns */; };
0294F09315D345A500840547 /* mod_pbxproj.py in Resources */ = {isa = PBXBuildFile; fileRef = 0294F09215D345A500840547 /* mod_pbxproj.py */; };
02998CEB1369BF7D006C73DB /* Growl Registration Ticket.growlRegDict in Resources */ = {isa = PBXBuildFile; fileRef = 02998CEA1369BF7D006C73DB /* Growl Registration Ticket.growlRegDict */; };
02998CF71369C339006C73DB /* Growl.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 02998CF61369C339006C73DB /* Growl.framework */; };
02998CF91369C38A006C73DB /* Growl.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 02998CF61369C339006C73DB /* Growl.framework */; };
02B1C8D816B25C74003C6E82 /* TNErrorDataView.m in Sources */ = {isa = PBXBuildFile; fileRef = 02B1C8D716B25C74003C6E82 /* TNErrorDataView.m */; };
4E2D0D01154840B400475C01 /* help.rtfd in Resources */ = {isa = PBXBuildFile; fileRef = 4E2D0D00154840B400475C01 /* help.rtfd */; };
4E2D0D03154840BC00475C01 /* help.rtfd in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4E2D0D00154840B400475C01 /* help.rtfd */; };
651DAE1F13B0CAB900ECA3F0 /* PRHEmptyGrowlDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 651DAE1D13B0CAB900ECA3F0 /* PRHEmptyGrowlDelegate.m */; };
E10E4CD717298E5F004AB09E /* project.pbxproj in Resources */ = {isa = PBXBuildFile; fileRef = E164FE2A172188F300263CE3 /* project.pbxproj */; };
E164FDD31720E77100263CE3 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E164FDD21720E77100263CE3 /* Cocoa.framework */; };
E164FDDF1720E77100263CE3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E164FDDE1720E77100263CE3 /* main.m */; };
E164FDE91720E77100263CE3 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = E164FDE71720E77100263CE3 /* MainMenu.xib */; };
E164FDF51720EBC100263CE3 /* AppController.m in Sources */ = {isa = PBXBuildFile; fileRef = E164FDF41720EBC100263CE3 /* AppController.m */; };
E164FDF71720EBD600263CE3 /* Growl.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E164FDF61720EBD600263CE3 /* Growl.framework */; };
E164FDFF1720EC5B00263CE3 /* XcodeCapp.m in Sources */ = {isa = PBXBuildFile; fileRef = E164FDFB1720EC5B00263CE3 /* XcodeCapp.m */; };
E164FE061720ED4500263CE3 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = E164FE041720ED4400263CE3 /* InfoPlist.strings */; };
E164FE181720F44B00263CE3 /* parser.j in Resources */ = {isa = PBXBuildFile; fileRef = E164FE171720F44B00263CE3 /* parser.j */; };
E164FE1A1720F49A00263CE3 /* Growl Registration Ticket.growlRegDict in Resources */ = {isa = PBXBuildFile; fileRef = E164FE191720F49A00263CE3 /* Growl Registration Ticket.growlRegDict */; };
E164FE1F1720F51E00263CE3 /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E164FE1D1720F50100263CE3 /* CoreServices.framework */; };
E164FE211720F55600263CE3 /* Growl.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = E164FDF61720EBD600263CE3 /* Growl.framework */; };
E164FE2F1721A34900263CE3 /* icon-active.png in Resources */ = {isa = PBXBuildFile; fileRef = E164FE2C1721A34900263CE3 /* icon-active.png */; };
E164FE301721A34900263CE3 /* icon-inactive.png in Resources */ = {isa = PBXBuildFile; fileRef = E164FE2D1721A34900263CE3 /* icon-inactive.png */; };
E164FE311721A34900263CE3 /* icon-working.png in Resources */ = {isa = PBXBuildFile; fileRef = E164FE2E1721A34900263CE3 /* icon-working.png */; };
E170C3FB17296205003DC489 /* pbxprojModifier.py in CopyFiles */ = {isa = PBXBuildFile; fileRef = E164FE141720F40400263CE3 /* pbxprojModifier.py */; };
E170C3FC1729620C003DC489 /* mod_pbxproj.py in CopyFiles */ = {isa = PBXBuildFile; fileRef = E164FE131720F40400263CE3 /* mod_pbxproj.py */; };
E170C3FD1729620F003DC489 /* parser.j in CopyFiles */ = {isa = PBXBuildFile; fileRef = E164FE171720F44B00263CE3 /* parser.j */; };
E170C40317298833003DC489 /* icon-active@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = E170C3FE17298833003DC489 /* icon-active@2x.png */; };
E170C40417298833003DC489 /* icon-error.png in Resources */ = {isa = PBXBuildFile; fileRef = E170C3FF17298833003DC489 /* icon-error.png */; };
E170C40517298833003DC489 /* icon-error@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = E170C40017298833003DC489 /* icon-error@2x.png */; };
E170C40617298833003DC489 /* icon-inactive@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = E170C40117298833003DC489 /* icon-inactive@2x.png */; };
E170C40717298833003DC489 /* icon-working@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = E170C40217298833003DC489 /* icon-working@2x.png */; };
E17A822D172700B90095CD83 /* XcodeCapp.iconset in Resources */ = {isa = PBXBuildFile; fileRef = E17A822C172700B90095CD83 /* XcodeCapp.iconset */; };
E17B55411732F57700809FFB /* Quartz.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E17B55401732F57700809FFB /* Quartz.framework */; };
E17B55431732F72700809FFB /* help.pdf in Resources */ = {isa = PBXBuildFile; fileRef = E17B55421732F72700809FFB /* help.pdf */; };
E1A5594A172C5EE20088FB61 /* FindSourceFilesOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = E1A55949172C5EE20088FB61 /* FindSourceFilesOperation.m */; };
E1A5594D172CA8D40088FB61 /* ProcessSourceOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = E1A5594C172CA8D40088FB61 /* ProcessSourceOperation.m */; };
E1E90C411735D846005C6D5B /* DDLogLevel.m in Sources */ = {isa = PBXBuildFile; fileRef = E1E90C391735D846005C6D5B /* DDLogLevel.m */; };
E1E90C421735D846005C6D5B /* Notifications.m in Sources */ = {isa = PBXBuildFile; fileRef = E1E90C3C1735D846005C6D5B /* Notifications.m */; };
E1E90C431735D846005C6D5B /* UserDefaults.m in Sources */ = {isa = PBXBuildFile; fileRef = E1E90C3F1735D846005C6D5B /* UserDefaults.m */; };
E1E90C4B1735D8A1005C6D5B /* DDASLLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = E1E90C461735D8A1005C6D5B /* DDASLLogger.m */; };
E1E90C4C1735D8A1005C6D5B /* DDLog.m in Sources */ = {isa = PBXBuildFile; fileRef = E1E90C481735D8A1005C6D5B /* DDLog.m */; };
E1E90C4D1735D8A1005C6D5B /* DDTTYLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = E1E90C4A1735D8A1005C6D5B /* DDTTYLogger.m */; };
E1ED6B71173414E100641628 /* icon_128x128.png in Resources */ = {isa = PBXBuildFile; fileRef = E1ED6B6F173414E100641628 /* icon_128x128.png */; };
E1ED6B72173414E100641628 /* icon_128x128@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = E1ED6B70173414E100641628 /* icon_128x128@2x.png */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
02998CF81369C383006C73DB /* CopyFiles */ = {
E164FE201720F54800263CE3 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
02998CF91369C38A006C73DB /* Growl.framework in CopyFiles */,
4E2D0D03154840BC00475C01 /* help.rtfd in CopyFiles */,
E164FE211720F55600263CE3 /* Growl.framework in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 0;
};
E170C3F9172961E6003DC489 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 12;
files = (
E170C3FB17296205003DC489 /* pbxprojModifier.py in CopyFiles */,
E170C3FC1729620C003DC489 /* mod_pbxproj.py in CopyFiles */,
E170C3FD1729620F003DC489 /* parser.j in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
0218442415D32B5D00A782B8 /* pbxprojModifier.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = pbxprojModifier.py; sourceTree = "<group>"; };
022BCEA61468632000B72910 /* project.pbxproj.sample */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = project.pbxproj.sample; sourceTree = "<group>"; };
024A041113699D6800DCBE4D /* parser.j */ = {isa = PBXFileReference; explicitFileType = sourcecode.javascript; fileEncoding = 4; path = parser.j; sourceTree = "<group>"; };
026F3B6113866B0B00EE5B83 /* xcodecapp-icon-inactive.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "xcodecapp-icon-inactive.png"; sourceTree = "<group>"; };
026F3B6413866E7D00EE5B83 /* xcodecapp-icon-active.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "xcodecapp-icon-active.png"; sourceTree = "<group>"; };
027D9989136969DD00D3DB13 /* XcodeCapp.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XcodeCapp.pch; sourceTree = "<group>"; };
027D99A613696A7000D3DB13 /* XcodeCapp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = XcodeCapp.app; sourceTree = BUILT_PRODUCTS_DIR; };
027FE49B1462D74C00B1AB92 /* TNXCodeCapp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TNXCodeCapp.h; sourceTree = "<group>"; };
027FE49C1462D74C00B1AB92 /* TNXCodeCapp.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TNXCodeCapp.m; sourceTree = "<group>"; };
027FE4B91462F64E00B1AB92 /* xcodecapp-icon-working.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "xcodecapp-icon-working.png"; sourceTree = "<group>"; };
027FE4BB1462F7D600B1AB92 /* FSEventCallback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSEventCallback.h; sourceTree = "<group>"; };
027FE4BC1462F7EB00B1AB92 /* FSEventCallback.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FSEventCallback.m; sourceTree = "<group>"; };
0289AC9314668CAF003CD975 /* XcodeCapp.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = XcodeCapp.icns; sourceTree = "<group>"; };
0294F09215D345A500840547 /* mod_pbxproj.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = mod_pbxproj.py; sourceTree = "<group>"; };
02998CEA1369BF7D006C73DB /* Growl Registration Ticket.growlRegDict */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = "Growl Registration Ticket.growlRegDict"; sourceTree = "<group>"; };
02998CF61369C339006C73DB /* Growl.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Growl.framework; sourceTree = "<group>"; };
02B1C8D616B25C74003C6E82 /* TNErrorDataView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TNErrorDataView.h; sourceTree = "<group>"; };
02B1C8D716B25C74003C6E82 /* TNErrorDataView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TNErrorDataView.m; sourceTree = "<group>"; };
089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
13E42FB307B3F0F600E4EEF1 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = "<absolute>"; };
29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
29B97319FDCFA39411CA2CEA /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/MainMenu.nib; sourceTree = "<group>"; };
29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
4E2D0D00154840B400475C01 /* help.rtfd */ = {isa = PBXFileReference; lastKnownFileType = wrapper.rtfd; path = help.rtfd; sourceTree = "<group>"; };
651DAE1C13B0CAB900ECA3F0 /* PRHEmptyGrowlDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = PRHEmptyGrowlDelegate.h; sourceTree = "<group>"; };
651DAE1D13B0CAB900ECA3F0 /* PRHEmptyGrowlDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PRHEmptyGrowlDelegate.m; sourceTree = "<group>"; };
8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
8E01A8DD0DE51A7C0008BB35 /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = /System/Library/Frameworks/CoreServices.framework; sourceTree = "<absolute>"; };
8E01A91D0DE9EED20008BB35 /* AppController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppController.h; sourceTree = "<group>"; };
8E01A91E0DE9EED20008BB35 /* AppController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = AppController.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
E12AA95F14637828006F55D6 /* macros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.objj.h; path = macros.h; sourceTree = "<group>"; };
E164FDCF1720E77100263CE3 /* XcodeCapp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = XcodeCapp.app; sourceTree = BUILT_PRODUCTS_DIR; };
E164FDD21720E77100263CE3 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
E164FDD51720E77100263CE3 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
E164FDD61720E77100263CE3 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
E164FDD71720E77100263CE3 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
E164FDDA1720E77100263CE3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
E164FDDE1720E77100263CE3 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
E164FDE01720E77100263CE3 /* XcodeCapp-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "XcodeCapp-Prefix.pch"; sourceTree = "<group>"; };
E164FDE81720E77100263CE3 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainMenu.xib; sourceTree = "<group>"; };
E164FDEF1720E7FA00263CE3 /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
E164FDF01720E7FA00263CE3 /* Release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
E164FDF41720EBC100263CE3 /* AppController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = AppController.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
E164FDF61720EBD600263CE3 /* Growl.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Growl.framework; path = XcodeCapp/Growl.framework; sourceTree = SOURCE_ROOT; };
E164FDFB1720EC5B00263CE3 /* XcodeCapp.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = XcodeCapp.m; sourceTree = "<group>"; usesTabs = 0; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
E164FE051720ED4500263CE3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = XcodeCapp/en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
E164FE131720F40400263CE3 /* mod_pbxproj.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = mod_pbxproj.py; sourceTree = "<group>"; };
E164FE141720F40400263CE3 /* pbxprojModifier.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = pbxprojModifier.py; sourceTree = "<group>"; };
E164FE171720F44B00263CE3 /* parser.j */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = parser.j; sourceTree = "<group>"; };
E164FE191720F49A00263CE3 /* Growl Registration Ticket.growlRegDict */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = "Growl Registration Ticket.growlRegDict"; sourceTree = "<group>"; };
E164FE1D1720F50100263CE3 /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; };
E164FE2A172188F300263CE3 /* project.pbxproj */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.pbxproject; lineEnding = 0; name = project.pbxproj; path = Resources/project.pbxproj; sourceTree = "<group>"; };
E164FE2C1721A34900263CE3 /* icon-active.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "icon-active.png"; path = "Resources/icon-active.png"; sourceTree = "<group>"; };
E164FE2D1721A34900263CE3 /* icon-inactive.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "icon-inactive.png"; path = "Resources/icon-inactive.png"; sourceTree = "<group>"; };
E164FE2E1721A34900263CE3 /* icon-working.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "icon-working.png"; path = "Resources/icon-working.png"; sourceTree = "<group>"; };
E170C3FE17298833003DC489 /* icon-active@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "icon-active@2x.png"; path = "Resources/icon-active@2x.png"; sourceTree = "<group>"; };
E170C3FF17298833003DC489 /* icon-error.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "icon-error.png"; path = "Resources/icon-error.png"; sourceTree = "<group>"; };
E170C40017298833003DC489 /* icon-error@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "icon-error@2x.png"; path = "Resources/icon-error@2x.png"; sourceTree = "<group>"; };
E170C40117298833003DC489 /* icon-inactive@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "icon-inactive@2x.png"; path = "Resources/icon-inactive@2x.png"; sourceTree = "<group>"; };
E170C40217298833003DC489 /* icon-working@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "icon-working@2x.png"; path = "Resources/icon-working@2x.png"; sourceTree = "<group>"; };
E17A822C172700B90095CD83 /* XcodeCapp.iconset */ = {isa = PBXFileReference; lastKnownFileType = folder.iconset; name = XcodeCapp.iconset; path = Resources/XcodeCapp.iconset; sourceTree = "<group>"; };
E17B55401732F57700809FFB /* Quartz.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quartz.framework; path = System/Library/Frameworks/Quartz.framework; sourceTree = SDKROOT; };
E17B55421732F72700809FFB /* help.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = help.pdf; path = Resources/help.pdf; sourceTree = "<group>"; };
E1A55949172C5EE20088FB61 /* FindSourceFilesOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = FindSourceFilesOperation.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
E1A5594C172CA8D40088FB61 /* ProcessSourceOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ProcessSourceOperation.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
E1E90C371735D82B005C6D5B /* AppController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppController.h; sourceTree = "<group>"; };
E1E90C381735D846005C6D5B /* DDLogLevel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DDLogLevel.h; sourceTree = "<group>"; };
E1E90C391735D846005C6D5B /* DDLogLevel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DDLogLevel.m; sourceTree = "<group>"; };
E1E90C3A1735D846005C6D5B /* FindSourceFilesOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FindSourceFilesOperation.h; sourceTree = "<group>"; };
E1E90C3B1735D846005C6D5B /* Notifications.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Notifications.h; sourceTree = "<group>"; };
E1E90C3C1735D846005C6D5B /* Notifications.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Notifications.m; sourceTree = "<group>"; };
E1E90C3D1735D846005C6D5B /* ProcessSourceOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProcessSourceOperation.h; sourceTree = "<group>"; };
E1E90C3E1735D846005C6D5B /* UserDefaults.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UserDefaults.h; sourceTree = "<group>"; };
E1E90C3F1735D846005C6D5B /* UserDefaults.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UserDefaults.m; sourceTree = "<group>"; };
E1E90C401735D846005C6D5B /* XcodeCapp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XcodeCapp.h; sourceTree = "<group>"; };
E1E90C451735D8A1005C6D5B /* DDASLLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DDASLLogger.h; sourceTree = "<group>"; };
E1E90C461735D8A1005C6D5B /* DDASLLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DDASLLogger.m; sourceTree = "<group>"; };
E1E90C471735D8A1005C6D5B /* DDLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DDLog.h; sourceTree = "<group>"; };
E1E90C481735D8A1005C6D5B /* DDLog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DDLog.m; sourceTree = "<group>"; };
E1E90C491735D8A1005C6D5B /* DDTTYLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DDTTYLogger.h; sourceTree = "<group>"; };
E1E90C4A1735D8A1005C6D5B /* DDTTYLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DDTTYLogger.m; sourceTree = "<group>"; };
E1ED6B6F173414E100641628 /* icon_128x128.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = icon_128x128.png; path = Resources/icon_128x128.png; sourceTree = "<group>"; };
E1ED6B70173414E100641628 /* icon_128x128@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "icon_128x128@2x.png"; path = "Resources/icon_128x128@2x.png"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
027D99A013696A7000D3DB13 /* Frameworks */ = {
E164FDCC1720E77100263CE3 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
027D99A113696A7000D3DB13 /* Cocoa.framework in Frameworks */,
027D99A213696A7000D3DB13 /* CoreServices.framework in Frameworks */,
02998CF71369C339006C73DB /* Growl.framework in Frameworks */,
E164FDD31720E77100263CE3 /* Cocoa.framework in Frameworks */,
E164FE1F1720F51E00263CE3 /* CoreServices.framework in Frameworks */,
E17B55411732F57700809FFB /* Quartz.framework in Frameworks */,
E164FDF71720EBD600263CE3 /* Growl.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
080E96DDFE201D6D7F000001 /* Classes */ = {
E164FDC61720E77100263CE3 = {
isa = PBXGroup;
children = (
651DAE1C13B0CAB900ECA3F0 /* PRHEmptyGrowlDelegate.h */,
651DAE1D13B0CAB900ECA3F0 /* PRHEmptyGrowlDelegate.m */,
02B1C8D616B25C74003C6E82 /* TNErrorDataView.h */,
02B1C8D716B25C74003C6E82 /* TNErrorDataView.m */,
8E01A91D0DE9EED20008BB35 /* AppController.h */,
8E01A91E0DE9EED20008BB35 /* AppController.m */,
027FE49B1462D74C00B1AB92 /* TNXCodeCapp.h */,
027FE49C1462D74C00B1AB92 /* TNXCodeCapp.m */,
027FE4BB1462F7D600B1AB92 /* FSEventCallback.h */,
027FE4BC1462F7EB00B1AB92 /* FSEventCallback.m */,
E164FDD81720E77100263CE3 /* XcodeCapp */,
E164FDD11720E77100263CE3 /* Frameworks */,
E164FDD01720E77100263CE3 /* Products */,
);
name = Classes;
sourceTree = "<group>";
};
1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = {
E164FDD01720E77100263CE3 /* Products */ = {
isa = PBXGroup;
children = (
8E01A8DD0DE51A7C0008BB35 /* CoreServices.framework */,
1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */,
);
name = "Linked Frameworks";
sourceTree = "<group>";
};
1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = {
isa = PBXGroup;
children = (
29B97324FDCFA39411CA2CEA /* AppKit.framework */,
13E42FB307B3F0F600E4EEF1 /* CoreData.framework */,
29B97325FDCFA39411CA2CEA /* Foundation.framework */,
);
name = "Other Frameworks";
sourceTree = "<group>";
};
19C28FACFE9D520D11CA2CBB /* Products */ = {
isa = PBXGroup;
children = (
027D99A613696A7000D3DB13 /* XcodeCapp.app */,
E164FDCF1720E77100263CE3 /* XcodeCapp.app */,
);
name = Products;
sourceTree = "<group>";
};
29B97314FDCFA39411CA2CEA /* xcodecapp-cocoa */ = {
E164FDD11720E77100263CE3 /* Frameworks */ = {
isa = PBXGroup;
children = (
080E96DDFE201D6D7F000001 /* Classes */,
29B97315FDCFA39411CA2CEA /* Other Sources */,
29B97317FDCFA39411CA2CEA /* Resources */,
29B97323FDCFA39411CA2CEA /* Frameworks */,
19C28FACFE9D520D11CA2CBB /* Products */,
E164FDD21720E77100263CE3 /* Cocoa.framework */,
E164FE1D1720F50100263CE3 /* CoreServices.framework */,
E164FDF61720EBD600263CE3 /* Growl.framework */,
E164FDD41720E77100263CE3 /* Other Frameworks */,
);
name = "xcodecapp-cocoa";
name = Frameworks;
sourceTree = "<group>";
};
29B97315FDCFA39411CA2CEA /* Other Sources */ = {
E164FDD41720E77100263CE3 /* Other Frameworks */ = {
isa = PBXGroup;
children = (
027D9989136969DD00D3DB13 /* XcodeCapp.pch */,
29B97316FDCFA39411CA2CEA /* main.m */,
E12AA95F14637828006F55D6 /* macros.h */,
E164FDD51720E77100263CE3 /* AppKit.framework */,
E164FDD61720E77100263CE3 /* CoreData.framework */,
E164FDD71720E77100263CE3 /* Foundation.framework */,
E17B55401732F57700809FFB /* Quartz.framework */,
);
name = "Other Sources";
name = "Other Frameworks";
sourceTree = "<group>";
};
29B97317FDCFA39411CA2CEA /* Resources */ = {
E164FDD81720E77100263CE3 /* XcodeCapp */ = {
isa = PBXGroup;
children = (
0294F09215D345A500840547 /* mod_pbxproj.py */,
0218442415D32B5D00A782B8 /* pbxprojModifier.py */,
4E2D0D00154840B400475C01 /* help.rtfd */,
022BCEA61468632000B72910 /* project.pbxproj.sample */,
027FE4B91462F64E00B1AB92 /* xcodecapp-icon-working.png */,
026F3B6413866E7D00EE5B83 /* xcodecapp-icon-active.png */,
026F3B6113866B0B00EE5B83 /* xcodecapp-icon-inactive.png */,
024A041113699D6800DCBE4D /* parser.j */,
8D1107310486CEB800E47090 /* Info.plist */,
0289AC9314668CAF003CD975 /* XcodeCapp.icns */,
089C165CFE840E0CC02AAC07 /* InfoPlist.strings */,
29B97318FDCFA39411CA2CEA /* MainMenu.nib */,
02998CEA1369BF7D006C73DB /* Growl Registration Ticket.growlRegDict */,
E164FDF41720EBC100263CE3 /* AppController.m */,
E1E90C371735D82B005C6D5B /* AppController.h */,
E164FDFB1720EC5B00263CE3 /* XcodeCapp.m */,
E1E90C401735D846005C6D5B /* XcodeCapp.h */,
E1E90C3C1735D846005C6D5B /* Notifications.m */,
E1E90C3B1735D846005C6D5B /* Notifications.h */,
E1E90C3F1735D846005C6D5B /* UserDefaults.m */,
E1E90C3E1735D846005C6D5B /* UserDefaults.h */,
E1E90C391735D846005C6D5B /* DDLogLevel.m */,
E1E90C381735D846005C6D5B /* DDLogLevel.h */,
E1A55947172C5EA80088FB61 /* Operations */,
E1E90C441735D8A1005C6D5B /* Lumberjack */,
E164FE121720F3E100263CE3 /* Scripts */,
E164FE071720F25400263CE3 /* Resources */,
E164FDD91720E77100263CE3 /* Supporting Files */,
);
path = XcodeCapp;
sourceTree = "<group>";
};
E164FDD91720E77100263CE3 /* Supporting Files */ = {
isa = PBXGroup;
children = (
E164FDDA1720E77100263CE3 /* Info.plist */,
E164FE041720ED4400263CE3 /* InfoPlist.strings */,
E164FDDE1720E77100263CE3 /* main.m */,
E164FDE01720E77100263CE3 /* XcodeCapp-Prefix.pch */,
E164FDEF1720E7FA00263CE3 /* Debug.xcconfig */,
E164FDF01720E7FA00263CE3 /* Release.xcconfig */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
E164FE071720F25400263CE3 /* Resources */ = {
isa = PBXGroup;
children = (
E1ED6B6F173414E100641628 /* icon_128x128.png */,
E1ED6B70173414E100641628 /* icon_128x128@2x.png */,
E164FDE71720E77100263CE3 /* MainMenu.xib */,
E164FE2D1721A34900263CE3 /* icon-inactive.png */,
E170C40117298833003DC489 /* icon-inactive@2x.png */,
E164FE2E1721A34900263CE3 /* icon-working.png */,
E170C40217298833003DC489 /* icon-working@2x.png */,
E164FE2C1721A34900263CE3 /* icon-active.png */,
E170C3FE17298833003DC489 /* icon-active@2x.png */,
E170C3FF17298833003DC489 /* icon-error.png */,
E170C40017298833003DC489 /* icon-error@2x.png */,
E17A822C172700B90095CD83 /* XcodeCapp.iconset */,
E17B55421732F72700809FFB /* help.pdf */,
E164FE2A172188F300263CE3 /* project.pbxproj */,
E164FE191720F49A00263CE3 /* Growl Registration Ticket.growlRegDict */,
);
name = Resources;
sourceTree = "<group>";
};
29B97323FDCFA39411CA2CEA /* Frameworks */ = {
E164FE121720F3E100263CE3 /* Scripts */ = {
isa = PBXGroup;
children = (
02998CF61369C339006C73DB /* Growl.framework */,
1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */,
1058C7A2FEA54F0111CA2CBB /* Other Frameworks */,
E164FE171720F44B00263CE3 /* parser.j */,
E164FE141720F40400263CE3 /* pbxprojModifier.py */,
E164FE131720F40400263CE3 /* mod_pbxproj.py */,
);
name = Frameworks;
path = Scripts;
sourceTree = "<group>";
};
E1A55947172C5EA80088FB61 /* Operations */ = {
isa = PBXGroup;
children = (
E1A55949172C5EE20088FB61 /* FindSourceFilesOperation.m */,
E1E90C3A1735D846005C6D5B /* FindSourceFilesOperation.h */,
E1A5594C172CA8D40088FB61 /* ProcessSourceOperation.m */,
E1E90C3D1735D846005C6D5B /* ProcessSourceOperation.h */,
);
name = Operations;
sourceTree = "<group>";
};
E1E90C441735D8A1005C6D5B /* Lumberjack */ = {
isa = PBXGroup;
children = (
E1E90C451735D8A1005C6D5B /* DDASLLogger.h */,
E1E90C461735D8A1005C6D5B /* DDASLLogger.m */,
E1E90C471735D8A1005C6D5B /* DDLog.h */,
E1E90C481735D8A1005C6D5B /* DDLog.m */,
E1E90C491735D8A1005C6D5B /* DDTTYLogger.h */,
E1E90C4A1735D8A1005C6D5B /* DDTTYLogger.m */,
);
path = Lumberjack;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
027D999913696A7000D3DB13 /* XcodeCapp */ = {
E164FDCE1720E77100263CE3 /* XcodeCapp */ = {
isa = PBXNativeTarget;
buildConfigurationList = 027D99A313696A7000D3DB13 /* Build configuration list for PBXNativeTarget "XcodeCapp" */;
buildConfigurationList = E164FDEC1720E77100263CE3 /* Build configuration list for PBXNativeTarget "XcodeCapp" */;
buildPhases = (
024699001602C04A00F0AE43 /* ShellScript */,
027D999A13696A7000D3DB13 /* Resources */,
027D999D13696A7000D3DB13 /* Sources */,
02998CF81369C383006C73DB /* CopyFiles */,
027D99A013696A7000D3DB13 /* Frameworks */,
E164FDCB1720E77100263CE3 /* Sources */,
E164FDCC1720E77100263CE3 /* Frameworks */,
E164FDCD1720E77100263CE3 /* Resources */,
E164FE201720F54800263CE3 /* CopyFiles */,
E170C3F9172961E6003DC489 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = XcodeCapp;
productInstallPath = "$(HOME)/Applications";
productName = "xcodecapp-cocoa";
productReference = 027D99A613696A7000D3DB13 /* XcodeCapp.app */;
productName = XcodeCapp;
productReference = E164FDCF1720E77100263CE3 /* XcodeCapp.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
29B97313FDCFA39411CA2CEA /* Project object */ = {
E164FDC71720E77100263CE3 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0440;
LastUpgradeCheck = 0460;
ORGANIZATIONNAME = "Cappuccino Project";
};
buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "XcodeCapp" */;
buildConfigurationList = E164FDCA1720E77100263CE3 /* Build configuration list for PBXProject "XcodeCapp" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 1;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = 29B97314FDCFA39411CA2CEA /* xcodecapp-cocoa */;
mainGroup = E164FDC61720E77100263CE3;
productRefGroup = E164FDD01720E77100263CE3 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
027D999913696A7000D3DB13 /* XcodeCapp */,
E164FDCE1720E77100263CE3 /* XcodeCapp */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
027D999A13696A7000D3DB13 /* Resources */ = {
E164FDCD1720E77100263CE3 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
027D999B13696A7000D3DB13 /* MainMenu.nib in Resources */,
027D999C13696A7000D3DB13 /* InfoPlist.strings in Resources */,
024A041413699DD400DCBE4D /* parser.j in Resources */,
02998CEB1369BF7D006C73DB /* Growl Registration Ticket.growlRegDict in Resources */,
026F3B6213866B0B00EE5B83 /* xcodecapp-icon-inactive.png in Resources */,
026F3B6513866E7D00EE5B83 /* xcodecapp-icon-active.png in Resources */,
027FE4BA1462F64E00B1AB92 /* xcodecapp-icon-working.png in Resources */,
0289AC9414668CAF003CD975 /* XcodeCapp.icns in Resources */,
022BCEA71468632000B72910 /* project.pbxproj.sample in Resources */,
4E2D0D01154840B400475C01 /* help.rtfd in Resources */,
0218442515D32B5D00A782B8 /* pbxprojModifier.py in Resources */,
0294F09315D345A500840547 /* mod_pbxproj.py in Resources */,
E164FDE91720E77100263CE3 /* MainMenu.xib in Resources */,
E164FE061720ED4500263CE3 /* InfoPlist.strings in Resources */,
E10E4CD717298E5F004AB09E /* project.pbxproj in Resources */,
E164FE181720F44B00263CE3 /* parser.j in Resources */,
E164FE1A1720F49A00263CE3 /* Growl Registration Ticket.growlRegDict in Resources */,
E164FE2F1721A34900263CE3 /* icon-active.png in Resources */,
E164FE301721A34900263CE3 /* icon-inactive.png in Resources */,
E164FE311721A34900263CE3 /* icon-working.png in Resources */,
E17A822D172700B90095CD83 /* XcodeCapp.iconset in Resources */,
E170C40317298833003DC489 /* icon-active@2x.png in Resources */,
E170C40417298833003DC489 /* icon-error.png in Resources */,
E170C40517298833003DC489 /* icon-error@2x.png in Resources */,
E170C40617298833003DC489 /* icon-inactive@2x.png in Resources */,
E170C40717298833003DC489 /* icon-working@2x.png in Resources */,
E17B55431732F72700809FFB /* help.pdf in Resources */,
E1ED6B71173414E100641628 /* icon_128x128.png in Resources */,
E1ED6B72173414E100641628 /* icon_128x128@2x.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
024699001602C04A00F0AE43 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"$(SRCROOT)/*.m",
"$(SRCROOT)/*.h",
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /usr/bin/perl;
shellScript = "#!/usr/bin/perl\n\nwhile (<>) {\n s/\\s+$//;\n print \"$_\\n\";\n}";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
027D999D13696A7000D3DB13 /* Sources */ = {
E164FDCB1720E77100263CE3 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
027D999E13696A7000D3DB13 /* main.m in Sources */,
027D999F13696A7000D3DB13 /* AppController.m in Sources */,
651DAE1F13B0CAB900ECA3F0 /* PRHEmptyGrowlDelegate.m in Sources */,
027FE49D1462D74C00B1AB92 /* TNXCodeCapp.m in Sources */,
027FE4BD1462F7EB00B1AB92 /* FSEventCallback.m in Sources */,
02B1C8D816B25C74003C6E82 /* TNErrorDataView.m in Sources */,
E164FDDF1720E77100263CE3 /* main.m in Sources */,
E164FDF51720EBC100263CE3 /* AppController.m in Sources */,
E164FDFF1720EC5B00263CE3 /* XcodeCapp.m in Sources */,
E1A5594A172C5EE20088FB61 /* FindSourceFilesOperation.m in Sources */,
E1A5594D172CA8D40088FB61 /* ProcessSourceOperation.m in Sources */,
E1E90C411735D846005C6D5B /* DDLogLevel.m in Sources */,
E1E90C421735D846005C6D5B /* Notifications.m in Sources */,
E1E90C431735D846005C6D5B /* UserDefaults.m in Sources */,
E1E90C4B1735D8A1005C6D5B /* DDASLLogger.m in Sources */,
E1E90C4C1735D8A1005C6D5B /* DDLog.m in Sources */,
E1E90C4D1735D8A1005C6D5B /* DDTTYLogger.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = {
E164FDE71720E77100263CE3 /* MainMenu.xib */ = {
isa = PBXVariantGroup;
children = (
089C165DFE840E0CC02AAC07 /* English */,
E164FDE81720E77100263CE3 /* en */,
);
name = InfoPlist.strings;
name = MainMenu.xib;
sourceTree = "<group>";
};
29B97318FDCFA39411CA2CEA /* MainMenu.nib */ = {
E164FE041720ED4400263CE3 /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
29B97319FDCFA39411CA2CEA /* English */,
E164FE051720ED4500263CE3 /* en */,
);
name = MainMenu.nib;
name = InfoPlist.strings;
path = ..;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
027D99A413696A7000D3DB13 /* Debug */ = {
E164FDEA1720E77100263CE3 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = E164FDEF1720E7FA00263CE3 /* Debug.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
CLANG_ENABLE_OBJC_ARC = YES;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)\"",
);
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_OBJC_GC = unsupported;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = XcodeCapp.pch;
INFOPLIST_FILE = Info.plist;
INSTALL_PATH = /Applications;
MACOSX_DEPLOYMENT_TARGET = 10.6.8;
PRODUCT_NAME = XcodeCapp;
DSTROOT = /;
};
name = Debug;
};
027D99A513696A7000D3DB13 /* Release */ = {
E164FDEB1720E77100263CE3 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = E164FDF01720E7FA00263CE3 /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
CLANG_ENABLE_OBJC_ARC = YES;
COMBINE_HIDPI_IMAGES = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)\"",
);
GCC_ENABLE_OBJC_GC = unsupported;
GCC_MODEL_TUNING = G5;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = XcodeCapp.pch;
INFOPLIST_FILE = Info.plist;
INSTALL_PATH = /Applications;
MACOSX_DEPLOYMENT_TARGET = 10.6.8;
PRODUCT_NAME = XcodeCapp;
COPY_PHASE_STRIP = NO;
DEPLOYMENT_LOCATION = YES;
DSTROOT = /;
};
name = Release;
};
C01FCF4F08A954540054247B /* Debug */ = {
E164FDED1720E77100263CE3 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
FRAMEWORK_SEARCH_PATHS = "";
GCC_ENABLE_OBJC_GC = required;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1";
GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS = "";
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.5;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/XcodeCapp\"",
);
MACOSX_DEPLOYMENT_TARGET = 10.6.8;
};
name = Debug;
};
C01FCF5008A954540054247B /* Release */ = {
E164FDEE1720E77100263CE3 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
FRAMEWORK_SEARCH_PATHS = "";
GCC_ENABLE_OBJC_GC = required;
GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS = "";
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.5;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/XcodeCapp\"",
);
MACOSX_DEPLOYMENT_TARGET = 10.6.8;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
027D99A313696A7000D3DB13 /* Build configuration list for PBXNativeTarget "XcodeCapp" */ = {
E164FDCA1720E77100263CE3 /* Build configuration list for PBXProject "XcodeCapp" */ = {
isa = XCConfigurationList;
buildConfigurations = (
027D99A413696A7000D3DB13 /* Debug */,
027D99A513696A7000D3DB13 /* Release */,
E164FDEA1720E77100263CE3 /* Debug */,
E164FDEB1720E77100263CE3 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
C01FCF4E08A954540054247B /* Build configuration list for PBXProject "XcodeCapp" */ = {
E164FDEC1720E77100263CE3 /* Build configuration list for PBXNativeTarget "XcodeCapp" */ = {
isa = XCConfigurationList;
buildConfigurations = (
C01FCF4F08A954540054247B /* Debug */,
C01FCF5008A954540054247B /* Release */,
E164FDED1720E77100263CE3 /* Debug */,
E164FDEE1720E77100263CE3 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 29B97313FDCFA39411CA2CEA /* Project object */;
rootObject = E164FDC71720E77100263CE3 /* Project object */;
}
@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEWorkspaceUserSettings_HasAskedToTakeAutomaticSnapshotBeforeSignificantChanges</key>
<true/>
<key>IDEWorkspaceUserSettings_SnapshotAutomaticallyBeforeSignificantChanges</key>
<true/>
</dict>
</plist>
@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Bucket
type = "1"
version = "1.0">
</Bucket>
@@ -1,42 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>XcodeCapp Debug.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
<key>XcodeCapp Release.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>1</integer>
</dict>
<key>XcodeCapp.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>027D998A13696A0700D3DB13</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>027D999913696A7000D3DB13</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>8D1107260486CEB800E47090</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>
+50
View File
@@ -0,0 +1,50 @@
/*
* This file is a part of program XcodeCapp
* Copyright (C) 2011 Antoine Mercadal (<primalmotion@archipelproject.org>)
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#import <Cocoa/Cocoa.h>
#import <CoreServices/CoreServices.h>
#import <Quartz/Quartz.h>
@class XcodeCapp;
@interface AppController : NSObject <NSApplicationDelegate, NSMenuDelegate>
@property (strong) IBOutlet NSMenu *statusMenu;
@property (assign) IBOutlet NSMenuItem *menuItemHistory;
@property (assign) IBOutlet NSMenuItem *menuItemOpenInXcode;
@property (assign) IBOutlet NSMenuItem *menuItemListen;
@property (strong) IBOutlet NSPanel *aboutWindow;
@property (strong) IBOutlet NSWindow *preferencesWindow;
@property (strong) IBOutlet NSWindow *helpWindow;
@property (assign) IBOutlet PDFView *helpView;
@property (strong) IBOutlet NSUserDefaultsController *preferencesController;
@property (strong) IBOutlet XcodeCapp *xcc;
+ (AppController *)sharedAppController;
- (IBAction)loadProject:(id)aSender;
- (IBAction)openHelp:(id)aSender;
- (IBAction)openAbout:(id)aSender;
- (void)loadProjectAtPath:(NSString *)path;
@end
+435
View File
@@ -0,0 +1,435 @@
/*
* This file is a part of program XcodeCapp
* Copyright (C) 2011 Antoine Mercadal (<primalmotion@archipelproject.org>)
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#import <AppKit/NSApplication.h>
#import "AppController.h"
#import "Notifications.h"
#import "XcodeCapp.h"
#import "UserDefaults.h"
AppController *SharedAppControllerInstance = nil;
@interface AppController ()
@property (nonatomic) NSImage *iconActive;
@property (nonatomic) NSImage *iconInactive;
@property (nonatomic) NSImage *iconWorking;
@property (nonatomic) NSImage *iconError;
@property (nonatomic) NSMenu *recentMenu;
@property NSStatusItem *statusItem;
@end
@implementation AppController
+ (AppController *)sharedAppController
{
return SharedAppControllerInstance;
}
#pragma mark - Initialization
- (void)awakeFromNib
{
SharedAppControllerInstance = self;
[self registerDefaultPreferences];
[self initLogging];
DDLogVerbose(@"\n******************************\n** XcodeCapp started **\n******************************\n");
self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
self.statusItem.menu = self.statusMenu;
self.statusItem.image = self.iconInactive;
self.statusItem.highlightMode = YES;
self.statusItem.length = self.iconInactive.size.width + 12; // Add some space around the icon
self.statusMenu.delegate = self;
self.aboutWindow.backgroundColor = [NSColor whiteColor];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
double firstLaunchVersion = [defaults doubleForKey:kDefaultFirstLaunchVersion];
// Note: the scanner will only get the major.minor version numbers, which is what we want.
NSScanner *scanner = [NSScanner scannerWithString:[self bundleVersion]];
double appVersion = 0.0;
[scanner scanDouble:&appVersion];
if ([defaults boolForKey:kDefaultFirstLaunch] || appVersion > firstLaunchVersion)
{
[defaults setBool:NO forKey:kDefaultFirstLaunch];
[defaults setDouble:appVersion forKey:kDefaultFirstLaunchVersion];
[self openHelp:self];
}
NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
[defaultCenter addObserver:self selector:@selector(batchDidStart:) name:XCCBatchDidStartNotification object:nil];
[defaultCenter addObserver:self selector:@selector(batchDidEnd:) name:XCCBatchDidEndNotification object:nil];
[defaultCenter addObserver:self selector:@selector(projectDidFinishLoading:) name:XCCProjectDidFinishLoadingNotification object:nil];
[self pruneProjectHistory];
[self updateHistoryMenu];
}
- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
if (![self.xcc executablesAreAccessible])
{
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
NSRunAlertPanel(@"Executables are missing.", @"Please make sure that python, jsc, objj and nib2cib (or symlinks to them) are somewhere within one of these directories:\n\n/bin\n/usr/bin\n/usr/local/bin\n/usr/local/narwhal/bin\n~/bin\n\nThen launch XcodeCapp again.", @"Quit", nil, nil);
[[NSApplication sharedApplication] terminate:self];
return;
}
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if (![defaults boolForKey:kDefaultXCCReopenLastProject])
return;
NSString *lastOpenedPath = [defaults objectForKey:kDefaultLastOpenedPath];
if (lastOpenedPath)
{
if ([[NSFileManager defaultManager] fileExistsAtPath:lastOpenedPath])
[self loadProjectAtPath:lastOpenedPath];
else
[defaults removeObjectForKey:kDefaultLastOpenedPath];
}
}
/*!
Register default values for preferences
*/
- (void)registerDefaultPreferences
{
NSDictionary *appDefaults = @{
kDefaultLastEventId: [NSNumber numberWithUnsignedLongLong:kFSEventStreamEventIdSinceNow],
kDefaultFirstLaunch: @YES,
kDefaultFirstLaunchVersion: @2.0,
kDefaultXCCAPIMode: [NSNumber numberWithInt:kXCCAPIModeAuto],
kDefaultXCCReactToInodeMod: @YES,
kDefaultXCCReopenLastProject: @YES,
kDefaultXCCAutoOpenErrorsPanelOnWarnings: @YES,
kDefaultXCCAutoOpenErrorsPanelOnErrors: @YES,
kDefaultXCCProjectHistory: [NSArray new],
kDefaultMaxRecentProjects: @20,
kDefaultLogLevel: [NSNumber numberWithInt:LOG_LEVEL_WARN],
kDefaultAutoOpenXcodeProject: @YES,
kDefaultShowProcessingNotices: @YES
};
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults registerDefaults:appDefaults];
[defaults synchronize];
[defaults addObserver:self
forKeyPath:kDefaultMaxRecentProjects
options:NSKeyValueObservingOptionNew
context:NULL];
}
- (void)initLogging
{
#if DEBUG
[DDLog addLogger:[DDTTYLogger sharedInstance]];
[[DDTTYLogger sharedInstance] setColorsEnabled:YES];
[DDLogLevel setLogLevel:LOG_LEVEL_VERBOSE];
#else
[DDLog addLogger:[DDASLLogger sharedInstance]];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[DDLogLevel setLogLevel:(int)[defaults integerForKey:kDefaultLogLevel]];
#endif
}
- (void)pruneProjectHistory
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *projectHistory = [[defaults arrayForKey:kDefaultXCCProjectHistory] mutableCopy];
NSFileManager *fm = [NSFileManager new];
for (NSInteger i = projectHistory.count - 1; i >= 0; --i)
{
if (![fm fileExistsAtPath:projectHistory[i]])
[projectHistory removeObjectAtIndex:i];
}
NSInteger maxProjects = [defaults integerForKey:kDefaultMaxRecentProjects];
if (projectHistory.count > maxProjects)
[projectHistory removeObjectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(maxProjects, projectHistory.count - maxProjects)]];
[defaults setObject:projectHistory forKey:kDefaultXCCProjectHistory];
}
#pragma mark - Properties
- (NSImage *)iconActive
{
if (!_iconActive)
_iconActive = [NSImage imageNamed:@"icon-active"];
return _iconActive;
}
- (NSImage *)iconInactive
{
if (!_iconInactive)
_iconInactive = [NSImage imageNamed:@"icon-inactive"];
return _iconInactive;
}
- (NSImage *)iconWorking
{
if (!_iconWorking)
_iconWorking = [NSImage imageNamed:@"icon-working"];
return _iconWorking;
}
- (NSImage *)iconError
{
if (!_iconError)
_iconError = [NSImage imageNamed:@"icon-error"];
return _iconError;
}
- (NSMenu *)recentMenu
{
if (!_recentMenu)
{
_recentMenu = [NSMenu new];
_recentMenu.delegate = self;
self.menuItemHistory.submenu = _recentMenu;
}
return _recentMenu;
}
#pragma mark - Notification handlers
- (void)batchDidStart:(NSNotification *)note
{
DDLogVerbose(@"Batch start");
self.statusItem.image = self.iconWorking;
}
- (void)batchDidEnd:(NSNotification *)note
{
DDLogVerbose(@"Batch end");
if (!self.xcc.isLoadingProject)
self.statusItem.image = self.xcc.hasErrors ? self.iconError : self.iconActive;
}
- (void)projectDidFinishLoading:(NSNotification *)note
{
self.statusItem.image = self.xcc.hasErrors ? self.iconError : self.iconActive;
self.menuItemListen.title = [NSString stringWithFormat:@"Close “%@”", self.xcc.projectPath.lastPathComponent];
self.menuItemListen.action = @selector(closeProject:);
}
// Watch changes to the max recent projects preference
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:kDefaultMaxRecentProjects])
[self pruneProjectHistory];
}
#pragma mark - Actions
- (IBAction)loadProject:(id)aSender
{
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
openPanel.title = @"Choose Cappuccino Project";
openPanel.canChooseDirectories = YES;
openPanel.canCreateDirectories = YES;
openPanel.canChooseFiles = NO;
if ([openPanel runModal] != NSFileHandlingPanelOKButton)
return;
NSString *projectPath = [[openPanel.URLs[0] path] stringByStandardizingPath];
[self loadProjectAtPath:projectPath];
}
- (void)closeProject:(id)aSender
{
[self.xcc stop];
self.statusItem.image = self.iconInactive;
self.menuItemListen.title = @"Open Project…";
self.menuItemListen.action = @selector(loadProject:);
[[NSUserDefaults standardUserDefaults] removeObjectForKey:kDefaultLastOpenedPath];
}
- (void)switchToProject:(NSMenuItem *)aSender
{
[self loadProjectAtPath:aSender.representedObject];
}
- (void)clearProjectHistory:(id)aSender
{
[[NSUserDefaults standardUserDefaults] setObject:[NSArray array] forKey:kDefaultXCCProjectHistory];
[self updateHistoryMenu];
}
- (IBAction)openHelp:(id)aSender
{
if (!self.helpView.document)
{
NSURL *helpURL = [[NSBundle mainBundle] URLForResource:@"help" withExtension:@"pdf"];
PDFDocument *help = [[PDFDocument alloc] initWithURL:helpURL];
self.helpView.document = help;
}
[self openWindow:self.helpWindow];
}
- (IBAction)openAbout:(id)aSender
{
[self openWindow:self.aboutWindow];
}
- (IBAction)openPreferences:(id)aSender
{
[self openWindow:self.preferencesWindow];
}
#pragma mark - Delegates
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)app
{
[self.xcc stop];
return NSTerminateNow;
}
- (BOOL)validateMenuItem:(NSMenuItem *)aMenuItem
{
NSMenu *menu = aMenuItem.menu;
if (aMenuItem == self.menuItemListen)
{
return !!self.xcc.projectPath;
}
else if (menu == self.recentMenu)
{
// Disable recent items if they don't exist or are not directories,
// but enable the Clear History item, which is last in the menu.
if ([menu indexOfItem:aMenuItem] == menu.itemArray.count - 1)
return YES;
NSFileManager *fm = [NSFileManager defaultManager];
BOOL exists, isDirectory;
exists = [fm fileExistsAtPath:aMenuItem.representedObject isDirectory:&isDirectory];
return exists && isDirectory;
}
return YES;
}
#pragma mark - Bindings
- (NSString *)bundleVersion
{
return [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
}
#pragma mark - Private Helpers
- (void)loadProjectAtPath:(NSString *)path
{
if ([self.xcc.projectPath isEqualToString:path])
return;
[self closeProject:self];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *projectHistory = [[defaults arrayForKey:kDefaultXCCProjectHistory] mutableCopy];
if ([projectHistory containsObject:path])
[projectHistory removeObject:path];
// The path may no longer be there, validate it
NSFileManager *fm = [NSFileManager defaultManager];
BOOL exists, isDirectory;
exists = [fm fileExistsAtPath:path isDirectory:&isDirectory];
if (exists && isDirectory)
{
[projectHistory insertObject:path atIndex:0];
}
else
{
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
NSRunAlertPanel(@"Project not found.", @"%@ %@", nil, nil, nil, path, !exists ? @"no longer exists." : @"is not a directory.");
}
[defaults setObject:projectHistory forKey:kDefaultXCCProjectHistory];
[self pruneProjectHistory];
[self updateHistoryMenu];
if (exists && isDirectory)
[self.xcc loadProjectAtPath:path];
}
- (void)openWindow:(NSWindow *)aWindow
{
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
[aWindow makeKeyAndOrderFront:nil];
}
- (void)updateHistoryMenu
{
[self.recentMenu removeAllItems];
NSArray *projectHistory = [[NSUserDefaults standardUserDefaults] arrayForKey:kDefaultXCCProjectHistory];
for (NSString *path in projectHistory)
{
NSMenuItem *item = [self.recentMenu addItemWithTitle:path.lastPathComponent action:@selector(switchToProject:) keyEquivalent:@""];
[item setEnabled:YES];
item.representedObject = path;
}
[self.recentMenu addItem:[NSMenuItem separatorItem]];
[self.recentMenu addItemWithTitle:@"Clear history" action:@selector(clearProjectHistory:) keyEquivalent:@""];
self.menuItemHistory.enabled = [projectHistory count] > 0;
}
@end
+17
View File
@@ -0,0 +1,17 @@
//
// DDLogLevel.h
// XcodeCapp
//
// Created by Aparajita on 4/29/13.
// Copyright (c) 2013 Cappuccino Project. All rights reserved.
//
#import <Foundation/Foundation.h>
extern int ddLogLevel;
@interface DDLogLevel : NSObject
+ (void)setLogLevel:(int)logLevel;
@end
+27
View File
@@ -0,0 +1,27 @@
//
// DDLog.m
// XcodeCapp
//
// Created by Aparajita on 4/29/13.
// Copyright (c) 2013 Cappuccino Project. All rights reserved.
//
#import "DDLogLevel.h"
int ddLogLevel = LOG_LEVEL_OFF;
@implementation DDLogLevel
+ (int)ddLogLevel
{
return ddLogLevel;
}
+ (void)setLogLevel:(int)logLevel
{
ddLogLevel = logLevel;
}
@end
+41
View File
@@ -0,0 +1,41 @@
//
// Debug.xcconfig
// XcodeCapp
//
// Created by Aparajita on 4/18/13.
//
//
//:configuration = Debug
ARCHS = $(ARCHS_STANDARD_64_BIT)
SDKROOT = macosx
ONLY_ACTIVE_ARCH = YES
DEBUG_INFORMATION_FORMAT = dwarf-with-dsym
COMBINE_HIDPI_IMAGES = YES
INSTALL_PATH = $(LOCAL_APPS_DIR)
MACOSX_DEPLOYMENT_TARGET = 10.6.8
COPY_PHASE_STRIP = NO
INFOPLIST_FILE = XcodeCapp/Info.plist
PRODUCT_NAME = XcodeCapp
ALWAYS_SEARCH_USER_PATHS = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "$(SRCROOT)/XcodeCapp"
GCC_OPTIMIZATION_LEVEL = 0
CLANG_ENABLE_OBJC_ARC = YES
GCC_PRECOMPILE_PREFIX_HEADER = YES
GCC_PREFIX_HEADER = XcodeCapp/XcodeCapp-Prefix.pch
GCC_PREPROCESSOR_DEFINITIONS = DEBUG=1
GCC_WARN_ABOUT_RETURN_TYPE = YES
GCC_WARN_UNUSED_VARIABLE = YES
WRAPPER_EXTENSION = app
GCC_C_LANGUAGE_STANDARD = gnu99
CLANG_CXX_LANGUAGE_STANDARD = gnu++0x
CLANG_CXX_LIBRARY = libc++
CLANG_WARN_EMPTY_BODY = YES
CLANG_WARN_CONSTANT_CONVERSION = YES
GCC_WARN_64_TO_32_BIT_CONVERSION = YES
CLANG_WARN_ENUM_CONVERSION = YES
CLANG_WARN_INT_CONVERSION = YES
GCC_WARN_ABOUT_RETURN_TYPE = YES
GCC_WARN_UNINITIALIZED_AUTOS = YES
GCC_WARN_UNUSED_VARIABLE = YES
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
@@ -0,0 +1,21 @@
//
// FindSourceFilesOperation.h
// XcodeCapp
//
// Created by Aparajita on 4/27/13.
// Copyright (c) 2013 Cappuccino Project. All rights reserved.
//
#import <Foundation/Foundation.h>
@class XcodeCapp;
extern NSString * const XCCNeedSourceToProjectPathMappingNotification;
@interface FindSourceFilesOperation : NSOperation
- (id)initWithXCC:(XcodeCapp *)xcc projectId:(NSNumber *)projectId path:(NSString *)path;
@end
@@ -0,0 +1,172 @@
//
// FindSourceFilesOperation.m
// XcodeCapp
//
// Created by Aparajita on 4/27/13.
// Copyright (c) 2013 Cappuccino Project. All rights reserved.
//
#import "FindSourceFilesOperation.h"
#import "ProcessSourceOperation.h"
#import "XcodeCapp.h"
NSString * const XCCNeedSourceToProjectPathMappingNotification = @"XCCNeedSourceToProjectPathMappingNotification";
@interface FindSourceFilesOperation ()
@property XcodeCapp *xcc;
@property NSNumber *projectId;
@property NSString *projectPathToSearch;
@property NSString *projectPath;
@end
@implementation FindSourceFilesOperation
- (id)initWithXCC:(XcodeCapp *)xcc projectId:(NSNumber *)projectId path:(NSString *)path
{
self = [super init];
if (self)
{
self.xcc = xcc;
self.projectId = projectId;
self.projectPathToSearch = path;
self.projectPath = xcc.projectPath;
}
return self;
}
- (void)main
{
[self findSourceFilesAtProjectPath:self.projectPathToSearch];
}
- (void)findSourceFilesAtProjectPath:(NSString *)aProjectPath
{
if (self.isCancelled)
return;
DDLogVerbose(@"-->findSourceFiles: %@", aProjectPath);
NSError *error = NULL;
NSString *projectPath = [self.projectPath stringByAppendingPathComponent:aProjectPath];
NSFileManager *fm = [NSFileManager defaultManager];
NSArray *urls = [fm contentsOfDirectoryAtURL:[NSURL fileURLWithPath:projectPath.stringByResolvingSymlinksInPath]
includingPropertiesForKeys:@[NSURLIsDirectoryKey, NSURLIsSymbolicLinkKey]
options:NSDirectoryEnumerationSkipsHiddenFiles | NSDirectoryEnumerationSkipsPackageDescendants | NSDirectoryEnumerationSkipsSubdirectoryDescendants
error:&error];
if (!urls)
return;
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
for (NSURL *url in urls)
{
if (self.isCancelled)
return;
NSString *filename = url.lastPathComponent;
NSString *projectRelativePath = [aProjectPath stringByAppendingPathComponent:filename];
NSString *realPath = url.path;
NSURL *resolvedURL = url;
NSNumber *isDirectory, *isSymlink;
[url getResourceValue:&isSymlink forKey:NSURLIsSymbolicLinkKey error:nil];
if (isSymlink.boolValue == YES)
{
resolvedURL = [url URLByResolvingSymlinksInPath];
if ([resolvedURL checkResourceIsReachableAndReturnError:nil])
{
filename = resolvedURL.lastPathComponent;
realPath = resolvedURL.path;
}
else
continue;
}
[resolvedURL getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:nil];
if (isDirectory.boolValue == YES)
{
if ([self.xcc shouldIgnoreDirectoryNamed:filename])
{
DDLogVerbose(@"ignored symlinked directory: %@", projectRelativePath);
continue;
}
// If the resolved path is not within the project directory and is not ignored, add a mapping to it
// so we can map the resolved path back to the project directory later.
if (isSymlink.boolValue == YES)
{
NSString *fullProjectPath = [self.projectPath stringByAppendingPathComponent:projectRelativePath];
if (![realPath hasPrefix:fullProjectPath] && ![self.xcc pathMatchesIgnoredPaths:fullProjectPath])
{
DDLogVerbose(@"symlinked directory: %@ -> %@", projectRelativePath, realPath);
NSDictionary *info =
@{
@"projectId":self.projectId,
@"sourcePath":realPath,
@"projectPath":fullProjectPath
};
if (self.isCancelled)
return;
[center postNotificationName:XCCNeedSourceToProjectPathMappingNotification object:self userInfo:info];
}
else
DDLogVerbose(@"ignored symlinked directory: %@", projectRelativePath);
}
[self findSourceFilesAtProjectPath:projectRelativePath];
continue;
}
if (self.isCancelled)
return;
if ([self.xcc pathMatchesIgnoredPaths:realPath])
continue;
NSString *projectSourcePath = [self.projectPath stringByAppendingPathComponent:projectRelativePath];
if ([self.xcc isObjjFile:filename] || [self.xcc isXibFile:filename])
{
NSString *processedPath;
if ([self.xcc isObjjFile:filename])
processedPath = [[self.xcc shadowBasePathForProjectSourcePath:projectSourcePath] stringByAppendingPathExtension:@"h"];
else
processedPath = [projectSourcePath.stringByDeletingPathExtension stringByAppendingPathExtension:@"cib"];
if (![fm fileExistsAtPath:processedPath])
[self createProcessingOperationForProjectSourcePath:projectSourcePath];
}
}
DDLogVerbose(@"<--findSourceFiles: %@", aProjectPath);
}
- (void)createProcessingOperationForProjectSourcePath:(NSString *)projectSourcePath
{
if (self.isCancelled)
return;
ProcessSourceOperation *op = [[ProcessSourceOperation alloc] initWithXCC:self.xcc
projectId:self.projectId
sourcePath:projectSourcePath];
[[NSOperationQueue currentQueue] addOperation:op];
}
@end
Binary file not shown.
@@ -0,0 +1,5 @@
#include <Growl/GrowlDefines.h>
#ifdef __OBJC__
# include <Growl/GrowlApplicationBridge.h>
#endif
@@ -18,14 +18,11 @@
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#import "GrowlDefines.h"
#import <Growl/GrowlDefines.h>
//Forward declarations
@protocol GrowlApplicationBridgeDelegate;
//Internal notification when the user chooses not to install (to avoid continuing to cache notifications awaiting installation)
#define GROWL_USER_CHOSE_NOT_TO_INSTALL_NOTIFICATION @"User chose not to install"
//------------------------------------------------------------------------------
#pragma mark -
@@ -45,9 +42,9 @@
* @method isGrowlInstalled
* @abstract Detects whether Growl is installed.
* @discussion Determines if the Growl prefpane and its helper app are installed.
* @result Returns YES if Growl is installed, NO otherwise.
* @result this method will forever return YES.
*/
+ (BOOL) isGrowlInstalled;
+ (BOOL) isGrowlInstalled __attribute__((deprecated));
/*!
* @method isGrowlRunning
@@ -57,6 +54,34 @@
*/
+ (BOOL) isGrowlRunning;
/*!
* @method isMistEnabled
* @abstract Gives the caller a fairly good indication of whether or not built-in notifications(Mist) will be used.
* @discussion since this call makes use of isGrowlRunning it is entirely possible for this value to change between call and
* executing a notification dispatch
* @result Returns YES if Growl isn't reachable and the developer has not opted-out of
* Mist and the user hasn't set the global mist enable key to false.
*/
+ (BOOL)isMistEnabled;
/*!
* @method setShouldUseBuiltInNotifications
* @abstract opt-out mechanism for the mist notification style in the event growl can't be reached.
* @discussion if growl is unavailable due to not being installed or as a result of being turned off then
* this option can enable/disable a built-in fire and forget display style
* @param should Specifies whether or not the developer wants to opt-in (default) or opt out
* of the built-in Mist style in the event Growl is unreachable.
*/
+ (void)setShouldUseBuiltInNotifications:(BOOL)should;
/*!
* @method shouldUseBuiltInNotifications
* @abstract returns the current opt-in state of the framework's use of the Mist display style.
* @result Returns NO if the developer opt-ed out of Mist, the default value is YES.
*/
+ (BOOL)shouldUseBuiltInNotifications;
#pragma mark -
/*!
@@ -87,7 +112,7 @@
*
* @param inDelegate The delegate for the GrowlApplicationBridge. It must conform to the GrowlApplicationBridgeDelegate protocol.
*/
+ (void) setGrowlDelegate:(NSObject<GrowlApplicationBridgeDelegate> *)inDelegate;
+ (void) setGrowlDelegate:(id<GrowlApplicationBridgeDelegate>)inDelegate;
/*!
* @method growlDelegate
@@ -95,7 +120,7 @@
* @discussion See setGrowlDelegate: for details.
* @result The Growl delegate.
*/
+ (NSObject<GrowlApplicationBridgeDelegate> *) growlDelegate;
+ (id<GrowlApplicationBridgeDelegate>) growlDelegate;
#pragma mark -
@@ -235,6 +260,7 @@
* Growl when next it is ready; <code>NO</code> if not.
*/
+ (void) setWillRegisterWhenGrowlIsReady:(BOOL)flag;
/*! @method willRegisterWhenGrowlIsReady
* @abstract Reports whether GrowlApplicationBridge will register with Growl
* when Growl next launches.
@@ -323,7 +349,7 @@
* Key Value
* --- -----
* <code>GROWL_APP_NAME</code> <code>CFBundleExecutableName</code>
* <code>GROWL_APP_ICON</code> The icon of the application.
* <code>GROWL_APP_ICON_DATA</code> The data of the icon of the application.
* <code>GROWL_APP_LOCATION</code> The location of the application.
* <code>GROWL_NOTIFICATIONS_DEFAULT</code> <code>GROWL_NOTIFICATIONS_ALL</code>
*
@@ -336,6 +362,7 @@
* copy of <code>regDict</code>.
*/
+ (NSDictionary *) registrationDictionaryByFillingInDictionary:(NSDictionary *)regDict;
/*! @method registrationDictionaryByFillingInDictionary:restrictToKeys:
* @abstract Tries to fill in missing keys in a registration dictionary.
* @discussion This method examines the passed-in dictionary for missing keys,
@@ -344,7 +371,7 @@
* Key Value
* --- -----
* <code>GROWL_APP_NAME</code> <code>CFBundleExecutableName</code>
* <code>GROWL_APP_ICON</code> The icon of the application.
* <code>GROWL_APP_ICON_DATA</code> The data of the icon of the application.
* <code>GROWL_APP_LOCATION</code> The location of the application.
* <code>GROWL_NOTIFICATIONS_DEFAULT</code> <code>GROWL_NOTIFICATIONS_ALL</code>
*
@@ -368,13 +395,39 @@
* the keys that it will look for are:
*
* \li <code>GROWL_APP_NAME</code>
* \li <code>GROWL_APP_ICON</code>
* \li <code>GROWL_APP_ICON_DATA</code>
*
* @since Growl.framework 1.1
*/
+ (NSDictionary *) notificationDictionaryByFillingInDictionary:(NSDictionary *)regDict;
+ (NSDictionary *) frameworkInfoDictionary;
#pragma mark -
/*!
*@method growlURLSchemeAvailable
*@abstract Lets the app know whether growl:// is registered on the system, used for certain methods below this
*@return Returns whether growl:// is registered on the system
*@discussion Methods such as openGrowlPreferences rely on the growl:// URL scheme to function
* Further, this method can provide a check on whether Growl is installed,
* however, the framework will not be relying on this method for choosing when/how to notify,
* and it is not recommended that the app rely on it for other than whether to use growl:// methods
*@since Growl.framework 1.4
*/
+ (BOOL) isGrowlURLSchemeAvailable;
/*!
* @method openGrowlPreferences:
* @abstract Open Growl preferences, optionally to this app's settings, growl:// method
* @param showApp Whether to show the application's settings, otherwise just opens to the last position
* @return Return's whether opening the URL was succesfull or not.
* @discussion Will launch if Growl is installed, but not running, and open the preferences window
* Uses growl:// URL scheme
* @since Growl.framework 1.4
*/
+ (BOOL) openGrowlPreferences:(BOOL)showApp;
@end
//------------------------------------------------------------------------------
@@ -383,27 +436,15 @@
/*!
* @protocol GrowlApplicationBridgeDelegate
* @abstract Required protocol for the Growl delegate.
* @discussion The methods in this protocol are required and are called
* @discussion The methods in this protocol are optional and are called
* automatically as needed by GrowlApplicationBridge. See
* <code>+[GrowlApplicationBridge setGrowlDelegate:]</code>.
* See also <code>GrowlApplicationBridgeDelegate_InformalProtocol</code>.
*/
@protocol GrowlApplicationBridgeDelegate
@protocol GrowlApplicationBridgeDelegate <NSObject>
// -registrationDictionaryForGrowl has moved to the informal protocol as of 0.7.
@end
//------------------------------------------------------------------------------
#pragma mark -
/*!
* @category NSObject(GrowlApplicationBridgeDelegate_InformalProtocol)
* @abstract Methods which may be optionally implemented by the GrowlDelegate.
* @discussion The methods in this informal protocol will only be called if implemented by the delegate.
*/
@interface NSObject (GrowlApplicationBridgeDelegate_InformalProtocol)
@optional
/*!
* @method registrationDictionaryForGrowl
@@ -510,66 +551,17 @@
*/
- (void) growlNotificationTimedOut:(id)clickContext;
/*!
* @method hasNetworkClientEntitlement
* @abstract Used only in sandboxed situations since we don't know whether the app has com.apple.security.network.client entitlement
* @discussion GrowlDelegate calls to find out if we have the com.apple.security.network.client entitlement,
* since we can't find this out without hitting the sandbox. We only call it if we detect that the application is sandboxed.
*/
- (BOOL) hasNetworkClientEntitlement;
@end
#pragma mark -
/*!
* @category NSObject(GrowlApplicationBridgeDelegate_Installation_InformalProtocol)
* @abstract Methods which may be optionally implemented by the Growl delegate when used with Growl-WithInstaller.framework.
* @discussion The methods in this informal protocol will only be called if
* implemented by the delegate. They allow greater control of the information
* presented to the user when installing or upgrading Growl from within your
* application when using Growl-WithInstaller.framework.
*/
@interface NSObject (GrowlApplicationBridgeDelegate_Installation_InformalProtocol)
/*!
* @method growlInstallationWindowTitle
* @abstract Return the title of the installation window.
* @discussion If not implemented, Growl will use a default, localized title.
* @result An NSString object to use as the title.
*/
- (NSString *)growlInstallationWindowTitle;
/*!
* @method growlUpdateWindowTitle
* @abstract Return the title of the upgrade window.
* @discussion If not implemented, Growl will use a default, localized title.
* @result An NSString object to use as the title.
*/
- (NSString *)growlUpdateWindowTitle;
/*!
* @method growlInstallationInformation
* @abstract Return the information to display when installing.
* @discussion This information may be as long or short as desired (the window
* will be sized to fit it). It will be displayed to the user as an
* explanation of what Growl is and what it can do in your application. It
* should probably note that no download is required to install.
*
* If this is not implemented, Growl will use a default, localized explanation.
* @result An NSAttributedString object to display.
*/
- (NSAttributedString *)growlInstallationInformation;
/*!
* @method growlUpdateInformation
* @abstract Return the information to display when upgrading.
* @discussion This information may be as long or short as desired (the window
* will be sized to fit it). It will be displayed to the user as an
* explanation that an updated version of Growl is included in your
* application and no download is required.
*
* If this is not implemented, Growl will use a default, localized explanation.
* @result An NSAttributedString object to display.
*/
- (NSAttributedString *)growlUpdateInformation;
@end
//private
@interface GrowlApplicationBridge (GrowlInstallationPrompt_private)
+ (void) _userChoseNotToInstallGrowl;
@end
#endif /* __GrowlApplicationBridge_h__ */
@@ -7,10 +7,8 @@
#ifdef __OBJC__
#define XSTR(x) (@x)
#define STRING_TYPE NSString *
#else
#define XSTR CFSTR
#define STRING_TYPE CFStringRef
#endif
/*! @header GrowlDefines.h
@@ -56,7 +54,7 @@
* This key is optional.
*/
#define GROWL_APP_ID XSTR("ApplicationId")
/*! @defined GROWL_APP_ICON
/*! @defined GROWL_APP_ICON_DATA
* @abstract The image data for your application's icon.
* @discussion Image data representing your application's icon. This may be
* superimposed on a notification icon as a badge, used as the notification
@@ -66,7 +64,7 @@
*
* Optional. Not supported by all display plugins.
*/
#define GROWL_APP_ICON XSTR("ApplicationIcon")
#define GROWL_APP_ICON_DATA XSTR("ApplicationIcon")
/*! @defined GROWL_NOTIFICATIONS_DEFAULT
* @abstract The array of notifications to turn on by default.
* @discussion These are the names of the notifications that should be enabled
@@ -101,6 +99,14 @@
* This key is optional.
*/
#define GROWL_NOTIFICATIONS_DESCRIPTIONS XSTR("NotificationDescriptions")
/*! @defined GROWL_NOTIFICATIONS_ICONS
* @abstract A dictionary of icons for each notification
* @discussion This is an NSDictionary whose keys are GROWL_NOTIFICATION_NAME strings and whose objects are
* icons for each notification, for GNTP spec
*
* This key is optional.
*/
#define GROWL_NOTIFICATIONS_ICONS XSTR("NotificationIcons")
/*! @defined GROWL_TICKET_VERSION
* @abstract The version of your registration ticket.
@@ -144,20 +150,20 @@
*/
#define GROWL_NOTIFICATION_DESCRIPTION XSTR("NotificationDescription")
/*! @defined GROWL_NOTIFICATION_ICON
* @discussion Image data for the notification icon. Must be in a format
* @discussion Image data for the notification icon. Image data must be in a format
* supported by NSImage, such as TIFF, PNG, GIF, JPEG, BMP, PICT, or PDF.
*
* Optional. Not supported by all display plugins.
*/
#define GROWL_NOTIFICATION_ICON XSTR("NotificationIcon")
#define GROWL_NOTIFICATION_ICON_DATA XSTR("NotificationIcon")
/*! @defined GROWL_NOTIFICATION_APP_ICON
* @discussion Image data for the application icon, in case GROWL_APP_ICON does
* not apply for some reason. Must be in a format supported by NSImage, such
* not apply for some reason. Image data be in a format supported by NSImage, such
* as TIFF, PNG, GIF, JPEG, BMP, PICT, or PDF.
*
* Optional. Not supported by all display plugins.
*/
#define GROWL_NOTIFICATION_APP_ICON XSTR("NotificationAppIcon")
#define GROWL_NOTIFICATION_APP_ICON_DATA XSTR("NotificationAppIcon")
/*! @defined GROWL_NOTIFICATION_PRIORITY
* @discussion The priority of the notification as an integer number from
* -2 to +2 (+2 being highest).
@@ -185,16 +191,6 @@
*/
#define GROWL_NOTIFICATION_CLICK_CONTEXT XSTR("NotificationClickContext")
/*! @defined GROWL_DISPLAY_PLUGIN
* @discussion The name of a display plugin which should be used for this notification.
* Optional. If this key is not set or the specified display plugin does not
* exist, the display plugin stored in the application ticket is used. This key
* allows applications to use different default display plugins for their
* notifications. The user can still override those settings in the preference
* pane.
*/
#define GROWL_DISPLAY_PLUGIN XSTR("NotificationDisplayPlugin")
/*! @defined GROWL_NOTIFICATION_IDENTIFIER
* @abstract An identifier for the notification for coalescing purposes.
* Notifications with the same identifier fall into the same class; only
@@ -224,6 +220,19 @@
*/
#define GROWL_NOTIFICATION_PROGRESS XSTR("NotificationProgress")
/*! @defined GROWL_NOTIFICATION_ALREADY_SHOWN
* @abstract If this key is set, it should contain a bool value wrapped
* in a NSNumber which describes whether the notification has
* already been displayed, for instance by built in Notification
* Center support. This value can be used to allow display
* plugins to skip a notification, while still allowing Growl
* actions to run on them.
*
* Optional. Not supported by all display plugins.
*/
#define GROWL_NOTIFICATION_ALREADY_SHOWN XSTR("AlreadyShown")
// Notifications
#pragma mark Notifications
@@ -245,7 +254,7 @@
* The userInfo dictionary for this notification can contain these keys:
* <ul>
* <li>GROWL_APP_NAME</li>
* <li>GROWL_APP_ICON</li>
* <li>GROWL_APP_ICON_DATA</li>
* <li>GROWL_NOTIFICATIONS_ALL</li>
* <li>GROWL_NOTIFICATIONS_DEFAULT</li>
* </ul>
@@ -288,12 +297,6 @@
* Growl_PostNotification.
*/
#define GROWL_NOTIFICATION XSTR("GrowlNotification")
/*! @defined GROWL_SHUTDOWN
* @abstract The distributed notification name that tells Growl to shutdown.
* @discussion The Growl preference pane posts this notification when the
* "Stop Growl" button is clicked.
*/
#define GROWL_SHUTDOWN XSTR("GrowlShutdown")
/*! @defined GROWL_PING
* @abstract A distributed notification to check whether Growl is running.
* @discussion This is used by the Growl preference pane. If it receives a
@@ -313,15 +316,48 @@
* registration dictionary supplied by its delegate.
*/
#define GROWL_IS_READY XSTR("Lend Me Some Sugar; I Am Your Neighbor!")
/*! @defined GROWL_NOTIFICATION_CLICKED
* @abstract The distributed notification sent when a supported notification is clicked.
/*! @defined GROWL_DISTRIBUTED_NOTIFICATION_CLICKED_SUFFIX
* @abstract Part of the name of the distributed notification sent when a supported notification is clicked.
* @discussion When a Growl notification with a click context is clicked on by
* the user, Growl posts this distributed notification.
* The GrowlApplicationBridge responds to this notification by calling a
* callback in its delegate.
* the user, Growl posts a distributed notification whose name is in the format:
* [NSString stringWithFormat:@"%@-%d-%@", appName, pid, GROWL_DISTRIBUTED_NOTIFICATION_CLICKED_SUFFIX]
* The GrowlApplicationBridge responds to this notification by calling a callback in its delegate.
*/
#define GROWL_NOTIFICATION_CLICKED XSTR("GrowlClicked!")
#define GROWL_NOTIFICATION_TIMED_OUT XSTR("GrowlTimedOut!")
#define GROWL_DISTRIBUTED_NOTIFICATION_CLICKED_SUFFIX XSTR("GrowlClicked!")
/*! @defined GROWL_DISTRIBUTED_NOTIFICATION_TIMED_OUT_SUFFIX
* @abstract Part of the name of the distributed notification sent when a supported notification times out without being clicked.
* @discussion When a Growl notification with a click context times out, Growl posts a distributed notification
* whose name is in the format:
* [NSString stringWithFormat:@"%@-%d-%@", appName, pid, GROWL_DISTRIBUTED_NOTIFICATION_TIMED_OUT_SUFFIX]
* The GrowlApplicationBridge responds to this notification by calling a callback in its delegate.
* NOTE: The user may have actually clicked the 'close' button; this triggers an *immediate* time-out of the notification.
*/
#define GROWL_DISTRIBUTED_NOTIFICATION_TIMED_OUT_SUFFIX XSTR("GrowlTimedOut!")
/*! @defined GROWL_DISTRIBUTED_NOTIFICATION_NOTIFICATIONCENTER_ON
* @abstract The distributed notification sent when the Notification Center support is toggled on in Growl 2.0
* @discussion When the user enables Notification Center support in Growl 2.0, this notification is sent
* to inform all running apps that they should now speak to Notification Center directly.
*/
#define GROWL_DISTRIBUTED_NOTIFICATION_NOTIFICATIONCENTER_ON XSTR("GrowlNotificationCenterOn!")
/*! @defined GROWL_DISTRIBUTED_NOTIFICATION_NOTIFICATIONCENTER_OFF
* @abstract The distributed notification sent when the Notification Center support is toggled off in Growl 2.0
* @discussion When the user enables Notification Center support in Growl 2.0, this notification is sent
* to inform all running apps that they should no longer speak to Notification Center directly.
*/
#define GROWL_DISTRIBUTED_NOTIFICATION_NOTIFICATIONCENTER_OFF XSTR("GrowlNotificationCenterOff!")
/*! @defined GROWL_DISTRIBUTED_NOTIFICATION_NOTIFICATIONCENTER_QUERY
* @abstract The distributed notification sent by an application to query Growl 2.0's notification center support.
* @discussion When an app starts up, it will send this query to get Growl 2.0 to spit out whether notification
* center support is on or off.
*/
#define GROWL_DISTRIBUTED_NOTIFICATION_NOTIFICATIONCENTER_QUERY XSTR("GrowlNotificationCenterYN?")
/*! @group Other symbols */
/* Symbols which don't fit into any of the other categories. */
@@ -345,4 +381,6 @@
#define GROWL_POSITION_PREFERENCE_KEY @"GrowlSelectedPosition"
#define GROWL_PLUGIN_CONFIG_ID XSTR("GrowlPluginConfigurationID")
#endif //ndef _GROWLDEFINES_H
@@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BuildMachineOSBuild</key>
<string>12C60</string>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
@@ -13,11 +15,25 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.2.1</string>
<string>2.0.1</string>
<key>CFBundleSignature</key>
<string>GRRR</string>
<key>CFBundleVersion</key>
<string>1.2.1</string>
<string>2.0.1</string>
<key>DTCompiler</key>
<string>com.apple.compilers.llvm.clang.1_0</string>
<key>DTPlatformBuild</key>
<string>4G2008a</string>
<key>DTPlatformVersion</key>
<string>GM</string>
<key>DTSDKBuild</key>
<string>12C37</string>
<key>DTSDKName</key>
<string>macosx10.8</string>
<key>DTXcode</key>
<string>0452</string>
<key>DTXcodeBuild</key>
<string>4G2008a</string>
<key>NSPrincipalClass</key>
<string>GrowlApplicationBridge</string>
</dict>
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>files</key>
<dict>
<key>Resources/Info.plist</key>
<data>
VZb3f8My4te/5JwcjfvotgCXTAs=
</data>
</dict>
<key>rules</key>
<dict>
<key>^Resources/</key>
<true/>
<key>^Resources/.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^Resources/.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^version.plist$</key>
<true/>
</dict>
</dict>
</plist>
@@ -17,11 +17,11 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>2.0</string>
<string>3.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>2.0</string>
<string>3.0.0</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.developer-tools</string>
<key>LSUIElement</key>
@@ -30,5 +30,14 @@
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>XCCCompatibilityVersion</key>
<real>3</real>
<key>XCCMandatoryExecutables</key>
<array>
<string>python</string>
<string>narwhal-jsc</string>
<string>objj</string>
<string>nib2cib</string>
</array>
</dict>
</plist>
+41
View File
@@ -0,0 +1,41 @@
#import <Foundation/Foundation.h>
#import <asl.h>
#import "DDLog.h"
/**
* Welcome to Cocoa Lumberjack!
*
* The project page has a wealth of documentation if you have any questions.
* https://github.com/robbiehanson/CocoaLumberjack
*
* If you're new to the project you may wish to read the "Getting Started" wiki.
* https://github.com/robbiehanson/CocoaLumberjack/wiki/GettingStarted
*
*
* This class provides a logger for the Apple System Log facility.
*
* As described in the "Getting Started" page,
* the traditional NSLog() function directs it's output to two places:
*
* - Apple System Log
* - StdErr (if stderr is a TTY) so log statements show up in Xcode console
*
* To duplicate NSLog() functionality you can simply add this logger and a tty logger.
* However, if you instead choose to use file logging (for faster performance),
* you may choose to use a file logger and a tty logger.
**/
@interface DDASLLogger : DDAbstractLogger <DDLogger>
{
aslclient client;
}
+ (DDASLLogger *)sharedInstance;
// Inherited from DDAbstractLogger
// - (id <DDLogFormatter>)logFormatter;
// - (void)setLogFormatter:(id <DDLogFormatter>)formatter;
@end
+99
View File
@@ -0,0 +1,99 @@
#import "DDASLLogger.h"
#import <libkern/OSAtomic.h>
/**
* Welcome to Cocoa Lumberjack!
*
* The project page has a wealth of documentation if you have any questions.
* https://github.com/robbiehanson/CocoaLumberjack
*
* If you're new to the project you may wish to read the "Getting Started" wiki.
* https://github.com/robbiehanson/CocoaLumberjack/wiki/GettingStarted
**/
#if ! __has_feature(objc_arc)
#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
#endif
@implementation DDASLLogger
static DDASLLogger *sharedInstance;
/**
* The runtime sends initialize to each class in a program exactly one time just before the class,
* or any class that inherits from it, is sent its first message from within the program. (Thus the
* method may never be invoked if the class is not used.) The runtime sends the initialize message to
* classes in a thread-safe manner. Superclasses receive this message before their subclasses.
*
* This method may also be called directly (assumably by accident), hence the safety mechanism.
**/
+ (void)initialize
{
static BOOL initialized = NO;
if (!initialized)
{
initialized = YES;
sharedInstance = [[DDASLLogger alloc] init];
}
}
+ (DDASLLogger *)sharedInstance
{
return sharedInstance;
}
- (id)init
{
if (sharedInstance != nil)
{
return nil;
}
if ((self = [super init]))
{
// A default asl client is provided for the main thread,
// but background threads need to create their own client.
client = asl_open(NULL, "com.apple.console", 0);
}
return self;
}
- (void)logMessage:(DDLogMessage *)logMessage
{
NSString *logMsg = logMessage->logMsg;
if (formatter)
{
logMsg = [formatter formatLogMessage:logMessage];
}
if (logMsg)
{
const char *msg = [logMsg UTF8String];
int aslLogLevel;
switch (logMessage->logLevel)
{
// Note: By default ASL will filter anything above level 5 (Notice).
// So our mappings shouldn't go above that level.
case 1 : aslLogLevel = ASL_LEVEL_CRIT; break;
case 2 : aslLogLevel = ASL_LEVEL_ERR; break;
case 3 : aslLogLevel = ASL_LEVEL_WARNING; break;
default : aslLogLevel = ASL_LEVEL_NOTICE; break;
}
asl_log(client, NULL, aslLogLevel, "%s", msg);
}
}
- (NSString *)loggerName
{
return @"cocoa.lumberjack.aslLogger";
}
@end
+597
View File
@@ -0,0 +1,597 @@
#import <Foundation/Foundation.h>
/**
* Welcome to Cocoa Lumberjack!
*
* The project page has a wealth of documentation if you have any questions.
* https://github.com/robbiehanson/CocoaLumberjack
*
* If you're new to the project you may wish to read the "Getting Started" wiki.
* https://github.com/robbiehanson/CocoaLumberjack/wiki/GettingStarted
*
* Otherwise, here is a quick refresher.
* There are three steps to using the macros:
*
* Step 1:
* Import the header in your implementation file:
*
* #import "DDLog.h"
*
* Step 2:
* Define your logging level in your implementation file:
*
* // Log levels: off, error, warn, info, verbose
* static const int ddLogLevel = LOG_LEVEL_VERBOSE;
*
* Step 3:
* Replace your NSLog statements with DDLog statements according to the severity of the message.
*
* NSLog(@"Fatal error, no dohickey found!"); -> DDLogError(@"Fatal error, no dohickey found!");
*
* DDLog works exactly the same as NSLog.
* This means you can pass it multiple variables just like NSLog.
**/
@class DDLogMessage;
@protocol DDLogger;
@protocol DDLogFormatter;
/**
* This is the single macro that all other macros below compile into.
* This big multiline macro makes all the other macros easier to read.
**/
#define LOG_MACRO(isAsynchronous, lvl, flg, ctx, atag, fnct, frmt, ...) \
[DDLog log:isAsynchronous \
level:lvl \
flag:flg \
context:ctx \
file:__FILE__ \
function:fnct \
line:__LINE__ \
tag:atag \
format:(frmt), ##__VA_ARGS__]
/**
* Define the Objective-C and C versions of the macro.
* These automatically inject the proper function name for either an objective-c method or c function.
*
* We also define shorthand versions for asynchronous and synchronous logging.
**/
#define LOG_OBJC_MACRO(async, lvl, flg, ctx, frmt, ...) \
LOG_MACRO(async, lvl, flg, ctx, nil, sel_getName(_cmd), frmt, ##__VA_ARGS__)
#define LOG_C_MACRO(async, lvl, flg, ctx, frmt, ...) \
LOG_MACRO(async, lvl, flg, ctx, nil, __FUNCTION__, frmt, ##__VA_ARGS__)
#define SYNC_LOG_OBJC_MACRO(lvl, flg, ctx, frmt, ...) \
LOG_OBJC_MACRO( NO, lvl, flg, ctx, frmt, ##__VA_ARGS__)
#define ASYNC_LOG_OBJC_MACRO(lvl, flg, ctx, frmt, ...) \
LOG_OBJC_MACRO(YES, lvl, flg, ctx, frmt, ##__VA_ARGS__)
#define SYNC_LOG_C_MACRO(lvl, flg, ctx, frmt, ...) \
LOG_C_MACRO( NO, lvl, flg, ctx, frmt, ##__VA_ARGS__)
#define ASYNC_LOG_C_MACRO(lvl, flg, ctx, frmt, ...) \
LOG_C_MACRO(YES, lvl, flg, ctx, frmt, ##__VA_ARGS__)
/**
* Define version of the macro that only execute if the logLevel is above the threshold.
* The compiled versions essentially look like this:
*
* if (logFlagForThisLogMsg & ddLogLevel) { execute log message }
*
* As shown further below, Lumberjack actually uses a bitmask as opposed to primitive log levels.
* This allows for a great amount of flexibility and some pretty advanced fine grained logging techniques.
*
* Note that when compiler optimizations are enabled (as they are for your release builds),
* the log messages above your logging threshold will automatically be compiled out.
*
* (If the compiler sees ddLogLevel declared as a constant, the compiler simply checks to see if the 'if' statement
* would execute, and if not it strips it from the binary.)
*
* We also define shorthand versions for asynchronous and synchronous logging.
**/
#define LOG_MAYBE(async, lvl, flg, ctx, fnct, frmt, ...) \
do { if(lvl & flg) LOG_MACRO(async, lvl, flg, ctx, nil, fnct, frmt, ##__VA_ARGS__); } while(0)
#define LOG_OBJC_MAYBE(async, lvl, flg, ctx, frmt, ...) \
LOG_MAYBE(async, lvl, flg, ctx, sel_getName(_cmd), frmt, ##__VA_ARGS__)
#define LOG_C_MAYBE(async, lvl, flg, ctx, frmt, ...) \
LOG_MAYBE(async, lvl, flg, ctx, __FUNCTION__, frmt, ##__VA_ARGS__)
#define SYNC_LOG_OBJC_MAYBE(lvl, flg, ctx, frmt, ...) \
LOG_OBJC_MAYBE( NO, lvl, flg, ctx, frmt, ##__VA_ARGS__)
#define ASYNC_LOG_OBJC_MAYBE(lvl, flg, ctx, frmt, ...) \
LOG_OBJC_MAYBE(YES, lvl, flg, ctx, frmt, ##__VA_ARGS__)
#define SYNC_LOG_C_MAYBE(lvl, flg, ctx, frmt, ...) \
LOG_C_MAYBE( NO, lvl, flg, ctx, frmt, ##__VA_ARGS__)
#define ASYNC_LOG_C_MAYBE(lvl, flg, ctx, frmt, ...) \
LOG_C_MAYBE(YES, lvl, flg, ctx, frmt, ##__VA_ARGS__)
/**
* Define versions of the macros that also accept tags.
*
* The DDLogMessage object includes a 'tag' ivar that may be used for a variety of purposes.
* It may be used to pass custom information to loggers or formatters.
* Or it may be used by 3rd party extensions to the framework.
*
* Thes macros just make it a little easier to extend logging functionality.
**/
#define LOG_OBJC_TAG_MACRO(async, lvl, flg, ctx, tag, frmt, ...) \
LOG_MACRO(async, lvl, flg, ctx, tag, sel_getName(_cmd), frmt, ##__VA_ARGS__)
#define LOG_C_TAG_MACRO(async, lvl, flg, ctx, tag, frmt, ...) \
LOG_MACRO(async, lvl, flg, ctx, tag, __FUNCTION__, frmt, ##__VA_ARGS__)
#define LOG_TAG_MAYBE(async, lvl, flg, ctx, tag, fnct, frmt, ...) \
do { if(lvl & flg) LOG_MACRO(async, lvl, flg, ctx, tag, fnct, frmt, ##__VA_ARGS__); } while(0)
#define LOG_OBJC_TAG_MAYBE(async, lvl, flg, ctx, tag, frmt, ...) \
LOG_TAG_MAYBE(async, lvl, flg, ctx, tag, sel_getName(_cmd), frmt, ##__VA_ARGS__)
#define LOG_C_TAG_MAYBE(async, lvl, flg, ctx, tag, frmt, ...) \
LOG_TAG_MAYBE(async, lvl, flg, ctx, tag, __FUNCTION__, frmt, ##__VA_ARGS__)
/**
* Define the standard options.
*
* We default to only 4 levels because it makes it easier for beginners
* to make the transition to a logging framework.
*
* More advanced users may choose to completely customize the levels (and level names) to suite their needs.
* For more information on this see the "Custom Log Levels" page:
* https://github.com/robbiehanson/CocoaLumberjack/wiki/CustomLogLevels
*
* Advanced users may also notice that we're using a bitmask.
* This is to allow for custom fine grained logging:
* https://github.com/robbiehanson/CocoaLumberjack/wiki/FineGrainedLogging
*
* -- Flags --
*
* Typically you will use the LOG_LEVELS (see below), but the flags may be used directly in certain situations.
* For example, say you have a lot of warning log messages, and you wanted to disable them.
* However, you still needed to see your error and info log messages.
* You could accomplish that with the following:
*
* static const int ddLogLevel = LOG_FLAG_ERROR | LOG_FLAG_INFO;
*
* Flags may also be consulted when writing custom log formatters,
* as the DDLogMessage class captures the individual flag that caused the log message to fire.
*
* -- Levels --
*
* Log levels are simply the proper bitmask of the flags.
*
* -- Booleans --
*
* The booleans may be used when your logging code involves more than one line.
* For example:
*
* if (LOG_VERBOSE) {
* for (id sprocket in sprockets)
* DDLogVerbose(@"sprocket: %@", [sprocket description])
* }
*
* -- Async --
*
* Defines the default asynchronous options.
* The default philosophy for asynchronous logging is very simple:
*
* Log messages with errors should be executed synchronously.
* After all, an error just occurred. The application could be unstable.
*
* All other log messages, such as debug output, are executed asynchronously.
* After all, if it wasn't an error, then it was just informational output,
* or something the application was easily able to recover from.
*
* -- Changes --
*
* You are strongly discouraged from modifying this file.
* If you do, you make it more difficult on yourself to merge future bug fixes and improvements from the project.
* Instead, create your own MyLogging.h or ApplicationNameLogging.h or CompanyLogging.h
*
* For an example of customizing your logging experience, see the "Custom Log Levels" page:
* https://github.com/robbiehanson/CocoaLumberjack/wiki/CustomLogLevels
**/
#define LOG_FLAG_ERROR (1 << 0) // 0...0001
#define LOG_FLAG_WARN (1 << 1) // 0...0010
#define LOG_FLAG_INFO (1 << 2) // 0...0100
#define LOG_FLAG_VERBOSE (1 << 3) // 0...1000
#define LOG_LEVEL_OFF 0
#define LOG_LEVEL_ERROR (LOG_FLAG_ERROR) // 0...0001
#define LOG_LEVEL_WARN (LOG_FLAG_ERROR | LOG_FLAG_WARN) // 0...0011
#define LOG_LEVEL_INFO (LOG_FLAG_ERROR | LOG_FLAG_WARN | LOG_FLAG_INFO) // 0...0111
#define LOG_LEVEL_VERBOSE (LOG_FLAG_ERROR | LOG_FLAG_WARN | LOG_FLAG_INFO | LOG_FLAG_VERBOSE) // 0...1111
#define LOG_ERROR (ddLogLevel & LOG_FLAG_ERROR)
#define LOG_WARN (ddLogLevel & LOG_FLAG_WARN)
#define LOG_INFO (ddLogLevel & LOG_FLAG_INFO)
#define LOG_VERBOSE (ddLogLevel & LOG_FLAG_VERBOSE)
#define LOG_ASYNC_ENABLED YES
#define LOG_ASYNC_ERROR ( NO && LOG_ASYNC_ENABLED)
#define LOG_ASYNC_WARN (YES && LOG_ASYNC_ENABLED)
#define LOG_ASYNC_INFO (YES && LOG_ASYNC_ENABLED)
#define LOG_ASYNC_VERBOSE (YES && LOG_ASYNC_ENABLED)
#define DDLogError(frmt, ...) LOG_OBJC_MAYBE(LOG_ASYNC_ERROR, ddLogLevel, LOG_FLAG_ERROR, 0, frmt, ##__VA_ARGS__)
#define DDLogWarn(frmt, ...) LOG_OBJC_MAYBE(LOG_ASYNC_WARN, ddLogLevel, LOG_FLAG_WARN, 0, frmt, ##__VA_ARGS__)
#define DDLogInfo(frmt, ...) LOG_OBJC_MAYBE(LOG_ASYNC_INFO, ddLogLevel, LOG_FLAG_INFO, 0, frmt, ##__VA_ARGS__)
#define DDLogVerbose(frmt, ...) LOG_OBJC_MAYBE(LOG_ASYNC_VERBOSE, ddLogLevel, LOG_FLAG_VERBOSE, 0, frmt, ##__VA_ARGS__)
#define DDLogCError(frmt, ...) LOG_C_MAYBE(LOG_ASYNC_ERROR, ddLogLevel, LOG_FLAG_ERROR, 0, frmt, ##__VA_ARGS__)
#define DDLogCWarn(frmt, ...) LOG_C_MAYBE(LOG_ASYNC_WARN, ddLogLevel, LOG_FLAG_WARN, 0, frmt, ##__VA_ARGS__)
#define DDLogCInfo(frmt, ...) LOG_C_MAYBE(LOG_ASYNC_INFO, ddLogLevel, LOG_FLAG_INFO, 0, frmt, ##__VA_ARGS__)
#define DDLogCVerbose(frmt, ...) LOG_C_MAYBE(LOG_ASYNC_VERBOSE, ddLogLevel, LOG_FLAG_VERBOSE, 0, frmt, ##__VA_ARGS__)
/**
* The THIS_FILE macro gives you an NSString of the file name.
* For simplicity and clarity, the file name does not include the full path or file extension.
*
* For example: DDLogWarn(@"%@: Unable to find thingy", THIS_FILE) -> @"MyViewController: Unable to find thingy"
**/
NSString *DDExtractFileNameWithoutExtension(const char *filePath, BOOL copy);
#define THIS_FILE (DDExtractFileNameWithoutExtension(__FILE__, NO))
/**
* The THIS_METHOD macro gives you the name of the current objective-c method.
*
* For example: DDLogWarn(@"%@ - Requires non-nil strings") -> @"setMake:model: requires non-nil strings"
*
* Note: This does NOT work in straight C functions (non objective-c).
* Instead you should use the predefined __FUNCTION__ macro.
**/
#define THIS_METHOD NSStringFromSelector(_cmd)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@interface DDLog : NSObject
/**
* Provides access to the underlying logging queue.
* This may be helpful to Logger classes for things like thread synchronization.
**/
+ (dispatch_queue_t)loggingQueue;
/**
* Logging Primitive.
*
* This method is used by the macros above.
* It is suggested you stick with the macros as they're easier to use.
**/
+ (void)log:(BOOL)synchronous
level:(int)level
flag:(int)flag
context:(int)context
file:(const char *)file
function:(const char *)function
line:(int)line
tag:(id)tag
format:(NSString *)format, ... __attribute__ ((format (__NSString__, 9, 10)));
/**
* Logging Primitive.
*
* This method can be used if you have a prepared va_list.
**/
+ (void)log:(BOOL)asynchronous
level:(int)level
flag:(int)flag
context:(int)context
file:(const char *)file
function:(const char *)function
line:(int)line
tag:(id)tag
format:(NSString *)format
args:(va_list)argList;
/**
* Since logging can be asynchronous, there may be times when you want to flush the logs.
* The framework invokes this automatically when the application quits.
**/
+ (void)flushLog;
/**
* Loggers
*
* If you want your log statements to go somewhere,
* you should create and add a logger.
**/
+ (void)addLogger:(id <DDLogger>)logger;
+ (void)removeLogger:(id <DDLogger>)logger;
+ (void)removeAllLoggers;
/**
* Registered Dynamic Logging
*
* These methods allow you to obtain a list of classes that are using registered dynamic logging,
* and also provides methods to get and set their log level during run time.
**/
+ (NSArray *)registeredClasses;
+ (NSArray *)registeredClassNames;
+ (int)logLevelForClass:(Class)aClass;
+ (int)logLevelForClassWithName:(NSString *)aClassName;
+ (void)setLogLevel:(int)logLevel forClass:(Class)aClass;
+ (void)setLogLevel:(int)logLevel forClassWithName:(NSString *)aClassName;
@end
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@protocol DDLogger <NSObject>
@required
- (void)logMessage:(DDLogMessage *)logMessage;
/**
* Formatters may optionally be added to any logger.
*
* If no formatter is set, the logger simply logs the message as it is given in logMessage,
* or it may use its own built in formatting style.
**/
- (id <DDLogFormatter>)logFormatter;
- (void)setLogFormatter:(id <DDLogFormatter>)formatter;
@optional
/**
* Since logging is asynchronous, adding and removing loggers is also asynchronous.
* In other words, the loggers are added and removed at appropriate times with regards to log messages.
*
* - Loggers will not receive log messages that were executed prior to when they were added.
* - Loggers will not receive log messages that were executed after they were removed.
*
* These methods are executed in the logging thread/queue.
* This is the same thread/queue that will execute every logMessage: invocation.
* Loggers may use these methods for thread synchronization or other setup/teardown tasks.
**/
- (void)didAddLogger;
- (void)willRemoveLogger;
/**
* Some loggers may buffer IO for optimization purposes.
* For example, a database logger may only save occasionaly as the disk IO is slow.
* In such loggers, this method should be implemented to flush any pending IO.
*
* This allows invocations of DDLog's flushLog method to be propogated to loggers that need it.
*
* Note that DDLog's flushLog method is invoked automatically when the application quits,
* and it may be also invoked manually by the developer prior to application crashes, or other such reasons.
**/
- (void)flush;
/**
* Each logger is executed concurrently with respect to the other loggers.
* Thus, a dedicated dispatch queue is used for each logger.
* Logger implementations may optionally choose to provide their own dispatch queue.
**/
- (dispatch_queue_t)loggerQueue;
/**
* If the logger implementation does not choose to provide its own queue,
* one will automatically be created for it.
* The created queue will receive its name from this method.
* This may be helpful for debugging or profiling reasons.
**/
- (NSString *)loggerName;
@end
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@protocol DDLogFormatter <NSObject>
@required
/**
* Formatters may optionally be added to any logger.
* This allows for increased flexibility in the logging environment.
* For example, log messages for log files may be formatted differently than log messages for the console.
*
* For more information about formatters, see the "Custom Formatters" page:
* https://github.com/robbiehanson/CocoaLumberjack/wiki/CustomFormatters
*
* The formatter may also optionally filter the log message by returning nil,
* in which case the logger will not log the message.
**/
- (NSString *)formatLogMessage:(DDLogMessage *)logMessage;
@optional
/**
* A single formatter instance can be added to multiple loggers.
* These methods provides hooks to notify the formatter of when it's added/removed.
*
* This is primarily for thread-safety.
* If a formatter is explicitly not thread-safe, it may wish to throw an exception if added to multiple loggers.
* Or if a formatter has potentially thread-unsafe code (e.g. NSDateFormatter),
* it could possibly use these hooks to switch to thread-safe versions of the code.
**/
- (void)didAddToLogger:(id <DDLogger>)logger;
- (void)willRemoveFromLogger:(id <DDLogger>)logger;
@end
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@protocol DDRegisteredDynamicLogging
/**
* Implement these methods to allow a file's log level to be managed from a central location.
*
* This is useful if you'd like to be able to change log levels for various parts
* of your code from within the running application.
*
* Imagine pulling up the settings for your application,
* and being able to configure the logging level on a per file basis.
*
* The implementation can be very straight-forward:
*
* + (int)ddLogLevel
* {
* return ddLogLevel;
* }
*
* + (void)ddSetLogLevel:(int)logLevel
* {
* ddLogLevel = logLevel;
* }
**/
+ (int)ddLogLevel;
+ (void)ddSetLogLevel:(int)logLevel;
@end
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* The DDLogMessage class encapsulates information about the log message.
* If you write custom loggers or formatters, you will be dealing with objects of this class.
**/
enum {
DDLogMessageCopyFile = 1 << 0,
DDLogMessageCopyFunction = 1 << 1,
};
typedef int DDLogMessageOptions;
@interface DDLogMessage : NSObject
{
// The public variables below can be accessed directly (for speed).
// For example: logMessage->logLevel
@public
int logLevel;
int logFlag;
int logContext;
NSString *logMsg;
NSDate *timestamp;
char *file;
char *function;
int lineNumber;
mach_port_t machThreadID;
char *queueLabel;
NSString *threadName;
// For 3rd party extensions to the framework, where flags and contexts aren't enough.
id tag;
// For 3rd party extensions that manually create DDLogMessage instances.
DDLogMessageOptions options;
}
/**
* Standard init method for a log message object.
* Used by the logging primitives. (And the macros use the logging primitives.)
*
* If you find need to manually create logMessage objects, there is one thing you should be aware of:
*
* If no flags are passed, the method expects the file and function parameters to be string literals.
* That is, it expects the given strings to exist for the duration of the object's lifetime,
* and it expects the given strings to be immutable.
* In other words, it does not copy these strings, it simply points to them.
* This is due to the fact that __FILE__ and __FUNCTION__ are usually used to specify these parameters,
* so it makes sense to optimize and skip the unnecessary allocations.
* However, if you need them to be copied you may use the options parameter to specify this.
* Options is a bitmask which supports DDLogMessageCopyFile and DDLogMessageCopyFunction.
**/
- (id)initWithLogMsg:(NSString *)logMsg
level:(int)logLevel
flag:(int)logFlag
context:(int)logContext
file:(const char *)file
function:(const char *)function
line:(int)line
tag:(id)tag
options:(DDLogMessageOptions)optionsMask;
/**
* Returns the threadID as it appears in NSLog.
* That is, it is a hexadecimal value which is calculated from the machThreadID.
**/
- (NSString *)threadID;
/**
* Convenience property to get just the file name, as the file variable is generally the full file path.
* This method does not include the file extension, which is generally unwanted for logging purposes.
**/
- (NSString *)fileName;
/**
* Returns the function variable in NSString form.
**/
- (NSString *)methodName;
@end
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* The DDLogger protocol specifies that an optional formatter can be added to a logger.
* Most (but not all) loggers will want to support formatters.
*
* However, writting getters and setters in a thread safe manner,
* while still maintaining maximum speed for the logging process, is a difficult task.
*
* To do it right, the implementation of the getter/setter has strict requiremenets:
* - Must NOT require the logMessage method to acquire a lock.
* - Must NOT require the logMessage method to access an atomic property (also a lock of sorts).
*
* To simplify things, an abstract logger is provided that implements the getter and setter.
*
* Logger implementations may simply extend this class,
* and they can ACCESS THE FORMATTER VARIABLE DIRECTLY from within their logMessage method!
**/
@interface DDAbstractLogger : NSObject <DDLogger>
{
id <DDLogFormatter> formatter;
dispatch_queue_t loggerQueue;
}
- (id <DDLogFormatter>)logFormatter;
- (void)setLogFormatter:(id <DDLogFormatter>)formatter;
@end
File diff suppressed because it is too large. Load diff
+167
View File
@@ -0,0 +1,167 @@
#import <Foundation/Foundation.h>
#if TARGET_OS_IPHONE
#import <UIKit/UIColor.h>
#else
#import <AppKit/NSColor.h>
#endif
#import "DDLog.h"
/**
* Welcome to Cocoa Lumberjack!
*
* The project page has a wealth of documentation if you have any questions.
* https://github.com/robbiehanson/CocoaLumberjack
*
* If you're new to the project you may wish to read the "Getting Started" wiki.
* https://github.com/robbiehanson/CocoaLumberjack/wiki/GettingStarted
*
*
* This class provides a logger for Terminal output or Xcode console output,
* depending on where you are running your code.
*
* As described in the "Getting Started" page,
* the traditional NSLog() function directs it's output to two places:
*
* - Apple System Log (so it shows up in Console.app)
* - StdErr (if stderr is a TTY, so log statements show up in Xcode console)
*
* To duplicate NSLog() functionality you can simply add this logger and an asl logger.
* However, if you instead choose to use file logging (for faster performance),
* you may choose to use only a file logger and a tty logger.
**/
@interface DDTTYLogger : DDAbstractLogger <DDLogger>
{
NSCalendar *calendar;
NSUInteger calendarUnitFlags;
NSString *appName;
char *app;
size_t appLen;
NSString *processID;
char *pid;
size_t pidLen;
BOOL colorsEnabled;
NSMutableArray *colorProfilesArray;
NSMutableDictionary *colorProfilesDict;
}
+ (DDTTYLogger *)sharedInstance;
/* Inherited from the DDLogger protocol:
*
* Formatters may optionally be added to any logger.
*
* If no formatter is set, the logger simply logs the message as it is given in logMessage,
* or it may use its own built in formatting style.
*
* More information about formatters can be found here:
* https://github.com/robbiehanson/CocoaLumberjack/wiki/CustomFormatters
*
* The actual implementation of these methods is inherited from DDAbstractLogger.
- (id <DDLogFormatter>)logFormatter;
- (void)setLogFormatter:(id <DDLogFormatter>)formatter;
*/
/**
* Want to use different colors for different log levels?
* Enable this property.
*
* If you run the application via the Terminal (not Xcode),
* the logger will map colors to xterm-256color or xterm-color (if available).
*
* Xcode does NOT natively support colors in the Xcode debugging console.
* You'll need to install the XcodeColors plugin to see colors in the Xcode console.
* https://github.com/robbiehanson/XcodeColors
*
* The default value if NO.
**/
@property (readwrite, assign) BOOL colorsEnabled;
/**
* The default color set (foregroundColor, backgroundColor) is:
*
* - LOG_FLAG_ERROR = (red, nil)
* - LOG_FLAG_WARN = (orange, nil)
*
* You can customize the colors however you see fit.
* Please note that you are passing a flag, NOT a level.
*
* GOOD : [ttyLogger setForegroundColor:pink backgroundColor:nil forFlag:LOG_FLAG_INFO]; // <- Good :)
* BAD : [ttyLogger setForegroundColor:pink backgroundColor:nil forFlag:LOG_LEVEL_INFO]; // <- BAD! :(
*
* LOG_FLAG_INFO = 0...00100
* LOG_LEVEL_INFO = 0...00111 <- Would match LOG_FLAG_INFO and LOG_FLAG_WARN and LOG_FLAG_ERROR
*
* If you run the application within Xcode, then the XcodeColors plugin is required.
*
* If you run the application from a shell, then DDTTYLogger will automatically map the given color to
* the closest available color. (xterm-256color or xterm-color which have 256 and 16 supported colors respectively.)
*
* This method invokes setForegroundColor:backgroundColor:forFlag:context: and passes the default context (0).
**/
#if TARGET_OS_IPHONE
- (void)setForegroundColor:(UIColor *)txtColor backgroundColor:(UIColor *)bgColor forFlag:(int)mask;
#else
- (void)setForegroundColor:(NSColor *)txtColor backgroundColor:(NSColor *)bgColor forFlag:(int)mask;
#endif
/**
* Just like setForegroundColor:backgroundColor:flag, but allows you to specify a particular logging context.
*
* A logging context is often used to identify log messages coming from a 3rd party framework,
* although logging context's can be used for many different functions.
*
* Logging context's are explained in further detail here:
* https://github.com/robbiehanson/CocoaLumberjack/wiki/CustomContext
**/
#if TARGET_OS_IPHONE
- (void)setForegroundColor:(UIColor *)txtColor backgroundColor:(UIColor *)bgColor forFlag:(int)mask context:(int)ctxt;
#else
- (void)setForegroundColor:(NSColor *)txtColor backgroundColor:(NSColor *)bgColor forFlag:(int)mask context:(int)ctxt;
#endif
/**
* Similar to the methods above, but allows you to map DDLogMessage->tag to a particular color profile.
* For example, you could do something like this:
*
* static NSString *const PurpleTag = @"PurpleTag";
*
* #define DDLogPurple(frmt, ...) LOG_OBJC_TAG_MACRO(NO, 0, 0, 0, PurpleTag, frmt, ##__VA_ARGS__)
*
* And then in your applicationDidFinishLaunching, or wherever you configure Lumberjack:
*
* #if TARGET_OS_IPHONE
* UIColor *purple = [UIColor colorWithRed:(64/255.0) green:(0/255.0) blue:(128/255.0) alpha:1.0];
* #else
* NSColor *purple = [NSColor colorWithCalibratedRed:(64/255.0) green:(0/255.0) blue:(128/255.0) alpha:1.0];
*
* [[DDTTYLogger sharedInstance] setForegroundColor:purple backgroundColor:nil forTag:PurpleTag];
* [DDLog addLogger:[DDTTYLogger sharedInstance]];
*
* This would essentially give you a straight NSLog replacement that prints in purple:
*
* DDLogPurple(@"I'm a purple log message!");
**/
#if TARGET_OS_IPHONE
- (void)setForegroundColor:(UIColor *)txtColor backgroundColor:(UIColor *)bgColor forTag:(id <NSCopying>)tag;
#else
- (void)setForegroundColor:(NSColor *)txtColor backgroundColor:(NSColor *)bgColor forTag:(id <NSCopying>)tag;
#endif
/**
* Clearing color profiles.
**/
- (void)clearColorsForFlag:(int)mask;
- (void)clearColorsForFlag:(int)mask context:(int)context;
- (void)clearColorsForTag:(id <NSCopying>)tag;
- (void)clearColorsForAllFlags;
- (void)clearColorsForAllTags;
- (void)clearAllColors;
@end
File diff suppressed because it is too large. Load diff
+19
View File
@@ -0,0 +1,19 @@
//
// Notifications.h
// XcodeCapp
//
// Created by Aparajita on 4/27/13.
// Copyright (c) 2013 Cappuccino Project. All rights reserved.
//
#ifndef XcodeCapp_Notifications_h
#define XcodeCapp_Notifications_h
extern NSString * const XCCProjectDidFinishLoadingNotification;
extern NSString * const XCCBatchDidStartNotification;
extern NSString * const XCCBatchDidEndNotification;
extern NSString * const XCCConversionDidStartNotification;
extern NSString * const XCCConversionDidEndNotification;
extern NSString * const XCCConversionDidGenerateErrorNotification;
#endif
+17
View File
@@ -0,0 +1,17 @@
//
// Notifications.m
// XcodeCapp
//
// Created by Aparajita on 4/27/13.
//
//
#include "Notifications.h"
NSString * const XCCProjectDidFinishLoadingNotification = @"XCCProjectDidFinishLoadingNotification";
NSString * const XCCBatchDidStartNotification = @"XCCBatchDidStartNotification";
NSString * const XCCBatchDidEndNotification = @"XCCBatchDidEndNotification";
NSString * const XCCConversionDidStartNotification = @"XCCConversionDidStartNotification";
NSString * const XCCConversionDidEndNotification = @"XCCConversionDidStopNotification";
NSString * const XCCConversionDidGenerateErrorNotification = @"XCCConversionDidGenerateErrorNotification";
@@ -0,0 +1,19 @@
//
// ProcessSourceOperation.h
// XcodeCapp
//
// Created by Aparajita on 4/27/13.
// Copyright (c) 2013 Cappuccino Project. All rights reserved.
//
#import <Foundation/Foundation.h>
@class XcodeCapp;
@interface ProcessSourceOperation : NSOperation
// sourcePath should be a path within the project (no resolved symlinks)
- (id)initWithXCC:(XcodeCapp *)xcc projectId:(NSNumber *)projectId sourcePath:(NSString *)sourcePath;
@end
@@ -0,0 +1,184 @@
//
// ProcessSourceOperation.m
// XcodeCapp
//
// Created by Aparajita on 4/27/13.
// Copyright (c) 2013 Cappuccino Project. All rights reserved.
//
#import "ProcessSourceOperation.h"
#import "Notifications.h"
#import "XcodeCapp.h"
@interface ProcessSourceOperation ()
@property XcodeCapp *xcc;
@property NSNumber *projectId;
@property NSString *sourcePath;
@property NSString *projectPath;
@end
@implementation ProcessSourceOperation
- (id)initWithXCC:(XcodeCapp *)xcc projectId:(NSNumber *)projectId sourcePath:(NSString *)sourcePath
{
self = [super init];
if (self)
{
self.xcc = xcc;
self.projectId = projectId;
self.sourcePath = sourcePath;
self.projectPath = xcc.projectPath;
}
return self;
}
- (void)main
{
if (self.isCancelled)
return;
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
NSDictionary *info = @{ @"projectId":self.projectId, @"path":self.sourcePath };
[center postNotificationName:XCCConversionDidStartNotification object:self userInfo:info];
DDLogVerbose(@"Conversion started: %@", self.sourcePath);
NSString *launchPath = nil;
NSArray *arguments = nil;
NSString *response = nil;
NSString *projectRelativePath = [self.sourcePath substringFromIndex:self.projectPath.length + 1];
NSString *notificationTitle = nil;
NSString *notificationMessage = projectRelativePath.lastPathComponent;
if ([self.xcc isXibFile:self.sourcePath])
{
launchPath = self.xcc.executablePaths[@"nib2cib"];
arguments = @[
@"--no-colors",
self.sourcePath
];
notificationTitle = @"Xib converted";
}
else if ([self.xcc isObjjFile:self.sourcePath])
{
launchPath = self.xcc.executablePaths[@"objj"];
arguments = @[
self.xcc.parserPath,
self.projectPath,
self.sourcePath
];
notificationTitle = @"Objective-J source processed";
}
else if ([self.xcc isXCCIgnoreFile:self.sourcePath])
{
if (self.isCancelled)
return;
[self.xcc performSelectorOnMainThread:@selector(computeIgnoredPaths) withObject:nil waitUntilDone:NO];
notificationTitle = @"Parsed .xcodecapp-ignore";
notificationMessage = @"Ignored paths updated";
}
// Run the task and get the response if needed
NSInteger status = 0;
if (arguments)
{
if (self.isCancelled)
return;
DDLogVerbose(@"Running processing task: %@", launchPath);
NSDictionary *taskResult = [self.xcc runTaskWithLaunchPath:launchPath
arguments:arguments
returnType:kTaskReturnTypeStdError];
status = [taskResult[@"status"] intValue];
response = taskResult[@"response"];
DDLogInfo(@"Processed %@: [%ld, %@]", self.sourcePath, status, status ? response : @"");
if (self.isCancelled)
return;
if (status != 0)
{
if (response.length == 0)
response = @"An unspecified error occurred";
if ([self.xcc isXibFile:self.sourcePath])
{
notificationTitle = @"Error converting xib";
NSString *message = [NSString stringWithFormat:@"%@\n%@", self.sourcePath.lastPathComponent, response];
NSDictionary *info =
@{
@"projectId":self.projectId,
@"message":message,
@"path":self.sourcePath,
@"status":taskResult[@"status"]
};
if (self.isCancelled)
return;
[center postNotificationName:XCCConversionDidGenerateErrorNotification object:self userInfo:info];
}
else
{
notificationTitle = [(status == XCCStatusCodeError ? @"Error" : @"Warning") stringByAppendingString:@" parsing Objective-J source"];
NSArray *errors = [response propertyList];
for (NSDictionary *error in errors)
{
NSMutableDictionary *info = [error mutableCopy];
info[@"projectId"] = self.projectId;
info[@"message"] = [NSString stringWithFormat:@"%@, line %d\n%@", [error[@"path"] lastPathComponent], [error[@"line"] intValue], error[@"message"]];
info[@"status"] = taskResult[@"status"];
if (self.isCancelled)
return;
[center postNotificationName:XCCConversionDidGenerateErrorNotification object:self userInfo:info];
}
}
[self notifyUserWithTitle:notificationTitle message:notificationMessage];
}
else if (!self.xcc.isLoadingProject)
{
[self notifyUserWithTitle:notificationTitle message:notificationMessage];
}
}
if (!self.isCancelled)
{
DDLogVerbose(@"Conversion ended: %@", self.sourcePath);
[center postNotificationName:XCCConversionDidEndNotification object:self userInfo:@{ @"projectId":self.projectId, @"path":self.sourcePath }];
}
}
- (void)notifyUserWithTitle:(NSString *)title message:(NSString *)message
{
NSDictionary *info = @{ @"projectId":self.projectId, @"title":title, @"message":message };
if (self.isCancelled)
return;
// nib2cib can take a while to run, show a message while the conversion is happening
[self.xcc wantUserNotificationWithInfo:info];
}
@end
@@ -0,0 +1,41 @@
//
// Release.xcconfig
// XcodeCapp
//
// Created by Aparajita on 4/18/13.
//
//
//:configuration = Release
ARCHS = $(ARCHS_STANDARD_64_BIT)
SDKROOT = macosx
ONLY_ACTIVE_ARCH = YES
DEBUG_INFORMATION_FORMAT = dwarf
COMBINE_HIDPI_IMAGES = YES
INSTALL_PATH = $(LOCAL_APPS_DIR)
MACOSX_DEPLOYMENT_TARGET = 10.6.8
COPY_PHASE_STRIP = YES
INFOPLIST_FILE = XcodeCapp/Info.plist
PRODUCT_NAME = XcodeCapp
ALWAYS_SEARCH_USER_PATHS = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "$(SRCROOT)/XcodeCapp"
GCC_OPTIMIZATION_LEVEL = s
CLANG_ENABLE_OBJC_ARC = YES
GCC_PRECOMPILE_PREFIX_HEADER = YES
GCC_PREFIX_HEADER = XcodeCapp/XcodeCapp-Prefix.pch
GCC_PREPROCESSOR_DEFINITIONS =
GCC_WARN_ABOUT_RETURN_TYPE = YES
GCC_WARN_UNUSED_VARIABLE = YES
WRAPPER_EXTENSION = app
GCC_C_LANGUAGE_STANDARD = gnu99
CLANG_CXX_LANGUAGE_STANDARD = gnu++0x
CLANG_CXX_LIBRARY = libc++
CLANG_WARN_EMPTY_BODY = YES
CLANG_WARN_CONSTANT_CONVERSION = YES
GCC_WARN_64_TO_32_BIT_CONVERSION = YES
CLANG_WARN_ENUM_CONVERSION = YES
CLANG_WARN_INT_CONVERSION = YES
GCC_WARN_ABOUT_RETURN_TYPE = YES
GCC_WARN_UNINITIALIZED_AUTOS = YES
GCC_WARN_UNUSED_VARIABLE = YES
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 874 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 943 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 619 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 713 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 719 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

@@ -0,0 +1,72 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXFileReference section */
E164FE29172185F500263CE3 /* Resources */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Resources; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXGroup section */
E164FE221721857400263CE3 = {
isa = PBXGroup;
children = (
E164FE29172185F500263CE3 /* Resources */,
);
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXProject section */
E164FE231721857400263CE3 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0460;
};
buildConfigurationList = E164FE261721857400263CE3 /* Build configuration list for PBXProject "Cappuccino" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = E164FE221721857400263CE3;
projectDirPath = "";
projectRoot = "";
targets = (
);
};
/* End PBXProject section */
/* Begin XCBuildConfiguration section */
E164FE271721857400263CE3 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
};
name = Debug;
};
E164FE281721857400263CE3 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
E164FE261721857400263CE3 /* Build configuration list for PBXProject "Cappuccino" */ = {
isa = XCConfigurationList;
buildConfigurations = (
E164FE271721857400263CE3 /* Debug */,
E164FE281721857400263CE3 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = E164FE231721857400263CE3 /* Project object */;
}
@@ -1,16 +1,16 @@
# Copyright 2012 Calvin Rien
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# A pbxproj file is an OpenStep format plist
# {} represents dictionary of key=value pairs delimited by ;
@@ -32,11 +32,22 @@
# the pbxproj file. Plutil is available in OS X 10.2 and higher
# Plutil can't write OpenStep plists, so I save as XML
import re, uuid, sys, os, shutil, subprocess, datetime, json
import datetime
import json
import ntpath
import os
import plistlib
import re
import shutil
import subprocess
import uuid
from UserDict import IterableUserDict
from UserList import UserList
regex = '[a-zA-Z0-9\\._/-]*'
class PBXEncoder(json.JSONEncoder):
def default(self, obj):
@@ -51,7 +62,7 @@ class PBXEncoder(json.JSONEncoder):
class PBXDict(IterableUserDict):
def __init__(self, d=None):
if d:
d = dict([(PBXType.Convert(k),PBXType.Convert(v)) for k,v in d.items()])
d = dict([(PBXType.Convert(k), PBXType.Convert(v)) for k, v in d.items()])
IterableUserDict.__init__(self, d)
@@ -62,7 +73,6 @@ class PBXDict(IterableUserDict):
self.data.pop(PBXType.Convert(key), None)
class PBXList(UserList):
def __init__(self, l=None):
if isinstance(l, basestring):
@@ -97,7 +107,7 @@ class PBXType(PBXDict):
def __init__(self, d=None):
PBXDict.__init__(self, d)
if not self.has_key('isa'):
if 'isa' not in self:
self['isa'] = self.__class__.__name__
self.id = None
@@ -140,18 +150,20 @@ class PBXFileReference(PBXType):
self.build_phase = None
types = {
'.a':('archive.ar', 'PBXFrameworksBuildPhase'),
'.a': ('archive.ar', 'PBXFrameworksBuildPhase'),
'.app': ('wrapper.application', None),
'.s': ('sourcecode.asm', 'PBXSourcesBuildPhase'),
'.c': ('sourcecode.c.c', 'PBXSourcesBuildPhase'),
'.cpp': ('sourcecode.cpp.cpp', 'PBXSourcesBuildPhase'),
'.framework': ('wrapper.framework','PBXFrameworksBuildPhase'),
'.framework': ('wrapper.framework', 'PBXFrameworksBuildPhase'),
'.h': ('sourcecode.c.h', None),
'.icns': ('image.icns','PBXResourcesBuildPhase'),
'.icns': ('image.icns', 'PBXResourcesBuildPhase'),
'.m': ('sourcecode.c.objc', 'PBXSourcesBuildPhase'),
'.j': ('sourcecode.c.objc', 'PBXSourcesBuildPhase'),
'.mm': ('sourcecode.cpp.objcpp', 'PBXSourcesBuildPhase'),
'.nib': ('wrapper.nib', 'PBXResourcesBuildPhase'),
'.plist': ('text.plist.xml', 'PBXResourcesBuildPhase'),
'.json': ('text.json', 'PBXResourcesBuildPhase'),
'.png': ('image.png', 'PBXResourcesBuildPhase'),
'.rtf': ('text.rtf', 'PBXResourcesBuildPhase'),
'.tiff': ('image.tiff', 'PBXResourcesBuildPhase'),
@@ -172,17 +184,22 @@ class PBXFileReference(PBXType):
'SOURCE_ROOT',
]
def guess_file_type(self):
def guess_file_type(self, ignore_unknown_type=True):
self.remove('explicitFileType')
self.remove('lastKnownFileType')
ext = os.path.splitext(self.get('name', ''))[1]
f_type, build_phase = PBXFileReference.types.get(ext, ('?', None))
if os.path.isdir(self.get('path')):
f_type = 'folder'
build_phase = None
ext = ''
else:
ext = os.path.splitext(self.get('name', ''))[1]
f_type, build_phase = PBXFileReference.types.get(ext, ('?', None))
self['lastKnownFileType'] = f_type
self.build_phase = build_phase
if f_type == '?':
if f_type == '?' and not ignore_unknown_type:
print 'unknown file extension: %s' % ext
print 'please add extension and Xcode type to PBXFileReference.types'
@@ -195,7 +212,7 @@ class PBXFileReference(PBXType):
self['explicitFileType'] = ft
@classmethod
def Create(cls, os_path, tree='SOURCE_ROOT'):
def Create(cls, os_path, tree='SOURCE_ROOT', ignore_unknown_type=True):
if tree not in cls.trees:
print 'Not a valid sourceTree type: %s' % tree
return None
@@ -205,10 +222,11 @@ class PBXFileReference(PBXType):
fr['path'] = os_path
fr['name'] = os.path.split(os_path)[1]
fr['sourceTree'] = '<absolute>' if os.path.isabs(os_path) else tree
fr.guess_file_type()
fr.guess_file_type(ignore_unknown_type=ignore_unknown_type)
return fr
class PBXBuildFile(PBXType):
def set_weak_link(self, weak=False):
k_settings = 'settings'
@@ -218,7 +236,7 @@ class PBXBuildFile(PBXType):
if not s:
if weak:
self[k_settings] = PBXDict({k_attributes:PBXList(['Weak'])})
self[k_settings] = PBXDict({k_attributes: PBXList(['Weak'])})
return True
@@ -243,10 +261,10 @@ class PBXBuildFile(PBXType):
k_settings = 'settings'
k_attributes = 'COMPILER_FLAGS'
if not self.has_key(k_settings):
if k_settings not in self:
self[k_settings] = PBXDict()
if not self[k_settings].has_key(k_attributes):
if k_attributes not in self[k_settings]:
self[k_settings][k_attributes] = flag
return True
@@ -273,6 +291,7 @@ class PBXBuildFile(PBXType):
return bf
class PBXGroup(PBXType):
def add_child(self, ref):
if not isinstance(ref, PBXDict):
@@ -283,7 +302,7 @@ class PBXGroup(PBXType):
if isa != 'PBXFileReference' and isa != 'PBXGroup':
return None
if not self.has_key('children'):
if 'children' not in self:
self['children'] = PBXList()
self['children'].add(ref.id)
@@ -291,7 +310,7 @@ class PBXGroup(PBXType):
return ref.id
def remove_child(self, id):
if not self.has_key('children'):
if 'children' not in self:
self['children'] = PBXList()
return
@@ -301,7 +320,7 @@ class PBXGroup(PBXType):
self['children'].remove(id)
def has_child(self, id):
if not self.has_key('children'):
if 'children' not in self:
self['children'] = PBXList()
return False
@@ -311,7 +330,7 @@ class PBXGroup(PBXType):
return id in self['children']
def get_name(self):
path_name = os.path.split(self.get('path',''))[1]
path_name = os.path.split(self.get('path', ''))[1]
return self.get('name', path_name)
@classmethod
@@ -350,12 +369,16 @@ class PBXVariantGroup(PBXType):
pass
class PBXTargetDependency(PBXType):
pass
class PBXBuildPhase(PBXType):
def add_build_file(self, bf):
if bf.get('isa') != 'PBXBuildFile':
return False
if not self.has_key('files'):
if 'files' not in self:
self['files'] = PBXList()
self['files'].add(bf.id)
@@ -363,14 +386,14 @@ class PBXBuildPhase(PBXType):
return True
def remove_build_file(self, id):
if not self.has_key('files'):
if 'files' not in self:
self['files'] = PBXList()
return
self['files'].remove(id)
def has_build_file(self, id):
if not self.has_key('files'):
if 'files' not in self:
self['files'] = PBXList()
return False
@@ -401,26 +424,30 @@ class PBXCopyFilesBuildPhase(PBXBuildPhase):
class XCBuildConfiguration(PBXType):
def add_search_paths(self, paths, base, key, recursive=True):
def add_search_paths(self, paths, base, key, recursive=True, escape=True):
modified = False
if not isinstance(paths, list):
paths = [paths]
if not self.has_key(base):
if base not in self:
self[base] = PBXDict()
for path in paths:
if recursive and not path.endswith('/**'):
path = os.path.join(path, '**')
if not self[base].has_key(key):
if key not in self[base]:
self[base][key] = PBXList()
elif isinstance(self[base][key], basestring):
self[base][key] = PBXList(self[base][key])
if self[base][key].add('\\"%s\\"' % path):
modified = True
if escape:
if self[base][key].add('\\"%s\\"' % path): # '\\"%s\\"' % path
modified = True
else:
if self[base][key].add(path): # '\\"%s\\"' % path
modified = True
return modified
@@ -430,6 +457,9 @@ class XCBuildConfiguration(PBXType):
def add_library_search_paths(self, paths, recursive=True):
return self.add_search_paths(paths, 'buildSettings', 'LIBRARY_SEARCH_PATHS', recursive=recursive)
def add_framework_search_paths(self, paths, recursive=True):
return self.add_search_paths(paths, 'buildSettings', 'FRAMEWORK_SEARCH_PATHS', recursive=recursive, escape=False)
def add_other_cflags(self, flags):
modified = False
@@ -439,12 +469,11 @@ class XCBuildConfiguration(PBXType):
if isinstance(flags, basestring):
flags = PBXList(flags)
if not self.has_key(base):
if base not in self:
self[base] = PBXDict()
for flag in flags:
if not self[base].has_key(key):
if key not in self[base]:
self[base][key] = PBXList()
elif isinstance(self[base][key], basestring):
self[base][key] = PBXList(self[base][key])
@@ -455,6 +484,52 @@ class XCBuildConfiguration(PBXType):
return modified
def add_other_ldflags(self, flags):
modified = False
base = 'buildSettings'
key = 'OTHER_LDFLAGS'
if isinstance(flags, basestring):
flags = PBXList(flags)
if base not in self:
self[base] = PBXDict()
for flag in flags:
if key not in self[base]:
self[base][key] = PBXList()
elif isinstance(self[base][key], basestring):
self[base][key] = PBXList(self[base][key])
if self[base][key].add(flag):
self[base][key] = [e for e in self[base][key] if e]
modified = True
return modified
def remove_other_ldflags(self, flags):
modified = False
base = 'buildSettings'
key = 'OTHER_LDFLAGS'
if isinstance(flags, basestring):
flags = PBXList(flags)
if base in self: # there are flags, so we can "remove" something
for flag in flags:
if key not in self[base]:
return False
elif isinstance(self[base][key], basestring):
self[base][key] = PBXList(self[base][key])
if self[base][key].remove(flag):
self[base][key] = [e for e in self[base][key] if e]
modified = True
return modified
class XCConfigurationList(PBXType):
pass
@@ -468,7 +543,7 @@ class XcodeProject(PBXDict):
if not path:
path = os.path.join(os.getcwd(), 'project.pbxproj')
self.pbxproj_path =os.path.abspath(path)
self.pbxproj_path = os.path.abspath(path)
self.source_root = os.path.abspath(os.path.join(os.path.split(path)[0], '..'))
IterableUserDict.__init__(self, d)
@@ -478,6 +553,7 @@ class XcodeProject(PBXDict):
self.modified = False
root_id = self.get('rootObject')
if root_id:
self.root_object = self.objects[root_id]
root_group_id = self.root_object.get('mainGroup')
@@ -487,7 +563,7 @@ class XcodeProject(PBXDict):
self.root_object = None
self.root_group = None
for k,v in self.objects.iteritems():
for k, v in self.objects.iteritems():
v.id = k
def add_other_cflags(self, flags):
@@ -497,6 +573,20 @@ class XcodeProject(PBXDict):
if b.add_other_cflags(flags):
self.modified = True
def add_other_ldflags(self, flags):
build_configs = [b for b in self.objects.values() if b.get('isa') == 'XCBuildConfiguration']
for b in build_configs:
if b.add_other_ldflags(flags):
self.modified = True
def remove_other_ldflags(self, flags):
build_configs = [b for b in self.objects.values() if b.get('isa') == 'XCBuildConfiguration']
for b in build_configs:
if b.remove_other_ldflags(flags):
self.modified = True
def add_header_search_paths(self, paths, recursive=True):
build_configs = [b for b in self.objects.values() if b.get('isa') == 'XCBuildConfiguration']
@@ -504,6 +594,13 @@ class XcodeProject(PBXDict):
if b.add_header_search_paths(paths, recursive):
self.modified = True
def add_framework_search_paths(self, paths, recursive=True):
build_configs = [b for b in self.objects.values() if b.get('isa') == 'XCBuildConfiguration']
for b in build_configs:
if b.add_framework_search_paths(paths, recursive):
self.modified = True
def add_library_search_paths(self, paths, recursive=True):
build_configs = [b for b in self.objects.values() if b.get('isa') == 'XCBuildConfiguration']
@@ -516,9 +613,13 @@ class XcodeProject(PBXDict):
def get_obj(self, id):
return self.objects.get(id)
def get_ids(self):
return self.objects.keys()
def get_files_by_os_path(self, os_path, tree='SOURCE_ROOT'):
files = [f for f in self.objects.values() if f.get('isa') == 'PBXFileReference'
and f.get('path') == os_path]
and f.get('path') == os_path
and f.get('sourceTree') == tree]
return files
@@ -579,7 +680,7 @@ class XcodeProject(PBXDict):
path = os.path.abspath(path)
groups = [g for g in self.objects.values() if g.get('isa') == 'PBXGroup'
and os.path.abspath(g.get('path','/dev/null')) == path]
and os.path.abspath(g.get('path', '/dev/null')) == path]
return groups
@@ -618,7 +719,7 @@ class XcodeProject(PBXDict):
# assume it's an id
parent = self.objects.get(parent, self.root_group)
path_dict = {os.path.split(os_path)[0]:parent}
path_dict = {os.path.split(os_path)[0]: parent}
special_list = []
for (grp_path, subdirs, files) in os.walk(os_path):
@@ -635,7 +736,6 @@ class XcodeProject(PBXDict):
if os.path.splitext(grp_path)[1] in XcodeProject.special_folders:
# if this file has a special extension (bundle or framework mainly) treat it as a file
special_list.append(grp_path)
new_files = self.verify_files([folder_name], parent=parent)
if new_files:
@@ -644,7 +744,7 @@ class XcodeProject(PBXDict):
continue
# create group
grp = self.get_or_create_group(folder_name, path=self.get_relative_path(grp_path) , parent=parent)
grp = self.get_or_create_group(folder_name, path=self.get_relative_path(grp_path), parent=parent)
path_dict[grp_path] = grp
results.append(grp)
@@ -652,7 +752,7 @@ class XcodeProject(PBXDict):
file_dict = {}
for f in files:
if f[0] == '.' or [m for m in excludes if re.match(m,f)]:
if f[0] == '.' or [m for m in excludes if re.match(m, f)]:
continue
kwds = {
@@ -667,7 +767,7 @@ class XcodeProject(PBXDict):
new_files = self.verify_files([n.get('name') for n in file_dict.values()], parent=grp)
add_files = [(k,v) for k,v in file_dict.items() if v.get('name') in new_files]
add_files = [(k, v) for k, v in file_dict.items() if v.get('name') in new_files]
for path, kwds in add_files:
kwds.pop('name', None)
@@ -682,9 +782,20 @@ class XcodeProject(PBXDict):
return results
def add_file(self, f_path, parent=None, tree='SOURCE_ROOT', create_build_files=True, weak=False):
results = []
def path_leaf(self, path):
head, tail = ntpath.split(path)
return tail or ntpath.basename(head)
def add_file_if_doesnt_exist(self, f_path, parent=None, tree='SOURCE_ROOT', create_build_files=True, weak=False, ignore_unknown_type=True):
for obj in self.objects.values():
if 'path' in obj:
if self.path_leaf(f_path) == self.path_leaf(obj.get('path')):
return []
return self.add_file(f_path, parent, tree, create_build_files, weak, ignore_unknown_type=ignore_unknown_type)
def add_file(self, f_path, parent=None, tree='SOURCE_ROOT', create_build_files=True, weak=False, ignore_unknown_type=True):
results = []
abs_path = ''
if os.path.isabs(f_path):
@@ -703,9 +814,10 @@ class XcodeProject(PBXDict):
# assume it's an id
parent = self.objects.get(parent, self.root_group)
file_ref = PBXFileReference.Create(f_path, tree)
file_ref = PBXFileReference.Create(f_path, tree, ignore_unknown_type=ignore_unknown_type)
parent.add_child(file_ref)
results.append(file_ref)
# create a build file for the file ref
if file_ref.build_phase and create_build_files:
phases = self.get_build_phases(file_ref.build_phase)
@@ -716,12 +828,17 @@ class XcodeProject(PBXDict):
phase.add_build_file(build_file)
results.append(build_file)
if abs_path and tree == 'SOURCE_ROOT' and os.path.isfile(abs_path)\
if abs_path and tree == 'SOURCE_ROOT' \
and os.path.isfile(abs_path) \
and file_ref.build_phase == 'PBXFrameworksBuildPhase':
library_path = os.path.join('$(SRCROOT)', os.path.split(f_path)[0])
self.add_library_search_paths([library_path], recursive=False)
library_path = os.path.join('$(SRCROOT)', os.path.split(f_path)[0])
self.add_library_search_paths([library_path], recursive=False)
if abs_path and tree == 'SOURCE_ROOT' \
and not os.path.isfile(abs_path) \
and file_ref.build_phase == 'PBXFrameworksBuildPhase':
framework_path = os.path.join('$(SRCROOT)', os.path.split(f_path)[0])
self.add_framework_search_paths([framework_path, '$(inherited)'], recursive=False)
for r in results:
self.objects[r.id] = r
@@ -731,14 +848,44 @@ class XcodeProject(PBXDict):
return results
def check_and_repair_framework(self, base):
name = os.path.basename(base)
if ".framework" in name:
basename = name[:-len(".framework")]
finalHeaders = os.path.join(base, "Headers")
finalCurrent = os.path.join(base, "Versions/Current")
finalLib = os.path.join(base, basename)
srcHeaders = "Versions/A/Headers"
srcCurrent = "A"
srcLib = "Versions/A/" + basename
if not os.path.exists(finalHeaders):
os.symlink(srcHeaders, finalHeaders)
if not os.path.exists(finalCurrent):
os.symlink(srcCurrent, finalCurrent)
if not os.path.exists(finalLib):
os.symlink(srcLib, finalLib)
def remove_group(self, grp):
pass
def remove_file(self, path, parent):
files = self.get_files_by_os_path(path)
for r in files:
del self.objects[r.id]
self.modified = True
def remove_file(self, id, recursive=True):
if not PBXType.IsGuid(id):
id = id.id
if id in self.objects:
self.objects.remove(id)
if recursive:
groups = [g for g in self.objects.values() if g.get('isa') == 'PBXGroup']
for group in groups:
if id in group['children']:
group.remove_child(id)
self.modified = True
def move_file(self, id, dest_grp=None):
pass
@@ -750,7 +897,7 @@ class XcodeProject(PBXDict):
print 'applying "%s" to "%s"' % (patch_path, xcode_path)
return subprocess.call(['patch', '-p1', '--forward', '--directory=%s'%xcode_path, '--input=%s'%patch_path])
return subprocess.call(['patch', '-p1', '--forward', '--directory=%s' % xcode_path, '--input=%s' % patch_path])
def apply_mods(self, mod_dict, default_path=None):
if not default_path:
@@ -760,7 +907,6 @@ class XcodeProject(PBXDict):
for k in keys:
v = mod_dict.pop(k)
mod_dict[k.lower()] = v
parent = mod_dict.pop('group', None)
@@ -775,7 +921,7 @@ class XcodeProject(PBXDict):
compiler_flags = mod_dict.pop('compiler_flags', {})
for k,v in mod_dict.items():
for k, v in mod_dict.items():
if k == 'patches':
for p in v:
if not os.path.isabs(p):
@@ -822,7 +968,6 @@ class XcodeProject(PBXDict):
continue
p = self.get_relative_path(p)
paths.append(os.path.join('$(SRCROOT)', p, "**"))
if k == 'headerpaths':
@@ -831,6 +976,8 @@ class XcodeProject(PBXDict):
self.add_library_search_paths(paths)
elif k == 'other_cflags':
self.add_other_cflags(v)
elif k == 'other_ldflags':
self.add_other_ldflags(v)
elif k == 'libs' or k == 'frameworks' or k == 'files':
paths = {}
@@ -847,7 +994,7 @@ class XcodeProject(PBXDict):
file_path = os.path.join(default_path, p)
search_path, file_name = os.path.split(file_path)
if [m for m in excludes if re.match(m,file_name)]:
if [m for m in excludes if re.match(m, file_name)]:
continue
try:
@@ -859,10 +1006,10 @@ class XcodeProject(PBXDict):
file_list = os.listdir(search_path)
for f in file_list:
if [m for m in excludes if re.match(m,f)]:
if [m for m in excludes if re.match(m, f)]:
continue
if re.search(expr,f):
if re.search(expr, f):
kwds['name'] = f
paths[os.path.join(search_path, f)] = kwds
p = None
@@ -876,10 +1023,10 @@ class XcodeProject(PBXDict):
kwds['name'] = file_name
if k == 'libs':
p = os.path.join('usr','lib',p)
p = os.path.join('usr', 'lib', p)
kwds['tree'] = 'SDKROOT'
elif k == 'frameworks':
p = os.path.join('System','Library','Frameworks',p)
p = os.path.join('System', 'Library', 'Frameworks', p)
kwds['tree'] = 'SDKROOT'
elif k == 'files' and not os.path.exists(file_path):
# don't add non-existent files to the project.
@@ -888,26 +1035,24 @@ class XcodeProject(PBXDict):
paths[p] = kwds
new_files = self.verify_files([n.get('name') for n in paths.values()])
add_files = [(k,v) for k,v in paths.items() if v.get('name') in new_files]
add_files = [(k, v) for k, v in paths.items() if v.get('name') in new_files]
for path, kwds in add_files:
kwds.pop('name', None)
if not kwds.has_key('parent') and parent:
if 'parent' not in kwds and parent:
kwds['parent'] = parent
self.add_file(path, **kwds)
if compiler_flags:
for k,v in compiler_flags.items():
for k, v in compiler_flags.items():
filerefs = []
for f in v:
filerefs.extend([fr.id for fr in self.objects.values() if fr.get('isa') == 'PBXFileReference'
and fr.get('name') == f])
buildfiles = [bf for bf in self.objects.values() if bf.get('isa') == 'PBXBuildFile'
and bf.get('fileRef') in filerefs]
@@ -915,54 +1060,268 @@ class XcodeProject(PBXDict):
if bf.add_compiler_flag(k):
self.modified = True
def backup(self, file_name=None):
def backup(self, file_name=None, backup_name=None):
if not file_name:
file_name = self.pbxproj_path
backup_name = "%s.%s.backup" % (file_name, datetime.datetime.now().strftime('%d%m%y-%H%M%S'))
if not backup_name:
backup_name = "%s.%s.backup" % (file_name, datetime.datetime.now().strftime('%d%m%y-%H%M%S'))
shutil.copy2(file_name, backup_name)
def save(self, file_name=None):
"""Saves in old (xml) format"""
if not file_name:
file_name = self.pbxproj_path
# JSON serialize the project and convert that json to an xml plist
p = subprocess.Popen([XcodeProject.plutil_path, '-convert', 'xml1', '-o', file_name, '-'], stdin=subprocess.PIPE)
p.communicate(PBXEncoder().encode(self.data))
# This code is adapted from plistlib.writePlist
with open(file_name, "w") as f:
writer = PBXWriter(f)
writer.writeln("<plist version=\"1.0\">")
writer.writeValue(self.data)
writer.writeln("</plist>")
def saveFormat3_2(self, file_name=None):
"""Save in Xcode 3.2 compatible (new) format"""
if not file_name:
file_name = self.pbxproj_path
# process to get the section's info and names
objs = self.data.get('objects')
sections = dict()
uuids = dict()
for key in objs:
l = list()
if objs.get(key).get('isa') in sections:
l = sections.get(objs.get(key).get('isa'))
l.append(tuple([key, objs.get(key)]))
sections[objs.get(key).get('isa')] = l
if 'name' in objs.get(key):
uuids[key] = objs.get(key).get('name')
elif 'path' in objs.get(key):
uuids[key] = objs.get(key).get('path')
else:
if objs.get(key).get('isa') == 'PBXProject':
uuids[objs.get(key).get('buildConfigurationList')] = 'Build configuration list for PBXProject "Unity-iPhone"'
elif objs.get(key).get('isa')[0:3] == 'PBX':
uuids[key] = objs.get(key).get('isa')[3:-10]
else:
uuids[key] = 'Build configuration list for PBXNativeTarget "TARGET_NAME"'
ro = self.data.get('rootObject')
uuids[ro] = 'Project Object'
for key in objs:
# transitive references (used in the BuildFile section)
if 'fileRef' in objs.get(key) and objs.get(key).get('fileRef') in uuids:
uuids[key] = uuids[objs.get(key).get('fileRef')]
# transitive reference to the target name (used in the Native target section)
if objs.get(key).get('isa') == 'PBXNativeTarget':
uuids[objs.get(key).get('buildConfigurationList')] = uuids[objs.get(key).get('buildConfigurationList')].replace('TARGET_NAME', uuids[key])
self.uuids = uuids
self.sections = sections
out = open(file_name, 'w')
out.write('// !$*UTF8*$!\n')
self._printNewXCodeFormat(out, self.data, '', enters=True)
out.close()
@classmethod
def addslashes(cls, s):
d = {'"': '\\"', "'": "\\'", "\0": "\\\0", "\\": "\\\\"}
return ''.join(d.get(c, c) for c in s)
def _printNewXCodeFormat(self, out, root, deep, enters=True):
if isinstance(root, IterableUserDict):
out.write('{')
if enters:
out.write('\n')
isa = root.pop('isa', '')
if isa != '': # keep the isa in the first spot
if enters:
out.write('\t' + deep)
out.write('isa = ')
self._printNewXCodeFormat(out, isa, '\t' + deep, enters=enters)
out.write(';')
if enters:
out.write('\n')
else:
out.write(' ')
for key in sorted(root.iterkeys()): # keep the same order as Apple.
if enters:
out.write('\t' + deep)
if re.match(regex, key).group(0) == key:
out.write(key.encode("utf-8") + ' = ')
else:
out.write('"' + key.encode("utf-8") + '" = ')
if key == 'objects':
out.write('{') # open the objects section
if enters:
out.write('\n')
#root.remove('objects') # remove it to avoid problems
sections = [
('PBXBuildFile', False),
('PBXCopyFilesBuildPhase', True),
('PBXFileReference', False),
('PBXFrameworksBuildPhase', True),
('PBXGroup', True),
('PBXNativeTarget', True),
('PBXProject', True),
('PBXResourcesBuildPhase', True),
('PBXShellScriptBuildPhase', True),
('PBXSourcesBuildPhase', True),
('XCBuildConfiguration', True),
('XCConfigurationList', True),
('PBXTargetDependency', True),
('PBXVariantGroup', True),
('PBXReferenceProxy', True),
('PBXContainerItemProxy', True)]
for section in sections: # iterate over the sections
if self.sections.get(section[0]) is None:
continue
out.write('\n/* Begin %s section */' % section[0].encode("utf-8"))
self.sections.get(section[0]).sort(cmp=lambda x, y: cmp(x[0], y[0]))
for pair in self.sections.get(section[0]):
key = pair[0]
value = pair[1]
out.write('\n')
if enters:
out.write('\t\t' + deep)
out.write(key.encode("utf-8"))
if key in self.uuids:
out.write(" /* " + self.uuids[key].encode("utf-8") + " */")
out.write(" = ")
self._printNewXCodeFormat(out, value, '\t\t' + deep, enters=section[1])
out.write(';')
out.write('\n/* End %s section */\n' % section[0].encode("utf-8"))
out.write(deep + '\t}') # close of the objects section
else:
self._printNewXCodeFormat(out, root[key], '\t' + deep, enters=enters)
out.write(';')
if enters:
out.write('\n')
else:
out.write(' ')
root['isa'] = isa # restore the isa for further calls
if enters:
out.write(deep)
out.write('}')
elif isinstance(root, UserList):
out.write('(')
if enters:
out.write('\n')
for value in root:
if enters:
out.write('\t' + deep)
self._printNewXCodeFormat(out, value, '\t' + deep, enters=enters)
out.write(',')
if enters:
out.write('\n')
if enters:
out.write(deep)
out.write(')')
else:
if len(root) > 0 and re.match(regex, root).group(0) == root:
out.write(root.encode("utf-8"))
else:
out.write('"' + XcodeProject.addslashes(root.encode("utf-8")) + '"')
if root in self.uuids:
out.write(" /* " + self.uuids[root].encode("utf-8") + " */")
@classmethod
def Load(cls, path):
cls.plutil_path = os.path.join(os.path.split(__file__)[0], 'plutil')
if not os.path.isfile(XcodeProject.plutil_path):
cls.plutil_path = 'plutil'
if subprocess.call([XcodeProject.plutil_path,'-lint','-s',path]):
print 'ERROR: not a valid .pbxproj file'
# load project by converting to xml and then convert that using plistlib
p = subprocess.Popen([XcodeProject.plutil_path, '-convert', 'xml1', '-o', '-', path], stdout=subprocess.PIPE)
stdout, stderr = p.communicate()
# If the plist was malformed, returncode will be non-zero
if p.returncode != 0:
print stdout
return None
# load project by converting to JSON and parse
p = subprocess.Popen([XcodeProject.plutil_path, '-convert', 'json', '-o', '-', path], stdout=subprocess.PIPE)
tree = json.loads(p.communicate()[0])
tree = plistlib.readPlistFromString(stdout)
return XcodeProject(tree, path)
def test(argv=None):
if not argv:
argv = sys.argv
# The code below was adapted from plistlib.py.
proj = XcodeProject.Load('../../Build/Unity-iPhone.xcodeproj/project.pbxproj')
class PBXWriter(plistlib.PlistWriter):
def writeValue(self, value):
if isinstance(value, (PBXList, PBXDict)):
plistlib.PlistWriter.writeValue(self, value.data)
else:
plistlib.PlistWriter.writeValue(self, value)
def simpleElement(self, element, value=None):
"""
We have to override this method to deal with Unicode text correctly.
Non-ascii characters have to get encoded as character references.
"""
if value is not None:
value = _escapeAndEncode(value)
self.writeln("<%s>%s</%s>" % (element, value, element))
else:
self.writeln("<%s/>" % element)
proj.add_folder('../Assets/Editor/Airship/UI/Default/StoreFront')
proj.add_file('../Assets/Editor/Airship/libUAirship-1.1.4.a')
proj.add_file('../Assets/Plugins/Airship/AirshipConfig.plist')
# Regex to find any control chars, except for \t \n and \r
_controlCharPat = re.compile(
r"[\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0b\x0c\x0e\x0f"
r"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f]")
proj.backup()
proj.save()
print str(proj)
def _escapeAndEncode(text):
m = _controlCharPat.search(text)
if m is not None:
raise ValueError("strings can't contains control characters; "
"use plistlib.Data instead")
text = text.replace("\r\n", "\n") # convert DOS line endings
text = text.replace("\r", "\n") # convert Mac line endings
text = text.replace("&", "&amp;") # escape '&'
text = text.replace("<", "&lt;") # escape '<'
text = text.replace(">", "&gt;") # escape '>'
return text.encode("ascii", "xmlcharrefreplace") # encode as ascii with xml character references
@@ -2,7 +2,7 @@
* parser.j
*
* Created by Francisco Tolmasky.
* Modified by Antoine Mercadal, with great help of Martin Carlberg
* Modified by Antoine Mercadal, with great help from Martin Carlberg
* Copyright 2008-2013, 280 North, Inc.
*
* This library is free software; you can redistribute it and/or
@@ -22,29 +22,37 @@
@import <Foundation/Foundation.j>
var FILE = require("file");
var FILE = require("file"),
OS = require("os"),
stream = require("narwhal/term").stream,
SLASH_REPLACEMENT = "∕"; // DIVISION SLASH, Unicode: U+2215
// Debug function to print some JS objects
function dump(obj)
{
CPLogPrint(JSON.stringify(obj));
print(JSON.stringify(obj));
}
var xcc = ObjectiveJ.acorn.walk.make(
function raise(pos, message)
{
var syntaxError = new SyntaxError(message);
syntaxError.line = pos.line;
throw syntaxError;
}
var errors = [],
xcc = ObjectiveJ.acorn.walk.make(
{
ClassDeclarationStatement: function(node, st, c)
{
if (node.categoryname)
{
CPLogPrint("Categories are not supported yet. Ignoring it.");
return;
}
var className = node.classname.name,
superclassname = node.superclassname.name,
superclassname = node.superclassname ? node.superclassname.name : "",
declaredOutletsName = [],
classInfo = {
"name": className,
"category": node.categoryname ? node.categoryname.name : "",
"superClass": superclassname,
"outlets": [],
"actions": [],
@@ -63,7 +71,7 @@ var xcc = ObjectiveJ.acorn.walk.make(
if (ivarHasOutlet)
{
if (declaredOutletsName.indexOf(ivarName) !== -1)
throw("Outlet named '" + ivarName + "' is declared multiple times.");
raise(ivarDecl.loc.start, "Outlet '" + ivarName + "' declared more than once");
declaredOutletsName.push(ivarName);
classInfo.outlets.push({"type": ivarType, "name": ivarName});
@@ -84,29 +92,31 @@ var xcc = ObjectiveJ.acorn.walk.make(
methodReturnType = [node.returntype ? node.returntype.name : "id"],
methodHasAction = node.action ? "IBAction" : null,
selector = selectors[0].name,
actionInformations = {"name": selector, "arguments":[]};
actionInfo = {"name": selector, "arguments":[]};
if (methodHasAction && arguments.length == 1)
if (methodHasAction)
{
if (st.actionNames.indexOf(selector) !== -1)
throw("Action named '" + selector + "' is declared multiple times.");
st.actionNames.push(selector);
for (var i = 0; i < arguments.length; i++)
if (arguments.length == 1)
{
var argument = arguments[i],
argumentName = argument.identifier.name,
argumentType = argument.type ? argument.type.name : null;
if (st.actionNames.indexOf(selector) !== -1)
raise(node.loc.start, "Action '" + selector + "' declared more than once");
actionInformations.arguments.push({"type": argumentType, "name": argumentName});
st.actionNames.push(selector);
for (var i = 0; i < arguments.length; i++)
{
var argument = arguments[i],
argumentName = argument.identifier.name,
argumentType = argument.type ? argument.type.name : null;
actionInfo.arguments.push({"type": argumentType, "name": argumentName});
}
st.actions.push(actionInfo)
}
st.actions.push(actionInformations)
else
raise(node.loc.start, "Action methods must have exactly one parameter");
}
else if (methodHasAction)
throw("Method '" + selector + "' is an action but has more than one parameter.");
}
}
);
@@ -121,63 +131,109 @@ function compile(node, state, visitor)
c(node, state);
};
function shadowBaseNameForPath(projectBasePath, path)
{
// Make the path project-relative
path = path.substring(projectBasePath.length + 1, path.length);
// strip the extension and replace slashes
return path.substring(0, path.length - 2).replace(/[/]/g, SLASH_REPLACEMENT);
}
/*
$1 Project base path
$2 Full project source path
*/
function main(args)
{
var fileURL = new CFURL(args[1]),
outputHeaderURL = new CFURL(args[2]),
outputSourceURL = new CFURL(args[3]),
source = FILE.read(fileURL, { charset: "UTF-8" }),
flags = ObjectiveJ.Preprocessor.Flags.IncludeDebugSymbols | ObjectiveJ.Preprocessor.Flags.IncludeTypeSignatures,
tokens = ObjectiveJ.acorn.parse(source),
classesInformation = [],
ObjectiveCSource = "",
ObjectiveCHeader = "";
compile(tokens, classesInformation, xcc);
// dump(classesInformation)
ObjectiveCHeader +=
"#import <Foundation/Foundation.h>\n" +
"#import <Cocoa/Cocoa.h>\n" +
"#import \"xcc_general_include.h\"\n\n";
ObjectiveCSource += "#import \"" + outputHeaderURL.absoluteString().replace(/\\/g,'/').replace(/(.*\/)/g, '') + "\"\n\n";
// Traverse each found classes
classesInformation.forEach(function(aClass)
try
{
// add new class definition
ObjectiveCHeader += "@interface " + aClass.name + " : " + NSCompatibleClassName(aClass.superClass) + "\n\n";
var projectBasePath = args[1],
sourcePath = args[2],
outputDirectory = [projectBasePath stringByAppendingPathComponent:@".XcodeSupport"],
baseFilename = shadowBaseNameForPath(projectBasePath, sourcePath),
outputHeaderURL = new CFURL([outputDirectory stringByAppendingPathComponent:baseFilename + ".h"]),
outputImplementationURL = new CFURL([outputDirectory stringByAppendingPathComponent:baseFilename + ".m"]),
source = FILE.read(sourcePath, { charset: "UTF-8" }),
flags = ObjectiveJ.Preprocessor.Flags.IncludeDebugSymbols | ObjectiveJ.Preprocessor.Flags.IncludeTypeSignatures,
tokens = ObjectiveJ.acorn.parse(source, { locations:true, sourceFile:sourcePath }),
classesInformation = [],
ObjectiveCSource = "",
ObjectiveCHeader = "",
hasErrors = NO;
// Add each outlets in header
aClass.outlets.forEach(function(anOutlet)
compile(tokens, classesInformation, xcc);
// dump(classesInformation)
ObjectiveCHeader +=
"#import <Cocoa/Cocoa.h>\n" +
'#import "xcc_general_include.h"\n';
ObjectiveCSource += "#import \"" + outputHeaderURL.lastPathComponent() + "\"\n";
// Traverse each found classes
classesInformation.forEach(function(aClass)
{
ObjectiveCHeader += "@property (assign) IBOutlet " + NSCompatibleClassName(anOutlet.type, YES) + " " + anOutlet.name + ";\n";
// add new class definition
if (aClass.superClass)
ObjectiveCHeader += [CPString stringWithFormat:@"\n@interface %@ : %@", aClass.name, NSCompatibleClassName(aClass.superClass)];
else
ObjectiveCHeader += [CPString stringWithFormat:@"\n@interface %@ (%@)", aClass.name, aClass.category];
// add each outlet in header
if (aClass.outlets.length > 0)
ObjectiveCHeader += "\n";
aClass.outlets.forEach(function(anOutlet)
{
ObjectiveCHeader += [CPString stringWithFormat:@"\n@property (assign) IBOutlet %@ %@;", NSCompatibleClassName(anOutlet.type, YES), anOutlet.name];
});
if (aClass.actions.length > 0)
ObjectiveCHeader += "\n";
// add each action in header
aClass.actions.forEach(function(anAction)
{
ObjectiveCHeader += [CPString stringWithFormat:@"\n- (IBAction)%@:(%@)%@;", anAction.name, anAction.arguments[0].type, anAction.arguments[0].name];
});
if (aClass.outlets.length > 0 || aClass.actions.length > 0)
ObjectiveCHeader += "\n";
ObjectiveCHeader += "\n@end\n";
// fill up the implementation file
ObjectiveCSource += "\n@implementation " + aClass.name + "\n@end\n";
});
ObjectiveCHeader += "\n";
if (ObjectiveCSource.length)
FILE.write(outputImplementationURL, ObjectiveCSource, { charset:"UTF-8" });
// Add each actions in header
aClass.actions.forEach(function(anAction)
{
ObjectiveCHeader += "- (IBAction)" + anAction.name + ":(" + anAction.arguments[0].type + ")" + anAction.arguments[0].name + ";\n";
});
if (ObjectiveCHeader.length)
FILE.write(outputHeaderURL, ObjectiveCHeader, { charset:"UTF-8" });
}
catch (e)
{
[errors addObject:@{
@"message": e.message,
@"path": sourcePath,
@"line": e.line
}];
ObjectiveCHeader += "\n@end\n\n\n";
hasErrors = YES;
}
// fill up the implementation file
ObjectiveCSource += "@implementation " + aClass.name + "\n@end\n\n";
});
if ([errors count])
{
var plist = [CPPropertyListSerialization dataFromPropertyList:errors format:CPPropertyListXMLFormat_v1_0];
// write files
if (ObjectiveCSource.length)
FILE.write(outputSourceURL, ObjectiveCSource, { charset:"UTF-8" });
stream.printError([plist rawString]);
if (ObjectiveCHeader.length)
FILE.write(outputHeaderURL, ObjectiveCHeader, { charset:"UTF-8" });
// If there were category warnings, hasErrors is NO, so return a warning status
OS.exit(hasErrors ? 1 : 2);
}
}
function NSCompatibleClassName(aClassName, asPointer)
@@ -514,6 +570,7 @@ var NSClasses = {
"NSStepper" : YES,
"NSStepperCell" : YES,
"NSString Application Kit Additions" : YES,
"NSTableCellView" : YES,
"NSTableColumn" : YES,
"NSTableHeaderCell" : YES,
"NSTableHeaderView" : YES,
+240
View File
@@ -0,0 +1,240 @@
# -*- coding: utf-8 -*-
import os.path
import re
import sys
from mod_pbxproj import XcodeProject
class PBXModifier (object):
XCODE_SUPPORT_FOLDER = u".XcodeSupport"
SLASH_REPLACEMENT = u"∕" # DIVISION SLASH Unicode U+2215
FRAMEWORKS_RE = re.compile(ur"^(.+/Frameworks/(?:Debug|Source)/([^/]+))/.+$")
XCC_GENERAL_INCLUDE = u"xcc_general_include.h"
def __init__(self, projectRootPath=None):
self.projectRootPath = projectRootPath
projectName = os.path.basename(projectRootPath)
self.pbxPath = os.path.join(projectRootPath, projectName + u".xcodeproj", u"project.pbxproj")
self.project = XcodeProject.Load(self.pbxPath)
self.shadowGroup = self.project.get_or_create_group(u"Cocoa Classes")
self.sourceGroup = self.project.get_or_create_group(u"Cappuccino Source")
self._frameworksGroup = None
@property
def frameworksGroup(self):
if self._frameworksGroup is None:
self._frameworksGroup = self.project.get_or_create_group(u"Frameworks", parent=self.sourceGroup)
return self._frameworksGroup
def update_general_include(self):
xcc_general_include_path = os.path.join(self.projectRootPath, self.XCODE_SUPPORT_FOLDER, self.XCC_GENERAL_INCLUDE)
content = u""
for path in os.listdir(os.path.join(self.projectRootPath, self.XCODE_SUPPORT_FOLDER)):
filename = unicode(os.path.basename(path))
if filename.endswith(".h") and filename != self.XCC_GENERAL_INCLUDE:
content += u'#include "{0}"\n'.format(filename)
f = open(xcc_general_include_path, "w")
f.write(content.encode("utf-8"))
f.close()
if len(self.project.get_files_by_os_path(os.path.join(self.XCODE_SUPPORT_FOLDER, self.XCC_GENERAL_INCLUDE))) == 0:
self.project.add_file(xcc_general_include_path, parent=self.shadowGroup)
def file_with_path(self, projectSourcePath):
relativePath = os.path.relpath(projectSourcePath, self.projectRootPath)
for fileRef in [f for f in self.project.objects.values() if f.get("isa") == "PBXFileReference"]:
filePath = projectSourcePath if fileRef.get("sourceTree") == "<absolute>" else relativePath
if fileRef.get("path") == filePath:
return fileRef
return None
def add_file(self, projectSourcePath, shadowHeaderPath, shadowImplementationPath):
resolvedPath = os.path.realpath(projectSourcePath)
# Shadow files are always project-relative
if not self.file_with_path(shadowHeaderPath):
self.project.add_file(shadowHeaderPath, parent=self.shadowGroup, tree="SOURCE_ROOT", create_build_files=False)
if not self.file_with_path(shadowImplementationPath):
self.project.add_file(shadowImplementationPath, parent=self.shadowGroup, tree="SOURCE_ROOT", create_build_files=False)
# If the file is within the project directory, the file reference will be project-relative, otherwise absolute
if resolvedPath.startswith(self.projectRootPath):
tree = "SOURCE_ROOT"
else:
tree = "<absolute>"
if not self.file_with_path(resolvedPath):
relativePath = os.path.relpath(projectSourcePath, self.projectRootPath)
if relativePath.startswith(u"Frameworks/"):
parent = self.frameworksGroup
else:
parent = self.sourceGroup
self.project.add_file(resolvedPath, parent=parent, tree=tree, create_build_files=False)
def remove_file(self, projectSourcePath, shadowHeaderPath, shadowImplementationPath):
resolvedPath = os.path.realpath(projectSourcePath)
for path in (shadowHeaderPath, shadowImplementationPath, resolvedPath):
fileRef = self.file_with_path(path)
if fileRef:
self.project.remove_file(fileRef)
def add_framework_resources(self, framework, resourcesPath):
files = self.project.get_files_by_os_path(resourcesPath, tree="<absolute>")
if not files:
files = self.project.add_file(resourcesPath, parent=None, tree="<absolute>", create_build_files=False)
if files:
files[0]['name'] = framework + u" Resources"
def compare_file_ids(self, id1, id2):
# A few special cases:
# - Frameworks group always goes last
# - XCC_GENERAL_INCLUDE always goes after another file
# - Frameworks/* file always goes after a non-Frameworks file
obj1 = self.project.get_obj(id1)
name1 = obj1.get("name", obj1.get("path"))
obj2 = self.project.get_obj(id2)
name2 = obj2.get("name", obj2.get("path"))
# Note: the "∕" in "Frameworks∕" is actually Unicode DIVISION_SLASH, not SOLIDUS (forward slash)
if name1 == u"Frameworks" and obj1.get("isa") == u"PBXGroup":
return 1
elif name2 == u"Frameworks" and obj2.get("isa") == u"PBXGroup":
return -1
elif name1 == self.XCC_GENERAL_INCLUDE and obj2.get("isa") == u"PBXFileReference":
return 1
elif name2 == self.XCC_GENERAL_INCLUDE and obj1.get("isa") == u"PBXFileReference":
return -1
elif name1.startswith(u"Frameworks∕") and not name2.startswith(u"Frameworks∕"):
return 1
elif name2.startswith(u"Frameworks∕") and not name1.startswith(u"Frameworks∕"):
return -1
return cmp(name1.lower(), name2.lower())
def compare_resource_folder_ids(self, id1, id2):
folder1 = self.project.get_obj(id1)
name1 = folder1.get("name", folder1.get("path"))
folder2 = self.project.get_obj(id2)
name2 = folder2.get("name", folder2.get("path"))
if name1 == u"Resources":
return -1
elif name2 == u"Resources":
return 1
return cmp(name1.lower(), name2.lower())
def sort_project(self):
# Sort the files alphabetically in our groups.
for group in (self.sourceGroup, self.shadowGroup, self._frameworksGroup):
if group is None:
continue
group.get("children").data.sort(cmp=self.compare_file_ids)
# Move resource folders to the top, Resources at the very top
root_ids = self.project.root_group.get("children").data
folder_ids = []
for id in root_ids:
item = self.project.get_obj(id)
name = item.get("name", item.get("path"))
if name.endswith(u"Resources") and item.get("lastKnownFileType") == "folder":
folder_ids.append(id)
folder_ids.sort(cmp=self.compare_resource_folder_ids, reverse=True)
for id in folder_ids:
index = root_ids.index(id)
del root_ids[index]
root_ids.insert(0, id)
def save_project(self):
self.project.backup(backup_name=self.project.pbxproj_path + ".backup")
self.sort_project()
self.project.saveFormat3_2()
def update_project_with_source(self, action, projectSourcePath):
relativePath = os.path.relpath(projectSourcePath, self.projectRootPath)
shadowBasePath = os.path.join(projectRootPath, self.XCODE_SUPPORT_FOLDER)
shadowBaseName = os.path.splitext(relativePath)[0].replace(u"/", self.SLASH_REPLACEMENT)
shadowHeaderPath = os.path.join(shadowBasePath, shadowBaseName + ".h")
shadowImplementationPath = os.path.join(shadowBasePath, shadowBaseName + ".m")
if action == "add":
fileRef = self.file_with_path(shadowHeaderPath)
if not fileRef:
self.update_general_include()
self.add_file(projectSourcePath, shadowHeaderPath, shadowImplementationPath)
match = self.FRAMEWORKS_RE.match(projectSourcePath)
if match:
framework = match.group(2)
resourcesPath = os.path.realpath(os.path.join(match.group(1), u"Resources"))
if os.path.isdir(resourcesPath):
self.add_framework_resources(framework, resourcesPath)
elif action == "remove":
self.update_general_include()
self.remove_file(projectSourcePath, shadowHeaderPath, shadowImplementationPath)
#
# Possible ways to call this script:
#
# "add" projectRootPath file
# "remove" projectRootPath file
# "update" projectRootPath action file... [action file...]
#
# File paths are full project paths, no resolved symlinks.
# action is "add" or "remove".
#
if __name__ == "__main__":
action = sys.argv[1]
projectRootPath = unicode(sys.argv[2])
modifier = PBXModifier(projectRootPath)
if action in ("add", "remove"):
projectSourcePath = unicode(sys.argv[3])
modifier.update_project_with_source(action, projectSourcePath)
modifier.save_project()
elif action == "update":
# When the action is "update", it is followed by "add" or "remove", followed by 1+ paths
args = sys.argv[3:]
action = unicode(args.pop(0))
while len(args):
arg = unicode(args.pop(0))
if arg in ("add", "remove"):
action = arg
continue
else:
modifier.update_project_with_source(action, arg)
modifier.save_project()
+30
View File
@@ -0,0 +1,30 @@
//
// UserDefaults.h
// XcodeCapp
//
// Created by Aparajita on 4/9/13.
//
//
#ifndef XcodeCapp_UserDefaults_h
#define XcodeCapp_UserDefaults_h
#import <Foundation/NSString.h>
extern NSString * const kDefaultLastEventId;
extern NSString * const kDefaultFirstLaunch;
extern NSString * const kDefaultFirstLaunchVersion;
extern NSString * const kDefaultXCCAPIMode;
extern NSString * const kDefaultXCCReactToInodeMod;
extern NSString * const kDefaultXCCReopenLastProject;
extern NSString * const kDefaultXCCAutoOpenErrorsPanelOnWarnings;
extern NSString * const kDefaultXCCAutoOpenErrorsPanelOnErrors;
extern NSString * const kDefaultXCCProjectHistory;
extern NSString * const kDefaultLastOpenedPath;
extern NSString * const kDefaultPathModificationDates;
extern NSString * const kDefaultMaxRecentProjects;
extern NSString * const kDefaultLogLevel;
extern NSString * const kDefaultAutoOpenXcodeProject;
extern NSString * const kDefaultShowProcessingNotices;
#endif
+26
View File
@@ -0,0 +1,26 @@
//
// UserDefaults.m
// XcodeCapp
//
// Created by Aparajita on 4/9/13.
//
//
#include "UserDefaults.h"
NSString * const kDefaultLastEventId = @"lastEventId";
NSString * const kDefaultFirstLaunch = @"FirstLaunch";
NSString * const kDefaultFirstLaunchVersion = @"firstLaunchVersion";
NSString * const kDefaultXCCAPIMode = @"XCCAPIMode";
NSString * const kDefaultXCCReactToInodeMod = @"XCCReactMode";
NSString * const kDefaultXCCReopenLastProject = @"XCCReopenLastProject";
NSString * const kDefaultXCCAutoOpenErrorsPanelOnWarnings = @"XCCAutoOpenErrorsPanelOnWarnings";
NSString * const kDefaultXCCAutoOpenErrorsPanelOnErrors = @"XCCAutoOpenErrorsPanelOnErrors";
NSString * const kDefaultXCCProjectHistory = @"XCCProjectHistory";
NSString * const kDefaultLastOpenedPath = @"LastOpenedPath";
NSString * const kDefaultPathModificationDates = @"pathModificationDates";
NSString * const kDefaultMaxRecentProjects = @"maxRecentProjects";
NSString * const kDefaultLogLevel = @"logLevel";
NSString * const kDefaultAutoOpenXcodeProject = @"autoOpenXcodeProject";
NSString * const kDefaultShowProcessingNotices = @"showProcessingNotices";
@@ -4,4 +4,13 @@
#ifdef __OBJC__
#import <Cocoa/Cocoa.h>
#import "DDLog.h"
#if DEBUG
#import "DDTTYLogger.h"
#else
#import "DDASLLogger.h"
#endif
#import "DDLogLevel.h"
#endif
Loaded 100 of 131 files, more files were not shown because too many files have changed in this diff. Show more