[Libreoffice-commits] core.git: ios/LibreOfficeLight

Jon Nermut jon.nermut at gmail.com
Sat Jan 20 17:58:51 UTC 2018


 ios/LibreOfficeLight/LibreOfficeLight.xcodeproj/project.pbxproj  |   28 
 ios/LibreOfficeLight/LibreOfficeLight/DocumentController.swift   |    4 
 ios/LibreOfficeLight/LibreOfficeLight/DocumentTiledView.swift    |   65 +
 ios/LibreOfficeLight/LibreOfficeLight/LOKit/DocumentHolder.swift |  327 +++++++++
 ios/LibreOfficeLight/LibreOfficeLight/LOKit/LOKitThread.swift    |  350 ----------
 ios/LibreOfficeLight/LibreOfficeLight/LOKit/RenderCache.swift    |   78 ++
 6 files changed, 489 insertions(+), 363 deletions(-)

New commits:
commit a468fef9ac977e812e83e3cce462b75d8d24c64d
Author: Jon Nermut <jon.nermut at gmail.com>
Date:   Sat Jan 20 17:08:43 2018 +1100

    iOS: keep track of doc part
    
    - implement swipe left/right and tap gestures for presentations
    - move some classes to their own files
    
    Change-Id: I3ddd3e17ec809c87097d5515f08038bbc969764f
    Reviewed-on: https://gerrit.libreoffice.org/48231
    Reviewed-by: jan iversen <jani at libreoffice.org>
    Tested-by: jan iversen <jani at libreoffice.org>

diff --git a/ios/LibreOfficeLight/LibreOfficeLight.xcodeproj/project.pbxproj b/ios/LibreOfficeLight/LibreOfficeLight.xcodeproj/project.pbxproj
index 48174b80e271..315d4d18151b 100644
--- a/ios/LibreOfficeLight/LibreOfficeLight.xcodeproj/project.pbxproj
+++ b/ios/LibreOfficeLight/LibreOfficeLight.xcodeproj/project.pbxproj
@@ -33,6 +33,8 @@
 		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 */; };
+		FC31D01E2012F65500E7F402 /* DocumentHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC31D01D2012F65500E7F402 /* DocumentHolder.swift */; };
+		FC31D0202012F6D300E7F402 /* RenderCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC31D01F2012F6D300E7F402 /* RenderCache.swift */; };
 		FCAB1CB82009DB6900F1CC34 /* DocumentOverlaysView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCAB1CB72009DB6900F1CC34 /* DocumentOverlaysView.swift */; };
 		FCC2E3FA2004A01500CEB504 /* Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCC2E3F62004A01400CEB504 /* Document.swift */; };
 		FCC2E3FC2004A01500CEB504 /* LibreOfficeKitWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCC2E3F82004A01400CEB504 /* LibreOfficeKitWrapper.swift */; };
@@ -77,6 +79,14 @@
 		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>"; };
