Revision: 13796
          http://sourceforge.net/p/skim-app/code/13796
Author:   hofman
Date:     2023-11-24 15:21:52 +0000 (Fri, 24 Nov 2023)
Log Message:
-----------
Add optional use of XPC connection to get skim notes for test app

Modified Paths:
--------------
    trunk/SkimNotes/SKNDocument.h
    trunk/SkimNotes/SKNDocument.m
    trunk/SkimNotes/SKNSkimReader.h
    trunk/SkimNotes/SKNSkimReader.m
    trunk/SkimNotes/SkimNotes.xcodeproj/project.pbxproj

Added Paths:
-----------
    trunk/SkimNotes/SKNXPCSkimReader.h
    trunk/SkimNotes/SKNXPCSkimReader.m

Modified: trunk/SkimNotes/SKNDocument.h
===================================================================
--- trunk/SkimNotes/SKNDocument.h       2023-11-24 14:49:15 UTC (rev 13795)
+++ trunk/SkimNotes/SKNDocument.h       2023-11-24 15:21:52 UTC (rev 13796)
@@ -39,10 +39,11 @@
 
 #import <Cocoa/Cocoa.h>
 
-// Uncomment one of these three #defines
+// Uncomment one of these four #defines
 
 #define FrameworkSample
 //#define AgentSample
+//#define XPCAgentSample
 //#define ToolSample
 
 @interface SKNDocument : NSDocument {

Modified: trunk/SkimNotes/SKNDocument.m
===================================================================
--- trunk/SkimNotes/SKNDocument.m       2023-11-24 14:49:15 UTC (rev 13795)
+++ trunk/SkimNotes/SKNDocument.m       2023-11-24 15:21:52 UTC (rev 13796)
@@ -39,6 +39,7 @@
 #import "SKNDocument.h"
 #import <SkimNotesBase/SkimNotesBase.h>
 #import "SKNSkimReader.h"
+#import "SKNXPCSkimReader.h"
 
 #define SKNPDFDocumentType @"com.adobe.pdf"
 #define SKNPDFBundleDocumentType @"net.sourceforge.skim-app.pdfd"
@@ -53,11 +54,15 @@
 + (void)initialize {
     [NSValueTransformer setValueTransformer:[[[SKNPlusOneTransformer alloc] 
init] autorelease] forName:@"SKNPlusOne"];
 }
- 
+
++ (BOOL)autosavesInPlace {
+    return NO;
+}
+
 - (id)init {
     self = [super init];
     if (self) {
-        notes = [[NSArray alloc] init];    
+        notes = [[NSArray alloc] init];
     }
     return self;
 }
@@ -134,6 +139,22 @@
         }
     }
 
+#elif defined(XPCAgentSample)
+    
+    NSData *data = nil;
+    
+    if ([ws type:docType conformsToType:SKNPDFDocumentType] ||
+        [ws type:docType conformsToType:SKNPDFBundleDocumentType] ||
+        [ws type:docType conformsToType:SKNSkimNotesDocumentType]) {
+        data = [[SKNXPCSkimReader sharedReader] SkimNotesAtURL:absoluteURL];
+        if (data) {
+            @try { array = [NSKeyedUnarchiver unarchiveObjectWithData:data]; }
+            @catch (id e) {}
+            if (array == nil)
+                array = [NSPropertyListSerialization propertyListWithData:data 
options:NSPropertyListImmutable format:NULL error:NULL];
+        }
+    }
+
 #elif defined(ToolSample)
     
     NSData *data = nil;

Modified: trunk/SkimNotes/SKNSkimReader.h
===================================================================
--- trunk/SkimNotes/SKNSkimReader.h     2023-11-24 14:49:15 UTC (rev 13795)
+++ trunk/SkimNotes/SKNSkimReader.h     2023-11-24 15:21:52 UTC (rev 13796)
@@ -48,11 +48,10 @@
     id agent;
 }
 
-+ (id)sharedReader;
+@property (class, nonatomic, readonly) id sharedReader;
 
-- (NSString *)agentIdentifier;
-// this should only be used before any of the following calls is made
-- (void)setAgentIdentifier:(NSString *)identifier;
+// this should only be set before any of the following calls is made
+@property (nonatomic, retain) NSString *agentIdentifier;
 
 - (NSData *)SkimNotesAtURL:(NSURL *)fileURL;
 - (NSData *)RTFNotesAtURL:(NSURL *)fileURL;

