summaryrefslogtreecommitdiffstats
path: root/Liaison
diff options
context:
space:
mode:
authorBrian Cully <bjc@kublai.com>2008-04-14 21:45:08 -0400
committerBrian Cully <github.20.shmit@spamgourmet.com>2008-04-14 21:45:08 -0400
commit17349a5e426dc7acf1216a3767a22f69974cbca0 (patch)
tree20029d02f07ab6257cccec36d34fb312f796e1d1 /Liaison
downloadliaison-17349a5e426dc7acf1216a3767a22f69974cbca0.tar.gz
liaison-17349a5e426dc7acf1216a3767a22f69974cbca0.zip
Initial commit.
Diffstat (limited to 'Liaison')
-rw-r--r--Liaison/ApplicationController.h21
-rw-r--r--Liaison/ApplicationController.m90
-rw-r--r--Liaison/ClientManager.h43
-rw-r--r--Liaison/ClientManager.m488
-rw-r--r--Liaison/CopyController.h27
-rw-r--r--Liaison/CopyController.m133
-rw-r--r--Liaison/DownloadManager.h36
-rw-r--r--Liaison/DownloadManager.m69
-rw-r--r--Liaison/DownloadStatusView.h27
-rw-r--r--Liaison/DownloadStatusView.m118
-rw-r--r--Liaison/Downloader.h63
-rw-r--r--Liaison/Downloader.m429
-rw-r--r--Liaison/English.lproj/CopyPanel.nib/Download_StopPressed.tiff0
-rw-r--r--Liaison/English.lproj/CopyPanel.nib/classes.nib38
-rw-r--r--Liaison/English.lproj/CopyPanel.nib/info.nib22
-rw-r--r--Liaison/English.lproj/CopyPanel.nib/objects.nibbin0 -> 2593 bytes
-rw-r--r--Liaison/English.lproj/InfoPlist.stringsbin0 -> 726 bytes
-rw-r--r--Liaison/English.lproj/Liaison Help/Liaison.html18
-rw-r--r--Liaison/English.lproj/Liaison Help/images/Liaison.pngbin0 -> 28966 bytes
-rw-r--r--Liaison/English.lproj/Liaison Help/images/Liaison_small.pngbin0 -> 944 bytes
-rw-r--r--Liaison/English.lproj/Liaison Help/pages/bugs.html25
-rw-r--r--Liaison/English.lproj/Liaison Help/pages/faq.html80
-rw-r--r--Liaison/English.lproj/Liaison Help/pages/glossary.html29
-rw-r--r--Liaison/English.lproj/Liaison Help/pages/navigation.html20
-rw-r--r--Liaison/English.lproj/Liaison Help/pages/overview.html36
-rw-r--r--Liaison/English.lproj/Liaison Help/pages/todo.html25
-rw-r--r--Liaison/English.lproj/Liaison Help/pages/whatsnew.html167
-rw-r--r--Liaison/English.lproj/Liaison.scriptTerminology22
-rw-r--r--Liaison/English.lproj/LoadPanel.nib/JavaCompiling.plist8
-rw-r--r--Liaison/English.lproj/LoadPanel.nib/_LoadPanel_EOArchive_English.java143
-rw-r--r--Liaison/English.lproj/LoadPanel.nib/classes.nib28
-rw-r--r--Liaison/English.lproj/LoadPanel.nib/info.nib16
-rw-r--r--Liaison/English.lproj/LoadPanel.nib/objects.nibbin0 -> 1356 bytes
-rw-r--r--Liaison/English.lproj/MainMenu.nib/classes.nib123
-rw-r--r--Liaison/English.lproj/MainMenu.nib/info.nib36
-rw-r--r--Liaison/English.lproj/MainMenu.nib/keyedobjects.nibbin0 -> 32761 bytes
-rw-r--r--Liaison/English.lproj/PreferencesWindow.nib/classes.nib50
-rw-r--r--Liaison/English.lproj/PreferencesWindow.nib/info.nib38
-rw-r--r--Liaison/English.lproj/PreferencesWindow.nib/objects.nibbin0 -> 2516 bytes
-rw-r--r--Liaison/English.lproj/WindowElements.strings37
-rw-r--r--Liaison/FileTableDelegate.h106
-rw-r--r--Liaison/FileTableDelegate.m1345
-rw-r--r--Liaison/FindController.h43
-rw-r--r--Liaison/FindController.m154
-rw-r--r--Liaison/FlippedBox.h6
-rw-r--r--Liaison/FlippedBox.m8
-rw-r--r--Liaison/Group.h52
-rw-r--r--Liaison/Group.m321
-rw-r--r--Liaison/GroupTableDelegate.h43
-rw-r--r--Liaison/GroupTableDelegate.m611
-rw-r--r--Liaison/ImageAndTextCell.h20
-rw-r--r--Liaison/ImageAndTextCell.m156
-rw-r--r--Liaison/Images/Add.tiffbin0 -> 2898 bytes
-rw-r--r--Liaison/Images/AddFiles.tiffbin0 -> 4378 bytes
-rw-r--r--Liaison/Images/AddGroup.tiffbin0 -> 4378 bytes
-rw-r--r--Liaison/Images/Download_Reload.tiffbin0 -> 438 bytes
-rw-r--r--Liaison/Images/Download_ReloadPressed.tiffbin0 -> 446 bytes
-rw-r--r--Liaison/Images/Download_Reveal.tiffbin0 -> 452 bytes
-rw-r--r--Liaison/Images/Download_RevealPressed.tiffbin0 -> 458 bytes
-rw-r--r--Liaison/Images/Download_Stop.tiffbin0 -> 438 bytes
-rw-r--r--Liaison/Images/Download_StopPressed.tiffbin0 -> 444 bytes
-rw-r--r--Liaison/Images/File Icons/Liaison.icnsbin0 -> 58750 bytes
-rw-r--r--Liaison/Images/LeftSearchCap.tiffbin0 -> 1116 bytes
-rw-r--r--Liaison/Images/NormalMailbox.tiffbin0 -> 1218 bytes
-rw-r--r--Liaison/Images/NormalMailboxLarge.tiffbin0 -> 7446 bytes
-rw-r--r--Liaison/Images/RemoveFiles.tiffbin0 -> 4378 bytes
-rw-r--r--Liaison/Images/RemoveGroup.tiffbin0 -> 4378 bytes
-rw-r--r--Liaison/Images/RightSearchCap.tiffbin0 -> 3728 bytes
-rw-r--r--Liaison/Images/SortAscending.gifbin0 -> 62 bytes
-rw-r--r--Liaison/Images/SortDescending.gifbin0 -> 62 bytes
-rw-r--r--Liaison/Images/TrashMailbox.tiffbin0 -> 1218 bytes
-rw-r--r--Liaison/Images/TrashMailboxLarge.tiffbin0 -> 4290 bytes
-rw-r--r--Liaison/Images/delete.tiffbin0 -> 4290 bytes
-rw-r--r--Liaison/Images/info (italic).tiffbin0 -> 17384 bytes
-rw-r--r--Liaison/Images/info (plain).tiffbin0 -> 3684 bytes
-rw-r--r--Liaison/Images/quickpick.tiffbin0 -> 1218 bytes
-rw-r--r--Liaison/Images/rendezvous.tiffbin0 -> 782 bytes
-rw-r--r--Liaison/Images/reveal.tiffbin0 -> 21332 bytes
-rw-r--r--Liaison/Info.plist74
-rw-r--r--Liaison/InspectorController.h24
-rw-r--r--Liaison/InspectorController.m250
-rw-r--r--Liaison/LiDataTranslator.h27
-rw-r--r--Liaison/LiDataTranslator.m120
-rw-r--r--Liaison/LiScrolLView.h10
-rw-r--r--Liaison/LiScrolLView.m34
-rw-r--r--Liaison/LiTableView.h2
-rw-r--r--Liaison/LiTableView.m166
-rw-r--r--Liaison/Liaison.h30
-rw-r--r--Liaison/Liaison.scriptSuite32
-rw-r--r--Liaison/Liaison.xcodeproj/project.pbxproj936
-rw-r--r--Liaison/LoadPanelController.h25
-rw-r--r--Liaison/LoadPanelController.m74
-rw-r--r--Liaison/NIBConnector.h30
-rw-r--r--Liaison/NIBConnector.m66
-rw-r--r--Liaison/NSException+LiDebugging.m23
-rw-r--r--Liaison/NSFileHandleExtensions.h14
-rw-r--r--Liaison/NSFileHandleExtensions.m24
-rw-r--r--Liaison/PluginManager.h27
-rw-r--r--Liaison/PluginManager.m116
-rw-r--r--Liaison/PreferencesController.h22
-rw-r--r--Liaison/PreferencesController.m154
-rw-r--r--Liaison/RenIPC.h21
-rw-r--r--Liaison/RenManager.h48
-rw-r--r--Liaison/RenManager.m357
-rw-r--r--Liaison/ServerManager.h41
-rw-r--r--Liaison/ServerManager.m428
-rw-r--r--Liaison/ViewOptionsController.h24
-rw-r--r--Liaison/ViewOptionsController.m158
-rw-r--r--Liaison/WindowController.h17
-rw-r--r--Liaison/WindowController.m306
-rw-r--r--Liaison/WriterThread.h38
-rw-r--r--Liaison/WriterThread.m211
-rw-r--r--Liaison/WriterThreadPool.h17
-rw-r--r--Liaison/WriterThreadPool.m95
-rw-r--r--Liaison/chef.lproj/CopyPanel.nib/classes.nib38
-rw-r--r--Liaison/chef.lproj/CopyPanel.nib/info.nib17
-rw-r--r--Liaison/chef.lproj/CopyPanel.nib/objects.nibbin0 -> 2733 bytes
-rw-r--r--Liaison/chef.lproj/InfoPlist.stringsbin0 -> 732 bytes
-rw-r--r--Liaison/chef.lproj/LoadPanel.nib/JavaCompiling.plist8
-rw-r--r--Liaison/chef.lproj/LoadPanel.nib/_LoadPanel_EOArchive_chef.java143
-rw-r--r--Liaison/chef.lproj/LoadPanel.nib/classes.nib28
-rw-r--r--Liaison/chef.lproj/LoadPanel.nib/info.nib16
-rw-r--r--Liaison/chef.lproj/LoadPanel.nib/objects.nibbin0 -> 1362 bytes
-rw-r--r--Liaison/chef.lproj/MainMenu.nib/classes.nib120
-rw-r--r--Liaison/chef.lproj/MainMenu.nib/info.nib38
-rw-r--r--Liaison/chef.lproj/MainMenu.nib/objects.nibbin0 -> 16364 bytes
-rw-r--r--Liaison/chef.lproj/PreferencesWindow.nib/classes.nib50
-rw-r--r--Liaison/chef.lproj/PreferencesWindow.nib/info.nib38
-rw-r--r--Liaison/chef.lproj/PreferencesWindow.nib/objects.nibbin0 -> 2533 bytes
-rw-r--r--Liaison/chef.lproj/WindowElements.strings37
-rw-r--r--Liaison/main.m12
131 files changed, 9956 insertions, 0 deletions
diff --git a/Liaison/ApplicationController.h b/Liaison/ApplicationController.h
new file mode 100644
index 0000000..b89cc19
--- /dev/null
+++ b/Liaison/ApplicationController.h
@@ -0,0 +1,21 @@
+/* ApplicationController */
+
+@protocol LiFileStoreDelegate;
+
+@interface ApplicationController : NSObject
+{
+ IBOutlet NSWindow *inspectorWindow;
+ IBOutlet NSWindow *mainWindow;
+
+ LiFileStore *theFileStore;
+}
++ (ApplicationController *)theApp;
+
+- (IBAction)openHomepage:(id)sender;
+
+- (IBAction)showInspectorWindow:(id)sender;
+- (IBAction)showMainWindow:(id)sender;
+@property (retain) NSWindow *mainWindow;
+@property (retain) NSWindow *inspectorWindow;
+@property (retain) LiFileStore *theFileStore;
+@end \ No newline at end of file
diff --git a/Liaison/ApplicationController.m b/Liaison/ApplicationController.m
new file mode 100644
index 0000000..edff915
--- /dev/null
+++ b/Liaison/ApplicationController.m
@@ -0,0 +1,90 @@
+#import "ApplicationController.h"
+
+#import "FileTableDelegate.h"
+#import "Group.h"
+#import "GroupTableDelegate.h"
+#import "NIBConnector.h"
+#import "PluginManager.h"
+#import "RenManager.h"
+#import "WindowController.h"
+
+@implementation ApplicationController
+// Set in awakeFromNib.
+static ApplicationController *theApp = nil;
+
++ (ApplicationController *)theApp;
+{
+ return theApp;
+}
+
+- (void)awakeFromNib
+{
+ theApp = self;
+}
+
+- (void)applicationDidFinishLaunching: (NSNotification *)aNotification
+{
+ RenManager *renManager;
+ NSArray *fileStorePlugins;
+ NSString *libraryPath;
+ id <LiFileStorePlugin>plugin;
+
+ // Make sure the library exists.
+ libraryPath = [[[Preferences sharedPreferences] libraryPath] stringByDeletingLastPathComponent];
+ [[NSFileManager defaultManager] createDirectoryAtPath: libraryPath
+ attributes: nil];
+
+ // Load the plugins.
+ fileStorePlugins = [[PluginManager defaultManager] fileStorePlugins];
+ plugin = [fileStorePlugins objectAtIndex: 0]; // XXX
+ [plugin initFileStore];
+
+ renManager = [RenManager sharedManager];
+ [renManager setFileStore: [plugin fileStore]];
+ [renManager startup];
+}
+
+- (BOOL)application: (NSApplication *)anApp openFile: (NSString *)aPath
+{
+ [[NSWorkspace sharedWorkspace] openFile: aPath];
+ [[NSDocumentController sharedDocumentController] noteNewRecentDocumentURL: [NSURL fileURLWithPath: aPath]];
+ return YES;
+}
+
+- (IBAction)openHomepage:(id)sender
+{
+ [[NSWorkspace sharedWorkspace] openURL:
+ [NSURL URLWithString: @"http://www.kublai.com/~shmit/software/Liaison/"]];
+}
+
+
+- (IBAction)showInspectorWindow:(id)sender
+{
+ [inspectorWindow makeKeyAndOrderFront: self];
+}
+
+- (IBAction)showMainWindow:(id)sender
+{
+ [mainWindow makeKeyAndOrderFront: self];
+}
+@synthesize theFileStore;
+@synthesize inspectorWindow;
+@synthesize mainWindow;
+@end
+
+@implementation ApplicationController (AppleScript)
+- (BOOL)application: (NSApplication *)anApp
+ delegateHandlesKey: (NSString *)aKey
+{
+ [LiLog logAsDebug: @"[ApplicationController application:delegateHandlesKey: %@", aKey];
+ if ([aKey isEqualToString: @"orderedFileStores"])
+ return YES;
+ return NO;
+}
+
+- (NSArray *)orderedFileStores
+{
+ [LiLog logAsDebug: @"[ApplicationController orderedFileStores]"];
+ return [LiFileStore allFileStores];
+}
+@end \ No newline at end of file
diff --git a/Liaison/ClientManager.h b/Liaison/ClientManager.h
new file mode 100644
index 0000000..206c3a5
--- /dev/null
+++ b/Liaison/ClientManager.h
@@ -0,0 +1,43 @@
+//
+// ClientManager.h
+// Liaison
+//
+// Created by Brian Cully on Sun Feb 16 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+@interface ClientManager : NSObject {
+ LiFileStore *theFileStore;
+ NSFileHandle *theFile;
+ NSString *theHostname;
+
+ NSFileHandle *theCopyFile;
+
+ NSMutableData *theBuffer;
+}
+
+- (id)initWithFile: (NSFileHandle *)aFile
+ andFileStore: (LiFileStore *)aFileStore;
+
+- (void)startup;
+- (void)shutdown;
+- (void)sendHostname;
+@property (retain,getter=copyFile) NSFileHandle *theCopyFile;
+@property (retain,getter=buffer) NSMutableData *theBuffer;
+@property (retain,getter=fileStore) LiFileStore *theFileStore;
+@property (retain,getter=file) NSFileHandle *theFile;
+@property (retain,getter=hostname) NSString *theHostname;
+@end
+
+@interface ClientManager (Accessors)
+- (NSMutableData *)buffer;
+- (void)setBuffer: (NSMutableData *)aBuffer;
+- (NSFileHandle *)copyFile;
+- (void)setCopyFile: (NSFileHandle *)aFile;
+- (NSFileHandle *)file;
+- (void)setFile: (NSFileHandle *)aFile;
+- (NSString *)hostname;
+- (void)setHostname: (NSString *)aHostname;
+- (LiFileStore *)fileStore;
+- (void)setFileStore: (LiFileStore *)aFileStore;
+@end
diff --git a/Liaison/ClientManager.m b/Liaison/ClientManager.m
new file mode 100644
index 0000000..2190bc4
--- /dev/null
+++ b/Liaison/ClientManager.m
@@ -0,0 +1,488 @@
+//
+// ClientManager.m
+// Liaison
+//
+// Created by Brian Cully on Sun Feb 16 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+#import "ClientManager.h"
+
+#import "LiDataTranslator.h"
+#import "RenIPC.h"
+
+#import <netinet/in.h>
+#import <string.h>
+#import <sys/socket.h>
+#import <unistd.h>
+
+@interface ClientManager (Downloader)
+- (void)startCopyOfFileHandle: (LiFileHandle *)aFileHandle;
+@end
+
+@implementation ClientManager
+- (BOOL)sendCommand: (NSDictionary *)aCmd
+{
+ NSData *cmdData;
+
+ cmdData = [aCmd encodedData];
+ if (cmdData != nil) {
+ [(NSFileHandleExtensions *)[self file] writeDataInBackground: cmdData];
+ return YES;
+ } else
+ return NO;
+}
+
+- (NSDictionary *)processData: (NSData *)someData
+{
+ NSDictionary *msg;
+
+ [[self buffer] appendData: someData];
+ msg = [NSDictionary dictionaryWithEncodedData: [self buffer]];
+ if (msg != nil)
+ [self setBuffer: [NSMutableData data]];
+ return msg;
+}
+
+- (id)initWithFile: (NSFileHandle *)aFile
+ andFileStore: (LiFileStore *)aFileStore
+{
+ NSNotificationCenter *defaultCenter;
+
+ self = [super init];
+
+ defaultCenter = [NSNotificationCenter defaultCenter];
+ [defaultCenter addObserver: self
+ selector: @selector(remoteClosed:)
+ name: FileHandleClosed
+ object: aFile];
+
+ [self setFile: aFile];
+ [self setFileStore: aFileStore];
+ [self setCopyFile: nil];
+ [self setBuffer: [NSMutableData data]];
+
+ return self;
+}
+
+- (void)dealloc
+{
+ NSNotificationCenter *defaultCenter;
+
+ [LiLog logAsDebug: @"[ClientManager dealloc]"];
+
+ defaultCenter = [NSNotificationCenter defaultCenter];
+ [defaultCenter removeObserver: self];
+
+ [self setFile: nil];
+ [self setFileStore: nil];
+ [self setCopyFile: nil];
+ [self setBuffer: nil];
+
+ [super dealloc];
+}
+
+- (void)startup
+{
+ NSNotificationCenter *defaultCenter;
+
+ defaultCenter = [NSNotificationCenter defaultCenter];
+ [defaultCenter addObserver: self
+ selector: @selector(doHandshake:)
+ name: NSFileHandleReadCompletionNotification
+ object: [self file]];
+ [[self file] readInBackgroundAndNotify];
+}
+
+- (BOOL)filePassesFilter: (LiFileHandle *)aFileHandle
+ withCustomAttributes: (NSDictionary *)someAttrs
+{
+ NSArray *groups;
+
+ if ([aFileHandle url] == nil || [[aFileHandle url] isFileURL] == NO)
+ return NO;
+
+ groups = [someAttrs objectForKey: LiGroupsAttribute];
+ if (groups != nil) {
+ return [groups count] > 0;
+ } else {
+ return [[aFileHandle groups] count] > 0;
+ }
+}
+
+- (BOOL)filePassesFilter: (LiFileHandle *)aFileHandle
+{
+ return [self filePassesFilter: aFileHandle
+ withCustomAttributes: nil];
+}
+
+- (NSMutableDictionary *)filteredAttributesForAttributes:
+ (NSDictionary *)someAttrs
+{
+ NSMutableDictionary *filteredAttributes;
+
+ [LiLog indentDebugLog];
+ filteredAttributes = [NSMutableDictionary dictionaryWithDictionary: someAttrs];
+ [filteredAttributes setObject: [NSNumber numberWithBool: NO]
+ forKey: LiIsEditableAttribute];
+ [filteredAttributes removeObjectForKey: LiDirectoryAttribute];
+ [filteredAttributes removeObjectForKey: LiApplicationAttribute];
+ [filteredAttributes removeObjectForKey: @"LiAliasAttribute"];
+
+ [LiLog unindentDebugLog];
+
+ return filteredAttributes;
+}
+
+- (NSMutableDictionary *)filteredAttributesForFileHandle:
+ (LiFileHandle *)aFileHandle
+{
+ return [self filteredAttributesForAttributes: [[aFileHandle fileStore] attributesForFileHandle: aFileHandle]];
+}
+
+- (void)sendFileList: (NSArray *)aFileList
+{
+ LiFileHandle *file;
+ NSMutableArray *filteredChanges;
+
+ [LiLog logAsDebug: @"Filtering file list."];
+ filteredChanges = [[NSMutableArray alloc] init];
+ for (file in aFileList) {
+ if ([self filePassesFilter: file]) {
+ NSDictionary *fileAttrs;
+
+ fileAttrs = [self filteredAttributesForFileHandle: file];
+ [filteredChanges addObject: fileAttrs];
+ }
+ }
+ [LiLog logAsDebug: @"Done filtering file list."];
+
+ if ([filteredChanges count] > 0) {
+ NSDictionary *tmpDict;
+
+ [LiLog logAsDebug: @"Creating command dictionary."];
+ tmpDict = [NSDictionary dictionaryWithObjects:
+ [NSArray arrayWithObjects: RenFilesAddedKey, filteredChanges, nil]
+ forKeys:
+ [NSArray arrayWithObjects: @"type", @"arg", nil]];
+ [LiLog logAsDebug: @"Done creating command dictionary."];
+ [self sendCommand: tmpDict];
+ }
+}
+
+- (void)sendDeleteList: (NSArray *)aFileList
+{
+ NSDictionary *fileInfo;
+ NSMutableArray *deleteList;
+
+ deleteList = [NSMutableArray array];
+ for (fileInfo in aFileList) {
+ LiFileHandle *file;
+ NSDictionary *oldAttributes;
+
+ file = [fileInfo objectForKey: LiFileHandleAttribute];
+ oldAttributes = [fileInfo objectForKey: LiFileOldAttributes];
+ if ([self filePassesFilter: file
+ withCustomAttributes: oldAttributes]) {
+ [deleteList addObject: [file fileID]];
+ }
+ }
+
+ if ([deleteList count] > 0) {
+ NSDictionary *tmpDict;
+
+ tmpDict = [NSDictionary dictionaryWithObjects:
+ [NSArray arrayWithObjects: RenFilesRemovedKey, deleteList, nil]
+ forKeys:
+ [NSArray arrayWithObjects: @"type", @"arg", nil]];
+ [self sendCommand: tmpDict];
+ }
+}
+
+- (void)sendChangeList: (NSArray *)aFileList
+{
+ NSDictionary *changeDict, *fileInfo;
+ NSMutableArray *changeList, *addList, *deleteList;
+
+ changeList = [NSMutableArray array];
+ addList = [NSMutableArray array];
+ deleteList = [NSMutableArray array];
+ for (fileInfo in aFileList) {
+ LiFileHandle *file;
+ NSDictionary *oldAttributes;
+
+ file = [fileInfo objectForKey: LiFileHandleAttribute];
+ oldAttributes = [fileInfo objectForKey: LiFileOldAttributes];
+ if ([self filePassesFilter: file
+ withCustomAttributes: oldAttributes]) {
+ NSDictionary *fileAttrs;
+
+ if ([self filePassesFilter: file]) {
+ NSEnumerator *attrEnum;
+ NSMutableDictionary *filtAttrs;
+ NSString *attr;
+
+ filtAttrs = [[NSMutableDictionary alloc] init];
+ fileAttrs = [self filteredAttributesForAttributes: oldAttributes];
+ attrEnum = [oldAttributes keyEnumerator];
+ while ((attr = [attrEnum nextObject]) != nil) {
+ [filtAttrs setObject: [file valueForAttribute: attr]
+ forKey: attr];
+ }
+ [filtAttrs setObject: [file fileID]
+ forKey: LiFileHandleAttribute];
+ [changeList addObject: filtAttrs];
+ [filtAttrs release];
+ } else
+ [deleteList addObject:
+ [NSDictionary dictionaryWithObjects:
+ [NSArray arrayWithObjects: file, oldAttributes, nil]
+ forKeys:
+ [NSArray arrayWithObjects: LiFileHandleAttribute, LiFileOldAttributes, nil]]];
+ } else if ([self filePassesFilter: file])
+ [addList addObject: file];
+ }
+ [self sendDeleteList: deleteList];
+ [self sendFileList: addList];
+
+ if ([changeList count] > 0) {
+ changeDict = [NSDictionary dictionaryWithObjects:
+ [NSArray arrayWithObjects: RenFilesChangedKey, changeList, nil]
+ forKeys:
+ [NSArray arrayWithObjects: @"type", @"arg", nil]];
+ [self sendCommand: changeDict];
+ }
+}
+
+/*
+ * Figure out what type of connection this is, and dispatch appropriately.
+ */
+- (void)doHandshake: (NSNotification *)aNotification
+{
+ NSData *data;
+ NSDictionary *clientMsg;
+ NSFileHandle *remoteSocket;
+ NSNotificationCenter *defaultCenter;
+
+ remoteSocket = [aNotification object];
+ defaultCenter = [NSNotificationCenter defaultCenter];
+
+ data = [[aNotification userInfo] objectForKey:
+ NSFileHandleNotificationDataItem];
+
+ clientMsg = [self processData: data];
+ if (clientMsg != nil) {
+ NSString *clientType;
+
+ clientType = [clientMsg objectForKey: @"type"];
+ if ([clientType isEqualToString: @"browser"]) {
+ [defaultCenter removeObserver: self
+ name: NSFileHandleReadCompletionNotification
+ object: [self file]];
+ [self sendFileList: [[self fileStore] allFileHandles]];
+
+ [defaultCenter addObserver: self
+ selector: @selector(respondToFileChanged:)
+ name: LiFileChangedNotification
+ object: [self fileStore]];
+ } else if ([clientType isEqualToString: @"download"]) {
+ LiFileHandle *tmpFile;
+ id handle;
+
+ handle = [clientMsg objectForKey: LiFileHandleAttribute];
+ tmpFile = [[[LiFileHandle alloc] init] autorelease];
+ [tmpFile setFileStore: [self fileStore]];
+ [tmpFile setFileID: handle];
+ if ([self filePassesFilter: tmpFile]) {
+ [defaultCenter removeObserver: self
+ name: NSFileHandleReadCompletionNotification
+ object: [self file]];
+
+ [self startCopyOfFileHandle: tmpFile];
+ } else {
+ [LiLog logAsWarning: @"attempt to access non-shared file: %@.", [tmpFile url]];
+ [self shutdown];
+ return;
+ }
+ }
+ }
+
+ [remoteSocket readInBackgroundAndNotify];
+}
+
+- (void)shutdown
+{
+ NSNotificationCenter *defaultCenter;
+
+ defaultCenter = [NSNotificationCenter defaultCenter];
+ [defaultCenter postNotificationName: CLIENTMANAGERDEATHNOTIFICATION
+ object: self
+ userInfo: nil];
+}
+
+- (void)sendHostname
+{
+ NSDictionary *tmpDict;
+
+ tmpDict = [NSDictionary dictionaryWithObjects:
+ [NSArray arrayWithObjects: RenHostnameKey, [self hostname], nil]
+ forKeys:
+ [NSArray arrayWithObjects: @"type", @"arg", nil]];
+ [self sendCommand: tmpDict];
+}
+
+- (void)respondToFileChanged: (NSNotification *)aNotification
+{
+ NSDictionary *fileDict;
+
+ fileDict = [aNotification userInfo];
+ if ([fileDict objectForKey: LiFilesAdded] != nil) {
+ [self sendFileList: [fileDict objectForKey: LiFilesAdded]];
+ }
+ if ([fileDict objectForKey: LiFilesChanged]) {
+ [self sendChangeList: [fileDict objectForKey: LiFilesChanged]];
+ }
+ if ([fileDict objectForKey: LiFilesRemoved]) {
+ [self sendDeleteList: [fileDict objectForKey: LiFilesRemoved]];
+ }
+}
+
+- (void)remoteClosed: (NSNotification *)aNotification
+{
+ [self shutdown];
+}
+@synthesize theFile;
+@synthesize theFileStore;
+@synthesize theHostname;
+@synthesize theBuffer;
+@synthesize theCopyFile;
+@end
+
+@implementation ClientManager (Accessors)
+- (NSMutableData *)buffer
+{
+ return theBuffer;
+}
+
+- (void)setBuffer: (NSMutableData *)aBuffer
+{
+ [aBuffer retain];
+ [theBuffer release];
+ theBuffer = aBuffer;
+}
+
+- (NSFileHandle *)copyFile
+{
+ return theCopyFile;
+}
+
+- (void)setCopyFile: (NSFileHandle *)aFile
+{
+ [aFile retain];
+ [theCopyFile release];
+ theCopyFile = aFile;
+}
+
+- (NSFileHandle *)file
+{
+ return theFile;
+}
+
+- (void)setFile: (NSFileHandle *)aFile
+{
+ [aFile retain];
+ [theFile release];
+ theFile = aFile;
+}
+
+- (NSString *)hostname
+{
+ return theHostname;
+}
+
+- (void)setHostname: (NSString *)aHostname
+{
+ [aHostname retain];
+ [theHostname release];
+ theHostname = aHostname;
+}
+
+- (LiFileStore *)fileStore
+{
+ return theFileStore;
+}
+
+- (void)setFileStore: (LiFileStore *)aFileStore
+{
+ [aFileStore retain];
+ [theFileStore release];
+ theFileStore = aFileStore;
+}
+@end
+
+@implementation ClientManager (Downloader)
+#define XMITBUFFLEN 1400
+
+- (void)readyToWrite: (NSNotification *)aNotification
+{
+ NSData *fileData;
+ NSFileHandle *remoteSocket;
+
+ remoteSocket = [aNotification object];
+ if (remoteSocket == nil)
+ remoteSocket = [self file];
+
+ fileData = [[self copyFile] readDataOfLength: XMITBUFFLEN];
+ if ([fileData length] < XMITBUFFLEN) {
+ NSNotificationCenter *defaultCenter;
+
+ defaultCenter = [NSNotificationCenter defaultCenter];
+ [defaultCenter removeObserver: self
+ name: FileHandleWriteComplete
+ object: remoteSocket];
+ [defaultCenter addObserver: self
+ selector: @selector(finishedCopying:)
+ name: FileHandleWriteComplete
+ object: remoteSocket];
+ }
+
+ [(NSFileHandleExtensions *)remoteSocket writeDataInBackground: fileData];
+}
+
+- (void)startCopyOfFileHandle: (LiFileHandle *)aFileHandle
+{
+ NSNotificationCenter *defaultCenter;
+ NSURL *fileURL;
+
+ defaultCenter = [NSNotificationCenter defaultCenter];
+ [defaultCenter addObserver: self
+ selector: @selector(readyToWrite:)
+ name: FileHandleWriteComplete
+ object: [self file]];
+
+ fileURL = [aFileHandle url];
+ if (fileURL != nil && [fileURL isFileURL]) {
+ [self setCopyFile:
+ [NSFileHandle fileHandleForReadingAtPath: [fileURL path]]];
+ }
+ [self readyToWrite: nil];
+}
+
+- (void)finishedCopying: (NSNotification *)aNotification
+{
+ NSFileHandle *remoteSocket;
+ NSNotificationCenter *defaultCenter;
+
+ remoteSocket = [aNotification object];
+
+ defaultCenter = [NSNotificationCenter defaultCenter];
+ [defaultCenter removeObserver: self
+ name: FileHandleWriteComplete
+ object: remoteSocket];
+ [defaultCenter removeObserver: self
+ name: NSFileHandleReadCompletionNotification
+ object: remoteSocket];
+
+ [self setCopyFile: nil];
+ [self shutdown];
+}
+@end \ No newline at end of file
diff --git a/Liaison/CopyController.h b/Liaison/CopyController.h
new file mode 100644
index 0000000..e12a6e0
--- /dev/null
+++ b/Liaison/CopyController.h
@@ -0,0 +1,27 @@
+/* CopyController */
+
+@class DownloadStatusView;
+
+@interface CopyController : NSObject
+{
+ IBOutlet NSWindow *theWindow;
+ IBOutlet DownloadStatusView *theTemplate;
+ IBOutlet NSBox *theContentBox;
+ IBOutlet NSScrollView *theScrollView;
+
+ BOOL theWindowIsShowing;
+
+ NSMutableDictionary *theDownloads;
+}
+
+- (void)showWindow;
+- (void)hideWindow;
+
+- (DownloadStatusView *)statusViewForFileHandle: (LiFileHandle *)aFile;
+@property (retain) NSScrollView *theScrollView;
+@property (retain) DownloadStatusView *theTemplate;
+@property (retain) NSBox *theContentBox;
+@property (retain) NSWindow *theWindow;
+@property BOOL theWindowIsShowing;
+@property (retain) NSMutableDictionary *theDownloads;
+@end
diff --git a/Liaison/CopyController.m b/Liaison/CopyController.m
new file mode 100644
index 0000000..079e65a
--- /dev/null
+++ b/Liaison/CopyController.m
@@ -0,0 +1,133 @@
+#import "CopyController.h"
+
+#import "DownloadStatusView.h"
+
+@implementation CopyController
+- (id)init
+{
+ self = [super init];
+
+ theDownloads = [[NSMutableDictionary alloc] init];
+ theWindowIsShowing = NO;
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [theDownloads release];
+
+ [super dealloc];
+}
+
+- (void)awakeFromNib
+{
+ [theContentBox setFrame: NSMakeRect(0.0, 0.0, 0.0, 0.0)];
+}
+
+- (BOOL)windowShouldClose:(id)sender
+{
+ theWindowIsShowing = NO;
+
+ return YES;
+}
+
+- (NSRect)windowWillUseStandardFrame: (NSWindow *)aWindow
+ defaultFrame: (NSRect)defaultFrame
+{
+ NSRect stdFrame;
+ NSSize smallSize, boxSize;
+
+ defaultFrame.origin.y += 64;
+ defaultFrame.size.height -= 64;
+
+ stdFrame = [NSWindow contentRectForFrameRect: [aWindow frame]
+ styleMask: [aWindow styleMask]];
+
+ boxSize = [theContentBox bounds].size;
+ smallSize.width = MIN(defaultFrame.size.width, boxSize.width);
+ smallSize.height = MIN(defaultFrame.size.height, boxSize.height);
+
+ // XXX
+ stdFrame.size.height = MAX(smallSize.height, 100.0);
+ stdFrame.size.width = MAX(smallSize.width, 220.0);
+ stdFrame = [NSWindow frameRectForContentRect: stdFrame
+ styleMask: [aWindow styleMask]];
+
+ return stdFrame;
+}
+
+- (void)showWindow
+{
+ if (theWindowIsShowing == NO) {
+ [theWindow makeKeyAndOrderFront: self];
+ theWindowIsShowing = YES;
+ }
+}
+
+- (void)hideWindow
+{
+ if (theWindowIsShowing) {
+ [theWindow close];
+ theWindowIsShowing = NO;
+ }
+}
+
+- (void)addDownloadView: (NSView *)aView
+{
+ NSRect viewFrame;
+ NSSize contentSize, viewSize, boxSize;
+
+ viewSize = [aView bounds].size;
+ boxSize = [theContentBox bounds].size;
+ contentSize.width = MAX(viewSize.width, boxSize.width);
+ contentSize.height = boxSize.height + viewSize.height;
+ [theContentBox setFrameSize: contentSize];
+
+ viewFrame.size = viewSize;
+ viewFrame.origin = NSMakePoint(0.0, boxSize.height);
+ [aView setFrame: viewFrame];
+
+ [[theScrollView contentView] addSubview: aView];
+ //viewFrame.origin.y *= -1;
+ [aView scrollRectToVisible: viewFrame];
+}
+
+- (DownloadStatusView *)newDownloadView
+{
+ DownloadStatusView *tmpView;
+ NSData *viewData;
+
+ viewData = [NSKeyedArchiver archivedDataWithRootObject: theTemplate];
+ tmpView = [NSKeyedUnarchiver unarchiveObjectWithData: viewData];
+
+ return tmpView;
+}
+
+- (DownloadStatusView *)statusViewForFileHandle: (LiFileHandle *)aFile;
+{
+ NSString *viewKey;
+ DownloadStatusView *downloadView;
+
+ viewKey = [NSString stringWithFormat: @"%@:%@", [aFile storeID], [aFile fileID]];
+
+ downloadView = [theDownloads objectForKey: viewKey];
+ if (downloadView == nil) {
+ downloadView = [self newDownloadView];
+ [downloadView setFileHandle: aFile];
+
+ [theDownloads setObject: downloadView forKey: viewKey];
+
+ [self addDownloadView: downloadView];
+ }
+
+ [self showWindow];
+ return downloadView;
+}
+@synthesize theTemplate;
+@synthesize theWindowIsShowing;
+@synthesize theScrollView;
+@synthesize theContentBox;
+@synthesize theWindow;
+@synthesize theDownloads;
+@end
diff --git a/Liaison/DownloadManager.h b/Liaison/DownloadManager.h
new file mode 100644
index 0000000..42f9ee6
--- /dev/null
+++ b/Liaison/DownloadManager.h
@@ -0,0 +1,36 @@
+//
+// DownloadManager.h
+// Liaison
+//
+// Created by Brian Cully on Wed Jun 04 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+@interface DownloadManager : NSObject
+{
+ NSMutableDictionary *theDownloads;
+}
+// The default download manager.
++ (DownloadManager *)defaultManager;
+
+// Tell the manager to download a file. When it's finished, it'll
+// call the selector, if it's there, and if context is supplied,
+// it'll be passed on to the selector as well.
+// The selector should have the following signature:
+// - (void)fileHandleFinishedDownloading: (LiFileHandle *)aFileHandle
+// context: (void *)someContext
+// errorString: (NSString *)anError
+// If errorString isn't nil, then the file did not finish downloading
+// properly, either by user action or by some other error.
+- (void)downloadFileHandle: (LiFileHandle *)aFileHandle
+ fromSocket: (NSFileHandle *)aSocket
+ target: (id)anObject
+ didFinishSelector: (SEL)aSelector
+ withContext: (void *)someContext;
+@property (retain,getter=downloads) NSMutableDictionary *theDownloads;
+@end
+
+@interface DownloadManager (Accessors)
+- (NSMutableDictionary *)downloads;
+- (void)setDownloads: (NSMutableDictionary *)someDownloads;
+@end \ No newline at end of file
diff --git a/Liaison/DownloadManager.m b/Liaison/DownloadManager.m
new file mode 100644
index 0000000..5fb1085
--- /dev/null
+++ b/Liaison/DownloadManager.m
@@ -0,0 +1,69 @@
+//
+// DownloadManager.m
+// Liaison
+//
+// Created by Brian Cully on Wed Jun 04 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+#import "DownloadManager.h"
+#import "Downloader.h"
+
+@implementation DownloadManager
+DownloadManager *defaultManager = nil;
+
++ (DownloadManager *)defaultManager
+{
+ if (defaultManager == nil)
+ defaultManager = [[DownloadManager alloc] init];
+ return defaultManager;
+}
+
+- (id)init
+{
+ self = [super init];
+
+ [self setDownloads: [NSMutableDictionary dictionary]];
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [self setDownloads: nil];
+
+ [super dealloc];
+}
+
+- (void)downloadFileHandle: (LiFileHandle *)aFileHandle
+ fromSocket: (NSFileHandle *)aSocket
+ target: (id)anObject
+ didFinishSelector: (SEL)aSelector
+ withContext: (void *)someContext
+{
+ Downloader *downloader;
+
+ downloader = [[Downloader alloc] initWithSocket: aSocket
+ target: anObject
+ selector: aSelector
+ context: someContext];
+ [[self downloads] setObject: downloader forKey: aFileHandle];
+ [downloader downloadFileHandle: aFileHandle];
+ [downloader release];
+}
+@synthesize theDownloads;
+@end
+
+@implementation DownloadManager (Accessors)
+- (NSMutableDictionary *)downloads
+{
+ return theDownloads;
+}
+
+- (void)setDownloads: (NSMutableDictionary *)someDownloads
+{
+ [someDownloads retain];
+ [theDownloads release];
+ theDownloads = someDownloads;
+}
+@end \ No newline at end of file
diff --git a/Liaison/DownloadStatusView.h b/Liaison/DownloadStatusView.h
new file mode 100644
index 0000000..7d7865a
--- /dev/null
+++ b/Liaison/DownloadStatusView.h
@@ -0,0 +1,27 @@
+/* DownloadStatusView */
+
+@interface DownloadStatusView : NSView
+{
+ IBOutlet NSTextField *theFilename;
+ IBOutlet NSImageView *theIcon;
+ IBOutlet NSProgressIndicator *theProgressBar;
+ IBOutlet NSButton *theButton;
+
+ LiFileHandle *theFileHandle;
+}
+- (LiFileHandle *)fileHandle;
+- (void)setFileHandle: (LiFileHandle *)aFileHandle;
+
+- (void)setIcon: (NSImage *)anIcon;
+- (void)setFilename: (NSString *)aFilename;
+- (void)setProgress: (double)aProgress;
+- (void)setButtonImage: (NSImage *)anImage;
+- (void)setButtonAltImage: (NSImage *)anImage;
+- (void)setButtonTarget: (id)aTarget;
+- (void)setButtonAction: (SEL)anAction;
+@property (retain,getter=fileHandle) LiFileHandle *theFileHandle;
+@property (retain) NSTextField *theFilename;
+@property (retain) NSButton *theButton;
+@property (retain) NSImageView *theIcon;
+@property (retain) NSProgressIndicator *theProgressBar;
+@end
diff --git a/Liaison/DownloadStatusView.m b/Liaison/DownloadStatusView.m
new file mode 100644
index 0000000..2844904
--- /dev/null
+++ b/Liaison/DownloadStatusView.m
@@ -0,0 +1,118 @@
+#import "DownloadStatusView.h"
+
+@implementation DownloadStatusView (NSCoding)
+- (id)initWithCoder: (NSCoder *)aCoder
+{
+ self = [super initWithCoder: aCoder];
+
+ if ([aCoder allowsKeyedCoding]) {
+ theFilename = [aCoder decodeObjectForKey: @"filename"];
+ theIcon = [aCoder decodeObjectForKey: @"icon"];
+ theProgressBar = [aCoder decodeObjectForKey: @"progressBar"];
+ theButton = [aCoder decodeObjectForKey: @"button"];
+ } else {
+ theFilename = [aCoder decodeObject];
+ theIcon = [aCoder decodeObject];
+ theProgressBar = [aCoder decodeObject];
+ theButton = [aCoder decodeObject];
+ }
+
+ [theFilename retain];
+ [theIcon retain];
+ [theProgressBar retain];
+ [theButton retain];
+
+ return self;
+}
+
+- (void)encodeWithCoder: (NSCoder *)aCoder
+{
+ if ([aCoder allowsKeyedCoding]) {
+ [aCoder encodeObject: theFilename forKey: @"filename"];
+ [aCoder encodeObject: theIcon forKey: @"icon"];
+ [aCoder encodeObject: theProgressBar forKey: @"progressBar"];
+ [aCoder encodeObject: theButton forKey: @"button"];
+ } else {
+ [aCoder encodeObject: theFilename];
+ [aCoder encodeObject: theIcon];
+ [aCoder encodeObject: theProgressBar];
+ [aCoder encodeObject: theButton];
+ }
+ [super encodeWithCoder: aCoder];
+}
+@end
+
+@implementation DownloadStatusView (NSViewSubclass)
+- (id)initWithFrame:(NSRect)frameRect
+{
+ self = [super initWithFrame:frameRect];
+
+ return self;
+}
+
+- (void)drawRect:(NSRect)rect
+{
+ [super drawRect: rect];
+}
+@end
+
+@implementation DownloadStatusView
+- (void)dealloc
+{
+ [self setFileHandle: nil];
+
+ [super dealloc];
+}
+
+- (LiFileHandle *)fileHandle
+{
+ return theFileHandle;
+}
+
+- (void)setFileHandle: (LiFileHandle *)aFileHandle
+{
+ [aFileHandle retain];
+ [theFileHandle release];
+ theFileHandle = aFileHandle;
+}
+
+- (void)setIcon: (NSImage *)anIcon
+{
+ [theIcon setImage: anIcon];
+}
+
+- (void)setFilename: (NSString *)aFilename
+{
+ [theFilename setStringValue: aFilename];
+}
+
+- (void)setProgress: (double)aProgress
+{
+ [theProgressBar setDoubleValue: aProgress];
+}
+
+- (void)setButtonImage: (NSImage *)anImage
+{
+ [theButton setImage: anImage];
+}
+
+- (void)setButtonAltImage: (NSImage *)anImage
+{
+ [theButton setAlternateImage: anImage];
+}
+
+- (void)setButtonTarget: (id)aTarget
+{
+ [theButton setTarget: aTarget];
+}
+
+- (void)setButtonAction: (SEL)anAction
+{
+ [theButton setAction: anAction];
+}
+@synthesize theFileHandle;
+@synthesize theButton;
+@synthesize theProgressBar;
+@synthesize theIcon;
+@synthesize theFilename;
+@end
diff --git a/Liaison/Downloader.h b/Liaison/Downloader.h
new file mode 100644
index 0000000..efe7f97
--- /dev/null
+++ b/Liaison/Downloader.h
@@ -0,0 +1,63 @@
+//
+// Downloader.h
+// Liaison
+//
+// Created by Brian Cully on Fri Jun 06 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+@class DownloadStatusView;
+
+@interface Downloader : NSObject
+{
+ DownloadStatusView *theStatusView;
+ LiFileHandle *theRemoteFileHandle;
+ NSDictionary *theFileAttributes;
+ NSFileHandle *theSocket;
+ NSFileHandle *theLocalFile;
+ NSString *theLocalFilePath;
+ void *theCallbackContext;
+ id theCallbackTarget;
+ SEL theCallbackSelector;
+
+ unsigned long theBytesInLocalFile;
+}
+- (id)initWithSocket: (NSFileHandle *)aSocket
+ target: (id)aTarget
+ selector: (SEL)aSelector
+ context: (void *)someContext;
+
+- (void)downloadFileHandle: (LiFileHandle *)aFileHandle;
+@property (retain,getter=localFilePath) NSString *theLocalFilePath;
+@property (getter=callbackContext,setter=setCallbackContext:) void *theCallbackContext;
+@property (retain,getter=statusView) DownloadStatusView *theStatusView;
+@property (getter=callbackSelector,setter=setCallbackSelector:) SEL theCallbackSelector;
+@property (retain,getter=socket) NSFileHandle *theSocket;
+@property (retain,getter=callbackTarget) id theCallbackTarget;
+@property (retain,getter=fileAttributes) NSDictionary *theFileAttributes;
+@property (retain,getter=remoteFileHandle) LiFileHandle *theRemoteFileHandle;
+@property (retain,getter=localFile) NSFileHandle *theLocalFile;
+@property unsigned long theBytesInLocalFile;
+@end
+
+@interface Downloader (Accessors)
+- (LiFileHandle *)remoteFileHandle;
+- (void)setRemoteFileHandle: (LiFileHandle *)aFileHandle;
+- (NSFileHandle *)localFile;
+- (void)setLocalFile: (NSFileHandle *)aFile;
+- (NSString *)localFilePath;
+- (void)setLocalFilePath: (NSString *)aPath;
+- (DownloadStatusView *)statusView;
+- (void)setStatusView: (DownloadStatusView *)aStatusView;
+
+- (NSDictionary *)fileAttributes;
+- (void)setFileAttributes: (NSDictionary *)someAttributes;
+- (NSFileHandle *)socket;
+- (void)setSocket: (NSFileHandle *)aSocket;
+- (id)callbackTarget;
+- (void)setCallbackTarget: (id)aTarget;
+- (SEL)callbackSelector;
+- (void)setCallbackSelector: (SEL)aSelector;
+- (void *)callbackContext;
+- (void)setCallbackContext: (void *)someContext;
+@end \ No newline at end of file
diff --git a/Liaison/Downloader.m b/Liaison/Downloader.m
new file mode 100644
index 0000000..d60d865
--- /dev/null
+++ b/Liaison/Downloader.m
@@ -0,0 +1,429 @@
+//
+// Downloader.m
+// Liaison
+//
+// Created by Brian Cully on Fri Jun 06 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+#import "Downloader.h"
+
+#import "CopyController.h"
+#import "DownloadStatusView.h"
+#import "LiDataTranslator.h"
+#import "NIBConnector.h"
+
+@interface Downloader (NetworkStuff)
+- (void)errorMsg: (NSString *)aMsg;
+- (void)beginCopy;
+- (void)shutdown;
+@end
+
+@implementation Downloader
+- (id)initWithSocket: (NSFileHandle *)aSocket
+ target: (id)aTarget
+ selector: (SEL)aSelector
+ context: (void *)someContext
+{
+ self = [super init];
+
+ [self setSocket: aSocket];
+ [self setCallbackTarget: aTarget];
+ [self setCallbackSelector: aSelector];
+ [self setCallbackContext: someContext];
+
+ return self;
+}
+
+- (void)dealloc
+{
+ NSNotificationCenter *defaultCenter;
+
+ defaultCenter = [NSNotificationCenter defaultCenter];
+ [defaultCenter removeObserver: self];
+
+ [self setSocket: nil];
+ [self setCallbackTarget: nil];
+ [self setCallbackSelector: nil];
+ [self setCallbackContext: nil];
+
+ [super dealloc];
+}
+
+- (void)downloadFileHandle: (LiFileHandle *)aFileHandle
+{
+ NSFileManager *defaultManager;
+ NSString *filename, *type, *path;
+
+ filename = [aFileHandle filename];
+ type = [aFileHandle type];
+ if ([type length] > 0)
+ filename = [filename stringByAppendingPathExtension: type];
+
+ path = [[[Preferences sharedPreferences] downloadDirectory]
+ stringByAppendingPathComponent: filename];
+ [self setLocalFilePath: path];
+
+ defaultManager = [NSFileManager defaultManager];
+ if ([defaultManager createFileAtPath: path
+ contents: nil
+ attributes: nil]) {
+ NSFileHandle *tmpFile;
+
+ tmpFile = [NSFileHandle fileHandleForWritingAtPath: path];
+ [self setLocalFile: tmpFile];
+
+ [self setRemoteFileHandle: aFileHandle];
+ [self setFileAttributes:
+ [[aFileHandle fileStore] attributesForFileHandle: aFileHandle]];
+ [self beginCopy];
+ } else {
+ NSString *errorMsg;
+
+ errorMsg = [NSString stringWithFormat:
+ @"couldn't write to %@: %@", path, strerror(errno)];
+ [self errorMsg: errorMsg];
+ }
+}
+@synthesize theBytesInLocalFile;
+@synthesize theLocalFilePath;
+@synthesize theFileAttributes;
+@synthesize theRemoteFileHandle;
+@synthesize theLocalFile;
+@synthesize theStatusView;
+@synthesize theCallbackTarget;
+@synthesize theSocket;
+@end
+
+@implementation Downloader (Accessors)
+- (LiFileHandle *)remoteFileHandle
+{
+ return theRemoteFileHandle;
+}
+
+- (void)setRemoteFileHandle: (LiFileHandle *)aFileHandle
+{
+ [aFileHandle retain];
+ [theRemoteFileHandle release];
+ theRemoteFileHandle = aFileHandle;
+}
+
+- (NSFileHandle *)localFile
+{
+ return theLocalFile;
+}
+
+- (void)setLocalFile: (NSFileHandle *)aFile
+{
+ [aFile retain];
+ [theLocalFile release];
+ theLocalFile = aFile;
+}
+
+- (NSString *)localFilePath
+{
+ return theLocalFilePath;
+}
+
+- (void)setLocalFilePath: (NSString *)aPath
+{
+ [aPath retain];
+ [theLocalFilePath release];
+ theLocalFilePath = aPath;
+}
+
+- (DownloadStatusView *)statusView
+{
+ return theStatusView;
+}
+
+- (void)setStatusView: (DownloadStatusView *)aStatusView
+{
+ [aStatusView retain];
+ [theStatusView release];
+ theStatusView = aStatusView;
+}
+
+- (NSDictionary *)fileAttributes
+{
+ return theFileAttributes;
+}
+
+- (void)setFileAttributes: (NSDictionary *)someAttributes
+{
+ [someAttributes retain];
+ [theFileAttributes release];
+ theFileAttributes = someAttributes;
+}
+
+- (NSFileHandle *)socket
+{
+ return theSocket;
+}
+
+- (void)setSocket: (NSFileHandle *)aSocket
+{
+ [aSocket retain];
+ [theSocket release];
+ theSocket = aSocket;
+}
+
+- (id)callbackTarget
+{
+ return theCallbackTarget;
+}
+
+- (void)setCallbackTarget: (id)aTarget
+{
+ [aTarget retain];
+ [theCallbackTarget release];
+ theCallbackTarget = aTarget;
+}
+
+- (SEL)callbackSelector
+{
+ return theCallbackSelector;
+}
+
+- (void)setCallbackSelector: (SEL)aSelector
+{
+ theCallbackSelector = aSelector;
+}
+
+- (void *)callbackContext
+{
+ return theCallbackContext;
+}
+
+- (void)setCallbackContext: (void *)someContext
+{
+ theCallbackContext = someContext;
+}
+@end
+
+@implementation Downloader (NetworkStuff)
+- (NSImage *)cancelImage
+{
+ NSBundle *myBundle;
+ NSString *tmpPath;
+ NSImage *tmpImage;
+
+ myBundle = [NSBundle bundleForClass: [self class]];
+ tmpPath = [myBundle pathForImageResource: @"Download_Stop.tiff"];
+ tmpImage = [[NSImage alloc] initWithContentsOfFile: tmpPath];
+ return [tmpImage autorelease];
+}
+
+- (NSImage *)cancelImagePressed
+{
+ NSBundle *myBundle;
+ NSString *tmpPath;
+ NSImage *tmpImage;
+
+ myBundle = [NSBundle bundleForClass: [self class]];
+ tmpPath = [myBundle pathForImageResource: @"Download_StopPressed.tiff"];
+ tmpImage = [[NSImage alloc] initWithContentsOfFile: tmpPath];
+ return [tmpImage autorelease];
+}
+
+- (NSImage *)reloadImage
+{
+ NSBundle *myBundle;
+ NSString *tmpPath;
+ NSImage *tmpImage;
+
+ myBundle = [NSBundle bundleForClass: [self class]];
+ tmpPath = [myBundle pathForImageResource: @"Download_Reload.tiff"];
+ tmpImage = [[NSImage alloc] initWithContentsOfFile: tmpPath];
+ return [tmpImage autorelease];
+}
+
+- (NSImage *)reloadImagePressed
+{
+ NSBundle *myBundle;
+ NSString *tmpPath;
+ NSImage *tmpImage;
+
+ myBundle = [NSBundle bundleForClass: [self class]];
+ tmpPath = [myBundle pathForImageResource: @"Download_ReloadPressed.tiff"];
+ tmpImage = [[NSImage alloc] initWithContentsOfFile: tmpPath];
+ return [tmpImage autorelease];
+}
+
+- (NSImage *)revealImage
+{
+ NSBundle *myBundle;
+ NSString *tmpPath;
+ NSImage *tmpImage;
+
+ myBundle = [NSBundle bundleForClass: [self class]];
+ tmpPath = [myBundle pathForImageResource: @"Download_Reveal.tiff"];
+ tmpImage = [[NSImage alloc] initWithContentsOfFile: tmpPath];
+ return [tmpImage autorelease];
+}
+
+- (NSImage *)revealImagePressed
+{
+ NSBundle *myBundle;
+ NSString *tmpPath;
+ NSImage *tmpImage;
+
+ myBundle = [NSBundle bundleForClass: [self class]];
+ tmpPath = [myBundle pathForImageResource: @"Download_RevealPressed.tiff"];
+ tmpImage = [[NSImage alloc] initWithContentsOfFile: tmpPath];
+ return [tmpImage autorelease];
+}
+
+- (void)setupStatusViewForFileHandle: (LiFileHandle *)aFileHandle
+{
+ DownloadStatusView *statusView;
+ NSImage *fileIcon;
+
+ fileIcon = [[NSWorkspace sharedWorkspace] iconForFileType:
+ [aFileHandle type]];
+ [fileIcon setSize: NSMakeSize(32.0, 32.0)];
+
+ statusView = [[[NIBConnector connector] copyController] statusViewForFileHandle: aFileHandle];
+ [statusView setFilename: [aFileHandle filename]];
+ [statusView setIcon: fileIcon];
+ [statusView setProgress: 0.0];
+ [statusView setButtonImage: [self cancelImage]];
+ [statusView setButtonAltImage: [self cancelImagePressed]];
+ [statusView setButtonTarget: self];
+ [statusView setButtonAction: @selector(cancelCopy)];
+ [self setStatusView: statusView];
+}
+
+- (void)errorMsg: (NSString *)aMsg
+{
+ id target;
+
+ [self shutdown];
+
+ target = [self callbackTarget];
+ if (target != nil) {
+ SEL callbackSelector;
+
+ callbackSelector = [self callbackSelector];
+ if (callbackSelector != nil) {
+ IMP callback;
+ void *context;
+
+ context = [self callbackContext];
+ callback = [target methodForSelector: callbackSelector];
+ if (callback != nil)
+ callback(target, callbackSelector,
+ [self remoteFileHandle], context, aMsg);
+ }
+ }
+}
+
+- (BOOL)sendCommand: (NSDictionary *)aCmd
+{
+ NSData *cmdData;
+
+ cmdData = [aCmd encodedData];
+ if (cmdData != nil) {
+ [[self socket] writeData: cmdData];
+ return YES;
+ } else
+ return NO;
+}
+
+- (void)beginCopy
+{
+ NSDictionary *copyCmd;
+ NSNotificationCenter *defaultCenter;
+
+ defaultCenter = [NSNotificationCenter defaultCenter];
+ [defaultCenter addObserver: self
+ selector: @selector(gotCopyData:)
+ name: NSFileHandleReadCompletionNotification
+ object: [self socket]];
+
+ [self setupStatusViewForFileHandle: [self remoteFileHandle]];
+
+ copyCmd = [NSDictionary dictionaryWithObjects:
+ [NSArray arrayWithObjects: @"download",
+ [[self remoteFileHandle] fileID], nil]
+ forKeys:
+ [NSArray arrayWithObjects: @"type",
+ @"LiFileHandleAttribute", nil]];
+ [self sendCommand: copyCmd];
+
+ theBytesInLocalFile = 0;
+ [[self socket] readInBackgroundAndNotify];
+}
+
+- (void)gotCopyData: (NSNotification *)aNotification
+{
+ NSData *fileData;
+ double totalLength, progress;
+
+ fileData = [[aNotification userInfo] objectForKey:
+ NSFileHandleNotificationDataItem];
+
+ totalLength = [[[self fileAttributes] objectForKey: LiFileSizeAttribute] doubleValue];
+ theBytesInLocalFile += [fileData length];
+ if ([fileData length] > 0 && theBytesInLocalFile < totalLength) {
+ [[self localFile] writeData: fileData];
+
+ if (totalLength > 0) {
+ progress = theBytesInLocalFile / totalLength;
+ [[self statusView] setProgress: progress];
+ }
+
+ [[self socket] readInBackgroundAndNotify];
+ } else {
+ DownloadStatusView *statusView;
+
+ [self shutdown];
+
+ statusView = [self statusView];
+ [statusView setButtonImage: [self revealImage]];
+ [statusView setButtonAltImage: [self revealImagePressed]];
+ [statusView setButtonTarget: self];
+ [statusView setButtonAction: @selector(revealInFinder)];
+ [statusView setProgress: 1.0];
+ }
+}
+
+- (void)cancelCopy
+{
+ DownloadStatusView *statusView;
+ NSFileManager *defaultManager;
+
+ defaultManager = [NSFileManager defaultManager];
+ [defaultManager removeFileAtPath: [self localFilePath]
+ handler: nil];
+
+ [self shutdown];
+
+ statusView = [self statusView];
+ [statusView setButtonImage: [self reloadImage]];
+ [statusView setButtonAltImage: [self reloadImagePressed]];
+ [statusView setButtonTarget: [self remoteFileHandle]];
+ [statusView setButtonAction: @selector(open)];
+ [statusView setProgress: 0.0];
+}
+
+- (void)shutdown
+{
+ NSNotificationCenter *defaultCenter;
+
+ defaultCenter = [NSNotificationCenter defaultCenter];
+ [defaultCenter removeObserver: self
+ name: NSFileHandleReadCompletionNotification
+ object: [self socket]];
+
+ [self setLocalFile: nil];
+ [self setSocket: nil];
+}
+
+- (void)revealInFinder
+{
+ if ([self localFilePath] != nil) {
+ [[NSWorkspace sharedWorkspace] selectFile: [self localFilePath] inFileViewerRootedAtPath: @""];
+ }
+}
+@end \ No newline at end of file
diff --git a/Liaison/English.lproj/CopyPanel.nib/Download_StopPressed.tiff b/Liaison/English.lproj/CopyPanel.nib/Download_StopPressed.tiff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Liaison/English.lproj/CopyPanel.nib/Download_StopPressed.tiff
diff --git a/Liaison/English.lproj/CopyPanel.nib/classes.nib b/Liaison/English.lproj/CopyPanel.nib/classes.nib
new file mode 100644
index 0000000..67bc2f5
--- /dev/null
+++ b/Liaison/English.lproj/CopyPanel.nib/classes.nib
@@ -0,0 +1,38 @@
+{
+ IBClasses = (
+ {
+ CLASS = CopyController;
+ LANGUAGE = ObjC;
+ OUTLETS = {
+ theContentBox = NSBox;
+ theScrollView = NSScrollView;
+ theTemplate = DownloadStatusView;
+ theWindow = NSWindow;
+ };
+ SUPERCLASS = NSObject;
+ },
+ {
+ CLASS = DownloadStatusView;
+ LANGUAGE = ObjC;
+ OUTLETS = {
+ theButton = NSButton;
+ theFilename = NSTextField;
+ theIcon = NSImageView;
+ theProgressBar = NSProgressIndicator;
+ };
+ SUPERCLASS = NSView;
+ },
+ {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; },
+ {CLASS = FlippedBox; LANGUAGE = ObjC; SUPERCLASS = NSBox; },
+ {
+ CLASS = NIBConnector;
+ LANGUAGE = ObjC;
+ OUTLETS = {
+ theCopyController = CopyController;
+ thePreferencesController = PreferencesController;
+ };
+ SUPERCLASS = NSObject;
+ }
+ );
+ IBVersion = 1;
+} \ No newline at end of file
diff --git a/Liaison/English.lproj/CopyPanel.nib/info.nib b/Liaison/English.lproj/CopyPanel.nib/info.nib
new file mode 100644
index 0000000..cfe2a0a
--- /dev/null
+++ b/Liaison/English.lproj/CopyPanel.nib/info.nib
@@ -0,0 +1,22 @@
+<?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>112 70 356 240 0 0 1600 1178 </string>
+ <key>IBEditorPositions</key>
+ <dict>
+ <key>16</key>
+ <string>690 764 220 82 0 0 1600 1178 </string>
+ </dict>
+ <key>IBFramework Version</key>
+ <string>326.0</string>
+ <key>IBOpenObjects</key>
+ <array>
+ <integer>16</integer>
+ <integer>28</integer>
+ </array>
+ <key>IBSystem Version</key>
+ <string>7B28</string>
+</dict>
+</plist>
diff --git a/Liaison/English.lproj/CopyPanel.nib/objects.nib b/Liaison/English.lproj/CopyPanel.nib/objects.nib
new file mode 100644
index 0000000..ad7bd0a
--- /dev/null
+++ b/Liaison/English.lproj/CopyPanel.nib/objects.nib
Binary files differ
diff --git a/Liaison/English.lproj/InfoPlist.strings b/Liaison/English.lproj/InfoPlist.strings
new file mode 100644
index 0000000..72d0e56
--- /dev/null
+++ b/Liaison/English.lproj/InfoPlist.strings
Binary files differ
diff --git a/Liaison/English.lproj/Liaison Help/Liaison.html b/Liaison/English.lproj/Liaison Help/Liaison.html
new file mode 100644
index 0000000..9f3f483
--- /dev/null
+++ b/Liaison/English.lproj/Liaison Help/Liaison.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
+<html lang="en">
+<head>
+ <title>Liaison Help</title>
+ <meta name="generator" content="BBEdit 6.5">
+ <meta name="AppleTitle" content="Liaison Help">
+ <meta name="AppleIcon" content="Liaison%20Help/images/Liaison_small.png">
+ <meta name="AppleFont" content="Lucida Grande,Helvetica">
+ <meta name="Description" content="Help for Liaison 0.4">
+ <meta name="keywords" content="laison">
+</head>
+
+<frameset cols="170,*" framespacing="0" border="0" frameborder="0" framespacing="0">
+ <frame name="navigationFrame" marginheight="0" marginwidth="0" scrolling="AUTO" src="pages/navigation.html">
+ <frame name="contentsFrame" marginheight="0" marginwidth="0" scrolling="AUTO" src="pages/overview.html">
+</frameset>
+</html> \ No newline at end of file
diff --git a/Liaison/English.lproj/Liaison Help/images/Liaison.png b/Liaison/English.lproj/Liaison Help/images/Liaison.png
new file mode 100644
index 0000000..e301611
--- /dev/null
+++ b/Liaison/English.lproj/Liaison Help/images/Liaison.png
Binary files differ
diff --git a/Liaison/English.lproj/Liaison Help/images/Liaison_small.png b/Liaison/English.lproj/Liaison Help/images/Liaison_small.png
new file mode 100644
index 0000000..436d6f6
--- /dev/null
+++ b/Liaison/English.lproj/Liaison Help/images/Liaison_small.png
Binary files differ
diff --git a/Liaison/English.lproj/Liaison Help/pages/bugs.html b/Liaison/English.lproj/Liaison Help/pages/bugs.html
new file mode 100644
index 0000000..d7da2a6
--- /dev/null
+++ b/Liaison/English.lproj/Liaison Help/pages/bugs.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
+<html lang="en">
+<head>
+ <title>Known Bugs</title>
+ <meta name="generator" content="BBEdit 6.5">
+</head>
+
+<body>
+<h1>Known Bugs</h1>
+<ul>
+ <li>Help is woefully incomplete.</li>
+ <li>Find panel doesn't do anything.</li>
+ <li>Applescript support isn't very useful.</li>
+ <li>The "Open" and "Show in Finder" menu items don't dim properly when the file table
+ isn't the first responder.</li>
+ <li>The view options do not update based on key window.</li>
+ <li>It's possible to get duplicate files if you remove a file from a path, move a new
+ file into the same path, and then add the new file to the library. (The old file handle
+ now resolves to the new file, and then you add the new file again - using alias data as
+ the primary key should fix this, but it's still possible - perhaps alias data isn't
+ being updated properly when the new file is found?)</li>
+</ul>
+</body>
+</html>
diff --git a/Liaison/English.lproj/Liaison Help/pages/faq.html b/Liaison/English.lproj/Liaison Help/pages/faq.html
new file mode 100644
index 0000000..a78b649
--- /dev/null
+++ b/Liaison/English.lproj/Liaison Help/pages/faq.html
@@ -0,0 +1,80 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
+<html lang="en">
+<head>
+ <title>Frequently Asked Questions</title>
+ <meta name="generator" content="BBEdit 6.5">
+</head>
+
+<body>
+ <h1>Frequently Asked Questions</h1>
+
+ <p>Click on a topic to the left to find out more about it, or click on a
+ question below to find the answer.</p>
+
+ <dt><strong>How do I use groups?</strong></dt>
+ <dd>
+ <p>You can use <a href="glossary.html#group">groups</a> to organize files
+ in your library. For instance, you might want to create a group for all your
+ image files, for all the files in a project you're working on, or to share
+ files with other Liaison users.</p>
+
+ <table border="0" cellpadding="5" cellspacing="0" width="100%" bgcolor="#fff8dc">
+ <tr>
+ <td colspan=2><strong>To create a group:</strong></td>
+ </tr>
+ <tr>
+ <td>
+ <strong>1</strong>
+ </td>
+ <td>
+ Select "New group" from the File menu.
+ </td>
+ </tr>
+ <tr>
+ <td colspan=2><strong>To add files to a group:</strong></td>
+ </tr>
+ <tr>
+ <td>
+ <strong>1</strong>
+ </td>
+ <td>
+ Select the list of files in the library, and drag
+ them to the newly created group.
+ </td>
+ </tr>
+ <tr>
+ <td colspan=2><strong>To rename a group:</strong></td>
+ </tr>
+ <tr>
+ <td>
+ <strong>1</strong>
+ </td>
+ <td>Double-click its entry in the group list and type in a new name.</td>
+ </tr>
+ </table>
+ </dd>
+
+ <dt><strong>How do I share my files?</strong></dt>
+ <dd>
+ <p>You can share any file in your library by putting it in a group.</p>
+ </dd>
+
+ <dt><strong>How do I download a shared file?</strong></dt>
+ <dd>
+ <p>Double-click on it, or drag it from Liaison to the Finder.</p>
+ </dd>
+
+ <dt><strong>What is meta-data and how do I edit it?</strong></dt>
+ <dd>
+ <p>Meta-data is information about the file itself, such as when it was
+ created, to what groups it belongs, its icon, its filename, and other
+ ways of referencing a file.</p>
+
+ <p>To edit meta-data, you can double click on a field in the file list,
+ or edit it via the inspector.</p>
+ </dd>
+
+ <p><font size="-2">&copy;2003 Brian Cully</font>
+</body>
+</html>
diff --git a/Liaison/English.lproj/Liaison Help/pages/glossary.html b/Liaison/English.lproj/Liaison Help/pages/glossary.html
new file mode 100644
index 0000000..4753608
--- /dev/null
+++ b/Liaison/English.lproj/Liaison Help/pages/glossary.html
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
+<html lang="en">
+<head>
+ <title>Glossary</title>
+ <meta name="generator" content="BBEdit 6.5">
+</head>
+
+<body>
+ <h1>Glossary</h1>
+ <ul>
+ <li>
+ <dt><a id="group">group</a></dt>
+ <dd>Groups are lists of files that you can create to organize your file
+ library or to share with other Liaison users.</dd>
+ </li>
+ <li>
+ <dt><a id="LAN">LAN</a></dt>
+ <dd>LAN is an acronym which means "Local Area Network," which is a computer
+ network that is confined to a limited geography, such as your office, your
+ home, or your local McDonald's&trade;.</dd>
+ </li>
+ <li>
+ <dt><a id="library">library</a></dt>
+ <dd>Libraries are lists of files on a computer that Liaison knows about.</dd>
+ </li>
+ </ul>
+</body>
+</html>
diff --git a/Liaison/English.lproj/Liaison Help/pages/navigation.html b/Liaison/English.lproj/Liaison Help/pages/navigation.html
new file mode 100644
index 0000000..51d2505
--- /dev/null
+++ b/Liaison/English.lproj/Liaison Help/pages/navigation.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
+<html lang="en">
+<head>
+ <title>Navigation</title>
+ <meta name="generator" content="BBEdit 6.5">
+</head>
+
+<body>
+ <center><img src="../images/Liaison.png" alt="" width="128" height="128"></center>
+ <ul>
+ <li><a href="overview.html" target="contentsFrame">Overview</a></li>
+ <li><a href="faq.html" target="contentsFrame">Frequently Asked Questions</a></li>
+ <li><a href="whatsnew.html" target="contentsFrame">What's new</a></li>
+ <li><a href="bugs.html" target="contentsFrame">Known Bugs</a></li>
+ <li><a href="todo.html" target="contentsFrame">To Do</a></li>
+ <li><a href="http://www.kublai.com/~shmit/software/Liaison/" target="new">Website</a></li>
+ </ul>
+</body>
+</html>
diff --git a/Liaison/English.lproj/Liaison Help/pages/overview.html b/Liaison/English.lproj/Liaison Help/pages/overview.html
new file mode 100644
index 0000000..d4fbffc
--- /dev/null
+++ b/Liaison/English.lproj/Liaison Help/pages/overview.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
+<html lang="en">
+<head>
+ <title>Overview</title>
+ <meta name="generator" content="BBEdit 6.5">
+</head>
+
+<body>
+ <h1>Overview</h1>
+
+ <p>Liaison is a file browser that lets your organize all your files. Here are some of the
+ things you can do with Liaison.</p>
+
+ <ul>
+ <li>
+ <p>Add files to your library. When you add files to your
+ <a href="glossary.html#library">library</a>, you can open them,
+ edit their meta-data, and even share them with other Liaison users.</p>
+ </li>
+ <li>
+ <p>Organize your file library. Once you add files to your library, you can organize
+ them into <a href="glossary.html#group">groups</a>. You can create groups to organize
+ files by theme, location, content, or anything else you can imagine.</p>
+ </li>
+ <li>
+ <p>Share your file library with other people. File sharing allows you to browse your
+ library across multiple computers on your <a href="glossary.html#LAN">LAN</a> and
+ download them to another computer. File sharing requires a copy of Liaison on each
+ computer which is used to share files.</p>
+ </li>
+ </ul>
+
+ <p>If you want to learn more about any of this, click a topic on the left.</p>
+</body>
+</html>
diff --git a/Liaison/English.lproj/Liaison Help/pages/todo.html b/Liaison/English.lproj/Liaison Help/pages/todo.html
new file mode 100644
index 0000000..d57a0bc
--- /dev/null
+++ b/Liaison/English.lproj/Liaison Help/pages/todo.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
+<html lang="en">
+<head>
+ <title>To Do</title>
+ <meta name="generator" content="BBEdit 6.5">
+</head>
+
+<body>
+<h1>To Do</h1>
+
+<ul>
+ <li>Application Wizard for first-time users.</li>
+ <li>Smart groups / saved searches.</li>
+ <li>Better preferences system. Perhaps Omni's?</li>
+ <li>Group level access control for sharing.</li>
+ <li>Password protected libraries.</li>
+ <li>Directory tracking.</li>
+ <li>Be able to send whole directories, most especially for bundles.</li>
+ <li>Implement file categories and browser.</li>
+ <li>Need better formatters for date information in the browser.</li>
+ <li>Group inspector.</li>
+</ul>
+</body>
+</html>
diff --git a/Liaison/English.lproj/Liaison Help/pages/whatsnew.html b/Liaison/English.lproj/Liaison Help/pages/whatsnew.html
new file mode 100644
index 0000000..64d8471
--- /dev/null
+++ b/Liaison/English.lproj/Liaison Help/pages/whatsnew.html
@@ -0,0 +1,167 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
+<html lang="en">
+<head>
+ <title>What's New</title>
+ <meta name="generator" content="BBEdit 6.5">
+</head>
+
+<body>
+<h1>What's new</h1>
+<h3>Since 0.4.2:</h3>
+<ul>
+ <li>Fixed a bug with the load sheet not closing properly.</li>
+ <li>You can now drag whole directories onto the group list or file list to add the files
+ in them to Liaison.</li>
+ <li>Drag-and-Drop of promised files works.</li>
+ <li>The table view no longer scrolls to the top of the list after you remove files.</li>
+ <li>You can drag files from Liaison to the finder now.</li>
+ <li>The shared inspector now updates when you make other windows key.</li>
+ <li>No longer cache icon information. This slows everything down a tiny bit, but saves
+ a huge amount of memory.</li>
+ <li>The icon that signifies an unlocatable file now updates automatically.</li>
+</ul>
+
+<h3>Since 0.4.1:</h3>
+<ul>
+ <li>Added help.</li>
+ <li>Project split up for future open source plans.</li>
+ <li>A lot of old code culled.</li>
+ <li>You can unsort your file list by clicking on the header.</li>
+ <li>Editing files now works consistently with the sort and search.</li>
+ <li>Fixed many bugs where the file selection would change unexpectedly.</li>
+ <li>Fixed bugs with groups being assigned incorrectly.</li>
+ <li>Added contextual menus to the file and group panels.</li>
+ <li>Fixed dimming of menus and toolbars with two exceptions: the "Open" and
+ "Show in Finder" menu items don't work properly when the group list is the
+ first responder.</li>
+</ul>
+
+<h3>Since 0.4:</h3>
+<ul>
+ <li>Many GUI fixes and enhancements.</li>
+ <li>Major speed improvements for loading large amounts of files at a time.</li>
+</ul>
+
+<h3>Since 0.3.3a:</h3>
+<ul>
+ <li>The file database has been ripped to shreds and rewritten from scratch. The new one
+ is much more extensible and faster overall.</li>
+ <li>Application is now prebound for faster launch times.</li>
+ <li>Network code has been rewritten to take advantage of the new file database, and is
+ much more connection friendly now for persistant connections.</li>
+ <li>Loading files into your library should be substantially faster. However, you cannot
+ load more than about one thousand at a time.</li>
+ <li>File selection is saved as you navigate through groups, if possible.</li>
+ <li>The zoom box works properly (or mostly properly) everywhere.</li>
+ <li>Preliminary AppleScript support has been added. It's not very useful, but it's there.</li>
+ <li>You are no longer able to group remote file stores within an over-all "Rendezvous"
+ group.</li>
+ <li>Some minor fixes for Panther.</li>
+ <li>BUG: The "Add Files to Library" menu item doesn't get properly dimmed.</li>
+</ul>
+
+<h3>What's new since 0.3.2a:</h3>
+<ul>
+ <li>Application starts again.</li>
+</ul>
+
+<h3>Since 0.3.1a:</h3>
+<ul>
+ <li>Fix date sorts.</li>
+ <li>Make string searches case insensitive.</li>
+ <li>Add a plugin architecture for the file browser and inspector.</li>
+ <li>Add a Find panel stub so I can work out the interface.</li>
+ <li>You can set the file's icon in the inspector now - simply drag an image
+ file (not a .icns file!) onto the file's icon in the file inspector.</li>
+ <li>You can copy a file's icon by dragging it from the icon in the file
+ inspector to a folder. Note: files are saved as TIFF documents, with one
+ layer per icon size (so layer 1 is 128x128, layer 2 is 32x32, layer 3 is
+ 16,16) -- Thanx Cocoa!</li>
+ <li>If a file change can't happen, a sheet is displayed.</li>
+</ul>
+
+<h3>Since 0.3a:</h3>
+<ul>
+ <li>You can set or delete HFS file creator codes via the inspector.</li>
+ <li>Add columns for last modified time, creation time, and file size.</li>
+ <li>Column sizes and order in the file list are remembered now.</li>
+ <li>Added a "view options" dialog box, under the view menu. You can use
+ this to set which columns are displayed.</li>
+ <li>The delete key and the "Delete" item under the "Edit" menu work now.</li>
+ <li>If you have an editable group selected, adding files will also add them
+ to the current group. Otherwise the files are just dumped in the library.</li>
+ <li>Fix network code. I don't know how long it's been broken, but it works
+ again as well as it did the last time I checked, perhaps better.</li>
+ <li>Display scroll bars when the group list is longer than the view.</li>
+ <li>When reloading a file in the download panel, don't create a new status
+ view, just re-use the old one.</li>
+ <li>If the hostname changes you can still download files properly.</li>
+</ul>
+
+<h3>Since 0.2.1a:</h3>
+<ul>
+ <li>Use aliases instead of paths as the database key. Files can now be
+ renamed without being lost to Liaison.
+ <div class="warning">
+ <strong>WARNING:</strong> This means
+ you have to delete your old library. You can accomplish this by deleting the
+ files in ~/Library/Liaison/. You must do this for Liaison to function
+ properly. Hopefully this shouldn't happen again.</li>
+ </div>
+ <li>Initialize the local group name if there's no existing library.</li>
+ <li>Add a little "!" icon for files that can't be located.</li>
+ <li>Edits in the file table and the inspector are now written to disk.</li>
+</ul>
+
+<h3>Since 0.2a:</h3>
+<ul>
+ <li>Editing a group doesn't clear the icon anymore.</li>
+ <li>Only draw the vertical grid; since we have alterntating row colors, we
+ don't need the horizontal grid.</li>
+ <li>File selection is saved between searches in the toolbar and sorts (from
+ clicking on the table header).</li>
+ <li>Empty groups are removed from the remote group list.</li>
+ <li>Add a menu item under the help menu to go to Liaison's web site.</li>
+ <li>Clear selection when group is changed.</li>
+</ul>
+
+<h3>Since 0.1.3a:</h3>
+<ul>
+ <li>File list is editable now. Changes still aren't written to disk.</li>
+ <li>Added a preference to put remote hosts under a rendezvous group,
+ or as a top-level group. By default, remote stores aren't displayed
+ under a rendezvous group.</li>
+ <li>File list has striped rows.</li>
+ <li>File extension is now relegated soley to the "type" column. The other
+ way just doesn't make sense.</li>
+ <li>"Reveal in Finder" tool bar icon.</li>
+ <li>Added "Get Info" under the file menu. This duplicates the "Inspector" menu
+ under the window menu.</li>
+</ul>
+
+<h3>Since 0.1.2a:</h3>
+<ul>
+ <li>A few new toolbar icons.</li>
+ <li>HFS type/creator codes are visible on remote hosts.</li>
+ <li>Writer threads are cleaned up properly.</li>
+ <li>The hooks for the Edit menu actions are in.</li>
+ <li>Remote group browsing works again.</li>
+</ul>
+
+<h3>Since 0.1.1a:</h3>
+<ul>
+ <li>File copying works again.</li>
+</ul>
+
+<h3>Since 0.1a:</h3>
+<ul>
+ <li>I have an icon! (However, I do not have Photoshop skillz).</li>
+ <li>Host name changes now update across the network.
+ (Actually, this needs work. I doubt a file copy will work without
+ restarting.)</li>
+ <li>Fixed a bug with the preferences always thinking the hostname had
+ changed.</li>
+</ul>
+</body>
+</html>
diff --git a/Liaison/English.lproj/Liaison.scriptTerminology b/Liaison/English.lproj/Liaison.scriptTerminology
new file mode 100644
index 0000000..55a5291
--- /dev/null
+++ b/Liaison/English.lproj/Liaison.scriptTerminology
@@ -0,0 +1,22 @@
+<?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>Classes</key>
+ <dict>
+ <key>NSApplication</key>
+ <dict>
+ <key>Description</key>
+ <string>Liaison's top level scripting object.</string>
+ <key>Name</key>
+ <string>application</string>
+ <key>PluralName</key>
+ <string>applications</string>
+ </dict>
+ </dict>
+ <key>Description</key>
+ <string>Liaison classes.</string>
+ <key>Name</key>
+ <string>Liaison suite</string>
+</dict>
+</plist>
diff --git a/Liaison/English.lproj/LoadPanel.nib/JavaCompiling.plist b/Liaison/English.lproj/LoadPanel.nib/JavaCompiling.plist
new file mode 100644
index 0000000..23e0cb0
--- /dev/null
+++ b/Liaison/English.lproj/LoadPanel.nib/JavaCompiling.plist
@@ -0,0 +1,8 @@
+<?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>JavaSourceSubpath</key>
+ <string>_LoadPanel_EOArchive_English.java</string>
+</dict>
+</plist>
diff --git a/Liaison/English.lproj/LoadPanel.nib/_LoadPanel_EOArchive_English.java b/Liaison/English.lproj/LoadPanel.nib/_LoadPanel_EOArchive_English.java
new file mode 100644
index 0000000..40b378d
--- /dev/null
+++ b/Liaison/English.lproj/LoadPanel.nib/_LoadPanel_EOArchive_English.java
@@ -0,0 +1,143 @@
+// _LoadPanel_EOArchive_English.java
+// Generated by EnterpriseObjects palette at Saturday, September 06, 2003 17:02:16 America/New_York
+
+import com.webobjects.eoapplication.*;
+import com.webobjects.eocontrol.*;
+import com.webobjects.eodistribution.client.*;
+import com.webobjects.eointerface.*;
+import com.webobjects.eointerface.swing.*;
+import com.webobjects.foundation.*;
+import java.awt.*;
+import javax.swing.*;
+import javax.swing.border.*;
+import javax.swing.table.*;
+import javax.swing.text.*;
+
+public class _LoadPanel_EOArchive_English extends com.webobjects.eoapplication.EOArchive {
+ LoadPanelController _loadPanelController0;
+ com.webobjects.eointerface.swing.EOFrame _eoFrame0;
+ com.webobjects.eointerface.swing.EOTextField _nsTextField0, _nsTextField1;
+ com.webobjects.eointerface.swing.EOView _nsProgressIndicator0;
+ javax.swing.JPanel _nsView0;
+
+ public _LoadPanel_EOArchive_English(Object owner, NSDisposableRegistry registry) {
+ super(owner, registry);
+ }
+
+ protected void _construct() {
+ Object owner = _owner();
+ EOArchive._ObjectInstantiationDelegate delegate = (owner instanceof EOArchive._ObjectInstantiationDelegate) ? (EOArchive._ObjectInstantiationDelegate)owner : null;
+ Object replacement;
+
+ super._construct();
+
+
+ if ((delegate != null) && ((replacement = delegate.objectForOutletPath(this, "theLoadPanelController.thePathField")) != null)) {
+ _nsTextField1 = (replacement == EOArchive._ObjectInstantiationDelegate.NullObject) ? null : (com.webobjects.eointerface.swing.EOTextField)replacement;
+ _replacedObjects.setObjectForKey(replacement, "_nsTextField1");
+ } else {
+ _nsTextField1 = (com.webobjects.eointerface.swing.EOTextField)_registered(new com.webobjects.eointerface.swing.EOTextField(), "NSTextField1");
+ }
+
+ if ((delegate != null) && ((replacement = delegate.objectForOutletPath(this, "theLoadPanelController.theProgressBar")) != null)) {
+ _nsProgressIndicator0 = (replacement == EOArchive._ObjectInstantiationDelegate.NullObject) ? null : (com.webobjects.eointerface.swing.EOView)replacement;
+ _replacedObjects.setObjectForKey(replacement, "_nsProgressIndicator0");
+ } else {
+ _nsProgressIndicator0 = (com.webobjects.eointerface.swing.EOView)_registered(new com.webobjects.eointerface.swing.EOView(), "1");
+ }
+
+ if ((delegate != null) && ((replacement = delegate.objectForOutletPath(this, "theLoadPanelController")) != null)) {
+ _loadPanelController0 = (replacement == EOArchive._ObjectInstantiationDelegate.NullObject) ? null : (LoadPanelController)replacement;
+ _replacedObjects.setObjectForKey(replacement, "_loadPanelController0");
+ } else {
+ _loadPanelController0 = (LoadPanelController)_registered(new LoadPanelController(), "LoadPanelController");
+ }
+
+ if ((delegate != null) && ((replacement = delegate.objectForOutletPath(this, "theLoadPanelController.theStatusField")) != null)) {
+ _nsTextField0 = (replacement == EOArchive._ObjectInstantiationDelegate.NullObject) ? null : (com.webobjects.eointerface.swing.EOTextField)replacement;
+ _replacedObjects.setObjectForKey(replacement, "_nsTextField0");
+ } else {
+ _nsTextField0 = (com.webobjects.eointerface.swing.EOTextField)_registered(new com.webobjects.eointerface.swing.EOTextField(), "NSTextField2");
+ }
+
+ if ((delegate != null) && ((replacement = delegate.objectForOutletPath(this, "theLoadPanelController.theLoadPanel")) != null)) {
+ _eoFrame0 = (replacement == EOArchive._ObjectInstantiationDelegate.NullObject) ? null : (com.webobjects.eointerface.swing.EOFrame)replacement;
+ _replacedObjects.setObjectForKey(replacement, "_eoFrame0");
+ } else {
+ _eoFrame0 = (com.webobjects.eointerface.swing.EOFrame)_registered(new com.webobjects.eointerface.swing.EOFrame(), "Load Panel");
+ }
+
+ _nsView0 = (JPanel)_eoFrame0.getContentPane();
+ }
+
+ protected void _awaken() {
+ super._awaken();
+
+ if (_replacedObjects.objectForKey("_loadPanelController0") == null) {
+ _connect(_owner(), _loadPanelController0, "theLoadPanelController");
+ }
+ }
+
+ protected void _init() {
+ super._init();
+
+ if (_replacedObjects.objectForKey("_loadPanelController0") == null) {
+ _connect(_loadPanelController0, _eoFrame0, "theLoadPanel");
+ }
+
+ if (_replacedObjects.objectForKey("_nsTextField1") == null) {
+ _setFontForComponent(_nsTextField1, "Lucida Grande", 11, Font.PLAIN);
+ _nsTextField1.setEditable(false);
+ _nsTextField1.setOpaque(false);
+ _nsTextField1.setText("Loading files\u2026");
+ _nsTextField1.setHorizontalAlignment(javax.swing.JTextField.LEFT);
+ _nsTextField1.setSelectable(false);
+ _nsTextField1.setEnabled(true);
+ _nsTextField1.setBorder(null);
+ }
+
+ if (_replacedObjects.objectForKey("_loadPanelController0") == null) {
+ _connect(_loadPanelController0, _nsTextField1, "thePathField");
+ }
+
+ if (_replacedObjects.objectForKey("_loadPanelController0") == null) {
+ _connect(_loadPanelController0, _nsProgressIndicator0, "theProgressBar");
+ }
+
+ if (_replacedObjects.objectForKey("_loadPanelController0") == null) {
+ _connect(_loadPanelController0, _nsTextField0, "theStatusField");
+ }
+
+ if (_replacedObjects.objectForKey("_nsTextField0") == null) {
+ _setFontForComponent(_nsTextField0, "Lucida Grande", 11, Font.PLAIN);
+ _nsTextField0.setEditable(false);
+ _nsTextField0.setOpaque(false);
+ _nsTextField0.setText("Getting info for:");
+ _nsTextField0.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
+ _nsTextField0.setSelectable(false);
+ _nsTextField0.setEnabled(true);
+ _nsTextField0.setBorder(null);
+ }
+
+ if (!(_nsView0.getLayout() instanceof EOViewLayout)) { _nsView0.setLayout(new EOViewLayout()); }
+ _nsTextField0.setSize(102, 18);
+ _nsTextField0.setLocation(10, 10);
+ ((EOViewLayout)_nsView0.getLayout()).setAutosizingMask(_nsTextField0, EOViewLayout.WidthSizable | EOViewLayout.MinYMargin);
+ _nsView0.add(_nsTextField0);
+ _nsTextField1.setSize(222, 18);
+ _nsTextField1.setLocation(114, 10);
+ ((EOViewLayout)_nsView0.getLayout()).setAutosizingMask(_nsTextField1, EOViewLayout.WidthSizable | EOViewLayout.MinYMargin);
+ _nsView0.add(_nsTextField1);
+ _nsProgressIndicator0.setSize(324, 20);
+ _nsProgressIndicator0.setLocation(11, 36);
+ ((EOViewLayout)_nsView0.getLayout()).setAutosizingMask(_nsProgressIndicator0, EOViewLayout.WidthSizable | EOViewLayout.MinYMargin);
+ _nsView0.add(_nsProgressIndicator0);
+
+ if (_replacedObjects.objectForKey("_eoFrame0") == null) {
+ _nsView0.setSize(346, 66);
+ _eoFrame0.setTitle("");
+ _eoFrame0.setLocation(395, 803);
+ _eoFrame0.setSize(346, 66);
+ }
+ }
+}
diff --git a/Liaison/English.lproj/LoadPanel.nib/classes.nib b/Liaison/English.lproj/LoadPanel.nib/classes.nib
new file mode 100644
index 0000000..e1ab46c
--- /dev/null
+++ b/Liaison/English.lproj/LoadPanel.nib/classes.nib
@@ -0,0 +1,28 @@
+{
+ IBClasses = (
+ {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; },
+ {
+ CLASS = LoadPanelController;
+ LANGUAGE = ObjC;
+ OUTLETS = {
+ theLoadPanel = NSPanel;
+ thePathField = NSTextField;
+ theProgressBar = NSProgressIndicator;
+ theStatusField = NSTextField;
+ };
+ SUPERCLASS = NSObject;
+ },
+ {
+ ACTIONS = {showDownloadWindow = id; };
+ CLASS = NIBConnector;
+ LANGUAGE = ObjC;
+ OUTLETS = {
+ theCopyController = CopyController;
+ theLoadPanelController = LoadPanelController;
+ thePreferencesController = PreferencesController;
+ };
+ SUPERCLASS = NSObject;
+ }
+ );
+ IBVersion = 1;
+} \ No newline at end of file
diff --git a/Liaison/English.lproj/LoadPanel.nib/info.nib b/Liaison/English.lproj/LoadPanel.nib/info.nib
new file mode 100644
index 0000000..7484a71
--- /dev/null
+++ b/Liaison/English.lproj/LoadPanel.nib/info.nib
@@ -0,0 +1,16 @@
+<?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>89 245 356 240 0 0 1600 1178 </string>
+ <key>IBFramework Version</key>
+ <string>291.0</string>
+ <key>IBOpenObjects</key>
+ <array>
+ <integer>6</integer>
+ </array>
+ <key>IBSystem Version</key>
+ <string>6L60</string>
+</dict>
+</plist>
diff --git a/Liaison/English.lproj/LoadPanel.nib/objects.nib b/Liaison/English.lproj/LoadPanel.nib/objects.nib
new file mode 100644
index 0000000..51e479f
--- /dev/null
+++ b/Liaison/English.lproj/LoadPanel.nib/objects.nib
Binary files differ
diff --git a/Liaison/English.lproj/MainMenu.nib/classes.nib b/Liaison/English.lproj/MainMenu.nib/classes.nib
new file mode 100644
index 0000000..3fbfcb0
--- /dev/null
+++ b/Liaison/English.lproj/MainMenu.nib/classes.nib
@@ -0,0 +1,123 @@
+{
+ IBClasses = (
+ {
+ ACTIONS = {openHomepage = id; showInspectorWindow = id; showMainWindow = id; };
+ CLASS = ApplicationController;
+ LANGUAGE = ObjC;
+ OUTLETS = {inspectorWindow = NSWindow; mainWindow = NSWindow; };
+ SUPERCLASS = NSObject;
+ },
+ {
+ CLASS = CopyController;
+ LANGUAGE = ObjC;
+ OUTLETS = {theFilenameField = NSTextField; theProgressBar = NSProgressIndicator; };
+ SUPERCLASS = NSObject;
+ },
+ {
+ ACTIONS = {addFiles = id; open = id; };
+ CLASS = FileTableDelegate;
+ LANGUAGE = ObjC;
+ OUTLETS = {
+ inspectorController = InspectorController;
+ statusLine = NSTextField;
+ tableView = NSTableView;
+ theContextMenu = NSMenu;
+ };
+ SUPERCLASS = NSObject;
+ },
+ {
+ ACTIONS = {
+ addFilterRow = id;
+ libraryUpdated = id;
+ removeFilterRow = id;
+ showWindow = id;
+ };
+ CLASS = FindController;
+ LANGUAGE = ObjC;
+ OUTLETS = {
+ theFileList = NSTableView;
+ theFilterBox = NSBox;
+ theFindView = NSView;
+ theFindWindow = NSWindow;
+ theLibraryPopUp = NSPopUpButton;
+ theOperatorPopUp = NSPopUpButton;
+ };
+ SUPERCLASS = NSObject;
+ },
+ {
+ ACTIONS = {deselectAll = id; openSelectedFiles = id; revealInFinder = id; };
+ CLASS = FirstResponder;
+ LANGUAGE = ObjC;
+ SUPERCLASS = NSObject;
+ },
+ {CLASS = FlippedBox; LANGUAGE = ObjC; SUPERCLASS = NSBox; },
+ {
+ ACTIONS = {addGroup = id; };
+ CLASS = GroupTableDelegate;
+ LANGUAGE = ObjC;
+ OUTLETS = {
+ outlineView = NSOutlineView;
+ statusLine = NSTextField;
+ theContextMenu = NSMenu;
+ theFileDelegate = FileTableDelegate;
+ theWindow = WindowController;
+ };
+ SUPERCLASS = NSObject;
+ },
+ {
+ CLASS = InspectorController;
+ LANGUAGE = ObjC;
+ OUTLETS = {theDefaultTabView = NSView; theTabView = NSTabView; theWindow = NSWindow; };
+ SUPERCLASS = NSObject;
+ },
+ {CLASS = LiScrollView; LANGUAGE = ObjC; SUPERCLASS = NSScrollView; },
+ {
+ ACTIONS = {delete = id; };
+ CLASS = LiTableView;
+ LANGUAGE = ObjC;
+ SUPERCLASS = NSTableView;
+ },
+ {
+ ACTIONS = {showDownloadWindow = id; showPreferencesWindow = id; };
+ CLASS = NIBConnector;
+ LANGUAGE = ObjC;
+ OUTLETS = {
+ theCopyController = CopyController;
+ theLoadPanelController = LoadPanelController;
+ thePreferencesController = PreferencesController;
+ };
+ SUPERCLASS = NSObject;
+ },
+ {
+ ACTIONS = {delete = id; };
+ CLASS = NSOutlineView;
+ LANGUAGE = ObjC;
+ SUPERCLASS = NSTableView;
+ },
+ {
+ ACTIONS = {showWindow = id; };
+ CLASS = ViewOptionsController;
+ LANGUAGE = ObjC;
+ OUTLETS = {
+ theContentView = id;
+ theFileDelegate = FileTableDelegate;
+ theHeaderField = id;
+ theWindow = NSWindow;
+ };
+ SUPERCLASS = NSObject;
+ },
+ {
+ CLASS = WindowController;
+ LANGUAGE = ObjC;
+ OUTLETS = {
+ theFileList = NSTableView;
+ theGroupList = NSOutlineView;
+ theInspectorWindow = NSWindow;
+ theMainWindow = NSWindow;
+ theSearchField = id;
+ };
+ SUPERCLASS = NSObject;
+ }
+ );
+ IBVersion = 1;
+} \ No newline at end of file
diff --git a/Liaison/English.lproj/MainMenu.nib/info.nib b/Liaison/English.lproj/MainMenu.nib/info.nib
new file mode 100644
index 0000000..ca7b875
--- /dev/null
+++ b/Liaison/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>17 80 461 291 0 0 1024 746 </string>
+ <key>IBEditorPositions</key>
+ <dict>
+ <key>29</key>
+ <string>5 1124 310 44 0 0 1600 1178 </string>
+ <key>377</key>
+ <string>711 772 176 64 0 0 1600 1178 </string>
+ <key>700</key>
+ <string>287 435 224 118 0 0 1024 746 </string>
+ <key>773</key>
+ <string>974 1012 129 130 0 0 1600 1178 </string>
+ <key>786</key>
+ <string>981 899 106 80 0 0 1600 1178 </string>
+ </dict>
+ <key>IBFramework Version</key>
+ <string>349.0</string>
+ <key>IBOldestOS</key>
+ <integer>3</integer>
+ <key>IBOpenObjects</key>
+ <array>
+ <integer>664</integer>
+ <integer>773</integer>
+ <integer>786</integer>
+ <integer>21</integer>
+ <integer>377</integer>
+ <integer>29</integer>
+ </array>
+ <key>IBSystem Version</key>
+ <string>7B85</string>
+</dict>
+</plist>
diff --git a/Liaison/English.lproj/MainMenu.nib/keyedobjects.nib b/Liaison/English.lproj/MainMenu.nib/keyedobjects.nib
new file mode 100644
index 0000000..482a180
--- /dev/null
+++ b/Liaison/English.lproj/MainMenu.nib/keyedobjects.nib
Binary files differ
diff --git a/Liaison/English.lproj/PreferencesWindow.nib/classes.nib b/Liaison/English.lproj/PreferencesWindow.nib/classes.nib
new file mode 100644
index 0000000..76a7d6c
--- /dev/null
+++ b/Liaison/English.lproj/PreferencesWindow.nib/classes.nib
@@ -0,0 +1,50 @@
+{
+ IBClasses = (
+ {
+ ACTIONS = {
+ addFiles = id;
+ addGroup = id;
+ removeGroup = id;
+ showInspectorWindow = id;
+ showMainWindow = id;
+ };
+ CLASS = ApplicationController;
+ LANGUAGE = ObjC;
+ OUTLETS = {
+ fileDelegate = FileTableDelegate;
+ fileTableView = NSTableView;
+ groupDelegate = GroupTableDelegate;
+ groupOutlineView = NSOutlineView;
+ inspectorWindow = NSWindow;
+ mainWindow = NSWindow;
+ openMenuItem = NSMenuItem;
+ showInspectorWindowMenuItem = NSMenuItem;
+ showMainWindowMenuItem = NSMenuItem;
+ thePanelController = LoadPanelController;
+ thePreferenceController = PreferenceController;
+ };
+ SUPERCLASS = NSObject;
+ },
+ {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; },
+ {
+ CLASS = NIBConnector;
+ LANGUAGE = ObjC;
+ OUTLETS = {thePreferencesController = PreferencesController; };
+ SUPERCLASS = NSObject;
+ },
+ {
+ ACTIONS = {applyChanges = id; selectDownloadDirectory = id; toggleNetworkEnabled = id; };
+ CLASS = PreferencesController;
+ LANGUAGE = ObjC;
+ OUTLETS = {
+ theDownloadField = NSTextField;
+ theHostnameField = NSTextField;
+ theHostnameFieldDescription = NSTextField;
+ theNetworkEnabledButton = NSButton;
+ theWindow = NSWindow;
+ };
+ SUPERCLASS = NSObject;
+ }
+ );
+ IBVersion = 1;
+} \ No newline at end of file
diff --git a/Liaison/English.lproj/PreferencesWindow.nib/info.nib b/Liaison/English.lproj/PreferencesWindow.nib/info.nib
new file mode 100644
index 0000000..ba3c2a9
--- /dev/null
+++ b/Liaison/English.lproj/PreferencesWindow.nib/info.nib
@@ -0,0 +1,38 @@
+<?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>50 54 356 240 0 0 1600 1178 </string>
+ <key>IBFramework Version</key>
+ <string>326.0</string>
+ <key>IBGroupedObjects</key>
+ <dict>
+ <key>0</key>
+ <array>
+ <string>39</string>
+ <string>40</string>
+ <string>49</string>
+ </array>
+ <key>1</key>
+ <array>
+ <string>21</string>
+ <string>22</string>
+ </array>
+ <key>2</key>
+ <array>
+ <string>34</string>
+ <string>35</string>
+ <string>36</string>
+ </array>
+ </dict>
+ <key>IBLastGroupID</key>
+ <string>3</string>
+ <key>IBOpenObjects</key>
+ <array>
+ <integer>5</integer>
+ </array>
+ <key>IBSystem Version</key>
+ <string>7B28</string>
+</dict>
+</plist>
diff --git a/Liaison/English.lproj/PreferencesWindow.nib/objects.nib b/Liaison/English.lproj/PreferencesWindow.nib/objects.nib
new file mode 100644
index 0000000..8dbe7e0
--- /dev/null
+++ b/Liaison/English.lproj/PreferencesWindow.nib/objects.nib
Binary files differ
diff --git a/Liaison/English.lproj/WindowElements.strings b/Liaison/English.lproj/WindowElements.strings
new file mode 100644
index 0000000..dbc6eb8
--- /dev/null
+++ b/Liaison/English.lproj/WindowElements.strings
@@ -0,0 +1,37 @@
+/* Common strings. */
+LiFileSingular = "file";
+LiFilePlural = "files";
+
+/* Default group names. */
+LiRendezvousGroupName = "Rendezvous";
+LiUntitledGroupName = "untitled";
+
+/* Toolbar strings. */
+LiToolbarAddFileLabel = "Add Files";
+LiToolbarAddFilePaletteLabel = "Add Files to Library";
+LiToolbarAddFileToolTip = "Add files to your library.";
+LiToolbarRemoveFileLabel = "Remove File";
+LiToolbarRemoveFilesLabel = "Remove Files";
+LiToolbarRemoveFilePaletteLabel = "Remove from Group";
+LiToolbarRemoveFileToolTip = "Remove the currently selected files.";
+LiToolbarRemoveFilesFromLibraryToolTip = "Remove the currently selected %@ from your library.";
+LiToolbarRemoveFilesFromGroupToolTip = "Remove the currently selected %@ from the group.";
+LiToolbarSearchLabel = "Search";
+LiToolbarSearchPaletteLabel = "Search in Group";
+LiToolbarSearchToolTip = "Search in selected group.";
+LiToolbarGetInfoLabel = "Get Info";
+LiToolbarGetInfoPaletteLabel = "Get Info";
+LiToolbarGetInfoToolTip = "Open the inspector window.";
+LiToolbarAddGroupLabel = "Add Group";
+LiToolbarAddGroupPaletteLabel = "Add Group";
+LiToolbarAddGroupToolTip = "Create a new group.";
+LiToolbarRemoveGroupLabel = "Delete Group";
+LiToolbarRemoveGroupPaletteLabel = "Delete Group";
+LiToolbarRemoveGroupToolTip = "Deletes the currently selected group.";
+LiToolbarRevealLabel = "Show in Finder";
+LiToolbarRevealPaletteLabel = "Show in Finder";
+LiToolbarRevealToolTip = "Show the selected item in the Finder.";
+
+/* File load panel. */
+LiLoadPanelTitle = "Add to Library";
+LiLoadingDirectory = "Scanning: ";
diff --git a/Liaison/FileTableDelegate.h b/Liaison/FileTableDelegate.h
new file mode 100644
index 0000000..43b4f8a
--- /dev/null
+++ b/Liaison/FileTableDelegate.h
@@ -0,0 +1,106 @@
+/* FileTableDelegate */
+
+@class InspectorController;
+
+@interface LiFileStore (BatchPathAdditions)
+- (NSArray *)addPaths: (NSArray *)aPathList toGroup: (NSString *)aGroup;
+@end
+
+@interface FileTableDelegate : NSObject
+{
+ IBOutlet InspectorController *inspectorController;
+ IBOutlet NSTableView *tableView;
+ IBOutlet NSTextField *statusLine;
+ IBOutlet NSMenu *theContextMenu;
+
+ NSImage *ascendingSortingImage, *descendingSortingImage;
+
+ LiFileStore *theFileStore;
+ LiFilter *theFilter;
+ NSString *theSearchString;
+ NSTableColumn *theSelectedColumn;
+ BOOL isAscending;
+
+ NSArray *theActiveList, *theSortedList;
+ NSMutableDictionary *theListPrefs;
+ NSMutableDictionary *theTableColumns;
+ NSMutableDictionary *theShownColumns;
+ NSMutableSet *theSavedSelection;
+}
+- (IBAction)addFiles: (id)sender;
+- (IBAction)openSelectedFiles: (id)sender;
+- (IBAction)revealInFinder: (id)sender;
+
+- (NSDictionary *)browserColumns;
+
+- (LiBrowserColumn *)columnForIdentifier: (NSString *)anIdentifier;
+- (void)showColumnWithIdentifier: (NSString *)anIdentifier;
+- (void)removeColumnWithIdentifier: (NSString *)anIdentifier;
+- (int)numberOfFiles;
+- (LiFileHandle *)fileAtIndex: (int)index;
+
+- (void)addAttributeFilter: (LiFilter *)aFilter;
+- (void)removeAttributeFilter: (LiFilter *)aFilter;
+
+- (void)saveSelectionOfTableView: (NSTableView *)aTableView;
+- (void)restoreSelectionToTableView: (NSTableView *)aTableView
+ refresh: (BOOL)inRefresh;
+
+- (NSSize)minSize;
+
+- (BOOL)validateAction: (SEL)anAction;
+
+- (void)redisplay;
+@property (retain,getter=shownColumns) NSMutableDictionary *theShownColumns;
+@property (retain,getter=searchString) NSString *theSearchString;
+@property (retain) InspectorController *inspectorController;
+@property (retain,getter=selectedColumn) NSTableColumn *theSelectedColumn;
+@property (retain,getter=savedSelection) NSMutableSet *theSavedSelection;
+@property (retain) NSMutableDictionary *theTableColumns;
+@property (retain,getter=filter) LiFilter *theFilter;
+@property (retain) NSTextField *statusLine;
+@property (retain) NSMenu *theContextMenu;
+@property BOOL isAscending;
+@property (retain,getter=tableView) NSTableView *tableView;
+@property (retain) NSMutableDictionary *theListPrefs;
+@property (retain,getter=fileStore) LiFileStore *theFileStore;
+@end
+
+@interface FileTableDelegate (LiTableViewDelegate)
+- (void)deleteSelectedRowsInTableView: (NSTableView *)aTableView;
+@end
+
+@interface FileTableDelegate (CommonAccessors)
+- (NSString *)group;
+- (void)setGroup: (NSString *)aGroupName;
+@end
+
+@interface FileTableDelegate (Accessors)
+- (LiFileStore *)fileStore;
+- (void)setFileStore: (LiFileStore *)aFileStore;
+- (LiFilter *)filter;
+- (void)setFilter: (LiFilter *)aFilter;
+
+- (NSMutableDictionary *)shownColumns;
+- (void)setShownColumns: (NSMutableDictionary *)someColumns;
+
+- (NSArray *)sortedList;
+- (void)setSortedList: (NSArray *)aFileList;
+- (NSArray *)activeList;
+- (void)setActiveList: (NSArray *)aFileList;
+- (NSArray *)fileList;
+- (NSString *)searchString;
+- (void)setSearchString: (NSString *)aSearchString;
+- (NSTableColumn *)selectedColumn;
+- (void)setSelectedColumn: (NSTableColumn *)aColumn;
+- (void)setSelectedColumn: (NSTableColumn *)aColumn
+ withContext: (void *)someContext;
+- (NSMutableSet *)savedSelection;
+- (void)setSavedSelection: (NSMutableSet *)aSelection;
+- (NSMutableDictionary *)listPrefs;
+- (void)setListPrefs: (NSMutableDictionary *)listPrefs;
+- (NSMutableDictionary *)columnPrefsForIdentifier: (NSString *)anIdentifier;
+- (void)setColumnPrefs: (NSMutableDictionary *)columnPrefs
+ forIdentifier: (NSString *)anIdentifier;
+- (NSTableView *)tableView;
+@end
diff --git a/Liaison/FileTableDelegate.m b/Liaison/FileTableDelegate.m
new file mode 100644
index 0000000..cd8591a
--- /dev/null
+++ b/Liaison/FileTableDelegate.m
@@ -0,0 +1,1345 @@
+#import "FileTableDelegate.h"
+
+#import "InspectorController.h"
+#import "ImageAndTextCell.h"
+#import "LoadPanelController.h"
+#import "NIBConnector.h"
+#import "PluginManager.h"
+
+#include <dirent.h>
+#include <sys/types.h>
+
+// XXX - should use SEL instead of NSString.
+struct SortContext {
+ BOOL ascending;
+ SEL compareMethod, getMethod;
+};
+
+int contextSorter(id file1, id file2, void *someContext)
+{
+ struct SortContext *context;
+ id leftVal, rightVal;
+
+ context = (struct SortContext *)someContext;
+ if (context->ascending) {
+ leftVal = [file1 performSelector: context->getMethod];
+ rightVal = [file2 performSelector: context->getMethod];
+ } else {
+ leftVal = [file2 performSelector: context->getMethod];
+ rightVal = [file1 performSelector: context->getMethod];
+ }
+
+ if (rightVal == nil) {
+ if (leftVal == nil)
+ return 0;
+ else
+ return -1;
+ } else if (leftVal == nil)
+ return 1;
+
+ return (int)[leftVal performSelector: context->compareMethod
+ withObject: rightVal];
+}
+
+static NSString *
+myLocalizedString(NSString *aString)
+{
+ return NSLocalizedStringFromTable(aString, @"WindowElements", @"");
+}
+
+static void
+logRect(NSString *desc, NSRect aRect)
+{
+ [LiLog logAsDebug: @"%@, (%f, %f, %f, %f)", desc, aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height];
+}
+
+@interface LiFileHandle (GUIStuff)
+- (void)revealInFinder;
+@end
+
+@implementation LiFileStore (BatchPathAdditions)
+- (BOOL)isValidPath: (NSString *)aPath
+{
+ NSString *filename;
+
+ filename = [aPath lastPathComponent];
+ if ([filename hasPrefix: @"."] ||
+ [filename isEqualToString: @"Icon\r"])
+ return NO;
+
+ return YES;
+}
+
+- (BOOL)addURLs: (NSArray *)aURLList toGroup: (NSString *)aGroup
+{
+ NSURL *fileURL;
+ int i, numFiles;
+
+ if ([aURLList count] == 0)
+ return YES;
+
+ numFiles = [aURLList count];
+
+ i = 0;
+ for (fileURL in aURLList) {
+ LiFileHandle *tmpFile;
+
+ NS_DURING
+ tmpFile = [self addURL: fileURL];
+
+ if (aGroup != nil) {
+ [tmpFile addToGroup: aGroup];
+ }
+ NS_HANDLER
+ [LiLog logAsWarning: @"Couldn't add file: %@ - %@", [localException name], [localException reason]];
+ NS_ENDHANDLER
+
+ i++;
+ }
+
+ return YES;
+}
+
+- (NSArray *)makeURLsFromPaths: (NSArray *)aFileList toGroup: (NSString *)aGroup
+{
+ LoadPanelController *loadPanelController;
+ NSMutableArray *myFileList;
+ NSString *path;
+ int i, fileCount;
+
+ loadPanelController = [[NIBConnector connector] loadPanelController];
+ [loadPanelController setStatus: myLocalizedString(@"LiLoadingDirectory")];
+ [loadPanelController setProgress: 100.0];
+ [loadPanelController setIndeterminantProgress: YES];
+ [loadPanelController update];
+ fileCount = [aFileList count];
+ i = 0;
+
+ myFileList = [NSMutableArray array];
+ for (path in aFileList) {
+ if ((i % 10) == 0) {
+ [loadPanelController setPath: [path lastPathComponent]];
+ [loadPanelController update];
+ }
+
+ if ([self isValidPath: path]) {
+ NSFileManager *defaultManager;
+ BOOL isBundle;
+ BOOL isDirectory;
+
+ isDirectory = NO;
+ defaultManager = [NSFileManager defaultManager];
+ [defaultManager fileExistsAtPath: path isDirectory: &isDirectory];
+ if ([path hasSuffix: @".app"] ||
+ [[NSWorkspace sharedWorkspace] isFilePackageAtPath: path])
+ isBundle = YES;
+ else
+ isBundle = NO;
+
+ if (isDirectory && !isBundle) {
+ struct dirent *dirp;
+ DIR *directory;
+ NSAutoreleasePool *rp;
+ NSMutableArray *dirContents;
+ int j;
+
+ [LiLog logAsDebug: @"Recursing into: %@", path];
+ [LiLog indentDebugLog];
+
+ // Let people know we're working in a subdirectory.
+ j = 0;
+
+ dirContents = [[NSMutableArray alloc] init];
+ directory = opendir([path UTF8String]);
+ while ((dirp = readdir(directory)) != NULL) {
+ NSString *subPath;
+
+ subPath = [NSString stringWithUTF8String: dirp->d_name];
+ if ([self isValidPath: subPath] == YES) {
+ NSString *fullPath;
+
+ if ((j % 10) == 0) {
+ [loadPanelController update];
+ }
+
+ fullPath = [path stringByAppendingPathComponent: subPath];
+ [dirContents addObject: fullPath];
+ }
+ j++;
+ }
+ closedir(directory);
+
+ rp = [[NSAutoreleasePool alloc] init];
+ [self addURLs: [self makeURLsFromPaths: dirContents toGroup: aGroup]
+ toGroup: aGroup];
+ [rp release];
+ [dirContents release];
+
+ [LiLog unindentDebugLog];
+ } else {
+ [LiLog logAsDebug: @"Adding %@ to file list", path];
+ [loadPanelController setPath: [path lastPathComponent]];
+ [myFileList addObject: [NSURL fileURLWithPath: path]];
+ }
+ }
+ i++;
+ }
+
+ return myFileList;
+}
+
+- (NSArray *)addPaths: (NSArray *)aPathList toGroup: (NSString *)aGroup
+{
+ if ([aPathList count] > 0) {
+ LoadPanelController *loadPanelController;
+ NSArray *fileList;
+
+ loadPanelController = [[NIBConnector connector] loadPanelController];
+ [loadPanelController show];
+ fileList = [self makeURLsFromPaths: aPathList toGroup: aGroup];
+ [self addURLs: fileList toGroup: aGroup];
+ [loadPanelController hide];
+ [self synchronize];
+ }
+
+ return nil;
+}
+@end
+
+@implementation FileTableDelegate (LiTableViewDelegate)
+- (void)tableViewDidBecomeFirstResponder: (NSTableView *)aTableView
+{
+ [LiLog logAsDebug: @"[FileTableDelegate becameFirstResponder]"];
+ [LiLog indentDebugLog];
+ if ([tableView numberOfSelectedRows] == 1) {
+ LiFileHandle *theRecord;
+
+ theRecord = [self fileAtIndex: [tableView selectedRow]];
+ // Auto-update a clicked file.
+ [theRecord update];
+ [[theRecord fileStore] synchronize];
+ [inspectorController setFile: theRecord];
+ }
+ [LiLog unindentDebugLog];
+}
+
+- (void)deleteSelectedRowsInTableView: (NSTableView *)aTableView
+{
+ NSEnumerator *rowEnum;
+ NSString *myGroup;
+ NSMutableArray *selectedFiles;
+ NSNumber *row;
+
+ rowEnum = [aTableView selectedRowEnumerator];
+ selectedFiles = [NSMutableArray array];
+ while ((row = [rowEnum nextObject]) != nil) {
+ LiFileHandle *record;
+
+ record = [self fileAtIndex: [row intValue]];
+ [selectedFiles addObject: record];
+ }
+
+ [aTableView deselectAll: self];
+
+ myGroup = [self group];
+ if (myGroup == nil) {
+ LiFileHandle *file;
+
+ for (file in selectedFiles) {
+ [[self fileStore] removeFileHandle: file];
+ }
+ } else {
+ LiFileHandle *file;
+
+ for (file in selectedFiles) {
+ [LiLog logAsDebug: @"Removing %@ from %@", [file filename], myGroup];
+ [LiLog logAsDebug: @"old groups: %@", [[file groups] description]];
+ [file removeFromGroup: myGroup];
+ [LiLog logAsDebug: @"new groups: %@", [[file groups] description]];
+ }
+ }
+ [[self fileStore] synchronize];
+}
+@end
+
+@implementation FileTableDelegate (TableViewDataSource)
+- (int)numberOfRowsInTableView:(NSTableView *)aTableView
+{
+ return [[self sortedList] count];
+}
+
+- (id)tableView: (NSTableView *)aTableView
+objectValueForTableColumn: (NSTableColumn *)aTableColumn
+ row: (int)rowIndex
+{
+ LiFileHandle *record;
+ LiBrowserColumn *col;
+
+ col = [self columnForIdentifier: [aTableColumn identifier]];
+ record = [self fileAtIndex: rowIndex];
+ return [col objectForRecord: record];
+}
+
+- (void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
+{
+ LiFileHandle *record;
+ LiBrowserColumn *col;
+
+ if (rowIndex < 0 || rowIndex >= (int)[[self sortedList] count])
+ return;
+
+ col = [self columnForIdentifier: [aTableColumn identifier]];
+ record = [self fileAtIndex: rowIndex];
+ [col setObject: anObject forRecord: record];
+
+ [[record fileStore] synchronize];
+}
+
+- (NSDragOperation)tableView: (NSTableView *)aTableView
+ validateDrop: (id <NSDraggingInfo>)sender
+ proposedRow: (int)index
+ proposedDropOperation: (NSDragOperation)operation;
+{
+ NSDragOperation dragOp;
+
+ dragOp = NSDragOperationNone;
+ if ([sender draggingSource] != tableView &&
+ [[self fileStore] isEditable] == YES) {
+ NSPasteboard *pboard;
+
+ pboard = [sender draggingPasteboard];
+ if ([pboard availableTypeFromArray:
+ [NSArray arrayWithObjects: NSFilenamesPboardType, NSFilesPromisePboardType, nil]]) {
+ [aTableView setDropRow: -1 dropOperation: NSTableViewDropOn];
+ dragOp = NSDragOperationCopy;
+ }
+ }
+
+ return dragOp;
+}
+
+- (BOOL)tableView: (NSTableView *)aTableView
+ acceptDrop: (id <NSDraggingInfo>)aSender
+ row: (int)anIndex
+ dropOperation: (NSTableViewDropOperation)anOperation
+{
+ NSArray *pathList;
+ NSPasteboard *pboard;
+
+ pboard = [aSender draggingPasteboard];
+
+ pathList = nil;
+ if ([pboard availableTypeFromArray:
+ [NSArray arrayWithObject: NSFilenamesPboardType]]) {
+ pathList = [pboard propertyListForType: NSFilenamesPboardType];
+ }
+
+ // Handle promised files.
+ if ([[pboard types] containsObject: NSFilesPromisePboardType]) {
+ NSEnumerator *pathEnum;
+ NSMutableArray *promisedPaths;
+ NSString *path;
+ NSURL *dropLocation;
+
+ dropLocation = [NSURL fileURLWithPath:
+ [[Preferences sharedPreferences] downloadDirectory]];
+ pathEnum = [[aSender namesOfPromisedFilesDroppedAtDestination: dropLocation] objectEnumerator];
+ promisedPaths = [NSMutableArray array];
+ while ((path = [pathEnum nextObject]) != nil) {
+ [promisedPaths addObject: [[dropLocation path] stringByAppendingPathComponent: path]];
+ }
+ pathList = promisedPaths;
+ }
+
+ if ([pathList count] > 0) {
+ [[self fileStore] addPaths: pathList toGroup: [self group]];
+ return YES;
+ }
+
+ // XXX - should select the added songs now.
+
+ return NO;
+
+}
+
+- (BOOL)tableView: (NSTableView *)aTableView
+ writeRows: (NSArray *)someRows
+ toPasteboard: (NSPasteboard *)aPboard
+{
+ NSMutableArray *liaisonPboard, *filenamePboard, *promisePboard;
+ NSMutableArray *typeList;
+ NSNumber *row;
+
+ liaisonPboard = [NSMutableArray array];
+ filenamePboard = [NSMutableArray array];
+ promisePboard = [NSMutableArray array];
+
+ for (row in someRows) {
+ LiFileHandle *file;
+ NSURL *url;
+
+ file = [self fileAtIndex: [row intValue]];
+ [liaisonPboard addObject: file];
+ url = [file url];
+ if ([url isFileURL] == YES) {
+ [filenamePboard addObject: [url path]];
+ }
+ }
+
+ typeList = [NSMutableArray array];
+ if ([liaisonPboard count] > 0)
+ [typeList addObject: LiaisonPboardType];
+ if ([filenamePboard count] > 0)
+ [typeList addObject: NSFilenamesPboardType];
+ // Make an HFS promise.
+
+ if ([typeList count] > 0) {
+ [aPboard declareTypes: typeList owner: self];
+
+ if ([liaisonPboard count] > 0) {
+ NSData *liaisonPboardData;
+
+ liaisonPboardData = [NSKeyedArchiver archivedDataWithRootObject:
+ liaisonPboard];
+ if ([aPboard setData: liaisonPboardData
+ forType: LiaisonPboardType] == NO)
+ return NO;
+ }
+
+ if ([filenamePboard count] > 0) {
+ if ([aPboard setPropertyList: filenamePboard
+ forType: NSFilenamesPboardType] == NO)
+ return NO;
+ }
+ return YES;
+ }
+ return NO;
+}
+@end
+
+@implementation FileTableDelegate (TableViewDelegate)
+- (void)tableView: (NSTableView*)aTableView
+didClickTableColumn: (NSTableColumn *)aTableColumn
+{
+ LiBrowserColumn *col;
+
+ col = [self columnForIdentifier: [aTableColumn identifier]];
+ if ([col compareMethod]) {
+ struct SortContext context;
+
+ if ([self selectedColumn] && [self selectedColumn] == aTableColumn) {
+ if (isAscending)
+ isAscending = NO;
+ else
+ aTableColumn = nil; // XXX, but it works.
+ } else
+ isAscending = YES;
+
+ context.ascending = isAscending;
+ context.getMethod = [col getMethod];
+ context.compareMethod = [col compareMethod];
+ [self saveSelectionOfTableView: tableView];
+ [self setSelectedColumn: aTableColumn withContext: &context];
+ [self redisplay];
+ [self restoreSelectionToTableView: tableView refresh: YES];
+ }
+}
+
+- (BOOL)tableView: (NSTableView *)theTable shouldSelectRow: (int)theRow
+{
+ if (theRow < 0 || (int)[[self sortedList] count] <= theRow)
+ return NO;
+ return YES;
+}
+
+- (BOOL)tableView: (NSTableView *)aTableView
+shouldEditTableColumn: (NSTableColumn *)aTableColumn
+ row: (int)rowIndex
+{
+ LiFileHandle *record;
+
+ record = [self fileAtIndex: rowIndex];
+ return [record isEditable];
+}
+
+- (void)tableView: (NSTableView *)aTableView
+ copyRow: (int)aRow
+{
+ [LiLog logAsDebug: @"[tableView copyRow]"];
+}
+
+- (void)tableViewSelectionDidChange: (NSNotification *)aNotification
+{
+ [LiLog logAsDebug: @"[FileTableDelegate tableViewSelectionDidChange: (notification)]"];
+ [LiLog indentDebugLog];
+ if ([tableView numberOfSelectedRows] == 1) {
+ LiFileHandle *theRecord;
+
+ theRecord = [self fileAtIndex: [tableView selectedRow]];
+ // Auto-update a clicked file.
+ [theRecord update];
+ [[theRecord fileStore] synchronize];
+ [inspectorController setFile: theRecord];
+ }
+ [LiLog unindentDebugLog];
+}
+
+- (void)doDoubleClickInTable: (NSTableView *)aTable;
+{
+ NSEnumerator *rowEnum;
+ NSNumber *selectedRow;
+
+ rowEnum = [aTable selectedRowEnumerator];
+ while ((selectedRow = [rowEnum nextObject]) != nil) {
+ LiFileHandle *file;
+
+ file = [self fileAtIndex: [selectedRow intValue]];
+ [file open];
+ }
+}
+
+- (void)tableViewColumnDidMove: (NSNotification *)aNotification
+{
+ NSEnumerator *colEnum;
+ NSMutableArray *colOrder;
+ NSMutableDictionary *listPrefs;
+ NSTableColumn *column;
+
+ listPrefs = [self listPrefs];
+ colOrder = [NSMutableArray array];
+ colEnum = [[[self tableView] tableColumns] objectEnumerator];
+ while ((column = [colEnum nextObject]) != nil) {
+ [colOrder addObject: [column identifier]];
+ }
+
+ [listPrefs setObject: colOrder forKey: @"columnOrder"];
+ [self setListPrefs: listPrefs];
+}
+
+- (void)tableViewColumnDidResize: (NSNotification *)aNotification
+{
+ NSMutableDictionary *colPrefs;
+ NSNumber *colSize;
+ NSTableColumn *column;
+
+ column = [[aNotification userInfo] objectForKey: @"NSTableColumn"];
+ if (column != nil) {
+ colPrefs = [self columnPrefsForIdentifier: [column identifier]];
+ colSize = [NSNumber numberWithFloat: [column width]];
+ [colPrefs setObject: colSize forKey: @"width"];
+ [self setColumnPrefs: colPrefs forIdentifier: [column identifier]];
+ }
+}
+@end
+
+@implementation FileTableDelegate
+- (void)openPanelDidEnd: (NSOpenPanel *)openPanel
+ returnCode: (int)returnCode
+ contextInfo: (void *)context
+{
+ [openPanel close];
+ if (returnCode == NSOKButton) {
+ [[self fileStore] addPaths: [openPanel filenames] toGroup: [self group]];
+ }
+}
+
+- (IBAction)addFiles: (id)sender
+{
+ NSOpenPanel *openPanel;
+
+ if ([[self fileStore] isEditable]) {
+ openPanel = [NSOpenPanel openPanel];
+ [openPanel setTitle: myLocalizedString(@"LiLoadPanelTitle")];
+ [openPanel setAllowsMultipleSelection: YES];
+ [openPanel setCanChooseDirectories: YES];
+ [openPanel setCanChooseFiles: YES];
+
+ [openPanel beginSheetForDirectory: nil file: nil types: nil
+ modalForWindow: [NSApp mainWindow] modalDelegate: self
+ didEndSelector: @selector(openPanelDidEnd:returnCode:contextInfo:)
+ contextInfo: nil];
+ }
+}
+
+- (IBAction)openSelectedFiles: (id)sender
+{
+ NSEnumerator *selectionEnum;
+ NSNumber *row;
+
+ selectionEnum = [tableView selectedRowEnumerator];
+ while ((row = [selectionEnum nextObject]) != nil) {
+ LiFileHandle *fileHandle;
+
+ fileHandle = [self fileAtIndex: [row intValue]];
+ [fileHandle open];
+ }
+}
+
+- (IBAction)revealInFinder: (id)sender
+{
+ NSEnumerator *rowEnum;
+ NSNumber *row;
+
+ rowEnum = [tableView selectedRowEnumerator];
+ while ((row = [rowEnum nextObject]) != nil) {
+ LiFileHandle *tmpFile;
+
+ tmpFile = [self fileAtIndex: [row intValue]];
+ [tmpFile revealInFinder];
+ }
+}
+
+- (NSDictionary *)browserColumns
+{
+ NSEnumerator *pluginEnum;
+ NSMutableDictionary *browserColumns;
+ NSObject <LiBrowserPlugin> *plugin;
+
+ browserColumns = [NSMutableDictionary dictionary];
+ pluginEnum = [[[PluginManager defaultManager] browserPlugins] objectEnumerator];
+ while ((plugin = [pluginEnum nextObject]) != nil) {
+ LiBrowserColumn *col;
+ NSEnumerator *colEnum;
+
+ colEnum = [[plugin columns] objectEnumerator];
+ while ((col = [colEnum nextObject]) != nil) {
+ NSString *identifier;
+
+ identifier = [NSString stringWithFormat: @"%@:%@",
+ NSStringFromClass([plugin class]), [col identifier]];
+ [browserColumns setObject: col forKey: identifier];
+ }
+ }
+
+ return browserColumns;
+}
+
+- (LiBrowserColumn *)columnForIdentifier: (NSString *)anIdentifier
+{
+ return [theTableColumns objectForKey: anIdentifier];
+}
+
+- (void)showColumnWithIdentifier: (NSString *)anIdentifier
+{
+ LiBrowserColumn *col;
+ NSMutableDictionary *shownColumns;
+
+ shownColumns = [self shownColumns];
+ if (shownColumns == nil) {
+ shownColumns = [NSMutableDictionary dictionary];
+ [self setShownColumns: shownColumns];
+ }
+
+ col = [self columnForIdentifier: anIdentifier];
+ if (col && [shownColumns objectForKey: anIdentifier] == nil) {
+ NSDictionary *colPrefs;
+ NSTableColumn *tableColumn;
+
+ colPrefs = [self columnPrefsForIdentifier: anIdentifier];
+ tableColumn = [[[NSTableColumn alloc] initWithIdentifier: anIdentifier] autorelease];
+ [tableColumn setEditable: [col editable]];
+ [tableColumn setDataCell: [col cell]];
+ [tableColumn setResizable: [col resizable]];
+ if ([col resizable]) {
+ NSNumber *colWidth;
+
+ colWidth = [colPrefs objectForKey: @"width"];
+ if (colWidth != nil)
+ [col setWidth: colWidth];
+ }
+
+ if ([col showsHeader])
+ [[tableColumn headerCell] setStringValue: [col name]];
+ else
+ [[tableColumn headerCell] setStringValue: @""];
+
+ if ([col width])
+ [tableColumn setWidth: [[col width] floatValue]];
+
+ [shownColumns setObject: tableColumn forKey: anIdentifier];
+
+ [tableView addTableColumn: tableColumn];
+ }
+}
+
+- (void)removeColumnWithIdentifier: (NSString *)anIdentifier
+{
+ NSTableColumn *col;
+
+ col = [[self shownColumns] objectForKey: anIdentifier];
+ if (col != nil) {
+ [[self shownColumns] removeObjectForKey: anIdentifier];
+ [tableView removeTableColumn: col];
+ }
+}
+
+- (id)init
+{
+ self = [super init];
+
+ theTableColumns = [[NSMutableDictionary alloc] init];
+
+ return self;
+}
+
+- (void)dealloc
+{
+ NSNotificationCenter *defaultCenter;
+
+ defaultCenter = [NSNotificationCenter defaultCenter];
+ [defaultCenter removeObserver: self];
+
+ [ascendingSortingImage release];
+ [descendingSortingImage release];
+ [self setActiveList: nil];
+ [self setFileStore: nil];
+ [self setFilter: nil];
+ [self setSearchString: nil];
+
+ [theTableColumns release];
+
+ [super dealloc];
+}
+
+- (void)awakeFromNib
+{
+ NSArray *shownColumns;
+ NSDictionary *listPrefs;
+ NSEnumerator *columnEnum;
+ NSString *columnID;
+
+ [LiLog logAsDebug: @"[FileTableDelegate awakeFromNib]"];
+ [LiLog indentDebugLog];
+
+ ascendingSortingImage = [[NSImage imageNamed: @"SortAscending.gif"] retain];
+ descendingSortingImage = [[NSImage imageNamed: @"SortDescending.gif"] retain];
+
+ [tableView setTarget: self];
+ [tableView setDoubleAction: @selector(doDoubleClickInTable:)];
+
+ // XXX
+ theTableColumns = (NSMutableDictionary *)[self browserColumns];
+ [theTableColumns retain];
+
+ listPrefs = [self listPrefs];
+ shownColumns = [listPrefs objectForKey: @"columnOrder"];
+ for (columnID in shownColumns) {
+ [self showColumnWithIdentifier: columnID];
+ }
+
+ if ([[[self tableView] tableColumns] count] == 0) {
+ columnEnum = [theTableColumns keyEnumerator];
+ while ((columnID = [columnEnum nextObject]) != nil) {
+ [LiLog logAsDebug: @"showCol: %@", columnID];
+ [self showColumnWithIdentifier: columnID];
+ }
+ }
+
+ /* Register for Drag-and-Drop operation. */
+ [tableView registerForDraggedTypes:
+ [NSArray arrayWithObjects: NSFilenamesPboardType, NSFilesPromisePboardType, NSHTMLPboardType, NSTIFFPboardType, NSPICTPboardType, NSURLPboardType, NSFileContentsPboardType, nil]];
+
+ [statusLine setStringValue: @""];
+
+ [LiLog unindentDebugLog];
+}
+
+- (void)respondToFileChanged: (NSNotification *)aNotification
+{
+ LiFileHandle *file;
+ NSArray *addedFileList, *changedFileList, *removedFileList;
+ NSDictionary *fileDict;
+ NSEnumerator *fileEnum;
+ BOOL needsRedisplay;
+
+ needsRedisplay = NO;
+
+ addedFileList = [[aNotification userInfo] objectForKey: LiFilesAdded];
+ changedFileList = [[aNotification userInfo] objectForKey: LiFilesChanged];
+ removedFileList = [[aNotification userInfo] objectForKey: LiFilesRemoved];
+
+ fileEnum = [addedFileList objectEnumerator];
+ while (needsRedisplay == NO &&
+ (file = [fileEnum nextObject]) != nil) {
+ if ([file matchesFilter: [self filter]])
+ needsRedisplay = YES;
+ }
+
+ fileEnum = [changedFileList objectEnumerator];
+ while (needsRedisplay == NO &&
+ (fileDict = [fileEnum nextObject]) != nil) {
+ LiFileHandle *tmpHandle;
+ NSDictionary *oldAttributes;
+
+ tmpHandle = [fileDict objectForKey: @"LiFileHandleAttribute"];
+ oldAttributes = [fileDict objectForKey: LiFileOldAttributes];
+ if ([[self fileStore] attributes: oldAttributes matchFilter: [self filter]] ||
+ [tmpHandle matchesFilter: [self filter]])
+ needsRedisplay = YES;
+ }
+
+ fileEnum = [removedFileList objectEnumerator];
+ while (needsRedisplay == NO &&
+ (fileDict = [fileEnum nextObject]) != nil) {
+ if ([[self fileStore] attributes: [fileDict objectForKey: LiFileOldAttributes]
+ matchFilter: [self filter]]) {
+ needsRedisplay = YES;
+ }
+ }
+
+ if (needsRedisplay) {
+ [self saveSelectionOfTableView: tableView];
+ [self redisplay];
+ [self restoreSelectionToTableView: tableView refresh: YES];
+ }
+}
+
+- (int)numberOfFiles
+{
+ return [[self sortedList] count];
+}
+
+- (LiFileHandle *)fileAtIndex: (int)index
+{
+ return [[self sortedList] objectAtIndex: index];
+}
+
+- (void)addAttributeFilter: (LiFilter *)aFilter
+{
+ [LiLog logAsDebug: @"[FileTableDelegate addAttributeFilter: %@]", [aFilter description]];
+ [LiLog indentDebugLog];
+
+ [self setFilter: aFilter];
+
+#if 0
+ filters = [self attributeFilters];
+ attrEnum = [someFilters keyEnumerator];
+ while ((attribute = [attrEnum nextObject]) != nil)
+ [filters setObject: [someFilters objectForKey: attribute]
+ forKey: attribute];
+
+ [LiLog unindentDebugLog];
+#endif
+}
+
+#if 0
+- (id)filterForAttribute: (NSString *)anAttribute
+{
+ [LiLog logAsDebug: @"[FileTableDelegate filterForAttribute: %@]", anAttribute];
+ [[LiLog indentDebugLog] logAsDebug: @"filters: %@", [[self attributeFilters] description]];
+ [LiLog unindentDebugLog];
+ return [[self attributeFilters] objectForKey: anAttribute];
+}
+#endif
+
+- (void)removeAttributeFilter: (LiFilter *)aFilter
+{
+ [LiLog logAsDebug: @"[FileTableDelegate removeAttributeFilter: %@]", [aFilter description]];
+ [self setFilter: nil];
+
+#if 0
+ filters = [self attributeFilters];
+ attrEnum = [someFilters keyEnumerator];
+ while ((attribute = [attrEnum nextObject]) != nil)
+ [filters removeObjectForKey: attribute];
+#endif
+}
+
+- (void)saveSelectionOfTableView: (NSTableView *)aTableView
+{
+ NSEnumerator *theEnum;
+ NSMutableSet *savedSelection;
+ NSNumber *rowNum;
+
+ savedSelection = [self savedSelection];
+ if (savedSelection == nil) {
+ savedSelection = [[NSMutableSet alloc] init];
+ theEnum = [aTableView selectedRowEnumerator];
+ while ((rowNum = [theEnum nextObject]) != nil) {
+ id item;
+
+ item = [[self sortedList] objectAtIndex: [rowNum intValue]];
+ [savedSelection addObject: item];
+ }
+ [self setSavedSelection: savedSelection];
+ }
+}
+
+- (void)restoreSelectionToTableView: (NSTableView *)aTableView
+ refresh: (BOOL)inRefresh
+{
+ NSMutableSet *savedSelection;
+ id item;
+ int savedLastRow;
+
+ [aTableView deselectAll: self];
+
+ savedLastRow = -1;
+ savedSelection = [self savedSelection];
+ if (savedSelection != nil) {
+ for (item in savedSelection) {
+ int row;
+
+ row = [[self sortedList] indexOfObject: item];
+ if (row != NSNotFound) {
+ [aTableView selectRow: row byExtendingSelection: YES];
+ savedLastRow = row;
+ }
+ }
+
+ [self setSavedSelection: nil];
+
+ if (inRefresh && savedLastRow > -1)
+ [aTableView scrollRowToVisible: savedLastRow];
+ }
+}
+
+- (NSSize)minSize
+{
+ NSArray *tableColumns;
+ NSRect headerRect;
+ NSTableColumn *column;
+ float minHeight, minWidth, rowHeight, scrollHeight, scrollWidth;
+ int numRows;
+
+ [LiLog logAsDebug: @"[FileTableDelegate minSize]"];
+ [LiLog indentDebugLog];
+
+ headerRect = [[[self tableView] headerView] frame];
+ logRect(@"headerRect", headerRect);
+
+ rowHeight = [[self tableView] rowHeight];
+ [LiLog logAsDebug: @"row height: %f", rowHeight];
+ numRows = [self numberOfRowsInTableView: [self tableView]];
+ [LiLog logAsDebug: @"number of rows: %d", numRows];
+
+ scrollWidth = 17.0;
+ scrollHeight = 34.0;
+
+ tableColumns = [[self tableView] tableColumns];
+ minWidth = scrollWidth;
+ [LiLog indentDebugLog];
+ for (column in tableColumns) {
+ float colWidth;
+
+ colWidth = [column width];
+ [LiLog logAsDebug: @"colWidth: %f", colWidth];
+ minWidth += colWidth;
+ }
+ [LiLog unindentDebugLog];
+ [LiLog logAsDebug: @"minWidth: %f", minWidth];
+
+ minHeight = headerRect.size.height + rowHeight * numRows + scrollHeight;
+ [LiLog logAsDebug: @"minHeight: %f", minHeight];
+
+ [LiLog unindentDebugLog];
+ return NSMakeSize(minWidth, minHeight);
+}
+
+
+- (BOOL)validateAction: (SEL)anAction
+{
+ //[LiLog logAsDebug: @"[FileTableDelegate validateAction: %@]", NSStringFromSelector(anAction)];
+ if (anAction == @selector(addFiles:)) {
+ return [[self fileStore] isEditable];
+ } else if (anAction == @selector(delete:)) {
+ return ([[self fileStore] isEditable] &&
+ [tableView numberOfSelectedRows] > 0);
+ } else if (anAction == @selector(revealInFinder:)) {
+ return ([[self fileStore] isEditable] &&
+ [tableView numberOfSelectedRows] > 0);
+ } else if (anAction == @selector(openSelectedFiles:)) {
+ return ([[self fileStore] isEditable] &&
+ [tableView numberOfSelectedRows] > 0);
+ } else
+ return YES;
+}
+
+- (BOOL)validateMenuItem: (NSMenuItem *)anItem
+{
+ return [self validateAction: [anItem action]];
+}
+
+- (void)redisplay
+{
+ NSString *filePlural;
+ unsigned long numRecords;
+
+ [self setSearchString: [self searchString]];
+ [self setSelectedColumn: [self selectedColumn]];
+
+ numRecords = [[self sortedList] count];
+ if (numRecords == 1) {
+ filePlural = myLocalizedString(@"LiFileSingular");
+ } else {
+ filePlural = myLocalizedString(@"LiFilePlural");
+ }
+ [statusLine setStringValue:
+ [NSString stringWithFormat: @"(%d %@)", numRecords, filePlural]];
+
+ [tableView reloadData];
+}
+@synthesize statusLine;
+@synthesize theTableColumns;
+@synthesize theFileStore;
+@synthesize theListPrefs;
+@synthesize isAscending;
+@synthesize tableView;
+@synthesize theContextMenu;
+@synthesize theFilter;
+@synthesize theSavedSelection;
+@synthesize theShownColumns;
+@synthesize theSelectedColumn;
+@synthesize inspectorController;
+@synthesize theSearchString;
+@end
+
+@implementation FileTableDelegate (CommonAccessors)
+#if 0
+- (NSString *)group
+{
+ return [self filterForAttribute: LiGroupsAttribute];
+}
+
+- (void)setGroup: (NSString *)aGroupName
+{
+ if (aGroupName == nil) {
+ if ([self group] != nil) {
+ NSDictionary *filter;
+
+ filter = [NSDictionary dictionaryWithObject: [self group]
+ forKey: LiGroupsAttribute];
+ [self removeAttributeFilters: filter];
+ }
+ } else {
+ NSDictionary *filter;
+
+ filter = [NSDictionary dictionaryWithObject: aGroupName
+ forKey: LiGroupsAttribute];
+ [self addAttributeFilters: filter];
+ }
+ // We're re-filtering.
+ [self setActiveList: nil];
+
+ [self redisplay];
+}
+#endif
+
+- (NSString *)group
+{
+ return [[self filter] value];
+}
+
+- (void)setGroup: (NSString *)aGroup
+{
+ LiFilter *filter;
+
+ if (aGroup != nil)
+ filter = [LiFilter filterWithAttribute: LiGroupsAttribute
+ compareSelector: @selector(isEqual:)
+ value: aGroup];
+ else
+ filter = nil;
+ [self setFilter: filter];
+}
+@end
+
+@implementation FileTableDelegate (Accessors)
+- (LiFileStore *)fileStore
+{
+ return theFileStore;
+}
+
+- (void)setFileStore: (LiFileStore *)aFileStore
+{
+ [LiLog logAsDebug: @"[FileTableDelegate setFileStore: %@]", aFileStore];
+
+ if (aFileStore != theFileStore) {
+ NSNotificationCenter *defaultCenter;
+
+ defaultCenter = [NSNotificationCenter defaultCenter];
+ if (theFileStore != nil) {
+ [defaultCenter removeObserver: self
+ name: LiFileChangedNotification
+ object: theFileStore];
+ [theFileStore release];
+ }
+ if (aFileStore != nil) {
+ [defaultCenter addObserver: self
+ selector: @selector(respondToFileChanged:)
+ name: LiFileChangedNotification
+ object: aFileStore];
+ theFileStore = [aFileStore retain];
+ }
+ }
+}
+
+#if 0
+- (NSMutableDictionary *)attributeFilters
+{
+ return theFilters;
+}
+
+- (void)setAttributeFilters: (NSMutableDictionary *)someFilters
+{
+ if (someFilters != theFilters) {
+ [someFilters retain];
+ [theFilters release];
+ theFilters = someFilters;
+ }
+}
+#endif
+
+- (LiFilter *)filter
+{
+ return theFilter;
+}
+
+- (void)setFilter: (LiFilter *)aFilter
+{
+ [aFilter retain];
+ [theFilter retain];
+ theFilter = aFilter;
+}
+
+- (NSMutableDictionary *)shownColumns
+{
+ return theShownColumns;
+}
+
+- (void)setShownColumns: (NSMutableDictionary *)someColumns
+{
+ [someColumns retain];
+ [theShownColumns release];
+ theShownColumns = someColumns;
+}
+
+- (NSArray *)sortedList
+{
+ if (theSortedList == nil)
+ return [self activeList];
+ else
+ return theSortedList;
+}
+
+- (void)setSortedList: (NSArray *)aFileList
+{
+ [aFileList retain];
+ [theSortedList release];
+ theSortedList = aFileList;
+}
+
+- (NSArray *)activeList
+{
+ if (theActiveList == nil)
+ return [self fileList];
+ else
+ return theActiveList;
+}
+
+- (void)setActiveList: (NSArray *)aFileList
+{
+ if (aFileList != theActiveList) {
+ [aFileList retain];
+ [theActiveList release];
+ theActiveList = aFileList;
+
+ [self setSortedList: nil];
+ }
+}
+
+- (NSArray *)fileList
+{
+ return [[self fileStore] filesMatchingFilter: [self filter]];
+}
+
+- (NSString *)searchString
+{
+ return theSearchString;
+}
+
+- (NSArray *)filter: (NSArray *)aFileList
+ byString: (NSString *)aSearchString
+{
+ LiFileHandle *record;
+ NSMutableArray *subset;
+
+ if ([aSearchString length] == 0)
+ return aFileList;
+
+ subset = [NSMutableArray array];
+ for (record in aFileList) {
+ NSString *filename;
+ NSRange range;
+
+ filename = [[record filename] lowercaseString];
+ range = [filename rangeOfString: aSearchString];
+ if (range.location != NSNotFound)
+ [subset addObject: record];
+ }
+
+ return subset;
+}
+
+- (void)setSearchString: (NSString *)aSearchString
+{
+ NSArray *tmpList;
+
+ if (aSearchString == nil) {
+ aSearchString = @"";
+ tmpList = [self fileList];
+ } else if ([aSearchString rangeOfString: theSearchString options: NSCaseInsensitiveSearch].location != NSNotFound) {
+ // Optimized filter - only need to select off the active list.
+ tmpList = [self filter: [self activeList] byString: aSearchString];
+ } else {
+ tmpList = [self filter: [self fileList] byString: aSearchString];
+ }
+
+ [aSearchString retain];
+ [theSearchString release];
+ theSearchString = aSearchString;
+
+ [self setActiveList: tmpList];
+}
+
+- (NSTableColumn *)selectedColumn
+{
+ return theSelectedColumn;
+}
+
+- (void)setSelectedColumn: (NSTableColumn *)aColumn
+{
+ LiBrowserColumn *col;
+ struct SortContext context;
+
+ col = [self columnForIdentifier: [aColumn identifier]];
+
+ context.ascending = isAscending;
+ context.getMethod = [col getMethod];
+ context.compareMethod = [col compareMethod];
+ [self setSelectedColumn: aColumn withContext: &context];
+}
+
+- (void)setSelectedColumn: (NSTableColumn *)aColumn
+ withContext: (void *)someContext
+{
+ struct SortContext *context;
+ NSArray *sortedList;
+
+ [aColumn retain];
+ [theSelectedColumn release];
+ theSelectedColumn = aColumn;
+
+ context = (struct SortContext *)someContext;
+
+ if ([tableView highlightedTableColumn] != theSelectedColumn) {
+ [tableView setIndicatorImage: nil
+ inTableColumn: [tableView highlightedTableColumn]];
+ }
+
+ [tableView setHighlightedTableColumn: theSelectedColumn];
+ if (theSelectedColumn != nil) {
+ if (context->ascending)
+ [tableView setIndicatorImage: ascendingSortingImage
+ inTableColumn: theSelectedColumn];
+ else
+ [tableView setIndicatorImage: descendingSortingImage
+ inTableColumn: theSelectedColumn];
+
+ sortedList = [[self activeList] sortedArrayUsingFunction: contextSorter
+ context: someContext];
+
+ [self setSortedList: sortedList];
+ } else {
+ [self setSortedList: nil];
+ }
+}
+
+- (NSMutableSet *)savedSelection
+{
+ return theSavedSelection;
+}
+
+- (void)setSavedSelection: (NSMutableSet *)aSelection
+{
+ [aSelection retain];
+ [theSavedSelection release];
+ theSavedSelection = aSelection;
+}
+
+- (NSMutableDictionary *)listPrefs
+{
+ if (theListPrefs == nil) {
+ NSDictionary *listPrefs;
+
+ listPrefs = [[Preferences sharedPreferences] fileListPrefs];
+ if (listPrefs != nil) {
+ theListPrefs = [[NSMutableDictionary alloc] initWithDictionary: listPrefs];
+ } else
+ theListPrefs = [[NSMutableDictionary alloc] init];
+ }
+ return theListPrefs;
+}
+
+- (void)setListPrefs: (NSMutableDictionary *)listPrefs
+{
+ [listPrefs retain];
+ [theListPrefs release];
+ theListPrefs = listPrefs;
+
+ [[Preferences sharedPreferences] setFileListPrefs: theListPrefs];
+}
+
+- (NSMutableDictionary *)columnPrefsForIdentifier: (NSString *)anIdentifier
+{
+ NSMutableDictionary *colPrefs, *retVal;
+
+ colPrefs = [[self listPrefs] objectForKey: @"columns"];
+ if (colPrefs == nil) {
+ colPrefs = [NSMutableDictionary dictionary];
+ [[self listPrefs] setObject: colPrefs forKey: @"columns"];
+ }
+
+ retVal = [colPrefs objectForKey: anIdentifier];
+ if (retVal == nil) {
+ retVal = [NSMutableDictionary dictionary];
+ [colPrefs setObject: retVal forKey: anIdentifier];
+ }
+
+ return retVal;
+}
+
+- (void)setColumnPrefs: (NSMutableDictionary *)columnPrefs
+ forIdentifier: (NSString *)anIdentifier
+{
+ NSMutableDictionary *colPrefs;
+
+ colPrefs = [[self listPrefs] objectForKey: @"columns"];
+ if (colPrefs == nil) {
+ colPrefs = [NSMutableDictionary dictionary];
+ [[self listPrefs] setObject: colPrefs forKey: @"columns"];
+ }
+
+ [colPrefs setObject: columnPrefs forKey: anIdentifier];
+
+ [[Preferences sharedPreferences] setFileListPrefs: theListPrefs];
+}
+
+- (NSTableView *)tableView
+{
+ return tableView;
+}
+@end
+
+@implementation LiFileHandle (GUIStuff)
+- (void)revealInFinder
+{
+ NSURL *fileURL;
+
+ fileURL = [self url];
+ if ([fileURL isFileURL]) {
+ [[NSWorkspace sharedWorkspace] selectFile:
+ [fileURL path] inFileViewerRootedAtPath: @""];
+ }
+}
+@end \ No newline at end of file
diff --git a/Liaison/FindController.h b/Liaison/FindController.h
new file mode 100644
index 0000000..5f36e08
--- /dev/null
+++ b/Liaison/FindController.h
@@ -0,0 +1,43 @@
+//
+// FindController.h
+// Liaison
+//
+// Created by Brian Cully on Sat Aug 23 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+@interface FindController : NSObject
+{
+ IBOutlet NSBox *theFilterBox;
+ IBOutlet NSTableView *theFileList;
+ IBOutlet NSView *theFindView;
+ IBOutlet NSWindow *theFindWindow;
+ IBOutlet NSPopUpButton *theLibraryPopUp;
+ IBOutlet NSPopUpButton *theOperatorPopUp;
+
+ LiFilter *theFilter;
+
+ NSMutableDictionary *theTableColumns;
+}
+- (IBAction)showWindow: (id)sender;
+
+- (IBAction)libraryUpdated: (id)sender;
+
+- (IBAction)addFilterRow: (id)sender;
+- (IBAction)removeFilterRow: (id)sender;
+@property (retain) NSPopUpButton *theOperatorPopUp;
+@property (retain,getter=filter) LiFilter *theFilter;
+@property (retain,getter=tableColumns) NSMutableDictionary *theTableColumns;
+@property (retain) NSView *theFindView;
+@property (retain) NSWindow *theFindWindow;
+@property (retain) NSPopUpButton *theLibraryPopUp;
+@property (retain) NSBox *theFilterBox;
+@property (retain) NSTableView *theFileList;
+@end
+
+@interface FindController (Accessors)
+- (LiFilter *)filter;
+- (void)setFilter: (LiFilter *)aFilter;
+- (NSMutableDictionary *)tableColumns;
+- (void)setTableColumns: (NSMutableDictionary *)someColumns;
+@end \ No newline at end of file
diff --git a/Liaison/FindController.m b/Liaison/FindController.m
new file mode 100644
index 0000000..8b16bbf
--- /dev/null
+++ b/Liaison/FindController.m
@@ -0,0 +1,154 @@
+//
+// FindController.m
+// Liaison
+//
+// Created by Brian Cully on Sat Aug 23 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+#import "FindController.h"
+
+#import "FileTableDelegate.h"
+
+@implementation FindController (WindowDelegate)
+- (void)windowDidBecomeKey: (NSNotification *)aNotificatin
+{
+ [LiLog logAsDebug: @"[FindController windowDidBecomeKey]"];
+ [[theFindWindow firstResponder] becomeFirstResponder];
+}
+@end
+
+@implementation FindController
+- (id)init
+{
+ self = [super init];
+ if (self != nil) {
+ NSNotificationCenter *defaultCenter;
+
+ defaultCenter = [NSNotificationCenter defaultCenter];
+ [defaultCenter addObserver: self
+ selector: @selector(respondToFileStoreChanged:)
+ name: LiFileStoresChangedNotification
+ object: nil];
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ NSNotificationCenter *defaultCenter;
+
+ defaultCenter = [NSNotificationCenter defaultCenter];
+ [defaultCenter removeObserver: self];
+
+ [self setFilter: nil];
+ [self setTableColumns: nil];
+
+ [super dealloc];
+}
+
+- (IBAction)showWindow: (id)sender
+{
+ FileTableDelegate *fileDelegate;
+
+ [LiLog logAsDebug: @"[FindController showWindow: (sender)]"];
+
+ fileDelegate = [theFileList delegate];
+ [fileDelegate setFileStore: [[LiFileStore allFileStores] objectAtIndex: 0]];
+ [fileDelegate setGroup: nil];
+
+ [theFindWindow makeKeyAndOrderFront: self];
+}
+
+- (IBAction)libraryUpdated: (id)sender
+{
+ LiFileStore *selectedStore;
+ int itemTag;
+
+ itemTag = [[theLibraryPopUp selectedItem] tag];
+ selectedStore = [LiFileStore fileStoreWithID: [NSNumber numberWithInt: itemTag]];
+ if (selectedStore != nil) {
+ FileTableDelegate *fileDelegate;
+
+ [LiLog logAsDebug: @"Selected store: %@", [selectedStore name]];
+ fileDelegate = [theFileList delegate];
+ [fileDelegate setFileStore: selectedStore];
+ [fileDelegate setGroup: nil];
+ }
+}
+
+- (IBAction)addFilterRow: (id)sender
+{
+ [LiLog logAsDebug: @"[FindController addFilterRow: (sender)]"];
+}
+
+- (IBAction)removeFilterRow: (id)sender
+{
+ [LiLog logAsDebug: @"[FindController removeFilterRow: (sender)]"];
+}
+@synthesize theLibraryPopUp;
+@synthesize theFilter;
+@synthesize theFileList;
+@synthesize theFindView;
+@synthesize theTableColumns;
+@synthesize theFindWindow;
+@synthesize theFilterBox;
+@synthesize theOperatorPopUp;
+@end
+
+@implementation FindController (Accessors)
+- (LiFilter *)filter
+{
+ return theFilter;
+}
+
+- (void)setFilter: (LiFilter *)aFilter
+{
+ [aFilter retain];
+ [theFilter release];
+ aFilter = theFilter;
+}
+
+- (NSMutableDictionary *)tableColumns
+{
+ return theTableColumns;
+}
+
+- (void)setTableColumns: (NSMutableDictionary *)someColumns
+{
+ [someColumns retain];
+ [theTableColumns release];
+ theTableColumns = someColumns;
+}
+@end
+
+@implementation FindController (Private)
+- (void)respondToFileStoreChanged: (NSNotification *)aNotification
+{
+ LiFileStore *fileStore;
+ NSEnumerator *fsEnum;
+ NSMenu *libraryMenu;
+ int i;
+
+ [LiLog logAsDebug: @"[FindController respondToFileStoreChanged: (notification)]"];
+ [LiLog indentDebugLog];
+
+ libraryMenu = [[[NSMenu alloc] initWithTitle: @"Libraries"] autorelease];
+ i = 0;
+ fsEnum = [LiFileStore fileStoreEnumerator];
+ for (i = 0; (fileStore = [fsEnum nextObject]) != nil; i++) {
+ NSMenuItem *fsItem;
+
+ [LiLog logAsDebug: @"found file store: %@", [fileStore name]];
+ fsItem = [[[NSMenuItem alloc] initWithTitle: [fileStore name]
+ action: nil
+ keyEquivalent: @""] autorelease];
+ [fsItem setImage: [fileStore icon]];
+ [fsItem setTag: [[fileStore storeID] intValue]];
+ [libraryMenu insertItem: fsItem atIndex: i];
+ }
+ [theLibraryPopUp setMenu: libraryMenu];
+
+ [LiLog unindentDebugLog];
+}
+@end \ No newline at end of file
diff --git a/Liaison/FlippedBox.h b/Liaison/FlippedBox.h
new file mode 100644
index 0000000..6d6ef0f
--- /dev/null
+++ b/Liaison/FlippedBox.h
@@ -0,0 +1,6 @@
+/* FlippedBox */
+
+@interface FlippedBox : NSBox
+{
+}
+@end
diff --git a/Liaison/FlippedBox.m b/Liaison/FlippedBox.m
new file mode 100644
index 0000000..fddf3e8
--- /dev/null
+++ b/Liaison/FlippedBox.m
@@ -0,0 +1,8 @@
+#import "FlippedBox.h"
+
+@implementation FlippedBox
+- (BOOL)isFlipped
+{
+ return YES;
+}
+@end
diff --git a/Liaison/Group.h b/Liaison/Group.h
new file mode 100644
index 0000000..2112bff
--- /dev/null
+++ b/Liaison/Group.h
@@ -0,0 +1,52 @@
+//
+// Group.h
+// Liaison
+//
+// Created by Brian Cully on Tue Feb 04 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+typedef enum _GroupType { LEAF, BRANCH } GroupType;
+
+@interface Group : NSObject <NSCoding>
+{
+ NSString *name;
+ GroupType type;
+ NSImage *icon;
+ NSMutableArray *children;
+ Group *parent;
+
+ LiFileStore *theFileStore;
+}
++ (Group *)groupWithName: (NSString *)aName;
++ (Group *)groupWithName: (NSString *)aName andType: (GroupType)aType;
+- (Group *)initWithName: (NSString *)aName;
+- (Group *)initWithName: (NSString *)aName andType: (GroupType)aType;
+
+- (id)initWithContentsOfFile: (NSString *)aFilename;
+- (BOOL)writeToFile: (NSString *)aFilename;
+
+- (LiFileStore *)fileStore;
+- (void)setFileStore: (LiFileStore *)aFileStore;
+
+- (NSString *)name;
+- (GroupType)type;
+- (void)setName: (NSString *)aName;
+- (void)setType: (GroupType)aType;
+- (NSImage *)icon;
+- (void)setIcon: (NSImage *)anIcon;
+
+- (Group *)parent;
+
+- (int)numberOfChildren;
+- (NSEnumerator *)childEnumerator;
+- (BOOL)hasChild: (id)aChild;
+- (void)addChild: (id)aChild;
+- (void)removeChild: (id)aChild;
+- (Group *)childNamed: (NSString *)aName;
+- (void)removeChildNamed: (NSString *)aName;
+- (id)childAtIndex: (int)index;
+- (void)removeChildAtIndex: (int)index;
+@property (retain) NSMutableArray *children;
+@property (retain,getter=fileStore) LiFileStore *theFileStore;
+@end
diff --git a/Liaison/Group.m b/Liaison/Group.m
new file mode 100644
index 0000000..f975f60
--- /dev/null
+++ b/Liaison/Group.m
@@ -0,0 +1,321 @@
+//
+// Group.m
+// Liaison
+//
+// Created by Brian Cully on Tue Feb 04 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+#import "Group.h"
+
+@implementation Group (Copying)
+- (id)copyWithZone: (NSZone *)aZone
+{
+ Group *tmpGroup;
+
+ tmpGroup = [[Group alloc] initWithName: [self name] andType: [self type]];
+ [tmpGroup setIcon: [self icon]];
+ [tmpGroup setFileStore: [self fileStore]];
+ tmpGroup->parent = parent; // XXX
+ tmpGroup->children = children; // XXX
+
+ return tmpGroup;
+}
+
+- (unsigned)hash
+{
+ unsigned nameHash, storeHash;
+
+ storeHash = (unsigned)[[[self fileStore] storeID] performSelector: @selector(hash)];
+ nameHash = [[self name] hash];
+ return (storeHash ^ nameHash);
+}
+
+- (BOOL)isEqual: (id)anObject
+{
+ if ([anObject isKindOfClass: [self class]]) {
+ Group *otherGroup;
+ id myStoreID, otherStoreID;
+
+ myStoreID = [[self fileStore] storeID];
+ otherGroup = anObject;
+ otherStoreID = [[otherGroup fileStore] storeID];
+
+ if ([myStoreID performSelector: @selector(compare:) withObject: otherStoreID] &&
+ [[self name] isEqualToString: [otherGroup name]])
+ return YES;
+ else
+ return NO;
+ }
+
+ return NO;
+}
+@end
+
+@implementation Group
++ (Group *)groupWithName: (NSString *)aName
+{
+ return [[[self alloc] initWithName: aName] autorelease];
+}
+
++ (Group *)groupWithName: (NSString *)aName andType: (GroupType)aType
+{
+ return [[[self alloc] initWithName: aName andType: aType] autorelease];
+}
+
+- (Group *)initWithName: (NSString *)aName
+{
+ return [self initWithName: aName andType: BRANCH];
+}
+
+- (Group *)initWithName: (NSString *)aName andType: (GroupType)aType;
+{
+ self = [super init];
+
+ [self setName: aName];
+ [self setType: aType];
+ icon = [[NSImage imageNamed: LiNormalGroupIcon] retain];
+ children = [[NSMutableArray alloc] init];
+
+ return self;
+}
+
+- (id)init
+{
+ NSException *myException;
+
+ [self autorelease];
+
+ myException = [NSException exceptionWithName: @"GroupInitFailure"
+ reason: @"[[Group alloc] init] isn't supported."
+ userInfo: nil];
+ [myException raise];
+ return nil;
+}
+
+- (void)dealloc
+{
+ [self setName: nil];
+ [icon release];
+ [children release];
+ [self setFileStore: nil];
+
+ [super dealloc];
+}
+
+- (id)initWithContentsOfFile: (NSString *)aFilename
+{
+ NSArray *groupArray;
+ NSDictionary *groupDict;
+ NSString *groupname;
+
+ groupDict = [NSDictionary dictionaryWithContentsOfFile: aFilename];
+ self = [self initWithName: [groupDict objectForKey: @"name"]];
+
+ groupArray = [groupDict objectForKey: @"children"];
+ for (groupname in groupArray) {
+ Group *newGroup;
+
+ // XXX - should encode type
+ newGroup = [[[Group alloc] initWithName: groupname
+ andType: LEAF] autorelease];
+ [newGroup setIcon: [NSImage imageNamed: LiNormalGroupIcon]];
+ [self addChild: newGroup];
+ }
+ return self;
+}
+
+- (BOOL)writeToFile: (NSString *)aFilename
+{
+ Group *group;
+ NSMutableArray *groupArray;
+ NSMutableDictionary *groupDict;
+
+ groupDict = [NSMutableDictionary dictionary];
+
+ groupArray = [NSMutableArray array];
+ for (group in children) {
+ [groupArray addObject: [group name]];
+ }
+
+ [groupDict setObject: name forKey: @"name"];
+ [groupDict setObject: groupArray forKey: @"children"];
+
+ return [groupDict writeToFile: aFilename atomically: NO];
+}
+
+- (LiFileStore *)fileStore
+{
+ return theFileStore;
+}
+
+- (void)setFileStore: (LiFileStore *)aFileStore
+{
+ [aFileStore retain];
+ [theFileStore release];
+ theFileStore = aFileStore;
+}
+
+- (id)initWithCoder: (NSCoder *)coder
+{
+ NSString *typeString;
+
+ self = [super init];
+
+ if ([coder allowsKeyedCoding]) {
+ [self setName: [coder decodeObjectForKey: @"name"]];
+ typeString = [coder decodeObjectForKey: @"type"];
+ children = [[coder decodeObjectForKey: @"children"] retain];
+ } else {
+ [self setName: [coder decodeObject]];
+ typeString = [coder decodeObject];
+ children = [[coder decodeObject] retain];
+ }
+
+ if ([typeString isEqualToString: @"LEAF"])
+ [self setType: LEAF];
+ else
+ [self setType: BRANCH];
+
+ icon = [[NSImage imageNamed: LiNormalGroupIcon] retain];
+
+ return self;
+}
+
+- (void)encodeWithCoder: (NSCoder *)coder
+{
+ NSString *typeString;
+
+ if ([self type] == LEAF)
+ typeString = @"LEAF";
+ else
+ typeString = @"BRANCH";
+
+ if ([coder allowsKeyedCoding]) {
+ [coder encodeObject: [self name] forKey: @"name"];
+ [coder encodeObject: typeString forKey: @"type"];
+ [coder encodeObject: children forKey: @"children"];
+ } else {
+ [coder encodeObject: [self name]];
+ [coder encodeObject: typeString];
+ [coder encodeObject: children];
+ }
+}
+
+- (NSString *)name
+{
+ return name;
+}
+
+- (GroupType)type
+{
+ return type;
+}
+
+- (void)setName: (NSString *)aName
+{
+ [aName retain];
+ [name release];
+ name = aName;
+}
+
+- (void)setType: (GroupType)aType
+{
+ type = aType;
+}
+
+- (NSImage *)icon
+{
+ return icon;
+}
+
+- (void)setIcon: (NSImage *)anIcon
+{
+ [anIcon retain];
+ [icon release];
+ icon = anIcon;
+}
+
+- (Group *)parent
+{
+ return parent;
+}
+
+- (void)setParent: (Group *)aParent
+{
+ [aParent retain];
+ [parent release];
+ parent = aParent;
+}
+
+- (int)numberOfChildren
+{
+ return [children count];
+}
+
+- (BOOL)hasChild: (id)aChild
+{
+ return [children containsObject: aChild];
+}
+
+- (NSEnumerator *)childEnumerator
+{
+ return [children objectEnumerator];
+}
+
+- (void)addChild: (id)aChild
+{
+ [children addObject: aChild];
+ [aChild setParent: self];
+}
+
+- (void)removeChild: (id)aChild
+{
+ [aChild setParent: nil];
+ [children removeObject: aChild];
+}
+
+- (Group *)childNamed: (NSString *)aName
+{
+ Group *child;
+ int i, numberOfChildren;
+
+ child = nil;
+ numberOfChildren = [children count];
+ for (i = 0; i < numberOfChildren; i++) {
+ child = [children objectAtIndex: i];
+ if ([[child name] isEqualToString: aName])
+ break;
+ }
+ if (i < numberOfChildren)
+ return child;
+ return nil;
+}
+
+- (void)removeChildNamed: (NSString *)aName
+{
+ int i, numberOfChildren;
+
+ numberOfChildren = [children count];
+ for (i = 0; i < numberOfChildren; i++) {
+ Group *child;
+
+ child = [children objectAtIndex: i];
+ if ([[child name] isEqualToString: aName])
+ break;
+ }
+ if (i < numberOfChildren)
+ [children removeObjectAtIndex: i];
+}
+
+- (id)childAtIndex: (int)index
+{
+ return [children objectAtIndex: index];
+}
+
+- (void)removeChildAtIndex: (int)index
+{
+ [children removeObjectAtIndex: index];
+}
+@synthesize children;
+@synthesize theFileStore;
+@end
diff --git a/Liaison/GroupTableDelegate.h b/Liaison/GroupTableDelegate.h
new file mode 100644
index 0000000..d31ef42
--- /dev/null
+++ b/Liaison/GroupTableDelegate.h
@@ -0,0 +1,43 @@
+/* GroupTableDelegate */
+
+@class Group;
+@class FileTableDelegate;
+@class WindowController;
+
+@interface GroupTableDelegate : NSObject
+{
+ IBOutlet NSOutlineView *outlineView;
+ IBOutlet NSTextField *statusLine;
+ IBOutlet WindowController *theWindow;
+ IBOutlet FileTableDelegate *theFileDelegate;
+ IBOutlet NSMenu *theContextMenu;
+
+ Group *theGroup;
+ Group *theSelectedGroup;
+
+ NSEvent *theMouseDownEvent;
+}
+- (IBAction)addGroup:(id)sender;
+
+- (NSSize)minSize;
+- (void)respondToFileStoreChanged: (NSNotification *)aNotification;
+
+- (BOOL)validateAction: (SEL)anAction;
+
+- (void)highlightDefaultGroup;
+@property (retain,getter=group) Group *theGroup;
+@property (retain) NSEvent *theMouseDownEvent;
+@property (retain) NSTextField *statusLine;
+@property (retain) FileTableDelegate *theFileDelegate;
+@property (retain) NSMenu *theContextMenu;
+@property (retain,getter=selectedGroup) Group *theSelectedGroup;
+@property (retain) NSOutlineView *outlineView;
+@property (retain) WindowController *theWindow;
+@end
+
+@interface GroupTableDelegate (Accessors)
+- (Group *)group;
+- (void)setGroup: (Group *)aGroup;
+- (Group *)selectedGroup;
+- (void)setSelectedGroup: (Group *)aGroup;
+@end \ No newline at end of file
diff --git a/Liaison/GroupTableDelegate.m b/Liaison/GroupTableDelegate.m
new file mode 100644
index 0000000..e744534
--- /dev/null
+++ b/Liaison/GroupTableDelegate.m
@@ -0,0 +1,611 @@
+#import "GroupTableDelegate.h"
+
+#import "FileTableDelegate.h"
+#import "Group.h"
+#import "ImageAndTextCell.h"
+#import "WindowController.h"
+
+static NSString *
+myLocalizedString(NSString *aString)
+{
+ return NSLocalizedStringFromTable(aString, @"WindowElements", @"");
+}
+
+@interface GroupTableDelegate (Private)
+- (BOOL)groupIsLibrary: (Group *)aGroup;
+@end
+
+@implementation GroupTableDelegate (LiTableViewDelegate)
+- (void)outlineViewDidBecomeFirstResponder: (NSOutlineView *)anOutlineView
+{
+ [LiLog logAsDebug: @"[GroupTableDelegate becameFirstResponder]"];
+}
+
+- (void)deleteSelectedRowsInOutlineView: (NSOutlineView *)anOutlineView
+{
+ Group *removedGroup;
+ int selectedRow;
+
+ selectedRow = [outlineView selectedRow] - 1;
+
+ removedGroup = [anOutlineView itemAtRow: [anOutlineView selectedRow]];
+ if ([self groupIsLibrary: removedGroup] == NO) {
+ LiFileHandle *file;
+ LiFilter *groupFilter;
+ NSArray *filesInGroup;
+ NSDictionary *removedGroupAttribute;
+ NSString *groupname;
+
+ groupname = [removedGroup name];
+ groupFilter = [LiFilter filterWithAttribute: LiGroupsAttribute
+ compareSelector: @selector(isEqual:)
+ value: groupname];
+
+ filesInGroup = [[removedGroup fileStore] filesMatchingFilter: groupFilter];
+ for (file in filesInGroup) {
+ [file removeFromGroup: groupname];
+ }
+
+ removedGroupAttribute = [NSDictionary dictionaryWithObject: groupname
+ forKey: LiGroupsAttribute];
+ [[[removedGroup fileStore] delegate] removeDefaultAttribute: removedGroupAttribute
+ fromFileStore: [removedGroup fileStore]];
+
+ [[removedGroup parent] removeChild: removedGroup];
+
+ [[removedGroup fileStore] synchronize];
+
+ [outlineView reloadData];
+ [outlineView selectRow: selectedRow byExtendingSelection: NO];
+ }
+}
+
+- (void)mouseDownEvent: (NSEvent *)mouseEvent
+{
+ [LiLog logAsDebug: @"[GroupTableDelegate mouseDownEvent: %@]", mouseEvent];
+ theMouseDownEvent = mouseEvent;
+}
+@end
+
+@implementation GroupTableDelegate (OutlineViewDelegate)
+- (id)outlineView: (NSOutlineView *)outlineView
+ child: (int)index ofItem:(Group *)item
+{
+ if (item == nil)
+ item = theGroup;
+
+ return [[item childAtIndex: index] retain];
+}
+
+- (BOOL)outlineView: (NSOutlineView *)outlineView
+ isItemExpandable: (Group *)item
+{
+ if (item == nil)
+ item = theGroup;
+
+ return ([item type] == BRANCH);
+}
+
+- (int)outlineView: (NSOutlineView *)outlineView
+numberOfChildrenOfItem: (Group *)item
+{
+ if (item == nil)
+ item = theGroup;
+
+ return [item numberOfChildren];
+}
+
+- (id)outlineView: (NSOutlineView *)outlineView
+objectValueForTableColumn: (NSTableColumn *)aTableColumn
+ byItem: (Group *)item
+{
+ if (item == nil)
+ item = theGroup;
+
+ if ([[aTableColumn identifier] isEqualToString: @"name"])
+ return [[item name] retain];
+ return @"nil";
+}
+
+- (void)outlineView: (NSOutlineView *)anOutlineView
+ setObjectValue: (id)anObject
+ forTableColumn: (NSTableColumn *)aTableColumn
+ byItem: (id)anItem
+{
+ NSArray *groupFiles;
+ NSDictionary *renamedGroupAttribute;
+ NSString *oldGroupName, *newGroupName;
+ LiFileHandle *file;
+ LiFilter *groupFilter;
+ Group *renamedGroup;
+
+ renamedGroup = anItem;
+ groupFilter = [LiFilter filterWithAttribute: LiGroupsAttribute
+ compareSelector: @selector(isEqual:)
+ value: [renamedGroup name]];
+ groupFiles = [[renamedGroup fileStore] filesMatchingFilter: groupFilter];
+ oldGroupName = [renamedGroup name];
+ newGroupName = anObject;
+ for (file in groupFiles) {
+ [file renameGroup: oldGroupName toGroup: newGroupName];
+ }
+ [renamedGroup setName: newGroupName];
+
+ renamedGroupAttribute = [NSDictionary dictionaryWithObject: oldGroupName
+ forKey: LiGroupsAttribute];
+ [[[renamedGroup fileStore] delegate] changeDefaultValueForAttribute: renamedGroupAttribute
+ toValue: newGroupName
+ inFileStore: [renamedGroup fileStore]];
+
+ [[renamedGroup fileStore] synchronize];
+ [self setSelectedGroup: renamedGroup];
+}
+
+- (BOOL)outlineView: (NSOutlineView *)outlineView
+shouldEditTableColumn: (NSTableColumn *)tableColumn
+ item: (id)anItem
+{
+ Group *group;
+
+ group = anItem;
+ return [[group fileStore] isEditable];
+}
+
+- (void)outlineView: (NSOutlineView *)outlineView
+ willDisplayCell: (NSCell *)aCell
+ forTableColumn: (NSTableColumn *)aColumn
+ item: (id)theItem
+{
+ if (theItem == nil)
+ return;
+
+ if ([[aColumn identifier] isEqualToString: @"name"]) {
+ [aCell setImage: [[theItem icon] retain]];
+ //[aCell setStringValue: [theItem name]];
+ }
+}
+
+- (BOOL)outlineView: (NSOutlineView *)anOutlineView
+ shouldSelectItem: (id)item
+{
+ return YES;
+}
+
+- (void)outlineViewSelectionDidChange: (NSNotification *)aNotification
+{
+ Group *group;
+
+ [LiLog logAsDebug: @"[GroupTableDelegate outlineViewSelectionDidChange: (Notification)]"];
+ [LiLog indentDebugLog];
+
+ group = [outlineView itemAtRow: [outlineView selectedRow]];
+ [self setSelectedGroup: group];
+
+ [LiLog unindentDebugLog];
+}
+
+- (NSDragOperation)outlineView: (NSOutlineView*)olv
+ validateDrop: (id <NSDraggingInfo>)info
+ proposedItem: (id)anItem
+ proposedChildIndex: (int)childIndex
+{
+ Group *group;
+ NSPasteboard *pboard;
+
+ group = anItem;
+ if (group == nil)
+ return NSDragOperationNone;
+
+ if (childIndex != NSOutlineViewDropOnItemIndex)
+ return NSDragOperationNone;
+
+ if ([[group fileStore] isEditable] == NO)
+ return NSDragOperationNone;
+
+ pboard = [info draggingPasteboard];
+ if ([pboard availableTypeFromArray:
+ [NSArray arrayWithObjects:
+ LiaisonPboardType, NSFilenamesPboardType, nil]])
+ return NSDragOperationCopy;
+
+ return NO;
+}
+
+- (BOOL)outlineView: (NSOutlineView*)olv
+ acceptDrop: (id <NSDraggingInfo>)info
+ item: (id)anItem
+ childIndex: (int)childIndex
+{
+ Group *item;
+ NSPasteboard *pboard;
+ NSString *groupName;
+
+ item = anItem;
+ if ([self groupIsLibrary: item])
+ groupName = nil;
+ else
+ groupName = [item name];
+
+ pboard = [info draggingPasteboard];
+
+ if ([pboard availableTypeFromArray:
+ [NSArray arrayWithObject: LiaisonPboardType]]) {
+ NSArray *theFileList;
+ NSData *theFileListData;
+ LiFileHandle *file;
+
+ theFileListData = [pboard dataForType: LiaisonPboardType];
+ theFileList = [NSKeyedUnarchiver unarchiveObjectWithData:
+ theFileListData];
+ for (file in theFileList) {
+ [file addToGroup: groupName];
+ }
+ } else if ([pboard availableTypeFromArray:
+ [NSArray arrayWithObject: NSFilenamesPboardType]]) {
+ NSArray *pathList;
+
+ pathList = [pboard propertyListForType: NSFilenamesPboardType];
+ if ([pathList count] > 0) {
+ [[item fileStore] addPaths: pathList toGroup: [item name]];
+ return YES;
+ } else
+ return NO;
+ }
+
+ [[item fileStore] synchronize];
+
+ return YES;
+}
+
+- (BOOL)outlineView: (NSOutlineView *)anOutlineView
+ writeItems: (NSArray *)someItems
+ toPasteboard: (NSPasteboard *)aPasteboard
+{
+ Group *item;
+ NSMutableArray *promisePboard;
+
+ promisePboard = [NSMutableArray array];
+
+ for (item in someItems) {
+ [promisePboard addObject: [item name]];
+ }
+
+ if ([promisePboard count] > 0) {
+ NSPoint dragPosition;
+ NSRect imageLocation;
+
+ dragPosition = [outlineView convertPoint: [theMouseDownEvent locationInWindow]
+ fromView: nil];
+ imageLocation.origin = dragPosition;
+ imageLocation.size = NSMakeSize(32,32);
+ [outlineView dragPromisedFilesOfTypes: [NSArray arrayWithObject: @""]
+ fromRect: NSMakeRect(0.0, 0.0, 0.0, 0.0)
+ source: self
+ slideBack: YES event: theMouseDownEvent];
+ return YES;
+ }
+
+ // We always return NO, because we start the drag ourselves, which is lame,
+ // but we have to in order to support promised files.
+ return NO;
+}
+
+/* We get this in response to dragging a directory to another app. */
+- (NSArray *)namesOfPromisedFilesDroppedAtDestination: (NSURL *)dropDestination
+{
+ NSArray *filenames;
+ NSFileManager *defaultManager;
+ NSString *dropDir, *path;
+ int suffix;
+
+ defaultManager = [NSFileManager defaultManager];
+ dropDir = [dropDestination path];
+ for (suffix = 1; suffix <= 100; suffix++) {
+ NSString *myDir;
+
+ if (suffix > 1)
+ myDir = [NSString stringWithFormat: @"DEBUGgroupName %d", suffix];
+ else
+ myDir = @"DEBUGgroupName";
+
+ path = [dropDir stringByAppendingPathComponent: myDir];
+ if ([defaultManager fileExistsAtPath: path] == NO) {
+ break;
+ }
+ }
+
+ filenames = nil;
+ if (suffix <= 100) {
+ [LiLog logAsDebug: @"Create dir: %@", path];
+ if ([defaultManager createDirectoryAtPath: path
+ attributes: nil] == YES)
+ filenames = [NSArray arrayWithObject: path];
+ }
+ return filenames;
+}
+@end
+
+@implementation GroupTableDelegate
+- (id)init
+{
+ NSNotificationCenter *defaultCenter;
+
+ self = [super init];
+
+ [self setGroup: nil];
+
+ // We want to watch for file changes, so we can change our view.
+ defaultCenter = [NSNotificationCenter defaultCenter];
+ [defaultCenter addObserver: self
+ selector: @selector(respondToFileStoreChanged:)
+ name: LiFileStoresChangedNotification
+ object: nil];
+ [defaultCenter addObserver: self
+ selector: @selector(respondToFileStoreChanged:)
+ name: LiFileChangedNotification
+ object: nil];
+
+ return self;
+}
+
+- (void)dealloc
+{
+ NSNotificationCenter *defaultCenter;
+
+ defaultCenter = [NSNotificationCenter defaultCenter];
+ [defaultCenter removeObserver: self];
+
+ [self setGroup: nil];
+ [super dealloc];
+}
+
+- (void)awakeFromNib
+{
+ NSTableColumn *tableColumn;
+ ImageAndTextCell *imageAndTextCell;
+
+ tableColumn = [outlineView tableColumnWithIdentifier: @"name"];
+ imageAndTextCell = [[[ImageAndTextCell alloc] init] autorelease];
+ [imageAndTextCell setEditable: YES];
+ [tableColumn setDataCell: imageAndTextCell];
+
+ [outlineView registerForDraggedTypes:
+ [NSArray arrayWithObjects:
+ LiaisonPboardType, NSFilenamesPboardType, nil]];
+}
+
+- (void)respondToFileStoreChanged: (NSNotification *)aNotification
+{
+ Group *myGroups, *child;
+ LiFileStore *fileStore;
+ NSEnumerator *fsEnum, *groupEnum;
+ NSMutableSet *oldFileStores, *newFileStores;
+ NSString *groupName;
+ BOOL needsRedisplay;
+
+ myGroups = [self group];
+ if (myGroups == nil) {
+ myGroups = [Group groupWithName: @"ALL" andType: BRANCH];
+ [self setGroup: myGroups];
+ }
+ needsRedisplay = NO;
+
+ newFileStores = [NSMutableSet setWithArray: [LiFileStore allFileStores]];
+ oldFileStores = [NSMutableSet set];
+ groupEnum = [myGroups childEnumerator];
+ while ((child = [groupEnum nextObject]) != nil) {
+ [oldFileStores addObject: [child fileStore]];
+ }
+
+ // Remove old file stores.
+ [oldFileStores minusSet: newFileStores];
+ if ([oldFileStores count] > 0)
+ needsRedisplay = YES;
+
+ groupEnum = [oldFileStores objectEnumerator];
+ while ((groupName = [[groupEnum nextObject] name]) != nil) {
+ [LiLog logAsDebug: @"Removing fileStore for %@ from the group list.", groupName];
+ [myGroups removeChildNamed: groupName];
+ }
+
+ fsEnum = [LiFileStore fileStoreEnumerator];
+ while ((fileStore = [fsEnum nextObject]) != nil) {
+ Group *storeGroup;
+ NSArray *allStoreGroups;
+ NSEnumerator *groupEnum;
+
+ allStoreGroups = [fileStore allValuesForAttribute: LiGroupsAttribute];
+ storeGroup = [myGroups childNamed: [fileStore name]];
+ if (storeGroup == nil) {
+ needsRedisplay = YES;
+ storeGroup = [Group groupWithName: [fileStore name] andType: BRANCH];
+
+ [myGroups addChild: storeGroup];
+ } else {
+ NSMutableSet *oldGroups, *newGroups;
+
+ newGroups = [NSMutableSet setWithArray: allStoreGroups];
+ oldGroups = [NSMutableSet set];
+ groupEnum = [storeGroup childEnumerator];
+ while ((child = [groupEnum nextObject]) != nil) {
+ [oldGroups addObject: [child name]];
+ }
+
+ // Remove old groups.
+ [oldGroups minusSet: newGroups];
+ if ([oldGroups count] > 0)
+ needsRedisplay = YES;
+
+ for (groupName in oldGroups) {
+ [LiLog logAsDebug: @"Removing group %@ from group list.", groupName];
+ [storeGroup removeChildNamed: groupName];
+ }
+ }
+
+ // Always set this.
+ [storeGroup setIcon: [fileStore icon]];
+ [storeGroup setFileStore: fileStore];
+
+ // Add new groups.
+ for (groupName in allStoreGroups) {
+ Group *subGroup;
+
+ if ([storeGroup childNamed: groupName] == nil) {
+ needsRedisplay = YES;
+ subGroup = [Group groupWithName: groupName andType: LEAF];
+ [subGroup setFileStore: fileStore];
+ [storeGroup addChild: subGroup];
+ }
+ }
+ }
+
+ if (needsRedisplay) {
+ [outlineView reloadData];
+ }
+}
+
+- (void)highlightDefaultGroup
+{
+ Group *firstGroup;
+
+ firstGroup = [outlineView itemAtRow: 0];
+ [outlineView selectRow: 0 byExtendingSelection: NO];
+}
+
+- (IBAction)addGroup:(id)sender
+{
+ Group *group;
+ NSString *untitledName;
+ unsigned long untitledPrefix;
+
+ [LiLog logAsDebug: @"[GroupTableDelegate addGroup: sender]"];
+ [LiLog indentDebugLog];
+
+ group = [self selectedGroup];
+ if ([[group fileStore] isEditable] == NO) {
+ [LiLog logAsDebug: @"Group %@ isn't editable.", [[group fileStore] name]];
+ }
+
+ if ([[group fileStore] isEditable]) {
+ Group *newGroup;
+ NSDictionary *newGroupAttribute;
+
+ if ([group type] == LEAF)
+ group = [group parent];
+ [LiLog logAsDebug: @"adding to group: %@", [group name]];
+
+ untitledPrefix = 1;
+ untitledName = myLocalizedString(@"LiUntitledGroupName");
+ while ([group childNamed: untitledName] != nil) {
+ untitledPrefix++;
+ untitledName = [NSString stringWithFormat: @"%@ %lu",
+ myLocalizedString(@"LiUntitledGroupName"), untitledPrefix];
+ }
+
+ newGroupAttribute = [NSDictionary dictionaryWithObject: untitledName
+ forKey: LiGroupsAttribute];
+ [[[group fileStore] delegate] addDefaultAttribute: newGroupAttribute
+ toFileStore: [group fileStore]];
+
+ newGroup = [Group groupWithName: untitledName andType: LEAF];
+ [newGroup setFileStore: [group fileStore]];
+ [group addChild: newGroup];
+
+ [outlineView reloadData];
+ [outlineView expandItem: group];
+ }
+
+ [LiLog unindentDebugLog];
+}
+
+- (NSSize)minSize
+{
+ float rowHeight;
+ int numRows;
+
+ [LiLog logAsDebug: @"[GroupTableDelegate minSize]"];
+ rowHeight = [outlineView rowHeight];
+ numRows = [outlineView numberOfRows];
+ return NSMakeSize([outlineView frame].size.width, rowHeight * numRows);
+}
+
+- (BOOL)validateAction: (SEL)anAction
+{
+ //[LiLog logAsDebug: @"[GroupTableDelegate validateAction: %@]", NSStringFromSelector(anAction)];
+ if (anAction == @selector(copy:))
+ return [outlineView numberOfSelectedRows] > 0;
+ else if (anAction == @selector(delete:)) {
+ if ([self groupIsLibrary: [self selectedGroup]])
+ return NO;
+ else
+ return [[[self selectedGroup] fileStore] isEditable];
+ } else if (anAction == @selector(addGroup:)) {
+ return [[[self selectedGroup] fileStore] isEditable];
+ } else
+ return YES;
+}
+
+- (BOOL)validateMenuItem: (NSMenuItem *)anItem
+{
+ return [self validateAction: [anItem action]];
+}
+@synthesize theGroup;
+@synthesize theMouseDownEvent;
+@synthesize theWindow;
+@synthesize theFileDelegate;
+@synthesize theSelectedGroup;
+@synthesize theContextMenu;
+@synthesize statusLine;
+@synthesize outlineView;
+@end
+
+@implementation GroupTableDelegate (Accessors)
+- (Group *)group
+{
+ return theGroup;
+}
+
+- (void)setGroup: (Group *)aGroup
+{
+ [aGroup retain];
+ [theGroup release];
+ theGroup = aGroup;
+
+ [outlineView reloadData];
+ [self highlightDefaultGroup];
+}
+
+- (Group *)selectedGroup
+{
+ return theSelectedGroup;
+}
+
+- (void)setSelectedGroup: (Group *)aGroup
+{
+ [aGroup retain];
+ [theSelectedGroup release];
+ theSelectedGroup = aGroup;
+
+ [theFileDelegate saveSelectionOfTableView: [theFileDelegate tableView]];
+
+ [theFileDelegate setFileStore: [theSelectedGroup fileStore]];
+
+ if ([self groupIsLibrary: theSelectedGroup]) {
+ [theFileDelegate setGroup: nil];
+ } else {
+ [theFileDelegate setGroup: [theSelectedGroup name]];
+ }
+ [theFileDelegate redisplay];
+ [theFileDelegate restoreSelectionToTableView: [theFileDelegate tableView] refresh: YES];
+}
+@end
+
+@implementation GroupTableDelegate (Private)
+- (BOOL)groupIsLibrary: (Group *)aGroup
+{
+ if ([[self group] hasChild: aGroup])
+ return YES;
+ else
+ return NO;
+}
+@end
diff --git a/Liaison/ImageAndTextCell.h b/Liaison/ImageAndTextCell.h
new file mode 100644
index 0000000..5c39369
--- /dev/null
+++ b/Liaison/ImageAndTextCell.h
@@ -0,0 +1,20 @@
+//
+// ImageAndTextCell.h
+//
+// Copyright (c) 2001-2002, Apple. All rights reserved.
+//
+
+@interface ImageAndTextCell : NSTextFieldCell
+{
+@private
+ NSImage *image;
+}
+
+- (void)drawWithFrame: (NSRect)cellFrame inView: (NSView *)controlView;
+- (NSSize)cellSize;
+@end
+
+@interface ImageAndTextCell (Accessors)
+- (NSImage *)image;
+- (void)setImage: (NSImage *)anImage;
+@end \ No newline at end of file
diff --git a/Liaison/ImageAndTextCell.m b/Liaison/ImageAndTextCell.m
new file mode 100644
index 0000000..2a81bb6
--- /dev/null
+++ b/Liaison/ImageAndTextCell.m
@@ -0,0 +1,156 @@
+/*
+ ImageAndTextCell.m
+ Copyright (c) 2001-2002, Apple Computer, Inc., all rights reserved.
+ Author: Chuck Pisula
+
+ Milestones:
+ Initially created 3/1/01
+
+ Subclass of NSTextFieldCell which can display text and an image simultaneously.
+*/
+
+/*
+ IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in
+ consideration of your agreement to the following terms, and your use, installation,
+ modification or redistribution of this Apple software constitutes acceptance of these
+ terms. If you do not agree with these terms, please do not use, install, modify or
+ redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and subject to these
+ terms, Apple grants you a personal, non-exclusive license, under AppleÕs copyrights in
+ this original Apple software (the "Apple Software"), to use, reproduce, modify and
+ redistribute the Apple Software, with or without modifications, in source and/or binary
+ forms; provided that if you redistribute the Apple Software in its entirety and without
+ modifications, you must retain this notice and the following text and disclaimers in all
+ such redistributions of the Apple Software. Neither the name, trademarks, service marks
+ or logos of Apple Computer, Inc. may be used to endorse or promote products derived from
+ the Apple Software without specific prior written permission from Apple. Except as expressly
+ stated in this notice, no other rights or licenses, express or implied, are granted by Apple
+ herein, including but not limited to any patent rights that may be infringed by your
+ derivative works or by other works in which the Apple Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES,
+ EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS
+ USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
+ REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
+ WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
+ OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#import "ImageAndTextCell.h"
+
+#define LEADINGEDGE 2
+
+@implementation ImageAndTextCell
+
+- (void)dealloc
+{
+ [self setImage: nil];
+ [super dealloc];
+}
+
+- copyWithZone: (NSZone *)zone
+{
+ ImageAndTextCell *cell;
+
+ cell = (ImageAndTextCell *)[super copyWithZone: zone];
+ [cell setImage: [self image]];
+ return cell;
+}
+
+- (NSRect)imageFrameForCellFrame: (NSRect)cellFrame
+{
+ if (image != nil) {
+ NSRect imageFrame;
+ imageFrame.size = [image size];
+ imageFrame.origin = cellFrame.origin;
+ imageFrame.origin.x += LEADINGEDGE;
+ imageFrame.origin.y +=
+ ceil((cellFrame.size.height - imageFrame.size.height) / 2);
+ return imageFrame;
+ }
+ else
+ return NSZeroRect;
+}
+
+- (void)editWithFrame: (NSRect)aRect
+ inView: (NSView *)controlView
+ editor: (NSText *)textObj
+ delegate: (id)anObject
+ event: (NSEvent *)theEvent
+{
+ NSRect textFrame, imageFrame;
+
+ NSDivideRect(aRect, &imageFrame, &textFrame,
+ LEADINGEDGE + [image size].width, NSMinXEdge);
+ [super editWithFrame: textFrame inView: controlView editor: textObj delegate: anObject event: theEvent];
+}
+
+- (void)selectWithFrame: (NSRect)aRect
+ inView: (NSView *)controlView
+ editor: (NSText *)textObj
+ delegate: (id)anObject
+ start: (int)selStart
+ length: (int)selLength
+{
+ NSRect textFrame, imageFrame;
+
+ NSDivideRect (aRect, &imageFrame, &textFrame, LEADINGEDGE + [image size].width, NSMinXEdge);
+ [super selectWithFrame: textFrame inView: controlView editor:textObj delegate:anObject start:selStart length:selLength];
+}
+
+- (void)drawWithFrame: (NSRect)cellFrame inView: (NSView *)controlView
+{
+ if (image != nil) {
+ NSSize imageSize;
+ NSRect imageFrame;
+
+ imageSize = [image size];
+ NSDivideRect(cellFrame, &imageFrame, &cellFrame, LEADINGEDGE + imageSize.width, NSMinXEdge);
+ if ([self drawsBackground]) {
+ [[self backgroundColor] set];
+ NSRectFill(imageFrame);
+ }
+ imageFrame.origin.x += LEADINGEDGE;
+ imageFrame.size = imageSize;
+
+ if ([controlView isFlipped])
+ imageFrame.origin.y +=
+ ceil((cellFrame.size.height + imageFrame.size.height) / 2);
+ else
+ imageFrame.origin.y +=
+ ceil((cellFrame.size.height - imageFrame.size.height) / 2);
+
+ [image compositeToPoint: imageFrame.origin operation: NSCompositeSourceOver];
+ }
+ [super drawWithFrame: cellFrame inView: controlView];
+}
+
+- (NSSize)cellSize
+{
+ NSSize cellSize;
+
+ cellSize = [super cellSize];
+ cellSize.width += (image ? [image size].width : 0) + LEADINGEDGE;
+ return cellSize;
+}
+@end
+
+@implementation ImageAndTextCell (Accessors)
+- (NSImage *)image
+{
+ return image;
+}
+
+- (void)setImage: (NSImage *)anImage
+{
+ [anImage retain];
+ [image release];
+ image = anImage;
+}
+@end \ No newline at end of file
diff --git a/Liaison/Images/Add.tiff b/Liaison/Images/Add.tiff
new file mode 100644
index 0000000..79d4e34
--- /dev/null
+++ b/Liaison/Images/Add.tiff
Binary files differ
diff --git a/Liaison/Images/AddFiles.tiff b/Liaison/Images/AddFiles.tiff
new file mode 100644
index 0000000..7fe8e48
--- /dev/null
+++ b/Liaison/Images/AddFiles.tiff
Binary files differ
diff --git a/Liaison/Images/AddGroup.tiff b/Liaison/Images/AddGroup.tiff
new file mode 100644
index 0000000..b8753d7
--- /dev/null
+++ b/Liaison/Images/AddGroup.tiff
Binary files differ
diff --git a/Liaison/Images/Download_Reload.tiff b/Liaison/Images/Download_Reload.tiff
new file mode 100644
index 0000000..f4cf9ab
--- /dev/null
+++ b/Liaison/Images/Download_Reload.tiff
Binary files differ
diff --git a/Liaison/Images/Download_ReloadPressed.tiff b/Liaison/Images/Download_ReloadPressed.tiff
new file mode 100644
index 0000000..bcd6900
--- /dev/null
+++ b/Liaison/Images/Download_ReloadPressed.tiff
Binary files differ
diff --git a/Liaison/Images/Download_Reveal.tiff b/Liaison/Images/Download_Reveal.tiff
new file mode 100644
index 0000000..c090901
--- /dev/null
+++ b/Liaison/Images/Download_Reveal.tiff
Binary files differ
diff --git a/Liaison/Images/Download_RevealPressed.tiff b/Liaison/Images/Download_RevealPressed.tiff
new file mode 100644
index 0000000..13f68a5
--- /dev/null
+++ b/Liaison/Images/Download_RevealPressed.tiff
Binary files differ
diff --git a/Liaison/Images/Download_Stop.tiff b/Liaison/Images/Download_Stop.tiff
new file mode 100644
index 0000000..156869d
--- /dev/null
+++ b/Liaison/Images/Download_Stop.tiff
Binary files differ
diff --git a/Liaison/Images/Download_StopPressed.tiff b/Liaison/Images/Download_StopPressed.tiff
new file mode 100644
index 0000000..c9ee4a1
--- /dev/null
+++ b/Liaison/Images/Download_StopPressed.tiff
Binary files differ
diff --git a/Liaison/Images/File Icons/Liaison.icns b/Liaison/Images/File Icons/Liaison.icns
new file mode 100644
index 0000000..d1b79dc
--- /dev/null
+++ b/Liaison/Images/File Icons/Liaison.icns
Binary files differ
diff --git a/Liaison/Images/LeftSearchCap.tiff b/Liaison/Images/LeftSearchCap.tiff
new file mode 100644
index 0000000..51664e1
--- /dev/null
+++ b/Liaison/Images/LeftSearchCap.tiff
Binary files differ
diff --git a/Liaison/Images/NormalMailbox.tiff b/Liaison/Images/NormalMailbox.tiff
new file mode 100644
index 0000000..351b06b
--- /dev/null
+++ b/Liaison/Images/NormalMailbox.tiff
Binary files differ
diff --git a/Liaison/Images/NormalMailboxLarge.tiff b/Liaison/Images/NormalMailboxLarge.tiff
new file mode 100644
index 0000000..5ebd76d
--- /dev/null
+++ b/Liaison/Images/NormalMailboxLarge.tiff
Binary files differ
diff --git a/Liaison/Images/RemoveFiles.tiff b/Liaison/Images/RemoveFiles.tiff
new file mode 100644
index 0000000..67fbe49
--- /dev/null
+++ b/Liaison/Images/RemoveFiles.tiff
Binary files differ
diff --git a/Liaison/Images/RemoveGroup.tiff b/Liaison/Images/RemoveGroup.tiff
new file mode 100644
index 0000000..dc840ab
--- /dev/null
+++ b/Liaison/Images/RemoveGroup.tiff
Binary files differ
diff --git a/Liaison/Images/RightSearchCap.tiff b/Liaison/Images/RightSearchCap.tiff
new file mode 100644
index 0000000..5672df6
--- /dev/null
+++ b/Liaison/Images/RightSearchCap.tiff
Binary files differ
diff --git a/Liaison/Images/SortAscending.gif b/Liaison/Images/SortAscending.gif
new file mode 100644
index 0000000..4c11dba
--- /dev/null
+++ b/Liaison/Images/SortAscending.gif
Binary files differ
diff --git a/Liaison/Images/SortDescending.gif b/Liaison/Images/SortDescending.gif
new file mode 100644
index 0000000..c95c00d
--- /dev/null
+++ b/Liaison/Images/SortDescending.gif
Binary files differ
diff --git a/Liaison/Images/TrashMailbox.tiff b/Liaison/Images/TrashMailbox.tiff
new file mode 100644
index 0000000..4e6995f
--- /dev/null
+++ b/Liaison/Images/TrashMailbox.tiff
Binary files differ
diff --git a/Liaison/Images/TrashMailboxLarge.tiff b/Liaison/Images/TrashMailboxLarge.tiff
new file mode 100644
index 0000000..ea8fe02
--- /dev/null
+++ b/Liaison/Images/TrashMailboxLarge.tiff
Binary files differ
diff --git a/Liaison/Images/delete.tiff b/Liaison/Images/delete.tiff
new file mode 100644
index 0000000..ea8fe02
--- /dev/null
+++ b/Liaison/Images/delete.tiff
Binary files differ
diff --git a/Liaison/Images/info (italic).tiff b/Liaison/Images/info (italic).tiff
new file mode 100644
index 0000000..435c112
--- /dev/null
+++ b/Liaison/Images/info (italic).tiff
Binary files differ
diff --git a/Liaison/Images/info (plain).tiff b/Liaison/Images/info (plain).tiff
new file mode 100644
index 0000000..3ecf888
--- /dev/null
+++ b/Liaison/Images/info (plain).tiff
Binary files differ
diff --git a/Liaison/Images/quickpick.tiff b/Liaison/Images/quickpick.tiff
new file mode 100644
index 0000000..2cda6de
--- /dev/null
+++ b/Liaison/Images/quickpick.tiff
Binary files differ
diff --git a/Liaison/Images/rendezvous.tiff b/Liaison/Images/rendezvous.tiff
new file mode 100644
index 0000000..f162ef0
--- /dev/null
+++ b/Liaison/Images/rendezvous.tiff
Binary files differ
diff --git a/Liaison/Images/reveal.tiff b/Liaison/Images/reveal.tiff
new file mode 100644
index 0000000..90c6e1b
--- /dev/null
+++ b/Liaison/Images/reveal.tiff
Binary files differ
diff --git a/Liaison/Info.plist b/Liaison/Info.plist
new file mode 100644
index 0000000..3035d74
--- /dev/null
+++ b/Liaison/Info.plist
@@ -0,0 +1,74 @@
+<?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>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleDocumentTypes</key>
+ <array>
+ <dict>
+ <key>CFBundleTypeExtensions</key>
+ <array>
+ <string>liaisonplugin</string>
+ </array>
+ <key>CFBundleTypeMIMETypes</key>
+ <array>
+ <string>application/x-com.kublai.Liaison.plugin</string>
+ </array>
+ <key>CFBundleTypeName</key>
+ <string>Liaison Plugin</string>
+ <key>CFBundleTypeRole</key>
+ <string>None</string>
+ <key>LSTypeIsPackage</key>
+ <true/>
+ </dict>
+ <dict>
+ <key>CFBundleTypeExtensions</key>
+ <array>
+ <string>*</string>
+ </array>
+ <key>CFBundleTypeName</key>
+ <string>Everything</string>
+ <key>CFBundleTypeRole</key>
+ <string>Viewer</string>
+ </dict>
+ </array>
+ <key>CFBundleExecutable</key>
+ <string>Liaison</string>
+ <key>CFBundleGetInfoString</key>
+ <string>version 0.4.2</string>
+ <key>CFBundleIconFile</key>
+ <string>Liaison.icns</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.kublai.Liaison</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>Liaison</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>v0.4.2</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleURLTypes</key>
+ <array>
+ <dict>
+ <key>CFBundleURLName</key>
+ <string>Liaison URL</string>
+ <key>CFBundleURLSchemes</key>
+ <array>
+ <string>liaison</string>
+ </array>
+ </dict>
+ </array>
+ <key>CFBundleVersion</key>
+ <string>0.4.2</string>
+ <key>NSAppleScriptEnabled</key>
+ <string>YES</string>
+ <key>NSMainNibFile</key>
+ <string>MainMenu</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+</dict>
+</plist>
diff --git a/Liaison/InspectorController.h b/Liaison/InspectorController.h
new file mode 100644
index 0000000..d17c05f
--- /dev/null
+++ b/Liaison/InspectorController.h
@@ -0,0 +1,24 @@
+/* InspectorController */
+
+@class LiInspectorView;
+
+@interface InspectorController : NSObject
+{
+ IBOutlet NSTabView *theTabView;
+ IBOutlet NSView *theDefaultTabView;
+ IBOutlet NSWindow *theWindow;
+
+ NSMutableDictionary *theInspectorViews;
+ LiFileHandle *theFile;
+}
+- (LiInspectorView *)inspectorViewForIdentifier: (NSString *)anIdentifier;
+- (NSRect)minWindowFrame;
+- (void)resizeWindow;
+
+- (void)setFile: (LiFileHandle *)aFile;
+@property (retain) LiFileHandle *theFile;
+@property (retain) NSWindow *theWindow;
+@property (retain) NSMutableDictionary *theInspectorViews;
+@property (retain) NSTabView *theTabView;
+@property (retain) NSView *theDefaultTabView;
+@end
diff --git a/Liaison/InspectorController.m b/Liaison/InspectorController.m
new file mode 100644
index 0000000..453574c
--- /dev/null
+++ b/Liaison/InspectorController.m
@@ -0,0 +1,250 @@
+#import "InspectorController.h"
+
+#import "PluginManager.h"
+
+@implementation InspectorController (WindowDelegate)
+- (NSRect)windowWillUseStandardFrame: (NSWindow *)aWindow
+ defaultFrame: (NSRect)defaultFrame
+{
+ return [self minWindowFrame];
+}
+@end
+
+@implementation InspectorController
+- (id)init
+{
+ self = [super init];
+
+ theInspectorViews = [[NSMutableDictionary alloc] init];
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [theInspectorViews release];
+ [super dealloc];
+}
+
+- (LiInspectorView *)viewForDefault
+{
+ LiInspectorView *view;
+
+ view = [[[LiInspectorView alloc] init] autorelease];
+ [view setIdentifier: @"nothing"];
+ [view setName: @"Nada"];
+ [view setImage: nil];
+ [view setIsHorizontallyResizable: NO];
+ [view setIsVerticallyResizable: NO];
+ [view setView: theDefaultTabView];
+ [view setViewSize: [[view view] frame].size];
+
+ return view;
+}
+
+- (void)awakeFromNib
+{
+ NSEnumerator *pluginEnum;
+ NSObject <LiInspectorPlugin> *plugin;
+
+ [self setFile: nil];
+
+ // Load our default, "nothing's there" view.
+ [theInspectorViews setObject: [self viewForDefault] forKey: @"DEFAULT"];
+
+ // Load plug-in views.
+ pluginEnum = [[[PluginManager defaultManager] inspectorPlugins] objectEnumerator];
+ while ((plugin = [pluginEnum nextObject]) != nil) {
+ LiInspectorView *view;
+ NSEnumerator *viewEnum;
+
+ viewEnum = [[plugin allInspectorViews] objectEnumerator];
+ while ((view = [viewEnum nextObject]) != nil) {
+ NSString *identifier;
+
+ identifier = [NSString stringWithFormat: @"%@:%@",
+ NSStringFromClass([plugin class]), [view identifier]];
+ [theInspectorViews setObject: view forKey: identifier];
+ }
+ }
+}
+
+- (LiInspectorView *)inspectorViewForIdentifier: (NSString *)anIdentifier
+{
+ return [theInspectorViews objectForKey: anIdentifier];
+}
+
+- (void)showInspectorViewWithIdentifier: (NSString *)anIdentifier
+{
+ LiInspectorView *view;
+
+ view = [self inspectorViewForIdentifier: anIdentifier];
+ if (view != nil) {
+ NSTabViewItem *tab;
+
+ tab = [[NSTabViewItem alloc] initWithIdentifier: anIdentifier];
+ [tab autorelease];
+ [tab setLabel: [view name]];
+ [tab setView: [view view]];
+ [theTabView addTabViewItem: tab];
+ }
+}
+
+- (void)removeInspectorViewWithIdentifier: (NSString *)anIdentifier
+{
+ LiInspectorView *view;
+
+ view = [self inspectorViewForIdentifier: anIdentifier];
+}
+
+- (NSRect)minWindowFrame
+{
+ LiInspectorView *inspectorView;
+ NSTabViewItem *tab;
+ NSRect windowFrame;
+ NSSize viewSize;
+ float newHeight, newWidth;
+
+ tab = [theTabView selectedTabViewItem];
+ inspectorView = [self inspectorViewForIdentifier: [tab identifier]];
+
+ viewSize = [inspectorView viewSize];
+ newHeight = viewSize.height + 40;
+ newWidth = viewSize.width;
+
+ windowFrame = [NSWindow contentRectForFrameRect: [theWindow frame]
+ styleMask: [theWindow styleMask]];
+ windowFrame.origin.y += windowFrame.size.height;
+ windowFrame.origin.y -= newHeight;
+ windowFrame.size.height = newHeight;
+ windowFrame.size.width = newWidth;
+
+ windowFrame = [NSWindow frameRectForContentRect: windowFrame
+ styleMask: [theWindow styleMask]];
+
+ return windowFrame;
+}
+
+- (void)resizeWindow
+{
+ LiInspectorView *inspectorView;
+ NSTabViewItem *tab;
+ NSRect minWindowFrame, windowFrame;
+ BOOL displayGrowBox = NO;
+
+ tab = [theTabView selectedTabViewItem];
+ inspectorView = [self inspectorViewForIdentifier: [tab identifier]];
+
+ windowFrame = [theWindow frame];
+ minWindowFrame = [self minWindowFrame];
+ if ([inspectorView isVerticallyResizable] &&
+ [inspectorView isHorizontallyResizable]) {
+ // Resize nothing.
+ if (windowFrame.size.width < minWindowFrame.size.width)
+ windowFrame.size.width = minWindowFrame.size.width;
+ if (windowFrame.size.height < minWindowFrame.size.height)
+ windowFrame.size.height = minWindowFrame.size.height;
+
+ displayGrowBox = YES;
+ } else {
+ if ([inspectorView isVerticallyResizable]) {
+ // Resize the width.
+ windowFrame.size.width = minWindowFrame.size.width;
+ if (windowFrame.size.height < minWindowFrame.size.height)
+ windowFrame.size.height = minWindowFrame.size.height;
+
+ displayGrowBox = YES;
+ } else if ([inspectorView isHorizontallyResizable]) {
+ // Resize the height.
+ windowFrame.origin.y = minWindowFrame.origin.y;
+ windowFrame.size.height = minWindowFrame.size.height;
+ if (windowFrame.size.width < minWindowFrame.size.width)
+ windowFrame.size.width = minWindowFrame.size.width;
+
+ displayGrowBox = YES;
+ } else
+ windowFrame = minWindowFrame;
+ }
+ [theWindow setFrame: windowFrame display: YES animate: YES];
+ [theWindow setShowsResizeIndicator: displayGrowBox];
+}
+
+- (void)setFile: (LiFileHandle *)aFile
+{
+ NSEnumerator *pluginEnum, *tabEnum;
+ NSMutableArray *shownTabs;
+ NSString *identifier;
+ NSTabViewItem *tab;
+ NSObject <LiInspectorPlugin> *plugin;
+
+ shownTabs = [NSMutableArray array];
+ pluginEnum = [[[PluginManager defaultManager] inspectorPlugins] objectEnumerator];
+ while ((plugin = [pluginEnum nextObject]) != nil) {
+ LiInspectorView *view;
+ NSEnumerator *viewEnum;
+
+ viewEnum = [[plugin inspectorViewsForFile: aFile] objectEnumerator];
+ while ((view = [viewEnum nextObject]) != nil) {
+ identifier = [NSString stringWithFormat: @"%@:%@",
+ NSStringFromClass([plugin class]), [view identifier]];
+ [shownTabs addObject: identifier];
+ }
+
+ NS_DURING
+ [plugin setFile: aFile];
+ NS_HANDLER
+ [LiLog logAsError:
+ @"Inspector plugin: %@ couldn't handle '%@': %@, %@",
+ NSStringFromClass([plugin class]),
+ [aFile filename], [localException name],
+ [localException reason]];
+ NS_ENDHANDLER
+ }
+
+ tabEnum = [[theTabView tabViewItems] objectEnumerator];
+ while ((tab = [tabEnum nextObject]) != nil) {
+ if ([shownTabs containsObject: [tab identifier]])
+ [shownTabs removeObject: [tab identifier]];
+ else
+ [theTabView removeTabViewItem: tab];
+ }
+
+ for (identifier in shownTabs) {
+ [self showInspectorViewWithIdentifier: identifier];
+ }
+
+ if ([theTabView numberOfTabViewItems] == 0) {
+ [LiLog logAsDebug: @"No tabs in view."];
+
+ [self showInspectorViewWithIdentifier: @"DEFAULT"];
+ }
+}
+@synthesize theWindow;
+@synthesize theInspectorViews;
+@synthesize theTabView;
+@synthesize theDefaultTabView;
+@synthesize theFile;
+@end
+
+@implementation InspectorController (TabViewDelegate)
+- (void)tabView: (NSTabView *)tabView didSelectTabViewItem: (NSTabViewItem *)tabViewItem
+{
+ LiInspectorView *inspectorView;
+ NSTabViewItem *tab;
+ NSSize minSize, maxSize;
+
+ tab = [theTabView selectedTabViewItem];
+ inspectorView = [self inspectorViewForIdentifier: [tab identifier]];
+
+ minSize = [self minWindowFrame].size;
+ maxSize = NSMakeSize(800.0, 800.0);
+ if ([inspectorView isHorizontallyResizable] == NO)
+ maxSize.width = minSize.width;
+ if ([inspectorView isVerticallyResizable] == NO)
+ maxSize.height = minSize.height;
+
+ [theWindow setMinSize: minSize];
+ [theWindow setMaxSize: maxSize];
+ [self resizeWindow];
+}
+@end \ No newline at end of file
diff --git a/Liaison/LiDataTranslator.h b/Liaison/LiDataTranslator.h
new file mode 100644
index 0000000..f988d71
--- /dev/null
+++ b/Liaison/LiDataTranslator.h
@@ -0,0 +1,27 @@
+//
+// LiDataTranslator.h
+// Liaison
+//
+// Created by Brian Cully on Thu Sep 25 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+#define LiPlainEncoding 0x10101010
+
+#import <Foundation/Foundation.h>
+
+@interface LiDataTranslator : NSObject
++ (LiDataTranslator *)sharedTranslator;
+- (NSData *)decodeData: (NSData *)someData;
+- (NSData *)encodeData: (NSData *)someData;
+@end
+
+@interface NSData (LiDataTranslator)
+- (NSData *)decodedData;
+- (NSData *)encodedData;
+@end
+
+@interface NSDictionary (LiDataTranslator)
++ (NSDictionary *)dictionaryWithEncodedData: (NSData *)someData;
+- (NSData *)encodedData;
+@end \ No newline at end of file
diff --git a/Liaison/LiDataTranslator.m b/Liaison/LiDataTranslator.m
new file mode 100644
index 0000000..4481c21
--- /dev/null
+++ b/Liaison/LiDataTranslator.m
@@ -0,0 +1,120 @@
+//
+// LiDataTranslator.m
+// Liaison
+//
+// Created by Brian Cully on Thu Sep 25 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+#import "LiDataTranslator.h"
+
+@implementation LiDataTranslator
+static LiDataTranslator *sharedTranslator = nil;
++ (LiDataTranslator *)sharedTranslator
+{
+ if (sharedTranslator == nil)
+ sharedTranslator = [[self alloc] init];
+ return sharedTranslator;
+}
+
+- (NSData *)decodeData: (NSData *)someData
+{
+ NSData *myData;
+ NSRange headerRange;
+ unsigned long encoding, length;
+ unsigned dataLen;
+
+ myData = nil;
+ headerRange.location = 0;
+ headerRange.length = sizeof(encoding) + sizeof(length);
+ dataLen = [someData length];
+ if (dataLen > headerRange.length) {
+
+ dataLen -= headerRange.length;
+ [someData getBytes: &encoding range: NSMakeRange(0, sizeof(encoding))];
+ [someData getBytes: &length range: NSMakeRange(sizeof(encoding), sizeof(length))];
+ encoding = ntohl(encoding);
+ length = ntohl(length);
+
+ if (dataLen >= length) {
+ myData = [someData subdataWithRange: NSMakeRange(headerRange.length, length)];
+ }
+ }
+
+ return myData;
+}
+
+- (NSData *)encodeData: (NSData *)someData
+{
+ NSMutableData *myData;
+
+ myData = nil;
+ if (someData != nil) {
+ unsigned long encoding, length;
+
+ myData = [NSMutableData data];
+ encoding = htonl(LiPlainEncoding);
+ length = htonl([someData length]);
+
+ [myData appendBytes: &encoding length: sizeof(encoding)];
+ [myData appendBytes: &length length: sizeof(length)];
+ [myData appendData: someData];
+ }
+ return myData;
+}
+@end
+
+@implementation NSData (LiDataTranslator)
+- (NSData *)decodedData
+{
+ return [[LiDataTranslator sharedTranslator] decodeData: self];
+}
+
+- (NSData *)encodedData
+{
+ return [[LiDataTranslator sharedTranslator] encodeData: self];
+}
+@end
+
+@implementation NSDictionary (LiDataTranslator)
++ (NSDictionary *)dictionaryWithEncodedData: (NSData *)someData
+{
+ NSData *myData;
+ NSDictionary *msg;
+
+ msg = nil;
+ myData = [someData decodedData];
+ if (myData != nil) {
+ NSString *errorString;
+
+ errorString = nil;
+ msg = [NSPropertyListSerialization propertyListFromData: myData
+ mutabilityOption: NSPropertyListImmutable
+ format: NULL
+ errorDescription: &errorString];
+ if (errorString != nil || [msg isKindOfClass: [NSDictionary class]] == NO) {
+ [msg release];
+ [errorString release];
+ return nil;
+ }
+ }
+ return msg;
+}
+
+- (NSData *)encodedData
+{
+ NSData *myData;
+ NSString *errorString;
+
+ errorString = nil;
+ myData = [NSPropertyListSerialization dataFromPropertyList: self
+ format: NSPropertyListBinaryFormat_v1_0
+ errorDescription: &errorString];
+ if (errorString != nil) {
+ [LiLog logAsError: @"Couldn't serialize dictionary: %@.", errorString];
+ [errorString release];
+ return nil;
+ }
+ return [myData encodedData];
+}
+@end \ No newline at end of file
diff --git a/Liaison/LiScrolLView.h b/Liaison/LiScrolLView.h
new file mode 100644
index 0000000..149e34b
--- /dev/null
+++ b/Liaison/LiScrolLView.h
@@ -0,0 +1,10 @@
+//
+// LiScrolLView.h
+// Liaison
+//
+// Created by Brian Cully on Sat May 10 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+@interface LiScrollView : NSScrollView
+@end
diff --git a/Liaison/LiScrolLView.m b/Liaison/LiScrolLView.m
new file mode 100644
index 0000000..2441ed0
--- /dev/null
+++ b/Liaison/LiScrolLView.m
@@ -0,0 +1,34 @@
+//
+// LiScrolLView.m
+// Liaison
+//
+// Created by Brian Cully on Sat May 10 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+#import "LiScrolLView.h"
+
+@implementation LiScrollView
+- (void)validateScrollers;
+{
+ BOOL horizVisible, vertVisible;
+ NSSize mySize, contentSize;
+
+ mySize = [self frame].size;
+ contentSize = [[self documentView] frame].size;
+
+ vertVisible = mySize.height < contentSize.height;
+ horizVisible = mySize.width < contentSize.width;
+
+ [self setHasVerticalScroller: vertVisible];
+ [self setHasHorizontalScroller: horizVisible];
+
+ return;
+}
+
+- (void)drawRect: (NSRect)aRect
+{
+ [self validateScrollers];
+ [super drawRect: aRect];
+}
+@end
diff --git a/Liaison/LiTableView.h b/Liaison/LiTableView.h
new file mode 100644
index 0000000..5b3869b
--- /dev/null
+++ b/Liaison/LiTableView.h
@@ -0,0 +1,2 @@
+@interface LiTableView : NSTableView
+@end
diff --git a/Liaison/LiTableView.m b/Liaison/LiTableView.m
new file mode 100644
index 0000000..9fa66f6
--- /dev/null
+++ b/Liaison/LiTableView.m
@@ -0,0 +1,166 @@
+#import "LiTableView.h"
+
+@implementation LiTableView
++ (void)load
+{
+ [self poseAsClass: [NSTableView class]];
+}
+
+- methodSignatureForSelector: (SEL)aSelector
+{
+ id signature;
+
+ signature = [super methodSignatureForSelector: aSelector];
+ if (signature == nil) {
+ NSString *selString;
+
+ selString = NSStringFromSelector(aSelector);
+
+ if ([selString isEqualToString: @"validateMenuItem:"]) {
+ signature = [[self delegate] methodSignatureForSelector: @selector(validateMenuItem:)];
+ } else if ([selString isEqualToString: @"delete:"]) {
+ if ([self isKindOfClass: [NSOutlineView class]])
+ signature = [[self dataSource] methodSignatureForSelector: @selector(deleteSelectedRowsInOutlineView:)];
+ else
+ signature = [[self dataSource] methodSignatureForSelector:
+ @selector(deleteSelectedRowsInTableView:)];
+ } else if ([[self delegate] respondsToSelector: aSelector])
+ signature = [[self delegate] methodSignatureForSelector: aSelector];
+ }
+ return signature;
+}
+
+- (void)forwardInvocation: (NSInvocation *)anInvocation
+{
+ NSString *selString;
+
+ selString = NSStringFromSelector([anInvocation selector]);
+ if ([selString isEqualToString: @"validateMenuItem:"]) {
+ [anInvocation setTarget: [self delegate]];
+ [anInvocation invoke];
+ } else if ([selString isEqualToString: @"delete:"]) {
+ [anInvocation setTarget: [self dataSource]];
+ if ([self isKindOfClass: [NSOutlineView class]]) {
+ [anInvocation setSelector: @selector(deleteSelectedRowsInOutlineView:)];
+ } else {
+ [anInvocation setSelector: @selector(deleteSelectedRowsInTableView:)];
+ }
+
+ [anInvocation setArgument: &self atIndex: 2];
+ [anInvocation invoke];
+ } else if ([[self delegate] respondsToSelector: [anInvocation selector]]) {
+ [anInvocation setTarget: [self delegate]];
+ [anInvocation invoke];
+ } else
+ [super forwardInvocation: anInvocation];
+}
+
+- (BOOL)respondsToSelector: (SEL)aSelector
+{
+ NSString *selString;
+
+ if ([super respondsToSelector: aSelector])
+ return YES;
+
+ selString = NSStringFromSelector(aSelector);
+ if ([selString isEqualToString: @"validateMenuItem:"]) {
+ return [[self delegate] respondsToSelector: aSelector];
+ } else if ([selString isEqualToString: @"delete:"]) {
+ if ([self isKindOfClass: [NSOutlineView class]]) {
+ return [[self dataSource] respondsToSelector:
+ @selector(deleteSelectedRowsInOutlineView:)];
+ } else {
+ return [[self dataSource] respondsToSelector: @selector(deleteSelectedRowsInTableView:)];
+ }
+ }
+ return [[self delegate] respondsToSelector: aSelector];
+}
+
+- (void)keyDown: (NSEvent *)theEvent
+{
+ NSString *keyString;
+ unichar keyChar;
+
+ keyString = [theEvent charactersIgnoringModifiers];
+ keyChar = [keyString characterAtIndex: 0];
+ switch (keyChar) {
+ case 0177: // Delete Key
+ case NSDeleteFunctionKey:
+ case NSDeleteCharFunctionKey: {
+ SEL selector;
+
+ if ([self isKindOfClass: [NSOutlineView class]]) {
+ selector = @selector(deleteSelectedRowsInOutlineView:);
+ } else {
+ selector = @selector(deleteSelectedRowsInTableView:);
+ }
+ if ([self selectedRow] >= 0 &&
+ [[self dataSource] respondsToSelector: selector]) {
+ [[self dataSource] performSelector: selector
+ withObject: self];
+ }
+ break;
+ } default:
+ [super keyDown: theEvent];
+ }
+}
+
+- (BOOL)becomeFirstResponder
+{
+ BOOL rc;
+
+ rc = [super becomeFirstResponder];
+ if (rc == YES) {
+ SEL selector;
+
+ if ([self isKindOfClass: [NSOutlineView class]])
+ selector = @selector(outlineViewDidBecomeFirstResponder:);
+ else
+ selector = @selector(tableViewDidBecomeFirstResponder:);
+
+ if ([[self delegate] respondsToSelector: selector])
+ [[self delegate] performSelector: selector
+ withObject: self];
+ }
+ return rc;
+}
+
+- (void)mouseDown: (NSEvent *)theEvent
+{
+ if ([[self delegate] respondsToSelector: @selector(mouseDownEvent:)])
+ [[self delegate] performSelector: @selector(mouseDownEvent:)
+ withObject: theEvent];
+ [super mouseDown: theEvent];
+}
+
+- (unsigned int)draggingSourceOperationMaskForLocal: (BOOL)isLocal
+{
+ if (isLocal)
+ return NSDragOperationPrivate;
+ else
+ return NSDragOperationCopy | NSDragOperationLink | NSDragOperationGeneric;
+}
+
+- (void)textDidEndEditing: (NSNotification *)aNotification
+{
+ int keyPressed;
+
+ keyPressed = [[[aNotification userInfo] objectForKey: @"NSTextMovement"] intValue];
+ if (keyPressed == NSReturnTextMovement) {
+ NSMutableDictionary *newUserInfo;
+ NSNotification *newNotification;
+
+ newUserInfo = [NSMutableDictionary dictionaryWithDictionary:
+ [aNotification userInfo]];
+ [newUserInfo setObject: [NSNumber numberWithInt:0]
+ forKey: @"NSTextMovement"];
+ newNotification = [NSNotification notificationWithName: [aNotification name]
+ object: [aNotification object]
+ userInfo: newUserInfo];
+ [super textDidEndEditing: newNotification];
+
+ [[self window] makeFirstResponder: self];
+ } else
+ [super textDidEndEditing: aNotification];
+}
+@end \ No newline at end of file
diff --git a/Liaison/Liaison.h b/Liaison/Liaison.h
new file mode 100644
index 0000000..6732b7d
--- /dev/null
+++ b/Liaison/Liaison.h
@@ -0,0 +1,30 @@
+/*
+ * Liaison.h
+ * Liaison
+ *
+ * Created by Brian Cully on Fri Feb 14 2003.
+ * Copyright (c) 2003 Brian Cully. All rights reserved.
+ *
+ * -----
+ *
+ * This header file contains common #defines for ipc and other
+ * "configurable" resources.
+ */
+
+// We're basically just a front end to this.
+#import <LiPlugin/LiPlugin.h>
+// That uses this.
+#import <Cocoa/Cocoa.h>
+
+// Icons in the browser outline view.
+#define LiLocalLibraryIcon @"local.tiff"
+#define LiRendezvousGroupIcon @"rendezvous.tiff"
+#define LiNormalGroupIcon @"quickpick.tiff"
+#define LiNetworkGroupIcon @"Network (Small).tiff"
+
+// For internal drag-and-drop.
+#define LiaisonPboardType @"LiaisonPboardType"
+
+// Load extensions and class posers.
+#import "NSFileHandleExtensions.h"
+#import "LiTableView.h" \ No newline at end of file
diff --git a/Liaison/Liaison.scriptSuite b/Liaison/Liaison.scriptSuite
new file mode 100644
index 0000000..255b1a9
--- /dev/null
+++ b/Liaison/Liaison.scriptSuite
@@ -0,0 +1,32 @@
+<?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>AppleEventCode</key>
+ <string>Lisn</string>
+ <key>Classes</key>
+ <dict>
+ <key>NSApplication</key>
+ <dict>
+ <key>AppleEventCode</key>
+ <string>capp</string>
+ <key>Superclass</key>
+ <string>NSCoreSuite.NSApplication</string>
+ <key>ToManyRelationships</key>
+ <dict>
+ <key>orderedFileStores</key>
+ <dict>
+ <key>AppleEventCode</key>
+ <string>LiFS</string>
+ <key>ReadOnly</key>
+ <string>YES</string>
+ <key>Type</key>
+ <string>LiBackend.LiFileStore</string>
+ </dict>
+ </dict>
+ </dict>
+ </dict>
+ <key>Name</key>
+ <string>Liaison</string>
+</dict>
+</plist>
diff --git a/Liaison/Liaison.xcodeproj/project.pbxproj b/Liaison/Liaison.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..26e71de
--- /dev/null
+++ b/Liaison/Liaison.xcodeproj/project.pbxproj
@@ -0,0 +1,936 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 42;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 286D15B30CDD52F0006463CC /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 286D15B20CDD52F0006463CC /* Info.plist */; };
+ F7D3988C0546870000BD181E /* LiBackend.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = F7D6588204F3576900000104 /* LiBackend.framework */; };
+ F7D3988D0546870000BD181E /* LiPlugin.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = F7D6588304F3576900000104 /* LiPlugin.framework */; };
+ F7D398900546870000BD181E /* Built In Functions.liaisonplugin in CopyFiles */ = {isa = PBXBuildFile; fileRef = F7D6588604F3579700000104 /* Built In Functions.liaisonplugin */; };
+ F7D398920546870000BD181E /* FileTableDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = F790696104EF48AB00000104 /* FileTableDelegate.h */; };
+ F7D398930546870000BD181E /* LiTableView.h in Headers */ = {isa = PBXBuildFile; fileRef = F790696604EF48AB00000104 /* LiTableView.h */; };
+ F7D398940546870000BD181E /* Downloader.h in Headers */ = {isa = PBXBuildFile; fileRef = F790696904EF48AB00000104 /* Downloader.h */; };
+ F7D398950546870000BD181E /* DownloadStatusView.h in Headers */ = {isa = PBXBuildFile; fileRef = F790696A04EF48AB00000104 /* DownloadStatusView.h */; };
+ F7D398960546870000BD181E /* CopyController.h in Headers */ = {isa = PBXBuildFile; fileRef = F790696B04EF48AB00000104 /* CopyController.h */; };
+ F7D398970546870000BD181E /* LoadPanelController.h in Headers */ = {isa = PBXBuildFile; fileRef = F790696D04EF48AB00000104 /* LoadPanelController.h */; };
+ F7D398980546870000BD181E /* DownloadManager.h in Headers */ = {isa = PBXBuildFile; fileRef = F790696E04EF48AB00000104 /* DownloadManager.h */; };
+ F7D398990546870000BD181E /* ApplicationController.h in Headers */ = {isa = PBXBuildFile; fileRef = F790697104EF48AB00000104 /* ApplicationController.h */; };
+ F7D3989A0546870000BD181E /* FlippedBox.h in Headers */ = {isa = PBXBuildFile; fileRef = F790697304EF48AB00000104 /* FlippedBox.h */; };
+ F7D3989B0546870000BD181E /* LiScrolLView.h in Headers */ = {isa = PBXBuildFile; fileRef = F790697404EF48AB00000104 /* LiScrolLView.h */; };
+ F7D3989C0546870000BD181E /* ImageAndTextCell.h in Headers */ = {isa = PBXBuildFile; fileRef = F790697604EF48AB00000104 /* ImageAndTextCell.h */; };
+ F7D3989D0546870000BD181E /* InspectorController.h in Headers */ = {isa = PBXBuildFile; fileRef = F790697804EF48AB00000104 /* InspectorController.h */; };
+ F7D3989E0546870000BD181E /* GroupTableDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = F790697904EF48AB00000104 /* GroupTableDelegate.h */; };
+ F7D3989F0546870000BD181E /* ClientManager.h in Headers */ = {isa = PBXBuildFile; fileRef = F790697C04EF48AB00000104 /* ClientManager.h */; };
+ F7D398A00546870000BD181E /* Liaison.h in Headers */ = {isa = PBXBuildFile; fileRef = F790697D04EF48AB00000104 /* Liaison.h */; };
+ F7D398A10546870000BD181E /* Group.h in Headers */ = {isa = PBXBuildFile; fileRef = F790698204EF48AC00000104 /* Group.h */; };
+ F7D398A20546870000BD181E /* WindowController.h in Headers */ = {isa = PBXBuildFile; fileRef = F79069B904EF4A2700000104 /* WindowController.h */; };
+ F7D398A30546870000BD181E /* ServerManager.h in Headers */ = {isa = PBXBuildFile; fileRef = F79069BD04EF4A2700000104 /* ServerManager.h */; };
+ F7D398A40546870000BD181E /* RenManager.h in Headers */ = {isa = PBXBuildFile; fileRef = F79069BE04EF4A2700000104 /* RenManager.h */; };
+ F7D398A50546870000BD181E /* PreferencesController.h in Headers */ = {isa = PBXBuildFile; fileRef = F79069BF04EF4A2700000104 /* PreferencesController.h */; };
+ F7D398A60546870000BD181E /* WriterThread.h in Headers */ = {isa = PBXBuildFile; fileRef = F79069C004EF4A2700000104 /* WriterThread.h */; };
+ F7D398A70546870000BD181E /* RenIPC.h in Headers */ = {isa = PBXBuildFile; fileRef = F79069C104EF4A2700000104 /* RenIPC.h */; };
+ F7D398A80546870000BD181E /* NIBConnector.h in Headers */ = {isa = PBXBuildFile; fileRef = F79069C604EF4A2700000104 /* NIBConnector.h */; };
+ F7D398A90546870000BD181E /* ViewOptionsController.h in Headers */ = {isa = PBXBuildFile; fileRef = F79069C804EF4A2700000104 /* ViewOptionsController.h */; };
+ F7D398AA0546870000BD181E /* PluginManager.h in Headers */ = {isa = PBXBuildFile; fileRef = F79069CE04EF4A2700000104 /* PluginManager.h */; };
+ F7D398AB0546870000BD181E /* WriterThreadPool.h in Headers */ = {isa = PBXBuildFile; fileRef = F79069CF04EF4A2700000104 /* WriterThreadPool.h */; };
+ F7D398AC0546870000BD181E /* NSFileHandleExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = F75ACF7304EF59C800000104 /* NSFileHandleExtensions.h */; };
+ F7D398AD0546870000BD181E /* FindController.h in Headers */ = {isa = PBXBuildFile; fileRef = F7A5B69904F841A900000104 /* FindController.h */; };
+ F7D398AE0546870000BD181E /* LiDataTranslator.h in Headers */ = {isa = PBXBuildFile; fileRef = F74B98C80523F102000001C7 /* LiDataTranslator.h */; };
+ F7D398B00546870000BD181E /* Liaison.scriptSuite in Resources */ = {isa = PBXBuildFile; fileRef = F79069A504EF48BA00000104 /* Liaison.scriptSuite */; };
+ F7D398B10546870000BD181E /* Liaison.scriptTerminology in Resources */ = {isa = PBXBuildFile; fileRef = F79069A904EF48C800000104 /* Liaison.scriptTerminology */; };
+ F7D398B20546870000BD181E /* MainMenu.nib in Resources */ = {isa = PBXBuildFile; fileRef = 29B97318FDCFA39411CA2CEA /* MainMenu.nib */; };
+ F7D398B30546870000BD181E /* LoadPanel.nib in Resources */ = {isa = PBXBuildFile; fileRef = F79069AD04EF48C800000104 /* LoadPanel.nib */; };
+ F7D398B40546870000BD181E /* CopyPanel.nib in Resources */ = {isa = PBXBuildFile; fileRef = F79069AF04EF48C800000104 /* CopyPanel.nib */; };
+ F7D398B50546870000BD181E /* PreferencesWindow.nib in Resources */ = {isa = PBXBuildFile; fileRef = F79069B104EF48C800000104 /* PreferencesWindow.nib */; };
+ F7D398B60546870000BD181E /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; };
+ F7D398B70546870000BD181E /* WindowElements.strings in Resources */ = {isa = PBXBuildFile; fileRef = F79069A704EF48C800000104 /* WindowElements.strings */; };
+ F7D398B80546870000BD181E /* Liaison Help in Resources */ = {isa = PBXBuildFile; fileRef = F73ECD6904F6BF0A00000104 /* Liaison Help */; };
+ F7D398B90546870000BD181E /* Liaison.icns in Resources */ = {isa = PBXBuildFile; fileRef = F790693604EF484300000104 /* Liaison.icns */; };
+ F7D398BA0546870000BD181E /* Add.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F790692A04EF484300000104 /* Add.tiff */; };
+ F7D398BB0546870000BD181E /* AddFiles.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F790692B04EF484300000104 /* AddFiles.tiff */; };
+ F7D398BC0546870000BD181E /* AddGroup.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F790692C04EF484300000104 /* AddGroup.tiff */; };
+ F7D398BD0546870000BD181E /* delete.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F790692D04EF484300000104 /* delete.tiff */; };
+ F7D398BE0546870000BD181E /* Download_Reload.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F790692E04EF484300000104 /* Download_Reload.tiff */; };
+ F7D398BF0546870000BD181E /* Download_ReloadPressed.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F790692F04EF484300000104 /* Download_ReloadPressed.tiff */; };
+ F7D398C00546870000BD181E /* Download_Reveal.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F790693004EF484300000104 /* Download_Reveal.tiff */; };
+ F7D398C10546870000BD181E /* Download_RevealPressed.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F790693104EF484300000104 /* Download_RevealPressed.tiff */; };
+ F7D398C20546870000BD181E /* Download_Stop.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F790693204EF484300000104 /* Download_Stop.tiff */; };
+ F7D398C30546870000BD181E /* Download_StopPressed.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F790693304EF484300000104 /* Download_StopPressed.tiff */; };
+ F7D398C40546870000BD181E /* info (italic).tiff in Resources */ = {isa = PBXBuildFile; fileRef = F790693704EF484300000104 /* info (italic).tiff */; };
+ F7D398C50546870000BD181E /* info (plain).tiff in Resources */ = {isa = PBXBuildFile; fileRef = F790693804EF484300000104 /* info (plain).tiff */; };
+ F7D398C60546870000BD181E /* LeftSearchCap.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F790693904EF484300000104 /* LeftSearchCap.tiff */; };
+ F7D398C70546870000BD181E /* NormalMailbox.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F790693A04EF484300000104 /* NormalMailbox.tiff */; };
+ F7D398C80546870000BD181E /* NormalMailboxLarge.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F790693B04EF484300000104 /* NormalMailboxLarge.tiff */; };
+ F7D398C90546870000BD181E /* quickpick.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F790693C04EF484300000104 /* quickpick.tiff */; };
+ F7D398CA0546870000BD181E /* RemoveFiles.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F790693D04EF484300000104 /* RemoveFiles.tiff */; };
+ F7D398CB0546870000BD181E /* RemoveGroup.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F790693E04EF484300000104 /* RemoveGroup.tiff */; };
+ F7D398CC0546870000BD181E /* rendezvous.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F790693F04EF484300000104 /* rendezvous.tiff */; };
+ F7D398CD0546870000BD181E /* reveal.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F790694004EF484300000104 /* reveal.tiff */; };
+ F7D398CE0546870000BD181E /* RightSearchCap.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F790694104EF484300000104 /* RightSearchCap.tiff */; };
+ F7D398CF0546870000BD181E /* SortAscending.gif in Resources */ = {isa = PBXBuildFile; fileRef = F790694204EF484300000104 /* SortAscending.gif */; };
+ F7D398D00546870000BD181E /* SortDescending.gif in Resources */ = {isa = PBXBuildFile; fileRef = F790694304EF484300000104 /* SortDescending.gif */; };
+ F7D398D10546870000BD181E /* TrashMailbox.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F790694404EF484300000104 /* TrashMailbox.tiff */; };
+ F7D398D20546870000BD181E /* TrashMailboxLarge.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F790694504EF484300000104 /* TrashMailboxLarge.tiff */; };
+ F7D398D40546870000BD181E /* LoadPanelController.m in Sources */ = {isa = PBXBuildFile; fileRef = F790696204EF48AB00000104 /* LoadPanelController.m */; };
+ F7D398D50546870000BD181E /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F790696304EF48AB00000104 /* main.m */; };
+ F7D398D60546870000BD181E /* FlippedBox.m in Sources */ = {isa = PBXBuildFile; fileRef = F790696404EF48AB00000104 /* FlippedBox.m */; };
+ F7D398D70546870000BD181E /* ApplicationController.m in Sources */ = {isa = PBXBuildFile; fileRef = F790696504EF48AB00000104 /* ApplicationController.m */; };
+ F7D398D80546870000BD181E /* LiTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = F790696804EF48AB00000104 /* LiTableView.m */; };
+ F7D398D90546870000BD181E /* DownloadStatusView.m in Sources */ = {isa = PBXBuildFile; fileRef = F790696C04EF48AB00000104 /* DownloadStatusView.m */; };
+ F7D398DA0546870000BD181E /* ClientManager.m in Sources */ = {isa = PBXBuildFile; fileRef = F790697004EF48AB00000104 /* ClientManager.m */; };
+ F7D398DB0546870000BD181E /* ImageAndTextCell.m in Sources */ = {isa = PBXBuildFile; fileRef = F790697204EF48AB00000104 /* ImageAndTextCell.m */; };
+ F7D398DC0546870000BD181E /* Downloader.m in Sources */ = {isa = PBXBuildFile; fileRef = F790697504EF48AB00000104 /* Downloader.m */; };
+ F7D398DD0546870000BD181E /* Group.m in Sources */ = {isa = PBXBuildFile; fileRef = F790697704EF48AB00000104 /* Group.m */; };
+ F7D398DE0546870000BD181E /* InspectorController.m in Sources */ = {isa = PBXBuildFile; fileRef = F790697A04EF48AB00000104 /* InspectorController.m */; };
+ F7D398DF0546870000BD181E /* FileTableDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F790697B04EF48AB00000104 /* FileTableDelegate.m */; };
+ F7D398E00546870000BD181E /* LiScrolLView.m in Sources */ = {isa = PBXBuildFile; fileRef = F790697E04EF48AC00000104 /* LiScrolLView.m */; };
+ F7D398E10546870000BD181E /* DownloadManager.m in Sources */ = {isa = PBXBuildFile; fileRef = F790697F04EF48AC00000104 /* DownloadManager.m */; };
+ F7D398E20546870000BD181E /* CopyController.m in Sources */ = {isa = PBXBuildFile; fileRef = F790698004EF48AC00000104 /* CopyController.m */; };
+ F7D398E30546870000BD181E /* GroupTableDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F790698104EF48AC00000104 /* GroupTableDelegate.m */; };
+ F7D398E40546870000BD181E /* ViewOptionsController.m in Sources */ = {isa = PBXBuildFile; fileRef = F79069C504EF4A2700000104 /* ViewOptionsController.m */; };
+ F7D398E50546870000BD181E /* PluginManager.m in Sources */ = {isa = PBXBuildFile; fileRef = F79069C704EF4A2700000104 /* PluginManager.m */; };
+ F7D398E60546870000BD181E /* WindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = F79069C904EF4A2700000104 /* WindowController.m */; };
+ F7D398E70546870000BD181E /* PreferencesController.m in Sources */ = {isa = PBXBuildFile; fileRef = F79069CA04EF4A2700000104 /* PreferencesController.m */; };
+ F7D398E80546870000BD181E /* WriterThreadPool.m in Sources */ = {isa = PBXBuildFile; fileRef = F79069CB04EF4A2700000104 /* WriterThreadPool.m */; };
+ F7D398E90546870000BD181E /* WriterThread.m in Sources */ = {isa = PBXBuildFile; fileRef = F79069CC04EF4A2700000104 /* WriterThread.m */; };
+ F7D398EA0546870000BD181E /* RenManager.m in Sources */ = {isa = PBXBuildFile; fileRef = F79069CD04EF4A2700000104 /* RenManager.m */; };
+ F7D398EB0546870000BD181E /* NIBConnector.m in Sources */ = {isa = PBXBuildFile; fileRef = F79069D004EF4A2700000104 /* NIBConnector.m */; };
+ F7D398EC0546870000BD181E /* ServerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = F79069D104EF4A2700000104 /* ServerManager.m */; };
+ F7D398ED0546870000BD181E /* NSFileHandleExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = F75ACF7404EF59C800000104 /* NSFileHandleExtensions.m */; };
+ F7D398EE0546870000BD181E /* FindController.m in Sources */ = {isa = PBXBuildFile; fileRef = F7A5B69A04F841A900000104 /* FindController.m */; };
+ F7D398EF0546870000BD181E /* NSException+LiDebugging.m in Sources */ = {isa = PBXBuildFile; fileRef = F7C329D30514615A00000103 /* NSException+LiDebugging.m */; };
+ F7D398F00546870000BD181E /* LiDataTranslator.m in Sources */ = {isa = PBXBuildFile; fileRef = F74B98C90523F102000001C7 /* LiDataTranslator.m */; };
+ F7D398F20546870000BD181E /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
+ F7D398F30546870000BD181E /* LiBackend.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F7D6588204F3576900000104 /* LiBackend.framework */; };
+ F7D398F40546870000BD181E /* LiPlugin.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F7D6588304F3576900000104 /* LiPlugin.framework */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ F7D3988B0546870000BD181E /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ F7D3988C0546870000BD181E /* LiBackend.framework in CopyFiles */,
+ F7D3988D0546870000BD181E /* LiPlugin.framework in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F7D3988F0546870000BD181E /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 13;
+ files = (
+ F7D398900546870000BD181E /* Built In Functions.liaisonplugin in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 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>"; };
+ 286D15B20CDD52F0006463CC /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; 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>"; };
+ F73ECD1304F6BEE000000104 /* English */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = folder; name = English; path = "English.lproj/Liaison Help"; sourceTree = "<group>"; };
+ F74B98C80523F102000001C7 /* LiDataTranslator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LiDataTranslator.h; sourceTree = "<group>"; };
+ F74B98C90523F102000001C7 /* LiDataTranslator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LiDataTranslator.m; sourceTree = "<group>"; };
+ F75ACF7304EF59C800000104 /* NSFileHandleExtensions.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = NSFileHandleExtensions.h; sourceTree = "<group>"; };
+ F75ACF7404EF59C800000104 /* NSFileHandleExtensions.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = NSFileHandleExtensions.m; sourceTree = "<group>"; };
+ F790692A04EF484300000104 /* Add.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = Add.tiff; sourceTree = "<group>"; };
+ F790692B04EF484300000104 /* AddFiles.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = AddFiles.tiff; sourceTree = "<group>"; };
+ F790692C04EF484300000104 /* AddGroup.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = AddGroup.tiff; sourceTree = "<group>"; };
+ F790692D04EF484300000104 /* delete.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = delete.tiff; sourceTree = "<group>"; };
+ F790692E04EF484300000104 /* Download_Reload.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = Download_Reload.tiff; sourceTree = "<group>"; };
+ F790692F04EF484300000104 /* Download_ReloadPressed.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = Download_ReloadPressed.tiff; sourceTree = "<group>"; };
+ F790693004EF484300000104 /* Download_Reveal.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = Download_Reveal.tiff; sourceTree = "<group>"; };
+ F790693104EF484300000104 /* Download_RevealPressed.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = Download_RevealPressed.tiff; sourceTree = "<group>"; };
+ F790693204EF484300000104 /* Download_Stop.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = Download_Stop.tiff; sourceTree = "<group>"; };
+ F790693304EF484300000104 /* Download_StopPressed.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = Download_StopPressed.tiff; sourceTree = "<group>"; };
+ F790693604EF484300000104 /* Liaison.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = Liaison.icns; sourceTree = "<group>"; };
+ F790693704EF484300000104 /* info (italic).tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "info (italic).tiff"; sourceTree = "<group>"; };
+ F790693804EF484300000104 /* info (plain).tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = "info (plain).tiff"; sourceTree = "<group>"; };
+ F790693904EF484300000104 /* LeftSearchCap.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = LeftSearchCap.tiff; sourceTree = "<group>"; };
+ F790693A04EF484300000104 /* NormalMailbox.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = NormalMailbox.tiff; sourceTree = "<group>"; };
+ F790693B04EF484300000104 /* NormalMailboxLarge.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = NormalMailboxLarge.tiff; sourceTree = "<group>"; };
+ F790693C04EF484300000104 /* quickpick.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = quickpick.tiff; sourceTree = "<group>"; };
+ F790693D04EF484300000104 /* RemoveFiles.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = RemoveFiles.tiff; sourceTree = "<group>"; };
+ F790693E04EF484300000104 /* RemoveGroup.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = RemoveGroup.tiff; sourceTree = "<group>"; };
+ F790693F04EF484300000104 /* rendezvous.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = rendezvous.tiff; sourceTree = "<group>"; };
+ F790694004EF484300000104 /* reveal.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = reveal.tiff; sourceTree = "<group>"; };
+ F790694104EF484300000104 /* RightSearchCap.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = RightSearchCap.tiff; sourceTree = "<group>"; };
+ F790694204EF484300000104 /* SortAscending.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = SortAscending.gif; sourceTree = "<group>"; };
+ F790694304EF484300000104 /* SortDescending.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = SortDescending.gif; sourceTree = "<group>"; };
+ F790694404EF484300000104 /* TrashMailbox.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = TrashMailbox.tiff; sourceTree = "<group>"; };
+ F790694504EF484300000104 /* TrashMailboxLarge.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = TrashMailboxLarge.tiff; sourceTree = "<group>"; };
+ F790696104EF48AB00000104 /* FileTableDelegate.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = FileTableDelegate.h; sourceTree = "<group>"; };
+ F790696204EF48AB00000104 /* LoadPanelController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = LoadPanelController.m; sourceTree = "<group>"; };
+ F790696304EF48AB00000104 /* main.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+ F790696404EF48AB00000104 /* FlippedBox.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = FlippedBox.m; sourceTree = "<group>"; };
+ F790696504EF48AB00000104 /* ApplicationController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = ApplicationController.m; sourceTree = "<group>"; };
+ F790696604EF48AB00000104 /* LiTableView.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = LiTableView.h; sourceTree = "<group>"; };
+ F790696804EF48AB00000104 /* LiTableView.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = LiTableView.m; sourceTree = "<group>"; };
+ F790696904EF48AB00000104 /* Downloader.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = Downloader.h; sourceTree = "<group>"; };
+ F790696A04EF48AB00000104 /* DownloadStatusView.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DownloadStatusView.h; sourceTree = "<group>"; };
+ F790696B04EF48AB00000104 /* CopyController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = CopyController.h; sourceTree = "<group>"; };
+ F790696C04EF48AB00000104 /* DownloadStatusView.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = DownloadStatusView.m; sourceTree = "<group>"; };
+ F790696D04EF48AB00000104 /* LoadPanelController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = LoadPanelController.h; sourceTree = "<group>"; };
+ F790696E04EF48AB00000104 /* DownloadManager.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DownloadManager.h; sourceTree = "<group>"; };
+ F790697004EF48AB00000104 /* ClientManager.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = ClientManager.m; sourceTree = "<group>"; };
+ F790697104EF48AB00000104 /* ApplicationController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = ApplicationController.h; sourceTree = "<group>"; };
+ F790697204EF48AB00000104 /* ImageAndTextCell.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = ImageAndTextCell.m; sourceTree = "<group>"; };
+ F790697304EF48AB00000104 /* FlippedBox.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = FlippedBox.h; sourceTree = "<group>"; };
+ F790697404EF48AB00000104 /* LiScrolLView.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = LiScrolLView.h; sourceTree = "<group>"; };
+ F790697504EF48AB00000104 /* Downloader.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = Downloader.m; sourceTree = "<group>"; };
+ F790697604EF48AB00000104 /* ImageAndTextCell.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = ImageAndTextCell.h; sourceTree = "<group>"; };
+ F790697704EF48AB00000104 /* Group.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = Group.m; sourceTree = "<group>"; };
+ F790697804EF48AB00000104 /* InspectorController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = InspectorController.h; sourceTree = "<group>"; };
+ F790697904EF48AB00000104 /* GroupTableDelegate.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = GroupTableDelegate.h; sourceTree = "<group>"; };
+ F790697A04EF48AB00000104 /* InspectorController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = InspectorController.m; sourceTree = "<group>"; };
+ F790697B04EF48AB00000104 /* FileTableDelegate.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = FileTableDelegate.m; sourceTree = "<group>"; };
+ F790697C04EF48AB00000104 /* ClientManager.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = ClientManager.h; sourceTree = "<group>"; };
+ F790697D04EF48AB00000104 /* Liaison.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = Liaison.h; sourceTree = "<group>"; };
+ F790697E04EF48AC00000104 /* LiScrolLView.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = LiScrolLView.m; sourceTree = "<group>"; };
+ F790697F04EF48AC00000104 /* DownloadManager.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = DownloadManager.m; sourceTree = "<group>"; };
+ F790698004EF48AC00000104 /* CopyController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = CopyController.m; sourceTree = "<group>"; };
+ F790698104EF48AC00000104 /* GroupTableDelegate.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = GroupTableDelegate.m; sourceTree = "<group>"; };
+ F790698204EF48AC00000104 /* Group.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = Group.h; sourceTree = "<group>"; };
+ F79069A504EF48BA00000104 /* Liaison.scriptSuite */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = Liaison.scriptSuite; sourceTree = "<group>"; };
+ F79069A804EF48C800000104 /* English */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/WindowElements.strings; sourceTree = "<group>"; };
+ F79069AA04EF48C800000104 /* English */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = English; path = English.lproj/Liaison.scriptTerminology; sourceTree = "<group>"; };
+ F79069AE04EF48C800000104 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/LoadPanel.nib; sourceTree = "<group>"; };
+ F79069B004EF48C800000104 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/CopyPanel.nib; sourceTree = "<group>"; };
+ F79069B204EF48C800000104 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/PreferencesWindow.nib; sourceTree = "<group>"; };
+ F79069B904EF4A2700000104 /* WindowController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = WindowController.h; sourceTree = "<group>"; };
+ F79069BD04EF4A2700000104 /* ServerManager.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = ServerManager.h; sourceTree = "<group>"; };
+ F79069BE04EF4A2700000104 /* RenManager.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = RenManager.h; sourceTree = "<group>"; };
+ F79069BF04EF4A2700000104 /* PreferencesController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = PreferencesController.h; sourceTree = "<group>"; };
+ F79069C004EF4A2700000104 /* WriterThread.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = WriterThread.h; sourceTree = "<group>"; };
+ F79069C104EF4A2700000104 /* RenIPC.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = RenIPC.h; sourceTree = "<group>"; };
+ F79069C504EF4A2700000104 /* ViewOptionsController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = ViewOptionsController.m; sourceTree = "<group>"; };
+ F79069C604EF4A2700000104 /* NIBConnector.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = NIBConnector.h; sourceTree = "<group>"; };
+ F79069C704EF4A2700000104 /* PluginManager.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = PluginManager.m; sourceTree = "<group>"; };
+ F79069C804EF4A2700000104 /* ViewOptionsController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = ViewOptionsController.h; sourceTree = "<group>"; };
+ F79069C904EF4A2700000104 /* WindowController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = WindowController.m; sourceTree = "<group>"; };
+ F79069CA04EF4A2700000104 /* PreferencesController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = PreferencesController.m; sourceTree = "<group>"; };
+ F79069CB04EF4A2700000104 /* WriterThreadPool.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = WriterThreadPool.m; sourceTree = "<group>"; };
+ F79069CC04EF4A2700000104 /* WriterThread.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = WriterThread.m; sourceTree = "<group>"; };
+ F79069CD04EF4A2700000104 /* RenManager.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = RenManager.m; sourceTree = "<group>"; };
+ F79069CE04EF4A2700000104 /* PluginManager.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = PluginManager.h; sourceTree = "<group>"; };
+ F79069CF04EF4A2700000104 /* WriterThreadPool.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = WriterThreadPool.h; sourceTree = "<group>"; };
+ F79069D004EF4A2700000104 /* NIBConnector.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = NIBConnector.m; sourceTree = "<group>"; };
+ F79069D104EF4A2700000104 /* ServerManager.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = ServerManager.m; sourceTree = "<group>"; };
+ F79069EC04EF4A8C00000104 /* chef */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = chef; path = chef.lproj/WindowElements.strings; sourceTree = "<group>"; };
+ F79069ED04EF4A9500000104 /* chef */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = chef; path = chef.lproj/LoadPanel.nib; sourceTree = "<group>"; };
+ F79069EE04EF4A9C00000104 /* chef */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = chef; path = chef.lproj/CopyPanel.nib; sourceTree = "<group>"; };
+ F79069EF04EF4AA300000104 /* chef */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = chef; path = chef.lproj/PreferencesWindow.nib; sourceTree = "<group>"; };
+ F79069F004EF4AAB00000104 /* chef */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = chef; path = chef.lproj/MainMenu.nib; sourceTree = "<group>"; };
+ F79069F104EF4AB300000104 /* chef */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = chef; path = chef.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+ F7A5B69904F841A900000104 /* FindController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FindController.h; sourceTree = "<group>"; };
+ F7A5B69A04F841A900000104 /* FindController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FindController.m; sourceTree = "<group>"; };
+ F7C329D30514615A00000103 /* NSException+LiDebugging.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSException+LiDebugging.m"; sourceTree = "<group>"; };
+ F7D398F60546870000BD181E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ F7D398F70546870000BD181E /* Liaison.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Liaison.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ F7D6588204F3576900000104 /* LiBackend.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LiBackend.framework; path = ../Frameworks/build/Development/LiBackend.framework; sourceTree = "<group>"; };
+ F7D6588304F3576900000104 /* LiPlugin.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LiPlugin.framework; path = ../Frameworks/build/Development/LiPlugin.framework; sourceTree = "<group>"; };
+ F7D6588604F3579700000104 /* Built In Functions.liaisonplugin */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "Built In Functions.liaisonplugin"; path = "../Plugins/BuiltInFunctions/build/Development/Built In Functions.liaisonplugin"; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ F7D398F10546870000BD181E /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F7D398F20546870000BD181E /* Cocoa.framework in Frameworks */,
+ F7D398F30546870000BD181E /* LiBackend.framework in Frameworks */,
+ F7D398F40546870000BD181E /* LiPlugin.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 080E96DDFE201D6D7F000001 /* Classes */ = {
+ isa = PBXGroup;
+ children = (
+ F7C329D10514613300000103 /* Debugging */,
+ F75ACE9404EF50D500000104 /* Main Window */,
+ F7A5B69804F8417E00000104 /* Find Window */,
+ F75ACEA304EF514B00000104 /* Inspector Window */,
+ F75ACEA204EF513800000104 /* Prefs Window */,
+ F7906A0304EF4FE000000104 /* View Options Window */,
+ F75ACEA104EF512000000104 /* NIBConnector */,
+ F7906A0404EF500D00000104 /* Plugin Manager */,
+ F75ACEA004EF510900000104 /* Network Models */,
+ F75ACEAF04EF517E00000104 /* Cocoa Extensions */,
+ );
+ name = Classes;
+ sourceTree = "<group>";
+ };
+ 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */,
+ );
+ name = "Linked Frameworks";
+ sourceTree = "<group>";
+ };
+ 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 29B97325FDCFA39411CA2CEA /* Foundation.framework */,
+ 29B97324FDCFA39411CA2CEA /* AppKit.framework */,
+ );
+ name = "Other Frameworks";
+ sourceTree = "<group>";
+ };
+ 19C28FACFE9D520D11CA2CBB /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ F7D398F70546870000BD181E /* Liaison.app */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 29B97314FDCFA39411CA2CEA /* Liaison */ = {
+ isa = PBXGroup;
+ children = (
+ 080E96DDFE201D6D7F000001 /* Classes */,
+ 29B97315FDCFA39411CA2CEA /* Other Sources */,
+ 29B97317FDCFA39411CA2CEA /* Resources */,
+ 286D15B20CDD52F0006463CC /* Info.plist */,
+ F790690F04EF46F200000104 /* Plugins */,
+ 29B97323FDCFA39411CA2CEA /* Frameworks */,
+ 19C28FACFE9D520D11CA2CBB /* Products */,
+ F7D398F60546870000BD181E /* Info.plist */,
+ );
+ name = Liaison;
+ sourceTree = "<group>";
+ };
+ 29B97315FDCFA39411CA2CEA /* Other Sources */ = {
+ isa = PBXGroup;
+ children = (
+ F790697D04EF48AB00000104 /* Liaison.h */,
+ F790696304EF48AB00000104 /* main.m */,
+ );
+ name = "Other Sources";
+ sourceTree = "<group>";
+ };
+ 29B97317FDCFA39411CA2CEA /* Resources */ = {
+ isa = PBXGroup;
+ children = (
+ F79069A504EF48BA00000104 /* Liaison.scriptSuite */,
+ F79069A904EF48C800000104 /* Liaison.scriptTerminology */,
+ F79069A704EF48C800000104 /* WindowElements.strings */,
+ F79069AD04EF48C800000104 /* LoadPanel.nib */,
+ F79069AF04EF48C800000104 /* CopyPanel.nib */,
+ F79069B104EF48C800000104 /* PreferencesWindow.nib */,
+ 29B97318FDCFA39411CA2CEA /* MainMenu.nib */,
+ 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */,
+ F790692904EF484300000104 /* Images */,
+ F73ECD6904F6BF0A00000104 /* Liaison Help */,
+ );
+ name = Resources;
+ sourceTree = "<group>";
+ };
+ 29B97323FDCFA39411CA2CEA /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ F7D6588204F3576900000104 /* LiBackend.framework */,
+ F7D6588304F3576900000104 /* LiPlugin.framework */,
+ 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */,
+ 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+ F75ACE9404EF50D500000104 /* Main Window */ = {
+ isa = PBXGroup;
+ children = (
+ F790697104EF48AB00000104 /* ApplicationController.h */,
+ F790696504EF48AB00000104 /* ApplicationController.m */,
+ F79069B904EF4A2700000104 /* WindowController.h */,
+ F79069C904EF4A2700000104 /* WindowController.m */,
+ F790696104EF48AB00000104 /* FileTableDelegate.h */,
+ F790697B04EF48AB00000104 /* FileTableDelegate.m */,
+ F790696D04EF48AB00000104 /* LoadPanelController.h */,
+ F790696204EF48AB00000104 /* LoadPanelController.m */,
+ F790698204EF48AC00000104 /* Group.h */,
+ F790697704EF48AB00000104 /* Group.m */,
+ F790697904EF48AB00000104 /* GroupTableDelegate.h */,
+ F790698104EF48AC00000104 /* GroupTableDelegate.m */,
+ );
+ name = "Main Window";
+ sourceTree = "<group>";
+ };
+ F75ACEA004EF510900000104 /* Network Models */ = {
+ isa = PBXGroup;
+ children = (
+ F79069C104EF4A2700000104 /* RenIPC.h */,
+ F74B98C80523F102000001C7 /* LiDataTranslator.h */,
+ F74B98C90523F102000001C7 /* LiDataTranslator.m */,
+ F79069BE04EF4A2700000104 /* RenManager.h */,
+ F79069CD04EF4A2700000104 /* RenManager.m */,
+ F790697C04EF48AB00000104 /* ClientManager.h */,
+ F790697004EF48AB00000104 /* ClientManager.m */,
+ F79069BD04EF4A2700000104 /* ServerManager.h */,
+ F79069D104EF4A2700000104 /* ServerManager.m */,
+ F790696904EF48AB00000104 /* Downloader.h */,
+ F790697504EF48AB00000104 /* Downloader.m */,
+ F790696E04EF48AB00000104 /* DownloadManager.h */,
+ F790697F04EF48AC00000104 /* DownloadManager.m */,
+ F790696B04EF48AB00000104 /* CopyController.h */,
+ F790698004EF48AC00000104 /* CopyController.m */,
+ F790696A04EF48AB00000104 /* DownloadStatusView.h */,
+ F790696C04EF48AB00000104 /* DownloadStatusView.m */,
+ );
+ name = "Network Models";
+ sourceTree = "<group>";
+ };
+ F75ACEA104EF512000000104 /* NIBConnector */ = {
+ isa = PBXGroup;
+ children = (
+ F79069C604EF4A2700000104 /* NIBConnector.h */,
+ F79069D004EF4A2700000104 /* NIBConnector.m */,
+ );
+ name = NIBConnector;
+ sourceTree = "<group>";
+ };
+ F75ACEA204EF513800000104 /* Prefs Window */ = {
+ isa = PBXGroup;
+ children = (
+ F79069BF04EF4A2700000104 /* PreferencesController.h */,
+ F79069CA04EF4A2700000104 /* PreferencesController.m */,
+ );
+ name = "Prefs Window";
+ sourceTree = "<group>";
+ };
+ F75ACEA304EF514B00000104 /* Inspector Window */ = {
+ isa = PBXGroup;
+ children = (
+ F790697804EF48AB00000104 /* InspectorController.h */,
+ F790697A04EF48AB00000104 /* InspectorController.m */,
+ );
+ name = "Inspector Window";
+ sourceTree = "<group>";
+ };
+ F75ACEAF04EF517E00000104 /* Cocoa Extensions */ = {
+ isa = PBXGroup;
+ children = (
+ F75ACF7704EF5D2600000104 /* NSFileHandleExtensions */,
+ F75ACF7904EF5D3B00000104 /* LiTableView */,
+ F75ACF7B04EF5D4D00000104 /* LiScrollView */,
+ F75ACF7A04EF5D4500000104 /* ImageAndTextCell */,
+ F75ACF7804EF5D3400000104 /* FlippedBox */,
+ );
+ name = "Cocoa Extensions";
+ sourceTree = "<group>";
+ };
+ F75ACF7704EF5D2600000104 /* NSFileHandleExtensions */ = {
+ isa = PBXGroup;
+ children = (
+ F75ACF7304EF59C800000104 /* NSFileHandleExtensions.h */,
+ F75ACF7404EF59C800000104 /* NSFileHandleExtensions.m */,
+ F79069CF04EF4A2700000104 /* WriterThreadPool.h */,
+ F79069CB04EF4A2700000104 /* WriterThreadPool.m */,
+ F79069C004EF4A2700000104 /* WriterThread.h */,
+ F79069CC04EF4A2700000104 /* WriterThread.m */,
+ );
+ name = NSFileHandleExtensions;
+ sourceTree = "<group>";
+ };
+ F75ACF7804EF5D3400000104 /* FlippedBox */ = {
+ isa = PBXGroup;
+ children = (
+ F790697304EF48AB00000104 /* FlippedBox.h */,
+ F790696404EF48AB00000104 /* FlippedBox.m */,
+ );
+ name = FlippedBox;
+ sourceTree = "<group>";
+ };
+ F75ACF7904EF5D3B00000104 /* LiTableView */ = {
+ isa = PBXGroup;
+ children = (
+ F790696604EF48AB00000104 /* LiTableView.h */,
+ F790696804EF48AB00000104 /* LiTableView.m */,
+ );
+ name = LiTableView;
+ sourceTree = "<group>";
+ };
+ F75ACF7A04EF5D4500000104 /* ImageAndTextCell */ = {
+ isa = PBXGroup;
+ children = (
+ F790697604EF48AB00000104 /* ImageAndTextCell.h */,
+ F790697204EF48AB00000104 /* ImageAndTextCell.m */,
+ );
+ name = ImageAndTextCell;
+ sourceTree = "<group>";
+ };
+ F75ACF7B04EF5D4D00000104 /* LiScrollView */ = {
+ isa = PBXGroup;
+ children = (
+ F790697404EF48AB00000104 /* LiScrolLView.h */,
+ F790697E04EF48AC00000104 /* LiScrolLView.m */,
+ );
+ name = LiScrollView;
+ sourceTree = "<group>";
+ };
+ F790690F04EF46F200000104 /* Plugins */ = {
+ isa = PBXGroup;
+ children = (
+ F7D6588604F3579700000104 /* Built In Functions.liaisonplugin */,
+ );
+ name = Plugins;
+ sourceTree = "<group>";
+ };
+ F790692904EF484300000104 /* Images */ = {
+ isa = PBXGroup;
+ children = (
+ F790693404EF484300000104 /* File Icons */,
+ F790692A04EF484300000104 /* Add.tiff */,
+ F790692B04EF484300000104 /* AddFiles.tiff */,
+ F790692C04EF484300000104 /* AddGroup.tiff */,
+ F790692D04EF484300000104 /* delete.tiff */,
+ F790692E04EF484300000104 /* Download_Reload.tiff */,
+ F790692F04EF484300000104 /* Download_ReloadPressed.tiff */,
+ F790693004EF484300000104 /* Download_Reveal.tiff */,
+ F790693104EF484300000104 /* Download_RevealPressed.tiff */,
+ F790693204EF484300000104 /* Download_Stop.tiff */,
+ F790693304EF484300000104 /* Download_StopPressed.tiff */,
+ F790693704EF484300000104 /* info (italic).tiff */,
+ F790693804EF484300000104 /* info (plain).tiff */,
+ F790693904EF484300000104 /* LeftSearchCap.tiff */,
+ F790693A04EF484300000104 /* NormalMailbox.tiff */,
+ F790693B04EF484300000104 /* NormalMailboxLarge.tiff */,
+ F790693C04EF484300000104 /* quickpick.tiff */,
+ F790693D04EF484300000104 /* RemoveFiles.tiff */,
+ F790693E04EF484300000104 /* RemoveGroup.tiff */,
+ F790693F04EF484300000104 /* rendezvous.tiff */,
+ F790694004EF484300000104 /* reveal.tiff */,
+ F790694104EF484300000104 /* RightSearchCap.tiff */,
+ F790694204EF484300000104 /* SortAscending.gif */,
+ F790694304EF484300000104 /* SortDescending.gif */,
+ F790694404EF484300000104 /* TrashMailbox.tiff */,
+ F790694504EF484300000104 /* TrashMailboxLarge.tiff */,
+ );
+ path = Images;
+ sourceTree = "<group>";
+ };
+ F790693404EF484300000104 /* File Icons */ = {
+ isa = PBXGroup;
+ children = (
+ F790693604EF484300000104 /* Liaison.icns */,
+ );
+ path = "File Icons";
+ sourceTree = "<group>";
+ };
+ F7906A0304EF4FE000000104 /* View Options Window */ = {
+ isa = PBXGroup;
+ children = (
+ F79069C804EF4A2700000104 /* ViewOptionsController.h */,
+ F79069C504EF4A2700000104 /* ViewOptionsController.m */,
+ );
+ name = "View Options Window";
+ sourceTree = "<group>";
+ };
+ F7906A0404EF500D00000104 /* Plugin Manager */ = {
+ isa = PBXGroup;
+ children = (
+ F79069CE04EF4A2700000104 /* PluginManager.h */,
+ F79069C704EF4A2700000104 /* PluginManager.m */,
+ );
+ name = "Plugin Manager";
+ sourceTree = "<group>";
+ };
+ F7A5B69804F8417E00000104 /* Find Window */ = {
+ isa = PBXGroup;
+ children = (
+ F7A5B69904F841A900000104 /* FindController.h */,
+ F7A5B69A04F841A900000104 /* FindController.m */,
+ );
+ name = "Find Window";
+ sourceTree = "<group>";
+ };
+ F7C329D10514613300000103 /* Debugging */ = {
+ isa = PBXGroup;
+ children = (
+ F7C329D30514615A00000103 /* NSException+LiDebugging.m */,
+ );
+ name = Debugging;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+ F7D398910546870000BD181E /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F7D398920546870000BD181E /* FileTableDelegate.h in Headers */,
+ F7D398930546870000BD181E /* LiTableView.h in Headers */,
+ F7D398940546870000BD181E /* Downloader.h in Headers */,
+ F7D398950546870000BD181E /* DownloadStatusView.h in Headers */,
+ F7D398960546870000BD181E /* CopyController.h in Headers */,
+ F7D398970546870000BD181E /* LoadPanelController.h in Headers */,
+ F7D398980546870000BD181E /* DownloadManager.h in Headers */,
+ F7D398990546870000BD181E /* ApplicationController.h in Headers */,
+ F7D3989A0546870000BD181E /* FlippedBox.h in Headers */,
+ F7D3989B0546870000BD181E /* LiScrolLView.h in Headers */,
+ F7D3989C0546870000BD181E /* ImageAndTextCell.h in Headers */,
+ F7D3989D0546870000BD181E /* InspectorController.h in Headers */,
+ F7D3989E0546870000BD181E /* GroupTableDelegate.h in Headers */,
+ F7D3989F0546870000BD181E /* ClientManager.h in Headers */,
+ F7D398A00546870000BD181E /* Liaison.h in Headers */,
+ F7D398A10546870000BD181E /* Group.h in Headers */,
+ F7D398A20546870000BD181E /* WindowController.h in Headers */,
+ F7D398A30546870000BD181E /* ServerManager.h in Headers */,
+ F7D398A40546870000BD181E /* RenManager.h in Headers */,
+ F7D398A50546870000BD181E /* PreferencesController.h in Headers */,
+ F7D398A60546870000BD181E /* WriterThread.h in Headers */,
+ F7D398A70546870000BD181E /* RenIPC.h in Headers */,
+ F7D398A80546870000BD181E /* NIBConnector.h in Headers */,
+ F7D398A90546870000BD181E /* ViewOptionsController.h in Headers */,
+ F7D398AA0546870000BD181E /* PluginManager.h in Headers */,
+ F7D398AB0546870000BD181E /* WriterThreadPool.h in Headers */,
+ F7D398AC0546870000BD181E /* NSFileHandleExtensions.h in Headers */,
+ F7D398AD0546870000BD181E /* FindController.h in Headers */,
+ F7D398AE0546870000BD181E /* LiDataTranslator.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+ F7D398890546870000BD181E /* Liaison */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 28986794095DABC900B5DC99 /* Build configuration list for PBXNativeTarget "Liaison" */;
+ buildPhases = (
+ F7D3988B0546870000BD181E /* CopyFiles */,
+ F7D3988F0546870000BD181E /* CopyFiles */,
+ F7D398910546870000BD181E /* Headers */,
+ F7D398AF0546870000BD181E /* Resources */,
+ F7D398D30546870000BD181E /* Sources */,
+ F7D398F10546870000BD181E /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = Liaison;
+ productInstallPath = "$(HOME)/Applications";
+ productName = Liaison;
+ productReference = F7D398F70546870000BD181E /* Liaison.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 29B97313FDCFA39411CA2CEA /* Project object */ = {
+ isa = PBXProject;
+ buildConfigurationList = 28986798095DABC900B5DC99 /* Build configuration list for PBXProject "Liaison" */;
+ compatibilityVersion = "Xcode 2.4";
+ hasScannedForEncodings = 1;
+ knownRegions = (
+ English,
+ Japanese,
+ French,
+ German,
+ chef,
+ );
+ mainGroup = 29B97314FDCFA39411CA2CEA /* Liaison */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ F7D398890546870000BD181E /* Liaison */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ F7D398AF0546870000BD181E /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F7D398B00546870000BD181E /* Liaison.scriptSuite in Resources */,
+ F7D398B10546870000BD181E /* Liaison.scriptTerminology in Resources */,
+ F7D398B20546870000BD181E /* MainMenu.nib in Resources */,
+ F7D398B30546870000BD181E /* LoadPanel.nib in Resources */,
+ F7D398B40546870000BD181E /* CopyPanel.nib in Resources */,
+ F7D398B50546870000BD181E /* PreferencesWindow.nib in Resources */,
+ F7D398B60546870000BD181E /* InfoPlist.strings in Resources */,
+ F7D398B70546870000BD181E /* WindowElements.strings in Resources */,
+ F7D398B80546870000BD181E /* Liaison Help in Resources */,
+ F7D398B90546870000BD181E /* Liaison.icns in Resources */,
+ F7D398BA0546870000BD181E /* Add.tiff in Resources */,
+ F7D398BB0546870000BD181E /* AddFiles.tiff in Resources */,
+ F7D398BC0546870000BD181E /* AddGroup.tiff in Resources */,
+ F7D398BD0546870000BD181E /* delete.tiff in Resources */,
+ F7D398BE0546870000BD181E /* Download_Reload.tiff in Resources */,
+ F7D398BF0546870000BD181E /* Download_ReloadPressed.tiff in Resources */,
+ F7D398C00546870000BD181E /* Download_Reveal.tiff in Resources */,
+ F7D398C10546870000BD181E /* Download_RevealPressed.tiff in Resources */,
+ F7D398C20546870000BD181E /* Download_Stop.tiff in Resources */,
+ F7D398C30546870000BD181E /* Download_StopPressed.tiff in Resources */,
+ F7D398C40546870000BD181E /* info (italic).tiff in Resources */,
+ F7D398C50546870000BD181E /* info (plain).tiff in Resources */,
+ F7D398C60546870000BD181E /* LeftSearchCap.tiff in Resources */,
+ F7D398C70546870000BD181E /* NormalMailbox.tiff in Resources */,
+ F7D398C80546870000BD181E /* NormalMailboxLarge.tiff in Resources */,
+ F7D398C90546870000BD181E /* quickpick.tiff in Resources */,
+ F7D398CA0546870000BD181E /* RemoveFiles.tiff in Resources */,
+ F7D398CB0546870000BD181E /* RemoveGroup.tiff in Resources */,
+ F7D398CC0546870000BD181E /* rendezvous.tiff in Resources */,
+ F7D398CD0546870000BD181E /* reveal.tiff in Resources */,
+ F7D398CE0546870000BD181E /* RightSearchCap.tiff in Resources */,
+ F7D398CF0546870000BD181E /* SortAscending.gif in Resources */,
+ F7D398D00546870000BD181E /* SortDescending.gif in Resources */,
+ F7D398D10546870000BD181E /* TrashMailbox.tiff in Resources */,
+ F7D398D20546870000BD181E /* TrashMailboxLarge.tiff in Resources */,
+ 286D15B30CDD52F0006463CC /* Info.plist in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ F7D398D30546870000BD181E /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F7D398D40546870000BD181E /* LoadPanelController.m in Sources */,
+ F7D398D50546870000BD181E /* main.m in Sources */,
+ F7D398D60546870000BD181E /* FlippedBox.m in Sources */,
+ F7D398D70546870000BD181E /* ApplicationController.m in Sources */,
+ F7D398D80546870000BD181E /* LiTableView.m in Sources */,
+ F7D398D90546870000BD181E /* DownloadStatusView.m in Sources */,
+ F7D398DA0546870000BD181E /* ClientManager.m in Sources */,
+ F7D398DB0546870000BD181E /* ImageAndTextCell.m in Sources */,
+ F7D398DC0546870000BD181E /* Downloader.m in Sources */,
+ F7D398DD0546870000BD181E /* Group.m in Sources */,
+ F7D398DE0546870000BD181E /* InspectorController.m in Sources */,
+ F7D398DF0546870000BD181E /* FileTableDelegate.m in Sources */,
+ F7D398E00546870000BD181E /* LiScrolLView.m in Sources */,
+ F7D398E10546870000BD181E /* DownloadManager.m in Sources */,
+ F7D398E20546870000BD181E /* CopyController.m in Sources */,
+ F7D398E30546870000BD181E /* GroupTableDelegate.m in Sources */,
+ F7D398E40546870000BD181E /* ViewOptionsController.m in Sources */,
+ F7D398E50546870000BD181E /* PluginManager.m in Sources */,
+ F7D398E60546870000BD181E /* WindowController.m in Sources */,
+ F7D398E70546870000BD181E /* PreferencesController.m in Sources */,
+ F7D398E80546870000BD181E /* WriterThreadPool.m in Sources */,
+ F7D398E90546870000BD181E /* WriterThread.m in Sources */,
+ F7D398EA0546870000BD181E /* RenManager.m in Sources */,
+ F7D398EB0546870000BD181E /* NIBConnector.m in Sources */,
+ F7D398EC0546870000BD181E /* ServerManager.m in Sources */,
+ F7D398ED0546870000BD181E /* NSFileHandleExtensions.m in Sources */,
+ F7D398EE0546870000BD181E /* FindController.m in Sources */,
+ F7D398EF0546870000BD181E /* NSException+LiDebugging.m in Sources */,
+ F7D398F00546870000BD181E /* LiDataTranslator.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 089C165DFE840E0CC02AAC07 /* English */,
+ F79069F104EF4AB300000104 /* chef */,
+ );
+ name = InfoPlist.strings;
+ sourceTree = "<group>";
+ };
+ 29B97318FDCFA39411CA2CEA /* MainMenu.nib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 29B97319FDCFA39411CA2CEA /* English */,
+ F79069F004EF4AAB00000104 /* chef */,
+ );
+ name = MainMenu.nib;
+ sourceTree = "<group>";
+ };
+ F73ECD6904F6BF0A00000104 /* Liaison Help */ = {
+ isa = PBXVariantGroup;
+ children = (
+ F73ECD1304F6BEE000000104 /* English */,
+ );
+ name = "Liaison Help";
+ sourceTree = "<group>";
+ };
+ F79069A704EF48C800000104 /* WindowElements.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ F79069A804EF48C800000104 /* English */,
+ F79069EC04EF4A8C00000104 /* chef */,
+ );
+ name = WindowElements.strings;
+ sourceTree = "<group>";
+ };
+ F79069A904EF48C800000104 /* Liaison.scriptTerminology */ = {
+ isa = PBXVariantGroup;
+ children = (
+ F79069AA04EF48C800000104 /* English */,
+ );
+ name = Liaison.scriptTerminology;
+ sourceTree = "<group>";
+ };
+ F79069AD04EF48C800000104 /* LoadPanel.nib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ F79069AE04EF48C800000104 /* English */,
+ F79069ED04EF4A9500000104 /* chef */,
+ );
+ name = LoadPanel.nib;
+ sourceTree = "<group>";
+ };
+ F79069AF04EF48C800000104 /* CopyPanel.nib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ F79069B004EF48C800000104 /* English */,
+ F79069EE04EF4A9C00000104 /* chef */,
+ );
+ name = CopyPanel.nib;
+ sourceTree = "<group>";
+ };
+ F79069B104EF48C800000104 /* PreferencesWindow.nib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ F79069B204EF48C800000104 /* English */,
+ F79069EF04EF4AA300000104 /* chef */,
+ );
+ name = PreferencesWindow.nib;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 28986795095DABC900B5DC99 /* Development */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COPY_PHASE_STRIP = NO;
+ FRAMEWORK_SEARCH_PATHS = ../Frameworks/build/Development;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREFIX_HEADER = Liaison.h;
+ GCC_TREAT_WARNINGS_AS_ERRORS = YES;
+ GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO;
+ GCC_WARN_UNKNOWN_PRAGMAS = NO;
+ HEADER_SEARCH_PATHS = "";
+ INFOPLIST_FILE = Info.plist;
+ INSTALL_PATH = "$(HOME)/Applications";
+ LIBRARY_SEARCH_PATHS = "";
+ OTHER_CFLAGS = "";
+ OTHER_LDFLAGS = "";
+ PRODUCT_NAME = Liaison;
+ SECTORDER_FLAGS = "";
+ WARNING_CFLAGS = "-Wmost";
+ WRAPPER_EXTENSION = app;
+ ZERO_LINK = YES;
+ };
+ name = Development;
+ };
+ 28986796095DABC900B5DC99 /* Deployment */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CC = "/usr/bin/gcc-3.3";
+ COPY_PHASE_STRIP = YES;
+ CPLUSPLUS = "/usr/bin/g++-3.3";
+ FRAMEWORK_SEARCH_PATHS = ../Frameworks/build;
+ GCC_ENABLE_FIX_AND_CONTINUE = NO;
+ GCC_PREFIX_HEADER = Liaison.h;
+ GCC_TREAT_WARNINGS_AS_ERRORS = YES;
+ GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO;
+ GCC_WARN_UNKNOWN_PRAGMAS = NO;
+ HEADER_SEARCH_PATHS = "";
+ INFOPLIST_FILE = Info.plist;
+ INSTALL_PATH = "$(HOME)/Applications";
+ LIBRARY_SEARCH_PATHS = "";
+ OTHER_CFLAGS = "";
+ OTHER_LDFLAGS = "";
+ PRODUCT_NAME = Liaison;
+ SECTORDER_FLAGS = "";
+ WARNING_CFLAGS = "-Wmost";
+ WRAPPER_EXTENSION = app;
+ ZERO_LINK = NO;
+ };
+ name = Deployment;
+ };
+ 28986797095DABC900B5DC99 /* Default */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CC = "/usr/bin/gcc-3.3";
+ CPLUSPLUS = "/usr/bin/g++-3.3";
+ FRAMEWORK_SEARCH_PATHS = ../Frameworks/build;
+ GCC_PREFIX_HEADER = Liaison.h;
+ GCC_TREAT_WARNINGS_AS_ERRORS = YES;
+ GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO;
+ GCC_WARN_UNKNOWN_PRAGMAS = NO;
+ HEADER_SEARCH_PATHS = "";
+ INFOPLIST_FILE = Info.plist;
+ INSTALL_PATH = "$(HOME)/Applications";
+ LIBRARY_SEARCH_PATHS = "";
+ OTHER_CFLAGS = "";
+ OTHER_LDFLAGS = "";
+ PRODUCT_NAME = Liaison;
+ SECTORDER_FLAGS = "";
+ WARNING_CFLAGS = "-Wmost";
+ WRAPPER_EXTENSION = app;
+ };
+ name = Default;
+ };
+ 28986799095DABC900B5DC99 /* Development */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ };
+ name = Development;
+ };
+ 2898679A095DABC900B5DC99 /* Deployment */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ };
+ name = Deployment;
+ };
+ 2898679B095DABC900B5DC99 /* Default */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ };
+ name = Default;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 28986794095DABC900B5DC99 /* Build configuration list for PBXNativeTarget "Liaison" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 28986795095DABC900B5DC99 /* Development */,
+ 28986796095DABC900B5DC99 /* Deployment */,
+ 28986797095DABC900B5DC99 /* Default */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Default;
+ };
+ 28986798095DABC900B5DC99 /* Build configuration list for PBXProject "Liaison" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 28986799095DABC900B5DC99 /* Development */,
+ 2898679A095DABC900B5DC99 /* Deployment */,
+ 2898679B095DABC900B5DC99 /* Default */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Default;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 29B97313FDCFA39411CA2CEA /* Project object */;
+}
diff --git a/Liaison/LoadPanelController.h b/Liaison/LoadPanelController.h
new file mode 100644
index 0000000..f83adf8
--- /dev/null
+++ b/Liaison/LoadPanelController.h
@@ -0,0 +1,25 @@
+/* LoadPanelController */
+
+@interface LoadPanelController : NSObject
+{
+ IBOutlet NSTextField *thePathField, *theStatusField;
+ IBOutlet NSProgressIndicator *theProgressBar;
+ IBOutlet NSPanel *theLoadPanel;
+
+ NSModalSession modalSession;
+
+ BOOL isShowing;
+}
+- (void)show;
+- (void)hide;
+
+- (void)setStatus: (NSString *)aStatusMsg;
+- (void)setPath: (NSString *)aPath;
+- (void)setProgress: (double)aProgress;
+- (void)setIndeterminantProgress: (BOOL)isIndeterminante;
+- (void)update;
+@property BOOL isShowing;
+@property (retain) NSPanel *theLoadPanel;
+@property (retain) NSProgressIndicator *theProgressBar;
+@property NSModalSession modalSession;
+@end
diff --git a/Liaison/LoadPanelController.m b/Liaison/LoadPanelController.m
new file mode 100644
index 0000000..efed833
--- /dev/null
+++ b/Liaison/LoadPanelController.m
@@ -0,0 +1,74 @@
+#import "LoadPanelController.h"
+
+@implementation LoadPanelController
+- (id)init
+{
+ self = [super init];
+
+ isShowing = NO;
+
+ return self;
+}
+
+- (void)sheetDidEnd: (NSWindow *)sheet
+ returnCode: (int)returnCode
+ contextInfo: (void *)contextInfo
+{
+ [sheet close];
+}
+
+- (void)show
+{
+ if (isShowing == NO) {
+ [NSApp beginSheet: theLoadPanel
+ modalForWindow: [NSApp mainWindow]
+ modalDelegate: self
+ didEndSelector: @selector(sheetDidEnd:returnCode:contextInfo:)
+ contextInfo: nil];
+ modalSession = [NSApp beginModalSessionForWindow: theLoadPanel];
+ isShowing = YES;
+ }
+}
+
+- (void)hide
+{
+ if (isShowing) {
+ [NSApp endModalSession: modalSession];
+ [NSApp endSheet: theLoadPanel];
+ isShowing = NO;
+ }
+}
+
+- (void)setStatus: (NSString *)aStatusMsg
+{
+ [theStatusField setStringValue: aStatusMsg];
+}
+
+- (void)setPath: (NSString *)aPath
+{
+ [thePathField setStringValue: aPath];
+}
+
+- (void)setProgress: (double)aProgress
+{
+ [theProgressBar setDoubleValue: aProgress];
+}
+
+- (void)setIndeterminantProgress: (BOOL)isIndeterminante
+{
+ [theProgressBar setIndeterminate: isIndeterminante];
+}
+
+- (void)update
+{
+ if (isShowing) {
+ if ([theProgressBar isIndeterminate])
+ [theProgressBar animate: self];
+ [NSApp runModalSession: modalSession];
+ }
+}
+@synthesize modalSession;
+@synthesize isShowing;
+@synthesize theProgressBar;
+@synthesize theLoadPanel;
+@end
diff --git a/Liaison/NIBConnector.h b/Liaison/NIBConnector.h
new file mode 100644
index 0000000..7af1fa8
--- /dev/null
+++ b/Liaison/NIBConnector.h
@@ -0,0 +1,30 @@
+//
+// NIBConnector.h
+// Liaison
+//
+// Created by Brian Cully on Mon Mar 03 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+@class PreferencesController;
+@class CopyController;
+@class LoadPanelController;
+
+// Links to other NIB controllers.
+@interface NIBConnector : NSObject {
+ IBOutlet PreferencesController *thePreferencesController;
+ IBOutlet CopyController *theCopyController;
+ IBOutlet LoadPanelController *theLoadPanelController;
+}
+
++ (NIBConnector *)connector;
+
+- (IBAction)showPreferencesWindow: (id)sender;
+- (IBAction)showDownloadWindow: (id)sender;
+
+- (CopyController *)copyController;
+- (LoadPanelController *)loadPanelController;
+@property (retain) CopyController *theCopyController;
+@property (retain) LoadPanelController *theLoadPanelController;
+@property (retain) PreferencesController *thePreferencesController;
+@end
diff --git a/Liaison/NIBConnector.m b/Liaison/NIBConnector.m
new file mode 100644
index 0000000..7873b78
--- /dev/null
+++ b/Liaison/NIBConnector.m
@@ -0,0 +1,66 @@
+//
+// NIBConnector.m
+// Liaison
+//
+// Created by Brian Cully on Mon Mar 03 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+#import "NIBConnector.h"
+
+#import "CopyController.h"
+#import "PreferencesController.h"
+
+@implementation NIBConnector
+static NIBConnector *sharedInstance = nil;
++ (NIBConnector *)connector
+{
+ // Set in awakeFromNib
+ return sharedInstance;
+}
+
+- (id) init
+{
+ self = [super init];
+
+ thePreferencesController = nil;
+ theCopyController = nil;
+
+ return self;
+}
+
+- (void)awakeFromNib
+{
+ if (sharedInstance == nil)
+ sharedInstance = self;
+}
+
+- (void)showPreferencesWindow: (id)sender
+{
+ if (thePreferencesController == nil)
+ [NSBundle loadNibNamed: @"PreferencesWindow.nib" owner: self];
+ [thePreferencesController showWindow];
+}
+
+- (IBAction)showDownloadWindow: (id)sender
+{
+ [[self copyController] showWindow];
+}
+
+- (CopyController *)copyController
+{
+ if (theCopyController == nil)
+ [NSBundle loadNibNamed: @"CopyPanel.nib" owner: self];
+ return theCopyController;
+}
+
+- (LoadPanelController *)loadPanelController
+{
+ if (theLoadPanelController == nil)
+ [NSBundle loadNibNamed: @"LoadPanel.nib" owner: self];
+ return theLoadPanelController;
+}
+@synthesize theCopyController;
+@synthesize thePreferencesController;
+@synthesize theLoadPanelController;
+@end
diff --git a/Liaison/NSException+LiDebugging.m b/Liaison/NSException+LiDebugging.m
new file mode 100644
index 0000000..4dc0067
--- /dev/null
+++ b/Liaison/NSException+LiDebugging.m
@@ -0,0 +1,23 @@
+//
+// NSException+LiDebugging.m
+// Liaison
+//
+// Created by Brian Cully on Sun Sep 14 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+@interface LiException : NSException
+@end
+
+@implementation LiException
++ (void)load
+{
+ [self poseAsClass: [NSException class]];
+}
+
+- (void)raise
+{
+ [LiLog logAsDebug: @"Raising exception"];
+ [super raise];
+}
+@end
diff --git a/Liaison/NSFileHandleExtensions.h b/Liaison/NSFileHandleExtensions.h
new file mode 100644
index 0000000..8267af3
--- /dev/null
+++ b/Liaison/NSFileHandleExtensions.h
@@ -0,0 +1,14 @@
+//
+// NSFileHandleExtensions.h
+// Liaison
+//
+// Created by Brian Cully on Sun May 25 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+#define FileHandleWriteComplete @"FileHandleWriteComplete"
+#define FileHandleClosed @"FileHandleClosed"
+
+@interface NSFileHandleExtensions : NSFileHandle
+- (void)writeDataInBackground: (NSData *)someData;
+@end
diff --git a/Liaison/NSFileHandleExtensions.m b/Liaison/NSFileHandleExtensions.m
new file mode 100644
index 0000000..fa65189
--- /dev/null
+++ b/Liaison/NSFileHandleExtensions.m
@@ -0,0 +1,24 @@
+//
+// NSFileHandleExtensions.m
+// Liaison
+//
+// Created by Brian Cully on Sun May 25 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+#import "NSFileHandleExtensions.h"
+
+#import "WriterThreadPool.h"
+
+@implementation NSFileHandleExtensions
+- (void)dealloc
+{
+ [[WriterThreadPool sharedPool] killThreadFor: self];
+ [super dealloc];
+}
+
+- (void)writeDataInBackground: (NSData *)someData
+{
+ [[WriterThreadPool sharedPool] writeData: someData to: self];
+}
+@end
diff --git a/Liaison/PluginManager.h b/Liaison/PluginManager.h
new file mode 100644
index 0000000..beb7eab
--- /dev/null
+++ b/Liaison/PluginManager.h
@@ -0,0 +1,27 @@
+//
+// PluginManager.h
+// Liaison
+//
+// Created by Brian Cully on Thu May 15 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+@interface PluginManager : NSObject
+{
+ NSMutableArray *theFileStorePlugins;
+ NSMutableArray *theBrowserPlugins;
+ NSMutableArray *theInspectorPlugins;
+}
+
++ (PluginManager *)defaultManager;
+- (void)scanForPlugins;
+@property (retain) NSMutableArray *theFileStorePlugins;
+@property (retain) NSMutableArray *theInspectorPlugins;
+@property (retain) NSMutableArray *theBrowserPlugins;
+@end
+
+@interface PluginManager (Accessors)
+- (NSArray *)fileStorePlugins;
+- (NSArray *)browserPlugins;
+- (NSArray *)inspectorPlugins;
+@end
diff --git a/Liaison/PluginManager.m b/Liaison/PluginManager.m
new file mode 100644
index 0000000..319dd20
--- /dev/null
+++ b/Liaison/PluginManager.m
@@ -0,0 +1,116 @@
+//
+// PluginManager.m
+// Liaison
+//
+// Created by Brian Cully on Thu May 15 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+#import "PluginManager.h"
+
+@implementation PluginManager
+static PluginManager *defaultManager = nil;
++ (PluginManager *)defaultManager
+{
+ if (defaultManager == nil)
+ defaultManager = [[self alloc] init];
+ return defaultManager;
+}
+
+- (id)init
+{
+ self = [super init];
+
+ theFileStorePlugins = [[NSMutableArray alloc] init];
+ theBrowserPlugins = [[NSMutableArray alloc] init];
+ theInspectorPlugins = [[NSMutableArray alloc] init];
+
+ [self scanForPlugins];
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [theFileStorePlugins release];
+ [theBrowserPlugins release];
+ [theInspectorPlugins release];
+
+ [super dealloc];
+}
+
+- (void)activatePluginAtPath: (NSString *)aPath
+{
+ NSBundle *pluginBundle;
+
+ pluginBundle = [NSBundle bundleWithPath: aPath];
+ if (pluginBundle != nil) {
+ NSDictionary *pluginDict;
+ NSString *classString;
+
+ pluginDict = [pluginBundle infoDictionary];
+ classString = [pluginDict objectForKey: @"NSPrincipalClass"];
+ if (classString != nil) {
+ Class pluginClass;
+
+ pluginClass = NSClassFromString(classString);
+ if (pluginClass == nil) {
+ id plugin;
+
+ pluginClass = [pluginBundle principalClass];
+ plugin = [[[pluginClass alloc] init] autorelease];
+ if ([pluginClass conformsToProtocol:@protocol(LiFileStorePlugin)]) {
+ [pluginClass setBundle: pluginBundle];
+ [theFileStorePlugins addObject: plugin];
+ }
+ if ([pluginClass conformsToProtocol:@protocol(LiBrowserPlugin)]) {
+ [pluginClass setBundle: pluginBundle];
+ [theBrowserPlugins addObject: plugin];
+ }
+ if ([pluginClass conformsToProtocol:@protocol(LiInspectorPlugin)]) {
+ [pluginClass setBundle: pluginBundle];
+ [theInspectorPlugins addObject: plugin];
+ }
+ }
+ }
+ }
+ return;
+}
+
+- (void)scanForPlugins
+{
+ NSString *appLocation;
+
+ appLocation = [[NSBundle mainBundle] builtInPlugInsPath];
+ if (appLocation) {
+ NSEnumerator *pluginEnum;
+ NSString *pluginPath;
+
+ pluginEnum = [[NSBundle pathsForResourcesOfType: @"liaisonplugin"
+ inDirectory: appLocation] objectEnumerator];
+ while ((pluginPath = [pluginEnum nextObject]) != nil) {
+ [self activatePluginAtPath: pluginPath];
+ }
+ }
+}
+@synthesize theBrowserPlugins;
+@synthesize theFileStorePlugins;
+@synthesize theInspectorPlugins;
+@end
+
+@implementation PluginManager (Accessors)
+- (NSArray *)fileStorePlugins
+{
+ return theFileStorePlugins;
+}
+
+- (NSArray *)browserPlugins
+{
+ return theBrowserPlugins;
+}
+
+- (NSArray *)inspectorPlugins
+{
+ return theInspectorPlugins;
+}
+@end
diff --git a/Liaison/PreferencesController.h b/Liaison/PreferencesController.h
new file mode 100644
index 0000000..f2569aa
--- /dev/null
+++ b/Liaison/PreferencesController.h
@@ -0,0 +1,22 @@
+/* PreferencesController */
+
+@interface PreferencesController : NSObject
+{
+ IBOutlet NSWindow *theWindow;
+
+ IBOutlet NSTextField *theDownloadField;
+ IBOutlet NSTextField *theHostnameFieldDescription;
+ IBOutlet NSTextField *theHostnameField;
+ IBOutlet NSButton *theNetworkEnabledButton;
+}
+- (IBAction)applyChanges:(id)sender;
+- (IBAction)selectDownloadDirectory:(id)sender;
+- (IBAction)toggleNetworkEnabled:(id)sender;
+
+- (void)showWindow;
+@property (retain) NSWindow *theWindow;
+@property (retain) NSButton *theNetworkEnabledButton;
+@property (retain) NSTextField *theHostnameFieldDescription;
+@property (retain) NSTextField *theDownloadField;
+@property (retain) NSTextField *theHostnameField;
+@end
diff --git a/Liaison/PreferencesController.m b/Liaison/PreferencesController.m
new file mode 100644
index 0000000..3676505
--- /dev/null
+++ b/Liaison/PreferencesController.m
@@ -0,0 +1,154 @@
+#import "PreferencesController.h"
+
+#import "RenManager.h"
+
+@implementation PreferencesController (ToolbarDelegate)
+- (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar*)toolbar
+{
+ return [NSArray arrayWithObjects:
+ NSToolbarSeparatorItemIdentifier,
+ NSToolbarSpaceItemIdentifier,
+ NSToolbarFlexibleSpaceItemIdentifier,
+ NSToolbarCustomizeToolbarItemIdentifier,
+ @"Network",
+ nil];
+}
+
+- (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar*)toolbar
+{
+ return [NSArray arrayWithObjects:
+ @"Network",
+ NSToolbarFlexibleSpaceItemIdentifier,
+ NSToolbarSeparatorItemIdentifier,
+ NSToolbarCustomizeToolbarItemIdentifier, nil];
+}
+
+- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar
+ itemForItemIdentifier:(NSString *)itemIdentifier
+ willBeInsertedIntoToolbar:(BOOL)flag
+{
+ NSToolbarItem *item;
+
+ item = [[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier];
+ if ([itemIdentifier isEqualToString: @"Network"]) {
+ [item setLabel: @"Network"];
+ [item setPaletteLabel: @"Network"];
+ [item setToolTip: @"Open network preferences."];
+ [item setImage: [NSImage imageNamed: @"Network (Large).tiff"]];
+ [item setTarget: self];
+ [item setAction: @selector(revealNetworkPane:)];
+ }
+
+ return item;
+}
+
+- (BOOL)validateToolbarItem: (NSToolbarItem *)theItem
+{
+ return YES;
+}
+@end
+
+@implementation PreferencesController
+- (void)setupToolbar
+{
+ NSToolbar *toolbar;
+
+ toolbar = [[[NSToolbar alloc]
+ initWithIdentifier: @"prefsToolbar"] autorelease];
+ [toolbar setDelegate: self];
+ [toolbar setAllowsUserCustomization: YES];
+ [toolbar setAutosavesConfiguration: YES];
+ [theWindow setToolbar: toolbar];
+}
+
+- (void)awakeFromNib
+{
+ // We don't need this yet, but leave the stubs.
+ //[self setupToolbar];
+}
+
+- (IBAction)applyChanges:(id)sender
+{
+ Preferences *prefs;
+
+ prefs = [Preferences sharedPreferences];
+
+ [prefs setDownloadDirectory: [theDownloadField stringValue]];
+
+ if ([[prefs hostname] isEqualToString:
+ [theHostnameField stringValue]] == NO) {
+ [prefs setHostname: [theHostnameField stringValue]];
+ [[RenManager sharedManager] updateHostname];
+ }
+
+ if ([theNetworkEnabledButton state] == 1 &&
+ [prefs networkEnabled] == NO) {
+ [prefs setNetworkEnabled: YES];
+ [[RenManager sharedManager] startSharing];
+ } else if ([theNetworkEnabledButton state] == 0 &&
+ [prefs networkEnabled] == YES) {
+ [prefs setNetworkEnabled: NO];
+ [[RenManager sharedManager] stopSharing];
+ }
+
+ [theWindow close];
+}
+
+- (IBAction)toggleNetworkEnabled:(id)sender
+{
+ if ([sender state] == 1) {
+ [theHostnameFieldDescription setEnabled: YES];
+ [theHostnameField setEnabled: YES];
+ } else {
+ [theHostnameFieldDescription setEnabled: NO];
+ [theHostnameField setEnabled: NO];
+ }
+}
+
+- (IBAction)selectDownloadDirectory:(id)sender
+{
+ NSOpenPanel *openPanel;
+
+ openPanel = [NSOpenPanel openPanel];
+ [openPanel setTitle: @"Select Download Directory"];
+ [openPanel setAllowsMultipleSelection: NO];
+ [openPanel setCanChooseDirectories: YES];
+ [openPanel setCanChooseFiles: NO];
+
+ [openPanel beginSheetForDirectory: [theDownloadField stringValue]
+ file: nil
+ types: nil
+ modalForWindow: theWindow
+ modalDelegate: self
+ didEndSelector: @selector(openPanelDidEnd:returnCode:contextInfo:)
+ contextInfo: nil];
+}
+
+- (void)openPanelDidEnd: (NSOpenPanel *)aPanel
+ returnCode: (int)rc
+ contextInfo: (void *)someContext
+{
+ if (rc == NSCancelButton)
+ return;
+
+ [theDownloadField setStringValue: [[aPanel filenames] objectAtIndex: 0]];
+}
+
+- (void)showWindow
+{
+ Preferences *prefs;
+
+ prefs = [Preferences sharedPreferences];
+ [theDownloadField setStringValue: [prefs downloadDirectory]];
+ [theHostnameField setStringValue: [prefs hostname]];
+ [theNetworkEnabledButton setState:
+ [prefs networkEnabled] ? 1 : 0];
+ [self toggleNetworkEnabled: theNetworkEnabledButton];
+ [theWindow makeKeyAndOrderFront: self];
+}
+@synthesize theWindow;
+@synthesize theNetworkEnabledButton;
+@synthesize theHostnameFieldDescription;
+@synthesize theHostnameField;
+@synthesize theDownloadField;
+@end
diff --git a/Liaison/RenIPC.h b/Liaison/RenIPC.h
new file mode 100644
index 0000000..714b130
--- /dev/null
+++ b/Liaison/RenIPC.h
@@ -0,0 +1,21 @@
+/*
+ * RenIPC.h
+ * Liaison
+ *
+ * Created by Brian Cully on Thu Mar 20 2003.
+ * Copyright (c) 2003 Brian Cully. All rights reserved.
+ *
+ */
+
+// Rendezvous port name.
+#define LiRendezvousPortName @"_liaison._tcp."
+
+// Notifications of death.
+#define SERVERMANAGERDEATHNOTIFICATION @"Server Died"
+#define CLIENTMANAGERDEATHNOTIFICATION @"Client Died"
+
+// Client/server keys.
+#define RenHostnameKey @"hostname"
+#define RenFilesAddedKey @"files added"
+#define RenFilesChangedKey @"files changed"
+#define RenFilesRemovedKey @"files removed" \ No newline at end of file
diff --git a/Liaison/RenManager.h b/Liaison/RenManager.h
new file mode 100644
index 0000000..e25734d
--- /dev/null
+++ b/Liaison/RenManager.h
@@ -0,0 +1,48 @@
+//
+// RenManager.h
+// Liaison
+//
+// Created by Brian Cully on Sun Feb 16 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+@interface RenManager : NSObject {
+ NSSocketPort *theListenPort;
+ NSFileHandle *theListenSocket;
+ NSNetService *theService;
+ NSNetServiceBrowser *theBrowser;
+ NSMutableDictionary *theServersByName;
+ NSMutableDictionary *theClients;
+
+ LiFileStore *theFileStore;
+
+ BOOL theShareIsEnabled;
+}
++ (RenManager *)sharedManager;
+
+- (void)startup;
+- (void)startSharing;
+- (void)stopSharing;
+- (void)updateHostname;
+@property (retain,getter=listenSocket) NSFileHandle *theListenSocket;
+@property (retain,getter=service) NSNetService *theService;
+@property (retain) NSMutableDictionary *theServersByName;
+@property (retain,getter=browser) NSNetServiceBrowser *theBrowser;
+@property (retain) NSMutableDictionary *theClients;
+@property BOOL theShareIsEnabled;
+@property (retain,getter=fileStore) LiFileStore *theFileStore;
+@property (retain,getter=listenPort) NSSocketPort *theListenPort;
+@end
+
+@interface RenManager (Accessors)
+- (NSNetServiceBrowser *)browser;
+- (void)setBrowser: (NSNetServiceBrowser *)aBrowser;
+- (LiFileStore *)fileStore;
+- (void)setFileStore: (LiFileStore *)aFileStore;
+- (NSSocketPort *)listenPort;
+- (void)setListenPort: (NSSocketPort *)aPort;
+- (NSFileHandle *)listenSocket;
+- (void)setListenSocket: (NSFileHandle *)aSocket;
+- (NSNetService *)service;
+- (void)setService: (NSNetService *)aService;
+@end \ No newline at end of file
diff --git a/Liaison/RenManager.m b/Liaison/RenManager.m
new file mode 100644
index 0000000..38323d3
--- /dev/null
+++ b/Liaison/RenManager.m
@@ -0,0 +1,357 @@
+//
+// RenManager.m
+// Liaison
+//
+// Created by Brian Cully on Sun Feb 16 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+#import "RenIPC.h"
+#import "RenManager.h"
+
+#import "ClientManager.h"
+#import "ServerManager.h"
+
+#import <netinet/in.h>
+#import <sys/socket.h>
+
+@implementation RenManager (NetServiceDelegate)
+- (void)serverManagerDied: (NSNotification *)aNotification
+{
+ ServerManager *deadServer;
+
+ [LiLog logAsDebug: @"server manager died."];
+ deadServer = [aNotification object];
+ [theServersByName removeObjectForKey: [[deadServer service] name]];
+}
+
+- (void)clientManagerDied: (NSNotification *)aNotification
+{
+ ClientManager *client;
+
+ client = [aNotification object];
+ if (client != nil) {
+ [LiLog logAsDebug: @"removing client for %@!", [client file]];
+ [theClients removeObjectForKey: [client file]];
+ [LiLog logAsDebug: @"removed client!"];
+ }
+}
+
+- (void)acceptConnection: (NSNotification *)aNotification
+{
+ ClientManager *client;
+ NSDictionary *userInfo;
+ NSFileHandle *socket;
+ NSNotificationCenter *defaultCenter;
+
+ userInfo = [aNotification userInfo];
+ socket = [userInfo objectForKey:
+ NSFileHandleNotificationFileHandleItem];
+
+ client = [[ClientManager alloc] initWithFile: socket
+ andFileStore: [self fileStore]];
+ //[socket release];
+
+ defaultCenter = [NSNotificationCenter defaultCenter];
+ [defaultCenter addObserver: self
+ selector: @selector(clientManagerDied:)
+ name: CLIENTMANAGERDEATHNOTIFICATION
+ object: client];
+
+ [theClients setObject: client forKey: [client file]];
+ [client startup];
+ [client release];
+
+ [[self listenSocket] acceptConnectionInBackgroundAndNotify];
+}
+
+- (void)netServiceWillPublish:(NSNetService *)sender
+{
+ theShareIsEnabled = YES;
+ [self setService: sender];
+}
+
+- (void)netServiceDidStop: (NSNetService *)sender
+{
+ theShareIsEnabled = NO;
+ [self setService: nil];
+}
+
+- (void)netServiceBrowser: (NSNetServiceBrowser *)aNetServiceBrowser
+ didFindService: (NSNetService *)aNetService
+ moreComing: (BOOL)moreComing
+{
+ [aNetService setDelegate: self];
+ [aNetService resolve];
+}
+
+- (void)netServiceBrowserDidStopSearch: (NSNetServiceBrowser *)browser
+{
+ [LiLog logAsDebug: @"stopping search."];
+}
+
+- (void)netServiceWillResolve: (NSNetService *)aNetService
+{
+}
+
+- (void)netServiceDidResolveAddress: (NSNetService *)aNetService
+{
+ ServerManager *server;
+
+ if ([theServersByName objectForKey: [aNetService name]] == nil) {
+ server = [[ServerManager alloc] initWithNetService: aNetService];
+ if (server != nil) {
+ [theServersByName setObject: server forKey: [aNetService name]];
+ [server startup];
+ }
+ }
+}
+
+// XXX - should set group name to IP address and proceed.
+- (void)netService: (NSNetService *)aNetService
+ didNotResolve: (NSDictionary *)someErrors
+{
+ [LiLog logAsDebug: @"XXX: Couldn't resolve address for %@.", [aNetService name]];
+}
+
+- (void)netServiceBrowser: (NSNetServiceBrowser *)aNetServiceBrowser
+ didRemoveService: (NSNetService *)aNetService
+ moreComing: (BOOL)moreComing
+{
+ ServerManager *removedServer;
+
+ [LiLog logAsDebug: @"removing service: %@", [aNetService name]];
+ removedServer = [theServersByName objectForKey: [aNetService name]];
+ [LiLog logAsDebug: @"\tremoved file store: %@", [[removedServer fileStore] storeID]];
+ [removedServer shutdown];
+}
+@end
+
+@implementation RenManager
+static RenManager *sharedManager = nil;
+
++ (RenManager *)sharedManager
+{
+ if (sharedManager == nil)
+ sharedManager = [[RenManager alloc] init];
+ return sharedManager;
+}
+
+- (id)init
+{
+ NSNotificationCenter *defaultCenter;
+
+ self = [super init];
+
+ defaultCenter = [NSNotificationCenter defaultCenter];
+ [defaultCenter addObserver: self
+ selector: @selector(serverManagerDied:)
+ name: SERVERMANAGERDEATHNOTIFICATION
+ object: nil];
+
+ [self setFileStore: nil];
+
+ theServersByName = [[NSMutableDictionary alloc] init];
+ theClients = [[NSMutableDictionary alloc] init];
+
+ [self setListenSocket: nil];
+ [self setListenPort: nil];
+ [self setService: nil];
+ [self setBrowser: nil];
+
+ theShareIsEnabled = NO;
+
+ return self;
+}
+
+- (void)dealloc
+{
+ NSNotificationCenter *defaultCenter;
+
+ defaultCenter = [NSNotificationCenter defaultCenter];
+ [defaultCenter removeObserver: self];
+
+ [self setFileStore: nil];
+
+ [theServersByName release];
+ [theClients release];
+
+ [self setListenSocket: nil];
+ [self setListenPort: nil];
+ [self setService: nil];
+ [self setBrowser: nil];
+
+ [super dealloc];
+}
+
+- (void)startup
+{
+ NSNetServiceBrowser *browser;
+
+ if ([[Preferences sharedPreferences] networkEnabled])
+ [self startSharing];
+
+ browser = [[NSNetServiceBrowser alloc] init];
+ [browser autorelease];
+ [browser setDelegate: self];
+ [browser searchForServicesOfType: LiRendezvousPortName
+ inDomain: @""];
+ [self setBrowser: browser];
+}
+
+- (void)startSharing
+{
+ NSFileHandle *listenSocket;
+ NSNetService *service;
+ NSNotificationCenter *defaultCenter;
+ NSSocketPort *listenPort;
+ NSString *hostname;
+ struct sockaddr *addr;
+ int port;
+
+ if (theShareIsEnabled)
+ return;
+
+ defaultCenter = [NSNotificationCenter defaultCenter];
+ [defaultCenter addObserver: self
+ selector: @selector(acceptConnection:)
+ name: NSFileHandleConnectionAcceptedNotification
+ object: nil];
+
+ listenPort = [[NSSocketPort alloc] init];
+ [listenPort autorelease];
+ listen([listenPort socket], 10);
+ [self setListenPort: listenPort];
+
+ listenSocket = [[NSFileHandle alloc] initWithFileDescriptor: [listenPort socket]
+ closeOnDealloc: YES];
+ [listenSocket autorelease];
+ [listenSocket acceptConnectionInBackgroundAndNotify];
+ [self setListenSocket: listenSocket];
+
+ hostname = [[Preferences sharedPreferences] hostname];
+ addr = (struct sockaddr *)[[listenPort address] bytes];
+ if (addr->sa_family == AF_INET6)
+ port = ((struct sockaddr_in6 *)addr)->sin6_port;
+ else
+ port = ((struct sockaddr_in *)addr)->sin_port;
+
+ service = [[NSNetService alloc] initWithDomain: @""
+ type: LiRendezvousPortName
+ name: hostname
+ port: port];
+ [service autorelease];
+ [service setDelegate: self];
+ [service publish];
+}
+
+- (void)stopSharing
+{
+ if (theShareIsEnabled) {
+ NSNotificationCenter *defaultCenter;
+ NSEnumerator *clientEnum;
+ id clientKey;
+
+ [[self service] stop];
+ [self setListenSocket: nil];
+
+ defaultCenter = [NSNotificationCenter defaultCenter];
+ [defaultCenter removeObserver: self
+ name: NSFileHandleConnectionAcceptedNotification
+ object: nil];
+
+ clientEnum = [theClients keyEnumerator];
+ while ((clientKey = [clientEnum nextObject]) != nil) {
+ ClientManager *client;
+
+ [LiLog logAsDebug: @"shutting down client"];
+ client = [theClients objectForKey: clientKey];
+ [client shutdown];
+ }
+ [LiLog logAsDebug: @"sharing stopped."];
+ }
+}
+
+- (void)updateHostname
+{
+ if (theShareIsEnabled) {
+ ClientManager *client;
+ NSEnumerator *clientEnum;
+
+ clientEnum = [theClients objectEnumerator];
+ while ((client = [clientEnum nextObject]) != nil) {
+ [client setHostname: [[Preferences sharedPreferences] hostname]];
+ [client sendHostname];
+ }
+ }
+}
+@synthesize theBrowser;
+@synthesize theServersByName;
+@synthesize theService;
+@synthesize theListenSocket;
+@synthesize theFileStore;
+@synthesize theShareIsEnabled;
+@synthesize theClients;
+@synthesize theListenPort;
+@end
+
+@implementation RenManager (Accessors)
+- (NSNetServiceBrowser *)browser
+{
+ return theBrowser;
+}
+
+- (void)setBrowser: (NSNetServiceBrowser *)aBrowser
+{
+ [aBrowser retain];
+ [theBrowser release];
+ theBrowser = aBrowser;
+}
+
+- (NSSocketPort *)listenPort
+{
+ return theListenPort;
+}
+
+- (void)setListenPort: (NSSocketPort *)aPort
+{
+ [aPort retain];
+ [theListenPort release];
+ theListenPort = aPort;
+}
+
+- (NSFileHandle *)listenSocket
+{
+ return theListenSocket;
+}
+
+- (void)setListenSocket: (NSFileHandle *)aSocket
+{
+ [aSocket retain];
+ [theListenSocket release];
+ theListenSocket = aSocket;
+}
+
+- (NSNetService *)service
+{
+ return theService;
+}
+
+- (void)setService: (NSNetService *)aService
+{
+ [aService retain];
+ [theService release];
+ theService = aService;
+}
+
+- (LiFileStore *)fileStore
+{
+ return theFileStore;
+}
+
+- (void)setFileStore: (LiFileStore *)aFileStore
+{
+ [aFileStore retain];
+ [theFileStore release];
+ theFileStore = aFileStore;
+}
+@end \ No newline at end of file
diff --git a/Liaison/ServerManager.h b/Liaison/ServerManager.h
new file mode 100644
index 0000000..1c4da97
--- /dev/null
+++ b/Liaison/ServerManager.h
@@ -0,0 +1,41 @@
+//
+// ServerManager.h
+// Liaison
+//
+// Created by Brian Cully on Sun Feb 16 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+@class DownloadStatusView;
+
+@interface ServerManager : NSObject
+{
+ NSFileHandle *theFile;
+ NSNetService *theService;
+ NSMutableData *theBuffer;
+
+ LiFileStore *theFileStore;
+}
+- (id)initWithNetService: (NSNetService *)aService;
+
+- (void)startup;
+- (void)shutdown;
+@property (retain,getter=service) NSNetService *theService;
+@property (retain,getter=buffer) NSMutableData *theBuffer;
+@property (retain,getter=fileStore) LiFileStore *theFileStore;
+@property (retain,getter=file) NSFileHandle *theFile;
+@end
+
+@interface ServerManager (Accessors)
+- (NSFileHandle *)file;
+- (void)setFile: (NSFileHandle *)aFile;
+- (NSNetService *)service;
+- (void)setService: (NSNetService *)aService;
+- (NSMutableData *)buffer;
+- (void)setBuffer: (NSMutableData *)aBuffer;
+- (LiFileStore *)fileStore;
+- (void)setFileStore: (LiFileStore *)aFileStore;
+@end
+
+@interface ServerManager (LiFileStore) <LiFileStoreDelegate>
+@end \ No newline at end of file
diff --git a/Liaison/ServerManager.m b/Liaison/ServerManager.m
new file mode 100644
index 0000000..903c3d5
--- /dev/null
+++ b/Liaison/ServerManager.m
@@ -0,0 +1,428 @@
+//
+// ServerManager.m
+// Liaison
+//
+// Created by Brian Cully on Sun Feb 16 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+#import "RenIPC.h"
+#import "ServerManager.h"
+
+#import "CopyController.h"
+#import "DownloadStatusView.h"
+#import "DownloadManager.h"
+#import "LiDataTranslator.h"
+#import "NIBConnector.h"
+
+#import <netinet/in.h>
+#import <sys/socket.h>
+#import <unistd.h>
+
+@interface ServerManager (NetworkStuff)
+- (BOOL)sendCommand: (NSDictionary *)aCmd;
+@end
+
+@interface NSNetService (FileHandleExtensions)
+- (NSFileHandle *)fileHandle;
+@end
+
+@implementation ServerManager
++ (NSImage *)fileStoreIcon
+{
+ NSImage *image;
+ NSString *iconPath;
+
+ image = [NSImage imageNamed: @"LiBuiltInFunctions RenStoreIcon"];
+ if (image == nil) {
+ iconPath = [[NSBundle bundleForClass: [self class]] pathForResource: @"rendezvous" ofType: @"tiff"];
+ image = [[NSImage alloc] initWithContentsOfFile: iconPath];
+ [image setName: @"LiBuiltInFunctions RenStoreIcon"];
+ }
+
+ return image;
+}
+
+- (id)initWithNetService: (NSNetService *)aService
+{
+ NSBundle *myBundle;
+
+ self = [super init];
+
+ [self setService: aService];
+ [self setFile: [[self service] fileHandle]];
+ if ([self file] == nil) {
+ [self autorelease];
+ return nil;
+ }
+ [self setFileStore: [LiFileStore fileStoreWithName: [aService name]]];
+ [[self fileStore] setEditable: NO];
+ [[self fileStore] setIcon: [[self class] fileStoreIcon]];
+ [[self fileStore] setDelegate: self];
+ [[self fileStore] addIndexForAttribute: LiGroupsAttribute];
+
+ theBuffer = [[NSMutableData alloc] init];
+
+ myBundle = [NSBundle bundleForClass: [self class]];
+
+ return self;
+}
+
+- (void)dealloc
+{
+ NSNotificationCenter *defaultCenter;
+
+ [LiLog logAsDebug: @"[ServerManager dealloc]"];
+
+ defaultCenter = [NSNotificationCenter defaultCenter];
+ [defaultCenter removeObserver: self];
+
+ [self setService: nil];
+ [self setFile: nil];
+ [self setFileStore: nil];
+ [self setBuffer: nil];
+
+ [super dealloc];
+}
+
+- (void)startup
+{
+ NSDictionary *handshake;
+ NSNotificationCenter *defaultCenter;
+
+ defaultCenter = [NSNotificationCenter defaultCenter];
+ [defaultCenter addObserver: self
+ selector: @selector(browserRead:)
+ name: NSFileHandleReadCompletionNotification
+ object: [self file]];
+
+ handshake = [NSDictionary dictionaryWithObject: @"browser"
+ forKey: @"type"];
+ [self sendCommand: handshake];
+
+ [[self file] readInBackgroundAndNotify];
+}
+
+- (void)shutdown
+{
+ NSNotificationCenter *defaultCenter;
+
+ defaultCenter = [NSNotificationCenter defaultCenter];
+ [defaultCenter removeObserver: self
+ name: NSFileHandleReadCompletionNotification
+ object: [self file]];
+
+ [LiFileStore removeStoreWithID: [[self fileStore] storeID]];
+ [self setFile: nil];
+ [self setFileStore: nil];
+
+ [defaultCenter postNotificationName: SERVERMANAGERDEATHNOTIFICATION
+ object: self
+ userInfo: nil];
+}
+@synthesize theBuffer;
+@synthesize theFileStore;
+@synthesize theFile;
+@synthesize theService;
+@end
+
+@implementation ServerManager (Accessors)
+- (NSFileHandle *)file
+{
+ return theFile;
+}
+
+- (void)setFile: (NSFileHandle *)aFile
+{
+ [aFile retain];
+ [theFile release];
+ theFile = aFile;
+}
+
+- (NSNetService *)service
+{
+ return theService;
+}
+
+- (void)setService: (NSNetService *)aService
+{
+ [aService retain];
+ [theService release];
+ theService = aService;
+}
+
+- (NSMutableData *)buffer
+{
+ return theBuffer;
+}
+
+- (void)setBuffer: (NSMutableData *)aBuffer
+{
+ [aBuffer retain];
+ [theBuffer release];
+ theBuffer = aBuffer;
+}
+
+- (LiFileStore *)fileStore
+{
+ return theFileStore;
+}
+
+- (void)setFileStore: (LiFileStore *)aFileStore
+{
+ [aFileStore retain];
+ [theFileStore release];
+ theFileStore = aFileStore;
+}
+@end
+
+@implementation ServerManager (LiFileStore)
+- (BOOL)synchronizeFileStore
+{
+ return YES;
+}
+
+- (void)synchronizeFileHandle: (LiFileHandle *)aFileHandle
+ withNewAttributes: (NSMutableDictionary *)someAttributes
+{
+}
+
+- (BOOL)shouldUpdateFileHandle: (LiFileHandle *)aFileHandle
+{
+ return NO;
+}
+
+- (void)updateFileHandle: (LiFileHandle *)aFileHandle
+{
+ return;
+}
+
+- (void)openFileHandle: (LiFileHandle *)aFileHandle
+{
+ NSFileHandle *copySocket;
+
+ [LiLog logAsDebug: @"[ServerManager openFileHandle: %@]", aFileHandle];
+ [LiLog indentDebugLog];
+
+ [LiLog logAsDebug: @"filename: %@", [aFileHandle filename]];
+ copySocket = [[self service] fileHandle];
+ [[DownloadManager defaultManager] downloadFileHandle: aFileHandle
+ fromSocket: copySocket
+ target: self
+ didFinishSelector: @selector(fileHandleFinishedDownloading:context:errorString:)
+ withContext: nil];
+ [LiLog unindentDebugLog];
+}
+
+- (void)fileHandleFinishedDownloading: (LiFileHandle *)aFileHandle
+ context: (void *)someContext
+ errorString: (NSString *)anError
+{
+ [LiLog logAsDebug: @"[ServerManager fileHandleFinishedDownloading]"];
+ [LiLog indentDebugLog];
+ if (anError != nil) {
+ [LiLog logAsError: @"Couldn't download %@: %@",
+ [aFileHandle filename], anError];
+ } else {
+ [LiLog logAsDebug: @"File downloaded successfully."];
+ }
+ [LiLog unindentDebugLog];
+}
+
+- (LiFileHandle *)addURL: (NSURL *)anURL
+ toFileStore: (LiFileStore *)aFileStore
+{
+ return nil;
+}
+
+- (NSURL *)urlForFileHandle: (LiFileHandle *)aFileHandle
+{
+ NSURL *url;
+ NSString *urlStr;
+
+ urlStr = [NSString stringWithFormat: @"liaison://%@/%@", [[aFileHandle fileStore] name], [aFileHandle fileID]];
+ url = [NSURL URLWithString: urlStr];
+ return url;
+}
+
+- (NSArray *)defaultValuesForAttribute: (NSString *)anAttribute
+{
+ return nil;
+}
+
+- (BOOL)addDefaultAttribute: (NSDictionary *)anAttribute toFileStore: (LiFileStore *)aFileStore
+{
+ return NO;
+}
+
+- (BOOL)changeDefaultValueForAttribute: (NSDictionary *)anAttribute toValue: (id)aValue inFileStore: (LiFileStore *)aFileStore
+{
+ return NO;
+}
+
+- (BOOL)removeDefaultAttribute: (NSDictionary *)anAttribute fromFileStore: (LiFileStore *)aFileStore
+{
+ return NO;
+}
+@end
+
+@implementation ServerManager (NetworkStuff)
+- (BOOL)sendCommand: (NSDictionary *)aCmd
+{
+ NSData *cmdData;
+ NSString *errorString;
+
+ errorString = nil;
+ cmdData = [aCmd encodedData];
+ if (cmdData != nil) {
+ [[self file] writeData: cmdData];
+ return YES;
+ } else
+ return NO;
+}
+
+- (void)processAddList: (NSArray *)changeList
+{
+ NSDictionary *fileInfo;
+
+ for (fileInfo in changeList) {
+ LiFileHandle *file;
+
+ file = [[self fileStore] addFileWithAttributes: fileInfo];
+ }
+
+ [[self fileStore] synchronize];
+}
+
+- (void)processChangeList: (NSArray *)aFileList
+{
+ NSDictionary *changeDict;
+
+ for (changeDict in aFileList) {
+ id fileID;
+
+ fileID = [changeDict objectForKey: LiFileHandleAttribute];
+ if (fileID != nil) {
+ LiFileHandle *tmpHandle;
+ NSEnumerator *attrEnum;
+ NSString *attribute;
+
+ tmpHandle = [[LiFileHandle alloc] init];
+ [tmpHandle setFileStore: [self fileStore]];
+ [tmpHandle setFileID: fileID];
+
+ attrEnum = [changeDict keyEnumerator];
+ while ((attribute = [attrEnum nextObject]) != nil) {
+ if ([attribute compare: LiFileHandleAttribute] != 0) {
+ [tmpHandle setValue: [changeDict objectForKey: attribute]
+ forAttribute: attribute];
+ }
+ }
+
+ [tmpHandle release];
+ }
+ }
+
+ [[self fileStore] synchronize];
+}
+
+- (void)processDeleteList: (NSArray *)aFileList
+{
+ id fileID;
+
+ for (fileID in aFileList) {
+ LiFileHandle *tmpFile;
+
+ tmpFile = [[LiFileHandle alloc] init];
+ [tmpFile setFileStore: [self fileStore]];
+ [tmpFile setFileID: fileID];
+
+ [[self fileStore] removeFileHandle: tmpFile];
+ [tmpFile release];
+ }
+
+ [[self fileStore] synchronize];
+}
+
+- (void)processServerMessage: (NSDictionary *)aMsg
+{
+ NSString *msgType;
+ id arg;
+
+ msgType = [aMsg objectForKey: @"type"];
+ arg = [aMsg objectForKey: @"arg"];
+ if ([msgType isEqualToString: RenHostnameKey]) {
+ [[self fileStore] setName: arg];
+ } else if ([msgType isEqualToString: RenFilesAddedKey]) {
+ [self processAddList: arg];
+ } else if ([msgType isEqualToString: RenFilesChangedKey]) {
+ [self processChangeList: arg];
+ } else if ([msgType isEqualToString: RenFilesRemovedKey]) {
+ [self processDeleteList: arg];
+ } else {
+ [LiLog logAsError: @"Unknown server message type: '%@'.", msgType];
+ }
+}
+
+- (void)processData: (NSData *)someData
+{
+ NSDictionary *msg;
+
+ [[self buffer] appendData: someData];
+ msg = [NSDictionary dictionaryWithEncodedData: [self buffer]];
+ if (msg != nil) {
+ [self setBuffer: [NSMutableData data]];
+ [self processServerMessage: msg];
+ }
+}
+
+- (void)browserRead: (NSNotification *)aNotification
+{
+ NSData *data;
+ NSFileHandle *remoteSocket;
+
+ remoteSocket = [aNotification object];
+ data = [[aNotification userInfo] objectForKey:
+ NSFileHandleNotificationDataItem];
+ if ([data length] > 0)
+ [self processData: data];
+ else {
+ [LiLog logAsDebug: @"browserRead shutdown"];
+ [self shutdown];
+ }
+
+ [[aNotification object] readInBackgroundAndNotify];
+}
+@end
+
+@implementation NSNetService (FileHandleExtensions)
+- (NSFileHandle *)fileHandle
+{
+ NSArray *serverAddresses;
+ NSData *data;
+ NSFileHandle *remoteSocket;
+ struct sockaddr_in *remoteAddr;
+ int remotePort, rc;
+
+ serverAddresses = [self addresses];
+ if ([serverAddresses count] <= 0)
+ return nil;
+
+ data = [serverAddresses objectAtIndex: 0];
+ remoteAddr = (struct sockaddr_in *)[data bytes];
+
+ remotePort = socket(AF_INET, SOCK_STREAM, 0);
+ remoteSocket = [[NSFileHandle alloc] initWithFileDescriptor: remotePort
+ closeOnDealloc: YES];
+ [remoteSocket autorelease];
+
+ rc = connect(remotePort,
+ (struct sockaddr *)remoteAddr,
+ sizeof(*remoteAddr));
+ if (rc == -1) {
+ [LiLog logAsWarning: @"couldn't connect to %@: %s.", [self name], strerror(errno)];
+ return nil;
+ }
+
+ return remoteSocket;
+}
+@end \ No newline at end of file
diff --git a/Liaison/ViewOptionsController.h b/Liaison/ViewOptionsController.h
new file mode 100644
index 0000000..87fee6d
--- /dev/null
+++ b/Liaison/ViewOptionsController.h
@@ -0,0 +1,24 @@
+/* ViewOptionsController */
+
+@class FileTableDelegate;
+
+@interface ViewOptionsController : NSObject
+{
+ NSMatrix *layoutMatrix;
+
+ IBOutlet FileTableDelegate *theFileDelegate;
+ IBOutlet id theHeaderField;
+ IBOutlet id theContentView;
+ IBOutlet NSWindow *theWindow;
+
+ NSMutableArray *theShownColumns;
+}
+
+- (IBAction)showWindow: (id)sender;
+@property (retain) id theHeaderField;
+@property (retain) FileTableDelegate *theFileDelegate;
+@property (retain) NSWindow *theWindow;
+@property (retain) NSMatrix *layoutMatrix;
+@property (retain) id theContentView;
+@property (retain) NSMutableArray *theShownColumns;
+@end
diff --git a/Liaison/ViewOptionsController.m b/Liaison/ViewOptionsController.m
new file mode 100644
index 0000000..21f4c47
--- /dev/null
+++ b/Liaison/ViewOptionsController.m
@@ -0,0 +1,158 @@
+#import "ViewOptionsController.h"
+
+#import "FileTableDelegate.h"
+
+@implementation ViewOptionsController
+- (id)init
+{
+ self = [super init];
+
+ theShownColumns = [[NSMutableArray alloc] init];
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [theShownColumns release];
+ [super dealloc];
+}
+
+- (void)toggledButton: (id)sender
+{
+ NSButtonCell *senderCell;
+ NSMutableArray *colOrder;
+ NSMutableDictionary *listPrefs;
+ NSString *colID;
+
+ senderCell = [sender selectedCell];
+ listPrefs = [theFileDelegate listPrefs];
+ colOrder = [listPrefs objectForKey: @"columnOrder"];
+ colID = [theShownColumns objectAtIndex: [senderCell tag]];
+ if (colID != nil) {
+ if ([senderCell state] == 1) {
+ [theFileDelegate showColumnWithIdentifier: colID];
+ [colOrder addObject: colID];
+ } else {
+ [theFileDelegate removeColumnWithIdentifier: colID];
+ [colOrder removeObject: colID];
+ }
+ }
+
+ [theFileDelegate setListPrefs: listPrefs];
+}
+
+- (void)sizeWindowToFit
+{
+ NSRect contentRect, headerRect, windowFrame, oldWindowFrame;
+
+ oldWindowFrame = [NSWindow contentRectForFrameRect: [theWindow frame]
+ styleMask: [theWindow styleMask]];
+
+ [layoutMatrix sizeToCells];
+ contentRect = [theContentView frame];
+ contentRect.size = [layoutMatrix bounds].size;
+ headerRect = [theHeaderField frame];
+
+ windowFrame.origin = oldWindowFrame.origin;
+ windowFrame.size.width = MAX(2*contentRect.origin.x + contentRect.size.width,
+ headerRect.origin.x + headerRect.size.width + 20);
+ windowFrame.size.height = headerRect.size.height + contentRect.size.height;
+ windowFrame = [NSWindow frameRectForContentRect: windowFrame
+ styleMask: [theWindow styleMask]];
+ [theWindow setFrame: windowFrame display: YES animate: NO];
+
+ [theHeaderField setFrameOrigin:
+ NSMakePoint(headerRect.origin.x, contentRect.size.height)];
+ [theContentView setFrame: contentRect];
+}
+
+- (IBAction)showWindow: (id)sender
+{
+ NSDictionary *browserColumns;
+ NSEnumerator *columnEnum;
+ NSMutableArray *optionCol;
+ NSString *identifier;
+ NSRect tmpRect;
+ unsigned int numRows;
+ int cellTag;
+
+ layoutMatrix = [[NSMatrix alloc] initWithFrame: NSMakeRect(0.0, 0.0, 0.0, 0.0)];
+ [layoutMatrix autorelease];
+ [layoutMatrix setMode: NSTrackModeMatrix];
+ [layoutMatrix setCellSize: NSMakeSize(100.0, 20.0)];
+ [layoutMatrix setDrawsBackground: YES];
+
+ cellTag = 0;
+ browserColumns = [theFileDelegate browserColumns];
+ numRows = ceil(sqrt([browserColumns count]));
+ optionCol = [NSMutableArray array];
+ columnEnum = [browserColumns keyEnumerator];
+ while ((identifier = [columnEnum nextObject]) != nil) {
+ LiBrowserColumn *col;
+
+ col = [theFileDelegate columnForIdentifier: identifier];
+ if (col != nil) {
+ NSButtonCell *checkBox;
+ NSTableColumn *tableCol;
+ BOOL checked;
+
+ tableCol = [[theFileDelegate tableView] tableColumnWithIdentifier: identifier];
+ if (tableCol == nil)
+ checked = 0;
+ else
+ checked = 1;
+
+ checkBox = [[[NSButtonCell alloc] init] autorelease];
+ [checkBox setButtonType: NSSwitchButton];
+ [checkBox setTitle: [col name]];
+ [checkBox setTag: cellTag];
+ [checkBox setTarget: self];
+ [checkBox setAction: @selector(toggledButton:)];
+ [checkBox setState: checked];
+
+ [theShownColumns addObject: identifier];
+
+ [optionCol addObject: checkBox];
+ if ([optionCol count] == numRows) {
+ if ([layoutMatrix numberOfColumns] == 0) {
+ unsigned int j;
+
+ [layoutMatrix addColumn];
+ for (j = [layoutMatrix numberOfRows]; j < numRows; j++) {
+ [layoutMatrix addRowWithCells:
+ [NSArray arrayWithObject: [optionCol objectAtIndex: j]]];
+ }
+ [layoutMatrix putCell: [optionCol objectAtIndex: 0]
+ atRow: 0 column: 0];
+ } else {
+ [layoutMatrix addColumnWithCells: optionCol];
+ }
+ [optionCol removeAllObjects];
+ }
+ }
+
+ cellTag++;
+ }
+
+ if ([optionCol count] > 0) {
+ while ([optionCol count] < numRows)
+ [optionCol addObject: [[[NSCell alloc] init] autorelease]];
+
+ [layoutMatrix addColumnWithCells: optionCol];
+ }
+
+ tmpRect = [layoutMatrix bounds];
+ [theContentView setFrameSize: tmpRect.size];
+ [theContentView addSubview: layoutMatrix];
+ [self sizeWindowToFit];
+
+ [theWindow makeKeyAndOrderFront: self];
+}
+@synthesize theShownColumns;
+@synthesize theFileDelegate;
+@synthesize theWindow;
+@synthesize theContentView;
+@synthesize theHeaderField;
+@synthesize layoutMatrix;
+@end
diff --git a/Liaison/WindowController.h b/Liaison/WindowController.h
new file mode 100644
index 0000000..38778c0
--- /dev/null
+++ b/Liaison/WindowController.h
@@ -0,0 +1,17 @@
+/* WindowController */
+
+@interface WindowController : NSObject
+{
+ IBOutlet NSTableView *theFileList;
+ IBOutlet NSOutlineView *theGroupList;
+ IBOutlet NSWindow *theInspectorWindow;
+ IBOutlet NSWindow *theMainWindow;
+
+ IBOutlet id theSearchField;
+}
+@property (retain) id theSearchField;
+@property (retain) NSTableView *theFileList;
+@property (retain) NSWindow *theMainWindow;
+@property (retain) NSWindow *theInspectorWindow;
+@property (retain) NSOutlineView *theGroupList;
+@end \ No newline at end of file
diff --git a/Liaison/WindowController.m b/Liaison/WindowController.m
new file mode 100644
index 0000000..5ecd5d9
--- /dev/null
+++ b/Liaison/WindowController.m
@@ -0,0 +1,306 @@
+#import "WindowController.h"
+
+#import "ApplicationController.h"
+#import "FileTableDelegate.h"
+#import "GroupTableDelegate.h"
+
+static NSString *
+myLocalizedString(NSString *aString)
+{
+ return NSLocalizedStringFromTable(aString, @"WindowElements", @"");
+}
+
+static void
+logRect(NSString *desc, NSRect aRect)
+{
+ [LiLog logAsDebug: @"%@, (%f, %f, %f, %f)", desc, aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height];
+}
+
+@implementation WindowController (WindowDelegate)
+- (NSRect)windowWillUseStandardFrame: (NSWindow *)aWindow
+ defaultFrame: (NSRect)defaultFrame
+{
+ FileTableDelegate *fileDelegate;
+ GroupTableDelegate *groupDelegate;
+ NSRect curGroupFrame, curFileFrame, windowFrame;
+ NSSize minGroupSize, minFileSize;
+ float newWidth, newHeight, splitterWidth, headerHeight;
+
+ [LiLog logAsDebug: @"[WindowController minSize]"];
+ [LiLog indentDebugLog];
+
+ groupDelegate = [theGroupList delegate];
+ fileDelegate = [theFileList delegate];
+
+ windowFrame = [aWindow frame];
+ logRect(@"Unadjusted window frame", windowFrame);
+
+ windowFrame = [NSWindow contentRectForFrameRect: [aWindow frame]
+ styleMask: [aWindow styleMask]];
+ logRect(@"Old window frame", windowFrame);
+
+ curFileFrame = [[theFileList superview] frame];
+ logRect(@"curFileFrame", curFileFrame);
+ curGroupFrame = [[theGroupList superview] frame];
+ logRect(@"curGroupFrame", curGroupFrame);
+
+ splitterWidth = windowFrame.size.width - (curFileFrame.size.width + curGroupFrame.size.width);
+ [LiLog logAsDebug: @"splitter width: %f", splitterWidth];
+ headerHeight = windowFrame.size.height - MAX(curFileFrame.size.height, curGroupFrame.size.height);
+ [LiLog logAsDebug: @"header height: %f, wf.height: %f, MAX(file,group).height: %f", headerHeight, windowFrame.size.height, MAX(curFileFrame.size.height, curGroupFrame.size.height)];
+
+ minGroupSize = [groupDelegate minSize];
+ [LiLog logAsDebug: @"minGroupSize: %f, %f", minGroupSize.width, minGroupSize.height];
+ minFileSize = [fileDelegate minSize];
+ [LiLog logAsDebug: @"minFileSize: %f, %f", minFileSize.width, minFileSize.height];
+
+ newWidth = minGroupSize.width + minFileSize.width + splitterWidth;
+ newHeight = MAX(minGroupSize.height, minFileSize.height) + headerHeight + 6.0;
+
+ [LiLog logAsDebug: @"newWidth, newHeight: %f, %f", newWidth, newHeight];
+
+ windowFrame.origin.y += windowFrame.size.height;
+ windowFrame.origin.y -= newHeight;
+ windowFrame.size = NSMakeSize(newWidth, newHeight);
+ windowFrame = [NSWindow frameRectForContentRect: windowFrame
+ styleMask: [aWindow styleMask]];
+
+ if ((windowFrame.origin.x + windowFrame.size.width) >
+ (defaultFrame.origin.x + defaultFrame.size.width)) {
+ [LiLog logAsDebug: @"Need to adjust width"];
+ if (windowFrame.size.width > defaultFrame.size.width) {
+ windowFrame.origin.x = defaultFrame.origin.x;
+ windowFrame.size.width = defaultFrame.size.width;
+ } else {
+ windowFrame.origin.x = defaultFrame.size.width - windowFrame.size.width;
+ }
+ }
+ if ((windowFrame.origin.y + windowFrame.size.height) >
+ (defaultFrame.origin.y + defaultFrame.size.height)) {
+ [LiLog logAsDebug: @"Need to adjust height"];
+ if (windowFrame.size.height > defaultFrame.size.height) {
+ windowFrame.origin.y = defaultFrame.origin.y;
+ windowFrame.size.height = defaultFrame.size.height;
+ } else {
+ windowFrame.origin.y = defaultFrame.size.height - windowFrame.size.height;
+ }
+ }
+ logRect(@"New window frame", windowFrame);
+
+ [LiLog unindentDebugLog];
+
+ return windowFrame;
+}
+
+- (void)windowDidBecomeKey: (NSNotification *)aNotificatin
+{
+ [LiLog logAsDebug: @"[WindowController windowDidBecomeKey]"];
+ [[theMainWindow firstResponder] becomeFirstResponder];
+}
+
+- (void)controlTextDidChange:(NSNotification *)aNotification
+{
+ FileTableDelegate *fileDelegate;
+
+ fileDelegate = [theFileList dataSource];
+ [fileDelegate saveSelectionOfTableView: theFileList];
+ [fileDelegate setSearchString:
+ [[[[aNotification object] stringValue] lowercaseString] retain]];
+ [fileDelegate redisplay];
+ [fileDelegate restoreSelectionToTableView: theFileList refresh: YES];
+}
+@end
+
+@implementation WindowController
+- (id)init
+{
+ self = [super init];
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [super dealloc];
+}
+
+- (void)setupToolbar
+{
+ NSToolbar *toolbar;
+
+ toolbar = [[[NSToolbar alloc]
+ initWithIdentifier: @"mainToolbar"] autorelease];
+ [toolbar setDelegate: self];
+ [toolbar setAllowsUserCustomization: YES];
+ [toolbar setAutosavesConfiguration: YES];
+ [theMainWindow setToolbar: toolbar];
+}
+
+- (void)awakeFromNib
+{
+ [self setupToolbar];
+}
+@synthesize theInspectorWindow;
+@synthesize theGroupList;
+@synthesize theFileList;
+@synthesize theSearchField;
+@synthesize theMainWindow;
+@end
+
+@implementation WindowController (ToolbarDelegateCategory)
+- (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar*)toolbar
+{
+ return [NSArray arrayWithObjects:
+ NSToolbarSeparatorItemIdentifier,
+ NSToolbarSpaceItemIdentifier,
+ NSToolbarFlexibleSpaceItemIdentifier,
+ NSToolbarCustomizeToolbarItemIdentifier,
+ @"AddFilesItem",
+ @"RemoveFilesItem",
+ @"SearchItem",
+ @"InfoItem",
+ @"AddGroupItem",
+ @"RemoveGroupItem",
+ @"RevealInFinderItem",
+ nil];
+}
+
+- (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar*)toolbar
+{
+ return [NSArray arrayWithObjects:
+ @"AddGroupItem",
+ @"RemoveGroupItem",
+ NSToolbarSeparatorItemIdentifier,
+ @"AddFilesItem",
+ @"RemoveFilesItem",
+ @"InfoItem",
+ @"RevealInFinderItem",
+ NSToolbarFlexibleSpaceItemIdentifier,
+ @"SearchItem",
+ NSToolbarSeparatorItemIdentifier,
+ NSToolbarCustomizeToolbarItemIdentifier,
+ nil];
+}
+
+- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar
+ itemForItemIdentifier:(NSString *)itemIdentifier
+ willBeInsertedIntoToolbar:(BOOL)flag
+{
+ NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:
+ itemIdentifier];
+
+ if ([itemIdentifier isEqualToString: @"AddFilesItem"]) {
+ [item setLabel: myLocalizedString(@"LiToolbarAddFileLabel")];
+ [item setPaletteLabel: myLocalizedString(@"LiToolbarAddFilePaletteLabel")];
+ [item setToolTip: myLocalizedString(@"LiToolbarAddFileToolTip")];
+
+ [item setImage: [NSImage imageNamed: @"AddFiles.tiff"]];
+ [item setTarget: self];
+ [item setAction: @selector(addToLibrary:)];
+ } else if ([itemIdentifier isEqualToString: @"RemoveFilesItem"]) {
+ [item setLabel: myLocalizedString(@"LiToolbarRemoveFileLabel")];
+ [item setPaletteLabel: myLocalizedString(@"LiToolbarRemoveFilePaletteLabel")];
+ [item setToolTip: myLocalizedString(@"LiToolbarRemoveFileToolTip")];
+
+ [item setImage: [NSImage imageNamed: @"RemoveFiles.tiff"]];
+ [item setTarget: self];
+ [item setAction: @selector(removeFiles:)];
+ } else if ([itemIdentifier isEqualToString: @"SearchItem"]) {
+ NSRect frame;
+
+ frame = [theSearchField frame];
+ [item setLabel: myLocalizedString(@"LiToolbarSearchLabel")];
+ [item setPaletteLabel: myLocalizedString(@"LiToolbarSearchPaletteLabel")];
+ [item setToolTip: myLocalizedString(@"LiToolbarSearchToolTip")];
+
+ [item setView: theSearchField];
+ [item setMinSize: frame.size];
+ [item setMaxSize: frame.size];
+ } else if ([itemIdentifier isEqualToString: @"InfoItem"]) {
+ [item setLabel: myLocalizedString(@"LiToolbarGetInfoLabel")];
+ [item setPaletteLabel: myLocalizedString(@"LiToolbarGetInfoPaletteLabel")];
+ [item setToolTip: myLocalizedString(@"LiToolbarGetInfoToolTip")];
+
+ [item setImage: [NSImage imageNamed: @"info (italic).tiff"]];
+ [item setTarget: self];
+ [item setAction: @selector(openInspector:)];
+ } else if ([itemIdentifier isEqualToString: @"AddGroupItem"]) {
+ [item setLabel: myLocalizedString(@"LiToolbarAddGroupLabel")];
+ [item setPaletteLabel: myLocalizedString(@"LiToolbarAddGroupPaletteLabel")];
+ [item setToolTip: myLocalizedString(@"LiToolbarAddGroupToolTip")];
+
+ [item setImage: [NSImage imageNamed: @"AddGroup.tiff"]];
+ [item setTarget: self];
+ [item setAction: @selector(addGroup:)];
+ } else if ([itemIdentifier isEqualToString: @"RemoveGroupItem"]) {
+ [item setLabel: myLocalizedString(@"LiToolbarRemoveGroupLabel")];
+ [item setPaletteLabel: myLocalizedString(@"LiToolbarRemoveGroupPaletteLabel")];
+ [item setToolTip: myLocalizedString(@"LiToolbarRemoveGroupToolTip")];
+
+ [item setImage: [NSImage imageNamed: @"RemoveGroup.tiff"]];
+ [item setTarget: self];
+ [item setAction: @selector(removeGroup:)];
+ } else if ([itemIdentifier isEqualToString: @"RevealInFinderItem"]) {
+ [item setLabel: myLocalizedString(@"LiToolbarRevealLabel")];
+ [item setPaletteLabel: myLocalizedString(@"LiToolbarRevealPaletteLabel")];
+ [item setToolTip: myLocalizedString(@"LiToolbarRevealToolTip")];
+
+ [item setImage: [NSImage imageNamed: @"reveal.tiff"]];
+ [item setTarget: self];
+ [item setAction: @selector(revealInFinder:)];
+ }
+ return [item autorelease];
+}
+
+- (BOOL)validateToolbarItem: (NSToolbarItem *)theItem
+{
+ GroupTableDelegate *groupDelegate;
+ FileTableDelegate *fileDelegate;
+
+ groupDelegate = [theGroupList delegate];
+ fileDelegate = [theFileList delegate];
+ if ([theItem action] == @selector(addToLibrary:)) {
+ return [fileDelegate validateAction: @selector(addFiles:)];
+ } else if ([theItem action] == @selector(revealInFinder:)) {
+ return [fileDelegate validateAction: @selector(revealInFinder:)];
+ } else if ([theItem action] == @selector(removeFiles:)) {
+ return [fileDelegate validateAction: @selector(delete:)];
+ } else if ([theItem action] == @selector(addGroup:)) {
+ return [groupDelegate validateAction: @selector(addGroup:)];
+ } else if ([theItem action] == @selector(removeGroup:)) {
+ return [groupDelegate validateAction: @selector(delete:)];
+ }
+
+ return YES;
+}
+
+- (IBAction)addGroup:(id)sender
+{
+ [[theGroupList delegate] addGroup: self];
+}
+
+- (IBAction)removeGroup:(id)sender
+{
+ [theGroupList performSelector: @selector(delete:) withObject: sender];
+}
+
+- (IBAction)addToLibrary:(id)sender
+{
+ [[theFileList delegate] addFiles: self];
+}
+
+- (IBAction)removeFiles:(id)sender
+{
+ [theFileList performSelector: @selector(delete:) withObject: sender];
+}
+
+- (IBAction)openInspector:(id)sender
+{
+ [[ApplicationController theApp] showInspectorWindow: self];
+}
+
+- (IBAction)revealInFinder:(id)sender
+{
+ [[theFileList delegate] revealInFinder: self];
+}
+@end \ No newline at end of file
diff --git a/Liaison/WriterThread.h b/Liaison/WriterThread.h
new file mode 100644
index 0000000..6dc8ece
--- /dev/null
+++ b/Liaison/WriterThread.h
@@ -0,0 +1,38 @@
+//
+// WriterThread.h
+// Liaison
+//
+// Created by Brian Cully on Wed Feb 26 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+#define WriterThreadDied @"LiWriterThreadDied"
+
+@interface WriterThread : NSObject {
+ NSMutableArray *theDataQueue;
+ NSConditionLock *theQueueLock;
+
+ NSFileHandle *theFile;
+
+ volatile BOOL theConnectionIsOpen;
+ volatile BOOL theKillFlag;
+}
+- (id)initWithFileHandle: (NSFileHandle *)aFileHandle;
+- (void)die;
+
+- (void)writeData: (NSData *)someData;
+@property (retain,getter=queueLock) NSConditionLock *theQueueLock;
+@property (retain,getter=dataQueue) NSMutableArray *theDataQueue;
+@property volatile BOOL theConnectionIsOpen;
+@property volatile BOOL theKillFlag;
+@property (assign,getter=file,setter=setFile:) NSFileHandle *theFile;
+@end
+
+@interface WriterThread (Accessors)
+- (NSMutableArray *)dataQueue;
+- (void)setDataQueue: (NSMutableArray *)aQueue;
+- (NSConditionLock *)queueLock;
+- (void)setQueueLock: (NSConditionLock *)aLock;
+- (NSFileHandle *)file;
+- (void)setFile: (NSFileHandle *)aFile;
+@end
diff --git a/Liaison/WriterThread.m b/Liaison/WriterThread.m
new file mode 100644
index 0000000..f34f8c9
--- /dev/null
+++ b/Liaison/WriterThread.m
@@ -0,0 +1,211 @@
+//
+// WriterThread.m
+// Liaison
+//
+// Created by Brian Cully on Wed Feb 26 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+#import "NSFileHandleExtensions.h"
+#import "WriterThread.h"
+
+#import <errno.h>
+#import <sys/types.h>
+#import <sys/uio.h>
+#import <unistd.h>
+
+#define NO_DATA 0
+#define HAS_DATA 1
+
+@implementation WriterThread
+- (id)initWithFileHandle: (NSFileHandle *)aFileHandle
+{
+ self = [super init];
+
+ theKillFlag = NO;
+ [self setDataQueue: [NSMutableArray array]];
+ [self setQueueLock:
+ [[[NSConditionLock alloc] initWithCondition: NO_DATA] autorelease]];
+ [self setFile: aFileHandle];
+
+ [NSThread detachNewThreadSelector: @selector(startThreadWithObject:)
+ toTarget: self
+ withObject: nil];
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [self setFile: nil];
+ [self setDataQueue: nil];
+ [self setQueueLock: nil];
+ [super dealloc];
+}
+
+- (BOOL)writeBufferedData: (NSData *)someData
+{
+ if (theConnectionIsOpen) {
+ int fileDescriptor;
+ ssize_t dataLen, wroteLen;
+ void *data;
+
+ fileDescriptor = [[self file] fileDescriptor];
+ data = (void *)[someData bytes];
+ dataLen = [someData length];
+ wroteLen = 0;
+ while (wroteLen < dataLen) {
+ ssize_t rc;
+
+ rc = write(fileDescriptor,
+ data+wroteLen,
+ dataLen-wroteLen);
+ if (rc >= 0) {
+ wroteLen += rc;
+ } else {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ theConnectionIsOpen = NO;
+ break;
+ }
+ }
+ }
+
+ return theConnectionIsOpen;
+}
+
+- (void)sendNextData
+{
+ NSData *dataToSend;
+ NSNotificationCenter *defaultCenter;
+
+ [[self queueLock] lockWhenCondition: HAS_DATA];
+ if (theKillFlag || theConnectionIsOpen == NO) {
+ return;
+ }
+ if ([[self dataQueue] count] <= 0) {
+ [[self queueLock] unlockWithCondition: NO_DATA];
+ [LiLog unindentDebugLog];
+ return;
+ }
+
+ dataToSend = [[[[self dataQueue] objectAtIndex: 0] retain] autorelease];
+ [[self dataQueue] removeObjectAtIndex: 0];
+ if ([[self dataQueue] count] > 0)
+ [[self queueLock] unlockWithCondition: HAS_DATA];
+ else
+ [[self queueLock] unlockWithCondition: NO_DATA];
+
+ defaultCenter = [NSNotificationCenter defaultCenter];
+ if ([self writeBufferedData: dataToSend] == NO) {
+ NSNotification *notification;
+
+ notification = [NSNotification notificationWithName: FileHandleClosed
+ object: [self file]];
+ [defaultCenter performSelectorOnMainThread: @selector(postNotification:)
+ withObject: notification
+ waitUntilDone: YES];
+ } else {
+ NSNotification *notification;
+
+ notification = [NSNotification notificationWithName: FileHandleWriteComplete
+ object: [self file]];
+ [defaultCenter performSelectorOnMainThread: @selector(postNotification:)
+ withObject: notification
+ waitUntilDone: NO];
+ }
+}
+
+- (void)startThreadWithObject: (id)anObject
+{
+ NSAutoreleasePool *rp;
+ NSDictionary *userInfo;
+ NSNotification *notification;
+ NSNotificationCenter *defaultCenter;
+ NSNumber *fd;
+
+ rp = [[NSAutoreleasePool alloc] init];
+
+ fd = [NSNumber numberWithInt: [[self file] fileDescriptor]];
+ theConnectionIsOpen = YES;
+ while (theKillFlag == NO) {
+ NSAutoreleasePool *srp;
+
+ srp = [[NSAutoreleasePool alloc] init];
+ [self sendNextData];
+ [srp release];
+ }
+
+ userInfo = [NSDictionary dictionaryWithObject: fd
+ forKey: @"FileDescriptorKey"];
+ defaultCenter = [NSNotificationCenter defaultCenter];
+ notification = [NSNotification notificationWithName: WriterThreadDied
+ object: self
+ userInfo: userInfo];
+ [defaultCenter performSelectorOnMainThread: @selector(postNotification:)
+ withObject: notification
+ waitUntilDone: YES];
+ [rp release];
+}
+
+- (void)die
+{
+ [[self queueLock] lock];
+ theKillFlag = YES;
+ [[self queueLock] unlockWithCondition: HAS_DATA];
+}
+
+- (void)writeData: (NSData *)someData
+{
+ if (someData == nil)
+ return;
+
+ [[self queueLock] lock];
+ NS_DURING
+ [[self dataQueue] addObject: someData];
+ NS_HANDLER
+ [LiLog logAsDebug: @"Got exception '%@' trying to add to data queue: %@.", [localException name], [localException reason]];
+ NS_ENDHANDLER
+ [[self queueLock] unlockWithCondition: HAS_DATA];
+}
+@synthesize theQueueLock;
+@synthesize theConnectionIsOpen;
+@synthesize theKillFlag;
+@synthesize theDataQueue;
+@end
+
+@implementation WriterThread (Accessors)
+- (NSMutableArray *)dataQueue
+{
+ return theDataQueue;
+}
+
+- (void)setDataQueue: (NSMutableArray *)aQueue
+{
+ [aQueue retain];
+ [theDataQueue release];
+ theDataQueue = aQueue;
+}
+
+- (NSConditionLock *)queueLock
+{
+ return theQueueLock;
+}
+
+- (void)setQueueLock: (NSConditionLock *)aLock
+{
+ [aLock retain];
+ [theQueueLock release];
+ theQueueLock = aLock;
+}
+
+- (NSFileHandle *)file
+{
+ return theFile;
+}
+
+- (void)setFile: (NSFileHandle *)aFile
+{
+ theFile = aFile;
+}
+@end \ No newline at end of file
diff --git a/Liaison/WriterThreadPool.h b/Liaison/WriterThreadPool.h
new file mode 100644
index 0000000..20825a5
--- /dev/null
+++ b/Liaison/WriterThreadPool.h
@@ -0,0 +1,17 @@
+//
+// WriterThreadPool.h
+// Liaison
+//
+// Created by Brian Cully on Wed Feb 26 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+@interface WriterThreadPool : NSObject {
+ NSMutableDictionary *theWriterThreads;
+}
++ (WriterThreadPool *)sharedPool;
+
+- (void)writeData: (NSData *)someData to: (NSFileHandle *)aFileHandle;
+- (void)killThreadFor: (NSFileHandle *)aFileHandle;
+@property (retain) NSMutableDictionary *theWriterThreads;
+@end
diff --git a/Liaison/WriterThreadPool.m b/Liaison/WriterThreadPool.m
new file mode 100644
index 0000000..6aefc44
--- /dev/null
+++ b/Liaison/WriterThreadPool.m
@@ -0,0 +1,95 @@
+//
+// WriterThreadPool.m
+// Liaison
+//
+// Created by Brian Cully on Wed Feb 26 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+
+#import "WriterThreadPool.h"
+
+#import "WriterThread.h"
+
+#import <signal.h>
+
+@implementation WriterThreadPool
+static WriterThreadPool *sharedPool = nil;
+
++ (WriterThreadPool *)sharedPool
+{
+ if (sharedPool == nil)
+ sharedPool = [[WriterThreadPool alloc] init];
+ return sharedPool;
+}
+
+void ign_handler()
+{
+ return;
+}
+
+- (id)init
+{
+ NSNotificationCenter *defaultCenter;
+ struct sigaction ign_action;
+
+ self = [super init];
+
+ ign_action.sa_handler = ign_handler;
+ sigemptyset(&ign_action.sa_mask);
+ ign_action.sa_flags = SA_RESTART;
+ sigaction(SIGPIPE, &ign_action, NULL);
+
+ theWriterThreads = [[NSMutableDictionary alloc] init];
+
+ defaultCenter = [NSNotificationCenter defaultCenter];
+ [defaultCenter addObserver: self
+ selector: @selector(writerThreadDied:)
+ name: WriterThreadDied
+ object: nil];
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [theWriterThreads release];
+ [super dealloc];
+}
+
+- (void)writeData: (NSData *)someData to: (NSFileHandle *)aFileHandle
+{
+ NSNumber *fd;
+ WriterThread *writer;
+
+ fd = [NSNumber numberWithInt: [aFileHandle fileDescriptor]];
+ writer = [theWriterThreads objectForKey: fd];
+ if (writer == nil) {
+ writer = [[WriterThread alloc] initWithFileHandle: aFileHandle];
+ [theWriterThreads setObject: writer forKey: fd];
+ [writer release];
+ }
+ [writer writeData: someData];
+}
+
+- (void)killThreadFor: (NSFileHandle *)aFileHandle
+{
+ NSNumber *fd;
+ WriterThread *writer;
+
+ fd = [NSNumber numberWithInt: [aFileHandle fileDescriptor]];
+ writer = [theWriterThreads objectForKey: fd];
+ [writer die];
+}
+
+- (void)writerThreadDied: (NSNotification *)aNotification
+{
+ NSNumber *fd;
+ WriterThread *writer;
+
+ writer = [aNotification object];
+
+ fd = [[aNotification userInfo] objectForKey: @"FileDescriptorKey"];
+ [theWriterThreads removeObjectForKey: fd];
+}
+@synthesize theWriterThreads;
+@end
diff --git a/Liaison/chef.lproj/CopyPanel.nib/classes.nib b/Liaison/chef.lproj/CopyPanel.nib/classes.nib
new file mode 100644
index 0000000..67bc2f5
--- /dev/null
+++ b/Liaison/chef.lproj/CopyPanel.nib/classes.nib
@@ -0,0 +1,38 @@
+{
+ IBClasses = (
+ {
+ CLASS = CopyController;
+ LANGUAGE = ObjC;
+ OUTLETS = {
+ theContentBox = NSBox;
+ theScrollView = NSScrollView;
+ theTemplate = DownloadStatusView;
+ theWindow = NSWindow;
+ };
+ SUPERCLASS = NSObject;
+ },
+ {
+ CLASS = DownloadStatusView;
+ LANGUAGE = ObjC;
+ OUTLETS = {
+ theButton = NSButton;
+ theFilename = NSTextField;
+ theIcon = NSImageView;
+ theProgressBar = NSProgressIndicator;
+ };
+ SUPERCLASS = NSView;
+ },
+ {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; },
+ {CLASS = FlippedBox; LANGUAGE = ObjC; SUPERCLASS = NSBox; },
+ {
+ CLASS = NIBConnector;
+ LANGUAGE = ObjC;
+ OUTLETS = {
+ theCopyController = CopyController;
+ thePreferencesController = PreferencesController;
+ };
+ SUPERCLASS = NSObject;
+ }
+ );
+ IBVersion = 1;
+} \ No newline at end of file
diff --git a/Liaison/chef.lproj/CopyPanel.nib/info.nib b/Liaison/chef.lproj/CopyPanel.nib/info.nib
new file mode 100644
index 0000000..cf2ac52
--- /dev/null
+++ b/Liaison/chef.lproj/CopyPanel.nib/info.nib
@@ -0,0 +1,17 @@
+<?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>60 38 356 240 0 0 1024 746 </string>
+ <key>IBEditorPositions</key>
+ <dict>
+ <key>16</key>
+ <string>373 456 220 82 0 0 1024 746 </string>
+ </dict>
+ <key>IBFramework Version</key>
+ <string>291.0</string>
+ <key>IBSystem Version</key>
+ <string>6L60</string>
+</dict>
+</plist>
diff --git a/Liaison/chef.lproj/CopyPanel.nib/objects.nib b/Liaison/chef.lproj/CopyPanel.nib/objects.nib
new file mode 100644
index 0000000..5ea039a
--- /dev/null
+++ b/Liaison/chef.lproj/CopyPanel.nib/objects.nib
Binary files differ
diff --git a/Liaison/chef.lproj/InfoPlist.strings b/Liaison/chef.lproj/InfoPlist.strings
new file mode 100644
index 0000000..150b632
--- /dev/null
+++ b/Liaison/chef.lproj/InfoPlist.strings
Binary files differ
diff --git a/Liaison/chef.lproj/LoadPanel.nib/JavaCompiling.plist b/Liaison/chef.lproj/LoadPanel.nib/JavaCompiling.plist
new file mode 100644
index 0000000..9153786
--- /dev/null
+++ b/Liaison/chef.lproj/LoadPanel.nib/JavaCompiling.plist
@@ -0,0 +1,8 @@
+<?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>JavaSourceSubpath</key>
+ <string>_LoadPanel_EOArchive_chef.java</string>
+</dict>
+</plist>
diff --git a/Liaison/chef.lproj/LoadPanel.nib/_LoadPanel_EOArchive_chef.java b/Liaison/chef.lproj/LoadPanel.nib/_LoadPanel_EOArchive_chef.java
new file mode 100644
index 0000000..cd2b01f
--- /dev/null
+++ b/Liaison/chef.lproj/LoadPanel.nib/_LoadPanel_EOArchive_chef.java
@@ -0,0 +1,143 @@
+// _LoadPanel_EOArchive_chef.java
+// Generated by EnterpriseObjects palette at Saturday, September 06, 2003 17:02:27 America/New_York
+
+import com.webobjects.eoapplication.*;
+import com.webobjects.eocontrol.*;
+import com.webobjects.eodistribution.client.*;
+import com.webobjects.eointerface.*;
+import com.webobjects.eointerface.swing.*;
+import com.webobjects.foundation.*;
+import java.awt.*;
+import javax.swing.*;
+import javax.swing.border.*;
+import javax.swing.table.*;
+import javax.swing.text.*;
+
+public class _LoadPanel_EOArchive_chef extends com.webobjects.eoapplication.EOArchive {
+ LoadPanelController _loadPanelController0;
+ com.webobjects.eointerface.swing.EOFrame _eoFrame0;
+ com.webobjects.eointerface.swing.EOTextField _nsTextField0, _nsTextField1;
+ com.webobjects.eointerface.swing.EOView _nsProgressIndicator0;
+ javax.swing.JPanel _nsView0;
+
+ public _LoadPanel_EOArchive_chef(Object owner, NSDisposableRegistry registry) {
+ super(owner, registry);
+ }
+
+ protected void _construct() {
+ Object owner = _owner();
+ EOArchive._ObjectInstantiationDelegate delegate = (owner instanceof EOArchive._ObjectInstantiationDelegate) ? (EOArchive._ObjectInstantiationDelegate)owner : null;
+ Object replacement;
+
+ super._construct();
+
+
+ if ((delegate != null) && ((replacement = delegate.objectForOutletPath(this, "theLoadPanelController.thePathField")) != null)) {
+ _nsTextField1 = (replacement == EOArchive._ObjectInstantiationDelegate.NullObject) ? null : (com.webobjects.eointerface.swing.EOTextField)replacement;
+ _replacedObjects.setObjectForKey(replacement, "_nsTextField1");
+ } else {
+ _nsTextField1 = (com.webobjects.eointerface.swing.EOTextField)_registered(new com.webobjects.eointerface.swing.EOTextField(), "NSTextField1");
+ }
+
+ if ((delegate != null) && ((replacement = delegate.objectForOutletPath(this, "theLoadPanelController.theProgressBar")) != null)) {
+ _nsProgressIndicator0 = (replacement == EOArchive._ObjectInstantiationDelegate.NullObject) ? null : (com.webobjects.eointerface.swing.EOView)replacement;
+ _replacedObjects.setObjectForKey(replacement, "_nsProgressIndicator0");
+ } else {
+ _nsProgressIndicator0 = (com.webobjects.eointerface.swing.EOView)_registered(new com.webobjects.eointerface.swing.EOView(), "1");
+ }
+
+ if ((delegate != null) && ((replacement = delegate.objectForOutletPath(this, "theLoadPanelController")) != null)) {
+ _loadPanelController0 = (replacement == EOArchive._ObjectInstantiationDelegate.NullObject) ? null : (LoadPanelController)replacement;
+ _replacedObjects.setObjectForKey(replacement, "_loadPanelController0");
+ } else {
+ _loadPanelController0 = (LoadPanelController)_registered(new LoadPanelController(), "LoadPanelController");
+ }
+
+ if ((delegate != null) && ((replacement = delegate.objectForOutletPath(this, "theLoadPanelController.theStatusField")) != null)) {
+ _nsTextField0 = (replacement == EOArchive._ObjectInstantiationDelegate.NullObject) ? null : (com.webobjects.eointerface.swing.EOTextField)replacement;
+ _replacedObjects.setObjectForKey(replacement, "_nsTextField0");
+ } else {
+ _nsTextField0 = (com.webobjects.eointerface.swing.EOTextField)_registered(new com.webobjects.eointerface.swing.EOTextField(), "NSTextField2");
+ }
+
+ if ((delegate != null) && ((replacement = delegate.objectForOutletPath(this, "theLoadPanelController.theLoadPanel")) != null)) {
+ _eoFrame0 = (replacement == EOArchive._ObjectInstantiationDelegate.NullObject) ? null : (com.webobjects.eointerface.swing.EOFrame)replacement;
+ _replacedObjects.setObjectForKey(replacement, "_eoFrame0");
+ } else {
+ _eoFrame0 = (com.webobjects.eointerface.swing.EOFrame)_registered(new com.webobjects.eointerface.swing.EOFrame(), "Load Panel");
+ }
+
+ _nsView0 = (JPanel)_eoFrame0.getContentPane();
+ }
+
+ protected void _awaken() {
+ super._awaken();
+
+ if (_replacedObjects.objectForKey("_loadPanelController0") == null) {
+ _connect(_owner(), _loadPanelController0, "theLoadPanelController");
+ }
+ }
+
+ protected void _init() {
+ super._init();
+
+ if (_replacedObjects.objectForKey("_loadPanelController0") == null) {
+ _connect(_loadPanelController0, _eoFrame0, "theLoadPanel");
+ }
+
+ if (_replacedObjects.objectForKey("_nsTextField1") == null) {
+ _setFontForComponent(_nsTextField1, "Lucida Grande", 11, Font.PLAIN);
+ _nsTextField1.setEditable(false);
+ _nsTextField1.setOpaque(false);
+ _nsTextField1.setText("Luedeeng feeles\u2026");
+ _nsTextField1.setHorizontalAlignment(javax.swing.JTextField.LEFT);
+ _nsTextField1.setSelectable(false);
+ _nsTextField1.setEnabled(true);
+ _nsTextField1.setBorder(null);
+ }
+
+ if (_replacedObjects.objectForKey("_loadPanelController0") == null) {
+ _connect(_loadPanelController0, _nsTextField1, "thePathField");
+ }
+
+ if (_replacedObjects.objectForKey("_loadPanelController0") == null) {
+ _connect(_loadPanelController0, _nsProgressIndicator0, "theProgressBar");
+ }
+
+ if (_replacedObjects.objectForKey("_loadPanelController0") == null) {
+ _connect(_loadPanelController0, _nsTextField0, "theStatusField");
+ }
+
+ if (_replacedObjects.objectForKey("_nsTextField0") == null) {
+ _setFontForComponent(_nsTextField0, "Lucida Grande", 11, Font.PLAIN);
+ _nsTextField0.setEditable(false);
+ _nsTextField0.setOpaque(false);
+ _nsTextField0.setText("Getteeng inffu fur:");
+ _nsTextField0.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
+ _nsTextField0.setSelectable(false);
+ _nsTextField0.setEnabled(true);
+ _nsTextField0.setBorder(null);
+ }
+
+ if (!(_nsView0.getLayout() instanceof EOViewLayout)) { _nsView0.setLayout(new EOViewLayout()); }
+ _nsTextField0.setSize(113, 18);
+ _nsTextField0.setLocation(10, 10);
+ ((EOViewLayout)_nsView0.getLayout()).setAutosizingMask(_nsTextField0, EOViewLayout.WidthSizable | EOViewLayout.MinYMargin);
+ _nsView0.add(_nsTextField0);
+ _nsTextField1.setSize(211, 18);
+ _nsTextField1.setLocation(125, 10);
+ ((EOViewLayout)_nsView0.getLayout()).setAutosizingMask(_nsTextField1, EOViewLayout.WidthSizable | EOViewLayout.MinYMargin);
+ _nsView0.add(_nsTextField1);
+ _nsProgressIndicator0.setSize(324, 20);
+ _nsProgressIndicator0.setLocation(11, 36);
+ ((EOViewLayout)_nsView0.getLayout()).setAutosizingMask(_nsProgressIndicator0, EOViewLayout.WidthSizable | EOViewLayout.MinYMargin);
+ _nsView0.add(_nsProgressIndicator0);
+
+ if (_replacedObjects.objectForKey("_eoFrame0") == null) {
+ _nsView0.setSize(346, 66);
+ _eoFrame0.setTitle("");
+ _eoFrame0.setLocation(401, 733);
+ _eoFrame0.setSize(346, 66);
+ }
+ }
+}
diff --git a/Liaison/chef.lproj/LoadPanel.nib/classes.nib b/Liaison/chef.lproj/LoadPanel.nib/classes.nib
new file mode 100644
index 0000000..e1ab46c
--- /dev/null
+++ b/Liaison/chef.lproj/LoadPanel.nib/classes.nib
@@ -0,0 +1,28 @@
+{
+ IBClasses = (
+ {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; },
+ {
+ CLASS = LoadPanelController;
+ LANGUAGE = ObjC;
+ OUTLETS = {
+ theLoadPanel = NSPanel;
+ thePathField = NSTextField;
+ theProgressBar = NSProgressIndicator;
+ theStatusField = NSTextField;
+ };
+ SUPERCLASS = NSObject;
+ },
+ {
+ ACTIONS = {showDownloadWindow = id; };
+ CLASS = NIBConnector;
+ LANGUAGE = ObjC;
+ OUTLETS = {
+ theCopyController = CopyController;
+ theLoadPanelController = LoadPanelController;
+ thePreferencesController = PreferencesController;
+ };
+ SUPERCLASS = NSObject;
+ }
+ );
+ IBVersion = 1;
+} \ No newline at end of file
diff --git a/Liaison/chef.lproj/LoadPanel.nib/info.nib b/Liaison/chef.lproj/LoadPanel.nib/info.nib
new file mode 100644
index 0000000..7484a71
--- /dev/null
+++ b/Liaison/chef.lproj/LoadPanel.nib/info.nib
@@ -0,0 +1,16 @@
+<?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>89 245 356 240 0 0 1600 1178 </string>
+ <key>IBFramework Version</key>
+ <string>291.0</string>
+ <key>IBOpenObjects</key>
+ <array>
+ <integer>6</integer>
+ </array>
+ <key>IBSystem Version</key>
+ <string>6L60</string>
+</dict>
+</plist>
diff --git a/Liaison/chef.lproj/LoadPanel.nib/objects.nib b/Liaison/chef.lproj/LoadPanel.nib/objects.nib
new file mode 100644
index 0000000..6126624
--- /dev/null
+++ b/Liaison/chef.lproj/LoadPanel.nib/objects.nib
Binary files differ
diff --git a/Liaison/chef.lproj/MainMenu.nib/classes.nib b/Liaison/chef.lproj/MainMenu.nib/classes.nib
new file mode 100644
index 0000000..209ba77
--- /dev/null
+++ b/Liaison/chef.lproj/MainMenu.nib/classes.nib
@@ -0,0 +1,120 @@
+{
+ IBClasses = (
+ {
+ ACTIONS = {
+ openHomepage = id;
+ showInspectorWindow = id;
+ showMainWindow = id;
+ showPreferenceWindow = id;
+ };
+ CLASS = ApplicationController;
+ LANGUAGE = ObjC;
+ OUTLETS = {
+ fileDelegate = FileTableDelegate;
+ fileTableView = NSTableView;
+ groupDelegate = GroupTableDelegate;
+ groupOutlineView = NSOutlineView;
+ inspectorWindow = NSWindow;
+ mainWindow = NSWindow;
+ openMenuItem = NSMenuItem;
+ showInspectorWindowMenuItem = NSMenuItem;
+ showMainWindowMenuItem = NSMenuItem;
+ };
+ SUPERCLASS = NSObject;
+ },
+ {
+ CLASS = CopyController;
+ LANGUAGE = ObjC;
+ OUTLETS = {theFilenameField = NSTextField; theProgressBar = NSProgressIndicator; };
+ SUPERCLASS = NSObject;
+ },
+ {
+ ACTIONS = {addFiles = id; };
+ CLASS = FileTableDelegate;
+ LANGUAGE = ObjC;
+ OUTLETS = {
+ inspectorController = InspectorController;
+ statusLine = NSTextField;
+ tableView = NSTableView;
+ };
+ SUPERCLASS = NSObject;
+ },
+ {
+ ACTIONS = {deselectAll = id; };
+ CLASS = FirstResponder;
+ LANGUAGE = ObjC;
+ SUPERCLASS = NSObject;
+ },
+ {CLASS = FlippedBox; LANGUAGE = ObjC; SUPERCLASS = NSBox; },
+ {
+ ACTIONS = {addGroup = id; };
+ CLASS = GroupTableDelegate;
+ LANGUAGE = ObjC;
+ OUTLETS = {
+ outlineView = NSOutlineView;
+ statusLine = NSTextField;
+ theFileDelegate = FileTableDelegate;
+ theWindow = WindowController;
+ };
+ SUPERCLASS = NSObject;
+ },
+ {
+ CLASS = InspectorController;
+ LANGUAGE = ObjC;
+ OUTLETS = {theDefaultTabView = NSView; theTabView = NSTabView; theWindow = NSWindow; };
+ SUPERCLASS = NSObject;
+ },
+ {CLASS = LiScrollView; LANGUAGE = ObjC; SUPERCLASS = NSScrollView; },
+ {
+ ACTIONS = {delete = id; };
+ CLASS = LiTableView;
+ LANGUAGE = ObjC;
+ SUPERCLASS = NSTableView;
+ },
+ {
+ ACTIONS = {showDownloadWindow = id; };
+ CLASS = NIBConnector;
+ LANGUAGE = ObjC;
+ OUTLETS = {
+ theCopyController = CopyController;
+ theLoadPanelController = LoadPanelController;
+ thePreferencesController = PreferencesController;
+ };
+ SUPERCLASS = NSObject;
+ },
+ {
+ ACTIONS = {showWindow = id; };
+ CLASS = ViewOptionsController;
+ LANGUAGE = ObjC;
+ OUTLETS = {
+ theContentView = id;
+ theFileDelegate = FileTableDelegate;
+ theHeaderField = id;
+ theWindow = NSWindow;
+ };
+ SUPERCLASS = NSObject;
+ },
+ {
+ ACTIONS = {
+ addGroup = id;
+ addToLibrary = id;
+ openFile = id;
+ openInspector = id;
+ removeFiles = id;
+ removeGroup = id;
+ revealInFinder = id;
+ };
+ CLASS = WindowController;
+ LANGUAGE = ObjC;
+ OUTLETS = {
+ theFileList = NSTableView;
+ theGroupList = NSOutlineView;
+ theInspectorWindow = NSWindow;
+ theMainWindow = NSWindow;
+ theSearchField = id;
+ };
+ SUPERCLASS = NSObject;
+ }
+ );
+ IBVersion = 1;
+} \ No newline at end of file
diff --git a/Liaison/chef.lproj/MainMenu.nib/info.nib b/Liaison/chef.lproj/MainMenu.nib/info.nib
new file mode 100644
index 0000000..bb7213f
--- /dev/null
+++ b/Liaison/chef.lproj/MainMenu.nib/info.nib
@@ -0,0 +1,38 @@
+<?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>23 72 461 291 0 0 1024 746 </string>
+ <key>IBEditorPositions</key>
+ <dict>
+ <key>29</key>
+ <string>3 696 310 44 0 0 1024 746 </string>
+ <key>377</key>
+ <string>423 486 177 62 0 0 1024 746 </string>
+ <key>700</key>
+ <string>287 435 224 118 0 0 1024 746 </string>
+ </dict>
+ <key>IBFramework Version</key>
+ <string>291.0</string>
+ <key>IBGroupedObjects</key>
+ <dict>
+ <key>1</key>
+ <array>
+ <string>666</string>
+ <string>671</string>
+ <string>672</string>
+ </array>
+ </dict>
+ <key>IBLastGroupID</key>
+ <string>2</string>
+ <key>IBOpenObjects</key>
+ <array>
+ <integer>21</integer>
+ <integer>301</integer>
+ <integer>208</integer>
+ </array>
+ <key>IBSystem Version</key>
+ <string>6L60</string>
+</dict>
+</plist>
diff --git a/Liaison/chef.lproj/MainMenu.nib/objects.nib b/Liaison/chef.lproj/MainMenu.nib/objects.nib
new file mode 100644
index 0000000..5fb322e
--- /dev/null
+++ b/Liaison/chef.lproj/MainMenu.nib/objects.nib
Binary files differ
diff --git a/Liaison/chef.lproj/PreferencesWindow.nib/classes.nib b/Liaison/chef.lproj/PreferencesWindow.nib/classes.nib
new file mode 100644
index 0000000..76a7d6c
--- /dev/null
+++ b/Liaison/chef.lproj/PreferencesWindow.nib/classes.nib
@@ -0,0 +1,50 @@
+{
+ IBClasses = (
+ {
+ ACTIONS = {
+ addFiles = id;
+ addGroup = id;
+ removeGroup = id;
+ showInspectorWindow = id;
+ showMainWindow = id;
+ };
+ CLASS = ApplicationController;
+ LANGUAGE = ObjC;
+ OUTLETS = {
+ fileDelegate = FileTableDelegate;
+ fileTableView = NSTableView;
+ groupDelegate = GroupTableDelegate;
+ groupOutlineView = NSOutlineView;
+ inspectorWindow = NSWindow;
+ mainWindow = NSWindow;
+ openMenuItem = NSMenuItem;
+ showInspectorWindowMenuItem = NSMenuItem;
+ showMainWindowMenuItem = NSMenuItem;
+ thePanelController = LoadPanelController;
+ thePreferenceController = PreferenceController;
+ };
+ SUPERCLASS = NSObject;
+ },
+ {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; },
+ {
+ CLASS = NIBConnector;
+ LANGUAGE = ObjC;
+ OUTLETS = {thePreferencesController = PreferencesController; };
+ SUPERCLASS = NSObject;
+ },
+ {
+ ACTIONS = {applyChanges = id; selectDownloadDirectory = id; toggleNetworkEnabled = id; };
+ CLASS = PreferencesController;
+ LANGUAGE = ObjC;
+ OUTLETS = {
+ theDownloadField = NSTextField;
+ theHostnameField = NSTextField;
+ theHostnameFieldDescription = NSTextField;
+ theNetworkEnabledButton = NSButton;
+ theWindow = NSWindow;
+ };
+ SUPERCLASS = NSObject;
+ }
+ );
+ IBVersion = 1;
+} \ No newline at end of file
diff --git a/Liaison/chef.lproj/PreferencesWindow.nib/info.nib b/Liaison/chef.lproj/PreferencesWindow.nib/info.nib
new file mode 100644
index 0000000..ba3c2a9
--- /dev/null
+++ b/Liaison/chef.lproj/PreferencesWindow.nib/info.nib
@@ -0,0 +1,38 @@
+<?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>50 54 356 240 0 0 1600 1178 </string>
+ <key>IBFramework Version</key>
+ <string>326.0</string>
+ <key>IBGroupedObjects</key>
+ <dict>
+ <key>0</key>
+ <array>
+ <string>39</string>
+ <string>40</string>
+ <string>49</string>
+ </array>
+ <key>1</key>
+ <array>
+ <string>21</string>
+ <string>22</string>
+ </array>
+ <key>2</key>
+ <array>
+ <string>34</string>
+ <string>35</string>
+ <string>36</string>
+ </array>
+ </dict>
+ <key>IBLastGroupID</key>
+ <string>3</string>
+ <key>IBOpenObjects</key>
+ <array>
+ <integer>5</integer>
+ </array>
+ <key>IBSystem Version</key>
+ <string>7B28</string>
+</dict>
+</plist>
diff --git a/Liaison/chef.lproj/PreferencesWindow.nib/objects.nib b/Liaison/chef.lproj/PreferencesWindow.nib/objects.nib
new file mode 100644
index 0000000..764eedf
--- /dev/null
+++ b/Liaison/chef.lproj/PreferencesWindow.nib/objects.nib
Binary files differ
diff --git a/Liaison/chef.lproj/WindowElements.strings b/Liaison/chef.lproj/WindowElements.strings
new file mode 100644
index 0000000..9753251
--- /dev/null
+++ b/Liaison/chef.lproj/WindowElements.strings
@@ -0,0 +1,37 @@
+/* Common strings */
+LiFileSingular = "feele-a";
+LiFilePlural = "feeles";
+
+/* Default group names. */
+LiRendezvousGroupName = "Rendezfuoos";
+LiUntitledGroupName = "unteetled";
+
+/* Toolbar strings. */
+LiToolbarAddFileLabel = "Edd Feeles";
+LiToolbarAddFilePaletteLabel = "Edd Feeles tu Leebrery";
+LiToolbarAddFileToolTip = "Edd feeles tu yuoor leebrery.";
+LiToolbarRemoveFileLabel = "Remufe-a Feele-a";
+LiToolbarRemoveFilesLabel = "Remufe-a Feeles";
+LiToolbarRemoveFilePaletteLabel = "Remufe-a frum Gruoop";
+LiToolbarRemoveFileToolTip = "Remufe-a zee coorrently selected feeles.";
+LiToolbarRemoveFilesFromLibraryToolTip = "Remufe-a zee coorrently selected %@ frum yuoor leebrery.";
+LiToolbarRemoveFilesFromGroupToolTip = "Remufe-a zee coorrently selected %@ frum zee gruoop.";
+LiToolbarSearchLabel = "Seerch";
+LiToolbarSearchPaletteLabel = "Seerch in Gruoop";
+LiToolbarSearchToolTip = "Seerch in selected gruoop.";
+LiToolbarGetInfoLabel = "Get Inffu";
+LiToolbarGetInfoPaletteLabel = "Get Inffu";
+LiToolbarGetInfoToolTip = "Oopee zee inspectur veendoo.";
+LiToolbarAddGroupLabel = "Edd Gruoop";
+LiToolbarAddGroupPaletteLabel = "Edd Gruoop";
+LiToolbarAddGroupToolTip = "Creete-a a noo gruoop.";
+LiToolbarRemoveGroupLabel = "Delete-a Gruoop";
+LiToolbarRemoveGroupPaletteLabel = "Delete-a Gruoop";
+LiToolbarRemoveGroupToolTip = "Deletes zee coorrently selected gruoop.";
+LiToolbarRevealLabel = "Shoo in Finder";
+LiToolbarRevealPaletteLabel = "Shoo in Finder";
+LiToolbarRevealToolTip = "Shoo zee selected item in zee Finder.";
+
+/* File load panel. */
+LiLoadPanelTitle = "Edd tu Leebrery";
+LiLoadingDirectory = "Scunneeng: "; \ No newline at end of file
diff --git a/Liaison/main.m b/Liaison/main.m
new file mode 100644
index 0000000..88c49d7
--- /dev/null
+++ b/Liaison/main.m
@@ -0,0 +1,12 @@
+//
+// main.m
+// Liaison
+//
+// Created by Brian Cully on Sun Feb 02 2003.
+// Copyright (c) 2003 Brian Cully. All rights reserved.
+//
+int main(int argc, const char *argv[])
+{
+ [NSFileHandleExtensions poseAsClass: [NSFileHandle class]];
+ return NSApplicationMain(argc, argv);
+}