+		FC31D00E2012EE4A00E7F402 /* LibreOfficeKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LibreOfficeKit.h; sourceTree = "<group>"; };
+		FC31D00F2012EE4A00E7F402 /* LibreOfficeKit.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LibreOfficeKit.hxx; sourceTree = "<group>"; };
+		FC31D0102012EE4A00E7F402 /* LibreOfficeKitEnums.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LibreOfficeKitEnums.h; sourceTree = "<group>"; };
+		FC31D0112012EE4A00E7F402 /* LibreOfficeKitGtk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LibreOfficeKitGtk.h; sourceTree = "<group>"; };
+		FC31D0122012EE4A00E7F402 /* LibreOfficeKitInit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LibreOfficeKitInit.h; sourceTree = "<group>"; };
+		FC31D0132012EE4A00E7F402 /* LibreOfficeKitTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LibreOfficeKitTypes.h; sourceTree = "<group>"; };
+		FC31D01D2012F65500E7F402 /* DocumentHolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentHolder.swift; sourceTree = "<group>"; };
+		FC31D01F2012F6D300E7F402 /* RenderCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenderCache.swift; sourceTree = "<group>"; };
 		FCAB1CB72009DB6900F1CC34 /* DocumentOverlaysView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentOverlaysView.swift; 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>"; };
@@ -194,13 +204,29 @@
 			name = Resources;
 			sourceTree = SOURCE_ROOT;
 		};
+		FC31D00D2012EE4A00E7F402 /* LibreOfficeKit */ = {
+			isa = PBXGroup;
+			children = (
+				FC31D00E2012EE4A00E7F402 /* LibreOfficeKit.h */,
+				FC31D00F2012EE4A00E7F402 /* LibreOfficeKit.hxx */,
+				FC31D0102012EE4A00E7F402 /* LibreOfficeKitEnums.h */,
+				FC31D0112012EE4A00E7F402 /* LibreOfficeKitGtk.h */,
+				FC31D0122012EE4A00E7F402 /* LibreOfficeKitInit.h */,
+				FC31D0132012EE4A00E7F402 /* LibreOfficeKitTypes.h */,
+			);
+			name = LibreOfficeKit;
+			path = ../../include/LibreOfficeKit;
+			sourceTree = "<group>";
+		};
 		FCC2E3F52004A01400CEB504 /* LOKit */ = {
 			isa = PBXGroup;
 			children = (
 				FCC2E4042004B74000CEB504 /* AsyncUtil.swift */,
 				FCC2E3F62004A01400CEB504 /* Document.swift */,
+				FC31D01D2012F65500E7F402 /* DocumentHolder.swift */,
 				FCC2E3F82004A01400CEB504 /* LibreOfficeKitWrapper.swift */,
 				FCC2E3F92004A01400CEB504 /* LOKitThread.swift */,
+				FC31D01F2012F6D300E7F402 /* RenderCache.swift */,
 				FCC2E4022004B72700CEB504 /* Util.swift */,
 			);
 			path = LOKit;
@@ -306,7 +332,9 @@
 			files = (
 				FCC2E4032004B72700CEB504 /* Util.swift in Sources */,
 				392ED9B31E5E4B03005C8435 /* ViewPrintManager.swift in Sources */,
+				FC31D01E2012F65500E7F402 /* DocumentHolder.swift in Sources */,
 				FCAB1CB82009DB6900F1CC34 /* DocumentOverlaysView.swift in Sources */,
+				FC31D0202012F6D300E7F402 /* RenderCache.swift in Sources */,
 				399648471E5B87DC00E73E83 /* ViewProperties.swift in Sources */,
 				FCC2E3FC2004A01500CEB504 /* LibreOfficeKitWrapper.swift in Sources */,
 				39284DB31FA5F207006F43E4 /* DocumentActions.swift in Sources */,
diff --git a/ios/LibreOfficeLight/LibreOfficeLight/DocumentController.swift b/ios/LibreOfficeLight/LibreOfficeLight/DocumentController.swift
index a15889985f31..3decec85410a 100755
--- a/ios/LibreOfficeLight/LibreOfficeLight/DocumentController.swift
+++ b/ios/LibreOfficeLight/LibreOfficeLight/DocumentController.swift
@@ -61,9 +61,11 @@ class DocumentController: UIViewController, MenuDelegate, UIDocumentBrowserViewC
     override func viewDidAppear(_ animated: Bool)
     {
         super.viewDidAppear(animated)
-        let res = Bundle.main.url(forResource: "example", withExtension: "odt")
+        //let res = Bundle.main.url(forResource: "example", withExtension: "odt")
         //let res = Bundle.main.url(forResource: "example2", withExtension: "docx")
 
+        let res = Bundle.main.url(forResource: "testdata/1", withExtension: "pptx")
+
         if let exampleDoc = res
         {
             self.doOpen(exampleDoc)
diff --git a/ios/LibreOfficeLight/LibreOfficeLight/DocumentTiledView.swift b/ios/LibreOfficeLight/LibreOfficeLight/DocumentTiledView.swift
index 20ca23178f5c..f0a36878c4b3 100644
--- a/ios/LibreOfficeLight/LibreOfficeLight/DocumentTiledView.swift
+++ b/ios/LibreOfficeLight/LibreOfficeLight/DocumentTiledView.swift
@@ -18,9 +18,7 @@ class DocumentTiledLayer : CATiledLayer
     }
 }
 
-
-
-
+/// The main tiled view, which sits inside the scroll view
 public class DocumentTiledView: UIView
 {
     var myScale: CGFloat
@@ -33,16 +31,11 @@ public class DocumentTiledView: UIView
 
     var drawCount = 0
 
-
-
     // 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() }
@@ -67,12 +60,28 @@ public class DocumentTiledView: UIView
 
         if let tiledLayer = self.layer as? CATiledLayer
         {
+            // these are all tweakable parameters, that give different behaviour to the tiled view
             tiledLayer.levelsOfDetail = 4
             tiledLayer.levelsOfDetailBias = 7
             tiledLayer.tileSize = CGSize(width: 1024.0, height: 1024.0)
             //tiledLayer.tileSize = CGSize(width: 512.0, height: 512.0)
         }
 
+        let tap = UITapGestureRecognizer(target: self, action: #selector(onTap) )
+        tap.numberOfTapsRequired = 1
+        self.addGestureRecognizer(tap)
+
+        if (document.isPresentation) // only for preso atm
+        {
+            // add swipe left/right gestures on a preso
+            let swipeRight = UISwipeGestureRecognizer(target: self, action: #selector(onSwipeRight))
+            swipeRight.direction = .right
+            self.addGestureRecognizer(swipeRight)
+
+            let swipeLeft = UISwipeGestureRecognizer(target: self, action: #selector(onSwipeLeft))
+            swipeLeft.direction = .left
+            self.addGestureRecognizer(swipeLeft)
+        }
     }
 
     required public init?(coder aDecoder: NSCoder)
@@ -80,6 +89,14 @@ public class DocumentTiledView: UIView
         fatalError("init(coder:) has not been implemented")
     }
 
+    func incrementPart(amount: Int)
+    {
+        document?.incrementPart(amount: Int32(amount))
+        document?.async { _ in
+            runOnMain { self.setNeedsDisplay() }
+        }
+    }
+
     public func twipsToPixels(rect: CGRect) -> CGRect
     {
         return rect.applying(CGAffineTransform(scaleX: 1.0/initialScaleFactor, y: 1.0/initialScaleFactor ))
@@ -196,8 +213,32 @@ public class DocumentTiledView: UIView
     }
 
 
-//    override func pressesBegan
-//    {
-//
-//    }
+}
+
+/// Gesture handlers
+public extension DocumentTiledView
+{
+    @objc func onTap(_ sender: UITapGestureRecognizer)
+    {
+        if (document?.isPresentation ?? false)
+        {
+            incrementPart(amount: 1)
+        }
+    }
+
+    @objc func onSwipeRight(_ sender: UISwipeGestureRecognizer)
+    {
+        if (document?.isPresentation ?? false)
+        {
+            incrementPart(amount: -1)
+        }
+    }
+
+    @objc func onSwipeLeft(_ sender: UISwipeGestureRecognizer)
+    {
+        if (document?.isPresentation ?? false)
+        {
+            incrementPart(amount: 1)
+        }
+    }
 }
diff --git a/ios/LibreOfficeLight/LibreOfficeLight/LOKit/DocumentHolder.swift b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/DocumentHolder.swift
new file mode 100644
index 000000000000..a380cc45edd0
--- /dev/null
+++ b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/DocumentHolder.swift
@@ -0,0 +1,327 @@
+//
+// 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
+
+/**
+ * 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
+    public weak var searchDelegate: SearchDelegate? = nil
+
+    private let cache = RenderCache()
+
+    public let documentType: LibreOfficeKitDocumentType
+    public let documentSize: CGSize
+    public let views: Int32
+    public let parts: Int32
+
+    public private(set) var currentPart: Int32 = 0
+
+    init(doc: Document)
+    {
+        self.doc = doc
+        self.documentType = doc.getDocumentType()
+        documentSize = doc.getDocumentSizeAsCGSize()
+        views = doc.getViewsCount()
+        parts = doc.getParts()
+
+        doc.registerCallback() {
+            [weak self] typ, payload in
+            self?.onDocumentEvent(type: typ, payload: payload)
+        }
+    }
+
+    public var isPresentation: Bool
+    {
+        return documentType == LOK_DOCTYPE_PRESENTATION
+    }
+    public var isText: Bool
+    {
+        return documentType == LOK_DOCTYPE_TEXT
+    }
+    public var isDrawing: Bool
+    {
+        return documentType == LOK_DOCTYPE_DRAWING
+    }
+    public var isSpeadsheet: Bool
+    {
+        return documentType == LOK_DOCTYPE_SPREADSHEET
+    }
+
+    /// Gives async access to the document
+    public func async(_ closure: @escaping (Document) -> ())
+    {
+        LOKitThread.instance.async
+        {
+            closure(self.doc)
+        }
+        self.invokeHandlers()
+    }
+
+    public func invokeHandlers()
+    {
+        LOKitThread.instance.async
+        {
+            self.doc.invokeHandlers()
+        }
+    }
+
+    /// 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
+        {
+            self.invokeHandlers()
+            return closure(self.doc)
+        }
+    }
+
+    /// Paints a tile and return synchronously, using a cached version if it can
+    public func paintTileToImage(canvasSize: CGSize,
+                                 tileRect: CGRect) -> UIImage?
+    {
+        if let cached = cache.get(part: currentPart, canvasSize: canvasSize, tileRect: tileRect)
+        {
+            return cached
+        }
+
+        let img = sync {
+            $0.paintTileToImage(canvasSize: canvasSize, tileRect: tileRect)
+        }
+        if let image = img
+        {
+            cache.add(cachedRender: CachedRender(part: currentPart, canvasSize: canvasSize, tileRect: tileRect, image: image))
+        }
+
+        return img
+    }
+
+    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) )
+            }
+
+        case LOK_CALLBACK_SEARCH_NOT_FOUND:
+            runOnMain {
+                self.searchDelegate?.searchNotFound()
+            }
+        case LOK_CALLBACK_SEARCH_RESULT_SELECTION:
+            runOnMain {
+                self.searchResults(payload: payload)
+            }
+
+        case LOK_CALLBACK_SET_PART:
+            if let p = payload, let newPart = Int32(p)
+            {
+                self.currentPart = newPart
+                // TODO: callback?
+            }
+
+        default:
+            print("onDocumentEvent type:\(type) not handled!")
+        }
+    }
+
+    private func searchResults(payload: String?)
+    {
+        if let d = payload, let data = d.data(using: .utf8)
+        {
+            let decoder = JSONDecoder()
+            do
+            {
+                let searchResults = try decoder.decode(SearchResults.self, from: data )
+                self.searchDelegate?.searchResultSelection(searchResults: searchResults)
+            }
+            catch
+            {
+                print("Error decoding payload: \(error)")
+            }
+        }
+    }
+
+    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", "0") // String.valueOf(0)); // search all == 1
+
+        if let jsonStr = encode(json: rootJson)
+        {
+            async {
+                $0.postUnoCommand(command: ".uno:ExecuteSearch", arguments: jsonStr, notifyWhenFinished: true)
+            }
+        }
+    }
+
+    public func incrementPart(amount: Int32)
+    {
+        async {
+            document in
+            let currentPart = document.getPart()
+            let numParts = document.getParts()
+            let newPart = currentPart + amount
+            if (newPart < numParts && newPart > 0)
+            {
+                document.setPart(nPart: newPart)
+            }
+        }
+    }
+}
+
+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?
+{
+    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.trimmingCharacters(in: .whitespacesAndNewlines)) }
+        if coords.count == 4
+        {
+            let rect = CGRect(x: coords[0],
+                              y: coords[1],
+                              width: coords[2],
+                              height: coords[3])
+            ret.append( rect )
+        }
+    }
+    return ret
+}
+
+// MARK :- Delegates
+
+public protocol DocumentUIDelegate: class
+{
+    func invalidateTiles(rects: [CGRect]? )
+    func invalidateVisibleCursor(rects: [CGRect]? )
+
+    func textSelection(rects: [CGRect]? )
+    func textSelectionStart(rects: [CGRect]? )
+    func textSelectionEnd(rects: [CGRect]? )
+}
+
+public protocol SearchDelegate: class
+{
+    func searchNotFound()
+    func searchResultSelection(searchResults: SearchResults)
+}
+
+/**
+ Encodes this example json:
+ {
+ "searchString": "Office",
+ "highlightAll": "true",
+ "searchResultSelection": [
+ {
+ "part": "0",
+ "rectangles": "1951, 10743, 627, 239"
+ },
+ {
+ "part": "0",
+ "rectangles": "5343, 9496, 627, 287"
+ },
+ {
+ "part": "0",
+ "rectangles": "1951, 9256, 627, 239"
+ },
+ {
+ "part": "0",
+ "rectangles": "6502, 5946, 626, 287"
+ },
+ {
+ "part": "0",
+ "rectangles": "6686, 5658, 627, 287"
+ },
+ {
+ "part": "0",
+ "rectangles": "4103, 5418, 573, 239"
+ },
+ {
+ "part": "0",
+ "rectangles": "1951, 5418, 627, 239"
+ },
+ {
+ "part": "0",
+ "rectangles": "4934, 1658, 1586, 559"
+ }
+ ]
+ }
+ */
+public struct SearchResults: Codable
+{
+    public var searchString: String?
+    public var highlightAll: String?
+    public var searchResultSelection: Array<PartAndRectangles>?
+}
+
+public struct PartAndRectangles: Codable
+{
+    public var part: String?
+    public var rectangles: String?
+
+    public var rectsAsCGRects: [CGRect]? {
+        return decodeRects(self.rectangles)
+    }
+}
+
diff --git a/ios/LibreOfficeLight/LibreOfficeLight/LOKit/LOKitThread.swift b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/LOKitThread.swift
index e8f60e0f2119..8e3607612a6b 100644
--- a/ios/LibreOfficeLight/LibreOfficeLight/LOKit/LOKitThread.swift
+++ b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/LOKitThread.swift
@@ -119,280 +119,6 @@ public class LOKitThread
     }
 }
 
-
-open class CachedRender
-{
-    open let canvasSize: CGSize
-    open let tileRect: CGRect
-    open let image: UIImage
-
-    public init(canvasSize: CGSize, tileRect: CGRect, image: UIImage)
-    {
-        self.canvasSize = canvasSize
-        self.tileRect = tileRect
-        self.image = image
-    }
-}
-
-class RenderCache
-{
-    let CACHE_LOWMEM = 4
-    let CACHE_NORMAL = 20
-
-    var cachedRenders: [CachedRender] = []
-    var hasReceivedMemoryWarning = false
-
-    let lock = NSRecursiveLock()
-
-    func emptyCache()
-    {
-        lock.lock(); defer { lock.unlock() }
-
-        cachedRenders.removeAll()
-
-    }
-
-    func pruneCache()
-    {
-        lock.lock(); defer { lock.unlock() }
-
-        let max = hasReceivedMemoryWarning ? CACHE_LOWMEM : CACHE_NORMAL
-        while cachedRenders.count > max
-        {
-            cachedRenders.remove(at: 0)
-        }
-    }
-
-    func add(cachedRender: CachedRender)
-    {
-        lock.lock(); defer { lock.unlock() }
-
-        cachedRenders.append(cachedRender)
-        pruneCache()
-    }
-
-    func get(canvasSize: CGSize, tileRect: CGRect) -> UIImage?
-    {
-        lock.lock(); defer { lock.unlock() }
-
-        if let cr = cachedRenders.first(where: { $0.canvasSize == canvasSize && $0.tileRect == tileRect })
-        {
-            return cr.image
-        }
-        return nil
-    }
-
-}
-
-/**
- * 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
-    public weak var searchDelegate: SearchDelegate? = nil
-
-    private let cache = RenderCache()
-
-    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)
-        }
-        self.invokeHandlers()
-    }
-
-    public func invokeHandlers()
-    {
-        LOKitThread.instance.async
-        {
-            self.doc.invokeHandlers()
-        }
-    }
-
-    /// 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
-        {
-            self.invokeHandlers()
-            return closure(self.doc)
-        }
-    }
-
-    /// Paints a tile and return synchronously, using a cached version if it can
-    public func paintTileToImage(canvasSize: CGSize,
-                                 tileRect: CGRect) -> UIImage?
-    {
-        if let cached = cache.get(canvasSize: canvasSize, tileRect: tileRect)
-        {
-            return cached
-        }
-
-        let img = sync {
-            $0.paintTileToImage(canvasSize: canvasSize, tileRect: tileRect)
-        }
-        if let image = img
-        {
-            cache.add(cachedRender: CachedRender(canvasSize: canvasSize, tileRect: tileRect, image: image))
-        }
-
-        return img
-    }
-
-
-    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) )
-            }
-
-        case LOK_CALLBACK_SEARCH_NOT_FOUND:
-            runOnMain {
-                self.searchDelegate?.searchNotFound()
-            }
-        case LOK_CALLBACK_SEARCH_RESULT_SELECTION:
-            runOnMain {
-                self.searchResults(payload: payload)
-            }
-
-
-        default:
-            print("onDocumentEvent type:\(type) not handled!")
-        }
-    }
-
-    private func searchResults(payload: String?)
-    {
-        if let d = payload, let data = d.data(using: .utf8)
-        {
-            let decoder = JSONDecoder()
-
-            do
-            {
-                let searchResults = try decoder.decode(SearchResults.self, from: data )
-
-                /*
-                if let srs = searchResults.searchResultSelection
-                {
-                    for par in srs
-                    {
-                        print("\(par.rectsAsCGRects)")
-                    }
-                }
-                */
-
-                self.searchDelegate?.searchResultSelection(searchResults: searchResults)
-            }
-            catch
-            {
-                print("Error decoding payload: \(error)")
-            }
-
-        }
-    }
-
-    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", "0") // 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.trimmingCharacters(in: .whitespacesAndNewlines)) }
-        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.
@@ -405,85 +131,9 @@ public protocol LOKitUIDelegate: class
 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]? )
