summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin C. Pierce <bcpierce@cis.upenn.edu>2007-04-13 19:43:44 +0000
committerBenjamin C. Pierce <bcpierce@cis.upenn.edu>2007-04-13 19:43:44 +0000
commitd9f3234ea6fe4eb44f14b9ccc455cde49037f9e0 (patch)
tree1aec43ed6afc4ee4cc316a3f5e6ec7909528bf99
parent275ec30d81e5b4d2c57280f63f2f1e4e6cf69047 (diff)
downloadunison-d9f3234ea6fe4eb44f14b9ccc455cde49037f9e0.zip
unison-d9f3234ea6fe4eb44f14b9ccc455cde49037f9e0.tar.gz
unison-d9f3234ea6fe4eb44f14b9ccc455cde49037f9e0.tar.bz2
* Copy small fix
-rw-r--r--src/RECENTNEWS4
-rw-r--r--src/mkProjectInfo.ml1
-rw-r--r--src/uimac/English.lproj/InfoPlist.stringsbin0 -> 530 bytes
-rw-r--r--src/uimac/English.lproj/MainMenu.nib/classes.nib106
-rw-r--r--src/uimac/English.lproj/MainMenu.nib/info.nib36
-rw-r--r--src/uimac/English.lproj/MainMenu.nib/objects.nibbin0 -> 16371 bytes
-rw-r--r--src/uimac/Info.plist0
-rw-r--r--src/uimac/Info.plist.template34
-rw-r--r--src/uimac/MyController.h69
-rw-r--r--src/uimac/MyController.m497
-rw-r--r--src/uimac/PreferencesController.h22
-rw-r--r--src/uimac/PreferencesController.m96
-rw-r--r--src/uimac/ProfileController.h19
-rw-r--r--src/uimac/ProfileController.m83
-rw-r--r--src/uimac/ProfileTableView.h10
-rw-r--r--src/uimac/ProfileTableView.m18
-rw-r--r--src/uimac/ReconItem.h30
-rw-r--r--src/uimac/ReconItem.m174
-rw-r--r--src/uimac/ReconTableView.h31
-rw-r--r--src/uimac/ReconTableView.m180
-rw-r--r--src/uimac/TrevorsUnison.icnsbin0 -> 24548 bytes
-rw-r--r--src/uimac/Unison.icnsbin0 -> 40076 bytes
-rw-r--r--src/uimac/cltool.c67
-rw-r--r--src/uimac/main.m89
-rw-r--r--src/uimac/uimac.pbproj/project.pbxproj580
25 files changed, 2146 insertions, 0 deletions
diff --git a/src/RECENTNEWS b/src/RECENTNEWS
index d5a960d..f142222 100644
--- a/src/RECENTNEWS
+++ b/src/RECENTNEWS
@@ -1,3 +1,7 @@
+CHANGES FROM VERSION 2.27.19
+
+* Copy small fix
+-------------------------------
CHANGES FROM VERSION 2.27.14
* Copying over changes from 2.27 branch to developer sources (checkpoint)
diff --git a/src/mkProjectInfo.ml b/src/mkProjectInfo.ml
index 098427c..74d30ae 100644
--- a/src/mkProjectInfo.ml
+++ b/src/mkProjectInfo.ml
@@ -51,3 +51,4 @@ Printf.printf "VERSION=%d.%d.%d\n" majorVersion minorVersion pointVersion;;
Printf.printf "NAME=%s\n" projectName;;
+
diff --git a/src/uimac/English.lproj/InfoPlist.strings b/src/uimac/English.lproj/InfoPlist.strings
new file mode 100644
index 0000000..ce15fb6
--- /dev/null
+++ b/src/uimac/English.lproj/InfoPlist.strings
Binary files differ
diff --git a/src/uimac/English.lproj/MainMenu.nib/classes.nib b/src/uimac/English.lproj/MainMenu.nib/classes.nib
new file mode 100644
index 0000000..56d717d
--- /dev/null
+++ b/src/uimac/English.lproj/MainMenu.nib/classes.nib
@@ -0,0 +1,106 @@
+{
+ IBClasses = (
+ {
+ ACTIONS = {
+ copyLR = id;
+ copyRL = id;
+ forceNewer = id;
+ forceOlder = id;
+ ignoreExt = id;
+ ignoreName = id;
+ ignorePath = id;
+ leaveAlone = id;
+ merge = id;
+ revert = id;
+ selectConflicts = id;
+ };
+ CLASS = FirstResponder;
+ LANGUAGE = ObjC;
+ SUPERCLASS = NSObject;
+ },
+ {
+ ACTIONS = {
+ cancelProfileButton = id;
+ createButton = id;
+ endPasswordWindow = id;
+ installCommandLineTool = id;
+ onlineHelp = id;
+ openButton = id;
+ raiseAboutWindow = id;
+ restartButton = id;
+ saveProfileButton = id;
+ syncButton = id;
+ };
+ CLASS = MyController;
+ LANGUAGE = ObjC;
+ OUTLETS = {
+ ConnectingView = NSView;
+ aboutWindow = NSWindow;
+ chooseProfileView = NSView;
+ detailsTextView = NSTextView;
+ mainWindow = NSWindow;
+ passwordCancelButton = NSButton;
+ passwordText = NSTextField;
+ passwordWindow = NSWindow;
+ preferencesController = PreferencesController;
+ preferencesView = NSView;
+ profileController = ProfileController;
+ statusText = NSTextField;
+ tableView = ReconTableView;
+ updatesText = NSTextField;
+ updatesView = NSView;
+ versionText = NSTextField;
+ };
+ SUPERCLASS = NSObject;
+ },
+ {
+ ACTIONS = {anyEnter = id; localClick = id; remoteClick = id; };
+ CLASS = PreferencesController;
+ LANGUAGE = ObjC;
+ OUTLETS = {
+ cancelButton = NSButton;
+ firstRootText = NSTextField;
+ localButtonCell = NSButtonCell;
+ profileNameText = NSTextField;
+ remoteButtonCell = NSButtonCell;
+ saveButton = NSButton;
+ secondRootHost = NSTextField;
+ secondRootText = NSTextField;
+ secondRootUser = NSTextField;
+ };
+ SUPERCLASS = NSObject;
+ },
+ {
+ CLASS = ProfileController;
+ LANGUAGE = ObjC;
+ OUTLETS = {tableView = NSTableView; };
+ SUPERCLASS = NSObject;
+ },
+ {
+ CLASS = ProfileTableView;
+ LANGUAGE = ObjC;
+ OUTLETS = {myController = MyController; };
+ SUPERCLASS = NSTableView;
+ },
+ {CLASS = ReconItem; LANGUAGE = ObjC; SUPERCLASS = NSObject; },
+ {
+ ACTIONS = {
+ copyLR = id;
+ copyRL = id;
+ forceNewer = id;
+ forceOlder = id;
+ ignoreExt = id;
+ ignoreName = id;
+ ignorePath = id;
+ leaveAlone = id;
+ merge = id;
+ revert = id;
+ selectConflicts = id;
+ };
+ CLASS = ReconTableView;
+ LANGUAGE = ObjC;
+ SUPERCLASS = NSTableView;
+ }
+ );
+ IBVersion = 1;
+} \ No newline at end of file
diff --git a/src/uimac/English.lproj/MainMenu.nib/info.nib b/src/uimac/English.lproj/MainMenu.nib/info.nib
new file mode 100644
index 0000000..9b49b29
--- /dev/null
+++ b/src/uimac/English.lproj/MainMenu.nib/info.nib
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IBDocumentLocation</key>
+ <string>318 45 509 310 0 0 1280 832 </string>
+ <key>IBEditorPositions</key>
+ <dict>
+ <key>197</key>
+ <string>450 391 383 326 0 0 1280 832 </string>
+ <key>198</key>
+ <string>307 297 669 515 0 0 1280 832 </string>
+ <key>29</key>
+ <string>72 209 280 44 0 0 1280 832 </string>
+ <key>307</key>
+ <string>392 388 499 332 0 0 1280 832 </string>
+ <key>423</key>
+ <string>450 391 383 326 0 0 1280 832 </string>
+ </dict>
+ <key>IBFramework Version</key>
+ <string>364.0</string>
+ <key>IBOpenObjects</key>
+ <array>
+ <integer>423</integer>
+ <integer>198</integer>
+ <integer>29</integer>
+ <integer>402</integer>
+ <integer>234</integer>
+ <integer>21</integer>
+ <integer>307</integer>
+ <integer>197</integer>
+ </array>
+ <key>IBSystem Version</key>
+ <string>7U16</string>
+</dict>
+</plist>
diff --git a/src/uimac/English.lproj/MainMenu.nib/objects.nib b/src/uimac/English.lproj/MainMenu.nib/objects.nib
new file mode 100644
index 0000000..6d5d3c6
--- /dev/null
+++ b/src/uimac/English.lproj/MainMenu.nib/objects.nib
Binary files differ
diff --git a/src/uimac/Info.plist b/src/uimac/Info.plist
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/uimac/Info.plist
diff --git a/src/uimac/Info.plist.template b/src/uimac/Info.plist.template
new file mode 100644
index 0000000..22a46af
--- /dev/null
+++ b/src/uimac/Info.plist.template
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleName</key>
+ <string>Unison</string>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>Unison</string>
+ <key>CFBundleIconFile</key>
+ <string>Unison.icns</string>
+ <key>CFBundleIdentifier</key>
+ <string>edu.upenn.cis.Unison</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>@@VERSION@@</string>
+ <key>CFBundleShortVersionString</key>
+ <string>@@VERSION@@</string>
+ <key>CFBundleGetInfoString</key>
+ <string>@@VERSION@@. ©1999-2007, licensed under GNU GPL.</string>
+ <key>NSHumanReadableCopyright</key>
+ <string>©1999-2006, licensed under GNU GPL.</string>
+ <key>NSMainNibFile</key>
+ <string>MainMenu</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+</dict>
+</plist>
diff --git a/src/uimac/MyController.h b/src/uimac/MyController.h
new file mode 100644
index 0000000..82ba65f
--- /dev/null
+++ b/src/uimac/MyController.h
@@ -0,0 +1,69 @@
+/* MyController */
+/* Copyright (c) 2003, see file COPYING for details. */
+
+#import <Cocoa/Cocoa.h>
+#define CAML_NAME_SPACE
+#include <caml/mlvalues.h>
+#import "ProfileController.h"
+#import "PreferencesController.h"
+#import "ReconTableView.h"
+
+@interface MyController : NSObject
+{
+ IBOutlet NSWindow *mainWindow;
+
+ IBOutlet ProfileController *profileController;
+ IBOutlet NSView *chooseProfileView;
+ NSSize chooseProfileSize;
+
+ IBOutlet PreferencesController *preferencesController;
+ IBOutlet NSView *preferencesView;
+ NSSize preferencesSize;
+
+ IBOutlet NSView *updatesView;
+ NSSize updatesSize;
+
+ IBOutlet NSView *ConnectingView;
+ NSSize ConnectingSize;
+
+ IBOutlet ReconTableView *tableView;
+ IBOutlet NSTextField *updatesText;
+
+ IBOutlet NSWindow *passwordWindow;
+ IBOutlet NSTextField *passwordText;
+ IBOutlet NSTextView *detailsTextView;
+ IBOutlet NSTextField *statusText;
+
+ IBOutlet NSButton *passwordCancelButton;
+
+ IBOutlet NSWindow *aboutWindow;
+ IBOutlet NSTextField *versionText;
+
+
+ NSView *blankView;
+ value caml_reconItems;
+ NSMutableArray *reconItems;
+ value preconn;
+
+ NSString *pName;
+}
+- (IBAction)createButton:(id)sender;
+- (IBAction)saveProfileButton:(id)sender;
+- (IBAction)cancelProfileButton:(id)sender;
+- (IBAction)openButton:(id)sender;
+- (IBAction)restartButton:(id)sender;
+- (IBAction)syncButton:(id)sender;
+- (IBAction)onlineHelp:(id)sender;
+- (int)numberOfRowsInTableView:(NSTableView *)aTableView;
+- (id)tableView:(NSTableView *)aTableView
+ objectValueForTableColumn:(NSTableColumn *)aTableColumn
+ row:(int)rowIndex;
+- (void)raisePasswordWindow:(NSString *)prompt;
+- (IBAction)raiseAboutWindow:(id)sender;
+- (void)controlTextDidEndEditing:(NSNotification *)notification;
+- (IBAction)endPasswordWindow:(id)sender;
+- (NSMutableArray *)reconItems;
+- (int)updateForIgnore:(int)i;
+- (void)displayDetails:(int)i;
+- (IBAction)installCommandLineTool:(id)sender;
+@end
diff --git a/src/uimac/MyController.m b/src/uimac/MyController.m
new file mode 100644
index 0000000..eb5c011
--- /dev/null
+++ b/src/uimac/MyController.m
@@ -0,0 +1,497 @@
+/* Copyright (c) 2003, see file COPYING for details. */
+
+#import "MyController.h"
+#import "ReconItem.h"
+#include <caml/callback.h>
+#include <caml/alloc.h>
+#include <caml/mlvalues.h>
+#include <caml/memory.h>
+
+extern value Callback_checkexn(value,value);
+extern value Callback2_checkexn(value,value,value);
+
+@implementation MyController
+
+static MyController *me; // needed by reloadTable and displayStatus, below
+
+- (void)resizeWindowToSize:(NSSize)newSize
+{
+ NSRect aFrame;
+
+ float newHeight = newSize.height;
+ float newWidth = newSize.width;
+
+ aFrame = [NSWindow contentRectForFrameRect:[mainWindow frame]
+ styleMask:[mainWindow styleMask]];
+
+ aFrame.origin.y += aFrame.size.height;
+ aFrame.origin.y -= newHeight;
+ aFrame.size.height = newHeight;
+ aFrame.size.width = newWidth;
+
+ aFrame = [NSWindow frameRectForContentRect:aFrame
+ styleMask:[mainWindow styleMask]];
+
+ [mainWindow setFrame:aFrame display:YES animate:YES];
+}
+
+- (void)chooseProfiles
+{
+ [mainWindow setContentView:blankView];
+ [self resizeWindowToSize:chooseProfileSize];
+ [mainWindow setContentView:chooseProfileView];
+ [mainWindow makeFirstResponder:[profileController tableView]]; // profiles get keyboard input
+}
+
+- (IBAction)createButton:(id)sender
+{
+ [preferencesController reset];
+ [mainWindow setContentView:blankView];
+ [self resizeWindowToSize:preferencesSize];
+ [mainWindow setContentView:preferencesView];
+}
+
+- (IBAction)saveProfileButton:(id)sender
+{
+ if ([preferencesController validatePrefs]) {
+ [profileController initProfiles]; // so the list contains the new profile
+ [self chooseProfiles];
+ }
+}
+
+- (IBAction)cancelProfileButton:(id)sender
+{
+ [self chooseProfiles];
+}
+
+- (void)updateReconItems
+{
+ [reconItems release];
+ reconItems = [[NSMutableArray alloc] init];
+ int j = 0;
+ int n = Wosize_val(caml_reconItems);
+ for (; j<n; j++) {
+ [reconItems insertObject:[ReconItem initWithRi:Field(caml_reconItems,j)]
+ atIndex:j];
+ }
+}
+
+- (void)displayDetails:(int)i
+{
+ if (i >= 0 && i < [reconItems count])
+ [detailsTextView setString:[[reconItems objectAtIndex:i] details]];
+}
+- (void)clearDetails
+{
+ [detailsTextView setString:@""];
+}
+
+- (void)doUpdateThread:(id)whatever
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ preconn = Val_unit; // so old preconn can be garbage collected
+ value *f = caml_named_value("unisonInit2");
+ caml_reconItems = Callback_checkexn(*f, Val_unit);
+ [pool release];
+}
+
+- (void)afterUpdate:(NSNotification *)notification
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self
+ name:NSThreadWillExitNotification
+ object:nil];
+ [self updateReconItems];
+ if ([reconItems count] > 0)
+ [tableView selectRow:0 byExtendingSelection:NO];
+
+ // label the left and right columns with the roots
+ NSTableHeaderCell *left = [[[tableView tableColumns] objectAtIndex:0] headerCell];
+ value *f = caml_named_value("unisonFirstRootString");
+ [left setObjectValue:[NSString stringWithCString:String_val(Callback_checkexn(*f, Val_unit))]];
+ NSTableHeaderCell *right = [[[tableView tableColumns] objectAtIndex:2] headerCell];
+ f = caml_named_value("unisonSecondRootString");
+ [right setObjectValue:[NSString stringWithCString:String_val(Callback_checkexn(*f, Val_unit))]];
+
+ // cause scrollbar to display if necessary
+ [tableView reloadData];
+
+ // activate menu items
+ [tableView setEditable:YES];
+}
+
+- (void)afterOpen
+{
+ NSLog(@"Connected.");
+ // move to updates window after clearing it
+ [self clearDetails];
+ [reconItems release];
+ reconItems = nil;
+ [mainWindow setContentView:blankView];
+ [self resizeWindowToSize:updatesSize];
+ [mainWindow setContentView:updatesView];
+
+ // reconItems table gets keyboard input
+ [mainWindow makeFirstResponder:tableView];
+
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(afterUpdate:)
+ name:NSThreadWillExitNotification object:nil];
+ [NSThread detachNewThreadSelector:@selector(doUpdateThread:)
+ toTarget:self withObject:nil];
+}
+
+- (void)connect:(value)profileName
+{
+ // contact server, propagate prefs
+ NSLog(@"Connecting...");
+
+ // Switch to ConnectingView
+ [mainWindow setContentView:blankView];
+ [self resizeWindowToSize:ConnectingSize];
+ [mainWindow setContentView:ConnectingView];
+ [ConnectingView setNeedsDisplay:YES]; // FIX: this doesn't seem to work fast enough
+
+ // possibly slow -- need another thread? Print "contacting server"
+ value *f = NULL;
+ f = caml_named_value("unisonInit1");
+ preconn = Callback_checkexn(*f, profileName);
+ if (preconn == Val_unit) {
+ [self afterOpen]; // no prompting required
+ return;
+ }
+ // prompting required
+ preconn = Field(preconn,0); // value of Some
+ f = caml_named_value("openConnectionPrompt");
+ value prompt = Callback_checkexn(*f, preconn);
+ if (prompt == Val_unit) {
+ // turns out, no prompt needed, but must finish opening connection
+ f = caml_named_value("openConnectionEnd");
+ Callback_checkexn(*f, preconn);
+ [self afterOpen];
+ return;
+ }
+ [self raisePasswordWindow:[NSString stringWithCString:String_val(Field(prompt,0))]];
+}
+
+- (IBAction)openButton:(id)sender
+{
+ NSString *profile = [profileController selected];
+ [updatesText setStringValue:[NSString stringWithFormat:@"Synchronizing profile '%@'",
+ profile]];
+ const char *s = [profile cString];
+ value caml_s = caml_copy_string(s);
+ [self connect:caml_s];
+ return;
+}
+
+- (IBAction)restartButton:(id)sender
+{
+ [tableView setEditable:NO];
+ [self chooseProfiles];
+}
+
+- (void)doSyncThread:(id)whatever
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ value *f = caml_named_value("unisonSynchronize");
+ Callback_checkexn(*f, Val_unit);
+ [pool release];
+}
+
+- (void)afterSync:(NSNotification *)notification
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self
+ name:NSThreadWillExitNotification
+ object:nil];
+ int i;
+ for (i = 0; i < [reconItems count]; i++) {
+ [[reconItems objectAtIndex:i] resetProgress];
+ }
+ [tableView reloadData];
+}
+
+- (IBAction)syncButton:(id)sender
+{
+ [tableView setEditable:NO];
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(afterSync:)
+ name:NSThreadWillExitNotification object:nil];
+ [NSThread detachNewThreadSelector:@selector(doSyncThread:)
+ toTarget:self withObject:nil];
+}
+
+- (void)updateTableView:(int)i
+{
+ [[reconItems objectAtIndex:i] resetProgress];
+ [tableView reloadData]; // FIX: can we redisplay just row i?
+}
+
+// A function called from ocaml
+CAMLprim value reloadTable(value row)
+{
+ int i = Int_val(row);
+ [me updateTableView:i]; // we need 'me' to access its instance variables
+ return Val_unit;
+}
+
+- (int)numberOfRowsInTableView:(NSTableView *)aTableView
+{
+ if (!reconItems) return 0;
+ else return [reconItems count];
+}
+
+- (id)tableView:(NSTableView *)aTableView
+ objectValueForTableColumn:(NSTableColumn *)aTableColumn
+ row:(int)rowIndex
+{
+ if (!reconItems) {
+ return @"[internal error]";
+ }
+ if (rowIndex >= 0 && rowIndex < [reconItems count]) {
+ NSString *identifier = [aTableColumn identifier];
+ ReconItem *ri = [reconItems objectAtIndex:rowIndex];
+ NSString *s = [ri valueForKey:identifier];
+ return s;
+ }
+ else return @"[internal error!]";
+}
+- (void)tableViewSelectionDidChange:(NSNotification *)note
+{
+ int n = [tableView numberOfSelectedRows];
+ if (n == 1) [self displayDetails:[tableView selectedRow]];
+ else [self clearDetails];
+}
+
+- (void)raisePasswordWindow:(NSString *)prompt
+{
+ // FIX: some prompts don't ask for password, need to look at it
+ NSLog(@"Got the prompt: '%@'",prompt);
+ value *f = caml_named_value("unisonPasswordMsg");
+ value v = Callback_checkexn(*f, caml_copy_string([prompt cString]));
+ if (v == Val_true) {
+ [NSApp beginSheet:passwordWindow
+ modalForWindow:mainWindow
+ modalDelegate:nil
+ didEndSelector:nil
+ contextInfo:nil];
+ return;
+ }
+ f = caml_named_value("unisonAuthenticityMsg");
+ v = Callback_checkexn(*f, caml_copy_string([prompt cString]));
+ if (v == Val_true) {
+ int i = NSRunAlertPanel(@"New host",prompt,@"Yes",@"No",nil);
+ if (i == NSAlertDefaultReturn) {
+ f = caml_named_value("openConnectionReply");
+ Callback2_checkexn(*f, preconn, caml_copy_string("yes"));
+ f = caml_named_value("openConnectionPrompt");
+ value prompt = Callback_checkexn(*f, preconn);
+ if (prompt == Val_unit) {
+ // all done with prompts, finish opening connection
+ f = caml_named_value("openConnectionEnd");
+ Callback_checkexn(*f, preconn);
+ [self afterOpen];
+ return;
+ }
+ else {
+ [self raisePasswordWindow:[NSString stringWithCString:String_val(Field(prompt,0))]];
+ return;
+ }
+ }
+ if (i == NSAlertAlternateReturn) {
+ f = caml_named_value("openConnectionCancel");
+ Callback_checkexn(*f, preconn);
+ return;
+ }
+ else {
+ NSLog(@"Unrecognized response '%d' from NSRunAlertPanel",i);
+ f = caml_named_value("openConnectionCancel");
+ Callback_checkexn(*f, preconn);
+ return;
+ }
+ }
+ NSLog(@"Unrecognized message from ssh: %@",prompt);
+ f = caml_named_value("openConnectionCancel");
+ Callback_checkexn(*f, preconn);
+}
+// The password window will invoke this when Enter occurs, b/c we
+// are the delegate.
+- (void)controlTextDidEndEditing:(NSNotification *)notification
+{
+ NSNumber *reason = [[notification userInfo] objectForKey:@"NSTextMovement"];
+ int code = [reason intValue];
+ if (code == NSReturnTextMovement)
+ [self endPasswordWindow:self];
+}
+// Or, the Continue button will invoke this when clicked
+- (IBAction)endPasswordWindow:(id)sender
+{
+ [passwordWindow orderOut:self];
+ [NSApp endSheet:passwordWindow];
+ if ([sender isEqualTo:passwordCancelButton]) {
+ value *f = caml_named_value("openConnectionCancel");
+ Callback_checkexn(*f, preconn);
+ [self chooseProfiles];
+ return;
+ }
+ NSString *password = [passwordText stringValue];
+ value *f = NULL;
+ const char *s = [password cString];
+ value caml_s = caml_copy_string(s);
+ f = caml_named_value("openConnectionReply");
+ Callback2_checkexn(*f, preconn, caml_s);
+ f = caml_named_value("openConnectionPrompt");
+ value prompt = Callback_checkexn(*f, preconn);
+ if (prompt == Val_unit) {
+ // all done with prompts, finish opening connection
+ f = caml_named_value("openConnectionEnd");
+ Callback_checkexn(*f, preconn);
+ [self afterOpen];
+ }
+ else [self raisePasswordWindow:[NSString stringWithCString:String_val(Field(prompt,0))]];
+}
+
+- (IBAction)raiseAboutWindow:(id)sender
+{
+ [aboutWindow makeKeyAndOrderFront:nil];
+}
+
+- (IBAction)onlineHelp:(id)sender
+{
+ [[NSWorkspace sharedWorkspace]
+ openURL:[NSURL URLWithString:@"http://www.cis.upenn.edu/~bcpierce/unison/docs.html"]];
+}
+
+
+- (NSMutableArray *)reconItems // used in ReconTableView only
+{
+ return reconItems;
+}
+
+- (int)updateForIgnore:(int)i
+{
+ value *f = caml_named_value("unisonUpdateForIgnore");
+ int j = Int_val(Callback_checkexn(*f,Val_int(i)));
+ f = caml_named_value("unisonState");
+ caml_reconItems = Callback_checkexn(*f, Val_unit);
+ [self updateReconItems];
+ return j;
+}
+
+- (void)statusTextSet:(NSString *)s {
+ [statusText setStringValue:s];
+}
+
+// A function called from ocaml
+CAMLprim value displayStatus(value s)
+{
+ [me statusTextSet:[NSString stringWithCString:String_val(s)]];
+// NSLog(@"dS: %s",String_val(s));
+ return Val_unit;
+}
+
+- (void)awakeFromNib
+{
+ /**** Initialize locals ****/
+ me = self;
+ chooseProfileSize = [chooseProfileView frame].size;
+ updatesSize = [updatesView frame].size;
+ preferencesSize = [preferencesView frame].size;
+ ConnectingSize = [ConnectingView frame].size;
+ blankView = [[NSView alloc] init];
+ /* Double clicking in the profile list will open the profile */
+ [[profileController tableView] setTarget:self];
+ [[profileController tableView] setDoubleAction:@selector(openButton:)];
+ /* Set up the version string in the about box. We use a custom
+ about box just because PRCS doesn't seem capable of getting the
+ version into the InfoPlist.strings file; otherwise we'd use the
+ standard about box. */
+ value *f = NULL;
+ f = caml_named_value("unisonGetVersion");
+ [versionText setStringValue:
+ [NSString stringWithCString:
+ String_val(Callback_checkexn(*f, Val_unit))]];
+
+ /* Ocaml initialization */
+ // FIX: Does this occur before ProfileController awakeFromNib?
+ caml_reconItems = preconn = Val_int(0);
+ caml_register_global_root(&caml_reconItems);
+ caml_register_global_root(&preconn);
+
+ /* Command-line processing */
+ f = caml_named_value("unisonInit0");
+ value clprofile = Callback_checkexn(*f, Val_unit);
+
+ /* Set up the first window the user will see */
+ if (Is_block(clprofile)) {
+ /* A profile name was given on the command line */
+ value caml_profile = Field(clprofile,0);
+ NSString *profile = [NSString stringWithCString:String_val(caml_profile)];
+ [updatesText setStringValue:[NSString stringWithFormat:@"Synchronizing profile '%@'",
+ profile]];
+ /* If invoked from terminal we need to bring the app to the front */
+ [NSApp activateIgnoringOtherApps:YES];
+
+ /* Start the connection */
+ [self connect:caml_profile];
+ }
+ else {
+ /* If invoked from terminal we need to bring the app to the front */
+ [NSApp activateIgnoringOtherApps:YES];
+ /* Bring up the dialog to choose a profile */
+ [self chooseProfiles];
+ }
+}
+
+/* from http://developer.apple.com/documentation/Security/Conceptual/authorization_concepts/index.html */
+#include <Security/Authorization.h>
+#include <Security/AuthorizationTags.h>
+- (IBAction)installCommandLineTool:(id)sender
+{
+ /* Install the command-line tool in /usr/bin/unison.
+ Requires root privilege, so we ask for it and pass the task off to /bin/sh. */
+
+ OSStatus myStatus;
+
+ AuthorizationFlags myFlags = kAuthorizationFlagDefaults;
+ AuthorizationRef myAuthorizationRef;
+ myStatus = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment,
+ myFlags, &myAuthorizationRef);
+ if (myStatus != errAuthorizationSuccess) return;
+
+ {
+ AuthorizationItem myItems = {kAuthorizationRightExecute, 0,
+ NULL, 0};
+ AuthorizationRights myRights = {1, &myItems};
+ myFlags = kAuthorizationFlagDefaults |
+ kAuthorizationFlagInteractionAllowed |
+ kAuthorizationFlagPreAuthorize |
+ kAuthorizationFlagExtendRights;
+ myStatus =
+ AuthorizationCopyRights(myAuthorizationRef,&myRights,NULL,myFlags,NULL);
+ }
+ if (myStatus == errAuthorizationSuccess) {
+ NSBundle *bundle = [NSBundle mainBundle];
+ NSString *bundle_path = [bundle bundlePath];
+ NSString *exec_path =
+ [bundle_path stringByAppendingString:@"/Contents/MacOS/cltool"];
+ // Not sure why but this doesn't work:
+ // [bundle pathForResource:@"cltool" ofType:nil];
+
+ if (exec_path == nil) return;
+ char *args[] = { "-f", (char *)[exec_path cString], "/usr/bin/unison", NULL };
+
+ myFlags = kAuthorizationFlagDefaults;
+ myStatus = AuthorizationExecuteWithPrivileges
+ (myAuthorizationRef, "/bin/cp", myFlags, args,
+ NULL);
+ }
+ AuthorizationFree (myAuthorizationRef, kAuthorizationFlagDefaults);
+
+ /*
+ if (myStatus == errAuthorizationCanceled)
+ NSLog(@"The attempt was canceled\n");
+ else if (myStatus) NSLog(@"There was an authorization error: %ld\n", myStatus);
+ */
+}
+
+@end
diff --git a/src/uimac/PreferencesController.h b/src/uimac/PreferencesController.h
new file mode 100644
index 0000000..a867505
--- /dev/null
+++ b/src/uimac/PreferencesController.h
@@ -0,0 +1,22 @@
+/* PreferencesController */
+
+#import <Cocoa/Cocoa.h>
+
+@interface PreferencesController : NSObject
+{
+ IBOutlet NSButton *cancelButton;
+ IBOutlet NSTextField *firstRootText;
+ IBOutlet NSButtonCell *localButtonCell;
+ IBOutlet NSTextField *profileNameText;
+ IBOutlet NSButtonCell *remoteButtonCell;
+ IBOutlet NSButton *saveButton;
+ IBOutlet NSTextField *secondRootHost;
+ IBOutlet NSTextField *secondRootText;
+ IBOutlet NSTextField *secondRootUser;
+}
+- (IBAction)anyEnter:(id)sender;
+- (IBAction)localClick:(id)sender;
+- (IBAction)remoteClick:(id)sender;
+- (BOOL)validatePrefs;
+- (void)reset;
+@end
diff --git a/src/uimac/PreferencesController.m b/src/uimac/PreferencesController.m
new file mode 100644
index 0000000..e5479ac
--- /dev/null
+++ b/src/uimac/PreferencesController.m
@@ -0,0 +1,96 @@
+#import "PreferencesController.h"
+#define CAML_NAME_SPACE
+#include <caml/alloc.h>
+#include <caml/callback.h>
+
+extern value Callback3_checkexn(value,value,value,value);
+
+@implementation PreferencesController
+
+- (void)reset
+{
+ [profileNameText setStringValue:@""];
+ [firstRootText setStringValue:@""];
+ [secondRootUser setStringValue:@""];
+ [secondRootHost setStringValue:@""];
+ [secondRootText setStringValue:@""];
+ [remoteButtonCell setState:NSOnState];
+ [localButtonCell setState:NSOffState];
+ [secondRootUser setSelectable:YES];
+ [secondRootUser setEditable:YES];
+ [secondRootHost setSelectable:YES];
+ [secondRootHost setEditable:YES];
+}
+
+- (BOOL)validatePrefs
+{
+ NSString *profileName = [profileNameText stringValue];
+ if (profileName == nil | [profileName isEqualTo:@""]) {
+ // FIX: should check for already existing names too
+ NSRunAlertPanel(@"Error",@"You must enter a profile name",@"OK",nil,nil);
+ return NO;
+ }
+ NSString *firstRoot = [firstRootText stringValue];
+ if (firstRoot == nil | [firstRoot isEqualTo:@""]) {
+ NSRunAlertPanel(@"Error",@"You must enter a first root",@"OK",nil,nil);
+ return NO;
+ }
+ NSString *secondRoot;
+ if ([remoteButtonCell state] == NSOnState) {
+ NSString *user = [secondRootUser stringValue];
+ if (user == nil | [user isEqualTo:@""]) {
+ NSRunAlertPanel(@"Error",@"You must enter a user",@"OK",nil,nil);
+ return NO;
+ }
+ NSString *host = [secondRootHost stringValue];
+ if (host == nil | [host isEqualTo:@""]) {
+ NSRunAlertPanel(@"Error",@"You must enter a host",@"OK",nil,nil);
+ return NO;
+ }
+ NSString *file = [secondRootText stringValue];
+ // OK for empty file, e.g., ssh://foo@bar/
+ secondRoot = [NSString stringWithFormat:@"ssh://%@@%@/%@",user,host,file];
+ }
+ else {
+ secondRoot = [secondRootText stringValue];
+ if (secondRoot == nil | [secondRoot isEqualTo:@""]) {
+ NSRunAlertPanel(@"Error",@"You must enter a second root file",@"OK",nil,nil);
+ return NO;
+ }
+ }
+ value *f = caml_named_value("unisonProfileInit");
+ Callback3_checkexn(*f, caml_copy_string([profileName cString]),
+ caml_copy_string([firstRoot cString]),
+ caml_copy_string([secondRoot cString]));
+ return YES;
+}
+
+/* The target when enter is pressed in any of the text fields */
+// FIX: this is broken, it takes tab, mouse clicks, etc.
+- (IBAction)anyEnter:(id)sender
+{
+ NSLog(@"enter");
+ [self validatePrefs];
+}
+
+- (IBAction)localClick:(id)sender
+{
+ NSLog(@"local");
+ [secondRootUser setStringValue:@""];
+ [secondRootHost setStringValue:@""];
+ [secondRootUser setSelectable:NO];
+ [secondRootUser setEditable:NO];
+ [secondRootHost setSelectable:NO];
+ [secondRootHost setEditable:NO];
+}
+
+- (IBAction)remoteClick:(id)sender
+{
+ NSLog(@"remote");
+ [secondRootUser setSelectable:YES];
+ [secondRootUser setEditable:YES];
+ [secondRootHost setSelectable:YES];
+ [secondRootHost setEditable:YES];
+}
+
+@end
diff --git a/src/uimac/ProfileController.h b/src/uimac/ProfileController.h
new file mode 100644
index 0000000..20bbc97
--- /dev/null
+++ b/src/uimac/ProfileController.h
@@ -0,0 +1,19 @@
+/* ProfileController */
+/* Copyright (c) 2003, see file COPYING for details. */
+
+#import <Cocoa/Cocoa.h>
+
+@interface ProfileController : NSObject
+{
+ IBOutlet NSTableView *tableView;
+ NSMutableArray *profiles;
+ int defaultIndex; // -1 if no default, else the index in profiles of @"default"
+}
+- (void)initProfiles;
+- (int)numberOfRowsInTableView:(NSTableView *)aTableView;
+- (id)tableView:(NSTableView *)aTableView
+ objectValueForTableColumn:(NSTableColumn *)aTableColumn
+ row:(int)rowIndex;
+- (NSString *)selected;
+- (NSTableView *)tableView; // allows MyController to set up firstResponder
+@end
diff --git a/src/uimac/ProfileController.m b/src/uimac/ProfileController.m
new file mode 100644
index 0000000..0515399
--- /dev/null
+++ b/src/uimac/ProfileController.m
@@ -0,0 +1,83 @@
+/* Copyright (c) 2003, see file COPYING for details. */
+
+#import "ProfileController.h"
+#define CAML_NAME_SPACE
+#include <caml/mlvalues.h>
+#include <caml/callback.h>
+
+extern value Callback_checkexn(value,value);
+
+@implementation ProfileController
+
+NSString *unisonDirectory()
+{
+ static value *f = NULL;
+ if (f == NULL)
+ f = caml_named_value("unisonDirectory");
+ return [NSString stringWithCString:String_val(Callback_checkexn(*f, Val_unit))];
+}
+
+- (void)initProfiles
+{
+ NSString *directory = unisonDirectory();
+ NSArray *files = [[NSFileManager defaultManager] directoryContentsAtPath:directory];
+ unsigned int count = [files count];
+ unsigned int i,j;
+
+ [profiles release];
+ profiles = [[NSMutableArray alloc] init];
+ defaultIndex = -1;
+
+ for (i = j = 0; i < count; i++) {
+ NSString *file = [files objectAtIndex:i];
+ if ([[file pathExtension] isEqualTo:@"prf"]) {
+ NSString *withoutExtension = [file stringByDeletingPathExtension];
+ [profiles insertObject:withoutExtension atIndex:j];
+ if ([@"default" isEqualTo:withoutExtension]) defaultIndex = j;
+ j++;
+ }
+ }
+ if (j > 0)
+ [tableView selectRow:0 byExtendingSelection:NO];
+}
+
+- (void)awakeFromNib
+{
+ // start with the default profile selected
+ [self initProfiles];
+ if (defaultIndex >= 0)
+ [tableView selectRow:defaultIndex byExtendingSelection:NO];
+ // on awake the scroll bar is inactive, but after adding profiles we might need it;
+ // reloadData makes it happen. Q: is setNeedsDisplay more efficient?
+ [tableView reloadData];
+}
+
+- (int)numberOfRowsInTableView:(NSTableView *)aTableView
+{
+ if (!profiles) return 0;
+ else return [profiles count];
+}
+
+- (id)tableView:(NSTableView *)aTableView
+ objectValueForTableColumn:(NSTableColumn *)aTableColumn
+ row:(int)rowIndex
+{
+ if (rowIndex >= 0 && rowIndex < [profiles count])
+ return [profiles objectAtIndex:rowIndex];
+ else return @"[internal error!]";
+}
+
+- (NSString *)selected
+{
+ int rowIndex = [tableView selectedRow];
+ if (rowIndex >= 0 && rowIndex < [profiles count])
+ return [profiles objectAtIndex:rowIndex];
+ else return @"[internal error!]";
+}
+
+- (NSTableView *)tableView
+{
+ return tableView;
+}
+
+@end
diff --git a/src/uimac/ProfileTableView.h b/src/uimac/ProfileTableView.h
new file mode 100644
index 0000000..6a5cbc5
--- /dev/null
+++ b/src/uimac/ProfileTableView.h
@@ -0,0 +1,10 @@
+/* ProfileTableView */
+
+#import <Cocoa/Cocoa.h>
+#import "MyController.h"
+
+@interface ProfileTableView : NSTableView
+{
+ IBOutlet MyController *myController;
+}
+@end
diff --git a/src/uimac/ProfileTableView.m b/src/uimac/ProfileTableView.m
new file mode 100644
index 0000000..07c1df6
--- /dev/null
+++ b/src/uimac/ProfileTableView.m
@@ -0,0 +1,18 @@
+#import "ProfileTableView.h"
+
+@implementation ProfileTableView
+
+- (void)keyDown:(NSEvent *)event
+{
+ unichar c = [[event characters] characterAtIndex:0];
+ switch (c) {
+ case '\r':
+ [myController openButton:self];
+ break;
+ default:
+ [super keyDown:event];
+ break;
+ }
+}
+
+@end
diff --git a/src/uimac/ReconItem.h b/src/uimac/ReconItem.h
new file mode 100644
index 0000000..54a07cc
--- /dev/null
+++ b/src/uimac/ReconItem.h
@@ -0,0 +1,30 @@
+/* ReconItem */
+
+#import <Cocoa/Cocoa.h>
+#define CAML_NAME_SPACE
+#include <caml/mlvalues.h>
+
+@interface ReconItem : NSObject
+{
+ NSString *path;
+ NSString *left;
+ NSString *right;
+ NSString *direction;
+ NSString *progress;
+ NSString *details;
+ value ri; // an ocaml Common.reconItem
+}
++ initWithRi:(value)ri;
+- (NSString *) path;
+- (NSString *) left;
+- (NSString *) right;
+- (NSString *) direction;
+- (void) doAction:(unichar)action;
+- (void) doIgnore:(unichar)action;
+- (NSString *) progress;
+- (void)resetProgress;
+- (NSString *) details;
+- (BOOL)isConflict;
+- (void)revertDirection;
+
+@end
diff --git a/src/uimac/ReconItem.m b/src/uimac/ReconItem.m
new file mode 100644
index 0000000..7e0686c
--- /dev/null
+++ b/src/uimac/ReconItem.m
@@ -0,0 +1,174 @@
+#import "ReconItem.h"
+#include <caml/callback.h>
+#include <caml/memory.h>
+
+extern value Callback_checkexn(value,value);
+
+@implementation ReconItem
+
+-(void)dealloc
+{
+ ri = Val_unit;
+ caml_remove_global_root(&ri);
+ [super dealloc];
+}
+
+- (void)setRi:(value)v
+{
+ caml_register_global_root(&ri); // needed in case of ocaml garbage collection
+ ri = v;
+}
+
++ (id)initWithRi:(value)v
+{
+ ReconItem *r = [[ReconItem alloc] init];
+ [r setRi:v];
+ return r;
+}
+
+- (NSString *)path
+{
+ if (path) return path;
+
+ value *f = caml_named_value("unisonRiToPath");
+ [path release];
+ path = [NSString stringWithCString:String_val(Callback_checkexn(*f, ri))];
+ [path retain];
+ return path;
+}
+
+- (NSString *)left
+{
+ if (left) return left;
+
+ value *f = caml_named_value("unisonRiToLeft");
+ [left release];
+ left = [NSString stringWithCString:String_val(Callback_checkexn(*f, ri))];
+ [left retain];
+ return left;
+}
+
+- (NSString *)right
+{
+ if (right) return right;
+
+ value *f = caml_named_value("unisonRiToRight");
+ [right release];
+ right = [NSString stringWithCString:String_val(Callback_checkexn(*f, ri))];
+ [right retain];
+ return right;
+}
+
+- (NSString *)direction
+{
+ if (direction) return direction;
+
+ value *f = caml_named_value("unisonRiToDirection");
+ value v = Callback_checkexn(*f, ri);
+ char *s = String_val(v);
+ [direction release];
+ direction = [NSString stringWithCString:s];
+ [direction retain];
+ return direction;
+}
+
+- (void)setDirection:(char *)d
+{
+ [direction release];
+ direction = nil;
+ value *f = caml_named_value(d);
+ Callback_checkexn(*f, ri);
+}
+
+- (void)doAction:(unichar)action
+{
+ switch (action) {
+ case '>':
+ [self setDirection:"unisonRiSetRight"];
+ break;
+ case '<':
+ [self setDirection:"unisonRiSetLeft"];
+ break;
+ case '/':
+ [self setDirection:"unisonRiSetConflict"];
+ break;
+ case '-':
+ [self setDirection:"unisonRiForceOlder"];
+ break;
+ case '+':
+ [self setDirection:"unisonRiForceNewer"];
+ break;
+ case 'm':
+ [self setDirection:"unisonRiSetMerge"];
+ break;
+ default:
+ NSLog(@"ReconItem.doAction : unknown action");
+ break;
+ }
+}
+
+- (void)doIgnore:(unichar)action
+{
+ value *f;
+ switch (action) {
+ case 'I':
+ f = caml_named_value("unisonIgnorePath");
+ Callback_checkexn(*f, ri);
+ break;
+ case 'E':
+ f = caml_named_value("unisonIgnoreExt");
+ Callback_checkexn(*f, ri);
+ break;
+ case 'N':
+ f = caml_named_value("unisonIgnoreName");
+ Callback_checkexn(*f, ri);
+ break;
+ default:
+ NSLog(@"ReconItem.doIgnore : unknown ignore");
+ break;
+ }
+}
+
+- (NSString *)progress
+{
+ if (progress) return progress;
+
+ value *f = caml_named_value("unisonRiToProgress");
+ progress = [NSString stringWithCString:String_val(Callback_checkexn(*f, ri))];
+ [progress retain];
+ return progress;
+}
+
+- (void)resetProgress
+{
+ // Get rid of the memoized progress because we expect it to change
+ [progress release];
+ progress = nil;
+}
+
+- (NSString *)details
+{
+ if (details) return details;
+
+ value *f = caml_named_value("unisonRiToDetails");
+ details = [NSString stringWithCString:String_val(Callback_checkexn(*f, ri))];
+ [details retain];
+ return details;
+}
+
+- (BOOL)isConflict
+{
+ value *f = caml_named_value("unisonRiIsConflict");
+ if (Callback_checkexn(*f, ri) == Val_true) return YES;
+ else return NO;
+}
+
+- (void)revertDirection
+{
+ value *f = caml_named_value("unisonRiRevert");
+ Callback_checkexn(*f, ri);
+ [direction release];
+ direction = nil;
+}
+
+@end
diff --git a/src/uimac/ReconTableView.h b/src/uimac/ReconTableView.h
new file mode 100644
index 0000000..0416bb0
--- /dev/null
+++ b/src/uimac/ReconTableView.h
@@ -0,0 +1,31 @@
+//
+// ReconTableView.h
+//
+// NSTableView extended to handle additional keyboard events for the reconcile window.
+// The keyDown: method is redefined.
+//
+// Created by Trevor Jim on Wed Aug 27 2003.
+// Copyright (c) 2003, licensed under GNU GPL.
+//
+
+#import <AppKit/AppKit.h>
+
+@interface ReconTableView : NSTableView {
+ BOOL editable;
+}
+- (BOOL)editable;
+- (void)setEditable:(BOOL)x;
+- (IBAction)ignorePath:(id)sender;
+- (IBAction)ignoreExt:(id)sender;
+- (IBAction)ignoreName:(id)sender;
+- (IBAction)copyLR:(id)sender;
+- (IBAction)copyRL:(id)sender;
+- (IBAction)leaveAlone:(id)sender;
+- (IBAction)forceOlder:(id)sender;
+- (IBAction)forceNewer:(id)sender;
+- (IBAction)selectConflicts:(id)sender;
+- (IBAction)revert:(id)sender;
+- (IBAction)merge:(id)sender;
+- (BOOL)validateMenuItem:(NSMenuItem *)item;
+
+@end
diff --git a/src/uimac/ReconTableView.m b/src/uimac/ReconTableView.m
new file mode 100644
index 0000000..fd17a2b
--- /dev/null
+++ b/src/uimac/ReconTableView.m
@@ -0,0 +1,180 @@
+//
+// ReconTableView.m
+// Unison
+//
+// Created by Trevor Jim on Wed Aug 27 2003.
+// Copyright (c) 2003. See file COPYING for details.
+//
+
+#import "ReconTableView.h"
+#import "ReconItem.h"
+#import "MyController.h"
+
+@implementation ReconTableView
+
+- (BOOL)editable
+{
+ return editable;
+}
+
+- (void)setEditable:(BOOL)x
+{
+ editable = x;
+}
+
+- (void)awakeFromNib
+{
+ editable = NO;
+}
+
+- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
+{
+ if ([menuItem action] == @selector(selectAll:)
+ || [menuItem action] == @selector(selectConflicts:)
+ || [menuItem action] == @selector(copyLR:)
+ || [menuItem action] == @selector(copyRL:)
+ || [menuItem action] == @selector(leaveAlone:)
+ || [menuItem action] == @selector(forceNewer:)
+ || [menuItem action] == @selector(forceOlder:)
+ || [menuItem action] == @selector(revert:)
+ || [menuItem action] == @selector(merge:)
+ || [menuItem action] == @selector(ignorePath:)
+ || [menuItem action] == @selector(ignoreExt:)
+ || [menuItem action] == @selector(ignoreName:))
+ return editable;
+ else return YES;
+}
+
+- (void)doIgnore:(unichar)c
+{
+ NSMutableArray *reconItems = [[self dataSource] reconItems];
+ NSEnumerator *e = [self selectedRowEnumerator];
+ NSNumber *n = [e nextObject];
+ int i = -1;
+ for (; n != nil; n = [e nextObject]) {
+ i = [n intValue];
+ [[reconItems objectAtIndex:i] doIgnore:c];
+ }
+ if (i>=0) { // something was selected
+ i = [[self dataSource] updateForIgnore:i];
+ [self selectRow:i byExtendingSelection:NO];
+ [self reloadData];
+ }
+}
+
+- (IBAction)ignorePath:(id)sender
+{
+ [self doIgnore:'I'];
+}
+
+- (IBAction)ignoreExt:(id)sender
+{
+ [self doIgnore:'E'];
+}
+
+- (IBAction)ignoreName:(id)sender
+{
+ [self doIgnore:'N'];
+}
+
+- (void)doAction:(unichar)c
+{
+ NSEnumerator *e = [self selectedRowEnumerator];
+ NSNumber *n = [e nextObject];
+ int numSelected = 0;
+ int i = -1;
+ for (; n != nil; n = [e nextObject]) {
+ numSelected++;
+ i = [n intValue];
+ NSMutableArray *reconItems = [[self dataSource] reconItems];
+ [[reconItems objectAtIndex:i] doAction:c];
+ }
+ if (numSelected>0) {
+ if (numSelected == 1 && [self numberOfRows] > i+1) {
+ // Move to next row, unless already at last row, or if more than one row selected
+ [self selectRow:i+1 byExtendingSelection:NO];
+ [self scrollRowToVisible:i+1];
+ }
+ else [self reloadData];
+ }
+}
+
+- (IBAction)copyLR:(id)sender
+{
+ [self doAction:'>'];
+}
+
+- (IBAction)copyRL:(id)sender
+{
+ [self doAction:'<'];
+}
+
+- (IBAction)leaveAlone:(id)sender
+{
+ [self doAction:'/'];
+}
+
+- (IBAction)forceOlder:(id)sender
+{
+ [self doAction:'-'];
+}
+
+- (IBAction)forceNewer:(id)sender
+{
+ [self doAction:'+'];
+}
+
+- (IBAction)selectConflicts:(id)sender
+{
+ [self deselectAll:self];
+ NSMutableArray *reconItems = [[self dataSource] reconItems];
+ int i = 0;
+ for (; i < [reconItems count]; i++) {
+ if ([[reconItems objectAtIndex:i] isConflict])
+ [self selectRow:i byExtendingSelection:YES];
+ }
+}
+
+- (IBAction)revert:(id)sender
+{
+ NSMutableArray *reconItems = [[self dataSource] reconItems];
+ NSEnumerator *e = [self selectedRowEnumerator];
+ NSNumber *n = [e nextObject];
+ int i;
+ for (; n != nil; n = [e nextObject]) {
+ i = [n intValue];
+ [[reconItems objectAtIndex:i] revertDirection];
+ }
+ [self reloadData];
+}
+
+- (IBAction)merge:(id)sender
+{
+ [self doAction:'m'];
+}
+
+/* There are menu commands for these, but we add some shortcuts so you don't
+ have to press the Command key */
+- (void)keyDown:(NSEvent *)event
+{
+ unichar c = [[event characters] characterAtIndex:0];
+ switch (c) {
+ case '>':
+ case NSRightArrowFunctionKey:
+ [self doAction:'>'];
+ break;
+ case '<':
+ case NSLeftArrowFunctionKey:
+ [self doAction:'<'];
+ break;
+ case '?':
+ case '/':
+ [self doAction:'/'];
+ break;
+ default:
+ [super keyDown:event];
+ break;
+ }
+}
+
+@end
diff --git a/src/uimac/TrevorsUnison.icns b/src/uimac/TrevorsUnison.icns
new file mode 100644
index 0000000..e1fa6f8
--- /dev/null
+++ b/src/uimac/TrevorsUnison.icns
Binary files differ
diff --git a/src/uimac/Unison.icns b/src/uimac/Unison.icns
new file mode 100644
index 0000000..34bf600
--- /dev/null
+++ b/src/uimac/Unison.icns
Binary files differ
diff --git a/src/uimac/cltool.c b/src/uimac/cltool.c
new file mode 100644
index 0000000..2726b19
--- /dev/null
+++ b/src/uimac/cltool.c
@@ -0,0 +1,67 @@
+/* cltool.c
+
+ This is a command-line tool for Mac OS X that looks up the unison
+ application, where ever it has been installed, and runs it. This
+ is intended to be installed in a standard place (e.g.,
+ /usr/bin/unison) to make it easy to invoke unison as a server, or
+ to use unison from the command line when it has been installed with
+ a GUI.
+
+ */
+
+#import <CoreServices/CoreServices.h>
+#import <ApplicationServices/ApplicationServices.h>
+#include <stdio.h>
+
+#define BUFSIZE 1024
+#define EXECPATH "/Contents/MacOS/Unison"
+
+int main(int argc, char **argv) {
+
+ /* Look up the application by its bundle identifier, which is given
+ in the Info.plist file. This will continue to work even if the
+ user changes the name of the application, unlike
+ fullPathForApplication. */
+
+ FSRef fsref;
+ OSStatus status;
+ int len;
+ char buf[BUFSIZE];
+
+ status = LSFindApplicationForInfo(kLSUnknownCreator,CFSTR("edu.upenn.cis.Unison"),NULL,&fsref,NULL);
+ if (status) {
+ if (status == kLSApplicationNotFoundErr) {
+ fprintf(stderr,"Error: can't find the Unison application using the Launch Services database.\n");
+ fprintf(stderr,"Try launching Unison from the Finder, and then try this again.\n",status);
+ }
+ else fprintf(stderr,"Error: can't find Unison application (%d).\n",status);
+ exit(1);
+ }
+
+ status = FSRefMakePath(&fsref,buf,BUFSIZE);
+ if (status) {
+ fprintf(stderr,"Error: problem building path to Unison application (%d).\n",status);
+ exit(1);
+ }
+
+ len = strlen(buf);
+ if (len + strlen(EXECPATH) + 1 > BUFSIZE) {
+ fprintf(stderr,"Error: path to Unison application exceeds internal buffer size (%d).\n",BUFSIZE);
+ exit(1);
+ }
+ strcat(buf,EXECPATH);
+
+ /* It's important to pass the absolute path on to the GUI,
+ that's how it knows where to find the bundle, e.g., the
+ Info.plist file. */
+ argv[0] = buf;
+
+ // printf("The Unison executable is at %s\n",argv[0]);
+ // printf("Running...\n");
+
+ execv(argv[0],argv);
+
+ /* If we get here the execv has failed; print an error message to stderr */
+ perror("unison");
+ exit(1);
+}
diff --git a/src/uimac/main.m b/src/uimac/main.m
new file mode 100644
index 0000000..a2b4852
--- /dev/null
+++ b/src/uimac/main.m
@@ -0,0 +1,89 @@
+//
+// main.m
+// uimac
+//
+// Created by Trevor Jim on Sun Aug 17 2003.
+// Copyright (c) 2003, see file COPYING for details.
+//
+
+#import <Cocoa/Cocoa.h>
+
+#define CAML_NAME_SPACE
+#include <caml/callback.h>
+
+void reportExn(value e) {
+ value *f = caml_named_value("unisonExnInfo");
+ char *m = String_val(caml_callback(*f,Extract_exception(e)));
+ NSString *s = [NSString stringWithFormat:@"Uncaught exception: %s", m];
+ NSLog(@"%@",s);
+ NSRunAlertPanel(@"Fatal error",s,@"Exit",nil,nil);
+}
+
+value Callback_checkexn(value c,value v) {
+ value e = caml_callback_exn(c,v);
+ if (!Is_exception_result(e)) return e;
+ reportExn(e);
+ exit(1);
+}
+
+value Callback2_checkexn(value c,value v1,value v2) {
+ value e = caml_callback2_exn(c,v1,v2);
+ if (!Is_exception_result(e)) return e;
+ reportExn(e);
+ exit(1);
+}
+
+value Callback3_checkexn(value c,value v1,value v2,value v3) {
+ value e = caml_callback3_exn(c,v1,v2,v3);
+ if (!Is_exception_result(e)) return e;
+ reportExn(e);
+ exit(1);
+}
+
+int main(int argc, const char *argv[])
+{
+ int i;
+
+ /* When you click-start or use the open command, the program is invoked with
+ a command-line arg of the form -psn_XXXXXXXXX. The XXXXXXXX is a "process
+ serial number" and it seems to be important for Carbon programs. We need
+ to get rid of it if it's there so the ocaml code won't exit. Note, the
+ extra arg is not added if the binary is invoked directly from the command
+ line without using the open command. */
+ if (argc == 2 && strncmp(argv[1],"-psn_",5) == 0) {
+ argc--;
+ argv[1] = NULL;
+ }
+
+ /* Initialize ocaml gc, etc. */
+ caml_startup((char **)argv); // cast to avoid warning, caml_startup assumes non-const,
+ // NSApplicationMain assumes const
+
+ /* Check for invocations that don't start up the gui */
+ for (i=1; i<argc; i++) {
+ if (!strcmp(argv[i],"-doc") ||
+ !strcmp(argv[i],"-help") ||
+ !strcmp(argv[i],"-version") ||
+ !strcmp(argv[i],"-server") ||
+ !strcmp(argv[i],"-socket") ||
+ !strcmp(argv[i],"-ui")) {
+ /* We install an autorelease pool here because there might be callbacks
+ from ocaml to objc code */
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ value *f = caml_named_value("unisonNonGuiStartup");
+ value e = caml_callback_exn(*f,Val_unit);
+ if (Is_exception_result(e)) {
+ value *f = caml_named_value("unisonExnInfo");
+ char *m = String_val(caml_callback(*f,Extract_exception(e)));
+ NSLog(@"Uncaught exception: %s", m);
+ exit(1);
+ }
+ [pool release];
+ /* If we get here without exiting first, the non GUI startup detected a
+ -ui graphic or command-line profile, and we should in fact start the GUI. */
+ }
+ }
+
+ /* go! */
+ return NSApplicationMain(argc, argv);
+}
diff --git a/src/uimac/uimac.pbproj/project.pbxproj b/src/uimac/uimac.pbproj/project.pbxproj
new file mode 100644
index 0000000..5fd3d1d
--- /dev/null
+++ b/src/uimac/uimac.pbproj/project.pbxproj
@@ -0,0 +1,580 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 39;
+ objects = {
+ 080E96DDFE201D6D7F000001 = {
+ children = (
+ );
+ isa = PBXGroup;
+ name = Classes;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ 089C165CFE840E0CC02AAC07 = {
+ children = (
+ 089C165DFE840E0CC02AAC07,
+ );
+ isa = PBXVariantGroup;
+ name = InfoPlist.strings;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ 089C165DFE840E0CC02AAC07 = {
+ fileEncoding = 10;
+ isa = PBXFileReference;
+ lastKnownFileType = text.plist.strings;
+ name = English;
+ path = English.lproj/InfoPlist.strings;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+//080
+//081
+//082
+//083
+//084
+//100
+//101
+//102
+//103
+//104
+ 1058C7A0FEA54F0111CA2CBB = {
+ children = (
+ 1058C7A1FEA54F0111CA2CBB,
+ );
+ isa = PBXGroup;
+ name = "Linked Frameworks";
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ 1058C7A1FEA54F0111CA2CBB = {
+ isa = PBXFileReference;
+ lastKnownFileType = wrapper.framework;
+ name = Cocoa.framework;
+ path = /System/Library/Frameworks/Cocoa.framework;
+ refType = 0;
+ sourceTree = "<absolute>";
+ };
+ 1058C7A2FEA54F0111CA2CBB = {
+ children = (
+ 29B97325FDCFA39411CA2CEA,
+ 29B97324FDCFA39411CA2CEA,
+ );
+ isa = PBXGroup;
+ name = "Other Frameworks";
+ refType = 4;
+ sourceTree = "<group>";
+ };
+//100
+//101
+//102
+//103
+//104
+//190
+//191
+//192
+//193
+//194
+ 19C28FACFE9D520D11CA2CBB = {
+ children = (
+ 69C625F50664EC3300B3C46A,
+ );
+ isa = PBXGroup;
+ name = Products;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+//190
+//191
+//192
+//193
+//194
+//290
+//291
+//292
+//293
+//294
+ 29B97313FDCFA39411CA2CEA = {
+ buildSettings = {
+ };
+ buildStyles = (
+ 4A9504CCFFE6A4B311CA0CBA,
+ 4A9504CDFFE6A4B311CA0CBA,
+ );
+ hasScannedForEncodings = 1;
+ isa = PBXProject;
+ mainGroup = 29B97314FDCFA39411CA2CEA;
+ projectDirPath = "";
+ targets = (
+ 69C625DD0664EC3300B3C46A,
+ );
+ };
+ 29B97314FDCFA39411CA2CEA = {
+ children = (
+ 69E407B907EB95AA00D37AA1,
+ 080E96DDFE201D6D7F000001,
+ 29B97315FDCFA39411CA2CEA,
+ 29B97317FDCFA39411CA2CEA,
+ 29B97323FDCFA39411CA2CEA,
+ 19C28FACFE9D520D11CA2CBB,
+ 69C625F40664EC3300B3C46A,
+ );
+ isa = PBXGroup;
+ name = uimac;
+ path = "";
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ 29B97315FDCFA39411CA2CEA = {
+ children = (
+ 29B97316FDCFA39411CA2CEA,
+ 69660DC604F08CC100CF23A4,
+ 69660DC704F08CC100CF23A4,
+ 69BA7DA804FD695200CF23A4,
+ 69BA7DA904FD695200CF23A4,
+ 690F564404F11EC300CF23A4,
+ 690F564504F11EC300CF23A4,
+ 69D3C6FA04F1CC3700CF23A4,
+ 69D3C6F904F1CC3700CF23A4,
+ 697985CD050CFA2D00CF23A4,
+ 697985CE050CFA2D00CF23A4,
+ 691CE180051BB44A00CF23A4,
+ 691CE181051BB44A00CF23A4,
+ );
+ isa = PBXGroup;
+ name = "Other Sources";
+ path = "";
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ 29B97316FDCFA39411CA2CEA = {
+ fileEncoding = 30;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.objc;
+ path = main.m;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ 29B97317FDCFA39411CA2CEA = {
+ children = (
+ 29B97318FDCFA39411CA2CEA,
+ 089C165CFE840E0CC02AAC07,
+ 69C625CA0664E94E00B3C46A,
+ );
+ isa = PBXGroup;
+ name = Resources;
+ path = "";
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ 29B97318FDCFA39411CA2CEA = {
+ children = (
+ 29B97319FDCFA39411CA2CEA,
+ );
+ isa = PBXVariantGroup;
+ name = MainMenu.nib;
+ path = "";
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ 29B97319FDCFA39411CA2CEA = {
+ isa = PBXFileReference;
+ lastKnownFileType = wrapper.nib;
+ name = English;
+ path = English.lproj/MainMenu.nib;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ 29B97323FDCFA39411CA2CEA = {
+ children = (
+ 1058C7A0FEA54F0111CA2CBB,
+ 1058C7A2FEA54F0111CA2CBB,
+ );
+ isa = PBXGroup;
+ name = Frameworks;
+ path = "";
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ 29B97324FDCFA39411CA2CEA = {
+ isa = PBXFileReference;
+ lastKnownFileType = wrapper.framework;
+ name = AppKit.framework;
+ path = /System/Library/Frameworks/AppKit.framework;
+ refType = 0;
+ sourceTree = "<absolute>";
+ };
+ 29B97325FDCFA39411CA2CEA = {
+ isa = PBXFileReference;
+ lastKnownFileType = wrapper.framework;
+ name = Foundation.framework;
+ path = /System/Library/Frameworks/Foundation.framework;
+ refType = 0;
+ sourceTree = "<absolute>";
+ };
+//290
+//291
+//292
+//293
+//294
+//4A0
+//4A1
+//4A2
+//4A3
+//4A4
+ 4A9504CCFFE6A4B311CA0CBA = {
+ buildSettings = {
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ NSZombieEnabled = YES;
+ OPTIMIZATION_CFLAGS = "-O0";
+ ZERO_LINK = YES;
+ };
+ isa = PBXBuildStyle;
+ name = Development;
+ };
+ 4A9504CDFFE6A4B311CA0CBA = {
+ buildSettings = {
+ COPY_PHASE_STRIP = YES;
+ GCC_ENABLE_FIX_AND_CONTINUE = NO;
+ GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES;
+ ZERO_LINK = NO;
+ };
+ isa = PBXBuildStyle;
+ name = Deployment;
+ };
+//4A0
+//4A1
+//4A2
+//4A3
+//4A4
+//690
+//691
+//692
+//693
+//694
+ 690F564404F11EC300CF23A4 = {
+ fileEncoding = 30;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.h;
+ path = ProfileController.h;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ 690F564504F11EC300CF23A4 = {
+ fileEncoding = 30;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.objc;
+ path = ProfileController.m;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ 691CE180051BB44A00CF23A4 = {
+ fileEncoding = 30;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.h;
+ path = ProfileTableView.h;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ 691CE181051BB44A00CF23A4 = {
+ fileEncoding = 30;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.objc;
+ path = ProfileTableView.m;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ 69660DC604F08CC100CF23A4 = {
+ fileEncoding = 30;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.h;
+ path = MyController.h;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ 69660DC704F08CC100CF23A4 = {
+ fileEncoding = 30;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.objc;
+ path = MyController.m;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ 697985CD050CFA2D00CF23A4 = {
+ fileEncoding = 30;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.h;
+ path = PreferencesController.h;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ 697985CE050CFA2D00CF23A4 = {
+ fileEncoding = 30;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.objc;
+ path = PreferencesController.m;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ 69BA7DA804FD695200CF23A4 = {
+ fileEncoding = 4;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.h;
+ path = ReconTableView.h;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ 69BA7DA904FD695200CF23A4 = {
+ fileEncoding = 4;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.objc;
+ path = ReconTableView.m;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ 69C625CA0664E94E00B3C46A = {
+ isa = PBXFileReference;
+ lastKnownFileType = image.icns;
+ path = Unison.icns;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ 69C625DD0664EC3300B3C46A = {
+ buildPhases = (
+ 69C625DE0664EC3300B3C46A,
+ 69C625E50664EC3300B3C46A,
+ 69C625E90664EC3300B3C46A,
+ 69C625F10664EC3300B3C46A,
+ );
+ buildRules = (
+ );
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = "";
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ HEADER_SEARCH_PATHS = "$(OCAMLLIBDIR)";
+ INFOPLIST_FILE = Info.plist;
+ INSTALL_PATH = "$(HOME)/Applications";
+ LIBRARY_SEARCH_PATHS = "";
+ OCAMLLIBDIR = /usr/local/lib/ocaml;
+ OTHER_CFLAGS = "";
+ OTHER_LDFLAGS = "$(SRCROOT)/../unison-blob.o -L$(OCAMLLIBDIR) -lunix -lstr -lasmrun";
+ PREBINDING = NO;
+ PRODUCT_NAME = Unison;
+ SECTORDER_FLAGS = "";
+ WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas";
+ WRAPPER_EXTENSION = app;
+ };
+ dependencies = (
+ );
+ isa = PBXNativeTarget;
+ name = uimac;
+ productInstallPath = "$(HOME)/Applications";
+ productName = uimac;
+ productReference = 69C625F50664EC3300B3C46A;
+ productType = "com.apple.product-type.application";
+ };
+ 69C625DE0664EC3300B3C46A = {
+ buildActionMask = 2147483647;
+ files = (
+ 69C625DF0664EC3300B3C46A,
+ 69C625E00664EC3300B3C46A,
+ 69C625E10664EC3300B3C46A,
+ 69C625E20664EC3300B3C46A,
+ 69C625E30664EC3300B3C46A,
+ 69C625E40664EC3300B3C46A,
+ );
+ isa = PBXHeadersBuildPhase;
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 69C625DF0664EC3300B3C46A = {
+ fileRef = 69660DC604F08CC100CF23A4;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 69C625E00664EC3300B3C46A = {
+ fileRef = 690F564404F11EC300CF23A4;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 69C625E10664EC3300B3C46A = {
+ fileRef = 69D3C6FA04F1CC3700CF23A4;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 69C625E20664EC3300B3C46A = {
+ fileRef = 69BA7DA804FD695200CF23A4;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 69C625E30664EC3300B3C46A = {
+ fileRef = 697985CD050CFA2D00CF23A4;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 69C625E40664EC3300B3C46A = {
+ fileRef = 691CE180051BB44A00CF23A4;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 69C625E50664EC3300B3C46A = {
+ buildActionMask = 2147483647;
+ files = (
+ 69C625E60664EC3300B3C46A,
+ 69C625E70664EC3300B3C46A,
+ 69C625E80664EC3300B3C46A,
+ );
+ isa = PBXResourcesBuildPhase;
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 69C625E60664EC3300B3C46A = {
+ fileRef = 29B97318FDCFA39411CA2CEA;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 69C625E70664EC3300B3C46A = {
+ fileRef = 089C165CFE840E0CC02AAC07;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 69C625E80664EC3300B3C46A = {
+ fileRef = 69C625CA0664E94E00B3C46A;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 69C625E90664EC3300B3C46A = {
+ buildActionMask = 2147483647;
+ files = (
+ 69C625EA0664EC3300B3C46A,
+ 69C625EB0664EC3300B3C46A,
+ 69C625EC0664EC3300B3C46A,
+ 69C625ED0664EC3300B3C46A,
+ 69C625EE0664EC3300B3C46A,
+ 69C625EF0664EC3300B3C46A,
+ 69C625F00664EC3300B3C46A,
+ );
+ isa = PBXSourcesBuildPhase;
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 69C625EA0664EC3300B3C46A = {
+ fileRef = 29B97316FDCFA39411CA2CEA;
+ isa = PBXBuildFile;
+ settings = {
+ ATTRIBUTES = (
+ );
+ };
+ };
+ 69C625EB0664EC3300B3C46A = {
+ fileRef = 69660DC704F08CC100CF23A4;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 69C625EC0664EC3300B3C46A = {
+ fileRef = 690F564504F11EC300CF23A4;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 69C625ED0664EC3300B3C46A = {
+ fileRef = 69D3C6F904F1CC3700CF23A4;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 69C625EE0664EC3300B3C46A = {
+ fileRef = 69BA7DA904FD695200CF23A4;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 69C625EF0664EC3300B3C46A = {
+ fileRef = 697985CE050CFA2D00CF23A4;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 69C625F00664EC3300B3C46A = {
+ fileRef = 691CE181051BB44A00CF23A4;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 69C625F10664EC3300B3C46A = {
+ buildActionMask = 2147483647;
+ files = (
+ 69C625F20664EC3300B3C46A,
+ 69E407BA07EB95AA00D37AA1,
+ );
+ isa = PBXFrameworksBuildPhase;
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 69C625F20664EC3300B3C46A = {
+ fileRef = 1058C7A1FEA54F0111CA2CBB;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ 69C625F40664EC3300B3C46A = {
+ isa = PBXFileReference;
+ lastKnownFileType = text.xml;
+ path = Info.plist;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ 69C625F50664EC3300B3C46A = {
+ explicitFileType = wrapper.application;
+ includeInIndex = 0;
+ isa = PBXFileReference;
+ path = Unison.app;
+ refType = 3;
+ sourceTree = BUILT_PRODUCTS_DIR;
+ };
+ 69D3C6F904F1CC3700CF23A4 = {
+ fileEncoding = 30;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.objc;
+ path = ReconItem.m;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ 69D3C6FA04F1CC3700CF23A4 = {
+ fileEncoding = 30;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.h;
+ path = ReconItem.h;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ 69E407B907EB95AA00D37AA1 = {
+ isa = PBXFileReference;
+ lastKnownFileType = wrapper.framework;
+ name = Security.framework;
+ path = /System/Library/Frameworks/Security.framework;
+ refType = 0;
+ sourceTree = "<absolute>";
+ };
+ 69E407BA07EB95AA00D37AA1 = {
+ fileRef = 69E407B907EB95AA00D37AA1;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ };
+ rootObject = 29B97313FDCFA39411CA2CEA;
+}