Merge branch 'pu/pb/index_quick_fix' into stable
[GitX.git] / ApplicationController.m
blob3bc67247402875e787b52c5d83f38acdf30c953f
1 //
2 //  GitTest_AppDelegate.m
3 //  GitTest
4 //
5 //  Created by Pieter de Bie on 13-06-08.
6 //  Copyright __MyCompanyName__ 2008 . All rights reserved.
7 //
9 #import "ApplicationController.h"
10 #import "PBGitRevisionCell.h"
11 #import "PBGitWindowController.h"
12 #import "PBRepositoryDocumentController.h"
13 #import "PBCLIProxy.h"
14 #import "PBServicesController.h"
15 #import "PBGitXProtocol.h"
16 #import "PBPrefsWindowController.h"
17 #import "PBNSURLPathUserDefaultsTransfomer.h"
18 #import "PBGitDefaults.h"
20 @implementation ApplicationController
21 @synthesize cliProxy;
23 - (ApplicationController*)init
25 #ifdef DEBUG_BUILD
26         [NSApp activateIgnoringOtherApps:YES];
27 #endif
29         if(self = [super init]) {
30                 if(![[NSBundle bundleWithPath:@"/System/Library/PrivateFrameworks/QuickLookUI.framework"] load])
31                         NSLog(@"Could not load QuickLook");
33                 self.cliProxy = [PBCLIProxy new];
34         }
36         /* Value Transformers */
37         NSValueTransformer *transformer = [[PBNSURLPathUserDefaultsTransfomer alloc] init];
38         [NSValueTransformer setValueTransformer:transformer forName:@"PBNSURLPathUserDefaultsTransfomer"];
39         
40         // Make sure the PBGitDefaults is initialized, by calling a random method
41         [PBGitDefaults class];
42         return self;
45 - (void)registerServices
47         // Register URL
48         [NSURLProtocol registerClass:[PBGitXProtocol class]];
50         // Register the service class
51         PBServicesController *services = [[PBServicesController alloc] init];
52         [NSApp setServicesProvider:services];
54         // Force update the services menu if we have a new services version
55         int serviceVersion = [[NSUserDefaults standardUserDefaults] integerForKey:@"Services Version"];
56         if (serviceVersion < 2)
57         {
58                 NSLog(@"Updating services menu…");
59                 NSUpdateDynamicServices();
60                 [[NSUserDefaults standardUserDefaults] setInteger:2 forKey:@"Services Version"];
61         }
64 - (void)applicationDidFinishLaunching:(NSNotification*)notification
66         [self registerServices];
68         // Only try to open a default document if there are no documents open already.
69         // For example, the application might have been launched by double-clicking a .git repository,
70         // or by dragging a folder to the app icon
71         if ([[[PBRepositoryDocumentController sharedDocumentController] documents] count])
72                 return;
74         if (![[NSApplication sharedApplication] isActive])
75                 return;
77         NSURL *url = nil;
79         // Try to find the current directory, to open that as a repository
80         if ([PBGitDefaults openCurDirOnLaunch]) {
81                 NSString *curPath = [[[NSProcessInfo processInfo] environment] objectForKey:@"PWD"];
82                 if (curPath)
83                         url = [NSURL fileURLWithPath:curPath];
84         }
86         // Try to open the found URL
87         NSError *error = nil;
88         if (url && [[PBRepositoryDocumentController sharedDocumentController] openDocumentWithContentsOfURL:url display:YES error:&error])
89                 return;
91         // The current directory was not enabled or could not be opened (most likely it’s not a git repository).
92         // show an open panel for the user to select a repository to view
93         if ([PBGitDefaults showOpenPanelOnLaunch])
94                 [[PBRepositoryDocumentController sharedDocumentController] openDocument:self];
97 - (void) windowWillClose: sender
99         [firstResponder terminate: sender];
102 - (IBAction)openPreferencesWindow:(id)sender
104         [[PBPrefsWindowController sharedPrefsWindowController] showWindow:nil];
107 - (IBAction)showAboutPanel:(id)sender
109         NSString *gitversion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleGitVersion"];
110         NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
111         if (gitversion)
112                 [dict addEntriesFromDictionary:[[NSDictionary alloc] initWithObjectsAndKeys:gitversion, @"Version", nil]];
114         #ifdef DEBUG_BUILD
115                 [dict addEntriesFromDictionary:[[NSDictionary alloc] initWithObjectsAndKeys:@"GitX (DEBUG)", @"ApplicationName", nil]];
116         #endif
118         [NSApp orderFrontStandardAboutPanelWithOptions:dict];
121 - (IBAction)installCliTool:(id)sender;
123         BOOL success               = NO;
124         NSString* installationPath = @"/usr/local/bin/";
125         NSString* installationName = @"gitx";
126         NSString* toolPath         = [[NSBundle mainBundle] pathForResource:@"gitx" ofType:@""];
127         if (toolPath) {
128                 AuthorizationRef auth;
129                 if (AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &auth) == errAuthorizationSuccess) {
130                         char const* mkdir_arg[] = { "-p", [installationPath UTF8String], NULL};
131                         char const* mkdir       = "/bin/mkdir";
132                         AuthorizationExecuteWithPrivileges(auth, mkdir, kAuthorizationFlagDefaults, (char**)mkdir_arg, NULL);
133                         char const* arguments[] = { "-f", "-s", [toolPath UTF8String], [[installationPath stringByAppendingString: installationName] UTF8String],  NULL };
134                         char const* helperTool  = "/bin/ln";
135                         if (AuthorizationExecuteWithPrivileges(auth, helperTool, kAuthorizationFlagDefaults, (char**)arguments, NULL) == errAuthorizationSuccess) {
136                                 int status;
137                                 int pid = wait(&status);
138                                 if (pid != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0)
139                                         success = true;
140                                 else
141                                         errno = WEXITSTATUS(status);
142                         }
144                         AuthorizationFree(auth, kAuthorizationFlagDefaults);
145                 }
146         }
148         if (success) {
149                 [[NSAlert alertWithMessageText:@"Installation Complete"
150                             defaultButton:nil
151                           alternateButton:nil
152                               otherButton:nil
153                 informativeTextWithFormat:@"The gitx tool has been installed to %@", installationPath] runModal];
154         } else {
155                 [[NSAlert alertWithMessageText:@"Installation Failed"
156                             defaultButton:nil
157                           alternateButton:nil
158                               otherButton:nil
159                 informativeTextWithFormat:@"Installation to %@ failed", installationPath] runModal];
160         }
164     Returns the support folder for the application, used to store the Core Data
165     store file.  This code uses a folder named "GitTest" for
166     the content, either in the NSApplicationSupportDirectory location or (if the
167     former cannot be found), the system's temporary directory.
168  */
170 - (IBAction) showHelp:(id) sender
172         [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://gitx.frim.nl/user_manual.html"]];
175 - (NSString *)applicationSupportFolder {
177     NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
178     NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : NSTemporaryDirectory();
179     return [basePath stringByAppendingPathComponent:@"GitTest"];
184     Creates, retains, and returns the managed object model for the application 
185     by merging all of the models found in the application bundle.
186  */
188 - (NSManagedObjectModel *)managedObjectModel {
190     if (managedObjectModel != nil) {
191         return managedObjectModel;
192     }
193         
194     managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];    
195     return managedObjectModel;
200     Returns the persistent store coordinator for the application.  This 
201     implementation will create and return a coordinator, having added the 
202     store for the application to it.  (The folder for the store is created, 
203     if necessary.)
204  */
206 - (NSPersistentStoreCoordinator *) persistentStoreCoordinator {
208     if (persistentStoreCoordinator != nil) {
209         return persistentStoreCoordinator;
210     }
212     NSFileManager *fileManager;
213     NSString *applicationSupportFolder = nil;
214     NSURL *url;
215     NSError *error;
216     
217     fileManager = [NSFileManager defaultManager];
218     applicationSupportFolder = [self applicationSupportFolder];
219     if ( ![fileManager fileExistsAtPath:applicationSupportFolder isDirectory:NULL] ) {
220         [fileManager createDirectoryAtPath:applicationSupportFolder attributes:nil];
221     }
222     
223     url = [NSURL fileURLWithPath: [applicationSupportFolder stringByAppendingPathComponent: @"GitTest.xml"]];
224     persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
225     if (![persistentStoreCoordinator addPersistentStoreWithType:NSXMLStoreType configuration:nil URL:url options:nil error:&error]){
226         [[NSApplication sharedApplication] presentError:error];
227     }    
229     return persistentStoreCoordinator;
234     Returns the managed object context for the application (which is already
235     bound to the persistent store coordinator for the application.) 
236  */
238 - (NSManagedObjectContext *) managedObjectContext {
240     if (managedObjectContext != nil) {
241         return managedObjectContext;
242     }
244     NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
245     if (coordinator != nil) {
246         managedObjectContext = [[NSManagedObjectContext alloc] init];
247         [managedObjectContext setPersistentStoreCoordinator: coordinator];
248     }
249     
250     return managedObjectContext;
255     Returns the NSUndoManager for the application.  In this case, the manager
256     returned is that of the managed object context for the application.
257  */
259 - (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window {
260     return [[self managedObjectContext] undoManager];
265     Performs the save action for the application, which is to send the save:
266     message to the application's managed object context.  Any encountered errors
267     are presented to the user.
268  */
270 - (IBAction) saveAction:(id)sender {
272     NSError *error = nil;
273     if (![[self managedObjectContext] save:&error]) {
274         [[NSApplication sharedApplication] presentError:error];
275     }
280     Implementation of the applicationShouldTerminate: method, used here to
281     handle the saving of changes in the application managed object context
282     before the application terminates.
283  */
285 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
287     NSError *error;
288     int reply = NSTerminateNow;
289     
290     if (managedObjectContext != nil) {
291         if ([managedObjectContext commitEditing]) {
292             if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
293                                 
294                 // This error handling simply presents error information in a panel with an 
295                 // "Ok" button, which does not include any attempt at error recovery (meaning, 
296                 // attempting to fix the error.)  As a result, this implementation will 
297                 // present the information to the user and then follow up with a panel asking 
298                 // if the user wishes to "Quit Anyway", without saving the changes.
300                 // Typically, this process should be altered to include application-specific 
301                 // recovery steps.  
303                 BOOL errorResult = [[NSApplication sharedApplication] presentError:error];
304                                 
305                 if (errorResult == YES) {
306                     reply = NSTerminateCancel;
307                 } 
309                 else {
310                                         
311                     int alertReturn = NSRunAlertPanel(nil, @"Could not save changes while quitting. Quit anyway?" , @"Quit anyway", @"Cancel", nil);
312                     if (alertReturn == NSAlertAlternateReturn) {
313                         reply = NSTerminateCancel;      
314                     }
315                 }
316             }
317         } 
318         
319         else {
320             reply = NSTerminateCancel;
321         }
322     }
323     
324     return reply;
329     Implementation of dealloc, to release the retained variables.
330  */
332 - (void) dealloc {
334     [managedObjectContext release], managedObjectContext = nil;
335     [persistentStoreCoordinator release], persistentStoreCoordinator = nil;
336     [managedObjectModel release], managedObjectModel = nil;
337     [super dealloc];
339 @end