summaryrefslogtreecommitdiff
path: root/ios
diff options
context:
space:
mode:
authorjan Iversen <jani@libreoffice.org>2018-01-11 15:36:16 +0100
committerjan Iversen <jani@libreoffice.org>2018-01-11 16:11:39 +0100
commitdd51c589774a88010d78530fc6d152a9af98f095 (patch)
tree37ff2edaa3ae0ac6ee8d92bfbe41516e9044b69e /ios
parent32c31ceb0da26c260661186b6c86831494902b58 (diff)
iOS, Rendering document.
This patch is with thanks to Jon Nermut. With this patch, the iPad renders documents as it should be rendered Change-Id: I54903fde3204b949d8c608842c004cd49a211d9a
Diffstat (limited to 'ios')
-rw-r--r--ios/LibreOfficeLight/LibreOfficeLight.xcodeproj/project.pbxproj58
-rw-r--r--ios/LibreOfficeLight/LibreOfficeLight/AppDelegate.swift26
-rwxr-xr-xios/LibreOfficeLight/LibreOfficeLight/DocumentController.swift177
-rw-r--r--ios/LibreOfficeLight/LibreOfficeLight/DocumentTiledView.swift229
-rw-r--r--ios/LibreOfficeLight/LibreOfficeLight/LOKit/AsyncUtil.swift92
-rw-r--r--ios/LibreOfficeLight/LibreOfficeLight/LOKit/Document.swift589
-rw-r--r--ios/LibreOfficeLight/LibreOfficeLight/LOKit/LOKitThread.swift287
-rw-r--r--ios/LibreOfficeLight/LibreOfficeLight/LOKit/LibreOfficeKitIOSTests.swift102
-rw-r--r--ios/LibreOfficeLight/LibreOfficeLight/LOKit/LibreOfficeKitWrapper.swift227
-rw-r--r--ios/LibreOfficeLight/LibreOfficeLight/LOKit/Util.swift43
-rwxr-xr-xios/LibreOfficeLight/LibreOfficeLight/en.lproj/Main.storyboard63
-rw-r--r--ios/LibreOfficeLight/LibreOfficeLight/lokit-Bridging-Header.h1
12 files changed, 1864 insertions, 30 deletions
diff --git a/ios/LibreOfficeLight/LibreOfficeLight.xcodeproj/project.pbxproj b/ios/LibreOfficeLight/LibreOfficeLight.xcodeproj/project.pbxproj
index 13b0a4675179..650895263ca6 100644
--- a/ios/LibreOfficeLight/LibreOfficeLight.xcodeproj/project.pbxproj
+++ b/ios/LibreOfficeLight/LibreOfficeLight.xcodeproj/project.pbxproj
@@ -33,6 +33,13 @@
39B091CE1E5F0BB800682A59 /* unorc in Resources */ = {isa = PBXBuildFile; fileRef = 39B08B9C1E5F0BB600682A59 /* unorc */; };
39E950531FC9842000D82C49 /* source in Resources */ = {isa = PBXBuildFile; fileRef = 39E950521FC9842000D82C49 /* source */; };
39EF4E2F1FA500C9001914AC /* PropertiesController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39EF4E2E1FA500C9001914AC /* PropertiesController.swift */; };
+ FCC2E3FA2004A01500CEB504 /* Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCC2E3F62004A01400CEB504 /* Document.swift */; };
+ FCC2E3FC2004A01500CEB504 /* LibreOfficeKitWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCC2E3F82004A01400CEB504 /* LibreOfficeKitWrapper.swift */; };
+ FCC2E3FD2004A01500CEB504 /* LOKitThread.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCC2E3F92004A01400CEB504 /* LOKitThread.swift */; };
+ FCC2E3FF2004B59B00CEB504 /* DocumentTiledView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCC2E3FE2004B59B00CEB504 /* DocumentTiledView.swift */; };
+ FCC2E4012004B65E00CEB504 /* example.odt in Resources */ = {isa = PBXBuildFile; fileRef = FCC2E4002004B65E00CEB504 /* example.odt */; };
+ FCC2E4032004B72700CEB504 /* Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCC2E4022004B72700CEB504 /* Util.swift */; };
+ FCC2E4052004B74000CEB504 /* AsyncUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCC2E4042004B74000CEB504 /* AsyncUtil.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@@ -68,7 +75,13 @@
39E950521FC9842000D82C49 /* source */ = {isa = PBXFileReference; lastKnownFileType = folder; name = source; path = ../source; sourceTree = "<group>"; };
39EE81531FA644E800B73AB8 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
39EF4E2E1FA500C9001914AC /* PropertiesController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PropertiesController.swift; sourceTree = "<group>"; };
- 39FF0D4C200681F300A3657D /* LibreOfficeKitInit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LibreOfficeKitInit.h; path = ../../include/LibreOfficeKit/LibreOfficeKitInit.h; sourceTree = "<group>"; };
+ FCC2E3F62004A01400CEB504 /* Document.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Document.swift; sourceTree = "<group>"; };
+ FCC2E3F82004A01400CEB504 /* LibreOfficeKitWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LibreOfficeKitWrapper.swift; sourceTree = "<group>"; };
+ FCC2E3F92004A01400CEB504 /* LOKitThread.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LOKitThread.swift; sourceTree = "<group>"; };
+ FCC2E3FE2004B59B00CEB504 /* DocumentTiledView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DocumentTiledView.swift; sourceTree = "<group>"; };
+ FCC2E4002004B65E00CEB504 /* example.odt */ = {isa = PBXFileReference; lastKnownFileType = file; name = example.odt; path = "../../android/default-document/example.odt"; sourceTree = "<group>"; };
+ FCC2E4022004B72700CEB504 /* Util.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Util.swift; sourceTree = "<group>"; };
+ FCC2E4042004B74000CEB504 /* AsyncUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncUtil.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -101,7 +114,6 @@
3956B72D1FAB3DBF00BF5DE4 /* extra */ = {
isa = PBXGroup;
children = (
- 39FF0D4C200681F300A3657D /* LibreOfficeKitInit.h */,
39E950521FC9842000D82C49 /* source */,
3975A8C91FBD70EE00A87B3A /* LibreOfficeKit.h */,
);
@@ -142,10 +154,12 @@
397E08FC1E597BD8001374E0 /* LibreOfficeLight */ = {
isa = PBXGroup;
children = (
+ FCC2E3F52004A01400CEB504 /* LOKit */,
39EE81531FA644E800B73AB8 /* Info.plist */,
39503A6F1F94C4AC00F19C78 /* lokit-Bridging-Header.h */,
397E08FD1E597BD8001374E0 /* AppDelegate.swift */,
3992D8591E5B762A00BEA987 /* DocumentController.swift */,
+ FCC2E3FE2004B59B00CEB504 /* DocumentTiledView.swift */,
39284DB21FA5F207006F43E4 /* DocumentActions.swift */,
39EF4E2E1FA500C9001914AC /* PropertiesController.swift */,
392ED9B21E5E4B03005C8435 /* ViewPrintManager.swift */,
@@ -159,6 +173,7 @@
39B084E41E5F0B5200682A59 /* Resources */ = {
isa = PBXGroup;
children = (
+ FCC2E4002004B65E00CEB504 /* example.odt */,
39022C201EDC2D0800100066 /* icudt60l.dat */,
39022C1E1EDC2AB000100066 /* share */,
39022C1C1EDC2A2C00100066 /* services */,
@@ -174,6 +189,18 @@
name = Resources;
sourceTree = SOURCE_ROOT;
};
+ FCC2E3F52004A01400CEB504 /* LOKit */ = {
+ isa = PBXGroup;
+ children = (
+ FCC2E4042004B74000CEB504 /* AsyncUtil.swift */,
+ FCC2E3F62004A01400CEB504 /* Document.swift */,
+ FCC2E3F82004A01400CEB504 /* LibreOfficeKitWrapper.swift */,
+ FCC2E3F92004A01400CEB504 /* LOKitThread.swift */,
+ FCC2E4022004B72700CEB504 /* Util.swift */,
+ );
+ path = LOKit;
+ sourceTree = "<group>";
+ };
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -257,6 +284,7 @@
39B08B9F1E5F0BB600682A59 /* oovbaapi.rdb in Resources */,
39B08B9D1E5F0BB600682A59 /* fundamentalrc in Resources */,
39B091CD1E5F0BB800682A59 /* udkapi.rdb in Resources */,
+ FCC2E4012004B65E00CEB504 /* example.odt in Resources */,
39B08BD91E5F0BB600682A59 /* services.rdb in Resources */,
39B091CE1E5F0BB800682A59 /* unorc in Resources */,
39022C1F1EDC2AB000100066 /* share in Resources */,
@@ -271,11 +299,17 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ FCC2E4032004B72700CEB504 /* Util.swift in Sources */,
392ED9B31E5E4B03005C8435 /* ViewPrintManager.swift in Sources */,
399648471E5B87DC00E73E83 /* ViewProperties.swift in Sources */,
+ FCC2E3FC2004A01500CEB504 /* LibreOfficeKitWrapper.swift in Sources */,
39284DB31FA5F207006F43E4 /* DocumentActions.swift in Sources */,
3992D85A1E5B762A00BEA987 /* DocumentController.swift in Sources */,
+ FCC2E3FD2004A01500CEB504 /* LOKitThread.swift in Sources */,
397E08FE1E597BD8001374E0 /* AppDelegate.swift in Sources */,
+ FCC2E3FA2004A01500CEB504 /* Document.swift in Sources */,
+ FCC2E3FF2004B59B00CEB504 /* DocumentTiledView.swift in Sources */,
+ FCC2E4052004B74000CEB504 /* AsyncUtil.swift in Sources */,
39EF4E2F1FA500C9001914AC /* PropertiesController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -447,7 +481,10 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "LibreOfficeLight/LibreOfficeLight-Prefix.pch";
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
- HEADER_SEARCH_PATHS = "$(inherited)";
+ HEADER_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/../../include/**",
+ );
INFOPLIST_FILE = LibreOfficeLight/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.2;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@@ -459,7 +496,7 @@
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
SWIFT_VERSION = 4.0;
- TARGETED_DEVICE_FAMILY = 2;
+ TARGETED_DEVICE_FAMILY = "1,2";
VALID_ARCHS = "arm64 x86_64";
};
name = Debug;
@@ -477,6 +514,10 @@
ENABLE_TESTABILITY = NO;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "LibreOfficeLight/LibreOfficeLight-Prefix.pch";
+ HEADER_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/../../include/**",
+ );
INFOPLIST_FILE = LibreOfficeLight/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.2;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@@ -488,7 +529,7 @@
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
SWIFT_VERSION = 4.0;
- TARGETED_DEVICE_FAMILY = 2;
+ TARGETED_DEVICE_FAMILY = "1,2";
VALID_ARCHS = "arm64 x86_64";
};
name = Release;
@@ -575,7 +616,10 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "LibreOfficeLight/LibreOfficeLight-Prefix.pch";
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
- HEADER_SEARCH_PATHS = "$(inherited)";
+ HEADER_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/../../include/**",
+ );
INFOPLIST_FILE = LibreOfficeLight/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.2;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@@ -587,7 +631,7 @@
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
SWIFT_VERSION = 4.0;
- TARGETED_DEVICE_FAMILY = 2;
+ TARGETED_DEVICE_FAMILY = "1,2";
VALID_ARCHS = "arm64 x86_64";
};
name = Simulator;
diff --git a/ios/LibreOfficeLight/LibreOfficeLight/AppDelegate.swift b/ios/LibreOfficeLight/LibreOfficeLight/AppDelegate.swift
index 766aa4976a29..1804cd1e3ae3 100644
--- a/ios/LibreOfficeLight/LibreOfficeLight/AppDelegate.swift
+++ b/ios/LibreOfficeLight/LibreOfficeLight/AppDelegate.swift
@@ -9,7 +9,6 @@ import UIKit
import Foundation
-
// AppDelegate is a Delegate class that receives calls from the iOS
// kernel, and theirby allows stop/start/sleep of the application
@UIApplicationMain
@@ -22,7 +21,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate
// sent when clicking on a OO document in another app
// allowing this app to handle the document.
// remark if the app is not started it will be started first
- func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool
+ func application(_ app: UIApplication,
+ open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:])
+ -> Bool
{
let document = window?.rootViewController?.childViewControllers[0] as! DocumentController
document.doOpen(url)
@@ -33,7 +34,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate
// this function is called when the app is first started (loaded from EEProm)
// it initializes the LO system and prepares for a normal run
- func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
+ func application(_ application: UIApplication,
+ didFinishLaunchingWithOptions launchOptions:
+ [UIApplicationLaunchOptionsKey: Any]?)
+ -> Bool
{
// Get version info
let appInfo = Bundle.main.infoDictionary! as Dictionary<String,AnyObject>
@@ -46,7 +50,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate
defaults.synchronize()
// start LibreOfficeKit
- BridgeLOkit_Init(Bundle.main.bundlePath)
+ let _ = LOKitThread.instance
+
return true
}
@@ -55,8 +60,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate
// Sent when the application is about to move from active to inactive state.
// This can occur for certain types of temporary interruptions
// (such as an incoming phone call or SMS message)
- // or when the user quits the application and it begins the transition to the background state.
- // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks.
+ // or when the user quits the application and it begins the transition
+ // jto the background state.
+ // Use this method to pause ongoing tasks, disable timers,
+ // and invalidate graphics rendering callbacks.
func applicationWillResignActive(_ application: UIApplication)
{
// NOT used in this App
@@ -66,13 +73,14 @@ class AppDelegate: UIResponder, UIApplicationDelegate
// Sent when the application enters background (hipernating)
// Use this method to release shared resources, save user data, invalidate timers,
- // and store enough application state information to restore your application to its current state
- // in case it is terminated later.
+ // and store enough application state information to restore your application
+ // to its current state jin case it is terminated later.
// If your application supports background execution,
// this method is called instead of applicationWillTerminate: when the user quits.
func applicationDidEnterBackground(_ application: UIApplication)
{
- let document = window?.rootViewController?.childViewControllers[0] as! DocumentController
+ let document = window?.rootViewController?.childViewControllers[0]
+ as! DocumentController
document.Hipernate()
}
diff --git a/ios/LibreOfficeLight/LibreOfficeLight/DocumentController.swift b/ios/LibreOfficeLight/LibreOfficeLight/DocumentController.swift
index 273b0e04ea97..181e707a3da5 100755
--- a/ios/LibreOfficeLight/LibreOfficeLight/DocumentController.swift
+++ b/ios/LibreOfficeLight/LibreOfficeLight/DocumentController.swift
@@ -8,13 +8,16 @@
import UIKit
-
// DocumentController is the main viewer in the app, it displays the selected
// documents and holds a top entry to view the properties as well as a normal
// menu to handle global actions
// It is a delegate class to receive Menu events as well as file handling events
class DocumentController: UIViewController, MenuDelegate, UIDocumentBrowserViewControllerDelegate
{
+ var document: DocumentHolder? = nil
+
+ var documentView: DocumentTiledView? = nil
+
// *** Handling of DocumentController
// this is normal functions every controller must implement
@@ -22,6 +25,10 @@ class DocumentController: UIViewController, MenuDelegate, UIDocumentBrowserViewC
// holds known document types
var KnownDocumentTypes : [String] = []
+ @IBOutlet weak var scrollView: UIScrollView!
+ @IBOutlet weak var mask: UIView!
+ @IBOutlet weak var progressBar: UIProgressView!
+ @IBOutlet weak var searchBar: UISearchBar!
// called once controller is loaded
override func viewDidLoad()
@@ -36,9 +43,19 @@ class DocumentController: UIViewController, MenuDelegate, UIDocumentBrowserViewC
let x = ((dict["UTTypeTagSpecification"] as! NSDictionary)["public.filename-extension"] as! NSArray)
KnownDocumentTypes.append( x[0] as! String )
}
+ LOKitThread.instance.progressDelegate = self
}
+ override func viewDidAppear(_ animated: Bool)
+ {
+ let res = Bundle.main.url(forResource: "example", withExtension: "odt")
+ //let res = Bundle.main.url(forResource: "example2", withExtension: "docx")
+ if let exampleDoc = res
+ {
+ self.doOpen(exampleDoc)
+ }
+ }
// called when there is a memory constraint
override func didReceiveMemoryWarning()
@@ -47,6 +64,14 @@ class DocumentController: UIViewController, MenuDelegate, UIDocumentBrowserViewC
// not used in this App
}
+ @IBAction func searchIconTapped(_ sender: Any)
+ {
+ searchBar.isHidden = !searchBar.isHidden
+ if (!searchBar.isHidden)
+ {
+ searchBar.becomeFirstResponder()
+ }
+ }
// *** Handling of Background (hipernate)
@@ -60,7 +85,7 @@ class DocumentController: UIViewController, MenuDelegate, UIDocumentBrowserViewC
// Moving to hipernate
public func Hipernate() -> Void
{
- BridgeLOkit_Hipernate()
+ //BridgeLOkit_Hipernate() // FIXME
}
@@ -68,7 +93,7 @@ class DocumentController: UIViewController, MenuDelegate, UIDocumentBrowserViewC
// Moving back to foreground
public func LeaveHipernate() -> Void
{
- BridgeLOkit_LeaveHipernate()
+ //BridgeLOkit_LeaveHipernate() // FIXME
}
@@ -318,9 +343,153 @@ class DocumentController: UIViewController, MenuDelegate, UIDocumentBrowserViewC
// Real open and presentation of document
public func doOpen(_ docURL : URL)
{
- BridgeLOkit_open(docURL.absoluteString);
+ LOKitThread.instance.documentLoad(url: docURL.absoluteString)
+ {
+ doc, error in
+
+ if let document = doc
+ {
+
+ runOnMain
+ {
+ self.setDocument(doc: document)
+ }
+ }
+ else
+ {
+ // TODO - alert user of failure
+
+ }
+ }
+
+ /* FIXME
BridgeLOkit_Sizing(4, 4, 256, 256);
+ */
+ }
+
+ /// Sets the document to use and set's up it's view. Should be called on the main thread
+ public func setDocument(doc: DocumentHolder)
+ {
+ if let existingDoc = self.document
+ {
+ // TODO - cleanup
+ self.document = nil
+ }
+ if let exisitingView = self.documentView
+ {
+ exisitingView.removeFromSuperview()
+ self.documentView = nil // forces the close of the view and it's held documents before we setup the new one
+ }
+
+ // setup the new doc view
+ self.document = doc
+
+ let frameToUse = self.scrollView.frame
+
+ let docView = DocumentTiledView(frame: frameToUse, document: doc, scale: 1.0)
+
+ self.scrollView.addSubview(docView)
+ self.scrollView.contentSize = docView.frame.size
+ self.documentView = docView
+
+ // debugging view borders
+ /*
+ self.scrollView.layer.borderColor = UIColor.red.cgColor
+ self.scrollView.layer.borderWidth = 1.0
+ docView.layer.borderColor = UIColor.green.cgColor
+ docView.layer.borderWidth = 1.0
+ */
+ }
+
+ // MARK: - UIScrollViewDelegate
+}
+
+extension DocumentController: UIScrollViewDelegate
+{
+ // return a view that will be scaled. if delegate returns nil, nothing happens
+ func viewForZooming(in scrollView: UIScrollView) -> UIView?
+ {
+ return self.documentView
+ }
+
+ // called before the scroll view begins zooming its content
+ func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?)
+ {
+ print("scrollViewWillBeginZooming currentScale=\(scrollView.zoomScale)")
+ }
+
+ // scale between minimum and maximum. called after any 'bounce' animations
+ func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat)
+ {
+ print("scrollViewDidEndZooming scale=\(scale)")
+ self.documentView?.scrollViewDidEndZooming(scrollView, with: view, atScale: scale)
+ }
+}
+
+ // MARK: - UIKeyInput
+// public var hasText: Bool
+// {
+// true
+// }
+//
+//
+// public func insertText(_ text: String)
+// {
+//
+// }
+//
+// public func deleteBackward()
+// {
+//
+// }
+
+extension DocumentController: ProgressDelegate
+{
+ // MARK: - ProgressDelegate
+ func statusIndicatorStart()
+ {
+ self.mask?.isHidden = false
+ self.progressBar?.isHidden = false
+ self.progressBar?.progress = 0.0
+ }
+
+ func statusIndicatorFinish()
+ {
+ // what would be nice would be to be able to wait until the initial tiles have rendered...
+ self.mask?.isHidden = true
+ self.progressBar?.isHidden = true
+ }
+
+ func statusIndicatorSetValue(value: Double)
+ {
+ self.progressBar?.progress = Float(value) / 100.0
}
}
+extension DocumentController: UISearchBarDelegate
+{
+ // called when text changes (including clear)
+ func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String)
+ {
+
+ }
+
+
+ // called when keyboard search button pressed
+ func searchBarSearchButtonClicked(_ searchBar: UISearchBar)
+ {
+ if let text = searchBar.text
+ {
+ if text.count > 0
+ {
+ document?.search(searchString: text, forwardDirection: true, from: CGPoint(x:0, y:0) )
+ }
+ }
+ }
+
+ func searchBarCancelButtonClicked(_ searchBar: UISearchBar)
+ {
+ searchBar.isHidden = true
+ }
+}
diff --git a/ios/LibreOfficeLight/LibreOfficeLight/DocumentTiledView.swift b/ios/LibreOfficeLight/LibreOfficeLight/DocumentTiledView.swift
new file mode 100644
index 000000000000..b49a8b0eb71f
--- /dev/null
+++ b/ios/LibreOfficeLight/LibreOfficeLight/DocumentTiledView.swift
@@ -0,0 +1,229 @@
+//
+// This file is part of the LibreOffice project.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+//
+
+import UIKit
+import QuartzCore
+
+
+class DocumentTiledLayer : CATiledLayer
+{
+ override class func fadeDuration() -> CFTimeInterval
+ {
+ return 0
+ }
+}
+
+open class CachedRender
+{
+ open let x: CGFloat
+ open let y: CGFloat
+ open let scale: CGFloat
+ open let image: CGImage
+
+ public init(x: CGFloat, y: CGFloat, scale: CGFloat, image: CGImage)
+ {
+ self.x = x
+ self.y = y
+ self.scale = scale
+ self.image = image
+ }
+}
+
+
+class DocumentTiledView: UIView
+{
+ var myScale: CGFloat
+
+ weak var document: DocumentHolder? = nil
+
+ let initialSize: CGSize
+ let docSize: CGSize
+ let initialScaleFactor: CGFloat
+
+ var drawCount = 0
+
+ let drawLock = NSLock()
+
+ // Create a new view with the desired frame and scale.
+ public init(frame: CGRect, document: DocumentHolder, scale: CGFloat)
+ {
+
+
+ self.document = document
+
+
+ myScale = scale
+ initialSize = frame.size
+ var size = document.sync { $0.getDocumentSizeAsCGSize() }
+
+ // avoid divide by zero crashes
+ if (size.width == 0)
+ {
+ size.width = 1
+ }
+ if (size.height == 0)
+ {
+ size.height = 1
+ }
+ self.docSize = size
+ initialScaleFactor = (docSize.width / initialSize.width)
+ let scaledFrame = CGRect(x: 0, y: 0, width: frame.width, height: frame.width * (docSize.height / docSize.width))
+
+ print("DocumentTiledView.init frame=\(frame.desc) \n scaledFrame=\(scaledFrame.desc)\n docSize=\(docSize) \n initialScaleFactor=\(initialScaleFactor)")
+ super.init(frame: scaledFrame)
+
+ //self.contentScaleFactor = 1.0
+
+ if let tiledLayer = self.layer as? CATiledLayer
+ {
+ tiledLayer.levelsOfDetail = 4
+ tiledLayer.levelsOfDetailBias = 7
+ tiledLayer.tileSize = CGSize(width: 1024.0, height: 1024.0)
+ //tiledLayer.tileSize = CGSize(width: 512.0, height: 512.0)
+ }
+
+ }
+
+ required init?(coder aDecoder: NSCoder)
+ {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+
+
+ override class var layerClass : AnyClass
+ {
+ return DocumentTiledLayer.self
+ }
+
+
+ override func draw(_ r: CGRect)
+ {
+ // UIView uses the existence of -drawRect: to determine if it should allow its CALayer
+ // to be invalidated, which would then lead to the layer creating a backing store and
+ // -drawLayer:inContext: being called.
+ // By implementing an empty -drawRect: method, we allow UIKit to continue to implement
+ // this logic, while doing our real drawing work inside of -drawLayer:inContext:
+ }
+
+ // Draw the CGPDFPageRef into the layer at the correct scale.
+ override func draw(_ layer: CALayer, in context: CGContext)
+ {
+// if self.superview == nil
+// {
+// // check that we are still active - ios is doing some really funny things where this method gets called after dealloc which causes bad bad karma
+// return
+// }
+ guard let document = self.document else
+ {
+ return
+ }
+
+ guard let tiledLayer = layer as? CATiledLayer else { return }
+
+
+
+ let tileSize: CGSize = tiledLayer.tileSize
+ let box: CGRect = context.boundingBoxOfClipPath
+ let ctm: CGAffineTransform = context.ctm
+
+ drawLock.lock()
+ defer { drawLock.unlock() }
+
+ drawCount += 1
+ let filename = "tile\(drawCount).png"
+
+ print("drawLayer \(filename)\n bounds=\(layer.bounds.desc)\n ctm.a=\(ctm.a)\n tileSize=\(tileSize)\n box=\(box.desc)")
+
+ //context.setFillColor(UIColor.white.cgColor)
+ context.setFillColor(UIColor.blue.cgColor)
+ context.fill(box)
+ context.saveGState()
+
+ context.interpolationQuality = CGInterpolationQuality.high
+ context.setRenderingIntent(CGColorRenderingIntent.defaultIntent)
+
+ // This is where the magic happens
+
+ let pageRect = box.applying(CGAffineTransform(scaleX: initialScaleFactor, y: initialScaleFactor ))
+ print(" pageRect: \(pageRect.desc)")
+
+ // Figure out how many pixels we need for the dimensions of our tile
+ // tileSize represents a "full size" one in pixels
+
+ //let fullSizeTileInPoints = CGSize(width: CGFloat(tileSize.width) / ctm.a, height: CGFloat(tileSize.height) / ctm.a)
+ //let cropRectTileFraction = CGSize(width: box.size.width / fullSizeTileInPoints.width, height: box.size.height / fullSizeTileInPoints.height)
+ //let bitmapSize = CGSize(width: tileSize.width * cropRectTileFraction.width, height: tileSize.height * cropRectTileFraction.height)
+
+ let canvasSize = tileSize; //CGSize(width:512, height:512) // FIXME - this needs to be calculated
+
+ // we have to do the call synchronously, as the tile has to be painted now, on the current thread
+ // TODO - cache the image, and check the cache before we do the sync call
+ let image = document.sync {
+ $0.paintTileToImage(canvasSize: canvasSize, tileRect: pageRect)
+ }
+
+ if let img = image
+ {
+ // Debugging: write the file to disk
+ /*
+ if let data = UIImagePNGRepresentation(img)
+ {
+ let filename = getDocumentsDirectory().appendingPathComponent(filename)
+ try? data.write(to: filename)
+ print("Wrote tile to: \(filename)")
+ }
+ */
+
+ // We use the UIImage draw function as it automatically handles the flipping of the co-ordinate system for us.
+ UIGraphicsPushContext(context);
+ img.draw(in: box)
+ UIGraphicsPopContext()
+ }
+
+ context.restoreGState()
+
+
+ }
+
+
+
+ /*
+ fileprivate func emptyCache()
+ {
+ cachedRenders.removeAll()
+ }
+
+ fileprivate func pruneCache()
+ {
+ let max = hasReceivedMemoryWarning ? CACHE_LOWMEM : CACHE_NORMAL
+ while cachedRenders.count > max
+ {
+ cachedRenders.popFirst()
+ }
+ }
+ */
+
+ deinit
+ {
+ self.document = nil
+
+ }
+
+
+ func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat)
+ {
+ //self.setNeedsDisplay()
+ }
+
+
+// override func pressesBegan
+// {
+//
+// }
+}
diff --git a/ios/LibreOfficeLight/LibreOfficeLight/LOKit/AsyncUtil.swift b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/AsyncUtil.swift
new file mode 100644
index 000000000000..52f8c1bddced
--- /dev/null
+++ b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/AsyncUtil.swift
@@ -0,0 +1,92 @@
+//
+// This file is part of the LibreOffice project.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+//
+
+import Foundation
+
+
+public typealias Runnable = () -> ()
+
+/// Runs the closure on a queued background thread
+public func runInBackground(_ runnable: @escaping Runnable)
+{
+ DispatchQueue.global(qos: .background).async(execute: runnable)
+}
+
+
+/// Runs the closure on the UI (main) thread. Exceptions are caught and logged
+public func runOnMain(_ runnable: @escaping () -> ())
+{
+ DispatchQueue.main.async(execute: runnable)
+}
+
+/// Returns true if we are on the Main / UI thread
+public func isMainThread() -> Bool
+{
+ return Thread.isMainThread
+}
+
+/// Runs tasks in a serial way on a single thread.
+/// Why wouldn't we just use DispatchQueue or NSOperationQueue to do this?
+/// Because neither guarantee running their tasks on the same thread all the time.
+/// And in fact DispatchQueue will try and run sync tasks on the current thread where it can.
+/// Both classes try and abstract the thread away, whereas we have to use the same thread, or we end up with deadlocks in LOKit
+public class SingleThreadedQueue: Thread
+{
+ public init(name: String)
+ {
+ super.init()
+ self.name = name
+ self.start()
+ }
+
+ override public func main()
+ {
+ // You need the NSPort here because a runloop with no sources or ports registered with it
+ // will simply exit immediately instead of running forever.
+ let keepAlive = Port()
+ let rl = RunLoop.current
+ keepAlive.schedule(in: rl, forMode: .commonModes)
+
+ rl.run()
+ }
+
+ /// Run the task on the serial queue, and return immediately
+ public func async( _ runnable: @escaping Runnable)
+ {
+ let operation = BlockOperation {
+ runnable()
+ }
+ async(operation: operation)
+ }
+
+ /// Run the task on the serial queue, and return immediately
+ public func async( operation: Operation)
+ {
+ if ( Thread.current == self)
+ {
+ operation.start();
+ }
+ else
+ {
+ operation.perform(#selector(Operation.start), on: self, with: nil, waitUntilDone: false)
+ }
+ }
+
+ public func sync<R>( _ closure: @escaping () -> R ) -> R
+ {
+ var ret: R! = nil
+ let op = BlockOperation {
+ ret = closure();
+ }
+ async(operation: op)
+ op.waitUntilFinished()
+ return ret
+ }
+
+}
+
diff --git a/ios/LibreOfficeLight/LibreOfficeLight/LOKit/Document.swift b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/Document.swift
new file mode 100644
index 000000000000..8f54704dc251
--- /dev/null
+++ b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/Document.swift
@@ -0,0 +1,589 @@
+//
+// This file is part of the LibreOffice project.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+//
+
+import Foundation
+import UIKit
+import QuartzCore
+
+
+/// The Document class represents one loaded document instance
+/// Obtained through LibreOffice.documentLoad()
+open class Document
+{
+ private let pDoc: UnsafeMutablePointer<LibreOfficeKitDocument>
+ private let docClass: LibreOfficeKitDocumentClass
+
+ internal init(pDoc: UnsafeMutablePointer<LibreOfficeKitDocument>)
+ {
+ self.pDoc = pDoc
+ self.docClass = pDoc.pointee.pClass.pointee
+ }
+
+ /**
+ * Stores the document's persistent data to a URL and
+ * continues to be a representation of the old URL.
+ *
+ * @param pUrl the location where to store the document
+ * @param pFormat the format to use while exporting, when omitted, then deducted from pURL's extension
+ * @param pFilterOptions options for the export filter, e.g. SkipImages.
+ * Another useful FilterOption is "TakeOwnership". It is consumed
+ * by the saveAs() itself, and when provided, the document identity
+ * changes to the provided pUrl - meaning that '.uno:ModifiedStatus'
+ * is triggered as with the "Save As..." in the UI.
+ * "TakeOwnership" mode must not be used when saving to PNG or PDF.
+ */
+ public func saveAs(url: String, format: String? = nil, filterOptions: String? = nil) -> Bool
+ {
+ return docClass.saveAs(pDoc, url, format, filterOptions) != 0
+ }
+
+ /**
+ * Get document type.
+ *
+ * @since LibreOffice 6.0
+ * @return an element of the LibreOfficeKitDocumentType enum.
+ */
+ public func getDocumentType() -> LibreOfficeKitDocumentType
+ {
+ return LibreOfficeKitDocumentType(rawValue: LibreOfficeKitDocumentType.RawValue(docClass.getDocumentType(pDoc)))
+ }
+
+ /**
+ * Get number of part that the document contains.
+ *
+ * Part refers to either individual sheets in a Calc, or slides in Impress,
+ * and has no relevance for Writer.
+ */
+ public func getParts() -> Int32
+ {
+ return docClass.getParts(pDoc);
+ }
+
+ public func initializeForRendering()
+ {
+ docClass.initializeForRendering(pDoc, "") // TODO: arguments??
+ }
+
+ /**
+ * Get the logical rectangle of each part in the document.
+ *
+ * A part refers to an individual page in Writer and has no relevant for
+ * Calc or Impress.
+ *
+ * @return a rectangle list, using the same format as
+ * LOK_CALLBACK_TEXT_SELECTION.
+ */
+ public func getPartRectanges() -> String
+ {
+ return toString( docClass.getPartPageRectangles(pDoc) ) ?? ""
+
+ // TODO: convert to CGRects? Comes out like "284, 284, 11906, 16838; 284, 17406, 11906, 16838; 284, 34528, 11906, 16838"
+
+ }
+
+ /// Get the current part of the document.
+ public func getPart() -> Int32
+ {
+ return docClass.getPart(pDoc);
+ }
+
+ /// Set the current part of the document.
+ public func setPart( nPart: Int32 )
+ {
+ docClass.setPart(pDoc, nPart);
+ }
+
+ /// Get the current part's name.
+ public func getPartName( nPart: Int32) -> String?
+ {
+ return toString( docClass.getPartName(pDoc, nPart) )
+
+ }
+
+ /// Get the current part's hash.
+ public func getPartHash( nPart: Int32 ) -> String?
+ {
+ return toString( docClass.getPartHash(pDoc, nPart) )
+
+ }
+
+ public func setPartMode( nMode: Int32 )
+ {
+ docClass.setPartMode( pDoc, nMode);
+ }
+
+ /**
+ * Renders a subset of the document to a pre-allocated buffer.
+ *
+ * Note that the buffer size and the tile size implicitly supports
+ * rendering at different zoom levels, as the number of rendered pixels and
+ * the rendered rectangle of the document are independent.
+ *
+ * @param pBuffer pointer to the buffer, its size is determined by nCanvasWidth and nCanvasHeight.
+ * @param nCanvasWidth number of pixels in a row of pBuffer.
+ * @param nCanvasHeight number of pixels in a column of pBuffer.
+ * @param nTilePosX logical X position of the top left corner of the rendered rectangle, in TWIPs.
+ * @param nTilePosY logical Y position of the top left corner of the rendered rectangle, in TWIPs.
+ * @param nTileWidth logical width of the rendered rectangle, in TWIPs.
+ * @param nTileHeight logical height of the rendered rectangle, in TWIPs.
+ */
+ public func paintTile( pBuffer: UnsafeMutablePointer<UInt8>,
+ canvasWidth: Int32,
+ canvasHeight: Int32,
+ tilePosX: Int32,
+ tilePosY: Int32,
+ tileWidth: Int32,
+ tileHeight: Int32)
+ {
+ print("paintTile canvasWidth=\(canvasWidth) canvasHeight=\(canvasHeight) tilePosX=\(tilePosX) tilePosY=\(tilePosY) tileWidth=\(tileWidth) tileHeight=\(tileHeight) ")
+ return docClass.paintTile(pDoc, pBuffer, canvasWidth, canvasHeight,
+ tilePosX, tilePosY, tileWidth, tileHeight);
+ }
+
+ /**
+ * Renders a window (dialog, popup, etc.) with give id
+ *
+ * @param nWindowId
+ * @param pBuffer Buffer with enough memory allocated to render any dialog
+ * @param x x-coordinate from where the dialog should start painting
+ * @param y y-coordinate from where the dialog should start painting
+ * @param width The width of the dialog image to be painted
+ * @param height The height of the dialog image to be painted
+ */
+ public func paintWindow( nWindowId: UInt32,
+ pBuffer: UnsafeMutablePointer<UInt8>,
+ x: Int32,
+ y: Int32,
+ width: Int32,
+ height: Int32)
+ {
+ return docClass.paintWindow(pDoc, nWindowId, pBuffer, x, y, width, height);
+ }
+
+ /**
+ * Posts a command to the window (dialog, popup, etc.) with given id
+ *
+ * @param nWindowid
+ */
+ public func postWindow( nWindowId: UInt32, nAction: Int32)
+ {
+ return docClass.postWindow(pDoc, nWindowId, nAction);
+ }
+
+ /**
+ * Gets the tile mode: the pixel format used for the pBuffer of paintTile().
+ *
+ * @return an element of the LibreOfficeKitTileMode enum.
+ */
+ public func getTileMode() -> LibreOfficeKitTileMode
+ {
+ return LibreOfficeKitTileMode(rawValue: LibreOfficeKitTileMode.RawValue(docClass.getTileMode(pDoc)));
+ }
+
+ /// Get the document sizes in TWIPs.
+ public func getDocumentSize() -> (Int, Int)
+ {
+ print(Thread.isMainThread)
+ // long* pWidth, long* pHeight
+ var pWidth: Int = 0
+ var pHeight: Int = 0
+ docClass.getDocumentSize(pDoc, &pWidth, &pHeight);
+ return (pWidth, pHeight)
+ }
+
+ /**
+ * Initialize document for rendering.
+ *
+ * Sets the rendering and document parameters to default values that are
+ * needed to render the document correctly using tiled rendering. This
+ * method has to be called right after documentLoad() in case any of the
+ * tiled rendering methods are to be used later.
+ *
+ * Example argument string for text documents:
+ *
+ * {
+ * ".uno:HideWhitespace":
+ * {
+ * "type": "boolean",
+ * "value": "true"
+ * }
+ * }
+ *
+ * @param pArguments arguments of the rendering
+ */
+ public func initializeForRendering(arguments: String? = nil)
+ {
+ docClass.initializeForRendering(pDoc, arguments);
+ }
+
+ /**
+ * Registers a callback. LOK will invoke this function when it wants to
+ * inform the client about events.
+ *
+ * @param pCallback the callback to invoke
+ * @param pData the user data, will be passed to the callback on invocation
+ */
+ public func registerCallback( callback: @escaping LibreOfficeCallback ) -> Int
+ {
+ let ret = Callbacks.register(callback: callback)
+ let pointer = UnsafeMutableRawPointer(bitPattern: ret)
+ docClass.registerCallback(pDoc, callbackFromLibreOffice, pointer)
+ return ret
+ }
+
+ /**
+ * Posts a keyboard event to the focused frame.
+ *
+ * @param nType Event type, like press or release.
+ * @param nCharCode contains the Unicode character generated by this event or 0
+ * @param nKeyCode contains the integer code representing the key of the event (non-zero for control keys)
+ */
+ public func postKeyEvent(nType: Int32, nCharCode: Int32, nKeyCode: Int32)
+ {
+ docClass.postKeyEvent(pDoc, nType, nCharCode, nKeyCode);
+ }
+
+ /**
+ * Posts a keyboard event to the dialog
+ *
+ * @param nWindowId
+ * @param nType Event type, like press or release.
+ * @param nCharCode contains the Unicode character generated by this event or 0
+ * @param nKeyCode contains the integer code representing the key of the event (non-zero for control keys)
+ */
+ public func postWindowKeyEvent( nWindowId: UInt32, nType: Int32, nCharCode: Int32, nKeyCode: Int32)
+ {
+ docClass.postWindowKeyEvent(pDoc, nWindowId, nType, nCharCode, nKeyCode);
+ }
+
+ /**
+ * Posts a mouse event to the document.
+ *
+ * @param nType Event type, like down, move or up.
+ * @param nX horizontal position in document coordinates
+ * @param nY vertical position in document coordinates
+ * @param nCount number of clicks: 1 for single click, 2 for double click
+ * @param nButtons: which mouse buttons: 1 for left, 2 for middle, 4 right
+ * @param nModifier: which keyboard modifier: (see include/vcl/vclenum.hxx for possible values)
+ */
+ public func postMouseEvent( nType: Int32, nX: Int32, nY: Int32, nCount: Int32, nButtons: Int32, nModifier: Int32)
+ {
+ docClass.postMouseEvent(pDoc, nType, nX, nY, nCount, nButtons, nModifier);
+ }
+
+ /**
+ * Posts a mouse event to the window with given id.
+ *
+ * @param nWindowId
+ * @param nType Event type, like down, move or up.
+ * @param nX horizontal position in document coordinates
+ * @param nY vertical position in document coordinates
+ * @param nCount number of clicks: 1 for single click, 2 for double click
+ * @param nButtons: which mouse buttons: 1 for left, 2 for middle, 4 right
+ * @param nModifier: which keyboard modifier: (see include/vcl/vclenum.hxx for possible values)
+ */
+ public func postWindowMouseEvent(nWindowId: UInt32, nType: Int32, nX: Int32, nY: Int32, nCount: Int32, nButtons: Int32, nModifier: Int32)
+ {
+ docClass.postWindowMouseEvent(pDoc, nWindowId, nType, nX, nY, nCount, nButtons, nModifier);
+ }
+
+ /**
+ * Posts an UNO command to the document.
+ *
+ * Example argument string:
+ *
+ * {
+ * "SearchItem.SearchString":
+ * {
+ * "type": "string",
+ * "value": "foobar"
+ * },
+ * "SearchItem.Backward":
+ * {
+ * "type": "boolean",
+ * "value": "false"
+ * }
+ * }
+ *
+ * @param pCommand uno command to be posted to the document, like ".uno:Bold"
+ * @param pArguments arguments of the uno command.
+ */
+ public func postUnoCommand(command: String, arguments: String? = nil, notifyWhenFinished: Bool = false)
+ {
+ docClass.postUnoCommand(pDoc, command, arguments, notifyWhenFinished);
+ }
+
+ /**
+ * Sets the start or end of a text selection.
+ *
+ * @param nType @see LibreOfficeKitSetTextSelectionType
+ * @param nX horizontal position in document coordinates
+ * @param nY vertical position in document coordinates
+ */
+ public func setTextSelection( nType: Int32, nX: Int32, nY: Int32)
+ {
+ docClass.setTextSelection(pDoc, nType, nX, nY);
+ }
+
+ /**
+ * Gets the currently selected text.
+ *
+ * @param pMimeType suggests the return format, for example text/plain;charset=utf-8.
+ * @param pUsedMimeType output parameter to inform about the determined format (suggested one or plain text).
+ */
+ // FIXME - work out how to use an inout param for usedMimeType
+ public func getTextSelection(mimeType: String, usedMimeType: UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>? = nil) -> String?
+ {
+ return toString( docClass.getTextSelection(pDoc, mimeType, usedMimeType) );
+ }
+
+ /**
+ * Pastes content at the current cursor position.
+ *
+ * @param pMimeType format of pData, for example text/plain;charset=utf-8.
+ * @param pData the actual data to be pasted.
+ * @return if the supplied data was pasted successfully.
+ */
+ public func paste(mimeType: String, data: String, size: Int) -> Bool
+ {
+ return docClass.paste(pDoc, mimeType, data, size);
+ }
+
+ /**
+ * Adjusts the graphic selection.
+ *
+ * @param nType @see LibreOfficeKitSetGraphicSelectionType
+ * @param nX horizontal position in document coordinates
+ * @param nY vertical position in document coordinates
+ */
+ public func setGraphicSelection( nType: Int32, nX: Int32, nY: Int32)
+ {
+ docClass.setGraphicSelection(pDoc, nType, nX, nY);
+ }
+
+ /**
+ * Gets rid of any text or graphic selection.
+ */
+ public func resetSelection()
+ {
+ docClass.resetSelection(pDoc);
+ }
+
+ /**
+ * Returns a json mapping of the possible values for the given command
+ * e.g. {commandName: ".uno:StyleApply", commandValues: {"familyName1" : ["list of style names in the family1"], etc.}}
+ * @param pCommand a uno command for which the possible values are requested
+ * @return {commandName: unoCmd, commandValues: {possible_values}}
+ */
+ public func getCommandValues(command: String) -> String?
+ {
+ return toString(docClass.getCommandValues(pDoc, command));
+ }
+
+ /**
+ * Save the client's view so that we can compute the right zoom level
+ * for the mouse events. This only affects CALC.
+ * @param nTilePixelWidth - tile width in pixels
+ * @param nTilePixelHeight - tile height in pixels
+ * @param nTileTwipWidth - tile width in twips
+ * @param nTileTwipHeight - tile height in twips
+ */
+ public func setClientZoom(
+ nTilePixelWidth: Int32,
+ nTilePixelHeight: Int32,
+ nTileTwipWidth: Int32,
+ nTileTwipHeight: Int32)
+ {
+ docClass.setClientZoom(pDoc, nTilePixelWidth, nTilePixelHeight, nTileTwipWidth, nTileTwipHeight);
+ }
+
+ /**
+ * Inform core about the currently visible area of the document on the
+ * client, so that it can perform e.g. page down (which depends on the
+ * visible height) in a sane way.
+ *
+ * @param nX - top left corner horizontal position
+ * @param nY - top left corner vertical position
+ * @param nWidth - area width
+ * @param nHeight - area height
+ */
+ public func setClientVisibleArea( nX: Int32, nY: Int32, nWidth: Int32, nHeight: Int32)
+ {
+ docClass.setClientVisibleArea(pDoc, nX, nY, nWidth, nHeight);
+ }
+
+ /**
+ * Show/Hide a single row/column header outline for Calc documents.
+ *
+ * @param bColumn - if we are dealing with a column or row group
+ * @param nLevel - the level to which the group belongs
+ * @param nIndex - the group entry index
+ * @param bHidden - the new group state (collapsed/expanded)
+ */
+ public func setOutlineState( column: Bool, level: Int32, index: Int32, hidden: Bool)
+ {
+ docClass.setOutlineState(pDoc, column, level, index, hidden);
+ }
+
+ /**
+ * Create a new view for an existing document.
+ * By default a loaded document has 1 view.
+ * @return the ID of the new view.
+ */
+ public func createView() -> Int32
+ {
+ return docClass.createView(pDoc);
+ }
+
+ /**
+ * Destroy a view of an existing document.
+ * @param nId a view ID, returned by createView().
+ */
+ public func destroyView( id: Int32 )
+ {
+ docClass.destroyView(pDoc, id);
+ }
+
+ /**
+ * Set an existing view of an existing document as current.
+ * @param nId a view ID, returned by createView().
+ */
+ public func setView(id: Int32)
+ {
+ docClass.setView(pDoc, id);
+ }
+
+ /**
+ * Get the current view.
+ * @return a view ID, previously returned by createView().
+ */
+ public func getView() -> Int32
+ {
+ return docClass.getView(pDoc);
+ }
+
+ /**
+ * Get number of views of this document.
+ */
+ public func getViewsCount() -> Int32
+ {
+ return docClass.getViewsCount(pDoc);
+ }
+
+ /**
+ * Paints a font name or character if provided to be displayed in the font list
+ * @param pFontName the font to be painted
+ */
+ // TODO
+// public func renderFont(fontName: String,
+// const char *pChar,
+// int *pFontWidth,
+// int *pFontHeight)
+// {
+// return docClass.renderFont(pDoc, pFontName, pChar, pFontWidth, pFontHeight);
+// }
+
+ /**
+ * Renders a subset of the document's part to a pre-allocated buffer.
+ *
+ * @param nPart the part number of the document of which the tile is painted.
+ * @see paintTile.
+ */
+ public func paintPartTile(pBuffer: UnsafeMutablePointer<UInt8>,
+ nPart: Int32,
+ nCanvasWidth: Int32,
+ nCanvasHeight: Int32,
+ nTilePosX: Int32,
+ nTilePosY: Int32,
+ nTileWidth: Int32,
+ nTileHeight: Int32)
+ {
+ return docClass.paintPartTile(pDoc, pBuffer, nPart,
+ nCanvasWidth, nCanvasHeight,
+ nTilePosX, nTilePosY,
+ nTileWidth, nTileHeight);
+ }
+
+ /**
+ * Returns the viewID for each existing view. Since viewIDs are not reused,
+ * viewIDs are not the same as the index of the view in the view array over
+ * time. Use getViewsCount() to know the minimal nSize that's large enough.
+ *
+ * @param pArray the array to write the viewIDs into
+ * @param nSize the size of pArray
+ * @returns true if pArray was large enough and result is written, false
+ * otherwise.
+ */
+// bool getViewIds(int* pArray,
+// size_t nSize)
+// {
+// return docClass.getViewIds(pDoc, pArray, nSize);
+// }
+
+ /**
+ * Set the language tag of the window with the specified nId.
+ *
+ * @param nId a view ID, returned by createView().
+ * @param language Bcp47 languageTag, like en-US or so.
+ */
+ public func setViewLanguage( id: Int32, language: String)
+ {
+ docClass.setViewLanguage(pDoc, id, language);
+ }
+
+}
+
+/**
+ * iOS friendly extensions of Document.
+ * TODO: move me back to the framework.
+ */
+public extension Document
+{
+ public func getDocumentSizeAsCGSize() -> CGSize
+ {
+ let (x,y) = self.getDocumentSize()
+ return CGSize(width: x, height: y)
+ }
+
+ public func paintTileToCurrentContext(canvasSize: CGSize,
+ tileRect: CGRect)
+ {
+ let ctx = UIGraphicsGetCurrentContext()
+ //print(ctx!)
+ let ptr = unsafeBitCast(ctx, to: UnsafeMutablePointer<UInt8>.self)
+ //print(ptr)
+
+ self.paintTile(pBuffer:ptr,
+ canvasWidth: Int32(canvasSize.width),
+ canvasHeight: Int32(canvasSize.height),
+ tilePosX: Int32(tileRect.minX),
+ tilePosY: Int32(tileRect.minY),
+ tileWidth: Int32(tileRect.size.width),
+ tileHeight: Int32(tileRect.size.height))
+ }
+
+ public func paintTileToImage(canvasSize: CGSize,
+ tileRect: CGRect) -> UIImage?
+ {
+ // the scaling etc here is all black magic.
+ // I don't really understand whats going on, other than that this combination works...
+
+ UIGraphicsBeginImageContextWithOptions(canvasSize, false, 1.0)
+ let ctx = UIGraphicsGetCurrentContext()!
+
+ // print(ctx)
+ // print(ctx.ctm)
+ // print(ctx.userSpaceToDeviceSpaceTransform)
+
+ self.paintTileToCurrentContext(canvasSize: canvasSize, tileRect: tileRect)
+ let image = UIGraphicsGetImageFromCurrentImageContext()
+ UIGraphicsEndImageContext()
+ return image
+ }
+}
+
diff --git a/ios/LibreOfficeLight/LibreOfficeLight/LOKit/LOKitThread.swift b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/LOKitThread.swift
new file mode 100644
index 000000000000..34109fb88c62
--- /dev/null
+++ b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/LOKitThread.swift
@@ -0,0 +1,287 @@
+//
+// This file is part of the LibreOffice project.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+//
+
+import Foundation
+import UIKit
+
+
+
+
+/// Serves the same purpose as the LOKitThread in the Android project - sequentialises all access to LOKit on a background thread, off the UI thread.
+/// It's a singleton, and keeps a single instance of LibreOfficeKit
+/// Public methods may be called from any thread, and will dispatch their work onto the held sequential queue.
+/// TODO: move me to framework
+public class LOKitThread
+{
+ public static let instance = LOKitThread() // statics are lazy and thread safe in swift, so no need for anything more complex
+
+
+ fileprivate let queue = SingleThreadedQueue(name: "LOKitThread.queue")
+
+ /// singleton LibreOffice instance. Can only be accessed through the queue.
+ var libreOffice: LibreOffice! = nil // initialised in didFinishLaunchingWithOptions
+
+ public weak var delegate: LOKitUIDelegate? = nil
+ public weak var progressDelegate: ProgressDelegate? = nil
+
+ private init()
+ {
+
+ async {
+ self.libreOffice = try! LibreOffice() // will blow up the app if it throws, but fair enough
+
+ // hook up event handler
+ self.libreOffice.registerCallback(callback: self.onLOKEvent)
+
+ }
+ }
+
+ private func onLOKEvent(type: LibreOfficeKitCallbackType, payload: String?)
+ {
+ //LibreOfficeLight.LibreOfficeKitKeyEventType.
+ print("onLOKEvent type:\(type) payload:\(payload ?? "")")
+
+ switch type
+ {
+ case LOK_CALLBACK_STATUS_INDICATOR_START:
+ runOnMain {
+ self.progressDelegate?.statusIndicatorStart()
+ }
+
+ case LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE:
+ runOnMain {
+ if let doub = Double(payload ?? "")
+ {
+ self.progressDelegate?.statusIndicatorSetValue(value: doub)
+ }
+ }
+
+ case LOK_CALLBACK_STATUS_INDICATOR_FINISH:
+ runOnMain {
+ self.progressDelegate?.statusIndicatorFinish()
+ }
+ default:
+ print("onLOKEvent type:\(type) not handled!")
+ }
+ }
+
+ /// Run the task on the serial queue, and return immediately
+ public func async(_ runnable: @escaping Runnable)
+ {
+ queue.async( runnable)
+ }
+
+ /// Run the task on the serial queue, and block to get the result
+ /// Careful of deadlocking!
+ public func sync<R>( _ closure: @escaping () -> R ) -> R
+ {
+ let ret = queue.sync( closure )
+ return ret
+ }
+
+ public func withLibreOffice( _ closure: @escaping (LibreOffice) -> ())
+ {
+ async {
+ closure(self.libreOffice)
+ }
+ }
+
+ /// Loads a document, and calls the callback with a wrapper if successful, or an error if not.
+ public func documentLoad(url: String, callback: @escaping (DocumentHolder?, Error?) -> ())
+ {
+ withLibreOffice
+ {
+ lo in
+
+ do
+ {
+ // this is trying to avoid null context errors which pop up on doc init
+ // doesnt seem to fix
+ UIGraphicsBeginImageContext(CGSize(width:1,height:1))
+ let doc = try lo.documentLoad(url: url)
+ print("Opened document: \(url)")
+ doc.initializeForRendering()
+ UIGraphicsEndImageContext()
+
+ callback(DocumentHolder(doc: doc), nil)
+ }
+ catch
+ {
+ print("Failed to load document: \(error)")
+ callback(nil, error)
+ }
+ }
+ }
+}
+
+/**
+ * Holds the document object so to enforce access in a thread safe way.
+ */
+public class DocumentHolder
+{
+ private let doc: Document
+
+ public weak var delegate: DocumentUIDelegate? = nil
+
+ init(doc: Document)
+ {
+ self.doc = doc
+ doc.registerCallback() {
+ [weak self] typ, payload in
+ self?.onDocumentEvent(type: typ, payload: payload)
+ }
+ }
+
+ /// Gives async access to the document
+ public func async(_ closure: @escaping (Document) -> ())
+ {
+ LOKitThread.instance.async
+ {
+ closure(self.doc)
+ }
+ }
+
+ /// Gives sync access to the document - blocks until the closure runs.
+ /// Careful of deadlocks.
+ public func sync<R>( _ closure: @escaping (Document) -> R ) -> R
+ {
+ return LOKitThread.instance.sync
+ {
+ return closure(self.doc)
+ }
+ }
+
+ private func onDocumentEvent(type: LibreOfficeKitCallbackType, payload: String?)
+ {
+ print("onDocumentEvent type:\(type) payload:\(payload ?? "")")
+
+ switch type
+ {
+ case LOK_CALLBACK_INVALIDATE_TILES:
+ runOnMain {
+ self.delegate?.invalidateTiles( rects: decodeRects(payload) )
+ }
+ case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
+ runOnMain {
+ self.delegate?.invalidateVisibleCursor( rects: decodeRects(payload) )
+ }
+ case LOK_CALLBACK_TEXT_SELECTION:
+ runOnMain {
+ self.delegate?.textSelection( rects: decodeRects(payload) )
+ }
+ case LOK_CALLBACK_TEXT_SELECTION_START:
+ runOnMain {
+ self.delegate?.textSelectionStart( rects: decodeRects(payload) )
+ }
+ case LOK_CALLBACK_TEXT_SELECTION_END:
+ runOnMain {
+ self.delegate?.textSelectionEnd( rects: decodeRects(payload) )
+ }
+ default:
+ print("onDocumentEvent type:\(type) not handled!")
+ }
+ }
+
+ public func search(searchString: String, forwardDirection: Bool = true, from: CGPoint)
+ {
+ var rootJson = JSONObject()
+
+ addProperty(&rootJson, "SearchItem.SearchString", "string", searchString);
+ addProperty(&rootJson, "SearchItem.Backward", "boolean", String(forwardDirection) );
+ addProperty(&rootJson, "SearchItem.SearchStartPointX", "long", String(describing: from.x) );
+ addProperty(&rootJson, "SearchItem.SearchStartPointY", "long", String(describing: from.y) );
+ addProperty(&rootJson, "SearchItem.Command", "long", "1") // String.valueOf(0)); // search all == 1
+
+ if let jsonStr = encode(json: rootJson)
+ {
+ async {
+ $0.postUnoCommand(command: ".uno:ExecuteSearch", arguments: jsonStr, notifyWhenFinished: true)
+ }
+ }
+ }
+
+
+}
+
+public typealias JSONObject = Dictionary<String, AnyObject>
+public func addProperty( _ json: inout JSONObject, _ parentValue: String, _ type: String, _ value: String)
+{
+ var child = JSONObject();
+ child["type"] = type as AnyObject
+ child["value"] = value as AnyObject
+ json[parentValue] = child as AnyObject
+}
+
+func encode(json: JSONObject) -> String?
+{
+ //let encoder = JSONEncoder()
+
+ if let data = try? JSONSerialization.data(withJSONObject: json, options: .prettyPrinted)
+ {
+ return String(data: data, encoding: String.Encoding.utf8)
+ }
+ return nil
+}
+
+/// Decodes a series of rectangles in the form: "x, y, width, height; x, y, width, height"
+public func decodeRects(_ payload: String?) -> [CGRect]?
+{
+ guard var pl = payload else { return nil }
+ pl = pl.trimmingCharacters(in: .whitespacesAndNewlines )
+ if pl == "EMPTY" || pl.count == 0
+ {
+ return nil
+ }
+ var ret = [CGRect]()
+ for rectStr in pl.split(separator: ";")
+ {
+ let coords = rectStr.split(separator: ",").flatMap { Double($0) }
+ if coords.count == 4
+ {
+ let rect = CGRect(x: coords[0],
+ y: coords[1],
+ width: coords[2],
+ height: coords[3])
+ ret.append( rect )
+ }
+ }
+ return ret
+}
+
+/**
+ * Delegate methods for global events emitted from LOKit.
+ * Mostly dispatched on the main thread unless noted.
+ */
+public protocol LOKitUIDelegate: class
+{
+ // Nothing ATM..
+}
+
+public protocol ProgressDelegate: class
+{
+ func statusIndicatorStart()
+
+ func statusIndicatorFinish()
+
+ func statusIndicatorSetValue(value: Double)
+}
+
+
+public protocol DocumentUIDelegate: class
+{
+ func invalidateTiles(rects: [CGRect]? )
+
+ func invalidateVisibleCursor(rects: [CGRect]? )
+
+ func textSelection(rects: [CGRect]? )
+ func textSelectionStart(rects: [CGRect]? )
+ func textSelectionEnd(rects: [CGRect]? )
+
+
+
+}
diff --git a/ios/LibreOfficeLight/LibreOfficeLight/LOKit/LibreOfficeKitIOSTests.swift b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/LibreOfficeKitIOSTests.swift
new file mode 100644
index 000000000000..de9f1ee82c2c
--- /dev/null
+++ b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/LibreOfficeKitIOSTests.swift
@@ -0,0 +1,102 @@
+//
+// LibreOfficeKitIOSTests.swift
+// LibreOfficeKitIOSTests
+//
+// Created by Jon Nermut on 30/12/17.
+// Copyright © 2017 LibreOffice. All rights reserved.
+//
+
+import XCTest
+@testable import LibreOfficeKitIOS
+
+class LibreOfficeKitIOSTests: XCTestCase {
+
+ override func setUp() {
+ super.setUp()
+ // Put setup code here. This method is called before the invocation of each test method in the class.
+ }
+
+ override func tearDown() {
+ // Put teardown code here. This method is called after the invocation of each test method in the class.
+ super.tearDown()
+ }
+
+
+
+ func testLoadingSimpleDoc() {
+
+ guard let lo = try? LibreOffice() else
+ {
+ XCTFail("Could not start LibreOffice")
+ return
+ }
+
+ let b = Bundle.init(for: LibreOfficeKitIOSTests.self)
+ guard let url = b.url(forResource: "test-page-format", withExtension: "docx") else
+ {
+ XCTFail("Failed to get url to test doc")
+ return
+ }
+
+ var loCallbackCount = 0
+ lo.registerCallback()
+ {
+ typ, payload in
+ print(typ)
+ print(payload)
+ loCallbackCount += 1
+ }
+
+ guard let doc = try? lo.documentLoad(url: url.absoluteString) else
+ {
+ XCTFail("Could not load document")
+ return
+ }
+
+ var docCallbackCount = 0
+ doc.registerCallback()
+ {
+ typ, payload in
+ print(typ)
+ print(payload)
+ docCallbackCount += 1
+ }
+
+ //let typ: LibreOfficeDocumentType = doc.getDocumentType()
+ //XCTAssertTrue(typ == LibreOfficeDocumentType.LOK_DOCTYPE_TEXT)
+
+ doc.initializeForRendering()
+ let rects = doc.getPartRectanges()
+ print(rects) // 284, 284, 12240, 15840; 284, 16408, 12240, 15840
+ let tileMode = doc.getTileMode()
+ print(tileMode) // 1
+ let canvasSize = CGSize(width: 1024,height: 1024)
+ let tile = CGRect(x: 284, y: 284, width: 12240, height: 12240)
+
+
+ guard let image = doc.paintTileToImage(canvasSize: canvasSize, tileRect: tile) else
+ {
+ XCTFail("No image")
+ return
+ }
+ if let data = UIImagePNGRepresentation(image)
+ {
+ let filename = getDocumentsDirectory().appendingPathComponent("tile1.png")
+ try? data.write(to: filename)
+ print("Wrote tile to: \(filename)")
+ }
+ }
+
+}
+
+func getDocumentsDirectory() -> URL
+{
+ let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
+ return paths[0]
+}
+
+public extension Document
+{
+
+}
+
diff --git a/ios/LibreOfficeLight/LibreOfficeLight/LOKit/LibreOfficeKitWrapper.swift b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/LibreOfficeKitWrapper.swift
new file mode 100644
index 000000000000..f1d6b947c8e1
--- /dev/null
+++ b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/LibreOfficeKitWrapper.swift
@@ -0,0 +1,227 @@
+//
+// This file is part of the LibreOffice project.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+//
+
+import Foundation
+
+
+public struct LibreOfficeError: Error
+{
+ let message: String
+ public init(_ message: String)
+ {
+ self.message = message
+ }
+}
+
+public typealias LibreOfficeCallback = (_ type: LibreOfficeKitCallbackType, _ payload: String?) -> ()
+
+func callbackFromLibreOffice(nType: Int32, payload: UnsafePointer<Int8>?, pData: UnsafeMutableRawPointer?)
+{
+ if let val = pData?.hashValue
+ {
+ if let theFunc = Callbacks.callbackRegister[val]
+ {
+ let payString = toString(payload)
+ theFunc(LibreOfficeKitCallbackType(rawValue: LibreOfficeKitCallbackType.RawValue(nType)), payString)
+ }
+ else
+ {
+ print("Unknown callback: \(val)")
+ }
+ }
+ else
+ {
+ print("callbackFromLibreOffice, but pData was nil")
+ }
+}
+
+
+internal struct Callbacks
+{
+ static var count = 0
+ static var callbackRegister: Dictionary<Int, LibreOfficeCallback> = [:]
+
+ static func register(callback: @escaping LibreOfficeCallback) -> Int
+ {
+ count += 1
+ let id = count
+ callbackRegister[id] = callback
+ return id
+
+ }
+}
+
+
+open class LibreOffice
+{
+ private let pLok: UnsafeMutablePointer<LibreOfficeKit>
+ private let lokClass: LibreOfficeKitClass
+
+ public init() throws
+ {
+ let b = Bundle.init(for: LibreOffice.self)
+ let path = b.bundlePath // not Bundle.main.bundlePath
+ BridgeLOkit_Init(path)
+ let pLok = BridgeLOkit_getLOK()
+ if let lokClass = pLok?.pointee.pClass?.pointee
+ {
+ self.pLok = pLok!
+ self.lokClass = lokClass
+ print("Loaded LibreOfficeKit: \(self.getVersionInfo() ?? "")")
+ return
+ }
+ throw LibreOfficeError("Unable to init LibreOfficeKit")
+ }
+
+ /**
+ * Get version information of the LOKit process
+ *
+ * @since LibreOffice 6.0
+ * @returns JSON string containing version information in format:
+ * {ProductName: <>, ProductVersion: <>, ProductExtension: <>, BuildId: <>}
+ *
+ * Eg: {"ProductName": "LibreOffice",
+ * "ProductVersion": "5.3",
+ * "ProductExtension": ".0.0.alpha0",
+ * "BuildId": "<full 40 char git hash>"}
+ */
+ public func getVersionInfo() -> String?
+ {
+ if let pRet = lokClass.getVersionInfo(pLok)
+ {
+ return String(cString: pRet) // TODO: convert JSON
+ }
+ return nil
+ }
+
+ /**
+ * Loads a document from an URL.
+ *
+ * @param pUrl the URL of the document to load
+ * @param pFilterOptions options for the import filter, e.g. SkipImages.
+ * Another useful FilterOption is "Language=...". It is consumed
+ * by the documentLoad() itself, and when provided, LibreOfficeKit
+ * switches the language accordingly first.
+ * @since pFilterOptions argument added in LibreOffice 5.0
+ */
+ public func documentLoad(url: String) throws -> Document
+ {
+ if let pDoc = lokClass.documentLoad(pLok, url)
+ {
+ return Document(pDoc: pDoc)
+ }
+ throw LibreOfficeError("Unable to load document")
+ }
+
+
+
+ /// Returns the last error as a string
+ public func getError() -> String?
+ {
+ if let cstr = lokClass.getError(pLok)
+ {
+ let ret = String(cString: cstr)
+ lokClass.freeError(cstr)
+ return ret
+ }
+ return nil
+ }
+
+
+ /**
+ * Registers a callback. LOK will invoke this function when it wants to
+ * inform the client about events.
+ *
+ * @since LibreOffice 6.0
+ * @param pCallback the callback to invoke
+ * @param pData the user data, will be passed to the callback on invocation
+ */
+ public func registerCallback( callback: @escaping LibreOfficeCallback ) -> Int
+ {
+ let ret = Callbacks.register(callback: callback)
+ let pointer = UnsafeMutableRawPointer(bitPattern: ret)
+ lokClass.registerCallback(pLok, callbackFromLibreOffice, pointer)
+ return ret
+ }
+
+ /**
+ * Returns details of filter types.
+ *
+ * Example returned string:
+ *
+ * {
+ * "writer8": {
+ * "MediaType": "application/vnd.oasis.opendocument.text"
+ * },
+ * "calc8": {
+ * "MediaType": "application/vnd.oasis.opendocument.spreadsheet"
+ * }
+ * }
+ *
+ * @since LibreOffice 6.0
+ */
+ public func getFilterTypes() -> String?
+ {
+ return toString(lokClass.getFilterTypes(pLok));
+ }
+
+ /**
+ * Set bitmask of optional features supported by the client.
+ *
+ * @since LibreOffice 6.0
+ * @see LibreOfficeKitOptionalFeatures
+ */
+ public func setOptionalFeatures(features: UInt64)
+ {
+ return lokClass.setOptionalFeatures(pLok, features);
+ }
+
+ /**
+ * Set password required for loading or editing a document.
+ *
+ * Loading the document is blocked until the password is provided.
+ *
+ * @param pURL the URL of the document, as sent to the callback
+ * @param pPassword the password, nullptr indicates no password
+ *
+ * In response to LOK_CALLBACK_DOCUMENT_PASSWORD, a valid password
+ * will continue loading the document, an invalid password will
+ * result in another LOK_CALLBACK_DOCUMENT_PASSWORD request,
+ * and a NULL password will abort loading the document.
+ *
+ * In response to LOK_CALLBACK_DOCUMENT_PASSWORD_TO_MODIFY, a valid
+ * password will continue loading the document, an invalid password will
+ * result in another LOK_CALLBACK_DOCUMENT_PASSWORD_TO_MODIFY request,
+ * and a NULL password will continue loading the document in read-only
+ * mode.
+ *
+ * @since LibreOffice 6.0
+ */
+ public func setDocumentPassword(URL: String, password: String)
+ {
+ lokClass.setDocumentPassword(pLok, URL, password);
+ }
+
+
+
+
+ /**
+ * Run a macro.
+ *
+ * Same syntax as on command line is permissible (ie. the macro:// URI forms)
+ *
+ * @since LibreOffice 6.0
+ * @param pURL macro url to run
+ */
+
+ public func runMacro( URL: String ) -> Bool
+ {
+ return lokClass.runMacro( pLok, URL ) != 0;
+ }
+}
+
diff --git a/ios/LibreOfficeLight/LibreOfficeLight/LOKit/Util.swift b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/Util.swift
new file mode 100644
index 000000000000..596ca45e34a9
--- /dev/null
+++ b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/Util.swift
@@ -0,0 +1,43 @@
+//
+// This file is part of the LibreOffice project.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+//
+
+import UIKit
+
+
+func getDocumentsDirectory() -> URL
+{
+ let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
+ return paths[0]
+}
+
+public extension CGRect
+{
+ public var desc: String
+ {
+ return "(x: \(self.origin.x), y: \(self.origin.y), width: \(self.size.width), height: \(self.size.height), maxX: \(self.maxX), maxY: \(self.maxY))"
+ }
+}
+
+public func toString(_ pointer: UnsafeMutablePointer<Int8>?) -> String?
+{
+ if let p = pointer
+ {
+ return String(cString: p)
+ }
+ return nil
+}
+
+public func toString(_ pointer: UnsafePointer<Int8>?) -> String?
+{
+ if let p = pointer
+ {
+ return String(cString: p)
+ }
+ return nil
+}
+
diff --git a/ios/LibreOfficeLight/LibreOfficeLight/en.lproj/Main.storyboard b/ios/LibreOfficeLight/LibreOfficeLight/en.lproj/Main.storyboard
index e2748dad3c8c..ccc91115c5e0 100755
--- a/ios/LibreOfficeLight/LibreOfficeLight/en.lproj/Main.storyboard
+++ b/ios/LibreOfficeLight/LibreOfficeLight/en.lproj/Main.storyboard
@@ -1,11 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13196" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="DGj-7d-jfR">
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="DGj-7d-jfR">
<device id="ipad9_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
- <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13174"/>
+ <deployment identifier="iOS"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
+ <capability name="Navigation items with more than one left or right bar item" minToolsVersion="7.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
@@ -20,6 +22,34 @@
<view key="view" contentMode="scaleToFill" id="kh9-bI-dsS">
<rect key="frame" x="0.0" y="0.0" width="768" height="1024"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+ <subviews>
+ <scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" fixedFrame="YES" minimumZoomScale="0.5" maximumZoomScale="8" translatesAutoresizingMaskIntoConstraints="NO" id="cJ7-wO-9D1">
+ <rect key="frame" x="0.0" y="64" width="768" height="960"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <connections>
+ <outlet property="delegate" destination="vXZ-lx-hvc" id="mWv-AB-k2W"/>
+ </connections>
+ </scrollView>
+ <view opaque="NO" userInteractionEnabled="NO" alpha="0.5" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="URZ-zU-xtO" userLabel="Mask">
+ <rect key="frame" x="0.0" y="64" width="768" height="960"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <subviews>
+ <progressView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" fixedFrame="YES" progressViewStyle="bar" translatesAutoresizingMaskIntoConstraints="NO" id="hRJ-mR-Vnv">
+ <rect key="frame" x="309" y="479" width="150" height="1.5"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES"/>
+ </progressView>
+ </subviews>
+ <color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
+ </view>
+ <searchBar hidden="YES" contentMode="redraw" fixedFrame="YES" placeholder="Search for text" showsCancelButton="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Aq0-4K-7FT">
+ <rect key="frame" x="0.0" y="64" width="768" height="56"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
+ <textInputTraits key="textInputTraits"/>
+ <connections>
+ <outlet property="delegate" destination="vXZ-lx-hvc" id="bZ5-fa-vQ6"/>
+ </connections>
+ </searchBar>
+ </subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
<toolbarItems/>
@@ -29,19 +59,32 @@
<action selector="doProperties:" destination="vXZ-lx-hvc" id="mHw-Uf-vh2"/>
</connections>
</barButtonItem>
- <barButtonItem key="rightBarButtonItem" systemItem="action" id="BNq-ol-ZVK">
- <connections>
- <segue destination="IER-X5-Ax8" kind="popoverPresentation" identifier="showActions" popoverAnchorBarButtonItem="BNq-ol-ZVK" id="xmZ-1A-ZrW">
- <popoverArrowDirection key="popoverArrowDirection" up="YES" down="YES" left="YES" right="YES"/>
- </segue>
- </connections>
- </barButtonItem>
+ <rightBarButtonItems>
+ <barButtonItem systemItem="action" id="BNq-ol-ZVK">
+ <connections>
+ <segue destination="IER-X5-Ax8" kind="popoverPresentation" identifier="showActions" popoverAnchorBarButtonItem="BNq-ol-ZVK" id="xmZ-1A-ZrW">
+ <popoverArrowDirection key="popoverArrowDirection" up="YES" down="YES" left="YES" right="YES"/>
+ </segue>
+ </connections>
+ </barButtonItem>
+ <barButtonItem systemItem="search" id="3s2-a6-9gP">
+ <connections>
+ <action selector="searchIconTapped:" destination="vXZ-lx-hvc" id="tSY-uz-ifH"/>
+ </connections>
+ </barButtonItem>
+ </rightBarButtonItems>
</navigationItem>
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
+ <connections>
+ <outlet property="mask" destination="URZ-zU-xtO" id="pkw-v3-0gr"/>
+ <outlet property="progressBar" destination="hRJ-mR-Vnv" id="4lJ-kG-9SW"/>
+ <outlet property="scrollView" destination="cJ7-wO-9D1" id="U50-LO-plb"/>
+ <outlet property="searchBar" destination="Aq0-4K-7FT" id="B3A-Ck-UdD"/>
+ </connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="x5A-6p-PRh" sceneMemberID="firstResponder"/>
</objects>
- <point key="canvasLocation" x="1231" y="304"/>
+ <point key="canvasLocation" x="1230.46875" y="303.515625"/>
</scene>
<!--Print Manager-->
<scene sceneID="viJ-XJ-htc">
diff --git a/ios/LibreOfficeLight/LibreOfficeLight/lokit-Bridging-Header.h b/ios/LibreOfficeLight/LibreOfficeLight/lokit-Bridging-Header.h
index d501ae863444..bc276e9d31e2 100644
--- a/ios/LibreOfficeLight/LibreOfficeLight/lokit-Bridging-Header.h
+++ b/ios/LibreOfficeLight/LibreOfficeLight/lokit-Bridging-Header.h
@@ -10,4 +10,5 @@
// LibreOfficeKit is a prelink of all used LO libraries, generated
// as its own xCode project.
+#define LOK_USE_UNSTABLE_API
#import "../../source/LibreOfficeKit.h"