-}
 
-public protocol SearchDelegate: class
-{
-    func searchNotFound()
-
-    func searchResultSelection(searchResults: SearchResults)
-}
-
-/**
- Encodes this example json:
- {
- "searchString": "Office",
- "highlightAll": "true",
- "searchResultSelection": [
- {
- "part": "0",
- "rectangles": "1951, 10743, 627, 239"
- },
- {
- "part": "0",
- "rectangles": "5343, 9496, 627, 287"
- },
- {
- "part": "0",
- "rectangles": "1951, 9256, 627, 239"
- },
- {
- "part": "0",
- "rectangles": "6502, 5946, 626, 287"
- },
- {
- "part": "0",
- "rectangles": "6686, 5658, 627, 287"
- },
- {
- "part": "0",
- "rectangles": "4103, 5418, 573, 239"
- },
- {
- "part": "0",
- "rectangles": "1951, 5418, 627, 239"
- },
- {
- "part": "0",
- "rectangles": "4934, 1658, 1586, 559"
- }
- ]
- }
-*/
-public struct SearchResults: Codable
-{
-    public var searchString: String?
-    public var highlightAll: String?
-    public var searchResultSelection: Array<PartAndRectangles>?
-}
-
-public struct PartAndRectangles: Codable
-{
-    public var part: String?
-    public var rectangles: String?
-
-    public var rectsAsCGRects: [CGRect]? {
-        return decodeRects(self.rectangles)
-    }
-}
diff --git a/ios/LibreOfficeLight/LibreOfficeLight/LOKit/RenderCache.swift b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/RenderCache.swift
new file mode 100644
index 000000000000..f217db0414a6
--- /dev/null
+++ b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/RenderCache.swift
@@ -0,0 +1,78 @@
+//
+// 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
+
+
+open class CachedRender
+{
+    open let part: Int32
+    open let canvasSize: CGSize
+    open let tileRect: CGRect
+    open let image: UIImage
+
+    public init(part: Int32, canvasSize: CGSize, tileRect: CGRect, image: UIImage)
+    {
+        self.canvasSize = canvasSize
+        self.tileRect = tileRect
+        self.image = image
+        self.part = part
+    }
+}
+
+class RenderCache
+{
+    let CACHE_LOWMEM = 4
+    let CACHE_NORMAL = 20
+
+    var cachedRenders: [CachedRender] = []
+    var hasReceivedMemoryWarning = false
+
+    let lock = NSRecursiveLock()
+
+    func emptyCache()
+    {
+        lock.lock(); defer { lock.unlock() }
+        cachedRenders.removeAll()
+    }
+
+    func pruneCache()
+    {
+        lock.lock(); defer { lock.unlock() }
+
+        let max = hasReceivedMemoryWarning ? CACHE_LOWMEM : CACHE_NORMAL
+        while cachedRenders.count > max
+        {
+            cachedRenders.remove(at: 0)
+        }
+    }
+
+    func add(cachedRender: CachedRender)
+    {
+        lock.lock(); defer { lock.unlock() }
+
+        cachedRenders.append(cachedRender)
+        pruneCache()
+    }
+
+    func get(part: Int32, canvasSize: CGSize, tileRect: CGRect) -> UIImage?
+    {
+        lock.lock(); defer { lock.unlock() }
+
+        if let cr = cachedRenders.first(where: {
+                $0.canvasSize == canvasSize
+                && $0.tileRect == tileRect
+                && $0.part == part
+        })
+        {
+            return cr.image
+        }
+        return nil
+    }
+}


More information about the Libreoffice-commits mailing list