Modified: trunk/SkimNotes/SKNSkimReader.m
===================================================================
--- trunk/SkimNotes/SKNSkimReader.m     2023-11-24 14:49:15 UTC (rev 13795)
+++ trunk/SkimNotes/SKNSkimReader.m     2023-11-24 15:21:52 UTC (rev 13796)
@@ -43,6 +43,8 @@
 
 @implementation SKNSkimReader
 
+@synthesize agentIdentifier;
+
 + (id)sharedReader {
     static id sharedReader = nil;
     if (nil == sharedReader)
@@ -72,10 +74,6 @@
     [super dealloc];
 }
 
-- (NSString *)agentIdentifier {
-    return agentIdentifier;
-}
-
 - (void)setAgentIdentifier:(NSString *)identifier {
     NSAssert(connection == nil, @"agentIdentifier must be set before 
connecting");
     if (connection == nil && agentIdentifier != identifier) {

Added: trunk/SkimNotes/SKNXPCSkimReader.h
===================================================================
--- trunk/SkimNotes/SKNXPCSkimReader.h                          (rev 0)
+++ trunk/SkimNotes/SKNXPCSkimReader.h  2023-11-24 15:21:52 UTC (rev 13796)
@@ -0,0 +1,66 @@
+//
+//  SKNXPCSkimReader.h
+//  SkimNotesTest
+//
+//  Created by Christiaan Hofman on 24/11/2023.
+/*
+ This software is Copyright (c) 2023
+ Christiaan Hofman. All rights reserved.
+ 
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 
+ - Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 
+ - Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ 
+ - Neither the name of Adam Maxwell nor the names of any
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+ 
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+
+@interface SKNXPCSkimReader : NSObject {
+    NSString *agentIdentifier;
+    NSXPCConnection *connection;
+    id agent;
+    BOOL synchronous;
+}
+
++ (id)sharedReader;
+
+// this should only be set before any of the following calls is made
+@property (nonatomic, retain) NSString *agentIdentifier;
+
+// should use either the synchronous or the asynchronous methods, not both
+
+// synchronous retrieval
+- (NSData *)SkimNotesAtURL:(NSURL *)fileURL;
+- (NSData *)RTFNotesAtURL:(NSURL *)fileURL;
+- (NSString *)textNotesAtURL:(NSURL *)fileURL;
+
+// asynchronous retrieval
+- (void)readSkimNotesAtURL:(NSURL *)fileURL reply:(void (^)(NSData *))reply;
+- (void)readRTFNotesAtURL:(NSURL *)fileURL reply:(void (^)(NSData *))reply;
+- (void)readTextNotesAtURL:(NSURL *)fileURL reply:(void (^)(NSString *))reply;
+
+@end

Added: trunk/SkimNotes/SKNXPCSkimReader.m
===================================================================
--- trunk/SkimNotes/SKNXPCSkimReader.m                          (rev 0)
+++ trunk/SkimNotes/SKNXPCSkimReader.m  2023-11-24 15:21:52 UTC (rev 13796)
@@ -0,0 +1,239 @@
+//
+//  SKNXPCSkimReader.m
+//  SkimNotesTest
+//
+//  Created by Christiaan Hofman on 24/11/2023.
+/*
+ This software is Copyright (c) 2023
+ Christiaan Hofman. All rights reserved.
+ 
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 
+ - Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 
+ - Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ 
+ - Neither the name of Adam Maxwell nor the names of any
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+ 
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "SKNXPCSkimReader.h"
+#import "SKNXPCAgentListenerProtocol.h"
+#import <ServiceManagement/ServiceManagement.h>
+
+@implementation SKNXPCSkimReader
+
+@synthesize agentIdentifier;
+
++ (id)sharedReader {
+    static id sharedReader = nil;
+    if (nil == sharedReader)
+        sharedReader = [[self alloc] init];
+    return sharedReader;
+}
+
+- (void)destroyConnection {
+    [agent release];
+    agent = nil;
+    
+    [connection invalidate];
+    [connection release];
+    connection = nil;
+}
+
+- (void)dealloc {
+    [[NSNotificationCenter defaultCenter] removeObserver:self];
+    [self destroyConnection];
+    [agentIdentifier release];
+    [super dealloc];
+}
+
+- (void)setAgentIdentifier:(NSString *)identifier {
+    NSAssert(connection == nil, @"agentIdentifier must be set before 
connecting");
+    if (connection == nil && agentIdentifier != identifier) {
+        [agentIdentifier release];
+        agentIdentifier = [identifier retain];
+    }
+}
+
+// this could be simplified
+- (NSString *)skimnotesToolPath {
+    // we assume the skimnotes tool is contained in the Resources of the main 
bundle
+    NSString *path = [[NSBundle mainBundle] pathForResource:@"skimnotes" 
ofType:nil];
+    if (path == nil) {
+        // in case this is included in a framework
+        path = [[NSBundle bundleForClass:[self class]] 
pathForResource:@"skimnotes" ofType:nil];
+    }
+    if (path == nil) {
+        // look for it in the Skim bundle
+        NSString *SkimPath = [[NSWorkspace sharedWorkspace] 
fullPathForApplication:@"Skim"];
+        path = SkimPath ? [[[NSBundle bundleWithPath:SkimPath] 
sharedSupportPath] stringByAppendingPathComponent:@"skimnotes"] : nil;
+    }
+    return path;
+}
+
+- (BOOL)launchedTask {
+    BOOL taskLaunched = NO;
+    NSString *launchPath = [self skimnotesToolPath];
+    
+    if (launchPath) {
+        NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
+        
+        // can also use a fixed identifier, or let the tool decide about an 
identifier and read it from stdout
+        if (nil == [self agentIdentifier])
+            [self setAgentIdentifier:[NSString 
stringWithFormat:@"%@.skimnotes-%@@", bundleIdentifier, [[NSProcessInfo 
processInfo] globallyUniqueString]]];
+        
+        NSArray *arguments = @[launchPath, @"agent", @"-xpc", [self 
agentIdentifier]];
+
+        AuthorizationRef auth = NULL;
+        OSStatus createStatus = AuthorizationCreate(NULL, 
kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &auth);
+        if (createStatus != errAuthorizationSuccess) {
+            auth = NULL;
+            NSLog(@"failed to create authorization reference: %d", 
createStatus);
+        }
+        
+        Boolean submittedJob = false;
+        if (auth != NULL) {
+            NSString *label = [NSString 
stringWithFormat:@"%@.skimnotes-agent", bundleIdentifier];
+            
+            // Try to remove the job from launchd if it is already running
+            // We could invoke SMJobCopyDictionary() first to see if the job 
exists, but I'd rather avoid
+            // using it because the headers indicate it may be removed one day 
without any replacement
+            CFErrorRef removeError = NULL;
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+            if (false == SMJobRemove(kSMDomainUserLaunchd, 
(CFStringRef)(label), auth, true, &removeError)) {
+#pragma clang diagnostic pop
+                if (removeError != NULL) {
+                    // It's normal for a job to not be found, so this is not 
an interesting error
+                    if (CFErrorGetCode(removeError) != kSMErrorJobNotFound)
+                        NSLog(@"remove job error: %@", removeError);
+                    CFRelease(removeError);
+                }
+            }
+            
+            NSDictionary *jobDictionary = @{@"Label" : label, 
@"ProgramArguments" : arguments, @"EnableTransactions" : @NO, @"KeepAlive" : 
@{@"SuccessfulExit" : @NO}, @"RunAtLoad" : @NO, @"Nice" : @0, @"ProcessType": 
@"Interactive", @"LaunchOnlyOnce": @YES, @"MachServices" : @{[self 
agentIdentifier] : @YES}};
+            
+            CFErrorRef submitError = NULL;
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+            // SMJobSubmit is deprecated but is the only way to submit a 
non-permanent
+            // helper and allows us to submit to user domain without requiring 
authorization
+            submittedJob = SMJobSubmit(kSMDomainUserLaunchd, 
(CFDictionaryRef)(jobDictionary), auth, &submitError);
+#pragma clang diagnostic pop
+            if (submittedJob == false) {
+                if (submitError != NULL) {
+                    NSLog(@"submit job error: %@", submitError);
+                    CFRelease(submitError);
+                }
+            } else {
+                taskLaunched = YES;
+            }
+        }
+        
+        if (auth != NULL)
+            AuthorizationFree(auth, kAuthorizationFlagDefaults);
+    } else {
+        NSLog(@"failed to find skimnotes tool");
+    }
+    return taskLaunched;
+}
+
+- (void)establishSynchronousConnection:(BOOL)sync {
+    if ([self launchedTask]) {
+        connection = [[NSXPCConnection alloc] initWithMachServiceName:[self 
agentIdentifier] options:0];
+        [connection setRemoteObjectInterface:[NSXPCInterface 
interfaceWithProtocol:@protocol(SKNXPCAgentListenerProtocol)]];
+        [connection setInvalidationHandler:^{
+            [self destroyConnection];
+        }];
+        if (sync)
+            agent = [[connection 
synchronousRemoteObjectProxyWithErrorHandler:^(NSError *error){}] retain];
+        else
+            agent = [[connection remoteObjectProxy] retain];
+        synchronous = sync;
+    }
+}
+
+- (BOOL)connectAndCheckTypeOfFile:(NSURL *)fileURL synchronous:(BOOL)sync {
+    if (nil == connection) {
+        [self establishSynchronousConnection:sync];
+    } else if (synchronous != sync) {
+        NSLog(@"attempt to mix synxhronous and asynchronous skim notes 
retrieval");
+        return NO;
+    }
+    
+    // these checks are client side to avoid connecting to the server unless 
it's really necessary
+    NSWorkspace *ws = [NSWorkspace sharedWorkspace];
+    NSString *fileType = [ws typeOfFile:[fileURL path] error:NULL];
+    
+    if (fileType != nil &&
+        ([ws type:fileType conformsToType:(NSString *)kUTTypePDF] ||
+         [ws type:fileType conformsToType:@"net.sourceforge.skim-app.pdfd"] ||
+         [ws type:fileType 
conformsToType:@"net.sourceforge.skim-app.skimnotes"]))
+        return YES;
+    
+    return NO;
+}
+
+- (NSData *)SkimNotesAtURL:(NSURL *)fileURL {
+    __block NSData *data = nil;
+    if ([self connectAndCheckTypeOfFile:fileURL synchronous:YES])
+        [agent readSkimNotesAtURL:fileURL reply:^(NSData *d){ data = [[d 
retain] autorelease]; }];
+    return data;
+}
+
+- (NSData *)RTFNotesAtURL:(NSURL *)fileURL {
+    __block NSData *data = nil;
+    if ([self connectAndCheckTypeOfFile:fileURL synchronous:YES])
+        [agent readRTFNotesAtURL:fileURL reply:^(NSData *d){ data = [[d 
retain] autorelease]; }];
+    return data;
+}
+
+- (NSString *)textNotesAtURL:(NSURL *)fileURL {
+    __block NSString *string = nil;
+    if ([self connectAndCheckTypeOfFile:fileURL synchronous:YES])
+        [agent readTextNotesAtURL:fileURL reply:^(NSString *s){ string = [s 
retain]; }];
+    return string;
+}
+
+- (void)readSkimNotesAtURL:(NSURL *)fileURL reply:(void (^)(NSData *))reply {
+    if ([self connectAndCheckTypeOfFile:fileURL synchronous:NO])
+        [agent readSkimNotesAtURL:fileURL reply:reply];
+    else
+        reply(nil);
+}
+
+- (void)readRTFNotesAtURL:(NSURL *)fileURL reply:(void (^)(NSData *))reply {
+    if ([self connectAndCheckTypeOfFile:fileURL synchronous:NO])
+        [agent readRTFNotesAtURL:fileURL reply:reply];
+    else
+        reply(nil);
+}
+
+- (void)readTextNotesAtURL:(NSURL *)fileURL reply:(void (^)(NSString *))reply {
+    if ([self connectAndCheckTypeOfFile:fileURL synchronous:NO])
+        [agent readTextNotesAtURL:fileURL reply:reply];
+    else
+        reply(nil);
+}
+
+@end

Modified: trunk/SkimNotes/SkimNotes.xcodeproj/project.pbxproj
===================================================================
--- trunk/SkimNotes/SkimNotes.xcodeproj/project.pbxproj 2023-11-24 14:49:15 UTC 
(rev 13795)
+++ trunk/SkimNotes/SkimNotes.xcodeproj/project.pbxproj 2023-11-24 15:21:52 UTC 
(rev 13796)
@@ -120,6 +120,7 @@
                CEDB9B040E2EA0200057FD09 /* skimnotes in Resources */ = {isa = 
PBXBuildFile; fileRef = CEBA2B550E0566B00000B2E6 /* skimnotes */; };
                CEDB9B060E2EA0450057FD09 /* Cocoa.framework in Frameworks */ = 
{isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; 
};
                CEDB9B210E2EA2F30057FD09 /* main.m in Sources */ = {isa = 
PBXBuildFile; fileRef = CEDB9B200E2EA2F30057FD09 /* main.m */; };
+               CEED30562B10B02C003F138F /* SKNXPCSkimReader.m in Sources */ = 
{isa = PBXBuildFile; fileRef = CEED30552B10B02C003F138F /* SKNXPCSkimReader.m 
*/; };
                CEEEEFFE2996564500A0D98F /* Quartz.framework in Frameworks */ = 
{isa = PBXBuildFile; fileRef = CEF57FD92988180700594EC0 /* Quartz.framework */; 
};
                CEEEEFFF2996564F00A0D98F /* Quartz.framework in Frameworks */ = 
{isa = PBXBuildFile; fileRef = CEF57FD92988180700594EC0 /* Quartz.framework */; 
};
                CEEEF0012996568E00A0D98F /* Quartz.framework in Frameworks */ = 
{isa = PBXBuildFile; fileRef = CEF57FD92988180700594EC0 /* Quartz.framework */; 
};
@@ -269,6 +270,8 @@
                CEDB9B0D0E2EA1380057FD09 /* SkimNotes-App.xcconfig */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = 
"SkimNotes-App.xcconfig"; sourceTree = "<group>"; };
                CEDB9B200E2EA2F30057FD09 /* main.m */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path 
= main.m; sourceTree = "<group>"; };
                CEED30532B10AE38003F138F /* SKNXPCAgentListenerProtocol.h */ = 
{isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = 
SKNXPCAgentListenerProtocol.h; sourceTree = "<group>"; };
+               CEED30542B10B02C003F138F /* SKNXPCSkimReader.h */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.c.h; path = 
SKNXPCSkimReader.h; sourceTree = "<group>"; };
+               CEED30552B10B02C003F138F /* SKNXPCSkimReader.m */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = 
SKNXPCSkimReader.m; sourceTree = "<group>"; };
                CEF57FCF2988170B00594EC0 /* PDFKit.framework */ = {isa = 
PBXFileReference; lastKnownFileType = wrapper.framework; name = 
PDFKit.framework; path = System/Library/Frameworks/PDFKit.framework; sourceTree 
= SDKROOT; };
                CEF57FD12988171C00594EC0 /* Foundation.framework */ = {isa = 
PBXFileReference; lastKnownFileType = wrapper.framework; name = 
Foundation.framework; path = System/Library/Frameworks/Foundation.framework; 
sourceTree = SDKROOT; };
                CEF57FD32988172600594EC0 /* AppKit.framework */ = {isa = 
PBXFileReference; lastKnownFileType = wrapper.framework; name = 
AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree 
= SDKROOT; };
@@ -520,6 +523,8 @@
                                CEDB9AC40E2E9D760057FD09 /* SKNDocument.m */,
                                CEDB9AC50E2E9D760057FD09 /* SKNSkimReader.h */,
                                CEDB9AC60E2E9D760057FD09 /* SKNSkimReader.m */,
+                               CEED30542B10B02C003F138F /* SKNXPCSkimReader.h 
*/,
+                               CEED30552B10B02C003F138F /* SKNXPCSkimReader.m 
*/,
                        );
                        name = "Test App Classes";
                        sourceTree = "<group>";
@@ -937,6 +942,7 @@
                                CEDB9AF40E2E9F250057FD09 /* SKNDocument.m in 
Sources */,
                                CEDB9AF50E2E9F260057FD09 /* SKNSkimReader.m in 
Sources */,
                                CEDB9B210E2EA2F30057FD09 /* main.m in Sources 
*/,
+                               CEED30562B10B02C003F138F /* SKNXPCSkimReader.m 
in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };

This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.



_______________________________________________
Skim-app-commit mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/skim-app-commit

Reply via email to