[NUI] Add Clipboard
authorBowon Ryu <bowon.ryu@samsung.com>
Mon, 17 Jul 2023 08:23:28 +0000 (17:23 +0900)
committertscholb <scholb.kim@samsung.com>
Tue, 1 Aug 2023 07:13:54 +0000 (16:13 +0900)
Implement the Clipboard singleton class
that allows set and retrieval of data from the clipboard.

Signed-off-by: Bowon Ryu <bowon.ryu@samsung.com>
src/Tizen.NUI/src/internal/Common/ClipboardSignal.cs [new file with mode: 0755]
src/Tizen.NUI/src/internal/Interop/Interop.Clipboard.cs [new file with mode: 0755]
src/Tizen.NUI/src/public/Clipboard/Clipboard.cs [new file with mode: 0755]
src/Tizen.NUI/src/public/Clipboard/ClipboardEvent.cs [new file with mode: 0755]

diff --git a/src/Tizen.NUI/src/internal/Common/ClipboardSignal.cs b/src/Tizen.NUI/src/internal/Common/ClipboardSignal.cs
new file mode 100755 (executable)
index 0000000..d95ae13
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using Tizen.NUI.BaseComponents;
+
+namespace Tizen.NUI
+{
+    internal class ClipboardSignal : Disposable
+    {
+        internal ClipboardSignal(global::System.IntPtr cPtr, bool cMemoryOwn) : base(cPtr, cMemoryOwn)
+        {
+        }
+
+        protected override void ReleaseSwigCPtr(System.Runtime.InteropServices.HandleRef swigCPtr)
+        {
+            Interop.Clipboard.DeleteClipboardSignal(swigCPtr);
+        }
+
+        public bool Empty()
+        {
+            bool ret = Interop.Clipboard.ClipboardSignalEmpty(SwigCPtr);
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            return ret;
+        }
+
+        public uint GetConnectionCount()
+        {
+            uint ret = Interop.Clipboard.ClipboardSignalGetConnectionCount(SwigCPtr);
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            return ret;
+        }
+
+        public void Connect(System.Delegate func)
+        {
+            System.IntPtr ip = System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate<System.Delegate>(func);
+            {
+                Interop.Clipboard.ClipboardSignalConnect(SwigCPtr, new System.Runtime.InteropServices.HandleRef(this, ip));
+                if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            }
+        }
+
+        public void Disconnect(System.Delegate func)
+        {
+            System.IntPtr ip = System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate<System.Delegate>(func);
+            {
+                Interop.Clipboard.ClipboardSignalDisconnect(SwigCPtr, new System.Runtime.InteropServices.HandleRef(this, ip));
+                if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            }
+        }
+
+        public void Emit(Clipboard clipboard)
+        {
+            Interop.Clipboard.ClipboardSignalEmit(SwigCPtr, Clipboard.getCPtr(clipboard));
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+        }
+
+        public ClipboardSignal() : this(Interop.Clipboard.NewClipboardSignal(), true)
+        {
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+        }
+    }
+}
diff --git a/src/Tizen.NUI/src/internal/Interop/Interop.Clipboard.cs b/src/Tizen.NUI/src/internal/Interop/Interop.Clipboard.cs
new file mode 100755 (executable)
index 0000000..1a306d3
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+namespace Tizen.NUI
+{
+    internal static partial class Interop
+    {
+        internal static partial class Clipboard
+        {
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_Clipboard_Get")]
+            public static extern global::System.IntPtr Get();
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_Clipboard_SetData")]
+            public static extern bool SetData(global::System.Runtime.InteropServices.HandleRef clipboard, string mimeType, string data);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_Clipboard_GetData")]
+            public static extern uint GetData(global::System.Runtime.InteropServices.HandleRef clipboard, string mimeType);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_Clipboard_DataSentSignal")]
+            public static extern global::System.IntPtr ClipboardDataSentSignal(global::System.Runtime.InteropServices.HandleRef clipboard);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_Clipboard_DataReceivedSignal")]
+            public static extern global::System.IntPtr ClipboardDataReceivedSignal(global::System.Runtime.InteropServices.HandleRef clipboard);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_ClipboardSignal_Empty")]
+            [return: global::System.Runtime.InteropServices.MarshalAs(global::System.Runtime.InteropServices.UnmanagedType.U1)]
+            public static extern bool ClipboardSignalEmpty(global::System.Runtime.InteropServices.HandleRef signal);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_ClipboardSignal_GetConnectionCount")]
+            public static extern uint ClipboardSignalGetConnectionCount(global::System.Runtime.InteropServices.HandleRef signal);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_ClipboardSignal_Connect")]
+            public static extern void ClipboardSignalConnect(global::System.Runtime.InteropServices.HandleRef signal, global::System.Runtime.InteropServices.HandleRef clipboard);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_ClipboardSignal_Disconnect")]
+            public static extern void ClipboardSignalDisconnect(global::System.Runtime.InteropServices.HandleRef signal, global::System.Runtime.InteropServices.HandleRef clipboard);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_ClipboardSignal_Emit")]
+            public static extern void ClipboardSignalEmit(global::System.Runtime.InteropServices.HandleRef signal, global::System.Runtime.InteropServices.HandleRef clipboard);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_new_ClipboardSignal")]
+            public static extern global::System.IntPtr NewClipboardSignal();
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_delete_ClipboardSignal")]
+            public static extern void DeleteClipboardSignal(global::System.Runtime.InteropServices.HandleRef signal);
+        }
+    }
+}
diff --git a/src/Tizen.NUI/src/public/Clipboard/Clipboard.cs b/src/Tizen.NUI/src/public/Clipboard/Clipboard.cs
new file mode 100755 (executable)
index 0000000..b7aff8b
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Runtime.InteropServices;
+
+using Tizen.NUI.BaseComponents;
+
+namespace Tizen.NUI
+{
+    /// <summary>
+    /// Clipboard.
+    /// </summary>
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public partial class Clipboard : BaseHandle
+    {
+        /// <summary>
+        /// User callback for clipboard event.
+        /// </summary>
+        /// <remarks>
+        /// Receives requested data through <see cref="Tizen.NUI.ClipEvent"/>.
+        /// </remarks>
+        public delegate void ClipboardCallback(bool success, ClipEvent clipEvent);
+
+        internal bool hasClipboardDataReceived = false;
+        internal Dictionary<uint, ClipboardCallback> receivedCallbackDictionary = new Dictionary<uint, ClipboardCallback>();
+
+        private Clipboard() : this(Interop.Clipboard.Get(), true)
+        {
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+        }
+
+        private Clipboard(global::System.IntPtr cPtr, bool cMemoryOwn) : base(cPtr, cMemoryOwn)
+        {
+        }
+
+        /// <summary>
+        /// Gets the singleton instance of Clipboard.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static Clipboard Instance { get; } = new Clipboard();
+
+        /// <summary>
+        /// Request set the given data to the clipboard.
+        /// </summary>
+        /// <param name="mimeType">The mime type of the data.</param>
+        /// <param name="data">The data to be set on the clipboard.</param>
+        /// <returns>True if the internal clipboard sending request is successful.</returns>
+        /// <example>
+        /// The following example demonstrates how to use the SetData.
+        /// <code>
+        /// string MIME_TYPE_PLAIN_TEXT = "text/plain;charset=utf-8";
+        /// Clipboard.Instance.SetData(MIME_TYPE_PLAIN_TEXT, "Hello Clipboard");
+        /// </code>
+        /// </example>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public bool SetData(string mimeType, string data)
+        {
+            bool setData = Interop.Clipboard.SetData(SwigCPtr, mimeType, data);
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            return setData;
+        }
+
+        /// <summary>
+        /// Request get data of the specified mime type from clipboard<br/>
+        /// and invokes the given callback with the received clipboard data.
+        /// </summary>
+        /// <param name="mimeType">The mime type of data to request.</param>
+        /// <param name="dataReceivedCallback">The callback method to handle the received clipboard data.</param>
+        /// <remarks>
+        /// GetData() method is introduced to fetch data of the specified mime type,<br/>
+        /// and it expects a callback function as a parameter.<br/>
+        /// The given callback is invoked with received clipboard data.<br/>
+        /// The callback is designed to be used only once for handling the data.
+        /// </remarks>
+        /// <example>
+        /// The following example demonstrates how to use the GetData and ClipboardCallback.
+        /// <code>
+        /// string MIME_TYPE_PLAIN_TEXT = "text/plain;charset=utf-8";
+        /// Clipboard.Instance.GetData(MIME_TYPE_PLAIN_TEXT, OnClipboardDataReceived);
+        /// ...
+        /// public void OnClipboardDataReceived(bool success, ClipEvent clipEvent)
+        /// {
+        ///     if (!success) return;
+        ///     string mimeType = clipEvent.MimeType;
+        ///     string data = clipEvent.Data;
+        /// }
+        /// </code>
+        /// </example>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void GetData(string mimeType, ClipboardCallback dataReceivedCallback)
+        {
+            if(!hasClipboardDataReceived)
+            {
+                ClipboardDataReceived += OnClipboardDataReceived;
+                hasClipboardDataReceived = true;
+            }
+
+            uint id = Interop.Clipboard.GetData(SwigCPtr, mimeType);
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+
+            if (id == 0)
+            {
+                // Calls the failure callback even if the request fails.
+                var clipEvent = new ClipEvent()
+                {
+                    MimeType = string.Empty,
+                    Data = string.Empty,
+                };
+                dataReceivedCallback(false, clipEvent);
+            }
+            else
+            {
+                receivedCallbackDictionary[id] = dataReceivedCallback;
+            }
+        }
+
+        private void OnClipboardDataReceived(object sender, ClipboardEventArgs e)
+        {
+            if (!receivedCallbackDictionary.Any()) return;
+
+            uint id = e.Id;
+            if (receivedCallbackDictionary.ContainsKey(id))
+            {
+                ClipboardCallback callback = receivedCallbackDictionary[id];
+                if (callback != null)
+                {
+                    callback(e.Success, e.ClipEvent);
+                }
+                receivedCallbackDictionary.Remove(id);
+            }
+        }
+
+        /// <summary>
+        /// Dispose.
+        /// </summary>
+        protected override void Dispose(DisposeTypes type)
+        {
+            if (disposed)
+            {
+                return;
+            }
+
+            if (hasClipboardDataReceived)
+            {
+                ClipboardDataReceived -= OnClipboardDataReceived;
+                receivedCallbackDictionary.Clear();
+            }
+
+            if (this.HasBody())
+            {
+                if (clipboardDataReceivedCallback != null)
+                {
+                    this.ClipboardDataReceivedSignal().Disconnect(clipboardDataReceivedCallback);
+                }
+            }
+
+            base.Dispose(type);
+        }
+    }
+}
diff --git a/src/Tizen.NUI/src/public/Clipboard/ClipboardEvent.cs b/src/Tizen.NUI/src/public/Clipboard/ClipboardEvent.cs
new file mode 100755 (executable)
index 0000000..932b9d2
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright(c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+using System;
+using System.ComponentModel;
+using System.Runtime.InteropServices;
+
+namespace Tizen.NUI
+{
+    /// <summary>
+    /// This specifies clipboard event data.
+    /// </summary>
+    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815: Override equals and operator equals on value types")]
+    [EditorBrowsable(EditorBrowsableState.Never)]
+    public struct ClipEvent
+    {
+        /// <summary>
+        /// The mime type of clipboard event data.
+        /// </summary>
+        public string MimeType { get; set; }
+        /// <summary>
+        /// The clipboard event data to receive.
+        /// </summary>
+        public string Data { get; set; }
+    }
+
+    /// <summary>
+    /// ClipboardEventArgs is a class to record clipboard event arguments which will be sent to user.<br/>
+    /// This is for internal use only.
+    /// </summary>
+    internal class ClipboardEventArgs : EventArgs
+    {
+        /// <summary>
+        /// True if data receive is successful.
+        /// </summary>
+        public bool Success { get; set; }
+
+        /// <summary>
+        /// The data request id to identify the response by request.
+        /// </summary>
+        public uint Id { get; set; }
+
+        /// <summary>
+        /// Clipboard event data.
+        /// </summary>
+        public ClipEvent ClipEvent { get; set; }
+    }
+
+    /// <summary>
+    /// Clipboard event.
+    /// </summary>
+    public partial class Clipboard
+    {
+        private EventHandler<ClipboardEventArgs> clipboardDataReceivedEventHandler;
+        private ClipboardDataReceivedCallback clipboardDataReceivedCallback;
+
+        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
+        private delegate void ClipboardDataReceivedCallback(uint id, string mimeType, string data);
+
+        private event EventHandler<ClipboardEventArgs> ClipboardDataReceived
+        {
+            add
+            {
+                if (clipboardDataReceivedEventHandler == null)
+                {
+                    clipboardDataReceivedCallback = (OnClipboardDataReceived);
+                    ClipboardDataReceivedSignal().Connect(clipboardDataReceivedCallback);
+                }
+                clipboardDataReceivedEventHandler += value;
+            }
+            remove
+            {
+                clipboardDataReceivedEventHandler -= value;
+                if (clipboardDataReceivedEventHandler == null && ClipboardDataReceivedSignal().Empty() == false)
+                {
+                    ClipboardDataReceivedSignal().Disconnect(clipboardDataReceivedCallback);
+                }
+            }
+        }
+
+        internal ClipboardSignal ClipboardDataReceivedSignal()
+        {
+            var ret = new ClipboardSignal(Interop.Clipboard.ClipboardDataReceivedSignal(SwigCPtr), false);
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            return ret;
+        }
+
+        private void OnClipboardDataReceived(uint id, string mimeType, string data)
+        {
+            var e = new ClipboardEventArgs();
+            var clipEvent = new ClipEvent()
+            {
+                MimeType = mimeType,
+                Data = data,
+            };
+            e.ClipEvent = clipEvent;
+            e.Id = id;
+            e.Success = (String.IsNullOrEmpty(e.ClipEvent.MimeType) && String.IsNullOrEmpty(e.ClipEvent.Data)) ? false : true;
+
+            clipboardDataReceivedEventHandler?.Invoke(this, e);
+        }
+    }
+}