mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-11-29 05:01:49 +00:00
* LinkBack support files, now in pure Objective-C (without any Objective-C++ which is not supported by automake)
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@22761 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
parent
d4a87d0dce
commit
dd714bfce8
@ -88,6 +88,10 @@ dnl LYX_CXX_RTTI
|
|||||||
dnl AC_CHECK_HEADERS(ostream istream sstream locale limits ios)
|
dnl AC_CHECK_HEADERS(ostream istream sstream locale limits ios)
|
||||||
dnl LYX_CXX_STL_MODERN_STREAMS
|
dnl LYX_CXX_STL_MODERN_STREAMS
|
||||||
|
|
||||||
|
### Objective-C compiler
|
||||||
|
AC_PROG_OBJC
|
||||||
|
_AM_DEPENDENCIES([OBJC])
|
||||||
|
|
||||||
### and now some special lyx flags.
|
### and now some special lyx flags.
|
||||||
AC_ARG_ENABLE(assertions,
|
AC_ARG_ENABLE(assertions,
|
||||||
AC_HELP_STRING([--enable-assertions],[add runtime sanity checks in the program]),,
|
AC_HELP_STRING([--enable-assertions],[add runtime sanity checks in the program]),,
|
||||||
|
@ -13,6 +13,9 @@ file(GLOB support_minizip_sources ${TOP_SRC_DIR}/src/support/minizip/*.c)
|
|||||||
file(GLOB support_minizip_cpp_sources ${TOP_SRC_DIR}/src/support/minizip/*.cpp)
|
file(GLOB support_minizip_cpp_sources ${TOP_SRC_DIR}/src/support/minizip/*.cpp)
|
||||||
file(GLOB support_minizip_headers ${TOP_SRC_DIR}/src/support/minizip/*.h)
|
file(GLOB support_minizip_headers ${TOP_SRC_DIR}/src/support/minizip/*.h)
|
||||||
|
|
||||||
|
file(GLOB support_linkback_sources ${TOP_SRC_DIR}/src/support/linkback/*.m*)
|
||||||
|
file(GLOB support_linkback_headers ${TOP_SRC_DIR}/src/support/linkback/*.h)
|
||||||
|
|
||||||
list(REMOVE_ITEM support_sources
|
list(REMOVE_ITEM support_sources
|
||||||
${TOP_SRC_DIR}/src/support/os_win32.cpp
|
${TOP_SRC_DIR}/src/support/os_win32.cpp
|
||||||
${TOP_SRC_DIR}/src/support/os_unix.cpp
|
${TOP_SRC_DIR}/src/support/os_unix.cpp
|
||||||
@ -24,6 +27,13 @@ list(REMOVE_ITEM support_sources
|
|||||||
${TOP_SRC_DIR}/src/support/minizip/iowin32.c
|
${TOP_SRC_DIR}/src/support/minizip/iowin32.c
|
||||||
${TOP_SRC_DIR}/src/support/gettext.cpp)
|
${TOP_SRC_DIR}/src/support/gettext.cpp)
|
||||||
|
|
||||||
|
if(APPLE)
|
||||||
|
message(STATUS "Mac LinkBack support")
|
||||||
|
else()
|
||||||
|
set(support_linkback_sources "")
|
||||||
|
set(support_linkback_headers "")
|
||||||
|
endif()
|
||||||
|
|
||||||
# needed to compile tex2lyx in merged mode
|
# needed to compile tex2lyx in merged mode
|
||||||
set(dont_merge ${TOP_SRC_DIR}/src/support/gettext.cpp)
|
set(dont_merge ${TOP_SRC_DIR}/src/support/gettext.cpp)
|
||||||
|
|
||||||
@ -40,8 +50,8 @@ include_directories(${TOP_SRC_DIR}/src/support
|
|||||||
|
|
||||||
|
|
||||||
if(NOT MERGE_FILES)
|
if(NOT MERGE_FILES)
|
||||||
set(support_sources ${support_sources} ${support_minizip_sources} ${support_minizip_cpp_sources})
|
set(support_sources ${support_sources} ${support_minizip_sources} ${support_minizip_cpp_sources} ${support_linkback_sources})
|
||||||
set(support_headers ${support_headers} ${support_minizip_headers})
|
set(support_headers ${support_headers} ${support_minizip_headers} ${support_linkback_headers})
|
||||||
add_library(support ${library_type} ${support_sources} ${support_headers} ${dont_merge})
|
add_library(support ${library_type} ${support_sources} ${support_headers} ${dont_merge})
|
||||||
else()
|
else()
|
||||||
lyx_const_touched_files(_allinone support_sources)
|
lyx_const_touched_files(_allinone support_sources)
|
||||||
@ -51,13 +61,14 @@ else()
|
|||||||
set_source_files_properties(_allinone_touched.C
|
set_source_files_properties(_allinone_touched.C
|
||||||
PROPERTIES OBJECT_DEPENDS "${depends_moc}")
|
PROPERTIES OBJECT_DEPENDS "${depends_moc}")
|
||||||
add_library(support ${library_type} ${_allinone_files}
|
add_library(support ${library_type} ${_allinone_files}
|
||||||
${support_minizip_sources} ${support_minizip_cpp_sources} ${support_headers} ${dont_merge})
|
${support_minizip_sources} ${support_minizip_cpp_sources} ${support_linkback_sources} ${support_headers} ${dont_merge})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(support boost_signals ${QT_QTCORE_LIBRARY} ${ZLIB_LIBRARY})
|
||||||
|
|
||||||
target_link_libraries(support boost_signals ${QT_QTCORE_LIBRARY} ${ZLIB_LIBRARY} )
|
if(APPLE)
|
||||||
|
target_link_libraries(support "objc" "-framework Appkit" "-framework CoreFoundation")
|
||||||
if(WIN32)
|
elseif(WIN32)
|
||||||
target_link_libraries(support shlwapi)
|
target_link_libraries(support shlwapi)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -111,6 +111,16 @@ liblyxsupport_la_SOURCES = \
|
|||||||
minizip/zip.h \
|
minizip/zip.h \
|
||||||
minizip/zipunzip.cpp
|
minizip/zipunzip.cpp
|
||||||
|
|
||||||
|
if INSTALL_MACOSX
|
||||||
|
liblyxsupport_la_SOURCES += \
|
||||||
|
linkback/LinkBack.h \
|
||||||
|
linkback/LinkBack.m \
|
||||||
|
linkback/LinkBackProxy.h \
|
||||||
|
linkback/LinkBackProxy.m \
|
||||||
|
linkback/LinkBackServer.h \
|
||||||
|
linkback/LinkBackServer.m
|
||||||
|
endif
|
||||||
|
|
||||||
############################## Tests ##################################
|
############################## Tests ##################################
|
||||||
|
|
||||||
EXTRA_DIST += \
|
EXTRA_DIST += \
|
||||||
|
165
src/support/linkback/LinkBack.h
Normal file
165
src/support/linkback/LinkBack.h
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
//
|
||||||
|
// LinkBack.h
|
||||||
|
// LinkBack Project
|
||||||
|
//
|
||||||
|
// Created by Charles Jolley on Tue Jun 15 2004.
|
||||||
|
// Copyright (c) 2004, Nisus Software, Inc.
|
||||||
|
// 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 the Nisus Software, Inc. nor the names of its
|
||||||
|
// 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>
|
||||||
|
|
||||||
|
// Use this pasteboard type to put LinkBack data to the pasteboard. Use MakeLinkBackData() to create the data.
|
||||||
|
extern NSString* LinkBackPboardType ;
|
||||||
|
|
||||||
|
// Default Action Names. These will be localized for you automatically.
|
||||||
|
extern NSString* LinkBackEditActionName ;
|
||||||
|
extern NSString* LinkBackRefreshActionName ;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Support Functions
|
||||||
|
//
|
||||||
|
NSString* LinkBackUniqueItemKey() ;
|
||||||
|
NSString* LinkBackEditMultipleMenuTitle() ;
|
||||||
|
NSString* LinkBackEditNoneMenuTitle() ;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Deprecated Support Functions -- use LinkBack Data Category instead
|
||||||
|
//
|
||||||
|
id MakeLinkBackData(NSString* serverName, id appData) ;
|
||||||
|
id LinkBackGetAppData(id linkBackData) ;
|
||||||
|
BOOL LinkBackDataBelongsToActiveApplication(id data) ;
|
||||||
|
|
||||||
|
//
|
||||||
|
// LinkBack Data Category
|
||||||
|
//
|
||||||
|
|
||||||
|
// Use these methods to create and access linkback data objects. You can also use the helper functions above.
|
||||||
|
|
||||||
|
@interface NSDictionary (LinkBackData)
|
||||||
|
|
||||||
|
+ (NSDictionary*)linkBackDataWithServerName:(NSString*)serverName appData:(id)appData ;
|
||||||
|
|
||||||
|
+ (NSDictionary*)linkBackDataWithServerName:(NSString*)serverName appData:(id)appData suggestedRefreshRate:(NSTimeInterval)rate ;
|
||||||
|
|
||||||
|
+ (NSDictionary*)linkBackDataWithServerName:(NSString*)serverName appData:(id)appData actionName:(NSString*)action suggestedRefreshRate:(NSTimeInterval)rate ;
|
||||||
|
|
||||||
|
- (BOOL)linkBackDataBelongsToActiveApplication ;
|
||||||
|
|
||||||
|
- (id)linkBackAppData ;
|
||||||
|
- (NSString*)linkBackSourceApplicationName ;
|
||||||
|
- (NSString*)linkBackActionName ;
|
||||||
|
- (NSString*)linkBackVersion ;
|
||||||
|
- (NSURL*)linkBackApplicationURL ;
|
||||||
|
|
||||||
|
- (NSTimeInterval)linkBackSuggestedRefreshRate ;
|
||||||
|
|
||||||
|
- (NSString*)linkBackEditMenuTitle ;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
//
|
||||||
|
// Delegate Protocols
|
||||||
|
//
|
||||||
|
|
||||||
|
@class LinkBack ;
|
||||||
|
|
||||||
|
@protocol LinkBackServerDelegate
|
||||||
|
- (void)linkBackDidClose:(LinkBack*)link ;
|
||||||
|
- (void)linkBackClientDidRequestEdit:(LinkBack*)link ;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@protocol LinkBackClientDelegate
|
||||||
|
- (void)linkBackDidClose:(LinkBack*)link ;
|
||||||
|
- (void)linkBackServerDidSendEdit:(LinkBack*)link ;
|
||||||
|
@end
|
||||||
|
|
||||||
|
// used for cross app communications
|
||||||
|
@protocol LinkBack
|
||||||
|
- (oneway void)remoteCloseLink ;
|
||||||
|
- (void)requestEditWithPasteboardName:(bycopy NSString*)pboardName ; // from client
|
||||||
|
- (void)refreshEditWithPasteboardName:(bycopy NSString*)pboardName ; // from server
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface LinkBack : NSObject <LinkBack> {
|
||||||
|
LinkBack* peer ; // the client or server on the other side.
|
||||||
|
BOOL isServer ;
|
||||||
|
id delegate ;
|
||||||
|
NSPasteboard* pboard ;
|
||||||
|
id repobj ;
|
||||||
|
NSString* sourceName ;
|
||||||
|
NSString* sourceApplicationName ;
|
||||||
|
NSString* key ;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (LinkBack*)activeLinkBackForItemKey:(id)key ;
|
||||||
|
// works for both the client and server side. Valid only while a link is connected.
|
||||||
|
|
||||||
|
// ...........................................................................
|
||||||
|
// General Use methods
|
||||||
|
//
|
||||||
|
- (NSPasteboard*)pasteboard ;
|
||||||
|
- (void)closeLink ;
|
||||||
|
|
||||||
|
- (id)representedObject ;
|
||||||
|
- (void)setRepresentedObject:(id)obj ;
|
||||||
|
// Applications can use this represented object to attach some meaning to the live link. For example, a client application may set this to the object to be modified when the edit is refreshed. This retains its value.
|
||||||
|
|
||||||
|
- (NSString*)sourceName ;
|
||||||
|
- (NSString*)sourceApplicationName ;
|
||||||
|
- (NSString*)itemKey ; // maybe this matters only on the client side.
|
||||||
|
|
||||||
|
// ...........................................................................
|
||||||
|
// Server-side methods
|
||||||
|
//
|
||||||
|
+ (BOOL)publishServerWithName:(NSString*)name delegate:(id<LinkBackServerDelegate>)del ;
|
||||||
|
|
||||||
|
+ (void)retractServerWithName:(NSString*)name ;
|
||||||
|
|
||||||
|
- (void)sendEdit ;
|
||||||
|
|
||||||
|
// ...........................................................................
|
||||||
|
// Client-Side Methods
|
||||||
|
//
|
||||||
|
+ (LinkBack*)editLinkBackData:(id)data sourceName:(NSString*)aName delegate:(id<LinkBackClientDelegate>)del itemKey:(NSString*)aKey ;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface LinkBack (InternalUseOnly)
|
||||||
|
|
||||||
|
- (id)initServerWithClient: (LinkBack*)aLinkBack delegate: (id<LinkBackServerDelegate>)aDel ;
|
||||||
|
|
||||||
|
- (id)initClientWithSourceName:(NSString*)aName delegate:(id<LinkBackClientDelegate>)aDel itemKey:(NSString*)aKey ;
|
||||||
|
|
||||||
|
- (BOOL)connectToServerWithName:(NSString*)aName inApplication:(NSString*)bundleIdentifier fallbackURL:(NSURL*)url appName:(NSString*)appName ;
|
||||||
|
|
||||||
|
- (void)requestEdit ;
|
||||||
|
|
||||||
|
@end
|
445
src/support/linkback/LinkBack.m
Normal file
445
src/support/linkback/LinkBack.m
Normal file
@ -0,0 +1,445 @@
|
|||||||
|
//
|
||||||
|
// LinkBack.m
|
||||||
|
// LinkBack
|
||||||
|
//
|
||||||
|
// Created by Charles Jolley on Tue Jun 15 2004.
|
||||||
|
// Copyright (c) 2004, Nisus Software, Inc.
|
||||||
|
// 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 the Nisus Software, Inc. nor the names of its
|
||||||
|
// 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 "LinkBack.h"
|
||||||
|
#import "LinkBackServer.h"
|
||||||
|
|
||||||
|
NSString* LinkBackPboardType = @"LinkBackData" ;
|
||||||
|
|
||||||
|
// LinkBack data keys. These are used in a LinkBack object, which is currently a dictionary. Do not depend on these values. They are public for testing purposes only.
|
||||||
|
NSString* LinkBackServerActionKey = @"serverActionKey" ;
|
||||||
|
NSString* LinkBackServerApplicationNameKey = @"serverAppName" ;
|
||||||
|
NSString* LinkBackServerNameKey = @"serverName" ;
|
||||||
|
NSString* LinkBackServerBundleIdentifierKey = @"bundleId" ;
|
||||||
|
NSString* LinkBackVersionKey = @"version" ;
|
||||||
|
NSString* LinkBackApplicationDataKey = @"appData" ;
|
||||||
|
NSString* LinkBackSuggestedRefreshKey = @"refresh" ;
|
||||||
|
NSString* LinkBackApplicationURLKey = @"ApplicationURL" ;
|
||||||
|
|
||||||
|
NSString* LinkBackEditActionName = @"_Edit" ;
|
||||||
|
NSString* LinkBackRefreshActionName = @"_Refresh" ;
|
||||||
|
|
||||||
|
// ...........................................................................
|
||||||
|
// Support Functions
|
||||||
|
//
|
||||||
|
|
||||||
|
id MakeLinkBackData(NSString* serverName, id appData)
|
||||||
|
{
|
||||||
|
return [NSDictionary linkBackDataWithServerName: serverName appData: appData] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
id LinkBackGetAppData(id LinkBackData)
|
||||||
|
{
|
||||||
|
return [LinkBackData linkBackAppData] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString* LinkBackUniqueItemKey()
|
||||||
|
{
|
||||||
|
static int counter = 0 ;
|
||||||
|
|
||||||
|
NSString* base = [[NSBundle mainBundle] bundleIdentifier] ;
|
||||||
|
unsigned long time = [NSDate timeIntervalSinceReferenceDate] ;
|
||||||
|
return [NSString stringWithFormat: @"%@%.8x.%.4x",base,time,counter++] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL LinkBackDataBelongsToActiveApplication(id data)
|
||||||
|
{
|
||||||
|
return [data linkBackDataBelongsToActiveApplication] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString* LinkBackEditMultipleMenuTitle()
|
||||||
|
{
|
||||||
|
NSBundle* bundle = [NSBundle bundleForClass: [LinkBack class]] ;
|
||||||
|
NSString* ret = [bundle localizedStringForKey: @"_EditMultiple" value: @"Edit LinkBack Items" table: @"Localized"] ;
|
||||||
|
return ret ;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString* LinkBackEditNoneMenuTitle()
|
||||||
|
{
|
||||||
|
NSBundle* bundle = [NSBundle bundleForClass: [LinkBack class]] ;
|
||||||
|
NSString* ret = [bundle localizedStringForKey: @"_EditNone" value: @"Edit LinkBack Item" table: @"Localized"] ;
|
||||||
|
return ret ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...........................................................................
|
||||||
|
// LinkBack Data Category
|
||||||
|
//
|
||||||
|
|
||||||
|
// Use these methods to create and access linkback data objects. You can also use the helper functions above.
|
||||||
|
|
||||||
|
@implementation NSDictionary (LinkBackData)
|
||||||
|
|
||||||
|
+ (NSDictionary*)linkBackDataWithServerName:(NSString*)serverName appData:(id)appData
|
||||||
|
{
|
||||||
|
return [self linkBackDataWithServerName: serverName appData: appData actionName: nil suggestedRefreshRate: 0];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSDictionary*)linkBackDataWithServerName:(NSString*)serverName appData:(id)appData suggestedRefreshRate:(NSTimeInterval)rate
|
||||||
|
{
|
||||||
|
return [self linkBackDataWithServerName: serverName appData: appData actionName: LinkBackRefreshActionName suggestedRefreshRate: rate] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSDictionary*)linkBackDataWithServerName:(NSString*)serverName appData:(id)appData actionName:(NSString*)action suggestedRefreshRate:(NSTimeInterval)rate ;
|
||||||
|
{
|
||||||
|
NSDictionary* appInfo = [[NSBundle mainBundle] infoDictionary] ;
|
||||||
|
|
||||||
|
NSMutableDictionary* ret = [[NSMutableDictionary alloc] init] ;
|
||||||
|
NSString* bundleId = [[NSBundle mainBundle] bundleIdentifier] ;
|
||||||
|
NSString* url = [appInfo objectForKey: @"LinkBackApplicationURL"] ;
|
||||||
|
NSString* appName = [[NSProcessInfo processInfo] processName] ;
|
||||||
|
id version = @"A" ;
|
||||||
|
|
||||||
|
if (nil==serverName) [NSException raise: NSInvalidArgumentException format: @"LinkBack Data cannot be created without a server name."] ;
|
||||||
|
|
||||||
|
// callback information
|
||||||
|
[ret setObject: bundleId forKey: LinkBackServerBundleIdentifierKey];
|
||||||
|
[ret setObject: serverName forKey: LinkBackServerNameKey] ;
|
||||||
|
[ret setObject: version forKey: LinkBackVersionKey] ;
|
||||||
|
|
||||||
|
// additional information
|
||||||
|
if (appName) [ret setObject: appName forKey: LinkBackServerApplicationNameKey] ;
|
||||||
|
if (action) [ret setObject: action forKey: LinkBackServerActionKey] ;
|
||||||
|
if (appData) [ret setObject: appData forKey: LinkBackApplicationDataKey] ;
|
||||||
|
if (url) [ret setObject: url forKey: LinkBackApplicationURLKey] ;
|
||||||
|
[ret setObject: [NSNumber numberWithFloat: rate] forKey: LinkBackSuggestedRefreshKey] ;
|
||||||
|
|
||||||
|
return [ret autorelease] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)linkBackDataBelongsToActiveApplication
|
||||||
|
{
|
||||||
|
NSString* bundleId = [[NSBundle mainBundle] bundleIdentifier] ;
|
||||||
|
NSString* dataId = [self objectForKey: LinkBackServerBundleIdentifierKey] ;
|
||||||
|
return (dataId && [dataId isEqualToString: bundleId]) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)linkBackAppData
|
||||||
|
{
|
||||||
|
return [self objectForKey: LinkBackApplicationDataKey] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString*)linkBackSourceApplicationName
|
||||||
|
{
|
||||||
|
return [self objectForKey: LinkBackServerApplicationNameKey] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString*)linkBackActionName
|
||||||
|
{
|
||||||
|
NSBundle* bundle = [NSBundle bundleForClass: [LinkBack class]] ;
|
||||||
|
NSString* ret = [self objectForKey: LinkBackServerActionKey] ;
|
||||||
|
if (nil==ret) ret = LinkBackEditActionName ;
|
||||||
|
|
||||||
|
ret = [bundle localizedStringForKey: ret value: ret table: @"Localized"] ;
|
||||||
|
return ret ;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString*)linkBackEditMenuTitle
|
||||||
|
{
|
||||||
|
NSBundle* bundle = [NSBundle bundleForClass: [LinkBack class]] ;
|
||||||
|
NSString* appName = [self linkBackSourceApplicationName] ;
|
||||||
|
NSString* action = [self linkBackActionName] ;
|
||||||
|
NSString* ret = [bundle localizedStringForKey: @"_EditPattern" value: @"%@ in %@" table: @"Localized"] ;
|
||||||
|
ret = [NSString stringWithFormat: ret, action, appName] ;
|
||||||
|
return ret ;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString*)linkBackVersion
|
||||||
|
{
|
||||||
|
return [self objectForKey: LinkBackVersionKey] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSTimeInterval)linkBackSuggestedRefreshRate
|
||||||
|
{
|
||||||
|
id obj = [self objectForKey: LinkBackSuggestedRefreshKey] ;
|
||||||
|
return (obj) ? [obj floatValue] : 0 ;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSURL*)linkBackApplicationURL
|
||||||
|
{
|
||||||
|
id obj = [self objectForKey: LinkBackApplicationURLKey] ;
|
||||||
|
if (obj) obj = [NSURL URLWithString: obj] ;
|
||||||
|
return obj ;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
// ...........................................................................
|
||||||
|
// LinkBackServer
|
||||||
|
//
|
||||||
|
// one of these exists for each registered server name. This is the receiver of server requests.
|
||||||
|
|
||||||
|
// ...........................................................................
|
||||||
|
// LinkBack Class
|
||||||
|
//
|
||||||
|
|
||||||
|
NSMutableDictionary* keyedLinkBacks = nil ;
|
||||||
|
|
||||||
|
@implementation LinkBack
|
||||||
|
|
||||||
|
+ (void)initialize
|
||||||
|
{
|
||||||
|
static BOOL inited = NO ;
|
||||||
|
if (inited) return ;
|
||||||
|
inited=YES; [super initialize] ;
|
||||||
|
keyedLinkBacks = [[NSMutableDictionary alloc] init] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (LinkBack*)activeLinkBackForItemKey:(id)aKey
|
||||||
|
{
|
||||||
|
return [keyedLinkBacks objectForKey: aKey] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)initServerWithClient: (LinkBack*)aLinkBack delegate: (id<LinkBackServerDelegate>)aDel
|
||||||
|
{
|
||||||
|
if (self = [super init]) {
|
||||||
|
peer = [aLinkBack retain] ;
|
||||||
|
sourceName = [[peer sourceName] copy] ;
|
||||||
|
sourceApplicationName = [[peer sourceApplicationName] copy] ;
|
||||||
|
key = [[peer itemKey] copy] ;
|
||||||
|
isServer = YES ;
|
||||||
|
delegate = aDel ;
|
||||||
|
[keyedLinkBacks setObject: self forKey: key] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self ;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)initClientWithSourceName:(NSString*)aName delegate:(id<LinkBackClientDelegate>)aDel itemKey:(NSString*)aKey ;
|
||||||
|
{
|
||||||
|
if (self = [super init]) {
|
||||||
|
isServer = NO ;
|
||||||
|
delegate = aDel ;
|
||||||
|
sourceName = [aName copy] ;
|
||||||
|
sourceApplicationName = [[NSProcessInfo processInfo] processName] ;
|
||||||
|
pboard = [[NSPasteboard pasteboardWithUniqueName] retain] ;
|
||||||
|
key = [aKey copy] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self ;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
[repobj release] ;
|
||||||
|
[sourceName release] ;
|
||||||
|
|
||||||
|
if (peer) [self closeLink] ;
|
||||||
|
[peer release] ;
|
||||||
|
|
||||||
|
if (!isServer) [pboard releaseGlobally] ; // client owns the pboard.
|
||||||
|
[pboard release] ;
|
||||||
|
|
||||||
|
[super dealloc] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...........................................................................
|
||||||
|
// General Use methods
|
||||||
|
|
||||||
|
- (NSPasteboard*)pasteboard
|
||||||
|
{
|
||||||
|
return pboard ;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)representedObject
|
||||||
|
{
|
||||||
|
return repobj ;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setRepresentedObject:(id)obj
|
||||||
|
{
|
||||||
|
[obj retain] ;
|
||||||
|
[repobj release] ;
|
||||||
|
repobj = obj ;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString*)sourceName
|
||||||
|
{
|
||||||
|
return sourceName ;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString*)sourceApplicationName
|
||||||
|
{
|
||||||
|
return sourceApplicationName ;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString*)itemKey
|
||||||
|
{
|
||||||
|
return key ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this method is called to initial a link closure from this side.
|
||||||
|
- (void)closeLink
|
||||||
|
{
|
||||||
|
// inform peer of closure
|
||||||
|
if (peer) {
|
||||||
|
[peer remoteCloseLink] ;
|
||||||
|
[peer release] ;
|
||||||
|
peer = nil ;
|
||||||
|
[self release] ;
|
||||||
|
[keyedLinkBacks removeObjectForKey: [self itemKey]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this method is called whenever the link is about to be or has been closed by the other side.
|
||||||
|
- (void)remoteCloseLink
|
||||||
|
{
|
||||||
|
if (peer) {
|
||||||
|
[peer release] ;
|
||||||
|
peer = nil ;
|
||||||
|
[self release] ;
|
||||||
|
[keyedLinkBacks removeObjectForKey: [self itemKey]];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (delegate) [delegate linkBackDidClose: self] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...........................................................................
|
||||||
|
// Server-side methods
|
||||||
|
//
|
||||||
|
+ (BOOL)publishServerWithName:(NSString*)name delegate:(id<LinkBackServerDelegate>)del
|
||||||
|
{
|
||||||
|
return [LinkBackServer publishServerWithName: name delegate: del] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)retractServerWithName:(NSString*)name
|
||||||
|
{
|
||||||
|
LinkBackServer* server = [LinkBackServer LinkBackServerWithName: name] ;
|
||||||
|
if (server) [server retract] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)sendEdit
|
||||||
|
{
|
||||||
|
if (!peer) [NSException raise: NSGenericException format: @"tried to request edit from a live link not connect to a server."] ;
|
||||||
|
[peer refreshEditWithPasteboardName: [pboard name]] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FROM CLIENT LinkBack
|
||||||
|
- (void)requestEditWithPasteboardName:(bycopy NSString*)pboardName
|
||||||
|
{
|
||||||
|
// get the new pasteboard, if needed
|
||||||
|
if ((!pboard) || ![pboardName isEqualToString: [pboard name]]) pboard = [[NSPasteboard pasteboardWithName: pboardName] retain] ;
|
||||||
|
|
||||||
|
// pass onto delegate
|
||||||
|
[delegate performSelectorOnMainThread: @selector(linkBackClientDidRequestEdit:) withObject: self waitUntilDone: NO] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...........................................................................
|
||||||
|
// Client-Side Methods
|
||||||
|
//
|
||||||
|
+ (LinkBack*)editLinkBackData:(id)data sourceName:(NSString*)aName delegate:(id<LinkBackClientDelegate>)del itemKey:(NSString*)aKey
|
||||||
|
{
|
||||||
|
// if an active live link already exists, use that. Otherwise, create a new one.
|
||||||
|
LinkBack* ret = [keyedLinkBacks objectForKey: aKey] ;
|
||||||
|
|
||||||
|
if(nil==ret) {
|
||||||
|
BOOL ok ;
|
||||||
|
NSString* serverName ;
|
||||||
|
NSString* serverId ;
|
||||||
|
NSString* appName ;
|
||||||
|
NSURL* url ;
|
||||||
|
|
||||||
|
// collect server contact information from data.
|
||||||
|
ok = [data isKindOfClass: [NSDictionary class]] ;
|
||||||
|
if (ok) {
|
||||||
|
serverName = [data objectForKey: LinkBackServerNameKey] ;
|
||||||
|
serverId = [data objectForKey: LinkBackServerBundleIdentifierKey];
|
||||||
|
appName = [data linkBackSourceApplicationName] ;
|
||||||
|
url = [data linkBackApplicationURL] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ok || !serverName || !serverId) [NSException raise: NSInvalidArgumentException format: @"LinkBackData is not of the correct format: %@", data] ;
|
||||||
|
|
||||||
|
// create the live link object and try to connect to the server.
|
||||||
|
ret = [[LinkBack alloc] initClientWithSourceName: aName delegate: del itemKey: aKey] ;
|
||||||
|
|
||||||
|
if (![ret connectToServerWithName: serverName inApplication: serverId fallbackURL: url appName: appName]) {
|
||||||
|
[ret release] ;
|
||||||
|
ret = nil ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now with a live link in hand, request an edit
|
||||||
|
if (ret) {
|
||||||
|
// if connected to server, publish data and inform server.
|
||||||
|
NSPasteboard* my_pboard = [ret pasteboard] ;
|
||||||
|
[my_pboard declareTypes: [NSArray arrayWithObject: LinkBackPboardType] owner: ret] ;
|
||||||
|
[my_pboard setPropertyList: data forType: LinkBackPboardType] ;
|
||||||
|
|
||||||
|
[ret requestEdit] ;
|
||||||
|
|
||||||
|
// if connection to server failed, return nil.
|
||||||
|
} else {
|
||||||
|
[ret release] ;
|
||||||
|
ret = nil ;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret ;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)connectToServerWithName:(NSString*)aName inApplication:(NSString*)bundleIdentifier fallbackURL:(NSURL*)url appName:(NSString*)appName
|
||||||
|
{
|
||||||
|
// get the LinkBackServer.
|
||||||
|
LinkBackServer* server = [LinkBackServer LinkBackServerWithName: aName inApplication: bundleIdentifier launchIfNeeded: YES fallbackURL: url appName: appName] ;
|
||||||
|
if (!server) return NO ; // failed to get server
|
||||||
|
|
||||||
|
peer = [[server initiateLinkBackFromClient: self] retain] ;
|
||||||
|
if (!peer) return NO ; // failed to initiate session
|
||||||
|
|
||||||
|
// if we connected, then add to the list of active keys
|
||||||
|
[keyedLinkBacks setObject: self forKey: [self itemKey]] ;
|
||||||
|
|
||||||
|
return YES ;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)requestEdit
|
||||||
|
{
|
||||||
|
if (!peer) [NSException raise: NSGenericException format: @"tried to request edit from a live link not connect to a server."] ;
|
||||||
|
[peer requestEditWithPasteboardName: [pboard name]] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// RECEIVED FROM SERVER
|
||||||
|
- (void)refreshEditWithPasteboardName:(bycopy NSString*)pboardName
|
||||||
|
{
|
||||||
|
// if pboard has changes, change to new pboard.
|
||||||
|
if (![pboardName isEqualToString: [pboard name]]) {
|
||||||
|
[pboard release] ;
|
||||||
|
pboard = [[NSPasteboard pasteboardWithName: pboardName] retain] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// inform delegate
|
||||||
|
[delegate performSelectorOnMainThread: @selector(linkBackServerDidSendEdit:) withObject: self waitUntilDone: NO] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
32
src/support/linkback/LinkBackProxy.h
Normal file
32
src/support/linkback/LinkBackProxy.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// -*- C++ -*-
|
||||||
|
/**
|
||||||
|
* \file LinkBackProxy.h
|
||||||
|
* This file is part of LyX, the document processor.
|
||||||
|
* Licence details can be found in the file COPYING.
|
||||||
|
*
|
||||||
|
* \author Stefan Schimanski
|
||||||
|
*
|
||||||
|
* Full author contact details are available in file CREDITS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LINKBACKPROXY_H
|
||||||
|
#define LINKBACKPROXY_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
///
|
||||||
|
int isLinkBackDataInPasteboard();
|
||||||
|
///
|
||||||
|
int editLinkBackFile(char const * filename);
|
||||||
|
///
|
||||||
|
void getLinkBackData(void const ** buf, unsigned * len);
|
||||||
|
///
|
||||||
|
void closeAllLinkBackLinks();
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // LINKBACKPROXY_H
|
231
src/support/linkback/LinkBackProxy.m
Normal file
231
src/support/linkback/LinkBackProxy.m
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
/**
|
||||||
|
* \file LinkBackProxy.mm
|
||||||
|
* This file is part of LyX, the document processor.
|
||||||
|
* Licence details can be found in the file COPYING.
|
||||||
|
*
|
||||||
|
* \author Stefan Schimanski
|
||||||
|
*
|
||||||
|
* Full author contact details are available in file CREDITS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
#include "support/linkback/LinkBackProxy.h"
|
||||||
|
|
||||||
|
#include "support/linkback/LinkBack.h"
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static NSAutoreleasePool * pool = nil;
|
||||||
|
|
||||||
|
@interface LyXLinkBackClient : NSObject <LinkBackClientDelegate> {
|
||||||
|
NSMutableSet * keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)load;
|
||||||
|
- (LyXLinkBackClient *)init;
|
||||||
|
- (BOOL)edit:(NSString *)fileName;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation LyXLinkBackClient
|
||||||
|
|
||||||
|
+ (void)load
|
||||||
|
{
|
||||||
|
pool = [[NSAutoreleasePool alloc] init];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (LyXLinkBackClient *)init
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
if (self != nil)
|
||||||
|
keys = [[NSMutableSet alloc] init];
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc {
|
||||||
|
// close all links
|
||||||
|
NSArray * allKeys = [keys allObjects];
|
||||||
|
unsigned i;
|
||||||
|
for (i = 0; i < [allKeys count]; ++i) {
|
||||||
|
LinkBack * link
|
||||||
|
= [LinkBack activeLinkBackForItemKey:[allKeys objectAtIndex:i]];
|
||||||
|
[link closeLink];
|
||||||
|
}
|
||||||
|
[keys release];
|
||||||
|
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)edit:(NSString *)fileName
|
||||||
|
{
|
||||||
|
if ([LinkBack activeLinkBackForItemKey:fileName])
|
||||||
|
return YES;
|
||||||
|
|
||||||
|
@try {
|
||||||
|
// get put data, i.e. PDF + LinkBack + 4 bytes PDF-length
|
||||||
|
NSData * data = [NSData dataWithContentsOfFile:fileName];
|
||||||
|
if (data == nil) {
|
||||||
|
NSLog(@"Cannot read file %@", fileName);
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get linkback data which comes behind the pdf data.
|
||||||
|
// The pdf data length are the last 4 bytes.
|
||||||
|
UInt32 pdfLen = 0;
|
||||||
|
pdfLen = *(UInt32 const *)(((UInt8 const *)[data bytes]) + [data length] - 4);
|
||||||
|
pdfLen = NSSwapBigLongToHost(pdfLen); // make it big endian
|
||||||
|
if (pdfLen >= [data length] - 4) {
|
||||||
|
NSLog(@"Invalid file %@ for LinkBack", fileName);
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSData * linkBackData
|
||||||
|
= [data subdataWithRange:NSMakeRange(pdfLen, [data length] - pdfLen - 4)];
|
||||||
|
if (linkBackData == nil) {
|
||||||
|
NSLog(@"Invalid file %@ for LinkBack", fileName);
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSMutableDictionary * linkBackDataDict
|
||||||
|
= [NSUnarchiver unarchiveObjectWithData:linkBackData];
|
||||||
|
if (linkBackDataDict == nil) {
|
||||||
|
NSLog(@"LinkBack data in %@ corrupted");
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the link to the LinkBack server
|
||||||
|
LinkBack * link = [LinkBack editLinkBackData:linkBackDataDict
|
||||||
|
sourceName:fileName delegate:self itemKey:fileName];
|
||||||
|
if (link == nil) {
|
||||||
|
NSLog(@"Failed to create LinkBack link for %@", fileName);
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
[keys addObject:fileName];
|
||||||
|
}
|
||||||
|
@catch (NSException * exception) {
|
||||||
|
NSLog(@"LinkBack exception: %@", exception);
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)linkBackDidClose:(LinkBack*)link
|
||||||
|
{
|
||||||
|
NSString * fileName = [link itemKey];
|
||||||
|
if (fileName) {
|
||||||
|
[keys removeObject:fileName];
|
||||||
|
NSLog(@"LinkBack link for %@ closed", fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)linkBackServerDidSendEdit:(LinkBack*)link
|
||||||
|
{
|
||||||
|
@try {
|
||||||
|
// get pasteboard and check that linkback and pdf data is available
|
||||||
|
NSPasteboard * pboard = [link pasteboard];
|
||||||
|
NSArray * linkBackType = [NSArray arrayWithObjects: LinkBackPboardType, nil];
|
||||||
|
NSArray * pdfType = [NSArray arrayWithObjects: NSPDFPboardType, nil];
|
||||||
|
if ([pboard availableTypeFromArray:linkBackType] == nil
|
||||||
|
|| [pboard availableTypeFromArray:pdfType] == nil) {
|
||||||
|
NSLog(@"No PDF or LinkBack data in pasteboard");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get new linkback data
|
||||||
|
id linkBackDataDict = [pboard propertyListForType:LinkBackPboardType];
|
||||||
|
if (linkBackDataDict == nil) {
|
||||||
|
NSLog(@"Cannot get LinkBack data from pasteboard");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
NSData * linkBackData = [NSArchiver archivedDataWithRootObject:linkBackDataDict];
|
||||||
|
if (linkBackData == nil) {
|
||||||
|
NSLog(@"Cannot archive LinkBack data");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get pdf
|
||||||
|
NSData * pdfData = [pboard dataForType:NSPDFPboardType];
|
||||||
|
if (pdfData == nil) {
|
||||||
|
NSLog(@"Cannot get PDF data from pasteboard");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the file
|
||||||
|
NSString * fileName = [link itemKey];
|
||||||
|
NSFileHandle * file = [NSFileHandle fileHandleForUpdatingAtPath:fileName];
|
||||||
|
if (file == nil) {
|
||||||
|
NSLog(@"LinkBack file %@ disappeared.", fileName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
[file truncateFileAtOffset:0];
|
||||||
|
[file writeData:pdfData];
|
||||||
|
[file writeData:linkBackData];
|
||||||
|
|
||||||
|
UInt32 pdfLen = NSSwapHostLongToBig([pdfData length]); // big endian
|
||||||
|
NSData * lenData = [NSData dataWithBytes:&pdfLen length:4];
|
||||||
|
[file writeData:lenData];
|
||||||
|
[file closeFile];
|
||||||
|
}
|
||||||
|
@catch (NSException * exception) {
|
||||||
|
NSLog(@"LinkBack exception in linkBackServerDidSendEdit: %@", exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static LyXLinkBackClient * linkBackClient = nil;
|
||||||
|
|
||||||
|
int isLinkBackDataInPasteboard()
|
||||||
|
{
|
||||||
|
NSArray * linkBackType = [NSArray arrayWithObjects: LinkBackPboardType, nil];
|
||||||
|
NSString * ret = [[NSPasteboard generalPasteboard] availableTypeFromArray:linkBackType];
|
||||||
|
return ret != nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void getLinkBackData(void const * * buf, unsigned * len)
|
||||||
|
{
|
||||||
|
// get linkback data from pasteboard
|
||||||
|
NSPasteboard * pboard = [NSPasteboard generalPasteboard];
|
||||||
|
id linkBackData = [pboard propertyListForType:LinkBackPboardType];
|
||||||
|
|
||||||
|
NSData * nsdata
|
||||||
|
= [NSArchiver archivedDataWithRootObject:linkBackData];
|
||||||
|
if (nsdata == nil) {
|
||||||
|
*buf = 0;
|
||||||
|
*len = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
*buf = [nsdata bytes];
|
||||||
|
*len = [nsdata length];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int editLinkBackFile(char const * docName)
|
||||||
|
{
|
||||||
|
// setup Obj-C and our client
|
||||||
|
if (linkBackClient == nil)
|
||||||
|
linkBackClient = [[LyXLinkBackClient alloc] init];
|
||||||
|
if (pool == nil)
|
||||||
|
pool = [[NSAutoreleasePool alloc] init];
|
||||||
|
|
||||||
|
// FIXME: really UTF8 here?
|
||||||
|
NSString * nsDocName = [NSString stringWithUTF8String:docName];
|
||||||
|
return [linkBackClient edit:nsDocName] == YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void closeAllLinkBackLinks()
|
||||||
|
{
|
||||||
|
[linkBackClient release];
|
||||||
|
linkBackClient = nil;
|
||||||
|
|
||||||
|
[pool release];
|
||||||
|
pool = nil;
|
||||||
|
}
|
||||||
|
|
67
src/support/linkback/LinkBackServer.h
Normal file
67
src/support/linkback/LinkBackServer.h
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
//
|
||||||
|
// LinkBackServer.h
|
||||||
|
// LinkBack
|
||||||
|
//
|
||||||
|
// Created by Charles Jolley on Tue Jun 15 2004.
|
||||||
|
// Copyright (c) 2004, Nisus Software, Inc.
|
||||||
|
// 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 the Nisus Software, Inc. nor the names of its
|
||||||
|
// 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>
|
||||||
|
|
||||||
|
@class LinkBack ;
|
||||||
|
@protocol LinkBackServerDelegate, LinkBackClientDelegate ;
|
||||||
|
|
||||||
|
@protocol LinkBackServer
|
||||||
|
- (LinkBack*)initiateLinkBackFromClient:(LinkBack*)clientLinkBack ;
|
||||||
|
@end
|
||||||
|
|
||||||
|
// This method is used as the standard way of constructing the actual server name a live link connection is posted under. It is constructed from the name and identifier.
|
||||||
|
NSString* MakeLinkBackServerName(NSString* bundleIdentifier, NSString* name) ;
|
||||||
|
|
||||||
|
// a LinkBack server is created for each published server. This simply responds to connection requests to create new live links.
|
||||||
|
@interface LinkBackServer : NSObject <LinkBackServer> {
|
||||||
|
NSConnection* listener ;
|
||||||
|
NSString* name ;
|
||||||
|
id<LinkBackServerDelegate> delegate ;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (LinkBackServer*)LinkBackServerWithName:(NSString*)name ;
|
||||||
|
+ (BOOL)publishServerWithName:(NSString*)name delegate:(id<LinkBackServerDelegate>)del ;
|
||||||
|
|
||||||
|
+ (LinkBackServer*)LinkBackServerWithName:(NSString*)name inApplication:(NSString*)bundleIdentifier launchIfNeeded:(BOOL)flag fallbackURL:(NSURL*)url appName:(NSString*)appName ;
|
||||||
|
|
||||||
|
// This method is used by clients to connect
|
||||||
|
|
||||||
|
- (id)initWithName:(NSString*)name delegate:(id<LinkBackServerDelegate>)aDel;
|
||||||
|
|
||||||
|
- (BOOL)publish ; // creates the connection and adds to the list.
|
||||||
|
- (void)retract ;
|
||||||
|
|
||||||
|
@end
|
281
src/support/linkback/LinkBackServer.m
Normal file
281
src/support/linkback/LinkBackServer.m
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
//
|
||||||
|
// LinkBackServer.m
|
||||||
|
// LinkBack
|
||||||
|
//
|
||||||
|
// Created by Charles Jolley on Tue Jun 15 2004.
|
||||||
|
// Copyright (c) 2004, Nisus Software, Inc.
|
||||||
|
// 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 the Nisus Software, Inc. nor the names of its
|
||||||
|
// 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 "LinkBackServer.h"
|
||||||
|
#import "LinkBack.h"
|
||||||
|
|
||||||
|
NSString* MakeLinkBackServerName(NSString* bundleIdentifier, NSString* name)
|
||||||
|
{
|
||||||
|
return [bundleIdentifier stringByAppendingFormat: @":%@",name] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSMutableDictionary* LinkBackServers = nil ;
|
||||||
|
|
||||||
|
@implementation LinkBackServer
|
||||||
|
|
||||||
|
+ (void)initialize
|
||||||
|
{
|
||||||
|
static BOOL inited = NO ;
|
||||||
|
if (inited) return ;
|
||||||
|
|
||||||
|
[super initialize] ;
|
||||||
|
inited = YES ;
|
||||||
|
|
||||||
|
if (!LinkBackServers) LinkBackServers = [[NSMutableDictionary alloc] init];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (LinkBackServer*)LinkBackServerWithName:(NSString*)aName
|
||||||
|
{
|
||||||
|
return [LinkBackServers objectForKey: aName] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (BOOL)publishServerWithName:(NSString*)aName delegate:(id<LinkBackServerDelegate>)del
|
||||||
|
{
|
||||||
|
LinkBackServer* serv = [[LinkBackServer alloc] initWithName: aName delegate: del] ;
|
||||||
|
BOOL ret = [serv publish] ; // retains if successful
|
||||||
|
[serv release] ;
|
||||||
|
return ret ;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL LinkBackServerIsSupported(NSString* name, id supportedServers)
|
||||||
|
{
|
||||||
|
BOOL ret = NO ;
|
||||||
|
int idx ;
|
||||||
|
NSString* curServer = supportedServers ;
|
||||||
|
|
||||||
|
// NOTE: supportedServers may be nil, an NSArray, or NSString.
|
||||||
|
if (supportedServers) {
|
||||||
|
if ([supportedServers isKindOfClass: [NSArray class]]) {
|
||||||
|
idx = [supportedServers count] ;
|
||||||
|
while((NO==ret) && (--idx >= 0)) {
|
||||||
|
curServer = [supportedServers objectAtIndex: idx] ;
|
||||||
|
ret = [curServer isEqualToString: name] ;
|
||||||
|
}
|
||||||
|
} else ret = [curServer isEqualToString: name] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret ;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString* FindLinkBackServer(NSString* bundleIdentifier, NSString* serverName, NSString* dir, int level)
|
||||||
|
{
|
||||||
|
NSString* ret = nil ;
|
||||||
|
|
||||||
|
NSFileManager* fm = [NSFileManager defaultManager] ;
|
||||||
|
NSArray* contents = [fm directoryContentsAtPath: dir] ;
|
||||||
|
int idx ;
|
||||||
|
|
||||||
|
NSLog(@"searching for %@ in folder: %@", serverName, dir) ;
|
||||||
|
|
||||||
|
// working info
|
||||||
|
NSString* cpath ;
|
||||||
|
NSBundle* cbundle ;
|
||||||
|
NSString* cbundleIdentifier ;
|
||||||
|
id supportedServers ;
|
||||||
|
|
||||||
|
// resolve any symlinks, expand tildes.
|
||||||
|
dir = [dir stringByStandardizingPath] ;
|
||||||
|
|
||||||
|
// find all .app bundles in the directory and test them.
|
||||||
|
idx = (contents) ? [contents count] : 0 ;
|
||||||
|
while((nil==ret) && (--idx >= 0)) {
|
||||||
|
cpath = [contents objectAtIndex: idx] ;
|
||||||
|
|
||||||
|
if ([[cpath pathExtension] isEqualToString: @"app"]) {
|
||||||
|
cpath = [dir stringByAppendingPathComponent: cpath] ;
|
||||||
|
cbundle = [NSBundle bundleWithPath: cpath] ;
|
||||||
|
cbundleIdentifier = [cbundle bundleIdentifier] ;
|
||||||
|
|
||||||
|
if ([cbundleIdentifier isEqualToString: bundleIdentifier]) {
|
||||||
|
supportedServers = [[cbundle infoDictionary] objectForKey: @"LinkBackServer"] ;
|
||||||
|
ret= (LinkBackServerIsSupported(serverName, supportedServers)) ? cpath : nil ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the app was not found, descend into non-app dirs. only descend 4 levels to avoid taking forever.
|
||||||
|
if ((nil==ret) && (level<4)) {
|
||||||
|
idx = (contents) ? [contents count] : 0 ;
|
||||||
|
while((nil==ret) && (--idx >= 0)) {
|
||||||
|
BOOL isdir ;
|
||||||
|
|
||||||
|
cpath = [contents objectAtIndex: idx] ;
|
||||||
|
[fm fileExistsAtPath: cpath isDirectory: &isdir] ;
|
||||||
|
if (isdir && (![[cpath pathExtension] isEqualToString: @"app"])) {
|
||||||
|
cpath = [dir stringByAppendingPathComponent: cpath] ;
|
||||||
|
ret = FindLinkBackServer(bundleIdentifier, serverName, cpath, level+1) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret ;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LinkBackRunAppNotFoundPanel(NSString* appName, NSURL* url)
|
||||||
|
{
|
||||||
|
int result ;
|
||||||
|
|
||||||
|
// strings for panel
|
||||||
|
NSBundle* b = [NSBundle bundleForClass: [LinkBack class]] ;
|
||||||
|
NSString* title ;
|
||||||
|
NSString* msg ;
|
||||||
|
NSString* ok ;
|
||||||
|
NSString* urlstr ;
|
||||||
|
|
||||||
|
title = NSLocalizedStringFromTableInBundle(@"_AppNotFoundTitle", @"Localized", b, @"app not found title") ;
|
||||||
|
ok = NSLocalizedStringFromTableInBundle(@"_OK", @"Localized", b, @"ok") ;
|
||||||
|
|
||||||
|
msg = (url) ? NSLocalizedStringFromTableInBundle(@"_AppNotFoundMessageWithURL", @"Localized", b, @"app not found msg") : NSLocalizedStringFromTableInBundle(@"_AppNotFoundMessageNoURL", @"Localized", b, @"app not found msg") ;
|
||||||
|
|
||||||
|
urlstr = (url) ? NSLocalizedStringFromTableInBundle(@"_GetApplication", @"Localized", b, @"Get application") : nil ;
|
||||||
|
|
||||||
|
title = [NSString stringWithFormat: title, appName] ;
|
||||||
|
|
||||||
|
result = NSRunCriticalAlertPanel(title, msg, ok, urlstr, nil) ;
|
||||||
|
if (NSAlertAlternateReturn == result) {
|
||||||
|
[[NSWorkspace sharedWorkspace] openURL: url] ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (LinkBackServer*)LinkBackServerWithName:(NSString*)aName inApplication:(NSString*)bundleIdentifier launchIfNeeded:(BOOL)flag fallbackURL:(NSURL*)url appName:(NSString*)appName ;
|
||||||
|
{
|
||||||
|
BOOL connect = YES ;
|
||||||
|
NSString* serverName = MakeLinkBackServerName(bundleIdentifier, aName) ;
|
||||||
|
id ret = nil ;
|
||||||
|
NSTimeInterval tryMark ;
|
||||||
|
|
||||||
|
// Try to connect
|
||||||
|
ret = [NSConnection rootProxyForConnectionWithRegisteredName: serverName host: nil] ;
|
||||||
|
|
||||||
|
// if launchIfNeeded, and the connection was not available, try to launch.
|
||||||
|
if((!ret) && (flag)) {
|
||||||
|
NSString* appPath ;
|
||||||
|
id linkBackServers ;
|
||||||
|
|
||||||
|
// first, try to find the app with the bundle identifier
|
||||||
|
appPath = [[NSWorkspace sharedWorkspace] absolutePathForAppBundleWithIdentifier: bundleIdentifier] ;
|
||||||
|
linkBackServers = [[[NSBundle bundleWithPath: appPath] infoDictionary] objectForKey: @"LinkBackServer"] ;
|
||||||
|
appPath = (LinkBackServerIsSupported(aName, linkBackServers)) ? appPath : nil ;
|
||||||
|
|
||||||
|
// if the found app is not supported, we will need to search for the app ourselves.
|
||||||
|
if (nil==appPath) appPath = FindLinkBackServer(bundleIdentifier, aName, @"/Applications",0);
|
||||||
|
|
||||||
|
if (nil==appPath) appPath = FindLinkBackServer(bundleIdentifier, aName, @"~/Applications",0);
|
||||||
|
|
||||||
|
if (nil==appPath) appPath = FindLinkBackServer(bundleIdentifier, aName, @"/Network/Applications",0);
|
||||||
|
|
||||||
|
// if app path has been found, launch the app.
|
||||||
|
if (appPath) {
|
||||||
|
[[NSWorkspace sharedWorkspace] launchApplication: appName] ;
|
||||||
|
} else {
|
||||||
|
LinkBackRunAppNotFoundPanel(appName, url) ;
|
||||||
|
connect = NO ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if needed, try to connect.
|
||||||
|
// retry connection for a while if we did not succeed at first. This gives the app time to launch.
|
||||||
|
if (connect && (nil==ret)) {
|
||||||
|
tryMark = [NSDate timeIntervalSinceReferenceDate] ;
|
||||||
|
do {
|
||||||
|
ret = [NSConnection rootProxyForConnectionWithRegisteredName: serverName host: nil] ;
|
||||||
|
} while ((!ret) && (([NSDate timeIntervalSinceReferenceDate]-tryMark)<10)) ;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup protocol and return
|
||||||
|
if (ret) [ret setProtocolForProxy: @protocol(LinkBackServer)] ;
|
||||||
|
return ret ;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)initWithName:(NSString*)aName delegate:(id<LinkBackServerDelegate>)aDel
|
||||||
|
{
|
||||||
|
if (self = [super init]) {
|
||||||
|
name = [aName copy] ;
|
||||||
|
delegate = aDel ;
|
||||||
|
listener = nil ;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self ;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
if (listener) [self retract] ;
|
||||||
|
[name release] ;
|
||||||
|
[super dealloc] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)publish
|
||||||
|
{
|
||||||
|
NSString* serverName = MakeLinkBackServerName([[NSBundle mainBundle] bundleIdentifier], name) ;
|
||||||
|
BOOL ret = YES ;
|
||||||
|
|
||||||
|
// create listener and connect
|
||||||
|
NSPort* port = [NSPort port] ;
|
||||||
|
listener = [NSConnection connectionWithReceivePort: port sendPort:port] ;
|
||||||
|
[listener setRootObject: self] ;
|
||||||
|
ret = [listener registerName: serverName] ;
|
||||||
|
|
||||||
|
// if successful, retain connection and add self to list of servers.
|
||||||
|
if (ret) {
|
||||||
|
[listener retain] ;
|
||||||
|
[LinkBackServers setObject: self forKey: name] ;
|
||||||
|
} else listener = nil ; // listener will dealloc on its own.
|
||||||
|
|
||||||
|
return ret ;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)retract
|
||||||
|
{
|
||||||
|
if (listener) {
|
||||||
|
[listener invalidate] ;
|
||||||
|
[listener release] ;
|
||||||
|
listener = nil ;
|
||||||
|
}
|
||||||
|
|
||||||
|
[LinkBackServers removeObjectForKey: name] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (LinkBack*)initiateLinkBackFromClient:(LinkBack*)clientLinkBack
|
||||||
|
{
|
||||||
|
LinkBack* ret = [[LinkBack alloc] initServerWithClient: clientLinkBack delegate: delegate] ;
|
||||||
|
|
||||||
|
// NOTE: we do not release because LinkBack will release itself when it the link closes. (caj)
|
||||||
|
|
||||||
|
return ret ;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
Loading…
Reference in New Issue
Block a user