Rebased ref, commits from common ancestor: commit bbb0ac9ba51848c0f85c47f94161e839c15d782a Author: Michael Meeks <michael.me...@collabora.com> AuthorDate: Sat Jul 20 11:22:09 2019 +0100 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Mon Jul 22 09:19:01 2019 +0100
clipboard: encourage paste to show up by having some dummy content. Having an empty system clipboard is a hyper-unusual state, but quite common for lok instances. If we populate some dummy state we hide that. Allows context-menu paste into new documents. Change-Id: I9ce04cb8a0abfa054a67cb94922d7ac5992d1b29 diff --git a/desktop/source/lib/lokclipboard.cxx b/desktop/source/lib/lokclipboard.cxx index f53202903761..17a9c5c54e4f 100644 --- a/desktop/source/lib/lokclipboard.cxx +++ b/desktop/source/lib/lokclipboard.cxx @@ -69,6 +69,9 @@ LOKClipboard::LOKClipboard() : cppu::WeakComponentImplHelper<css::datatransfer::clipboard::XSystemClipboard, css::lang::XServiceInfo>(m_aMutex) { + // Encourage 'paste' menu items to always show up. + uno::Reference<datatransfer::XTransferable> xTransferable(new LOKTransferable()); + setContents(xTransferable, uno::Reference<datatransfer::clipboard::XClipboardOwner>()); } Sequence<OUString> LOKClipboard::getSupportedServiceNames_static() @@ -150,6 +153,18 @@ LOKTransferable::LOKTransferable(const OUString& sMimeType, aContent <<= aSequence; m_aContent.push_back(aContent); } + +/// Use to ensure we have some dummy content on the clipboard to allow a 1st 'paste' +LOKTransferable::LOKTransferable() +{ + m_aContent.reserve(1); + m_aFlavors = css::uno::Sequence<css::datatransfer::DataFlavor>(1); + initFlavourFromMime(m_aFlavors[0], "text/plain"); + uno::Any aContent; + aContent <<= OUString(); + m_aContent.push_back(aContent); +} + // cf. sot/source/base/exchange.cxx for these two exceptional types. void LOKTransferable::initFlavourFromMime(css::datatransfer::DataFlavor& rFlavor, OUString aMimeType) diff --git a/desktop/source/lib/lokclipboard.hxx b/desktop/source/lib/lokclipboard.hxx index 1ade5c0a3e0b..3b8f9d8ee7c9 100644 --- a/desktop/source/lib/lokclipboard.hxx +++ b/desktop/source/lib/lokclipboard.hxx @@ -77,6 +77,7 @@ class LOKTransferable : public cppu::WeakImplHelper<css::datatransfer::XTransfer void initFlavourFromMime(css::datatransfer::DataFlavor& rFlavor, OUString sMimeType); public: + LOKTransferable(); LOKTransferable(const size_t nInCount, const char** pInMimeTypes, const size_t* pInSizes, const char** pInStreams); LOKTransferable(const OUString& sMimeType, const css::uno::Sequence<sal_Int8>& aSequence); diff --git a/vcl/source/treelist/transfer.cxx b/vcl/source/treelist/transfer.cxx index 10782592f5a2..5e41bde0378d 100644 --- a/vcl/source/treelist/transfer.cxx +++ b/vcl/source/treelist/transfer.cxx @@ -392,7 +392,7 @@ sal_Bool SAL_CALL TransferableHelper::isComplex() { // By default everything is complex, until proven otherwise // in the respective document type transferable handler. - return sal_True; + return true; } Sequence< DataFlavor > SAL_CALL TransferableHelper::getTransferDataFlavors() commit 86589fbc93c2750c350c7b96f915caad4f813359 Author: Michael Meeks <michael.me...@collabora.com> AuthorDate: Fri Jun 28 18:36:15 2019 +0100 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Sat Jul 20 21:45:55 2019 +0100 Try harder to generate HTML from text. Change-Id: I5b4778cceefc652274a72b779cd6c060a50bf06c diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx index 7562133b52c4..32d1edbed9ef 100644 --- a/desktop/source/lib/init.cxx +++ b/desktop/source/lib/init.cxx @@ -3339,6 +3339,54 @@ static OUString getGenerator() static bool getFromTransferrable( const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable, + const OString &aInMimeType, OString &aRet); + +static bool encodeImageAsHTML( + const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable, + const OString &aMimeType, OString &aRet) +{ + if (!getFromTransferrable(xTransferable, aMimeType, aRet)) + return false; + + // Encode in base64. + auto aSeq = Sequence<sal_Int8>(reinterpret_cast<const sal_Int8*>(aRet.getStr()), + aRet.getLength()); + OUStringBuffer aBase64Data; + comphelper::Base64::encode(aBase64Data, aSeq); + + // Embed in HTML. + aRet = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n" + "<html><head>" + "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\"/><meta " + "name=\"generator\" content=\"" + + getGenerator().toUtf8() + + "\"/>" + "</head><body><img src=\"data:" + aMimeType + ";base64," + + aBase64Data.makeStringAndClear().toUtf8() + "\"/></body></html>"; + + return true; +} + +static bool encodeTextAsHTML( + const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable, + const OString &aMimeType, OString &aRet) +{ + if (!getFromTransferrable(xTransferable, aMimeType, aRet)) + return false; + + // Embed in HTML - FIXME: needs some escaping. + aRet = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n" + "<html><head>" + "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\"/><meta " + "name=\"generator\" content=\"" + + getGenerator().toUtf8() + + "\"/></head><body><pre>" + aRet + "</pre></body></html>"; + + return true; +} + +static bool getFromTransferrable( + const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable, const OString &aInMimeType, OString &aRet) { OString aMimeType(aInMimeType); @@ -3364,28 +3412,15 @@ static bool getFromTransferrable( if (!xTransferable->isDataFlavorSupported(aFlavor)) { - // If html is not supported, might be a graphic-selection, which supports png. - if (aInMimeType == "text/html" && getFromTransferrable(xTransferable, "image/png", aRet)) + // Try harder for HTML it is our copy/paste meta-file format + if (aInMimeType == "text/html") { - // Encode in base64. - auto aSeq = Sequence<sal_Int8>(reinterpret_cast<const sal_Int8*>(aRet.getStr()), - aRet.getLength()); - OUStringBuffer aBase64Data; - comphelper::Base64::encode(aBase64Data, aSeq); - - // Embed in HTML. - static const OString aHeader - = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">" - "<html><head>" - "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\"/><meta " - "name=\"generator\" content=\"" - + getGenerator().toUtf8() - + "\"/>" - "</head><body><img src=\"data:image/png;charset=utf-8;base64,"; - - aRet = aHeader + aBase64Data.makeStringAndClear().toUtf8() + "\"/></body></html>"; - - return true; + // Desperate measures - convert text to HTML instead. + if (encodeTextAsHTML(xTransferable, "text/plain;charset=utf-8", aRet)) + return true; + // If html is not supported, might be a graphic-selection, + if (encodeImageAsHTML(xTransferable, "image/png", aRet)) + return true; } SetLastExceptionMsg("Flavor " + aFlavor.MimeType + " is not supported"); @@ -3503,7 +3538,7 @@ static int doc_getSelectionType(LibreOfficeKitDocument* pThis) if (!bSuccess) return LOK_SELTYPE_NONE; - if (aRet.getLength() > 1000) // About 2 paragraphs. + if (aRet.getLength() > 10000) return LOK_SELTYPE_COMPLEX; return aRet.getLength() ? LOK_SELTYPE_TEXT : LOK_SELTYPE_NONE; commit fa43c97755c4caa750714db65514d4c1368d661a Author: Michael Meeks <michael.me...@collabora.com> AuthorDate: Sat Jul 20 11:15:05 2019 +0100 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Sat Jul 20 21:45:55 2019 +0100 lok: minimal implementation of isComplex for calc for now. Change-Id: Ic4141d30a0ff48a9ec5ecc8e6119d9d779c4a0fc diff --git a/sc/source/ui/app/seltrans.cxx b/sc/source/ui/app/seltrans.cxx index 79a0437f6061..c2d85dea6a2e 100644 --- a/sc/source/ui/app/seltrans.cxx +++ b/sc/source/ui/app/seltrans.cxx @@ -413,4 +413,16 @@ void ScSelectionTransferObj::ObjectReleased() TransferableHelper::ObjectReleased(); } +sal_Bool ScSelectionTransferObj::isComplex() +{ + switch (eMode) + { + case SC_SELTRANS_CELL: + case SC_SELTRANS_CELLS: + return false; + default: + return true; + } +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/app/transobj.cxx b/sc/source/ui/app/transobj.cxx index c78e8ae9e859..d8709cbe578c 100644 --- a/sc/source/ui/app/transobj.cxx +++ b/sc/source/ui/app/transobj.cxx @@ -234,6 +234,42 @@ void ScTransferObj::AddSupportedFormats() } } +static ScRange lcl_reduceBlock(ScDocumentUniquePtr &pDoc, ScRange aReducedBlock, bool bIncludeVisual = false) +{ + if ((aReducedBlock.aEnd.Col() == MAXCOL || aReducedBlock.aEnd.Row() == MAXROW) && + aReducedBlock.aStart.Tab() == aReducedBlock.aEnd.Tab()) + { + // Shrink the block here so we don't waste time creating huge + // output when whole columns or rows are selected. + + SCCOL nPrintAreaEndCol = 0; + SCROW nPrintAreaEndRow = 0; + if (bIncludeVisual) + pDoc->GetPrintArea( aReducedBlock.aStart.Tab(), nPrintAreaEndCol, nPrintAreaEndRow, true ); + + // Shrink the area to allow pasting to external applications. + // Shrink to real data area for HTML, RTF and RICHTEXT, but include + // all objects and top-left area for BITMAP and PNG. + SCCOL nStartCol = aReducedBlock.aStart.Col(); + SCROW nStartRow = aReducedBlock.aStart.Row(); + SCCOL nEndCol = aReducedBlock.aEnd.Col(); + SCROW nEndRow = aReducedBlock.aEnd.Row(); + bool bShrunk = false; + pDoc->ShrinkToUsedDataArea( bShrunk, aReducedBlock.aStart.Tab(), nStartCol, nStartRow, nEndCol, nEndRow, + false, bIncludeVisual /*bStickyTopRow*/, bIncludeVisual /*bStickyLeftCol*/, + bIncludeVisual /*bConsiderCellNotes*/, bIncludeVisual /*bConsiderCellDrawObjects*/); + + if ( nPrintAreaEndRow > nEndRow ) + nEndRow = nPrintAreaEndRow; + + if ( nPrintAreaEndCol > nEndCol ) + nEndCol = nPrintAreaEndCol; + + aReducedBlock = ScRange(nStartCol, nStartRow, aReducedBlock.aStart.Tab(), nEndCol, nEndRow, aReducedBlock.aEnd.Tab()); + } + return aReducedBlock; +} + bool ScTransferObj::GetData( const datatransfer::DataFlavor& rFlavor, const OUString& /*rDestDoc*/ ) { SotClipboardFormatId nFormat = SotExchange::GetFormat( rFlavor ); @@ -250,39 +286,11 @@ bool ScTransferObj::GetData( const datatransfer::DataFlavor& rFlavor, const OUSt || nFormat == SotClipboardFormatId::BITMAP || nFormat == SotClipboardFormatId::PNG; - if (bReduceBlockFormat && (m_aBlock.aEnd.Col() == MAXCOL || m_aBlock.aEnd.Row() == MAXROW) && - m_aBlock.aStart.Tab() == m_aBlock.aEnd.Tab()) - { - // Shrink the block here so we don't waste time creating huge - // output when whole columns or rows are selected. - - SCCOL nPrintAreaEndCol = 0; - SCROW nPrintAreaEndRow = 0; - const bool bIncludeVisual = (nFormat == SotClipboardFormatId::BITMAP || - nFormat == SotClipboardFormatId::PNG); - if (bIncludeVisual) - m_pDoc->GetPrintArea( m_aBlock.aStart.Tab(), nPrintAreaEndCol, nPrintAreaEndRow, true ); - - // Shrink the area to allow pasting to external applications. - // Shrink to real data area for HTML, RTF and RICHTEXT, but include - // all objects and top-left area for BITMAP and PNG. - SCCOL nStartCol = aReducedBlock.aStart.Col(); - SCROW nStartRow = aReducedBlock.aStart.Row(); - SCCOL nEndCol = aReducedBlock.aEnd.Col(); - SCROW nEndRow = aReducedBlock.aEnd.Row(); - bool bShrunk = false; - m_pDoc->ShrinkToUsedDataArea( bShrunk, aReducedBlock.aStart.Tab(), nStartCol, nStartRow, nEndCol, nEndRow, - false, bIncludeVisual /*bStickyTopRow*/, bIncludeVisual /*bStickyLeftCol*/, - bIncludeVisual /*bConsiderCellNotes*/, bIncludeVisual /*bConsiderCellDrawObjects*/); - - if ( nPrintAreaEndRow > nEndRow ) - nEndRow = nPrintAreaEndRow; - - if ( nPrintAreaEndCol > nEndCol ) - nEndCol = nPrintAreaEndCol; - - aReducedBlock = ScRange(nStartCol, nStartRow, aReducedBlock.aStart.Tab(), nEndCol, nEndRow, aReducedBlock.aEnd.Tab()); - } + const bool bIncludeVisual = (nFormat == SotClipboardFormatId::BITMAP || + nFormat == SotClipboardFormatId::PNG); + + if (bReduceBlockFormat) + aReducedBlock = lcl_reduceBlock(m_pDoc, m_aBlock, bIncludeVisual); if ( nFormat == SotClipboardFormatId::LINKSRCDESCRIPTOR || nFormat == SotClipboardFormatId::OBJECTDESCRIPTOR ) { @@ -535,6 +543,15 @@ bool ScTransferObj::WriteObject( tools::SvRef<SotStorageStream>& rxOStm, void* p return bRet; } +sal_Bool ScTransferObj::isComplex() +{ + ScRange aReduced = lcl_reduceBlock(m_pDoc, m_aBlock); + size_t nCells = ( (aReduced.aEnd.Col() - aReduced.aStart.Col() + 1) * + (aReduced.aEnd.Row() - aReduced.aStart.Row() + 1) * + (aReduced.aEnd.Tab() - aReduced.aStart.Tab() + 1) ); + return nCells > 1000; +} + void ScTransferObj::DragFinished( sal_Int8 nDropAction ) { if ( nDropAction == DND_ACTION_MOVE && !m_bDragWasInternal && !(m_nDragSourceFlags & ScDragSrc::Navigator) ) diff --git a/sc/source/ui/inc/seltrans.hxx b/sc/source/ui/inc/seltrans.hxx index af73f0adf896..87155a53e957 100644 --- a/sc/source/ui/inc/seltrans.hxx +++ b/sc/source/ui/inc/seltrans.hxx @@ -67,6 +67,7 @@ public: virtual void AddSupportedFormats() override; virtual bool GetData( const css::datatransfer::DataFlavor& rFlavor, const OUString& rDestDoc ) override; virtual void ObjectReleased() override; + virtual sal_Bool isComplex() override; }; #endif diff --git a/sc/source/ui/inc/transobj.hxx b/sc/source/ui/inc/transobj.hxx index 5b7f405fb55e..b0e3395a18bc 100644 --- a/sc/source/ui/inc/transobj.hxx +++ b/sc/source/ui/inc/transobj.hxx @@ -76,6 +76,7 @@ public: virtual bool WriteObject( tools::SvRef<SotStorageStream>& rxOStm, void* pUserObject, sal_uInt32 nUserObjectId, const css::datatransfer::DataFlavor& rFlavor ) override; virtual void DragFinished( sal_Int8 nDropAction ) override; + virtual sal_Bool isComplex() override; ScDocument* GetDocument() const { return m_pDoc.get(); } // owned by ScTransferObj const ScRange& GetRange() const { return m_aBlock; } commit f73000516e195f79f2e8e034562c907cf63bedf0 Author: Michael Meeks <michael.me...@collabora.com> AuthorDate: Fri Jun 28 12:30:40 2019 +0100 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Sat Jul 20 21:45:55 2019 +0100 lok: vital to quote nested JSON inserted into a json property. Somehow this managed to work fine in most browsers, but when the Kit/Poco tried to parse JSON to extract viewID it could explode. Change-Id: I39d2ecc9ee95b7e6f67a23c8b15f9a1d01769ddc diff --git a/sfx2/source/view/lokhelper.cxx b/sfx2/source/view/lokhelper.cxx index a7828287205d..55b157ef6cb2 100644 --- a/sfx2/source/view/lokhelper.cxx +++ b/sfx2/source/view/lokhelper.cxx @@ -143,11 +143,26 @@ void SfxLokHelper::setViewLanguage(int nId, const OUString& rBcp47LanguageTag) } } +static OString lcl_escapeQuotes(const OString &rStr) +{ + if (rStr.getLength() < 1) + return rStr; + // FIXME: need an optimized 'escape' method for O[U]String. + OStringBuffer aBuf(rStr.getLength() + 8); + for (sal_Int32 i = 0; i < rStr.getLength(); ++i) + { + if (rStr[i] == '"' || rStr[i] == '\\') + aBuf.append('\\'); + aBuf.append(rStr[i]); + } + return aBuf.makeStringAndClear(); +} + void SfxLokHelper::notifyOtherView(SfxViewShell* pThisView, SfxViewShell const* pOtherView, int nType, const OString& rKey, const OString& rPayload) { OString aPayload = OString("{ \"viewId\": \"") + OString::number(SfxLokHelper::getView(pThisView)) + "\", \"part\": \"" + OString::number(pThisView->getPart()) + - "\", \"" + rKey + "\": \"" + rPayload + "\" }"; + "\", \"" + rKey + "\": \"" + lcl_escapeQuotes(rPayload) + "\" }"; pOtherView->libreOfficeKitViewCallback(nType, aPayload.getStr()); } commit e2c1d8e88afd34ed0129076efbe965191f5e8a4b Author: Szymon Kłos <szymon.k...@collabora.com> AuthorDate: Sat Jul 20 11:07:56 2019 +0100 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Sat Jul 20 21:45:55 2019 +0100 Set clipboard for a window when created This allows to paste in dialogs input using tunneled context menu. Change-Id: Ie6cc6f1a6cd453734c8f6084b0bd50d1d7ab6c09 diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx index 4c8f37804810..7562133b52c4 100644 --- a/desktop/source/lib/init.cxx +++ b/desktop/source/lib/init.cxx @@ -1437,6 +1437,20 @@ void CallbackFlushHandler::queue(const int type, const char* data) assert(aCallbackData.validate() && "Validation after setJson failed!"); } } + else if (aTree.get<std::string>("action", "") == "created") + { + VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId); + if (!pWindow) + { + gImpl->maLastExceptionMsg = "Document doesn't support dialog rendering, or window not found."; + return; + } + + auto xClip = forceSetClipboardForCurrentView(m_pDocument); + + uno::Reference<datatransfer::clipboard::XClipboard> xClipboard(xClip.get()); + pWindow->SetClipboard(xClipboard); + } } break; } commit 07b88656d22839e8e8b46b5149c0a4e6cb4b303f Author: Ashod Nakashian <ashod.nakash...@collabora.co.uk> AuthorDate: Sat Jul 20 11:07:01 2019 +0100 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Sat Jul 20 21:45:55 2019 +0100 LOK: Copy Graphics as embedded PNG in HTML Change-Id: Id8c14d7304d30bfcd956b126dfe291ef044f62bf diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx index 167b05bebf60..4c8f37804810 100644 --- a/desktop/source/lib/init.cxx +++ b/desktop/source/lib/init.cxx @@ -47,6 +47,7 @@ #include <rtl/strbuf.hxx> #include <rtl/uri.hxx> #include <cppuhelper/bootstrap.hxx> +#include <comphelper/base64.hxx> #include <comphelper/dispatchcommand.hxx> #include <comphelper/lok.hxx> #include <comphelper/processfactory.hxx> @@ -3313,6 +3314,15 @@ static void doc_setTextSelection(LibreOfficeKitDocument* pThis, int nType, int n pDoc->setTextSelection(nType, nX, nY); } +static OUString getGenerator() +{ + OUString sGenerator( + Translate::ExpandVariables("%PRODUCTNAME %PRODUCTVERSION%PRODUCTEXTENSION (%1)")); + OUString os("$_OS"); + ::rtl::Bootstrap::expandMacros(os); + return sGenerator.replaceFirst("%1", os); +} + static bool getFromTransferrable( const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable, const OString &aInMimeType, OString &aRet) @@ -3340,6 +3350,30 @@ static bool getFromTransferrable( if (!xTransferable->isDataFlavorSupported(aFlavor)) { + // If html is not supported, might be a graphic-selection, which supports png. + if (aInMimeType == "text/html" && getFromTransferrable(xTransferable, "image/png", aRet)) + { + // Encode in base64. + auto aSeq = Sequence<sal_Int8>(reinterpret_cast<const sal_Int8*>(aRet.getStr()), + aRet.getLength()); + OUStringBuffer aBase64Data; + comphelper::Base64::encode(aBase64Data, aSeq); + + // Embed in HTML. + static const OString aHeader + = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">" + "<html><head>" + "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\"/><meta " + "name=\"generator\" content=\"" + + getGenerator().toUtf8() + + "\"/>" + "</head><body><img src=\"data:image/png;charset=utf-8;base64,"; + + aRet = aHeader + aBase64Data.makeStringAndClear().toUtf8() + "\"/></body></html>"; + + return true; + } + SetLastExceptionMsg("Flavor " + aFlavor.MimeType + " is not supported"); return false; } @@ -3376,7 +3410,7 @@ static bool getFromTransferrable( aRet = OString(reinterpret_cast<sal_Char*>(aSequence.getArray()), aSequence.getLength()); } - return true;; + return true; } // Tolerate embedded \0s etc. diff --git a/vcl/source/treelist/transfer.cxx b/vcl/source/treelist/transfer.cxx index c5f61cb850c4..10782592f5a2 100644 --- a/vcl/source/treelist/transfer.cxx +++ b/vcl/source/treelist/transfer.cxx @@ -390,7 +390,8 @@ Any SAL_CALL TransferableHelper::getTransferData2( const DataFlavor& rFlavor, co sal_Bool SAL_CALL TransferableHelper::isComplex() { - //FIXME: get from each document. + // By default everything is complex, until proven otherwise + // in the respective document type transferable handler. return sal_True; } commit d1a0446548bd1fb55114dc6eb33768444cabcfc1 Author: Michael Meeks <michael.me...@collabora.com> AuthorDate: Sat Jul 20 11:05:49 2019 +0100 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Sat Jul 20 21:45:55 2019 +0100 lok: cleanup clipboards associated with a view on destruction. Also defer destruction potentially indefinitely for bad users who quit without destroying views, documents, shutting down nicely etc. Change-Id: Ieea6ad00b2983d372b745179bfe3b884c3c64eb0 diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx index c09ae8329f9c..167b05bebf60 100644 --- a/desktop/source/lib/init.cxx +++ b/desktop/source/lib/init.cxx @@ -1546,6 +1546,8 @@ static void doc_destroy(LibreOfficeKitDocument *pThis) SolarMutexGuard aGuard; + LOKClipboardFactory::releaseClipboardForView(-1); + LibLODocument_Impl *pDocument = static_cast<LibLODocument_Impl*>(pThis); delete pDocument; } @@ -4284,6 +4286,8 @@ static void doc_destroyView(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis SolarMutexGuard aGuard; SetLastExceptionMsg(); + LOKClipboardFactory::releaseClipboardForView(nId); + SfxLokHelper::destroyView(nId); } diff --git a/desktop/source/lib/lokclipboard.cxx b/desktop/source/lib/lokclipboard.cxx index ef52a4596c43..f53202903761 100644 --- a/desktop/source/lib/lokclipboard.cxx +++ b/desktop/source/lib/lokclipboard.cxx @@ -8,14 +8,16 @@ */ #include "lokclipboard.hxx" +#include <vcl/lazydelete.hxx> #include <sfx2/lokhelper.hxx> #include <cppuhelper/supportsservice.hxx> using namespace css; using namespace css::uno; -osl::Mutex LOKClipboardFactory::gMutex; -std::unordered_map<int, rtl::Reference<LOKClipboard>> LOKClipboardFactory::gClipboards; +/* static */ osl::Mutex LOKClipboardFactory::gMutex; +static vcl::DeleteOnDeinit<std::unordered_map<int, rtl::Reference<LOKClipboard>>> +gClipboards(new std::unordered_map<int, rtl::Reference<LOKClipboard>>()); rtl::Reference<LOKClipboard> LOKClipboardFactory::getClipboardForCurView() { @@ -23,18 +25,39 @@ rtl::Reference<LOKClipboard> LOKClipboardFactory::getClipboardForCurView() osl::MutexGuard aGuard(gMutex); - auto it = gClipboards.find(nViewId); - if (it != gClipboards.end()) + auto it = gClipboards.get()->find(nViewId); + if (it != gClipboards.get()->end()) { SAL_INFO("lok", "Got clip: " << it->second.get() << " from " << nViewId); return it->second; } rtl::Reference<LOKClipboard> xClip(new LOKClipboard()); - gClipboards[nViewId] = xClip; + (*gClipboards.get())[nViewId] = xClip; SAL_INFO("lok", "Created clip: " << xClip.get() << " for viewId " << nViewId); return xClip; } +/// FIXME: should really copy and stash its content for a bit. +void LOKClipboardFactory::releaseClipboardForView(int nViewId) +{ + osl::MutexGuard aGuard(gMutex); + + if (nViewId < 0) // clear all + { + gClipboards.get()->clear(); + SAL_INFO("lok", "Released all clipboards on doc destroy\n"); + } + else + { + auto it = gClipboards.get()->find(nViewId); + if (it != gClipboards.get()->end()) + { + gClipboards.get()->erase(it); + SAL_INFO("lok", "Released clip: " << it->second.get() << " for destroyed " << nViewId); + } + } +} + uno::Reference<uno::XInterface> SAL_CALL LOKClipboardFactory::createInstanceWithArguments(const Sequence<Any>& /* rArgs */) { diff --git a/desktop/source/lib/lokclipboard.hxx b/desktop/source/lib/lokclipboard.hxx index 54c1b37945ff..1ade5c0a3e0b 100644 --- a/desktop/source/lib/lokclipboard.hxx +++ b/desktop/source/lib/lokclipboard.hxx @@ -92,7 +92,6 @@ public: class LOKClipboardFactory : public ::cppu::WeakComponentImplHelper<css::lang::XSingleServiceFactory> { static osl::Mutex gMutex; - static std::unordered_map<int, rtl::Reference<LOKClipboard>> gClipboards; public: LOKClipboardFactory() @@ -106,7 +105,12 @@ public: } css::uno::Reference<css::uno::XInterface> SAL_CALL createInstanceWithArguments(const css::uno::Sequence<css::uno::Any>& /* rArgs */) override; + + /// Fetch clipboard from the gobal pool. static rtl::Reference<LOKClipboard> getClipboardForCurView(); + + /// Release a clipboard before its document dies, nViewId of -1 clears all. + static void releaseClipboardForView(int nViewId); }; #endif commit bda4410f61b2005acba6cf23fbf7a5e661a6e9ef Author: Ashod Nakashian <ashod.nakash...@collabora.co.uk> AuthorDate: Tue Jun 25 23:45:37 2019 -0400 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Sat Jul 20 21:45:55 2019 +0100 LOK: Improved selection complexity detection Only Graphics and OLE now unconditionally trigger 'complex' case, and for all others, we actually tally the number of text characters. Currently anything above 512KB is flagged as 'complex'. Change-Id: I19fbef72f2eb725648b2a18c1ee41b1612d2bac0 diff --git a/include/LibreOfficeKit/LibreOfficeKit.hxx b/include/LibreOfficeKit/LibreOfficeKit.hxx index a118cac1833b..705ef3f1fc0b 100644 --- a/include/LibreOfficeKit/LibreOfficeKit.hxx +++ b/include/LibreOfficeKit/LibreOfficeKit.hxx @@ -356,7 +356,7 @@ public: * * @return an element of the LibreOfficeKitSelectionType enum. */ - int getSelectionType(LibreOfficeKitDocument* /*pThis*/) + int getSelectionType() { return mpDoc->pClass->getSelectionType(mpDoc); } diff --git a/sw/source/uibase/dochdl/swdtflvr.cxx b/sw/source/uibase/dochdl/swdtflvr.cxx index af99de435f85..4873c20b392b 100644 --- a/sw/source/uibase/dochdl/swdtflvr.cxx +++ b/sw/source/uibase/dochdl/swdtflvr.cxx @@ -421,15 +421,6 @@ namespace sal_Bool SwTransferable::isComplex() { - const SelectionType nSelectionType = m_pWrtShell->GetSelectionType(); - - // Anything other than text is complex by definition. - if (nSelectionType != SelectionType::Text) - return true; - - if (m_pWrtShell->IsFrameSelected() || m_pWrtShell->IsObjSelected()) - return true; - // Copy into a new Doc so we don't mess with the existing one. //FIXME: We *should* be able to avoid this and improve the performance. m_pClpDocFac.reset(new SwDocFac); @@ -439,21 +430,29 @@ sal_Bool SwTransferable::isComplex() .LockExpFields(); // never update fields - leave text as it is lclOverWriteDoc(*m_pWrtShell, *pTmpDoc); - bool isComplex = false; + sal_Int32 nTextLength = 0; + const SwNode* pEndOfContent = &m_pWrtShell->GetDoc()->GetNodes().GetEndOfContent(); SwNodes& aNodes = pTmpDoc->GetNodes(); for( sal_uLong nIndex = 0; nIndex < aNodes.Count(); ++nIndex) { SwNode& rNd = *aNodes[nIndex]; - if (rNd.IsContentNode() && !rNd.IsTextNode()) - { - isComplex = true; + if (&rNd == pEndOfContent) break; + + if (rNd.IsOLENode() || rNd.IsGrfNode()) + return true; // Complex + + SwTextNode* pTextNode = rNd.GetTextNode(); + if (pTextNode) + { + nTextLength += pTextNode->GetText().getLength(); + if (nTextLength >= 1024 * 512) + return true; // Complex } - else if (&rNd == &m_pWrtShell->GetDoc()->GetNodes().GetEndOfContent()) - break; } - return isComplex; + // Simple + return false; } bool SwTransferable::GetData( const DataFlavor& rFlavor, const OUString& rDestDoc ) commit 8e591f2582710d942fe2c2c432709e9a45bd9383 Author: Ashod Nakashian <ashod.nakash...@collabora.co.uk> AuthorDate: Sat Jul 20 11:01:38 2019 +0100 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Sat Jul 20 21:45:55 2019 +0100 LOK: Enable embedding images in text/html format output Change-Id: Ibd8bed796678ee26de2ceb6e434dce24da4eab05 diff --git a/desktop/qa/desktop_lib/test_desktop_lib.cxx b/desktop/qa/desktop_lib/test_desktop_lib.cxx index 43ec7036c3a9..2d78696ca533 100644 --- a/desktop/qa/desktop_lib/test_desktop_lib.cxx +++ b/desktop/qa/desktop_lib/test_desktop_lib.cxx @@ -2676,7 +2676,7 @@ void DesktopLOKTest::testComplexSelection() pText = pDocument->pClass->getTextSelection(pDocument, "text/html", nullptr); CPPUNIT_ASSERT(pText != nullptr); CPPUNIT_ASSERT(std::string(pText).find(aText.getStr()) != std::string::npos); // Must have the text. - // CPPUNIT_ASSERT(std::string(pText).find("<img") != std::string::npos); // Must have the image as well. + CPPUNIT_ASSERT(std::string(pText).find("<img") != std::string::npos); // Must have the image as well. free(pText); // We expect this to be complex. diff --git a/sw/source/filter/html/wrthtml.cxx b/sw/source/filter/html/wrthtml.cxx index 54afa80ba7c9..5c1a6ab52a42 100644 --- a/sw/source/filter/html/wrthtml.cxx +++ b/sw/source/filter/html/wrthtml.cxx @@ -91,7 +91,7 @@ using namespace css; static sal_Char sIndentTabs[MAX_INDENT_LEVEL+2] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; -SwHTMLWriter::SwHTMLWriter( const OUString& rBaseURL ) +SwHTMLWriter::SwHTMLWriter( const OUString& rBaseURL, const OUString& rFilterOptions ) : m_pNumRuleInfo(new SwHTMLNumRuleInfo) , m_nHTMLMode(0) , m_eCSS1Unit(FieldUnit::NONE) @@ -163,6 +163,8 @@ SwHTMLWriter::SwHTMLWriter( const OUString& rBaseURL ) mpTempBaseURL->EnableKillingFile(); SetBaseURL(mpTempBaseURL->GetURL()); } + + SetupFilterOptions(rFilterOptions); } SwHTMLWriter::~SwHTMLWriter() @@ -185,21 +187,26 @@ void SwHTMLWriter::SetupFilterOptions(SfxMedium& rMedium) return; - OUString sFilterOptions = static_cast<const SfxStringItem*>(pItem)->GetValue(); - if (sFilterOptions == "SkipImages") + const OUString sFilterOptions = static_cast<const SfxStringItem*>(pItem)->GetValue(); + SetupFilterOptions(sFilterOptions); +} + +void SwHTMLWriter::SetupFilterOptions(const OUString& rFilterOptions) +{ + if (rFilterOptions == "SkipImages") { mbSkipImages = true; } - else if (sFilterOptions == "SkipHeaderFooter") + else if (rFilterOptions == "SkipHeaderFooter") { mbSkipHeaderFooter = true; } - else if (sFilterOptions == "EmbedImages" ) + else if (rFilterOptions == "EmbedImages") { mbEmbedImages = true; } - uno::Sequence<OUString> aOptionSeq = comphelper::string::convertCommaSeparated(sFilterOptions); + uno::Sequence<OUString> aOptionSeq = comphelper::string::convertCommaSeparated(rFilterOptions); const OUString aXhtmlNsKey("xhtmlns="); for (const auto& rOption : aOptionSeq) { @@ -823,13 +830,6 @@ void SwHTMLWriter::Out_SwDoc( SwPaM* pPam ) OutHTML_Section( *this, *rNd.GetSectionNode() ); m_nBkmkTabPos = m_bWriteAll ? FindPos_Bkmk( *m_pCurrentPam->GetPoint() ) : -1; } - else if( rNd.IsGrfNode() ) - { - SwGrfNode* pGrfNd = rNd.GetGrfNode(); - assert(pGrfNd && !"FIXME: Implement Graphic copy as HTML."); - // if (pGrfNd) - // OutHTML_SwGrfNode( *this, *pGrfNd ); - } else if( &rNd == &m_pDoc->GetNodes().GetEndOfContent() ) break; @@ -1555,9 +1555,9 @@ HTMLSaveData::~HTMLSaveData() } } -void GetHTMLWriter( const OUString&, const OUString& rBaseURL, WriterRef& xRet ) +void GetHTMLWriter( const OUString& rFilterOptions, const OUString& rBaseURL, WriterRef& xRet ) { - xRet = new SwHTMLWriter( rBaseURL ); + xRet = new SwHTMLWriter( rBaseURL, rFilterOptions ); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/wrthtml.hxx b/sw/source/filter/html/wrthtml.hxx index 47893227f98f..98c9005fe095 100644 --- a/sw/source/filter/html/wrthtml.hxx +++ b/sw/source/filter/html/wrthtml.hxx @@ -269,6 +269,8 @@ class SW_DLLPUBLIC SwHTMLWriter : public Writer void AddLinkTarget( const OUString& rURL ); void CollectLinkTargets(); + void SetupFilterOptions(const OUString& rFilterOptions); + protected: ErrCode WriteStream() override; void SetupFilterOptions(SfxMedium& rMedium) override; @@ -403,7 +405,9 @@ public: /// Tracks which text portion attributes are currently open: a which id -> open count map. std::map<sal_uInt16, int> maStartedAttributes; - explicit SwHTMLWriter( const OUString& rBaseURL ); + /// Construct an instance of SwHTMLWriter and optionally give it + /// the filter options directly, which can also be set via SetupFilterOptions(). + explicit SwHTMLWriter( const OUString& rBaseURL, const OUString& rFilterOptions = "" ); virtual ~SwHTMLWriter() override; void Out_SwDoc( SwPaM* ); // write the marked range diff --git a/sw/source/uibase/dochdl/swdtflvr.cxx b/sw/source/uibase/dochdl/swdtflvr.cxx index af9570f82d1c..af99de435f85 100644 --- a/sw/source/uibase/dochdl/swdtflvr.cxx +++ b/sw/source/uibase/dochdl/swdtflvr.cxx @@ -432,7 +432,7 @@ sal_Bool SwTransferable::isComplex() // Copy into a new Doc so we don't mess with the existing one. //FIXME: We *should* be able to avoid this and improve the performance. - m_pClpDocFac = new SwDocFac; + m_pClpDocFac.reset(new SwDocFac); SwDoc* const pTmpDoc = lcl_GetDoc(*m_pClpDocFac); pTmpDoc->getIDocumentFieldsAccess() @@ -773,8 +773,12 @@ bool SwTransferable::WriteObject( tools::SvRef<SotStorageStream>& xStream, break; case SWTRANSFER_OBJECTTYPE_HTML: - GetHTMLWriter(OUString(), OUString(), xWrt); + { + // LOK is interested in getting images embedded for copy/paste support. + const OUString aFilterOptions("EmbedImages"); + GetHTMLWriter( comphelper::LibreOfficeKit::isActive() ? aFilterOptions : OUString(), OUString(), xWrt ); break; + } case SWTRANSFER_OBJECTTYPE_RTF: case SWTRANSFER_OBJECTTYPE_RICHTEXT: commit fd9fa9f3b4b34400468b3e005f4b94b47b807679 Author: Ashod Nakashian <ashod.nakash...@collabora.co.uk> AuthorDate: Tue Jun 25 12:36:52 2019 -0400 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Sat Jul 20 21:45:55 2019 +0100 LOK: detect Graphics in isComplex for Writer Change-Id: I814d71a21fa5352a9a1051f5477cb452a0dc5c2f diff --git a/desktop/qa/desktop_lib/test_desktop_lib.cxx b/desktop/qa/desktop_lib/test_desktop_lib.cxx index b9e3a81ea1f0..43ec7036c3a9 100644 --- a/desktop/qa/desktop_lib/test_desktop_lib.cxx +++ b/desktop/qa/desktop_lib/test_desktop_lib.cxx @@ -2680,7 +2680,7 @@ void DesktopLOKTest::testComplexSelection() free(pText); // We expect this to be complex. - // CPPUNIT_ASSERT_EQUAL((int)LOK_SELTYPE_COMPLEX, pDocument->pClass->getSelectionType(pDocument)); // Fails! + CPPUNIT_ASSERT_EQUAL((int)LOK_SELTYPE_COMPLEX, pDocument->pClass->getSelectionType(pDocument)); } namespace { diff --git a/sw/source/uibase/dochdl/swdtflvr.cxx b/sw/source/uibase/dochdl/swdtflvr.cxx index 9c2440d55e8f..af9570f82d1c 100644 --- a/sw/source/uibase/dochdl/swdtflvr.cxx +++ b/sw/source/uibase/dochdl/swdtflvr.cxx @@ -430,6 +430,8 @@ sal_Bool SwTransferable::isComplex() if (m_pWrtShell->IsFrameSelected() || m_pWrtShell->IsObjSelected()) return true; + // Copy into a new Doc so we don't mess with the existing one. + //FIXME: We *should* be able to avoid this and improve the performance. m_pClpDocFac = new SwDocFac; SwDoc* const pTmpDoc = lcl_GetDoc(*m_pClpDocFac); @@ -437,36 +439,18 @@ sal_Bool SwTransferable::isComplex() .LockExpFields(); // never update fields - leave text as it is lclOverWriteDoc(*m_pWrtShell, *pTmpDoc); - // in CORE a new one was created (OLE-objects copied!) - m_aDocShellRef = pTmpDoc->GetTmpDocShell(); - if (m_aDocShellRef.Is()) - SwTransferable::InitOle(m_aDocShellRef); - pTmpDoc->SetTmpDocShell(nullptr); - - SwPaM aOrigPam(pTmpDoc->GetNodes().GetEndOfContent()); - aOrigPam.Move(fnMoveBackward, GoInDoc); - aOrigPam.SetMark(); - aOrigPam.Move(fnMoveForward, GoInDoc); - - SwPaM aPam(*aOrigPam.End(), *aOrigPam.Start()); - bool isComplex = false; - while (aPam.GetPoint()->nNode.GetIndex() < aPam.GetMark()->nNode.GetIndex() - || (aPam.GetPoint()->nNode.GetIndex() == aPam.GetMark()->nNode.GetIndex() - && aPam.GetPoint()->nContent.GetIndex() <= aPam.GetMark()->nContent.GetIndex())) + SwNodes& aNodes = pTmpDoc->GetNodes(); + for( sal_uLong nIndex = 0; nIndex < aNodes.Count(); ++nIndex) { - SwNode& rNd = aPam.GetNode(); - + SwNode& rNd = *aNodes[nIndex]; if (rNd.IsContentNode() && !rNd.IsTextNode()) { - //FIXME: this doesn't detect GrfNode, which we need to detect complex selections. isComplex = true; break; } else if (&rNd == &m_pWrtShell->GetDoc()->GetNodes().GetEndOfContent()) break; - - ++aPam.GetPoint()->nNode; } return isComplex; commit 8885dc3897b5c30d533f6e40bc83be6811d8481c Author: Ashod Nakashian <ashod.nakash...@collabora.co.uk> AuthorDate: Mon Jun 24 22:07:08 2019 -0400 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Sat Jul 20 21:45:55 2019 +0100 LOK: Implement getSelectionType Detects all complex scenarios, except for Graphics. DesktopLOKTest::testComplexSelection() has commented out check that currently fails due to the missing detection of Graphics nodes. Change-Id: Ifce17192d26daba218d2c3d38577cccec0699e99 diff --git a/desktop/qa/data/objects.odt b/desktop/qa/data/objects.odt new file mode 100644 index 000000000000..45c2b39cc1a4 Binary files /dev/null and b/desktop/qa/data/objects.odt differ diff --git a/desktop/qa/desktop_lib/test_desktop_lib.cxx b/desktop/qa/desktop_lib/test_desktop_lib.cxx index 7647788658df..b9e3a81ea1f0 100644 --- a/desktop/qa/desktop_lib/test_desktop_lib.cxx +++ b/desktop/qa/desktop_lib/test_desktop_lib.cxx @@ -2637,12 +2637,17 @@ void DesktopLOKTest::testComplexSelection() { // Start with a blank text file and add contents. LibLODocument_Impl* pDocument = loadDoc("blank_text.odt"); - // LibLODocument_Impl* pDocument = loadDoc("sheet_with_image.ods"); static const OString aText("hello world"); + // Certainly not complex. + CPPUNIT_ASSERT_EQUAL((int)LOK_SELTYPE_NONE, pDocument->pClass->getSelectionType(pDocument)); + // Paste text. CPPUNIT_ASSERT(pDocument->pClass->paste(pDocument, "text/plain;charset=utf-8", aText.getStr(), aText.getLength())); + // No selection. + CPPUNIT_ASSERT_EQUAL((int)LOK_SELTYPE_NONE, pDocument->pClass->getSelectionType(pDocument)); + // Paste an image. OUString aFileURL; createFileURL("paste.jpg", aFileURL); @@ -2654,10 +2659,6 @@ void DesktopLOKTest::testComplexSelection() pDocument->pClass->postUnoCommand(pDocument, ".uno:SelectAll", nullptr, false); Scheduler::ProcessEventsToIdle(); - // We expect this to be complex. - const int type = pDocument->pClass->getSelectionType(pDocument); - CPPUNIT_ASSERT_EQUAL((int)LOK_SELTYPE_COMPLEX, type); - // Export as plain text, we should get only the text part "hello". char* pText = pDocument->pClass->getTextSelection(pDocument, "text/plain;charset=utf-8", nullptr); CPPUNIT_ASSERT(pText != nullptr); @@ -2677,6 +2678,9 @@ void DesktopLOKTest::testComplexSelection() CPPUNIT_ASSERT(std::string(pText).find(aText.getStr()) != std::string::npos); // Must have the text. // CPPUNIT_ASSERT(std::string(pText).find("<img") != std::string::npos); // Must have the image as well. free(pText); + + // We expect this to be complex. + // CPPUNIT_ASSERT_EQUAL((int)LOK_SELTYPE_COMPLEX, pDocument->pClass->getSelectionType(pDocument)); // Fails! } namespace { diff --git a/sw/source/uibase/dochdl/swdtflvr.cxx b/sw/source/uibase/dochdl/swdtflvr.cxx index 2fd02b3e0ab6..9c2440d55e8f 100644 --- a/sw/source/uibase/dochdl/swdtflvr.cxx +++ b/sw/source/uibase/dochdl/swdtflvr.cxx @@ -419,6 +419,59 @@ namespace } } +sal_Bool SwTransferable::isComplex() +{ + const SelectionType nSelectionType = m_pWrtShell->GetSelectionType(); + + // Anything other than text is complex by definition. + if (nSelectionType != SelectionType::Text) + return true; + + if (m_pWrtShell->IsFrameSelected() || m_pWrtShell->IsObjSelected()) + return true; + + m_pClpDocFac = new SwDocFac; + SwDoc* const pTmpDoc = lcl_GetDoc(*m_pClpDocFac); + + pTmpDoc->getIDocumentFieldsAccess() + .LockExpFields(); // never update fields - leave text as it is + lclOverWriteDoc(*m_pWrtShell, *pTmpDoc); + + // in CORE a new one was created (OLE-objects copied!) + m_aDocShellRef = pTmpDoc->GetTmpDocShell(); + if (m_aDocShellRef.Is()) + SwTransferable::InitOle(m_aDocShellRef); + pTmpDoc->SetTmpDocShell(nullptr); + + SwPaM aOrigPam(pTmpDoc->GetNodes().GetEndOfContent()); + aOrigPam.Move(fnMoveBackward, GoInDoc); + aOrigPam.SetMark(); + aOrigPam.Move(fnMoveForward, GoInDoc); + + SwPaM aPam(*aOrigPam.End(), *aOrigPam.Start()); + + bool isComplex = false; + while (aPam.GetPoint()->nNode.GetIndex() < aPam.GetMark()->nNode.GetIndex() + || (aPam.GetPoint()->nNode.GetIndex() == aPam.GetMark()->nNode.GetIndex() + && aPam.GetPoint()->nContent.GetIndex() <= aPam.GetMark()->nContent.GetIndex())) + { + SwNode& rNd = aPam.GetNode(); + + if (rNd.IsContentNode() && !rNd.IsTextNode()) + { + //FIXME: this doesn't detect GrfNode, which we need to detect complex selections. + isComplex = true; + break; + } + else if (&rNd == &m_pWrtShell->GetDoc()->GetNodes().GetEndOfContent()) + break; + + ++aPam.GetPoint()->nNode; + } + + return isComplex; +} + bool SwTransferable::GetData( const DataFlavor& rFlavor, const OUString& rDestDoc ) { SotClipboardFormatId nFormat = SotExchange::GetFormat( rFlavor ); diff --git a/sw/source/uibase/inc/swdtflvr.hxx b/sw/source/uibase/inc/swdtflvr.hxx index 1b3cf2536964..0b15b8f15acf 100644 --- a/sw/source/uibase/inc/swdtflvr.hxx +++ b/sw/source/uibase/inc/swdtflvr.hxx @@ -151,6 +151,7 @@ protected: const css::datatransfer::DataFlavor& rFlavor ) override; virtual void DragFinished( sal_Int8 nDropAction ) override; virtual void ObjectReleased() override; + virtual sal_Bool isComplex() override; using TransferableHelper::StartDrag; commit 07aa3102367a7680e88fb5ed94e7893eb1d03fc9 Author: Michael Meeks <michael.me...@collabora.com> AuthorDate: Sat Jul 20 10:55:35 2019 +0100 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Sat Jul 20 21:45:54 2019 +0100 clipboard: get view setting correct before setClipboard. Change-Id: If8e9b057b819074f035c598569e3bf6d3d2fff00 diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx index 5e4d0baa53b4..c09ae8329f9c 100644 --- a/desktop/source/lib/init.cxx +++ b/desktop/source/lib/init.cxx @@ -3487,12 +3487,12 @@ static int doc_getClipboard(LibreOfficeKitDocument* pThis, } rtl::Reference<LOKClipboard> xClip(LOKClipboardFactory::getClipboardForCurView()); - SAL_INFO("lok", "Got clipboard for view " << xClip.get()); css::uno::Reference<css::datatransfer::XTransferable> xTransferable = xClip->getContents(); + SAL_INFO("lok", "Got from clip: " << xClip.get() << " transferrable: " << xTransferable); if (!xTransferable) { - SetLastExceptionMsg("No selection available"); + SetLastExceptionMsg("No clipboard content available"); return 0; } @@ -3582,6 +3582,8 @@ static int doc_setClipboard(LibreOfficeKitDocument* pThis, auto xClip = forceSetClipboardForCurrentView(pThis); xClip->setContents(xTransferable, uno::Reference<datatransfer::clipboard::XClipboardOwner>()); + SAL_INFO("lok", "Set clip: " << xClip.get() << " to: " << xTransferable); + if (!pDoc->isMimeTypeSupported()) { SetLastExceptionMsg("Document doesn't support this mime type"); diff --git a/desktop/source/lib/lokclipboard.cxx b/desktop/source/lib/lokclipboard.cxx index a959258269a1..ef52a4596c43 100644 --- a/desktop/source/lib/lokclipboard.cxx +++ b/desktop/source/lib/lokclipboard.cxx @@ -25,10 +25,13 @@ rtl::Reference<LOKClipboard> LOKClipboardFactory::getClipboardForCurView() auto it = gClipboards.find(nViewId); if (it != gClipboards.end()) + { + SAL_INFO("lok", "Got clip: " << it->second.get() << " from " << nViewId); return it->second; + } rtl::Reference<LOKClipboard> xClip(new LOKClipboard()); gClipboards[nViewId] = xClip; - SAL_INFO("lok", "Created clipboard for view " << nViewId << " as " << xClip.get()); + SAL_INFO("lok", "Created clip: " << xClip.get() << " for viewId " << nViewId); return xClip; } @@ -81,6 +84,7 @@ void LOKClipboard::setContents( std::vector<Reference<datatransfer::clipboard::XClipboardListener>> aListeners(m_aListeners); datatransfer::clipboard::ClipboardEvent aEv; aEv.Contents = m_xTransferable; + SAL_INFO("lok", "Clip: " << this << " set contents to " << m_xTransferable); aGuard.clear(); commit 8ead02bd06ad15e5a83cd2776b90679ec43e0c02 Author: Michael Meeks <michael.me...@collabora.com> AuthorDate: Sat Jul 20 10:52:51 2019 +0100 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Sat Jul 20 21:45:54 2019 +0100 lok: clipboard: per view clipboard creation. A bit brutal, but the mess around clipboard instantiation is awful. Change-Id: I62d6af8bf6813e6bab81123417ea8bfb28394e29 diff --git a/desktop/qa/desktop_lib/test_desktop_lib.cxx b/desktop/qa/desktop_lib/test_desktop_lib.cxx index 4d78c39cd6f3..7647788658df 100644 --- a/desktop/qa/desktop_lib/test_desktop_lib.cxx +++ b/desktop/qa/desktop_lib/test_desktop_lib.cxx @@ -588,7 +588,7 @@ void DesktopLOKTest::testPasteWriter() free(pText); // textt/plain should be rejected. - CPPUNIT_ASSERT(!pDocument->pClass->paste(pDocument, "text/plain;charset=utf-8", aText.getStr(), aText.getLength())); + CPPUNIT_ASSERT(!pDocument->pClass->paste(pDocument, "textt/plain;charset=utf-8", aText.getStr(), aText.getLength())); // Writer is expected to support text/html. CPPUNIT_ASSERT(pDocument->pClass->paste(pDocument, "text/html", aText.getStr(), aText.getLength())); @@ -2765,7 +2765,7 @@ void DesktopLOKTest::testABI() CPPUNIT_ASSERT_EQUAL(documentClassOffset(48), offsetof(struct _LibreOfficeKitDocumentClass, createViewWithOptions)); CPPUNIT_ASSERT_EQUAL(documentClassOffset(49), offsetof(struct _LibreOfficeKitDocumentClass, selectPart)); CPPUNIT_ASSERT_EQUAL(documentClassOffset(50), offsetof(struct _LibreOfficeKitDocumentClass, moveSelectedParts)); - CPPUNIT_ASSERT_EQUAL(documentClassOffset(51), offsetof(struct _LibreOfficeKitDocumentClass, getSelection)); + CPPUNIT_ASSERT_EQUAL(documentClassOffset(51), offsetof(struct _LibreOfficeKitDocumentClass, getClipboard)); CPPUNIT_ASSERT_EQUAL(documentClassOffset(52), offsetof(struct _LibreOfficeKitDocumentClass, setClipboard)); CPPUNIT_ASSERT_EQUAL(documentClassOffset(53), offsetof(struct _LibreOfficeKitDocumentClass, getSelectionType)); diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx index 32ed33379e65..5e4d0baa53b4 100644 --- a/desktop/source/lib/init.cxx +++ b/desktop/source/lib/init.cxx @@ -3565,9 +3565,6 @@ static int doc_setClipboard(LibreOfficeKitDocument* pThis, const size_t *pInSizes, const char **pInStreams) { - SolarMutexGuard aGuard; - if (gImpl) - comphelper::ProfileZone aZone("doc_paste"); SolarMutexGuard aGuard; diff --git a/desktop/source/lib/lokclipboard.cxx b/desktop/source/lib/lokclipboard.cxx index 74e5131f435d..a959258269a1 100644 --- a/desktop/source/lib/lokclipboard.cxx +++ b/desktop/source/lib/lokclipboard.cxx @@ -9,6 +9,7 @@ #include "lokclipboard.hxx" #include <sfx2/lokhelper.hxx> +#include <cppuhelper/supportsservice.hxx> using namespace css; using namespace css::uno; @@ -105,17 +106,38 @@ void LOKClipboard::removeClipboardListener( m_aListeners.erase(std::remove(m_aListeners.begin(), m_aListeners.end(), listener), m_aListeners.end()); } - -LOKTransferable::LOKTransferable(const char* pMimeType, const char* pData, std::size_t nSize) - : m_aMimeType(OUString::fromUtf8(pMimeType)) - , m_aSequence(reinterpret_cast<const sal_Int8*>(pData), nSize) +LOKTransferable::LOKTransferable(const OUString& sMimeType, + const css::uno::Sequence<sal_Int8>& aSequence) { -} + m_aContent.reserve(1); + m_aFlavors = css::uno::Sequence<css::datatransfer::DataFlavor>(1); + initFlavourFromMime(m_aFlavors[0], sMimeType); -LOKTransferable::LOKTransferable(OUString sMimeType, const css::uno::Sequence<sal_Int8>& aSequence) - : m_aMimeType(std::move(sMimeType)) - , m_aSequence(aSequence) + uno::Any aContent; + if (m_aFlavors[0].DataType == cppu::UnoType<OUString>::get()) + { + auto pText = reinterpret_cast<const sal_Char*>(aSequence.getConstArray()); + aContent <<= OUString(pText, aSequence.getLength(), RTL_TEXTENCODING_UTF8); + } + else + aContent <<= aSequence; + m_aContent.push_back(aContent); +} +// cf. sot/source/base/exchange.cxx for these two exceptional types. +void LOKTransferable::initFlavourFromMime(css::datatransfer::DataFlavor& rFlavor, + OUString aMimeType) { + if (aMimeType.startsWith("text/plain")) + { + aMimeType = "text/plain;charset=utf-16"; + rFlavor.DataType = cppu::UnoType<OUString>::get(); + } + else if (aMimeType == "application/x-libreoffice-tsvc") + rFlavor.DataType = cppu::UnoType<OUString>::get(); + else + rFlavor.DataType = cppu::UnoType<uno::Sequence<sal_Int8>>::get(); + rFlavor.MimeType = aMimeType; + rFlavor.HumanPresentableName = aMimeType; } LOKTransferable::LOKTransferable(const size_t nInCount, const char** pInMimeTypes, @@ -152,7 +174,7 @@ LOKTransferable::LOKTransferable(const size_t nInCount, const char** pInMimeType uno::Any SAL_CALL LOKTransferable::getTransferData(const datatransfer::DataFlavor& rFlavor) { - assert(m_aContent.size() == m_aFlavors.getLength()); + assert(m_aContent.size() == (size_t)m_aFlavors.getLength()); for (size_t i = 0; i < m_aContent.size(); ++i) { if (m_aFlavors[i].MimeType == rFlavor.MimeType) diff --git a/desktop/source/lib/lokclipboard.hxx b/desktop/source/lib/lokclipboard.hxx index d917a377eb11..54c1b37945ff 100644 --- a/desktop/source/lib/lokclipboard.hxx +++ b/desktop/source/lib/lokclipboard.hxx @@ -29,6 +29,7 @@ class LOKClipboard css::lang::XServiceInfo> { osl::Mutex m_aMutex; + css::uno::Reference<css::datatransfer::XTransferable> m_xTransferable; css::uno::Reference<css::datatransfer::clipboard::XClipboardOwner> m_aOwner; std::vector<css::uno::Reference<css::datatransfer::clipboard::XClipboardListener>> m_aListeners; @@ -73,9 +74,12 @@ class LOKTransferable : public cppu::WeakImplHelper<css::datatransfer::XTransfer css::uno::Sequence<css::datatransfer::DataFlavor> m_aFlavors; std::vector<css::uno::Any> m_aContent; + void initFlavourFromMime(css::datatransfer::DataFlavor& rFlavor, OUString sMimeType); + public: LOKTransferable(const size_t nInCount, const char** pInMimeTypes, const size_t* pInSizes, const char** pInStreams); + LOKTransferable(const OUString& sMimeType, const css::uno::Sequence<sal_Int8>& aSequence); css::uno::Any SAL_CALL getTransferData(const css::datatransfer::DataFlavor& rFlavor) override; commit 8b47a854a4e719c559437f10707a731f241ea075 Author: Michael Meeks <michael.me...@collabora.com> AuthorDate: Sat Jul 20 10:43:39 2019 +0100 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Sat Jul 20 21:45:54 2019 +0100 lok: clipboard: per view clipboard creation. A bit brutal, but the mess around clipboard instantiation is awful. Change-Id: I62d6af8bf6813e6bab81123417ea8bfb28394e29 diff --git a/desktop/qa/desktop_lib/test_desktop_lib.cxx b/desktop/qa/desktop_lib/test_desktop_lib.cxx index 462da8e4e6b5..4d78c39cd6f3 100644 --- a/desktop/qa/desktop_lib/test_desktop_lib.cxx +++ b/desktop/qa/desktop_lib/test_desktop_lib.cxx @@ -2765,9 +2765,13 @@ void DesktopLOKTest::testABI() CPPUNIT_ASSERT_EQUAL(documentClassOffset(48), offsetof(struct _LibreOfficeKitDocumentClass, createViewWithOptions)); CPPUNIT_ASSERT_EQUAL(documentClassOffset(49), offsetof(struct _LibreOfficeKitDocumentClass, selectPart)); CPPUNIT_ASSERT_EQUAL(documentClassOffset(50), offsetof(struct _LibreOfficeKitDocumentClass, moveSelectedParts)); + CPPUNIT_ASSERT_EQUAL(documentClassOffset(51), offsetof(struct _LibreOfficeKitDocumentClass, getSelection)); + CPPUNIT_ASSERT_EQUAL(documentClassOffset(52), offsetof(struct _LibreOfficeKitDocumentClass, setClipboard)); + CPPUNIT_ASSERT_EQUAL(documentClassOffset(53), offsetof(struct _LibreOfficeKitDocumentClass, getSelectionType)); + // Extending is fine, update this, and add new assert for the offsetof the // new method - CPPUNIT_ASSERT_EQUAL(documentClassOffset(51), sizeof(struct _LibreOfficeKitDocumentClass)); + CPPUNIT_ASSERT_EQUAL(documentClassOffset(54), sizeof(struct _LibreOfficeKitDocumentClass)); } CPPUNIT_TEST_SUITE_REGISTRATION(DesktopLOKTest); diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx index ffa31c2f50e7..32ed33379e65 100644 --- a/desktop/source/lib/init.cxx +++ b/desktop/source/lib/init.cxx @@ -790,7 +790,7 @@ static char* doc_getTextSelection(LibreOfficeKitDocument* pThis, const char* pMimeType, char** pUsedMimeType); static int doc_getSelectionType(LibreOfficeKitDocument* pThis); -static int doc_getSelection (LibreOfficeKitDocument* pThis, +static int doc_getClipboard (LibreOfficeKitDocument* pThis, const char **pMimeTypes, size_t *pOutCount, char ***pOutMimeTypes, @@ -860,6 +860,8 @@ static int doc_getSignatureState(LibreOfficeKitDocument* pThis); static size_t doc_renderShapeSelection(LibreOfficeKitDocument* pThis, char** pOutput); +static rtl::Reference<LOKClipboard> forceSetClipboardForCurrentView(LibreOfficeKitDocument *pThis); + LibLODocument_Impl::LibLODocument_Impl(const uno::Reference <css::lang::XComponent> &xComponent) : mxComponent(xComponent) { @@ -898,7 +900,7 @@ LibLODocument_Impl::LibLODocument_Impl(const uno::Reference <css::lang::XCompone m_pDocumentClass->setTextSelection = doc_setTextSelection; m_pDocumentClass->getTextSelection = doc_getTextSelection; m_pDocumentClass->getSelectionType = doc_getSelectionType; - m_pDocumentClass->getSelection = doc_getSelection; + m_pDocumentClass->getClipboard = doc_getClipboard; m_pDocumentClass->setClipboard = doc_setClipboard; m_pDocumentClass->paste = doc_paste; m_pDocumentClass->setGraphicSelection = doc_setGraphicSelection; @@ -938,6 +940,8 @@ LibLODocument_Impl::LibLODocument_Impl(const uno::Reference <css::lang::XCompone gDocumentClass = m_pDocumentClass; } pClass = m_pDocumentClass.get(); + + forceSetClipboardForCurrentView(this); } LibLODocument_Impl::~LibLODocument_Impl() @@ -3455,7 +3459,7 @@ static int doc_getSelectionType(LibreOfficeKitDocument* pThis) return aRet.getLength() ? LOK_SELTYPE_TEXT : LOK_SELTYPE_NONE; } -static int doc_getSelection(LibreOfficeKitDocument* pThis, +static int doc_getClipboard(LibreOfficeKitDocument* pThis, const char **pMimeTypes, size_t *pOutCount, char ***pOutMimeTypes, @@ -3482,7 +3486,10 @@ static int doc_getSelection(LibreOfficeKitDocument* pThis, return 0; } - css::uno::Reference<css::datatransfer::XTransferable> xTransferable = pDoc->getSelection(); + rtl::Reference<LOKClipboard> xClip(LOKClipboardFactory::getClipboardForCurView()); + SAL_INFO("lok", "Got clipboard for view " << xClip.get()); + + css::uno::Reference<css::datatransfer::XTransferable> xTransferable = xClip->getContents(); if (!xTransferable) { SetLastExceptionMsg("No selection available"); @@ -3535,6 +3542,23 @@ static int doc_getSelection(LibreOfficeKitDocument* pThis, return 1; } +/* + * Unfortunately clipboard creation using UNO is insanely baroque. + * we also need to ensure that this works for the first view which + * has no clear 'createView' called for it (unfortunately). + */ +static rtl::Reference<LOKClipboard> forceSetClipboardForCurrentView(LibreOfficeKitDocument *pThis) +{ + ITiledRenderable* pDoc = getTiledRenderable(pThis); + rtl::Reference<LOKClipboard> xClip(LOKClipboardFactory::getClipboardForCurView()); + + SAL_INFO("lok", "Set to clipboard for view " << xClip.get()); + // FIXME: using a hammer here - should not be necessary if all tests used createView. + pDoc->setClipboard(uno::Reference<datatransfer::clipboard::XClipboard>(xClip->getXI(), UNO_QUERY)); + + return xClip; +} + static int doc_setClipboard(LibreOfficeKitDocument* pThis, const size_t nInCount, const char **pInMimeTypes, @@ -3557,9 +3581,9 @@ static int doc_setClipboard(LibreOfficeKitDocument* pThis, } uno::Reference<datatransfer::XTransferable> xTransferable(new LOKTransferable(nInCount, pInMimeTypes, pInSizes, pInStreams)); - uno::Reference<datatransfer::clipboard::XClipboard> xClipboard(new LOKClipboard); - xClipboard->setContents(xTransferable, uno::Reference<datatransfer::clipboard::XClipboardOwner>()); - pDoc->setClipboard(xClipboard); + + auto xClip = forceSetClipboardForCurrentView(pThis); + xClip->setContents(xTransferable, uno::Reference<datatransfer::clipboard::XClipboardOwner>()); if (!pDoc->isMimeTypeSupported()) { @@ -4225,7 +4249,7 @@ static void doc_setOutlineState(LibreOfficeKitDocument* pThis, bool bColumn, int pDoc->setOutlineState(bColumn, nLevel, nIndex, bHidden); } -static int doc_createViewWithOptions(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/, +static int doc_createViewWithOptions(LibreOfficeKitDocument* pThis, const char* pOptions) { comphelper::ProfileZone aZone("doc_createView"); @@ -4242,7 +4266,11 @@ static int doc_createViewWithOptions(SAL_UNUSED_PARAMETER LibreOfficeKitDocument comphelper::LibreOfficeKit::setLanguageTag(LanguageTag(aLanguage)); } - return SfxLokHelper::createView(); + int nId = SfxLokHelper::createView(); + + forceSetClipboardForCurrentView(pThis); + + return nId; } static int doc_createView(LibreOfficeKitDocument* pThis) diff --git a/desktop/source/lib/lokclipboard.cxx b/desktop/source/lib/lokclipboard.cxx index 015690bd9bc4..74e5131f435d 100644 --- a/desktop/source/lib/lokclipboard.cxx +++ b/desktop/source/lib/lokclipboard.cxx @@ -8,23 +8,103 @@ */ #include "lokclipboard.hxx" -#include <comphelper/sequence.hxx> +#include <sfx2/lokhelper.hxx> -using namespace com::sun::star; +using namespace css; +using namespace css::uno; -uno::Reference<datatransfer::XTransferable> SAL_CALL LOKClipboard::getContents() +osl::Mutex LOKClipboardFactory::gMutex; +std::unordered_map<int, rtl::Reference<LOKClipboard>> LOKClipboardFactory::gClipboards; + +rtl::Reference<LOKClipboard> LOKClipboardFactory::getClipboardForCurView() +{ + int nViewId = SfxLokHelper::getView(); // currently active. + + osl::MutexGuard aGuard(gMutex); + + auto it = gClipboards.find(nViewId); + if (it != gClipboards.end()) + return it->second; + rtl::Reference<LOKClipboard> xClip(new LOKClipboard()); + gClipboards[nViewId] = xClip; + SAL_INFO("lok", "Created clipboard for view " << nViewId << " as " << xClip.get()); + return xClip; +} + +uno::Reference<uno::XInterface> + SAL_CALL LOKClipboardFactory::createInstanceWithArguments(const Sequence<Any>& /* rArgs */) +{ + return uno::Reference<uno::XInterface>( + static_cast<cppu::OWeakObject*>(getClipboardForCurView().get())); +} + +LOKClipboard::LOKClipboard() + : cppu::WeakComponentImplHelper<css::datatransfer::clipboard::XSystemClipboard, + css::lang::XServiceInfo>(m_aMutex) +{ +} + +Sequence<OUString> LOKClipboard::getSupportedServiceNames_static() +{ + Sequence<OUString> aRet{ "com.sun.star.datatransfer.clipboard.SystemClipboard" }; + return aRet; +} + +OUString LOKClipboard::getImplementationName() +{ + return OUString("com.sun.star.datatransfer.LOKClipboard"); +} + +Sequence<OUString> LOKClipboard::getSupportedServiceNames() +{ + return getSupportedServiceNames_static(); +} + +sal_Bool LOKClipboard::supportsService(const OUString& ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +Reference<css::datatransfer::XTransferable> LOKClipboard::getContents() { return m_xTransferable; } + +void LOKClipboard::setContents( + const Reference<css::datatransfer::XTransferable>& xTrans, + const Reference<css::datatransfer::clipboard::XClipboardOwner>& xClipboardOwner) { - return m_xTransferable; + osl::ClearableMutexGuard aGuard(m_aMutex); + Reference<datatransfer::clipboard::XClipboardOwner> xOldOwner(m_aOwner); + Reference<datatransfer::XTransferable> xOldContents(m_xTransferable); + m_xTransferable = xTrans; + m_aOwner = xClipboardOwner; + + std::vector<Reference<datatransfer::clipboard::XClipboardListener>> aListeners(m_aListeners); + datatransfer::clipboard::ClipboardEvent aEv; + aEv.Contents = m_xTransferable; + + aGuard.clear(); + + if (xOldOwner.is() && xOldOwner != xClipboardOwner) + xOldOwner->lostOwnership(this, xOldContents); + for (auto const& listener : aListeners) + { + listener->changedContents(aEv); + } } -void SAL_CALL LOKClipboard::setContents( - const uno::Reference<datatransfer::XTransferable>& xTransferable, - const uno::Reference<datatransfer::clipboard::XClipboardOwner>& /*xClipboardOwner*/) +void LOKClipboard::addClipboardListener( + const Reference<datatransfer::clipboard::XClipboardListener>& listener) { - m_xTransferable = xTransferable; + osl::ClearableMutexGuard aGuard(m_aMutex); + m_aListeners.push_back(listener); } -OUString SAL_CALL LOKClipboard::getName() { return OUString(); } +void LOKClipboard::removeClipboardListener( + const Reference<datatransfer::clipboard::XClipboardListener>& listener) +{ + osl::ClearableMutexGuard aGuard(m_aMutex); + m_aListeners.erase(std::remove(m_aListeners.begin(), m_aListeners.end(), listener), + m_aListeners.end()); +} LOKTransferable::LOKTransferable(const char* pMimeType, const char* pData, std::size_t nSize) : m_aMimeType(OUString::fromUtf8(pMimeType)) diff --git a/desktop/source/lib/lokclipboard.hxx b/desktop/source/lib/lokclipboard.hxx index 5da8b3e2b63e..d917a377eb11 100644 --- a/desktop/source/lib/lokclipboard.hxx +++ b/desktop/source/lib/lokclipboard.hxx @@ -11,24 +11,60 @@ #define INCLUDED_DESKTOP_SOURCE_LIB_LOKCLIPBOARD_HXX #include <vector> +#include <unordered_map> +#include <rtl/ref.hxx> #include <cppuhelper/implbase.hxx> -#include <com/sun/star/datatransfer/clipboard/XClipboard.hpp> +#include <cppuhelper/compbase.hxx> +#include <comphelper/sequence.hxx> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/datatransfer/clipboard/XSystemClipboard.hpp> + +using namespace css::uno; /// A clipboard implementation for LibreOfficeKit. -class LOKClipboard : public cppu::WeakImplHelper<css::datatransfer::clipboard::XClipboard> +class LOKClipboard + : public cppu::WeakComponentImplHelper<css::datatransfer::clipboard::XSystemClipboard, + css::lang::XServiceInfo> { - css::uno::Reference<css::datatransfer::XTransferable> m_xTransferable; + osl::Mutex m_aMutex; + css::uno::Reference<css::datatransfer::clipboard::XClipboardOwner> m_aOwner; + std::vector<css::uno::Reference<css::datatransfer::clipboard::XClipboardListener>> m_aListeners; public: - css::uno::Reference<css::datatransfer::XTransferable> SAL_CALL getContents() override; + LOKClipboard(); + + /// get an XInterface easily. + css::uno::Reference<css::uno::XInterface> getXI() + { + return css::uno::Reference<css::uno::XInterface>(static_cast<cppu::OWeakObject*>(this)); + } + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; + virtual Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + static Sequence<OUString> getSupportedServiceNames_static(); + // XClipboard + css::uno::Reference<css::datatransfer::XTransferable> SAL_CALL getContents() override; void SAL_CALL setContents( const css::uno::Reference<css::datatransfer::XTransferable>& xTransferable, const css::uno::Reference<css::datatransfer::clipboard::XClipboardOwner>& xClipboardOwner) override; + OUString SAL_CALL getName() override { return OUString("CLIPBOARD"); } + + // XClipboardEx + sal_Int8 SAL_CALL getRenderingCapabilities() override { return 0; } - OUString SAL_CALL getName() override; + // XClipboardNotifier + void SAL_CALL addClipboardListener( + const css::uno::Reference<css::datatransfer::clipboard::XClipboardListener>& listener) + override; + void SAL_CALL removeClipboardListener( + const css::uno::Reference<css::datatransfer::clipboard::XClipboardListener>& listener) + override; }; /// Represents the contents of LOKClipboard. @@ -38,10 +74,8 @@ class LOKTransferable : public cppu::WeakImplHelper<css::datatransfer::XTransfer std::vector<css::uno::Any> m_aContent; public: - LOKTransferable(const size_t nInCount, - const char **pInMimeTypes, - const size_t *pInSizes, - const char **pInStreams); + LOKTransferable(const size_t nInCount, const char** pInMimeTypes, const size_t* pInSizes, + const char** pInStreams); css::uno::Any SAL_CALL getTransferData(const css::datatransfer::DataFlavor& rFlavor) override; @@ -50,6 +84,27 @@ public: sal_Bool SAL_CALL isDataFlavorSupported(const css::datatransfer::DataFlavor& rFlavor) override; }; +/// Theoretically to hook into the (horrible) vcl dtranscomp.cxx code. +class LOKClipboardFactory : public ::cppu::WeakComponentImplHelper<css::lang::XSingleServiceFactory> +{ + static osl::Mutex gMutex; + static std::unordered_map<int, rtl::Reference<LOKClipboard>> gClipboards; + +public: + LOKClipboardFactory() + : cppu::WeakComponentImplHelper<css::lang::XSingleServiceFactory>(gMutex) + { + } + + css::uno::Reference<css::uno::XInterface> SAL_CALL createInstance() override + { + return createInstanceWithArguments(css::uno::Sequence<css::uno::Any>()); + } + css::uno::Reference<css::uno::XInterface> SAL_CALL + createInstanceWithArguments(const css::uno::Sequence<css::uno::Any>& /* rArgs */) override; + static rtl::Reference<LOKClipboard> getClipboardForCurView(); +}; + #endif /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/include/LibreOfficeKit/LibreOfficeKit.h b/include/LibreOfficeKit/LibreOfficeKit.h index 83e600cad980..c28e2dd129ed 100644 --- a/include/LibreOfficeKit/LibreOfficeKit.h +++ b/include/LibreOfficeKit/LibreOfficeKit.h @@ -385,8 +385,8 @@ struct _LibreOfficeKitDocumentClass void (*moveSelectedParts) (LibreOfficeKitDocument* pThis, int nPosition, bool bDuplicate); /// Pass a nullptr terminated array of mime-type strings - /// @see lok::Document::getSelection for more details - int (*getSelection) (LibreOfficeKitDocument* pThis, + /// @see lok::Document::getClipboard for more details + int (*getClipboard) (LibreOfficeKitDocument* pThis, const char **pMimeTypes, size_t *pOutCount, char ***pOutMimeTypes, diff --git a/include/LibreOfficeKit/LibreOfficeKit.hxx b/include/LibreOfficeKit/LibreOfficeKit.hxx index cb58022ae12b..a118cac1833b 100644 --- a/include/LibreOfficeKit/LibreOfficeKit.hxx +++ b/include/LibreOfficeKit/LibreOfficeKit.hxx @@ -362,7 +362,7 @@ public: } /** - * Gets the selected content for the current view as a series of binary streams. + * Gets the content on the clipboard for the current view as a series of binary streams. * * NB. returns a complete set of possible selection types if nullptr is passed for pMimeTypes. * @@ -374,13 +374,13 @@ public: * * @returns: true on success, false on error. */ - bool getSelection(const char **pMimeTypes, + bool getClipboard(const char **pMimeTypes, size_t *pOutCount, char ***pOutMimeTypes, size_t **pOutSizes, char ***pOutStreams) { - return mpDoc->pClass->getSelection(mpDoc, pMimeTypes, pOutCount, pOutMimeTypes, pOutSizes, pOutStreams); + return mpDoc->pClass->getClipboard(mpDoc, pMimeTypes, pOutCount, pOutMimeTypes, pOutSizes, pOutStreams); } /** commit 63f0eb9229ee7b9d26be700a5342aa48d5159ca3 Author: Andras Timar <andras.ti...@collabora.com> AuthorDate: Mon Jun 24 10:33:51 2019 +0200 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Sat Jul 20 21:45:54 2019 +0100 build fix: missing #include <ndgrf.hxx> Change-Id: I72caa16d3c5b5698226cc7d3f07d3d32820032d8 diff --git a/sw/source/filter/html/wrthtml.cxx b/sw/source/filter/html/wrthtml.cxx index bba11082b6db..54afa80ba7c9 100644 --- a/sw/source/filter/html/wrthtml.cxx +++ b/sw/source/filter/html/wrthtml.cxx @@ -55,6 +55,7 @@ #include <docary.hxx> #include <pam.hxx> #include <doc.hxx> +#include <ndgrf.hxx> #include <ndtxt.hxx> #include <mdiexp.hxx> #include <fltini.hxx> commit f566493a0fe822e6f19ac92e042be24c1c9772e4 Author: Szymon Kłos <szymon.k...@collabora.com> AuthorDate: Mon Jun 24 09:24:59 2019 +0200 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Sat Jul 20 21:45:54 2019 +0100 Fix build: unused argument Change-Id: Id79d9c093da21b9f68bb38a51ac5f142a69364ed diff --git a/include/LibreOfficeKit/LibreOfficeKit.hxx b/include/LibreOfficeKit/LibreOfficeKit.hxx index d671994bfcf7..cb58022ae12b 100644 --- a/include/LibreOfficeKit/LibreOfficeKit.hxx +++ b/include/LibreOfficeKit/LibreOfficeKit.hxx @@ -356,7 +356,7 @@ public: * * @return an element of the LibreOfficeKitSelectionType enum. */ - int getSelectionType(LibreOfficeKitDocument* pThis) + int getSelectionType(LibreOfficeKitDocument* /*pThis*/) { return mpDoc->pClass->getSelectionType(mpDoc); } commit 4407236d59a4647851541a60e842827b8025ac72 Author: Ashod Nakashian <ashod.nakash...@collabora.co.uk> AuthorDate: Fri Jul 19 23:51:16 2019 +0100 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Sat Jul 20 21:45:54 2019 +0100 LOK: Support detecting complex selections Includes unit-test, but is otherwise not complete implementation. Change-Id: I5fdcd543358caab1858f6351238c21312665839c diff --git a/desktop/qa/desktop_lib/test_desktop_lib.cxx b/desktop/qa/desktop_lib/test_desktop_lib.cxx index e590b0a7f974..462da8e4e6b5 100644 --- a/desktop/qa/desktop_lib/test_desktop_lib.cxx +++ b/desktop/qa/desktop_lib/test_desktop_lib.cxx @@ -139,6 +139,7 @@ public: void testInsertCertificate_PEM_DOCX(); void testSignDocument_PEM_PDF(); void testTextSelectionHandles(); + void testComplexSelection(); void testDialogPaste(); void testShowHideDialog(); void testABI(); @@ -193,6 +194,7 @@ public: CPPUNIT_TEST(testSignDocument_PEM_PDF); #endif CPPUNIT_TEST(testTextSelectionHandles); + CPPUNIT_TEST(testComplexSelection); CPPUNIT_TEST(testDialogPaste); CPPUNIT_TEST(testShowHideDialog); CPPUNIT_TEST(testABI); @@ -586,7 +588,7 @@ void DesktopLOKTest::testPasteWriter() free(pText); // textt/plain should be rejected. - CPPUNIT_ASSERT(!pDocument->pClass->paste(pDocument, "textt/plain;charset=utf-8", aText.getStr(), aText.getLength())); + CPPUNIT_ASSERT(!pDocument->pClass->paste(pDocument, "text/plain;charset=utf-8", aText.getStr(), aText.getLength())); // Writer is expected to support text/html. CPPUNIT_ASSERT(pDocument->pClass->paste(pDocument, "text/html", aText.getStr(), aText.getLength())); @@ -2631,6 +2633,52 @@ void DesktopLOKTest::testShowHideDialog() CPPUNIT_ASSERT_EQUAL(std::string("invalidate"), aView.m_aCallbackWindowResult.get<std::string>("action")); } +void DesktopLOKTest::testComplexSelection() +{ + // Start with a blank text file and add contents. + LibLODocument_Impl* pDocument = loadDoc("blank_text.odt"); + // LibLODocument_Impl* pDocument = loadDoc("sheet_with_image.ods"); + static const OString aText("hello world"); + + // Paste text. + CPPUNIT_ASSERT(pDocument->pClass->paste(pDocument, "text/plain;charset=utf-8", aText.getStr(), aText.getLength())); + + // Paste an image. + OUString aFileURL; + createFileURL("paste.jpg", aFileURL); + std::ifstream aImageStream(aFileURL.toUtf8().copy(strlen("file://")).getStr()); + std::vector<char> aImageContents((std::istreambuf_iterator<char>(aImageStream)), std::istreambuf_iterator<char>()); + CPPUNIT_ASSERT(pDocument->pClass->paste(pDocument, "image/jpeg", aImageContents.data(), aImageContents.size())); + + // Now select-all. + pDocument->pClass->postUnoCommand(pDocument, ".uno:SelectAll", nullptr, false); + Scheduler::ProcessEventsToIdle(); + + // We expect this to be complex. + const int type = pDocument->pClass->getSelectionType(pDocument); + CPPUNIT_ASSERT_EQUAL((int)LOK_SELTYPE_COMPLEX, type); + + // Export as plain text, we should get only the text part "hello". + char* pText = pDocument->pClass->getTextSelection(pDocument, "text/plain;charset=utf-8", nullptr); + CPPUNIT_ASSERT(pText != nullptr); + CPPUNIT_ASSERT_EQUAL(aText, OString(pText)); + free(pText); + + // Export as rtf, we should also get the image. + pText = pDocument->pClass->getTextSelection(pDocument, "text/rtf", nullptr); + CPPUNIT_ASSERT(pText != nullptr); + CPPUNIT_ASSERT(std::string(pText).find(aText.getStr()) != std::string::npos); // Must have the text. + CPPUNIT_ASSERT(std::string(pText).find("pict{") != std::string::npos); // Must have the image as well. + free(pText); + + // Export as html, we should also get the image. + pText = pDocument->pClass->getTextSelection(pDocument, "text/html", nullptr); + CPPUNIT_ASSERT(pText != nullptr); + CPPUNIT_ASSERT(std::string(pText).find(aText.getStr()) != std::string::npos); // Must have the text. + // CPPUNIT_ASSERT(std::string(pText).find("<img") != std::string::npos); // Must have the image as well. + free(pText); +} + namespace { constexpr size_t classOffset(int i) diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx index 97b2532ce432..ffa31c2f50e7 100644 --- a/desktop/source/lib/init.cxx +++ b/desktop/source/lib/init.cxx @@ -78,6 +78,7 @@ #include <com/sun/star/util/URLTransformer.hpp> #include <com/sun/star/datatransfer/clipboard/XClipboard.hpp> #include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp> +#include <com/sun/star/datatransfer/XTransferable2.hpp> #include <com/sun/star/text/TextContentAnchorType.hpp> #include <com/sun/star/document/XRedlinesSupplier.hpp> #include <com/sun/star/ui/GlobalAcceleratorConfiguration.hpp> @@ -788,6 +789,7 @@ static void doc_setTextSelection (LibreOfficeKitDocument* pThis, static char* doc_getTextSelection(LibreOfficeKitDocument* pThis, const char* pMimeType, char** pUsedMimeType); +static int doc_getSelectionType(LibreOfficeKitDocument* pThis); static int doc_getSelection (LibreOfficeKitDocument* pThis, const char **pMimeTypes, size_t *pOutCount, @@ -895,6 +897,7 @@ LibLODocument_Impl::LibLODocument_Impl(const uno::Reference <css::lang::XCompone m_pDocumentClass->postUnoCommand = doc_postUnoCommand; m_pDocumentClass->setTextSelection = doc_setTextSelection; m_pDocumentClass->getTextSelection = doc_getTextSelection; + m_pDocumentClass->getSelectionType = doc_getSelectionType; m_pDocumentClass->getSelection = doc_getSelection; m_pDocumentClass->setClipboard = doc_setClipboard; m_pDocumentClass->paste = doc_paste; @@ -3419,6 +3422,39 @@ static char* doc_getTextSelection(LibreOfficeKitDocument* pThis, const char* pMi return convertOString(aRet); } +static int doc_getSelectionType(LibreOfficeKitDocument* pThis) +{ + SolarMutexGuard aGuard; + SetLastExceptionMsg(); + + ITiledRenderable* pDoc = getTiledRenderable(pThis); + if (!pDoc) + { + SetLastExceptionMsg("Document doesn't support tiled rendering"); + return LOK_SELTYPE_NONE; + } + + css::uno::Reference<css::datatransfer::XTransferable2> xTransferable(pDoc->getSelection(), css::uno::UNO_QUERY); + if (!xTransferable) + { + SetLastExceptionMsg("No selection available"); + return LOK_SELTYPE_NONE; + } + + if (xTransferable->isComplex()) + return LOK_SELTYPE_COMPLEX; + + OString aRet; + bool bSuccess = getFromTransferrable(xTransferable, OString("text/plain;charset=utf-8"), aRet); + if (!bSuccess) + return LOK_SELTYPE_NONE; + + if (aRet.getLength() > 1000) // About 2 paragraphs. + return LOK_SELTYPE_COMPLEX; + + return aRet.getLength() ? LOK_SELTYPE_TEXT : LOK_SELTYPE_NONE; +} + static int doc_getSelection(LibreOfficeKitDocument* pThis, const char **pMimeTypes, size_t *pOutCount, diff --git a/include/LibreOfficeKit/LibreOfficeKit.h b/include/LibreOfficeKit/LibreOfficeKit.h index c03408308600..83e600cad980 100644 --- a/include/LibreOfficeKit/LibreOfficeKit.h +++ b/include/LibreOfficeKit/LibreOfficeKit.h @@ -400,6 +400,9 @@ struct _LibreOfficeKitDocumentClass const size_t *pInSizes, const char **pInStreams); + /// @see lok::Document::getSelectionType + int (*getSelectionType) (LibreOfficeKitDocument* pThis); + #endif // defined LOK_USE_UNSTABLE_API || defined LIBO_INTERNAL_ONLY }; diff --git a/include/LibreOfficeKit/LibreOfficeKit.hxx b/include/LibreOfficeKit/LibreOfficeKit.hxx index 50d56ce9c8d4..d671994bfcf7 100644 --- a/include/LibreOfficeKit/LibreOfficeKit.hxx +++ b/include/LibreOfficeKit/LibreOfficeKit.hxx @@ -352,6 +352,16 @@ public: } /** + * Gets the type of the selected content. + * + * @return an element of the LibreOfficeKitSelectionType enum. + */ + int getSelectionType(LibreOfficeKitDocument* pThis) + { + return mpDoc->pClass->getSelectionType(mpDoc); + } + + /** * Gets the selected content for the current view as a series of binary streams. * * NB. returns a complete set of possible selection types if nullptr is passed for pMimeTypes. diff --git a/include/LibreOfficeKit/LibreOfficeKitEnums.h b/include/LibreOfficeKit/LibreOfficeKitEnums.h index 5b3dda95d7a3..64a3a98aeaab 100644 --- a/include/LibreOfficeKit/LibreOfficeKitEnums.h +++ b/include/LibreOfficeKit/LibreOfficeKitEnums.h @@ -47,6 +47,15 @@ typedef enum } LibreOfficeKitWindowAction; +typedef enum +{ + LOK_SELTYPE_NONE, + LOK_SELTYPE_TEXT, + LOK_SELTYPE_LARGE_TEXT, + LOK_SELTYPE_COMPLEX +} +LibreOfficeKitSelectionType; + /** Optional features of LibreOfficeKit, in particular callbacks that block * LibreOfficeKit until the corresponding reply is received, which would * deadlock if the client does not support the feature. diff --git a/include/vcl/transfer.hxx b/include/vcl/transfer.hxx index 888fe254e115..722d3f26b42b 100644 --- a/include/vcl/transfer.hxx +++ b/include/vcl/transfer.hxx @@ -184,6 +184,7 @@ private: // Transferable2 virtual css::uno::Any SAL_CALL getTransferData2( const css::datatransfer::DataFlavor& rFlavor, const OUString& rDestDoc ) override; + virtual sal_Bool SAL_CALL isComplex() override; // XEventListener virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; diff --git a/offapi/com/sun/star/datatransfer/XTransferable2.idl b/offapi/com/sun/star/datatransfer/XTransferable2.idl index f792a969ded8..a6b53f0671e2 100644 --- a/offapi/com/sun/star/datatransfer/XTransferable2.idl +++ b/offapi/com/sun/star/datatransfer/XTransferable2.idl @@ -31,6 +31,11 @@ interface XTransferable2 : com::sun::star::datatransfer::XTransferable */ any getTransferData2( [in] DataFlavor aFlavor, [in] string aDestShellID ) raises ( UnsupportedFlavorException, com::sun::star::io::IOException ); + + /** + * Returns true if the selection contains embedded objects or is a large text blob. + */ + boolean isComplex(); }; }; }; }; }; diff --git a/sw/source/filter/html/wrthtml.cxx b/sw/source/filter/html/wrthtml.cxx index 51b7317f64e3..bba11082b6db 100644 --- a/sw/source/filter/html/wrthtml.cxx +++ b/sw/source/filter/html/wrthtml.cxx @@ -822,6 +822,13 @@ void SwHTMLWriter::Out_SwDoc( SwPaM* pPam ) OutHTML_Section( *this, *rNd.GetSectionNode() ); m_nBkmkTabPos = m_bWriteAll ? FindPos_Bkmk( *m_pCurrentPam->GetPoint() ) : -1; } + else if( rNd.IsGrfNode() ) + { + SwGrfNode* pGrfNd = rNd.GetGrfNode(); + assert(pGrfNd && !"FIXME: Implement Graphic copy as HTML."); + // if (pGrfNd) + // OutHTML_SwGrfNode( *this, *pGrfNd ); + } else if( &rNd == &m_pDoc->GetNodes().GetEndOfContent() ) break; diff --git a/vcl/source/treelist/transfer.cxx b/vcl/source/treelist/transfer.cxx index 3f7d7c5a655c..c5f61cb850c4 100644 --- a/vcl/source/treelist/transfer.cxx +++ b/vcl/source/treelist/transfer.cxx @@ -388,6 +388,11 @@ Any SAL_CALL TransferableHelper::getTransferData2( const DataFlavor& rFlavor, co return maAny; } +sal_Bool SAL_CALL TransferableHelper::isComplex() +{ + //FIXME: get from each document. + return sal_True; +} Sequence< DataFlavor > SAL_CALL TransferableHelper::getTransferDataFlavors() { commit 919b76f509cd4cb17a1cc770f83a792fcad1ad3c Author: Michael Meeks <michael.me...@collabora.com> AuthorDate: Sat Jun 22 12:48:49 2019 +0100 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Sat Jul 20 21:45:54 2019 +0100 lok: use utf-8 for text/plain always. Change-Id: Ie91f89b3dc6ba3eddcf8383948dddc7878eae4d8 diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx index 9d41ebf9ad58..97b2532ce432 100644 --- a/desktop/source/lib/init.cxx +++ b/desktop/source/lib/init.cxx @@ -3477,10 +3477,13 @@ static int doc_getSelection(LibreOfficeKitDocument* pThis, *pOutStreams = static_cast<char **>(malloc(*pOutCount * sizeof(char *))); for (size_t i = 0; i < aMimeTypes.size(); ++i) { - (*pOutMimeTypes)[i] = strdup(aMimeTypes[i].getStr()); + if (aMimeTypes[i] == "text/plain;charset=utf-16") + (*pOutMimeTypes)[i] = strdup("text/plain;charset=utf-8"); + else + (*pOutMimeTypes)[i] = strdup(aMimeTypes[i].getStr()); OString aRet; - bool bSuccess = getFromTransferrable(xTransferable, aMimeTypes[i], aRet); + bool bSuccess = getFromTransferrable(xTransferable, (*pOutMimeTypes)[i], aRet); if (!bSuccess || aRet.getLength() < 1) { (*pOutSizes)[i] = 0; commit 681ab74ef75513f6229fde28097068ebc9f5e3cc Author: Michael Meeks <michael.me...@collabora.com> AuthorDate: Fri Jul 19 23:44:20 2019 +0100 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Sat Jul 20 21:45:54 2019 +0100 Add more powerful selection fetch & clipboard set methods. Change-Id: I6633356d13480377a83a006588ec69ebcb56a93f diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx index 1a18e296a092..9d41ebf9ad58 100644 --- a/desktop/source/lib/init.cxx +++ b/desktop/source/lib/init.cxx @@ -170,6 +170,7 @@ static std::weak_ptr< LibreOfficeKitDocumentClass > gDocumentClass; static void SetLastExceptionMsg(const OUString& s = OUString()) { + SAL_WARN("lok", "lok exception " + s); if (gImpl) gImpl->maLastExceptionMsg = s; } @@ -787,6 +788,17 @@ static void doc_setTextSelection (LibreOfficeKitDocument* pThis, static char* doc_getTextSelection(LibreOfficeKitDocument* pThis, const char* pMimeType, char** pUsedMimeType); +static int doc_getSelection (LibreOfficeKitDocument* pThis, + const char **pMimeTypes, + size_t *pOutCount, + char ***pOutMimeTypes, + size_t **pOutSizes, + char ***pOutStreams); +static int doc_setClipboard (LibreOfficeKitDocument* pThis, + const size_t nInCount, + const char **pInMimeTypes, + const size_t *pInSizes, + const char **pInStreams); static bool doc_paste(LibreOfficeKitDocument* pThis, const char* pMimeType, const char* pData, @@ -883,6 +895,8 @@ LibLODocument_Impl::LibLODocument_Impl(const uno::Reference <css::lang::XCompone m_pDocumentClass->postUnoCommand = doc_postUnoCommand; m_pDocumentClass->setTextSelection = doc_setTextSelection; m_pDocumentClass->getTextSelection = doc_getTextSelection; + m_pDocumentClass->getSelection = doc_getSelection; + m_pDocumentClass->setClipboard = doc_setClipboard; m_pDocumentClass->paste = doc_paste; m_pDocumentClass->setGraphicSelection = doc_setGraphicSelection; m_pDocumentClass->resetSelection = doc_resetSelection; @@ -3292,10 +3306,11 @@ static void doc_setTextSelection(LibreOfficeKitDocument* pThis, int nType, int n static bool getFromTransferrable( const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable, - const char *pMimeType, OString &aRet) + const OString &aInMimeType, OString &aRet) { + OString aMimeType(aInMimeType); + // Take care of UTF-8 text here. - OString aMimeType(pMimeType); bool bConvert = false; sal_Int32 nIndex = 0; if (aMimeType.getToken(0, ';', nIndex) == "text/plain") @@ -3355,6 +3370,14 @@ static bool getFromTransferrable( return true;; } +// Tolerate embedded \0s etc. +static char *convertOString(const OString &rStr) +{ + char* pMemory = static_cast<char*>(malloc(rStr.getLength() + 1)); + memcpy(pMemory, rStr.getStr(), rStr.getLength() + 1); + return pMemory; +} + static char* doc_getTextSelection(LibreOfficeKitDocument* pThis, const char* pMimeType, char** pUsedMimeType) { comphelper::ProfileZone aZone("doc_getTextSelection"); @@ -3381,30 +3404,107 @@ static char* doc_getTextSelection(LibreOfficeKitDocument* pThis, const char* pMi pType = "text/plain;charset=utf-8"; OString aRet; - bool bSuccess = getFromTransferrable(xTransferable, pType, aRet); + bool bSuccess = getFromTransferrable(xTransferable, OString(pType), aRet); if (!bSuccess) return nullptr; - char* pMemory = static_cast<char*>(malloc(aRet.getLength() + 1)); - assert(pMemory); // Don't handle OOM conditions - strcpy(pMemory, aRet.getStr()); - if (pUsedMimeType) // legacy { if (pMimeType) + *pUsedMimeType = strdup(pMimeType); + else + *pUsedMimeType = nullptr; + } + + return convertOString(aRet); +} + +static int doc_getSelection(LibreOfficeKitDocument* pThis, + const char **pMimeTypes, + size_t *pOutCount, + char ***pOutMimeTypes, + size_t **pOutSizes, + char ***pOutStreams) +{ + SolarMutexGuard aGuard; + SetLastExceptionMsg(); + + assert (pOutCount); + assert (pOutMimeTypes); + assert (pOutSizes); + assert (pOutStreams); + + *pOutCount = 0; + *pOutMimeTypes = nullptr; + *pOutSizes = nullptr; + *pOutStreams = nullptr; + + ITiledRenderable* pDoc = getTiledRenderable(pThis); + if (!pDoc) + { + SetLastExceptionMsg("Document doesn't support tiled rendering"); + return 0; + } + + css::uno::Reference<css::datatransfer::XTransferable> xTransferable = pDoc->getSelection(); + if (!xTransferable) + { + SetLastExceptionMsg("No selection available"); + return 0; + } + + std::vector<OString> aMimeTypes; + if (!pMimeTypes) // everything + { + uno::Sequence< css::datatransfer::DataFlavor > flavors = xTransferable->getTransferDataFlavors(); + if (!flavors.getLength()) + { + SetLastExceptionMsg("Flavourless selection"); + return 0; + } + for (auto &it : flavors) + aMimeTypes.push_back(OUStringToOString(it.MimeType, RTL_TEXTENCODING_UTF8)); + } + else + { + for (size_t i = 0; pMimeTypes[i]; ++i) + aMimeTypes.push_back(OString(pMimeTypes[i])); + } + + *pOutCount = aMimeTypes.size(); + *pOutSizes = static_cast<size_t *>(malloc(*pOutCount * sizeof(size_t))); + *pOutMimeTypes = static_cast<char **>(malloc(*pOutCount * sizeof(char *))); + *pOutStreams = static_cast<char **>(malloc(*pOutCount * sizeof(char *))); + for (size_t i = 0; i < aMimeTypes.size(); ++i) + { + (*pOutMimeTypes)[i] = strdup(aMimeTypes[i].getStr()); + + OString aRet; + bool bSuccess = getFromTransferrable(xTransferable, aMimeTypes[i], aRet); + if (!bSuccess || aRet.getLength() < 1) { - *pUsedMimeType = static_cast<char*>(malloc(strlen(pMimeType) + 1)); - strcpy(*pUsedMimeType, pMimeType); + (*pOutSizes)[i] = 0; + (*pOutStreams)[i] = nullptr; } else - *pUsedMimeType = nullptr; + { + (*pOutSizes)[i] = aRet.getLength(); + (*pOutStreams)[i] = convertOString(aRet); + } } - return pMemory; + return 1; } -static bool doc_paste(LibreOfficeKitDocument* pThis, const char* pMimeType, const char* pData, size_t nSize) +static int doc_setClipboard(LibreOfficeKitDocument* pThis, + const size_t nInCount, + const char **pInMimeTypes, + const size_t *pInSizes, + const char **pInStreams) { + SolarMutexGuard aGuard; + if (gImpl) + comphelper::ProfileZone aZone("doc_paste"); SolarMutexGuard aGuard; @@ -3417,16 +3517,34 @@ static bool doc_paste(LibreOfficeKitDocument* pThis, const char* pMimeType, cons return false; } - uno::Reference<datatransfer::XTransferable> xTransferable(new LOKTransferable(pMimeType, pData, nSize)); + uno::Reference<datatransfer::XTransferable> xTransferable(new LOKTransferable(nInCount, pInMimeTypes, pInSizes, pInStreams)); uno::Reference<datatransfer::clipboard::XClipboard> xClipboard(new LOKClipboard); xClipboard->setContents(xTransferable, uno::Reference<datatransfer::clipboard::XClipboardOwner>()); pDoc->setClipboard(xClipboard); + if (!pDoc->isMimeTypeSupported()) { SetLastExceptionMsg("Document doesn't support this mime type"); return false; } + return true; +} + +static bool doc_paste(LibreOfficeKitDocument* pThis, const char* pMimeType, const char* pData, size_t nSize) +{ + SolarMutexGuard aGuard; + + const char *pInMimeTypes[1]; + const char *pInStreams[1]; + size_t pInSizes[1]; + pInMimeTypes[0] = pMimeType; + pInSizes[0] = nSize; + pInStreams[0] = pData; + + if (!doc_setClipboard(pThis, 1, pInMimeTypes, pInSizes, pInStreams)) + return false; + uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence( { {"AnchorType", uno::makeAny(static_cast<sal_uInt16>(text::TextContentAnchorType_AS_CHARACTER))}, diff --git a/desktop/source/lib/lokclipboard.cxx b/desktop/source/lib/lokclipboard.cxx index e5ba5fe9e80a..015690bd9bc4 100644 --- a/desktop/source/lib/lokclipboard.cxx +++ b/desktop/source/lib/lokclipboard.cxx @@ -38,50 +38,65 @@ LOKTransferable::LOKTransferable(OUString sMimeType, const css::uno::Sequence<sa { } -uno::Any SAL_CALL LOKTransferable::getTransferData(const datatransfer::DataFlavor& rFlavor) +LOKTransferable::LOKTransferable(const size_t nInCount, const char** pInMimeTypes, + const size_t* pInSizes, const char** pInStreams) { - uno::Any aRet; - if (rFlavor.DataType == cppu::UnoType<OUString>::get()) + m_aContent.reserve(nInCount); + m_aFlavors = css::uno::Sequence<css::datatransfer::DataFlavor>(nInCount); + for (size_t i = 0; i < nInCount; ++i) { - auto pText = reinterpret_cast<sal_Char*>(m_aSequence.getArray()); - aRet <<= OUString(pText, m_aSequence.getLength(), RTL_TEXTENCODING_UTF8); + OUString aMimeType = OUString::fromUtf8(pInMimeTypes[i]); + + // cf. sot/source/base/exchange.cxx for these two exceptional types. + if (aMimeType.startsWith("text/plain")) + { + aMimeType = "text/plain;charset=utf-16"; + m_aFlavors[i].DataType = cppu::UnoType<OUString>::get(); + } + else if (aMimeType == "application/x-libreoffice-tsvc") + m_aFlavors[i].DataType = cppu::UnoType<OUString>::get(); + else + m_aFlavors[i].DataType = cppu::UnoType<uno::Sequence<sal_Int8>>::get(); + m_aFlavors[i].MimeType = aMimeType; + m_aFlavors[i].HumanPresentableName = aMimeType; + + uno::Any aContent; + if (m_aFlavors[i].DataType == cppu::UnoType<OUString>::get()) + aContent <<= OUString(pInStreams[i], pInSizes[i], RTL_TEXTENCODING_UTF8); + else + aContent <<= css::uno::Sequence<sal_Int8>( + reinterpret_cast<const sal_Int8*>(pInStreams[i]), pInSizes[i]); + m_aContent.push_back(aContent); } - else - aRet <<= m_aSequence; - return aRet; } -std::vector<datatransfer::DataFlavor> LOKTransferable::getTransferDataFlavorsAsVector() +uno::Any SAL_CALL LOKTransferable::getTransferData(const datatransfer::DataFlavor& rFlavor) { - std::vector<datatransfer::DataFlavor> aRet; - datatransfer::DataFlavor aFlavor; - aFlavor.MimeType = m_aMimeType; - aFlavor.DataType = cppu::UnoType<uno::Sequence<sal_Int8>>::get(); - - sal_Int32 nIndex(0); - if (m_aMimeType.getToken(0, ';', nIndex) == "text/plain") + assert(m_aContent.size() == m_aFlavors.getLength()); + for (size_t i = 0; i < m_aContent.size(); ++i) { - if (m_aMimeType.getToken(0, ';', nIndex) != "charset=utf-16") - aFlavor.MimeType = "text/plain;charset=utf-16"; - aFlavor.DataType = cppu::UnoType<OUString>::get(); + if (m_aFlavors[i].MimeType == rFlavor.MimeType) + { + if (m_aFlavors[i].DataType != rFlavor.DataType) + SAL_WARN("lok", "Horror type mismatch!"); + return m_aContent[i]; + } } - aRet.push_back(aFlavor); - - return aRet; + return uno::Any(); } uno::Sequence<datatransfer::DataFlavor> SAL_CALL LOKTransferable::getTransferDataFlavors() { - return comphelper::containerToSequence(getTransferDataFlavorsAsVector()); + return m_aFlavors; } sal_Bool SAL_CALL LOKTransferable::isDataFlavorSupported(const datatransfer::DataFlavor& rFlavor) { - const std::vector<datatransfer::DataFlavor> aFlavors = getTransferDataFlavorsAsVector(); - return std::any_of(aFlavors.begin(), aFlavors.end(), - [&rFlavor](const datatransfer::DataFlavor& i) { - return i.MimeType == rFlavor.MimeType && i.DataType == rFlavor.DataType; - }); ... etc. - the rest is truncated _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits